breakpoints.c revision 9294d82f67e20f5f2b61f317ad04f5cb717c7d27
1#include "config.h"
2
3#include <stdlib.h>
4#include <string.h>
5#include <assert.h>
6
7#ifdef __powerpc__
8#include <sys/ptrace.h>
9#endif
10
11#include "breakpoint.h"
12#include "common.h"
13
14/*****************************************************************************/
15
16struct breakpoint *
17address2bpstruct(Process *proc, void *addr)
18{
19	assert(proc != NULL);
20	assert(proc->breakpoints != NULL);
21	assert(proc->leader == proc);
22	debug(DEBUG_FUNCTION, "address2bpstruct(pid=%d, addr=%p)", proc->pid, addr);
23	return dict_find_entry(proc->breakpoints, addr);
24}
25
26struct breakpoint *
27insert_breakpoint(Process *proc, void *addr,
28		  struct library_symbol *libsym, int enable)
29{
30	struct breakpoint *sbp;
31
32	Process * leader = proc->leader;
33
34	/* Only the group leader should be getting the breakpoints and
35	 * thus have ->breakpoint initialized.  */
36	assert(leader != NULL);
37	assert(leader->breakpoints != NULL);
38
39#ifdef __arm__
40	int thumb_mode = (int)addr & 1;
41	if (thumb_mode)
42		addr = (void *)((int)addr & ~1);
43#endif
44
45	debug(DEBUG_FUNCTION, "insert_breakpoint(pid=%d, addr=%p, symbol=%s)", proc->pid, addr, libsym ? libsym->name : "NULL");
46	debug(1, "symbol=%s, addr=%p", libsym?libsym->name:"(nil)", addr);
47
48	if (!addr)
49		return NULL;
50
51	if (libsym)
52		libsym->needs_init = 0;
53
54	sbp = dict_find_entry(leader->breakpoints, addr);
55	if (sbp == NULL) {
56		sbp = calloc(1, sizeof(*sbp));
57		if (sbp == NULL) {
58			return NULL;	/* TODO FIXME XXX: error_mem */
59		}
60		dict_enter(leader->breakpoints, addr, sbp);
61		sbp->addr = addr;
62		sbp->libsym = libsym;
63	}
64#ifdef __arm__
65	sbp->thumb_mode = thumb_mode | proc->thumb_mode;
66	proc->thumb_mode = 0;
67#endif
68	sbp->enabled++;
69	if (sbp->enabled == 1 && enable) {
70		assert(proc->pid != 0);
71		enable_breakpoint(proc, sbp);
72	}
73
74	return sbp;
75}
76
77void
78delete_breakpoint(Process *proc, void *addr)
79{
80	struct breakpoint *sbp;
81
82	debug(DEBUG_FUNCTION, "delete_breakpoint(pid=%d, addr=%p)", proc->pid, addr);
83
84	Process * leader = proc->leader;
85	assert(leader != NULL);
86
87	sbp = dict_find_entry(leader->breakpoints, addr);
88	assert(sbp);		/* FIXME: remove after debugging has been done. */
89	/* This should only happen on out-of-memory conditions. */
90	if (sbp == NULL)
91		return;
92
93	sbp->enabled--;
94	if (sbp->enabled == 0)
95		disable_breakpoint(proc, sbp);
96	assert(sbp->enabled >= 0);
97}
98
99static void
100enable_bp_cb(void *addr, void *sbp, void *proc)
101{
102	debug(DEBUG_FUNCTION, "enable_bp_cb(pid=%d)", ((Process *)proc)->pid);
103	if (((Breakpoint *)sbp)->enabled) {
104		enable_breakpoint(proc, sbp);
105	}
106}
107
108void
109enable_all_breakpoints(Process *proc) {
110	debug(DEBUG_FUNCTION, "enable_all_breakpoints(pid=%d)", proc->pid);
111	if (proc->breakpoints_enabled <= 0) {
112#ifdef __powerpc__
113		unsigned long a;
114
115		/*
116		 * PPC HACK! (XXX FIXME TODO)
117		 * If the dynamic linker hasn't populated the PLT then
118		 * dont enable the breakpoints
119		 */
120		if (options.libcalls) {
121			a = ptrace(PTRACE_PEEKTEXT, proc->pid,
122				   sym2addr(proc, proc->list_of_symbols),
123				   0);
124			if (a == 0x0)
125				return;
126		}
127#endif
128
129		debug(1, "Enabling breakpoints for pid %u...", proc->pid);
130		if (proc->breakpoints) {
131			dict_apply_to_all(proc->breakpoints, enable_bp_cb,
132					  proc);
133		}
134#ifdef __mips__
135		{
136			/*
137			 * I'm sure there is a nicer way to do this. We need to
138			 * insert breakpoints _after_ the child has been started.
139			 */
140			struct library_symbol *sym;
141			struct library_symbol *new_sym;
142			sym=proc->list_of_symbols;
143			while(sym){
144				void *addr= sym2addr(proc,sym);
145				if(!addr){
146					sym=sym->next;
147					continue;
148				}
149				if(dict_find_entry(proc->breakpoints,addr)){
150					sym=sym->next;
151					continue;
152				}
153				debug(2,"inserting bp %p %s",addr,sym->name);
154				new_sym=malloc(sizeof(*new_sym) + strlen(sym->name) + 1);
155				memcpy(new_sym,sym,sizeof(*new_sym) + strlen(sym->name) + 1);
156				new_sym->next=proc->list_of_symbols;
157				proc->list_of_symbols=new_sym;
158				insert_breakpoint(proc, addr, new_sym);
159				sym=sym->next;
160			}
161		}
162#endif
163	}
164	proc->breakpoints_enabled = 1;
165}
166
167static void
168disable_bp_cb(void *addr, void *sbp, void *proc)
169{
170	debug(DEBUG_FUNCTION, "disable_bp_cb(pid=%d)", ((Process *)proc)->pid);
171	if (((Breakpoint *)sbp)->enabled) {
172		disable_breakpoint(proc, sbp);
173	}
174}
175
176void
177disable_all_breakpoints(Process *proc) {
178	debug(DEBUG_FUNCTION, "disable_all_breakpoints(pid=%d)", proc->pid);
179	assert(proc->leader == proc);
180	if (proc->breakpoints_enabled) {
181		debug(1, "Disabling breakpoints for pid %u...", proc->pid);
182		dict_apply_to_all(proc->breakpoints, disable_bp_cb, proc);
183	}
184	proc->breakpoints_enabled = 0;
185}
186
187static void
188free_bp_cb(void *addr, void *sbp, void *data) {
189	debug(DEBUG_FUNCTION, "free_bp_cb(sbp=%p)", sbp);
190	assert(sbp);
191	free(sbp);
192}
193
194int
195breakpoints_init(Process *proc, int enable)
196{
197	struct library_symbol *sym;
198
199	debug(DEBUG_FUNCTION, "breakpoints_init(pid=%d)", proc->pid);
200	if (proc->breakpoints) {	/* let's remove that struct */
201		dict_apply_to_all(proc->breakpoints, free_bp_cb, NULL);
202		dict_clear(proc->breakpoints);
203		proc->breakpoints = NULL;
204	}
205
206	/* Only the thread group leader should hold the breakpoints.
207	 * (N.B. PID may be set to 0 temporarily when called by
208	 * handle_exec).  */
209	assert(proc->leader == proc);
210
211	proc->breakpoints = dict_init(dict_key2hash_int,
212				      dict_key_cmp_int);
213
214	destroy_library_symbol_chain(proc->list_of_symbols);
215	proc->list_of_symbols = NULL;
216
217	if (options.libcalls && proc->filename) {
218		proc->list_of_symbols = read_elf(proc);
219		if (proc->list_of_symbols == NULL) {
220			/* XXX leak breakpoints */
221			return -1;
222		}
223
224		if (opt_e) {
225			struct library_symbol **tmp1 = &proc->list_of_symbols;
226			while (*tmp1) {
227				struct opt_e_t *tmp2 = opt_e;
228				int keep = !opt_e_enable;
229
230				while (tmp2) {
231					if (!strcmp((*tmp1)->name,
232						    tmp2->name)) {
233						keep = opt_e_enable;
234					}
235					tmp2 = tmp2->next;
236				}
237				if (!keep) {
238					*tmp1 = (*tmp1)->next;
239				} else {
240					tmp1 = &((*tmp1)->next);
241				}
242			}
243		}
244	}
245
246	for (sym = proc->list_of_symbols; sym; sym = sym->next)
247		insert_breakpoint(proc, sym2addr(proc, sym), sym, enable);
248
249	proc->callstack_depth = 0;
250	proc->breakpoints_enabled = -1;
251	return 0;
252}
253
254void
255reinitialize_breakpoints(Process *proc) {
256	struct library_symbol *sym;
257
258	debug(DEBUG_FUNCTION, "reinitialize_breakpoints(pid=%d)", proc->pid);
259
260	sym = proc->list_of_symbols;
261
262	while (sym) {
263		if (sym->needs_init) {
264			insert_breakpoint(proc, sym2addr(proc, sym), sym, 1);
265			if (sym->needs_init && !sym->is_weak) {
266				fprintf(stderr,
267					"could not re-initialize breakpoint for \"%s\" in file \"%s\"\n",
268					sym->name, proc->filename);
269				exit(1);
270			}
271		}
272		sym = sym->next;
273	}
274}
275