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