summaryrefslogtreecommitdiff
path: root/sysdeps/linux-gnu/trace.h
blob: 1599738b3d02646805873b543bd8a784018f1de9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/*
 * This file is part of ltrace.
 * Copyright (C) 2011,2012,2013 Petr Machata, Red Hat Inc.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 */

#ifndef LTRACE_LINUX_TRACE_H
#define LTRACE_LINUX_TRACE_H

#include "proc.h"

/* This publishes some Linux-specific data structures used for process
 * handling.  */

/**
 * This is used for bookkeeping related to PIDs that the event
 * handlers work with.
 */
struct pid_task {
	pid_t pid;	/* This may be 0 for tasks that exited
			 * mid-handling.  */
	int sigstopped : 1;
	int got_event : 1;
	int delivered : 1;
	int vforked : 1;
	int sysret : 1;
};

struct pid_set {
	struct pid_task *tasks;
	size_t count;
	size_t alloc;
};

/**
 * Breakpoint re-enablement.  When we hit a breakpoint, we must
 * disable it, single-step, and re-enable it.  That single-step can be
 * done only by one task in a task group, while others are stopped,
 * otherwise the processes would race for who sees the breakpoint
 * disabled and who doesn't.  The following is to keep track of it
 * all.
 */
struct process_stopping_handler
{
	struct event_handler super;

	/* The task that is doing the re-enablement.  */
	struct process *task_enabling_breakpoint;

	/* The pointer being re-enabled.  */
	struct breakpoint *breakpoint_being_enabled;

	/* Software singlestep breakpoints, if any needed.  */
	struct breakpoint *sws_bps[2];

	/* When all tasks are stopped, this callback gets called.  */
	void (*on_all_stopped)(struct process_stopping_handler *);

	/* When we get a singlestep event, this is called to decide
	 * whether to stop stepping, or whether to enable the
	 * brakpoint, sink remaining signals, and continue
	 * everyone.  */
	enum callback_status (*keep_stepping_p)
		(struct process_stopping_handler *);

	/* Whether we need to use ugly workaround to get around
	 * various problems with singlestepping.  */
	enum callback_status (*ugly_workaround_p)
		(struct process_stopping_handler *);

	enum {
		/* We are waiting for everyone to land in t/T.  */
		PSH_STOPPING = 0,

		/* We are doing the PTRACE_SINGLESTEP.  */
		PSH_SINGLESTEP,

		/* We are waiting for all the SIGSTOPs to arrive so
		 * that we can sink them.  */
		PSH_SINKING,

		/* This is for tracking the ugly workaround.  */
		PSH_UGLY_WORKAROUND,
	} state;

	int exiting;

	struct pid_set pids;
};

/* Allocate a process stopping handler, initialize it and install it.
 * Return 0 on success or a negative value on failure.  Pass NULL for
 * each callback to use a default instead.  The default for
 * ON_ALL_STOPPED is LINUX_PTRACE_DISABLE_AND_SINGLESTEP, the default
 * for KEEP_STEPPING_P and UGLY_WORKAROUND_P is "no".  */
int process_install_stopping_handler
	(struct process *proc, struct breakpoint *sbp,
	 void (*on_all_stopped)(struct process_stopping_handler *),
	 enum callback_status (*keep_stepping_p)
		 (struct process_stopping_handler *),
	 enum callback_status (*ugly_workaround_p)
		(struct process_stopping_handler *));

void linux_ptrace_disable_and_singlestep(struct process_stopping_handler *self);
void linux_ptrace_disable_and_continue(struct process_stopping_handler *self);

/* When main binary needs to call an IFUNC function defined in the
 * binary itself, a PLT entry is set up so that dynamic linker can get
 * involved and resolve the symbol.  But unlike other PLT relocation,
 * this one can't rely on symbol table being available.  So it doesn't
 * reference the symbol by its name, but by its address, and
 * correspondingly, has another type.  When arch backend wishes to
 * support these IRELATIVE relocations, it should override
 * arch_elf_add_plt_entry and dispatch to this function for IRELATIVE
 * relocations.
 *
 * This function behaves as arch_elf_add_plt_entry, except that it
 * doesn't take name for a parameter, but instead looks up the name in
 * symbol tables in LTE.  */
enum plt_status linux_elf_add_plt_entry_irelative(struct process *proc,
						  struct ltelf *lte,
						  GElf_Rela *rela, size_t ndx,
						  struct library_symbol **ret);

/* Service routine of the above.  Determines a name corresponding to
 * ADDR, or invents a new one.  Returns NULL on failures, otherwise it
 * returns a malloc'd pointer that the caller is responsible for
 * freeing.  */
char *linux_elf_find_irelative_name(struct ltelf *lte, GElf_Addr addr);

/* Returns ${NAME}.IFUNC in a newly-malloc'd block, or NULL on
 * failures.  */
char *linux_append_IFUNC_to_name(const char *name);

/* Returns a statically allocated prototype that represents the
 * prototype "void *()".  Never fails.  */
struct prototype *linux_IFUNC_prototype(void);


#endif /* LTRACE_LINUX_TRACE_H */