| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const Allocator = @import("mem.zig").Allocator; | 1 | const mem = @import("mem.zig"); |
| 2 | const io = @import("io.zig"); | 2 | const io = @import("io.zig"); |
| 3 | const os = @import("os.zig"); | 3 | const os = @import("os.zig"); |
| 4 | const elf = @import("elf.zig"); | 4 | const elf = @import("elf.zig"); |
| ... | @@ -21,28 +21,38 @@ pub fn printStackTrace() -> %void { | ... | @@ -21,28 +21,38 @@ pub fn printStackTrace() -> %void { |
| 21 | pub fn writeStackTrace(out_stream: &io.OutStream) -> %void { | 21 | pub fn writeStackTrace(out_stream: &io.OutStream) -> %void { |
| 22 | switch (@compileVar("object_format")) { | 22 | switch (@compileVar("object_format")) { |
| 23 | elf => { | 23 | elf => { |
| 24 | var st: ElfStackTrace = undefined; | 24 | var stack_trace = ElfStackTrace { |
| | 25 | .self_exe_stream = undefined, |
| | 26 | .elf = undefined, |
| | 27 | .debug_info = undefined, |
| | 28 | .debug_abbrev = undefined, |
| | 29 | .debug_str = undefined, |
| | 30 | .abbrev_table_list = List(AbbrevTableHeader).init(&global_allocator), |
| | 31 | .compile_unit_list = List(CompileUnit).init(&global_allocator), |
| | 32 | }; |
| | 33 | const st = &stack_trace; |
| 25 | %return io.openSelfExe(&st.self_exe_stream); | 34 | %return io.openSelfExe(&st.self_exe_stream); |
| 26 | defer %return st.self_exe_stream.close(); | 35 | defer st.self_exe_stream.close() %% {}; |
| 27 | | 36 | |
| 28 | %return st.elf.openStream(&global_allocator, &st.self_exe_stream); | 37 | %return st.elf.openStream(&global_allocator, &st.self_exe_stream); |
| 29 | defer %return st.elf.close(); | 38 | defer %return st.elf.close(); |
| 30 | | 39 | |
| 31 | st.aranges = %return st.elf.findSection(".debug_aranges"); | | |
| 32 | st.debug_info = (%return st.elf.findSection(".debug_info")) ?? return error.MissingDebugInfo; | 40 | st.debug_info = (%return st.elf.findSection(".debug_info")) ?? return error.MissingDebugInfo; |
| 33 | st.debug_abbrev = (%return st.elf.findSection(".debug_abbrev")) ?? return error.MissingDebugInfo; | 41 | st.debug_abbrev = (%return st.elf.findSection(".debug_abbrev")) ?? return error.MissingDebugInfo; |
| | 42 | st.debug_str = (%return st.elf.findSection(".debug_str")) ?? return error.MissingDebugInfo; |
| | 43 | %return scanAllCompileUnits(st); |
| 34 | | 44 | |
| 35 | var maybe_fp: ?&const u8 = @frameAddress(); | 45 | var maybe_fp: ?&const u8 = @frameAddress(); |
| 36 | while (true) { | 46 | while (true) { |
| 37 | const fp = maybe_fp ?? break; | 47 | const fp = maybe_fp ?? break; |
| 38 | const return_address = *(&const usize)(usize(fp) + @sizeOf(usize)); | 48 | const return_address = *(&const usize)(usize(fp) + @sizeOf(usize)); |
| 39 | | 49 | |
| 40 | // read .debug_aranges to find out which compile unit the address is in | 50 | const compile_unit = findCompileUnit(st, return_address) ?? return error.MissingDebugInfo; |
| 41 | const compile_unit_offset = %return findCompileUnitOffset(&st, return_address); | 51 | const name = %return compile_unit.die.getAttrString(st, DW.AT_name); |
| 42 | | 52 | |
| 43 | %return out_stream.printInt(usize, return_address); | 53 | %return out_stream.printInt(usize, return_address); |
| 44 | %return out_stream.printf(" -> "); | 54 | %return out_stream.printf(" -> "); |
| 45 | %return out_stream.printInt(u64, compile_unit_offset); | 55 | %return out_stream.printf(name); |
| 46 | %return out_stream.printf("\n"); | 56 | %return out_stream.printf("\n"); |
| 47 | maybe_fp = *(&const ?&const u8)(fp); | 57 | maybe_fp = *(&const ?&const u8)(fp); |
| 48 | } | 58 | } |
| ... | @@ -62,9 +72,38 @@ pub fn writeStackTrace(out_stream: &io.OutStream) -> %void { | ... | @@ -62,9 +72,38 @@ pub fn writeStackTrace(out_stream: &io.OutStream) -> %void { |
| 62 | struct ElfStackTrace { | 72 | struct ElfStackTrace { |
| 63 | self_exe_stream: io.InStream, | 73 | self_exe_stream: io.InStream, |
| 64 | elf: elf.Elf, | 74 | elf: elf.Elf, |
| 65 | aranges: ?&elf.SectionHeader, | | |
| 66 | debug_info: &elf.SectionHeader, | 75 | debug_info: &elf.SectionHeader, |
| 67 | debug_abbrev: &elf.SectionHeader, | 76 | debug_abbrev: &elf.SectionHeader, |
| | 77 | debug_str: &elf.SectionHeader, |
| | 78 | abbrev_table_list: List(AbbrevTableHeader), |
| | 79 | compile_unit_list: List(CompileUnit), |
| | 80 | } |
| | 81 | |
| | 82 | struct CompileUnit { |
| | 83 | is_64: bool, |
| | 84 | die: &Die, |
| | 85 | pc_start: u64, |
| | 86 | pc_end: u64, |
| | 87 | } |
| | 88 | |
| | 89 | const AbbrevTable = List(AbbrevTableEntry); |
| | 90 | |
| | 91 | struct AbbrevTableHeader { |
| | 92 | // offset from .debug_abbrev |
| | 93 | offset: u64, |
| | 94 | table: AbbrevTable, |
| | 95 | } |
| | 96 | |
| | 97 | struct AbbrevTableEntry { |
| | 98 | has_children: bool, |
| | 99 | abbrev_code: u64, |
| | 100 | tag_id: u64, |
| | 101 | attrs: List(AbbrevAttr), |
| | 102 | } |
| | 103 | |
| | 104 | struct AbbrevAttr { |
| | 105 | attr_id: u64, |
| | 106 | form_id: u64, |
| 68 | } | 107 | } |
| 69 | | 108 | |
| 70 | enum FormValue { | 109 | enum FormValue { |
| ... | @@ -84,8 +123,76 @@ enum FormValue { | ... | @@ -84,8 +123,76 @@ enum FormValue { |
| 84 | struct Constant { | 123 | struct Constant { |
| 85 | payload: []u8, | 124 | payload: []u8, |
| 86 | signed: bool, | 125 | signed: bool, |
| | 126 | |
| | 127 | fn asUnsignedLe(self: &const Constant) -> %u64 { |
| | 128 | if (self.payload.len > @sizeOf(u64)) |
| | 129 | return error.InvalidDebugInfo; |
| | 130 | if (self.signed) |
| | 131 | return error.InvalidDebugInfo; |
| | 132 | return mem.sliceAsInt(self.payload, false, u64); |
| | 133 | } |
| | 134 | } |
| | 135 | |
| | 136 | struct Die { |
| | 137 | tag_id: u64, |
| | 138 | has_children: bool, |
| | 139 | attrs: List(Attr), |
| | 140 | |
| | 141 | struct Attr { |
| | 142 | id: u64, |
| | 143 | value: FormValue, |
| | 144 | } |
| | 145 | |
| | 146 | fn getAttr(self: &const Die, id: u64) -> ?&const FormValue { |
| | 147 | for (self.attrs.toSlice()) |*attr| { |
| | 148 | if (attr.id == id) |
| | 149 | return &attr.value; |
| | 150 | } |
| | 151 | return null; |
| | 152 | } |
| | 153 | |
| | 154 | fn getAttrAddr(self: &const Die, id: u64) -> %u64 { |
| | 155 | const form_value = self.getAttr(id) ?? return error.InvalidDebugInfo; |
| | 156 | return switch (*form_value) { |
| | 157 | Address => |value| value, |
| | 158 | else => error.InvalidDebugInfo, |
| | 159 | }; |
| | 160 | } |
| | 161 | |
| | 162 | fn getAttrUnsignedLe(self: &const Die, id: u64) -> %u64 { |
| | 163 | const form_value = self.getAttr(id) ?? return error.InvalidDebugInfo; |
| | 164 | return switch (*form_value) { |
| | 165 | Const => |value| value.asUnsignedLe(), |
| | 166 | else => error.InvalidDebugInfo, |
| | 167 | }; |
| | 168 | } |
| | 169 | |
| | 170 | fn getAttrString(self: &const Die, st: &ElfStackTrace, id: u64) -> %[]u8 { |
| | 171 | const form_value = self.getAttr(id) ?? return error.InvalidDebugInfo; |
| | 172 | return switch (*form_value) { |
| | 173 | String => |value| value, |
| | 174 | StrPtr => |offset| getString(st, offset), |
| | 175 | else => error.InvalidDebugInfo, |
| | 176 | } |
| | 177 | } |
| 87 | } | 178 | } |
| 88 | | 179 | |
| | 180 | fn readString(in_stream: &io.InStream) -> %[]u8 { |
| | 181 | var buf = List(u8).init(&global_allocator); |
| | 182 | while (true) { |
| | 183 | const byte = %return in_stream.readByte(); |
| | 184 | if (byte == 0) |
| | 185 | break; |
| | 186 | %return buf.append(byte); |
| | 187 | } |
| | 188 | return buf.items; |
| | 189 | } |
| | 190 | |
| | 191 | fn getString(st: &ElfStackTrace, offset: u64) -> %[]u8 { |
| | 192 | const pos = st.debug_str.offset + offset; |
| | 193 | %return st.self_exe_stream.seekTo(pos); |
| | 194 | return readString(&st.self_exe_stream); |
| | 195 | } |
| 89 | | 196 | |
| 90 | fn readAllocBytes(in_stream: &io.InStream, size: usize) -> %[]u8 { | 197 | fn readAllocBytes(in_stream: &io.InStream, size: usize) -> %[]u8 { |
| 91 | const buf = %return global_allocator.alloc(u8, size); | 198 | const buf = %return global_allocator.alloc(u8, size); |
| ... | @@ -99,25 +206,19 @@ fn parseFormValueBlockLen(in_stream: &io.InStream, size: usize) -> %FormValue { | ... | @@ -99,25 +206,19 @@ fn parseFormValueBlockLen(in_stream: &io.InStream, size: usize) -> %FormValue { |
| 99 | return FormValue.Block { buf }; | 206 | return FormValue.Block { buf }; |
| 100 | } | 207 | } |
| 101 | | 208 | |
| 102 | fn parseFormValueBlock(in_stream: &io.InStream, inline T: type) -> %FormValue { | 209 | fn parseFormValueBlock(in_stream: &io.InStream, size: usize) -> %FormValue { |
| 103 | const block_len = %return in_stream.readIntLe(T); | 210 | const block_len = %return in_stream.readVarInt(false, usize, size); |
| 104 | return parseFormValueBlockLen(in_stream, block_len); | 211 | return parseFormValueBlockLen(in_stream, block_len); |
| 105 | } | 212 | } |
| 106 | | 213 | |
| 107 | fn parseFormValueConstantLen(in_stream: &io.InStream, signed: bool, size: usize) -> %FormValue { | 214 | fn parseFormValueConstant(in_stream: &io.InStream, signed: bool, size: usize) -> %FormValue { |
| 108 | const buf = %return readAllocBytes(in_stream, size); | 215 | FormValue.Const { Constant { |
| 109 | return FormValue.Const { Constant { | | |
| 110 | .signed = signed, | 216 | .signed = signed, |
| 111 | .payload = buf, | 217 | .payload = %return readAllocBytes(in_stream, size), |
| 112 | }}; | 218 | }} |
| 113 | } | 219 | } |
| 114 | | 220 | |
| 115 | fn parseFormValueConstant(in_stream: &io.InStream, signed: bool, inline T: type) -> %FormValue { | 221 | fn parseFormValueDwarfOffsetSize(in_stream: &io.InStream, is_64: bool) -> %u64 { |
| 116 | const block_len = %return in_stream.readIntLe(T); | | |
| 117 | return parseFormValueConstantLen(in_stream, signed, block_len); | | |
| 118 | } | | |
| 119 | | | |
| 120 | fn parseFormValueAddrSize(in_stream: &io.InStream, is_64: bool) -> %u64 { | | |
| 121 | return if (is_64) { | 222 | return if (is_64) { |
| 122 | %return in_stream.readIntLe(u64) | 223 | %return in_stream.readIntLe(u64) |
| 123 | } else { | 224 | } else { |
| ... | @@ -125,6 +226,16 @@ fn parseFormValueAddrSize(in_stream: &io.InStream, is_64: bool) -> %u64 { | ... | @@ -125,6 +226,16 @@ fn parseFormValueAddrSize(in_stream: &io.InStream, is_64: bool) -> %u64 { |
| 125 | }; | 226 | }; |
| 126 | } | 227 | } |
| 127 | | 228 | |
| | 229 | fn parseFormValueTargetAddrSize(in_stream: &io.InStream) -> %u64 { |
| | 230 | return if (@sizeOf(usize) == 4) { |
| | 231 | u64(%return in_stream.readIntLe(u32)) |
| | 232 | } else if (@sizeOf(usize) == 8) { |
| | 233 | %return in_stream.readIntLe(u64) |
| | 234 | } else { |
| | 235 | @unreachable(); |
| | 236 | }; |
| | 237 | } |
| | 238 | |
| 128 | fn parseFormValueRefLen(in_stream: &io.InStream, size: usize) -> %FormValue { | 239 | fn parseFormValueRefLen(in_stream: &io.InStream, size: usize) -> %FormValue { |
| 129 | const buf = %return readAllocBytes(in_stream, size); | 240 | const buf = %return readAllocBytes(in_stream, size); |
| 130 | return FormValue.Ref { buf }; | 241 | return FormValue.Ref { buf }; |
| ... | @@ -137,24 +248,22 @@ fn parseFormValueRef(in_stream: &io.InStream, inline T: type) -> %FormValue { | ... | @@ -137,24 +248,22 @@ fn parseFormValueRef(in_stream: &io.InStream, inline T: type) -> %FormValue { |
| 137 | | 248 | |
| 138 | fn parseFormValue(in_stream: &io.InStream, form_id: u64, is_64: bool) -> %FormValue { | 249 | fn parseFormValue(in_stream: &io.InStream, form_id: u64, is_64: bool) -> %FormValue { |
| 139 | return switch (form_id) { | 250 | return switch (form_id) { |
| 140 | DW.FORM_addr => FormValue.Address { | 251 | DW.FORM_addr => FormValue.Address { %return parseFormValueTargetAddrSize(in_stream) }, |
| 141 | %return parseFormValueAddrSize(in_stream, is_64) | 252 | DW.FORM_block1 => parseFormValueBlock(in_stream, 1), |
| 142 | }, | 253 | DW.FORM_block2 => parseFormValueBlock(in_stream, 2), |
| 143 | DW.FORM_block1 => parseFormValueBlock(in_stream, u8), | 254 | DW.FORM_block4 => parseFormValueBlock(in_stream, 4), |
| 144 | DW.FORM_block2 => parseFormValueBlock(in_stream, u16), | | |
| 145 | DW.FORM_block4 => parseFormValueBlock(in_stream, u32), | | |
| 146 | DW.FORM_block => { | 255 | DW.FORM_block => { |
| 147 | const block_len = %return readULeb128(in_stream); | 256 | const block_len = %return readULeb128(in_stream); |
| 148 | parseFormValueBlockLen(in_stream, block_len) | 257 | parseFormValueBlockLen(in_stream, block_len) |
| 149 | }, | 258 | }, |
| 150 | DW.FORM_data1 => parseFormValueConstant(in_stream, false, u8), | 259 | DW.FORM_data1 => parseFormValueConstant(in_stream, false, 1), |
| 151 | DW.FORM_data2 => parseFormValueConstant(in_stream, false, u16), | 260 | DW.FORM_data2 => parseFormValueConstant(in_stream, false, 2), |
| 152 | DW.FORM_data4 => parseFormValueConstant(in_stream, false, u32), | 261 | DW.FORM_data4 => parseFormValueConstant(in_stream, false, 4), |
| 153 | DW.FORM_data8 => parseFormValueConstant(in_stream, false, u64), | 262 | DW.FORM_data8 => parseFormValueConstant(in_stream, false, 8), |
| 154 | DW.FORM_udata, DW.FORM_sdata => { | 263 | DW.FORM_udata, DW.FORM_sdata => { |
| 155 | const block_len = %return readULeb128(in_stream); | 264 | const block_len = %return readULeb128(in_stream); |
| 156 | const signed = form_id == DW.FORM_sdata; | 265 | const signed = form_id == DW.FORM_sdata; |
| 157 | parseFormValueConstantLen(in_stream, signed, block_len) | 266 | parseFormValueConstant(in_stream, signed, block_len) |
| 158 | }, | 267 | }, |
| 159 | DW.FORM_exprloc => { | 268 | DW.FORM_exprloc => { |
| 160 | const size = %return readULeb128(in_stream); | 269 | const size = %return readULeb128(in_stream); |
| ... | @@ -164,7 +273,7 @@ fn parseFormValue(in_stream: &io.InStream, form_id: u64, is_64: bool) -> %FormVa | ... | @@ -164,7 +273,7 @@ fn parseFormValue(in_stream: &io.InStream, form_id: u64, is_64: bool) -> %FormVa |
| 164 | DW.FORM_flag => FormValue.Flag { (%return in_stream.readByte()) != 0 }, | 273 | DW.FORM_flag => FormValue.Flag { (%return in_stream.readByte()) != 0 }, |
| 165 | DW.FORM_flag_present => FormValue.Flag { true }, | 274 | DW.FORM_flag_present => FormValue.Flag { true }, |
| 166 | DW.FORM_sec_offset => FormValue.SecOffset { | 275 | DW.FORM_sec_offset => FormValue.SecOffset { |
| 167 | %return parseFormValueAddrSize(in_stream, is_64) | 276 | %return parseFormValueDwarfOffsetSize(in_stream, is_64) |
| 168 | }, | 277 | }, |
| 169 | | 278 | |
| 170 | DW.FORM_ref1 => parseFormValueRef(in_stream, u8), | 279 | DW.FORM_ref1 => parseFormValueRef(in_stream, u8), |
| ... | @@ -176,22 +285,11 @@ fn parseFormValue(in_stream: &io.InStream, form_id: u64, is_64: bool) -> %FormVa | ... | @@ -176,22 +285,11 @@ fn parseFormValue(in_stream: &io.InStream, form_id: u64, is_64: bool) -> %FormVa |
| 176 | parseFormValueRefLen(in_stream, ref_len) | 285 | parseFormValueRefLen(in_stream, ref_len) |
| 177 | }, | 286 | }, |
| 178 | | 287 | |
| 179 | DW.FORM_ref_addr => FormValue.RefAddr { %return parseFormValueAddrSize(in_stream, is_64) }, | 288 | DW.FORM_ref_addr => FormValue.RefAddr { %return parseFormValueDwarfOffsetSize(in_stream, is_64) }, |
| 180 | DW.FORM_ref_sig8 => FormValue.RefSig8 { %return in_stream.readIntLe(u64) }, | 289 | DW.FORM_ref_sig8 => FormValue.RefSig8 { %return in_stream.readIntLe(u64) }, |
| 181 | | 290 | |
| 182 | DW.FORM_string => { | 291 | DW.FORM_string => FormValue.String { %return readString(in_stream) }, |
| 183 | var buf: List(u8) = undefined; | 292 | DW.FORM_strp => FormValue.StrPtr { %return parseFormValueDwarfOffsetSize(in_stream, is_64) }, |
| 184 | buf.init(&global_allocator); | | |
| 185 | while (true) { | | |
| 186 | const byte = %return in_stream.readByte(); | | |
| 187 | if (byte == 0) | | |
| 188 | break; | | |
| 189 | %return buf.append(byte); | | |
| 190 | } | | |
| 191 | | | |
| 192 | FormValue.String { buf.items } | | |
| 193 | }, | | |
| 194 | DW.FORM_strp => FormValue.StrPtr { %return parseFormValueAddrSize(in_stream, is_64) }, | | |
| 195 | DW.FORM_indirect => { | 293 | DW.FORM_indirect => { |
| 196 | const child_form_id = %return readULeb128(in_stream); | 294 | const child_form_id = %return readULeb128(in_stream); |
| 197 | parseFormValue(in_stream, child_form_id, is_64) | 295 | parseFormValue(in_stream, child_form_id, is_64) |
| ... | @@ -200,16 +298,87 @@ fn parseFormValue(in_stream: &io.InStream, form_id: u64, is_64: bool) -> %FormVa | ... | @@ -200,16 +298,87 @@ fn parseFormValue(in_stream: &io.InStream, form_id: u64, is_64: bool) -> %FormVa |
| 200 | } | 298 | } |
| 201 | } | 299 | } |
| 202 | | 300 | |
| 203 | fn findCompileUnitOffset(st: &ElfStackTrace, target_address: usize) -> %u64 { | 301 | fn parseAbbrevTable(in_stream: &io.InStream) -> %AbbrevTable { |
| 204 | if (const result ?= %return arangesOffset(st, target_address)) | 302 | var result = AbbrevTable.init(&global_allocator); |
| 205 | return result; | 303 | while (true) { |
| | 304 | const abbrev_code = %return readULeb128(in_stream); |
| | 305 | if (abbrev_code == 0) |
| | 306 | return result; |
| | 307 | %return result.append(AbbrevTableEntry { |
| | 308 | .abbrev_code = abbrev_code, |
| | 309 | .tag_id = %return readULeb128(in_stream), |
| | 310 | .has_children = (%return in_stream.readByte()) == DW.CHILDREN_yes, |
| | 311 | .attrs = List(AbbrevAttr).init(&global_allocator), |
| | 312 | }); |
| | 313 | const attrs = &result.items[result.len - 1].attrs; |
| 206 | | 314 | |
| 207 | // iterate over compile units looking for a match with the low pc and high pc | 315 | while (true) { |
| 208 | %return st.elf.seekToSection(st.debug_info); | 316 | const attr_id = %return readULeb128(in_stream); |
| | 317 | const form_id = %return readULeb128(in_stream); |
| | 318 | if (attr_id == 0 && form_id == 0) |
| | 319 | break; |
| | 320 | %return attrs.append(AbbrevAttr { |
| | 321 | .attr_id = attr_id, |
| | 322 | .form_id = form_id, |
| | 323 | }); |
| | 324 | } |
| | 325 | } |
| | 326 | } |
| | 327 | |
| | 328 | /// Gets an already existing AbbrevTable given the abbrev_offset, or if not found, |
| | 329 | /// seeks in the stream and parses it. |
| | 330 | fn getAbbrevTable(st: &ElfStackTrace, abbrev_offset: u64) -> %&AbbrevTable { |
| | 331 | for (st.abbrev_table_list.toSlice()) |header| { |
| | 332 | if (header.offset == abbrev_offset) { |
| | 333 | return &header.table; |
| | 334 | } |
| | 335 | } |
| | 336 | %return st.self_exe_stream.seekTo(st.debug_abbrev.offset + abbrev_offset); |
| | 337 | %return st.abbrev_table_list.append(AbbrevTableHeader { |
| | 338 | .offset = abbrev_offset, |
| | 339 | .table = %return parseAbbrevTable(&st.self_exe_stream), |
| | 340 | }); |
| | 341 | return &st.abbrev_table_list.items[st.abbrev_table_list.len - 1].table; |
| | 342 | } |
| | 343 | |
| | 344 | fn getAbbrevTableEntry(abbrev_table: &const AbbrevTable, abbrev_code: u64) -> ?&const AbbrevTableEntry { |
| | 345 | for (abbrev_table.toSlice()) |*table_entry| { |
| | 346 | if (table_entry.abbrev_code == abbrev_code) |
| | 347 | return table_entry; |
| | 348 | } |
| | 349 | return null; |
| | 350 | } |
| | 351 | |
| | 352 | fn parseDie(in_stream: &io.InStream, abbrev_table: &const AbbrevTable, is_64: bool) -> %Die { |
| | 353 | const abbrev_code = %return readULeb128(in_stream); |
| | 354 | const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) ?? return error.InvalidDebugInfo; |
| | 355 | |
| | 356 | var result = Die { |
| | 357 | .tag_id = table_entry.tag_id, |
| | 358 | .has_children = table_entry.has_children, |
| | 359 | .attrs = List(Die.Attr).init(&global_allocator), |
| | 360 | }; |
| | 361 | %return result.attrs.resize(table_entry.attrs.len); |
| | 362 | for (table_entry.attrs.toSlice()) |attr, i| { |
| | 363 | result.attrs.items[i] = Die.Attr { |
| | 364 | .id = attr.attr_id, |
| | 365 | .value = %return parseFormValue(in_stream, attr.form_id, is_64), |
| | 366 | }; |
| | 367 | } |
| | 368 | return result; |
| | 369 | } |
| | 370 | |
| | 371 | fn scanAllCompileUnits(st: &ElfStackTrace) -> %void { |
| | 372 | const debug_info_end = st.debug_info.offset + st.debug_info.size; |
| | 373 | var this_unit_offset = st.debug_info.offset; |
| | 374 | while (this_unit_offset < debug_info_end) { |
| | 375 | %return st.self_exe_stream.seekTo(this_unit_offset); |
| 209 | | 376 | |
| 210 | while (true) { | | |
| 211 | var is_64: bool = undefined; | 377 | var is_64: bool = undefined; |
| 212 | const unit_length = %return readInitialLength(&st.self_exe_stream, &is_64); | 378 | const unit_length = %return readInitialLength(&st.self_exe_stream, &is_64); |
| | 379 | if (unit_length == 0) |
| | 380 | return; |
| | 381 | const next_offset = unit_length + (if (is_64) usize(12) else usize(4)); |
| 213 | | 382 | |
| 214 | const version = %return st.self_exe_stream.readInt(st.elf.is_big_endian, u16); | 383 | const version = %return st.self_exe_stream.readInt(st.elf.is_big_endian, u16); |
| 215 | if (version != 4) return error.InvalidDebugInfo; | 384 | if (version != 4) return error.InvalidDebugInfo; |
| ... | @@ -223,10 +392,45 @@ fn findCompileUnitOffset(st: &ElfStackTrace, target_address: usize) -> %u64 { | ... | @@ -223,10 +392,45 @@ fn findCompileUnitOffset(st: &ElfStackTrace, target_address: usize) -> %u64 { |
| 223 | const address_size = %return st.self_exe_stream.readByte(); | 392 | const address_size = %return st.self_exe_stream.readByte(); |
| 224 | if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo; | 393 | if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo; |
| 225 | | 394 | |
| 226 | const abbrev_tag_id = %return st.self_exe_stream.readByte(); | 395 | const compile_unit_pos = %return st.self_exe_stream.getPos(); |
| | 396 | const abbrev_table = %return getAbbrevTable(st, debug_abbrev_offset); |
| | 397 | |
| | 398 | %return st.self_exe_stream.seekTo(compile_unit_pos); |
| 227 | | 399 | |
| | 400 | const compile_unit_die = (%return global_allocator.alloc(Die, 1)).ptr; |
| | 401 | *compile_unit_die = %return parseDie(&st.self_exe_stream, abbrev_table, is_64); |
| 228 | | 402 | |
| | 403 | if (compile_unit_die.tag_id != DW.TAG_compile_unit) |
| | 404 | return error.InvalidDebugInfo; |
| | 405 | const low_pc = %return compile_unit_die.getAttrAddr(DW.AT_low_pc); |
| | 406 | |
| | 407 | const high_pc_value = compile_unit_die.getAttr(DW.AT_high_pc) ?? return error.MissingDebugInfo; |
| | 408 | const pc_end = switch (*high_pc_value) { |
| | 409 | Address => |value| value, |
| | 410 | Const => |value| { |
| | 411 | const offset = %return value.asUnsignedLe(); |
| | 412 | low_pc + offset |
| | 413 | }, |
| | 414 | else => return error.InvalidDebugInfo, |
| | 415 | }; |
| | 416 | |
| | 417 | %return st.compile_unit_list.append(CompileUnit { |
| | 418 | .is_64 = is_64, |
| | 419 | .pc_start = low_pc, |
| | 420 | .pc_end = pc_end, |
| | 421 | .die = compile_unit_die, |
| | 422 | }); |
| | 423 | |
| | 424 | this_unit_offset += next_offset; |
| | 425 | } |
| | 426 | } |
| | 427 | |
| | 428 | fn findCompileUnit(st: &ElfStackTrace, target_address: u64) -> ?&const CompileUnit { |
| | 429 | for (st.compile_unit_list.toSlice()) |*compile_unit| { |
| | 430 | if (target_address >= compile_unit.pc_start && target_address < compile_unit.pc_end) |
| | 431 | return compile_unit; |
| 229 | } | 432 | } |
| | 433 | return null; |
| 230 | } | 434 | } |
| 231 | | 435 | |
| 232 | fn readInitialLength(in_stream: &io.InStream, is_64: &bool) -> %u64 { | 436 | fn readInitialLength(in_stream: &io.InStream, is_64: &bool) -> %u64 { |
| ... | @@ -283,79 +487,26 @@ fn readILeb128(in_stream: &io.InStream) -> %i64 { | ... | @@ -283,79 +487,26 @@ fn readILeb128(in_stream: &io.InStream) -> %i64 { |
| 283 | } | 487 | } |
| 284 | } | 488 | } |
| 285 | | 489 | |
| 286 | fn arangesOffset(st: &ElfStackTrace, target_address: usize) -> %?u64 { | 490 | pub var global_allocator = mem.Allocator { |
| 287 | // TODO ability to implicitly cast null to %?T | | |
| 288 | const aranges = st.aranges ?? return (?u64)(null); | | |
| 289 | | | |
| 290 | %return st.elf.seekToSection(aranges); | | |
| 291 | | | |
| 292 | const first_32_bits = %return st.self_exe_stream.readIntLe(u32); | | |
| 293 | var is_64: bool = undefined; | | |
| 294 | const unit_length = %return readInitialLength(&st.self_exe_stream, &is_64); | | |
| 295 | var unit_index: u64 = 0; | | |
| 296 | | | |
| 297 | while (unit_index < unit_length) { | | |
| 298 | const version = %return st.self_exe_stream.readIntLe(u16); | | |
| 299 | if (version != 2) return error.InvalidDebugInfo; | | |
| 300 | unit_index += 2; | | |
| 301 | | | |
| 302 | const debug_info_offset = if (is_64) { | | |
| 303 | unit_index += 4; | | |
| 304 | %return st.self_exe_stream.readIntLe(u64) | | |
| 305 | } else { | | |
| 306 | unit_index += 2; | | |
| 307 | %return st.self_exe_stream.readIntLe(u32) | | |
| 308 | }; | | |
| 309 | | | |
| 310 | const address_size = %return st.self_exe_stream.readByte(); | | |
| 311 | if (address_size > 8) return error.UnsupportedDebugInfo; | | |
| 312 | unit_index += 1; | | |
| 313 | | | |
| 314 | const segment_size = %return st.self_exe_stream.readByte(); | | |
| 315 | if (segment_size > 0) return error.UnsupportedDebugInfo; | | |
| 316 | unit_index += 1; | | |
| 317 | | | |
| 318 | const align = segment_size + 2 * address_size; | | |
| 319 | const padding = (%return st.self_exe_stream.getPos()) % align; | | |
| 320 | %return st.self_exe_stream.seekForward(padding); | | |
| 321 | unit_index += padding; | | |
| 322 | | | |
| 323 | while (true) { | | |
| 324 | const address = %return st.self_exe_stream.readVarInt(false, u64, address_size); | | |
| 325 | const length = %return st.self_exe_stream.readVarInt(false, u64, address_size); | | |
| 326 | unit_index += align; | | |
| 327 | if (address == 0 && length == 0) break; | | |
| 328 | | | |
| 329 | if (target_address >= address && target_address < address + length) { | | |
| 330 | // TODO ability to implicitly cast T to %?T | | |
| 331 | return (?u64)(debug_info_offset); | | |
| 332 | } | | |
| 333 | } | | |
| 334 | } | | |
| 335 | | | |
| 336 | return error.MissingDebugInfo; | | |
| 337 | } | | |
| 338 | | | |
| 339 | pub var global_allocator = Allocator { | | |
| 340 | .allocFn = globalAlloc, | 491 | .allocFn = globalAlloc, |
| 341 | .reallocFn = globalRealloc, | 492 | .reallocFn = globalRealloc, |
| 342 | .freeFn = globalFree, | 493 | .freeFn = globalFree, |
| 343 | .context = null, | 494 | .context = null, |
| 344 | }; | 495 | }; |
| 345 | | 496 | |
| 346 | var some_mem: [10 * 1024]u8 = undefined; | 497 | var some_mem: [100 * 1024]u8 = undefined; |
| 347 | var some_mem_index: usize = 0; | 498 | var some_mem_index: usize = 0; |
| 348 | | 499 | |
| 349 | fn globalAlloc(self: &Allocator, n: usize) -> %[]u8 { | 500 | fn globalAlloc(self: &mem.Allocator, n: usize) -> %[]u8 { |
| 350 | const result = some_mem[some_mem_index ... some_mem_index + n]; | 501 | const result = some_mem[some_mem_index ... some_mem_index + n]; |
| 351 | some_mem_index += n; | 502 | some_mem_index += n; |
| 352 | return result; | 503 | return result; |
| 353 | } | 504 | } |
| 354 | | 505 | |
| 355 | fn globalRealloc(self: &Allocator, old_mem: []u8, new_size: usize) -> %[]u8 { | 506 | fn globalRealloc(self: &mem.Allocator, old_mem: []u8, new_size: usize) -> %[]u8 { |
| 356 | const result = %return globalAlloc(self, new_size); | 507 | const result = %return globalAlloc(self, new_size); |
| 357 | @memcpy(result.ptr, old_mem.ptr, old_mem.len); | 508 | @memcpy(result.ptr, old_mem.ptr, old_mem.len); |
| 358 | return result; | 509 | return result; |
| 359 | } | 510 | } |
| 360 | | 511 | |
| 361 | fn globalFree(self: &Allocator, old_mem: []u8) { } | 512 | fn globalFree(self: &mem.Allocator, old_mem: []u8) { } |