private static uint GetSymValue(Elf_Header *aHeader, int aTableIdx, int aSymIdx) { uint BaseAddress = (uint)aHeader; Elf_Shdr * SymSection = (Elf_Shdr *)(BaseAddress + aHeader->e_shoff) + aTableIdx; Elf32_Sym *SymTab = (Elf32_Sym *)(SymSection->sh_addr) + aSymIdx; switch (SymTab->st_shndx) { case SHN_UNDEF: { var StrTabAdd = ((Elf_Shdr *)(BaseAddress + aHeader->e_shoff) + SymSection->sh_link)->sh_addr; string SymName = new string((sbyte *)(StrTabAdd + SymTab->st_name)); Debug.Write("Undefined Symbol: %s\n", SymName); Heap.Free(SymName); throw new Exception("[ELF]: Extern Symbol not supported"); } case SHN_ABS: return(SymTab->st_value); default: Elf_Shdr *TargetSection = (Elf_Shdr *)(BaseAddress + aHeader->e_shoff) + SymTab->st_shndx; return(TargetSection->sh_addr + SymTab->st_value); } }
private static void RegisterSymbol(Elf_Header *aHeader, Elf_Shdr *aShdr, string aPath) { uint BaseAddress = (uint)aHeader; Elf32_Sym *SymTab = (Elf32_Sym *)aShdr->sh_addr; var StrTabAdd = ((Elf_Shdr *)(BaseAddress + aHeader->e_shoff) + aShdr->sh_link)->sh_addr; uint count = aShdr->sh_size / aShdr->sh_entsize; uint Address; for (uint i = 0; i < count; i++, SymTab++) { uint flag = (uint)(SymTab->st_info >> 4); if (flag == STB_GLOBAL) { switch (SymTab->st_shndx) { case SHN_UNDEF: continue; // for now ignore UNDEF Symbols case SHN_ABS: Address = SymTab->st_value; break; default: var TargetSection = (Elf_Shdr *)(BaseAddress + aHeader->e_shoff) + SymTab->st_shndx; Address = TargetSection->sh_addr + SymTab->st_value; break; } string SymName = new string((sbyte *)(StrTabAdd + SymTab->st_name)); Debug.Write("Symbol: %s\n", SymName); } } }
const uint STB_WEAK = 2; /* Weak, (ie. __attribute__((weak))) */ internal static uint Load(string aPath) { Stream xStream = VirtualFileSystem.GetFile(aPath); if (xStream == null) { throw new Exception("[ELF]: File not found!"); } var xData = new byte[xStream.FileSize]; xStream.Read(xData, xData.Length); uint BaseAddress = xData.GetDataOffset(); Elf_Header *Header = (Elf_Header *)BaseAddress; /* verify ELF header and if this code support this type of elf */ CheckHeader(Header); /* prepare sections and allocate memory (if required) */ Elf_Shdr *Shdr = (Elf_Shdr *)(BaseAddress + Header->e_shoff); for (int i = 0; i < Header->e_shnum; i++, Shdr++) { Shdr->sh_addr = BaseAddress + Shdr->sh_offset; if ((Shdr->sh_flags & SHF_ALLOC) != 0) { LoadSection(BaseAddress, Shdr); } } /* Iterate over all sections and perform relocations */ Shdr = (Elf_Shdr *)(BaseAddress + Header->e_shoff); for (int i = 0; i < Header->e_shnum; i++, Shdr++) { switch (Shdr->sh_type) { case SHT_SYMTAB: { RegisterSymbol(Header, Shdr, aPath); } break; case SHT_REL: { Shdr->sh_addr = BaseAddress + Shdr->sh_offset; Relocate(Header, Shdr); } break; } } uint LoadAddress = Header->e_entry; Heap.Free(xData); return(LoadAddress); }
private static void Relocate(Elf_Header *aHeader, Elf_Shdr *aShdr) { uint BaseAddress = (uint)aHeader; Elf32_Rel *Reloc = (Elf32_Rel *)aShdr->sh_addr; Elf_Shdr * TargetSection = (Elf_Shdr *)(BaseAddress + aHeader->e_shoff) + aShdr->sh_info; uint RelocCount = aShdr->sh_size / aShdr->sh_entsize; byte SymIdx; uint SymVal, RelocType; for (uint i = 0; i < RelocCount; i++, Reloc++) { SymVal = 0; SymIdx = (byte)(Reloc->r_info >> 8); RelocType = Reloc->r_info & 0xFF; if (SymIdx != SHN_UNDEF) { if (RelocType == R_386_PLT32) { SymVal = 0; } else { SymVal = GetSymValue(aHeader, TargetSection->sh_link, SymIdx); } } uint *add_ref = (uint *)(TargetSection->sh_addr + Reloc->r_offset); switch (RelocType) { case R_386_32: *add_ref = SymVal + *add_ref; // S + A break; case R_386_PLT32: // L + A - P case R_386_PC32: // S + A - P default: throw new Exception("[ELF]: Unsupported Relocation type"); } } }
private static uint LoadSection(uint aBaseAddress, Elf_Shdr *aShdr) { // make sure address if aligned uint align = aShdr->sh_addralign; uint addr = Heap.kmalloc(aShdr->sh_size + align); if (align != 0) { uint offset = addr & (align - 1); addr += align - offset; } if (aShdr->sh_type != SHT_NOBITS) { Memory.FastCopy(addr, aBaseAddress + aShdr->sh_offset, aShdr->sh_size); } aShdr->sh_addr = addr; return(addr); }