breakpoints.c revision 366c2f46d844f040458df9b7e35fc3b8527ed2d3
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#include "proc.h"
14
15void
16breakpoint_on_hit(struct breakpoint *bp, struct Process *proc)
17{
18	assert(bp != NULL);
19	if (bp->cbs != NULL && bp->cbs->on_hit != NULL)
20		(bp->cbs->on_hit) (bp, proc);
21}
22
23void
24breakpoint_on_destroy(struct breakpoint *bp)
25{
26	assert(bp != NULL);
27	if (bp->cbs != NULL && bp->cbs->on_destroy != NULL)
28		(bp->cbs->on_destroy) (bp);
29}
30
31/*****************************************************************************/
32
33struct breakpoint *
34address2bpstruct(Process *proc, void *addr)
35{
36	assert(proc != NULL);
37	assert(proc->breakpoints != NULL);
38	assert(proc->leader == proc);
39	debug(DEBUG_FUNCTION, "address2bpstruct(pid=%d, addr=%p)", proc->pid, addr);
40	return dict_find_entry(proc->breakpoints, addr);
41}
42
43struct breakpoint *
44insert_breakpoint(Process *proc, void *addr,
45		  struct library_symbol *libsym, int enable)
46{
47	struct breakpoint *sbp;
48
49	Process * leader = proc->leader;
50
51	/* Only the group leader should be getting the breakpoints and
52	 * thus have ->breakpoint initialized.  */
53	assert(leader != NULL);
54	assert(leader->breakpoints != NULL);
55
56#ifdef __arm__
57	int thumb_mode = (int)addr & 1;
58	if (thumb_mode)
59		addr = (void *)((int)addr & ~1);
60#endif
61
62	debug(DEBUG_FUNCTION, "insert_breakpoint(pid=%d, addr=%p, symbol=%s)", proc->pid, addr, libsym ? libsym->name : "NULL");
63	debug(1, "symbol=%s, addr=%p", libsym?libsym->name:"(nil)", addr);
64
65	if (!addr)
66		return NULL;
67
68	if (libsym)
69		libsym->needs_init = 0;
70
71	sbp = dict_find_entry(leader->breakpoints, addr);
72	if (sbp == NULL) {
73		sbp = calloc(1, sizeof(*sbp));
74		if (sbp == NULL) {
75			return NULL;	/* TODO FIXME XXX: error_mem */
76		}
77		dict_enter(leader->breakpoints, addr, sbp);
78		sbp->addr = addr;
79		sbp->libsym = libsym;
80	}
81#ifdef __arm__
82	sbp->thumb_mode = thumb_mode | proc->thumb_mode;
83	proc->thumb_mode = 0;
84#endif
85	sbp->enabled++;
86	if (sbp->enabled == 1 && enable) {
87		assert(proc->pid != 0);
88		enable_breakpoint(proc, sbp);
89	}
90
91	return sbp;
92}
93
94void
95delete_breakpoint(Process *proc, void *addr)
96{
97	struct breakpoint *sbp;
98
99	debug(DEBUG_FUNCTION, "delete_breakpoint(pid=%d, addr=%p)", proc->pid, addr);
100
101	Process * leader = proc->leader;
102	assert(leader != NULL);
103
104	sbp = dict_find_entry(leader->breakpoints, addr);
105	assert(sbp);		/* FIXME: remove after debugging has been done. */
106	/* This should only happen on out-of-memory conditions. */
107	if (sbp == NULL)
108		return;
109
110	sbp->enabled--;
111	if (sbp->enabled == 0)
112		disable_breakpoint(proc, sbp);
113	assert(sbp->enabled >= 0);
114}
115
116static void
117enable_bp_cb(void *addr, void *sbp, void *proc)
118{
119	debug(DEBUG_FUNCTION, "enable_bp_cb(pid=%d)", ((Process *)proc)->pid);
120	if (((struct breakpoint *)sbp)->enabled)
121		enable_breakpoint(proc, sbp);
122}
123
124void
125enable_all_breakpoints(Process *proc)
126{
127	debug(DEBUG_FUNCTION, "enable_all_breakpoints(pid=%d)", proc->pid);
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
165static void
166disable_bp_cb(void *addr, void *sbp, void *proc)
167{
168	debug(DEBUG_FUNCTION, "disable_bp_cb(pid=%d)", ((Process *)proc)->pid);
169	if (((struct breakpoint *)sbp)->enabled)
170		disable_breakpoint(proc, sbp);
171}
172
173void
174disable_all_breakpoints(Process *proc) {
175	debug(DEBUG_FUNCTION, "disable_all_breakpoints(pid=%d)", proc->pid);
176	assert(proc->leader == proc);
177	dict_apply_to_all(proc->breakpoints, disable_bp_cb, proc);
178}
179
180static void
181free_bp_cb(void *addr, void *sbp, void *data) {
182	debug(DEBUG_FUNCTION, "free_bp_cb(sbp=%p)", sbp);
183	assert(sbp);
184	free(sbp);
185}
186
187static void
188entry_callback_hit(struct breakpoint *bp, struct Process *proc)
189{
190	if (proc == NULL || proc->leader == NULL)
191		return;
192	delete_breakpoint(proc, bp->addr); // xxx
193	reinitialize_breakpoints(proc->leader);
194}
195
196int
197breakpoints_init(Process *proc, int enable)
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	GElf_Addr entry;
218	if (options.libcalls && proc->filename) {
219		proc->list_of_symbols = read_elf(proc, &entry);
220		if (proc->list_of_symbols == NULL) {
221		fail:
222			/* XXX leak breakpoints */
223			return -1;
224		}
225
226		if (opt_e) {
227			struct library_symbol **tmp1 = &proc->list_of_symbols;
228			while (*tmp1) {
229				struct opt_e_t *tmp2 = opt_e;
230				int keep = !opt_e_enable;
231
232				while (tmp2) {
233					if (!strcmp((*tmp1)->name,
234						    tmp2->name)) {
235						keep = opt_e_enable;
236					}
237					tmp2 = tmp2->next;
238				}
239				if (!keep) {
240					*tmp1 = (*tmp1)->next;
241				} else {
242					tmp1 = &((*tmp1)->next);
243				}
244			}
245		}
246	}
247
248	struct breakpoint *entry_bp
249		= insert_breakpoint(proc, (void *)(uintptr_t)entry, NULL, 1);
250	if (entry_bp == NULL) {
251		fprintf(stderr, "fail!\n");
252		goto fail;
253	}
254
255	static struct bp_callbacks entry_callbacks = {
256		.on_hit = entry_callback_hit,
257	};
258	entry_bp->cbs = &entry_callbacks;
259
260	proc->callstack_depth = 0;
261	return 0;
262}
263
264void
265reinitialize_breakpoints(Process *proc) {
266	struct library_symbol *sym;
267
268	debug(DEBUG_FUNCTION, "reinitialize_breakpoints(pid=%d)", proc->pid);
269
270	sym = proc->list_of_symbols;
271
272	while (sym) {
273		if (sym->needs_init) {
274			insert_breakpoint(proc, sym2addr(proc, sym), sym, 1);
275			if (sym->needs_init && !sym->is_weak) {
276				fprintf(stderr,
277					"could not re-initialize breakpoint for \"%s\" in file \"%s\"\n",
278					sym->name, proc->filename);
279				exit(1);
280			}
281		}
282		sym = sym->next;
283	}
284}
285