| ... | @@ -67,6 +67,18 @@ code_section_index: ?u32 = null, | ... | @@ -67,6 +67,18 @@ code_section_index: ?u32 = null, |
| 67 | debug_info_index: ?u32 = null, | 67 | debug_info_index: ?u32 = null, |
| 68 | /// The index of the segment representing the custom '.debug_line' section. | 68 | /// The index of the segment representing the custom '.debug_line' section. |
| 69 | debug_line_index: ?u32 = null, | 69 | debug_line_index: ?u32 = null, |
| | 70 | /// The index of the segment representing the custom '.debug_loc' section. |
| | 71 | debug_loc_index: ?u32 = null, |
| | 72 | /// The index of the segment representing the custom '.debug_ranges' section. |
| | 73 | debug_ranges_index: ?u32 = null, |
| | 74 | /// The index of the segment representing the custom '.debug_pubnames' section. |
| | 75 | debug_pubnames_index: ?u32 = null, |
| | 76 | /// The index of the segment representing the custom '.debug_pubtypes' section. |
| | 77 | debug_pubtypes_index: ?u32 = null, |
| | 78 | /// The index of the segment representing the custom '.debug_pubtypes' section. |
| | 79 | debug_str_index: ?u32 = null, |
| | 80 | /// The index of the segment representing the custom '.debug_pubtypes' section. |
| | 81 | debug_abbrev_index: ?u32 = null, |
| 70 | /// The count of imported functions. This number will be appended | 82 | /// The count of imported functions. This number will be appended |
| 71 | /// to the function indexes as their index starts at the lowest non-extern function. | 83 | /// to the function indexes as their index starts at the lowest non-extern function. |
| 72 | imported_functions_count: u32 = 0, | 84 | imported_functions_count: u32 = 0, |
| ... | @@ -83,24 +95,15 @@ imports: std.AutoHashMapUnmanaged(SymbolLoc, types.Import) = .{}, | ... | @@ -83,24 +95,15 @@ imports: std.AutoHashMapUnmanaged(SymbolLoc, types.Import) = .{}, |
| 83 | segments: std.ArrayListUnmanaged(Segment) = .{}, | 95 | segments: std.ArrayListUnmanaged(Segment) = .{}, |
| 84 | /// Maps a data segment key (such as .rodata) to the index into `segments`. | 96 | /// Maps a data segment key (such as .rodata) to the index into `segments`. |
| 85 | data_segments: std.StringArrayHashMapUnmanaged(u32) = .{}, | 97 | data_segments: std.StringArrayHashMapUnmanaged(u32) = .{}, |
| 86 | /// A list of `types.Segment` which provide meta data | 98 | /// A table of `types.Segment` which provide meta data |
| 87 | /// about a data symbol such as its name | 99 | /// about a data symbol such as its name where the key is |
| 88 | segment_info: std.ArrayListUnmanaged(types.Segment) = .{}, | 100 | /// the segment index, which can be found from `data_segments` |
| | 101 | segment_info: std.AutoArrayHashMapUnmanaged(u32, types.Segment) = .{}, |
| 89 | /// Deduplicated string table for strings used by symbols, imports and exports. | 102 | /// Deduplicated string table for strings used by symbols, imports and exports. |
| 90 | string_table: StringTable = .{}, | 103 | string_table: StringTable = .{}, |
| 91 | /// Debug information for wasm | 104 | /// Debug information for wasm |
| 92 | dwarf: ?Dwarf = null, | 105 | dwarf: ?Dwarf = null, |
| 93 | | 106 | |
| 94 | // *debug information* // | | |
| 95 | /// Contains all bytes for the '.debug_info' section | | |
| 96 | debug_info: std.ArrayListUnmanaged(u8) = .{}, | | |
| 97 | /// Contains all bytes for the '.debug_line' section | | |
| 98 | debug_line: std.ArrayListUnmanaged(u8) = .{}, | | |
| 99 | /// Contains all bytes for the '.debug_abbrev' section | | |
| 100 | debug_abbrev: std.ArrayListUnmanaged(u8) = .{}, | | |
| 101 | /// Contains all bytes for the '.debug_ranges' section | | |
| 102 | debug_aranges: std.ArrayListUnmanaged(u8) = .{}, | | |
| 103 | | | |
| 104 | // Output sections | 107 | // Output sections |
| 105 | /// Output type section | 108 | /// Output type section |
| 106 | func_types: std.ArrayListUnmanaged(wasm.Type) = .{}, | 109 | func_types: std.ArrayListUnmanaged(wasm.Type) = .{}, |
| ... | @@ -156,6 +159,19 @@ export_names: std.AutoHashMapUnmanaged(SymbolLoc, u32) = .{}, | ... | @@ -156,6 +159,19 @@ export_names: std.AutoHashMapUnmanaged(SymbolLoc, u32) = .{}, |
| 156 | /// The actual table is populated during `flush`. | 159 | /// The actual table is populated during `flush`. |
| 157 | error_table_symbol: ?u32 = null, | 160 | error_table_symbol: ?u32 = null, |
| 158 | | 161 | |
| | 162 | // Debug section atoms. These are only set when the current compilation |
| | 163 | // unit contains Zig code. The lifetime of these atoms are extended |
| | 164 | // until the end of the compiler's lifetime. Meaning they're not freed |
| | 165 | // during `flush()` in incremental-mode. |
| | 166 | debug_info_atom: ?*Atom = null, |
| | 167 | debug_line_atom: ?*Atom = null, |
| | 168 | debug_loc_atom: ?*Atom = null, |
| | 169 | debug_ranges_atom: ?*Atom = null, |
| | 170 | debug_abbrev_atom: ?*Atom = null, |
| | 171 | debug_str_atom: ?*Atom = null, |
| | 172 | debug_pubnames_atom: ?*Atom = null, |
| | 173 | debug_pubtypes_atom: ?*Atom = null, |
| | 174 | |
| 159 | pub const Segment = struct { | 175 | pub const Segment = struct { |
| 160 | alignment: u32, | 176 | alignment: u32, |
| 161 | size: u32, | 177 | size: u32, |
| ... | @@ -209,6 +225,18 @@ pub const SymbolLoc = struct { | ... | @@ -209,6 +225,18 @@ pub const SymbolLoc = struct { |
| 209 | } | 225 | } |
| 210 | return wasm_bin.string_table.get(wasm_bin.symbols.items[self.index].name); | 226 | return wasm_bin.string_table.get(wasm_bin.symbols.items[self.index].name); |
| 211 | } | 227 | } |
| | 228 | |
| | 229 | /// From a given symbol location, returns the final location. |
| | 230 | /// e.g. when a symbol was resolved and replaced by the symbol |
| | 231 | /// in a different file, this will return said location. |
| | 232 | /// If the symbol wasn't replaced by another, this will return |
| | 233 | /// the given location itself. |
| | 234 | pub fn finalLoc(self: SymbolLoc, wasm_bin: *const Wasm) SymbolLoc { |
| | 235 | if (wasm_bin.discarded.get(self)) |new_loc| { |
| | 236 | return new_loc.finalLoc(wasm_bin); |
| | 237 | } |
| | 238 | return self; |
| | 239 | } |
| 212 | }; | 240 | }; |
| 213 | | 241 | |
| 214 | /// Generic string table that duplicates strings | 242 | /// Generic string table that duplicates strings |
| ... | @@ -335,6 +363,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option | ... | @@ -335,6 +363,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option |
| 335 | }; | 363 | }; |
| 336 | } | 364 | } |
| 337 | | 365 | |
| | 366 | try wasm_bin.initDebugSections(); |
| 338 | return wasm_bin; | 367 | return wasm_bin; |
| 339 | } | 368 | } |
| 340 | | 369 | |
| ... | @@ -363,6 +392,24 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Wasm { | ... | @@ -363,6 +392,24 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Wasm { |
| 363 | return self; | 392 | return self; |
| 364 | } | 393 | } |
| 365 | | 394 | |
| | 395 | /// Initializes symbols and atoms for the debug sections |
| | 396 | /// Initialization is only done when compiling Zig code. |
| | 397 | /// When Zig is invoked as a linker instead, the atoms |
| | 398 | /// and symbols come from the object files instead. |
| | 399 | pub fn initDebugSections(self: *Wasm) !void { |
| | 400 | if (self.dwarf == null) return; // not compiling Zig code, so no need to pre-initialize debug sections |
| | 401 | assert(self.debug_info_index == null); |
| | 402 | // this will create an Atom and set the index for us. |
| | 403 | self.debug_info_atom = try self.createDebugSectionForIndex(&self.debug_info_index, ".debug_info"); |
| | 404 | self.debug_line_atom = try self.createDebugSectionForIndex(&self.debug_line_index, ".debug_line"); |
| | 405 | self.debug_loc_atom = try self.createDebugSectionForIndex(&self.debug_loc_index, ".debug_loc"); |
| | 406 | self.debug_abbrev_atom = try self.createDebugSectionForIndex(&self.debug_abbrev_index, ".debug_abbrev"); |
| | 407 | self.debug_ranges_atom = try self.createDebugSectionForIndex(&self.debug_ranges_index, ".debug_ranges"); |
| | 408 | self.debug_str_atom = try self.createDebugSectionForIndex(&self.debug_str_index, ".debug_str"); |
| | 409 | self.debug_pubnames_atom = try self.createDebugSectionForIndex(&self.debug_pubnames_index, ".debug_pubnames"); |
| | 410 | self.debug_pubtypes_atom = try self.createDebugSectionForIndex(&self.debug_pubtypes_index, ".debug_pubtypes"); |
| | 411 | } |
| | 412 | |
| 366 | fn parseInputFiles(self: *Wasm, files: []const []const u8) !void { | 413 | fn parseInputFiles(self: *Wasm, files: []const []const u8) !void { |
| 367 | for (files) |path| { | 414 | for (files) |path| { |
| 368 | if (try self.parseObjectFile(path)) continue; | 415 | if (try self.parseObjectFile(path)) continue; |
| ... | @@ -644,7 +691,7 @@ pub fn deinit(self: *Wasm) void { | ... | @@ -644,7 +691,7 @@ pub fn deinit(self: *Wasm) void { |
| 644 | for (self.func_types.items) |*func_type| { | 691 | for (self.func_types.items) |*func_type| { |
| 645 | func_type.deinit(gpa); | 692 | func_type.deinit(gpa); |
| 646 | } | 693 | } |
| 647 | for (self.segment_info.items) |segment_info| { | 694 | for (self.segment_info.values()) |segment_info| { |
| 648 | gpa.free(segment_info.name); | 695 | gpa.free(segment_info.name); |
| 649 | } | 696 | } |
| 650 | for (self.objects.items) |*object| { | 697 | for (self.objects.items) |*object| { |
| ... | @@ -692,11 +739,6 @@ pub fn deinit(self: *Wasm) void { | ... | @@ -692,11 +739,6 @@ pub fn deinit(self: *Wasm) void { |
| 692 | if (self.dwarf) |*dwarf| { | 739 | if (self.dwarf) |*dwarf| { |
| 693 | dwarf.deinit(); | 740 | dwarf.deinit(); |
| 694 | } | 741 | } |
| 695 | | | |
| 696 | self.debug_info.deinit(gpa); | | |
| 697 | self.debug_line.deinit(gpa); | | |
| 698 | self.debug_abbrev.deinit(gpa); | | |
| 699 | self.debug_aranges.deinit(gpa); | | |
| 700 | } | 742 | } |
| 701 | | 743 | |
| 702 | pub fn allocateDeclIndexes(self: *Wasm, decl_index: Module.Decl.Index) !void { | 744 | pub fn allocateDeclIndexes(self: *Wasm, decl_index: Module.Decl.Index) !void { |
| ... | @@ -1337,16 +1379,7 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void { | ... | @@ -1337,16 +1379,7 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void { |
| 1337 | const index = gop.value_ptr.*; | 1379 | const index = gop.value_ptr.*; |
| 1338 | self.segments.items[index].size += atom.size; | 1380 | self.segments.items[index].size += atom.size; |
| 1339 | | 1381 | |
| 1340 | // segment indexes can be off by 1 due to also containing a segment | 1382 | symbol.index = @intCast(u32, self.segment_info.getIndex(index).?); |
| 1341 | // for the code section, so we must check if the existing segment | | |
| 1342 | // is larger than that of the code section, and substract the index by 1 in such case. | | |
| 1343 | var info_add = if (self.code_section_index) |idx| blk: { | | |
| 1344 | if (idx < index) break :blk @as(u32, 1); | | |
| 1345 | break :blk 0; | | |
| 1346 | } else @as(u32, 0); | | |
| 1347 | if (self.debug_info_index != null) info_add += 1; | | |
| 1348 | if (self.debug_line_index != null) info_add += 1; | | |
| 1349 | symbol.index = index - info_add; | | |
| 1350 | // segment info already exists, so free its memory | 1383 | // segment info already exists, so free its memory |
| 1351 | self.base.allocator.free(segment_name); | 1384 | self.base.allocator.free(segment_name); |
| 1352 | break :result index; | 1385 | break :result index; |
| ... | @@ -1359,8 +1392,8 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void { | ... | @@ -1359,8 +1392,8 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void { |
| 1359 | }); | 1392 | }); |
| 1360 | gop.value_ptr.* = index; | 1393 | gop.value_ptr.* = index; |
| 1361 | | 1394 | |
| 1362 | const info_index = @intCast(u32, self.segment_info.items.len); | 1395 | const info_index = @intCast(u32, self.segment_info.count()); |
| 1363 | try self.segment_info.append(self.base.allocator, segment_info); | 1396 | try self.segment_info.put(self.base.allocator, index, segment_info); |
| 1364 | symbol.index = info_index; | 1397 | symbol.index = info_index; |
| 1365 | break :result index; | 1398 | break :result index; |
| 1366 | } | 1399 | } |
| ... | @@ -1370,18 +1403,54 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void { | ... | @@ -1370,18 +1403,54 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void { |
| 1370 | const segment: *Segment = &self.segments.items[final_index]; | 1403 | const segment: *Segment = &self.segments.items[final_index]; |
| 1371 | segment.alignment = std.math.max(segment.alignment, atom.alignment); | 1404 | segment.alignment = std.math.max(segment.alignment, atom.alignment); |
| 1372 | | 1405 | |
| 1373 | if (self.atoms.getPtr(final_index)) |last| { | 1406 | try self.appendAtomAtIndex(final_index, atom); |
| | 1407 | } |
| | 1408 | |
| | 1409 | /// From a given index, append the given `Atom` at the back of the linked list. |
| | 1410 | /// Simply inserts it into the map of atoms when it doesn't exist yet. |
| | 1411 | pub fn appendAtomAtIndex(self: *Wasm, index: u32, atom: *Atom) !void { |
| | 1412 | if (self.atoms.getPtr(index)) |last| { |
| 1374 | last.*.next = atom; | 1413 | last.*.next = atom; |
| 1375 | atom.prev = last.*; | 1414 | atom.prev = last.*; |
| 1376 | last.* = atom; | 1415 | last.* = atom; |
| 1377 | } else { | 1416 | } else { |
| 1378 | try self.atoms.putNoClobber(self.base.allocator, final_index, atom); | 1417 | try self.atoms.putNoClobber(self.base.allocator, index, atom); |
| 1379 | } | 1418 | } |
| 1380 | } | 1419 | } |
| 1381 | | 1420 | |
| | 1421 | /// Allocates debug atoms into their respective debug sections |
| | 1422 | /// to merge them with maybe-existing debug atoms from object files. |
| | 1423 | fn allocateDebugAtoms(self: *Wasm) !void { |
| | 1424 | if (self.dwarf == null) return; |
| | 1425 | |
| | 1426 | const allocAtom = struct { |
| | 1427 | fn f(bin: *Wasm, maybe_index: *?u32, atom: *Atom) !void { |
| | 1428 | const index = maybe_index.* orelse idx: { |
| | 1429 | const index = @intCast(u32, bin.segments.items.len); |
| | 1430 | try bin.appendDummySegment(); |
| | 1431 | maybe_index.* = index; |
| | 1432 | break :idx index; |
| | 1433 | }; |
| | 1434 | atom.size = @intCast(u32, atom.code.items.len); |
| | 1435 | bin.symbols.items[atom.sym_index].index = index; |
| | 1436 | try bin.appendAtomAtIndex(index, atom); |
| | 1437 | } |
| | 1438 | }.f; |
| | 1439 | |
| | 1440 | try allocAtom(self, &self.debug_info_index, self.debug_info_atom.?); |
| | 1441 | try allocAtom(self, &self.debug_line_index, self.debug_line_atom.?); |
| | 1442 | try allocAtom(self, &self.debug_loc_index, self.debug_loc_atom.?); |
| | 1443 | try allocAtom(self, &self.debug_str_index, self.debug_str_atom.?); |
| | 1444 | try allocAtom(self, &self.debug_ranges_index, self.debug_ranges_atom.?); |
| | 1445 | try allocAtom(self, &self.debug_abbrev_index, self.debug_abbrev_atom.?); |
| | 1446 | try allocAtom(self, &self.debug_pubnames_index, self.debug_pubnames_atom.?); |
| | 1447 | try allocAtom(self, &self.debug_pubtypes_index, self.debug_pubtypes_atom.?); |
| | 1448 | } |
| | 1449 | |
| 1382 | fn allocateAtoms(self: *Wasm) !void { | 1450 | fn allocateAtoms(self: *Wasm) !void { |
| 1383 | // first sort the data segments | 1451 | // first sort the data segments |
| 1384 | try sortDataSegments(self); | 1452 | try sortDataSegments(self); |
| | 1453 | try allocateDebugAtoms(self); |
| 1385 | | 1454 | |
| 1386 | var it = self.atoms.iterator(); | 1455 | var it = self.atoms.iterator(); |
| 1387 | while (it.next()) |entry| { | 1456 | while (it.next()) |entry| { |
| ... | @@ -1399,7 +1468,7 @@ fn allocateAtoms(self: *Wasm) !void { | ... | @@ -1399,7 +1468,7 @@ fn allocateAtoms(self: *Wasm) !void { |
| 1399 | atom.size, | 1468 | atom.size, |
| 1400 | }); | 1469 | }); |
| 1401 | offset += atom.size; | 1470 | offset += atom.size; |
| 1402 | self.symbol_atom.putAssumeCapacity(atom.symbolLoc(), atom); // Update atom pointers | 1471 | try self.symbol_atom.put(self.base.allocator, atom.symbolLoc(), atom); // Update atom pointers |
| 1403 | atom = atom.next orelse break; | 1472 | atom = atom.next orelse break; |
| 1404 | } | 1473 | } |
| 1405 | segment.size = std.mem.alignForwardGeneric(u32, offset, segment.alignment); | 1474 | segment.size = std.mem.alignForwardGeneric(u32, offset, segment.alignment); |
| ... | @@ -1753,7 +1822,7 @@ fn setupMemory(self: *Wasm) !void { | ... | @@ -1753,7 +1822,7 @@ fn setupMemory(self: *Wasm) !void { |
| 1753 | /// From a given object's index and the index of the segment, returns the corresponding | 1822 | /// From a given object's index and the index of the segment, returns the corresponding |
| 1754 | /// index of the segment within the final data section. When the segment does not yet | 1823 | /// index of the segment within the final data section. When the segment does not yet |
| 1755 | /// exist, a new one will be initialized and appended. The new index will be returned in that case. | 1824 | /// exist, a new one will be initialized and appended. The new index will be returned in that case. |
| 1756 | pub fn getMatchingSegment(self: *Wasm, object_index: u16, relocatable_index: u32) !u32 { | 1825 | pub fn getMatchingSegment(self: *Wasm, object_index: u16, relocatable_index: u32) !?u32 { |
| 1757 | const object: Object = self.objects.items[object_index]; | 1826 | const object: Object = self.objects.items[object_index]; |
| 1758 | const relocatable_data = object.relocatable_data[relocatable_index]; | 1827 | const relocatable_data = object.relocatable_data[relocatable_index]; |
| 1759 | const index = @intCast(u32, self.segments.items.len); | 1828 | const index = @intCast(u32, self.segments.items.len); |
| ... | @@ -1765,27 +1834,83 @@ pub fn getMatchingSegment(self: *Wasm, object_index: u16, relocatable_index: u32 | ... | @@ -1765,27 +1834,83 @@ pub fn getMatchingSegment(self: *Wasm, object_index: u16, relocatable_index: u32 |
| 1765 | const result = try self.data_segments.getOrPut(self.base.allocator, segment_info.outputName(merge_segment)); | 1834 | const result = try self.data_segments.getOrPut(self.base.allocator, segment_info.outputName(merge_segment)); |
| 1766 | if (!result.found_existing) { | 1835 | if (!result.found_existing) { |
| 1767 | result.value_ptr.* = index; | 1836 | result.value_ptr.* = index; |
| 1768 | try self.segments.append(self.base.allocator, .{ | 1837 | try self.appendDummySegment(); |
| 1769 | .alignment = 1, | | |
| 1770 | .size = 0, | | |
| 1771 | .offset = 0, | | |
| 1772 | }); | | |
| 1773 | return index; | 1838 | return index; |
| 1774 | } else return result.value_ptr.*; | 1839 | } else return result.value_ptr.*; |
| 1775 | }, | 1840 | }, |
| 1776 | .code => return self.code_section_index orelse blk: { | 1841 | .code => return self.code_section_index orelse blk: { |
| 1777 | self.code_section_index = index; | 1842 | self.code_section_index = index; |
| 1778 | try self.segments.append(self.base.allocator, .{ | 1843 | try self.appendDummySegment(); |
| 1779 | .alignment = 1, | | |
| 1780 | .size = 0, | | |
| 1781 | .offset = 0, | | |
| 1782 | }); | | |
| 1783 | break :blk index; | 1844 | break :blk index; |
| 1784 | }, | 1845 | }, |
| 1785 | .custom => return error.@"TODO: Custom section relocations for wasm", | 1846 | .debug => { |
| | 1847 | const debug_name = object.getDebugName(relocatable_data); |
| | 1848 | if (mem.eql(u8, debug_name, ".debug_info")) { |
| | 1849 | return self.debug_info_index orelse blk: { |
| | 1850 | self.debug_info_index = index; |
| | 1851 | try self.appendDummySegment(); |
| | 1852 | break :blk index; |
| | 1853 | }; |
| | 1854 | } else if (mem.eql(u8, debug_name, ".debug_line")) { |
| | 1855 | return self.debug_line_index orelse blk: { |
| | 1856 | self.debug_line_index = index; |
| | 1857 | try self.appendDummySegment(); |
| | 1858 | break :blk index; |
| | 1859 | }; |
| | 1860 | } else if (mem.eql(u8, debug_name, ".debug_loc")) { |
| | 1861 | return self.debug_loc_index orelse blk: { |
| | 1862 | self.debug_loc_index = index; |
| | 1863 | try self.appendDummySegment(); |
| | 1864 | break :blk index; |
| | 1865 | }; |
| | 1866 | } else if (mem.eql(u8, debug_name, ".debug_ranges")) { |
| | 1867 | return self.debug_line_index orelse blk: { |
| | 1868 | self.debug_ranges_index = index; |
| | 1869 | try self.appendDummySegment(); |
| | 1870 | break :blk index; |
| | 1871 | }; |
| | 1872 | } else if (mem.eql(u8, debug_name, ".debug_pubnames")) { |
| | 1873 | return self.debug_pubnames_index orelse blk: { |
| | 1874 | self.debug_pubnames_index = index; |
| | 1875 | try self.appendDummySegment(); |
| | 1876 | break :blk index; |
| | 1877 | }; |
| | 1878 | } else if (mem.eql(u8, debug_name, ".debug_pubtypes")) { |
| | 1879 | return self.debug_pubtypes_index orelse blk: { |
| | 1880 | self.debug_pubtypes_index = index; |
| | 1881 | try self.appendDummySegment(); |
| | 1882 | break :blk index; |
| | 1883 | }; |
| | 1884 | } else if (mem.eql(u8, debug_name, ".debug_abbrev")) { |
| | 1885 | return self.debug_abbrev_index orelse blk: { |
| | 1886 | self.debug_abbrev_index = index; |
| | 1887 | try self.appendDummySegment(); |
| | 1888 | break :blk index; |
| | 1889 | }; |
| | 1890 | } else if (mem.eql(u8, debug_name, ".debug_str")) { |
| | 1891 | return self.debug_str_index orelse blk: { |
| | 1892 | self.debug_str_index = index; |
| | 1893 | try self.appendDummySegment(); |
| | 1894 | break :blk index; |
| | 1895 | }; |
| | 1896 | } else { |
| | 1897 | log.warn("found unknown debug section '{s}'", .{debug_name}); |
| | 1898 | log.warn(" debug section will be skipped", .{}); |
| | 1899 | return null; |
| | 1900 | } |
| | 1901 | }, |
| 1786 | } | 1902 | } |
| 1787 | } | 1903 | } |
| 1788 | | 1904 | |
| | 1905 | /// Appends a new segment with default field values |
| | 1906 | fn appendDummySegment(self: *Wasm) !void { |
| | 1907 | try self.segments.append(self.base.allocator, .{ |
| | 1908 | .alignment = 1, |
| | 1909 | .size = 0, |
| | 1910 | .offset = 0, |
| | 1911 | }); |
| | 1912 | } |
| | 1913 | |
| 1789 | /// Returns the symbol index of the error name table. | 1914 | /// Returns the symbol index of the error name table. |
| 1790 | /// | 1915 | /// |
| 1791 | /// When the symbol does not yet exist, it will create a new one instead. | 1916 | /// When the symbol does not yet exist, it will create a new one instead. |
| ... | @@ -1903,50 +2028,52 @@ fn populateErrorNameTable(self: *Wasm) !void { | ... | @@ -1903,50 +2028,52 @@ fn populateErrorNameTable(self: *Wasm) !void { |
| 1903 | try self.parseAtom(names_atom, .{ .data = .read_only }); | 2028 | try self.parseAtom(names_atom, .{ .data = .read_only }); |
| 1904 | } | 2029 | } |
| 1905 | | 2030 | |
| 1906 | pub fn getDebugInfoIndex(self: *Wasm) !u32 { | 2031 | /// From a given index variable, creates a new debug section. |
| 1907 | assert(self.dwarf != null); | 2032 | /// This initializes the index, appends a new segment, |
| 1908 | return self.debug_info_index orelse { | 2033 | /// and finally, creates a managed `Atom`. |
| 1909 | self.debug_info_index = @intCast(u32, self.segments.items.len); | 2034 | pub fn createDebugSectionForIndex(self: *Wasm, index: *?u32, name: []const u8) !*Atom { |
| 1910 | const segment = try self.segments.addOne(self.base.allocator); | 2035 | const new_index = @intCast(u32, self.segments.items.len); |
| 1911 | segment.* = .{ | 2036 | index.* = new_index; |
| 1912 | .size = 0, | 2037 | try self.appendDummySegment(); |
| 1913 | .offset = 0, | 2038 | // _ = index; |
| 1914 | // debug sections always have alignment '1' | 2039 | |
| 1915 | .alignment = 1, | 2040 | const sym_index = self.symbols_free_list.popOrNull() orelse idx: { |
| 1916 | }; | 2041 | const tmp_index = @intCast(u32, self.symbols.items.len); |
| 1917 | return self.debug_info_index.?; | 2042 | _ = try self.symbols.addOne(self.base.allocator); |
| | 2043 | break :idx tmp_index; |
| 1918 | }; | 2044 | }; |
| 1919 | } | 2045 | self.symbols.items[sym_index] = .{ |
| 1920 | | 2046 | .tag = .section, |
| 1921 | pub fn getDebugLineIndex(self: *Wasm) !u32 { | 2047 | .name = try self.string_table.put(self.base.allocator, name), |
| 1922 | assert(self.dwarf != null); | 2048 | .index = 0, |
| 1923 | return self.debug_line_index orelse { | 2049 | .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL), |
| 1924 | self.debug_line_index = @intCast(u32, self.segments.items.len); | | |
| 1925 | const segment = try self.segments.addOne(self.base.allocator); | | |
| 1926 | segment.* = .{ | | |
| 1927 | .size = 0, | | |
| 1928 | .offset = 0, | | |
| 1929 | .alignment = 1, | | |
| 1930 | }; | | |
| 1931 | return self.debug_line_index.?; | | |
| 1932 | }; | 2050 | }; |
| | 2051 | |
| | 2052 | const atom = try self.base.allocator.create(Atom); |
| | 2053 | atom.* = Atom.empty; |
| | 2054 | atom.alignment = 1; // debug sections are always 1-byte-aligned |
| | 2055 | atom.sym_index = sym_index; |
| | 2056 | try self.managed_atoms.append(self.base.allocator, atom); |
| | 2057 | try self.symbol_atom.put(self.base.allocator, atom.symbolLoc(), atom); |
| | 2058 | return atom; |
| 1933 | } | 2059 | } |
| 1934 | | 2060 | |
| 1935 | fn resetState(self: *Wasm) void { | 2061 | fn resetState(self: *Wasm) void { |
| 1936 | for (self.segment_info.items) |*segment_info| { | 2062 | for (self.segment_info.values()) |segment_info| { |
| 1937 | self.base.allocator.free(segment_info.name); | 2063 | self.base.allocator.free(segment_info.name); |
| 1938 | } | 2064 | } |
| 1939 | const mod = self.base.options.module.?; | 2065 | if (self.base.options.module) |mod| { |
| 1940 | var decl_it = self.decls.keyIterator(); | 2066 | var decl_it = self.decls.keyIterator(); |
| 1941 | while (decl_it.next()) |decl_index_ptr| { | 2067 | while (decl_it.next()) |decl_index_ptr| { |
| 1942 | const decl = mod.declPtr(decl_index_ptr.*); | 2068 | const decl = mod.declPtr(decl_index_ptr.*); |
| 1943 | const atom = &decl.link.wasm; | 2069 | const atom = &decl.link.wasm; |
| 1944 | atom.next = null; | 2070 | atom.next = null; |
| 1945 | atom.prev = null; | 2071 | atom.prev = null; |
| 1946 | | 2072 | |
| 1947 | for (atom.locals.items) |*local_atom| { | 2073 | for (atom.locals.items) |*local_atom| { |
| 1948 | local_atom.next = null; | 2074 | local_atom.next = null; |
| 1949 | local_atom.prev = null; | 2075 | local_atom.prev = null; |
| | 2076 | } |
| 1950 | } | 2077 | } |
| 1951 | } | 2078 | } |
| 1952 | self.functions.clearRetainingCapacity(); | 2079 | self.functions.clearRetainingCapacity(); |
| ... | @@ -1959,6 +2086,12 @@ fn resetState(self: *Wasm) void { | ... | @@ -1959,6 +2086,12 @@ fn resetState(self: *Wasm) void { |
| 1959 | self.code_section_index = null; | 2086 | self.code_section_index = null; |
| 1960 | self.debug_info_index = null; | 2087 | self.debug_info_index = null; |
| 1961 | self.debug_line_index = null; | 2088 | self.debug_line_index = null; |
| | 2089 | self.debug_loc_index = null; |
| | 2090 | self.debug_str_index = null; |
| | 2091 | self.debug_ranges_index = null; |
| | 2092 | self.debug_abbrev_index = null; |
| | 2093 | self.debug_pubnames_index = null; |
| | 2094 | self.debug_pubtypes_index = null; |
| 1962 | } | 2095 | } |
| 1963 | | 2096 | |
| 1964 | pub fn flush(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !void { | 2097 | pub fn flush(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !void { |
| ... | @@ -2036,29 +2169,34 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod | ... | @@ -2036,29 +2169,34 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2036 | defer self.resetState(); | 2169 | defer self.resetState(); |
| 2037 | try self.setupStart(); | 2170 | try self.setupStart(); |
| 2038 | try self.setupImports(); | 2171 | try self.setupImports(); |
| 2039 | const mod = self.base.options.module.?; | 2172 | if (self.base.options.module) |mod| { |
| 2040 | var decl_it = self.decls.keyIterator(); | 2173 | var decl_it = self.decls.keyIterator(); |
| 2041 | while (decl_it.next()) |decl_index_ptr| { | 2174 | while (decl_it.next()) |decl_index_ptr| { |
| 2042 | const decl = mod.declPtr(decl_index_ptr.*); | 2175 | const decl = mod.declPtr(decl_index_ptr.*); |
| 2043 | if (decl.isExtern()) continue; | 2176 | if (decl.isExtern()) continue; |
| 2044 | const atom = &decl.*.link.wasm; | 2177 | const atom = &decl.*.link.wasm; |
| 2045 | if (decl.ty.zigTypeTag() == .Fn) { | 2178 | if (decl.ty.zigTypeTag() == .Fn) { |
| 2046 | try self.parseAtom(atom, .{ .function = decl.fn_link.wasm }); | 2179 | try self.parseAtom(atom, .{ .function = decl.fn_link.wasm }); |
| 2047 | } else if (decl.getVariable()) |variable| { | 2180 | } else if (decl.getVariable()) |variable| { |
| 2048 | if (!variable.is_mutable) { | 2181 | if (!variable.is_mutable) { |
| 2049 | try self.parseAtom(atom, .{ .data = .read_only }); | 2182 | try self.parseAtom(atom, .{ .data = .read_only }); |
| 2050 | } else if (variable.init.isUndefDeep()) { | 2183 | } else if (variable.init.isUndefDeep()) { |
| 2051 | try self.parseAtom(atom, .{ .data = .uninitialized }); | 2184 | try self.parseAtom(atom, .{ .data = .uninitialized }); |
| | 2185 | } else { |
| | 2186 | try self.parseAtom(atom, .{ .data = .initialized }); |
| | 2187 | } |
| 2052 | } else { | 2188 | } else { |
| 2053 | try self.parseAtom(atom, .{ .data = .initialized }); | 2189 | try self.parseAtom(atom, .{ .data = .read_only }); |
| | 2190 | } |
| | 2191 | |
| | 2192 | // also parse atoms for a decl's locals |
| | 2193 | for (atom.locals.items) |*local_atom| { |
| | 2194 | try self.parseAtom(local_atom, .{ .data = .read_only }); |
| 2054 | } | 2195 | } |
| 2055 | } else { | | |
| 2056 | try self.parseAtom(atom, .{ .data = .read_only }); | | |
| 2057 | } | 2196 | } |
| 2058 | | 2197 | |
| 2059 | // also parse atoms for a decl's locals | 2198 | if (self.dwarf) |*dwarf| { |
| 2060 | for (atom.locals.items) |*local_atom| { | 2199 | try dwarf.flushModule(&self.base, self.base.options.module.?); |
| 2061 | try self.parseAtom(local_atom, .{ .data = .read_only }); | | |
| 2062 | } | 2200 | } |
| 2063 | } | 2201 | } |
| 2064 | | 2202 | |
| ... | @@ -2066,9 +2204,6 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod | ... | @@ -2066,9 +2204,6 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2066 | try object.parseIntoAtoms(self.base.allocator, @intCast(u16, object_index), self); | 2204 | try object.parseIntoAtoms(self.base.allocator, @intCast(u16, object_index), self); |
| 2067 | } | 2205 | } |
| 2068 | | 2206 | |
| 2069 | if (self.dwarf) |*dwarf| { | | |
| 2070 | try dwarf.flushModule(&self.base, self.base.options.module.?); | | |
| 2071 | } | | |
| 2072 | try self.allocateAtoms(); | 2207 | try self.allocateAtoms(); |
| 2073 | try self.setupMemory(); | 2208 | try self.setupMemory(); |
| 2074 | self.mapFunctionTable(); | 2209 | self.mapFunctionTable(); |
| ... | @@ -2424,19 +2559,44 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod | ... | @@ -2424,19 +2559,44 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2424 | } | 2559 | } |
| 2425 | } else if (!self.base.options.strip) { | 2560 | } else if (!self.base.options.strip) { |
| 2426 | if (self.dwarf) |*dwarf| { | 2561 | if (self.dwarf) |*dwarf| { |
| 2427 | if (self.debug_info_index != null) { | 2562 | const mod = self.base.options.module.?; |
| 2428 | try dwarf.writeDbgAbbrev(&self.base); | 2563 | try dwarf.writeDbgAbbrev(&self.base); |
| 2429 | // for debug info and ranges, the address is always 0, | 2564 | // for debug info and ranges, the address is always 0, |
| 2430 | // as locations are always offsets relative to 'code' section. | 2565 | // as locations are always offsets relative to 'code' section. |
| 2431 | try dwarf.writeDbgInfoHeader(&self.base, mod, 0, code_section_size); | 2566 | try dwarf.writeDbgInfoHeader(&self.base, mod, 0, code_section_size); |
| 2432 | try dwarf.writeDbgAranges(&self.base, 0, code_section_size); | 2567 | try dwarf.writeDbgAranges(&self.base, 0, code_section_size); |
| 2433 | try dwarf.writeDbgLineHeader(&self.base, mod); | 2568 | try dwarf.writeDbgLineHeader(&self.base, mod); |
| 2434 | | 2569 | } |
| 2435 | try emitDebugSection(file, self.debug_info.items, ".debug_info"); | 2570 | |
| 2436 | try emitDebugSection(file, self.debug_aranges.items, ".debug_ranges"); | 2571 | var debug_bytes = std.ArrayList(u8).init(self.base.allocator); |
| 2437 | try emitDebugSection(file, self.debug_abbrev.items, ".debug_abbrev"); | 2572 | defer debug_bytes.deinit(); |
| 2438 | try emitDebugSection(file, self.debug_line.items, ".debug_line"); | 2573 | |
| 2439 | try emitDebugSection(file, dwarf.strtab.items, ".debug_str"); | 2574 | const DebugSection = struct { |
| | 2575 | name: []const u8, |
| | 2576 | index: ?u32, |
| | 2577 | }; |
| | 2578 | |
| | 2579 | const debug_sections: []const DebugSection = &.{ |
| | 2580 | .{ .name = ".debug_info", .index = self.debug_info_index }, |
| | 2581 | .{ .name = ".debug_pubtypes", .index = self.debug_pubtypes_index }, |
| | 2582 | .{ .name = ".debug_abbrev", .index = self.debug_abbrev_index }, |
| | 2583 | .{ .name = ".debug_line", .index = self.debug_line_index }, |
| | 2584 | .{ .name = ".debug_str", .index = self.debug_str_index }, |
| | 2585 | .{ .name = ".debug_pubnames", .index = self.debug_pubnames_index }, |
| | 2586 | .{ .name = ".debug_loc", .index = self.debug_loc_index }, |
| | 2587 | .{ .name = ".debug_ranges", .index = self.debug_ranges_index }, |
| | 2588 | }; |
| | 2589 | |
| | 2590 | for (debug_sections) |item| { |
| | 2591 | if (item.index) |index| { |
| | 2592 | var atom = self.atoms.get(index).?.getFirst(); |
| | 2593 | while (true) { |
| | 2594 | atom.resolveRelocs(self); |
| | 2595 | try debug_bytes.appendSlice(atom.code.items); |
| | 2596 | atom = atom.next orelse break; |
| | 2597 | } |
| | 2598 | try emitDebugSection(file, debug_bytes.items, item.name); |
| | 2599 | debug_bytes.clearRetainingCapacity(); |
| 2440 | } | 2600 | } |
| 2441 | } | 2601 | } |
| 2442 | try self.emitNameSection(file, arena); | 2602 | try self.emitNameSection(file, arena); |
| ... | @@ -2444,6 +2604,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod | ... | @@ -2444,6 +2604,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2444 | } | 2604 | } |
| 2445 | | 2605 | |
| 2446 | fn emitDebugSection(file: fs.File, data: []const u8, name: []const u8) !void { | 2606 | fn emitDebugSection(file: fs.File, data: []const u8, name: []const u8) !void { |
| | 2607 | if (data.len == 0) return; |
| 2447 | const header_offset = try reserveCustomSectionHeader(file); | 2608 | const header_offset = try reserveCustomSectionHeader(file); |
| 2448 | const writer = file.writer(); | 2609 | const writer = file.writer(); |
| 2449 | try leb.writeULEB128(writer, @intCast(u32, name.len)); | 2610 | try leb.writeULEB128(writer, @intCast(u32, name.len)); |
| ... | @@ -3149,8 +3310,8 @@ fn emitSegmentInfo(self: *Wasm, file: fs.File, arena: Allocator) !void { | ... | @@ -3149,8 +3310,8 @@ fn emitSegmentInfo(self: *Wasm, file: fs.File, arena: Allocator) !void { |
| 3149 | var payload = std.ArrayList(u8).init(arena); | 3310 | var payload = std.ArrayList(u8).init(arena); |
| 3150 | const writer = payload.writer(); | 3311 | const writer = payload.writer(); |
| 3151 | try leb.writeULEB128(file.writer(), @enumToInt(types.SubsectionType.WASM_SEGMENT_INFO)); | 3312 | try leb.writeULEB128(file.writer(), @enumToInt(types.SubsectionType.WASM_SEGMENT_INFO)); |
| 3152 | try leb.writeULEB128(writer, @intCast(u32, self.segment_info.items.len)); | 3313 | try leb.writeULEB128(writer, @intCast(u32, self.segment_info.count())); |
| 3153 | for (self.segment_info.items) |segment_info| { | 3314 | for (self.segment_info.values()) |segment_info| { |
| 3154 | log.debug("Emit segment: {s} align({d}) flags({b})", .{ | 3315 | log.debug("Emit segment: {s} align({d}) flags({b})", .{ |
| 3155 | segment_info.name, | 3316 | segment_info.name, |
| 3156 | @ctz(segment_info.alignment), | 3317 | @ctz(segment_info.alignment), |