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