authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-02-17 21:12:20+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-02-23 16:07:36+01:00
logf7f3678b485fff790af278ff5436e0b63db998f8
tree3743647e0f716c6fd7f9239fc5e99c78ba2acb97
parent91a88a789ffa80ebb57c77ae0fe37594276e3707

wasm-linker: Emit symbol table for object file

When creating a relocatable object file, emit the symbol table. We do this by iterating over all atoms, and finding the corresponding symbols of those. This provides us all the meta information such as size, and offset as well. This data is required for defined data symbols. When we emit an object file, the "Names" section does not have to be emitted, as all symbol names are already in the symbol table, so the names section is redundant.

1 files changed, 87 insertions(+), 1 deletions(-)

src/link/Wasm.zig+87-1
......@@ -1132,6 +1132,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
11321132
11331133 const file = self.base.file.?;
11341134 const header_size = 5 + 1;
1135 const is_obj = self.base.options.output_mode == .Obj;
11351136
11361137 // No need to rewrite the magic/version header
11371138 try file.setEndPos(@sizeOf(@TypeOf(wasm.magic ++ wasm.version)));
......@@ -1443,7 +1444,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
14431444 }
14441445
14451446 // Custom section "name" which contains symbol names
1446 {
1447 if (!is_obj) {
14471448 const Name = struct {
14481449 index: u32,
14491450 name: []const u8,
......@@ -1492,6 +1493,10 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
14921493 @intCast(u32, (try file.getPos()) - header_offset - header_size),
14931494 );
14941495 }
1496
1497 if (is_obj) {
1498 try self.emitLinkSection(file, arena);
1499 }
14951500}
14961501
14971502fn emitNameSubsection(self: *Wasm, section_id: std.wasm.NameSubsection, names: anytype, writer: anytype) !void {
......@@ -2030,6 +2035,87 @@ fn writeCustomSectionHeader(file: fs.File, offset: u64, size: u32) !void {
20302035 try file.pwriteAll(&buf, offset);
20312036}
20322037
2038fn emitLinkSection(self: *Wasm, file: fs.File, arena: Allocator) !void {
2039 const offset = try reserveCustomSectionHeader(file);
2040 const writer = file.writer();
2041 // emit "linking" custom section name
2042 const section_name = "linking";
2043 try leb.writeULEB128(writer, section_name.len);
2044 try writer.writeAll(section_name);
2045
2046 // meta data version, which is currently '2'
2047 try leb.writeULEB128(writer, @as(u32, 2));
2048
2049 // For each subsection type (found in types.Subsection) we can emit a section.
2050 // Currently, we only support emitting segment info and the symbol table.
2051 try self.emitSymbolTable(file, arena);
2052
2053 const size = @intCast(u32, (try file.getPos()) - offset - 6);
2054 try writeCustomSectionHeader(file, offset, size);
2055}
2056
2057fn emitSymbolTable(self: *Wasm, file: fs.File, arena: Allocator) !void {
2058 // After emitting the subtype, we must emit the subsection's length
2059 // so first write it to a temporary arraylist to calculate the length
2060 // and then write all data at once.
2061 var payload = std.ArrayList(u8).init(arena);
2062 const writer = payload.writer();
2063
2064 try leb.writeULEB128(file.writer(), @enumToInt(types.SubsectionType.WASM_SYMBOL_TABLE));
2065
2066 var symbol_count: u32 = 0;
2067
2068 var atom_it = self.atoms.valueIterator();
2069 while (atom_it.next()) |next_atom| {
2070 var atom: ?*Atom = next_atom.*.getFirst();
2071 while (atom) |current_atom| {
2072 const sym_loc: SymbolLoc = .{ .file = current_atom.file, .index = current_atom.sym_index };
2073 const symbol = sym_loc.getSymbol(self).*;
2074 if (symbol.tag == .dead) continue; // Do not emit dead symbols
2075 symbol_count += 1;
2076 try leb.writeULEB128(writer, @enumToInt(symbol.tag));
2077 try leb.writeULEB128(writer, symbol.flags);
2078
2079 switch (symbol.tag) {
2080 .data => {
2081 const name = mem.sliceTo(symbol.name, 0);
2082 try leb.writeULEB128(writer, @intCast(u32, name.len));
2083 try writer.writeAll(name);
2084
2085 if (symbol.isDefined()) {
2086 try leb.writeULEB128(writer, symbol.index);
2087 try leb.writeULEB128(writer, @as(u32, current_atom.offset));
2088 try leb.writeULEB128(writer, @as(u32, current_atom.size));
2089 }
2090 },
2091 .section => {
2092 try leb.writeULEB128(writer, symbol.index);
2093 },
2094 else => {
2095 try leb.writeULEB128(writer, symbol.index);
2096 if (symbol.isDefined()) {
2097 const name = mem.sliceTo(symbol.name, 0);
2098 try leb.writeULEB128(writer, @intCast(u32, name.len));
2099 try writer.writeAll(name);
2100 }
2101 },
2102 }
2103 atom = current_atom.next orelse break;
2104 }
2105 }
2106
2107 var buf: [5]u8 = undefined;
2108 leb.writeUnsignedFixed(5, &buf, symbol_count);
2109 try payload.insertSlice(0, &buf);
2110 try leb.writeULEB128(file.writer(), @intCast(u32, payload.items.len));
2111
2112 const iovec: std.os.iovec_const = .{
2113 .iov_base = payload.items.ptr,
2114 .iov_len = payload.items.len,
2115 };
2116 try file.writevAll(&.{iovec});
2117}
2118
20332119/// Searches for an a matching function signature, when not found
20342120/// a new entry will be made. The index of the existing/new signature will be returned.
20352121pub fn putOrGetFuncType(self: *Wasm, func_type: wasm.Type) !u32 {