breakpoints.c revision e84fa00bdabf20f57d1d02d159aa3fcab4e549fe
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	if (proc->breakpoints_enabled <= 0) {
128#ifdef __powerpc__
129		unsigned long a;
130
131		/*
132		 * PPC HACK! (XXX FIXME TODO)
133		 * If the dynamic linker hasn't populated the PLT then
134		 * dont enable the breakpoints
135		 */
136		if (options.libcalls) {
137			a = ptrace(PTRACE_PEEKTEXT, proc->pid,
138				   sym2addr(proc, proc->list_of_symbols),
139				   0);
140			if (a == 0x0)
141				return;
142		}
143#endif
144
145		debug(1, "Enabling breakpoints for pid %u...", proc->pid);
146		if (proc->breakpoints) {
147			dict_apply_to_all(proc->breakpoints, enable_bp_cb,
148					  proc);
149		}
150#ifdef __mips__
151		{
152			/*
153			 * I'm sure there is a nicer way to do this. We need to
154			 * insert breakpoints _after_ the child has been started.
155			 */
156			struct library_symbol *sym;
157			struct library_symbol *new_sym;
158			sym=proc->list_of_symbols;
159			while(sym){
160				void *addr= sym2addr(proc,sym);
161				if(!addr){
162					sym=sym->next;
163					continue;
164				}
165				if(dict_find_entry(proc->breakpoints,addr)){
166					sym=sym->next;
167					continue;
168				}
169				debug(2,"inserting bp %p %s",addr,sym->name);
170				new_sym=malloc(sizeof(*new_sym) + strlen(sym->name) + 1);
171				memcpy(new_sym,sym,sizeof(*new_sym) + strlen(sym->name) + 1);
172				new_sym->next=proc->list_of_symbols;
173				proc->list_of_symbols=new_sym;
174				insert_breakpoint(proc, addr, new_sym);
175				sym=sym->next;
176			}
177		}
178#endif
179	}
180	proc->breakpoints_enabled = 1;
181}
182
183static void
184disable_bp_cb(void *addr, void *sbp, void *proc)
185{
186	debug(DEBUG_FUNCTION, "disable_bp_cb(pid=%d)", ((Process *)proc)->pid);
187	if (((Breakpoint *)sbp)->enabled) {
188		disable_breakpoint(proc, sbp);
189	}
190}
191
192void
193disable_all_breakpoints(Process *proc) {
194	debug(DEBUG_FUNCTION, "disable_all_breakpoints(pid=%d)", proc->pid);
195	assert(proc->leader == proc);
196	if (proc->breakpoints_enabled) {
197		debug(1, "Disabling breakpoints for pid %u...", proc->pid);
198		dict_apply_to_all(proc->breakpoints, disable_bp_cb, proc);
199	}
200	proc->breakpoints_enabled = 0;
201}
202
203static void
204free_bp_cb(void *addr, void *sbp, void *data) {
205	debug(DEBUG_FUNCTION, "free_bp_cb(sbp=%p)", sbp);
206	assert(sbp);
207	free(sbp);
208}
209
210int
211breakpoints_init(Process *proc, int enable)
212{
213	struct library_symbol *sym;
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			/* XXX leak breakpoints */
238			return -1;
239		}
240
241		if (opt_e) {
242			struct library_symbol **tmp1 = &proc->list_of_symbols;
243			while (*tmp1) {
244				struct opt_e_t *tmp2 = opt_e;
245				int keep = !opt_e_enable;
246
247				while (tmp2) {
248					if (!strcmp((*tmp1)->name,
249						    tmp2->name)) {
250						keep = opt_e_enable;
251					}
252					tmp2 = tmp2->next;
253				}
254				if (!keep) {
255					*tmp1 = (*tmp1)->next;
256				} else {
257					tmp1 = &((*tmp1)->next);
258				}
259			}
260		}
261	}
262
263	for (sym = proc->list_of_symbols; sym; sym = sym->next)
264		insert_breakpoint(proc, sym2addr(proc, sym), sym, enable);
265
266	proc->callstack_depth = 0;
267	proc->breakpoints_enabled = -1;
268	return 0;
269}
270
271void
272reinitialize_breakpoints(Process *proc) {
273	struct library_symbol *sym;
274
275	debug(DEBUG_FUNCTION, "reinitialize_breakpoints(pid=%d)", proc->pid);
276
277	sym = proc->list_of_symbols;
278
279	while (sym) {
280		if (sym->needs_init) {
281			insert_breakpoint(proc, sym2addr(proc, sym), sym, 1);
282			if (sym->needs_init && !sym->is_weak) {
283				fprintf(stderr,
284					"could not re-initialize breakpoint for \"%s\" in file \"%s\"\n",
285					sym->name, proc->filename);
286				exit(1);
287			}
288		}
289		sym = sym->next;
290	}
291}
292