├── .gitignore ├── LICENSE_1_0.txt ├── README.md ├── dub.json ├── example ├── app.d ├── dub.json └── dub.selections.json └── source └── elf ├── low.d ├── low32.d ├── low64.d ├── meta.d ├── package.d └── sections ├── debugabbrev └── package.d ├── debugline ├── debugline32.d ├── debugline64.d └── package.d ├── package.d ├── stringtable.d └── symboltable.d /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | .dub 3 | *.sublime* 4 | bin/ 5 | -------------------------------------------------------------------------------- /LICENSE_1_0.txt: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | elf-d 2 | ------------ 3 | 4 | Reads 32-bit and 64-bit elf binary files. 5 | 6 | Features 7 | ------------ 8 | 9 | - Read general elf file properties like file class, abi version, machine isa, ... 10 | - Parse elf sections. 11 | - Read elf symbol tables and string tables. 12 | - Read DWARF line program tables and produce address info (.debug_line section). 13 | 14 | How to run example 15 | ------------ 16 | 17 | Run `dub run elf-d:example` in the parent directory. (Note: running the example produces a lot of output to stdout) 18 | 19 | 20 | TODOs 21 | ------------ 22 | 23 | - Fix endianness issue (currently only native endianness is supported). 24 | - Add interpretation for more sections. 25 | 26 | License 27 | ------------ 28 | 29 | Licensed under Boost. Check accompanying file LICENSE_1_0.txt or copy at 30 | http://www.boost.org/LICENSE_1_0.txt 31 | 32 | -------------------------------------------------------------------------------- /dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "elf-d", 3 | "description": "A library to introspect elf binaries", 4 | "copyright": "Copyright © 2014, Yazan Dabain", 5 | "authors": ["Yazan Dabain"], 6 | "license": "BSL 1.0", 7 | 8 | "targetType": "staticLibrary", 9 | "targetPath": "bin", 10 | 11 | "sourcePaths": ["source"], 12 | 13 | "subPackages": [ 14 | "example" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /example/app.d: -------------------------------------------------------------------------------- 1 | import std.stdio; 2 | import std.string; 3 | import std.algorithm, std.range; 4 | import elf; 5 | 6 | void main() { 7 | import std.file: thisExePath; 8 | auto file = thisExePath(); 9 | 10 | ELF elf = ELF.fromFile(file); 11 | 12 | writeln("File: ", file); 13 | 14 | writeln(); 15 | writeln("ELF file properties:"); 16 | 17 | // ELF file general properties 18 | writeln(" fileClass: ", elf.header.identifier.fileClass); 19 | writeln(" dataEncoding: ", elf.header.identifier.dataEncoding); 20 | writeln(" abiVersion: ", elf.header.identifier.abiVersion); 21 | writeln(" osABI: ", elf.header.identifier.osABI); 22 | writeln(" objectFileType: ", elf.header.objectFileType); 23 | writeln(" machineISA: ", elf.header.machineISA); 24 | writeln(" version_: ", elf.header.version_); 25 | writefln(" entryPoint: 0x%x", elf.header.entryPoint); 26 | writeln(" programHeaderOffset: ", elf.header.programHeaderOffset); 27 | writeln(" sectionHeaderOffset: ", elf.header.sectionHeaderOffset); 28 | writeln(" sizeOfProgramHeaderEntry: ", elf.header.sizeOfProgramHeaderEntry); 29 | writeln(" numberOfProgramHeaderEntries: ", elf.header.numberOfProgramHeaderEntries); 30 | writeln(" sizeOfSectionHeaderEntry: ", elf.header.sizeOfSectionHeaderEntry); 31 | writeln(" numberOfSectionHeaderEntries: ", elf.header.numberOfSectionHeaderEntries); 32 | 33 | writeln(); 34 | writeln("Sections:"); 35 | 36 | // ELF sections 37 | foreach (section; elf.sections) { 38 | writeln(" Section (", section.name, ")"); 39 | writefln(" type: %s", section.type); 40 | writefln(" address: 0x%x", section.address); 41 | writefln(" offset: 0x%x", section.offset); 42 | writefln(" flags: 0x%08b", section.flags); 43 | writefln(" size: %s bytes", section.size); 44 | writefln(" entry size: %s bytes", section.entrySize); 45 | writeln(); 46 | } 47 | 48 | printDebugAbbrev(elf); 49 | //printDebugLine(elf); 50 | //printSymbolTables(elf); 51 | } 52 | 53 | void printDebugAbbrev(ELF elf) { 54 | writeln(); 55 | writeln("'.debug_abbrev' section contents:"); 56 | 57 | // ELF .debug_abbrev information 58 | ELFSection dlSection = elf.getSection(".debug_abbrev").get; 59 | 60 | auto da = DebugAbbrev(dlSection); 61 | foreach (tag; da.tags) { 62 | writefln("Tag (0x%x):", tag.code); 63 | writefln(" name: %s", tag.name); 64 | writefln(" has children: %s", tag.hasChildren); 65 | writefln(" attributes:"); 66 | foreach (attr; tag.attributes) { 67 | writefln(" %s\t%s", attr.name, attr.form); 68 | } 69 | writeln(); 70 | } 71 | } 72 | 73 | void printDebugLine(ELF elf) { 74 | writeln(); 75 | writeln("'.debug_line' section contents:"); 76 | 77 | // ELF .debug_line information 78 | ELFSection dlSection = elf.getSection(".debug_line").get; 79 | 80 | auto dl = DebugLine(dlSection); 81 | foreach (program; dl.programs) { 82 | writefln(" Files:\n%-( %s\n%)\n", program.allFiles()); 83 | writefln("%-( %s\n%)", program.addressInfo.map!(a => "0x%x => %s@%s".format(a.address, program.fileFromIndex(a.fileIndex), a.line))); 84 | } 85 | } 86 | 87 | void printSymbolTables(ELF elf) { 88 | writeln(); 89 | writeln("Symbol table sections contents:"); 90 | 91 | foreach (section; only(".symtab", ".dynsym")) { 92 | ELFSection s = elf.getSection(section).get; 93 | writeln(" Symbol table ", section, " contains: ", SymbolTable(s).symbols().walkLength()); 94 | 95 | writefln("%-( %s\n%)", SymbolTable(s).symbols().map!(s => "%s\t%s\t%s".format(s.binding, s.type, s.name))); 96 | writeln(); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /example/dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "description": "A library to introspect elf binaries", 4 | "copyright": "Copyright © 2014, Yazan Dabain", 5 | "authors": ["Yazan Dabain"], 6 | "license": "BSL 1.0", 7 | 8 | "targetName": "example", 9 | "targetType": "executable", 10 | "targetPath": "../bin", 11 | 12 | "sourceFiles": ["app.d"], 13 | "mainSourceFile": "app.d", 14 | 15 | "dependencies": { 16 | "elf-d": {"path": "../", "version": "~master"} 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /example/dub.selections.json: -------------------------------------------------------------------------------- 1 | { 2 | "fileVersion": 1, 3 | "versions": {} 4 | } -------------------------------------------------------------------------------- /source/elf/low.d: -------------------------------------------------------------------------------- 1 | // Copyright Yazan Dabain 2014. 2 | // Distributed under the Boost Software License, Version 1.0. 3 | // (See accompanying file LICENSE_1_0.txt or copy at 4 | // http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | module elf.low; 7 | 8 | import elf.low32, elf.low64; 9 | import elf.meta; 10 | 11 | import std.conv : to; 12 | 13 | alias ELF_Half = ELF64_Half; 14 | alias ELF_Word = ELF64_Word; 15 | alias ELF_SWord = ELF64_SWord; 16 | alias ELF_XWord = ELF64_XWord; 17 | alias ELF_Addr = ELF64_Addr; 18 | alias ELF_Off = ELF64_Off; 19 | alias ELF_Section = ELF64_Section; 20 | 21 | align(1) struct ELFIdent { 22 | align(1): 23 | char mag0; 24 | char mag1; 25 | char mag2; 26 | char mag3; 27 | ubyte class_; 28 | ubyte data; 29 | ubyte version_; 30 | ubyte osabi; 31 | ubyte abiversion; 32 | ubyte[6] pad; 33 | ubyte nident; 34 | } 35 | 36 | static assert(ELFIdent.sizeof == 16); 37 | 38 | struct Identifier { 39 | ELFIdent data; 40 | 41 | FileClass fileClass() { 42 | return cast(FileClass) data.class_; 43 | } 44 | 45 | DataEncoding dataEncoding() { 46 | return cast(DataEncoding) data.data; 47 | } 48 | 49 | OSABI osABI() { 50 | return cast(OSABI) data.osabi; 51 | } 52 | 53 | ubyte abiVersion() { 54 | return data.abiversion; 55 | } 56 | } 57 | 58 | enum FileClass : ubyte { 59 | class32 = 1, class64 = 2, 60 | } 61 | 62 | enum DataEncoding : ubyte { 63 | littleEndian = 1, bigEndian = 2, 64 | } 65 | 66 | enum OSABI : ubyte { 67 | sysv = 0x00, 68 | hpux = 0x01, 69 | netBSD = 0x02, 70 | linux = 0x03, 71 | solaris = 0x06, 72 | aix = 0x07, 73 | irix = 0x08, 74 | freeBSD = 0x09, 75 | openBSD = 0x0C, 76 | standalone = 0xFF, 77 | } 78 | 79 | enum ObjectFileType : ELF_Half { 80 | none = 0x0000, 81 | relocatable = 0x0001, 82 | executable = 0x0002, 83 | shared_ = 0x0003, 84 | core = 0x0004, 85 | lowOS = 0xFE00, 86 | highOS = 0xFEFF, 87 | lowProccessor = 0xFF00, 88 | highProcessor = 0xFFFF, 89 | } 90 | 91 | enum SectionType : ELF_Word { 92 | null_ = 0x0000_0000, 93 | programBits = 0x0000_0001, 94 | symbolTable = 0x0000_0002, 95 | stringTable = 0x0000_0003, 96 | relocation = 0x0000_0004, 97 | symbolHashTable = 0x0000_0005, 98 | dynamicLinkingTable = 0x0000_0006, 99 | note = 0x0000_0007, 100 | noBits = 0x0000_0008, 101 | rel = 0x0000_0009, 102 | shlib = 0x0000_000A, 103 | dynamicLoaderSymbolTable = 0x0000_000B, 104 | lowOS = 0x6000_0000, 105 | highOS = 0x6FFF_FFFF, 106 | lowProcessor = 0x7000_0000, 107 | highProcessor = 0x7FFF_FFFF, 108 | } 109 | 110 | // TODO: Review this 111 | enum SectionFlag : ELF64_XWord { 112 | write = 0x0000_0001, 113 | alloc = 0x0000_0002, 114 | executable = 0x0000_0004, 115 | maskOS = 0x0F00_0000, 116 | maskProcessor = 0xF000_0000, 117 | } 118 | 119 | enum TargetISA : ELF_Word { 120 | sparc = 0x02, 121 | x86 = 0x03, 122 | mips = 0x08, 123 | powerpc = 0x14, 124 | arm = 0x28, 125 | superh = 0x2A, 126 | ia64 = 0x32, 127 | x86_64 = 0x3E, 128 | aarch64 = 0xB7, 129 | } 130 | -------------------------------------------------------------------------------- /source/elf/low32.d: -------------------------------------------------------------------------------- 1 | // Copyright Yazan Dabain 2014. 2 | // Distributed under the Boost Software License, Version 1.0. 3 | // (See accompanying file LICENSE_1_0.txt or copy at 4 | // http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | module elf.low32; 7 | import elf.low; 8 | 9 | package: 10 | 11 | alias ELF32_Addr = uint; 12 | alias ELF32_Off = uint; 13 | alias ELF32_Half = ushort; 14 | alias ELF32_Word = uint; 15 | alias ELF32_SWord = int; 16 | 17 | alias ELF32_Section = ushort; 18 | 19 | align(1) struct ELFHeader32L { 20 | align(1): 21 | ELFIdent ident; 22 | ELF32_Half type; 23 | ELF32_Half machine; 24 | ELF32_Word version_; 25 | ELF32_Addr entry; 26 | ELF32_Off phoff; 27 | ELF32_Off shoff; 28 | ELF32_Word flags; 29 | ELF32_Half ehsize; 30 | ELF32_Half phentsize; 31 | ELF32_Half phnum; 32 | ELF32_Half shentsize; 33 | ELF32_Half shnum; 34 | ELF32_Half shstrndx; 35 | } 36 | 37 | align(1) struct ELFSection32L { 38 | align(1): 39 | ELF32_Word name; 40 | ELF32_Word type; 41 | ELF32_Word flags; 42 | ELF32_Addr address; 43 | ELF32_Off offset; 44 | ELF32_Word size; 45 | ELF32_Word link; 46 | ELF32_Word info; 47 | ELF32_Word addralign; 48 | ELF32_Word entsize; 49 | } 50 | 51 | align(1) struct ELFSymbol32L { 52 | align(1): 53 | ELF32_Word name; 54 | ELF32_Addr value; 55 | ELF32_Word size; 56 | ubyte info; 57 | ubyte other; 58 | ELF32_Section shndx; 59 | } 60 | -------------------------------------------------------------------------------- /source/elf/low64.d: -------------------------------------------------------------------------------- 1 | // Copyright Yazan Dabain 2014. 2 | // Distributed under the Boost Software License, Version 1.0. 3 | // (See accompanying file LICENSE_1_0.txt or copy at 4 | // http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | module elf.low64; 7 | import elf.low; 8 | 9 | package: 10 | 11 | alias ELF64_Addr = ulong; 12 | alias ELF64_Off = ulong; 13 | alias ELF64_Half = ushort; 14 | alias ELF64_Word = uint; 15 | alias ELF64_SWord = int; 16 | alias ELF64_XWord = ulong; 17 | alias ELF64_SXword = long; 18 | 19 | alias ELF64_Section = ushort; 20 | 21 | align(1) struct ELFHeader64L { 22 | align(1): 23 | ELFIdent ident; 24 | ELF64_Half type; 25 | ELF64_Half machine; 26 | ELF64_Word version_; 27 | ELF64_Addr entry; 28 | ELF64_Off phoff; 29 | ELF64_Off shoff; 30 | ELF64_Word flags; 31 | ELF64_Half ehsize; 32 | ELF64_Half phentsize; 33 | ELF64_Half phnum; 34 | ELF64_Half shentsize; 35 | ELF64_Half shnum; 36 | ELF64_Half shstrndx; 37 | } 38 | 39 | align(1) struct ELFSection64L { 40 | align(1): 41 | ELF64_Word name; 42 | ELF64_Word type; 43 | ELF64_XWord flags; 44 | ELF64_Addr address; 45 | ELF64_Off offset; 46 | ELF64_XWord size; 47 | ELF64_Word link; 48 | ELF64_Word info; 49 | ELF64_XWord addralign; 50 | ELF64_XWord entsize; 51 | } 52 | 53 | align(1) struct ELFSymbol64L { 54 | align(1): 55 | ELF64_Word name; 56 | ubyte info; 57 | ubyte other; 58 | ELF64_Half shndx; 59 | ELF64_Addr value; 60 | ELF64_XWord size; 61 | } 62 | -------------------------------------------------------------------------------- /source/elf/meta.d: -------------------------------------------------------------------------------- 1 | // Copyright Yazan Dabain 2014. 2 | // Distributed under the Boost Software License, Version 1.0. 3 | // (See accompanying file LICENSE_1_0.txt or copy at 4 | // http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | module elf.meta; 7 | 8 | package: 9 | 10 | struct ReadFrom { 11 | string name; 12 | } 13 | 14 | string generateVirtualReads(Interface, string dataSource)() { 15 | import std.string; 16 | string output = ""; 17 | 18 | foreach (MemberName; __traits(allMembers, Interface)) { 19 | static if (is(typeof(__traits(getMember, Interface, MemberName)))) { 20 | foreach (Attribute; __traits(getAttributes, __traits(getMember, Interface, MemberName))) { 21 | static if (is(typeof(Attribute) == ReadFrom)) { 22 | enum SourceMemberName = Attribute.name; 23 | alias MemberType = typeof(__traits(getMember, Interface, MemberName)); 24 | 25 | output ~= " @property override %s %s() {\n".format(MemberType.stringof, MemberName); 26 | output ~= " return cast(%s) %s.%s;\n".format(MemberType.stringof, dataSource, SourceMemberName); 27 | output ~= " }\n"; 28 | } 29 | } 30 | } 31 | } 32 | 33 | return output; 34 | } 35 | -------------------------------------------------------------------------------- /source/elf/package.d: -------------------------------------------------------------------------------- 1 | // Copyright Yazan Dabain 2014. 2 | // Distributed under the Boost Software License, Version 1.0. 3 | // (See accompanying file LICENSE_1_0.txt or copy at 4 | // http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | module elf; 7 | 8 | public import elf.sections; 9 | 10 | import elf.low, elf.low32, elf.low64, elf.meta; 11 | 12 | import std.mmfile; 13 | import std.exception; 14 | import std.conv : to; 15 | import std.typecons : Nullable; 16 | 17 | static if (__VERSION__ >= 2079) 18 | alias elfEnforce = enforce!ELFException; 19 | else 20 | alias elfEnforce = enforceEx!ELFException; 21 | 22 | abstract class ELF { 23 | MmFile m_file; 24 | 25 | private this(MmFile file) { 26 | this.m_file = file; 27 | } 28 | 29 | static ELF fromFile(string filepath) { 30 | MmFile file = new MmFile(filepath); 31 | return ELF.fromFile(file); 32 | } 33 | 34 | static ELF fromFile(MmFile file) { 35 | elfEnforce(file.length > 16); 36 | elfEnforce(file[0 .. 4] == ['\x7f', 'E', 'L', 'F']); 37 | bool is32Bit = (file[4] == 1); 38 | bool is64Bit = (file[4] == 2); 39 | 40 | if (is32Bit) { 41 | return new ELF32(file); 42 | } else if (is64Bit) { 43 | return new ELF64(file); 44 | } else { 45 | throw new ELFException("invalid elf file class"); 46 | } 47 | } 48 | 49 | @property ELFHeader header(); 50 | ELFSection buildSection(ubyte[] section); 51 | 52 | @property auto sections() { 53 | struct Sections { 54 | private size_t m_currentIndex = 0; 55 | ELF m_elf; 56 | 57 | this(ELF elf) { this.m_elf = elf; } 58 | 59 | @property bool empty() { return m_currentIndex >= m_elf.header.numberOfSectionHeaderEntries; } 60 | @property size_t length() { return m_elf.header.numberOfSectionHeaderEntries - m_currentIndex; } 61 | 62 | @property ELFSection front() { 63 | elfEnforce(!empty, "out of bounds exception"); 64 | return this[m_currentIndex]; 65 | } 66 | 67 | void popFront() { 68 | elfEnforce(!empty, "out of bounds exception"); 69 | this.m_currentIndex++; 70 | } 71 | 72 | @property typeof(this) save() { 73 | return this; 74 | } 75 | 76 | ELFSection opIndex(size_t index) { 77 | elfEnforce(index < m_elf.header.numberOfSectionHeaderEntries, "out of bounds exception"); 78 | auto sectionStart = m_elf.header.sectionHeaderOffset + index * m_elf.header.sizeOfSectionHeaderEntry; 79 | auto section = m_elf.m_file[sectionStart .. sectionStart + m_elf.header.sizeOfSectionHeaderEntry]; 80 | return this.m_elf.buildSection(cast(ubyte[]) section); 81 | } 82 | } 83 | return Sections(this); 84 | } 85 | 86 | // linear lookup 87 | Nullable!ELFSection getSection(string name) { 88 | foreach (section; this.sections) { 89 | if (section.name == name) return Nullable!ELFSection(section); 90 | } 91 | return Nullable!ELFSection(); 92 | } 93 | 94 | StringTable getSectionNamesStringTable() { 95 | ELFSection section = this.sections[this.header.sectionHeaderStringTableIndex]; 96 | return StringTable(section); 97 | } 98 | 99 | Nullable!StringTable getSymbolsStringTable() { 100 | Nullable!ELFSection section = this.getSection(".strtab"); 101 | if (section.isNull) return Nullable!StringTable(); 102 | else return Nullable!StringTable(StringTable(section.get())); 103 | } 104 | } 105 | 106 | final class ELF64 : ELF { 107 | ELFHeader64 m_header; 108 | 109 | this(MmFile file) { 110 | super(file); 111 | 112 | ELFHeader64L headerData = *(cast(ELFHeader64L*) file[0 .. ELFHeader64L.sizeof].ptr); 113 | this.m_header = new ELFHeader64(headerData); 114 | } 115 | 116 | override @property ELFHeader header() { 117 | return this.m_header; 118 | } 119 | 120 | override ELFSection64 buildSection(ubyte[] sectionData) { 121 | elfEnforce(sectionData.length == ELFSection64L.sizeof); 122 | ELFSection64L sectionRep = *(cast(ELFSection64L*) sectionData.ptr); 123 | ELFSection64 section = new ELFSection64(this, sectionRep); 124 | section.m_elf = this; 125 | return section; 126 | } 127 | } 128 | 129 | final class ELF32 : ELF { 130 | ELFHeader32 m_header; 131 | 132 | this(MmFile file) { 133 | super(file); 134 | 135 | ELFHeader32L headerData = *(cast(ELFHeader32L*) file[0 .. ELFHeader32L.sizeof].ptr); 136 | this.m_header = new ELFHeader32(headerData); 137 | } 138 | 139 | override @property ELFHeader header() { 140 | return this.m_header; 141 | } 142 | 143 | override ELFSection32 buildSection(ubyte[] sectionData) { 144 | elfEnforce(sectionData.length == ELFSection32L.sizeof); 145 | ELFSection32L sectionRep = *(cast(ELFSection32L*) sectionData.ptr); 146 | ELFSection32 section = new ELFSection32(this, sectionRep); 147 | return section; 148 | } 149 | } 150 | 151 | abstract class ELFHeader { 152 | @property: 153 | @ReadFrom("ident") Identifier identifier(); 154 | @ReadFrom("type") ObjectFileType objectFileType(); 155 | @ReadFrom("machine") TargetISA machineISA(); 156 | @ReadFrom("version_") ELF_Word version_(); 157 | @ReadFrom("entry") ELF_Addr entryPoint(); 158 | @ReadFrom("phoff") ELF_Off programHeaderOffset(); 159 | @ReadFrom("shoff") ELF_Off sectionHeaderOffset(); 160 | @ReadFrom("phentsize") ELF_Half sizeOfProgramHeaderEntry(); 161 | @ReadFrom("phnum") ELF_Half numberOfProgramHeaderEntries(); 162 | @ReadFrom("shentsize") ELF_Half sizeOfSectionHeaderEntry(); 163 | @ReadFrom("shnum") ELF_Half numberOfSectionHeaderEntries(); 164 | @ReadFrom("shstrndx") ELF_Half sectionHeaderStringTableIndex(); 165 | } 166 | 167 | final class ELFHeader32 : ELFHeader { 168 | private ELFHeader32L m_data; 169 | mixin(generateVirtualReads!(ELFHeader, "m_data")); 170 | 171 | this(ELFHeader32L data) { 172 | this.m_data = data; 173 | } 174 | } 175 | 176 | final class ELFHeader64 : ELFHeader { 177 | private ELFHeader64L m_data; 178 | mixin(generateVirtualReads!(ELFHeader, "m_data")); 179 | 180 | this(ELFHeader64L data) { 181 | this.m_data = data; 182 | } 183 | } 184 | 185 | abstract class ELFSection { 186 | package ELF m_elf; 187 | 188 | @property: 189 | @ReadFrom("name") ELF_Word nameIndex(); 190 | @ReadFrom("type") SectionType type(); 191 | @ReadFrom("flags") SectionFlag flags(); 192 | @ReadFrom("address") ELF_Addr address(); 193 | @ReadFrom("offset") ELF_Off offset(); 194 | @ReadFrom("size") ELF_XWord size(); 195 | @ReadFrom("link") ELF_Word link(); 196 | @ReadFrom("info") ELF_Word info(); 197 | @ReadFrom("addralign") ELF_XWord addrAlign(); 198 | @ReadFrom("entsize") ELF_XWord entrySize(); 199 | 200 | ubyte bits(); 201 | 202 | auto name() { 203 | return m_elf.getSectionNamesStringTable().getStringAt(this.nameIndex()); 204 | } 205 | 206 | auto contents() { 207 | return cast(ubyte[]) m_elf.m_file[offset() .. offset() + size()]; 208 | } 209 | } 210 | 211 | final class ELFSection32 : ELFSection { 212 | private ELFSection32L m_data; 213 | mixin(generateVirtualReads!(ELFSection, "m_data")); 214 | 215 | this(ELF32 elf, ELFSection32L data) { 216 | this.m_elf = elf; 217 | this.m_data = data; 218 | } 219 | 220 | override @property ubyte bits() { 221 | return 32; 222 | } 223 | } 224 | 225 | final class ELFSection64 : ELFSection { 226 | private ELFSection64L m_data; 227 | mixin(generateVirtualReads!(ELFSection, "m_data")); 228 | 229 | this(ELF64 elf, ELFSection64L data) { 230 | this.m_elf = elf; 231 | this.m_data = data; 232 | } 233 | 234 | override @property ubyte bits() { 235 | return 64; 236 | } 237 | } 238 | 239 | class ELFException : Exception { 240 | this(string msg, string file = __FILE__, size_t line = __LINE__) { 241 | super(msg, file, line); 242 | } 243 | } 244 | -------------------------------------------------------------------------------- /source/elf/sections/debugabbrev/package.d: -------------------------------------------------------------------------------- 1 | // Copyright Yazan Dabain 2014. 2 | // Distributed under the Boost Software License, Version 1.0. 3 | // (See accompanying file LICENSE_1_0.txt or copy at 4 | // http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | module elf.sections.debugabbrev; 7 | 8 | // this implementation follows the DWARF v3 documentation 9 | 10 | import std.exception; 11 | import std.range; 12 | import std.conv : to; 13 | import elf, elf.meta; 14 | 15 | static if (__VERSION__ >= 2079) 16 | alias elfEnforce = enforce!ELFException; 17 | else 18 | alias elfEnforce = enforceEx!ELFException; 19 | 20 | alias ULEB128 = ulong; 21 | alias LEB128 = long; 22 | 23 | struct Tag { 24 | ULEB128 code; 25 | TagEncoding name; 26 | bool hasChildren; 27 | Attribute[] attributes; 28 | } 29 | 30 | struct Attribute { 31 | AttributeName name; 32 | AttributeForm form; 33 | } 34 | 35 | struct DebugAbbrev { 36 | private Tag[ULEB128] m_tags; // key is tag code 37 | 38 | this(ELFSection section) { 39 | ubyte[] contents = section.contents(); 40 | 41 | while (true) { 42 | // read tag 43 | Tag tag; 44 | tag.code = contents.readULEB128(); 45 | if (tag.code == 0) break; // read all tags 46 | tag.name = cast(TagEncoding) contents.readULEB128(); 47 | tag.hasChildren = contents.read!bool(); 48 | 49 | while (true) { 50 | // read attributes 51 | Attribute attr; 52 | attr.name = cast(AttributeName) contents.readULEB128(); 53 | attr.form = cast(AttributeForm) contents.readULEB128(); 54 | 55 | if (attr.name == 0 && attr.form == 0) break; 56 | tag.attributes ~= attr; 57 | } 58 | 59 | m_tags[tag.code] = tag; 60 | } 61 | } 62 | 63 | @property const(Tag[ULEB128]) tags() const { return m_tags; } 64 | } 65 | 66 | private T read(T)(ref ubyte[] buffer) { 67 | T result = *(cast(T*) buffer[0 .. T.sizeof].ptr); 68 | buffer.popFrontExactly(T.sizeof); 69 | return result; 70 | } 71 | 72 | private ulong readULEB128(ref ubyte[] buffer) { 73 | import std.array; 74 | ulong val = 0; 75 | ubyte b; 76 | uint shift = 0; 77 | 78 | while (true) { 79 | b = buffer.read!ubyte(); 80 | 81 | val |= (b & 0x7f) << shift; 82 | if ((b & 0x80) == 0) break; 83 | shift += 7; 84 | } 85 | 86 | return val; 87 | } 88 | 89 | unittest { 90 | ubyte[] data = [0xe5, 0x8e, 0x26, 0xDE, 0xAD, 0xBE, 0xEF]; 91 | assert(readULEB128(data) == 624_485); 92 | assert(data[] == [0xDE, 0xAD, 0xBE, 0xEF]); 93 | } 94 | 95 | private long readSLEB128(ref ubyte[] buffer) { 96 | import std.array; 97 | long val = 0; 98 | uint shift = 0; 99 | ubyte b; 100 | int size = 8 << 3; 101 | 102 | while (true) { 103 | b = buffer.read!ubyte(); 104 | val |= (b & 0x7f) << shift; 105 | shift += 7; 106 | if ((b & 0x80) == 0) 107 | break; 108 | } 109 | 110 | if (shift < size && (b & 0x40) != 0) val |= -(1 << shift); 111 | return val; 112 | } 113 | 114 | enum TagEncoding : ULEB128 { 115 | arrayType = 0x01, 116 | classType = 0x02, 117 | entryPoint = 0x03, 118 | enumerationType = 0x04, 119 | formalParameter = 0x05, 120 | importedDeclaration = 0x08, 121 | label = 0x0a, 122 | lexicalBlock = 0x0b, 123 | member = 0x0d, 124 | pointerType = 0x0f, 125 | referenceType = 0x10, 126 | compileUnit = 0x11, 127 | stringType = 0x12, 128 | structureType = 0x13, 129 | subroutineType = 0x15, 130 | typedef_ = 0x16, 131 | unionType = 0x17, 132 | unspecifiedParameters = 0x18, 133 | variant = 0x19, 134 | commonBlock = 0x1a, 135 | commonInclusion = 0x1b, 136 | inheritance = 0x1c, 137 | inlinedSubroutine = 0x1d, 138 | module_ = 0x1e, 139 | ptrToMemberType = 0x1f, 140 | setType = 0x20, 141 | subrangeType = 0x21, 142 | withStmt = 0x22, 143 | accessDeclaration = 0x23, 144 | baseType = 0x24, 145 | catchBlock = 0x25, 146 | constType = 0x26, 147 | constant = 0x27, 148 | enumerator = 0x28, 149 | fileType = 0x29, 150 | friend = 0x2a, 151 | namelist = 0x2b, 152 | namelistItem = 0x2c, 153 | packedType = 0x2d, 154 | subprogram = 0x2e, 155 | templateTypeParameter = 0x2f, 156 | templateValueParameter = 0x30, 157 | thrownType = 0x31, 158 | tryBlock = 0x32, 159 | variantPart = 0x33, 160 | variable = 0x34, 161 | volatileType = 0x35, 162 | 163 | // added in dwarf 3 { 164 | dwarfProcedure = 0x36, 165 | restrictType = 0x37, 166 | interfaceType = 0x38, 167 | namespace = 0x39, 168 | importedModule = 0x3a, 169 | unspecifiedType = 0x3b, 170 | partialUnit = 0x3c, 171 | importedUnit = 0x3d, 172 | condition = 0x3f, 173 | sharedType = 0x40, 174 | // } end in dwarf 3 175 | 176 | loUser = 0x4080, 177 | hiUser = 0xffff, 178 | } 179 | 180 | enum AttributeName : ULEB128 { 181 | sibling = 0x01, 182 | location = 0x02, 183 | name = 0x03, 184 | ordering = 0x09, 185 | byteSize = 0x0b, 186 | bitOffset = 0x0c, 187 | bitSize = 0x0d, 188 | stmtList = 0x10, 189 | lowPC = 0x11, 190 | highPC = 0x12, 191 | language = 0x13, 192 | discr = 0x15, 193 | discrValue = 0x16, 194 | visibility = 0x17, 195 | import_ = 0x18, 196 | stringLength = 0x19, 197 | commonReference = 0x1a, 198 | compDir = 0x1b, 199 | constValue = 0x1c, 200 | containingType = 0x1d, 201 | defaultValue = 0x1e, 202 | inline = 0x20, 203 | isOptional = 0x21, 204 | lowerBound = 0x22, 205 | producer = 0x25, 206 | prototyped = 0x27, 207 | returnAddr = 0x2a, 208 | startScope = 0x2c, 209 | bitStride = 0x2e, 210 | upperBound = 0x2f, 211 | abstractOrigin = 0x31, 212 | accessibility = 0x32, 213 | addressClass = 0x33, 214 | artificial = 0x34, 215 | baseTypes = 0x35, 216 | callingConvention = 0x36, 217 | count = 0x37, 218 | dataMemberLocation = 0x38, 219 | declColumn = 0x39, 220 | declFile = 0x3a, 221 | declLine = 0x3b, 222 | declaration = 0x3c, 223 | discrList = 0x3d, 224 | encoding = 0x3e, 225 | external = 0x3f, 226 | frameBase = 0x40, 227 | friend = 0x41, 228 | identifierCase = 0x42, 229 | macroInfo = 0x43, 230 | namelistItem = 0x44, 231 | priority = 0x45, 232 | segment = 0x46, 233 | specification = 0x47, 234 | staticLink = 0x48, 235 | type = 0x49, 236 | useLocation = 0x4a, 237 | variableParameter = 0x4b, 238 | virtuality = 0x4c, 239 | vtableElemLocation = 0x4d, 240 | 241 | // added in dwarf 3 { 242 | allocated = 0x4e, 243 | associated = 0x4f, 244 | dataLocation = 0x50, 245 | byteStride = 0x51, 246 | entryPc = 0x52, 247 | useUTF8 = 0x53, 248 | extension = 0x54, 249 | ranges = 0x55, 250 | trampoline = 0x56, 251 | callColumn = 0x57, 252 | callFile = 0x58, 253 | callLine = 0x59, 254 | description = 0x5a, 255 | binaryScale = 0x5b, 256 | decimalScale = 0x5c, 257 | small = 0x5d, 258 | decimalSign = 0x5e, 259 | digitCount = 0x5f, 260 | pictureString = 0x60, 261 | mutable = 0x61, 262 | threadsScaled = 0x62, 263 | explicit = 0x63, 264 | objectPointer = 0x64, 265 | endianity = 0x65, 266 | elemental = 0x66, 267 | pure_ = 0x67, 268 | recursive = 0x68, 269 | // } end in dwarf 3 270 | 271 | loUser = 0x2000, 272 | hiUser = 0x3fff, 273 | } 274 | 275 | enum AttributeForm : ULEB128 { 276 | addr = 0x01, 277 | block2 = 0x03, 278 | block4 = 0x04, 279 | data2 = 0x05, 280 | data4 = 0x06, 281 | data8 = 0x07, 282 | string_ = 0x08, 283 | block = 0x09, 284 | block1 = 0x0a, 285 | data1 = 0x0b, 286 | flag = 0x0c, 287 | sdata = 0x0d, 288 | strp = 0x0e, 289 | udata = 0x0f, 290 | refAddr = 0x10, 291 | ref1 = 0x11, 292 | ref2 = 0x12, 293 | ref4 = 0x13, 294 | ref8 = 0x14, 295 | refUdata = 0x15, 296 | indirect = 0x16, 297 | } 298 | -------------------------------------------------------------------------------- /source/elf/sections/debugline/debugline32.d: -------------------------------------------------------------------------------- 1 | // Copyright Yazan Dabain 2014. 2 | // Distributed under the Boost Software License, Version 1.0. 3 | // (See accompanying file LICENSE_1_0.txt or copy at 4 | // http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | module elf.sections.debugline.debugline32; 7 | 8 | align(1) struct LineProgramHeader32L { 9 | align(1): 10 | uint unitLength; 11 | ushort dwarfVersion; 12 | uint headerLength; 13 | ubyte minimumInstructionLength; 14 | bool defaultIsStatement; 15 | byte lineBase; 16 | ubyte lineRange; 17 | ubyte opcodeBase; 18 | } 19 | -------------------------------------------------------------------------------- /source/elf/sections/debugline/debugline64.d: -------------------------------------------------------------------------------- 1 | // Copyright Yazan Dabain 2014. 2 | // Distributed under the Boost Software License, Version 1.0. 3 | // (See accompanying file LICENSE_1_0.txt or copy at 4 | // http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | module elf.sections.debugline.debugline64; 7 | 8 | align(1) struct LineProgramHeader64L { 9 | align(1): 10 | uint unitLength_; 11 | ulong unitLength; 12 | ushort dwarfVersion; 13 | ulong headerLength; 14 | ubyte minimumInstructionLength; 15 | bool defaultIsStatement; 16 | byte lineBase; 17 | ubyte lineRange; 18 | ubyte opcodeBase; 19 | } 20 | -------------------------------------------------------------------------------- /source/elf/sections/debugline/package.d: -------------------------------------------------------------------------------- 1 | // Copyright Yazan Dabain 2014. 2 | // Distributed under the Boost Software License, Version 1.0. 3 | // (See accompanying file LICENSE_1_0.txt or copy at 4 | // http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | module elf.sections.debugline; 7 | 8 | // this implementation follows the DWARF v3 documentation 9 | 10 | import std.exception; 11 | import std.range; 12 | import std.conv : to; 13 | import elf, elf.meta; 14 | 15 | private import elf.sections.debugline.debugline32, elf.sections.debugline.debugline64; 16 | 17 | static if (__VERSION__ >= 2079) 18 | alias elfEnforce = enforce!ELFException; 19 | else 20 | alias elfEnforce = enforceEx!ELFException; 21 | 22 | struct DebugLine { 23 | private LineProgram[] m_lps; 24 | 25 | private enum uint DWARF_64BIT_FLAG = 0xffff_ffff; 26 | 27 | this(ELFSection section) { 28 | this.m_lps = new LineProgram[0]; 29 | 30 | ubyte[] lineProgramContents = section.contents(); 31 | 32 | while (!lineProgramContents.empty) { 33 | LineProgram lp; 34 | 35 | // detect dwarf 32bit or 64bit 36 | uint initialLength = * cast(uint*) lineProgramContents.ptr; 37 | if (initialLength == DWARF_64BIT_FLAG) { 38 | LineProgramHeader64L data = * cast(LineProgramHeader64L*) lineProgramContents.ptr; 39 | lp.m_header = new LineProgramHeader64(data); 40 | } else { 41 | LineProgramHeader32L data = * cast(LineProgramHeader32L*) lineProgramContents.ptr; 42 | lp.m_header = new LineProgramHeader32(data); 43 | } 44 | 45 | // start reading sections 46 | lp.m_standardOpcodeLengths = new ubyte[lp.m_header.opcodeBase - 1]; 47 | foreach (i; 0 .. lp.m_standardOpcodeLengths.length) { 48 | lp.m_standardOpcodeLengths[i] = lineProgramContents[lp.m_header.datasize + i .. lp.m_header.datasize + i + 1][0]; 49 | } 50 | 51 | lp.m_files = new FileInfo[0]; 52 | lp.m_dirs = new string[0]; 53 | 54 | auto pathData = lineProgramContents[lp.m_header.datasize + lp.m_standardOpcodeLengths.length .. $]; 55 | 56 | while (pathData[0] != 0) { 57 | lp.m_dirs ~= (cast(char*) pathData.ptr).to!string(); 58 | pathData = pathData[lp.m_dirs[$ - 1].length + 1 .. $]; 59 | } 60 | 61 | pathData.popFront(); 62 | 63 | while (pathData[0] != 0) { 64 | string file = (cast(char*) pathData.ptr).to!string(); 65 | pathData = pathData[file.length + 1 .. $]; 66 | 67 | auto dirIndex = pathData.readULEB128(); 68 | auto lastMod = pathData.readULEB128(); // unused 69 | auto fileLength = pathData.readULEB128(); // unused 70 | 71 | lp.m_files ~= FileInfo(file, dirIndex); 72 | } 73 | 74 | static if (__VERSION__ < 2065) { // bug workaround for older versions 75 | auto startOffset = lp.m_header.is32bit() ? uint.sizeof * 2 + ushort.sizeof : uint.sizeof + 2 * ulong.sizeof + ushort.sizeof; 76 | auto endOffset = lp.m_header.is32bit() ? uint.sizeof : uint.sizeof + ulong.sizeof; 77 | } else { 78 | auto startOffset = lp.m_header.bits == 32 ? LineProgramHeader32L.minimumInstructionLength.offsetof : LineProgramHeader64L.minimumInstructionLength.offsetof; 79 | auto endOffset = lp.m_header.bits == 32 ? LineProgramHeader32L.unitLength.sizeof : LineProgramHeader64L.unitLength.offsetof + LineProgramHeader64L.unitLength.sizeof; 80 | } 81 | 82 | auto program = lineProgramContents[startOffset + lp.m_header.headerLength() .. endOffset + lp.m_header.unitLength()]; 83 | 84 | buildMachine(lp, program); 85 | m_lps ~= lp; 86 | 87 | lineProgramContents = lineProgramContents[endOffset + lp.m_header.unitLength() .. $]; 88 | } 89 | 90 | } 91 | 92 | private void buildMachine(ref LineProgram lp, ubyte[] program) { 93 | import std.range; 94 | 95 | Machine m; 96 | m.isStatement = lp.m_header.defaultIsStatement(); 97 | 98 | lp.m_addresses = new AddressInfo[0]; 99 | 100 | // import std.stdio, std.string; 101 | // alias trace = writeln; 102 | 103 | while (!program.empty) { 104 | ubyte opcode = program.read!ubyte(); 105 | 106 | if (opcode < lp.m_header.opcodeBase) { 107 | 108 | switch (opcode) with (StandardOpcode) { 109 | case extendedOp: 110 | ulong len = program.readULEB128(); 111 | ubyte eopcode = program.read!ubyte(); 112 | 113 | switch (eopcode) with (ExtendedOpcode) { 114 | case endSequence: 115 | m.isEndSequence = true; 116 | // trace("endSequence ", "0x%x".format(m.address)); 117 | lp.m_addresses ~= AddressInfo(m.line, m.fileIndex, m.address); 118 | m = Machine.init; 119 | m.isStatement = lp.m_header.defaultIsStatement; 120 | break; 121 | 122 | case setAddress: 123 | ulong address = program.read!ulong(); 124 | // trace("setAddress ", "0x%x".format(address)); 125 | m.address = address; 126 | break; 127 | 128 | case defineFile: 129 | auto file = (cast(char*) program.ptr).to!string(); 130 | program = program[file.length + 1 .. $]; 131 | auto dirIndex = program.readULEB128(); // unused 132 | auto fileMod = program.readULEB128(); // unused 133 | auto fileSize = program.readULEB128(); // unused 134 | // trace("defineFile"); 135 | break; 136 | 137 | default: 138 | // unknown opcode 139 | // trace("unknown extended opcode ", eopcode); 140 | program = program[len - 1 .. $]; 141 | break; 142 | 143 | } 144 | 145 | break; 146 | 147 | case copy: 148 | // trace("copy"); 149 | lp.m_addresses ~= AddressInfo(m.line, m.fileIndex, m.address); 150 | m.isBasicBlock = false; 151 | m.isPrologueEnd = false; 152 | m.isEpilogueBegin = false; 153 | break; 154 | 155 | case advancePC: 156 | ulong op = readULEB128(program); 157 | // trace("advancePC ", op * lp.m_header.minimumInstructionLength); 158 | m.address += op * lp.m_header.minimumInstructionLength; 159 | break; 160 | 161 | case advanceLine: 162 | long ad = readSLEB128(program); 163 | // trace("advanceLine ", ad); 164 | m.line += ad; 165 | break; 166 | 167 | case setFile: 168 | uint index = readULEB128(program).to!uint(); 169 | // trace("setFile to ", index); 170 | m.fileIndex = index; 171 | break; 172 | 173 | case setColumn: 174 | uint col = readULEB128(program).to!uint(); 175 | // trace("setColumn ", col); 176 | m.column = col; 177 | break; 178 | 179 | case negateStatement: 180 | // trace("negateStatement"); 181 | m.isStatement = !m.isStatement; 182 | break; 183 | 184 | case setBasicBlock: 185 | // trace("setBasicBlock"); 186 | m.isBasicBlock = true; 187 | break; 188 | 189 | case constAddPC: 190 | m.address += (255 - lp.m_header.opcodeBase) / lp.m_header.lineRange * lp.m_header.minimumInstructionLength; 191 | // trace("constAddPC ", "0x%x".format(m.address)); 192 | break; 193 | 194 | case fixedAdvancePC: 195 | uint add = program.read!uint(); 196 | // trace("fixedAdvancePC ", add); 197 | m.address += add; 198 | break; 199 | 200 | case setPrologueEnd: 201 | m.isPrologueEnd = true; 202 | // trace("setPrologueEnd"); 203 | break; 204 | 205 | case setEpilogueBegin: 206 | m.isEpilogueBegin = true; 207 | // trace("setEpilogueBegin"); 208 | break; 209 | 210 | case setISA: 211 | m.isa = readULEB128(program).to!uint(); 212 | // trace("setISA ", m.isa); 213 | break; 214 | 215 | default: 216 | throw new ELFException("unimplemented/invalid opcode " ~ opcode.to!string); 217 | } 218 | 219 | } else { 220 | opcode -= lp.m_header.opcodeBase; 221 | auto ainc = (opcode / lp.m_header.lineRange) * lp.m_header.minimumInstructionLength; 222 | m.address += ainc; 223 | auto linc = lp.m_header.lineBase + (opcode % lp.m_header.lineRange); 224 | m.line += linc; 225 | 226 | // trace("special ", ainc, " ", linc); 227 | lp.m_addresses ~= AddressInfo(m.line, m.fileIndex, m.address); 228 | } 229 | } 230 | } 231 | 232 | const(LineProgram)[] programs() const { 233 | return m_lps; 234 | } 235 | } 236 | 237 | abstract class LineProgramHeader { 238 | @property: 239 | @ReadFrom("unitLength") ulong unitLength(); 240 | @ReadFrom("dwarfVersion") ushort dwarfVersion(); 241 | @ReadFrom("headerLength") ulong headerLength(); 242 | @ReadFrom("minimumInstructionLength") ubyte minimumInstructionLength(); 243 | @ReadFrom("defaultIsStatement") bool defaultIsStatement(); 244 | @ReadFrom("lineBase") byte lineBase(); 245 | @ReadFrom("lineRange") ubyte lineRange(); 246 | @ReadFrom("opcodeBase") ubyte opcodeBase(); 247 | 248 | size_t datasize(); 249 | ubyte bits(); 250 | } 251 | 252 | final class LineProgramHeader32 : LineProgramHeader { 253 | private LineProgramHeader32L m_data; 254 | mixin(generateVirtualReads!(LineProgramHeader, "m_data")); 255 | 256 | this(LineProgramHeader32L lph) { 257 | this.m_data = lph; 258 | } 259 | 260 | @property override size_t datasize() { 261 | return LineProgramHeader32L.sizeof; 262 | } 263 | 264 | @property override ubyte bits() { 265 | return 32; 266 | } 267 | } 268 | 269 | final class LineProgramHeader64 : LineProgramHeader { 270 | private LineProgramHeader64L m_data; 271 | mixin(generateVirtualReads!(LineProgramHeader, "m_data")); 272 | 273 | this(LineProgramHeader64L lph) { 274 | this.m_data = lph; 275 | } 276 | 277 | @property override size_t datasize() { 278 | return LineProgramHeader64L.sizeof; 279 | } 280 | 281 | @property override ubyte bits() { 282 | return 64; 283 | } 284 | } 285 | 286 | private T read(T)(ref ubyte[] buffer) { 287 | T result = *(cast(T*) buffer[0 .. T.sizeof].ptr); 288 | buffer.popFrontExactly(T.sizeof); 289 | return result; 290 | } 291 | 292 | private ulong readULEB128(ref ubyte[] buffer) { 293 | import std.array; 294 | ulong val = 0; 295 | ubyte b; 296 | uint shift = 0; 297 | 298 | while (true) { 299 | b = buffer.read!ubyte(); 300 | 301 | val |= (b & 0x7f) << shift; 302 | if ((b & 0x80) == 0) break; 303 | shift += 7; 304 | } 305 | 306 | return val; 307 | } 308 | 309 | unittest { 310 | ubyte[] data = [0xe5, 0x8e, 0x26, 0xDE, 0xAD, 0xBE, 0xEF]; 311 | assert(readULEB128(data) == 624_485); 312 | assert(data[] == [0xDE, 0xAD, 0xBE, 0xEF]); 313 | } 314 | 315 | private long readSLEB128(ref ubyte[] buffer) { 316 | import std.array; 317 | long val = 0; 318 | uint shift = 0; 319 | ubyte b; 320 | int size = 8 << 3; 321 | 322 | while (true) { 323 | b = buffer.read!ubyte(); 324 | val |= (b & 0x7f) << shift; 325 | shift += 7; 326 | if ((b & 0x80) == 0) 327 | break; 328 | } 329 | 330 | if (shift < size && (b & 0x40) != 0) val |= -(1 << shift); 331 | return val; 332 | } 333 | 334 | private enum StandardOpcode : ubyte { 335 | extendedOp = 0, 336 | copy = 1, 337 | advancePC = 2, 338 | advanceLine = 3, 339 | setFile = 4, 340 | setColumn = 5, 341 | negateStatement = 6, 342 | setBasicBlock = 7, 343 | constAddPC = 8, 344 | fixedAdvancePC = 9, 345 | setPrologueEnd = 10, 346 | setEpilogueBegin = 11, 347 | setISA = 12, 348 | } 349 | 350 | private enum ExtendedOpcode : ubyte { 351 | endSequence = 1, 352 | setAddress = 2, 353 | defineFile = 3, 354 | } 355 | 356 | private struct Machine { 357 | ulong address = 0; 358 | uint operationIndex = 0; 359 | uint fileIndex = 1; 360 | uint line = 1; 361 | uint column = 0; 362 | bool isStatement; 363 | bool isBasicBlock = false; 364 | bool isEndSequence = false; 365 | bool isPrologueEnd = false; 366 | bool isEpilogueBegin = false; 367 | uint isa = 0; 368 | uint discriminator = 0; 369 | } 370 | 371 | struct LineProgram { 372 | private { 373 | LineProgramHeader m_header; 374 | 375 | ubyte[] m_standardOpcodeLengths; 376 | FileInfo[] m_files; 377 | string[] m_dirs; 378 | 379 | AddressInfo[] m_addresses; 380 | } 381 | 382 | const(AddressInfo)[] addressInfo() const { 383 | return m_addresses; 384 | } 385 | 386 | string fileFromIndex(ulong fileIndex) const { 387 | import std.path : buildPath; 388 | 389 | FileInfo f = m_files[fileIndex - 1]; 390 | if (f.dirIndex == 0) return f.file; 391 | else return buildPath(m_dirs[f.dirIndex - 1], f.file); 392 | } 393 | 394 | string[] allFiles() const { 395 | import std.path : buildPath; 396 | 397 | string[] result; 398 | foreach (file; m_files) 399 | { 400 | if (file.dirIndex == 0) result ~= file.file; 401 | else result ~= buildPath(m_dirs[file.dirIndex - 1], file.file); 402 | } 403 | 404 | return result; 405 | } 406 | } 407 | 408 | struct FileInfo { 409 | string file; 410 | size_t dirIndex; 411 | } 412 | 413 | struct AddressInfo { 414 | ulong line; 415 | ulong fileIndex; 416 | ulong address; 417 | } 418 | -------------------------------------------------------------------------------- /source/elf/sections/package.d: -------------------------------------------------------------------------------- 1 | // Copyright Yazan Dabain 2014. 2 | // Distributed under the Boost Software License, Version 1.0. 3 | // (See accompanying file LICENSE_1_0.txt or copy at 4 | // http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | module elf.sections; 7 | 8 | public import elf.sections.stringtable; 9 | public import elf.sections.symboltable; 10 | public import elf.sections.debugline; 11 | public import elf.sections.debugabbrev; 12 | -------------------------------------------------------------------------------- /source/elf/sections/stringtable.d: -------------------------------------------------------------------------------- 1 | // Copyright Yazan Dabain 2014. 2 | // Distributed under the Boost Software License, Version 1.0. 3 | // (See accompanying file LICENSE_1_0.txt or copy at 4 | // http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | module elf.sections.stringtable; 7 | 8 | import std.exception; 9 | import std.conv : to; 10 | import elf; 11 | 12 | static if (__VERSION__ >= 2079) 13 | alias elfEnforce = enforce!ELFException; 14 | else 15 | alias elfEnforce = enforceEx!ELFException; 16 | 17 | struct StringTable { 18 | private ELFSection m_section; 19 | 20 | this(ELFSection section) { 21 | this.m_section = section; 22 | } 23 | 24 | string getStringAt(size_t index) { 25 | import std.algorithm: countUntil; 26 | 27 | elfEnforce(index < m_section.size); 28 | ptrdiff_t len = m_section.contents[index .. $].countUntil('\0'); 29 | elfEnforce(len >= 0); 30 | 31 | return cast(string) m_section.contents[index .. index + len]; 32 | } 33 | 34 | auto strings() { 35 | static struct Strings { 36 | private ELFSection m_section; 37 | private size_t m_currentIndex = 0; 38 | 39 | @property bool empty() { return m_currentIndex >= m_section.size; } 40 | 41 | @property string front() { 42 | elfEnforce(!empty, "out of bounds exception"); 43 | ptrdiff_t len = frontLength(); 44 | elfEnforce(len >= 0, "invalid data"); 45 | return cast(string) m_section.contents[m_currentIndex .. m_currentIndex + len]; 46 | } 47 | 48 | private auto frontLength() { 49 | import std.algorithm: countUntil; 50 | 51 | ptrdiff_t len = m_section.contents[m_currentIndex .. $].countUntil('\0'); 52 | return len; 53 | } 54 | 55 | void popFront() { 56 | elfEnforce(!empty, "out of bounds exception"); 57 | this.m_currentIndex += frontLength() + 1; 58 | } 59 | 60 | @property typeof(this) save() { 61 | return this; 62 | } 63 | 64 | this(ELFSection section) { 65 | this.m_section = section; 66 | } 67 | } 68 | 69 | return Strings(this.m_section); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /source/elf/sections/symboltable.d: -------------------------------------------------------------------------------- 1 | // Copyright Yazan Dabain 2014. 2 | // Distributed under the Boost Software License, Version 1.0. 3 | // (See accompanying file LICENSE_1_0.txt or copy at 4 | // http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | module elf.sections.symboltable; 7 | 8 | import std.exception; 9 | import std.conv : to; 10 | import elf, elf.low, elf.low32, elf.low64, elf.meta; 11 | 12 | static if (__VERSION__ >= 2079) 13 | alias elfEnforce = enforce!ELFException; 14 | else 15 | alias elfEnforce = enforceEx!ELFException; 16 | 17 | 18 | struct SymbolTable { 19 | private SymbolTableImpl m_impl; 20 | 21 | this(ELFSection section) { 22 | if (section.bits == 32) { 23 | m_impl = new SymbolTable32Impl(section); 24 | } else { 25 | m_impl = new SymbolTable64Impl(section); 26 | } 27 | } 28 | 29 | ELFSymbol getSymbolAt(size_t index) { 30 | return m_impl.getSymbolAt(index); 31 | } 32 | 33 | auto symbols() { 34 | static struct Symbols { 35 | private SymbolTableImpl m_impl; 36 | private size_t m_currentIndex = 0; 37 | 38 | @property bool empty() { return m_currentIndex >= m_impl.length; } 39 | 40 | @property ELFSymbol front() { 41 | elfEnforce(!empty, "out of bounds exception"); 42 | return m_impl.getSymbolAt(m_currentIndex); 43 | } 44 | 45 | void popFront() { 46 | elfEnforce(!empty, "out of bounds exception"); 47 | this.m_currentIndex++; 48 | } 49 | 50 | @property typeof(this) save() { 51 | return this; 52 | } 53 | 54 | this(SymbolTableImpl impl) { 55 | this.m_impl = impl; 56 | } 57 | } 58 | 59 | return Symbols(this.m_impl); 60 | } 61 | } 62 | 63 | private interface SymbolTableImpl { 64 | ELFSymbol getSymbolAt(size_t index); 65 | @property ulong length(); 66 | } 67 | 68 | private class SymbolTable32Impl : SymbolTableImpl { 69 | private ELFSection32 m_section; 70 | 71 | this(ELFSection section) { 72 | elfEnforce(section.bits == 32); 73 | this(cast(ELFSection32) section); 74 | } 75 | 76 | this(ELFSection32 section) { 77 | this.m_section = section; 78 | } 79 | 80 | ELFSymbol getSymbolAt(size_t index) { 81 | elfEnforce(index * ELFSymbol32L.sizeof < m_section.size); 82 | ELFSymbol32L symbol; 83 | symbol = *cast(ELFSymbol32L*) m_section.contents[index * ELFSymbol32L.sizeof .. (index + 1) * ELFSymbol32L.sizeof].ptr; 84 | return new ELFSymbol32(m_section, symbol); 85 | } 86 | 87 | @property ulong length() { 88 | return m_section.size / ELFSymbol32L.sizeof; 89 | } 90 | } 91 | 92 | private class SymbolTable64Impl : SymbolTableImpl { 93 | private ELFSection64 m_section; 94 | 95 | this(ELFSection section) { 96 | elfEnforce(section.bits == 64); 97 | this(cast(ELFSection64) section); 98 | } 99 | 100 | this(ELFSection64 section) { 101 | this.m_section = section; 102 | } 103 | 104 | ELFSymbol getSymbolAt(size_t index) { 105 | elfEnforce(index * ELFSymbol64L.sizeof < m_section.size); 106 | ELFSymbol64L symbol; 107 | symbol = *cast(ELFSymbol64L*) m_section.contents[index * ELFSymbol64L.sizeof .. (index + 1) * ELFSymbol64L.sizeof].ptr; 108 | return new ELFSymbol64(m_section, symbol); 109 | } 110 | 111 | @property ulong length() { 112 | return m_section.size / ELFSymbol64L.sizeof; 113 | } 114 | } 115 | 116 | abstract class ELFSymbol { 117 | private ELFSection m_section; 118 | 119 | @property: 120 | @ReadFrom("name") ELF_Word nameIndex(); 121 | @ReadFrom("info") ubyte info(); 122 | @ReadFrom("other") ubyte other(); 123 | @ReadFrom("shndx") ELF_Section sectionIndex(); 124 | @ReadFrom("value") ELF_Addr value(); 125 | @ReadFrom("size") ELF_XWord size(); 126 | 127 | string name() { 128 | StringTable strtab = StringTable(m_section.m_elf.sections[m_section.link()]); 129 | return strtab.getStringAt(nameIndex); 130 | } 131 | 132 | SymbolBinding binding() { 133 | return cast(SymbolBinding) (this.info() >> 4); 134 | } 135 | 136 | SymbolType type() { 137 | return cast(SymbolType) (this.info() & 0xF); 138 | } 139 | } 140 | 141 | final class ELFSymbol32 : ELFSymbol { 142 | private ELFSymbol32L m_symbol; 143 | mixin(generateVirtualReads!(ELFSymbol, "m_symbol")); 144 | 145 | this(ELFSection32 section, ELFSymbol32L symbol) { 146 | this.m_section = section; 147 | this.m_symbol = symbol; 148 | } 149 | } 150 | 151 | final class ELFSymbol64 : ELFSymbol { 152 | private ELFSymbol64L m_symbol; 153 | mixin(generateVirtualReads!(ELFSymbol, "m_symbol")); 154 | 155 | this(ELFSection64 section, ELFSymbol64L symbol) { 156 | this.m_section = section; 157 | this.m_symbol = symbol; 158 | } 159 | } 160 | 161 | enum SymbolBinding { 162 | local = 0, 163 | global = 1, 164 | weak = 2, 165 | loproc = 13, 166 | hiproc = 15, 167 | } 168 | 169 | enum SymbolType { 170 | notype = 0, 171 | object = 1, 172 | func = 2, 173 | section = 3, 174 | file = 4, 175 | loproc = 13, 176 | hiproc = 15, 177 | } 178 | --------------------------------------------------------------------------------