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