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