| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | | const Allocator = @import("mem.zig").Allocator; |
| 1 | const mem = @import("mem.zig"); |
| 2 | 2 | const io = @import("io.zig"); |
| 3 | 3 | const os = @import("os.zig"); |
| 4 | 4 | const elf = @import("elf.zig"); |
| ... | ... | @@ -21,28 +21,38 @@ pub fn printStackTrace() -> %void { |
| 21 | 21 | pub fn writeStackTrace(out_stream: &io.OutStream) -> %void { |
| 22 | 22 | switch (@compileVar("object_format")) { |
| 23 | 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 | 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 | 37 | %return st.elf.openStream(&global_allocator, &st.self_exe_stream); |
| 29 | 38 | defer %return st.elf.close(); |
| 30 | 39 | |
| 31 | | st.aranges = %return st.elf.findSection(".debug_aranges"); |
| 32 | 40 | st.debug_info = (%return st.elf.findSection(".debug_info")) ?? return error.MissingDebugInfo; |
| 33 | 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 | 45 | var maybe_fp: ?&const u8 = @frameAddress(); |
| 36 | 46 | while (true) { |
| 37 | 47 | const fp = maybe_fp ?? break; |
| 38 | 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 |
| 41 | | const compile_unit_offset = %return findCompileUnitOffset(&st, return_address); |
| 50 | const compile_unit = findCompileUnit(st, return_address) ?? return error.MissingDebugInfo; |
| 51 | const name = %return compile_unit.die.getAttrString(st, DW.AT_name); |
| 42 | 52 | |
| 43 | 53 | %return out_stream.printInt(usize, return_address); |
| 44 | 54 | %return out_stream.printf(" -> "); |
| 45 | | %return out_stream.printInt(u64, compile_unit_offset); |
| 55 | %return out_stream.printf(name); |
| 46 | 56 | %return out_stream.printf("\n"); |
| 47 | 57 | maybe_fp = *(&const ?&const u8)(fp); |
| 48 | 58 | } |
| ... | ... | @@ -62,9 +72,38 @@ pub fn writeStackTrace(out_stream: &io.OutStream) -> %void { |
| 62 | 72 | struct ElfStackTrace { |
| 63 | 73 | self_exe_stream: io.InStream, |
| 64 | 74 | elf: elf.Elf, |
| 65 | | aranges: ?&elf.SectionHeader, |
| 66 | 75 | debug_info: &elf.SectionHeader, |
| 67 | 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 | 109 | enum FormValue { |
| ... | ... | @@ -84,8 +123,76 @@ enum FormValue { |
| 84 | 123 | struct Constant { |
| 85 | 124 | payload: []u8, |
| 86 | 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 | 197 | fn readAllocBytes(in_stream: &io.InStream, size: usize) -> %[]u8 { |
| 91 | 198 | const buf = %return global_allocator.alloc(u8, size); |
| ... | ... | @@ -99,25 +206,19 @@ fn parseFormValueBlockLen(in_stream: &io.InStream, size: usize) -> %FormValue { |
| 99 | 206 | return FormValue.Block { buf }; |
| 100 | 207 | } |
| 101 | 208 | |
| 102 | | fn parseFormValueBlock(in_stream: &io.InStream, inline T: type) -> %FormValue { |
| 103 | | const block_len = %return in_stream.readIntLe(T); |
| 209 | fn parseFormValueBlock(in_stream: &io.InStream, size: usize) -> %FormValue { |
| 210 | const block_len = %return in_stream.readVarInt(false, usize, size); |
| 104 | 211 | return parseFormValueBlockLen(in_stream, block_len); |
| 105 | 212 | } |
| 106 | 213 | |
| 107 | | fn parseFormValueConstantLen(in_stream: &io.InStream, signed: bool, size: usize) -> %FormValue { |
| 108 | | const buf = %return readAllocBytes(in_stream, size); |
| 109 | | return FormValue.Const { Constant { |
| 214 | fn parseFormValueConstant(in_stream: &io.InStream, signed: bool, size: usize) -> %FormValue { |
| 215 | FormValue.Const { Constant { |
| 110 | 216 | .signed = signed, |
| 111 | | .payload = buf, |
| 112 | | }}; |
| 217 | .payload = %return readAllocBytes(in_stream, size), |
| 218 | }} |
| 113 | 219 | } |
| 114 | 220 | |
| 115 | | fn parseFormValueConstant(in_stream: &io.InStream, signed: bool, inline T: type) -> %FormValue { |
| 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 { |
| 221 | fn parseFormValueDwarfOffsetSize(in_stream: &io.InStream, is_64: bool) -> %u64 { |
| 121 | 222 | return if (is_64) { |
| 122 | 223 | %return in_stream.readIntLe(u64) |
| 123 | 224 | } else { |
| ... | ... | @@ -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 | 239 | fn parseFormValueRefLen(in_stream: &io.InStream, size: usize) -> %FormValue { |
| 129 | 240 | const buf = %return readAllocBytes(in_stream, size); |
| 130 | 241 | return FormValue.Ref { buf }; |
| ... | ... | @@ -137,24 +248,22 @@ fn parseFormValueRef(in_stream: &io.InStream, inline T: type) -> %FormValue { |
| 137 | 248 | |
| 138 | 249 | fn parseFormValue(in_stream: &io.InStream, form_id: u64, is_64: bool) -> %FormValue { |
| 139 | 250 | return switch (form_id) { |
| 140 | | DW.FORM_addr => FormValue.Address { |
| 141 | | %return parseFormValueAddrSize(in_stream, is_64) |
| 142 | | }, |
| 143 | | DW.FORM_block1 => parseFormValueBlock(in_stream, u8), |
| 144 | | DW.FORM_block2 => parseFormValueBlock(in_stream, u16), |
| 145 | | DW.FORM_block4 => parseFormValueBlock(in_stream, u32), |
| 251 | DW.FORM_addr => FormValue.Address { %return parseFormValueTargetAddrSize(in_stream) }, |
| 252 | DW.FORM_block1 => parseFormValueBlock(in_stream, 1), |
| 253 | DW.FORM_block2 => parseFormValueBlock(in_stream, 2), |
| 254 | DW.FORM_block4 => parseFormValueBlock(in_stream, 4), |
| 146 | 255 | DW.FORM_block => { |
| 147 | 256 | const block_len = %return readULeb128(in_stream); |
| 148 | 257 | parseFormValueBlockLen(in_stream, block_len) |
| 149 | 258 | }, |
| 150 | | DW.FORM_data1 => parseFormValueConstant(in_stream, false, u8), |
| 151 | | DW.FORM_data2 => parseFormValueConstant(in_stream, false, u16), |
| 152 | | DW.FORM_data4 => parseFormValueConstant(in_stream, false, u32), |
| 153 | | DW.FORM_data8 => parseFormValueConstant(in_stream, false, u64), |
| 259 | DW.FORM_data1 => parseFormValueConstant(in_stream, false, 1), |
| 260 | DW.FORM_data2 => parseFormValueConstant(in_stream, false, 2), |
| 261 | DW.FORM_data4 => parseFormValueConstant(in_stream, false, 4), |
| 262 | DW.FORM_data8 => parseFormValueConstant(in_stream, false, 8), |
| 154 | 263 | DW.FORM_udata, DW.FORM_sdata => { |
| 155 | 264 | const block_len = %return readULeb128(in_stream); |
| 156 | 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 | 268 | DW.FORM_exprloc => { |
| 160 | 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 | 273 | DW.FORM_flag => FormValue.Flag { (%return in_stream.readByte()) != 0 }, |
| 165 | 274 | DW.FORM_flag_present => FormValue.Flag { true }, |
| 166 | 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 | 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 | 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 | 289 | DW.FORM_ref_sig8 => FormValue.RefSig8 { %return in_stream.readIntLe(u64) }, |
| 181 | 290 | |
| 182 | | DW.FORM_string => { |
| 183 | | var buf: List(u8) = undefined; |
| 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) }, |
| 291 | DW.FORM_string => FormValue.String { %return readString(in_stream) }, |
| 292 | DW.FORM_strp => FormValue.StrPtr { %return parseFormValueDwarfOffsetSize(in_stream, is_64) }, |
| 195 | 293 | DW.FORM_indirect => { |
| 196 | 294 | const child_form_id = %return readULeb128(in_stream); |
| 197 | 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 | 298 | } |
| 201 | 299 | } |
| 202 | 300 | |
| 203 | | fn findCompileUnitOffset(st: &ElfStackTrace, target_address: usize) -> %u64 { |
| 204 | | if (const result ?= %return arangesOffset(st, target_address)) |
| 205 | | return result; |
| 301 | fn parseAbbrevTable(in_stream: &io.InStream) -> %AbbrevTable { |
| 302 | var result = AbbrevTable.init(&global_allocator); |
| 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 |
| 208 | | %return st.elf.seekToSection(st.debug_info); |
| 315 | while (true) { |
| 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 | 377 | var is_64: bool = undefined; |
| 212 | 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 | 383 | const version = %return st.self_exe_stream.readInt(st.elf.is_big_endian, u16); |
| 215 | 384 | if (version != 4) return error.InvalidDebugInfo; |
| ... | ... | @@ -223,10 +392,45 @@ fn findCompileUnitOffset(st: &ElfStackTrace, target_address: usize) -> %u64 { |
| 223 | 392 | const address_size = %return st.self_exe_stream.readByte(); |
| 224 | 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 | 436 | fn readInitialLength(in_stream: &io.InStream, is_64: &bool) -> %u64 { |
| ... | ... | @@ -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 { |
| 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 { |
| 490 | pub var global_allocator = mem.Allocator { |
| 340 | 491 | .allocFn = globalAlloc, |
| 341 | 492 | .reallocFn = globalRealloc, |
| 342 | 493 | .freeFn = globalFree, |
| 343 | 494 | .context = null, |
| 344 | 495 | }; |
| 345 | 496 | |
| 346 | | var some_mem: [10 * 1024]u8 = undefined; |
| 497 | var some_mem: [100 * 1024]u8 = undefined; |
| 347 | 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 | 501 | const result = some_mem[some_mem_index ... some_mem_index + n]; |
| 351 | 502 | some_mem_index += n; |
| 352 | 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 | 507 | const result = %return globalAlloc(self, new_size); |
| 357 | 508 | @memcpy(result.ptr, old_mem.ptr, old_mem.len); |
| 358 | 509 | return result; |
| 359 | 510 | } |
| 360 | 511 | |
| 361 | | fn globalFree(self: &Allocator, old_mem: []u8) { } |
| 512 | fn globalFree(self: &mem.Allocator, old_mem: []u8) { } |