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