authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-02-18 21:47:57+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-02-23 16:07:36+01:00
log2b0431a8d3933a46fecba2bf064bf45df54a111d
tree1ec5e1afa83d482e751e603488e8bf19cc7fa8bc
parentdaf741318e51c9eea38cad80c996536093f0fcef

wasm-linker: Do not merge data segments for obj

When creating a relocatable object file, we do no longer perform the following actions: - Merge data segments - Calculate stack size - Relocations We now also make the stack pointer symbol `undefined` for this use case as well as add the symbol as an import.

3 files changed, 129 insertions(+), 93 deletions(-)

src/link/Wasm.zig+114-80
...@@ -163,14 +163,6 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option...@@ -163,14 +163,6 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
163 try file.writeAll(&(wasm.magic ++ wasm.version));163 try file.writeAll(&(wasm.magic ++ wasm.version));
164164
165 // As sym_index '0' is reserved, we use it for our stack pointer symbol165 // As sym_index '0' is reserved, we use it for our stack pointer symbol
166 const global = try wasm_bin.wasm_globals.addOne(allocator);
167 global.* = .{
168 .global_type = .{
169 .valtype = .i32,
170 .mutable = true,
171 },
172 .init = .{ .i32_const = 0 },
173 };
174 const symbol = try wasm_bin.symbols.addOne(allocator);166 const symbol = try wasm_bin.symbols.addOne(allocator);
175 symbol.* = .{167 symbol.* = .{
176 .name = "__stack_pointer",168 .name = "__stack_pointer",
...@@ -178,6 +170,28 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option...@@ -178,6 +170,28 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
178 .flags = 0,170 .flags = 0,
179 .index = 0,171 .index = 0,
180 };172 };
173 // For object files we will import the stack pointer symbol
174 if (options.output_mode == .Obj) {
175 symbol.setUndefined(true);
176 try wasm_bin.imports.putNoClobber(
177 allocator,
178 .{ .file = null, .index = 0 },
179 .{
180 .module_name = wasm_bin.host_name,
181 .name = "__stack_pointer",
182 .kind = .{ .global = .{ .valtype = .i32, .mutable = true } },
183 },
184 );
185 } else {
186 const global = try wasm_bin.wasm_globals.addOne(allocator);
187 global.* = .{
188 .global_type = .{
189 .valtype = .i32,
190 .mutable = true,
191 },
192 .init = .{ .i32_const = 0 },
193 };
194 }
181 return wasm_bin;195 return wasm_bin;
182}196}
183197
...@@ -651,36 +665,50 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void {...@@ -651,36 +665,50 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void {
651 break :result self.code_section_index.?;665 break :result self.code_section_index.?;
652 },666 },
653 .data => result: {667 .data => result: {
654 const gop = try self.data_segments.getOrPut(self.base.allocator, ".rodata");
655 const atom_index = if (gop.found_existing) blk: {
656 self.segments.items[gop.value_ptr.*].size += atom.size;
657 break :blk gop.value_ptr.*;
658 } else blk: {
659 const index = @intCast(u32, self.segments.items.len);
660 try self.segments.append(self.base.allocator, .{
661 .alignment = atom.alignment,
662 .size = 0,
663 .offset = 0,
664 });
665 gop.value_ptr.* = index;
666 break :blk index;
667 };
668 const info_index = @intCast(u32, self.segment_info.items.len);
669 // TODO: Add mutables global decls to .bss section instead668 // TODO: Add mutables global decls to .bss section instead
670 const segment_name = try std.mem.concat(self.base.allocator, u8, &.{669 const segment_name = try std.mem.concat(self.base.allocator, u8, &.{
671 ".rodata.",670 ".rodata.",
672 std.mem.span(symbol.name),671 std.mem.span(symbol.name),
673 });672 });
674 errdefer self.base.allocator.free(segment_name);673 errdefer self.base.allocator.free(segment_name);
675 try self.segment_info.append(self.base.allocator, .{674 const segment_info: types.Segment = .{
676 .name = segment_name,675 .name = segment_name,
677 .alignment = atom.alignment,676 .alignment = atom.alignment,
678 .flags = 0,677 .flags = 0,
679 });678 };
680 symbol.tag = .data;679 symbol.tag = .data;
681 symbol.index = info_index;
682680
683 break :result atom_index;681 const should_merge = self.base.options.output_mode != .Obj;
682 const gop = try self.data_segments.getOrPut(self.base.allocator, segment_info.outputName(should_merge));
683 if (gop.found_existing) {
684 const index = gop.value_ptr.*;
685 self.segments.items[index].size += atom.size;
686
687 // segment indexes can be off by 1 due to also containing a segment
688 // for the code section, so we must check if the existing segment
689 // is larger than that of the code section, and substract the index by 1 in such case.
690 const info_add = if (self.code_section_index) |idx| blk: {
691 if (idx < index) break :blk @as(u32, 1);
692 break :blk 0;
693 } else @as(u32, 0);
694 symbol.index = index - info_add;
695 // segment info already exists, so free its memory
696 self.base.allocator.free(segment_name);
697 break :result index;
698 } else {
699 const index = @intCast(u32, self.segments.items.len);
700 try self.segments.append(self.base.allocator, .{
701 .alignment = atom.alignment,
702 .size = 0,
703 .offset = 0,
704 });
705 gop.value_ptr.* = index;
706
707 const info_index = @intCast(u32, self.segment_info.items.len);
708 try self.segment_info.append(self.base.allocator, segment_info);
709 symbol.index = info_index;
710 break :result index;
711 }
684 },712 },
685 };713 };
686714
...@@ -932,7 +960,9 @@ fn setupMemory(self: *Wasm) !void {...@@ -932,7 +960,9 @@ fn setupMemory(self: *Wasm) !void {
932 break :blk base;960 break :blk base;
933 } else 0;961 } else 0;
934962
935 if (place_stack_first) {963 const is_obj = self.base.options.output_mode == .Obj;
964
965 if (place_stack_first and !is_obj) {
936 memory_ptr = std.mem.alignForwardGeneric(u64, memory_ptr, stack_alignment);966 memory_ptr = std.mem.alignForwardGeneric(u64, memory_ptr, stack_alignment);
937 memory_ptr += stack_size;967 memory_ptr += stack_size;
938 // We always put the stack pointer global at index 0968 // We always put the stack pointer global at index 0
...@@ -951,7 +981,7 @@ fn setupMemory(self: *Wasm) !void {...@@ -951,7 +981,7 @@ fn setupMemory(self: *Wasm) !void {
951 offset += segment.size;981 offset += segment.size;
952 }982 }
953983
954 if (!place_stack_first) {984 if (!place_stack_first and !is_obj) {
955 memory_ptr = std.mem.alignForwardGeneric(u64, memory_ptr, stack_alignment);985 memory_ptr = std.mem.alignForwardGeneric(u64, memory_ptr, stack_alignment);
956 memory_ptr += stack_size;986 memory_ptr += stack_size;
957 self.wasm_globals.items[0].init.i32_const = @bitCast(i32, @intCast(u32, memory_ptr));987 self.wasm_globals.items[0].init.i32_const = @bitCast(i32, @intCast(u32, memory_ptr));
...@@ -1011,7 +1041,8 @@ pub fn getMatchingSegment(self: *Wasm, object_index: u16, relocatable_index: u32...@@ -1011,7 +1041,8 @@ pub fn getMatchingSegment(self: *Wasm, object_index: u16, relocatable_index: u32
1011 switch (relocatable_data.type) {1041 switch (relocatable_data.type) {
1012 .data => {1042 .data => {
1013 const segment_info = object.segment_info[relocatable_data.index];1043 const segment_info = object.segment_info[relocatable_data.index];
1014 const result = try self.data_segments.getOrPut(self.base.allocator, segment_info.outputName());1044 const merge_segment = self.base.options.output_mode != .Obj;
1045 const result = try self.data_segments.getOrPut(self.base.allocator, segment_info.outputName(merge_segment));
1015 if (!result.found_existing) {1046 if (!result.found_existing) {
1016 result.value_ptr.* = index;1047 result.value_ptr.* = index;
1017 try self.segments.append(self.base.allocator, .{1048 try self.segments.append(self.base.allocator, .{
...@@ -1368,7 +1399,9 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {...@@ -1368,7 +1399,9 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
1368 const writer = file.writer();1399 const writer = file.writer();
1369 var atom: *Atom = self.atoms.get(code_index).?.getFirst();1400 var atom: *Atom = self.atoms.get(code_index).?.getFirst();
1370 while (true) {1401 while (true) {
1371 try atom.resolveRelocs(self);1402 if (!is_obj) {
1403 try atom.resolveRelocs(self);
1404 }
1372 try leb.writeULEB128(writer, atom.size);1405 try leb.writeULEB128(writer, atom.size);
1373 try writer.writeAll(atom.code.items);1406 try writer.writeAll(atom.code.items);
1374 atom = atom.next orelse break;1407 atom = atom.next orelse break;
...@@ -1390,8 +1423,9 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {...@@ -1390,8 +1423,9 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
1390 var it = self.data_segments.iterator();1423 var it = self.data_segments.iterator();
1391 var segment_count: u32 = 0;1424 var segment_count: u32 = 0;
1392 while (it.next()) |entry| {1425 while (it.next()) |entry| {
1393 // do not output 'bss' section1426 // do not output 'bss' section unless we import memory and therefore
1394 if (std.mem.eql(u8, entry.key_ptr.*, ".bss")) continue;1427 // want to guarantee the data is zero initialized
1428 if (std.mem.eql(u8, entry.key_ptr.*, ".bss") and !import_memory) continue;
1395 segment_count += 1;1429 segment_count += 1;
1396 const atom_index = entry.value_ptr.*;1430 const atom_index = entry.value_ptr.*;
1397 var atom: *Atom = self.atoms.getPtr(atom_index).?.*.getFirst();1431 var atom: *Atom = self.atoms.getPtr(atom_index).?.*.getFirst();
...@@ -1406,7 +1440,9 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {...@@ -1406,7 +1440,9 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
1406 // fill in the offset table and the data segments1440 // fill in the offset table and the data segments
1407 var current_offset: u32 = 0;1441 var current_offset: u32 = 0;
1408 while (true) {1442 while (true) {
1409 try atom.resolveRelocs(self);1443 if (!is_obj) {
1444 try atom.resolveRelocs(self);
1445 }
14101446
1411 // Pad with zeroes to ensure all segments are aligned1447 // Pad with zeroes to ensure all segments are aligned
1412 if (current_offset != atom.offset) {1448 if (current_offset != atom.offset) {
...@@ -1443,60 +1479,58 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {...@@ -1443,60 +1479,58 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
1443 );1479 );
1444 }1480 }
14451481
1446 // Custom section "name" which contains symbol names1482 if (is_obj) {
1447 if (!is_obj) {1483 try self.emitLinkSection(file, arena);
1448 const Name = struct {1484 } else {
1449 index: u32,1485 try self.emitNameSection(file, arena);
1450 name: []const u8,1486 }
14511487}
1452 fn lessThan(context: void, lhs: @This(), rhs: @This()) bool {
1453 _ = context;
1454 return lhs.index < rhs.index;
1455 }
1456 };
14571488
1458 var funcs = try std.ArrayList(Name).initCapacity(self.base.allocator, self.functions.items.len + self.imported_functions_count);1489fn emitNameSection(self: *Wasm, file: fs.File, arena: Allocator) !void {
1459 defer funcs.deinit();1490 const Name = struct {
1460 var globals = try std.ArrayList(Name).initCapacity(self.base.allocator, self.wasm_globals.items.len);1491 index: u32,
1461 defer globals.deinit();1492 name: []const u8,
1462 var segments = try std.ArrayList(Name).initCapacity(self.base.allocator, self.data_segments.count());
1463 defer segments.deinit();
14641493
1465 for (self.resolved_symbols.keys()) |sym_loc| {1494 fn lessThan(context: void, lhs: @This(), rhs: @This()) bool {
1466 const symbol = sym_loc.getSymbol(self).*;1495 _ = context;
1467 switch (symbol.tag) {1496 return lhs.index < rhs.index;
1468 .function => funcs.appendAssumeCapacity(.{ .index = symbol.index, .name = mem.sliceTo(symbol.name, 0) }),
1469 .global => globals.appendAssumeCapacity(.{ .index = symbol.index, .name = mem.sliceTo(symbol.name, 0) }),
1470 else => {},
1471 }
1472 }
1473 // data segments are already 'ordered'
1474 for (self.data_segments.keys()) |key, index| {
1475 segments.appendAssumeCapacity(.{ .index = @intCast(u32, index), .name = key });
1476 }1497 }
1498 };
14771499
1478 std.sort.sort(Name, funcs.items, {}, Name.lessThan);1500 var funcs = try std.ArrayList(Name).initCapacity(arena, self.functions.items.len + self.imported_functions_count);
1479 std.sort.sort(Name, globals.items, {}, Name.lessThan);1501 var globals = try std.ArrayList(Name).initCapacity(arena, self.wasm_globals.items.len);
1502 var segments = try std.ArrayList(Name).initCapacity(arena, self.data_segments.count());
14801503
1481 const header_offset = try reserveCustomSectionHeader(file);1504 for (self.resolved_symbols.keys()) |sym_loc| {
1482 const writer = file.writer();1505 const symbol = sym_loc.getSymbol(self).*;
1483 try leb.writeULEB128(writer, @intCast(u32, "name".len));1506 switch (symbol.tag) {
1484 try writer.writeAll("name");1507 .function => funcs.appendAssumeCapacity(.{ .index = symbol.index, .name = mem.sliceTo(symbol.name, 0) }),
1508 .global => globals.appendAssumeCapacity(.{ .index = symbol.index, .name = mem.sliceTo(symbol.name, 0) }),
1509 else => {},
1510 }
1511 }
1512 // data segments are already 'ordered'
1513 for (self.data_segments.keys()) |key, index| {
1514 segments.appendAssumeCapacity(.{ .index = @intCast(u32, index), .name = key });
1515 }
14851516
1486 try self.emitNameSubsection(.function, funcs.items, writer);1517 std.sort.sort(Name, funcs.items, {}, Name.lessThan);
1487 try self.emitNameSubsection(.global, globals.items, writer);1518 std.sort.sort(Name, globals.items, {}, Name.lessThan);
1488 try self.emitNameSubsection(.data_segment, segments.items, writer);
14891519
1490 try writeCustomSectionHeader(1520 const header_offset = try reserveCustomSectionHeader(file);
1491 file,1521 const writer = file.writer();
1492 header_offset,1522 try leb.writeULEB128(writer, @intCast(u32, "name".len));
1493 @intCast(u32, (try file.getPos()) - header_offset - header_size),1523 try writer.writeAll("name");
1494 );
1495 }
14961524
1497 if (is_obj) {1525 try self.emitNameSubsection(.function, funcs.items, writer);
1498 try self.emitLinkSection(file, arena);1526 try self.emitNameSubsection(.global, globals.items, writer);
1499 }1527 try self.emitNameSubsection(.data_segment, segments.items, writer);
1528
1529 try writeCustomSectionHeader(
1530 file,
1531 header_offset,
1532 @intCast(u32, (try file.getPos()) - header_offset - 6),
1533 );
1500}1534}
15011535
1502fn emitNameSubsection(self: *Wasm, section_id: std.wasm.NameSubsection, names: anytype, writer: anytype) !void {1536fn emitNameSubsection(self: *Wasm, section_id: std.wasm.NameSubsection, names: anytype, writer: anytype) !void {
src/link/Wasm/Atom.zig+13-12
...@@ -147,33 +147,34 @@ pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) !void {...@@ -147,33 +147,34 @@ pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) !void {
147fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wasm) !u64 {147fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wasm) !u64 {
148 const target_loc: Wasm.SymbolLoc = .{ .file = self.file, .index = relocation.index };148 const target_loc: Wasm.SymbolLoc = .{ .file = self.file, .index = relocation.index };
149 const symbol = target_loc.getSymbol(wasm_bin).*;149 const symbol = target_loc.getSymbol(wasm_bin).*;
150 return switch (relocation.relocation_type) {150 switch (relocation.relocation_type) {
151 .R_WASM_FUNCTION_INDEX_LEB => symbol.index,151 .R_WASM_FUNCTION_INDEX_LEB => return symbol.index,
152 .R_WASM_TABLE_NUMBER_LEB => symbol.index,152 .R_WASM_TABLE_NUMBER_LEB => return symbol.index,
153 .R_WASM_TABLE_INDEX_I32,153 .R_WASM_TABLE_INDEX_I32,
154 .R_WASM_TABLE_INDEX_I64,154 .R_WASM_TABLE_INDEX_I64,
155 .R_WASM_TABLE_INDEX_SLEB,155 .R_WASM_TABLE_INDEX_SLEB,
156 .R_WASM_TABLE_INDEX_SLEB64,156 .R_WASM_TABLE_INDEX_SLEB64,
157 => return wasm_bin.function_table.get(relocation.index) orelse 0,157 => return wasm_bin.function_table.get(relocation.index) orelse 0,
158 .R_WASM_TYPE_INDEX_LEB => wasm_bin.functions.items[symbol.index].type_index,158 .R_WASM_TYPE_INDEX_LEB => return wasm_bin.functions.items[symbol.index].type_index,
159 .R_WASM_GLOBAL_INDEX_I32,159 .R_WASM_GLOBAL_INDEX_I32,
160 .R_WASM_GLOBAL_INDEX_LEB,160 .R_WASM_GLOBAL_INDEX_LEB,
161 => symbol.index,161 => return symbol.index,
162 .R_WASM_MEMORY_ADDR_I32,162 .R_WASM_MEMORY_ADDR_I32,
163 .R_WASM_MEMORY_ADDR_I64,163 .R_WASM_MEMORY_ADDR_I64,
164 .R_WASM_MEMORY_ADDR_LEB,164 .R_WASM_MEMORY_ADDR_LEB,
165 .R_WASM_MEMORY_ADDR_LEB64,165 .R_WASM_MEMORY_ADDR_LEB64,
166 .R_WASM_MEMORY_ADDR_SLEB,166 .R_WASM_MEMORY_ADDR_SLEB,
167 .R_WASM_MEMORY_ADDR_SLEB64,167 .R_WASM_MEMORY_ADDR_SLEB64,
168 => blk: {168 => {
169 if (symbol.isUndefined() and (symbol.tag == .data or symbol.isWeak())) {169 if (symbol.isUndefined() and (symbol.tag == .data or symbol.isWeak())) {
170 return 0;170 return 0;
171 }171 }
172 const segment_name = wasm_bin.segment_info.items[symbol.index].outputName();172 const merge_segment = wasm_bin.base.options.output_mode != .Obj;
173 const segment_name = wasm_bin.segment_info.items[symbol.index].outputName(merge_segment);
173 const atom_index = wasm_bin.data_segments.get(segment_name).?;174 const atom_index = wasm_bin.data_segments.get(segment_name).?;
174 var target_atom = wasm_bin.atoms.getPtr(atom_index).?.*.getFirst();175 var target_atom = wasm_bin.atoms.getPtr(atom_index).?.*.getFirst();
175 while (true) {176 while (true) {
176 // TODO: Can we simplify this by providing the ability to find and atom177 // TODO: Can we simplify this by providing the ability to find an atom
177 // based on a symbol location.178 // based on a symbol location.
178 if (target_atom.sym_index == relocation.index) {179 if (target_atom.sym_index == relocation.index) {
179 if (target_atom.file) |file| {180 if (target_atom.file) |file| {
...@@ -183,11 +184,11 @@ fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wa...@@ -183,11 +184,11 @@ fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wa
183 target_atom = target_atom.next orelse break;184 target_atom = target_atom.next orelse break;
184 }185 }
185 const segment = wasm_bin.segments.items[atom_index];186 const segment = wasm_bin.segments.items[atom_index];
186 break :blk target_atom.offset + segment.offset + (relocation.addend orelse 0);187 return target_atom.offset + segment.offset + (relocation.addend orelse 0);
187 },188 },
188 .R_WASM_EVENT_INDEX_LEB => symbol.index,189 .R_WASM_EVENT_INDEX_LEB => return symbol.index,
189 .R_WASM_SECTION_OFFSET_I32,190 .R_WASM_SECTION_OFFSET_I32,
190 .R_WASM_FUNCTION_OFFSET_I32,191 .R_WASM_FUNCTION_OFFSET_I32,
191 => relocation.offset,192 => return relocation.offset,
192 };193 }
193}194}
src/link/Wasm/types.zig+2-1
...@@ -93,7 +93,8 @@ pub const Segment = struct {...@@ -93,7 +93,8 @@ pub const Segment = struct {
93 /// Bitfield containing flags for a segment93 /// Bitfield containing flags for a segment
94 flags: u32,94 flags: u32,
9595
96 pub fn outputName(self: Segment) []const u8 {96 pub fn outputName(self: Segment, merge_segments: bool) []const u8 {
97 if (!merge_segments) return self.name;
97 if (std.mem.startsWith(u8, self.name, ".rodata.")) {98 if (std.mem.startsWith(u8, self.name, ".rodata.")) {
98 return ".rodata";99 return ".rodata";
99 } else if (std.mem.startsWith(u8, self.name, ".text.")) {100 } else if (std.mem.startsWith(u8, self.name, ".text.")) {