Home | History | Annotate | Download | only in mips
      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
     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
     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 *
    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
    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
    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
    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
    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.  */
    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
    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
    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 
    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
    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
    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
    413 arch_library_symbol_destroy(struct library_symbol *libsym)
    414 {
    415 }
    416 
    417 int
    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