plt.c revision 673ff510953b65b844a58478aa434120f457c014
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 gelf_getsym(lte->dynsym, ELF64_R_SYM(rela->r_info),
162				   sym) != NULL ? 0 : -1;
163	}
164
165	/* Fixup the offset.  */
166	sym_index += lte->arch.mips_gotsym;
167
168	if (gelf_getsym(lte->dynsym, sym_index, sym) == NULL)
169		return -1;
170
171	if (ELF64_ST_TYPE(sym->st_info) != STT_FUNC) {
172		const char *name = lte->dynstr + sym->st_name;
173		debug(2, "sym %s not a function", name);
174		return 1;
175	}
176
177	return 0;
178}
179
180/**
181  MIPS ABI Supplement:
182
183  DT_PLTGOT This member holds the address of the .got section.
184
185  DT_MIPS_SYMTABNO This member holds the number of entries in the
186  .dynsym section.
187
188  DT_MIPS_LOCAL_GOTNO This member holds the number of local global
189  offset table entries.
190
191  DT_MIPS_GOTSYM This member holds the index of the first dyamic
192  symbol table entry that corresponds to an entry in the gobal offset
193  table.
194
195 */
196int
197arch_elf_init(struct ltelf *lte, struct library *lib)
198{
199	Elf_Scn *scn;
200	GElf_Shdr shdr;
201
202	/* FIXME: for CPIC we should really scan both GOT tables
203	 * to pick up relocations to external functions.  Right now
204	 * function pointers from the main binary to external functions
205	 * can't be traced in CPIC mode.  */
206	if (mips_elf_is_cpic(lte->ehdr.e_flags)) {
207		return 0; /* We are already done.  */
208	}
209
210	if (elf_get_section_type(lte, SHT_DYNAMIC, &scn, &shdr) < 0
211	    || scn == NULL) {
212	fail:
213		error(0, 0, "Couldn't get SHT_DYNAMIC: %s",
214		      elf_errmsg(-1));
215		return -1;
216	}
217
218	Elf_Data *data = elf_loaddata(scn, &shdr);
219	if (data == NULL)
220		goto fail;
221
222	size_t j;
223	for (j = 0; j < shdr.sh_size / shdr.sh_entsize; ++j) {
224		GElf_Dyn dyn;
225		if (gelf_getdyn(data, j, &dyn) == NULL)
226			goto fail;
227
228		if(dyn.d_tag == DT_PLTGOT) {
229			lte->arch.pltgot_addr = dyn.d_un.d_ptr;
230		}
231		if(dyn.d_tag == DT_MIPS_LOCAL_GOTNO){
232			lte->arch.mips_local_gotno = dyn.d_un.d_val;
233		}
234		if(dyn.d_tag == DT_MIPS_GOTSYM){
235			lte->arch.mips_gotsym = dyn.d_un.d_val;
236		}
237	}
238
239	/* Tell the generic code how many dynamic trace:able symbols
240	 * we've got.  */
241	lte->relplt_count = lte->dynsym_count - lte->arch.mips_gotsym;
242	return 0;
243}
244
245void
246arch_elf_destroy(struct ltelf *lte)
247{
248}
249
250/* When functions return we check if the symbol needs an updated
251   breakpoint with the resolved address.  */
252void arch_symbol_ret(struct process *proc, struct library_symbol *libsym)
253{
254	struct breakpoint *bp;
255	arch_addr_t resolved_addr;
256	struct process *leader = proc->leader;
257
258	/* Only deal with unresolved symbols.  */
259	if (libsym->arch.type != MIPS_PLT_UNRESOLVED)
260		return;
261
262	/* Get out if we are always using the PLT.  */
263	if (libsym->arch.pltalways)
264		return;
265
266	resolved_addr = sym2addr(proc, libsym);
267	libsym->arch.resolved_addr = (uintptr_t) resolved_addr;
268	libsym->arch.type = MIPS_PLT_RESOLVED;
269
270	if (libsym->arch.stub_addr == libsym->arch.resolved_addr) {
271		/* Prelinked symbol. No need to add new breakpoint.  */
272		return;
273	}
274
275	bp = malloc(sizeof (*bp));
276	if (bp == NULL) {
277		fprintf(stderr, "Failed to allocate bp for %s\n",
278			libsym->name);
279		return;
280	}
281
282	if (breakpoint_init(bp, leader, resolved_addr, libsym) < 0)
283		goto err;
284
285	if (proc_add_breakpoint(leader, bp) < 0) {
286		breakpoint_destroy(bp);
287		goto err;
288	}
289
290	if (breakpoint_turn_on(bp, leader) < 0) {
291		proc_remove_breakpoint(leader, bp);
292		breakpoint_destroy(bp);
293		goto err;
294	}
295	return;
296err:
297	free(bp);
298}
299
300static enum callback_status
301cb_enable_breakpoint_sym(struct library_symbol *libsym, void *data)
302{
303	struct process *proc = data;
304	arch_addr_t bp_addr;
305
306	if (!libsym->arch.gotonly)
307		return CBS_CONT;
308
309	/* Update state.  */
310	bp_addr = sym2addr(proc, libsym);
311	/* XXX The cast to uintptr_t should be removed when
312	 * arch_addr_t becomes integral type.  keywords: double cast.  */
313	libsym->arch.resolved_addr = (uintptr_t) bp_addr;
314
315	if (libsym->arch.resolved_addr == 0)
316		/* FIXME: What does this mean?  */
317		return CBS_CONT;
318
319	libsym->arch.type = MIPS_PLT_RESOLVED;
320
321	/* Now, activate the symbol causing a breakpoint to be added.  */
322	if (proc_activate_delayed_symbol(proc, libsym) < 0) {
323		fprintf(stderr, "Failed to activate delayed sym %s\n",
324			libsym->name);
325	}
326	return CBS_CONT;
327}
328
329static enum callback_status
330cb_enable_breakpoint_lib(struct process *proc, struct library *lib, void *data)
331{
332	library_each_symbol(lib, NULL, cb_enable_breakpoint_sym, proc);
333	return CBS_CONT;
334}
335
336void arch_dynlink_done(struct process *proc)
337{
338	proc_each_library(proc->leader, NULL, cb_enable_breakpoint_lib, NULL);
339}
340
341enum plt_status
342arch_elf_add_plt_entry(struct process *proc, struct ltelf *lte,
343                       const char *a_name, GElf_Rela *rela, size_t ndx,
344                       struct library_symbol **ret)
345{
346	char *name = NULL;
347	int sym_index = ndx + lte->arch.mips_gotsym;
348
349	struct library_symbol *libsym = malloc(sizeof(*libsym));
350	if (libsym == NULL)
351		return PLT_FAIL;
352
353	GElf_Addr addr = arch_plt_sym_val(lte, sym_index, 0);
354
355	name = strdup(a_name);
356	if (name == NULL) {
357		fprintf(stderr, "%s: failed %s(%#llx): %s\n", __func__,
358			name, addr, strerror(errno));
359		goto fail;
360	}
361
362	/* XXX The double cast should be removed when
363	 * arch_addr_t becomes integral type.  */
364	if (library_symbol_init(libsym,
365				(arch_addr_t) (uintptr_t) addr,
366				name, 1, LS_TOPLT_EXEC) < 0) {
367		fprintf(stderr, "%s: failed %s : %llx\n", __func__, name, addr);
368		goto fail;
369	}
370
371	arch_addr_t bp_addr = sym2addr(proc, libsym);
372	/* XXX This cast should be removed when
373	 * arch_addr_t becomes integral type.  keywords: double cast. */
374	libsym->arch.stub_addr = (uintptr_t) bp_addr;
375
376	if (bp_addr == 0) {
377		/* Function pointers without PLT entries.  */
378		libsym->plt_type = LS_TOPLT_NONE;
379		libsym->arch.gotonly = 1;
380		libsym->arch.type = MIPS_PLT_UNRESOLVED;
381
382		/* Delay breakpoint activation until the symbol gets
383		 * resolved.  */
384		libsym->delayed = 1;
385	} else if (mips_elf_is_cpic(lte->ehdr.e_flags)) {
386		libsym->arch.pltalways = 1;
387	}
388
389	*ret = libsym;
390	return PLT_OK;
391
392fail:
393	free(name);
394	free(libsym);
395	return PLT_FAIL;
396}
397
398int
399arch_library_symbol_init(struct library_symbol *libsym)
400{
401	libsym->arch.pltalways = 0;
402	libsym->arch.gotonly = 0;
403	libsym->arch.type = MIPS_PLT_UNRESOLVED;
404	if (libsym->plt_type == LS_TOPLT_NONE) {
405		libsym->arch.type = MIPS_PLT_RESOLVED;
406	}
407	return 0;
408}
409
410void
411arch_library_symbol_destroy(struct library_symbol *libsym)
412{
413}
414
415int
416arch_library_symbol_clone(struct library_symbol *retp,
417                          struct library_symbol *libsym)
418{
419	retp->arch = libsym->arch;
420	return 0;
421}
422
423/**@}*/
424