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 }