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