| ... | ... | @@ -95,9 +95,10 @@ imports: std.AutoHashMapUnmanaged(SymbolLoc, types.Import) = .{}, |
| 95 | 95 | segments: std.ArrayListUnmanaged(Segment) = .{}, |
| 96 | 96 | /// Maps a data segment key (such as .rodata) to the index into `segments`. |
| 97 | 97 | data_segments: std.StringArrayHashMapUnmanaged(u32) = .{}, |
| 98 | | /// A list of `types.Segment` which provide meta data |
| 99 | | /// about a data symbol such as its name |
| 100 | | segment_info: std.ArrayListUnmanaged(types.Segment) = .{}, |
| 98 | /// A table of `types.Segment` which provide meta data |
| 99 | /// about a data symbol such as its name where the key is |
| 100 | /// the segment index, which can be found from `data_segments` |
| 101 | segment_info: std.AutoArrayHashMapUnmanaged(u32, types.Segment) = .{}, |
| 101 | 102 | /// Deduplicated string table for strings used by symbols, imports and exports. |
| 102 | 103 | string_table: StringTable = .{}, |
| 103 | 104 | /// Debug information for wasm |
| ... | ... | @@ -158,6 +159,19 @@ export_names: std.AutoHashMapUnmanaged(SymbolLoc, u32) = .{}, |
| 158 | 159 | /// The actual table is populated during `flush`. |
| 159 | 160 | error_table_symbol: ?u32 = null, |
| 160 | 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 | |
| 161 | 175 | pub const Segment = struct { |
| 162 | 176 | alignment: u32, |
| 163 | 177 | size: u32, |
| ... | ... | @@ -384,15 +398,16 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Wasm { |
| 384 | 398 | /// and symbols come from the object files instead. |
| 385 | 399 | pub fn initDebugSections(self: *Wasm) !void { |
| 386 | 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); |
| 387 | 402 | // this will create an Atom and set the index for us. |
| 388 | | try self.createDebugSectionForIndex(&self.debug_info_index); |
| 389 | | try self.createDebugSectionForIndex(&self.debug_line_index); |
| 390 | | try self.createDebugSectionForIndex(&self.debug_loc_index); |
| 391 | | try self.createDebugSectionForIndex(&self.debug_abbrev_index); |
| 392 | | try self.createDebugSectionForIndex(&self.debug_ranges_index); |
| 393 | | try self.createDebugSectionForIndex(&self.debug_str_index); |
| 394 | | try self.createDebugSectionForIndex(&self.debug_pubnames_index); |
| 395 | | try self.createDebugSectionForIndex(&self.debug_pubtypes_index); |
| 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"); |
| 396 | 411 | } |
| 397 | 412 | |
| 398 | 413 | fn parseInputFiles(self: *Wasm, files: []const []const u8) !void { |
| ... | ... | @@ -676,7 +691,7 @@ pub fn deinit(self: *Wasm) void { |
| 676 | 691 | for (self.func_types.items) |*func_type| { |
| 677 | 692 | func_type.deinit(gpa); |
| 678 | 693 | } |
| 679 | | for (self.segment_info.items) |segment_info| { |
| 694 | for (self.segment_info.values()) |segment_info| { |
| 680 | 695 | gpa.free(segment_info.name); |
| 681 | 696 | } |
| 682 | 697 | for (self.objects.items) |*object| { |
| ... | ... | @@ -1364,16 +1379,7 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void { |
| 1364 | 1379 | const index = gop.value_ptr.*; |
| 1365 | 1380 | self.segments.items[index].size += atom.size; |
| 1366 | 1381 | |
| 1367 | | // segment indexes can be off by 1 due to also containing a segment |
| 1368 | | // for the code section, so we must check if the existing segment |
| 1369 | | // is larger than that of the code section, and substract the index by 1 in such case. |
| 1370 | | var info_add = if (self.code_section_index) |idx| blk: { |
| 1371 | | if (idx < index) break :blk @as(u32, 1); |
| 1372 | | break :blk 0; |
| 1373 | | } else @as(u32, 0); |
| 1374 | | if (self.debug_info_index != null) info_add += 1; |
| 1375 | | if (self.debug_line_index != null) info_add += 1; |
| 1376 | | symbol.index = index - info_add; |
| 1382 | symbol.index = @intCast(u32, self.segment_info.getIndex(index).?); |
| 1377 | 1383 | // segment info already exists, so free its memory |
| 1378 | 1384 | self.base.allocator.free(segment_name); |
| 1379 | 1385 | break :result index; |
| ... | ... | @@ -1386,8 +1392,8 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void { |
| 1386 | 1392 | }); |
| 1387 | 1393 | gop.value_ptr.* = index; |
| 1388 | 1394 | |
| 1389 | | const info_index = @intCast(u32, self.segment_info.items.len); |
| 1390 | | try self.segment_info.append(self.base.allocator, segment_info); |
| 1395 | const info_index = @intCast(u32, self.segment_info.count()); |
| 1396 | try self.segment_info.put(self.base.allocator, index, segment_info); |
| 1391 | 1397 | symbol.index = info_index; |
| 1392 | 1398 | break :result index; |
| 1393 | 1399 | } |
| ... | ... | @@ -1397,18 +1403,54 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void { |
| 1397 | 1403 | const segment: *Segment = &self.segments.items[final_index]; |
| 1398 | 1404 | segment.alignment = std.math.max(segment.alignment, atom.alignment); |
| 1399 | 1405 | |
| 1400 | | 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| { |
| 1401 | 1413 | last.*.next = atom; |
| 1402 | 1414 | atom.prev = last.*; |
| 1403 | 1415 | last.* = atom; |
| 1404 | 1416 | } else { |
| 1405 | | try self.atoms.putNoClobber(self.base.allocator, final_index, atom); |
| 1417 | try self.atoms.putNoClobber(self.base.allocator, index, atom); |
| 1406 | 1418 | } |
| 1407 | 1419 | } |
| 1408 | 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 | |
| 1409 | 1450 | fn allocateAtoms(self: *Wasm) !void { |
| 1410 | 1451 | // first sort the data segments |
| 1411 | 1452 | try sortDataSegments(self); |
| 1453 | try allocateDebugAtoms(self); |
| 1412 | 1454 | |
| 1413 | 1455 | var it = self.atoms.iterator(); |
| 1414 | 1456 | while (it.next()) |entry| { |
| ... | ... | @@ -1426,7 +1468,7 @@ fn allocateAtoms(self: *Wasm) !void { |
| 1426 | 1468 | atom.size, |
| 1427 | 1469 | }); |
| 1428 | 1470 | offset += atom.size; |
| 1429 | | 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 |
| 1430 | 1472 | atom = atom.next orelse break; |
| 1431 | 1473 | } |
| 1432 | 1474 | segment.size = std.mem.alignForwardGeneric(u32, offset, segment.alignment); |
| ... | ... | @@ -1989,20 +2031,35 @@ fn populateErrorNameTable(self: *Wasm) !void { |
| 1989 | 2031 | /// From a given index variable, creates a new debug section. |
| 1990 | 2032 | /// This initializes the index, appends a new segment, |
| 1991 | 2033 | /// and finally, creates a managed `Atom`. |
| 1992 | | pub fn createDebugSectionForIndex(self: *Wasm, index: *?u32) !void { |
| 2034 | pub fn createDebugSectionForIndex(self: *Wasm, index: *?u32, name: []const u8) !*Atom { |
| 1993 | 2035 | const new_index = @intCast(u32, self.segments.items.len); |
| 1994 | 2036 | index.* = new_index; |
| 1995 | 2037 | try self.appendDummySegment(); |
| 2038 | // _ = index; |
| 2039 | |
| 2040 | const sym_index = self.symbols_free_list.popOrNull() orelse idx: { |
| 2041 | const tmp_index = @intCast(u32, self.symbols.items.len); |
| 2042 | _ = try self.symbols.addOne(self.base.allocator); |
| 2043 | break :idx tmp_index; |
| 2044 | }; |
| 2045 | self.symbols.items[sym_index] = .{ |
| 2046 | .tag = .section, |
| 2047 | .name = try self.string_table.put(self.base.allocator, name), |
| 2048 | .index = 0, |
| 2049 | .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL), |
| 2050 | }; |
| 1996 | 2051 | |
| 1997 | 2052 | const atom = try self.base.allocator.create(Atom); |
| 1998 | 2053 | atom.* = Atom.empty; |
| 1999 | 2054 | atom.alignment = 1; // debug sections are always 1-byte-aligned |
| 2055 | atom.sym_index = sym_index; |
| 2000 | 2056 | try self.managed_atoms.append(self.base.allocator, atom); |
| 2001 | | try self.atoms.put(self.base.allocator, new_index, atom); |
| 2057 | try self.symbol_atom.put(self.base.allocator, atom.symbolLoc(), atom); |
| 2058 | return atom; |
| 2002 | 2059 | } |
| 2003 | 2060 | |
| 2004 | 2061 | fn resetState(self: *Wasm) void { |
| 2005 | | for (self.segment_info.items) |*segment_info| { |
| 2062 | for (self.segment_info.values()) |segment_info| { |
| 2006 | 2063 | self.base.allocator.free(segment_info.name); |
| 2007 | 2064 | } |
| 2008 | 2065 | if (self.base.options.module) |mod| { |
| ... | ... | @@ -2029,6 +2086,12 @@ fn resetState(self: *Wasm) void { |
| 2029 | 2086 | self.code_section_index = null; |
| 2030 | 2087 | self.debug_info_index = null; |
| 2031 | 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; |
| 2032 | 2095 | } |
| 2033 | 2096 | |
| 2034 | 2097 | pub fn flush(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !void { |
| ... | ... | @@ -2508,26 +2571,31 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2508 | 2571 | var debug_bytes = std.ArrayList(u8).init(self.base.allocator); |
| 2509 | 2572 | defer debug_bytes.deinit(); |
| 2510 | 2573 | |
| 2511 | | const debug_sections = .{ |
| 2512 | | .{ ".debug_info", self.debug_info_index }, |
| 2513 | | .{ ".debug_pubtypes", self.debug_pubtypes_index }, |
| 2514 | | .{ ".debug_abbrev", self.debug_abbrev_index }, |
| 2515 | | .{ ".debug_line", self.debug_line_index }, |
| 2516 | | .{ ".debug_str", self.debug_str_index }, |
| 2517 | | .{ ".debug_pubnames", self.debug_pubnames_index }, |
| 2518 | | .{ ".debug_loc", self.debug_loc_index }, |
| 2519 | | .{ ".debug_ranges", self.debug_ranges_index }, |
| 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 }, |
| 2520 | 2588 | }; |
| 2521 | 2589 | |
| 2522 | | inline for (debug_sections) |item| { |
| 2523 | | if (item[1]) |index| { |
| 2590 | for (debug_sections) |item| { |
| 2591 | if (item.index) |index| { |
| 2524 | 2592 | var atom = self.atoms.get(index).?.getFirst(); |
| 2525 | 2593 | while (true) { |
| 2526 | 2594 | atom.resolveRelocs(self); |
| 2527 | 2595 | try debug_bytes.appendSlice(atom.code.items); |
| 2528 | 2596 | atom = atom.next orelse break; |
| 2529 | 2597 | } |
| 2530 | | try emitDebugSection(file, debug_bytes.items, item[0]); |
| 2598 | try emitDebugSection(file, debug_bytes.items, item.name); |
| 2531 | 2599 | debug_bytes.clearRetainingCapacity(); |
| 2532 | 2600 | } |
| 2533 | 2601 | } |
| ... | ... | @@ -3242,8 +3310,8 @@ fn emitSegmentInfo(self: *Wasm, file: fs.File, arena: Allocator) !void { |
| 3242 | 3310 | var payload = std.ArrayList(u8).init(arena); |
| 3243 | 3311 | const writer = payload.writer(); |
| 3244 | 3312 | try leb.writeULEB128(file.writer(), @enumToInt(types.SubsectionType.WASM_SEGMENT_INFO)); |
| 3245 | | try leb.writeULEB128(writer, @intCast(u32, self.segment_info.items.len)); |
| 3246 | | for (self.segment_info.items) |segment_info| { |
| 3313 | try leb.writeULEB128(writer, @intCast(u32, self.segment_info.count())); |
| 3314 | for (self.segment_info.values()) |segment_info| { |
| 3247 | 3315 | log.debug("Emit segment: {s} align({d}) flags({b})", .{ |
| 3248 | 3316 | segment_info.name, |
| 3249 | 3317 | @ctz(segment_info.alignment), |