breakpoints.c revision cb9a28da448439eab4bf554810fd1004fbc00885
1#include "config.h"
2
3#include <stdlib.h>
4#include <string.h>
5#include <assert.h>
6#include <error.h>
7#include <errno.h>
8
9#ifdef __powerpc__
10#include <sys/ptrace.h>
11#endif
12
13#include "breakpoint.h"
14#include "common.h"
15#include "proc.h"
16#include "library.h"
17
18#ifndef ARCH_HAVE_TRANSLATE_ADDRESS
19int
20arch_translate_address(struct Process *proc,
21		       target_address_t addr, target_address_t *ret)
22{
23	*ret = addr;
24	return 0;
25}
26#endif
27
28void
29breakpoint_on_hit(struct breakpoint *bp, struct Process *proc)
30{
31	assert(bp != NULL);
32	if (bp->cbs != NULL && bp->cbs->on_hit != NULL)
33		(bp->cbs->on_hit)(bp, proc);
34}
35
36void
37breakpoint_on_continue(struct breakpoint *bp, struct Process *proc)
38{
39	assert(bp != NULL);
40	if (bp->cbs != NULL && bp->cbs->on_continue != NULL)
41		(bp->cbs->on_continue)(bp, proc);
42	else
43		continue_after_breakpoint(proc, bp);
44}
45
46/*****************************************************************************/
47
48struct breakpoint *
49address2bpstruct(Process *proc, void *addr)
50{
51	assert(proc != NULL);
52	assert(proc->breakpoints != NULL);
53	assert(proc->leader == proc);
54	debug(DEBUG_FUNCTION, "address2bpstruct(pid=%d, addr=%p)", proc->pid, addr);
55	return dict_find_entry(proc->breakpoints, addr);
56}
57
58#ifndef ARCH_HAVE_BREAKPOINT_DATA
59int
60arch_breakpoint_init(struct Process *proc, struct breakpoint *sbp)
61{
62	return 0;
63}
64
65void
66arch_breakpoint_destroy(struct breakpoint *sbp)
67{
68}
69#endif
70
71int
72breakpoint_init(struct breakpoint *bp, struct Process *proc,
73		target_address_t addr, struct library_symbol *libsym)
74{
75	bp->cbs = NULL;
76	bp->addr = addr;
77	memset(bp->orig_value, 0, sizeof(bp->orig_value));
78	bp->enabled = 0;
79	bp->libsym = libsym;
80	return arch_breakpoint_init(proc, bp);
81}
82
83void
84breakpoint_set_callbacks(struct breakpoint *bp, struct bp_callbacks *cbs)
85{
86	if (bp->cbs != NULL)
87		assert(bp->cbs == NULL);
88	bp->cbs = cbs;
89}
90
91void
92breakpoint_destroy(struct breakpoint *bp)
93{
94	if (bp == NULL)
95		return;
96
97	/* XXX I'm not convinced that we need on_destroy.  We already
98	 * have arch_breakpoint_destroy, which is necessary as a
99	 * counterpart of arch_breakpoint_init in any case.  */
100	if (bp->cbs != NULL && bp->cbs->on_destroy != NULL)
101		(bp->cbs->on_destroy) (bp);
102
103	arch_breakpoint_destroy(bp);
104}
105
106struct breakpoint *
107insert_breakpoint(struct Process *proc, void *addr,
108		  struct library_symbol *libsym)
109{
110	Process *leader = proc->leader;
111
112	/* Only the group leader should be getting the breakpoints and
113	 * thus have ->breakpoint initialized.  */
114	assert(leader != NULL);
115	assert(leader->breakpoints != NULL);
116
117	debug(DEBUG_FUNCTION, "insert_breakpoint(pid=%d, addr=%p, symbol=%s)", proc->pid, addr, libsym ? libsym->name : "NULL");
118	debug(1, "symbol=%s, addr=%p", libsym?libsym->name:"(nil)", addr);
119
120	if (addr == 0) {
121		/* XXX we need a better way to deal with this.  For
122		 * now, just abuse errno to carry the error
123		 * information.  */
124		errno = EINVAL;
125		return NULL;
126	}
127
128	struct breakpoint *sbp = dict_find_entry(leader->breakpoints, addr);
129	if (sbp == NULL) {
130		sbp = malloc(sizeof(*sbp));
131		if (sbp == NULL
132		    || breakpoint_init(sbp, proc, addr, libsym) < 0
133		    || dict_enter(leader->breakpoints, addr, sbp) < 0) {
134			free(sbp);
135			return NULL;
136		}
137	}
138
139	sbp->enabled++;
140	if (sbp->enabled == 1) {
141		assert(proc->pid != 0);
142		enable_breakpoint(proc, sbp);
143	}
144
145	return sbp;
146}
147
148void
149delete_breakpoint(Process *proc, void *addr)
150{
151	struct breakpoint *sbp;
152
153	debug(DEBUG_FUNCTION, "delete_breakpoint(pid=%d, addr=%p)", proc->pid, addr);
154
155	Process * leader = proc->leader;
156	assert(leader != NULL);
157
158	sbp = dict_find_entry(leader->breakpoints, addr);
159	assert(sbp);		/* FIXME: remove after debugging has been done. */
160	/* This should only happen on out-of-memory conditions. */
161	if (sbp == NULL)
162		return;
163
164	sbp->enabled--;
165	if (sbp->enabled == 0)
166		disable_breakpoint(proc, sbp);
167	assert(sbp->enabled >= 0);
168}
169
170const char *
171breakpoint_name(const struct breakpoint *bp)
172{
173	assert(bp != NULL);
174	return bp->libsym != NULL ? bp->libsym->name : NULL;
175}
176
177static void
178enable_bp_cb(void *addr, void *sbp, void *proc)
179{
180	debug(DEBUG_FUNCTION, "enable_bp_cb(pid=%d)", ((Process *)proc)->pid);
181	if (((struct breakpoint *)sbp)->enabled)
182		enable_breakpoint(proc, sbp);
183}
184
185void
186enable_all_breakpoints(Process *proc)
187{
188	debug(DEBUG_FUNCTION, "enable_all_breakpoints(pid=%d)", proc->pid);
189
190	debug(1, "Enabling breakpoints for pid %u...", proc->pid);
191	if (proc->breakpoints) {
192		dict_apply_to_all(proc->breakpoints, enable_bp_cb,
193				  proc);
194	}
195#ifdef __mips__
196	{
197		/*
198		 * I'm sure there is a nicer way to do this. We need to
199		 * insert breakpoints _after_ the child has been started.
200		 */
201		struct library_symbol *sym;
202		struct library_symbol *new_sym;
203		sym=proc->list_of_symbols;
204		while(sym){
205			void *addr= sym2addr(proc,sym);
206			if(!addr){
207				sym=sym->next;
208				continue;
209			}
210			if(dict_find_entry(proc->breakpoints,addr)){
211				sym=sym->next;
212				continue;
213			}
214			debug(2,"inserting bp %p %s",addr,sym->name);
215			new_sym=malloc(sizeof(*new_sym) + strlen(sym->name) + 1);
216			memcpy(new_sym,sym,sizeof(*new_sym) + strlen(sym->name) + 1);
217			new_sym->next=proc->list_of_symbols;
218			proc->list_of_symbols=new_sym;
219			insert_breakpoint(proc, addr, new_sym);
220			sym=sym->next;
221		}
222	}
223#endif
224}
225
226static void
227disable_bp_cb(void *addr, void *sbp, void *proc)
228{
229	debug(DEBUG_FUNCTION, "disable_bp_cb(pid=%d)", ((Process *)proc)->pid);
230	if (((struct breakpoint *)sbp)->enabled)
231		disable_breakpoint(proc, sbp);
232}
233
234void
235disable_all_breakpoints(Process *proc) {
236	debug(DEBUG_FUNCTION, "disable_all_breakpoints(pid=%d)", proc->pid);
237	assert(proc->leader == proc);
238	dict_apply_to_all(proc->breakpoints, disable_bp_cb, proc);
239}
240
241static void
242entry_callback_hit(struct breakpoint *bp, struct Process *proc)
243{
244	fprintf(stderr, "entry_callback_hit\n");
245	if (proc == NULL || proc->leader == NULL)
246		return;
247	delete_breakpoint(proc, bp->addr); // xxx
248	//enable_all_breakpoints(proc);
249
250	linkmap_init(proc);
251}
252
253int
254breakpoints_init(Process *proc, int enable)
255{
256	fprintf(stderr, "breakpoints_init %d enable=%d\n", proc->pid, enable);
257	debug(DEBUG_FUNCTION, "breakpoints_init(pid=%d)", proc->pid);
258
259	/* XXX breakpoint dictionary should be initialized
260	 * outside.  Here we just put in breakpoints.  */
261	assert(proc->breakpoints != NULL);
262
263	/* Only the thread group leader should hold the breakpoints.  */
264	assert(proc->leader == proc);
265
266	if (options.libcalls && proc->filename) {
267		struct library *lib = ltelf_read_main_binary(proc, proc->filename);
268		switch (lib != NULL) {
269		fail:
270			proc_remove_library(proc, lib);
271			library_destroy(lib);
272		case 0:
273			return -1;
274		}
275		proc_add_library(proc, lib);
276		fprintf(stderr, "note: symbols in %s were not filtered.\n",
277			lib->name);
278
279		struct breakpoint *entry_bp
280			= insert_breakpoint(proc, lib->entry, NULL);
281		if (entry_bp == NULL) {
282			error(0, errno, "couldn't insert entry breakpoint");
283			goto fail;
284		}
285
286		fprintf(stderr, "setting entry_callbacks by hand, fix it\n");
287		static struct bp_callbacks entry_callbacks = {
288			.on_hit = entry_callback_hit,
289		};
290		entry_bp->cbs = &entry_callbacks;
291	}
292
293	proc->callstack_depth = 0;
294	return 0;
295}
296