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)?  */
42 static inline int
mips_elf_is_cpic(unsigned int elf_flags)43 mips_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  */
71 GElf_Addr
arch_plt_sym_val(struct ltelf * lte,size_t ndx,GElf_Rela * rela)72 arch_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  */
105 void *
sym2addr(struct process * proc,struct library_symbol * sym)106 sym2addr(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
127 int
arch_find_dl_debug(struct process * proc,arch_addr_t dyn_addr,arch_addr_t * ret)128 arch_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  */
155 int
arch_get_sym_info(struct ltelf * lte,const char * filename,size_t sym_index,GElf_Rela * rela,GElf_Sym * sym)156 arch_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  */
195 int
arch_elf_init(struct ltelf * lte,struct library * lib)196 arch_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 
247 void
arch_elf_destroy(struct ltelf * lte)248 arch_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.  */
arch_symbol_ret(struct process * proc,struct library_symbol * libsym)254 void 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;
298 err:
299 	free(bp);
300 }
301 
302 static enum callback_status
cb_enable_breakpoint_sym(struct library_symbol * libsym,void * data)303 cb_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 
331 static enum callback_status
cb_enable_breakpoint_lib(struct process * proc,struct library * lib,void * data)332 cb_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 
arch_dynlink_done(struct process * proc)338 void arch_dynlink_done(struct process *proc)
339 {
340 	proc_each_library(proc->leader, NULL, cb_enable_breakpoint_lib, NULL);
341 }
342 
343 enum plt_status
arch_elf_add_plt_entry(struct process * proc,struct ltelf * lte,const char * a_name,GElf_Rela * rela,size_t ndx,struct library_symbol ** ret)344 arch_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 
394 fail:
395 	free(name);
396 	free(libsym);
397 	return PLT_FAIL;
398 }
399 
400 int
arch_library_symbol_init(struct library_symbol * libsym)401 arch_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 
412 void
arch_library_symbol_destroy(struct library_symbol * libsym)413 arch_library_symbol_destroy(struct library_symbol *libsym)
414 {
415 }
416 
417 int
arch_library_symbol_clone(struct library_symbol * retp,struct library_symbol * libsym)418 arch_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