1 module dwarf.debuginfo; 2 // this implementation follows the DWARF v4 documentation 3 4 import std.exception; 5 import std.range; 6 import std.conv : to; 7 import dwarf.meta; 8 import dwarf.elf; 9 import dwarf.debugabbrev; 10 11 import std.stdio; 12 import std.format; 13 import std.algorithm; 14 15 struct DebugInfo { 16 private Appender!(CompilationUnit[]) units_; 17 18 private enum uint DWARF_64BIT_FLAG = 0xffff_ffff; 19 20 this(ubyte[] contents, const(Tag[ULEB128]) tags, ubyte[] strs) { 21 while (!contents.empty) { 22 CompilationUnit unit; 23 24 ubyte[] nextUnit; 25 // detect dwarf 32bit or 64bit 26 uint initialLength = * cast(uint*) contents.ptr; 27 unit.is64bit = (*cast(uint*) contents.ptr) == DWARF_64BIT_FLAG; 28 if (unit.is64bit) { 29 contents.popFrontExactly(uint.sizeof); // skip 64bit marker 30 unit.unitLength = contents.read!(ulong); 31 nextUnit = contents[unit.unitLength .. $]; 32 unit.dwarfVersion = contents.read!(ushort); 33 unit.debugAbbrevOffset = contents.read!(ulong); 34 unit.addressSize = contents.read!(ubyte); 35 } else { 36 unit.unitLength = contents.read!(uint); 37 nextUnit = contents[unit.unitLength .. $]; 38 unit.dwarfVersion = contents.read!(ushort); 39 unit.debugAbbrevOffset = contents.read!(uint); 40 unit.addressSize = contents.read!(ubyte); 41 } 42 43 auto tagCode = contents.readULEB128; 44 auto tag = tags[tagCode]; 45 if (tag.name == TagEncoding.compileUnit) { 46 unit.attributes = tag.attributes.map!((attribute){ 47 return RawAttribute(attribute.name, 48 attribute.form, 49 contents.readRawAttribute(attribute.form, unit)); 50 }).array(); 51 } 52 unit.strs = strs; 53 units_.put(unit); 54 contents = nextUnit; 55 } 56 } 57 58 const(CompilationUnit)[] units() { return units_.data; } 59 } 60 61 auto readBytes(ref ubyte[] contents, size_t length) { 62 ubyte[] bytes = contents[0 .. length]; 63 contents.popFrontExactly(length); 64 return bytes; 65 } 66 auto readRawULEB128(ref ubyte[] contents) { 67 size_t len = 0; 68 for (;;) { 69 if (contents[len++] < 0xA0) 70 return contents.readBytes(len); 71 } 72 } 73 auto readRawString(ref ubyte[] contents) { 74 size_t len = 0; 75 for (;;) { 76 if (contents[len] == 0) 77 return contents.readBytes(len); 78 else 79 len++; 80 } 81 } 82 alias readRawSLEB128 = readRawULEB128; 83 auto readRawAttribute(ref ubyte[] contents, const ref AttributeForm form, ref CompilationUnit unit) { 84 final switch(form) with (AttributeForm) { 85 case addr: return contents.readBytes(unit.addressSize); 86 case block2: auto len = contents.read!(ushort); return contents.readBytes(len); 87 case block4: auto len = contents.read!(uint); return contents.readBytes(len); 88 case data2: return contents.readBytes(2); 89 case data4: return contents.readBytes(4); 90 case data8: return contents.readBytes(8); 91 case string_: return contents.readRawString(); 92 case block: auto len = contents.readULEB128; return contents.readBytes(len); 93 case block1: auto len = contents.read!(ubyte); return contents.readBytes(len); 94 case data1: return contents.readBytes(1); 95 case flag: return contents.readBytes(1); 96 case sdata: return contents.readRawSLEB128(); 97 case strp: return contents.readBytes(uint.sizeof); 98 case udata: return contents.readRawULEB128(); 99 case refAddr: return contents.readBytes(unit.is64bit ? 8 : 4); 100 case ref1: return contents.readBytes(1); 101 case ref2: return contents.readBytes(2); 102 case ref4: return contents.readBytes(4); 103 case ref8: return contents.readBytes(8); 104 case refUdata: return contents.readRawULEB128(); 105 case indirect: auto indirectForm = cast(AttributeForm)contents.readULEB128; return contents.readRawAttribute(indirectForm, unit); 106 case secOffset: return contents.readBytes(unit.is64bit ? 8 : 4); 107 case exprLoc: auto len = contents.readULEB128; return contents.readBytes(len); 108 case flagPresent: return contents[0 .. 0]; 109 case refSig8: return contents.readRawULEB128(); 110 } 111 } 112 113 struct RawAttribute { 114 AttributeName name; 115 AttributeForm form; 116 ubyte[] payload; 117 } 118 119 struct CompilationUnit { 120 ulong unitLength; 121 ushort dwarfVersion; 122 ulong debugAbbrevOffset; 123 ubyte addressSize; 124 bool is64bit; 125 RawAttribute[] attributes; 126 ubyte[] strs; 127 } 128 129 auto loadStrp(const ref CompilationUnit unit, const(ubyte[]) rawOffset) { 130 uint offset = *cast(uint*)&rawOffset[0]; 131 return (cast(char*)unit.strs[offset..$].ptr).to!string(); 132 } 133 134 auto getCompDir(const ref CompilationUnit unit) { 135 auto range = unit.attributes.filter!(a => a.name == AttributeName.compDir).map!(raw => unit.loadStrp(raw.payload)); 136 if (range.empty) 137 throw new Error("Failed to find compDir in CompilationUnit"); 138 return range.front(); 139 } 140 141 private T read(T)(ref ubyte[] buffer) { 142 T result = *(cast(T*) buffer[0 .. T.sizeof].ptr); 143 buffer.popFrontExactly(T.sizeof); 144 return result; 145 } 146 147 private ulong readULEB128(ref ubyte[] buffer) { 148 import std.array; 149 ulong val = 0; 150 ubyte b; 151 uint shift = 0; 152 153 while (true) { 154 b = buffer.read!ubyte(); 155 156 val |= (b & 0x7f) << shift; 157 if ((b & 0x80) == 0) break; 158 shift += 7; 159 } 160 161 return val; 162 } 163 164 unittest { 165 ubyte[] data = [0xe5, 0x8e, 0x26, 0xDE, 0xAD, 0xBE, 0xEF]; 166 assert(readULEB128(data) == 624_485); 167 assert(data[] == [0xDE, 0xAD, 0xBE, 0xEF]); 168 } 169 170 private long readSLEB128(ref ubyte[] buffer) { 171 import std.array; 172 long val = 0; 173 uint shift = 0; 174 ubyte b; 175 int size = 8 << 3; 176 177 while (true) { 178 b = buffer.read!ubyte(); 179 val |= (b & 0x7f) << shift; 180 shift += 7; 181 if ((b & 0x80) == 0) 182 break; 183 } 184 185 if (shift < size && (b & 0x40) != 0) val |= -(1 << shift); 186 return val; 187 }