1/* 2 * elf_module.c 3 * 4 * Created on: Aug 11, 2008 5 * Author: Stefan Bucur <stefanb@zytor.com> 6 */ 7 8#include <errno.h> 9#include <stdlib.h> 10#include <string.h> 11#include <stdio.h> 12#include <elf.h> 13#include <dprintf.h> 14#include <core.h> 15 16#include <linux/list.h> 17#include <sys/module.h> 18#include <sys/exec.h> 19 20#include "elfutils.h" 21#include "../common.h" 22 23/* 24 * 25 * The implementation assumes that the loadable segments are present 26 * in the PHT sorted by their offsets, so that only forward seeks would 27 * be necessary. 28 */ 29int load_segments(struct elf_module *module, Elf_Ehdr *elf_hdr) { 30 int i; 31 int res = 0; 32 char *pht = NULL; 33 char *sht = NULL; 34 Elf64_Phdr *cr_pht; 35 Elf64_Shdr *cr_sht; 36 37 Elf64_Addr min_addr = 0x0000000000000000; // Min. ELF vaddr 38 Elf64_Addr max_addr = 0x0000000000000000; // Max. ELF vaddr 39 Elf64_Word max_align = sizeof(void*); // Min. align of posix_memalign() 40 Elf64_Addr min_alloc, max_alloc; // Min. and max. aligned allocables 41 42 Elf64_Addr dyn_addr = 0x0000000000000000; 43 44 // Get to the PHT 45 image_seek(elf_hdr->e_phoff, module); 46 47 // Load the PHT 48 pht = malloc(elf_hdr->e_phnum * elf_hdr->e_phentsize); 49 if (!pht) 50 return -1; 51 52 image_read(pht, elf_hdr->e_phnum * elf_hdr->e_phentsize, module); 53 54 // Compute the memory needings of the module 55 for (i=0; i < elf_hdr->e_phnum; i++) { 56 cr_pht = (Elf64_Phdr*)(pht + i * elf_hdr->e_phentsize); 57 58 switch (cr_pht->p_type) { 59 case PT_LOAD: 60 if (i == 0) { 61 min_addr = cr_pht->p_vaddr; 62 } else { 63 min_addr = MIN(min_addr, cr_pht->p_vaddr); 64 } 65 66 max_addr = MAX(max_addr, cr_pht->p_vaddr + cr_pht->p_memsz); 67 max_align = MAX(max_align, cr_pht->p_align); 68 break; 69 case PT_DYNAMIC: 70 dyn_addr = cr_pht->p_vaddr; 71 break; 72 default: 73 // Unsupported - ignore 74 break; 75 } 76 } 77 78 if (max_addr - min_addr == 0) { 79 // No loadable segments 80 DBG_PRINT("No loadable segments found\n"); 81 goto out; 82 } 83 84 if (dyn_addr == 0) { 85 DBG_PRINT("No dynamic information segment found\n"); 86 goto out; 87 } 88 89 // The minimum address that should be allocated 90 min_alloc = min_addr - (min_addr % max_align); 91 92 // The maximum address that should be allocated 93 max_alloc = max_addr - (max_addr % max_align); 94 if (max_addr % max_align > 0) 95 max_alloc += max_align; 96 97 98 if (elf_malloc(&module->module_addr, 99 max_align, 100 max_alloc-min_alloc) != 0) { 101 102 DBG_PRINT("Could not allocate segments\n"); 103 goto out; 104 } 105 106 module->base_addr = (Elf64_Addr)(module->module_addr) - min_alloc; 107 module->module_size = max_alloc - min_alloc; 108 109 // Zero-initialize the memory 110 memset(module->module_addr, 0, module->module_size); 111 112 for (i = 0; i < elf_hdr->e_phnum; i++) { 113 cr_pht = (Elf64_Phdr*)(pht + i * elf_hdr->e_phentsize); 114 115 if (cr_pht->p_type == PT_LOAD) { 116 // Copy the segment at its destination 117 if (cr_pht->p_offset < module->u.l._cr_offset) { 118 // The segment contains data before the current offset 119 // It can be discarded without worry - it would contain only 120 // headers 121 Elf64_Off aux_off = module->u.l._cr_offset - cr_pht->p_offset; 122 123 if (image_read((char *)module_get_absolute(cr_pht->p_vaddr, module) + aux_off, 124 cr_pht->p_filesz - aux_off, module) < 0) { 125 res = -1; 126 goto out; 127 } 128 } else { 129 if (image_seek(cr_pht->p_offset, module) < 0) { 130 res = -1; 131 goto out; 132 } 133 134 if (image_read(module_get_absolute(cr_pht->p_vaddr, module), 135 cr_pht->p_filesz, module) < 0) { 136 res = -1; 137 goto out; 138 } 139 } 140 141 /* 142 DBG_PRINT("Loadable segment of size 0x%08x copied from vaddr 0x%08x at 0x%08x\n", 143 cr_pht->p_filesz, 144 cr_pht->p_vaddr, 145 (Elf64_Addr)module_get_absolute(cr_pht->p_vaddr, module)); 146 */ 147 } 148 } 149 150 // Get to the SHT 151 image_seek(elf_hdr->e_shoff, module); 152 153 // Load the SHT 154 sht = malloc(elf_hdr->e_shnum * elf_hdr->e_shentsize); 155 if (!sht) { 156 res = -1; 157 goto out; 158 } 159 160 image_read(sht, elf_hdr->e_shnum * elf_hdr->e_shentsize, module); 161 162 // Setup the symtable size 163 for (i = 0; i < elf_hdr->e_shnum; i++) { 164 cr_sht = (Elf64_Shdr*)(sht + i * elf_hdr->e_shentsize); 165 166 if (cr_sht->sh_type == SHT_DYNSYM) { 167 module->symtable_size = cr_sht->sh_size; 168 break; 169 } 170 } 171 172 free(sht); 173 174 // Setup dynamic segment location 175 module->dyn_table = module_get_absolute(dyn_addr, module); 176 177 /* 178 DBG_PRINT("Base address: 0x%08x, aligned at 0x%08x\n", module->base_addr, 179 max_align); 180 DBG_PRINT("Module size: 0x%08x\n", module->module_size); 181 */ 182 183out: 184 // Free up allocated memory 185 if (pht != NULL) 186 free(pht); 187 188 return res; 189} 190 191int perform_relocation(struct elf_module *module, Elf_Rel *rel) { 192 Elf64_Xword *dest = module_get_absolute(rel->r_offset, module); 193 194 // The symbol reference index 195 Elf64_Word sym = ELF64_R_SYM(rel->r_info); 196 unsigned char type = ELF64_R_TYPE(rel->r_info); 197 198 // The symbol definition (if applicable) 199 Elf64_Sym *sym_def = NULL; 200 struct elf_module *sym_module = NULL; 201 Elf64_Addr sym_addr = 0x0; 202 203 if (sym > 0) { 204 // Find out details about the symbol 205 206 // The symbol reference 207 Elf64_Sym *sym_ref = symbol_get_entry(module, sym); 208 209 // The symbol definition 210 sym_def = 211 global_find_symbol(module->str_table + sym_ref->st_name, 212 &sym_module); 213 214 if (sym_def == NULL) { 215 DBG_PRINT("Cannot perform relocation for symbol %s\n", 216 module->str_table + sym_ref->st_name); 217 218 if (ELF64_ST_BIND(sym_ref->st_info) != STB_WEAK) 219 return -1; 220 221 // This must be a derivative-specific 222 // function. We're OK as long as we never 223 // execute the function. 224 sym_def = global_find_symbol("undefined_symbol", &sym_module); 225 } 226 227 // Compute the absolute symbol virtual address 228 sym_addr = (Elf64_Addr)module_get_absolute(sym_def->st_value, sym_module); 229 230 if (sym_module != module) { 231 // Create a dependency 232 enforce_dependency(sym_module, module); 233 } 234 } 235 236 switch (type) { 237 case R_X86_64_NONE: 238 // Do nothing 239 break; 240 case R_X86_64_64: 241 *dest += sym_addr; 242 break; 243 case R_X86_64_PC32: 244 *dest += sym_addr - (Elf32_Addr)dest; 245 break; 246 case R_X86_64_COPY: 247 if (sym_addr > 0) { 248 memcpy((void*)dest, (void*)sym_addr, sym_def->st_size); 249 } 250 break; 251 case R_X86_64_GLOB_DAT: 252 case R_X86_64_JUMP_SLOT: 253 //Maybe TODO: Keep track of the GOT entries allocations 254 *dest = sym_addr; 255 break; 256 case R_X86_64_RELATIVE: 257 *dest += module->base_addr; 258 break; 259 default: 260 DBG_PRINT("Relocation type %d not supported\n", type); 261 return -1; 262 } 263 264 return 0; 265} 266 267int resolve_symbols(struct elf_module *module) { 268 Elf64_Dyn *dyn_entry = module->dyn_table; 269 unsigned int i; 270 int res; 271 272 Elf64_Word plt_rel_size = 0; 273 void *plt_rel = NULL; 274 275 void *rel = NULL; 276 Elf64_Word rel_size = 0; 277 Elf64_Word rel_entry = 0; 278 Elf64_Xword rela_size = 0; 279 Elf64_Xword rela_entry = 0; 280 Elf64_Xword sym_ent = 0; 281 282 // The current relocation 283 Elf64_Rel *crt_rel; 284 285 while (dyn_entry->d_tag != DT_NULL) { 286 switch(dyn_entry->d_tag) { 287 288 // PLT relocation information 289 case DT_PLTRELSZ: 290 plt_rel_size = dyn_entry->d_un.d_val; 291 break; 292 case DT_PLTREL: 293 if (dyn_entry->d_un.d_val != DT_REL && dyn_entry->d_un.d_val != DT_RELA) { 294 DBG_PRINT("Unsupported PLT relocation\n"); 295 return -1; 296 } 297 //break; 298 case DT_JMPREL: 299 plt_rel = module_get_absolute(dyn_entry->d_un.d_ptr, module); 300 break; 301 302 // Standard relocation information 303 case DT_REL: 304 rel = module_get_absolute(dyn_entry->d_un.d_ptr, module); 305 break; 306 case DT_RELA: 307 rel = module_get_absolute(dyn_entry->d_un.d_ptr, module); 308 break; 309 case DT_RELSZ: 310 rel_size = dyn_entry->d_un.d_val; 311 break; 312 case DT_RELASZ: 313 rela_size = dyn_entry->d_un.d_val; 314 break; 315 case DT_RELENT: 316 rel_entry = dyn_entry->d_un.d_val; 317 break; 318 case DT_RELAENT: 319 rela_entry = dyn_entry->d_un.d_val; 320 break; 321 /* FIXME: We may need to rely upon SYMENT if DT_RELAENT is missing in the object file */ 322 case DT_SYMENT: 323 sym_ent = dyn_entry->d_un.d_val; 324 break; 325 326 // Module initialization and termination 327 case DT_INIT: 328 // TODO Implement initialization functions 329 break; 330 case DT_FINI: 331 // TODO Implement finalization functions 332 break; 333 } 334 335 dyn_entry++; 336 } 337 338 if (rel_size > 0) { 339 // Process standard relocations 340 for (i = 0; i < rel_size/rel_entry; i++) { 341 crt_rel = (Elf64_Rel*)(rel + i*rel_entry); 342 343 res = perform_relocation(module, crt_rel); 344 345 if (res < 0) 346 return res; 347 } 348 349 } 350 351 if (rela_size > 0) { 352 // Process standard relocations 353 for (i = 0; i < rela_size/rela_entry; i++) { 354 crt_rel = (Elf64_Rel*)(rel + i*rela_entry); 355 356 res = perform_relocation(module, crt_rel); 357 358 if (res < 0) 359 return res; 360 } 361 } 362 if (plt_rel_size > 0) { 363 // TODO: Permit this lazily 364 // Process PLT relocations 365 /* some modules do not have DT_SYMENT, set it sym_ent in such cases */ 366 if (!rela_entry) rela_entry = sym_ent; 367 //for (i = 0; i < plt_rel_size/sizeof(Elf64_Rel); i++) { 368 for (i = 0; i < plt_rel_size/rela_entry; i++) { 369 //crt_rel = (Elf64_Rel*)(plt_rel + i*sizeof(Elf64_Rel)); 370 crt_rel = (Elf64_Rel*)(plt_rel + i*rela_entry); 371 372 res = perform_relocation(module, crt_rel); 373 374 if (res < 0) 375 return res; 376 } 377 } 378 379 return 0; 380} 381