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