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 Elf32_Phdr *cr_pht; 35 Elf32_Shdr *cr_sht; 36 37 Elf32_Addr min_addr = 0x00000000; // Min. ELF vaddr 38 Elf32_Addr max_addr = 0x00000000; // Max. ELF vaddr 39 Elf32_Word max_align = sizeof(void*); // Min. align of posix_memalign() 40 Elf32_Addr min_alloc, max_alloc; // Min. and max. aligned allocables 41 42 Elf32_Addr dyn_addr = 0x00000000; 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 = (Elf32_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 = (Elf32_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 = (Elf32_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 Elf32_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 (Elf32_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 = (Elf32_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 Elf32_Word *dest = module_get_absolute(rel->r_offset, module); 193 194 // The symbol reference index 195 Elf32_Word sym = ELF32_R_SYM(rel->r_info); 196 unsigned char type = ELF32_R_TYPE(rel->r_info); 197 198 // The symbol definition (if applicable) 199 Elf32_Sym *sym_def = NULL; 200 struct elf_module *sym_module = NULL; 201 Elf32_Addr sym_addr = 0x0; 202 203 if (sym > 0) { 204 // Find out details about the symbol 205 206 // The symbol reference 207 Elf32_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 (ELF32_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 = (Elf32_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_386_NONE: 238 // Do nothing 239 break; 240 case R_386_32: 241 *dest += sym_addr; 242 break; 243 case R_386_PC32: 244 *dest += sym_addr - (Elf32_Addr)dest; 245 break; 246 case R_386_COPY: 247 if (sym_addr > 0) { 248 memcpy((void*)dest, (void*)sym_addr, sym_def->st_size); 249 } 250 break; 251 case R_386_GLOB_DAT: 252 case R_386_JMP_SLOT: 253 // Maybe TODO: Keep track of the GOT entries allocations 254 *dest = sym_addr; 255 break; 256 case R_386_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 Elf32_Dyn *dyn_entry = module->dyn_table; 269 unsigned int i; 270 int res; 271 272 Elf32_Word plt_rel_size = 0; 273 char *plt_rel = NULL; 274 275 char *rel = NULL; 276 Elf32_Word rel_size = 0; 277 Elf32_Word rel_entry = 0; 278 279 // The current relocation 280 Elf32_Rel *crt_rel; 281 282 while (dyn_entry->d_tag != DT_NULL) { 283 switch(dyn_entry->d_tag) { 284 285 // PLT relocation information 286 case DT_PLTRELSZ: 287 plt_rel_size = dyn_entry->d_un.d_val; 288 break; 289 case DT_PLTREL: 290 if (dyn_entry->d_un.d_val != DT_REL) { 291 DBG_PRINT("Unsupported PLT relocation\n"); 292 return -1; 293 } 294 case DT_JMPREL: 295 plt_rel = module_get_absolute(dyn_entry->d_un.d_ptr, module); 296 break; 297 298 // Standard relocation information 299 case DT_REL: 300 rel = module_get_absolute(dyn_entry->d_un.d_ptr, module); 301 break; 302 case DT_RELSZ: 303 rel_size = dyn_entry->d_un.d_val; 304 break; 305 case DT_RELENT: 306 rel_entry = dyn_entry->d_un.d_val; 307 break; 308 309 // Module initialization and termination 310 case DT_INIT: 311 // TODO Implement initialization functions 312 break; 313 case DT_FINI: 314 // TODO Implement finalization functions 315 break; 316 } 317 318 dyn_entry++; 319 } 320 321 if (rel_size > 0) { 322 // Process standard relocations 323 for (i = 0; i < rel_size/rel_entry; i++) { 324 crt_rel = (Elf32_Rel*)(rel + i*rel_entry); 325 326 res = perform_relocation(module, crt_rel); 327 328 if (res < 0) 329 return res; 330 } 331 332 } 333 334 if (plt_rel_size > 0) { 335 // TODO: Permit this lazily 336 // Process PLT relocations 337 for (i = 0; i < plt_rel_size/sizeof(Elf32_Rel); i++) { 338 crt_rel = (Elf32_Rel*)(plt_rel + i*sizeof(Elf32_Rel)); 339 340 res = perform_relocation(module, crt_rel); 341 342 if (res < 0) 343 return res; 344 } 345 } 346 347 return 0; 348} 349 350