wasm-linker: write debug sections from objects

We now link relocatable debug sections with the correct
section symbol and then allocate and resolve the debug atoms
before writing them into the final binary.

Although this does perform the relocation, the actual relocations
are not done correctly yet.
This commit is contained in:
Luuk de Gram
2022-08-31 23:03:29 +02:00
parent f060edb0f3
commit c347751338
2 changed files with 59 additions and 36 deletions

View File

@@ -2495,21 +2495,39 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
}
} else if (!self.base.options.strip) {
if (self.dwarf) |*dwarf| {
if (self.debug_info_index != null) {
if (self.base.options.module) |mod| {
try dwarf.writeDbgAbbrev(&self.base);
// for debug info and ranges, the address is always 0,
// as locations are always offsets relative to 'code' section.
try dwarf.writeDbgInfoHeader(&self.base, mod, 0, code_section_size);
try dwarf.writeDbgAranges(&self.base, 0, code_section_size);
try dwarf.writeDbgLineHeader(&self.base, mod);
}
const mod = self.base.options.module.?;
try dwarf.writeDbgAbbrev(&self.base);
// for debug info and ranges, the address is always 0,
// as locations are always offsets relative to 'code' section.
try dwarf.writeDbgInfoHeader(&self.base, mod, 0, code_section_size);
try dwarf.writeDbgAranges(&self.base, 0, code_section_size);
try dwarf.writeDbgLineHeader(&self.base, mod);
}
try emitDebugSection(file, self.debug_info.items, ".debug_info");
try emitDebugSection(file, self.debug_aranges.items, ".debug_ranges");
try emitDebugSection(file, self.debug_abbrev.items, ".debug_abbrev");
try emitDebugSection(file, self.debug_line.items, ".debug_line");
try emitDebugSection(file, dwarf.strtab.items, ".debug_str");
var debug_bytes = std.ArrayList(u8).init(self.base.allocator);
defer debug_bytes.deinit();
const debug_sections = .{
.{ ".debug_info", self.debug_info_index },
.{ ".debug_pubtypes", self.debug_pubtypes_index },
.{ ".debug_abbrev", self.debug_abbrev_index },
.{ ".debug_line", self.debug_line_index },
.{ ".debug_str", self.debug_str_index },
.{ ".debug_pubnames", self.debug_pubnames_index },
.{ ".debug_loc", self.debug_loc_index },
.{ ".debug_ranges", self.debug_ranges_index },
};
inline for (debug_sections) |item| {
if (item[1]) |index| {
var atom = self.atoms.get(index).?.getFirst();
while (true) {
atom.resolveRelocs(self);
try debug_bytes.appendSlice(atom.code.items);
atom = atom.next orelse break;
}
try emitDebugSection(file, debug_bytes.items, item[0]);
debug_bytes.clearRetainingCapacity();
}
}
try self.emitNameSection(file, arena);
@@ -2517,6 +2535,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
}
fn emitDebugSection(file: fs.File, data: []const u8, name: []const u8) !void {
if (data.len == 0) return;
const header_offset = try reserveCustomSectionHeader(file);
const writer = file.writer();
try leb.writeULEB128(writer, @intCast(u32, name.len));

View File

@@ -77,7 +77,7 @@ const RelocatableData = struct {
/// The size in bytes of the data representing the segment within the section
size: u32,
/// The index within the section itself, or in case of a debug section,
/// the offset within the `debug_names` table.
/// the offset within the `string_table`.
index: u32,
/// The offset within the section where the data starts
offset: u32,
@@ -101,9 +101,16 @@ const RelocatableData = struct {
return switch (self.type) {
.data => .data,
.code => .function,
.debug => unreachable, // illegal, debug sections are not represented by a symbol
.debug => .section,
};
}
/// Returns the index within a section itself, or in case of a debug section,
/// returns the section index within the object file.
pub fn getIndex(self: RelocatableData) u32 {
if (self.type == .debug) return self.section_index;
return self.index;
}
};
pub const InitError = error{NotObjectFile} || ParseError || std.fs.File.ReadError;
@@ -205,7 +212,7 @@ pub fn importedCountByKind(self: *const Object, kind: std.wasm.ExternalKind) u32
/// From a given `RelocatableDate`, find the corresponding debug section name
pub fn getDebugName(self: *const Object, relocatable_data: RelocatableData) []const u8 {
return std.mem.sliceTo(self.debug_names[relocatable_data.index..], 0);
return self.string_table.get(relocatable_data.index);
}
/// Checks if the object file is an MVP version.
@@ -363,6 +370,7 @@ fn Parser(comptime ReaderType: type) type {
if (std.mem.eql(u8, name, "linking")) {
is_object_file.* = true;
self.object.relocatable_data = relocatable_data.items; // at this point no new relocatable sections will appear so we're free to store them.
try self.parseMetadata(gpa, @intCast(usize, reader.context.bytes_left));
} else if (std.mem.startsWith(u8, name, "reloc")) {
try self.parseRelocations(gpa);
@@ -374,19 +382,16 @@ fn Parser(comptime ReaderType: type) type {
errdefer gpa.free(debug_content);
try reader.readNoEof(debug_content);
const debug_name_index = @intCast(u32, debug_names.items.len);
try debug_names.ensureUnusedCapacity(name.len + 1);
debug_names.appendSliceAssumeCapacity(try gpa.dupe(u8, name));
debug_names.appendAssumeCapacity(0);
try relocatable_data.append(.{
.type = .debug,
.data = debug_content.ptr,
.size = debug_size,
.index = debug_name_index,
.index = try self.object.string_table.put(gpa, name),
.offset = len - debug_size,
.section_index = section_index,
});
} else {
log.info("found unknown custom section '{s}' - skipping parsing", .{name});
try reader.skipBytes(reader.context.bytes_left, .{});
}
},
@@ -551,9 +556,6 @@ fn Parser(comptime ReaderType: type) type {
else => |e| return e,
}
self.object.relocatable_data = relocatable_data.toOwnedSlice();
const names = debug_names.toOwnedSlice();
self.object.debug_names = names[0 .. names.len - 1 :0];
}
/// Based on the "features" custom section, parses it into a list of
@@ -774,7 +776,12 @@ fn Parser(comptime ReaderType: type) type {
},
.section => {
symbol.index = try leb.readULEB128(u32, reader);
symbol.name = try self.object.string_table.put(gpa, @tagName(symbol.tag));
for (self.object.relocatable_data) |data| {
if (data.section_index == symbol.index) {
symbol.name = data.index;
break;
}
}
},
else => {
symbol.index = try leb.readULEB128(u32, reader);
@@ -864,7 +871,6 @@ fn assertEnd(reader: anytype) !void {
/// Parses an object file into atoms, for code and data sections
pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin: *Wasm) !void {
log.debug("Parsing data section into atoms", .{});
const Key = struct {
kind: Symbol.Tag,
index: u32,
@@ -876,7 +882,7 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin
for (self.symtable) |symbol, symbol_index| {
switch (symbol.tag) {
.function, .data => if (!symbol.isUndefined()) {
.function, .data, .section => if (!symbol.isUndefined()) {
const gop = try symbol_for_segment.getOrPut(.{ .kind = symbol.tag, .index = symbol.index });
const sym_idx = @intCast(u32, symbol_index);
if (!gop.found_existing) {
@@ -925,13 +931,11 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin
try atom.code.appendSlice(gpa, relocatable_data.data[0..relocatable_data.size]);
if (relocatable_data.type != .debug) {
const symbols = symbol_for_segment.getPtr(.{
.kind = relocatable_data.getSymbolKind(),
.index = @intCast(u32, relocatable_data.index),
}) orelse continue; // encountered a segment we do not create an atom for
const sym_index = symbols.pop();
atom.sym_index = sym_index;
if (symbol_for_segment.getPtr(.{
.kind = relocatable_data.getSymbolKind(),
.index = relocatable_data.getIndex(),
})) |symbols| {
atom.sym_index = symbols.pop();
// symbols referencing the same atom will be added as alias
// or as 'parent' when they are global.
@@ -957,7 +961,7 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin
} else {
try wasm_bin.atoms.putNoClobber(gpa, final_index, atom);
}
log.debug("Parsed into atom: '{s}'", .{self.string_table.get(self.symtable[atom.sym_index].name)});
log.debug("Parsed into atom: '{s}' at segment index {d}", .{ self.string_table.get(self.symtable[atom.sym_index].name), final_index });
}
}