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