int arch_loader_relocate_elf_module(void * buf, addr_t *entry, addr_t *tm_exiter, void *load_address, struct section_data *sd) { uint32_t i, x; uint64_t module_entry=0, reloc_addr, mem_addr, module_exiter=0; elf_header_t * eh; elf64_section_header_t * sh; elf64_rel_t * reloc; elf64_rela_t * rela; elf64_symtab_entry_t * symtab; int error=0; eh = (elf_header_t *)buf; arch_loader_copy_sections(eh, load_address, sd); /* grab the functions we'll need */ for(i = 0; i < eh->shnum; i++) { sh = (elf64_section_header_t*)((addr_t)buf + eh->shoff + (i * eh->shsize)); if(sh->type == 2) { for(x = 0; x < sh->size; x += sh->sect_size) { symtab = (elf64_symtab_entry_t*)((addr_t)buf + sh->offset + x); if(!symtab->name) continue; /* TODO: this is all ugly. we should check the result of get_symbol_string * ...but really, we should re-write all of the ELF parsing code. */ if(!memcmp((uint8_t*)get_symbol_string(buf, symtab->name), (uint8_t*)"module_install", 14)) module_entry = sd->vbase[symtab->shndx] + symtab->address; if(!memcmp((uint8_t*)get_symbol_string(buf, symtab->name), (uint8_t*)"module_exit", 11)) module_exiter = sd->vbase[symtab->shndx] + symtab->address; } } } if(!module_entry) { printk(KERN_INFO, "[mod]: module_install() entry point was not found\n"); kfree(load_address); return 0; } *entry = module_entry; *tm_exiter = module_exiter; /* fix up the relocation entries */ for(i = 0; i < eh->shnum; i++) { sh = (elf64_section_header_t*)((addr_t)buf + eh->shoff + (i * eh->shsize)); /* 64-bit ELF only deals in rela relocation sections */ if(sh->type == 4) { for(x = 0; x < sh->size; x += sh->sect_size) { rela = (elf64_rela_t*)((addr_t)buf + sh->offset + x); symtab = fill_symbol_struct(buf, GET_RELOC_SYM(rela->info)); mem_addr = sd->vbase[sh->info] + rela->offset; reloc_addr = sd->vbase[symtab->shndx] + symtab->address; uint64_t P = mem_addr; if(sh->address) { printk(KERN_INFO, "[mod]: unsure how to do this...\n"); return 0; } if(symtab->shndx == 0) { reloc_addr = loader_find_kernel_function(get_symbol_string(buf, symtab->name)); if(!reloc_addr) { printk(KERN_INFO, "[mod]: %x: unresolved dependency \"%s\"\n", rela->info, get_symbol_string(buf, symtab->name)); return 0; } } else { if(GET_RELOC_TYPE(rela->info) == R_X86_64_64) reloc_addr += *(uint64_t *)(mem_addr) + rela->addend; else if(GET_RELOC_TYPE(rela->info) == R_X86_64_32) reloc_addr += *(uint64_t *)(mem_addr) + rela->addend; else if(GET_RELOC_TYPE(rela->info) == R_X86_64_PC32) reloc_addr += *(uint64_t *)(mem_addr) + rela->addend - P; else { printk(KERN_INFO, "[mod]: invalid relocation type (%x)\n", GET_RELOC_TYPE(rela->info)); return 0; } } elf64_write_field(GET_RELOC_TYPE(rela->info), mem_addr, reloc_addr); elf64_symtab_entry_t *ste = &((elf64_symtab_entry_t *)sd->vbase[sd->symtab])[GET_RELOC_SYM(rela->info)]; ste->address = mem_addr; } } } return 1; }
static int elf_mod_parse(uintptr_t elf, const char *name, int export_symbol, uintptr_t * common_data, uint32_t * common_size, uintptr_t * mod_load_ptr, uintptr_t * mod_unload_ptr) { uint32_t i, x; uintptr_t reloc_addr; void * mem_addr; struct elfhdr *eh; struct secthdr *sh; struct reloc_a_s *reloc; struct symtab_s *symtab; uintptr_t cur_common_alloc = 0; uintptr_t cur_common_align = 1; eh = (struct elfhdr *)elf; kprintf("[ II ] shnum = %d, shsize = %d\n", eh->e_shnum, eh->e_shentsize); for (i = 0; i < eh->e_shnum; i++) { sh = (struct secthdr*)(elf + eh->e_shoff + (i * eh->e_shentsize)); kprintf("[ II ] sh type = %d\n", sh->sh_type); if (sh->sh_type == SH_TYPE_SYMTAB) { for (x = 0; x < sh->sh_size; x += sh->sh_entsize) { symtab = (struct symtab_s *)(elf + sh->sh_offset + x); kprintf("[ II ] found sym: [%s] info[%02x] size[%d] addr[%08x]\n", get_symbol_string(elf, symtab->sym_name), symtab->sym_info, symtab->sym_size, symtab->sym_address); if (symtab->sym_shndx != SHN_UNDEF && symtab->sym_shndx < 0xff00) { kprintf("[ II ] value offset [%08x]\n", get_section_offset(elf, symtab->sym_shndx) + symtab->sym_address); const char * sym_name = get_symbol_string(elf, symtab->sym_name); switch (GET_SYMTAB_BIND(symtab->sym_info)) { case STB_GLOBAL: kprintf("[ II ] global symbol\n"); case STB_LOCAL: if (strcmp(sym_name, MOD_INIT_MODULE) == 0) { *mod_load_ptr = get_section_offset(elf, symtab->sym_shndx) + symtab->sym_address + elf; } else if (strcmp(sym_name, MOD_CLEANUP_MODULE) == 0) { *mod_unload_ptr = get_section_offset(elf, symtab->sym_shndx) + symtab->sym_address + elf; } // global if (GET_SYMTAB_BIND(symtab->sym_info) == 1 && export_symbol) { mod_touch_symbol(sym_name, (void *)(get_section_offset(elf, symtab->sym_shndx) + symtab->sym_address + elf), 0); } break; case STB_WEAK: kprintf("[ II ] weak symbol\n"); if (export_symbol) { elf_mod_create_symbol(sym_name, (void *)(symtab->sym_address + elf), 0); } break; } } else if (symtab->sym_shndx == SHN_COMMON) { // TODO: implement SHN_COMMON kprintf("[ EE ] not implemented\n"); } else { kprintf("[ II ] shndx[%04x]\n", symtab->sym_shndx); } } } else if (sh->sh_type == SH_TYPE_NOBITS) { kprintf("[ II ] bss section, alloc %d byte align 0x%x\n", sh->sh_size, sh->sh_addralign); if (bsf(sh->sh_addralign != bsr(sh->sh_addralign))) { error(" bad align\n"); return -1; } if (sh->sh_addralign > cur_common_align) { cur_common_align = sh->sh_addralign; } cur_common_alloc = ((cur_common_alloc - 1) | (sh->sh_addralign - 1)) + 1; sh->sh_addr = cur_common_alloc; cur_common_alloc += sh->sh_size; } } uintptr_t common_space; if (cur_common_align > PGSIZE) { error("align failed\n"); return -1; } else if (cur_common_alloc > 0) { // TODO: kmalloc would return non-kernel-area memory pointer, which would cause PG FAULT common_space = (uintptr_t)kmalloc(cur_common_alloc); memset((void*)common_space, 0, cur_common_alloc); *common_data = common_space; *common_size = cur_common_alloc; } else { *common_data = 0; *common_size = 0; } // fill the relocation entries for (i = 0; i < eh->e_shnum; i++) { sh = (struct secthdr *)(elf + eh->e_shoff + (i * eh->e_shentsize)); if (sh->sh_type == SH_TYPE_RELA) { for (x = 0; x < sh->sh_size; x += sh->sh_entsize) { reloc = (struct reloc_a_s *)(elf + sh->sh_offset + x); symtab = fill_symbol_struct(elf, GET_RELOC_SYM(reloc->rl_info)); kprintf("[ II ] reloc[%02x] offset[%08x] for [%s], sym offset[%08x]\n", GET_RELOC_TYPE(reloc->rl_info), reloc->rl_offset, get_symbol_string(elf, symtab->sym_name), symtab->sym_address); mem_addr = (void *)(elf + reloc->rl_offset + get_section_offset(elf, sh->sh_info)); /* external reference (kernel symbol most likely) */ if (symtab->sym_shndx == SHN_UNDEF) { const char *sym_name = get_symbol_string(elf, symtab->sym_name); int idx = find_export_sym(sym_name, 0); if (idx == -1) { if (strcmp(sym_name, name) == 0) { reloc_addr = elf; } else { error("unresolved symbol \"%s\", set with 0\n", sym_name); reloc_addr = 0; } } else { kprintf("external symbol %s addr = %p\n", sym_name, ex_sym_ptr[idx]); reloc_addr = ex_sym_ptr[idx]; } } else if (symtab->sym_shndx < 0xff00) { kprintf("section offset %16x, addr %16x\n", get_section_offset(elf, symtab->sym_shndx), symtab->sym_address); if (((struct secthdr *)(elf + eh->e_shoff + (symtab->sym_shndx * eh->e_shentsize)))->sh_type == SH_TYPE_NOBITS) { reloc_addr = common_space; } else { reloc_addr = elf; } reloc_addr += get_section_offset(elf, symtab->sym_shndx); reloc_addr += symtab->sym_address; } else if (symtab->sym_shndx == SHN_COMMON) { reloc_addr = common_space + symtab->sym_address; } else { error("unhandled syn_shndx\n"); } switch (GET_RELOC_TYPE(reloc->rl_info)) { case 0x02: // S + A - P reloc_addr = reloc_addr - (uintptr_t)mem_addr; //*(uintptr_t *)mem_addr = reloc_addr + *(uintptr_t *)mem_addr; *(uint32_t *)mem_addr = reloc_addr + reloc->rl_addend; kprintf("fill rel address %08x to %08x\n", *(uint32_t *)mem_addr, mem_addr); break; case 0x0b: // S + A *(uint32_t *)mem_addr = reloc_addr + reloc->rl_addend; kprintf("fill rel address %08x to %08x\n", *(uint32_t *)mem_addr, mem_addr); break; default: error("unsupported relocation type (%x)\n", GET_RELOC_TYPE(reloc->rl_info)); break; } } } else if (sh->sh_type == SH_TYPE_REL) { kprintf("[ EE ] relocation SH_TYPE_REL not implemented\n"); } } return 0; }
int arch_specific_parse_elf_module(uint8_t * buf, addr_t *entry, addr_t *exiter, addr_t *deps) { uint32_t i, x; uint32_t module_entry=0, reloc_addr, mem_addr, module_exiter=0, module_deps=0; elf32_header_t * eh; elf32_section_header_t * sh; elf32_reloc_entry_t * reloc; elf32_symtab_entry_t * symtab; int error=0; eh = (elf32_header_t *)buf; /* grab the functions we'll need */ for(i = 0; i < eh->shnum; i++) { sh = (elf32_section_header_t*)(buf + eh->shoff + (i * eh->shsize)); if(sh->type == 2) { for(x = 0; x < sh->size; x += sh->sect_size) { symtab = (elf32_symtab_entry_t*)(buf + sh->offset + x); if(!memcmp((uint8_t*)get_symbol_string(buf, symtab->name), (uint8_t*)"module_install", 14)) module_entry = get_section_offset(buf, symtab->shndx) + symtab->address + (uint32_t)buf; if(!memcmp((uint8_t*)get_symbol_string(buf, symtab->name), (uint8_t*)"module_exit", 11)) module_exiter = get_section_offset(buf, symtab->shndx) + symtab->address + (uint32_t)buf; if(!memcmp((uint8_t*)get_symbol_string(buf, symtab->name), (uint8_t*)"module_deps", 11)) module_deps = get_section_offset(buf, symtab->shndx) + symtab->address + (uint32_t)buf; } } } if(!module_entry) { printk(KERN_INFO, "[mod]: module_install() entry point was not found\n"); return 1; } *entry = module_entry; *exiter = module_exiter; *deps = module_deps; /* fix up the relocation entries */ for(i = 0; i < eh->shnum; i++) { sh = (elf32_section_header_t*)(buf + eh->shoff + (i * eh->shsize)); if(sh->type == 9) { for(x = 0; x < sh->size; x += sh->sect_size) { reloc = (elf32_reloc_entry_t*)(buf + sh->offset + x); symtab = fill_symbol_struct(buf, GET_RELOC_SYM(reloc->info)); /* absolute physical address */ if(GET_RELOC_TYPE(reloc->info) == 0x01) { mem_addr = (uint32_t)buf + reloc->offset; mem_addr += get_section_offset(buf, sh->info); /* external reference (kernel symbol most likely) */ if(symtab->shndx == 0) { reloc_addr = find_kernel_function(get_symbol_string(buf, symtab->name)); if(!reloc_addr) { printk(KERN_INFO, "[mod]: *ABS* unresolved dependency \"%s\"\n", get_symbol_string(buf, symtab->name)); error++; } } else { reloc_addr = (uint32_t)buf + symtab->address + *(intptr_t*)mem_addr; reloc_addr += get_section_offset(buf, symtab->shndx); } *(intptr_t*)mem_addr = reloc_addr; } /* relative physical address */ else if(GET_RELOC_TYPE(reloc->info) == 0x02) { mem_addr = (uint32_t)buf + reloc->offset; mem_addr += get_section_offset(buf, sh->info); /* external reference (kernel symbol most likely) */ if(symtab->shndx == 0) { reloc_addr = find_kernel_function(get_symbol_string(buf, symtab->name)); if(!reloc_addr) { printk(KERN_INFO, "[mod]: *REL* unresolved dependency \"%s\"\n", get_symbol_string(buf, symtab->name)); error++; } } else { reloc_addr = (uint32_t)buf + symtab->address; reloc_addr += get_section_offset(buf, symtab->shndx); } /* we need to make this relative to the memory address */ reloc_addr = mem_addr - reloc_addr + 4; *(intptr_t*)mem_addr = -reloc_addr; } else { printk(KERN_INFO, "[mod]: invalid relocation type (%x)\n", GET_RELOC_TYPE(reloc->info)); error++; } } } } return error; }