authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-02-19 21:25:50+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-02-23 16:07:36+01:00
log0a48a763fd3548a7b8609555cecbafc21ebe1fc3
treeefcb52976b8182f51b7c1d80ce88bb5b6997c5a7
parent2b0431a8d3933a46fecba2bf064bf45df54a111d

wasm-linker: Emit relocations for object files

When generating a relocatable object file, we now emit a custom "reloc.CODE" and "reloc.DATA" section which will contain the relocations for each section. Using a new symbol location -> Atom mapping, we can now easily find the corresponding `Atom` from a symbol. This can be used to construct the symbol table, as well as easier access to a target atom when performing a relocation for a data symbol.

2 files changed, 174 insertions(+), 58 deletions(-)

src/link/Wasm.zig+166-45
...@@ -110,6 +110,10 @@ discarded: std.AutoHashMapUnmanaged(SymbolLoc, SymbolLoc) = .{},...@@ -110,6 +110,10 @@ discarded: std.AutoHashMapUnmanaged(SymbolLoc, SymbolLoc) = .{},
110/// List of all symbol locations which have been resolved by the linker and will be emit110/// List of all symbol locations which have been resolved by the linker and will be emit
111/// into the final binary.111/// into the final binary.
112resolved_symbols: std.AutoArrayHashMapUnmanaged(SymbolLoc, void) = .{},112resolved_symbols: std.AutoArrayHashMapUnmanaged(SymbolLoc, void) = .{},
113/// Maps a symbol's location to an atom. This can be used to find meta
114/// data of a symbol, such as its size, or its offset to perform a relocation.
115/// Undefined (and synthetic) symbols do not have an Atom and therefore cannot be mapped.
116symbol_atom: std.AutoHashMapUnmanaged(SymbolLoc, *Atom) = .{},
113117
114pub const Segment = struct {118pub const Segment = struct {
115 alignment: u32,119 alignment: u32,
...@@ -170,6 +174,10 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option...@@ -170,6 +174,10 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
170 .flags = 0,174 .flags = 0,
171 .index = 0,175 .index = 0,
172 };176 };
177 const loc: SymbolLoc = .{ .file = null, .index = 0 };
178 try wasm_bin.resolved_symbols.putNoClobber(allocator, loc, {});
179 try wasm_bin.globals.putNoClobber(allocator, "__stack_pointer", loc);
180
173 // For object files we will import the stack pointer symbol181 // For object files we will import the stack pointer symbol
174 if (options.output_mode == .Obj) {182 if (options.output_mode == .Obj) {
175 symbol.setUndefined(true);183 symbol.setUndefined(true);
...@@ -336,6 +344,7 @@ pub fn deinit(self: *Wasm) void {...@@ -336,6 +344,7 @@ pub fn deinit(self: *Wasm) void {
336 self.globals.deinit(gpa);344 self.globals.deinit(gpa);
337 self.resolved_symbols.deinit(gpa);345 self.resolved_symbols.deinit(gpa);
338 self.discarded.deinit(gpa);346 self.discarded.deinit(gpa);
347 self.symbol_atom.deinit(gpa);
339 self.atoms.deinit(gpa);348 self.atoms.deinit(gpa);
340 self.managed_atoms.deinit(gpa);349 self.managed_atoms.deinit(gpa);
341 self.segments.deinit(gpa);350 self.segments.deinit(gpa);
...@@ -506,6 +515,10 @@ pub fn createLocalSymbol(self: *Wasm, decl: *Module.Decl, ty: Type) !u32 {...@@ -506,6 +515,10 @@ pub fn createLocalSymbol(self: *Wasm, decl: *Module.Decl, ty: Type) !u32 {
506 atom.sym_index = @intCast(u32, self.symbols.items.len);515 atom.sym_index = @intCast(u32, self.symbols.items.len);
507 self.symbols.appendAssumeCapacity(symbol);516 self.symbols.appendAssumeCapacity(symbol);
508 }517 }
518 try self.resolved_symbols.putNoClobber(self.base.allocator, .{
519 .file = null,
520 .index = atom.sym_index,
521 }, {});
509522
510 try decl.link.wasm.locals.append(self.base.allocator, atom);523 try decl.link.wasm.locals.append(self.base.allocator, atom);
511 return atom.sym_index;524 return atom.sym_index;
...@@ -614,12 +627,13 @@ fn addOrUpdateImport(self: *Wasm, decl: *Module.Decl) !void {...@@ -614,12 +627,13 @@ fn addOrUpdateImport(self: *Wasm, decl: *Module.Decl) !void {
614 const symbol: *Symbol = &self.symbols.items[symbol_index];627 const symbol: *Symbol = &self.symbols.items[symbol_index];
615 symbol.name = decl.name;628 symbol.name = decl.name;
616 symbol.setUndefined(true);629 symbol.setUndefined(true);
617 // also add it as a global so it can be resolved
618 try self.globals.putNoClobber(630 try self.globals.putNoClobber(
619 self.base.allocator,631 self.base.allocator,
620 mem.sliceTo(symbol.name, 0),632 mem.sliceTo(symbol.name, 0),
621 .{ .file = null, .index = symbol_index },633 .{ .file = null, .index = symbol_index },
622 );634 );
635 try self.resolved_symbols.put(self.base.allocator, .{ .file = null, .index = symbol_index }, {});
636
623 switch (decl.ty.zigTypeTag()) {637 switch (decl.ty.zigTypeTag()) {
624 .Fn => {638 .Fn => {
625 const gop = try self.imports.getOrPut(self.base.allocator, .{ .index = symbol_index, .file = null });639 const gop = try self.imports.getOrPut(self.base.allocator, .{ .index = symbol_index, .file = null });
...@@ -730,20 +744,22 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void {...@@ -730,20 +744,22 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void {
730}744}
731745
732fn allocateAtoms(self: *Wasm) !void {746fn allocateAtoms(self: *Wasm) !void {
733 var it = self.atoms.iterator();747 var it = self.atoms.valueIterator();
734 while (it.next()) |entry| {748 while (it.next()) |current_atom| {
735 var atom: *Atom = entry.value_ptr.*.getFirst();749 var atom: *Atom = current_atom.*.getFirst();
736 var offset: u32 = 0;750 var offset: u32 = 0;
737 while (true) {751 while (true) {
738 offset = std.mem.alignForwardGeneric(u32, offset, atom.alignment);752 offset = std.mem.alignForwardGeneric(u32, offset, atom.alignment);
739 atom.offset = offset;753 atom.offset = offset;
754 const symbol_loc = atom.symbolLoc();
740 log.debug("Atom '{s}' allocated from 0x{x:0>8} to 0x{x:0>8} size={d}", .{755 log.debug("Atom '{s}' allocated from 0x{x:0>8} to 0x{x:0>8} size={d}", .{
741 (SymbolLoc{ .file = atom.file, .index = atom.sym_index }).getSymbol(self).name,756 symbol_loc.getSymbol(self).name,
742 offset,757 offset,
743 offset + atom.size,758 offset + atom.size,
744 atom.size,759 atom.size,
745 });760 });
746 offset += atom.size;761 offset += atom.size;
762 try self.symbol_atom.putNoClobber(self.base.allocator, symbol_loc, atom);
747 atom = atom.next orelse break;763 atom = atom.next orelse break;
748 }764 }
749 }765 }
...@@ -1295,7 +1311,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {...@@ -1295,7 +1311,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
1295 }1311 }
12961312
1297 // Global section (used to emit stack pointer)1313 // Global section (used to emit stack pointer)
1298 {1314 if (self.wasm_globals.items.len > 0) {
1299 const header_offset = try reserveVecSectionHeader(file);1315 const header_offset = try reserveVecSectionHeader(file);
1300 const writer = file.writer();1316 const writer = file.writer();
13011317
...@@ -1480,7 +1496,13 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {...@@ -1480,7 +1496,13 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
1480 }1496 }
14811497
1482 if (is_obj) {1498 if (is_obj) {
1483 try self.emitLinkSection(file, arena);1499 // relocations need to point to the index of a symbol in the final symbol table. To save memory,
1500 // we never store all symbols in a single table, but store a location reference instead.
1501 // This means that for a relocatable object file, we need to generate one and provide it to the relocation sections.
1502 var symbol_table = std.AutoArrayHashMap(SymbolLoc, u32).init(arena);
1503 try self.emitLinkSection(file, arena, &symbol_table);
1504 try self.emitCodeRelocations(file, arena, 6, symbol_table);
1505 try self.emitDataRelocations(file, arena, 7, symbol_table);
1484 } else {1506 } else {
1485 try self.emitNameSection(file, arena);1507 try self.emitNameSection(file, arena);
1486 }1508 }
...@@ -1498,7 +1520,7 @@ fn emitNameSection(self: *Wasm, file: fs.File, arena: Allocator) !void {...@@ -1498,7 +1520,7 @@ fn emitNameSection(self: *Wasm, file: fs.File, arena: Allocator) !void {
1498 };1520 };
14991521
1500 var funcs = try std.ArrayList(Name).initCapacity(arena, self.functions.items.len + self.imported_functions_count);1522 var funcs = try std.ArrayList(Name).initCapacity(arena, self.functions.items.len + self.imported_functions_count);
1501 var globals = try std.ArrayList(Name).initCapacity(arena, self.wasm_globals.items.len);1523 var globals = try std.ArrayList(Name).initCapacity(arena, self.wasm_globals.items.len + self.imported_globals_count);
1502 var segments = try std.ArrayList(Name).initCapacity(arena, self.data_segments.count());1524 var segments = try std.ArrayList(Name).initCapacity(arena, self.data_segments.count());
15031525
1504 for (self.resolved_symbols.keys()) |sym_loc| {1526 for (self.resolved_symbols.keys()) |sym_loc| {
...@@ -2069,7 +2091,7 @@ fn writeCustomSectionHeader(file: fs.File, offset: u64, size: u32) !void {...@@ -2069,7 +2091,7 @@ fn writeCustomSectionHeader(file: fs.File, offset: u64, size: u32) !void {
2069 try file.pwriteAll(&buf, offset);2091 try file.pwriteAll(&buf, offset);
2070}2092}
20712093
2072fn emitLinkSection(self: *Wasm, file: fs.File, arena: Allocator) !void {2094fn emitLinkSection(self: *Wasm, file: fs.File, arena: Allocator, symbol_table: *std.AutoArrayHashMap(SymbolLoc, u32)) !void {
2073 const offset = try reserveCustomSectionHeader(file);2095 const offset = try reserveCustomSectionHeader(file);
2074 const writer = file.writer();2096 const writer = file.writer();
2075 // emit "linking" custom section name2097 // emit "linking" custom section name
...@@ -2082,14 +2104,14 @@ fn emitLinkSection(self: *Wasm, file: fs.File, arena: Allocator) !void {...@@ -2082,14 +2104,14 @@ fn emitLinkSection(self: *Wasm, file: fs.File, arena: Allocator) !void {
20822104
2083 // For each subsection type (found in types.Subsection) we can emit a section.2105 // For each subsection type (found in types.Subsection) we can emit a section.
2084 // Currently, we only support emitting segment info and the symbol table.2106 // Currently, we only support emitting segment info and the symbol table.
2085 try self.emitSymbolTable(file, arena);2107 try self.emitSymbolTable(file, arena, symbol_table);
2086 try self.emitSegmentInfo(file, arena);2108 try self.emitSegmentInfo(file, arena);
20872109
2088 const size = @intCast(u32, (try file.getPos()) - offset - 6);2110 const size = @intCast(u32, (try file.getPos()) - offset - 6);
2089 try writeCustomSectionHeader(file, offset, size);2111 try writeCustomSectionHeader(file, offset, size);
2090}2112}
20912113
2092fn emitSymbolTable(self: *Wasm, file: fs.File, arena: Allocator) !void {2114fn emitSymbolTable(self: *Wasm, file: fs.File, arena: Allocator, symbol_table: *std.AutoArrayHashMap(SymbolLoc, u32)) !void {
2093 // After emitting the subtype, we must emit the subsection's length2115 // After emitting the subtype, we must emit the subsection's length
2094 // so first write it to a temporary arraylist to calculate the length2116 // so first write it to a temporary arraylist to calculate the length
2095 // and then write all data at once.2117 // and then write all data at once.
...@@ -2099,43 +2121,39 @@ fn emitSymbolTable(self: *Wasm, file: fs.File, arena: Allocator) !void {...@@ -2099,43 +2121,39 @@ fn emitSymbolTable(self: *Wasm, file: fs.File, arena: Allocator) !void {
2099 try leb.writeULEB128(file.writer(), @enumToInt(types.SubsectionType.WASM_SYMBOL_TABLE));2121 try leb.writeULEB128(file.writer(), @enumToInt(types.SubsectionType.WASM_SYMBOL_TABLE));
21002122
2101 var symbol_count: u32 = 0;2123 var symbol_count: u32 = 0;
2102 var atom_it = self.atoms.valueIterator();2124 for (self.resolved_symbols.keys()) |sym_loc| {
2103 while (atom_it.next()) |next_atom| {2125 const symbol = sym_loc.getSymbol(self).*;
2104 var atom: ?*Atom = next_atom.*.getFirst();2126 if (symbol.tag == .dead) continue; // Do not emit dead symbols
2105 while (atom) |current_atom| {2127 try symbol_table.putNoClobber(sym_loc, symbol_count);
2106 const sym_loc: SymbolLoc = .{ .file = current_atom.file, .index = current_atom.sym_index };2128 symbol_count += 1;
2107 const symbol = sym_loc.getSymbol(self).*;2129 log.debug("Emit symbol: {}", .{symbol});
2108 if (symbol.tag == .dead) continue; // Do not emit dead symbols2130 try leb.writeULEB128(writer, @enumToInt(symbol.tag));
2109 symbol_count += 1;2131 try leb.writeULEB128(writer, symbol.flags);
2110 log.debug("Emit symbol: {}", .{symbol});2132
2111 try leb.writeULEB128(writer, @enumToInt(symbol.tag));2133 switch (symbol.tag) {
2112 try leb.writeULEB128(writer, symbol.flags);2134 .data => {
21132135 const name = mem.sliceTo(symbol.name, 0);
2114 switch (symbol.tag) {2136 try leb.writeULEB128(writer, @intCast(u32, name.len));
2115 .data => {2137 try writer.writeAll(name);
2138
2139 if (symbol.isDefined()) {
2140 try leb.writeULEB128(writer, symbol.index);
2141 const atom = self.symbol_atom.get(sym_loc).?;
2142 try leb.writeULEB128(writer, @as(u32, atom.offset));
2143 try leb.writeULEB128(writer, @as(u32, atom.size));
2144 }
2145 },
2146 .section => {
2147 try leb.writeULEB128(writer, symbol.index);
2148 },
2149 else => {
2150 try leb.writeULEB128(writer, symbol.index);
2151 if (symbol.isDefined()) {
2116 const name = mem.sliceTo(symbol.name, 0);2152 const name = mem.sliceTo(symbol.name, 0);
2117 try leb.writeULEB128(writer, @intCast(u32, name.len));2153 try leb.writeULEB128(writer, @intCast(u32, name.len));
2118 try writer.writeAll(name);2154 try writer.writeAll(name);
21192155 }
2120 if (symbol.isDefined()) {2156 },
2121 try leb.writeULEB128(writer, symbol.index);
2122 try leb.writeULEB128(writer, @as(u32, current_atom.offset));
2123 try leb.writeULEB128(writer, @as(u32, current_atom.size));
2124 }
2125 },
2126 .section => {
2127 try leb.writeULEB128(writer, symbol.index);
2128 },
2129 else => {
2130 try leb.writeULEB128(writer, symbol.index);
2131 if (symbol.isDefined()) {
2132 const name = mem.sliceTo(symbol.name, 0);
2133 try leb.writeULEB128(writer, @intCast(u32, name.len));
2134 try writer.writeAll(name);
2135 }
2136 },
2137 }
2138 atom = current_atom.next orelse break;
2139 }2157 }
2140 }2158 }
21412159
...@@ -2176,6 +2194,109 @@ fn emitSegmentInfo(self: *Wasm, file: fs.File, arena: Allocator) !void {...@@ -2176,6 +2194,109 @@ fn emitSegmentInfo(self: *Wasm, file: fs.File, arena: Allocator) !void {
2176 try file.writevAll(&.{iovec});2194 try file.writevAll(&.{iovec});
2177}2195}
21782196
2197/// For each relocatable section, emits a custom "relocation.<section_name>" section
2198fn emitCodeRelocations(
2199 self: *Wasm,
2200 file: fs.File,
2201 arena: Allocator,
2202 section_index: u32,
2203 symbol_table: std.AutoArrayHashMap(SymbolLoc, u32),
2204) !void {
2205 const code_index = self.code_section_index orelse return;
2206 var payload = std.ArrayList(u8).init(arena);
2207 const writer = payload.writer();
2208
2209 // write custom section information
2210 const name = "reloc.CODE";
2211 try leb.writeULEB128(writer, @intCast(u32, name.len));
2212 try writer.writeAll(name);
2213 try leb.writeULEB128(writer, section_index);
2214 const reloc_start = payload.items.len;
2215
2216 var count: u32 = 0;
2217 var atom: *Atom = self.atoms.get(code_index).?.getFirst();
2218 while (true) {
2219 for (atom.relocs.items) |relocation| {
2220 count += 1;
2221 const sym_loc: SymbolLoc = .{ .file = atom.file, .index = relocation.index };
2222 const symbol_index = symbol_table.get(sym_loc).?;
2223 try leb.writeULEB128(writer, @enumToInt(relocation.relocation_type));
2224 try leb.writeULEB128(writer, atom.offset + relocation.offset);
2225 try leb.writeULEB128(writer, symbol_index);
2226 if (relocation.relocation_type.addendIsPresent()) {
2227 try leb.writeULEB128(writer, relocation.addend orelse 0);
2228 }
2229 }
2230 atom = atom.next orelse break;
2231 }
2232 if (count == 0) return;
2233 var buf: [5]u8 = undefined;
2234 leb.writeUnsignedFixed(5, &buf, count);
2235 try payload.insertSlice(reloc_start, &buf);
2236 const iovec: std.os.iovec_const = .{
2237 .iov_base = payload.items.ptr,
2238 .iov_len = payload.items.len,
2239 };
2240 const header_offset = try reserveCustomSectionHeader(file);
2241 try file.writevAll(&.{iovec});
2242 const size = @intCast(u32, payload.items.len);
2243 try writeCustomSectionHeader(file, header_offset, size);
2244}
2245
2246fn emitDataRelocations(
2247 self: *Wasm,
2248 file: fs.File,
2249 arena: Allocator,
2250 section_index: u32,
2251 symbol_table: std.AutoArrayHashMap(SymbolLoc, u32),
2252) !void {
2253 if (self.data_segments.count() == 0) return;
2254 var payload = std.ArrayList(u8).init(arena);
2255 const writer = payload.writer();
2256
2257 // write custom section information
2258 const name = "reloc.DATA";
2259 try leb.writeULEB128(writer, @intCast(u32, name.len));
2260 try writer.writeAll(name);
2261 try leb.writeULEB128(writer, section_index);
2262 const reloc_start = payload.items.len;
2263
2264 var count: u32 = 0;
2265 for (self.data_segments.values()) |segment_index| {
2266 var atom: *Atom = self.atoms.get(segment_index).?.getFirst();
2267 while (true) {
2268 for (atom.relocs.items) |relocation| {
2269 count += 1;
2270 const sym_loc: SymbolLoc = .{
2271 .file = atom.file,
2272 .index = relocation.index,
2273 };
2274 const symbol_index = symbol_table.get(sym_loc).?;
2275 try leb.writeULEB128(writer, @enumToInt(relocation.relocation_type));
2276 try leb.writeULEB128(writer, atom.offset + relocation.offset);
2277 try leb.writeULEB128(writer, symbol_index);
2278 if (relocation.relocation_type.addendIsPresent()) {
2279 try leb.writeULEB128(writer, relocation.addend orelse 0);
2280 }
2281 }
2282 atom = atom.next orelse break;
2283 }
2284 }
2285 if (count == 0) return;
2286
2287 var buf: [5]u8 = undefined;
2288 leb.writeUnsignedFixed(5, &buf, count);
2289 try payload.insertSlice(reloc_start, &buf);
2290 const iovec: std.os.iovec_const = .{
2291 .iov_base = payload.items.ptr,
2292 .iov_len = payload.items.len,
2293 };
2294 const header_offset = try reserveCustomSectionHeader(file);
2295 try file.writevAll(&.{iovec});
2296 const size = @intCast(u32, payload.items.len);
2297 try writeCustomSectionHeader(file, header_offset, size);
2298}
2299
2179/// Searches for an a matching function signature, when not found2300/// Searches for an a matching function signature, when not found
2180/// a new entry will be made. The index of the existing/new signature will be returned.2301/// a new entry will be made. The index of the existing/new signature will be returned.
2181pub fn putOrGetFuncType(self: *Wasm, func_type: wasm.Type) !u32 {2302pub fn putOrGetFuncType(self: *Wasm, func_type: wasm.Type) !u32 {
src/link/Wasm/Atom.zig+8-13
...@@ -94,12 +94,16 @@ pub fn symbolAtom(self: *Atom, symbol_index: u32) *Atom {...@@ -94,12 +94,16 @@ pub fn symbolAtom(self: *Atom, symbol_index: u32) *Atom {
94 } else unreachable; // Used a symbol index not present in this atom or its children.94 } else unreachable; // Used a symbol index not present in this atom or its children.
95}95}
9696
97/// Returns the location of the symbol that represents this `Atom`
98pub fn symbolLoc(self: Atom) Wasm.SymbolLoc {
99 return .{ .file = self.file, .index = self.sym_index };
100}
101
97/// Resolves the relocations within the atom, writing the new value102/// Resolves the relocations within the atom, writing the new value
98/// at the calculated offset.103/// at the calculated offset.
99pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) !void {104pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) !void {
100 if (self.relocs.items.len == 0) return;105 if (self.relocs.items.len == 0) return;
101 const loc: Wasm.SymbolLoc = .{ .file = self.file, .index = self.sym_index };106 const symbol = self.symbolLoc().getSymbol(wasm_bin).*;
102 const symbol = loc.getSymbol(wasm_bin).*;
103 log.debug("Resolving relocs in atom '{s}' count({d})", .{107 log.debug("Resolving relocs in atom '{s}' count({d})", .{
104 symbol.name,108 symbol.name,
105 self.relocs.items.len,109 self.relocs.items.len,
...@@ -169,20 +173,11 @@ fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wa...@@ -169,20 +173,11 @@ fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wa
169 if (symbol.isUndefined() and (symbol.tag == .data or symbol.isWeak())) {173 if (symbol.isUndefined() and (symbol.tag == .data or symbol.isWeak())) {
170 return 0;174 return 0;
171 }175 }
176
172 const merge_segment = wasm_bin.base.options.output_mode != .Obj;177 const merge_segment = wasm_bin.base.options.output_mode != .Obj;
173 const segment_name = wasm_bin.segment_info.items[symbol.index].outputName(merge_segment);178 const segment_name = wasm_bin.segment_info.items[symbol.index].outputName(merge_segment);
174 const atom_index = wasm_bin.data_segments.get(segment_name).?;179 const atom_index = wasm_bin.data_segments.get(segment_name).?;
175 var target_atom = wasm_bin.atoms.getPtr(atom_index).?.*.getFirst();180 const target_atom = wasm_bin.symbol_atom.get(target_loc).?;
176 while (true) {
177 // TODO: Can we simplify this by providing the ability to find an atom
178 // based on a symbol location.
179 if (target_atom.sym_index == relocation.index) {
180 if (target_atom.file) |file| {
181 if (self.file != null and self.file.? == file) break;
182 } else if (self.file == null) break;
183 }
184 target_atom = target_atom.next orelse break;
185 }
186 const segment = wasm_bin.segments.items[atom_index];181 const segment = wasm_bin.segments.items[atom_index];
187 return target_atom.offset + segment.offset + (relocation.addend orelse 0);182 return target_atom.offset + segment.offset + (relocation.addend orelse 0);
188 },183 },