plt.c revision 552d75e2a226782cc9ecf966e6e343af8f51031f
1/*
2 * This file is part of ltrace.
3 * Copyright (C) 2012,2013 Petr Machata, Red Hat Inc.
4 * Copyright (C) 2012 Edgar E. Iglesias, Axis Communications
5 * Copyright (C) 2008,2009 Juan Cespedes
6 * Copyright (C) 2006 Eric Vaitl, Cisco Systems, Inc.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of the
11 * License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21 * 02110-1301 USA
22 */
23
24#include <string.h>
25#include <error.h>
26#include <errno.h>
27#include <gelf.h>
28#include <sys/ptrace.h>
29
30#include "common.h"
31#include "debug.h"
32#include "proc.h"
33#include "library.h"
34#include "breakpoint.h"
35#include "backend.h"
36
37/**
38   \addtogroup mips
39   @{
40 */
41
42/* Are we in pure CPIC mode (the non-PIC ABI extension)?  */
43static inline int
44mips_elf_is_cpic(unsigned int elf_flags)
45{
46	return (elf_flags & (EF_MIPS_PIC | EF_MIPS_CPIC)) == EF_MIPS_CPIC;
47}
48
49/**
50   \param lte Structure containing link table entry information
51   \param ndx Index into .dynsym
52   \param rela Not used.
53   \return Address of GOT table entry
54
55   MIPS ABI Supplement:
56
57   DT_PLTGOT This member holds the address of the .got section.
58
59   DT_MIPS_SYMTABNO This member holds the number of entries in the
60   .dynsym section.
61
62   DT_MIPS_LOCAL_GOTNO This member holds the number of local global
63   offset table entries.
64
65   DT_MIPS_GOTSYM This member holds the index of the first dyamic
66   symbol table entry that corresponds to an entry in the gobal offset
67   table.
68
69   Called by read_elf when building the symbol table.
70
71 */
72GElf_Addr
73arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela *rela)
74{
75    debug(1,"plt_addr %zx ndx %#zx",lte->arch.pltgot_addr, ndx);
76
77    if (mips_elf_is_cpic(lte->ehdr.e_flags)) {
78        /* Return a pointer into the PLT.  */
79        return lte->plt_addr + 16 * 2 + (ndx * 16);
80    }
81
82    /* Return a pointer to a GOT entry.  */
83    return lte->arch.pltgot_addr +
84	    sizeof(void *) * (lte->arch.mips_local_gotno
85			      + (ndx - lte->arch.mips_gotsym));
86}
87/**
88   \param proc The process to work on.
89   \param sym The library symbol.
90   \return What is at the got table address
91
92   The return value should be the address to put the breakpoint at.
93
94   On the mips the library_symbol.enter_addr is the .got addr for the
95   symbol and the breakpoint.addr is the actual breakpoint address.
96
97   Other processors use a plt, the mips is "special" in that is uses
98   the .got for both function and data relocations. Prior to program
99   startup, return 0.
100
101   \warning MIPS relocations are lazy. This means that the breakpoint
102   may move after the first call. Ltrace dictionary routines don't
103   have a delete and symbol is one to one with breakpoint, so if the
104   breakpoint changes I just add a new breakpoint for the new address.
105 */
106void *
107sym2addr(struct process *proc, struct library_symbol *sym)
108{
109    long ret;
110
111    if (sym->arch.pltalways
112        || (!sym->arch.gotonly && sym->plt_type == LS_TOPLT_NONE)) {
113        return sym->enter_addr;
114    }
115
116    if(!proc->pid){
117        return 0;
118    }
119    ret=ptrace(PTRACE_PEEKTEXT, proc->pid, sym->enter_addr, 0);
120    if(ret==-1){
121        ret =0;
122    }
123    return (void *)ret;;
124}
125
126/* Address of run time loader map, used for debugging.  */
127#define DT_MIPS_RLD_MAP         0x70000016
128int
129arch_find_dl_debug(struct process *proc, arch_addr_t dyn_addr,
130		   arch_addr_t *ret)
131{
132	arch_addr_t rld_addr;
133	int r;
134
135	/* MIPS puts the address of the r_debug structure into the
136	 * DT_MIPS_RLD_MAP entry instead of into the DT_DEBUG entry.  */
137	r = proc_find_dynamic_entry_addr(proc, dyn_addr,
138					 DT_MIPS_RLD_MAP, &rld_addr);
139	if (r == 0) {
140		if (umovebytes(proc, rld_addr,
141			       ret, sizeof *ret) != sizeof *ret) {
142			r = -1;
143		}
144	}
145	return r;
146}
147
148
149/*
150 * MIPS doesn't have traditional got.plt entries with corresponding
151 * relocations.
152 *
153 * sym_index is an offset into the external GOT entries. Filter out
154 * stuff that are not functions.
155 */
156int
157arch_get_sym_info(struct ltelf *lte, const char *filename,
158		  size_t sym_index, GElf_Rela *rela, GElf_Sym *sym)
159{
160	if (mips_elf_is_cpic(lte->ehdr.e_flags))
161		return elf_get_sym_info(lte, filename, sym_index, rela, sym);
162
163	/* Fixup the offset.  */
164	sym_index += lte->arch.mips_gotsym;
165
166	if (gelf_getsym(lte->dynsym, sym_index, sym) == NULL)
167		return -1;
168
169	if (ELF64_ST_TYPE(sym->st_info) != STT_FUNC) {
170		const char *name = lte->dynstr + sym->st_name;
171		debug(2, "sym %s not a function", name);
172		return 1;
173	}
174
175	return 0;
176}
177
178/**
179  MIPS ABI Supplement:
180
181  DT_PLTGOT This member holds the address of the .got section.
182
183  DT_MIPS_SYMTABNO This member holds the number of entries in the
184  .dynsym section.
185
186  DT_MIPS_LOCAL_GOTNO This member holds the number of local global
187  offset table entries.
188
189  DT_MIPS_GOTSYM This member holds the index of the first dyamic
190  symbol table entry that corresponds to an entry in the gobal offset
191  table.
192
193 */
194int
195arch_elf_init(struct ltelf *lte, struct library *lib)
196{
197	Elf_Scn *scn;
198	GElf_Shdr shdr;
199
200	/* FIXME: for CPIC we should really scan both GOT tables
201	 * to pick up relocations to external functions.  Right now
202	 * function pointers from the main binary to external functions
203	 * can't be traced in CPIC mode.  */
204	if (mips_elf_is_cpic(lte->ehdr.e_flags)) {
205		return 0; /* We are already done.  */
206	}
207
208	if (elf_get_section_type(lte, SHT_DYNAMIC, &scn, &shdr) < 0
209	    || scn == NULL) {
210	fail:
211		error(0, 0, "Couldn't get SHT_DYNAMIC: %s",
212		      elf_errmsg(-1));
213		return -1;
214	}
215
216	Elf_Data *data = elf_loaddata(scn, &shdr);
217	if (data == NULL)
218		goto fail;
219
220	size_t j;
221	for (j = 0; j < shdr.sh_size / shdr.sh_entsize; ++j) {
222		GElf_Dyn dyn;
223		if (gelf_getdyn(data, j, &dyn) == NULL)
224			goto fail;
225
226		if(dyn.d_tag == DT_PLTGOT) {
227			lte->arch.pltgot_addr = dyn.d_un.d_ptr;
228		}
229		if(dyn.d_tag == DT_MIPS_LOCAL_GOTNO){
230			lte->arch.mips_local_gotno = dyn.d_un.d_val;
231		}
232		if(dyn.d_tag == DT_MIPS_GOTSYM){
233			lte->arch.mips_gotsym = dyn.d_un.d_val;
234		}
235	}
236
237	/* Tell the generic code how many dynamic trace:able symbols
238	 * we've got.  */
239	lte->relplt_count = lte->dynsym_count - lte->arch.mips_gotsym;
240	return 0;
241}
242
243void
244arch_elf_destroy(struct ltelf *lte)
245{
246}
247
248/* When functions return we check if the symbol needs an updated
249   breakpoint with the resolved address.  */
250void arch_symbol_ret(struct process *proc, struct library_symbol *libsym)
251{
252	struct breakpoint *bp;
253	arch_addr_t resolved_addr;
254	struct process *leader = proc->leader;
255
256	/* Only deal with unresolved symbols.  */
257	if (libsym->arch.type != MIPS_PLT_UNRESOLVED)
258		return;
259
260	/* Get out if we are always using the PLT.  */
261	if (libsym->arch.pltalways)
262		return;
263
264	resolved_addr = sym2addr(proc, libsym);
265	libsym->arch.resolved_addr = (uintptr_t) resolved_addr;
266	libsym->arch.type = MIPS_PLT_RESOLVED;
267
268	if (libsym->arch.stub_addr == libsym->arch.resolved_addr) {
269		/* Prelinked symbol. No need to add new breakpoint.  */
270		return;
271	}
272
273	bp = malloc(sizeof (*bp));
274	if (bp == NULL) {
275		fprintf(stderr, "Failed to allocate bp for %s\n",
276			libsym->name);
277		return;
278	}
279
280	if (breakpoint_init(bp, leader, resolved_addr, libsym) < 0)
281		goto err;
282
283	if (proc_add_breakpoint(leader, bp) < 0) {
284		breakpoint_destroy(bp);
285		goto err;
286	}
287
288	if (breakpoint_turn_on(bp, leader) < 0) {
289		proc_remove_breakpoint(leader, bp);
290		breakpoint_destroy(bp);
291		goto err;
292	}
293	return;
294err:
295	free(bp);
296}
297
298static enum callback_status
299cb_enable_breakpoint_sym(struct library_symbol *libsym, void *data)
300{
301	struct process *proc = data;
302	arch_addr_t bp_addr;
303
304	if (!libsym->arch.gotonly)
305		return CBS_CONT;
306
307	/* Update state.  */
308	bp_addr = sym2addr(proc, libsym);
309	/* XXX The cast to uintptr_t should be removed when
310	 * arch_addr_t becomes integral type.  keywords: double cast.  */
311	libsym->arch.resolved_addr = (uintptr_t) bp_addr;
312
313	if (libsym->arch.resolved_addr == 0)
314		/* FIXME: What does this mean?  */
315		return CBS_CONT;
316
317	libsym->arch.type = MIPS_PLT_RESOLVED;
318
319	/* Now, activate the symbol causing a breakpoint to be added.  */
320	if (proc_activate_delayed_symbol(proc, libsym) < 0) {
321		fprintf(stderr, "Failed to activate delayed sym %s\n",
322			libsym->name);
323	}
324	return CBS_CONT;
325}
326
327static enum callback_status
328cb_enable_breakpoint_lib(struct process *proc, struct library *lib, void *data)
329{
330	library_each_symbol(lib, NULL, cb_enable_breakpoint_sym, proc);
331	return CBS_CONT;
332}
333
334void arch_dynlink_done(struct process *proc)
335{
336	proc_each_library(proc->leader, NULL, cb_enable_breakpoint_lib, NULL);
337}
338
339enum plt_status
340arch_elf_add_plt_entry(struct process *proc, struct ltelf *lte,
341                       const char *a_name, GElf_Rela *rela, size_t ndx,
342                       struct library_symbol **ret)
343{
344	char *name = NULL;
345	int sym_index = ndx + lte->arch.mips_gotsym;
346
347	struct library_symbol *libsym = malloc(sizeof(*libsym));
348	if (libsym == NULL)
349		return PLT_FAIL;
350
351	GElf_Addr addr = arch_plt_sym_val(lte, sym_index, 0);
352
353	name = strdup(a_name);
354	if (name == NULL) {
355		fprintf(stderr, "%s: failed %s(%#llx): %s\n", __func__,
356			name, addr, strerror(errno));
357		goto fail;
358	}
359
360	/* XXX The double cast should be removed when
361	 * arch_addr_t becomes integral type.  */
362	if (library_symbol_init(libsym,
363				(arch_addr_t) (uintptr_t) addr,
364				name, 1, LS_TOPLT_EXEC) < 0) {
365		fprintf(stderr, "%s: failed %s : %llx\n", __func__, name, addr);
366		goto fail;
367	}
368
369	arch_addr_t bp_addr = sym2addr(proc, libsym);
370	/* XXX This cast should be removed when
371	 * arch_addr_t becomes integral type.  keywords: double cast. */
372	libsym->arch.stub_addr = (uintptr_t) bp_addr;
373
374	if (bp_addr == 0) {
375		/* Function pointers without PLT entries.  */
376		libsym->plt_type = LS_TOPLT_NONE;
377		libsym->arch.gotonly = 1;
378		libsym->arch.type = MIPS_PLT_UNRESOLVED;
379
380		/* Delay breakpoint activation until the symbol gets
381		 * resolved.  */
382		libsym->delayed = 1;
383	} else if (mips_elf_is_cpic(lte->ehdr.e_flags)) {
384		libsym->arch.pltalways = 1;
385	}
386
387	*ret = libsym;
388	return PLT_OK;
389
390fail:
391	free(name);
392	free(libsym);
393	return PLT_FAIL;
394}
395
396int
397arch_library_symbol_init(struct library_symbol *libsym)
398{
399	libsym->arch.pltalways = 0;
400	libsym->arch.gotonly = 0;
401	libsym->arch.type = MIPS_PLT_UNRESOLVED;
402	if (libsym->plt_type == LS_TOPLT_NONE) {
403		libsym->arch.type = MIPS_PLT_RESOLVED;
404	}
405	return 0;
406}
407
408void
409arch_library_symbol_destroy(struct library_symbol *libsym)
410{
411}
412
413int
414arch_library_symbol_clone(struct library_symbol *retp,
415                          struct library_symbol *libsym)
416{
417	retp->arch = libsym->arch;
418	return 0;
419}
420
421/**@}*/
422