breakpoints.c revision 17bcdd38264a9adc161d6cdee834969efc803f57
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 (((struct breakpoint *)sbp)->enabled)
120		enable_breakpoint(proc, sbp);
121}
122
123void
124enable_all_breakpoints(Process *proc)
125{
126	debug(DEBUG_FUNCTION, "enable_all_breakpoints(pid=%d)", proc->pid);
127
128	debug(1, "Enabling breakpoints for pid %u...", proc->pid);
129	if (proc->breakpoints) {
130		dict_apply_to_all(proc->breakpoints, enable_bp_cb,
131				  proc);
132	}
133#ifdef __mips__
134	{
135		/*
136		 * I'm sure there is a nicer way to do this. We need to
137		 * insert breakpoints _after_ the child has been started.
138		 */
139		struct library_symbol *sym;
140		struct library_symbol *new_sym;
141		sym=proc->list_of_symbols;
142		while(sym){
143			void *addr= sym2addr(proc,sym);
144			if(!addr){
145				sym=sym->next;
146				continue;
147			}
148			if(dict_find_entry(proc->breakpoints,addr)){
149				sym=sym->next;
150				continue;
151			}
152			debug(2,"inserting bp %p %s",addr,sym->name);
153			new_sym=malloc(sizeof(*new_sym) + strlen(sym->name) + 1);
154			memcpy(new_sym,sym,sizeof(*new_sym) + strlen(sym->name) + 1);
155			new_sym->next=proc->list_of_symbols;
156			proc->list_of_symbols=new_sym;
157			insert_breakpoint(proc, addr, new_sym);
158			sym=sym->next;
159		}
160	}
161#endif
162}
163
164static void
165disable_bp_cb(void *addr, void *sbp, void *proc)
166{
167	debug(DEBUG_FUNCTION, "disable_bp_cb(pid=%d)", ((Process *)proc)->pid);
168	if (((struct breakpoint *)sbp)->enabled)
169		disable_breakpoint(proc, sbp);
170}
171
172void
173disable_all_breakpoints(Process *proc) {
174	debug(DEBUG_FUNCTION, "disable_all_breakpoints(pid=%d)", proc->pid);
175	assert(proc->leader == proc);
176	dict_apply_to_all(proc->breakpoints, disable_bp_cb, proc);
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
186static void
187entry_callback_hit(struct breakpoint *bp, struct Process *proc)
188{
189	if (proc == NULL || proc->leader == NULL)
190		return;
191	delete_breakpoint(proc, bp->addr); // xxx
192	reinitialize_breakpoints(proc->leader);
193}
194
195int
196breakpoints_init(Process *proc, int enable)
197{
198	debug(DEBUG_FUNCTION, "breakpoints_init(pid=%d)", proc->pid);
199	if (proc->breakpoints) {	/* let's remove that struct */
200		dict_apply_to_all(proc->breakpoints, free_bp_cb, NULL);
201		dict_clear(proc->breakpoints);
202		proc->breakpoints = NULL;
203	}
204
205	/* Only the thread group leader should hold the breakpoints.
206	 * (N.B. PID may be set to 0 temporarily when called by
207	 * handle_exec).  */
208	assert(proc->leader == proc);
209
210	proc->breakpoints = dict_init(dict_key2hash_int,
211				      dict_key_cmp_int);
212
213	destroy_library_symbol_chain(proc->list_of_symbols);
214	proc->list_of_symbols = NULL;
215
216	GElf_Addr entry;
217	if (options.libcalls && proc->filename) {
218		proc->list_of_symbols = read_elf(proc, &entry);
219		if (proc->list_of_symbols == NULL) {
220		fail:
221			/* XXX leak breakpoints */
222			return -1;
223		}
224
225		if (opt_e) {
226			struct library_symbol **tmp1 = &proc->list_of_symbols;
227			while (*tmp1) {
228				struct opt_e_t *tmp2 = opt_e;
229				int keep = !opt_e_enable;
230
231				while (tmp2) {
232					if (!strcmp((*tmp1)->name,
233						    tmp2->name)) {
234						keep = opt_e_enable;
235					}
236					tmp2 = tmp2->next;
237				}
238				if (!keep) {
239					*tmp1 = (*tmp1)->next;
240				} else {
241					tmp1 = &((*tmp1)->next);
242				}
243			}
244		}
245	}
246
247	struct breakpoint *entry_bp
248		= insert_breakpoint(proc, (void *)(uintptr_t)entry, NULL, 1);
249	if (entry_bp == NULL) {
250		fprintf(stderr, "fail!\n");
251		goto fail;
252	}
253
254	static struct bp_callbacks entry_callbacks = {
255		.on_hit = entry_callback_hit,
256	};
257	entry_bp->cbs = &entry_callbacks;
258
259	proc->callstack_depth = 0;
260	return 0;
261}
262
263void
264reinitialize_breakpoints(Process *proc) {
265	struct library_symbol *sym;
266
267	debug(DEBUG_FUNCTION, "reinitialize_breakpoints(pid=%d)", proc->pid);
268
269	sym = proc->list_of_symbols;
270
271	while (sym) {
272		if (sym->needs_init) {
273			insert_breakpoint(proc, sym2addr(proc, sym), sym, 1);
274			if (sym->needs_init && !sym->is_weak) {
275				fprintf(stderr,
276					"could not re-initialize breakpoint for \"%s\" in file \"%s\"\n",
277					sym->name, proc->filename);
278				exit(1);
279			}
280		}
281		sym = sym->next;
282	}
283}
284