| ... | ... | @@ -1,12 +1,9 @@ |
| 1 | 1 | const builtin = @import("builtin"); |
| 2 | 2 | const std = @import("std.zig"); |
| 3 | 3 | const debug = std.debug; |
| 4 | | const fs = std.fs; |
| 5 | | const io = std.io; |
| 6 | 4 | const mem = std.mem; |
| 7 | 5 | const math = std.math; |
| 8 | | const leb = @import("leb128.zig"); |
| 9 | | const assert = std.debug.assert; |
| 6 | const assert = debug.assert; |
| 10 | 7 | const native_endian = builtin.cpu.arch.endian(); |
| 11 | 8 | |
| 12 | 9 | pub const TAG = @import("dwarf/TAG.zig"); |
| ... | ... | @@ -167,8 +164,8 @@ const Func = struct { |
| 167 | 164 | |
| 168 | 165 | pub const CompileUnit = struct { |
| 169 | 166 | version: u16, |
| 170 | | is_64: bool, |
| 171 | | die: *Die, |
| 167 | format: Format, |
| 168 | die: Die, |
| 172 | 169 | pc_range: ?PcRange, |
| 173 | 170 | |
| 174 | 171 | str_offsets_base: usize, |
| ... | ... | @@ -178,101 +175,88 @@ pub const CompileUnit = struct { |
| 178 | 175 | frame_base: ?*const FormValue, |
| 179 | 176 | }; |
| 180 | 177 | |
| 181 | | const AbbrevTable = std.ArrayList(AbbrevTableEntry); |
| 182 | | |
| 183 | | const AbbrevTableHeader = struct { |
| 184 | | // offset from .debug_abbrev |
| 185 | | offset: u64, |
| 186 | | table: AbbrevTable, |
| 187 | | |
| 188 | | fn deinit(header: *AbbrevTableHeader) void { |
| 189 | | for (header.table.items) |*entry| { |
| 190 | | entry.deinit(); |
| 191 | | } |
| 192 | | header.table.deinit(); |
| 193 | | } |
| 194 | | }; |
| 195 | | |
| 196 | | const AbbrevTableEntry = struct { |
| 197 | | has_children: bool, |
| 198 | | abbrev_code: u64, |
| 178 | const Abbrev = struct { |
| 179 | code: u64, |
| 199 | 180 | tag_id: u64, |
| 200 | | attrs: std.ArrayList(AbbrevAttr), |
| 181 | has_children: bool, |
| 182 | attrs: []Attr, |
| 201 | 183 | |
| 202 | | fn deinit(entry: *AbbrevTableEntry) void { |
| 203 | | entry.attrs.deinit(); |
| 184 | fn deinit(abbrev: *Abbrev, allocator: mem.Allocator) void { |
| 185 | allocator.free(abbrev.attrs); |
| 186 | abbrev.* = undefined; |
| 204 | 187 | } |
| 205 | | }; |
| 206 | 188 | |
| 207 | | const AbbrevAttr = struct { |
| 208 | | attr_id: u64, |
| 209 | | form_id: u64, |
| 210 | | /// Only valid if form_id is .implicit_const |
| 211 | | payload: i64, |
| 212 | | }; |
| 189 | const Attr = struct { |
| 190 | id: u64, |
| 191 | form_id: u64, |
| 192 | /// Only valid if form_id is .implicit_const |
| 193 | payload: i64, |
| 194 | }; |
| 213 | 195 | |
| 214 | | pub const FormValue = union(enum) { |
| 215 | | Address: u64, |
| 216 | | AddrOffset: usize, |
| 217 | | Block: []u8, |
| 218 | | Const: Constant, |
| 219 | | ExprLoc: []u8, |
| 220 | | Flag: bool, |
| 221 | | SecOffset: u64, |
| 222 | | Ref: u64, |
| 223 | | RefAddr: u64, |
| 224 | | String: []const u8, |
| 225 | | StrPtr: u64, |
| 226 | | StrOffset: usize, |
| 227 | | LineStrPtr: u64, |
| 228 | | LocListOffset: u64, |
| 229 | | RangeListOffset: u64, |
| 230 | | data16: [16]u8, |
| 231 | | |
| 232 | | fn getString(fv: FormValue, di: DwarfInfo) ![]const u8 { |
| 233 | | switch (fv) { |
| 234 | | .String => |s| return s, |
| 235 | | .StrPtr => |off| return di.getString(off), |
| 236 | | .LineStrPtr => |off| return di.getLineString(off), |
| 237 | | else => return badDwarf(), |
| 196 | const Table = struct { |
| 197 | // offset from .debug_abbrev |
| 198 | offset: u64, |
| 199 | abbrevs: []Abbrev, |
| 200 | |
| 201 | fn deinit(table: *Table, allocator: mem.Allocator) void { |
| 202 | for (table.abbrevs) |*abbrev| { |
| 203 | abbrev.deinit(allocator); |
| 204 | } |
| 205 | allocator.free(table.abbrevs); |
| 206 | table.* = undefined; |
| 238 | 207 | } |
| 239 | | } |
| 240 | 208 | |
| 241 | | fn getUInt(fv: FormValue, comptime U: type) !U { |
| 242 | | switch (fv) { |
| 243 | | .Const => |c| { |
| 244 | | const int = try c.asUnsignedLe(); |
| 245 | | return math.cast(U, int) orelse return badDwarf(); |
| 246 | | }, |
| 247 | | .SecOffset => |x| return math.cast(U, x) orelse return badDwarf(), |
| 248 | | else => return badDwarf(), |
| 209 | fn get(table: *const Table, abbrev_code: u64) ?*const Abbrev { |
| 210 | return for (table.abbrevs) |*abbrev| { |
| 211 | if (abbrev.code == abbrev_code) break abbrev; |
| 212 | } else null; |
| 249 | 213 | } |
| 250 | | } |
| 214 | }; |
| 215 | }; |
| 251 | 216 | |
| 252 | | fn getData16(fv: FormValue) ![16]u8 { |
| 217 | pub const FormValue = union(enum) { |
| 218 | addr: u64, |
| 219 | addrx: usize, |
| 220 | block: []const u8, |
| 221 | udata: u64, |
| 222 | data16: *const [16]u8, |
| 223 | sdata: i64, |
| 224 | exprloc: []const u8, |
| 225 | flag: bool, |
| 226 | sec_offset: u64, |
| 227 | ref: u64, |
| 228 | ref_addr: u64, |
| 229 | string: [:0]const u8, |
| 230 | strp: u64, |
| 231 | strx: usize, |
| 232 | line_strp: u64, |
| 233 | loclistx: u64, |
| 234 | rnglistx: u64, |
| 235 | |
| 236 | fn getString(fv: FormValue, di: DwarfInfo) ![:0]const u8 { |
| 253 | 237 | switch (fv) { |
| 254 | | .data16 => |d| return d, |
| 238 | .string => |s| return s, |
| 239 | .strp => |off| return di.getString(off), |
| 240 | .line_strp => |off| return di.getLineString(off), |
| 255 | 241 | else => return badDwarf(), |
| 256 | 242 | } |
| 257 | 243 | } |
| 258 | | }; |
| 259 | | |
| 260 | | const Constant = struct { |
| 261 | | payload: u64, |
| 262 | | signed: bool, |
| 263 | 244 | |
| 264 | | fn asUnsignedLe(self: Constant) !u64 { |
| 265 | | if (self.signed) return badDwarf(); |
| 266 | | return self.payload; |
| 245 | fn getUInt(fv: FormValue, comptime U: type) !U { |
| 246 | return switch (fv) { |
| 247 | inline .udata, |
| 248 | .sdata, |
| 249 | .sec_offset, |
| 250 | => |c| math.cast(U, c) orelse badDwarf(), |
| 251 | else => badDwarf(), |
| 252 | }; |
| 267 | 253 | } |
| 268 | 254 | }; |
| 269 | 255 | |
| 270 | 256 | const Die = struct { |
| 271 | | // Arena for Die's Attr's and FormValue's. |
| 272 | | arena: std.heap.ArenaAllocator, |
| 273 | 257 | tag_id: u64, |
| 274 | 258 | has_children: bool, |
| 275 | | attrs: std.ArrayListUnmanaged(Attr) = .{}, |
| 259 | attrs: []Attr, |
| 276 | 260 | |
| 277 | 261 | const Attr = struct { |
| 278 | 262 | id: u64, |
| ... | ... | @@ -280,12 +264,12 @@ const Die = struct { |
| 280 | 264 | }; |
| 281 | 265 | |
| 282 | 266 | fn deinit(self: *Die, allocator: mem.Allocator) void { |
| 283 | | self.arena.deinit(); |
| 284 | | self.attrs.deinit(allocator); |
| 267 | allocator.free(self.attrs); |
| 268 | self.* = undefined; |
| 285 | 269 | } |
| 286 | 270 | |
| 287 | 271 | fn getAttr(self: *const Die, id: u64) ?*const FormValue { |
| 288 | | for (self.attrs.items) |*attr| { |
| 272 | for (self.attrs) |*attr| { |
| 289 | 273 | if (attr.id == id) return &attr.value; |
| 290 | 274 | } |
| 291 | 275 | return null; |
| ... | ... | @@ -299,8 +283,8 @@ const Die = struct { |
| 299 | 283 | ) error{ InvalidDebugInfo, MissingDebugInfo }!u64 { |
| 300 | 284 | const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; |
| 301 | 285 | return switch (form_value.*) { |
| 302 | | FormValue.Address => |value| value, |
| 303 | | FormValue.AddrOffset => |index| di.readDebugAddr(compile_unit, index), |
| 286 | .addr => |value| value, |
| 287 | .addrx => |index| di.readDebugAddr(compile_unit, index), |
| 304 | 288 | else => error.InvalidDebugInfo, |
| 305 | 289 | }; |
| 306 | 290 | } |
| ... | ... | @@ -313,7 +297,7 @@ const Die = struct { |
| 313 | 297 | fn getAttrUnsignedLe(self: *const Die, id: u64) !u64 { |
| 314 | 298 | const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; |
| 315 | 299 | return switch (form_value.*) { |
| 316 | | FormValue.Const => |value| value.asUnsignedLe(), |
| 300 | .Const => |value| value.asUnsignedLe(), |
| 317 | 301 | else => error.InvalidDebugInfo, |
| 318 | 302 | }; |
| 319 | 303 | } |
| ... | ... | @@ -321,7 +305,7 @@ const Die = struct { |
| 321 | 305 | fn getAttrRef(self: *const Die, id: u64) !u64 { |
| 322 | 306 | const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; |
| 323 | 307 | return switch (form_value.*) { |
| 324 | | FormValue.Ref => |value| value, |
| 308 | .ref => |value| value, |
| 325 | 309 | else => error.InvalidDebugInfo, |
| 326 | 310 | }; |
| 327 | 311 | } |
| ... | ... | @@ -335,24 +319,27 @@ const Die = struct { |
| 335 | 319 | ) error{ InvalidDebugInfo, MissingDebugInfo }![]const u8 { |
| 336 | 320 | const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; |
| 337 | 321 | switch (form_value.*) { |
| 338 | | FormValue.String => |value| return value, |
| 339 | | FormValue.StrPtr => |offset| return di.getString(offset), |
| 340 | | FormValue.StrOffset => |index| { |
| 322 | .string => |value| return value, |
| 323 | .strp => |offset| return di.getString(offset), |
| 324 | .strx => |index| { |
| 341 | 325 | const debug_str_offsets = di.section(.debug_str_offsets) orelse return badDwarf(); |
| 342 | 326 | if (compile_unit.str_offsets_base == 0) return badDwarf(); |
| 343 | | if (compile_unit.is_64) { |
| 344 | | const byte_offset = compile_unit.str_offsets_base + 8 * index; |
| 345 | | if (byte_offset + 8 > debug_str_offsets.len) return badDwarf(); |
| 346 | | const offset = mem.readInt(u64, debug_str_offsets[byte_offset..][0..8], di.endian); |
| 347 | | return getStringGeneric(opt_str, offset); |
| 348 | | } else { |
| 349 | | const byte_offset = compile_unit.str_offsets_base + 4 * index; |
| 350 | | if (byte_offset + 4 > debug_str_offsets.len) return badDwarf(); |
| 351 | | const offset = mem.readInt(u32, debug_str_offsets[byte_offset..][0..4], di.endian); |
| 352 | | return getStringGeneric(opt_str, offset); |
| 327 | switch (compile_unit.format) { |
| 328 | .@"32" => { |
| 329 | const byte_offset = compile_unit.str_offsets_base + 4 * index; |
| 330 | if (byte_offset + 4 > debug_str_offsets.len) return badDwarf(); |
| 331 | const offset = mem.readInt(u32, debug_str_offsets[byte_offset..][0..4], di.endian); |
| 332 | return getStringGeneric(opt_str, offset); |
| 333 | }, |
| 334 | .@"64" => { |
| 335 | const byte_offset = compile_unit.str_offsets_base + 8 * index; |
| 336 | if (byte_offset + 8 > debug_str_offsets.len) return badDwarf(); |
| 337 | const offset = mem.readInt(u64, debug_str_offsets[byte_offset..][0..8], di.endian); |
| 338 | return getStringGeneric(opt_str, offset); |
| 339 | }, |
| 353 | 340 | } |
| 354 | 341 | }, |
| 355 | | FormValue.LineStrPtr => |offset| return di.getLineString(offset), |
| 342 | .line_strp => |offset| return di.getLineString(offset), |
| 356 | 343 | else => return badDwarf(), |
| 357 | 344 | } |
| 358 | 345 | } |
| ... | ... | @@ -458,7 +445,7 @@ const LineNumberProgram = struct { |
| 458 | 445 | if (file_entry.dir_index >= self.include_dirs.len) return badDwarf(); |
| 459 | 446 | const dir_name = self.include_dirs[file_entry.dir_index].path; |
| 460 | 447 | |
| 461 | | const file_name = try fs.path.join(allocator, &[_][]const u8{ |
| 448 | const file_name = try std.fs.path.join(allocator, &[_][]const u8{ |
| 462 | 449 | dir_name, file_entry.path, |
| 463 | 450 | }); |
| 464 | 451 | |
| ... | ... | @@ -481,168 +468,97 @@ const LineNumberProgram = struct { |
| 481 | 468 | } |
| 482 | 469 | }; |
| 483 | 470 | |
| 484 | | fn readUnitLength(in_stream: anytype, endian: std.builtin.Endian, is_64: *bool) !u64 { |
| 485 | | const first_32_bits = try in_stream.readInt(u32, endian); |
| 486 | | is_64.* = (first_32_bits == 0xffffffff); |
| 487 | | if (is_64.*) { |
| 488 | | return in_stream.readInt(u64, endian); |
| 489 | | } else { |
| 490 | | if (first_32_bits >= 0xfffffff0) return badDwarf(); |
| 491 | | // TODO this cast should not be needed |
| 492 | | return @as(u64, first_32_bits); |
| 493 | | } |
| 494 | | } |
| 495 | | |
| 496 | | // TODO the nosuspends here are workarounds |
| 497 | | fn readAllocBytes(allocator: mem.Allocator, in_stream: anytype, size: usize) ![]u8 { |
| 498 | | const buf = try allocator.alloc(u8, size); |
| 499 | | errdefer allocator.free(buf); |
| 500 | | if ((try nosuspend in_stream.read(buf)) < size) return error.EndOfFile; |
| 501 | | return buf; |
| 502 | | } |
| 503 | | |
| 504 | | // TODO the nosuspends here are workarounds |
| 505 | | fn readAddress(in_stream: anytype, endian: std.builtin.Endian, is_64: bool) !u64 { |
| 506 | | return nosuspend if (is_64) |
| 507 | | try in_stream.readInt(u64, endian) |
| 508 | | else |
| 509 | | @as(u64, try in_stream.readInt(u32, endian)); |
| 510 | | } |
| 511 | | |
| 512 | | fn parseFormValueBlockLen(allocator: mem.Allocator, in_stream: anytype, size: usize) !FormValue { |
| 513 | | const buf = try readAllocBytes(allocator, in_stream, size); |
| 514 | | return FormValue{ .Block = buf }; |
| 515 | | } |
| 516 | | |
| 517 | | // TODO the nosuspends here are workarounds |
| 518 | | fn parseFormValueBlock(allocator: mem.Allocator, in_stream: anytype, endian: std.builtin.Endian, size: usize) !FormValue { |
| 519 | | const block_len = try nosuspend in_stream.readVarInt(usize, endian, size); |
| 520 | | return parseFormValueBlockLen(allocator, in_stream, block_len); |
| 521 | | } |
| 522 | | |
| 523 | | fn parseFormValueConstant(in_stream: anytype, signed: bool, endian: std.builtin.Endian, comptime size: i32) !FormValue { |
| 524 | | // TODO: Please forgive me, I've worked around zig not properly spilling some intermediate values here. |
| 525 | | // `nosuspend` should be removed from all the function calls once it is fixed. |
| 526 | | return FormValue{ |
| 527 | | .Const = Constant{ |
| 528 | | .signed = signed, |
| 529 | | .payload = switch (size) { |
| 530 | | 1 => try nosuspend in_stream.readInt(u8, endian), |
| 531 | | 2 => try nosuspend in_stream.readInt(u16, endian), |
| 532 | | 4 => try nosuspend in_stream.readInt(u32, endian), |
| 533 | | 8 => try nosuspend in_stream.readInt(u64, endian), |
| 534 | | -1 => blk: { |
| 535 | | if (signed) { |
| 536 | | const x = try nosuspend leb.readILEB128(i64, in_stream); |
| 537 | | break :blk @as(u64, @bitCast(x)); |
| 538 | | } else { |
| 539 | | const x = try nosuspend leb.readULEB128(u64, in_stream); |
| 540 | | break :blk x; |
| 541 | | } |
| 542 | | }, |
| 543 | | else => @compileError("Invalid size"), |
| 544 | | }, |
| 471 | const UnitHeader = struct { |
| 472 | format: Format, |
| 473 | header_length: u4, |
| 474 | unit_length: u64, |
| 475 | }; |
| 476 | fn readUnitHeader(fbr: *FixedBufferReader) !UnitHeader { |
| 477 | return switch (try fbr.readInt(u32)) { |
| 478 | 0...0xfffffff0 - 1 => |unit_length| .{ |
| 479 | .format = .@"32", |
| 480 | .header_length = 4, |
| 481 | .unit_length = unit_length, |
| 545 | 482 | }, |
| 546 | | }; |
| 547 | | } |
| 548 | | |
| 549 | | // TODO the nosuspends here are workarounds |
| 550 | | fn parseFormValueRef(in_stream: anytype, endian: std.builtin.Endian, size: i32) !FormValue { |
| 551 | | return FormValue{ |
| 552 | | .Ref = switch (size) { |
| 553 | | 1 => try nosuspend in_stream.readInt(u8, endian), |
| 554 | | 2 => try nosuspend in_stream.readInt(u16, endian), |
| 555 | | 4 => try nosuspend in_stream.readInt(u32, endian), |
| 556 | | 8 => try nosuspend in_stream.readInt(u64, endian), |
| 557 | | -1 => try nosuspend leb.readULEB128(u64, in_stream), |
| 558 | | else => unreachable, |
| 483 | 0xfffffff0...0xffffffff - 1 => badDwarf(), |
| 484 | 0xffffffff => .{ |
| 485 | .format = .@"64", |
| 486 | .header_length = 12, |
| 487 | .unit_length = try fbr.readInt(u64), |
| 559 | 488 | }, |
| 560 | 489 | }; |
| 561 | 490 | } |
| 562 | 491 | |
| 563 | | // TODO the nosuspends here are workarounds |
| 564 | | fn parseFormValue(allocator: mem.Allocator, in_stream: anytype, form_id: u64, endian: std.builtin.Endian, is_64: bool) anyerror!FormValue { |
| 492 | fn parseFormValue( |
| 493 | fbr: *FixedBufferReader, |
| 494 | form_id: u64, |
| 495 | format: Format, |
| 496 | implicit_const: ?i64, |
| 497 | ) anyerror!FormValue { |
| 565 | 498 | return switch (form_id) { |
| 566 | | FORM.addr => FormValue{ .Address = try readAddress(in_stream, endian, @sizeOf(usize) == 8) }, |
| 567 | | FORM.addrx1 => return FormValue{ .AddrOffset = try in_stream.readInt(u8, endian) }, |
| 568 | | FORM.addrx2 => return FormValue{ .AddrOffset = try in_stream.readInt(u16, endian) }, |
| 569 | | FORM.addrx3 => return FormValue{ .AddrOffset = try in_stream.readInt(u24, endian) }, |
| 570 | | FORM.addrx4 => return FormValue{ .AddrOffset = try in_stream.readInt(u32, endian) }, |
| 571 | | FORM.addrx => return FormValue{ .AddrOffset = try nosuspend leb.readULEB128(usize, in_stream) }, |
| 572 | | |
| 573 | | FORM.block1 => parseFormValueBlock(allocator, in_stream, endian, 1), |
| 574 | | FORM.block2 => parseFormValueBlock(allocator, in_stream, endian, 2), |
| 575 | | FORM.block4 => parseFormValueBlock(allocator, in_stream, endian, 4), |
| 576 | | FORM.block => { |
| 577 | | const block_len = try nosuspend leb.readULEB128(usize, in_stream); |
| 578 | | return parseFormValueBlockLen(allocator, in_stream, block_len); |
| 579 | | }, |
| 580 | | FORM.data1 => parseFormValueConstant(in_stream, false, endian, 1), |
| 581 | | FORM.data2 => parseFormValueConstant(in_stream, false, endian, 2), |
| 582 | | FORM.data4 => parseFormValueConstant(in_stream, false, endian, 4), |
| 583 | | FORM.data8 => parseFormValueConstant(in_stream, false, endian, 8), |
| 584 | | FORM.data16 => { |
| 585 | | var buf: [16]u8 = undefined; |
| 586 | | if ((try nosuspend in_stream.readAll(&buf)) < 16) return error.EndOfFile; |
| 587 | | return FormValue{ .data16 = buf }; |
| 588 | | }, |
| 589 | | FORM.udata, FORM.sdata => { |
| 590 | | const signed = form_id == FORM.sdata; |
| 591 | | return parseFormValueConstant(in_stream, signed, endian, -1); |
| 592 | | }, |
| 593 | | FORM.exprloc => { |
| 594 | | const size = try nosuspend leb.readULEB128(usize, in_stream); |
| 595 | | const buf = try readAllocBytes(allocator, in_stream, size); |
| 596 | | return FormValue{ .ExprLoc = buf }; |
| 597 | | }, |
| 598 | | FORM.flag => FormValue{ .Flag = (try nosuspend in_stream.readByte()) != 0 }, |
| 599 | | FORM.flag_present => FormValue{ .Flag = true }, |
| 600 | | FORM.sec_offset => FormValue{ .SecOffset = try readAddress(in_stream, endian, is_64) }, |
| 601 | | |
| 602 | | FORM.ref1 => parseFormValueRef(in_stream, endian, 1), |
| 603 | | FORM.ref2 => parseFormValueRef(in_stream, endian, 2), |
| 604 | | FORM.ref4 => parseFormValueRef(in_stream, endian, 4), |
| 605 | | FORM.ref8 => parseFormValueRef(in_stream, endian, 8), |
| 606 | | FORM.ref_udata => parseFormValueRef(in_stream, endian, -1), |
| 607 | | |
| 608 | | FORM.ref_addr => FormValue{ .RefAddr = try readAddress(in_stream, endian, is_64) }, |
| 609 | | FORM.ref_sig8 => FormValue{ .Ref = try nosuspend in_stream.readInt(u64, endian) }, |
| 610 | | |
| 611 | | FORM.string => FormValue{ .String = try in_stream.readUntilDelimiterAlloc(allocator, 0, math.maxInt(usize)) }, |
| 612 | | FORM.strp => FormValue{ .StrPtr = try readAddress(in_stream, endian, is_64) }, |
| 613 | | FORM.strx1 => return FormValue{ .StrOffset = try in_stream.readInt(u8, endian) }, |
| 614 | | FORM.strx2 => return FormValue{ .StrOffset = try in_stream.readInt(u16, endian) }, |
| 615 | | FORM.strx3 => return FormValue{ .StrOffset = try in_stream.readInt(u24, endian) }, |
| 616 | | FORM.strx4 => return FormValue{ .StrOffset = try in_stream.readInt(u32, endian) }, |
| 617 | | FORM.strx => return FormValue{ .StrOffset = try nosuspend leb.readULEB128(usize, in_stream) }, |
| 618 | | FORM.line_strp => FormValue{ .LineStrPtr = try readAddress(in_stream, endian, is_64) }, |
| 619 | | FORM.indirect => { |
| 620 | | const child_form_id = try nosuspend leb.readULEB128(u64, in_stream); |
| 621 | | if (true) { |
| 622 | | return parseFormValue(allocator, in_stream, child_form_id, endian, is_64); |
| 623 | | } |
| 624 | | const F = @TypeOf(async parseFormValue(allocator, in_stream, child_form_id, endian, is_64)); |
| 625 | | const frame = try allocator.create(F); |
| 626 | | defer allocator.destroy(frame); |
| 627 | | return await @asyncCall(frame, {}, parseFormValue, .{ allocator, in_stream, child_form_id, endian, is_64 }); |
| 628 | | }, |
| 629 | | FORM.implicit_const => FormValue{ .Const = Constant{ .signed = true, .payload = undefined } }, |
| 630 | | FORM.loclistx => return FormValue{ .LocListOffset = try nosuspend leb.readULEB128(u64, in_stream) }, |
| 631 | | FORM.rnglistx => return FormValue{ .RangeListOffset = try nosuspend leb.readULEB128(u64, in_stream) }, |
| 499 | FORM.addr => .{ .addr = try fbr.readAddress(switch (@bitSizeOf(usize)) { |
| 500 | 32 => .@"32", |
| 501 | 64 => .@"64", |
| 502 | else => @compileError("unsupported @sizeOf(usize)"), |
| 503 | }) }, |
| 504 | FORM.addrx1 => .{ .addrx = try fbr.readInt(u8) }, |
| 505 | FORM.addrx2 => .{ .addrx = try fbr.readInt(u16) }, |
| 506 | FORM.addrx3 => .{ .addrx = try fbr.readInt(u24) }, |
| 507 | FORM.addrx4 => .{ .addrx = try fbr.readInt(u32) }, |
| 508 | FORM.addrx => .{ .addrx = try fbr.readUleb128(usize) }, |
| 509 | |
| 510 | FORM.block1, |
| 511 | FORM.block2, |
| 512 | FORM.block4, |
| 513 | FORM.block, |
| 514 | => .{ .block = try fbr.readBytes(switch (form_id) { |
| 515 | FORM.block1 => try fbr.readInt(u8), |
| 516 | FORM.block2 => try fbr.readInt(u16), |
| 517 | FORM.block4 => try fbr.readInt(u32), |
| 518 | FORM.block => try fbr.readUleb128(usize), |
| 519 | else => unreachable, |
| 520 | }) }, |
| 521 | |
| 522 | FORM.data1 => .{ .udata = try fbr.readInt(u8) }, |
| 523 | FORM.data2 => .{ .udata = try fbr.readInt(u16) }, |
| 524 | FORM.data4 => .{ .udata = try fbr.readInt(u32) }, |
| 525 | FORM.data8 => .{ .udata = try fbr.readInt(u64) }, |
| 526 | FORM.data16 => .{ .data16 = (try fbr.readBytes(16))[0..16] }, |
| 527 | FORM.udata => .{ .udata = try fbr.readUleb128(u64) }, |
| 528 | FORM.sdata => .{ .sdata = try fbr.readIleb128(i64) }, |
| 529 | FORM.exprloc => .{ .exprloc = try fbr.readBytes(try fbr.readUleb128(usize)) }, |
| 530 | FORM.flag => .{ .flag = (try fbr.readByte()) != 0 }, |
| 531 | FORM.flag_present => .{ .flag = true }, |
| 532 | FORM.sec_offset => .{ .sec_offset = try fbr.readAddress(format) }, |
| 533 | |
| 534 | FORM.ref1 => .{ .ref = try fbr.readInt(u8) }, |
| 535 | FORM.ref2 => .{ .ref = try fbr.readInt(u16) }, |
| 536 | FORM.ref4 => .{ .ref = try fbr.readInt(u32) }, |
| 537 | FORM.ref8 => .{ .ref = try fbr.readInt(u64) }, |
| 538 | FORM.ref_udata => .{ .ref = try fbr.readUleb128(u64) }, |
| 539 | |
| 540 | FORM.ref_addr => .{ .ref_addr = try fbr.readAddress(format) }, |
| 541 | FORM.ref_sig8 => .{ .ref = try fbr.readInt(u64) }, |
| 542 | |
| 543 | FORM.string => .{ .string = try fbr.readBytesTo(0) }, |
| 544 | FORM.strp => .{ .strp = try fbr.readAddress(format) }, |
| 545 | FORM.strx1 => .{ .strx = try fbr.readInt(u8) }, |
| 546 | FORM.strx2 => .{ .strx = try fbr.readInt(u16) }, |
| 547 | FORM.strx3 => .{ .strx = try fbr.readInt(u24) }, |
| 548 | FORM.strx4 => .{ .strx = try fbr.readInt(u32) }, |
| 549 | FORM.strx => .{ .strx = try fbr.readUleb128(usize) }, |
| 550 | FORM.line_strp => .{ .line_strp = try fbr.readAddress(format) }, |
| 551 | FORM.indirect => parseFormValue(fbr, try fbr.readUleb128(u64), format, implicit_const), |
| 552 | FORM.implicit_const => .{ .sdata = implicit_const orelse return badDwarf() }, |
| 553 | FORM.loclistx => .{ .loclistx = try fbr.readUleb128(u64) }, |
| 554 | FORM.rnglistx => .{ .rnglistx = try fbr.readUleb128(u64) }, |
| 632 | 555 | else => { |
| 633 | | //std.debug.print("unrecognized form id: {x}\n", .{form_id}); |
| 556 | //debug.print("unrecognized form id: {x}\n", .{form_id}); |
| 634 | 557 | return badDwarf(); |
| 635 | 558 | }, |
| 636 | 559 | }; |
| 637 | 560 | } |
| 638 | 561 | |
| 639 | | fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*const AbbrevTableEntry { |
| 640 | | for (abbrev_table.items) |*table_entry| { |
| 641 | | if (table_entry.abbrev_code == abbrev_code) return table_entry; |
| 642 | | } |
| 643 | | return null; |
| 644 | | } |
| 645 | | |
| 646 | 562 | pub const DwarfSection = enum { |
| 647 | 563 | debug_info, |
| 648 | 564 | debug_abbrev, |
| ... | ... | @@ -690,7 +606,7 @@ pub const DwarfInfo = struct { |
| 690 | 606 | is_macho: bool, |
| 691 | 607 | |
| 692 | 608 | // Filled later by the initializer |
| 693 | | abbrev_table_list: std.ArrayListUnmanaged(AbbrevTableHeader) = .{}, |
| 609 | abbrev_table_list: std.ArrayListUnmanaged(Abbrev.Table) = .{}, |
| 694 | 610 | compile_unit_list: std.ArrayListUnmanaged(CompileUnit) = .{}, |
| 695 | 611 | func_list: std.ArrayListUnmanaged(Func) = .{}, |
| 696 | 612 | |
| ... | ... | @@ -713,17 +629,17 @@ pub const DwarfInfo = struct { |
| 713 | 629 | if (opt_section) |s| if (s.owned) allocator.free(s.data); |
| 714 | 630 | } |
| 715 | 631 | for (di.abbrev_table_list.items) |*abbrev| { |
| 716 | | abbrev.deinit(); |
| 632 | abbrev.deinit(allocator); |
| 717 | 633 | } |
| 718 | 634 | di.abbrev_table_list.deinit(allocator); |
| 719 | 635 | for (di.compile_unit_list.items) |*cu| { |
| 720 | 636 | cu.die.deinit(allocator); |
| 721 | | allocator.destroy(cu.die); |
| 722 | 637 | } |
| 723 | 638 | di.compile_unit_list.deinit(allocator); |
| 724 | 639 | di.func_list.deinit(allocator); |
| 725 | 640 | di.cie_map.deinit(allocator); |
| 726 | 641 | di.fde_list.deinit(allocator); |
| 642 | di.* = undefined; |
| 727 | 643 | } |
| 728 | 644 | |
| 729 | 645 | pub fn getSymbolName(di: *DwarfInfo, address: u64) ?[]const u8 { |
| ... | ... | @@ -739,102 +655,125 @@ pub const DwarfInfo = struct { |
| 739 | 655 | } |
| 740 | 656 | |
| 741 | 657 | fn scanAllFunctions(di: *DwarfInfo, allocator: mem.Allocator) !void { |
| 742 | | var stream = io.fixedBufferStream(di.section(.debug_info).?); |
| 743 | | const in = stream.reader(); |
| 744 | | const seekable = stream.seekableStream(); |
| 658 | var fbr: FixedBufferReader = .{ .buf = di.section(.debug_info).?, .endian = di.endian }; |
| 745 | 659 | var this_unit_offset: u64 = 0; |
| 746 | 660 | |
| 747 | | var tmp_arena = std.heap.ArenaAllocator.init(allocator); |
| 748 | | defer tmp_arena.deinit(); |
| 749 | | const arena = tmp_arena.allocator(); |
| 661 | while (this_unit_offset < fbr.buf.len) { |
| 662 | try fbr.seekTo(this_unit_offset); |
| 750 | 663 | |
| 751 | | while (this_unit_offset < try seekable.getEndPos()) { |
| 752 | | try seekable.seekTo(this_unit_offset); |
| 664 | const unit_header = try readUnitHeader(&fbr); |
| 665 | if (unit_header.unit_length == 0) return; |
| 666 | const next_offset = unit_header.header_length + unit_header.unit_length; |
| 753 | 667 | |
| 754 | | var is_64: bool = undefined; |
| 755 | | const unit_length = try readUnitLength(in, di.endian, &is_64); |
| 756 | | if (unit_length == 0) return; |
| 757 | | const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4)); |
| 758 | | |
| 759 | | const version = try in.readInt(u16, di.endian); |
| 668 | const version = try fbr.readInt(u16); |
| 760 | 669 | if (version < 2 or version > 5) return badDwarf(); |
| 761 | 670 | |
| 762 | 671 | var address_size: u8 = undefined; |
| 763 | 672 | var debug_abbrev_offset: u64 = undefined; |
| 764 | 673 | if (version >= 5) { |
| 765 | | const unit_type = try in.readInt(u8, di.endian); |
| 674 | const unit_type = try fbr.readInt(u8); |
| 766 | 675 | if (unit_type != UT.compile) return badDwarf(); |
| 767 | | address_size = try in.readByte(); |
| 768 | | debug_abbrev_offset = if (is_64) |
| 769 | | try in.readInt(u64, di.endian) |
| 770 | | else |
| 771 | | try in.readInt(u32, di.endian); |
| 676 | address_size = try fbr.readByte(); |
| 677 | debug_abbrev_offset = try fbr.readAddress(unit_header.format); |
| 772 | 678 | } else { |
| 773 | | debug_abbrev_offset = if (is_64) |
| 774 | | try in.readInt(u64, di.endian) |
| 775 | | else |
| 776 | | try in.readInt(u32, di.endian); |
| 777 | | address_size = try in.readByte(); |
| 679 | debug_abbrev_offset = try fbr.readAddress(unit_header.format); |
| 680 | address_size = try fbr.readByte(); |
| 778 | 681 | } |
| 779 | 682 | if (address_size != @sizeOf(usize)) return badDwarf(); |
| 780 | 683 | |
| 781 | | const compile_unit_pos = try seekable.getPos(); |
| 782 | 684 | const abbrev_table = try di.getAbbrevTable(allocator, debug_abbrev_offset); |
| 783 | 685 | |
| 784 | | try seekable.seekTo(compile_unit_pos); |
| 686 | var max_attrs: usize = 0; |
| 687 | var zig_padding_abbrev_code: u7 = 0; |
| 688 | for (abbrev_table.abbrevs) |abbrev| { |
| 689 | max_attrs = @max(max_attrs, abbrev.attrs.len); |
| 690 | if (math.cast(u7, abbrev.code)) |code| { |
| 691 | if (abbrev.tag_id == TAG.ZIG_padding and |
| 692 | !abbrev.has_children and |
| 693 | abbrev.attrs.len == 0) |
| 694 | { |
| 695 | zig_padding_abbrev_code = code; |
| 696 | } |
| 697 | } |
| 698 | } |
| 699 | const attrs_buf = try allocator.alloc(Die.Attr, max_attrs * 3); |
| 700 | defer allocator.free(attrs_buf); |
| 701 | var attrs_bufs: [3][]Die.Attr = undefined; |
| 702 | for (&attrs_bufs, 0..) |*buf, index| buf.* = attrs_buf[index * max_attrs ..][0..max_attrs]; |
| 785 | 703 | |
| 786 | 704 | const next_unit_pos = this_unit_offset + next_offset; |
| 787 | 705 | |
| 788 | | var compile_unit: CompileUnit = undefined; |
| 706 | var compile_unit: CompileUnit = .{ |
| 707 | .version = version, |
| 708 | .format = unit_header.format, |
| 709 | .die = undefined, |
| 710 | .pc_range = null, |
| 789 | 711 | |
| 790 | | while ((try seekable.getPos()) < next_unit_pos) { |
| 791 | | var die_obj = (try di.parseDie(arena, in, abbrev_table, is_64)) orelse continue; |
| 792 | | const after_die_offset = try seekable.getPos(); |
| 712 | .str_offsets_base = 0, |
| 713 | .addr_base = 0, |
| 714 | .rnglists_base = 0, |
| 715 | .loclists_base = 0, |
| 716 | .frame_base = null, |
| 717 | }; |
| 718 | |
| 719 | while (true) { |
| 720 | fbr.pos = mem.indexOfNonePos(u8, fbr.buf, fbr.pos, &.{ |
| 721 | zig_padding_abbrev_code, 0, |
| 722 | }) orelse fbr.buf.len; |
| 723 | if (fbr.pos >= next_unit_pos) break; |
| 724 | var die_obj = (try parseDie( |
| 725 | &fbr, |
| 726 | attrs_bufs[0], |
| 727 | abbrev_table, |
| 728 | unit_header.format, |
| 729 | )) orelse continue; |
| 793 | 730 | |
| 794 | 731 | switch (die_obj.tag_id) { |
| 795 | 732 | TAG.compile_unit => { |
| 796 | | compile_unit = .{ |
| 797 | | .version = version, |
| 798 | | .is_64 = is_64, |
| 799 | | .die = &die_obj, |
| 800 | | .pc_range = null, |
| 801 | | |
| 802 | | .str_offsets_base = if (die_obj.getAttr(AT.str_offsets_base)) |fv| try fv.getUInt(usize) else 0, |
| 803 | | .addr_base = if (die_obj.getAttr(AT.addr_base)) |fv| try fv.getUInt(usize) else 0, |
| 804 | | .rnglists_base = if (die_obj.getAttr(AT.rnglists_base)) |fv| try fv.getUInt(usize) else 0, |
| 805 | | .loclists_base = if (die_obj.getAttr(AT.loclists_base)) |fv| try fv.getUInt(usize) else 0, |
| 806 | | .frame_base = die_obj.getAttr(AT.frame_base), |
| 807 | | }; |
| 733 | compile_unit.die = die_obj; |
| 734 | compile_unit.die.attrs = attrs_bufs[1][0..die_obj.attrs.len]; |
| 735 | @memcpy(compile_unit.die.attrs, die_obj.attrs); |
| 736 | |
| 737 | compile_unit.str_offsets_base = if (die_obj.getAttr(AT.str_offsets_base)) |fv| try fv.getUInt(usize) else 0; |
| 738 | compile_unit.addr_base = if (die_obj.getAttr(AT.addr_base)) |fv| try fv.getUInt(usize) else 0; |
| 739 | compile_unit.rnglists_base = if (die_obj.getAttr(AT.rnglists_base)) |fv| try fv.getUInt(usize) else 0; |
| 740 | compile_unit.loclists_base = if (die_obj.getAttr(AT.loclists_base)) |fv| try fv.getUInt(usize) else 0; |
| 741 | compile_unit.frame_base = die_obj.getAttr(AT.frame_base); |
| 808 | 742 | }, |
| 809 | 743 | TAG.subprogram, TAG.inlined_subroutine, TAG.subroutine, TAG.entry_point => { |
| 810 | 744 | const fn_name = x: { |
| 811 | | var depth: i32 = 3; |
| 812 | 745 | var this_die_obj = die_obj; |
| 813 | 746 | // Prevent endless loops |
| 814 | | while (depth > 0) : (depth -= 1) { |
| 747 | for (0..3) |_| { |
| 815 | 748 | if (this_die_obj.getAttr(AT.name)) |_| { |
| 816 | 749 | break :x try this_die_obj.getAttrString(di, AT.name, di.section(.debug_str), compile_unit); |
| 817 | 750 | } else if (this_die_obj.getAttr(AT.abstract_origin)) |_| { |
| 751 | const after_die_offset = fbr.pos; |
| 752 | defer fbr.pos = after_die_offset; |
| 753 | |
| 818 | 754 | // Follow the DIE it points to and repeat |
| 819 | 755 | const ref_offset = try this_die_obj.getAttrRef(AT.abstract_origin); |
| 820 | 756 | if (ref_offset > next_offset) return badDwarf(); |
| 821 | | try seekable.seekTo(this_unit_offset + ref_offset); |
| 822 | | this_die_obj = (try di.parseDie( |
| 823 | | arena, |
| 824 | | in, |
| 757 | try fbr.seekTo(this_unit_offset + ref_offset); |
| 758 | this_die_obj = (try parseDie( |
| 759 | &fbr, |
| 760 | attrs_bufs[2], |
| 825 | 761 | abbrev_table, |
| 826 | | is_64, |
| 762 | unit_header.format, |
| 827 | 763 | )) orelse return badDwarf(); |
| 828 | 764 | } else if (this_die_obj.getAttr(AT.specification)) |_| { |
| 765 | const after_die_offset = fbr.pos; |
| 766 | defer fbr.pos = after_die_offset; |
| 767 | |
| 829 | 768 | // Follow the DIE it points to and repeat |
| 830 | 769 | const ref_offset = try this_die_obj.getAttrRef(AT.specification); |
| 831 | 770 | if (ref_offset > next_offset) return badDwarf(); |
| 832 | | try seekable.seekTo(this_unit_offset + ref_offset); |
| 833 | | this_die_obj = (try di.parseDie( |
| 834 | | arena, |
| 835 | | in, |
| 771 | try fbr.seekTo(this_unit_offset + ref_offset); |
| 772 | this_die_obj = (try parseDie( |
| 773 | &fbr, |
| 774 | attrs_bufs[2], |
| 836 | 775 | abbrev_table, |
| 837 | | is_64, |
| 776 | unit_header.format, |
| 838 | 777 | )) orelse return badDwarf(); |
| 839 | 778 | } else { |
| 840 | 779 | break :x null; |
| ... | ... | @@ -847,15 +786,12 @@ pub const DwarfInfo = struct { |
| 847 | 786 | var range_added = if (die_obj.getAttrAddr(di, AT.low_pc, compile_unit)) |low_pc| blk: { |
| 848 | 787 | if (die_obj.getAttr(AT.high_pc)) |high_pc_value| { |
| 849 | 788 | const pc_end = switch (high_pc_value.*) { |
| 850 | | FormValue.Address => |value| value, |
| 851 | | FormValue.Const => |value| b: { |
| 852 | | const offset = try value.asUnsignedLe(); |
| 853 | | break :b (low_pc + offset); |
| 854 | | }, |
| 789 | .addr => |value| value, |
| 790 | .udata => |offset| low_pc + offset, |
| 855 | 791 | else => return badDwarf(), |
| 856 | 792 | }; |
| 857 | 793 | |
| 858 | | try di.func_list.append(allocator, Func{ |
| 794 | try di.func_list.append(allocator, .{ |
| 859 | 795 | .name = fn_name, |
| 860 | 796 | .pc_range = .{ |
| 861 | 797 | .start = low_pc, |
| ... | ... | @@ -880,7 +816,7 @@ pub const DwarfInfo = struct { |
| 880 | 816 | |
| 881 | 817 | while (try iter.next()) |range| { |
| 882 | 818 | range_added = true; |
| 883 | | try di.func_list.append(allocator, Func{ |
| 819 | try di.func_list.append(allocator, .{ |
| 884 | 820 | .name = fn_name, |
| 885 | 821 | .pc_range = .{ |
| 886 | 822 | .start = range.start_addr, |
| ... | ... | @@ -891,7 +827,7 @@ pub const DwarfInfo = struct { |
| 891 | 827 | } |
| 892 | 828 | |
| 893 | 829 | if (fn_name != null and !range_added) { |
| 894 | | try di.func_list.append(allocator, Func{ |
| 830 | try di.func_list.append(allocator, .{ |
| 895 | 831 | .name = fn_name, |
| 896 | 832 | .pc_range = null, |
| 897 | 833 | }); |
| ... | ... | @@ -899,8 +835,6 @@ pub const DwarfInfo = struct { |
| 899 | 835 | }, |
| 900 | 836 | else => {}, |
| 901 | 837 | } |
| 902 | | |
| 903 | | try seekable.seekTo(after_die_offset); |
| 904 | 838 | } |
| 905 | 839 | |
| 906 | 840 | this_unit_offset += next_offset; |
| ... | ... | @@ -908,56 +842,57 @@ pub const DwarfInfo = struct { |
| 908 | 842 | } |
| 909 | 843 | |
| 910 | 844 | fn scanAllCompileUnits(di: *DwarfInfo, allocator: mem.Allocator) !void { |
| 911 | | var stream = io.fixedBufferStream(di.section(.debug_info).?); |
| 912 | | const in = stream.reader(); |
| 913 | | const seekable = stream.seekableStream(); |
| 845 | var fbr: FixedBufferReader = .{ .buf = di.section(.debug_info).?, .endian = di.endian }; |
| 914 | 846 | var this_unit_offset: u64 = 0; |
| 915 | 847 | |
| 916 | | while (this_unit_offset < try seekable.getEndPos()) { |
| 917 | | try seekable.seekTo(this_unit_offset); |
| 848 | var attrs_buf = std.ArrayList(Die.Attr).init(allocator); |
| 849 | defer attrs_buf.deinit(); |
| 850 | |
| 851 | while (this_unit_offset < fbr.buf.len) { |
| 852 | try fbr.seekTo(this_unit_offset); |
| 918 | 853 | |
| 919 | | var is_64: bool = undefined; |
| 920 | | const unit_length = try readUnitLength(in, di.endian, &is_64); |
| 921 | | if (unit_length == 0) return; |
| 922 | | const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4)); |
| 854 | const unit_header = try readUnitHeader(&fbr); |
| 855 | if (unit_header.unit_length == 0) return; |
| 856 | const next_offset = unit_header.header_length + unit_header.unit_length; |
| 923 | 857 | |
| 924 | | const version = try in.readInt(u16, di.endian); |
| 858 | const version = try fbr.readInt(u16); |
| 925 | 859 | if (version < 2 or version > 5) return badDwarf(); |
| 926 | 860 | |
| 927 | 861 | var address_size: u8 = undefined; |
| 928 | 862 | var debug_abbrev_offset: u64 = undefined; |
| 929 | 863 | if (version >= 5) { |
| 930 | | const unit_type = try in.readInt(u8, di.endian); |
| 864 | const unit_type = try fbr.readInt(u8); |
| 931 | 865 | if (unit_type != UT.compile) return badDwarf(); |
| 932 | | address_size = try in.readByte(); |
| 933 | | debug_abbrev_offset = if (is_64) |
| 934 | | try in.readInt(u64, di.endian) |
| 935 | | else |
| 936 | | try in.readInt(u32, di.endian); |
| 866 | address_size = try fbr.readByte(); |
| 867 | debug_abbrev_offset = try fbr.readAddress(unit_header.format); |
| 937 | 868 | } else { |
| 938 | | debug_abbrev_offset = if (is_64) |
| 939 | | try in.readInt(u64, di.endian) |
| 940 | | else |
| 941 | | try in.readInt(u32, di.endian); |
| 942 | | address_size = try in.readByte(); |
| 869 | debug_abbrev_offset = try fbr.readAddress(unit_header.format); |
| 870 | address_size = try fbr.readByte(); |
| 943 | 871 | } |
| 944 | 872 | if (address_size != @sizeOf(usize)) return badDwarf(); |
| 945 | 873 | |
| 946 | | const compile_unit_pos = try seekable.getPos(); |
| 947 | 874 | const abbrev_table = try di.getAbbrevTable(allocator, debug_abbrev_offset); |
| 948 | 875 | |
| 949 | | try seekable.seekTo(compile_unit_pos); |
| 876 | var max_attrs: usize = 0; |
| 877 | for (abbrev_table.abbrevs) |abbrev| { |
| 878 | max_attrs = @max(max_attrs, abbrev.attrs.len); |
| 879 | } |
| 880 | try attrs_buf.resize(max_attrs); |
| 950 | 881 | |
| 951 | | const compile_unit_die = try allocator.create(Die); |
| 952 | | errdefer allocator.destroy(compile_unit_die); |
| 953 | | compile_unit_die.* = (try di.parseDie(allocator, in, abbrev_table, is_64)) orelse |
| 954 | | return badDwarf(); |
| 882 | var compile_unit_die = (try parseDie( |
| 883 | &fbr, |
| 884 | attrs_buf.items, |
| 885 | abbrev_table, |
| 886 | unit_header.format, |
| 887 | )) orelse return badDwarf(); |
| 955 | 888 | |
| 956 | 889 | if (compile_unit_die.tag_id != TAG.compile_unit) return badDwarf(); |
| 957 | 890 | |
| 891 | compile_unit_die.attrs = try allocator.dupe(Die.Attr, compile_unit_die.attrs); |
| 892 | |
| 958 | 893 | var compile_unit: CompileUnit = .{ |
| 959 | 894 | .version = version, |
| 960 | | .is_64 = is_64, |
| 895 | .format = unit_header.format, |
| 961 | 896 | .pc_range = null, |
| 962 | 897 | .die = compile_unit_die, |
| 963 | 898 | .str_offsets_base = if (compile_unit_die.getAttr(AT.str_offsets_base)) |fv| try fv.getUInt(usize) else 0, |
| ... | ... | @@ -971,11 +906,8 @@ pub const DwarfInfo = struct { |
| 971 | 906 | if (compile_unit_die.getAttrAddr(di, AT.low_pc, compile_unit)) |low_pc| { |
| 972 | 907 | if (compile_unit_die.getAttr(AT.high_pc)) |high_pc_value| { |
| 973 | 908 | const pc_end = switch (high_pc_value.*) { |
| 974 | | FormValue.Address => |value| value, |
| 975 | | FormValue.Const => |value| b: { |
| 976 | | const offset = try value.asUnsignedLe(); |
| 977 | | break :b (low_pc + offset); |
| 978 | | }, |
| 909 | .addr => |value| value, |
| 910 | .udata => |offset| low_pc + offset, |
| 979 | 911 | else => return badDwarf(), |
| 980 | 912 | }; |
| 981 | 913 | break :x PcRange{ |
| ... | ... | @@ -1002,40 +934,39 @@ pub const DwarfInfo = struct { |
| 1002 | 934 | section_type: DwarfSection, |
| 1003 | 935 | di: *const DwarfInfo, |
| 1004 | 936 | compile_unit: *const CompileUnit, |
| 1005 | | stream: io.FixedBufferStream([]const u8), |
| 937 | fbr: FixedBufferReader, |
| 1006 | 938 | |
| 1007 | 939 | pub fn init(ranges_value: *const FormValue, di: *const DwarfInfo, compile_unit: *const CompileUnit) !@This() { |
| 1008 | 940 | const section_type = if (compile_unit.version >= 5) DwarfSection.debug_rnglists else DwarfSection.debug_ranges; |
| 1009 | 941 | const debug_ranges = di.section(section_type) orelse return error.MissingDebugInfo; |
| 1010 | 942 | |
| 1011 | 943 | const ranges_offset = switch (ranges_value.*) { |
| 1012 | | .SecOffset => |off| off, |
| 1013 | | .Const => |c| try c.asUnsignedLe(), |
| 1014 | | .RangeListOffset => |idx| off: { |
| 1015 | | if (compile_unit.is_64) { |
| 1016 | | const offset_loc = @as(usize, @intCast(compile_unit.rnglists_base + 8 * idx)); |
| 1017 | | if (offset_loc + 8 > debug_ranges.len) return badDwarf(); |
| 1018 | | const offset = mem.readInt(u64, debug_ranges[offset_loc..][0..8], di.endian); |
| 1019 | | break :off compile_unit.rnglists_base + offset; |
| 1020 | | } else { |
| 1021 | | const offset_loc = @as(usize, @intCast(compile_unit.rnglists_base + 4 * idx)); |
| 1022 | | if (offset_loc + 4 > debug_ranges.len) return badDwarf(); |
| 1023 | | const offset = mem.readInt(u32, debug_ranges[offset_loc..][0..4], di.endian); |
| 1024 | | break :off compile_unit.rnglists_base + offset; |
| 944 | .sec_offset, .udata => |off| off, |
| 945 | .rnglistx => |idx| off: { |
| 946 | switch (compile_unit.format) { |
| 947 | .@"32" => { |
| 948 | const offset_loc = @as(usize, @intCast(compile_unit.rnglists_base + 4 * idx)); |
| 949 | if (offset_loc + 4 > debug_ranges.len) return badDwarf(); |
| 950 | const offset = mem.readInt(u32, debug_ranges[offset_loc..][0..4], di.endian); |
| 951 | break :off compile_unit.rnglists_base + offset; |
| 952 | }, |
| 953 | .@"64" => { |
| 954 | const offset_loc = @as(usize, @intCast(compile_unit.rnglists_base + 8 * idx)); |
| 955 | if (offset_loc + 8 > debug_ranges.len) return badDwarf(); |
| 956 | const offset = mem.readInt(u64, debug_ranges[offset_loc..][0..8], di.endian); |
| 957 | break :off compile_unit.rnglists_base + offset; |
| 958 | }, |
| 1025 | 959 | } |
| 1026 | 960 | }, |
| 1027 | 961 | else => return badDwarf(), |
| 1028 | 962 | }; |
| 1029 | 963 | |
| 1030 | | var stream = io.fixedBufferStream(debug_ranges); |
| 1031 | | try stream.seekTo(ranges_offset); |
| 1032 | | |
| 1033 | 964 | // All the addresses in the list are relative to the value |
| 1034 | 965 | // specified by DW_AT.low_pc or to some other value encoded |
| 1035 | 966 | // in the list itself. |
| 1036 | 967 | // If no starting value is specified use zero. |
| 1037 | 968 | const base_address = compile_unit.die.getAttrAddr(di, AT.low_pc, compile_unit.*) catch |err| switch (err) { |
| 1038 | | error.MissingDebugInfo => @as(u64, 0), // TODO https://github.com/ziglang/zig/issues/11135 |
| 969 | error.MissingDebugInfo => 0, |
| 1039 | 970 | else => return err, |
| 1040 | 971 | }; |
| 1041 | 972 | |
| ... | ... | @@ -1044,28 +975,31 @@ pub const DwarfInfo = struct { |
| 1044 | 975 | .section_type = section_type, |
| 1045 | 976 | .di = di, |
| 1046 | 977 | .compile_unit = compile_unit, |
| 1047 | | .stream = stream, |
| 978 | .fbr = .{ |
| 979 | .buf = debug_ranges, |
| 980 | .pos = math.cast(usize, ranges_offset) orelse return badDwarf(), |
| 981 | .endian = di.endian, |
| 982 | }, |
| 1048 | 983 | }; |
| 1049 | 984 | } |
| 1050 | 985 | |
| 1051 | 986 | // Returns the next range in the list, or null if the end was reached. |
| 1052 | 987 | pub fn next(self: *@This()) !?struct { start_addr: u64, end_addr: u64 } { |
| 1053 | | const in = self.stream.reader(); |
| 1054 | 988 | switch (self.section_type) { |
| 1055 | 989 | .debug_rnglists => { |
| 1056 | | const kind = try in.readByte(); |
| 990 | const kind = try self.fbr.readByte(); |
| 1057 | 991 | switch (kind) { |
| 1058 | 992 | RLE.end_of_list => return null, |
| 1059 | 993 | RLE.base_addressx => { |
| 1060 | | const index = try leb.readULEB128(usize, in); |
| 994 | const index = try self.fbr.readUleb128(usize); |
| 1061 | 995 | self.base_address = try self.di.readDebugAddr(self.compile_unit.*, index); |
| 1062 | 996 | return try self.next(); |
| 1063 | 997 | }, |
| 1064 | 998 | RLE.startx_endx => { |
| 1065 | | const start_index = try leb.readULEB128(usize, in); |
| 999 | const start_index = try self.fbr.readUleb128(usize); |
| 1066 | 1000 | const start_addr = try self.di.readDebugAddr(self.compile_unit.*, start_index); |
| 1067 | 1001 | |
| 1068 | | const end_index = try leb.readULEB128(usize, in); |
| 1002 | const end_index = try self.fbr.readUleb128(usize); |
| 1069 | 1003 | const end_addr = try self.di.readDebugAddr(self.compile_unit.*, end_index); |
| 1070 | 1004 | |
| 1071 | 1005 | return .{ |
| ... | ... | @@ -1074,10 +1008,10 @@ pub const DwarfInfo = struct { |
| 1074 | 1008 | }; |
| 1075 | 1009 | }, |
| 1076 | 1010 | RLE.startx_length => { |
| 1077 | | const start_index = try leb.readULEB128(usize, in); |
| 1011 | const start_index = try self.fbr.readUleb128(usize); |
| 1078 | 1012 | const start_addr = try self.di.readDebugAddr(self.compile_unit.*, start_index); |
| 1079 | 1013 | |
| 1080 | | const len = try leb.readULEB128(usize, in); |
| 1014 | const len = try self.fbr.readUleb128(usize); |
| 1081 | 1015 | const end_addr = start_addr + len; |
| 1082 | 1016 | |
| 1083 | 1017 | return .{ |
| ... | ... | @@ -1086,8 +1020,8 @@ pub const DwarfInfo = struct { |
| 1086 | 1020 | }; |
| 1087 | 1021 | }, |
| 1088 | 1022 | RLE.offset_pair => { |
| 1089 | | const start_addr = try leb.readULEB128(usize, in); |
| 1090 | | const end_addr = try leb.readULEB128(usize, in); |
| 1023 | const start_addr = try self.fbr.readUleb128(usize); |
| 1024 | const end_addr = try self.fbr.readUleb128(usize); |
| 1091 | 1025 | |
| 1092 | 1026 | // This is the only kind that uses the base address |
| 1093 | 1027 | return .{ |
| ... | ... | @@ -1096,12 +1030,12 @@ pub const DwarfInfo = struct { |
| 1096 | 1030 | }; |
| 1097 | 1031 | }, |
| 1098 | 1032 | RLE.base_address => { |
| 1099 | | self.base_address = try in.readInt(usize, self.di.endian); |
| 1033 | self.base_address = try self.fbr.readInt(usize); |
| 1100 | 1034 | return try self.next(); |
| 1101 | 1035 | }, |
| 1102 | 1036 | RLE.start_end => { |
| 1103 | | const start_addr = try in.readInt(usize, self.di.endian); |
| 1104 | | const end_addr = try in.readInt(usize, self.di.endian); |
| 1037 | const start_addr = try self.fbr.readInt(usize); |
| 1038 | const end_addr = try self.fbr.readInt(usize); |
| 1105 | 1039 | |
| 1106 | 1040 | return .{ |
| 1107 | 1041 | .start_addr = start_addr, |
| ... | ... | @@ -1109,8 +1043,8 @@ pub const DwarfInfo = struct { |
| 1109 | 1043 | }; |
| 1110 | 1044 | }, |
| 1111 | 1045 | RLE.start_length => { |
| 1112 | | const start_addr = try in.readInt(usize, self.di.endian); |
| 1113 | | const len = try leb.readULEB128(usize, in); |
| 1046 | const start_addr = try self.fbr.readInt(usize); |
| 1047 | const len = try self.fbr.readUleb128(usize); |
| 1114 | 1048 | const end_addr = start_addr + len; |
| 1115 | 1049 | |
| 1116 | 1050 | return .{ |
| ... | ... | @@ -1122,8 +1056,8 @@ pub const DwarfInfo = struct { |
| 1122 | 1056 | } |
| 1123 | 1057 | }, |
| 1124 | 1058 | .debug_ranges => { |
| 1125 | | const start_addr = try in.readInt(usize, self.di.endian); |
| 1126 | | const end_addr = try in.readInt(usize, self.di.endian); |
| 1059 | const start_addr = try self.fbr.readInt(usize); |
| 1060 | const end_addr = try self.fbr.readInt(usize); |
| 1127 | 1061 | if (start_addr == 0 and end_addr == 0) return null; |
| 1128 | 1062 | |
| 1129 | 1063 | // This entry selects a new value for the base address |
| ... | ... | @@ -1160,93 +1094,96 @@ pub const DwarfInfo = struct { |
| 1160 | 1094 | |
| 1161 | 1095 | /// Gets an already existing AbbrevTable given the abbrev_offset, or if not found, |
| 1162 | 1096 | /// seeks in the stream and parses it. |
| 1163 | | fn getAbbrevTable(di: *DwarfInfo, allocator: mem.Allocator, abbrev_offset: u64) !*const AbbrevTable { |
| 1164 | | for (di.abbrev_table_list.items) |*header| { |
| 1165 | | if (header.offset == abbrev_offset) { |
| 1166 | | return &header.table; |
| 1097 | fn getAbbrevTable(di: *DwarfInfo, allocator: mem.Allocator, abbrev_offset: u64) !*const Abbrev.Table { |
| 1098 | for (di.abbrev_table_list.items) |*table| { |
| 1099 | if (table.offset == abbrev_offset) { |
| 1100 | return table; |
| 1167 | 1101 | } |
| 1168 | 1102 | } |
| 1169 | | try di.abbrev_table_list.append(allocator, AbbrevTableHeader{ |
| 1170 | | .offset = abbrev_offset, |
| 1171 | | .table = try di.parseAbbrevTable(allocator, abbrev_offset), |
| 1172 | | }); |
| 1173 | | return &di.abbrev_table_list.items[di.abbrev_table_list.items.len - 1].table; |
| 1103 | try di.abbrev_table_list.append( |
| 1104 | allocator, |
| 1105 | try di.parseAbbrevTable(allocator, abbrev_offset), |
| 1106 | ); |
| 1107 | return &di.abbrev_table_list.items[di.abbrev_table_list.items.len - 1]; |
| 1174 | 1108 | } |
| 1175 | 1109 | |
| 1176 | | fn parseAbbrevTable(di: *DwarfInfo, allocator: mem.Allocator, offset: u64) !AbbrevTable { |
| 1177 | | var stream = io.fixedBufferStream(di.section(.debug_abbrev).?); |
| 1178 | | const in = stream.reader(); |
| 1179 | | const seekable = stream.seekableStream(); |
| 1110 | fn parseAbbrevTable(di: *DwarfInfo, allocator: mem.Allocator, offset: u64) !Abbrev.Table { |
| 1111 | var fbr: FixedBufferReader = .{ |
| 1112 | .buf = di.section(.debug_abbrev).?, |
| 1113 | .pos = math.cast(usize, offset) orelse return badDwarf(), |
| 1114 | .endian = di.endian, |
| 1115 | }; |
| 1180 | 1116 | |
| 1181 | | try seekable.seekTo(offset); |
| 1182 | | var result = AbbrevTable.init(allocator); |
| 1183 | | errdefer { |
| 1184 | | for (result.items) |*entry| { |
| 1185 | | entry.attrs.deinit(); |
| 1117 | var abbrevs = std.ArrayList(Abbrev).init(allocator); |
| 1118 | defer { |
| 1119 | for (abbrevs.items) |*abbrev| { |
| 1120 | abbrev.deinit(allocator); |
| 1186 | 1121 | } |
| 1187 | | result.deinit(); |
| 1122 | abbrevs.deinit(); |
| 1188 | 1123 | } |
| 1189 | 1124 | |
| 1125 | var attrs = std.ArrayList(Abbrev.Attr).init(allocator); |
| 1126 | defer attrs.deinit(); |
| 1127 | |
| 1190 | 1128 | while (true) { |
| 1191 | | const abbrev_code = try leb.readULEB128(u64, in); |
| 1192 | | if (abbrev_code == 0) return result; |
| 1193 | | try result.append(AbbrevTableEntry{ |
| 1194 | | .abbrev_code = abbrev_code, |
| 1195 | | .tag_id = try leb.readULEB128(u64, in), |
| 1196 | | .has_children = (try in.readByte()) == CHILDREN.yes, |
| 1197 | | .attrs = std.ArrayList(AbbrevAttr).init(allocator), |
| 1198 | | }); |
| 1199 | | const attrs = &result.items[result.items.len - 1].attrs; |
| 1129 | const code = try fbr.readUleb128(u64); |
| 1130 | if (code == 0) break; |
| 1131 | const tag_id = try fbr.readUleb128(u64); |
| 1132 | const has_children = (try fbr.readByte()) == CHILDREN.yes; |
| 1200 | 1133 | |
| 1201 | 1134 | while (true) { |
| 1202 | | const attr_id = try leb.readULEB128(u64, in); |
| 1203 | | const form_id = try leb.readULEB128(u64, in); |
| 1135 | const attr_id = try fbr.readUleb128(u64); |
| 1136 | const form_id = try fbr.readUleb128(u64); |
| 1204 | 1137 | if (attr_id == 0 and form_id == 0) break; |
| 1205 | | // DW_FORM_implicit_const stores its value immediately after the attribute pair :( |
| 1206 | | const payload = if (form_id == FORM.implicit_const) try leb.readILEB128(i64, in) else undefined; |
| 1207 | | try attrs.append(AbbrevAttr{ |
| 1208 | | .attr_id = attr_id, |
| 1138 | try attrs.append(.{ |
| 1139 | .id = attr_id, |
| 1209 | 1140 | .form_id = form_id, |
| 1210 | | .payload = payload, |
| 1141 | .payload = switch (form_id) { |
| 1142 | FORM.implicit_const => try fbr.readIleb128(i64), |
| 1143 | else => undefined, |
| 1144 | }, |
| 1211 | 1145 | }); |
| 1212 | 1146 | } |
| 1147 | |
| 1148 | try abbrevs.append(.{ |
| 1149 | .code = code, |
| 1150 | .tag_id = tag_id, |
| 1151 | .has_children = has_children, |
| 1152 | .attrs = try attrs.toOwnedSlice(), |
| 1153 | }); |
| 1213 | 1154 | } |
| 1155 | |
| 1156 | return .{ |
| 1157 | .offset = offset, |
| 1158 | .abbrevs = try abbrevs.toOwnedSlice(), |
| 1159 | }; |
| 1214 | 1160 | } |
| 1215 | 1161 | |
| 1216 | 1162 | fn parseDie( |
| 1217 | | di: *DwarfInfo, |
| 1218 | | allocator: mem.Allocator, |
| 1219 | | in_stream: anytype, |
| 1220 | | abbrev_table: *const AbbrevTable, |
| 1221 | | is_64: bool, |
| 1163 | fbr: *FixedBufferReader, |
| 1164 | attrs_buf: []Die.Attr, |
| 1165 | abbrev_table: *const Abbrev.Table, |
| 1166 | format: Format, |
| 1222 | 1167 | ) !?Die { |
| 1223 | | const abbrev_code = try leb.readULEB128(u64, in_stream); |
| 1168 | const abbrev_code = try fbr.readUleb128(u64); |
| 1224 | 1169 | if (abbrev_code == 0) return null; |
| 1225 | | const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return badDwarf(); |
| 1226 | | |
| 1227 | | var result = Die{ |
| 1228 | | // Lives as long as the Die. |
| 1229 | | .arena = std.heap.ArenaAllocator.init(allocator), |
| 1170 | const table_entry = abbrev_table.get(abbrev_code) orelse return badDwarf(); |
| 1171 | |
| 1172 | const attrs = attrs_buf[0..table_entry.attrs.len]; |
| 1173 | for (attrs, table_entry.attrs) |*result_attr, attr| result_attr.* = Die.Attr{ |
| 1174 | .id = attr.id, |
| 1175 | .value = try parseFormValue( |
| 1176 | fbr, |
| 1177 | attr.form_id, |
| 1178 | format, |
| 1179 | attr.payload, |
| 1180 | ), |
| 1181 | }; |
| 1182 | return .{ |
| 1230 | 1183 | .tag_id = table_entry.tag_id, |
| 1231 | 1184 | .has_children = table_entry.has_children, |
| 1185 | .attrs = attrs, |
| 1232 | 1186 | }; |
| 1233 | | try result.attrs.resize(allocator, table_entry.attrs.items.len); |
| 1234 | | for (table_entry.attrs.items, 0..) |attr, i| { |
| 1235 | | result.attrs.items[i] = Die.Attr{ |
| 1236 | | .id = attr.attr_id, |
| 1237 | | .value = try parseFormValue( |
| 1238 | | result.arena.allocator(), |
| 1239 | | in_stream, |
| 1240 | | attr.form_id, |
| 1241 | | di.endian, |
| 1242 | | is_64, |
| 1243 | | ), |
| 1244 | | }; |
| 1245 | | if (attr.form_id == FORM.implicit_const) { |
| 1246 | | result.attrs.items[i].value.Const.payload = @as(u64, @bitCast(attr.payload)); |
| 1247 | | } |
| 1248 | | } |
| 1249 | | return result; |
| 1250 | 1187 | } |
| 1251 | 1188 | |
| 1252 | 1189 | pub fn getLineNumberInfo( |
| ... | ... | @@ -1255,50 +1192,47 @@ pub const DwarfInfo = struct { |
| 1255 | 1192 | compile_unit: CompileUnit, |
| 1256 | 1193 | target_address: u64, |
| 1257 | 1194 | ) !debug.LineInfo { |
| 1258 | | var stream = io.fixedBufferStream(di.section(.debug_line).?); |
| 1259 | | const in = stream.reader(); |
| 1260 | | const seekable = stream.seekableStream(); |
| 1261 | | |
| 1262 | 1195 | const compile_unit_cwd = try compile_unit.die.getAttrString(di, AT.comp_dir, di.section(.debug_line_str), compile_unit); |
| 1263 | 1196 | const line_info_offset = try compile_unit.die.getAttrSecOffset(AT.stmt_list); |
| 1264 | 1197 | |
| 1265 | | try seekable.seekTo(line_info_offset); |
| 1198 | var fbr: FixedBufferReader = .{ .buf = di.section(.debug_line).?, .endian = di.endian }; |
| 1199 | try fbr.seekTo(line_info_offset); |
| 1266 | 1200 | |
| 1267 | | var is_64: bool = undefined; |
| 1268 | | const unit_length = try readUnitLength(in, di.endian, &is_64); |
| 1269 | | if (unit_length == 0) { |
| 1270 | | return missingDwarf(); |
| 1271 | | } |
| 1272 | | const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4)); |
| 1201 | const unit_header = try readUnitHeader(&fbr); |
| 1202 | if (unit_header.unit_length == 0) return missingDwarf(); |
| 1203 | const next_offset = unit_header.header_length + unit_header.unit_length; |
| 1273 | 1204 | |
| 1274 | | const version = try in.readInt(u16, di.endian); |
| 1205 | const version = try fbr.readInt(u16); |
| 1275 | 1206 | if (version < 2) return badDwarf(); |
| 1276 | 1207 | |
| 1277 | | var addr_size: u8 = if (is_64) 8 else 4; |
| 1208 | var addr_size: u8 = switch (unit_header.format) { |
| 1209 | .@"32" => 4, |
| 1210 | .@"64" => 8, |
| 1211 | }; |
| 1278 | 1212 | var seg_size: u8 = 0; |
| 1279 | 1213 | if (version >= 5) { |
| 1280 | | addr_size = try in.readByte(); |
| 1281 | | seg_size = try in.readByte(); |
| 1214 | addr_size = try fbr.readByte(); |
| 1215 | seg_size = try fbr.readByte(); |
| 1282 | 1216 | } |
| 1283 | 1217 | |
| 1284 | | const prologue_length = if (is_64) try in.readInt(u64, di.endian) else try in.readInt(u32, di.endian); |
| 1285 | | const prog_start_offset = (try seekable.getPos()) + prologue_length; |
| 1218 | const prologue_length = try fbr.readAddress(unit_header.format); |
| 1219 | const prog_start_offset = fbr.pos + prologue_length; |
| 1286 | 1220 | |
| 1287 | | const minimum_instruction_length = try in.readByte(); |
| 1221 | const minimum_instruction_length = try fbr.readByte(); |
| 1288 | 1222 | if (minimum_instruction_length == 0) return badDwarf(); |
| 1289 | 1223 | |
| 1290 | 1224 | if (version >= 4) { |
| 1291 | 1225 | // maximum_operations_per_instruction |
| 1292 | | _ = try in.readByte(); |
| 1226 | _ = try fbr.readByte(); |
| 1293 | 1227 | } |
| 1294 | 1228 | |
| 1295 | | const default_is_stmt = (try in.readByte()) != 0; |
| 1296 | | const line_base = try in.readByteSigned(); |
| 1229 | const default_is_stmt = (try fbr.readByte()) != 0; |
| 1230 | const line_base = try fbr.readByteSigned(); |
| 1297 | 1231 | |
| 1298 | | const line_range = try in.readByte(); |
| 1232 | const line_range = try fbr.readByte(); |
| 1299 | 1233 | if (line_range == 0) return badDwarf(); |
| 1300 | 1234 | |
| 1301 | | const opcode_base = try in.readByte(); |
| 1235 | const opcode_base = try fbr.readByte(); |
| 1302 | 1236 | |
| 1303 | 1237 | const standard_opcode_lengths = try allocator.alloc(u8, opcode_base - 1); |
| 1304 | 1238 | defer allocator.free(standard_opcode_lengths); |
| ... | ... | @@ -1306,33 +1240,31 @@ pub const DwarfInfo = struct { |
| 1306 | 1240 | { |
| 1307 | 1241 | var i: usize = 0; |
| 1308 | 1242 | while (i < opcode_base - 1) : (i += 1) { |
| 1309 | | standard_opcode_lengths[i] = try in.readByte(); |
| 1243 | standard_opcode_lengths[i] = try fbr.readByte(); |
| 1310 | 1244 | } |
| 1311 | 1245 | } |
| 1312 | 1246 | |
| 1313 | | var tmp_arena = std.heap.ArenaAllocator.init(allocator); |
| 1314 | | defer tmp_arena.deinit(); |
| 1315 | | const arena = tmp_arena.allocator(); |
| 1316 | | |
| 1317 | | var include_directories = std.ArrayList(FileEntry).init(arena); |
| 1318 | | var file_entries = std.ArrayList(FileEntry).init(arena); |
| 1247 | var include_directories = std.ArrayList(FileEntry).init(allocator); |
| 1248 | defer include_directories.deinit(); |
| 1249 | var file_entries = std.ArrayList(FileEntry).init(allocator); |
| 1250 | defer file_entries.deinit(); |
| 1319 | 1251 | |
| 1320 | 1252 | if (version < 5) { |
| 1321 | 1253 | try include_directories.append(.{ .path = compile_unit_cwd }); |
| 1322 | 1254 | |
| 1323 | 1255 | while (true) { |
| 1324 | | const dir = try in.readUntilDelimiterAlloc(arena, 0, math.maxInt(usize)); |
| 1256 | const dir = try fbr.readBytesTo(0); |
| 1325 | 1257 | if (dir.len == 0) break; |
| 1326 | 1258 | try include_directories.append(.{ .path = dir }); |
| 1327 | 1259 | } |
| 1328 | 1260 | |
| 1329 | 1261 | while (true) { |
| 1330 | | const file_name = try in.readUntilDelimiterAlloc(arena, 0, math.maxInt(usize)); |
| 1262 | const file_name = try fbr.readBytesTo(0); |
| 1331 | 1263 | if (file_name.len == 0) break; |
| 1332 | | const dir_index = try leb.readULEB128(u32, in); |
| 1333 | | const mtime = try leb.readULEB128(u64, in); |
| 1334 | | const size = try leb.readULEB128(u64, in); |
| 1335 | | try file_entries.append(FileEntry{ |
| 1264 | const dir_index = try fbr.readUleb128(u32); |
| 1265 | const mtime = try fbr.readUleb128(u64); |
| 1266 | const size = try fbr.readUleb128(u64); |
| 1267 | try file_entries.append(.{ |
| 1336 | 1268 | .path = file_name, |
| 1337 | 1269 | .dir_index = dir_index, |
| 1338 | 1270 | .mtime = mtime, |
| ... | ... | @@ -1346,16 +1278,16 @@ pub const DwarfInfo = struct { |
| 1346 | 1278 | }; |
| 1347 | 1279 | { |
| 1348 | 1280 | var dir_ent_fmt_buf: [10]FileEntFmt = undefined; |
| 1349 | | const directory_entry_format_count = try in.readByte(); |
| 1281 | const directory_entry_format_count = try fbr.readByte(); |
| 1350 | 1282 | if (directory_entry_format_count > dir_ent_fmt_buf.len) return badDwarf(); |
| 1351 | 1283 | for (dir_ent_fmt_buf[0..directory_entry_format_count]) |*ent_fmt| { |
| 1352 | 1284 | ent_fmt.* = .{ |
| 1353 | | .content_type_code = try leb.readULEB128(u8, in), |
| 1354 | | .form_code = try leb.readULEB128(u16, in), |
| 1285 | .content_type_code = try fbr.readUleb128(u8), |
| 1286 | .form_code = try fbr.readUleb128(u16), |
| 1355 | 1287 | }; |
| 1356 | 1288 | } |
| 1357 | 1289 | |
| 1358 | | const directories_count = try leb.readULEB128(usize, in); |
| 1290 | const directories_count = try fbr.readUleb128(usize); |
| 1359 | 1291 | try include_directories.ensureUnusedCapacity(directories_count); |
| 1360 | 1292 | { |
| 1361 | 1293 | var i: usize = 0; |
| ... | ... | @@ -1363,18 +1295,20 @@ pub const DwarfInfo = struct { |
| 1363 | 1295 | var e: FileEntry = .{ .path = &.{} }; |
| 1364 | 1296 | for (dir_ent_fmt_buf[0..directory_entry_format_count]) |ent_fmt| { |
| 1365 | 1297 | const form_value = try parseFormValue( |
| 1366 | | arena, |
| 1367 | | in, |
| 1298 | &fbr, |
| 1368 | 1299 | ent_fmt.form_code, |
| 1369 | | di.endian, |
| 1370 | | is_64, |
| 1300 | unit_header.format, |
| 1301 | null, |
| 1371 | 1302 | ); |
| 1372 | 1303 | switch (ent_fmt.content_type_code) { |
| 1373 | 1304 | LNCT.path => e.path = try form_value.getString(di.*), |
| 1374 | 1305 | LNCT.directory_index => e.dir_index = try form_value.getUInt(u32), |
| 1375 | 1306 | LNCT.timestamp => e.mtime = try form_value.getUInt(u64), |
| 1376 | 1307 | LNCT.size => e.size = try form_value.getUInt(u64), |
| 1377 | | LNCT.MD5 => e.md5 = try form_value.getData16(), |
| 1308 | LNCT.MD5 => e.md5 = switch (form_value) { |
| 1309 | .data16 => |data16| data16.*, |
| 1310 | else => return badDwarf(), |
| 1311 | }, |
| 1378 | 1312 | else => continue, |
| 1379 | 1313 | } |
| 1380 | 1314 | } |
| ... | ... | @@ -1384,16 +1318,16 @@ pub const DwarfInfo = struct { |
| 1384 | 1318 | } |
| 1385 | 1319 | |
| 1386 | 1320 | var file_ent_fmt_buf: [10]FileEntFmt = undefined; |
| 1387 | | const file_name_entry_format_count = try in.readByte(); |
| 1321 | const file_name_entry_format_count = try fbr.readByte(); |
| 1388 | 1322 | if (file_name_entry_format_count > file_ent_fmt_buf.len) return badDwarf(); |
| 1389 | 1323 | for (file_ent_fmt_buf[0..file_name_entry_format_count]) |*ent_fmt| { |
| 1390 | 1324 | ent_fmt.* = .{ |
| 1391 | | .content_type_code = try leb.readULEB128(u8, in), |
| 1392 | | .form_code = try leb.readULEB128(u16, in), |
| 1325 | .content_type_code = try fbr.readUleb128(u8), |
| 1326 | .form_code = try fbr.readUleb128(u16), |
| 1393 | 1327 | }; |
| 1394 | 1328 | } |
| 1395 | 1329 | |
| 1396 | | const file_names_count = try leb.readULEB128(usize, in); |
| 1330 | const file_names_count = try fbr.readUleb128(usize); |
| 1397 | 1331 | try file_entries.ensureUnusedCapacity(file_names_count); |
| 1398 | 1332 | { |
| 1399 | 1333 | var i: usize = 0; |
| ... | ... | @@ -1401,18 +1335,20 @@ pub const DwarfInfo = struct { |
| 1401 | 1335 | var e: FileEntry = .{ .path = &.{} }; |
| 1402 | 1336 | for (file_ent_fmt_buf[0..file_name_entry_format_count]) |ent_fmt| { |
| 1403 | 1337 | const form_value = try parseFormValue( |
| 1404 | | arena, |
| 1405 | | in, |
| 1338 | &fbr, |
| 1406 | 1339 | ent_fmt.form_code, |
| 1407 | | di.endian, |
| 1408 | | is_64, |
| 1340 | unit_header.format, |
| 1341 | null, |
| 1409 | 1342 | ); |
| 1410 | 1343 | switch (ent_fmt.content_type_code) { |
| 1411 | 1344 | LNCT.path => e.path = try form_value.getString(di.*), |
| 1412 | 1345 | LNCT.directory_index => e.dir_index = try form_value.getUInt(u32), |
| 1413 | 1346 | LNCT.timestamp => e.mtime = try form_value.getUInt(u64), |
| 1414 | 1347 | LNCT.size => e.size = try form_value.getUInt(u64), |
| 1415 | | LNCT.MD5 => e.md5 = try form_value.getData16(), |
| 1348 | LNCT.MD5 => e.md5 = switch (form_value) { |
| 1349 | .data16 => |data16| data16.*, |
| 1350 | else => return badDwarf(), |
| 1351 | }, |
| 1416 | 1352 | else => continue, |
| 1417 | 1353 | } |
| 1418 | 1354 | } |
| ... | ... | @@ -1428,17 +1364,17 @@ pub const DwarfInfo = struct { |
| 1428 | 1364 | version, |
| 1429 | 1365 | ); |
| 1430 | 1366 | |
| 1431 | | try seekable.seekTo(prog_start_offset); |
| 1367 | try fbr.seekTo(prog_start_offset); |
| 1432 | 1368 | |
| 1433 | 1369 | const next_unit_pos = line_info_offset + next_offset; |
| 1434 | 1370 | |
| 1435 | | while ((try seekable.getPos()) < next_unit_pos) { |
| 1436 | | const opcode = try in.readByte(); |
| 1371 | while (fbr.pos < next_unit_pos) { |
| 1372 | const opcode = try fbr.readByte(); |
| 1437 | 1373 | |
| 1438 | 1374 | if (opcode == LNS.extended_op) { |
| 1439 | | const op_size = try leb.readULEB128(u64, in); |
| 1375 | const op_size = try fbr.readUleb128(u64); |
| 1440 | 1376 | if (op_size < 1) return badDwarf(); |
| 1441 | | const sub_op = try in.readByte(); |
| 1377 | const sub_op = try fbr.readByte(); |
| 1442 | 1378 | switch (sub_op) { |
| 1443 | 1379 | LNE.end_sequence => { |
| 1444 | 1380 | prog.end_sequence = true; |
| ... | ... | @@ -1446,25 +1382,22 @@ pub const DwarfInfo = struct { |
| 1446 | 1382 | prog.reset(); |
| 1447 | 1383 | }, |
| 1448 | 1384 | LNE.set_address => { |
| 1449 | | const addr = try in.readInt(usize, di.endian); |
| 1385 | const addr = try fbr.readInt(usize); |
| 1450 | 1386 | prog.address = addr; |
| 1451 | 1387 | }, |
| 1452 | 1388 | LNE.define_file => { |
| 1453 | | const path = try in.readUntilDelimiterAlloc(arena, 0, math.maxInt(usize)); |
| 1454 | | const dir_index = try leb.readULEB128(u32, in); |
| 1455 | | const mtime = try leb.readULEB128(u64, in); |
| 1456 | | const size = try leb.readULEB128(u64, in); |
| 1457 | | try file_entries.append(FileEntry{ |
| 1389 | const path = try fbr.readBytesTo(0); |
| 1390 | const dir_index = try fbr.readUleb128(u32); |
| 1391 | const mtime = try fbr.readUleb128(u64); |
| 1392 | const size = try fbr.readUleb128(u64); |
| 1393 | try file_entries.append(.{ |
| 1458 | 1394 | .path = path, |
| 1459 | 1395 | .dir_index = dir_index, |
| 1460 | 1396 | .mtime = mtime, |
| 1461 | 1397 | .size = size, |
| 1462 | 1398 | }); |
| 1463 | 1399 | }, |
| 1464 | | else => { |
| 1465 | | const fwd_amt = math.cast(isize, op_size - 1) orelse return badDwarf(); |
| 1466 | | try seekable.seekBy(fwd_amt); |
| 1467 | | }, |
| 1400 | else => try fbr.seekForward(op_size - 1), |
| 1468 | 1401 | } |
| 1469 | 1402 | } else if (opcode >= opcode_base) { |
| 1470 | 1403 | // special opcodes |
| ... | ... | @@ -1482,19 +1415,19 @@ pub const DwarfInfo = struct { |
| 1482 | 1415 | prog.basic_block = false; |
| 1483 | 1416 | }, |
| 1484 | 1417 | LNS.advance_pc => { |
| 1485 | | const arg = try leb.readULEB128(usize, in); |
| 1418 | const arg = try fbr.readUleb128(usize); |
| 1486 | 1419 | prog.address += arg * minimum_instruction_length; |
| 1487 | 1420 | }, |
| 1488 | 1421 | LNS.advance_line => { |
| 1489 | | const arg = try leb.readILEB128(i64, in); |
| 1422 | const arg = try fbr.readIleb128(i64); |
| 1490 | 1423 | prog.line += arg; |
| 1491 | 1424 | }, |
| 1492 | 1425 | LNS.set_file => { |
| 1493 | | const arg = try leb.readULEB128(usize, in); |
| 1426 | const arg = try fbr.readUleb128(usize); |
| 1494 | 1427 | prog.file = arg; |
| 1495 | 1428 | }, |
| 1496 | 1429 | LNS.set_column => { |
| 1497 | | const arg = try leb.readULEB128(u64, in); |
| 1430 | const arg = try fbr.readUleb128(u64); |
| 1498 | 1431 | prog.column = arg; |
| 1499 | 1432 | }, |
| 1500 | 1433 | LNS.negate_stmt => { |
| ... | ... | @@ -1508,14 +1441,13 @@ pub const DwarfInfo = struct { |
| 1508 | 1441 | prog.address += inc_addr; |
| 1509 | 1442 | }, |
| 1510 | 1443 | LNS.fixed_advance_pc => { |
| 1511 | | const arg = try in.readInt(u16, di.endian); |
| 1444 | const arg = try fbr.readInt(u16); |
| 1512 | 1445 | prog.address += arg; |
| 1513 | 1446 | }, |
| 1514 | 1447 | LNS.set_prologue_end => {}, |
| 1515 | 1448 | else => { |
| 1516 | 1449 | if (opcode - 1 >= standard_opcode_lengths.len) return badDwarf(); |
| 1517 | | const len_bytes = standard_opcode_lengths[opcode - 1]; |
| 1518 | | try seekable.seekBy(len_bytes); |
| 1450 | try fbr.seekForward(standard_opcode_lengths[opcode - 1]); |
| 1519 | 1451 | }, |
| 1520 | 1452 | } |
| 1521 | 1453 | } |
| ... | ... | @@ -1524,11 +1456,11 @@ pub const DwarfInfo = struct { |
| 1524 | 1456 | return missingDwarf(); |
| 1525 | 1457 | } |
| 1526 | 1458 | |
| 1527 | | fn getString(di: DwarfInfo, offset: u64) ![]const u8 { |
| 1459 | fn getString(di: DwarfInfo, offset: u64) ![:0]const u8 { |
| 1528 | 1460 | return getStringGeneric(di.section(.debug_str), offset); |
| 1529 | 1461 | } |
| 1530 | 1462 | |
| 1531 | | fn getLineString(di: DwarfInfo, offset: u64) ![]const u8 { |
| 1463 | fn getLineString(di: DwarfInfo, offset: u64) ![:0]const u8 { |
| 1532 | 1464 | return getStringGeneric(di.section(.debug_line_str), offset); |
| 1533 | 1465 | } |
| 1534 | 1466 | |
| ... | ... | @@ -1564,38 +1496,37 @@ pub const DwarfInfo = struct { |
| 1564 | 1496 | /// of FDEs is built for binary searching during unwinding. |
| 1565 | 1497 | pub fn scanAllUnwindInfo(di: *DwarfInfo, allocator: mem.Allocator, base_address: usize) !void { |
| 1566 | 1498 | if (di.section(.eh_frame_hdr)) |eh_frame_hdr| blk: { |
| 1567 | | var stream = io.fixedBufferStream(eh_frame_hdr); |
| 1568 | | const reader = stream.reader(); |
| 1499 | var fbr: FixedBufferReader = .{ .buf = eh_frame_hdr, .endian = native_endian }; |
| 1569 | 1500 | |
| 1570 | | const version = try reader.readByte(); |
| 1501 | const version = try fbr.readByte(); |
| 1571 | 1502 | if (version != 1) break :blk; |
| 1572 | 1503 | |
| 1573 | | const eh_frame_ptr_enc = try reader.readByte(); |
| 1504 | const eh_frame_ptr_enc = try fbr.readByte(); |
| 1574 | 1505 | if (eh_frame_ptr_enc == EH.PE.omit) break :blk; |
| 1575 | | const fde_count_enc = try reader.readByte(); |
| 1506 | const fde_count_enc = try fbr.readByte(); |
| 1576 | 1507 | if (fde_count_enc == EH.PE.omit) break :blk; |
| 1577 | | const table_enc = try reader.readByte(); |
| 1508 | const table_enc = try fbr.readByte(); |
| 1578 | 1509 | if (table_enc == EH.PE.omit) break :blk; |
| 1579 | 1510 | |
| 1580 | | const eh_frame_ptr = std.math.cast(usize, try readEhPointer(reader, eh_frame_ptr_enc, @sizeOf(usize), .{ |
| 1581 | | .pc_rel_base = @intFromPtr(&eh_frame_hdr[stream.pos]), |
| 1511 | const eh_frame_ptr = math.cast(usize, try readEhPointer(&fbr, eh_frame_ptr_enc, @sizeOf(usize), .{ |
| 1512 | .pc_rel_base = @intFromPtr(&eh_frame_hdr[fbr.pos]), |
| 1582 | 1513 | .follow_indirect = true, |
| 1583 | | }, builtin.cpu.arch.endian()) orelse return badDwarf()) orelse return badDwarf(); |
| 1514 | }) orelse return badDwarf()) orelse return badDwarf(); |
| 1584 | 1515 | |
| 1585 | | const fde_count = std.math.cast(usize, try readEhPointer(reader, fde_count_enc, @sizeOf(usize), .{ |
| 1586 | | .pc_rel_base = @intFromPtr(&eh_frame_hdr[stream.pos]), |
| 1516 | const fde_count = math.cast(usize, try readEhPointer(&fbr, fde_count_enc, @sizeOf(usize), .{ |
| 1517 | .pc_rel_base = @intFromPtr(&eh_frame_hdr[fbr.pos]), |
| 1587 | 1518 | .follow_indirect = true, |
| 1588 | | }, builtin.cpu.arch.endian()) orelse return badDwarf()) orelse return badDwarf(); |
| 1519 | }) orelse return badDwarf()) orelse return badDwarf(); |
| 1589 | 1520 | |
| 1590 | 1521 | const entry_size = try ExceptionFrameHeader.entrySize(table_enc); |
| 1591 | 1522 | const entries_len = fde_count * entry_size; |
| 1592 | | if (entries_len > eh_frame_hdr.len - stream.pos) return badDwarf(); |
| 1523 | if (entries_len > eh_frame_hdr.len - fbr.pos) return badDwarf(); |
| 1593 | 1524 | |
| 1594 | 1525 | di.eh_frame_hdr = .{ |
| 1595 | 1526 | .eh_frame_ptr = eh_frame_ptr, |
| 1596 | 1527 | .table_enc = table_enc, |
| 1597 | 1528 | .fde_count = fde_count, |
| 1598 | | .entries = eh_frame_hdr[stream.pos..][0..entries_len], |
| 1529 | .entries = eh_frame_hdr[fbr.pos..][0..entries_len], |
| 1599 | 1530 | }; |
| 1600 | 1531 | |
| 1601 | 1532 | // No need to scan .eh_frame, we have a binary search table already |
| ... | ... | @@ -1605,16 +1536,16 @@ pub const DwarfInfo = struct { |
| 1605 | 1536 | const frame_sections = [2]DwarfSection{ .eh_frame, .debug_frame }; |
| 1606 | 1537 | for (frame_sections) |frame_section| { |
| 1607 | 1538 | if (di.section(frame_section)) |section_data| { |
| 1608 | | var stream = io.fixedBufferStream(section_data); |
| 1609 | | while (stream.pos < stream.buffer.len) { |
| 1610 | | const entry_header = try EntryHeader.read(&stream, frame_section, di.endian); |
| 1539 | var fbr: FixedBufferReader = .{ .buf = section_data, .endian = di.endian }; |
| 1540 | while (fbr.pos < fbr.buf.len) { |
| 1541 | const entry_header = try EntryHeader.read(&fbr, frame_section); |
| 1611 | 1542 | switch (entry_header.type) { |
| 1612 | 1543 | .cie => { |
| 1613 | 1544 | const cie = try CommonInformationEntry.parse( |
| 1614 | 1545 | entry_header.entry_bytes, |
| 1615 | 1546 | di.sectionVirtualOffset(frame_section, base_address).?, |
| 1616 | 1547 | true, |
| 1617 | | entry_header.is_64, |
| 1548 | entry_header.format, |
| 1618 | 1549 | frame_section, |
| 1619 | 1550 | entry_header.length_offset, |
| 1620 | 1551 | @sizeOf(usize), |
| ... | ... | @@ -1638,7 +1569,7 @@ pub const DwarfInfo = struct { |
| 1638 | 1569 | } |
| 1639 | 1570 | } |
| 1640 | 1571 | |
| 1641 | | std.mem.sortUnstable(FrameDescriptionEntry, di.fde_list.items, {}, struct { |
| 1572 | mem.sortUnstable(FrameDescriptionEntry, di.fde_list.items, {}, struct { |
| 1642 | 1573 | fn lessThan(ctx: void, a: FrameDescriptionEntry, b: FrameDescriptionEntry) bool { |
| 1643 | 1574 | _ = ctx; |
| 1644 | 1575 | return a.pc_begin < b.pc_begin; |
| ... | ... | @@ -1668,27 +1599,31 @@ pub const DwarfInfo = struct { |
| 1668 | 1599 | const frame_section = di.section(dwarf_section) orelse return error.MissingFDE; |
| 1669 | 1600 | if (fde_offset >= frame_section.len) return error.MissingFDE; |
| 1670 | 1601 | |
| 1671 | | var stream = io.fixedBufferStream(frame_section); |
| 1672 | | try stream.seekTo(fde_offset); |
| 1602 | var fbr: FixedBufferReader = .{ |
| 1603 | .buf = frame_section, |
| 1604 | .pos = fde_offset, |
| 1605 | .endian = di.endian, |
| 1606 | }; |
| 1673 | 1607 | |
| 1674 | | const fde_entry_header = try EntryHeader.read(&stream, dwarf_section, di.endian); |
| 1608 | const fde_entry_header = try EntryHeader.read(&fbr, dwarf_section); |
| 1675 | 1609 | if (fde_entry_header.type != .fde) return error.MissingFDE; |
| 1676 | 1610 | |
| 1677 | 1611 | const cie_offset = fde_entry_header.type.fde; |
| 1678 | | try stream.seekTo(cie_offset); |
| 1612 | try fbr.seekTo(cie_offset); |
| 1679 | 1613 | |
| 1680 | | const cie_entry_header = try EntryHeader.read(&stream, dwarf_section, builtin.cpu.arch.endian()); |
| 1614 | fbr.endian = native_endian; |
| 1615 | const cie_entry_header = try EntryHeader.read(&fbr, dwarf_section); |
| 1681 | 1616 | if (cie_entry_header.type != .cie) return badDwarf(); |
| 1682 | 1617 | |
| 1683 | 1618 | cie = try CommonInformationEntry.parse( |
| 1684 | 1619 | cie_entry_header.entry_bytes, |
| 1685 | 1620 | 0, |
| 1686 | 1621 | true, |
| 1687 | | cie_entry_header.is_64, |
| 1622 | cie_entry_header.format, |
| 1688 | 1623 | dwarf_section, |
| 1689 | 1624 | cie_entry_header.length_offset, |
| 1690 | 1625 | @sizeOf(usize), |
| 1691 | | builtin.cpu.arch.endian(), |
| 1626 | native_endian, |
| 1692 | 1627 | ); |
| 1693 | 1628 | |
| 1694 | 1629 | fde = try FrameDescriptionEntry.parse( |
| ... | ... | @@ -1697,7 +1632,7 @@ pub const DwarfInfo = struct { |
| 1697 | 1632 | true, |
| 1698 | 1633 | cie, |
| 1699 | 1634 | @sizeOf(usize), |
| 1700 | | builtin.cpu.arch.endian(), |
| 1635 | native_endian, |
| 1701 | 1636 | ); |
| 1702 | 1637 | } else if (di.eh_frame_hdr) |header| { |
| 1703 | 1638 | const eh_frame_len = if (di.section(.eh_frame)) |eh_frame| eh_frame.len else null; |
| ... | ... | @@ -1711,7 +1646,7 @@ pub const DwarfInfo = struct { |
| 1711 | 1646 | ); |
| 1712 | 1647 | } else { |
| 1713 | 1648 | const index = std.sort.binarySearch(FrameDescriptionEntry, context.pc, di.fde_list.items, {}, struct { |
| 1714 | | pub fn compareFn(_: void, pc: usize, mid_item: FrameDescriptionEntry) std.math.Order { |
| 1649 | pub fn compareFn(_: void, pc: usize, mid_item: FrameDescriptionEntry) math.Order { |
| 1715 | 1650 | if (pc < mid_item.pc_begin) return .lt; |
| 1716 | 1651 | |
| 1717 | 1652 | const range_end = mid_item.pc_begin + mid_item.pc_range; |
| ... | ... | @@ -1725,8 +1660,8 @@ pub const DwarfInfo = struct { |
| 1725 | 1660 | cie = di.cie_map.get(fde.cie_length_offset) orelse return error.MissingCIE; |
| 1726 | 1661 | } |
| 1727 | 1662 | |
| 1728 | | var expression_context = .{ |
| 1729 | | .is_64 = cie.is_64, |
| 1663 | var expression_context: expressions.ExpressionContext = .{ |
| 1664 | .format = cie.format, |
| 1730 | 1665 | .isValidMemory = context.isValidMemory, |
| 1731 | 1666 | .compile_unit = di.findCompileUnit(fde.pc_begin) catch null, |
| 1732 | 1667 | .thread_context = context.thread_context, |
| ... | ... | @@ -1973,10 +1908,10 @@ pub fn unwindFrameMachO(context: *UnwindContext, unwind_info: []const u8, eh_fra |
| 1973 | 1908 | .raw_encoding = common_encodings[entry.encodingIndex], |
| 1974 | 1909 | }; |
| 1975 | 1910 | } else { |
| 1976 | | const local_index = try std.math.sub( |
| 1911 | const local_index = try math.sub( |
| 1977 | 1912 | u8, |
| 1978 | 1913 | entry.encodingIndex, |
| 1979 | | std.math.cast(u8, header.commonEncodingsArrayCount) orelse return error.InvalidUnwindInfo, |
| 1914 | math.cast(u8, header.commonEncodingsArrayCount) orelse return error.InvalidUnwindInfo, |
| 1980 | 1915 | ); |
| 1981 | 1916 | const local_encodings = mem.bytesAsSlice( |
| 1982 | 1917 | macho.compact_unwind_encoding_t, |
| ... | ... | @@ -2187,7 +2122,7 @@ pub fn unwindFrameMachO(context: *UnwindContext, unwind_info: []const u8, eh_fra |
| 2187 | 2122 | |
| 2188 | 2123 | fn unwindFrameMachODwarf(context: *UnwindContext, eh_frame: []const u8, fde_offset: usize) !usize { |
| 2189 | 2124 | var di = DwarfInfo{ |
| 2190 | | .endian = builtin.cpu.arch.endian(), |
| 2125 | .endian = native_endian, |
| 2191 | 2126 | .is_macho = true, |
| 2192 | 2127 | }; |
| 2193 | 2128 | defer di.deinit(context.allocator); |
| ... | ... | @@ -2207,8 +2142,8 @@ pub const UnwindContext = struct { |
| 2207 | 2142 | thread_context: *debug.ThreadContext, |
| 2208 | 2143 | reg_context: abi.RegisterContext, |
| 2209 | 2144 | isValidMemory: *const fn (address: usize) bool, |
| 2210 | | vm: call_frame.VirtualMachine = .{}, |
| 2211 | | stack_machine: expressions.StackMachine(.{ .call_frame_context = true }) = .{}, |
| 2145 | vm: call_frame.VirtualMachine, |
| 2146 | stack_machine: expressions.StackMachine(.{ .call_frame_context = true }), |
| 2212 | 2147 | |
| 2213 | 2148 | pub fn init(allocator: mem.Allocator, thread_context: *const debug.ThreadContext, isValidMemory: *const fn (address: usize) bool) !UnwindContext { |
| 2214 | 2149 | const pc = abi.stripInstructionPtrAuthCode((try abi.regValueNative(usize, thread_context, abi.ipRegNum(), null)).*); |
| ... | ... | @@ -2223,6 +2158,8 @@ pub const UnwindContext = struct { |
| 2223 | 2158 | .thread_context = context_copy, |
| 2224 | 2159 | .reg_context = undefined, |
| 2225 | 2160 | .isValidMemory = isValidMemory, |
| 2161 | .vm = .{}, |
| 2162 | .stack_machine = .{}, |
| 2226 | 2163 | }; |
| 2227 | 2164 | } |
| 2228 | 2165 | |
| ... | ... | @@ -2230,6 +2167,7 @@ pub const UnwindContext = struct { |
| 2230 | 2167 | self.vm.deinit(self.allocator); |
| 2231 | 2168 | self.stack_machine.deinit(self.allocator); |
| 2232 | 2169 | self.allocator.destroy(self.thread_context); |
| 2170 | self.* = undefined; |
| 2233 | 2171 | } |
| 2234 | 2172 | |
| 2235 | 2173 | pub fn getFp(self: *const UnwindContext) !usize { |
| ... | ... | @@ -2281,8 +2219,7 @@ const EhPointerContext = struct { |
| 2281 | 2219 | text_rel_base: ?u64 = null, |
| 2282 | 2220 | function_rel_base: ?u64 = null, |
| 2283 | 2221 | }; |
| 2284 | | |
| 2285 | | fn readEhPointer(reader: anytype, enc: u8, addr_size_bytes: u8, ctx: EhPointerContext, endian: std.builtin.Endian) !?u64 { |
| 2222 | fn readEhPointer(fbr: *FixedBufferReader, enc: u8, addr_size_bytes: u8, ctx: EhPointerContext) !?u64 { |
| 2286 | 2223 | if (enc == EH.PE.omit) return null; |
| 2287 | 2224 | |
| 2288 | 2225 | const value: union(enum) { |
| ... | ... | @@ -2291,20 +2228,20 @@ fn readEhPointer(reader: anytype, enc: u8, addr_size_bytes: u8, ctx: EhPointerCo |
| 2291 | 2228 | } = switch (enc & EH.PE.type_mask) { |
| 2292 | 2229 | EH.PE.absptr => .{ |
| 2293 | 2230 | .unsigned = switch (addr_size_bytes) { |
| 2294 | | 2 => try reader.readInt(u16, endian), |
| 2295 | | 4 => try reader.readInt(u32, endian), |
| 2296 | | 8 => try reader.readInt(u64, endian), |
| 2231 | 2 => try fbr.readInt(u16), |
| 2232 | 4 => try fbr.readInt(u32), |
| 2233 | 8 => try fbr.readInt(u64), |
| 2297 | 2234 | else => return error.InvalidAddrSize, |
| 2298 | 2235 | }, |
| 2299 | 2236 | }, |
| 2300 | | EH.PE.uleb128 => .{ .unsigned = try leb.readULEB128(u64, reader) }, |
| 2301 | | EH.PE.udata2 => .{ .unsigned = try reader.readInt(u16, endian) }, |
| 2302 | | EH.PE.udata4 => .{ .unsigned = try reader.readInt(u32, endian) }, |
| 2303 | | EH.PE.udata8 => .{ .unsigned = try reader.readInt(u64, endian) }, |
| 2304 | | EH.PE.sleb128 => .{ .signed = try leb.readILEB128(i64, reader) }, |
| 2305 | | EH.PE.sdata2 => .{ .signed = try reader.readInt(i16, endian) }, |
| 2306 | | EH.PE.sdata4 => .{ .signed = try reader.readInt(i32, endian) }, |
| 2307 | | EH.PE.sdata8 => .{ .signed = try reader.readInt(i64, endian) }, |
| 2237 | EH.PE.uleb128 => .{ .unsigned = try fbr.readUleb128(u64) }, |
| 2238 | EH.PE.udata2 => .{ .unsigned = try fbr.readInt(u16) }, |
| 2239 | EH.PE.udata4 => .{ .unsigned = try fbr.readInt(u32) }, |
| 2240 | EH.PE.udata8 => .{ .unsigned = try fbr.readInt(u64) }, |
| 2241 | EH.PE.sleb128 => .{ .signed = try fbr.readIleb128(i64) }, |
| 2242 | EH.PE.sdata2 => .{ .signed = try fbr.readInt(i16) }, |
| 2243 | EH.PE.sdata4 => .{ .signed = try fbr.readInt(i32) }, |
| 2244 | EH.PE.sdata8 => .{ .signed = try fbr.readInt(i64) }, |
| 2308 | 2245 | else => return badDwarf(), |
| 2309 | 2246 | }; |
| 2310 | 2247 | |
| ... | ... | @@ -2396,18 +2333,17 @@ pub const ExceptionFrameHeader = struct { |
| 2396 | 2333 | var left: usize = 0; |
| 2397 | 2334 | var len: usize = self.fde_count; |
| 2398 | 2335 | |
| 2399 | | var stream = io.fixedBufferStream(self.entries); |
| 2400 | | const reader = stream.reader(); |
| 2336 | var fbr: FixedBufferReader = .{ .buf = self.entries, .endian = native_endian }; |
| 2401 | 2337 | |
| 2402 | 2338 | while (len > 1) { |
| 2403 | 2339 | const mid = left + len / 2; |
| 2404 | 2340 | |
| 2405 | | try stream.seekTo(mid * entry_size); |
| 2406 | | const pc_begin = try readEhPointer(reader, self.table_enc, @sizeOf(usize), .{ |
| 2407 | | .pc_rel_base = @intFromPtr(&self.entries[stream.pos]), |
| 2341 | fbr.pos = mid * entry_size; |
| 2342 | const pc_begin = try readEhPointer(&fbr, self.table_enc, @sizeOf(usize), .{ |
| 2343 | .pc_rel_base = @intFromPtr(&self.entries[fbr.pos]), |
| 2408 | 2344 | .follow_indirect = true, |
| 2409 | 2345 | .data_rel_base = eh_frame_hdr_ptr, |
| 2410 | | }, builtin.cpu.arch.endian()) orelse return badDwarf(); |
| 2346 | }) orelse return badDwarf(); |
| 2411 | 2347 | |
| 2412 | 2348 | if (pc < pc_begin) { |
| 2413 | 2349 | len /= 2; |
| ... | ... | @@ -2419,20 +2355,20 @@ pub const ExceptionFrameHeader = struct { |
| 2419 | 2355 | } |
| 2420 | 2356 | |
| 2421 | 2357 | if (len == 0) return badDwarf(); |
| 2422 | | try stream.seekTo(left * entry_size); |
| 2358 | fbr.pos = left * entry_size; |
| 2423 | 2359 | |
| 2424 | 2360 | // Read past the pc_begin field of the entry |
| 2425 | | _ = try readEhPointer(reader, self.table_enc, @sizeOf(usize), .{ |
| 2426 | | .pc_rel_base = @intFromPtr(&self.entries[stream.pos]), |
| 2361 | _ = try readEhPointer(&fbr, self.table_enc, @sizeOf(usize), .{ |
| 2362 | .pc_rel_base = @intFromPtr(&self.entries[fbr.pos]), |
| 2427 | 2363 | .follow_indirect = true, |
| 2428 | 2364 | .data_rel_base = eh_frame_hdr_ptr, |
| 2429 | | }, builtin.cpu.arch.endian()) orelse return badDwarf(); |
| 2365 | }) orelse return badDwarf(); |
| 2430 | 2366 | |
| 2431 | | const fde_ptr = math.cast(usize, try readEhPointer(reader, self.table_enc, @sizeOf(usize), .{ |
| 2432 | | .pc_rel_base = @intFromPtr(&self.entries[stream.pos]), |
| 2367 | const fde_ptr = math.cast(usize, try readEhPointer(&fbr, self.table_enc, @sizeOf(usize), .{ |
| 2368 | .pc_rel_base = @intFromPtr(&self.entries[fbr.pos]), |
| 2433 | 2369 | .follow_indirect = true, |
| 2434 | 2370 | .data_rel_base = eh_frame_hdr_ptr, |
| 2435 | | }, builtin.cpu.arch.endian()) orelse return badDwarf()) orelse return badDwarf(); |
| 2371 | }) orelse return badDwarf()) orelse return badDwarf(); |
| 2436 | 2372 | |
| 2437 | 2373 | // Verify the length fields of the FDE header are readable |
| 2438 | 2374 | if (!self.isValidPtr(fde_ptr, isValidMemory, eh_frame_len) or fde_ptr < self.eh_frame_ptr) return badDwarf(); |
| ... | ... | @@ -2445,17 +2381,20 @@ pub const ExceptionFrameHeader = struct { |
| 2445 | 2381 | const eh_frame = @as([*]const u8, @ptrFromInt(self.eh_frame_ptr))[0 .. eh_frame_len orelse math.maxInt(u32)]; |
| 2446 | 2382 | |
| 2447 | 2383 | const fde_offset = fde_ptr - self.eh_frame_ptr; |
| 2448 | | var eh_frame_stream = io.fixedBufferStream(eh_frame); |
| 2449 | | try eh_frame_stream.seekTo(fde_offset); |
| 2384 | var eh_frame_fbr: FixedBufferReader = .{ |
| 2385 | .buf = eh_frame, |
| 2386 | .pos = fde_offset, |
| 2387 | .endian = native_endian, |
| 2388 | }; |
| 2450 | 2389 | |
| 2451 | | const fde_entry_header = try EntryHeader.read(&eh_frame_stream, .eh_frame, builtin.cpu.arch.endian()); |
| 2390 | const fde_entry_header = try EntryHeader.read(&eh_frame_fbr, .eh_frame); |
| 2452 | 2391 | if (!self.isValidPtr(@intFromPtr(&fde_entry_header.entry_bytes[fde_entry_header.entry_bytes.len - 1]), isValidMemory, eh_frame_len)) return badDwarf(); |
| 2453 | 2392 | if (fde_entry_header.type != .fde) return badDwarf(); |
| 2454 | 2393 | |
| 2455 | 2394 | // CIEs always come before FDEs (the offset is a subtraction), so we can assume this memory is readable |
| 2456 | 2395 | const cie_offset = fde_entry_header.type.fde; |
| 2457 | | try eh_frame_stream.seekTo(cie_offset); |
| 2458 | | const cie_entry_header = try EntryHeader.read(&eh_frame_stream, .eh_frame, builtin.cpu.arch.endian()); |
| 2396 | try eh_frame_fbr.seekTo(cie_offset); |
| 2397 | const cie_entry_header = try EntryHeader.read(&eh_frame_fbr, .eh_frame); |
| 2459 | 2398 | if (!self.isValidPtr(@intFromPtr(&cie_entry_header.entry_bytes[cie_entry_header.entry_bytes.len - 1]), isValidMemory, eh_frame_len)) return badDwarf(); |
| 2460 | 2399 | if (cie_entry_header.type != .cie) return badDwarf(); |
| 2461 | 2400 | |
| ... | ... | @@ -2463,11 +2402,11 @@ pub const ExceptionFrameHeader = struct { |
| 2463 | 2402 | cie_entry_header.entry_bytes, |
| 2464 | 2403 | 0, |
| 2465 | 2404 | true, |
| 2466 | | cie_entry_header.is_64, |
| 2405 | cie_entry_header.format, |
| 2467 | 2406 | .eh_frame, |
| 2468 | 2407 | cie_entry_header.length_offset, |
| 2469 | 2408 | @sizeOf(usize), |
| 2470 | | builtin.cpu.arch.endian(), |
| 2409 | native_endian, |
| 2471 | 2410 | ); |
| 2472 | 2411 | |
| 2473 | 2412 | fde.* = try FrameDescriptionEntry.parse( |
| ... | ... | @@ -2476,7 +2415,7 @@ pub const ExceptionFrameHeader = struct { |
| 2476 | 2415 | true, |
| 2477 | 2416 | cie.*, |
| 2478 | 2417 | @sizeOf(usize), |
| 2479 | | builtin.cpu.arch.endian(), |
| 2418 | native_endian, |
| 2480 | 2419 | ); |
| 2481 | 2420 | } |
| 2482 | 2421 | }; |
| ... | ... | @@ -2484,62 +2423,60 @@ pub const ExceptionFrameHeader = struct { |
| 2484 | 2423 | pub const EntryHeader = struct { |
| 2485 | 2424 | /// Offset of the length field in the backing buffer |
| 2486 | 2425 | length_offset: usize, |
| 2487 | | is_64: bool, |
| 2426 | format: Format, |
| 2488 | 2427 | type: union(enum) { |
| 2489 | 2428 | cie, |
| 2490 | 2429 | /// Value is the offset of the corresponding CIE |
| 2491 | 2430 | fde: u64, |
| 2492 | | terminator: void, |
| 2431 | terminator, |
| 2493 | 2432 | }, |
| 2494 | 2433 | /// The entry's contents, not including the ID field |
| 2495 | 2434 | entry_bytes: []const u8, |
| 2496 | 2435 | |
| 2497 | | /// Reads a header for either an FDE or a CIE, then advances the stream to the position after the trailing structure. |
| 2498 | | /// `stream` must be a stream backed by either the .eh_frame or .debug_frame sections. |
| 2499 | | pub fn read(stream: *std.io.FixedBufferStream([]const u8), dwarf_section: DwarfSection, endian: std.builtin.Endian) !EntryHeader { |
| 2500 | | assert(dwarf_section == .eh_frame or dwarf_section == .debug_frame); |
| 2436 | /// The length of the entry including the ID field, but not the length field itself |
| 2437 | pub fn entryLength(self: EntryHeader) usize { |
| 2438 | return self.entry_bytes.len + @as(u8, if (self.is_64) 8 else 4); |
| 2439 | } |
| 2501 | 2440 | |
| 2502 | | const reader = stream.reader(); |
| 2503 | | const length_offset = stream.pos; |
| 2441 | /// Reads a header for either an FDE or a CIE, then advances the fbr to the position after the trailing structure. |
| 2442 | /// `fbr` must be a FixedBufferReader backed by either the .eh_frame or .debug_frame sections. |
| 2443 | pub fn read(fbr: *FixedBufferReader, dwarf_section: DwarfSection) !EntryHeader { |
| 2444 | assert(dwarf_section == .eh_frame or dwarf_section == .debug_frame); |
| 2504 | 2445 | |
| 2505 | | var is_64: bool = undefined; |
| 2506 | | const length = math.cast(usize, try readUnitLength(reader, endian, &is_64)) orelse return badDwarf(); |
| 2507 | | if (length == 0) return .{ |
| 2446 | const length_offset = fbr.pos; |
| 2447 | const unit_header = try readUnitHeader(fbr); |
| 2448 | const unit_length = math.cast(usize, unit_header.unit_length) orelse return badDwarf(); |
| 2449 | if (unit_length == 0) return .{ |
| 2508 | 2450 | .length_offset = length_offset, |
| 2509 | | .is_64 = is_64, |
| 2510 | | .type = .{ .terminator = {} }, |
| 2451 | .format = unit_header.format, |
| 2452 | .type = .terminator, |
| 2511 | 2453 | .entry_bytes = &.{}, |
| 2512 | 2454 | }; |
| 2455 | const start_offset = fbr.pos; |
| 2456 | const end_offset = start_offset + unit_length; |
| 2457 | defer fbr.pos = end_offset; |
| 2513 | 2458 | |
| 2514 | | const id_len = @as(u8, if (is_64) 8 else 4); |
| 2515 | | const id = if (is_64) try reader.readInt(u64, endian) else try reader.readInt(u32, endian); |
| 2516 | | const entry_bytes = stream.buffer[stream.pos..][0 .. length - id_len]; |
| 2459 | const id = try fbr.readAddress(unit_header.format); |
| 2460 | const entry_bytes = fbr.buf[fbr.pos..end_offset]; |
| 2517 | 2461 | const cie_id: u64 = switch (dwarf_section) { |
| 2518 | 2462 | .eh_frame => CommonInformationEntry.eh_id, |
| 2519 | | .debug_frame => if (is_64) CommonInformationEntry.dwarf64_id else CommonInformationEntry.dwarf32_id, |
| 2463 | .debug_frame => switch (unit_header.format) { |
| 2464 | .@"32" => CommonInformationEntry.dwarf32_id, |
| 2465 | .@"64" => CommonInformationEntry.dwarf64_id, |
| 2466 | }, |
| 2520 | 2467 | else => unreachable, |
| 2521 | 2468 | }; |
| 2522 | 2469 | |
| 2523 | | const result = EntryHeader{ |
| 2470 | return .{ |
| 2524 | 2471 | .length_offset = length_offset, |
| 2525 | | .is_64 = is_64, |
| 2526 | | .type = if (id == cie_id) .{ .cie = {} } else .{ |
| 2527 | | .fde = switch (dwarf_section) { |
| 2528 | | .eh_frame => try std.math.sub(u64, stream.pos - id_len, id), |
| 2529 | | .debug_frame => id, |
| 2530 | | else => unreachable, |
| 2531 | | }, |
| 2532 | | }, |
| 2472 | .format = unit_header.format, |
| 2473 | .type = if (id == cie_id) .cie else .{ .fde = switch (dwarf_section) { |
| 2474 | .eh_frame => try math.sub(u64, start_offset, id), |
| 2475 | .debug_frame => id, |
| 2476 | else => unreachable, |
| 2477 | } }, |
| 2533 | 2478 | .entry_bytes = entry_bytes, |
| 2534 | 2479 | }; |
| 2535 | | |
| 2536 | | stream.pos += entry_bytes.len; |
| 2537 | | return result; |
| 2538 | | } |
| 2539 | | |
| 2540 | | /// The length of the entry including the ID field, but not the length field itself |
| 2541 | | pub fn entryLength(self: EntryHeader) usize { |
| 2542 | | return self.entry_bytes.len + @as(u8, if (self.is_64) 8 else 4); |
| 2543 | 2480 | } |
| 2544 | 2481 | }; |
| 2545 | 2482 | |
| ... | ... | @@ -2558,7 +2495,7 @@ pub const CommonInformationEntry = struct { |
| 2558 | 2495 | length_offset: u64, |
| 2559 | 2496 | version: u8, |
| 2560 | 2497 | address_size: u8, |
| 2561 | | is_64: bool, |
| 2498 | format: Format, |
| 2562 | 2499 | |
| 2563 | 2500 | // Only present in version 4 |
| 2564 | 2501 | segment_selector_size: ?u8, |
| ... | ... | @@ -2602,7 +2539,7 @@ pub const CommonInformationEntry = struct { |
| 2602 | 2539 | cie_bytes: []const u8, |
| 2603 | 2540 | pc_rel_offset: i64, |
| 2604 | 2541 | is_runtime: bool, |
| 2605 | | is_64: bool, |
| 2542 | format: Format, |
| 2606 | 2543 | dwarf_section: DwarfSection, |
| 2607 | 2544 | length_offset: u64, |
| 2608 | 2545 | addr_size_bytes: u8, |
| ... | ... | @@ -2610,10 +2547,9 @@ pub const CommonInformationEntry = struct { |
| 2610 | 2547 | ) !CommonInformationEntry { |
| 2611 | 2548 | if (addr_size_bytes > 8) return error.UnsupportedAddrSize; |
| 2612 | 2549 | |
| 2613 | | var stream = io.fixedBufferStream(cie_bytes); |
| 2614 | | const reader = stream.reader(); |
| 2550 | var fbr: FixedBufferReader = .{ .buf = cie_bytes, .endian = endian }; |
| 2615 | 2551 | |
| 2616 | | const version = try reader.readByte(); |
| 2552 | const version = try fbr.readByte(); |
| 2617 | 2553 | switch (dwarf_section) { |
| 2618 | 2554 | .eh_frame => if (version != 1 and version != 3) return error.UnsupportedDwarfVersion, |
| 2619 | 2555 | .debug_frame => if (version != 4) return error.UnsupportedDwarfVersion, |
| ... | ... | @@ -2624,9 +2560,9 @@ pub const CommonInformationEntry = struct { |
| 2624 | 2560 | var has_aug_data = false; |
| 2625 | 2561 | |
| 2626 | 2562 | var aug_str_len: usize = 0; |
| 2627 | | const aug_str_start = stream.pos; |
| 2628 | | var aug_byte = try reader.readByte(); |
| 2629 | | while (aug_byte != 0) : (aug_byte = try reader.readByte()) { |
| 2563 | const aug_str_start = fbr.pos; |
| 2564 | var aug_byte = try fbr.readByte(); |
| 2565 | while (aug_byte != 0) : (aug_byte = try fbr.readByte()) { |
| 2630 | 2566 | switch (aug_byte) { |
| 2631 | 2567 | 'z' => { |
| 2632 | 2568 | if (aug_str_len != 0) return badDwarf(); |
| ... | ... | @@ -2634,7 +2570,7 @@ pub const CommonInformationEntry = struct { |
| 2634 | 2570 | }, |
| 2635 | 2571 | 'e' => { |
| 2636 | 2572 | if (has_aug_data or aug_str_len != 0) return badDwarf(); |
| 2637 | | if (try reader.readByte() != 'h') return badDwarf(); |
| 2573 | if (try fbr.readByte() != 'h') return badDwarf(); |
| 2638 | 2574 | has_eh_data = true; |
| 2639 | 2575 | }, |
| 2640 | 2576 | else => if (has_eh_data) return badDwarf(), |
| ... | ... | @@ -2645,15 +2581,15 @@ pub const CommonInformationEntry = struct { |
| 2645 | 2581 | |
| 2646 | 2582 | if (has_eh_data) { |
| 2647 | 2583 | // legacy data created by older versions of gcc - unsupported here |
| 2648 | | for (0..addr_size_bytes) |_| _ = try reader.readByte(); |
| 2584 | for (0..addr_size_bytes) |_| _ = try fbr.readByte(); |
| 2649 | 2585 | } |
| 2650 | 2586 | |
| 2651 | | const address_size = if (version == 4) try reader.readByte() else addr_size_bytes; |
| 2652 | | const segment_selector_size = if (version == 4) try reader.readByte() else null; |
| 2587 | const address_size = if (version == 4) try fbr.readByte() else addr_size_bytes; |
| 2588 | const segment_selector_size = if (version == 4) try fbr.readByte() else null; |
| 2653 | 2589 | |
| 2654 | | const code_alignment_factor = try leb.readULEB128(u32, reader); |
| 2655 | | const data_alignment_factor = try leb.readILEB128(i32, reader); |
| 2656 | | const return_address_register = if (version == 1) try reader.readByte() else try leb.readULEB128(u8, reader); |
| 2590 | const code_alignment_factor = try fbr.readUleb128(u32); |
| 2591 | const data_alignment_factor = try fbr.readIleb128(i32); |
| 2592 | const return_address_register = if (version == 1) try fbr.readByte() else try fbr.readUleb128(u8); |
| 2657 | 2593 | |
| 2658 | 2594 | var lsda_pointer_enc: u8 = EH.PE.omit; |
| 2659 | 2595 | var personality_enc: ?u8 = null; |
| ... | ... | @@ -2662,31 +2598,25 @@ pub const CommonInformationEntry = struct { |
| 2662 | 2598 | |
| 2663 | 2599 | var aug_data: []const u8 = &[_]u8{}; |
| 2664 | 2600 | const aug_str = if (has_aug_data) blk: { |
| 2665 | | const aug_data_len = try leb.readULEB128(usize, reader); |
| 2666 | | const aug_data_start = stream.pos; |
| 2601 | const aug_data_len = try fbr.readUleb128(usize); |
| 2602 | const aug_data_start = fbr.pos; |
| 2667 | 2603 | aug_data = cie_bytes[aug_data_start..][0..aug_data_len]; |
| 2668 | 2604 | |
| 2669 | 2605 | const aug_str = cie_bytes[aug_str_start..][0..aug_str_len]; |
| 2670 | 2606 | for (aug_str[1..]) |byte| { |
| 2671 | 2607 | switch (byte) { |
| 2672 | 2608 | 'L' => { |
| 2673 | | lsda_pointer_enc = try reader.readByte(); |
| 2609 | lsda_pointer_enc = try fbr.readByte(); |
| 2674 | 2610 | }, |
| 2675 | 2611 | 'P' => { |
| 2676 | | personality_enc = try reader.readByte(); |
| 2677 | | personality_routine_pointer = try readEhPointer( |
| 2678 | | reader, |
| 2679 | | personality_enc.?, |
| 2680 | | addr_size_bytes, |
| 2681 | | .{ |
| 2682 | | .pc_rel_base = try pcRelBase(@intFromPtr(&cie_bytes[stream.pos]), pc_rel_offset), |
| 2683 | | .follow_indirect = is_runtime, |
| 2684 | | }, |
| 2685 | | endian, |
| 2686 | | ); |
| 2612 | personality_enc = try fbr.readByte(); |
| 2613 | personality_routine_pointer = try readEhPointer(&fbr, personality_enc.?, addr_size_bytes, .{ |
| 2614 | .pc_rel_base = try pcRelBase(@intFromPtr(&cie_bytes[fbr.pos]), pc_rel_offset), |
| 2615 | .follow_indirect = is_runtime, |
| 2616 | }); |
| 2687 | 2617 | }, |
| 2688 | 2618 | 'R' => { |
| 2689 | | fde_pointer_enc = try reader.readByte(); |
| 2619 | fde_pointer_enc = try fbr.readByte(); |
| 2690 | 2620 | }, |
| 2691 | 2621 | 'S', 'B', 'G' => {}, |
| 2692 | 2622 | else => return badDwarf(), |
| ... | ... | @@ -2694,16 +2624,16 @@ pub const CommonInformationEntry = struct { |
| 2694 | 2624 | } |
| 2695 | 2625 | |
| 2696 | 2626 | // aug_data_len can include padding so the CIE ends on an address boundary |
| 2697 | | try stream.seekTo(aug_data_start + aug_data_len); |
| 2627 | fbr.pos = aug_data_start + aug_data_len; |
| 2698 | 2628 | break :blk aug_str; |
| 2699 | 2629 | } else &[_]u8{}; |
| 2700 | 2630 | |
| 2701 | | const initial_instructions = cie_bytes[stream.pos..]; |
| 2631 | const initial_instructions = cie_bytes[fbr.pos..]; |
| 2702 | 2632 | return .{ |
| 2703 | 2633 | .length_offset = length_offset, |
| 2704 | 2634 | .version = version, |
| 2705 | 2635 | .address_size = address_size, |
| 2706 | | .is_64 = is_64, |
| 2636 | .format = format, |
| 2707 | 2637 | .segment_selector_size = segment_selector_size, |
| 2708 | 2638 | .code_alignment_factor = code_alignment_factor, |
| 2709 | 2639 | .data_alignment_factor = data_alignment_factor, |
| ... | ... | @@ -2751,56 +2681,37 @@ pub const FrameDescriptionEntry = struct { |
| 2751 | 2681 | ) !FrameDescriptionEntry { |
| 2752 | 2682 | if (addr_size_bytes > 8) return error.InvalidAddrSize; |
| 2753 | 2683 | |
| 2754 | | var stream = io.fixedBufferStream(fde_bytes); |
| 2755 | | const reader = stream.reader(); |
| 2684 | var fbr: FixedBufferReader = .{ .buf = fde_bytes, .endian = endian }; |
| 2756 | 2685 | |
| 2757 | | const pc_begin = try readEhPointer( |
| 2758 | | reader, |
| 2759 | | cie.fde_pointer_enc, |
| 2760 | | addr_size_bytes, |
| 2761 | | .{ |
| 2762 | | .pc_rel_base = try pcRelBase(@intFromPtr(&fde_bytes[stream.pos]), pc_rel_offset), |
| 2763 | | .follow_indirect = is_runtime, |
| 2764 | | }, |
| 2765 | | endian, |
| 2766 | | ) orelse return badDwarf(); |
| 2767 | | |
| 2768 | | const pc_range = try readEhPointer( |
| 2769 | | reader, |
| 2770 | | cie.fde_pointer_enc, |
| 2771 | | addr_size_bytes, |
| 2772 | | .{ |
| 2773 | | .pc_rel_base = 0, |
| 2774 | | .follow_indirect = false, |
| 2775 | | }, |
| 2776 | | endian, |
| 2777 | | ) orelse return badDwarf(); |
| 2686 | const pc_begin = try readEhPointer(&fbr, cie.fde_pointer_enc, addr_size_bytes, .{ |
| 2687 | .pc_rel_base = try pcRelBase(@intFromPtr(&fde_bytes[fbr.pos]), pc_rel_offset), |
| 2688 | .follow_indirect = is_runtime, |
| 2689 | }) orelse return badDwarf(); |
| 2690 | |
| 2691 | const pc_range = try readEhPointer(&fbr, cie.fde_pointer_enc, addr_size_bytes, .{ |
| 2692 | .pc_rel_base = 0, |
| 2693 | .follow_indirect = false, |
| 2694 | }) orelse return badDwarf(); |
| 2778 | 2695 | |
| 2779 | 2696 | var aug_data: []const u8 = &[_]u8{}; |
| 2780 | 2697 | const lsda_pointer = if (cie.aug_str.len > 0) blk: { |
| 2781 | | const aug_data_len = try leb.readULEB128(usize, reader); |
| 2782 | | const aug_data_start = stream.pos; |
| 2698 | const aug_data_len = try fbr.readUleb128(usize); |
| 2699 | const aug_data_start = fbr.pos; |
| 2783 | 2700 | aug_data = fde_bytes[aug_data_start..][0..aug_data_len]; |
| 2784 | 2701 | |
| 2785 | 2702 | const lsda_pointer = if (cie.lsda_pointer_enc != EH.PE.omit) |
| 2786 | | try readEhPointer( |
| 2787 | | reader, |
| 2788 | | cie.lsda_pointer_enc, |
| 2789 | | addr_size_bytes, |
| 2790 | | .{ |
| 2791 | | .pc_rel_base = try pcRelBase(@intFromPtr(&fde_bytes[stream.pos]), pc_rel_offset), |
| 2792 | | .follow_indirect = is_runtime, |
| 2793 | | }, |
| 2794 | | endian, |
| 2795 | | ) |
| 2703 | try readEhPointer(&fbr, cie.lsda_pointer_enc, addr_size_bytes, .{ |
| 2704 | .pc_rel_base = try pcRelBase(@intFromPtr(&fde_bytes[fbr.pos]), pc_rel_offset), |
| 2705 | .follow_indirect = is_runtime, |
| 2706 | }) |
| 2796 | 2707 | else |
| 2797 | 2708 | null; |
| 2798 | 2709 | |
| 2799 | | try stream.seekTo(aug_data_start + aug_data_len); |
| 2710 | fbr.pos = aug_data_start + aug_data_len; |
| 2800 | 2711 | break :blk lsda_pointer; |
| 2801 | 2712 | } else null; |
| 2802 | 2713 | |
| 2803 | | const instructions = fde_bytes[stream.pos..]; |
| 2714 | const instructions = fde_bytes[fbr.pos..]; |
| 2804 | 2715 | return .{ |
| 2805 | 2716 | .cie_length_offset = cie.length_offset, |
| 2806 | 2717 | .pc_begin = pc_begin, |
| ... | ... | @@ -2820,6 +2731,75 @@ fn pcRelBase(field_ptr: usize, pc_rel_offset: i64) !usize { |
| 2820 | 2731 | } |
| 2821 | 2732 | } |
| 2822 | 2733 | |
| 2734 | // Reading debug info needs to be fast, even when compiled in debug mode, |
| 2735 | // so avoid using a `std.io.FixedBufferStream` which is too slow. |
| 2736 | const FixedBufferReader = struct { |
| 2737 | buf: []const u8, |
| 2738 | pos: usize = 0, |
| 2739 | endian: std.builtin.Endian, |
| 2740 | |
| 2741 | pub const Error = error{ EndOfBuffer, Overflow }; |
| 2742 | |
| 2743 | fn seekTo(fbr: *FixedBufferReader, pos: u64) Error!void { |
| 2744 | if (pos > fbr.buf.len) return error.EndOfBuffer; |
| 2745 | fbr.pos = @intCast(pos); |
| 2746 | } |
| 2747 | |
| 2748 | fn seekForward(fbr: *FixedBufferReader, amount: u64) Error!void { |
| 2749 | if (fbr.buf.len - fbr.pos < amount) return error.EndOfBuffer; |
| 2750 | fbr.pos += @intCast(amount); |
| 2751 | } |
| 2752 | |
| 2753 | pub inline fn readByte(fbr: *FixedBufferReader) Error!u8 { |
| 2754 | if (fbr.pos >= fbr.buf.len) return error.EndOfBuffer; |
| 2755 | defer fbr.pos += 1; |
| 2756 | return fbr.buf[fbr.pos]; |
| 2757 | } |
| 2758 | |
| 2759 | fn readByteSigned(fbr: *FixedBufferReader) Error!i8 { |
| 2760 | return @bitCast(try fbr.readByte()); |
| 2761 | } |
| 2762 | |
| 2763 | fn readInt(fbr: *FixedBufferReader, comptime T: type) Error!T { |
| 2764 | const size = @divExact(@typeInfo(T).Int.bits, 8); |
| 2765 | if (fbr.buf.len - fbr.pos < size) return error.EndOfBuffer; |
| 2766 | defer fbr.pos += size; |
| 2767 | return mem.readInt(T, fbr.buf[fbr.pos..][0..size], fbr.endian); |
| 2768 | } |
| 2769 | |
| 2770 | fn readUleb128(fbr: *FixedBufferReader, comptime T: type) Error!T { |
| 2771 | return std.leb.readULEB128(T, fbr); |
| 2772 | } |
| 2773 | |
| 2774 | fn readIleb128(fbr: *FixedBufferReader, comptime T: type) Error!T { |
| 2775 | return std.leb.readILEB128(T, fbr); |
| 2776 | } |
| 2777 | |
| 2778 | fn readAddress(fbr: *FixedBufferReader, format: Format) Error!u64 { |
| 2779 | return switch (format) { |
| 2780 | .@"32" => try fbr.readInt(u32), |
| 2781 | .@"64" => try fbr.readInt(u64), |
| 2782 | }; |
| 2783 | } |
| 2784 | |
| 2785 | fn readBytes(fbr: *FixedBufferReader, len: usize) Error![]const u8 { |
| 2786 | if (fbr.buf.len - fbr.pos < len) return error.EndOfBuffer; |
| 2787 | defer fbr.pos += len; |
| 2788 | return fbr.buf[fbr.pos..][0..len]; |
| 2789 | } |
| 2790 | |
| 2791 | fn readBytesTo(fbr: *FixedBufferReader, comptime sentinel: u8) Error![:sentinel]const u8 { |
| 2792 | const end = @call(.always_inline, mem.indexOfScalarPos, .{ |
| 2793 | u8, |
| 2794 | fbr.buf, |
| 2795 | fbr.pos, |
| 2796 | sentinel, |
| 2797 | }) orelse return error.EndOfBuffer; |
| 2798 | defer fbr.pos = end + 1; |
| 2799 | return fbr.buf[fbr.pos..end :sentinel]; |
| 2800 | } |
| 2801 | }; |
| 2802 | |
| 2823 | 2803 | test { |
| 2824 | 2804 | std.testing.refAllDecls(@This()); |
| 2825 | 2805 | } |