ELF-i386 Relocatable Kernel Modules (Extern Issue)
Posted: Mon Jun 08, 2015 8:11 pm
Ok, this has finally stumped me...
Loading the ELF into memory everything seems to be working fine, but I'm running into a strange issue.
Let's say I have the following in a Module:
the output of the printf is "0x10E15D(0x114004)", which is the correct location for the function, but I get a GPF upon calling HAL_RegisterDev, (cs invalid says bochs).
Now to "patch" the issue I can instead use this code:
in which case HAL_RegisterDev is still at location 0x10E15D, but upon calling the function everything runs as it should.
And for completion here is parts of my ELF Loader:
Loading the ELF into memory everything seems to be working fine, but I'm running into a strange issue.
Let's say I have the following in a Module:
Code: Select all
extern void HAL_RegisterDev(DEV_p Device);
......
printf("0x%x(0x%x)\n", (uint32_t) HAL_RegisterDev, (uint32_t) MyDevice);
HAL_RegisterDev(MyDevice);
Now to "patch" the issue I can instead use this code:
Code: Select all
void (*HAL_RegisterDev)(DEV_p Device) = (void(*)(DEV_p)) 0;
......
HAL_RegisterDev = (void(*)(DEV_p)) _API_Get_Symbol("HAL_RegisterDev");
......
printf("0x%x(0x%x)\n", (uint32_t) HAL_RegisterDev, (uint32_t) MyDevice);
HAL_RegisterDev(MyDevice);
And for completion here is parts of my ELF Loader:
Code: Select all
void _ELF_PREPARE_SECTIONS(void *File)
{
ELF32_Head_p Head = File;
for(uint16_t shd = 0; shd < ELF_SHead_Count(Head); shd++) {
ELF32_SHead_p SHead = ELF_SHead(Head, shd);
if((SHead->sh_type == SHT_NOBITS) && (SHead->sh_flags & SHF_ALLOC)) {
void *SectionLoc = calloc_page((SHead->sh_size / 0x1000) + ((SHead->sh_size % 0x1000) ? 1 : 0));
SHead->sh_offset = (Elf32_Off) (SectionLoc - File);
SHead->sh_addr = SectionLoc;
}
}
}
bool elf_do_reloc(void *File, uint16_t relocSection)
{
ELF32_Head_p hdr = File;
ELF32_SHead_p reltab = ELF_SHead(hdr, relocSection);
ELF32_SHead_p target = ELF_SHead(hdr, reltab->sh_info);
//volatile ELF32_Rel_p RelocTable;
for(uint32_t x = 0; x < (uint32_t)(reltab->sh_size / reltab->sh_entsize); x++) {
ELF32_Rel_p RelocTable = (ELF32_Rel_p) (((uint32_t) File + (uint32_t)reltab->sh_offset) + (uint32_t) (x * (uint32_t)reltab->sh_entsize));
int addr = (int)hdr + target->sh_offset;
int *ref = (int *)(addr + RelocTable->r_offset);
// Symbol value
int symval = 0;
if(ELF32_R_SYM(RelocTable->r_info) != 0) {
symval = ELF_Get_SymVal(hdr, reltab->sh_link, ELF32_R_SYM(RelocTable->r_info));
if(symval == 0) return FALSE;
}
// Relocate based on type
switch(ELF32_R_TYPE(RelocTable->r_info)) {
case R_386_NONE:
// No relocation
break;
case R_386_32:
// Symbol + Offset
*ref = DO_386_32(symval, *ref);
break;
case R_386_PC32:
// Symbol + Offset - Section Offset
*ref = DO_386_PC32(symval, *ref, (int)target);
break;
default:
return FALSE;
}
}
return TRUE;
}
uint32_t Load_ELF(void *File)
{
ELF32_Head_p Head = File;
if(!ELF_isSigned(Head))
return FALSE;
_ELF_PREPARE_SECTIONS(File);
if(ELF_isRelocatable(Head)) {
// Relocatable ELF
for(uint16_t shd = 0; shd < (uint16_t) ELF_SHead_Count(Head); shd++)
if(ELF_SHead(Head, shd)->sh_type == SHT_REL)
if(!elf_do_reloc(File, shd))
return FALSE;
} else if(ELF_isExecutable(Head)) {
// Non-Relocatable ELF
return FALSE;
} else
return FALSE;
return ELF_Get_Entry(File);
}
static inline int ELF_Get_SymVal(ELF32_Head_p Head, Elf32_Word Section, Elf32_Word offset) {
ELF32_SHead_p symtab = ELF_SHead(Head, Section);
int symaddr = (int)Head + symtab->sh_offset;
ELF32_SymTbl_p symbol = &((ELF32_SymTbl_p)symaddr)[offset];
if(symbol->st_shndx == SHN_UNDEF) {
ELF32_SHead_p strtab = ELF_SHead(Head, symtab->sh_link);
const char *name = (const char *)Head + strtab->sh_offset + symbol->st_name;
void *target = (void*)_API_Get_Symbol(name);
if(target)
return (int)target;
return 0;
} else if(symbol->st_shndx == SHN_ABS)
return (int) symbol->st_value;
else {
ELF32_SHead_p target = ELF_SHead(Head, symbol->st_shndx);
return (int)Head + symbol->st_value + target->sh_offset;
}
}
static inline uint32_t ELF_Get_Entry(void *FILE) {
ELF32_Head_p Head = (ELF32_Head_p) FILE;
if (Head->e_entry) {
if(Head->e_entry > FILE)
return (uint32_t) Head->e_entry;
else
return (uint32_t) ((uint32_t)FILE + (uint32_t)ELF_SHead_By_String(Head, ".text")->sh_offset + Head->e_entry);
} else {
ELF32_SHead_p SHead;
if(SHead = ELF_SHead_By_String(Head, ".text.startup"))
return (uint32_t) (FILE + SHead->sh_offset);
else if(SHead = ELF_SHead_By_String(Head, ".text"))
return (uint32_t) (FILE + SHead->sh_offset);
else
return NULL;
}
}