authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-06-25 22:51:06+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-06-25 22:51:06+02:00
log08459ff1c21d546c55e2ae954126e121ee88972e
tree49d16fc3e374e6a163572aeeb0fedcbf588ef996
parent960c14206094484cee056b8806b9b8f75a84891d
parent140bac6395610a3b9e5d5f92e2b366a27f6dcdc8
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #11933 from Luukdegram/wasm-link-bss

stage2: wasm-linker - Place decls in the correct segment and order segments

2 files changed, 78 insertions(+), 12 deletions(-)

src/link/Wasm.zig+75-10
...@@ -1143,9 +1143,29 @@ pub fn addOrUpdateImport(...@@ -1143,9 +1143,29 @@ pub fn addOrUpdateImport(
1143 } else @panic("TODO: Implement undefined symbols for non-function declarations");1143 } else @panic("TODO: Implement undefined symbols for non-function declarations");
1144}1144}
11451145
1146/// Kind represents the type of an Atom, which is only
1147/// used to parse a decl into an Atom to define in which section
1148/// or segment it should be placed.
1146const Kind = union(enum) {1149const Kind = union(enum) {
1147 data: void,1150 /// Represents the segment the data symbol should
1151 /// be inserted into.
1152 /// TODO: Add TLS segments
1153 data: enum {
1154 read_only,
1155 uninitialized,
1156 initialized,
1157 },
1148 function: FnData,1158 function: FnData,
1159
1160 /// Returns the segment name the data kind represents.
1161 /// Asserts `kind` has its active tag set to `data`.
1162 fn segmentName(kind: Kind) []const u8 {
1163 switch (kind.data) {
1164 .read_only => return ".rodata.",
1165 .uninitialized => return ".bss.",
1166 .initialized => return ".data.",
1167 }
1168 }
1149};1169};
11501170
1151/// Parses an Atom and inserts its metadata into the corresponding sections.1171/// Parses an Atom and inserts its metadata into the corresponding sections.
...@@ -1174,9 +1194,8 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void {...@@ -1174,9 +1194,8 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void {
1174 break :result self.code_section_index.?;1194 break :result self.code_section_index.?;
1175 },1195 },
1176 .data => result: {1196 .data => result: {
1177 // TODO: Add mutables global decls to .bss section instead
1178 const segment_name = try std.mem.concat(self.base.allocator, u8, &.{1197 const segment_name = try std.mem.concat(self.base.allocator, u8, &.{
1179 ".rodata.",1198 kind.segmentName(),
1180 self.string_table.get(symbol.name),1199 self.string_table.get(symbol.name),
1181 });1200 });
1182 errdefer self.base.allocator.free(segment_name);1201 errdefer self.base.allocator.free(segment_name);
...@@ -1236,6 +1255,9 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void {...@@ -1236,6 +1255,9 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void {
1236}1255}
12371256
1238fn allocateAtoms(self: *Wasm) !void {1257fn allocateAtoms(self: *Wasm) !void {
1258 // first sort the data segments
1259 try sortDataSegments(self);
1260
1239 var it = self.atoms.iterator();1261 var it = self.atoms.iterator();
1240 while (it.next()) |entry| {1262 while (it.next()) |entry| {
1241 const segment = &self.segments.items[entry.key_ptr.*];1263 const segment = &self.segments.items[entry.key_ptr.*];
...@@ -1259,6 +1281,36 @@ fn allocateAtoms(self: *Wasm) !void {...@@ -1259,6 +1281,36 @@ fn allocateAtoms(self: *Wasm) !void {
1259 }1281 }
1260}1282}
12611283
1284fn sortDataSegments(self: *Wasm) !void {
1285 var new_mapping: std.StringArrayHashMapUnmanaged(u32) = .{};
1286 try new_mapping.ensureUnusedCapacity(self.base.allocator, self.data_segments.count());
1287 errdefer new_mapping.deinit(self.base.allocator);
1288
1289 const keys = try self.base.allocator.dupe([]const u8, self.data_segments.keys());
1290 defer self.base.allocator.free(keys);
1291
1292 const SortContext = struct {
1293 fn sort(_: void, lhs: []const u8, rhs: []const u8) bool {
1294 return order(lhs) <= order(rhs);
1295 }
1296
1297 fn order(name: []const u8) u8 {
1298 if (mem.startsWith(u8, name, ".rodata")) return 0;
1299 if (mem.startsWith(u8, name, ".data")) return 1;
1300 if (mem.startsWith(u8, name, ".text")) return 2;
1301 return 3;
1302 }
1303 };
1304
1305 std.sort.sort([]const u8, keys, {}, SortContext.sort);
1306 for (keys) |key| {
1307 const segment_index = self.data_segments.get(key).?;
1308 new_mapping.putAssumeCapacity(key, segment_index);
1309 }
1310 self.data_segments.deinit(self.base.allocator);
1311 self.data_segments = new_mapping;
1312}
1313
1262fn setupImports(self: *Wasm) !void {1314fn setupImports(self: *Wasm) !void {
1263 log.debug("Merging imports", .{});1315 log.debug("Merging imports", .{});
1264 var discarded_it = self.discarded.keyIterator();1316 var discarded_it = self.discarded.keyIterator();
...@@ -1722,8 +1774,8 @@ fn populateErrorNameTable(self: *Wasm) !void {...@@ -1722,8 +1774,8 @@ fn populateErrorNameTable(self: *Wasm) !void {
17221774
1723 // link the atoms with the rest of the binary so they can be allocated1775 // link the atoms with the rest of the binary so they can be allocated
1724 // and relocations will be performed.1776 // and relocations will be performed.
1725 try self.parseAtom(atom, .data);1777 try self.parseAtom(atom, .{ .data = .read_only });
1726 try self.parseAtom(names_atom, .data);1778 try self.parseAtom(names_atom, .{ .data = .read_only });
1727}1779}
17281780
1729pub fn getDebugInfoIndex(self: *Wasm) !u32 {1781pub fn getDebugInfoIndex(self: *Wasm) !u32 {
...@@ -1866,13 +1918,21 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod...@@ -1866,13 +1918,21 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
1866 const atom = &decl.*.link.wasm;1918 const atom = &decl.*.link.wasm;
1867 if (decl.ty.zigTypeTag() == .Fn) {1919 if (decl.ty.zigTypeTag() == .Fn) {
1868 try self.parseAtom(atom, .{ .function = decl.fn_link.wasm });1920 try self.parseAtom(atom, .{ .function = decl.fn_link.wasm });
1921 } else if (decl.getVariable()) |variable| {
1922 if (!variable.is_mutable) {
1923 try self.parseAtom(atom, .{ .data = .read_only });
1924 } else if (variable.init.isUndefDeep()) {
1925 try self.parseAtom(atom, .{ .data = .uninitialized });
1926 } else {
1927 try self.parseAtom(atom, .{ .data = .initialized });
1928 }
1869 } else {1929 } else {
1870 try self.parseAtom(atom, .data);1930 try self.parseAtom(atom, .{ .data = .read_only });
1871 }1931 }
18721932
1873 // also parse atoms for a decl's locals1933 // also parse atoms for a decl's locals
1874 for (atom.locals.items) |*local_atom| {1934 for (atom.locals.items) |*local_atom| {
1875 try self.parseAtom(local_atom, .data);1935 try self.parseAtom(local_atom, .{ .data = .read_only });
1876 }1936 }
1877 }1937 }
18781938
...@@ -2167,7 +2227,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod...@@ -2167,7 +2227,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
2167 while (it.next()) |entry| {2227 while (it.next()) |entry| {
2168 // do not output 'bss' section unless we import memory and therefore2228 // do not output 'bss' section unless we import memory and therefore
2169 // want to guarantee the data is zero initialized2229 // want to guarantee the data is zero initialized
2170 if (std.mem.eql(u8, entry.key_ptr.*, ".bss") and !import_memory) continue;2230 if (!import_memory and std.mem.eql(u8, entry.key_ptr.*, ".bss")) continue;
2171 segment_count += 1;2231 segment_count += 1;
2172 const atom_index = entry.value_ptr.*;2232 const atom_index = entry.value_ptr.*;
2173 var atom: *Atom = self.atoms.getPtr(atom_index).?.*.getFirst();2233 var atom: *Atom = self.atoms.getPtr(atom_index).?.*.getFirst();
...@@ -2310,8 +2370,13 @@ fn emitNameSection(self: *Wasm, file: fs.File, arena: Allocator) !void {...@@ -2310,8 +2370,13 @@ fn emitNameSection(self: *Wasm, file: fs.File, arena: Allocator) !void {
2310 }2370 }
2311 }2371 }
2312 // data segments are already 'ordered'2372 // data segments are already 'ordered'
2313 for (self.data_segments.keys()) |key, index| {2373 var data_segment_index: u32 = 0;
2314 segments.appendAssumeCapacity(.{ .index = @intCast(u32, index), .name = key });2374 for (self.data_segments.keys()) |key| {
2375 // bss section is not emitted when this condition holds true, so we also
2376 // do not output a name for it.
2377 if (!self.base.options.import_memory and std.mem.eql(u8, key, ".bss")) continue;
2378 segments.appendAssumeCapacity(.{ .index = data_segment_index, .name = key });
2379 data_segment_index += 1;
2315 }2380 }
23162381
2317 std.sort.sort(Name, funcs.values(), {}, Name.lessThan);2382 std.sort.sort(Name, funcs.values(), {}, Name.lessThan);
src/link/Wasm/types.zig+3-2
...@@ -125,14 +125,15 @@ pub const Segment = struct {...@@ -125,14 +125,15 @@ pub const Segment = struct {
125 /// Bitfield containing flags for a segment125 /// Bitfield containing flags for a segment
126 flags: u32,126 flags: u32,
127127
128 /// Returns the name as how it will be output into the final object
129 /// file or binary. When `merge_segments` is true, this will return the
130 /// short name. i.e. ".rodata". When false, it returns the entire name instead.
128 pub fn outputName(self: Segment, merge_segments: bool) []const u8 {131 pub fn outputName(self: Segment, merge_segments: bool) []const u8 {
129 if (!merge_segments) return self.name;132 if (!merge_segments) return self.name;
130 if (std.mem.startsWith(u8, self.name, ".rodata.")) {133 if (std.mem.startsWith(u8, self.name, ".rodata.")) {
131 return ".rodata";134 return ".rodata";
132 } else if (std.mem.startsWith(u8, self.name, ".text.")) {135 } else if (std.mem.startsWith(u8, self.name, ".text.")) {
133 return ".text";136 return ".text";
134 } else if (std.mem.startsWith(u8, self.name, ".rodata.")) {
135 return ".rodata";
136 } else if (std.mem.startsWith(u8, self.name, ".data.")) {137 } else if (std.mem.startsWith(u8, self.name, ".data.")) {
137 return ".data";138 return ".data";
138 } else if (std.mem.startsWith(u8, self.name, ".bss.")) {139 } else if (std.mem.startsWith(u8, self.name, ".bss.")) {