breakpoints.c revision 1228a91e6560c5e5ac4fdd051adc9b34bf9fc047
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 "ltrace.h"
14#include "options.h"
15#include "debug.h"
16#include "dict.h"
17#include "elf.h"
18
19/*****************************************************************************/
20
21struct breakpoint *address2bpstruct(struct process *proc, void *addr)
22{
23	return dict_find_entry(proc->breakpoints, addr);
24}
25
26void
27insert_breakpoint(struct process *proc, void *addr,
28		  struct library_symbol *libsym)
29{
30	struct breakpoint *sbp;
31	debug(1, "symbol=%s, addr=%p", libsym?libsym->name:"(nil)", addr);
32
33	if (!proc->breakpoints) {
34		proc->breakpoints =
35		    dict_init(dict_key2hash_int, dict_key_cmp_int);
36		/* atexit(brk_dict_clear); *//* why bother to do this on exit? */
37	}
38
39	if (!addr)
40		return;
41
42	if (libsym)
43		libsym->needs_init = 0;
44
45	sbp = dict_find_entry(proc->breakpoints, addr);
46	if (!sbp) {
47		sbp = calloc(1, sizeof(struct breakpoint));
48		if (!sbp) {
49			return;	/* TODO FIXME XXX: error_mem */
50		}
51		dict_enter(proc->breakpoints, addr, sbp);
52		sbp->addr = addr;
53		sbp->libsym = libsym;
54		if (libsym)
55			libsym->brkpnt = sbp;
56	}
57	sbp->enabled++;
58	if (sbp->enabled == 1 && proc->pid)
59		enable_breakpoint(proc->pid, sbp);
60}
61
62void delete_breakpoint(struct process *proc, void *addr)
63{
64	struct breakpoint *sbp = dict_find_entry(proc->breakpoints, addr);
65	assert(sbp);		/* FIXME: remove after debugging has been done. */
66	/* This should only happen on out-of-memory conditions. */
67	if (sbp == NULL)
68		return;
69
70	sbp->enabled--;
71	if (sbp->enabled == 0)
72		disable_breakpoint(proc->pid, sbp);
73	assert(sbp->enabled >= 0);
74}
75
76static void enable_bp_cb(void *addr, void *sbp, void *proc)
77{
78	if (((struct breakpoint *)sbp)->enabled) {
79		enable_breakpoint(((struct process *)proc)->pid, sbp);
80	}
81}
82
83void enable_all_breakpoints(struct process *proc)
84{
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 (opt_L) {
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                    // I'm sure there is a nicer way to do this. We need to
111                    // insert breakpoints _after_ the child has been started.
112                    struct library_symbol *sym;
113                    struct library_symbol *new_sym;
114                    sym=proc->list_of_symbols;
115                    while(sym){
116                        void *addr= sym2addr(proc,sym);
117                        if(!addr){
118                            sym=sym->next;
119                            continue;
120                        }
121                        if(dict_find_entry(proc->breakpoints,addr)){
122                            sym=sym->next;
123                            continue;
124                        }
125                        debug(2,"inserting bp %p %s",addr,sym->name);
126                        new_sym=malloc(sizeof(*new_sym));
127                        memcpy(new_sym,sym,sizeof(*new_sym));
128                        new_sym->next=proc->list_of_symbols;
129                        proc->list_of_symbols=new_sym;
130                        new_sym->brkpnt=0;
131                        insert_breakpoint(proc, addr, new_sym);
132                        sym=sym->next;
133                    }
134                }
135#endif
136	}
137	proc->breakpoints_enabled = 1;
138}
139
140static void disable_bp_cb(void *addr, void *sbp, void *proc)
141{
142	if (((struct breakpoint *)sbp)->enabled) {
143		disable_breakpoint(((struct process *)proc)->pid, sbp);
144	}
145}
146
147void disable_all_breakpoints(struct process *proc)
148{
149	if (proc->breakpoints_enabled) {
150		debug(1, "Disabling breakpoints for pid %u...", proc->pid);
151		dict_apply_to_all(proc->breakpoints, disable_bp_cb, proc);
152	}
153	proc->breakpoints_enabled = 0;
154}
155
156static void free_bp_cb(void *addr, void *sbp, void *data)
157{
158	assert(sbp);
159	free(sbp);
160}
161
162void breakpoints_init(struct process *proc)
163{
164	struct library_symbol *sym;
165
166	if (proc->breakpoints) {	/* let's remove that struct */
167		/* TODO FIXME XXX: free() all "struct breakpoint"s */
168		dict_apply_to_all(proc->breakpoints, free_bp_cb, NULL);
169		dict_clear(proc->breakpoints);
170		proc->breakpoints = NULL;
171	}
172
173	if (opt_L && proc->filename) {
174		proc->list_of_symbols = read_elf(proc);
175		if (opt_e) {
176			struct library_symbol **tmp1 = &(proc->list_of_symbols);
177			while (*tmp1) {
178				struct opt_e_t *tmp2 = opt_e;
179				int keep = !opt_e_enable;
180
181				while (tmp2) {
182					if (!strcmp((*tmp1)->name, tmp2->name)) {
183						keep = opt_e_enable;
184					}
185					tmp2 = tmp2->next;
186				}
187				if (!keep) {
188					*tmp1 = (*tmp1)->next;
189				} else {
190					tmp1 = &((*tmp1)->next);
191				}
192			}
193		}
194	} else {
195		proc->list_of_symbols = NULL;
196	}
197	for (sym = proc->list_of_symbols; sym; sym = sym->next) {
198		/* proc->pid==0 delays enabling. */
199		insert_breakpoint(proc, sym2addr(proc, sym), sym);
200	}
201	proc->callstack_depth = 0;
202	proc->breakpoints_enabled = -1;
203}
204
205void reinitialize_breakpoints(struct process *proc)
206{
207	struct library_symbol *sym = proc->list_of_symbols;
208
209	while (sym) {
210		if (sym->needs_init) {
211			insert_breakpoint(proc, sym2addr(proc, sym),
212					  sym);
213			if (sym->needs_init && !sym->is_weak) {
214				fprintf(stderr,
215					"could not re-initialize breakpoint for \"%s\" in file \"%s\"\n",
216					sym->name, proc->filename);
217				exit(1);
218			}
219		}
220		sym = sym->next;
221	}
222}
223