////////////////////////////////////////////////////////////////////// // // // elfUtils.pas: .ELF loading, section parsing, and flattening. // // // // The contents of this file are subject to the Bottled Light // // Public License Version 1.0 (the "License"); you may not use this // // file except in compliance with the License. You may obtain a // // copy of the License at http://www.bottledlight.com/BLPL/ // // // // Software distributed under the License is distributed on an // // "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or // // implied. See the License for the specific language governing // // rights and limitations under the License. // // // // The Original Code is the Mappy VM User Interface, released // // April 1st, 2003. The Initial Developer of the Original Code is // // Bottled Light, Inc. Portions created by Bottled Light, Inc. are // // Copyright (C) 2001-2003 Bottled Light, Inc. All Rights Reserved. // // // // Author(s): // // Michael Noland (joat), michael@bottledlight.com // // // // Changelog: // // 1.0: First public release (April 1st, 2003) // // // // Notes: // // Flattening: the process of turning a segmented .ELF into a // // a linear image one can just stick in ROM space. // // // // The developers of the ELF and DWARF standard has an odd sense // // of humor, but flattening an ELF does have a ring to it, as // // do DWARFs attacking my sanity all the time. // // // ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// unit elfUtils; /////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// interface //////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// uses Classes, SysUtils, Contnrs, Math, console, nexus, AddressSpace; ////////////////////////////////////////////////////////////////////// const // Indicies for the identifier block ELF_INDEX_MAGIC0 = 0; // File identification ELF_INDEX_MAGIC1 = 1; // File identification ELF_INDEX_MAGIC2 = 2; // File identification ELF_INDEX_MAGIC3 = 3; // File identification ELF_INDEX_CLASS = 4; // File class ELF_INDEX_DATA = 5; // Data encoding ELF_INDEX_VERSION = 6; // File version ELF_INDEX_PAD = 7; // Start of padding bytes ELF_INDEX_NIDENT = 16; // Size of e_ident[] // Magic numbers for the identifier block ELF_MAGIC0 = $7F; // e_ident[ELF_INDEX_MAG0] ELF_MAGIC1 = ord('E'); // e_ident[ELF_INDEX_MAG1] ELF_MAGIC2 = ord('L'); // e_ident[ELF_INDEX_MAG2] ELF_MAGIC3 = ord('F'); // e_ident[ELF_INDEX_MAG3] ////////////////////////////////////////////////////////////////////// // Type of ELF ELF_TYPE_NONE = 0; // No file type ELF_TYPE_REL = 1; // Relocatable file ELF_TYPE_EXEC = 2; // Executable file ELF_TYPE_DYN = 3; // Shared object file ELF_TYPE_CORE = 4; // Core file ELF_TYPE_LOPROC = $ff00; // Processor-specific ELF_TYPE_HIPROC = $ffff; // Processor-specific // Version codes ELF_VERSION_NONE = 0; // Invalid version ELF_VERSION_CURRENT = 1; // Current version ////////////////////////////////////////////////////////////////////// // Target machine for the ELF ELF_MACHINE_NONE = 0; // No machine ELF_MACHINE_M32 = 1; // AT&T WE 32100 ELF_MACHINE_SPARC = 2; // SPARC ELF_MACHINE_386 = 3; // Intel 80386 ELF_MACHINE_68K = 4; // Motorola 68000 ELF_MACHINE_88K = 5; // Motorola 88000 ELF_MACHINE_860 = 7; // Intel 80860 ELF_MACHINE_MIPS = 8; // MIPS RS3000 ELF_MACHINE_ARM = 40; // ARM7tdmi // Target architechure width ELF_CLASS_NONE = 0; // Invalid class ELF_CLASS_32 = 1; // 32-bit objects ELF_CLASS_64 = 2; // 64-bit objects // Target architechure organization ELF_DATA_NONE = 0; // Invalid data encoding ELF_DATA_LSB = 1; // See below ELF_DATA_MSB = 2; // See below ////////////////////////////////////////////////////////////////////// // Reserved section indicies SECTION_UNDEF = 0; SECTION_LORESERVE = $FF00; SECTION_LOPROC = $FF00; SECTION_HIPROC = $FF1F; SECTION_ABS = $FFF1; SECTION_COMMON = $FFF2; SECTION_HIRESERVE = $FFFF; // Section types SECTION_TYPE_NULL = 0; SECTION_TYPE_PROGBITS = 1; SECTION_TYPE_SYMTAB = 2; SECTION_TYPE_STRTAB = 3; SECTION_TYPE_RELA = 4; SECTION_TYPE_HASH = 5; SECTION_TYPE_DYNAMIC = 6; SECTION_TYPE_NOTE = 7; SECTION_TYPE_NOBITS = 8; SECTION_TYPE_REL = 9; SECTION_TYPE_SHLIB = 10; SECTION_TYPE_DYNSYM = 11; SECTION_TYPE_LOPROC = $70000000; SECTION_TYPE_HIPROC = $7FFFFFFF; SECTION_TYPE_LOUSER = $80000000; SECTION_TYPE_HIUSER = $FFFFFFFF; // Section flags SECTION_FLAGS_WRITE = $1; SECTION_FLAGS_ALLOC = $2; SECTION_FLAGS_EXECINSTR = $4; SECTION_FLAGS_MASKPROC = $F0000000; ////////////////////////////////////////////////////////////////////// // Symbol Table Entry Binding Modes SYMBOL_BINDING_LOCAL = 0; SYMBOL_BINDING_GLOBAL = 1; SYMBOL_BINDING_WEAK = 2; SYMBOL_BINDING_LOPROC = 13; SYMBOL_BINDING_HIPROC = 15; // Symbol Table Entry Types SYMBOL_TYPE_NOTYPE = 0; SYMBOL_TYPE_OBJECT = 1; SYMBOL_TYPE_FUNC = 2; SYMBOL_TYPE_SECTION = 3; SYMBOL_TYPE_FILE = 4; SYMBOL_TYPE_LOPROC = 13; SYMBOL_TYPE_HIPROC = 15; ////////////////////////////////////////////////////////////////////// type // ELF File Header TElf32Header = packed record ident: array[0..15] of byte; elfType: uint16; machine: uint16; version: uint32; entryPoint: uint32; progTableOffset: uint32; sectionTableOffset: uint32; flags: uint32; headerSize: uint16; progEntrySize: uint16; numProgEntries: uint16; sectionEntrySize: uint16; numSectionEntries: uint16; stringTableIndex: uint16; end; PElf32Header = ^TElf32Header; // ELF Section Header TElf32SectionHeader = packed record sh_name: uint32; sh_type: uint32; sh_flags: uint32; sh_addr: uint32; sh_offset: uint32; sh_size: uint32; sh_link: uint32; sh_info: uint32; sh_addralign: uint32; sh_entsize: uint32; end; PElf32SectionHeader = ^TElf32SectionHeader; // ELF Symbol Table Entry TElf32SymbolEntry = packed record name: uint32; value: uint32; size: uint32; info: byte; other: byte; sectionIndex: uint16; end; PElf32SymbolEntry = ^TElf32SymbolEntry; ////////////////////////////////////////////////////////////////////// type TELFFile = class; TReadableResource = class public data: PByteArray; dataPointer: pointer; size: uint32; function ReadUint8: uint32; function ReadInt8: int32; function ReadUint16: uint32; function ReadInt16: int32; function ReadUint32: uint32; function ReadInt32: int32; function ReadUint64: uint32; function ReadInt64: int32; function ReadLEB128: uint32; function ReadSLEB128: int32; procedure ReadBlock(var block; size: uint32); function ReadString: PChar; function ReadAddress: uint32; procedure SeekRelAddress(offset: integer); procedure Seek(absOffset: integer); end; // A container class for an ELF section TELFSection = class(TReadableResource) public FHeader: PElf32SectionHeader; constructor Create(myParent: TElfFile; header: PElf32SectionHeader); destructor Destroy; override; procedure ProcessSymtab(q: integer); function Name: string; function HasSubstance: boolean; function ReadElfString(index: integer): string; private FParent: TElfFile; end; ////////////////////////////////////////////////////////////////////// // A container class for an ELF file (has children sections and // manages the memory associated with them) TELFFile = class private FSections: TObjectList; function GetSection(index: integer): TElfSection; public header: PElf32Header; size: integer; data: PByteArray; property Sections[index: integer]: TElfSection read GetSection; function FindSectionByName(name: string): TElfSection; function ReadString(index: integer): string; function NumSections: integer; constructor Create(stream: TStream); procedure Build; destructor Destroy; override; function Flatten(var data: Puint8array): integer; end; ////////////////////////////////////////////////////////////////////// var elf: TELFFile; ////////////////////////////////////////////////////////////////////// implementation /////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// // TELFSection /////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// constructor TELFSection.Create(myParent: TElfFile; header: PElf32SectionHeader); begin FParent := myParent; FHeader := header; data := @(FParent.data^[FHeader^.sh_offset]); size := FHeader^.sh_size; end; ////////////////////////////////////////////////////////////////////// destructor TELFSection.Destroy; begin end; ////////////////////////////////////////////////////////////////////// function TELFSection.HasSubstance: boolean; begin with FHeader^ do Result := (sh_type = SECTION_TYPE_PROGBITS) and (sh_flags and SECTION_FLAGS_ALLOC <> 0); end; ////////////////////////////////////////////////////////////////////// function TELFSection.Name: string; begin Result := FParent.ReadString(FHeader^.sh_name); end; ////////////////////////////////////////////////////////////////////// procedure TELFSection.ProcessSymtab(q: integer); {const types: array[0..15] of string = ('N/A', 'Object', 'Function', 'Section', 'File', '','','','','','','','','Lo Proc','','Hi Proc'); type TSymbolTable = array[0..0] of TElf32SymbolEntry; PSymbolTable = ^TSymbolTable; var section: TElfSection; sym: PSymbolTable; i: integer;} begin { section := FParent.Sections[FHeader^.sh_link]; sym := @(data^); if FHeader^.sh_type = SECTION_TYPE_SYMTAB then for i := 0 to (FHeader^.sh_size div FHeader^.sh_entsize) - 1 do if sym^[i].sectionIndex < SECTION_LORESERVE then begin printf(Format('%-33s is at $%8.8x, of type %-8s and size %-4d]', [section.ReadString(sym^[i].st_name), sym^[i].st_value, types[sym^[i].st_info and $F], sym^[i].st_size])); end;} end; ////////////////////////////////////////////////////////////////////// function TELFSection.ReadElfString(index: integer): string; begin if (index < $FF00) and (FHeader^.sh_type = SECTION_TYPE_STRTAB) then Result := PChar(@(data^[index])) else Result := ''; end; ////////////////////////////////////////////////////////////////////// // TReadableResource ///////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// function TReadableResource.ReadUint8: uint32; begin Result := Puint8(dataPointer)^; dataPointer := pointer(integer(dataPointer) + 1); end; ////////////////////////////////////////////////////////////////////// function TReadableResource.ReadInt8: int32; begin Result := Pint8(dataPointer)^; dataPointer := pointer(integer(dataPointer) + 1); end; ////////////////////////////////////////////////////////////////////// function TReadableResource.ReadUint16: uint32; begin Result := Puint16(dataPointer)^; dataPointer := pointer(integer(dataPointer) + 2); end; ////////////////////////////////////////////////////////////////////// function TReadableResource.ReadInt16: int32; begin Result := Pint16(dataPointer)^; dataPointer := pointer(integer(dataPointer) + 2); end; ////////////////////////////////////////////////////////////////////// function TReadableResource.ReadUint32: uint32; begin Result := Puint32(dataPointer)^; dataPointer := pointer(integer(dataPointer) + 4); end; ////////////////////////////////////////////////////////////////////// function TReadableResource.ReadInt32: int32; begin Result := Pint32(dataPointer)^; dataPointer := pointer(integer(dataPointer) + 4); end; ////////////////////////////////////////////////////////////////////// function TReadableResource.ReadUint64: uint32; begin // fixme! ReadUint32; Result := ReadUint32; end; ////////////////////////////////////////////////////////////////////// function TReadableResource.ReadInt64: int32; begin ReadInt32; Result := ReadInt32; end; ////////////////////////////////////////////////////////////////////// function TReadableResource.ReadLEB128: uint32; var shift: integer; b: byte; begin Result := 0; shift := 0; repeat b := ReadUint8; Result := Result or ((b and $7F) shl shift); shift := shift + 7; until b and (1 shl 7) = 0; end; ////////////////////////////////////////////////////////////////////// function TReadableResource.ReadSLEB128: int32; var shift: integer; b: byte; begin Result := 0; shift := 0; repeat b := ReadUint8; Result := Result or ((b and $7F) shl shift); shift := shift + 7; until b and (1 shl 7) = 0; if (shift < 32) and (b and (1 shl 6) <> 0) then Result := Result or (-(1 shl shift)); end; ////////////////////////////////////////////////////////////////////// procedure TReadableResource.ReadBlock(var block; size: uint32); begin Move((dataPointer)^, block, size); dataPointer := pointer(uint32(dataPointer) + size); end; ////////////////////////////////////////////////////////////////////// function TReadableResource.ReadString: PChar; begin Result := dataPointer; dataPointer := pointer(integer(dataPointer) + Length(Result) + 1); end; ////////////////////////////////////////////////////////////////////// function TReadableResource.ReadAddress: uint32; begin Result := ReadUint32; end; ////////////////////////////////////////////////////////////////////// procedure TReadableResource.SeekRelAddress(offset: integer); begin dataPointer := pointer(integer(dataPointer) + offset); end; ////////////////////////////////////////////////////////////////////// procedure TReadableResource.Seek(absOffset: integer); begin dataPointer := pointer(integer(data) + absOffset); end; ////////////////////////////////////////////////////////////////////// // TElfFile ////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// procedure TELFFile.Build; var i: uint32; section: TElfSection; begin // Load the sections into their own classes FSections := TObjectList.Create; for i := 0 to header^.numSectionEntries - 1 do begin section := TElfSection.Create(self, @(data^[header^.sectionTableOffset + i * SizeOf(TElf32SectionHeader)])); FSections.Add(section); end; end; ////////////////////////////////////////////////////////////////////// constructor TELFFile.Create(stream: TStream); begin // Read in the data from the stream size := stream.Size; GetMem(data, size); stream.Read(data^, size); // Access the header header := PElf32Header(data); // Verify its a valid version 1 little-endian executable 32-bit ARM ELF file if (header.ident[ELF_INDEX_MAGIC0] = ELF_MAGIC0) and (header.ident[ELF_INDEX_MAGIC1] = ELF_MAGIC1) and (header.ident[ELF_INDEX_MAGIC2] = ELF_MAGIC2) and (header.ident[ELF_INDEX_MAGIC3] = ELF_MAGIC3) and (header.ident[ELF_INDEX_CLASS] = ELF_CLASS_32) and (header.ident[ELF_INDEX_DATA] = ELF_DATA_LSB) and (header.ident[ELF_INDEX_VERSION] = ELF_VERSION_CURRENT) and (header.elfType = ELF_TYPE_EXEC) and (header.machine = ELF_MACHINE_ARM) and (header.version = ELF_VERSION_CURRENT) then else Free; end; ////////////////////////////////////////////////////////////////////// destructor TELFFile.Destroy; begin FreeMem(data, size); FSections.Free; inherited; end; ////////////////////////////////////////////////////////////////////// function TELFFile.FindSectionByName(name: string): TElfSection; var index: integer; begin for index := 0 to NumSections - 1 do begin Result := Sections[index]; if Result.name = name then Exit; end; Result := nil; end; ////////////////////////////////////////////////////////////////////// function RoundUpToPow2(num: uint32): uint32; begin Result := 1; while Result < num do Result := Result shl 1; end; ////////////////////////////////////////////////////////////////////// // Flattens any run-time active segments into a linear image, stored // in data, and returns the size of this image function TELFFile.Flatten(var data: Puint8array): integer; var acc, size: longword; i: integer; section: TElfSection; begin size := 0; for i := 0 to NumSections - 1 do begin section := Sections[i]; if section.HasSubstance then Inc(size, section.FHeader.sh_size); end; // Spew the segments into a memory buffer Result := RoundUpToPow2(size); GetMem(data, Result); acc := 0; for i := 0 to NumSections - 1 do begin section := Sections[i]; if section.HasSubstance then begin Move(section.data^, data^[acc], section.FHeader.sh_size); acc := acc + section.FHeader.sh_size; end; end; end; ////////////////////////////////////////////////////////////////////// function TELFFile.GetSection(index: integer): TElfSection; begin Result := TElfSection(FSections.Items[index]); end; ////////////////////////////////////////////////////////////////////// function TELFFile.NumSections: integer; begin Result := FSections.Count; end; ////////////////////////////////////////////////////////////////////// function TELFFile.ReadString(index: integer): string; begin if (header^.stringTableIndex > 0) and (header^.stringTableIndex < SECTION_LORESERVE) then Result := Sections[header^.stringTableIndex].ReadElfString(index) else Result := ''; end; ////////////////////////////////////////////////////////////////////// end. //////////////////////////////////////////////////////////////////////