| author | |
| committer | |
| log | 4e09e363cd00b1dd36467ef7958d750ea09f296d |
| tree | 6b0b98cf032a6177ec2a8189f5138390d41b9481 |
| parent | c013f45ad08c2c6d727bf336767e23d988f5f30b |
| parent | 29c7f6810fe00cf69adf81d635d3410402b530e8 |
| signature |
macho: add basic handling of DWARFv54 files changed, 522 insertions(+), 459 deletions(-)
CMakeLists.txt+1-1| ... | @@ -611,6 +611,7 @@ set(ZIG_STAGE2_SOURCES | ... | @@ -611,6 +611,7 @@ set(ZIG_STAGE2_SOURCES |
| 611 | src/link/MachO/Atom.zig | 611 | src/link/MachO/Atom.zig |
| 612 | src/link/MachO/CodeSignature.zig | 612 | src/link/MachO/CodeSignature.zig |
| 613 | src/link/MachO/DebugSymbols.zig | 613 | src/link/MachO/DebugSymbols.zig |
| 614 | src/link/MachO/Dwarf.zig | ||
| 614 | src/link/MachO/Dylib.zig | 615 | src/link/MachO/Dylib.zig |
| 615 | src/link/MachO/InternalObject.zig | 616 | src/link/MachO/InternalObject.zig |
| 616 | src/link/MachO/Object.zig | 617 | src/link/MachO/Object.zig |
| ... | @@ -622,7 +623,6 @@ set(ZIG_STAGE2_SOURCES | ... | @@ -622,7 +623,6 @@ set(ZIG_STAGE2_SOURCES |
| 622 | src/link/MachO/dyld_info/Rebase.zig | 623 | src/link/MachO/dyld_info/Rebase.zig |
| 623 | src/link/MachO/dyld_info/Trie.zig | 624 | src/link/MachO/dyld_info/Trie.zig |
| 624 | src/link/MachO/dyld_info/bind.zig | 625 | src/link/MachO/dyld_info/bind.zig |
| 625 | src/link/MachO/dwarf.zig | ||
| 626 | src/link/MachO/eh_frame.zig | 626 | src/link/MachO/eh_frame.zig |
| 627 | src/link/MachO/fat.zig | 627 | src/link/MachO/fat.zig |
| 628 | src/link/MachO/file.zig | 628 | src/link/MachO/file.zig |
src/link/MachO/Dwarf.zig created+409| ... | @@ -0,0 +1,409 @@ | ||
| 1 | debug_info: []u8 = &[0]u8{}, | ||
| 2 | debug_abbrev: []u8 = &[0]u8{}, | ||
| 3 | debug_str: []u8 = &[0]u8{}, | ||
| 4 | debug_str_offsets: []u8 = &[0]u8{}, | ||
| 5 | |||
| 6 | pub fn deinit(dwarf: *Dwarf, allocator: Allocator) void { | ||
| 7 | allocator.free(dwarf.debug_info); | ||
| 8 | allocator.free(dwarf.debug_abbrev); | ||
| 9 | allocator.free(dwarf.debug_str); | ||
| 10 | allocator.free(dwarf.debug_str_offsets); | ||
| 11 | } | ||
| 12 | |||
| 13 | /// Pulls an offset into __debug_str section from a __debug_str_offs section. | ||
| 14 | /// This is new in DWARFv5 and requires the producer to specify DW_FORM_strx* (`index` arg) | ||
| 15 | /// but also DW_AT_str_offsets_base with DW_FORM_sec_offset (`base` arg) in the opening header | ||
| 16 | /// of a "referencing entity" such as DW_TAG_compile_unit. | ||
| 17 | fn getOffset(debug_str_offsets: []const u8, base: u64, index: u64, dw_fmt: DwarfFormat) error{Overflow}!u64 { | ||
| 18 | const base_as_usize = math.cast(usize, base) orelse return error.Overflow; | ||
| 19 | const index_as_usize = math.cast(usize, index) orelse return error.Overflow; | ||
| 20 | return switch (dw_fmt) { | ||
| 21 | .dwarf32 => @as( | ||
| 22 | *align(1) const u32, | ||
| 23 | @ptrCast(debug_str_offsets.ptr + base_as_usize + index_as_usize * @sizeOf(u32)), | ||
| 24 | ).*, | ||
| 25 | .dwarf64 => @as( | ||
| 26 | *align(1) const u64, | ||
| 27 | @ptrCast(debug_str_offsets.ptr + base_as_usize + index_as_usize * @sizeOf(u64)), | ||
| 28 | ).*, | ||
| 29 | }; | ||
| 30 | } | ||
| 31 | |||
| 32 | pub const InfoReader = struct { | ||
| 33 | ctx: Dwarf, | ||
| 34 | pos: usize = 0, | ||
| 35 | |||
| 36 | fn bytes(p: InfoReader) []const u8 { | ||
| 37 | return p.ctx.debug_info; | ||
| 38 | } | ||
| 39 | |||
| 40 | pub fn readCompileUnitHeader(p: *InfoReader) !CompileUnitHeader { | ||
| 41 | var length: u64 = try p.readInt(u32); | ||
| 42 | const is_64bit = length == 0xffffffff; | ||
| 43 | if (is_64bit) { | ||
| 44 | length = try p.readInt(u64); | ||
| 45 | } | ||
| 46 | const dw_fmt: DwarfFormat = if (is_64bit) .dwarf64 else .dwarf32; | ||
| 47 | const version = try p.readInt(Version); | ||
| 48 | const rest: struct { | ||
| 49 | debug_abbrev_offset: u64, | ||
| 50 | address_size: u8, | ||
| 51 | unit_type: u8, | ||
| 52 | } = switch (version) { | ||
| 53 | 4 => .{ | ||
| 54 | .debug_abbrev_offset = try p.readOffset(dw_fmt), | ||
| 55 | .address_size = try p.readByte(), | ||
| 56 | .unit_type = 0, | ||
| 57 | }, | ||
| 58 | 5 => .{ | ||
| 59 | // According to the spec, version 5 introduced .unit_type field in the header, and | ||
| 60 | // it reordered .debug_abbrev_offset with .address_size fields. | ||
| 61 | .unit_type = try p.readByte(), | ||
| 62 | .address_size = try p.readByte(), | ||
| 63 | .debug_abbrev_offset = try p.readOffset(dw_fmt), | ||
| 64 | }, | ||
| 65 | else => return error.InvalidVersion, | ||
| 66 | }; | ||
| 67 | return .{ | ||
| 68 | .format = dw_fmt, | ||
| 69 | .length = length, | ||
| 70 | .version = version, | ||
| 71 | .debug_abbrev_offset = rest.debug_abbrev_offset, | ||
| 72 | .address_size = rest.address_size, | ||
| 73 | .unit_type = rest.unit_type, | ||
| 74 | }; | ||
| 75 | } | ||
| 76 | |||
| 77 | pub fn seekToDie(p: *InfoReader, code: Code, cuh: CompileUnitHeader, abbrev_reader: *AbbrevReader) !void { | ||
| 78 | const cuh_length = math.cast(usize, cuh.length) orelse return error.Overflow; | ||
| 79 | const end_pos = p.pos + switch (cuh.format) { | ||
| 80 | .dwarf32 => @as(usize, 4), | ||
| 81 | .dwarf64 => 12, | ||
| 82 | } + cuh_length; | ||
| 83 | while (p.pos < end_pos) { | ||
| 84 | const di_code = try p.readUleb128(u64); | ||
| 85 | if (di_code == 0) return error.UnexpectedEndOfFile; | ||
| 86 | if (di_code == code) return; | ||
| 87 | |||
| 88 | while (try abbrev_reader.readAttr()) |attr| { | ||
| 89 | try p.skip(attr.form, cuh); | ||
| 90 | } | ||
| 91 | } | ||
| 92 | return error.UnexpectedEndOfFile; | ||
| 93 | } | ||
| 94 | |||
| 95 | /// When skipping attributes, we don't really need to be able to handle them all | ||
| 96 | /// since we only ever care about the DW_TAG_compile_unit. | ||
| 97 | pub fn skip(p: *InfoReader, form: Form, cuh: CompileUnitHeader) !void { | ||
| 98 | switch (form) { | ||
| 99 | dw.FORM.sec_offset, | ||
| 100 | dw.FORM.ref_addr, | ||
| 101 | => { | ||
| 102 | _ = try p.readOffset(cuh.format); | ||
| 103 | }, | ||
| 104 | |||
| 105 | dw.FORM.addr => { | ||
| 106 | _ = try p.readNBytes(cuh.address_size); | ||
| 107 | }, | ||
| 108 | |||
| 109 | dw.FORM.block1, | ||
| 110 | dw.FORM.block2, | ||
| 111 | dw.FORM.block4, | ||
| 112 | dw.FORM.block, | ||
| 113 | => { | ||
| 114 | _ = try p.readBlock(form); | ||
| 115 | }, | ||
| 116 | |||
| 117 | dw.FORM.exprloc => { | ||
| 118 | _ = try p.readExprLoc(); | ||
| 119 | }, | ||
| 120 | |||
| 121 | dw.FORM.flag_present => {}, | ||
| 122 | |||
| 123 | dw.FORM.data1, | ||
| 124 | dw.FORM.ref1, | ||
| 125 | dw.FORM.flag, | ||
| 126 | dw.FORM.data2, | ||
| 127 | dw.FORM.ref2, | ||
| 128 | dw.FORM.data4, | ||
| 129 | dw.FORM.ref4, | ||
| 130 | dw.FORM.data8, | ||
| 131 | dw.FORM.ref8, | ||
| 132 | dw.FORM.ref_sig8, | ||
| 133 | dw.FORM.udata, | ||
| 134 | dw.FORM.ref_udata, | ||
| 135 | dw.FORM.sdata, | ||
| 136 | => { | ||
| 137 | _ = try p.readConstant(form); | ||
| 138 | }, | ||
| 139 | |||
| 140 | dw.FORM.strp, | ||
| 141 | dw.FORM.string, | ||
| 142 | => { | ||
| 143 | _ = try p.readString(form, cuh); | ||
| 144 | }, | ||
| 145 | |||
| 146 | else => if (cuh.version >= 5) switch (form) { | ||
| 147 | dw.FORM.strx, | ||
| 148 | dw.FORM.strx1, | ||
| 149 | dw.FORM.strx2, | ||
| 150 | dw.FORM.strx3, | ||
| 151 | dw.FORM.strx4, | ||
| 152 | => { | ||
| 153 | // We are just iterating over the __debug_info data, so we don't care about an actual | ||
| 154 | // string, therefore we set the `base = 0`. | ||
| 155 | _ = try p.readStringIndexed(form, cuh, 0); | ||
| 156 | }, | ||
| 157 | |||
| 158 | dw.FORM.addrx, | ||
| 159 | dw.FORM.addrx1, | ||
| 160 | dw.FORM.addrx2, | ||
| 161 | dw.FORM.addrx3, | ||
| 162 | dw.FORM.addrx4, | ||
| 163 | => { | ||
| 164 | _ = try p.readIndex(form); | ||
| 165 | }, | ||
| 166 | |||
| 167 | else => return error.UnhandledForm, | ||
| 168 | } else return error.UnhandledForm, | ||
| 169 | } | ||
| 170 | } | ||
| 171 | |||
| 172 | pub fn readBlock(p: *InfoReader, form: Form) ![]const u8 { | ||
| 173 | const len: u64 = switch (form) { | ||
| 174 | dw.FORM.block1 => try p.readByte(), | ||
| 175 | dw.FORM.block2 => try p.readInt(u16), | ||
| 176 | dw.FORM.block4 => try p.readInt(u32), | ||
| 177 | dw.FORM.block => try p.readUleb128(u64), | ||
| 178 | else => unreachable, | ||
| 179 | }; | ||
| 180 | return p.readNBytes(len); | ||
| 181 | } | ||
| 182 | |||
| 183 | pub fn readExprLoc(p: *InfoReader) ![]const u8 { | ||
| 184 | const len: u64 = try p.readUleb128(u64); | ||
| 185 | return p.readNBytes(len); | ||
| 186 | } | ||
| 187 | |||
| 188 | pub fn readConstant(p: *InfoReader, form: Form) !u64 { | ||
| 189 | return switch (form) { | ||
| 190 | dw.FORM.data1, dw.FORM.ref1, dw.FORM.flag => try p.readByte(), | ||
| 191 | dw.FORM.data2, dw.FORM.ref2 => try p.readInt(u16), | ||
| 192 | dw.FORM.data4, dw.FORM.ref4 => try p.readInt(u32), | ||
| 193 | dw.FORM.data8, dw.FORM.ref8, dw.FORM.ref_sig8 => try p.readInt(u64), | ||
| 194 | dw.FORM.udata, dw.FORM.ref_udata => try p.readUleb128(u64), | ||
| 195 | dw.FORM.sdata => @bitCast(try p.readIleb128(i64)), | ||
| 196 | else => return error.UnhandledConstantForm, | ||
| 197 | }; | ||
| 198 | } | ||
| 199 | |||
| 200 | pub fn readIndex(p: *InfoReader, form: Form) !u64 { | ||
| 201 | return switch (form) { | ||
| 202 | dw.FORM.strx1, dw.FORM.addrx1 => try p.readByte(), | ||
| 203 | dw.FORM.strx2, dw.FORM.addrx2 => try p.readInt(u16), | ||
| 204 | dw.FORM.strx3, dw.FORM.addrx3 => error.UnhandledForm, | ||
| 205 | dw.FORM.strx4, dw.FORM.addrx4 => try p.readInt(u32), | ||
| 206 | dw.FORM.strx, dw.FORM.addrx => try p.readUleb128(u64), | ||
| 207 | else => return error.UnhandledIndexForm, | ||
| 208 | }; | ||
| 209 | } | ||
| 210 | |||
| 211 | pub fn readString(p: *InfoReader, form: Form, cuh: CompileUnitHeader) ![:0]const u8 { | ||
| 212 | switch (form) { | ||
| 213 | dw.FORM.strp => { | ||
| 214 | const off = try p.readOffset(cuh.format); | ||
| 215 | const off_u = math.cast(usize, off) orelse return error.Overflow; | ||
| 216 | return mem.sliceTo(@as([*:0]const u8, @ptrCast(p.ctx.debug_str.ptr + off_u)), 0); | ||
| 217 | }, | ||
| 218 | dw.FORM.string => { | ||
| 219 | const start = p.pos; | ||
| 220 | while (p.pos < p.bytes().len) : (p.pos += 1) { | ||
| 221 | if (p.bytes()[p.pos] == 0) break; | ||
| 222 | } | ||
| 223 | if (p.bytes()[p.pos] != 0) return error.UnexpectedEndOfFile; | ||
| 224 | return p.bytes()[start..p.pos :0]; | ||
| 225 | }, | ||
| 226 | else => unreachable, | ||
| 227 | } | ||
| 228 | } | ||
| 229 | |||
| 230 | pub fn readStringIndexed(p: *InfoReader, form: Form, cuh: CompileUnitHeader, base: u64) ![:0]const u8 { | ||
| 231 | switch (form) { | ||
| 232 | dw.FORM.strx, | ||
| 233 | dw.FORM.strx1, | ||
| 234 | dw.FORM.strx2, | ||
| 235 | dw.FORM.strx3, | ||
| 236 | dw.FORM.strx4, | ||
| 237 | => { | ||
| 238 | const index = try p.readIndex(form); | ||
| 239 | const off = math.cast( | ||
| 240 | usize, | ||
| 241 | try getOffset(p.ctx.debug_str_offsets, base, index, cuh.format), | ||
| 242 | ) orelse return error.Overflow; | ||
| 243 | return mem.sliceTo(@as([*:0]const u8, @ptrCast(p.ctx.debug_str.ptr + off)), 0); | ||
| 244 | }, | ||
| 245 | else => unreachable, | ||
| 246 | } | ||
| 247 | } | ||
| 248 | |||
| 249 | pub fn readByte(p: *InfoReader) !u8 { | ||
| 250 | if (p.pos + 1 > p.bytes().len) return error.UnexpectedEndOfFile; | ||
| 251 | defer p.pos += 1; | ||
| 252 | return p.bytes()[p.pos]; | ||
| 253 | } | ||
| 254 | |||
| 255 | pub fn readNBytes(p: *InfoReader, num: u64) ![]const u8 { | ||
| 256 | const num_usize = math.cast(usize, num) orelse return error.Overflow; | ||
| 257 | if (p.pos + num_usize > p.bytes().len) return error.UnexpectedEndOfFile; | ||
| 258 | defer p.pos += num_usize; | ||
| 259 | return p.bytes()[p.pos..][0..num_usize]; | ||
| 260 | } | ||
| 261 | |||
| 262 | pub fn readInt(p: *InfoReader, comptime Int: type) !Int { | ||
| 263 | if (p.pos + @sizeOf(Int) > p.bytes().len) return error.UnexpectedEndOfFile; | ||
| 264 | defer p.pos += @sizeOf(Int); | ||
| 265 | return mem.readInt(Int, p.bytes()[p.pos..][0..@sizeOf(Int)], .little); | ||
| 266 | } | ||
| 267 | |||
| 268 | pub fn readOffset(p: *InfoReader, dw_fmt: DwarfFormat) !u64 { | ||
| 269 | return switch (dw_fmt) { | ||
| 270 | .dwarf32 => try p.readInt(u32), | ||
| 271 | .dwarf64 => try p.readInt(u64), | ||
| 272 | }; | ||
| 273 | } | ||
| 274 | |||
| 275 | pub fn readUleb128(p: *InfoReader, comptime Type: type) !Type { | ||
| 276 | var stream = std.io.fixedBufferStream(p.bytes()[p.pos..]); | ||
| 277 | var creader = std.io.countingReader(stream.reader()); | ||
| 278 | const value: Type = try leb.readUleb128(Type, creader.reader()); | ||
| 279 | p.pos += math.cast(usize, creader.bytes_read) orelse return error.Overflow; | ||
| 280 | return value; | ||
| 281 | } | ||
| 282 | |||
| 283 | pub fn readIleb128(p: *InfoReader, comptime Type: type) !Type { | ||
| 284 | var stream = std.io.fixedBufferStream(p.bytes()[p.pos..]); | ||
| 285 | var creader = std.io.countingReader(stream.reader()); | ||
| 286 | const value: Type = try leb.readIleb128(Type, creader.reader()); | ||
| 287 | p.pos += math.cast(usize, creader.bytes_read) orelse return error.Overflow; | ||
| 288 | return value; | ||
| 289 | } | ||
| 290 | |||
| 291 | pub fn seekTo(p: *InfoReader, off: u64) !void { | ||
| 292 | p.pos = math.cast(usize, off) orelse return error.Overflow; | ||
| 293 | } | ||
| 294 | }; | ||
| 295 | |||
| 296 | pub const AbbrevReader = struct { | ||
| 297 | ctx: Dwarf, | ||
| 298 | pos: usize = 0, | ||
| 299 | |||
| 300 | fn bytes(p: AbbrevReader) []const u8 { | ||
| 301 | return p.ctx.debug_abbrev; | ||
| 302 | } | ||
| 303 | |||
| 304 | pub fn hasMore(p: AbbrevReader) bool { | ||
| 305 | return p.pos < p.bytes().len; | ||
| 306 | } | ||
| 307 | |||
| 308 | pub fn readDecl(p: *AbbrevReader) !?AbbrevDecl { | ||
| 309 | const pos = p.pos; | ||
| 310 | const code = try p.readUleb128(Code); | ||
| 311 | if (code == 0) return null; | ||
| 312 | |||
| 313 | const tag = try p.readUleb128(Tag); | ||
| 314 | const has_children = (try p.readByte()) > 0; | ||
| 315 | return .{ | ||
| 316 | .code = code, | ||
| 317 | .pos = pos, | ||
| 318 | .len = p.pos - pos, | ||
| 319 | .tag = tag, | ||
| 320 | .has_children = has_children, | ||
| 321 | }; | ||
| 322 | } | ||
| 323 | |||
| 324 | pub fn readAttr(p: *AbbrevReader) !?AbbrevAttr { | ||
| 325 | const pos = p.pos; | ||
| 326 | const at = try p.readUleb128(At); | ||
| 327 | const form = try p.readUleb128(Form); | ||
| 328 | return if (at == 0 and form == 0) null else .{ | ||
| 329 | .at = at, | ||
| 330 | .form = form, | ||
| 331 | .pos = pos, | ||
| 332 | .len = p.pos - pos, | ||
| 333 | }; | ||
| 334 | } | ||
| 335 | |||
| 336 | pub fn readByte(p: *AbbrevReader) !u8 { | ||
| 337 | if (p.pos + 1 > p.bytes().len) return error.Eof; | ||
| 338 | defer p.pos += 1; | ||
| 339 | return p.bytes()[p.pos]; | ||
| 340 | } | ||
| 341 | |||
| 342 | pub fn readUleb128(p: *AbbrevReader, comptime Type: type) !Type { | ||
| 343 | var stream = std.io.fixedBufferStream(p.bytes()[p.pos..]); | ||
| 344 | var creader = std.io.countingReader(stream.reader()); | ||
| 345 | const value: Type = try leb.readUleb128(Type, creader.reader()); | ||
| 346 | p.pos += math.cast(usize, creader.bytes_read) orelse return error.Overflow; | ||
| 347 | return value; | ||
| 348 | } | ||
| 349 | |||
| 350 | pub fn seekTo(p: *AbbrevReader, off: u64) !void { | ||
| 351 | p.pos = math.cast(usize, off) orelse return error.Overflow; | ||
| 352 | } | ||
| 353 | }; | ||
| 354 | |||
| 355 | const AbbrevDecl = struct { | ||
| 356 | code: Code, | ||
| 357 | pos: usize, | ||
| 358 | len: usize, | ||
| 359 | tag: Tag, | ||
| 360 | has_children: bool, | ||
| 361 | }; | ||
| 362 | |||
| 363 | const AbbrevAttr = struct { | ||
| 364 | at: At, | ||
| 365 | form: Form, | ||
| 366 | pos: usize, | ||
| 367 | len: usize, | ||
| 368 | }; | ||
| 369 | |||
| 370 | const CompileUnitHeader = struct { | ||
| 371 | format: DwarfFormat, | ||
| 372 | length: u64, | ||
| 373 | version: Version, | ||
| 374 | debug_abbrev_offset: u64, | ||
| 375 | address_size: u8, | ||
| 376 | unit_type: u8, | ||
| 377 | }; | ||
| 378 | |||
| 379 | const Die = struct { | ||
| 380 | pos: usize, | ||
| 381 | len: usize, | ||
| 382 | }; | ||
| 383 | |||
| 384 | const DwarfFormat = enum { | ||
| 385 | dwarf32, | ||
| 386 | dwarf64, | ||
| 387 | }; | ||
| 388 | |||
| 389 | const dw = std.dwarf; | ||
| 390 | const leb = std.leb; | ||
| 391 | const log = std.log.scoped(.link); | ||
| 392 | const math = std.math; | ||
| 393 | const mem = std.mem; | ||
| 394 | const std = @import("std"); | ||
| 395 | const Allocator = mem.Allocator; | ||
| 396 | const Dwarf = @This(); | ||
| 397 | const File = @import("file.zig").File; | ||
| 398 | const MachO = @import("../MachO.zig"); | ||
| 399 | const Object = @import("Object.zig"); | ||
| 400 | |||
| 401 | pub const At = u64; | ||
| 402 | pub const Code = u64; | ||
| 403 | pub const Form = u64; | ||
| 404 | pub const Tag = u64; | ||
| 405 | pub const Version = u16; | ||
| 406 | |||
| 407 | pub const AT = dw.AT; | ||
| 408 | pub const FORM = dw.FORM; | ||
| 409 | pub const TAG = dw.TAG; | ||
src/link/MachO/Object.zig+112-172| ... | @@ -443,11 +443,8 @@ fn initCstringLiterals(self: *Object, allocator: Allocator, file: File.Handle, m | ... | @@ -443,11 +443,8 @@ fn initCstringLiterals(self: *Object, allocator: Allocator, file: File.Handle, m |
| 443 | for (slice.items(.header), 0..) |sect, n_sect| { | 443 | for (slice.items(.header), 0..) |sect, n_sect| { |
| 444 | if (!isCstringLiteral(sect)) continue; | 444 | if (!isCstringLiteral(sect)) continue; |
| 445 | 445 | ||
| 446 | const sect_size = math.cast(usize, sect.size) orelse return error.Overflow; | 446 | const data = try self.readSectionData(allocator, file, @intCast(n_sect)); |
| 447 | const data = try allocator.alloc(u8, sect_size); | ||
| 448 | defer allocator.free(data); | 447 | defer allocator.free(data); |
| 449 | const amt = try file.preadAll(data, sect.offset + self.offset); | ||
| 450 | if (amt != data.len) return error.InputOutput; | ||
| 451 | 448 | ||
| 452 | var count: u32 = 0; | 449 | var count: u32 = 0; |
| 453 | var start: u32 = 0; | 450 | var start: u32 = 0; |
| ... | @@ -646,13 +643,10 @@ pub fn resolveLiterals(self: *Object, lp: *MachO.LiteralPool, macho_file: *MachO | ... | @@ -646,13 +643,10 @@ pub fn resolveLiterals(self: *Object, lp: *MachO.LiteralPool, macho_file: *MachO |
| 646 | } | 643 | } |
| 647 | 644 | ||
| 648 | const slice = self.sections.slice(); | 645 | const slice = self.sections.slice(); |
| 649 | for (slice.items(.header), slice.items(.subsections)) |header, subs| { | 646 | for (slice.items(.header), slice.items(.subsections), 0..) |header, subs, n_sect| { |
| 650 | if (isCstringLiteral(header) or isFixedSizeLiteral(header)) { | 647 | if (isCstringLiteral(header) or isFixedSizeLiteral(header)) { |
| 651 | const sect_size = math.cast(usize, header.size) orelse return error.Overflow; | 648 | const data = try self.readSectionData(gpa, file, @intCast(n_sect)); |
| 652 | const data = try gpa.alloc(u8, sect_size); | ||
| 653 | defer gpa.free(data); | 649 | defer gpa.free(data); |
| 654 | const amt = try file.preadAll(data, header.offset + self.offset); | ||
| 655 | if (amt != data.len) return error.InputOutput; | ||
| 656 | 650 | ||
| 657 | for (subs.items) |sub| { | 651 | for (subs.items) |sub| { |
| 658 | const atom = self.getAtom(sub.atom).?; | 652 | const atom = self.getAtom(sub.atom).?; |
| ... | @@ -686,12 +680,7 @@ pub fn resolveLiterals(self: *Object, lp: *MachO.LiteralPool, macho_file: *MachO | ... | @@ -686,12 +680,7 @@ pub fn resolveLiterals(self: *Object, lp: *MachO.LiteralPool, macho_file: *MachO |
| 686 | buffer.resize(target_size) catch unreachable; | 680 | buffer.resize(target_size) catch unreachable; |
| 687 | const gop = try sections_data.getOrPut(target.n_sect); | 681 | const gop = try sections_data.getOrPut(target.n_sect); |
| 688 | if (!gop.found_existing) { | 682 | if (!gop.found_existing) { |
| 689 | const target_sect = slice.items(.header)[target.n_sect]; | 683 | gop.value_ptr.* = try self.readSectionData(gpa, file, @intCast(target.n_sect)); |
| 690 | const target_sect_size = math.cast(usize, target_sect.size) orelse return error.Overflow; | ||
| 691 | const data = try gpa.alloc(u8, target_sect_size); | ||
| 692 | const amt = try file.preadAll(data, target_sect.offset + self.offset); | ||
| 693 | if (amt != data.len) return error.InputOutput; | ||
| 694 | gop.value_ptr.* = data; | ||
| 695 | } | 684 | } |
| 696 | const data = gop.value_ptr.*; | 685 | const data = gop.value_ptr.*; |
| 697 | const target_off = math.cast(usize, target.off) orelse return error.Overflow; | 686 | const target_off = math.cast(usize, target.off) orelse return error.Overflow; |
| ... | @@ -1000,7 +989,7 @@ fn initRelocs(self: *Object, file: File.Handle, cpu_arch: std.Target.Cpu.Arch, m | ... | @@ -1000,7 +989,7 @@ fn initRelocs(self: *Object, file: File.Handle, cpu_arch: std.Target.Cpu.Arch, m |
| 1000 | defer tracy.end(); | 989 | defer tracy.end(); |
| 1001 | const slice = self.sections.slice(); | 990 | const slice = self.sections.slice(); |
| 1002 | 991 | ||
| 1003 | for (slice.items(.header), slice.items(.relocs)) |sect, *out| { | 992 | for (slice.items(.header), slice.items(.relocs), 0..) |sect, *out, n_sect| { |
| 1004 | if (sect.nreloc == 0) continue; | 993 | if (sect.nreloc == 0) continue; |
| 1005 | // We skip relocs for __DWARF since even in -r mode, the linker is expected to emit | 994 | // We skip relocs for __DWARF since even in -r mode, the linker is expected to emit |
| 1006 | // debug symbol stabs in the relocatable. This made me curious why that is. For now, | 995 | // debug symbol stabs in the relocatable. This made me curious why that is. For now, |
| ... | @@ -1009,8 +998,8 @@ fn initRelocs(self: *Object, file: File.Handle, cpu_arch: std.Target.Cpu.Arch, m | ... | @@ -1009,8 +998,8 @@ fn initRelocs(self: *Object, file: File.Handle, cpu_arch: std.Target.Cpu.Arch, m |
| 1009 | !mem.eql(u8, sect.sectName(), "__compact_unwind")) continue; | 998 | !mem.eql(u8, sect.sectName(), "__compact_unwind")) continue; |
| 1010 | 999 | ||
| 1011 | switch (cpu_arch) { | 1000 | switch (cpu_arch) { |
| 1012 | .x86_64 => try x86_64.parseRelocs(self, sect, out, file, macho_file), | 1001 | .x86_64 => try x86_64.parseRelocs(self, @intCast(n_sect), sect, out, file, macho_file), |
| 1013 | .aarch64 => try aarch64.parseRelocs(self, sect, out, file, macho_file), | 1002 | .aarch64 => try aarch64.parseRelocs(self, @intCast(n_sect), sect, out, file, macho_file), |
| 1014 | else => unreachable, | 1003 | else => unreachable, |
| 1015 | } | 1004 | } |
| 1016 | 1005 | ||
| ... | @@ -1146,11 +1135,8 @@ fn initUnwindRecords(self: *Object, allocator: Allocator, sect_id: u8, file: Fil | ... | @@ -1146,11 +1135,8 @@ fn initUnwindRecords(self: *Object, allocator: Allocator, sect_id: u8, file: Fil |
| 1146 | }; | 1135 | }; |
| 1147 | 1136 | ||
| 1148 | const header = self.sections.items(.header)[sect_id]; | 1137 | const header = self.sections.items(.header)[sect_id]; |
| 1149 | const size = math.cast(usize, header.size) orelse return error.Overflow; | 1138 | const data = try self.readSectionData(allocator, file, sect_id); |
| 1150 | const data = try allocator.alloc(u8, size); | ||
| 1151 | defer allocator.free(data); | 1139 | defer allocator.free(data); |
| 1152 | const amt = try file.preadAll(data, header.offset + self.offset); | ||
| 1153 | if (amt != data.len) return error.InputOutput; | ||
| 1154 | 1140 | ||
| 1155 | const nrecs = @divExact(data.len, @sizeOf(macho.compact_unwind_entry)); | 1141 | const nrecs = @divExact(data.len, @sizeOf(macho.compact_unwind_entry)); |
| 1156 | const recs = @as([*]align(1) const macho.compact_unwind_entry, @ptrCast(data.ptr))[0..nrecs]; | 1142 | const recs = @as([*]align(1) const macho.compact_unwind_entry, @ptrCast(data.ptr))[0..nrecs]; |
| ... | @@ -1359,151 +1345,106 @@ fn parseDebugInfo(self: *Object, macho_file: *MachO) !void { | ... | @@ -1359,151 +1345,106 @@ fn parseDebugInfo(self: *Object, macho_file: *MachO) !void { |
| 1359 | defer tracy.end(); | 1345 | defer tracy.end(); |
| 1360 | 1346 | ||
| 1361 | const gpa = macho_file.base.comp.gpa; | 1347 | const gpa = macho_file.base.comp.gpa; |
| 1348 | const file = macho_file.getFileHandle(self.file_handle); | ||
| 1362 | 1349 | ||
| 1363 | var debug_info_index: ?usize = null; | 1350 | var dwarf: Dwarf = .{}; |
| 1364 | var debug_abbrev_index: ?usize = null; | 1351 | defer dwarf.deinit(gpa); |
| 1365 | var debug_str_index: ?usize = null; | ||
| 1366 | 1352 | ||
| 1367 | for (self.sections.items(.header), 0..) |sect, index| { | 1353 | for (self.sections.items(.header), 0..) |sect, index| { |
| 1354 | const n_sect: u8 = @intCast(index); | ||
| 1368 | if (sect.attrs() & macho.S_ATTR_DEBUG == 0) continue; | 1355 | if (sect.attrs() & macho.S_ATTR_DEBUG == 0) continue; |
| 1369 | if (mem.eql(u8, sect.sectName(), "__debug_info")) debug_info_index = index; | 1356 | if (mem.eql(u8, sect.sectName(), "__debug_info")) { |
| 1370 | if (mem.eql(u8, sect.sectName(), "__debug_abbrev")) debug_abbrev_index = index; | 1357 | dwarf.debug_info = try self.readSectionData(gpa, file, n_sect); |
| 1371 | if (mem.eql(u8, sect.sectName(), "__debug_str")) debug_str_index = index; | 1358 | } |
| 1359 | if (mem.eql(u8, sect.sectName(), "__debug_abbrev")) { | ||
| 1360 | dwarf.debug_abbrev = try self.readSectionData(gpa, file, n_sect); | ||
| 1361 | } | ||
| 1362 | if (mem.eql(u8, sect.sectName(), "__debug_str")) { | ||
| 1363 | dwarf.debug_str = try self.readSectionData(gpa, file, n_sect); | ||
| 1364 | } | ||
| 1365 | // __debug_str_offs[ets] section is a new addition in DWARFv5 and is generally | ||
| 1366 | // required in order to correctly parse strings. | ||
| 1367 | if (mem.eql(u8, sect.sectName(), "__debug_str_offs")) { | ||
| 1368 | dwarf.debug_str_offsets = try self.readSectionData(gpa, file, n_sect); | ||
| 1369 | } | ||
| 1372 | } | 1370 | } |
| 1373 | 1371 | ||
| 1374 | if (debug_info_index == null or debug_abbrev_index == null) return; | 1372 | if (dwarf.debug_info.len == 0) return; |
| 1375 | 1373 | ||
| 1376 | const slice = self.sections.slice(); | 1374 | // TODO return error once we fix emitting DWARF in self-hosted backend. |
| 1377 | const file = macho_file.getFileHandle(self.file_handle); | 1375 | // https://github.com/ziglang/zig/issues/21719 |
| 1378 | const debug_info = blk: { | 1376 | self.compile_unit = self.findCompileUnit(gpa, dwarf) catch null; |
| 1379 | const sect = slice.items(.header)[debug_info_index.?]; | 1377 | } |
| 1380 | const size = math.cast(usize, sect.size) orelse return error.Overflow; | 1378 | |
| 1381 | const data = try gpa.alloc(u8, size); | 1379 | fn findCompileUnit(self: *Object, gpa: Allocator, ctx: Dwarf) !CompileUnit { |
| 1382 | const amt = try file.preadAll(data, sect.offset + self.offset); | 1380 | var info_reader = Dwarf.InfoReader{ .ctx = ctx }; |
| 1383 | if (amt != data.len) return error.InputOutput; | 1381 | var abbrev_reader = Dwarf.AbbrevReader{ .ctx = ctx }; |
| 1384 | break :blk data; | ||
| 1385 | }; | ||
| 1386 | defer gpa.free(debug_info); | ||
| 1387 | const debug_abbrev = blk: { | ||
| 1388 | const sect = slice.items(.header)[debug_abbrev_index.?]; | ||
| 1389 | const size = math.cast(usize, sect.size) orelse return error.Overflow; | ||
| 1390 | const data = try gpa.alloc(u8, size); | ||
| 1391 | const amt = try file.preadAll(data, sect.offset + self.offset); | ||
| 1392 | if (amt != data.len) return error.InputOutput; | ||
| 1393 | break :blk data; | ||
| 1394 | }; | ||
| 1395 | defer gpa.free(debug_abbrev); | ||
| 1396 | const debug_str = if (debug_str_index) |sid| blk: { | ||
| 1397 | const sect = slice.items(.header)[sid]; | ||
| 1398 | const size = math.cast(usize, sect.size) orelse return error.Overflow; | ||
| 1399 | const data = try gpa.alloc(u8, size); | ||
| 1400 | const amt = try file.preadAll(data, sect.offset + self.offset); | ||
| 1401 | if (amt != data.len) return error.InputOutput; | ||
| 1402 | break :blk data; | ||
| 1403 | } else &[0]u8{}; | ||
| 1404 | defer gpa.free(debug_str); | ||
| 1405 | |||
| 1406 | self.compile_unit = self.findCompileUnit(.{ | ||
| 1407 | .gpa = gpa, | ||
| 1408 | .debug_info = debug_info, | ||
| 1409 | .debug_abbrev = debug_abbrev, | ||
| 1410 | .debug_str = debug_str, | ||
| 1411 | }) catch null; // TODO figure out what errors are fatal, and when we silently fail | ||
| 1412 | } | ||
| 1413 | |||
| 1414 | fn findCompileUnit(self: *Object, args: struct { | ||
| 1415 | gpa: Allocator, | ||
| 1416 | debug_info: []const u8, | ||
| 1417 | debug_abbrev: []const u8, | ||
| 1418 | debug_str: []const u8, | ||
| 1419 | }) !CompileUnit { | ||
| 1420 | var cu_wip: struct { | ||
| 1421 | comp_dir: ?[:0]const u8 = null, | ||
| 1422 | tu_name: ?[:0]const u8 = null, | ||
| 1423 | } = .{}; | ||
| 1424 | |||
| 1425 | const gpa = args.gpa; | ||
| 1426 | var info_reader = dwarf.InfoReader{ .bytes = args.debug_info, .strtab = args.debug_str }; | ||
| 1427 | var abbrev_reader = dwarf.AbbrevReader{ .bytes = args.debug_abbrev }; | ||
| 1428 | 1382 | ||
| 1429 | const cuh = try info_reader.readCompileUnitHeader(); | 1383 | const cuh = try info_reader.readCompileUnitHeader(); |
| 1430 | try abbrev_reader.seekTo(cuh.debug_abbrev_offset); | 1384 | try abbrev_reader.seekTo(cuh.debug_abbrev_offset); |
| 1431 | 1385 | ||
| 1432 | const cu_decl = (try abbrev_reader.readDecl()) orelse return error.Eof; | 1386 | const cu_decl = (try abbrev_reader.readDecl()) orelse return error.UnexpectedEndOfFile; |
| 1433 | if (cu_decl.tag != dwarf.TAG.compile_unit) return error.UnexpectedTag; | 1387 | if (cu_decl.tag != Dwarf.TAG.compile_unit) return error.UnexpectedTag; |
| 1434 | 1388 | ||
| 1435 | try info_reader.seekToDie(cu_decl.code, cuh, &abbrev_reader); | 1389 | try info_reader.seekToDie(cu_decl.code, cuh, &abbrev_reader); |
| 1436 | 1390 | ||
| 1437 | while (try abbrev_reader.readAttr()) |attr| switch (attr.at) { | 1391 | const Pos = struct { |
| 1438 | dwarf.AT.name => { | 1392 | pos: usize, |
| 1439 | cu_wip.tu_name = try info_reader.readString(attr.form, cuh); | 1393 | form: Dwarf.Form, |
| 1440 | }, | ||
| 1441 | dwarf.AT.comp_dir => { | ||
| 1442 | cu_wip.comp_dir = try info_reader.readString(attr.form, cuh); | ||
| 1443 | }, | ||
| 1444 | else => switch (attr.form) { | ||
| 1445 | dwarf.FORM.sec_offset, | ||
| 1446 | dwarf.FORM.ref_addr, | ||
| 1447 | => { | ||
| 1448 | _ = try info_reader.readOffset(cuh.format); | ||
| 1449 | }, | ||
| 1450 | |||
| 1451 | dwarf.FORM.addr => { | ||
| 1452 | _ = try info_reader.readNBytes(cuh.address_size); | ||
| 1453 | }, | ||
| 1454 | |||
| 1455 | dwarf.FORM.block1, | ||
| 1456 | dwarf.FORM.block2, | ||
| 1457 | dwarf.FORM.block4, | ||
| 1458 | dwarf.FORM.block, | ||
| 1459 | => { | ||
| 1460 | _ = try info_reader.readBlock(attr.form); | ||
| 1461 | }, | ||
| 1462 | |||
| 1463 | dwarf.FORM.exprloc => { | ||
| 1464 | _ = try info_reader.readExprLoc(); | ||
| 1465 | }, | ||
| 1466 | |||
| 1467 | dwarf.FORM.flag_present => {}, | ||
| 1468 | |||
| 1469 | dwarf.FORM.data1, | ||
| 1470 | dwarf.FORM.ref1, | ||
| 1471 | dwarf.FORM.flag, | ||
| 1472 | dwarf.FORM.data2, | ||
| 1473 | dwarf.FORM.ref2, | ||
| 1474 | dwarf.FORM.data4, | ||
| 1475 | dwarf.FORM.ref4, | ||
| 1476 | dwarf.FORM.data8, | ||
| 1477 | dwarf.FORM.ref8, | ||
| 1478 | dwarf.FORM.ref_sig8, | ||
| 1479 | dwarf.FORM.udata, | ||
| 1480 | dwarf.FORM.ref_udata, | ||
| 1481 | dwarf.FORM.sdata, | ||
| 1482 | => { | ||
| 1483 | _ = try info_reader.readConstant(attr.form); | ||
| 1484 | }, | ||
| 1485 | |||
| 1486 | dwarf.FORM.strp, | ||
| 1487 | dwarf.FORM.string, | ||
| 1488 | => { | ||
| 1489 | _ = try info_reader.readString(attr.form, cuh); | ||
| 1490 | }, | ||
| 1491 | |||
| 1492 | else => { | ||
| 1493 | // TODO actual errors? | ||
| 1494 | log.err("unhandled DW_FORM_* value with identifier {x}", .{attr.form}); | ||
| 1495 | return error.UnhandledForm; | ||
| 1496 | }, | ||
| 1497 | }, | ||
| 1498 | }; | 1394 | }; |
| 1499 | 1395 | var saved: struct { | |
| 1500 | if (cu_wip.comp_dir == null) return error.MissingCompDir; | 1396 | tu_name: ?Pos, |
| 1501 | if (cu_wip.tu_name == null) return error.MissingTuName; | 1397 | comp_dir: ?Pos, |
| 1502 | 1398 | str_offsets_base: ?Pos, | |
| 1503 | return .{ | 1399 | } = .{ |
| 1504 | .comp_dir = try self.addString(gpa, cu_wip.comp_dir.?), | 1400 | .tu_name = null, |
| 1505 | .tu_name = try self.addString(gpa, cu_wip.tu_name.?), | 1401 | .comp_dir = null, |
| 1402 | .str_offsets_base = null, | ||
| 1506 | }; | 1403 | }; |
| 1404 | while (try abbrev_reader.readAttr()) |attr| { | ||
| 1405 | const pos: Pos = .{ .pos = info_reader.pos, .form = attr.form }; | ||
| 1406 | switch (attr.at) { | ||
| 1407 | Dwarf.AT.name => saved.tu_name = pos, | ||
| 1408 | Dwarf.AT.comp_dir => saved.comp_dir = pos, | ||
| 1409 | Dwarf.AT.str_offsets_base => saved.str_offsets_base = pos, | ||
| 1410 | else => {}, | ||
| 1411 | } | ||
| 1412 | try info_reader.skip(attr.form, cuh); | ||
| 1413 | } | ||
| 1414 | |||
| 1415 | if (saved.comp_dir == null) return error.MissingCompileDir; | ||
| 1416 | if (saved.tu_name == null) return error.MissingTuName; | ||
| 1417 | |||
| 1418 | const str_offsets_base: ?u64 = if (saved.str_offsets_base) |str_offsets_base| str_offsets_base: { | ||
| 1419 | try info_reader.seekTo(str_offsets_base.pos); | ||
| 1420 | break :str_offsets_base try info_reader.readOffset(cuh.format); | ||
| 1421 | } else null; | ||
| 1422 | |||
| 1423 | var cu: CompileUnit = .{ .comp_dir = .{}, .tu_name = .{} }; | ||
| 1424 | for (&[_]struct { Pos, *MachO.String }{ | ||
| 1425 | .{ saved.comp_dir.?, &cu.comp_dir }, | ||
| 1426 | .{ saved.tu_name.?, &cu.tu_name }, | ||
| 1427 | }) |tuple| { | ||
| 1428 | const pos, const str_offset_ptr = tuple; | ||
| 1429 | try info_reader.seekTo(pos.pos); | ||
| 1430 | str_offset_ptr.* = switch (pos.form) { | ||
| 1431 | Dwarf.FORM.strp, | ||
| 1432 | Dwarf.FORM.string, | ||
| 1433 | => try self.addString(gpa, try info_reader.readString(pos.form, cuh)), | ||
| 1434 | Dwarf.FORM.strx, | ||
| 1435 | Dwarf.FORM.strx1, | ||
| 1436 | Dwarf.FORM.strx2, | ||
| 1437 | Dwarf.FORM.strx3, | ||
| 1438 | Dwarf.FORM.strx4, | ||
| 1439 | => blk: { | ||
| 1440 | const base = str_offsets_base orelse return error.MissingStrOffsetsBase; | ||
| 1441 | break :blk try self.addString(gpa, try info_reader.readStringIndexed(pos.form, cuh, base)); | ||
| 1442 | }, | ||
| 1443 | else => return error.InvalidForm, | ||
| 1444 | }; | ||
| 1445 | } | ||
| 1446 | |||
| 1447 | return cu; | ||
| 1507 | } | 1448 | } |
| 1508 | 1449 | ||
| 1509 | pub fn resolveSymbols(self: *Object, macho_file: *MachO) !void { | 1450 | pub fn resolveSymbols(self: *Object, macho_file: *MachO) !void { |
| ... | @@ -2561,6 +2502,17 @@ pub fn getUnwindRecord(self: *Object, index: UnwindInfo.Record.Index) *UnwindInf | ... | @@ -2561,6 +2502,17 @@ pub fn getUnwindRecord(self: *Object, index: UnwindInfo.Record.Index) *UnwindInf |
| 2561 | return &self.unwind_records.items[index]; | 2502 | return &self.unwind_records.items[index]; |
| 2562 | } | 2503 | } |
| 2563 | 2504 | ||
| 2505 | /// Caller owns the memory. | ||
| 2506 | pub fn readSectionData(self: Object, allocator: Allocator, file: File.Handle, n_sect: u8) ![]u8 { | ||
| 2507 | const header = self.sections.items(.header)[n_sect]; | ||
| 2508 | const size = math.cast(usize, header.size) orelse return error.Overflow; | ||
| 2509 | const data = try allocator.alloc(u8, size); | ||
| 2510 | const amt = try file.preadAll(data, header.offset + self.offset); | ||
| 2511 | errdefer allocator.free(data); | ||
| 2512 | if (amt != data.len) return error.InputOutput; | ||
| 2513 | return data; | ||
| 2514 | } | ||
| 2515 | |||
| 2564 | pub fn format( | 2516 | pub fn format( |
| 2565 | self: *Object, | 2517 | self: *Object, |
| 2566 | comptime unused_fmt_string: []const u8, | 2518 | comptime unused_fmt_string: []const u8, |
| ... | @@ -2848,6 +2800,7 @@ const CompactUnwindCtx = struct { | ... | @@ -2848,6 +2800,7 @@ const CompactUnwindCtx = struct { |
| 2848 | const x86_64 = struct { | 2800 | const x86_64 = struct { |
| 2849 | fn parseRelocs( | 2801 | fn parseRelocs( |
| 2850 | self: *Object, | 2802 | self: *Object, |
| 2803 | n_sect: u8, | ||
| 2851 | sect: macho.section_64, | 2804 | sect: macho.section_64, |
| 2852 | out: *std.ArrayListUnmanaged(Relocation), | 2805 | out: *std.ArrayListUnmanaged(Relocation), |
| 2853 | handle: File.Handle, | 2806 | handle: File.Handle, |
| ... | @@ -2857,19 +2810,12 @@ const x86_64 = struct { | ... | @@ -2857,19 +2810,12 @@ const x86_64 = struct { |
| 2857 | 2810 | ||
| 2858 | const relocs_buffer = try gpa.alloc(u8, sect.nreloc * @sizeOf(macho.relocation_info)); | 2811 | const relocs_buffer = try gpa.alloc(u8, sect.nreloc * @sizeOf(macho.relocation_info)); |
| 2859 | defer gpa.free(relocs_buffer); | 2812 | defer gpa.free(relocs_buffer); |
| 2860 | { | 2813 | const amt = try handle.preadAll(relocs_buffer, sect.reloff + self.offset); |
| 2861 | const amt = try handle.preadAll(relocs_buffer, sect.reloff + self.offset); | 2814 | if (amt != relocs_buffer.len) return error.InputOutput; |
| 2862 | if (amt != relocs_buffer.len) return error.InputOutput; | ||
| 2863 | } | ||
| 2864 | const relocs = @as([*]align(1) const macho.relocation_info, @ptrCast(relocs_buffer.ptr))[0..sect.nreloc]; | 2815 | const relocs = @as([*]align(1) const macho.relocation_info, @ptrCast(relocs_buffer.ptr))[0..sect.nreloc]; |
| 2865 | 2816 | ||
| 2866 | const sect_size = math.cast(usize, sect.size) orelse return error.Overflow; | 2817 | const code = try self.readSectionData(gpa, handle, n_sect); |
| 2867 | const code = try gpa.alloc(u8, sect_size); | ||
| 2868 | defer gpa.free(code); | 2818 | defer gpa.free(code); |
| 2869 | { | ||
| 2870 | const amt = try handle.preadAll(code, sect.offset + self.offset); | ||
| 2871 | if (amt != code.len) return error.InputOutput; | ||
| 2872 | } | ||
| 2873 | 2819 | ||
| 2874 | try out.ensureTotalCapacityPrecise(gpa, relocs.len); | 2820 | try out.ensureTotalCapacityPrecise(gpa, relocs.len); |
| 2875 | 2821 | ||
| ... | @@ -3021,6 +2967,7 @@ const x86_64 = struct { | ... | @@ -3021,6 +2967,7 @@ const x86_64 = struct { |
| 3021 | const aarch64 = struct { | 2967 | const aarch64 = struct { |
| 3022 | fn parseRelocs( | 2968 | fn parseRelocs( |
| 3023 | self: *Object, | 2969 | self: *Object, |
| 2970 | n_sect: u8, | ||
| 3024 | sect: macho.section_64, | 2971 | sect: macho.section_64, |
| 3025 | out: *std.ArrayListUnmanaged(Relocation), | 2972 | out: *std.ArrayListUnmanaged(Relocation), |
| 3026 | handle: File.Handle, | 2973 | handle: File.Handle, |
| ... | @@ -3030,19 +2977,12 @@ const aarch64 = struct { | ... | @@ -3030,19 +2977,12 @@ const aarch64 = struct { |
| 3030 | 2977 | ||
| 3031 | const relocs_buffer = try gpa.alloc(u8, sect.nreloc * @sizeOf(macho.relocation_info)); | 2978 | const relocs_buffer = try gpa.alloc(u8, sect.nreloc * @sizeOf(macho.relocation_info)); |
| 3032 | defer gpa.free(relocs_buffer); | 2979 | defer gpa.free(relocs_buffer); |
| 3033 | { | 2980 | const amt = try handle.preadAll(relocs_buffer, sect.reloff + self.offset); |
| 3034 | const amt = try handle.preadAll(relocs_buffer, sect.reloff + self.offset); | 2981 | if (amt != relocs_buffer.len) return error.InputOutput; |
| 3035 | if (amt != relocs_buffer.len) return error.InputOutput; | ||
| 3036 | } | ||
| 3037 | const relocs = @as([*]align(1) const macho.relocation_info, @ptrCast(relocs_buffer.ptr))[0..sect.nreloc]; | 2982 | const relocs = @as([*]align(1) const macho.relocation_info, @ptrCast(relocs_buffer.ptr))[0..sect.nreloc]; |
| 3038 | 2983 | ||
| 3039 | const sect_size = math.cast(usize, sect.size) orelse return error.Overflow; | 2984 | const code = try self.readSectionData(gpa, handle, n_sect); |
| 3040 | const code = try gpa.alloc(u8, sect_size); | ||
| 3041 | defer gpa.free(code); | 2985 | defer gpa.free(code); |
| 3042 | { | ||
| 3043 | const amt = try handle.preadAll(code, sect.offset + self.offset); | ||
| 3044 | if (amt != code.len) return error.InputOutput; | ||
| 3045 | } | ||
| 3046 | 2986 | ||
| 3047 | try out.ensureTotalCapacityPrecise(gpa, relocs.len); | 2987 | try out.ensureTotalCapacityPrecise(gpa, relocs.len); |
| 3048 | 2988 | ||
| ... | @@ -3219,7 +3159,6 @@ const aarch64 = struct { | ... | @@ -3219,7 +3159,6 @@ const aarch64 = struct { |
| 3219 | }; | 3159 | }; |
| 3220 | 3160 | ||
| 3221 | const assert = std.debug.assert; | 3161 | const assert = std.debug.assert; |
| 3222 | const dwarf = @import("dwarf.zig"); | ||
| 3223 | const eh_frame = @import("eh_frame.zig"); | 3162 | const eh_frame = @import("eh_frame.zig"); |
| 3224 | const log = std.log.scoped(.link); | 3163 | const log = std.log.scoped(.link); |
| 3225 | const macho = std.macho; | 3164 | const macho = std.macho; |
| ... | @@ -3233,6 +3172,7 @@ const Allocator = mem.Allocator; | ... | @@ -3233,6 +3172,7 @@ const Allocator = mem.Allocator; |
| 3233 | const Archive = @import("Archive.zig"); | 3172 | const Archive = @import("Archive.zig"); |
| 3234 | const Atom = @import("Atom.zig"); | 3173 | const Atom = @import("Atom.zig"); |
| 3235 | const Cie = eh_frame.Cie; | 3174 | const Cie = eh_frame.Cie; |
| 3175 | const Dwarf = @import("Dwarf.zig"); | ||
| 3236 | const Fde = eh_frame.Fde; | 3176 | const Fde = eh_frame.Fde; |
| 3237 | const File = @import("file.zig").File; | 3177 | const File = @import("file.zig").File; |
| 3238 | const LoadCommandIterator = macho.LoadCommandIterator; | 3178 | const LoadCommandIterator = macho.LoadCommandIterator; |
src/link/MachO/dwarf.zig deleted-286| ... | @@ -1,286 +0,0 @@ | ||
| 1 | pub const InfoReader = struct { | ||
| 2 | bytes: []const u8, | ||
| 3 | strtab: []const u8, | ||
| 4 | pos: usize = 0, | ||
| 5 | |||
| 6 | pub fn readCompileUnitHeader(p: *InfoReader) !CompileUnitHeader { | ||
| 7 | var length: u64 = try p.readInt(u32); | ||
| 8 | const is_64bit = length == 0xffffffff; | ||
| 9 | if (is_64bit) { | ||
| 10 | length = try p.readInt(u64); | ||
| 11 | } | ||
| 12 | const dw_fmt: DwarfFormat = if (is_64bit) .dwarf64 else .dwarf32; | ||
| 13 | return .{ | ||
| 14 | .format = dw_fmt, | ||
| 15 | .length = length, | ||
| 16 | .version = try p.readInt(u16), | ||
| 17 | .debug_abbrev_offset = try p.readOffset(dw_fmt), | ||
| 18 | .address_size = try p.readByte(), | ||
| 19 | }; | ||
| 20 | } | ||
| 21 | |||
| 22 | pub fn seekToDie(p: *InfoReader, code: Code, cuh: CompileUnitHeader, abbrev_reader: *AbbrevReader) !void { | ||
| 23 | const cuh_length = math.cast(usize, cuh.length) orelse return error.Overflow; | ||
| 24 | const end_pos = p.pos + switch (cuh.format) { | ||
| 25 | .dwarf32 => @as(usize, 4), | ||
| 26 | .dwarf64 => 12, | ||
| 27 | } + cuh_length; | ||
| 28 | while (p.pos < end_pos) { | ||
| 29 | const di_code = try p.readUleb128(u64); | ||
| 30 | if (di_code == 0) return error.Eof; | ||
| 31 | if (di_code == code) return; | ||
| 32 | |||
| 33 | while (try abbrev_reader.readAttr()) |attr| switch (attr.at) { | ||
| 34 | dwarf.FORM.sec_offset, | ||
| 35 | dwarf.FORM.ref_addr, | ||
| 36 | => { | ||
| 37 | _ = try p.readOffset(cuh.format); | ||
| 38 | }, | ||
| 39 | |||
| 40 | dwarf.FORM.addr => { | ||
| 41 | _ = try p.readNBytes(cuh.address_size); | ||
| 42 | }, | ||
| 43 | |||
| 44 | dwarf.FORM.block1, | ||
| 45 | dwarf.FORM.block2, | ||
| 46 | dwarf.FORM.block4, | ||
| 47 | dwarf.FORM.block, | ||
| 48 | => { | ||
| 49 | _ = try p.readBlock(attr.form); | ||
| 50 | }, | ||
| 51 | |||
| 52 | dwarf.FORM.exprloc => { | ||
| 53 | _ = try p.readExprLoc(); | ||
| 54 | }, | ||
| 55 | |||
| 56 | dwarf.FORM.flag_present => {}, | ||
| 57 | |||
| 58 | dwarf.FORM.data1, | ||
| 59 | dwarf.FORM.ref1, | ||
| 60 | dwarf.FORM.flag, | ||
| 61 | dwarf.FORM.data2, | ||
| 62 | dwarf.FORM.ref2, | ||
| 63 | dwarf.FORM.data4, | ||
| 64 | dwarf.FORM.ref4, | ||
| 65 | dwarf.FORM.data8, | ||
| 66 | dwarf.FORM.ref8, | ||
| 67 | dwarf.FORM.ref_sig8, | ||
| 68 | dwarf.FORM.udata, | ||
| 69 | dwarf.FORM.ref_udata, | ||
| 70 | dwarf.FORM.sdata, | ||
| 71 | => { | ||
| 72 | _ = try p.readConstant(attr.form); | ||
| 73 | }, | ||
| 74 | |||
| 75 | dwarf.FORM.strp, | ||
| 76 | dwarf.FORM.string, | ||
| 77 | => { | ||
| 78 | _ = try p.readString(attr.form, cuh); | ||
| 79 | }, | ||
| 80 | |||
| 81 | else => { | ||
| 82 | // TODO better errors | ||
| 83 | log.err("unhandled DW_FORM_* value with identifier {x}", .{attr.form}); | ||
| 84 | return error.UnhandledDwFormValue; | ||
| 85 | }, | ||
| 86 | }; | ||
| 87 | } | ||
| 88 | } | ||
| 89 | |||
| 90 | pub fn readBlock(p: *InfoReader, form: Form) ![]const u8 { | ||
| 91 | const len: u64 = switch (form) { | ||
| 92 | dwarf.FORM.block1 => try p.readByte(), | ||
| 93 | dwarf.FORM.block2 => try p.readInt(u16), | ||
| 94 | dwarf.FORM.block4 => try p.readInt(u32), | ||
| 95 | dwarf.FORM.block => try p.readUleb128(u64), | ||
| 96 | else => unreachable, | ||
| 97 | }; | ||
| 98 | return p.readNBytes(len); | ||
| 99 | } | ||
| 100 | |||
| 101 | pub fn readExprLoc(p: *InfoReader) ![]const u8 { | ||
| 102 | const len: u64 = try p.readUleb128(u64); | ||
| 103 | return p.readNBytes(len); | ||
| 104 | } | ||
| 105 | |||
| 106 | pub fn readConstant(p: *InfoReader, form: Form) !u64 { | ||
| 107 | return switch (form) { | ||
| 108 | dwarf.FORM.data1, dwarf.FORM.ref1, dwarf.FORM.flag => try p.readByte(), | ||
| 109 | dwarf.FORM.data2, dwarf.FORM.ref2 => try p.readInt(u16), | ||
| 110 | dwarf.FORM.data4, dwarf.FORM.ref4 => try p.readInt(u32), | ||
| 111 | dwarf.FORM.data8, dwarf.FORM.ref8, dwarf.FORM.ref_sig8 => try p.readInt(u64), | ||
| 112 | dwarf.FORM.udata, dwarf.FORM.ref_udata => try p.readUleb128(u64), | ||
| 113 | dwarf.FORM.sdata => @bitCast(try p.readIleb128(i64)), | ||
| 114 | else => return error.UnhandledConstantForm, | ||
| 115 | }; | ||
| 116 | } | ||
| 117 | |||
| 118 | pub fn readString(p: *InfoReader, form: Form, cuh: CompileUnitHeader) ![:0]const u8 { | ||
| 119 | switch (form) { | ||
| 120 | dwarf.FORM.strp => { | ||
| 121 | const off = try p.readOffset(cuh.format); | ||
| 122 | const off_u = math.cast(usize, off) orelse return error.Overflow; | ||
| 123 | return mem.sliceTo(@as([*:0]const u8, @ptrCast(p.strtab.ptr + off_u)), 0); | ||
| 124 | }, | ||
| 125 | dwarf.FORM.string => { | ||
| 126 | const start = p.pos; | ||
| 127 | while (p.pos < p.bytes.len) : (p.pos += 1) { | ||
| 128 | if (p.bytes[p.pos] == 0) break; | ||
| 129 | } | ||
| 130 | if (p.bytes[p.pos] != 0) return error.Eof; | ||
| 131 | return p.bytes[start..p.pos :0]; | ||
| 132 | }, | ||
| 133 | else => unreachable, | ||
| 134 | } | ||
| 135 | } | ||
| 136 | |||
| 137 | pub fn readByte(p: *InfoReader) !u8 { | ||
| 138 | if (p.pos + 1 > p.bytes.len) return error.Eof; | ||
| 139 | defer p.pos += 1; | ||
| 140 | return p.bytes[p.pos]; | ||
| 141 | } | ||
| 142 | |||
| 143 | pub fn readNBytes(p: *InfoReader, num: u64) ![]const u8 { | ||
| 144 | const num_usize = math.cast(usize, num) orelse return error.Overflow; | ||
| 145 | if (p.pos + num_usize > p.bytes.len) return error.Eof; | ||
| 146 | defer p.pos += num_usize; | ||
| 147 | return p.bytes[p.pos..][0..num_usize]; | ||
| 148 | } | ||
| 149 | |||
| 150 | pub fn readInt(p: *InfoReader, comptime Int: type) !Int { | ||
| 151 | if (p.pos + @sizeOf(Int) > p.bytes.len) return error.Eof; | ||
| 152 | defer p.pos += @sizeOf(Int); | ||
| 153 | return mem.readInt(Int, p.bytes[p.pos..][0..@sizeOf(Int)], .little); | ||
| 154 | } | ||
| 155 | |||
| 156 | pub fn readOffset(p: *InfoReader, dw_fmt: DwarfFormat) !u64 { | ||
| 157 | return switch (dw_fmt) { | ||
| 158 | .dwarf32 => try p.readInt(u32), | ||
| 159 | .dwarf64 => try p.readInt(u64), | ||
| 160 | }; | ||
| 161 | } | ||
| 162 | |||
| 163 | pub fn readUleb128(p: *InfoReader, comptime Type: type) !Type { | ||
| 164 | var stream = std.io.fixedBufferStream(p.bytes[p.pos..]); | ||
| 165 | var creader = std.io.countingReader(stream.reader()); | ||
| 166 | const value: Type = try leb.readUleb128(Type, creader.reader()); | ||
| 167 | p.pos += math.cast(usize, creader.bytes_read) orelse return error.Overflow; | ||
| 168 | return value; | ||
| 169 | } | ||
| 170 | |||
| 171 | pub fn readIleb128(p: *InfoReader, comptime Type: type) !Type { | ||
| 172 | var stream = std.io.fixedBufferStream(p.bytes[p.pos..]); | ||
| 173 | var creader = std.io.countingReader(stream.reader()); | ||
| 174 | const value: Type = try leb.readIleb128(Type, creader.reader()); | ||
| 175 | p.pos += math.cast(usize, creader.bytes_read) orelse return error.Overflow; | ||
| 176 | return value; | ||
| 177 | } | ||
| 178 | |||
| 179 | pub fn seekTo(p: *InfoReader, off: u64) !void { | ||
| 180 | p.pos = math.cast(usize, off) orelse return error.Overflow; | ||
| 181 | } | ||
| 182 | }; | ||
| 183 | |||
| 184 | pub const AbbrevReader = struct { | ||
| 185 | bytes: []const u8, | ||
| 186 | pos: usize = 0, | ||
| 187 | |||
| 188 | pub fn hasMore(p: AbbrevReader) bool { | ||
| 189 | return p.pos < p.bytes.len; | ||
| 190 | } | ||
| 191 | |||
| 192 | pub fn readDecl(p: *AbbrevReader) !?AbbrevDecl { | ||
| 193 | const pos = p.pos; | ||
| 194 | const code = try p.readUleb128(Code); | ||
| 195 | if (code == 0) return null; | ||
| 196 | |||
| 197 | const tag = try p.readUleb128(Tag); | ||
| 198 | const has_children = (try p.readByte()) > 0; | ||
| 199 | return .{ | ||
| 200 | .code = code, | ||
| 201 | .pos = pos, | ||
| 202 | .len = p.pos - pos, | ||
| 203 | .tag = tag, | ||
| 204 | .has_children = has_children, | ||
| 205 | }; | ||
| 206 | } | ||
| 207 | |||
| 208 | pub fn readAttr(p: *AbbrevReader) !?AbbrevAttr { | ||
| 209 | const pos = p.pos; | ||
| 210 | const at = try p.readUleb128(At); | ||
| 211 | const form = try p.readUleb128(Form); | ||
| 212 | return if (at == 0 and form == 0) null else .{ | ||
| 213 | .at = at, | ||
| 214 | .form = form, | ||
| 215 | .pos = pos, | ||
| 216 | .len = p.pos - pos, | ||
| 217 | }; | ||
| 218 | } | ||
| 219 | |||
| 220 | pub fn readByte(p: *AbbrevReader) !u8 { | ||
| 221 | if (p.pos + 1 > p.bytes.len) return error.Eof; | ||
| 222 | defer p.pos += 1; | ||
| 223 | return p.bytes[p.pos]; | ||
| 224 | } | ||
| 225 | |||
| 226 | pub fn readUleb128(p: *AbbrevReader, comptime Type: type) !Type { | ||
| 227 | var stream = std.io.fixedBufferStream(p.bytes[p.pos..]); | ||
| 228 | var creader = std.io.countingReader(stream.reader()); | ||
| 229 | const value: Type = try leb.readUleb128(Type, creader.reader()); | ||
| 230 | p.pos += math.cast(usize, creader.bytes_read) orelse return error.Overflow; | ||
| 231 | return value; | ||
| 232 | } | ||
| 233 | |||
| 234 | pub fn seekTo(p: *AbbrevReader, off: u64) !void { | ||
| 235 | p.pos = math.cast(usize, off) orelse return error.Overflow; | ||
| 236 | } | ||
| 237 | }; | ||
| 238 | |||
| 239 | const AbbrevDecl = struct { | ||
| 240 | code: Code, | ||
| 241 | pos: usize, | ||
| 242 | len: usize, | ||
| 243 | tag: Tag, | ||
| 244 | has_children: bool, | ||
| 245 | }; | ||
| 246 | |||
| 247 | const AbbrevAttr = struct { | ||
| 248 | at: At, | ||
| 249 | form: Form, | ||
| 250 | pos: usize, | ||
| 251 | len: usize, | ||
| 252 | }; | ||
| 253 | |||
| 254 | const CompileUnitHeader = struct { | ||
| 255 | format: DwarfFormat, | ||
| 256 | length: u64, | ||
| 257 | version: u16, | ||
| 258 | debug_abbrev_offset: u64, | ||
| 259 | address_size: u8, | ||
| 260 | }; | ||
| 261 | |||
| 262 | const Die = struct { | ||
| 263 | pos: usize, | ||
| 264 | len: usize, | ||
| 265 | }; | ||
| 266 | |||
| 267 | const DwarfFormat = enum { | ||
| 268 | dwarf32, | ||
| 269 | dwarf64, | ||
| 270 | }; | ||
| 271 | |||
| 272 | const dwarf = std.dwarf; | ||
| 273 | const leb = std.leb; | ||
| 274 | const log = std.log.scoped(.link); | ||
| 275 | const math = std.math; | ||
| 276 | const mem = std.mem; | ||
| 277 | const std = @import("std"); | ||
| 278 | |||
| 279 | const At = u64; | ||
| 280 | const Code = u64; | ||
| 281 | const Form = u64; | ||
| 282 | const Tag = u64; | ||
| 283 | |||
| 284 | pub const AT = dwarf.AT; | ||
| 285 | pub const FORM = dwarf.FORM; | ||
| 286 | pub const TAG = dwarf.TAG; | ||