breakpoints.c revision 81c65272697a63d81e6baece69bef6ba8f55932e
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	if (libsym)
92		libsym->needs_init = 0;
93
94	struct breakpoint *sbp = dict_find_entry(leader->breakpoints, addr);
95	if (sbp == NULL) {
96		sbp = malloc(sizeof(*sbp));
97		if (sbp == NULL
98		    || breakpoint_init(sbp, proc, addr, libsym, NULL) < 0
99		    || dict_enter(leader->breakpoints, addr, sbp) < 0) {
100			free(sbp);
101			return NULL;
102		}
103	}
104
105	sbp->enabled++;
106	if (sbp->enabled == 1 && enable) {
107		assert(proc->pid != 0);
108		enable_breakpoint(proc, sbp);
109	}
110
111	return sbp;
112}
113
114void
115delete_breakpoint(Process *proc, void *addr)
116{
117	struct breakpoint *sbp;
118
119	debug(DEBUG_FUNCTION, "delete_breakpoint(pid=%d, addr=%p)", proc->pid, addr);
120
121	Process * leader = proc->leader;
122	assert(leader != NULL);
123
124	sbp = dict_find_entry(leader->breakpoints, addr);
125	assert(sbp);		/* FIXME: remove after debugging has been done. */
126	/* This should only happen on out-of-memory conditions. */
127	if (sbp == NULL)
128		return;
129
130	sbp->enabled--;
131	if (sbp->enabled == 0)
132		disable_breakpoint(proc, sbp);
133	assert(sbp->enabled >= 0);
134}
135
136static void
137enable_bp_cb(void *addr, void *sbp, void *proc)
138{
139	debug(DEBUG_FUNCTION, "enable_bp_cb(pid=%d)", ((Process *)proc)->pid);
140	if (((struct breakpoint *)sbp)->enabled)
141		enable_breakpoint(proc, sbp);
142}
143
144void
145enable_all_breakpoints(Process *proc)
146{
147	debug(DEBUG_FUNCTION, "enable_all_breakpoints(pid=%d)", proc->pid);
148
149	debug(1, "Enabling breakpoints for pid %u...", proc->pid);
150	if (proc->breakpoints) {
151		dict_apply_to_all(proc->breakpoints, enable_bp_cb,
152				  proc);
153	}
154#ifdef __mips__
155	{
156		/*
157		 * I'm sure there is a nicer way to do this. We need to
158		 * insert breakpoints _after_ the child has been started.
159		 */
160		struct library_symbol *sym;
161		struct library_symbol *new_sym;
162		sym=proc->list_of_symbols;
163		while(sym){
164			void *addr= sym2addr(proc,sym);
165			if(!addr){
166				sym=sym->next;
167				continue;
168			}
169			if(dict_find_entry(proc->breakpoints,addr)){
170				sym=sym->next;
171				continue;
172			}
173			debug(2,"inserting bp %p %s",addr,sym->name);
174			new_sym=malloc(sizeof(*new_sym) + strlen(sym->name) + 1);
175			memcpy(new_sym,sym,sizeof(*new_sym) + strlen(sym->name) + 1);
176			new_sym->next=proc->list_of_symbols;
177			proc->list_of_symbols=new_sym;
178			insert_breakpoint(proc, addr, new_sym);
179			sym=sym->next;
180		}
181	}
182#endif
183}
184
185static void
186disable_bp_cb(void *addr, void *sbp, void *proc)
187{
188	debug(DEBUG_FUNCTION, "disable_bp_cb(pid=%d)", ((Process *)proc)->pid);
189	if (((struct breakpoint *)sbp)->enabled)
190		disable_breakpoint(proc, sbp);
191}
192
193void
194disable_all_breakpoints(Process *proc) {
195	debug(DEBUG_FUNCTION, "disable_all_breakpoints(pid=%d)", proc->pid);
196	assert(proc->leader == proc);
197	dict_apply_to_all(proc->breakpoints, disable_bp_cb, proc);
198}
199
200static enum callback_status
201reinitialize_breakpoints(struct Process *proc, struct library *library,
202			 void *data)
203{
204	debug(DEBUG_FUNCTION, "reinitialize_breakpoints_in(pid=%d, %s)",
205	      proc->pid, library->name);
206
207	struct library_symbol *sym;
208	for (sym = library->symbols; sym != NULL; sym = sym->next)
209		if (sym->needs_init) {
210			target_address_t addr = sym2addr(proc, sym);
211			if (insert_breakpoint(proc, addr, sym, 1) == NULL
212			    || (sym->needs_init && !sym->is_weak))
213				fprintf(stderr,
214					"could not re-initialize breakpoint "
215					"for \"%s\" in file \"%s\"\n",
216					sym->name, proc->filename);
217		}
218
219	return CBS_CONT;
220}
221
222static void
223entry_callback_hit(struct breakpoint *bp, struct Process *proc)
224{
225	fprintf(stderr, "entry_callback_hit\n");
226	if (proc == NULL || proc->leader == NULL)
227		return;
228	delete_breakpoint(proc, bp->addr); // xxx
229
230	linkmap_init(proc);
231	proc_each_library(proc->leader, NULL, reinitialize_breakpoints, NULL);
232}
233
234int
235breakpoints_init(Process *proc, int enable)
236{
237	fprintf(stderr, "breakpoints_init %d enable=%d\n", proc->pid, enable);
238	debug(DEBUG_FUNCTION, "breakpoints_init(pid=%d)", proc->pid);
239
240	/* XXX breakpoint dictionary should be initialized
241	 * outside.  Here we just put in breakpoints.  */
242	assert(proc->breakpoints != NULL);
243
244	/* Only the thread group leader should hold the breakpoints.  */
245	assert(proc->leader == proc);
246
247	if (options.libcalls && proc->filename) {
248		struct library *lib = ltelf_read_main_binary(proc, proc->filename);
249		switch (lib != NULL) {
250		fail:
251			proc_remove_library(proc, lib);
252			library_destroy(lib);
253		case 0:
254			return -1;
255		}
256		proc_add_library(proc, lib);
257		fprintf(stderr, "note: symbols in %s were not filtered.\n",
258			lib->name);
259
260		struct breakpoint *entry_bp
261			= insert_breakpoint(proc, lib->entry, NULL, 1);
262		if (entry_bp == NULL) {
263			error(0, errno, "couldn't insert entry breakpoint");
264			goto fail;
265		}
266
267		fprintf(stderr, "setting entry_callbacks by hand, fix it\n");
268		static struct bp_callbacks entry_callbacks = {
269			.on_hit = entry_callback_hit,
270		};
271		entry_bp->cbs = &entry_callbacks;
272	}
273
274	proc->callstack_depth = 0;
275	return 0;
276}
277