blob ccfdfd0d (131691B) - Raw
1 allocator: Allocator, 2 bin_file: *File, 3 format: Format, 4 ptr_width: PtrWidth, 5 6 /// A list of `Atom`s whose Line Number Programs have surplus capacity. 7 /// This is the same concept as `Section.free_list` in Elf; see those doc comments. 8 src_fn_free_list: std.AutoHashMapUnmanaged(Atom.Index, void) = .{}, 9 src_fn_first_index: ?Atom.Index = null, 10 src_fn_last_index: ?Atom.Index = null, 11 src_fns: std.ArrayListUnmanaged(Atom) = .{}, 12 src_fn_decls: AtomTable = .{}, 13 14 /// A list of `Atom`s whose corresponding .debug_info tags have surplus capacity. 15 /// This is the same concept as `text_block_free_list`; see those doc comments. 16 di_atom_free_list: std.AutoHashMapUnmanaged(Atom.Index, void) = .{}, 17 di_atom_first_index: ?Atom.Index = null, 18 di_atom_last_index: ?Atom.Index = null, 19 di_atoms: std.ArrayListUnmanaged(Atom) = .{}, 20 di_atom_decls: AtomTable = .{}, 21 22 dbg_line_header: DbgLineHeader, 23 24 abbrev_table_offset: ?u64 = null, 25 26 /// TODO replace with InternPool 27 /// Table of debug symbol names. 28 strtab: StringTable = .{}, 29 30 /// Quick lookup array of all defined source files referenced by at least one Decl. 31 /// They will end up in the DWARF debug_line header as two lists: 32 /// * []include_directory 33 /// * []file_names 34 di_files: std.AutoArrayHashMapUnmanaged(*const Module.File, void) = .{}, 35 36 global_abbrev_relocs: std.ArrayListUnmanaged(AbbrevRelocation) = .{}, 37 38 const AtomTable = std.AutoHashMapUnmanaged(InternPool.DeclIndex, Atom.Index); 39 40 const Atom = struct { 41 /// Offset into .debug_info pointing to the tag for this Decl, or 42 /// offset from the beginning of the Debug Line Program header that contains this function. 43 off: u32, 44 /// Size of the .debug_info tag for this Decl, not including padding, or 45 /// size of the line number program component belonging to this function, not 46 /// including padding. 47 len: u32, 48 49 prev_index: ?Index, 50 next_index: ?Index, 51 52 pub const Index = u32; 53 }; 54 55 const DbgLineHeader = struct { 56 minimum_instruction_length: u8, 57 maximum_operations_per_instruction: u8, 58 default_is_stmt: bool, 59 line_base: i8, 60 line_range: u8, 61 opcode_base: u8, 62 }; 63 64 /// Represents state of the analysed Decl. 65 /// Includes Decl's abbrev table of type Types, matching arena 66 /// and a set of relocations that will be resolved once this 67 /// Decl's inner Atom is assigned an offset within the DWARF section. 68 pub const DeclState = struct { 69 dwarf: *Dwarf, 70 mod: *Module, 71 di_atom_decls: *const AtomTable, 72 dbg_line_func: InternPool.Index, 73 dbg_line: std.ArrayList(u8), 74 dbg_info: std.ArrayList(u8), 75 abbrev_type_arena: std.heap.ArenaAllocator, 76 abbrev_table: std.ArrayListUnmanaged(AbbrevEntry), 77 abbrev_resolver: std.AutoHashMapUnmanaged(InternPool.Index, u32), 78 abbrev_relocs: std.ArrayListUnmanaged(AbbrevRelocation), 79 exprloc_relocs: std.ArrayListUnmanaged(ExprlocRelocation), 80 81 pub fn deinit(self: *DeclState) void { 82 const gpa = self.dwarf.allocator; 83 self.dbg_line.deinit(); 84 self.dbg_info.deinit(); 85 self.abbrev_type_arena.deinit(); 86 self.abbrev_table.deinit(gpa); 87 self.abbrev_resolver.deinit(gpa); 88 self.abbrev_relocs.deinit(gpa); 89 self.exprloc_relocs.deinit(gpa); 90 } 91 92 /// Adds local type relocation of the form: @offset => @this + addend 93 /// @this signifies the offset within the .debug_abbrev section of the containing atom. 94 fn addTypeRelocLocal(self: *DeclState, atom_index: Atom.Index, offset: u32, addend: u32) !void { 95 log.debug("{x}: @this + {x}", .{ offset, addend }); 96 try self.abbrev_relocs.append(self.dwarf.allocator, .{ 97 .target = null, 98 .atom_index = atom_index, 99 .offset = offset, 100 .addend = addend, 101 }); 102 } 103 104 /// Adds global type relocation of the form: @offset => @symbol + 0 105 /// @symbol signifies a type abbreviation posititioned somewhere in the .debug_abbrev section 106 /// which we use as our target of the relocation. 107 fn addTypeRelocGlobal(self: *DeclState, atom_index: Atom.Index, ty: Type, offset: u32) !void { 108 const gpa = self.dwarf.allocator; 109 const resolv = self.abbrev_resolver.get(ty.toIntern()) orelse blk: { 110 const sym_index: u32 = @intCast(self.abbrev_table.items.len); 111 try self.abbrev_table.append(gpa, .{ 112 .atom_index = atom_index, 113 .type = ty, 114 .offset = undefined, 115 }); 116 log.debug("%{d}: {}", .{ sym_index, ty.fmt(self.mod) }); 117 try self.abbrev_resolver.putNoClobber(gpa, ty.toIntern(), sym_index); 118 break :blk sym_index; 119 }; 120 log.debug("{x}: %{d} + 0", .{ offset, resolv }); 121 try self.abbrev_relocs.append(gpa, .{ 122 .target = resolv, 123 .atom_index = atom_index, 124 .offset = offset, 125 .addend = 0, 126 }); 127 } 128 129 fn addDbgInfoType( 130 self: *DeclState, 131 mod: *Module, 132 atom_index: Atom.Index, 133 ty: Type, 134 ) error{OutOfMemory}!void { 135 const dbg_info_buffer = &self.dbg_info; 136 const target = mod.getTarget(); 137 const target_endian = target.cpu.arch.endian(); 138 const ip = &mod.intern_pool; 139 140 switch (ty.zigTypeTag(mod)) { 141 .NoReturn => unreachable, 142 .Void => { 143 try dbg_info_buffer.append(@intFromEnum(AbbrevCode.zero_bit_type)); 144 }, 145 .Bool => { 146 try dbg_info_buffer.ensureUnusedCapacity(12); 147 dbg_info_buffer.appendAssumeCapacity(@intFromEnum(AbbrevCode.base_type)); 148 // DW.AT.encoding, DW.FORM.data1 149 dbg_info_buffer.appendAssumeCapacity(DW.ATE.boolean); 150 // DW.AT.byte_size, DW.FORM.udata 151 try leb128.writeUleb128(dbg_info_buffer.writer(), ty.abiSize(mod)); 152 // DW.AT.name, DW.FORM.string 153 try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(mod)}); 154 }, 155 .Int => { 156 const info = ty.intInfo(mod); 157 try dbg_info_buffer.ensureUnusedCapacity(12); 158 dbg_info_buffer.appendAssumeCapacity(@intFromEnum(AbbrevCode.base_type)); 159 // DW.AT.encoding, DW.FORM.data1 160 dbg_info_buffer.appendAssumeCapacity(switch (info.signedness) { 161 .signed => DW.ATE.signed, 162 .unsigned => DW.ATE.unsigned, 163 }); 164 // DW.AT.byte_size, DW.FORM.udata 165 try leb128.writeUleb128(dbg_info_buffer.writer(), ty.abiSize(mod)); 166 // DW.AT.name, DW.FORM.string 167 try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(mod)}); 168 }, 169 .Optional => { 170 if (ty.isPtrLikeOptional(mod)) { 171 try dbg_info_buffer.ensureUnusedCapacity(12); 172 dbg_info_buffer.appendAssumeCapacity(@intFromEnum(AbbrevCode.base_type)); 173 // DW.AT.encoding, DW.FORM.data1 174 dbg_info_buffer.appendAssumeCapacity(DW.ATE.address); 175 // DW.AT.byte_size, DW.FORM.udata 176 try leb128.writeUleb128(dbg_info_buffer.writer(), ty.abiSize(mod)); 177 // DW.AT.name, DW.FORM.string 178 try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(mod)}); 179 } else { 180 // Non-pointer optionals are structs: struct { .maybe = *, .val = * } 181 const payload_ty = ty.optionalChild(mod); 182 // DW.AT.structure_type 183 try dbg_info_buffer.append(@intFromEnum(AbbrevCode.struct_type)); 184 // DW.AT.byte_size, DW.FORM.udata 185 const abi_size = ty.abiSize(mod); 186 try leb128.writeUleb128(dbg_info_buffer.writer(), abi_size); 187 // DW.AT.name, DW.FORM.string 188 try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(mod)}); 189 // DW.AT.member 190 try dbg_info_buffer.ensureUnusedCapacity(21); 191 dbg_info_buffer.appendAssumeCapacity(@intFromEnum(AbbrevCode.struct_member)); 192 // DW.AT.name, DW.FORM.string 193 dbg_info_buffer.appendSliceAssumeCapacity("maybe"); 194 dbg_info_buffer.appendAssumeCapacity(0); 195 // DW.AT.type, DW.FORM.ref4 196 var index = dbg_info_buffer.items.len; 197 dbg_info_buffer.appendNTimesAssumeCapacity(0, 4); 198 try self.addTypeRelocGlobal(atom_index, Type.bool, @intCast(index)); 199 // DW.AT.data_member_location, DW.FORM.udata 200 dbg_info_buffer.appendAssumeCapacity(0); 201 // DW.AT.member 202 dbg_info_buffer.appendAssumeCapacity(@intFromEnum(AbbrevCode.struct_member)); 203 // DW.AT.name, DW.FORM.string 204 dbg_info_buffer.appendSliceAssumeCapacity("val"); 205 dbg_info_buffer.appendAssumeCapacity(0); 206 // DW.AT.type, DW.FORM.ref4 207 index = dbg_info_buffer.items.len; 208 dbg_info_buffer.appendNTimesAssumeCapacity(0, 4); 209 try self.addTypeRelocGlobal(atom_index, payload_ty, @intCast(index)); 210 // DW.AT.data_member_location, DW.FORM.udata 211 const offset = abi_size - payload_ty.abiSize(mod); 212 try leb128.writeUleb128(dbg_info_buffer.writer(), offset); 213 // DW.AT.structure_type delimit children 214 try dbg_info_buffer.append(0); 215 } 216 }, 217 .Pointer => { 218 if (ty.isSlice(mod)) { 219 // Slices are structs: struct { .ptr = *, .len = N } 220 const ptr_bits = target.ptrBitWidth(); 221 const ptr_bytes: u8 = @intCast(@divExact(ptr_bits, 8)); 222 // DW.AT.structure_type 223 try dbg_info_buffer.ensureUnusedCapacity(2); 224 dbg_info_buffer.appendAssumeCapacity(@intFromEnum(AbbrevCode.struct_type)); 225 // DW.AT.byte_size, DW.FORM.udata 226 try leb128.writeUleb128(dbg_info_buffer.writer(), ty.abiSize(mod)); 227 // DW.AT.name, DW.FORM.string 228 try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(mod)}); 229 // DW.AT.member 230 try dbg_info_buffer.ensureUnusedCapacity(21); 231 dbg_info_buffer.appendAssumeCapacity(@intFromEnum(AbbrevCode.struct_member)); 232 // DW.AT.name, DW.FORM.string 233 dbg_info_buffer.appendSliceAssumeCapacity("ptr"); 234 dbg_info_buffer.appendAssumeCapacity(0); 235 // DW.AT.type, DW.FORM.ref4 236 var index = dbg_info_buffer.items.len; 237 dbg_info_buffer.appendNTimesAssumeCapacity(0, 4); 238 const ptr_ty = ty.slicePtrFieldType(mod); 239 try self.addTypeRelocGlobal(atom_index, ptr_ty, @intCast(index)); 240 // DW.AT.data_member_location, DW.FORM.udata 241 dbg_info_buffer.appendAssumeCapacity(0); 242 // DW.AT.member 243 dbg_info_buffer.appendAssumeCapacity(@intFromEnum(AbbrevCode.struct_member)); 244 // DW.AT.name, DW.FORM.string 245 dbg_info_buffer.appendSliceAssumeCapacity("len"); 246 dbg_info_buffer.appendAssumeCapacity(0); 247 // DW.AT.type, DW.FORM.ref4 248 index = dbg_info_buffer.items.len; 249 dbg_info_buffer.appendNTimesAssumeCapacity(0, 4); 250 try self.addTypeRelocGlobal(atom_index, Type.usize, @intCast(index)); 251 // DW.AT.data_member_location, DW.FORM.udata 252 dbg_info_buffer.appendAssumeCapacity(ptr_bytes); 253 // DW.AT.structure_type delimit children 254 dbg_info_buffer.appendAssumeCapacity(0); 255 } else { 256 try dbg_info_buffer.ensureUnusedCapacity(9); 257 dbg_info_buffer.appendAssumeCapacity(@intFromEnum(AbbrevCode.ptr_type)); 258 // DW.AT.type, DW.FORM.ref4 259 const index = dbg_info_buffer.items.len; 260 dbg_info_buffer.appendNTimesAssumeCapacity(0, 4); 261 try self.addTypeRelocGlobal(atom_index, ty.childType(mod), @intCast(index)); 262 } 263 }, 264 .Array => { 265 // DW.AT.array_type 266 try dbg_info_buffer.append(@intFromEnum(AbbrevCode.array_type)); 267 // DW.AT.name, DW.FORM.string 268 try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(mod)}); 269 // DW.AT.type, DW.FORM.ref4 270 var index = dbg_info_buffer.items.len; 271 try dbg_info_buffer.ensureUnusedCapacity(9); 272 dbg_info_buffer.appendNTimesAssumeCapacity(0, 4); 273 try self.addTypeRelocGlobal(atom_index, ty.childType(mod), @intCast(index)); 274 // DW.AT.subrange_type 275 dbg_info_buffer.appendAssumeCapacity(@intFromEnum(AbbrevCode.array_dim)); 276 // DW.AT.type, DW.FORM.ref4 277 index = dbg_info_buffer.items.len; 278 dbg_info_buffer.appendNTimesAssumeCapacity(0, 4); 279 try self.addTypeRelocGlobal(atom_index, Type.usize, @intCast(index)); 280 // DW.AT.count, DW.FORM.udata 281 const len = ty.arrayLenIncludingSentinel(mod); 282 try leb128.writeUleb128(dbg_info_buffer.writer(), len); 283 // DW.AT.array_type delimit children 284 try dbg_info_buffer.append(0); 285 }, 286 .Struct => { 287 // DW.AT.structure_type 288 try dbg_info_buffer.append(@intFromEnum(AbbrevCode.struct_type)); 289 // DW.AT.byte_size, DW.FORM.udata 290 try leb128.writeUleb128(dbg_info_buffer.writer(), ty.abiSize(mod)); 291 292 blk: { 293 switch (ip.indexToKey(ty.ip_index)) { 294 .anon_struct_type => |fields| { 295 // DW.AT.name, DW.FORM.string 296 try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(mod)}); 297 298 for (fields.types.get(ip), 0..) |field_ty, field_index| { 299 // DW.AT.member 300 try dbg_info_buffer.append(@intFromEnum(AbbrevCode.struct_member)); 301 // DW.AT.name, DW.FORM.string 302 try dbg_info_buffer.writer().print("{d}\x00", .{field_index}); 303 // DW.AT.type, DW.FORM.ref4 304 const index = dbg_info_buffer.items.len; 305 try dbg_info_buffer.appendNTimes(0, 4); 306 try self.addTypeRelocGlobal(atom_index, Type.fromInterned(field_ty), @intCast(index)); 307 // DW.AT.data_member_location, DW.FORM.udata 308 const field_off = ty.structFieldOffset(field_index, mod); 309 try leb128.writeUleb128(dbg_info_buffer.writer(), field_off); 310 } 311 }, 312 .struct_type => { 313 const struct_type = ip.loadStructType(ty.toIntern()); 314 // DW.AT.name, DW.FORM.string 315 try ty.print(dbg_info_buffer.writer(), mod); 316 try dbg_info_buffer.append(0); 317 318 if (struct_type.layout == .@"packed") { 319 log.debug("TODO implement .debug_info for packed structs", .{}); 320 break :blk; 321 } 322 323 if (struct_type.isTuple(ip)) { 324 for (struct_type.field_types.get(ip), struct_type.offsets.get(ip), 0..) |field_ty, field_off, field_index| { 325 if (!Type.fromInterned(field_ty).hasRuntimeBits(mod)) continue; 326 // DW.AT.member 327 try dbg_info_buffer.append(@intFromEnum(AbbrevCode.struct_member)); 328 // DW.AT.name, DW.FORM.string 329 try dbg_info_buffer.writer().print("{d}\x00", .{field_index}); 330 // DW.AT.type, DW.FORM.ref4 331 const index = dbg_info_buffer.items.len; 332 try dbg_info_buffer.appendNTimes(0, 4); 333 try self.addTypeRelocGlobal(atom_index, Type.fromInterned(field_ty), @intCast(index)); 334 // DW.AT.data_member_location, DW.FORM.udata 335 try leb128.writeUleb128(dbg_info_buffer.writer(), field_off); 336 } 337 } else { 338 for ( 339 struct_type.field_names.get(ip), 340 struct_type.field_types.get(ip), 341 struct_type.offsets.get(ip), 342 ) |field_name, field_ty, field_off| { 343 if (!Type.fromInterned(field_ty).hasRuntimeBits(mod)) continue; 344 const field_name_slice = field_name.toSlice(ip); 345 // DW.AT.member 346 try dbg_info_buffer.ensureUnusedCapacity(field_name_slice.len + 2); 347 dbg_info_buffer.appendAssumeCapacity(@intFromEnum(AbbrevCode.struct_member)); 348 // DW.AT.name, DW.FORM.string 349 dbg_info_buffer.appendSliceAssumeCapacity(field_name_slice[0 .. field_name_slice.len + 1]); 350 // DW.AT.type, DW.FORM.ref4 351 const index = dbg_info_buffer.items.len; 352 try dbg_info_buffer.appendNTimes(0, 4); 353 try self.addTypeRelocGlobal(atom_index, Type.fromInterned(field_ty), @intCast(index)); 354 // DW.AT.data_member_location, DW.FORM.udata 355 try leb128.writeUleb128(dbg_info_buffer.writer(), field_off); 356 } 357 } 358 }, 359 else => unreachable, 360 } 361 } 362 363 // DW.AT.structure_type delimit children 364 try dbg_info_buffer.append(0); 365 }, 366 .Enum => { 367 // DW.AT.enumeration_type 368 try dbg_info_buffer.append(@intFromEnum(AbbrevCode.enum_type)); 369 // DW.AT.byte_size, DW.FORM.udata 370 try leb128.writeUleb128(dbg_info_buffer.writer(), ty.abiSize(mod)); 371 // DW.AT.name, DW.FORM.string 372 try ty.print(dbg_info_buffer.writer(), mod); 373 try dbg_info_buffer.append(0); 374 375 const enum_type = ip.loadEnumType(ty.ip_index); 376 for (enum_type.names.get(ip), 0..) |field_name, field_i| { 377 const field_name_slice = field_name.toSlice(ip); 378 // DW.AT.enumerator 379 try dbg_info_buffer.ensureUnusedCapacity(field_name_slice.len + 2 + @sizeOf(u64)); 380 dbg_info_buffer.appendAssumeCapacity(@intFromEnum(AbbrevCode.enum_variant)); 381 // DW.AT.name, DW.FORM.string 382 dbg_info_buffer.appendSliceAssumeCapacity(field_name_slice[0 .. field_name_slice.len + 1]); 383 // DW.AT.const_value, DW.FORM.data8 384 const value: u64 = value: { 385 if (enum_type.values.len == 0) break :value field_i; // auto-numbered 386 const value = enum_type.values.get(ip)[field_i]; 387 // TODO do not assume a 64bit enum value - could be bigger. 388 // See https://github.com/ziglang/zig/issues/645 389 const field_int_val = try Value.fromInterned(value).intFromEnum(ty, mod); 390 break :value @bitCast(field_int_val.toSignedInt(mod)); 391 }; 392 mem.writeInt(u64, dbg_info_buffer.addManyAsArrayAssumeCapacity(8), value, target_endian); 393 } 394 395 // DW.AT.enumeration_type delimit children 396 try dbg_info_buffer.append(0); 397 }, 398 .Union => { 399 const union_obj = mod.typeToUnion(ty).?; 400 const layout = mod.getUnionLayout(union_obj); 401 const payload_offset = if (layout.tag_align.compare(.gte, layout.payload_align)) layout.tag_size else 0; 402 const tag_offset = if (layout.tag_align.compare(.gte, layout.payload_align)) 0 else layout.payload_size; 403 // TODO this is temporary to match current state of unions in Zig - we don't yet have 404 // safety checks implemented meaning the implicit tag is not yet stored and generated 405 // for untagged unions. 406 const is_tagged = layout.tag_size > 0; 407 if (is_tagged) { 408 // DW.AT.structure_type 409 try dbg_info_buffer.append(@intFromEnum(AbbrevCode.struct_type)); 410 // DW.AT.byte_size, DW.FORM.udata 411 try leb128.writeUleb128(dbg_info_buffer.writer(), layout.abi_size); 412 // DW.AT.name, DW.FORM.string 413 try ty.print(dbg_info_buffer.writer(), mod); 414 try dbg_info_buffer.append(0); 415 416 // DW.AT.member 417 try dbg_info_buffer.ensureUnusedCapacity(13); 418 dbg_info_buffer.appendAssumeCapacity(@intFromEnum(AbbrevCode.struct_member)); 419 // DW.AT.name, DW.FORM.string 420 dbg_info_buffer.appendSliceAssumeCapacity("payload"); 421 dbg_info_buffer.appendAssumeCapacity(0); 422 // DW.AT.type, DW.FORM.ref4 423 const inner_union_index = dbg_info_buffer.items.len; 424 dbg_info_buffer.appendNTimesAssumeCapacity(0, 4); 425 try self.addTypeRelocLocal(atom_index, @intCast(inner_union_index), 5); 426 // DW.AT.data_member_location, DW.FORM.udata 427 try leb128.writeUleb128(dbg_info_buffer.writer(), payload_offset); 428 } 429 430 // DW.AT.union_type 431 try dbg_info_buffer.append(@intFromEnum(AbbrevCode.union_type)); 432 // DW.AT.byte_size, DW.FORM.udata, 433 try leb128.writeUleb128(dbg_info_buffer.writer(), layout.payload_size); 434 // DW.AT.name, DW.FORM.string 435 if (is_tagged) { 436 try dbg_info_buffer.writer().print("AnonUnion\x00", .{}); 437 } else { 438 try ty.print(dbg_info_buffer.writer(), mod); 439 try dbg_info_buffer.append(0); 440 } 441 442 for (union_obj.field_types.get(ip), union_obj.loadTagType(ip).names.get(ip)) |field_ty, field_name| { 443 if (!Type.fromInterned(field_ty).hasRuntimeBits(mod)) continue; 444 const field_name_slice = field_name.toSlice(ip); 445 // DW.AT.member 446 try dbg_info_buffer.append(@intFromEnum(AbbrevCode.struct_member)); 447 // DW.AT.name, DW.FORM.string 448 try dbg_info_buffer.appendSlice(field_name_slice[0 .. field_name_slice.len + 1]); 449 // DW.AT.type, DW.FORM.ref4 450 const index = dbg_info_buffer.items.len; 451 try dbg_info_buffer.appendNTimes(0, 4); 452 try self.addTypeRelocGlobal(atom_index, Type.fromInterned(field_ty), @intCast(index)); 453 // DW.AT.data_member_location, DW.FORM.udata 454 try dbg_info_buffer.append(0); 455 } 456 // DW.AT.union_type delimit children 457 try dbg_info_buffer.append(0); 458 459 if (is_tagged) { 460 // DW.AT.member 461 try dbg_info_buffer.ensureUnusedCapacity(9); 462 dbg_info_buffer.appendAssumeCapacity(@intFromEnum(AbbrevCode.struct_member)); 463 // DW.AT.name, DW.FORM.string 464 dbg_info_buffer.appendSliceAssumeCapacity("tag"); 465 dbg_info_buffer.appendAssumeCapacity(0); 466 // DW.AT.type, DW.FORM.ref4 467 const index = dbg_info_buffer.items.len; 468 dbg_info_buffer.appendNTimesAssumeCapacity(0, 4); 469 try self.addTypeRelocGlobal(atom_index, Type.fromInterned(union_obj.enum_tag_ty), @intCast(index)); 470 // DW.AT.data_member_location, DW.FORM.udata 471 try leb128.writeUleb128(dbg_info_buffer.writer(), tag_offset); 472 473 // DW.AT.structure_type delimit children 474 try dbg_info_buffer.append(0); 475 } 476 }, 477 .ErrorSet => try addDbgInfoErrorSet(mod, ty, target, &self.dbg_info), 478 .ErrorUnion => { 479 const error_ty = ty.errorUnionSet(mod); 480 const payload_ty = ty.errorUnionPayload(mod); 481 const payload_align = if (payload_ty.isNoReturn(mod)) .none else payload_ty.abiAlignment(mod); 482 const error_align = Type.anyerror.abiAlignment(mod); 483 const abi_size = ty.abiSize(mod); 484 const payload_off = if (error_align.compare(.gte, payload_align)) Type.anyerror.abiSize(mod) else 0; 485 const error_off = if (error_align.compare(.gte, payload_align)) 0 else payload_ty.abiSize(mod); 486 487 // DW.AT.structure_type 488 try dbg_info_buffer.append(@intFromEnum(AbbrevCode.struct_type)); 489 // DW.AT.byte_size, DW.FORM.udata 490 try leb128.writeUleb128(dbg_info_buffer.writer(), abi_size); 491 // DW.AT.name, DW.FORM.string 492 try ty.print(dbg_info_buffer.writer(), mod); 493 try dbg_info_buffer.append(0); 494 495 if (!payload_ty.isNoReturn(mod)) { 496 // DW.AT.member 497 try dbg_info_buffer.ensureUnusedCapacity(11); 498 dbg_info_buffer.appendAssumeCapacity(@intFromEnum(AbbrevCode.struct_member)); 499 // DW.AT.name, DW.FORM.string 500 dbg_info_buffer.appendSliceAssumeCapacity("value"); 501 dbg_info_buffer.appendAssumeCapacity(0); 502 // DW.AT.type, DW.FORM.ref4 503 const index = dbg_info_buffer.items.len; 504 dbg_info_buffer.appendNTimesAssumeCapacity(0, 4); 505 try self.addTypeRelocGlobal(atom_index, payload_ty, @intCast(index)); 506 // DW.AT.data_member_location, DW.FORM.udata 507 try leb128.writeUleb128(dbg_info_buffer.writer(), payload_off); 508 } 509 510 { 511 // DW.AT.member 512 try dbg_info_buffer.ensureUnusedCapacity(9); 513 dbg_info_buffer.appendAssumeCapacity(@intFromEnum(AbbrevCode.struct_member)); 514 // DW.AT.name, DW.FORM.string 515 dbg_info_buffer.appendSliceAssumeCapacity("err"); 516 dbg_info_buffer.appendAssumeCapacity(0); 517 // DW.AT.type, DW.FORM.ref4 518 const index = dbg_info_buffer.items.len; 519 dbg_info_buffer.appendNTimesAssumeCapacity(0, 4); 520 try self.addTypeRelocGlobal(atom_index, error_ty, @intCast(index)); 521 // DW.AT.data_member_location, DW.FORM.udata 522 try leb128.writeUleb128(dbg_info_buffer.writer(), error_off); 523 } 524 525 // DW.AT.structure_type delimit children 526 try dbg_info_buffer.append(0); 527 }, 528 else => { 529 log.debug("TODO implement .debug_info for type '{}'", .{ty.fmt(self.mod)}); 530 try dbg_info_buffer.append(@intFromEnum(AbbrevCode.zero_bit_type)); 531 }, 532 } 533 } 534 535 pub const DbgInfoLoc = union(enum) { 536 register: u8, 537 register_pair: [2]u8, 538 stack: struct { 539 fp_register: u8, 540 offset: i32, 541 }, 542 wasm_local: u32, 543 memory: u64, 544 linker_load: LinkerLoad, 545 immediate: u64, 546 undef, 547 none, 548 nop, 549 }; 550 551 pub fn genArgDbgInfo( 552 self: *DeclState, 553 name: [:0]const u8, 554 ty: Type, 555 owner_decl: InternPool.DeclIndex, 556 loc: DbgInfoLoc, 557 ) error{OutOfMemory}!void { 558 const dbg_info = &self.dbg_info; 559 const atom_index = self.di_atom_decls.get(owner_decl).?; 560 const name_with_null = name.ptr[0 .. name.len + 1]; 561 562 switch (loc) { 563 .register => |reg| { 564 try dbg_info.ensureUnusedCapacity(4); 565 dbg_info.appendAssumeCapacity(@intFromEnum(AbbrevCode.parameter)); 566 // DW.AT.location, DW.FORM.exprloc 567 var expr_len = std.io.countingWriter(std.io.null_writer); 568 if (reg < 32) { 569 expr_len.writer().writeByte(DW.OP.reg0 + reg) catch unreachable; 570 } else { 571 expr_len.writer().writeByte(DW.OP.regx) catch unreachable; 572 leb128.writeUleb128(expr_len.writer(), reg) catch unreachable; 573 } 574 leb128.writeUleb128(dbg_info.writer(), expr_len.bytes_written) catch unreachable; 575 if (reg < 32) { 576 dbg_info.appendAssumeCapacity(DW.OP.reg0 + reg); 577 } else { 578 dbg_info.appendAssumeCapacity(DW.OP.regx); 579 leb128.writeUleb128(dbg_info.writer(), reg) catch unreachable; 580 } 581 }, 582 .register_pair => |regs| { 583 const reg_bits = self.mod.getTarget().ptrBitWidth(); 584 const reg_bytes: u8 = @intCast(@divExact(reg_bits, 8)); 585 const abi_size = ty.abiSize(self.mod); 586 try dbg_info.ensureUnusedCapacity(10); 587 dbg_info.appendAssumeCapacity(@intFromEnum(AbbrevCode.parameter)); 588 // DW.AT.location, DW.FORM.exprloc 589 var expr_len = std.io.countingWriter(std.io.null_writer); 590 for (regs, 0..) |reg, reg_i| { 591 if (reg < 32) { 592 expr_len.writer().writeByte(DW.OP.reg0 + reg) catch unreachable; 593 } else { 594 expr_len.writer().writeByte(DW.OP.regx) catch unreachable; 595 leb128.writeUleb128(expr_len.writer(), reg) catch unreachable; 596 } 597 expr_len.writer().writeByte(DW.OP.piece) catch unreachable; 598 leb128.writeUleb128( 599 expr_len.writer(), 600 @min(abi_size - reg_i * reg_bytes, reg_bytes), 601 ) catch unreachable; 602 } 603 leb128.writeUleb128(dbg_info.writer(), expr_len.bytes_written) catch unreachable; 604 for (regs, 0..) |reg, reg_i| { 605 if (reg < 32) { 606 dbg_info.appendAssumeCapacity(DW.OP.reg0 + reg); 607 } else { 608 dbg_info.appendAssumeCapacity(DW.OP.regx); 609 leb128.writeUleb128(dbg_info.writer(), reg) catch unreachable; 610 } 611 dbg_info.appendAssumeCapacity(DW.OP.piece); 612 leb128.writeUleb128( 613 dbg_info.writer(), 614 @min(abi_size - reg_i * reg_bytes, reg_bytes), 615 ) catch unreachable; 616 } 617 }, 618 .stack => |info| { 619 try dbg_info.ensureUnusedCapacity(9); 620 dbg_info.appendAssumeCapacity(@intFromEnum(AbbrevCode.parameter)); 621 // DW.AT.location, DW.FORM.exprloc 622 var expr_len = std.io.countingWriter(std.io.null_writer); 623 if (info.fp_register < 32) { 624 expr_len.writer().writeByte(DW.OP.breg0 + info.fp_register) catch unreachable; 625 } else { 626 expr_len.writer().writeByte(DW.OP.bregx) catch unreachable; 627 leb128.writeUleb128(expr_len.writer(), info.fp_register) catch unreachable; 628 } 629 leb128.writeIleb128(expr_len.writer(), info.offset) catch unreachable; 630 leb128.writeUleb128(dbg_info.writer(), expr_len.bytes_written) catch unreachable; 631 if (info.fp_register < 32) { 632 dbg_info.appendAssumeCapacity(DW.OP.breg0 + info.fp_register); 633 } else { 634 dbg_info.appendAssumeCapacity(DW.OP.bregx); 635 leb128.writeUleb128(dbg_info.writer(), info.fp_register) catch unreachable; 636 } 637 leb128.writeIleb128(dbg_info.writer(), info.offset) catch unreachable; 638 }, 639 .wasm_local => |value| { 640 const leb_size = link.File.Wasm.getUleb128Size(value); 641 try dbg_info.ensureUnusedCapacity(3 + leb_size); 642 // wasm locations are encoded as follow: 643 // DW_OP_WASM_location wasm-op 644 // where wasm-op is defined as 645 // wasm-op := wasm-local | wasm-global | wasm-operand_stack 646 // where each argument is encoded as 647 // <opcode> i:uleb128 648 dbg_info.appendSliceAssumeCapacity(&.{ 649 @intFromEnum(AbbrevCode.parameter), 650 DW.OP.WASM_location, 651 DW.OP.WASM_local, 652 }); 653 leb128.writeUleb128(dbg_info.writer(), value) catch unreachable; 654 }, 655 else => unreachable, 656 } 657 658 try dbg_info.ensureUnusedCapacity(5 + name_with_null.len); 659 const index = dbg_info.items.len; 660 dbg_info.appendNTimesAssumeCapacity(0, 4); 661 try self.addTypeRelocGlobal(atom_index, ty, @intCast(index)); // DW.AT.type, DW.FORM.ref4 662 dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string 663 } 664 665 pub fn genVarDbgInfo( 666 self: *DeclState, 667 name: [:0]const u8, 668 ty: Type, 669 owner_decl: InternPool.DeclIndex, 670 is_ptr: bool, 671 loc: DbgInfoLoc, 672 ) error{OutOfMemory}!void { 673 const dbg_info = &self.dbg_info; 674 const atom_index = self.di_atom_decls.get(owner_decl).?; 675 const name_with_null = name.ptr[0 .. name.len + 1]; 676 try dbg_info.append(@intFromEnum(AbbrevCode.variable)); 677 const gpa = self.dwarf.allocator; 678 const mod = self.mod; 679 const target = mod.getTarget(); 680 const endian = target.cpu.arch.endian(); 681 const child_ty = if (is_ptr) ty.childType(mod) else ty; 682 683 switch (loc) { 684 .register => |reg| { 685 try dbg_info.ensureUnusedCapacity(3); 686 // DW.AT.location, DW.FORM.exprloc 687 var expr_len = std.io.countingWriter(std.io.null_writer); 688 if (reg < 32) { 689 expr_len.writer().writeByte(DW.OP.reg0 + reg) catch unreachable; 690 } else { 691 expr_len.writer().writeByte(DW.OP.regx) catch unreachable; 692 leb128.writeUleb128(expr_len.writer(), reg) catch unreachable; 693 } 694 leb128.writeUleb128(dbg_info.writer(), expr_len.bytes_written) catch unreachable; 695 if (reg < 32) { 696 dbg_info.appendAssumeCapacity(DW.OP.reg0 + reg); 697 } else { 698 dbg_info.appendAssumeCapacity(DW.OP.regx); 699 leb128.writeUleb128(dbg_info.writer(), reg) catch unreachable; 700 } 701 }, 702 703 .register_pair => |regs| { 704 const reg_bits = self.mod.getTarget().ptrBitWidth(); 705 const reg_bytes: u8 = @intCast(@divExact(reg_bits, 8)); 706 const abi_size = child_ty.abiSize(self.mod); 707 try dbg_info.ensureUnusedCapacity(9); 708 // DW.AT.location, DW.FORM.exprloc 709 var expr_len = std.io.countingWriter(std.io.null_writer); 710 for (regs, 0..) |reg, reg_i| { 711 if (reg < 32) { 712 expr_len.writer().writeByte(DW.OP.reg0 + reg) catch unreachable; 713 } else { 714 expr_len.writer().writeByte(DW.OP.regx) catch unreachable; 715 leb128.writeUleb128(expr_len.writer(), reg) catch unreachable; 716 } 717 expr_len.writer().writeByte(DW.OP.piece) catch unreachable; 718 leb128.writeUleb128( 719 expr_len.writer(), 720 @min(abi_size - reg_i * reg_bytes, reg_bytes), 721 ) catch unreachable; 722 } 723 leb128.writeUleb128(dbg_info.writer(), expr_len.bytes_written) catch unreachable; 724 for (regs, 0..) |reg, reg_i| { 725 if (reg < 32) { 726 dbg_info.appendAssumeCapacity(DW.OP.reg0 + reg); 727 } else { 728 dbg_info.appendAssumeCapacity(DW.OP.regx); 729 leb128.writeUleb128(dbg_info.writer(), reg) catch unreachable; 730 } 731 dbg_info.appendAssumeCapacity(DW.OP.piece); 732 leb128.writeUleb128( 733 dbg_info.writer(), 734 @min(abi_size - reg_i * reg_bytes, reg_bytes), 735 ) catch unreachable; 736 } 737 }, 738 739 .stack => |info| { 740 try dbg_info.ensureUnusedCapacity(9); 741 // DW.AT.location, DW.FORM.exprloc 742 var expr_len = std.io.countingWriter(std.io.null_writer); 743 if (info.fp_register < 32) { 744 expr_len.writer().writeByte(DW.OP.breg0 + info.fp_register) catch unreachable; 745 } else { 746 expr_len.writer().writeByte(DW.OP.bregx) catch unreachable; 747 leb128.writeUleb128(expr_len.writer(), info.fp_register) catch unreachable; 748 } 749 leb128.writeIleb128(expr_len.writer(), info.offset) catch unreachable; 750 leb128.writeUleb128(dbg_info.writer(), expr_len.bytes_written) catch unreachable; 751 if (info.fp_register < 32) { 752 dbg_info.appendAssumeCapacity(DW.OP.breg0 + info.fp_register); 753 } else { 754 dbg_info.appendAssumeCapacity(DW.OP.bregx); 755 leb128.writeUleb128(dbg_info.writer(), info.fp_register) catch unreachable; 756 } 757 leb128.writeIleb128(dbg_info.writer(), info.offset) catch unreachable; 758 }, 759 760 .wasm_local => |value| { 761 const leb_size = link.File.Wasm.getUleb128Size(value); 762 try dbg_info.ensureUnusedCapacity(2 + leb_size); 763 // wasm locals are encoded as follow: 764 // DW_OP_WASM_location wasm-op 765 // where wasm-op is defined as 766 // wasm-op := wasm-local | wasm-global | wasm-operand_stack 767 // where wasm-local is encoded as 768 // wasm-local := 0x00 i:uleb128 769 dbg_info.appendSliceAssumeCapacity(&.{ 770 DW.OP.WASM_location, 771 DW.OP.WASM_local, 772 }); 773 leb128.writeUleb128(dbg_info.writer(), value) catch unreachable; 774 }, 775 776 .memory, 777 .linker_load, 778 => { 779 const ptr_width: u8 = @intCast(@divExact(target.ptrBitWidth(), 8)); 780 try dbg_info.ensureUnusedCapacity(2 + ptr_width); 781 dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc 782 1 + ptr_width + @intFromBool(is_ptr), 783 DW.OP.addr, // literal address 784 }); 785 const offset: u32 = @intCast(dbg_info.items.len); 786 const addr = switch (loc) { 787 .memory => |x| x, 788 else => 0, 789 }; 790 switch (ptr_width) { 791 0...4 => { 792 try dbg_info.writer().writeInt(u32, @intCast(addr), endian); 793 }, 794 5...8 => { 795 try dbg_info.writer().writeInt(u64, addr, endian); 796 }, 797 else => unreachable, 798 } 799 if (is_ptr) { 800 // We need deref the address as we point to the value via GOT entry. 801 try dbg_info.append(DW.OP.deref); 802 } 803 switch (loc) { 804 .linker_load => |load_struct| switch (load_struct.type) { 805 .direct => { 806 log.debug("{x}: target sym %{d}", .{ offset, load_struct.sym_index }); 807 try self.exprloc_relocs.append(gpa, .{ 808 .type = .direct_load, 809 .target = load_struct.sym_index, 810 .offset = offset, 811 }); 812 }, 813 .got => { 814 log.debug("{x}: target sym %{d} via GOT", .{ offset, load_struct.sym_index }); 815 try self.exprloc_relocs.append(gpa, .{ 816 .type = .got_load, 817 .target = load_struct.sym_index, 818 .offset = offset, 819 }); 820 }, 821 else => {}, // TODO 822 }, 823 else => {}, 824 } 825 }, 826 827 .immediate => |x| { 828 try dbg_info.ensureUnusedCapacity(2); 829 const fixup = dbg_info.items.len; 830 dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc 831 1, 832 if (child_ty.isSignedInt(mod)) DW.OP.consts else DW.OP.constu, 833 }); 834 if (child_ty.isSignedInt(mod)) { 835 try leb128.writeIleb128(dbg_info.writer(), @as(i64, @bitCast(x))); 836 } else { 837 try leb128.writeUleb128(dbg_info.writer(), x); 838 } 839 try dbg_info.append(DW.OP.stack_value); 840 dbg_info.items[fixup] += @intCast(dbg_info.items.len - fixup - 2); 841 }, 842 843 .undef => { 844 // DW.AT.location, DW.FORM.exprloc 845 // uleb128(exprloc_len) 846 // DW.OP.implicit_value uleb128(len_of_bytes) bytes 847 const abi_size: u32 = @intCast(child_ty.abiSize(mod)); 848 var implicit_value_len = std.ArrayList(u8).init(gpa); 849 defer implicit_value_len.deinit(); 850 try leb128.writeUleb128(implicit_value_len.writer(), abi_size); 851 const total_exprloc_len = 1 + implicit_value_len.items.len + abi_size; 852 try leb128.writeUleb128(dbg_info.writer(), total_exprloc_len); 853 try dbg_info.ensureUnusedCapacity(total_exprloc_len); 854 dbg_info.appendAssumeCapacity(DW.OP.implicit_value); 855 dbg_info.appendSliceAssumeCapacity(implicit_value_len.items); 856 dbg_info.appendNTimesAssumeCapacity(0xaa, abi_size); 857 }, 858 859 .none => { 860 try dbg_info.ensureUnusedCapacity(3); 861 dbg_info.appendSliceAssumeCapacity(&[3]u8{ // DW.AT.location, DW.FORM.exprloc 862 2, DW.OP.lit0, DW.OP.stack_value, 863 }); 864 }, 865 866 .nop => { 867 try dbg_info.ensureUnusedCapacity(2); 868 dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc 869 1, DW.OP.nop, 870 }); 871 }, 872 } 873 874 try dbg_info.ensureUnusedCapacity(5 + name_with_null.len); 875 const index = dbg_info.items.len; 876 dbg_info.appendNTimesAssumeCapacity(0, 4); // dw.at.type, dw.form.ref4 877 try self.addTypeRelocGlobal(atom_index, child_ty, @intCast(index)); 878 dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string 879 } 880 881 pub fn advancePCAndLine( 882 self: *DeclState, 883 delta_line: i33, 884 delta_pc: u64, 885 ) error{OutOfMemory}!void { 886 const dbg_line = &self.dbg_line; 887 try dbg_line.ensureUnusedCapacity(5 + 5 + 1); 888 889 const header = self.dwarf.dbg_line_header; 890 assert(header.maximum_operations_per_instruction == 1); 891 const delta_op: u64 = 0; 892 893 const remaining_delta_line: i9 = @intCast(if (delta_line < header.line_base or 894 delta_line - header.line_base >= header.line_range) 895 remaining: { 896 assert(delta_line != 0); 897 dbg_line.appendAssumeCapacity(DW.LNS.advance_line); 898 leb128.writeIleb128(dbg_line.writer(), delta_line) catch unreachable; 899 break :remaining 0; 900 } else delta_line); 901 902 const op_advance = @divExact(delta_pc, header.minimum_instruction_length) * 903 header.maximum_operations_per_instruction + delta_op; 904 const max_op_advance: u9 = (std.math.maxInt(u8) - header.opcode_base) / header.line_range; 905 const remaining_op_advance: u8 = @intCast(if (op_advance >= 2 * max_op_advance) remaining: { 906 dbg_line.appendAssumeCapacity(DW.LNS.advance_pc); 907 leb128.writeUleb128(dbg_line.writer(), op_advance) catch unreachable; 908 break :remaining 0; 909 } else if (op_advance >= max_op_advance) remaining: { 910 dbg_line.appendAssumeCapacity(DW.LNS.const_add_pc); 911 break :remaining op_advance - max_op_advance; 912 } else op_advance); 913 914 if (remaining_delta_line == 0 and remaining_op_advance == 0) { 915 dbg_line.appendAssumeCapacity(DW.LNS.copy); 916 } else { 917 dbg_line.appendAssumeCapacity(@intCast((remaining_delta_line - header.line_base) + 918 (header.line_range * remaining_op_advance) + header.opcode_base)); 919 } 920 } 921 922 pub fn setColumn(self: *DeclState, column: u32) error{OutOfMemory}!void { 923 try self.dbg_line.ensureUnusedCapacity(1 + 5); 924 self.dbg_line.appendAssumeCapacity(DW.LNS.set_column); 925 leb128.writeUleb128(self.dbg_line.writer(), column + 1) catch unreachable; 926 } 927 928 pub fn setPrologueEnd(self: *DeclState) error{OutOfMemory}!void { 929 try self.dbg_line.append(DW.LNS.set_prologue_end); 930 } 931 932 pub fn setEpilogueBegin(self: *DeclState) error{OutOfMemory}!void { 933 try self.dbg_line.append(DW.LNS.set_epilogue_begin); 934 } 935 936 pub fn setInlineFunc(self: *DeclState, func: InternPool.Index) error{OutOfMemory}!void { 937 if (self.dbg_line_func == func) return; 938 939 try self.dbg_line.ensureUnusedCapacity((1 + 4) + (1 + 5)); 940 941 const old_func_info = self.mod.funcInfo(self.dbg_line_func); 942 const new_func_info = self.mod.funcInfo(func); 943 944 const old_file = try self.dwarf.addDIFile(self.mod, old_func_info.owner_decl); 945 const new_file = try self.dwarf.addDIFile(self.mod, new_func_info.owner_decl); 946 if (old_file != new_file) { 947 self.dbg_line.appendAssumeCapacity(DW.LNS.set_file); 948 leb128.writeUnsignedFixed(4, self.dbg_line.addManyAsArrayAssumeCapacity(4), new_file); 949 } 950 951 const old_src_line: i33 = self.mod.declPtr(old_func_info.owner_decl).src_line; 952 const new_src_line: i33 = self.mod.declPtr(new_func_info.owner_decl).src_line; 953 if (new_src_line != old_src_line) { 954 self.dbg_line.appendAssumeCapacity(DW.LNS.advance_line); 955 leb128.writeSignedFixed(5, self.dbg_line.addManyAsArrayAssumeCapacity(5), new_src_line - old_src_line); 956 } 957 958 self.dbg_line_func = func; 959 } 960 }; 961 962 pub const AbbrevEntry = struct { 963 atom_index: Atom.Index, 964 type: Type, 965 offset: u32, 966 }; 967 968 pub const AbbrevRelocation = struct { 969 /// If target is null, we deal with a local relocation that is based on simple offset + addend 970 /// only. 971 target: ?u32, 972 atom_index: Atom.Index, 973 offset: u32, 974 addend: u32, 975 }; 976 977 pub const ExprlocRelocation = struct { 978 /// Type of the relocation: direct load ref, or GOT load ref (via GOT table) 979 type: enum { 980 direct_load, 981 got_load, 982 }, 983 /// Index of the target in the linker's locals symbol table. 984 target: u32, 985 /// Offset within the debug info buffer where to patch up the address value. 986 offset: u32, 987 }; 988 989 pub const PtrWidth = enum { p32, p64 }; 990 991 pub const AbbrevCode = enum(u8) { 992 null, 993 padding, 994 compile_unit, 995 subprogram, 996 subprogram_retvoid, 997 base_type, 998 ptr_type, 999 struct_type, 1000 struct_member, 1001 enum_type, 1002 enum_variant, 1003 union_type, 1004 zero_bit_type, 1005 parameter, 1006 variable, 1007 array_type, 1008 array_dim, 1009 }; 1010 1011 /// The reloc offset for the virtual address of a function in its Line Number Program. 1012 /// Size is a virtual address integer. 1013 const dbg_line_vaddr_reloc_index = 3; 1014 /// The reloc offset for the virtual address of a function in its .debug_info TAG.subprogram. 1015 /// Size is a virtual address integer. 1016 const dbg_info_low_pc_reloc_index = 1; 1017 1018 const min_nop_size = 2; 1019 1020 /// When allocating, the ideal_capacity is calculated by 1021 /// actual_capacity + (actual_capacity / ideal_factor) 1022 const ideal_factor = 3; 1023 1024 pub fn init(lf: *File, format: Format) Dwarf { 1025 const comp = lf.comp; 1026 const gpa = comp.gpa; 1027 const target = comp.root_mod.resolved_target.result; 1028 const ptr_width: PtrWidth = switch (target.ptrBitWidth()) { 1029 0...32 => .p32, 1030 33...64 => .p64, 1031 else => unreachable, 1032 }; 1033 return .{ 1034 .allocator = gpa, 1035 .bin_file = lf, 1036 .format = format, 1037 .ptr_width = ptr_width, 1038 .dbg_line_header = switch (target.cpu.arch) { 1039 .x86_64, .aarch64 => .{ 1040 .minimum_instruction_length = 1, 1041 .maximum_operations_per_instruction = 1, 1042 .default_is_stmt = true, 1043 .line_base = -5, 1044 .line_range = 14, 1045 .opcode_base = DW.LNS.set_isa + 1, 1046 }, 1047 else => .{ 1048 .minimum_instruction_length = 1, 1049 .maximum_operations_per_instruction = 1, 1050 .default_is_stmt = true, 1051 .line_base = 1, 1052 .line_range = 1, 1053 .opcode_base = DW.LNS.set_isa + 1, 1054 }, 1055 }, 1056 }; 1057 } 1058 1059 pub fn deinit(self: *Dwarf) void { 1060 const gpa = self.allocator; 1061 1062 self.src_fn_free_list.deinit(gpa); 1063 self.src_fns.deinit(gpa); 1064 self.src_fn_decls.deinit(gpa); 1065 1066 self.di_atom_free_list.deinit(gpa); 1067 self.di_atoms.deinit(gpa); 1068 self.di_atom_decls.deinit(gpa); 1069 1070 self.strtab.deinit(gpa); 1071 self.di_files.deinit(gpa); 1072 self.global_abbrev_relocs.deinit(gpa); 1073 } 1074 1075 /// Initializes Decl's state and its matching output buffers. 1076 /// Call this before `commitDeclState`. 1077 pub fn initDeclState(self: *Dwarf, mod: *Module, decl_index: InternPool.DeclIndex) !DeclState { 1078 const tracy = trace(@src()); 1079 defer tracy.end(); 1080 1081 const decl = mod.declPtr(decl_index); 1082 const decl_linkage_name = try decl.fullyQualifiedName(mod); 1083 1084 log.debug("initDeclState {}{*}", .{ decl_linkage_name.fmt(&mod.intern_pool), decl }); 1085 1086 const gpa = self.allocator; 1087 var decl_state: DeclState = .{ 1088 .dwarf = self, 1089 .mod = mod, 1090 .di_atom_decls = &self.di_atom_decls, 1091 .dbg_line_func = undefined, 1092 .dbg_line = std.ArrayList(u8).init(gpa), 1093 .dbg_info = std.ArrayList(u8).init(gpa), 1094 .abbrev_type_arena = std.heap.ArenaAllocator.init(gpa), 1095 .abbrev_table = .{}, 1096 .abbrev_resolver = .{}, 1097 .abbrev_relocs = .{}, 1098 .exprloc_relocs = .{}, 1099 }; 1100 errdefer decl_state.deinit(); 1101 const dbg_line_buffer = &decl_state.dbg_line; 1102 const dbg_info_buffer = &decl_state.dbg_info; 1103 1104 const di_atom_index = try self.getOrCreateAtomForDecl(.di_atom, decl_index); 1105 1106 assert(decl.has_tv); 1107 1108 switch (decl.typeOf(mod).zigTypeTag(mod)) { 1109 .Fn => { 1110 _ = try self.getOrCreateAtomForDecl(.src_fn, decl_index); 1111 1112 // For functions we need to add a prologue to the debug line program. 1113 const ptr_width_bytes = self.ptrWidthBytes(); 1114 try dbg_line_buffer.ensureTotalCapacity((3 + ptr_width_bytes) + (1 + 4) + (1 + 4) + (1 + 5) + 1); 1115 1116 decl_state.dbg_line_func = decl.val.toIntern(); 1117 const func = decl.val.getFunction(mod).?; 1118 log.debug("decl.src_line={d}, func.lbrace_line={d}, func.rbrace_line={d}", .{ 1119 decl.src_line, 1120 func.lbrace_line, 1121 func.rbrace_line, 1122 }); 1123 const line: u28 = @intCast(decl.src_line + func.lbrace_line); 1124 1125 dbg_line_buffer.appendSliceAssumeCapacity(&.{ 1126 DW.LNS.extended_op, 1127 ptr_width_bytes + 1, 1128 DW.LNE.set_address, 1129 }); 1130 // This is the "relocatable" vaddr, corresponding to `code_buffer` index `0`. 1131 assert(dbg_line_vaddr_reloc_index == dbg_line_buffer.items.len); 1132 dbg_line_buffer.appendNTimesAssumeCapacity(0, ptr_width_bytes); 1133 1134 dbg_line_buffer.appendAssumeCapacity(DW.LNS.advance_line); 1135 // This is the "relocatable" relative line offset from the previous function's end curly 1136 // to this function's begin curly. 1137 assert(self.getRelocDbgLineOff() == dbg_line_buffer.items.len); 1138 // Here we use a ULEB128-fixed-4 to make sure this field can be overwritten later. 1139 leb128.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), line); 1140 1141 dbg_line_buffer.appendAssumeCapacity(DW.LNS.set_file); 1142 assert(self.getRelocDbgFileIndex() == dbg_line_buffer.items.len); 1143 // Once we support more than one source file, this will have the ability to be more 1144 // than one possible value. 1145 const file_index = try self.addDIFile(mod, decl_index); 1146 leb128.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), file_index); 1147 1148 dbg_line_buffer.appendAssumeCapacity(DW.LNS.set_column); 1149 leb128.writeUleb128(dbg_line_buffer.writer(), func.lbrace_column + 1) catch unreachable; 1150 1151 // Emit a line for the begin curly with prologue_end=false. The codegen will 1152 // do the work of setting prologue_end=true and epilogue_begin=true. 1153 dbg_line_buffer.appendAssumeCapacity(DW.LNS.copy); 1154 1155 // .debug_info subprogram 1156 const decl_name_slice = decl.name.toSlice(&mod.intern_pool); 1157 const decl_linkage_name_slice = decl_linkage_name.toSlice(&mod.intern_pool); 1158 try dbg_info_buffer.ensureUnusedCapacity(1 + ptr_width_bytes + 4 + 4 + 1159 (decl_name_slice.len + 1) + (decl_linkage_name_slice.len + 1)); 1160 1161 const fn_ret_type = decl.typeOf(mod).fnReturnType(mod); 1162 const fn_ret_has_bits = fn_ret_type.hasRuntimeBits(mod); 1163 dbg_info_buffer.appendAssumeCapacity(@intFromEnum( 1164 @as(AbbrevCode, if (fn_ret_has_bits) .subprogram else .subprogram_retvoid), 1165 )); 1166 // These get overwritten after generating the machine code. These values are 1167 // "relocations" and have to be in this fixed place so that functions can be 1168 // moved in virtual address space. 1169 assert(dbg_info_low_pc_reloc_index == dbg_info_buffer.items.len); 1170 dbg_info_buffer.appendNTimesAssumeCapacity(0, ptr_width_bytes); // DW.AT.low_pc, DW.FORM.addr 1171 assert(self.getRelocDbgInfoSubprogramHighPC() == dbg_info_buffer.items.len); 1172 dbg_info_buffer.appendNTimesAssumeCapacity(0, 4); // DW.AT.high_pc, DW.FORM.data4 1173 if (fn_ret_has_bits) { 1174 try decl_state.addTypeRelocGlobal(di_atom_index, fn_ret_type, @intCast(dbg_info_buffer.items.len)); 1175 dbg_info_buffer.appendNTimesAssumeCapacity(0, 4); // DW.AT.type, DW.FORM.ref4 1176 } 1177 dbg_info_buffer.appendSliceAssumeCapacity( 1178 decl_name_slice[0 .. decl_name_slice.len + 1], 1179 ); // DW.AT.name, DW.FORM.string 1180 dbg_info_buffer.appendSliceAssumeCapacity( 1181 decl_linkage_name_slice[0 .. decl_linkage_name_slice.len + 1], 1182 ); // DW.AT.linkage_name, DW.FORM.string 1183 }, 1184 else => { 1185 // TODO implement .debug_info for global variables 1186 }, 1187 } 1188 1189 return decl_state; 1190 } 1191 1192 pub fn commitDeclState( 1193 self: *Dwarf, 1194 zcu: *Module, 1195 decl_index: InternPool.DeclIndex, 1196 sym_addr: u64, 1197 sym_size: u64, 1198 decl_state: *DeclState, 1199 ) !void { 1200 const tracy = trace(@src()); 1201 defer tracy.end(); 1202 1203 const gpa = self.allocator; 1204 const decl = zcu.declPtr(decl_index); 1205 const ip = &zcu.intern_pool; 1206 const namespace = zcu.namespacePtr(decl.src_namespace); 1207 const target = namespace.file_scope.mod.resolved_target.result; 1208 const target_endian = target.cpu.arch.endian(); 1209 1210 var dbg_line_buffer = &decl_state.dbg_line; 1211 var dbg_info_buffer = &decl_state.dbg_info; 1212 1213 assert(decl.has_tv); 1214 switch (decl.typeOf(zcu).zigTypeTag(zcu)) { 1215 .Fn => { 1216 try decl_state.setInlineFunc(decl.val.toIntern()); 1217 1218 // Since the Decl is a function, we need to update the .debug_line program. 1219 // Perform the relocations based on vaddr. 1220 switch (self.ptr_width) { 1221 .p32 => { 1222 { 1223 const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..4]; 1224 mem.writeInt(u32, ptr, @intCast(sym_addr), target_endian); 1225 } 1226 { 1227 const ptr = dbg_info_buffer.items[dbg_info_low_pc_reloc_index..][0..4]; 1228 mem.writeInt(u32, ptr, @intCast(sym_addr), target_endian); 1229 } 1230 }, 1231 .p64 => { 1232 { 1233 const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..8]; 1234 mem.writeInt(u64, ptr, sym_addr, target_endian); 1235 } 1236 { 1237 const ptr = dbg_info_buffer.items[dbg_info_low_pc_reloc_index..][0..8]; 1238 mem.writeInt(u64, ptr, sym_addr, target_endian); 1239 } 1240 }, 1241 } 1242 { 1243 log.debug("relocating subprogram high PC value: {x} => {x}", .{ 1244 self.getRelocDbgInfoSubprogramHighPC(), 1245 sym_size, 1246 }); 1247 const ptr = dbg_info_buffer.items[self.getRelocDbgInfoSubprogramHighPC()..][0..4]; 1248 mem.writeInt(u32, ptr, @intCast(sym_size), target_endian); 1249 } 1250 1251 try dbg_line_buffer.appendSlice(&[_]u8{ DW.LNS.extended_op, 1, DW.LNE.end_sequence }); 1252 1253 // Now we have the full contents and may allocate a region to store it. 1254 1255 // This logic is nearly identical to the logic below in `updateDeclDebugInfo` for 1256 // `TextBlock` and the .debug_info. If you are editing this logic, you 1257 // probably need to edit that logic too. 1258 const src_fn_index = self.src_fn_decls.get(decl_index).?; 1259 const src_fn = self.getAtomPtr(.src_fn, src_fn_index); 1260 src_fn.len = @intCast(dbg_line_buffer.items.len); 1261 1262 if (self.src_fn_last_index) |last_index| blk: { 1263 if (src_fn_index == last_index) break :blk; 1264 if (src_fn.next_index) |next_index| { 1265 const next = self.getAtomPtr(.src_fn, next_index); 1266 // Update existing function - non-last item. 1267 if (src_fn.off + src_fn.len + min_nop_size > next.off) { 1268 // It grew too big, so we move it to a new location. 1269 if (src_fn.prev_index) |prev_index| { 1270 self.src_fn_free_list.put(gpa, prev_index, {}) catch {}; 1271 self.getAtomPtr(.src_fn, prev_index).next_index = src_fn.next_index; 1272 } 1273 next.prev_index = src_fn.prev_index; 1274 src_fn.next_index = null; 1275 // Populate where it used to be with NOPs. 1276 switch (self.bin_file.tag) { 1277 .elf => { 1278 const elf_file = self.bin_file.cast(File.Elf).?; 1279 const debug_line_sect = &elf_file.shdrs.items[elf_file.debug_line_section_index.?]; 1280 const file_pos = debug_line_sect.sh_offset + src_fn.off; 1281 try pwriteDbgLineNops(elf_file.base.file.?, file_pos, 0, &[0]u8{}, src_fn.len); 1282 }, 1283 .macho => { 1284 const macho_file = self.bin_file.cast(File.MachO).?; 1285 if (macho_file.base.isRelocatable()) { 1286 const debug_line_sect = &macho_file.sections.items(.header)[macho_file.debug_line_sect_index.?]; 1287 const file_pos = debug_line_sect.offset + src_fn.off; 1288 try pwriteDbgLineNops(macho_file.base.file.?, file_pos, 0, &[0]u8{}, src_fn.len); 1289 } else { 1290 const d_sym = macho_file.getDebugSymbols().?; 1291 const debug_line_sect = d_sym.getSectionPtr(d_sym.debug_line_section_index.?); 1292 const file_pos = debug_line_sect.offset + src_fn.off; 1293 try pwriteDbgLineNops(d_sym.file, file_pos, 0, &[0]u8{}, src_fn.len); 1294 } 1295 }, 1296 .wasm => { 1297 // const wasm_file = self.bin_file.cast(File.Wasm).?; 1298 // const debug_line = wasm_file.getAtomPtr(wasm_file.debug_line_atom.?).code; 1299 // writeDbgLineNopsBuffered(debug_line.items, src_fn.off, 0, &.{}, src_fn.len); 1300 }, 1301 else => unreachable, 1302 } 1303 // TODO Look at the free list before appending at the end. 1304 src_fn.prev_index = last_index; 1305 const last = self.getAtomPtr(.src_fn, last_index); 1306 last.next_index = src_fn_index; 1307 self.src_fn_last_index = src_fn_index; 1308 1309 src_fn.off = last.off + padToIdeal(last.len); 1310 } 1311 } else if (src_fn.prev_index == null) { 1312 // Append new function. 1313 // TODO Look at the free list before appending at the end. 1314 src_fn.prev_index = last_index; 1315 const last = self.getAtomPtr(.src_fn, last_index); 1316 last.next_index = src_fn_index; 1317 self.src_fn_last_index = src_fn_index; 1318 1319 src_fn.off = last.off + padToIdeal(last.len); 1320 } 1321 } else { 1322 // This is the first function of the Line Number Program. 1323 self.src_fn_first_index = src_fn_index; 1324 self.src_fn_last_index = src_fn_index; 1325 1326 src_fn.off = padToIdeal(self.dbgLineNeededHeaderBytes(&[0][]u8{}, &[0][]u8{})); 1327 } 1328 1329 const last_src_fn_index = self.src_fn_last_index.?; 1330 const last_src_fn = self.getAtom(.src_fn, last_src_fn_index); 1331 const needed_size = last_src_fn.off + last_src_fn.len; 1332 const prev_padding_size: u32 = if (src_fn.prev_index) |prev_index| blk: { 1333 const prev = self.getAtom(.src_fn, prev_index); 1334 break :blk src_fn.off - (prev.off + prev.len); 1335 } else 0; 1336 const next_padding_size: u32 = if (src_fn.next_index) |next_index| blk: { 1337 const next = self.getAtom(.src_fn, next_index); 1338 break :blk next.off - (src_fn.off + src_fn.len); 1339 } else 0; 1340 1341 // We only have support for one compilation unit so far, so the offsets are directly 1342 // from the .debug_line section. 1343 switch (self.bin_file.tag) { 1344 .elf => { 1345 const elf_file = self.bin_file.cast(File.Elf).?; 1346 const shdr_index = elf_file.debug_line_section_index.?; 1347 try elf_file.growNonAllocSection(shdr_index, needed_size, 1, true); 1348 const debug_line_sect = elf_file.shdrs.items[shdr_index]; 1349 const file_pos = debug_line_sect.sh_offset + src_fn.off; 1350 try pwriteDbgLineNops( 1351 elf_file.base.file.?, 1352 file_pos, 1353 prev_padding_size, 1354 dbg_line_buffer.items, 1355 next_padding_size, 1356 ); 1357 }, 1358 1359 .macho => { 1360 const macho_file = self.bin_file.cast(File.MachO).?; 1361 if (macho_file.base.isRelocatable()) { 1362 const sect_index = macho_file.debug_line_sect_index.?; 1363 try macho_file.growSection(sect_index, needed_size); 1364 const sect = macho_file.sections.items(.header)[sect_index]; 1365 const file_pos = sect.offset + src_fn.off; 1366 try pwriteDbgLineNops( 1367 macho_file.base.file.?, 1368 file_pos, 1369 prev_padding_size, 1370 dbg_line_buffer.items, 1371 next_padding_size, 1372 ); 1373 } else { 1374 const d_sym = macho_file.getDebugSymbols().?; 1375 const sect_index = d_sym.debug_line_section_index.?; 1376 try d_sym.growSection(sect_index, needed_size, true, macho_file); 1377 const sect = d_sym.getSection(sect_index); 1378 const file_pos = sect.offset + src_fn.off; 1379 try pwriteDbgLineNops( 1380 d_sym.file, 1381 file_pos, 1382 prev_padding_size, 1383 dbg_line_buffer.items, 1384 next_padding_size, 1385 ); 1386 } 1387 }, 1388 1389 .wasm => { 1390 // const wasm_file = self.bin_file.cast(File.Wasm).?; 1391 // const atom = wasm_file.getAtomPtr(wasm_file.debug_line_atom.?); 1392 // const debug_line = &atom.code; 1393 // const segment_size = debug_line.items.len; 1394 // if (needed_size != segment_size) { 1395 // log.debug(" needed size does not equal allocated size: {d}", .{needed_size}); 1396 // if (needed_size > segment_size) { 1397 // log.debug(" allocating {d} bytes for 'debug line' information", .{needed_size - segment_size}); 1398 // try debug_line.resize(self.allocator, needed_size); 1399 // @memset(debug_line.items[segment_size..], 0); 1400 // } 1401 // debug_line.items.len = needed_size; 1402 // } 1403 // writeDbgLineNopsBuffered( 1404 // debug_line.items, 1405 // src_fn.off, 1406 // prev_padding_size, 1407 // dbg_line_buffer.items, 1408 // next_padding_size, 1409 // ); 1410 }, 1411 else => unreachable, 1412 } 1413 1414 // .debug_info - End the TAG.subprogram children. 1415 try dbg_info_buffer.append(0); 1416 }, 1417 else => {}, 1418 } 1419 1420 if (dbg_info_buffer.items.len == 0) 1421 return; 1422 1423 const di_atom_index = self.di_atom_decls.get(decl_index).?; 1424 if (decl_state.abbrev_table.items.len > 0) { 1425 // Now we emit the .debug_info types of the Decl. These will count towards the size of 1426 // the buffer, so we have to do it before computing the offset, and we can't perform the actual 1427 // relocations yet. 1428 var sym_index: usize = 0; 1429 while (sym_index < decl_state.abbrev_table.items.len) : (sym_index += 1) { 1430 const symbol = &decl_state.abbrev_table.items[sym_index]; 1431 const ty = symbol.type; 1432 if (ip.isErrorSetType(ty.toIntern())) continue; 1433 1434 symbol.offset = @intCast(dbg_info_buffer.items.len); 1435 try decl_state.addDbgInfoType(zcu, di_atom_index, ty); 1436 } 1437 } 1438 1439 try self.updateDeclDebugInfoAllocation(di_atom_index, @intCast(dbg_info_buffer.items.len)); 1440 1441 while (decl_state.abbrev_relocs.popOrNull()) |reloc| { 1442 if (reloc.target) |reloc_target| { 1443 const symbol = decl_state.abbrev_table.items[reloc_target]; 1444 const ty = symbol.type; 1445 if (ip.isErrorSetType(ty.toIntern())) { 1446 log.debug("resolving %{d} deferred until flush", .{reloc_target}); 1447 try self.global_abbrev_relocs.append(gpa, .{ 1448 .target = null, 1449 .offset = reloc.offset, 1450 .atom_index = reloc.atom_index, 1451 .addend = reloc.addend, 1452 }); 1453 } else { 1454 const atom = self.getAtom(.di_atom, symbol.atom_index); 1455 const value = atom.off + symbol.offset + reloc.addend; 1456 log.debug("{x}: [() => {x}] (%{d}, '{}')", .{ 1457 reloc.offset, 1458 value, 1459 reloc_target, 1460 ty.fmt(zcu), 1461 }); 1462 mem.writeInt( 1463 u32, 1464 dbg_info_buffer.items[reloc.offset..][0..@sizeOf(u32)], 1465 value, 1466 target_endian, 1467 ); 1468 } 1469 } else { 1470 const atom = self.getAtom(.di_atom, reloc.atom_index); 1471 mem.writeInt( 1472 u32, 1473 dbg_info_buffer.items[reloc.offset..][0..@sizeOf(u32)], 1474 atom.off + reloc.offset + reloc.addend, 1475 target_endian, 1476 ); 1477 } 1478 } 1479 1480 while (decl_state.exprloc_relocs.popOrNull()) |reloc| { 1481 switch (self.bin_file.tag) { 1482 .macho => { 1483 const macho_file = self.bin_file.cast(File.MachO).?; 1484 if (macho_file.base.isRelocatable()) { 1485 // TODO 1486 } else { 1487 const d_sym = macho_file.getDebugSymbols().?; 1488 try d_sym.relocs.append(d_sym.allocator, .{ 1489 .type = switch (reloc.type) { 1490 .direct_load => .direct_load, 1491 .got_load => .got_load, 1492 }, 1493 .target = reloc.target, 1494 .offset = reloc.offset + self.getAtom(.di_atom, di_atom_index).off, 1495 .addend = 0, 1496 }); 1497 } 1498 }, 1499 .elf => {}, // TODO 1500 else => unreachable, 1501 } 1502 } 1503 1504 try self.writeDeclDebugInfo(di_atom_index, dbg_info_buffer.items); 1505 } 1506 1507 fn updateDeclDebugInfoAllocation(self: *Dwarf, atom_index: Atom.Index, len: u32) !void { 1508 const tracy = trace(@src()); 1509 defer tracy.end(); 1510 1511 // This logic is nearly identical to the logic above in `updateDecl` for 1512 // `SrcFn` and the line number programs. If you are editing this logic, you 1513 // probably need to edit that logic too. 1514 const gpa = self.allocator; 1515 1516 const atom = self.getAtomPtr(.di_atom, atom_index); 1517 atom.len = len; 1518 if (self.di_atom_last_index) |last_index| blk: { 1519 if (atom_index == last_index) break :blk; 1520 if (atom.next_index) |next_index| { 1521 const next = self.getAtomPtr(.di_atom, next_index); 1522 // Update existing Decl - non-last item. 1523 if (atom.off + atom.len + min_nop_size > next.off) { 1524 // It grew too big, so we move it to a new location. 1525 if (atom.prev_index) |prev_index| { 1526 self.di_atom_free_list.put(gpa, prev_index, {}) catch {}; 1527 self.getAtomPtr(.di_atom, prev_index).next_index = atom.next_index; 1528 } 1529 next.prev_index = atom.prev_index; 1530 atom.next_index = null; 1531 // Populate where it used to be with NOPs. 1532 switch (self.bin_file.tag) { 1533 .elf => { 1534 const elf_file = self.bin_file.cast(File.Elf).?; 1535 const debug_info_sect = &elf_file.shdrs.items[elf_file.debug_info_section_index.?]; 1536 const file_pos = debug_info_sect.sh_offset + atom.off; 1537 try pwriteDbgInfoNops(elf_file.base.file.?, file_pos, 0, &[0]u8{}, atom.len, false); 1538 }, 1539 .macho => { 1540 const macho_file = self.bin_file.cast(File.MachO).?; 1541 if (macho_file.base.isRelocatable()) { 1542 const debug_info_sect = macho_file.sections.items(.header)[macho_file.debug_info_sect_index.?]; 1543 const file_pos = debug_info_sect.offset + atom.off; 1544 try pwriteDbgInfoNops(macho_file.base.file.?, file_pos, 0, &[0]u8{}, atom.len, false); 1545 } else { 1546 const d_sym = macho_file.getDebugSymbols().?; 1547 const debug_info_sect = d_sym.getSectionPtr(d_sym.debug_info_section_index.?); 1548 const file_pos = debug_info_sect.offset + atom.off; 1549 try pwriteDbgInfoNops(d_sym.file, file_pos, 0, &[0]u8{}, atom.len, false); 1550 } 1551 }, 1552 .wasm => { 1553 // const wasm_file = self.bin_file.cast(File.Wasm).?; 1554 // const debug_info_index = wasm_file.debug_info_atom.?; 1555 // const debug_info = &wasm_file.getAtomPtr(debug_info_index).code; 1556 // try writeDbgInfoNopsToArrayList(gpa, debug_info, atom.off, 0, &.{0}, atom.len, false); 1557 }, 1558 else => unreachable, 1559 } 1560 // TODO Look at the free list before appending at the end. 1561 atom.prev_index = last_index; 1562 const last = self.getAtomPtr(.di_atom, last_index); 1563 last.next_index = atom_index; 1564 self.di_atom_last_index = atom_index; 1565 1566 atom.off = last.off + padToIdeal(last.len); 1567 } 1568 } else if (atom.prev_index == null) { 1569 // Append new Decl. 1570 // TODO Look at the free list before appending at the end. 1571 atom.prev_index = last_index; 1572 const last = self.getAtomPtr(.di_atom, last_index); 1573 last.next_index = atom_index; 1574 self.di_atom_last_index = atom_index; 1575 1576 atom.off = last.off + padToIdeal(last.len); 1577 } 1578 } else { 1579 // This is the first Decl of the .debug_info 1580 self.di_atom_first_index = atom_index; 1581 self.di_atom_last_index = atom_index; 1582 1583 atom.off = @intCast(padToIdeal(self.dbgInfoHeaderBytes())); 1584 } 1585 } 1586 1587 fn writeDeclDebugInfo(self: *Dwarf, atom_index: Atom.Index, dbg_info_buf: []const u8) !void { 1588 const tracy = trace(@src()); 1589 defer tracy.end(); 1590 1591 // This logic is nearly identical to the logic above in `updateDecl` for 1592 // `SrcFn` and the line number programs. If you are editing this logic, you 1593 // probably need to edit that logic too. 1594 1595 const atom = self.getAtom(.di_atom, atom_index); 1596 const last_decl_index = self.di_atom_last_index.?; 1597 const last_decl = self.getAtom(.di_atom, last_decl_index); 1598 // +1 for a trailing zero to end the children of the decl tag. 1599 const needed_size = last_decl.off + last_decl.len + 1; 1600 const prev_padding_size: u32 = if (atom.prev_index) |prev_index| blk: { 1601 const prev = self.getAtom(.di_atom, prev_index); 1602 break :blk atom.off - (prev.off + prev.len); 1603 } else 0; 1604 const next_padding_size: u32 = if (atom.next_index) |next_index| blk: { 1605 const next = self.getAtom(.di_atom, next_index); 1606 break :blk next.off - (atom.off + atom.len); 1607 } else 0; 1608 1609 // To end the children of the decl tag. 1610 const trailing_zero = atom.next_index == null; 1611 1612 // We only have support for one compilation unit so far, so the offsets are directly 1613 // from the .debug_info section. 1614 switch (self.bin_file.tag) { 1615 .elf => { 1616 const elf_file = self.bin_file.cast(File.Elf).?; 1617 const shdr_index = elf_file.debug_info_section_index.?; 1618 try elf_file.growNonAllocSection(shdr_index, needed_size, 1, true); 1619 const debug_info_sect = &elf_file.shdrs.items[shdr_index]; 1620 const file_pos = debug_info_sect.sh_offset + atom.off; 1621 try pwriteDbgInfoNops( 1622 elf_file.base.file.?, 1623 file_pos, 1624 prev_padding_size, 1625 dbg_info_buf, 1626 next_padding_size, 1627 trailing_zero, 1628 ); 1629 }, 1630 1631 .macho => { 1632 const macho_file = self.bin_file.cast(File.MachO).?; 1633 if (macho_file.base.isRelocatable()) { 1634 const sect_index = macho_file.debug_info_sect_index.?; 1635 try macho_file.growSection(sect_index, needed_size); 1636 const sect = macho_file.sections.items(.header)[sect_index]; 1637 const file_pos = sect.offset + atom.off; 1638 try pwriteDbgInfoNops( 1639 macho_file.base.file.?, 1640 file_pos, 1641 prev_padding_size, 1642 dbg_info_buf, 1643 next_padding_size, 1644 trailing_zero, 1645 ); 1646 } else { 1647 const d_sym = macho_file.getDebugSymbols().?; 1648 const sect_index = d_sym.debug_info_section_index.?; 1649 try d_sym.growSection(sect_index, needed_size, true, macho_file); 1650 const sect = d_sym.getSection(sect_index); 1651 const file_pos = sect.offset + atom.off; 1652 try pwriteDbgInfoNops( 1653 d_sym.file, 1654 file_pos, 1655 prev_padding_size, 1656 dbg_info_buf, 1657 next_padding_size, 1658 trailing_zero, 1659 ); 1660 } 1661 }, 1662 1663 .wasm => { 1664 // const wasm_file = self.bin_file.cast(File.Wasm).?; 1665 // const info_atom = wasm_file.debug_info_atom.?; 1666 // const debug_info = &wasm_file.getAtomPtr(info_atom).code; 1667 // const segment_size = debug_info.items.len; 1668 // if (needed_size != segment_size) { 1669 // log.debug(" needed size does not equal allocated size: {d}", .{needed_size}); 1670 // if (needed_size > segment_size) { 1671 // log.debug(" allocating {d} bytes for 'debug info' information", .{needed_size - segment_size}); 1672 // try debug_info.resize(self.allocator, needed_size); 1673 // @memset(debug_info.items[segment_size..], 0); 1674 // } 1675 // debug_info.items.len = needed_size; 1676 // } 1677 // log.debug(" writeDbgInfoNopsToArrayList debug_info_len={d} offset={d} content_len={d} next_padding_size={d}", .{ 1678 // debug_info.items.len, atom.off, dbg_info_buf.len, next_padding_size, 1679 // }); 1680 // try writeDbgInfoNopsToArrayList( 1681 // gpa, 1682 // debug_info, 1683 // atom.off, 1684 // prev_padding_size, 1685 // dbg_info_buf, 1686 // next_padding_size, 1687 // trailing_zero, 1688 // ); 1689 }, 1690 else => unreachable, 1691 } 1692 } 1693 1694 pub fn updateDeclLineNumber(self: *Dwarf, mod: *Module, decl_index: InternPool.DeclIndex) !void { 1695 const tracy = trace(@src()); 1696 defer tracy.end(); 1697 1698 const atom_index = try self.getOrCreateAtomForDecl(.src_fn, decl_index); 1699 const atom = self.getAtom(.src_fn, atom_index); 1700 if (atom.len == 0) return; 1701 1702 const decl = mod.declPtr(decl_index); 1703 const func = decl.val.getFunction(mod).?; 1704 log.debug("decl.src_line={d}, func.lbrace_line={d}, func.rbrace_line={d}", .{ 1705 decl.src_line, 1706 func.lbrace_line, 1707 func.rbrace_line, 1708 }); 1709 const line: u28 = @intCast(decl.src_line + func.lbrace_line); 1710 var data: [4]u8 = undefined; 1711 leb128.writeUnsignedFixed(4, &data, line); 1712 1713 switch (self.bin_file.tag) { 1714 .elf => { 1715 const elf_file = self.bin_file.cast(File.Elf).?; 1716 const shdr = elf_file.shdrs.items[elf_file.debug_line_section_index.?]; 1717 const file_pos = shdr.sh_offset + atom.off + self.getRelocDbgLineOff(); 1718 try elf_file.base.file.?.pwriteAll(&data, file_pos); 1719 }, 1720 .macho => { 1721 const macho_file = self.bin_file.cast(File.MachO).?; 1722 if (macho_file.base.isRelocatable()) { 1723 const sect = macho_file.sections.items(.header)[macho_file.debug_line_sect_index.?]; 1724 const file_pos = sect.offset + atom.off + self.getRelocDbgLineOff(); 1725 try macho_file.base.file.?.pwriteAll(&data, file_pos); 1726 } else { 1727 const d_sym = macho_file.getDebugSymbols().?; 1728 const sect = d_sym.getSection(d_sym.debug_line_section_index.?); 1729 const file_pos = sect.offset + atom.off + self.getRelocDbgLineOff(); 1730 try d_sym.file.pwriteAll(&data, file_pos); 1731 } 1732 }, 1733 .wasm => { 1734 // const wasm_file = self.bin_file.cast(File.Wasm).?; 1735 // const offset = atom.off + self.getRelocDbgLineOff(); 1736 // const line_atom_index = wasm_file.debug_line_atom.?; 1737 // wasm_file.getAtomPtr(line_atom_index).code.items[offset..][0..data.len].* = data; 1738 }, 1739 else => unreachable, 1740 } 1741 } 1742 1743 pub fn freeDecl(self: *Dwarf, decl_index: InternPool.DeclIndex) void { 1744 const gpa = self.allocator; 1745 1746 // Free SrcFn atom 1747 if (self.src_fn_decls.fetchRemove(decl_index)) |kv| { 1748 const src_fn_index = kv.value; 1749 const src_fn = self.getAtom(.src_fn, src_fn_index); 1750 _ = self.src_fn_free_list.remove(src_fn_index); 1751 1752 if (src_fn.prev_index) |prev_index| { 1753 self.src_fn_free_list.put(gpa, prev_index, {}) catch {}; 1754 const prev = self.getAtomPtr(.src_fn, prev_index); 1755 prev.next_index = src_fn.next_index; 1756 if (src_fn.next_index) |next_index| { 1757 self.getAtomPtr(.src_fn, next_index).prev_index = prev_index; 1758 } else { 1759 self.src_fn_last_index = prev_index; 1760 } 1761 } else if (src_fn.next_index) |next_index| { 1762 self.src_fn_first_index = next_index; 1763 self.getAtomPtr(.src_fn, next_index).prev_index = null; 1764 } 1765 if (self.src_fn_first_index == src_fn_index) { 1766 self.src_fn_first_index = src_fn.next_index; 1767 } 1768 if (self.src_fn_last_index == src_fn_index) { 1769 self.src_fn_last_index = src_fn.prev_index; 1770 } 1771 } 1772 1773 // Free DI atom 1774 if (self.di_atom_decls.fetchRemove(decl_index)) |kv| { 1775 const di_atom_index = kv.value; 1776 const di_atom = self.getAtomPtr(.di_atom, di_atom_index); 1777 1778 if (self.di_atom_first_index == di_atom_index) { 1779 self.di_atom_first_index = di_atom.next_index; 1780 } 1781 if (self.di_atom_last_index == di_atom_index) { 1782 // TODO shrink the .debug_info section size here 1783 self.di_atom_last_index = di_atom.prev_index; 1784 } 1785 1786 if (di_atom.prev_index) |prev_index| { 1787 self.getAtomPtr(.di_atom, prev_index).next_index = di_atom.next_index; 1788 // TODO the free list logic like we do for SrcFn above 1789 } else { 1790 di_atom.prev_index = null; 1791 } 1792 1793 if (di_atom.next_index) |next_index| { 1794 self.getAtomPtr(.di_atom, next_index).prev_index = di_atom.prev_index; 1795 } else { 1796 di_atom.next_index = null; 1797 } 1798 } 1799 } 1800 1801 pub fn writeDbgAbbrev(self: *Dwarf) !void { 1802 // These are LEB encoded but since the values are all less than 127 1803 // we can simply append these bytes. 1804 // zig fmt: off 1805 const abbrev_buf = [_]u8{ 1806 @intFromEnum(AbbrevCode.padding), 1807 @as(u8, 0x80) | @as(u7, @truncate(DW.TAG.ZIG_padding >> 0)), 1808 @as(u8, 0x80) | @as(u7, @truncate(DW.TAG.ZIG_padding >> 7)), 1809 @as(u8, 0x00) | @as(u7, @intCast(DW.TAG.ZIG_padding >> 14)), 1810 DW.CHILDREN.no, 1811 0, 0, 1812 1813 @intFromEnum(AbbrevCode.compile_unit), 1814 DW.TAG.compile_unit, 1815 DW.CHILDREN.yes, 1816 DW.AT.stmt_list, DW.FORM.sec_offset, 1817 DW.AT.low_pc, DW.FORM.addr, 1818 DW.AT.high_pc, DW.FORM.addr, 1819 DW.AT.name, DW.FORM.strp, 1820 DW.AT.comp_dir, DW.FORM.strp, 1821 DW.AT.producer, DW.FORM.strp, 1822 DW.AT.language, DW.FORM.data2, 1823 0, 0, 1824 1825 @intFromEnum(AbbrevCode.subprogram), 1826 DW.TAG.subprogram, 1827 DW.CHILDREN.yes, 1828 DW.AT.low_pc, DW.FORM.addr, 1829 DW.AT.high_pc, DW.FORM.data4, 1830 DW.AT.type, DW.FORM.ref4, 1831 DW.AT.name, DW.FORM.string, 1832 DW.AT.linkage_name, DW.FORM.string, 1833 0, 0, 1834 1835 @intFromEnum(AbbrevCode.subprogram_retvoid), 1836 DW.TAG.subprogram, 1837 DW.CHILDREN.yes, 1838 DW.AT.low_pc, DW.FORM.addr, 1839 DW.AT.high_pc, DW.FORM.data4, 1840 DW.AT.name, DW.FORM.string, 1841 DW.AT.linkage_name, DW.FORM.string, 1842 0, 0, 1843 1844 @intFromEnum(AbbrevCode.base_type), 1845 DW.TAG.base_type, DW.CHILDREN.no, 1846 DW.AT.encoding, DW.FORM.data1, 1847 DW.AT.byte_size, DW.FORM.udata, 1848 DW.AT.name, DW.FORM.string, 1849 0, 0, 1850 1851 @intFromEnum(AbbrevCode.ptr_type), 1852 DW.TAG.pointer_type, DW.CHILDREN.no, 1853 DW.AT.type, DW.FORM.ref4, 1854 0, 0, 1855 1856 @intFromEnum(AbbrevCode.struct_type), 1857 DW.TAG.structure_type, DW.CHILDREN.yes, 1858 DW.AT.byte_size, DW.FORM.udata, 1859 DW.AT.name, DW.FORM.string, 1860 0, 0, 1861 1862 @intFromEnum(AbbrevCode.struct_member), 1863 DW.TAG.member, 1864 DW.CHILDREN.no, 1865 DW.AT.name, DW.FORM.string, 1866 DW.AT.type, DW.FORM.ref4, 1867 DW.AT.data_member_location, DW.FORM.udata, 1868 0, 0, 1869 1870 @intFromEnum(AbbrevCode.enum_type), 1871 DW.TAG.enumeration_type, 1872 DW.CHILDREN.yes, 1873 DW.AT.byte_size, DW.FORM.udata, 1874 DW.AT.name, DW.FORM.string, 1875 0, 0, 1876 1877 @intFromEnum(AbbrevCode.enum_variant), 1878 DW.TAG.enumerator, DW.CHILDREN.no, 1879 DW.AT.name, DW.FORM.string, 1880 DW.AT.const_value, DW.FORM.data8, 1881 0, 0, 1882 1883 @intFromEnum(AbbrevCode.union_type), 1884 DW.TAG.union_type, DW.CHILDREN.yes, 1885 DW.AT.byte_size, DW.FORM.udata, 1886 DW.AT.name, DW.FORM.string, 1887 0, 0, 1888 1889 @intFromEnum(AbbrevCode.zero_bit_type), 1890 DW.TAG.unspecified_type, 1891 DW.CHILDREN.no, 1892 0, 0, 1893 1894 @intFromEnum(AbbrevCode.parameter), 1895 DW.TAG.formal_parameter, 1896 DW.CHILDREN.no, 1897 DW.AT.location, DW.FORM.exprloc, 1898 DW.AT.type, DW.FORM.ref4, 1899 DW.AT.name, DW.FORM.string, 1900 0, 0, 1901 1902 @intFromEnum(AbbrevCode.variable), 1903 DW.TAG.variable, 1904 DW.CHILDREN.no, 1905 DW.AT.location, DW.FORM.exprloc, 1906 DW.AT.type, DW.FORM.ref4, 1907 DW.AT.name, DW.FORM.string, 1908 0, 0, 1909 1910 @intFromEnum(AbbrevCode.array_type), 1911 DW.TAG.array_type, 1912 DW.CHILDREN.yes, 1913 DW.AT.name, DW.FORM.string, 1914 DW.AT.type, DW.FORM.ref4, 1915 0, 0, 1916 1917 @intFromEnum(AbbrevCode.array_dim), 1918 DW.TAG.subrange_type, 1919 DW.CHILDREN.no, 1920 DW.AT.type, DW.FORM.ref4, 1921 DW.AT.count, DW.FORM.udata, 1922 0, 0, 1923 1924 0, 1925 }; 1926 // zig fmt: on 1927 const abbrev_offset = 0; 1928 self.abbrev_table_offset = abbrev_offset; 1929 1930 const needed_size = abbrev_buf.len; 1931 switch (self.bin_file.tag) { 1932 .elf => { 1933 const elf_file = self.bin_file.cast(File.Elf).?; 1934 const shdr_index = elf_file.debug_abbrev_section_index.?; 1935 try elf_file.growNonAllocSection(shdr_index, needed_size, 1, false); 1936 const debug_abbrev_sect = &elf_file.shdrs.items[shdr_index]; 1937 const file_pos = debug_abbrev_sect.sh_offset + abbrev_offset; 1938 try elf_file.base.file.?.pwriteAll(&abbrev_buf, file_pos); 1939 }, 1940 .macho => { 1941 const macho_file = self.bin_file.cast(File.MachO).?; 1942 if (macho_file.base.isRelocatable()) { 1943 const sect_index = macho_file.debug_abbrev_sect_index.?; 1944 try macho_file.growSection(sect_index, needed_size); 1945 const sect = macho_file.sections.items(.header)[sect_index]; 1946 const file_pos = sect.offset + abbrev_offset; 1947 try macho_file.base.file.?.pwriteAll(&abbrev_buf, file_pos); 1948 } else { 1949 const d_sym = macho_file.getDebugSymbols().?; 1950 const sect_index = d_sym.debug_abbrev_section_index.?; 1951 try d_sym.growSection(sect_index, needed_size, false, macho_file); 1952 const sect = d_sym.getSection(sect_index); 1953 const file_pos = sect.offset + abbrev_offset; 1954 try d_sym.file.pwriteAll(&abbrev_buf, file_pos); 1955 } 1956 }, 1957 .wasm => { 1958 // const wasm_file = self.bin_file.cast(File.Wasm).?; 1959 // const debug_abbrev = &wasm_file.getAtomPtr(wasm_file.debug_abbrev_atom.?).code; 1960 // try debug_abbrev.resize(gpa, needed_size); 1961 // debug_abbrev.items[0..abbrev_buf.len].* = abbrev_buf; 1962 }, 1963 else => unreachable, 1964 } 1965 } 1966 1967 fn dbgInfoHeaderBytes(self: *Dwarf) usize { 1968 _ = self; 1969 return 120; 1970 } 1971 1972 pub fn writeDbgInfoHeader(self: *Dwarf, zcu: *Module, low_pc: u64, high_pc: u64) !void { 1973 // If this value is null it means there is an error in the module; 1974 // leave debug_info_header_dirty=true. 1975 const first_dbg_info_off = self.getDebugInfoOff() orelse return; 1976 1977 // We have a function to compute the upper bound size, because it's needed 1978 // for determining where to put the offset of the first `LinkBlock`. 1979 const needed_bytes = self.dbgInfoHeaderBytes(); 1980 var di_buf = try std.ArrayList(u8).initCapacity(self.allocator, needed_bytes); 1981 defer di_buf.deinit(); 1982 1983 const comp = self.bin_file.comp; 1984 const target = comp.root_mod.resolved_target.result; 1985 const target_endian = target.cpu.arch.endian(); 1986 const init_len_size: usize = switch (self.format) { 1987 .dwarf32 => 4, 1988 .dwarf64 => 12, 1989 }; 1990 1991 // initial length - length of the .debug_info contribution for this compilation unit, 1992 // not including the initial length itself. 1993 // We have to come back and write it later after we know the size. 1994 const after_init_len = di_buf.items.len + init_len_size; 1995 const dbg_info_end = self.getDebugInfoEnd().?; 1996 const init_len = dbg_info_end - after_init_len + 1; 1997 1998 if (self.format == .dwarf64) di_buf.appendNTimesAssumeCapacity(0xff, 4); 1999 self.writeOffsetAssumeCapacity(&di_buf, init_len); 2000 2001 mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 4, target_endian); // DWARF version 2002 const abbrev_offset = self.abbrev_table_offset.?; 2003 2004 self.writeOffsetAssumeCapacity(&di_buf, abbrev_offset); 2005 di_buf.appendAssumeCapacity(self.ptrWidthBytes()); // address size 2006 2007 // Write the form for the compile unit, which must match the abbrev table above. 2008 const name_strp = try self.strtab.insert(self.allocator, zcu.root_mod.root_src_path); 2009 var compile_unit_dir_buffer: [std.fs.max_path_bytes]u8 = undefined; 2010 const compile_unit_dir = resolveCompilationDir(zcu, &compile_unit_dir_buffer); 2011 const comp_dir_strp = try self.strtab.insert(self.allocator, compile_unit_dir); 2012 const producer_strp = try self.strtab.insert(self.allocator, link.producer_string); 2013 2014 di_buf.appendAssumeCapacity(@intFromEnum(AbbrevCode.compile_unit)); 2015 self.writeOffsetAssumeCapacity(&di_buf, 0); // DW.AT.stmt_list, DW.FORM.sec_offset 2016 self.writeAddrAssumeCapacity(&di_buf, low_pc); 2017 self.writeAddrAssumeCapacity(&di_buf, high_pc); 2018 self.writeOffsetAssumeCapacity(&di_buf, name_strp); 2019 self.writeOffsetAssumeCapacity(&di_buf, comp_dir_strp); 2020 self.writeOffsetAssumeCapacity(&di_buf, producer_strp); 2021 2022 // We are still waiting on dwarf-std.org to assign DW_LANG_Zig a number: 2023 // http://dwarfstd.org/ShowIssue.php?issue=171115.1 2024 // Until then we say it is C99. 2025 mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), DW.LANG.C99, target_endian); 2026 2027 if (di_buf.items.len > first_dbg_info_off) { 2028 // Move the first N decls to the end to make more padding for the header. 2029 @panic("TODO: handle .debug_info header exceeding its padding"); 2030 } 2031 const jmp_amt = first_dbg_info_off - di_buf.items.len; 2032 switch (self.bin_file.tag) { 2033 .elf => { 2034 const elf_file = self.bin_file.cast(File.Elf).?; 2035 const debug_info_sect = &elf_file.shdrs.items[elf_file.debug_info_section_index.?]; 2036 const file_pos = debug_info_sect.sh_offset; 2037 try pwriteDbgInfoNops(elf_file.base.file.?, file_pos, 0, di_buf.items, jmp_amt, false); 2038 }, 2039 .macho => { 2040 const macho_file = self.bin_file.cast(File.MachO).?; 2041 if (macho_file.base.isRelocatable()) { 2042 const debug_info_sect = macho_file.sections.items(.header)[macho_file.debug_info_sect_index.?]; 2043 const file_pos = debug_info_sect.offset; 2044 try pwriteDbgInfoNops(macho_file.base.file.?, file_pos, 0, di_buf.items, jmp_amt, false); 2045 } else { 2046 const d_sym = macho_file.getDebugSymbols().?; 2047 const debug_info_sect = d_sym.getSection(d_sym.debug_info_section_index.?); 2048 const file_pos = debug_info_sect.offset; 2049 try pwriteDbgInfoNops(d_sym.file, file_pos, 0, di_buf.items, jmp_amt, false); 2050 } 2051 }, 2052 .wasm => { 2053 // const wasm_file = self.bin_file.cast(File.Wasm).?; 2054 // const debug_info = &wasm_file.getAtomPtr(wasm_file.debug_info_atom.?).code; 2055 // try writeDbgInfoNopsToArrayList(self.allocator, debug_info, 0, 0, di_buf.items, jmp_amt, false); 2056 }, 2057 else => unreachable, 2058 } 2059 } 2060 2061 fn resolveCompilationDir(module: *Module, buffer: *[std.fs.max_path_bytes]u8) []const u8 { 2062 // We fully resolve all paths at this point to avoid lack of source line info in stack 2063 // traces or lack of debugging information which, if relative paths were used, would 2064 // be very location dependent. 2065 // TODO: the only concern I have with this is WASI as either host or target, should 2066 // we leave the paths as relative then? 2067 const root_dir_path = module.root_mod.root.root_dir.path orelse "."; 2068 const sub_path = module.root_mod.root.sub_path; 2069 const realpath = if (std.fs.path.isAbsolute(root_dir_path)) r: { 2070 @memcpy(buffer[0..root_dir_path.len], root_dir_path); 2071 break :r root_dir_path; 2072 } else std.fs.realpath(root_dir_path, buffer) catch return root_dir_path; 2073 const len = realpath.len + 1 + sub_path.len; 2074 if (buffer.len < len) return root_dir_path; 2075 buffer[realpath.len] = '/'; 2076 @memcpy(buffer[realpath.len + 1 ..][0..sub_path.len], sub_path); 2077 return buffer[0..len]; 2078 } 2079 2080 fn writeAddrAssumeCapacity(self: *Dwarf, buf: *std.ArrayList(u8), addr: u64) void { 2081 const comp = self.bin_file.comp; 2082 const target = comp.root_mod.resolved_target.result; 2083 const target_endian = target.cpu.arch.endian(); 2084 switch (self.ptr_width) { 2085 .p32 => mem.writeInt(u32, buf.addManyAsArrayAssumeCapacity(4), @intCast(addr), target_endian), 2086 .p64 => mem.writeInt(u64, buf.addManyAsArrayAssumeCapacity(8), addr, target_endian), 2087 } 2088 } 2089 2090 fn writeOffsetAssumeCapacity(self: *Dwarf, buf: *std.ArrayList(u8), off: u64) void { 2091 const comp = self.bin_file.comp; 2092 const target = comp.root_mod.resolved_target.result; 2093 const target_endian = target.cpu.arch.endian(); 2094 switch (self.format) { 2095 .dwarf32 => mem.writeInt(u32, buf.addManyAsArrayAssumeCapacity(4), @intCast(off), target_endian), 2096 .dwarf64 => mem.writeInt(u64, buf.addManyAsArrayAssumeCapacity(8), off, target_endian), 2097 } 2098 } 2099 2100 /// Writes to the file a buffer, prefixed and suffixed by the specified number of 2101 /// bytes of NOPs. Asserts each padding size is at least `min_nop_size` and total padding bytes 2102 /// are less than 1044480 bytes (if this limit is ever reached, this function can be 2103 /// improved to make more than one pwritev call, or the limit can be raised by a fixed 2104 /// amount by increasing the length of `vecs`). 2105 fn pwriteDbgLineNops( 2106 file: fs.File, 2107 offset: u64, 2108 prev_padding_size: usize, 2109 buf: []const u8, 2110 next_padding_size: usize, 2111 ) !void { 2112 const tracy = trace(@src()); 2113 defer tracy.end(); 2114 2115 const page_of_nops = [1]u8{DW.LNS.negate_stmt} ** 4096; 2116 const three_byte_nop = [3]u8{ DW.LNS.advance_pc, 0b1000_0000, 0 }; 2117 var vecs: [512]std.posix.iovec_const = undefined; 2118 var vec_index: usize = 0; 2119 { 2120 var padding_left = prev_padding_size; 2121 if (padding_left % 2 != 0) { 2122 vecs[vec_index] = .{ 2123 .base = &three_byte_nop, 2124 .len = three_byte_nop.len, 2125 }; 2126 vec_index += 1; 2127 padding_left -= three_byte_nop.len; 2128 } 2129 while (padding_left > page_of_nops.len) { 2130 vecs[vec_index] = .{ 2131 .base = &page_of_nops, 2132 .len = page_of_nops.len, 2133 }; 2134 vec_index += 1; 2135 padding_left -= page_of_nops.len; 2136 } 2137 if (padding_left > 0) { 2138 vecs[vec_index] = .{ 2139 .base = &page_of_nops, 2140 .len = padding_left, 2141 }; 2142 vec_index += 1; 2143 } 2144 } 2145 2146 vecs[vec_index] = .{ 2147 .base = buf.ptr, 2148 .len = buf.len, 2149 }; 2150 if (buf.len > 0) vec_index += 1; 2151 2152 { 2153 var padding_left = next_padding_size; 2154 if (padding_left % 2 != 0) { 2155 vecs[vec_index] = .{ 2156 .base = &three_byte_nop, 2157 .len = three_byte_nop.len, 2158 }; 2159 vec_index += 1; 2160 padding_left -= three_byte_nop.len; 2161 } 2162 while (padding_left > page_of_nops.len) { 2163 vecs[vec_index] = .{ 2164 .base = &page_of_nops, 2165 .len = page_of_nops.len, 2166 }; 2167 vec_index += 1; 2168 padding_left -= page_of_nops.len; 2169 } 2170 if (padding_left > 0) { 2171 vecs[vec_index] = .{ 2172 .base = &page_of_nops, 2173 .len = padding_left, 2174 }; 2175 vec_index += 1; 2176 } 2177 } 2178 try file.pwritevAll(vecs[0..vec_index], offset - prev_padding_size); 2179 } 2180 2181 fn writeDbgLineNopsBuffered( 2182 buf: []u8, 2183 offset: u32, 2184 prev_padding_size: usize, 2185 content: []const u8, 2186 next_padding_size: usize, 2187 ) void { 2188 assert(buf.len >= content.len + prev_padding_size + next_padding_size); 2189 const tracy = trace(@src()); 2190 defer tracy.end(); 2191 2192 const three_byte_nop = [3]u8{ DW.LNS.advance_pc, 0b1000_0000, 0 }; 2193 { 2194 var padding_left = prev_padding_size; 2195 if (padding_left % 2 != 0) { 2196 buf[offset - padding_left ..][0..3].* = three_byte_nop; 2197 padding_left -= 3; 2198 } 2199 2200 while (padding_left > 0) : (padding_left -= 1) { 2201 buf[offset - padding_left] = DW.LNS.negate_stmt; 2202 } 2203 } 2204 2205 @memcpy(buf[offset..][0..content.len], content); 2206 2207 { 2208 var padding_left = next_padding_size; 2209 if (padding_left % 2 != 0) { 2210 buf[offset + content.len + padding_left ..][0..3].* = three_byte_nop; 2211 padding_left -= 3; 2212 } 2213 2214 while (padding_left > 0) : (padding_left -= 1) { 2215 buf[offset + content.len + padding_left] = DW.LNS.negate_stmt; 2216 } 2217 } 2218 } 2219 2220 /// Writes to the file a buffer, prefixed and suffixed by the specified number of 2221 /// bytes of padding. 2222 fn pwriteDbgInfoNops( 2223 file: fs.File, 2224 offset: u64, 2225 prev_padding_size: usize, 2226 buf: []const u8, 2227 next_padding_size: usize, 2228 trailing_zero: bool, 2229 ) !void { 2230 const tracy = trace(@src()); 2231 defer tracy.end(); 2232 2233 const page_of_nops = [1]u8{@intFromEnum(AbbrevCode.padding)} ** 4096; 2234 var vecs: [32]std.posix.iovec_const = undefined; 2235 var vec_index: usize = 0; 2236 { 2237 var padding_left = prev_padding_size; 2238 while (padding_left > page_of_nops.len) { 2239 vecs[vec_index] = .{ 2240 .base = &page_of_nops, 2241 .len = page_of_nops.len, 2242 }; 2243 vec_index += 1; 2244 padding_left -= page_of_nops.len; 2245 } 2246 if (padding_left > 0) { 2247 vecs[vec_index] = .{ 2248 .base = &page_of_nops, 2249 .len = padding_left, 2250 }; 2251 vec_index += 1; 2252 } 2253 } 2254 2255 vecs[vec_index] = .{ 2256 .base = buf.ptr, 2257 .len = buf.len, 2258 }; 2259 if (buf.len > 0) vec_index += 1; 2260 2261 { 2262 var padding_left = next_padding_size; 2263 while (padding_left > page_of_nops.len) { 2264 vecs[vec_index] = .{ 2265 .base = &page_of_nops, 2266 .len = page_of_nops.len, 2267 }; 2268 vec_index += 1; 2269 padding_left -= page_of_nops.len; 2270 } 2271 if (padding_left > 0) { 2272 vecs[vec_index] = .{ 2273 .base = &page_of_nops, 2274 .len = padding_left, 2275 }; 2276 vec_index += 1; 2277 } 2278 } 2279 2280 if (trailing_zero) { 2281 var zbuf = [1]u8{0}; 2282 vecs[vec_index] = .{ 2283 .base = &zbuf, 2284 .len = zbuf.len, 2285 }; 2286 vec_index += 1; 2287 } 2288 2289 try file.pwritevAll(vecs[0..vec_index], offset - prev_padding_size); 2290 } 2291 2292 fn writeDbgInfoNopsToArrayList( 2293 gpa: Allocator, 2294 buffer: *std.ArrayListUnmanaged(u8), 2295 offset: u32, 2296 prev_padding_size: usize, 2297 content: []const u8, 2298 next_padding_size: usize, 2299 trailing_zero: bool, 2300 ) Allocator.Error!void { 2301 try buffer.resize(gpa, @max( 2302 buffer.items.len, 2303 offset + content.len + next_padding_size + 1, 2304 )); 2305 @memset(buffer.items[offset - prev_padding_size .. offset], @intFromEnum(AbbrevCode.padding)); 2306 @memcpy(buffer.items[offset..][0..content.len], content); 2307 @memset(buffer.items[offset + content.len ..][0..next_padding_size], @intFromEnum(AbbrevCode.padding)); 2308 2309 if (trailing_zero) { 2310 buffer.items[offset + content.len + next_padding_size] = 0; 2311 } 2312 } 2313 2314 pub fn writeDbgAranges(self: *Dwarf, addr: u64, size: u64) !void { 2315 const comp = self.bin_file.comp; 2316 const target = comp.root_mod.resolved_target.result; 2317 const target_endian = target.cpu.arch.endian(); 2318 const ptr_width_bytes = self.ptrWidthBytes(); 2319 2320 // Enough for all the data without resizing. When support for more compilation units 2321 // is added, the size of this section will become more variable. 2322 var di_buf = try std.ArrayList(u8).initCapacity(self.allocator, 100); 2323 defer di_buf.deinit(); 2324 2325 // initial length - length of the .debug_aranges contribution for this compilation unit, 2326 // not including the initial length itself. 2327 // We have to come back and write it later after we know the size. 2328 if (self.format == .dwarf64) di_buf.appendNTimesAssumeCapacity(0xff, 4); 2329 const init_len_index = di_buf.items.len; 2330 self.writeOffsetAssumeCapacity(&di_buf, 0); 2331 const after_init_len = di_buf.items.len; 2332 mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 2, target_endian); // version 2333 2334 // When more than one compilation unit is supported, this will be the offset to it. 2335 // For now it is always at offset 0 in .debug_info. 2336 self.writeOffsetAssumeCapacity(&di_buf, 0); // .debug_info offset 2337 di_buf.appendAssumeCapacity(ptr_width_bytes); // address_size 2338 di_buf.appendAssumeCapacity(0); // segment_selector_size 2339 2340 const end_header_offset = di_buf.items.len; 2341 const begin_entries_offset = mem.alignForward(usize, end_header_offset, ptr_width_bytes * 2); 2342 di_buf.appendNTimesAssumeCapacity(0, begin_entries_offset - end_header_offset); 2343 2344 // Currently only one compilation unit is supported, so the address range is simply 2345 // identical to the main program header virtual address and memory size. 2346 self.writeAddrAssumeCapacity(&di_buf, addr); 2347 self.writeAddrAssumeCapacity(&di_buf, size); 2348 2349 // Sentinel. 2350 self.writeAddrAssumeCapacity(&di_buf, 0); 2351 self.writeAddrAssumeCapacity(&di_buf, 0); 2352 2353 // Go back and populate the initial length. 2354 const init_len = di_buf.items.len - after_init_len; 2355 switch (self.format) { 2356 .dwarf32 => mem.writeInt(u32, di_buf.items[init_len_index..][0..4], @intCast(init_len), target_endian), 2357 .dwarf64 => mem.writeInt(u64, di_buf.items[init_len_index..][0..8], init_len, target_endian), 2358 } 2359 2360 const needed_size: u32 = @intCast(di_buf.items.len); 2361 switch (self.bin_file.tag) { 2362 .elf => { 2363 const elf_file = self.bin_file.cast(File.Elf).?; 2364 const shdr_index = elf_file.debug_aranges_section_index.?; 2365 try elf_file.growNonAllocSection(shdr_index, needed_size, 16, false); 2366 const debug_aranges_sect = &elf_file.shdrs.items[shdr_index]; 2367 const file_pos = debug_aranges_sect.sh_offset; 2368 try elf_file.base.file.?.pwriteAll(di_buf.items, file_pos); 2369 }, 2370 .macho => { 2371 const macho_file = self.bin_file.cast(File.MachO).?; 2372 if (macho_file.base.isRelocatable()) { 2373 const sect_index = macho_file.debug_aranges_sect_index.?; 2374 try macho_file.growSection(sect_index, needed_size); 2375 const sect = macho_file.sections.items(.header)[sect_index]; 2376 const file_pos = sect.offset; 2377 try macho_file.base.file.?.pwriteAll(di_buf.items, file_pos); 2378 } else { 2379 const d_sym = macho_file.getDebugSymbols().?; 2380 const sect_index = d_sym.debug_aranges_section_index.?; 2381 try d_sym.growSection(sect_index, needed_size, false, macho_file); 2382 const sect = d_sym.getSection(sect_index); 2383 const file_pos = sect.offset; 2384 try d_sym.file.pwriteAll(di_buf.items, file_pos); 2385 } 2386 }, 2387 .wasm => { 2388 // const wasm_file = self.bin_file.cast(File.Wasm).?; 2389 // const debug_ranges = &wasm_file.getAtomPtr(wasm_file.debug_ranges_atom.?).code; 2390 // try debug_ranges.resize(gpa, needed_size); 2391 // @memcpy(debug_ranges.items[0..di_buf.items.len], di_buf.items); 2392 }, 2393 else => unreachable, 2394 } 2395 } 2396 2397 pub fn writeDbgLineHeader(self: *Dwarf) !void { 2398 const comp = self.bin_file.comp; 2399 const gpa = self.allocator; 2400 const target = comp.root_mod.resolved_target.result; 2401 const target_endian = target.cpu.arch.endian(); 2402 const init_len_size: usize = switch (self.format) { 2403 .dwarf32 => 4, 2404 .dwarf64 => 12, 2405 }; 2406 2407 const dbg_line_prg_off = self.getDebugLineProgramOff() orelse return; 2408 assert(self.getDebugLineProgramEnd().? != 0); 2409 2410 // Convert all input DI files into a set of include dirs and file names. 2411 var arena = std.heap.ArenaAllocator.init(gpa); 2412 defer arena.deinit(); 2413 const paths = try self.genIncludeDirsAndFileNames(arena.allocator()); 2414 2415 // The size of this header is variable, depending on the number of directories, 2416 // files, and padding. We have a function to compute the upper bound size, however, 2417 // because it's needed for determining where to put the offset of the first `SrcFn`. 2418 const needed_bytes = self.dbgLineNeededHeaderBytes(paths.dirs, paths.files); 2419 var di_buf = try std.ArrayList(u8).initCapacity(gpa, needed_bytes); 2420 defer di_buf.deinit(); 2421 2422 if (self.format == .dwarf64) di_buf.appendNTimesAssumeCapacity(0xff, 4); 2423 self.writeOffsetAssumeCapacity(&di_buf, 0); 2424 2425 mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 4, target_endian); // version 2426 2427 // Empirically, debug info consumers do not respect this field, or otherwise 2428 // consider it to be an error when it does not point exactly to the end of the header. 2429 // Therefore we rely on the NOP jump at the beginning of the Line Number Program for 2430 // padding rather than this field. 2431 const before_header_len = di_buf.items.len; 2432 self.writeOffsetAssumeCapacity(&di_buf, 0); // We will come back and write this. 2433 const after_header_len = di_buf.items.len; 2434 2435 assert(self.dbg_line_header.opcode_base == DW.LNS.set_isa + 1); 2436 di_buf.appendSliceAssumeCapacity(&[_]u8{ 2437 self.dbg_line_header.minimum_instruction_length, 2438 self.dbg_line_header.maximum_operations_per_instruction, 2439 @intFromBool(self.dbg_line_header.default_is_stmt), 2440 @bitCast(self.dbg_line_header.line_base), 2441 self.dbg_line_header.line_range, 2442 self.dbg_line_header.opcode_base, 2443 2444 // Standard opcode lengths. The number of items here is based on `opcode_base`. 2445 // The value is the number of LEB128 operands the instruction takes. 2446 0, // `DW.LNS.copy` 2447 1, // `DW.LNS.advance_pc` 2448 1, // `DW.LNS.advance_line` 2449 1, // `DW.LNS.set_file` 2450 1, // `DW.LNS.set_column` 2451 0, // `DW.LNS.negate_stmt` 2452 0, // `DW.LNS.set_basic_block` 2453 0, // `DW.LNS.const_add_pc` 2454 1, // `DW.LNS.fixed_advance_pc` 2455 0, // `DW.LNS.set_prologue_end` 2456 0, // `DW.LNS.set_epilogue_begin` 2457 1, // `DW.LNS.set_isa` 2458 }); 2459 2460 for (paths.dirs, 0..) |dir, i| { 2461 log.debug("adding new include dir at {d} of '{s}'", .{ i + 1, dir }); 2462 di_buf.appendSliceAssumeCapacity(dir); 2463 di_buf.appendAssumeCapacity(0); 2464 } 2465 di_buf.appendAssumeCapacity(0); // include directories sentinel 2466 2467 for (paths.files, 0..) |file, i| { 2468 const dir_index = paths.files_dirs_indexes[i]; 2469 log.debug("adding new file name at {d} of '{s}' referencing directory {d}", .{ 2470 i + 1, 2471 file, 2472 dir_index + 1, 2473 }); 2474 di_buf.appendSliceAssumeCapacity(file); 2475 di_buf.appendSliceAssumeCapacity(&[_]u8{ 2476 0, // null byte for the relative path name 2477 @intCast(dir_index), // directory_index 2478 0, // mtime (TODO supply this) 2479 0, // file size bytes (TODO supply this) 2480 }); 2481 } 2482 di_buf.appendAssumeCapacity(0); // file names sentinel 2483 2484 const header_len = di_buf.items.len - after_header_len; 2485 switch (self.format) { 2486 .dwarf32 => mem.writeInt(u32, di_buf.items[before_header_len..][0..4], @intCast(header_len), target_endian), 2487 .dwarf64 => mem.writeInt(u64, di_buf.items[before_header_len..][0..8], header_len, target_endian), 2488 } 2489 2490 assert(needed_bytes == di_buf.items.len); 2491 2492 if (di_buf.items.len > dbg_line_prg_off) { 2493 const needed_with_padding = padToIdeal(needed_bytes); 2494 const delta = needed_with_padding - dbg_line_prg_off; 2495 2496 const first_fn_index = self.src_fn_first_index.?; 2497 const first_fn = self.getAtom(.src_fn, first_fn_index); 2498 const last_fn_index = self.src_fn_last_index.?; 2499 const last_fn = self.getAtom(.src_fn, last_fn_index); 2500 2501 var src_fn_index = first_fn_index; 2502 2503 var buffer = try gpa.alloc(u8, last_fn.off + last_fn.len - first_fn.off); 2504 defer gpa.free(buffer); 2505 2506 switch (self.bin_file.tag) { 2507 .elf => { 2508 const elf_file = self.bin_file.cast(File.Elf).?; 2509 const shdr_index = elf_file.debug_line_section_index.?; 2510 const needed_size = elf_file.shdrs.items[shdr_index].sh_size + delta; 2511 try elf_file.growNonAllocSection(shdr_index, needed_size, 1, true); 2512 const file_pos = elf_file.shdrs.items[shdr_index].sh_offset + first_fn.off; 2513 2514 const amt = try elf_file.base.file.?.preadAll(buffer, file_pos); 2515 if (amt != buffer.len) return error.InputOutput; 2516 2517 try elf_file.base.file.?.pwriteAll(buffer, file_pos + delta); 2518 }, 2519 .macho => { 2520 const macho_file = self.bin_file.cast(File.MachO).?; 2521 if (macho_file.base.isRelocatable()) { 2522 const sect_index = macho_file.debug_line_sect_index.?; 2523 const needed_size: u32 = @intCast(macho_file.sections.items(.header)[sect_index].size + delta); 2524 try macho_file.growSection(sect_index, needed_size); 2525 const file_pos = macho_file.sections.items(.header)[sect_index].offset + first_fn.off; 2526 2527 const amt = try macho_file.base.file.?.preadAll(buffer, file_pos); 2528 if (amt != buffer.len) return error.InputOutput; 2529 2530 try macho_file.base.file.?.pwriteAll(buffer, file_pos + delta); 2531 } else { 2532 const d_sym = macho_file.getDebugSymbols().?; 2533 const sect_index = d_sym.debug_line_section_index.?; 2534 const needed_size: u32 = @intCast(d_sym.getSection(sect_index).size + delta); 2535 try d_sym.growSection(sect_index, needed_size, true, macho_file); 2536 const file_pos = d_sym.getSection(sect_index).offset + first_fn.off; 2537 2538 const amt = try d_sym.file.preadAll(buffer, file_pos); 2539 if (amt != buffer.len) return error.InputOutput; 2540 2541 try d_sym.file.pwriteAll(buffer, file_pos + delta); 2542 } 2543 }, 2544 .wasm => { 2545 _ = &buffer; 2546 // const wasm_file = self.bin_file.cast(File.Wasm).?; 2547 // const debug_line = &wasm_file.getAtomPtr(wasm_file.debug_line_atom.?).code; 2548 // { 2549 // const src = debug_line.items[first_fn.off..]; 2550 // @memcpy(buffer[0..src.len], src); 2551 // } 2552 // try debug_line.resize(self.allocator, debug_line.items.len + delta); 2553 // @memcpy(debug_line.items[first_fn.off + delta ..][0..buffer.len], buffer); 2554 }, 2555 else => unreachable, 2556 } 2557 2558 while (true) { 2559 const src_fn = self.getAtomPtr(.src_fn, src_fn_index); 2560 src_fn.off += delta; 2561 2562 if (src_fn.next_index) |next_index| { 2563 src_fn_index = next_index; 2564 } else break; 2565 } 2566 } 2567 2568 // Backpatch actual length of the debug line program 2569 const init_len = self.getDebugLineProgramEnd().? - init_len_size; 2570 switch (self.format) { 2571 .dwarf32 => { 2572 mem.writeInt(u32, di_buf.items[0..4], @intCast(init_len), target_endian); 2573 }, 2574 .dwarf64 => { 2575 mem.writeInt(u64, di_buf.items[4..][0..8], init_len, target_endian); 2576 }, 2577 } 2578 2579 // We use NOPs because consumers empirically do not respect the header length field. 2580 const jmp_amt = self.getDebugLineProgramOff().? - di_buf.items.len; 2581 switch (self.bin_file.tag) { 2582 .elf => { 2583 const elf_file = self.bin_file.cast(File.Elf).?; 2584 const debug_line_sect = &elf_file.shdrs.items[elf_file.debug_line_section_index.?]; 2585 const file_pos = debug_line_sect.sh_offset; 2586 try pwriteDbgLineNops(elf_file.base.file.?, file_pos, 0, di_buf.items, jmp_amt); 2587 }, 2588 .macho => { 2589 const macho_file = self.bin_file.cast(File.MachO).?; 2590 if (macho_file.base.isRelocatable()) { 2591 const debug_line_sect = macho_file.sections.items(.header)[macho_file.debug_line_sect_index.?]; 2592 const file_pos = debug_line_sect.offset; 2593 try pwriteDbgLineNops(macho_file.base.file.?, file_pos, 0, di_buf.items, jmp_amt); 2594 } else { 2595 const d_sym = macho_file.getDebugSymbols().?; 2596 const debug_line_sect = d_sym.getSection(d_sym.debug_line_section_index.?); 2597 const file_pos = debug_line_sect.offset; 2598 try pwriteDbgLineNops(d_sym.file, file_pos, 0, di_buf.items, jmp_amt); 2599 } 2600 }, 2601 .wasm => { 2602 // const wasm_file = self.bin_file.cast(File.Wasm).?; 2603 // const debug_line = &wasm_file.getAtomPtr(wasm_file.debug_line_atom.?).code; 2604 // writeDbgLineNopsBuffered(debug_line.items, 0, 0, di_buf.items, jmp_amt); 2605 }, 2606 else => unreachable, 2607 } 2608 } 2609 2610 fn getDebugInfoOff(self: Dwarf) ?u32 { 2611 const first_index = self.di_atom_first_index orelse return null; 2612 const first = self.getAtom(.di_atom, first_index); 2613 return first.off; 2614 } 2615 2616 fn getDebugInfoEnd(self: Dwarf) ?u32 { 2617 const last_index = self.di_atom_last_index orelse return null; 2618 const last = self.getAtom(.di_atom, last_index); 2619 return last.off + last.len; 2620 } 2621 2622 fn getDebugLineProgramOff(self: Dwarf) ?u32 { 2623 const first_index = self.src_fn_first_index orelse return null; 2624 const first = self.getAtom(.src_fn, first_index); 2625 return first.off; 2626 } 2627 2628 fn getDebugLineProgramEnd(self: Dwarf) ?u32 { 2629 const last_index = self.src_fn_last_index orelse return null; 2630 const last = self.getAtom(.src_fn, last_index); 2631 return last.off + last.len; 2632 } 2633 2634 /// Always 4 or 8 depending on whether this is 32-bit or 64-bit format. 2635 fn ptrWidthBytes(self: Dwarf) u8 { 2636 return switch (self.ptr_width) { 2637 .p32 => 4, 2638 .p64 => 8, 2639 }; 2640 } 2641 2642 fn dbgLineNeededHeaderBytes(self: Dwarf, dirs: []const []const u8, files: []const []const u8) u32 { 2643 var size: usize = switch (self.format) { // length field 2644 .dwarf32 => 4, 2645 .dwarf64 => 12, 2646 }; 2647 size += @sizeOf(u16); // version field 2648 size += switch (self.format) { // offset to end-of-header 2649 .dwarf32 => 4, 2650 .dwarf64 => 8, 2651 }; 2652 size += 18; // opcodes 2653 2654 for (dirs) |dir| { // include dirs 2655 size += dir.len + 1; 2656 } 2657 size += 1; // include dirs sentinel 2658 2659 for (files) |file| { // file names 2660 size += file.len + 1 + 1 + 1 + 1; 2661 } 2662 size += 1; // file names sentinel 2663 2664 return @intCast(size); 2665 } 2666 2667 /// The reloc offset for the line offset of a function from the previous function's line. 2668 /// It's a fixed-size 4-byte ULEB128. 2669 fn getRelocDbgLineOff(self: Dwarf) usize { 2670 return dbg_line_vaddr_reloc_index + self.ptrWidthBytes() + 1; 2671 } 2672 2673 fn getRelocDbgFileIndex(self: Dwarf) usize { 2674 return self.getRelocDbgLineOff() + 5; 2675 } 2676 2677 fn getRelocDbgInfoSubprogramHighPC(self: Dwarf) u32 { 2678 return dbg_info_low_pc_reloc_index + self.ptrWidthBytes(); 2679 } 2680 2681 fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) { 2682 return actual_size +| (actual_size / ideal_factor); 2683 } 2684 2685 pub fn flushModule(self: *Dwarf, module: *Module) !void { 2686 const comp = self.bin_file.comp; 2687 const target = comp.root_mod.resolved_target.result; 2688 2689 if (self.global_abbrev_relocs.items.len > 0) { 2690 const gpa = self.allocator; 2691 var arena_alloc = std.heap.ArenaAllocator.init(gpa); 2692 defer arena_alloc.deinit(); 2693 const arena = arena_alloc.allocator(); 2694 2695 var dbg_info_buffer = std.ArrayList(u8).init(arena); 2696 try addDbgInfoErrorSetNames( 2697 module, 2698 Type.anyerror, 2699 module.global_error_set.keys(), 2700 target, 2701 &dbg_info_buffer, 2702 ); 2703 2704 const di_atom_index = try self.createAtom(.di_atom); 2705 log.debug("updateDeclDebugInfoAllocation in flushModule", .{}); 2706 try self.updateDeclDebugInfoAllocation(di_atom_index, @intCast(dbg_info_buffer.items.len)); 2707 log.debug("writeDeclDebugInfo in flushModule", .{}); 2708 try self.writeDeclDebugInfo(di_atom_index, dbg_info_buffer.items); 2709 2710 const file_pos = switch (self.bin_file.tag) { 2711 .elf => pos: { 2712 const elf_file = self.bin_file.cast(File.Elf).?; 2713 const debug_info_sect = &elf_file.shdrs.items[elf_file.debug_info_section_index.?]; 2714 break :pos debug_info_sect.sh_offset; 2715 }, 2716 .macho => pos: { 2717 const macho_file = self.bin_file.cast(File.MachO).?; 2718 if (macho_file.base.isRelocatable()) { 2719 const debug_info_sect = &macho_file.sections.items(.header)[macho_file.debug_info_sect_index.?]; 2720 break :pos debug_info_sect.offset; 2721 } else { 2722 const d_sym = macho_file.getDebugSymbols().?; 2723 const debug_info_sect = d_sym.getSectionPtr(d_sym.debug_info_section_index.?); 2724 break :pos debug_info_sect.offset; 2725 } 2726 }, 2727 // for wasm, the offset is always 0 as we write to memory first 2728 .wasm => 0, 2729 else => unreachable, 2730 }; 2731 2732 var buf: [@sizeOf(u32)]u8 = undefined; 2733 mem.writeInt(u32, &buf, self.getAtom(.di_atom, di_atom_index).off, target.cpu.arch.endian()); 2734 2735 while (self.global_abbrev_relocs.popOrNull()) |reloc| { 2736 const atom = self.getAtom(.di_atom, reloc.atom_index); 2737 switch (self.bin_file.tag) { 2738 .elf => { 2739 const elf_file = self.bin_file.cast(File.Elf).?; 2740 try elf_file.base.file.?.pwriteAll(&buf, file_pos + atom.off + reloc.offset); 2741 }, 2742 .macho => { 2743 const macho_file = self.bin_file.cast(File.MachO).?; 2744 if (macho_file.base.isRelocatable()) { 2745 try macho_file.base.file.?.pwriteAll(&buf, file_pos + atom.off + reloc.offset); 2746 } else { 2747 const d_sym = macho_file.getDebugSymbols().?; 2748 try d_sym.file.pwriteAll(&buf, file_pos + atom.off + reloc.offset); 2749 } 2750 }, 2751 .wasm => { 2752 // const wasm_file = self.bin_file.cast(File.Wasm).?; 2753 // const debug_info = wasm_file.getAtomPtr(wasm_file.debug_info_atom.?).code; 2754 // debug_info.items[atom.off + reloc.offset ..][0..buf.len].* = buf; 2755 }, 2756 else => unreachable, 2757 } 2758 } 2759 } 2760 } 2761 2762 fn addDIFile(self: *Dwarf, mod: *Module, decl_index: InternPool.DeclIndex) !u28 { 2763 const decl = mod.declPtr(decl_index); 2764 const file_scope = decl.getFileScope(mod); 2765 const gop = try self.di_files.getOrPut(self.allocator, file_scope); 2766 if (!gop.found_existing) { 2767 switch (self.bin_file.tag) { 2768 .elf => { 2769 const elf_file = self.bin_file.cast(File.Elf).?; 2770 elf_file.markDirty(elf_file.debug_line_section_index.?); 2771 }, 2772 .macho => { 2773 const macho_file = self.bin_file.cast(File.MachO).?; 2774 if (macho_file.base.isRelocatable()) { 2775 macho_file.markDirty(macho_file.debug_line_sect_index.?); 2776 } else { 2777 const d_sym = macho_file.getDebugSymbols().?; 2778 d_sym.markDirty(d_sym.debug_line_section_index.?, macho_file); 2779 } 2780 }, 2781 .wasm => {}, 2782 else => unreachable, 2783 } 2784 } 2785 return @intCast(gop.index + 1); 2786 } 2787 2788 fn genIncludeDirsAndFileNames(self: *Dwarf, arena: Allocator) !struct { 2789 dirs: []const []const u8, 2790 files: []const []const u8, 2791 files_dirs_indexes: []u28, 2792 } { 2793 var dirs = std.StringArrayHashMap(void).init(arena); 2794 try dirs.ensureTotalCapacity(self.di_files.count()); 2795 2796 var files = std.ArrayList([]const u8).init(arena); 2797 try files.ensureTotalCapacityPrecise(self.di_files.count()); 2798 2799 var files_dir_indexes = std.ArrayList(u28).init(arena); 2800 try files_dir_indexes.ensureTotalCapacity(self.di_files.count()); 2801 2802 for (self.di_files.keys()) |dif| { 2803 const full_path = try dif.mod.root.joinString(arena, dif.sub_file_path); 2804 const dir_path = std.fs.path.dirname(full_path) orelse "."; 2805 const sub_file_path = std.fs.path.basename(full_path); 2806 // https://github.com/ziglang/zig/issues/19353 2807 var buffer: [std.fs.max_path_bytes]u8 = undefined; 2808 const resolved = if (!std.fs.path.isAbsolute(dir_path)) 2809 std.posix.realpath(dir_path, &buffer) catch dir_path 2810 else 2811 dir_path; 2812 2813 const dir_index: u28 = index: { 2814 const dirs_gop = dirs.getOrPutAssumeCapacity(try arena.dupe(u8, resolved)); 2815 break :index @intCast(dirs_gop.index + 1); 2816 }; 2817 2818 files_dir_indexes.appendAssumeCapacity(dir_index); 2819 files.appendAssumeCapacity(sub_file_path); 2820 } 2821 2822 return .{ 2823 .dirs = dirs.keys(), 2824 .files = files.items, 2825 .files_dirs_indexes = files_dir_indexes.items, 2826 }; 2827 } 2828 2829 fn addDbgInfoErrorSet( 2830 mod: *Module, 2831 ty: Type, 2832 target: std.Target, 2833 dbg_info_buffer: *std.ArrayList(u8), 2834 ) !void { 2835 return addDbgInfoErrorSetNames(mod, ty, ty.errorSetNames(mod).get(&mod.intern_pool), target, dbg_info_buffer); 2836 } 2837 2838 fn addDbgInfoErrorSetNames( 2839 mod: *Module, 2840 /// Used for printing the type name only. 2841 ty: Type, 2842 error_names: []const InternPool.NullTerminatedString, 2843 target: std.Target, 2844 dbg_info_buffer: *std.ArrayList(u8), 2845 ) !void { 2846 const target_endian = target.cpu.arch.endian(); 2847 2848 // DW.AT.enumeration_type 2849 try dbg_info_buffer.append(@intFromEnum(AbbrevCode.enum_type)); 2850 // DW.AT.byte_size, DW.FORM.udata 2851 const abi_size = Type.anyerror.abiSize(mod); 2852 try leb128.writeUleb128(dbg_info_buffer.writer(), abi_size); 2853 // DW.AT.name, DW.FORM.string 2854 try ty.print(dbg_info_buffer.writer(), mod); 2855 try dbg_info_buffer.append(0); 2856 2857 // DW.AT.enumerator 2858 const no_error = "(no error)"; 2859 try dbg_info_buffer.ensureUnusedCapacity(no_error.len + 2 + @sizeOf(u64)); 2860 dbg_info_buffer.appendAssumeCapacity(@intFromEnum(AbbrevCode.enum_variant)); 2861 // DW.AT.name, DW.FORM.string 2862 dbg_info_buffer.appendSliceAssumeCapacity(no_error); 2863 dbg_info_buffer.appendAssumeCapacity(0); 2864 // DW.AT.const_value, DW.FORM.data8 2865 mem.writeInt(u64, dbg_info_buffer.addManyAsArrayAssumeCapacity(8), 0, target_endian); 2866 2867 for (error_names) |error_name| { 2868 const int = try mod.getErrorValue(error_name); 2869 const error_name_slice = error_name.toSlice(&mod.intern_pool); 2870 // DW.AT.enumerator 2871 try dbg_info_buffer.ensureUnusedCapacity(error_name_slice.len + 2 + @sizeOf(u64)); 2872 dbg_info_buffer.appendAssumeCapacity(@intFromEnum(AbbrevCode.enum_variant)); 2873 // DW.AT.name, DW.FORM.string 2874 dbg_info_buffer.appendSliceAssumeCapacity(error_name_slice[0 .. error_name_slice.len + 1]); 2875 // DW.AT.const_value, DW.FORM.data8 2876 mem.writeInt(u64, dbg_info_buffer.addManyAsArrayAssumeCapacity(8), int, target_endian); 2877 } 2878 2879 // DW.AT.enumeration_type delimit children 2880 try dbg_info_buffer.append(0); 2881 } 2882 2883 const Kind = enum { src_fn, di_atom }; 2884 2885 fn createAtom(self: *Dwarf, comptime kind: Kind) !Atom.Index { 2886 const index = blk: { 2887 switch (kind) { 2888 .src_fn => { 2889 const index: Atom.Index = @intCast(self.src_fns.items.len); 2890 _ = try self.src_fns.addOne(self.allocator); 2891 break :blk index; 2892 }, 2893 .di_atom => { 2894 const index: Atom.Index = @intCast(self.di_atoms.items.len); 2895 _ = try self.di_atoms.addOne(self.allocator); 2896 break :blk index; 2897 }, 2898 } 2899 }; 2900 const atom = self.getAtomPtr(kind, index); 2901 atom.* = .{ 2902 .off = 0, 2903 .len = 0, 2904 .prev_index = null, 2905 .next_index = null, 2906 }; 2907 return index; 2908 } 2909 2910 fn getOrCreateAtomForDecl(self: *Dwarf, comptime kind: Kind, decl_index: InternPool.DeclIndex) !Atom.Index { 2911 switch (kind) { 2912 .src_fn => { 2913 const gop = try self.src_fn_decls.getOrPut(self.allocator, decl_index); 2914 if (!gop.found_existing) { 2915 gop.value_ptr.* = try self.createAtom(kind); 2916 } 2917 return gop.value_ptr.*; 2918 }, 2919 .di_atom => { 2920 const gop = try self.di_atom_decls.getOrPut(self.allocator, decl_index); 2921 if (!gop.found_existing) { 2922 gop.value_ptr.* = try self.createAtom(kind); 2923 } 2924 return gop.value_ptr.*; 2925 }, 2926 } 2927 } 2928 2929 fn getAtom(self: *const Dwarf, comptime kind: Kind, index: Atom.Index) Atom { 2930 return switch (kind) { 2931 .src_fn => self.src_fns.items[index], 2932 .di_atom => self.di_atoms.items[index], 2933 }; 2934 } 2935 2936 fn getAtomPtr(self: *Dwarf, comptime kind: Kind, index: Atom.Index) *Atom { 2937 return switch (kind) { 2938 .src_fn => &self.src_fns.items[index], 2939 .di_atom => &self.di_atoms.items[index], 2940 }; 2941 } 2942 2943 pub const Format = enum { 2944 dwarf32, 2945 dwarf64, 2946 }; 2947 2948 const Dwarf = @This(); 2949 2950 const std = @import("std"); 2951 const builtin = @import("builtin"); 2952 const assert = std.debug.assert; 2953 const fs = std.fs; 2954 const leb128 = std.leb; 2955 const log = std.log.scoped(.dwarf); 2956 const mem = std.mem; 2957 2958 const link = @import("../link.zig"); 2959 const trace = @import("../tracy.zig").trace; 2960 2961 const Allocator = mem.Allocator; 2962 const DW = std.dwarf; 2963 const File = link.File; 2964 const LinkBlock = File.LinkBlock; 2965 const LinkFn = File.LinkFn; 2966 const LinkerLoad = @import("../codegen.zig").LinkerLoad; 2967 const Zcu = @import("../Zcu.zig"); 2968 /// Deprecated. 2969 const Module = Zcu; 2970 const InternPool = @import("../InternPool.zig"); 2971 const StringTable = @import("StringTable.zig"); 2972 const Type = @import("../type.zig").Type; 2973 const Value = @import("../Value.zig");