/* * This file is part of ltrace. * Copyright (C) 2013 Petr Machata, Red Hat Inc. * Copyright (C) 2004,2008,2009 Juan Cespedes * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA */ #include <gelf.h> #include <stdbool.h> #include "proc.h" #include "common.h" #include "library.h" #include "trace.h" static GElf_Addr x86_plt_offset(uint32_t i) { /* Skip the first PLT entry, which contains a stub to call the * resolver. */ return (i + 1) * 16; } GElf_Addr arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela *rela) { uint32_t i = *VECT_ELEMENT(<e->arch.plt_map, uint32_t, ndx); return x86_plt_offset(i) + lte->plt_addr; } void * sym2addr(struct process *proc, struct library_symbol *sym) { return sym->enter_addr; } 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) { bool irelative = false; if (lte->ehdr.e_machine == EM_X86_64) { #ifdef R_X86_64_IRELATIVE irelative = GELF_R_TYPE(rela->r_info) == R_X86_64_IRELATIVE; #endif } else { assert(lte->ehdr.e_machine == EM_386); #ifdef R_386_IRELATIVE irelative = GELF_R_TYPE(rela->r_info) == R_386_IRELATIVE; #endif } if (irelative) return linux_elf_add_plt_entry_irelative(proc, lte, rela, ndx, ret); return PLT_DEFAULT; } int arch_elf_init(struct ltelf *lte, struct library *lib) { VECT_INIT(<e->arch.plt_map, unsigned int); /* IRELATIVE slots may make the whole situation a fair deal * more complex. On x86{,_64}, the PLT slots are not * presented in the order of the corresponding relocations, * but in the order it which these symbols are in the symbol * table. That's static symbol table, which may be stripped * off, not dynsym--that doesn't contain IFUNC symbols at all. * So we have to decode each PLT entry to figure out what * entry it corresponds to. We need to interpret the PLT * table to figure this out. * * On i386, the PLT entry format is as follows: * * 8048300: ff 25 0c a0 04 08 jmp *0x804a00c * 8048306: 68 20 00 00 00 push $0x20 * 804830b: e9 e0 ff ff ff jmp 80482f0 <_init+0x30> * * For PIE binaries it is the following: * * 410: ff a3 10 00 00 00 jmp *0x10(%ebx) * 416: 68 00 00 00 00 push $0x0 * 41b: e9 d0 ff ff ff jmp 3f0 <_init+0x30> * * On x86_64, it is: * * 400420: ff 25 f2 0b 20 00 jmpq *0x200bf2(%rip) # 601018 <_GLOBAL_OFFSET_TABLE_+0x18> * 400426: 68 00 00 00 00 pushq $0x0 * 40042b: e9 e0 ff ff ff jmpq 400410 <_init+0x18> * * On i386, the argument to push is an offset of relocation to * use. The first PLT slot has an offset of 0x0, the second * 0x8, etc. On x86_64, it's directly the index that we are * looking for. */ /* Here we scan the PLT table and initialize a map of * relocation->slot number in lte->arch.plt_map. */ size_t i; for (i = 0; i < vect_size(<e->plt_relocs); ++i) { GElf_Addr offset = x86_plt_offset(i); uint32_t reloc_arg = 0; uint8_t byte; if (elf_read_next_u8(lte->plt_data, &offset, &byte) < 0 || byte != 0xff || elf_read_next_u8(lte->plt_data, &offset, &byte) < 0 || (byte != 0xa3 && byte != 0x25)) goto next; /* Skip immediate argument in the instruction. */ offset += 4; if (elf_read_next_u8(lte->plt_data, &offset, &byte) < 0 || byte != 0x68 || elf_read_next_u32(lte->plt_data, &offset, &reloc_arg) < 0) { reloc_arg = 0; goto next; } if (lte->ehdr.e_machine == EM_386) { if (reloc_arg % 8 != 0) { reloc_arg = 0; goto next; } reloc_arg /= 8; } next: if (VECT_PUSHBACK(<e->arch.plt_map, &reloc_arg) < 0) { arch_elf_destroy(lte); return -1; } } return 0; } void arch_elf_destroy(struct ltelf *lte) { VECT_DESTROY(<e->arch.plt_map, uint32_t, NULL, NULL); }