breakpoints.c revision 8d1b92ba755f6d6229f5e230fc43d958b13836f8
1#if HAVE_CONFIG_H
2#include "config.h"
3#endif
4
5#include <stdlib.h>
6#include <string.h>
7#include <assert.h>
8
9#ifdef __powerpc__
10#include <sys/ptrace.h>
11#endif
12
13#include "common.h"
14
15/*****************************************************************************/
16
17Breakpoint *
18address2bpstruct(Process *proc, void *addr) {
19	debug(DEBUG_FUNCTION, "address2bpstruct(pid=%d, addr=%p)", proc->pid, addr);
20	return dict_find_entry(proc->breakpoints, addr);
21}
22
23void
24insert_breakpoint(Process *proc, void *addr,
25		  struct library_symbol *libsym) {
26	Breakpoint *sbp;
27
28	debug(DEBUG_FUNCTION, "insert_breakpoint(pid=%d, addr=%p, symbol=%s)", proc->pid, addr, libsym ? libsym->name : "NULL");
29	debug(1, "symbol=%s, addr=%p", libsym?libsym->name:"(nil)", addr);
30
31	if (!addr)
32		return;
33
34	if (libsym)
35		libsym->needs_init = 0;
36
37	sbp = dict_find_entry(proc->breakpoints, addr);
38	if (!sbp) {
39		sbp = calloc(1, sizeof(Breakpoint));
40		if (!sbp) {
41			return;	/* TODO FIXME XXX: error_mem */
42		}
43		dict_enter(proc->breakpoints, addr, sbp);
44		sbp->addr = addr;
45		sbp->libsym = libsym;
46	}
47#ifdef __arm__
48	sbp->thumb_mode = proc->thumb_mode;
49	proc->thumb_mode = 0;
50#endif
51	sbp->enabled++;
52	if (sbp->enabled == 1 && proc->pid)
53		enable_breakpoint(proc->pid, sbp);
54}
55
56void
57delete_breakpoint(Process *proc, void *addr) {
58	Breakpoint *sbp;
59
60	debug(DEBUG_FUNCTION, "delete_breakpoint(pid=%d, addr=%p)", proc->pid, addr);
61
62	sbp = dict_find_entry(proc->breakpoints, addr);
63	assert(sbp);		/* FIXME: remove after debugging has been done. */
64	/* This should only happen on out-of-memory conditions. */
65	if (sbp == NULL)
66		return;
67
68	sbp->enabled--;
69	if (sbp->enabled == 0)
70		disable_breakpoint(proc->pid, sbp);
71	assert(sbp->enabled >= 0);
72}
73
74static void
75enable_bp_cb(void *addr, void *sbp, void *proc) {
76	debug(DEBUG_FUNCTION, "enable_bp_cb(pid=%d)", ((Process *)proc)->pid);
77	if (((Breakpoint *)sbp)->enabled) {
78		enable_breakpoint(((Process *)proc)->pid, sbp);
79	}
80}
81
82void
83enable_all_breakpoints(Process *proc) {
84	debug(DEBUG_FUNCTION, "enable_all_breakpoints(pid=%d)", proc->pid);
85	if (proc->breakpoints_enabled <= 0) {
86#ifdef __powerpc__
87		unsigned long a;
88
89		/*
90		 * PPC HACK! (XXX FIXME TODO)
91		 * If the dynamic linker hasn't populated the PLT then
92		 * dont enable the breakpoints
93		 */
94		if (options.libcalls) {
95			a = ptrace(PTRACE_PEEKTEXT, proc->pid,
96				   sym2addr(proc, proc->list_of_symbols),
97				   0);
98			if (a == 0x0)
99				return;
100		}
101#endif
102
103		debug(1, "Enabling breakpoints for pid %u...", proc->pid);
104		if (proc->breakpoints) {
105			dict_apply_to_all(proc->breakpoints, enable_bp_cb,
106					  proc);
107		}
108#ifdef __mips__
109		{
110			/*
111			 * I'm sure there is a nicer way to do this. We need to
112			 * insert breakpoints _after_ the child has been started.
113			 */
114			struct library_symbol *sym;
115			struct library_symbol *new_sym;
116			sym=proc->list_of_symbols;
117			while(sym){
118				void *addr= sym2addr(proc,sym);
119				if(!addr){
120					sym=sym->next;
121					continue;
122				}
123				if(dict_find_entry(proc->breakpoints,addr)){
124					sym=sym->next;
125					continue;
126				}
127				debug(2,"inserting bp %p %s",addr,sym->name);
128				new_sym=malloc(sizeof(*new_sym));
129				memcpy(new_sym,sym,sizeof(*new_sym));
130				new_sym->next=proc->list_of_symbols;
131				proc->list_of_symbols=new_sym;
132				insert_breakpoint(proc, addr, new_sym);
133				sym=sym->next;
134			}
135		}
136#endif
137	}
138	proc->breakpoints_enabled = 1;
139}
140
141static void
142disable_bp_cb(void *addr, void *sbp, void *proc) {
143	debug(DEBUG_FUNCTION, "disable_bp_cb(pid=%d)", ((Process *)proc)->pid);
144	if (((Breakpoint *)sbp)->enabled) {
145		disable_breakpoint(((Process *)proc)->pid, sbp);
146	}
147}
148
149void
150disable_all_breakpoints(Process *proc) {
151	debug(DEBUG_FUNCTION, "disable_all_breakpoints(pid=%d)", proc->pid);
152	if (proc->breakpoints_enabled) {
153		debug(1, "Disabling breakpoints for pid %u...", proc->pid);
154		dict_apply_to_all(proc->breakpoints, disable_bp_cb, proc);
155	}
156	proc->breakpoints_enabled = 0;
157}
158
159static void
160free_bp_cb(void *addr, void *sbp, void *data) {
161	debug(DEBUG_FUNCTION, "free_bp_cb(sbp=%p)", sbp);
162	assert(sbp);
163	free(sbp);
164}
165
166void
167breakpoints_init(Process *proc) {
168	struct library_symbol *sym;
169
170	debug(DEBUG_FUNCTION, "breakpoints_init(pid=%d)", proc->pid);
171	if (proc->breakpoints) {	/* let's remove that struct */
172		dict_apply_to_all(proc->breakpoints, free_bp_cb, NULL);
173		dict_clear(proc->breakpoints);
174		proc->breakpoints = NULL;
175	}
176	proc->breakpoints = dict_init(dict_key2hash_int, dict_key_cmp_int);
177
178	if (options.libcalls && proc->filename) {
179		/* FIXME: memory leak when called by exec(): */
180		proc->list_of_symbols = read_elf(proc);
181		if (opt_e) {
182			struct library_symbol **tmp1 = &(proc->list_of_symbols);
183			while (*tmp1) {
184				struct opt_e_t *tmp2 = opt_e;
185				int keep = !opt_e_enable;
186
187				while (tmp2) {
188					if (!strcmp((*tmp1)->name, tmp2->name)) {
189						keep = opt_e_enable;
190					}
191					tmp2 = tmp2->next;
192				}
193				if (!keep) {
194					*tmp1 = (*tmp1)->next;
195				} else {
196					tmp1 = &((*tmp1)->next);
197				}
198			}
199		}
200	} else {
201		proc->list_of_symbols = NULL;
202	}
203	for (sym = proc->list_of_symbols; sym; sym = sym->next) {
204		/* proc->pid==0 delays enabling. */
205		insert_breakpoint(proc, sym2addr(proc, sym), sym);
206	}
207	proc->callstack_depth = 0;
208	proc->breakpoints_enabled = -1;
209}
210
211void
212reinitialize_breakpoints(Process *proc) {
213	struct library_symbol *sym;
214
215	debug(DEBUG_FUNCTION, "reinitialize_breakpoints(pid=%d)", proc->pid);
216
217	sym = proc->list_of_symbols;
218
219	while (sym) {
220		if (sym->needs_init) {
221			insert_breakpoint(proc, sym2addr(proc, sym),
222					  sym);
223			if (sym->needs_init && !sym->is_weak) {
224				fprintf(stderr,
225					"could not re-initialize breakpoint for \"%s\" in file \"%s\"\n",
226					sym->name, proc->filename);
227				exit(1);
228			}
229		}
230		sym = sym->next;
231	}
232}
233