1 /* 2 * This file is part of ltrace. 3 * Copyright (C) 2013 Petr Machata, Red Hat Inc. 4 * Copyright (C) 2004,2008,2009 Juan Cespedes 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License as 8 * published by the Free Software Foundation; either version 2 of the 9 * License, or (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, but 12 * WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 19 * 02110-1301 USA 20 */ 21 22 #include <gelf.h> 23 #include <stdbool.h> 24 25 #include "proc.h" 26 #include "common.h" 27 #include "library.h" 28 #include "trace.h" 29 30 static GElf_Addr 31 x86_plt_offset(uint32_t i) 32 { 33 /* Skip the first PLT entry, which contains a stub to call the 34 * resolver. */ 35 return (i + 1) * 16; 36 } 37 38 GElf_Addr 39 arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela *rela) 40 { 41 uint32_t i = *VECT_ELEMENT(<e->arch.plt_map, uint32_t, ndx); 42 return x86_plt_offset(i) + lte->plt_addr; 43 } 44 45 void * 46 sym2addr(struct process *proc, struct library_symbol *sym) 47 { 48 return sym->enter_addr; 49 } 50 51 enum plt_status 52 arch_elf_add_plt_entry(struct process *proc, struct ltelf *lte, 53 const char *a_name, GElf_Rela *rela, size_t ndx, 54 struct library_symbol **ret) 55 { 56 bool irelative = false; 57 if (lte->ehdr.e_machine == EM_X86_64) { 58 #ifdef R_X86_64_IRELATIVE 59 irelative = GELF_R_TYPE(rela->r_info) == R_X86_64_IRELATIVE; 60 #endif 61 } else { 62 assert(lte->ehdr.e_machine == EM_386); 63 #ifdef R_386_IRELATIVE 64 irelative = GELF_R_TYPE(rela->r_info) == R_386_IRELATIVE; 65 #endif 66 } 67 68 if (irelative) 69 return linux_elf_add_plt_entry_irelative(proc, lte, rela, 70 ndx, ret); 71 72 return PLT_DEFAULT; 73 } 74 75 int 76 arch_elf_init(struct ltelf *lte, struct library *lib) 77 { 78 VECT_INIT(<e->arch.plt_map, unsigned int); 79 80 /* IRELATIVE slots may make the whole situation a fair deal 81 * more complex. On x86{,_64}, the PLT slots are not 82 * presented in the order of the corresponding relocations, 83 * but in the order it which these symbols are in the symbol 84 * table. That's static symbol table, which may be stripped 85 * off, not dynsym--that doesn't contain IFUNC symbols at all. 86 * So we have to decode each PLT entry to figure out what 87 * entry it corresponds to. We need to interpret the PLT 88 * table to figure this out. 89 * 90 * On i386, the PLT entry format is as follows: 91 * 92 * 8048300: ff 25 0c a0 04 08 jmp *0x804a00c 93 * 8048306: 68 20 00 00 00 push $0x20 94 * 804830b: e9 e0 ff ff ff jmp 80482f0 <_init+0x30> 95 * 96 * For PIE binaries it is the following: 97 * 98 * 410: ff a3 10 00 00 00 jmp *0x10(%ebx) 99 * 416: 68 00 00 00 00 push $0x0 100 * 41b: e9 d0 ff ff ff jmp 3f0 <_init+0x30> 101 * 102 * On x86_64, it is: 103 * 104 * 400420: ff 25 f2 0b 20 00 jmpq *0x200bf2(%rip) # 601018 <_GLOBAL_OFFSET_TABLE_+0x18> 105 * 400426: 68 00 00 00 00 pushq $0x0 106 * 40042b: e9 e0 ff ff ff jmpq 400410 <_init+0x18> 107 * 108 * On i386, the argument to push is an offset of relocation to 109 * use. The first PLT slot has an offset of 0x0, the second 110 * 0x8, etc. On x86_64, it's directly the index that we are 111 * looking for. 112 */ 113 114 /* Here we scan the PLT table and initialize a map of 115 * relocation->slot number in lte->arch.plt_map. */ 116 117 size_t i; 118 for (i = 0; i < vect_size(<e->plt_relocs); ++i) { 119 120 GElf_Addr offset = x86_plt_offset(i); 121 uint32_t reloc_arg = 0; 122 123 uint8_t byte; 124 if (elf_read_next_u8(lte->plt_data, &offset, &byte) < 0 125 || byte != 0xff 126 || elf_read_next_u8(lte->plt_data, &offset, &byte) < 0 127 || (byte != 0xa3 && byte != 0x25)) 128 goto next; 129 130 /* Skip immediate argument in the instruction. */ 131 offset += 4; 132 133 if (elf_read_next_u8(lte->plt_data, &offset, &byte) < 0 134 || byte != 0x68 135 || elf_read_next_u32(lte->plt_data, 136 &offset, &reloc_arg) < 0) { 137 reloc_arg = 0; 138 goto next; 139 } 140 141 if (lte->ehdr.e_machine == EM_386) { 142 if (reloc_arg % 8 != 0) { 143 reloc_arg = 0; 144 goto next; 145 } 146 reloc_arg /= 8; 147 } 148 149 next: 150 if (VECT_PUSHBACK(<e->arch.plt_map, &reloc_arg) < 0) { 151 arch_elf_destroy(lte); 152 return -1; 153 } 154 } 155 156 return 0; 157 } 158 159 void 160 arch_elf_destroy(struct ltelf *lte) 161 { 162 VECT_DESTROY(<e->arch.plt_map, uint32_t, NULL, NULL); 163 } 164