authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2024-01-17 17:21:59+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2024-02-29 15:23:03+01:00
log143e9599d64e7ac7991f360679a5611ee0d59376
tree495335f70d3fb3783a133f128bbc6a86280b40cb
parent12505c6d3d4ccfc859b67e4b43c5b3844bebb475
signaturelock-open Commit is signed but in an unrecognized format.

wasm: use `File` abstraction instead of object

When merging sections we now make use of the `File` abstraction so all objects such as globals, functions, imports, etc are also merged from the `ZigObject` module. This allows us to use a singular way to perform each link action without having to check the kind of the file. The logic is mostly handled in the abstract file module, unless its complexity warrants the handling within the corresponding module itself.

3 files changed, 192 insertions(+), 128 deletions(-)

src/link/Wasm.zig+117-92
......@@ -152,7 +152,7 @@ entry: ?u32 = null,
152152function_table: std.AutoHashMapUnmanaged(SymbolLoc, u32) = .{},
153153
154154/// All object files and their data which are linked into the final binary
155objects: std.ArrayListUnmanaged(Object) = .{},
155objects: std.ArrayListUnmanaged(File.Index) = .{},
156156/// All archive files that are lazy loaded.
157157/// e.g. when an undefined symbol references a symbol from the archive.
158158archives: std.ArrayListUnmanaged(Archive) = .{},
......@@ -442,7 +442,7 @@ pub fn createEmpty(
442442 // can be passed to LLD.
443443 const sub_path = if (use_lld) zcu_object_sub_path.? else emit.sub_path;
444444
445 const file = try emit.directory.handle.createFile(sub_path, .{
445 wasm.base.file = try emit.directory.handle.createFile(sub_path, .{
446446 .truncate = true,
447447 .read = true,
448448 .mode = if (fs.has_executable_bit)
......@@ -453,7 +453,6 @@ pub fn createEmpty(
453453 else
454454 0,
455455 });
456 wasm.base.file = file;
457456 wasm.name = sub_path;
458457
459458 // create stack pointer symbol
......@@ -582,6 +581,15 @@ pub fn createEmpty(
582581 return wasm;
583582}
584583
584pub fn file(wasm: *Wasm, index: File.Index) ?File {
585 const tag = wasm.files.items(.tags)[index];
586 return switch (tag) {
587 .null => null,
588 .zig_object => .{ .zig_object = &wasm.files.items(.data)[index].zig_object },
589 .object => .{ .object = &wasm.files.items(.data)[index].object },
590 };
591}
592
585593pub fn zigObjectPtr(wasm: *Wasm) ?*ZigObject {
586594 if (wasm.zig_object_index == .null) return null;
587595 return &wasm.files.items(.data)[@intFromEnum(wasm.zig_object_index)].zig_object;
......@@ -650,16 +658,18 @@ fn parseInputFiles(wasm: *Wasm, files: []const []const u8) !void {
650658/// file and parsed successfully. Returns false when file is not an object file.
651659/// May return an error instead when parsing failed.
652660fn parseObjectFile(wasm: *Wasm, path: []const u8) !bool {
653 const file = try fs.cwd().openFile(path, .{});
654 errdefer file.close();
661 const obj_file = try fs.cwd().openFile(path, .{});
662 errdefer obj_file.close();
655663
656664 const gpa = wasm.base.comp.gpa;
657 var object = Object.create(gpa, file, path, null) catch |err| switch (err) {
665 var object = Object.create(gpa, obj_file, path, null) catch |err| switch (err) {
658666 error.InvalidMagicByte, error.NotObjectFile => return false,
659667 else => |e| return e,
660668 };
661669 errdefer object.deinit(gpa);
662 try wasm.objects.append(gpa, object);
670 object.index = @enumFromInt(wasm.files.len);
671 try wasm.files.append(gpa, .{ .object = object });
672 try wasm.objects.append(gpa, object.index);
663673 return true;
664674}
665675
......@@ -693,11 +703,11 @@ pub inline fn getAtomPtr(wasm: *Wasm, index: Atom.Index) *Atom {
693703fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool {
694704 const gpa = wasm.base.comp.gpa;
695705
696 const file = try fs.cwd().openFile(path, .{});
697 errdefer file.close();
706 const archive_file = try fs.cwd().openFile(path, .{});
707 errdefer archive_file.close();
698708
699709 var archive: Archive = .{
700 .file = file,
710 .file = archive_file,
701711 .name = path,
702712 };
703713 archive.parse(gpa) catch |err| switch (err) {
......@@ -727,8 +737,10 @@ fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool {
727737 }
728738
729739 for (offsets.keys()) |file_offset| {
730 const object = try wasm.objects.addOne(gpa);
731 object.* = try archive.parseObject(gpa, file_offset);
740 var object = try archive.parseObject(gpa, file_offset);
741 object.index = @enumFromInt(wasm.files.len);
742 try wasm.files.append(gpa, .{ .object = object });
743 try wasm.objects.append(gpa, object.index);
732744 }
733745
734746 return true;
......@@ -784,8 +796,8 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void {
784796 const existing_loc = maybe_existing.value_ptr.*;
785797 const existing_sym: *Symbol = existing_loc.getSymbol(wasm);
786798
787 const existing_file_path = if (existing_loc.file) |file| blk: {
788 break :blk wasm.objects.items[file].name;
799 const existing_file_path = if (existing_loc.file) |file_index| blk: {
800 break :blk wasm.objects.items[file_index].name;
789801 } else wasm.name;
790802
791803 if (!existing_sym.isUndefined()) outer: {
......@@ -911,10 +923,11 @@ fn resolveSymbolsInArchives(wasm: *Wasm) !void {
911923 // Symbol is found in unparsed object file within current archive.
912924 // Parse object and and resolve symbols again before we check remaining
913925 // undefined symbols.
914 const object_file_index: u16 = @intCast(wasm.objects.items.len);
915 const object = try archive.parseObject(gpa, offset.items[0]);
916 try wasm.objects.append(gpa, object);
917 try wasm.resolveSymbolsInObject(object_file_index);
926 var object = try archive.parseObject(gpa, offset.items[0]);
927 object.index = @enumFromInt(wasm.files.len);
928 try wasm.files.append(gpa, .{ .object = object });
929 try wasm.objects.append(gpa, object.index);
930 try wasm.resolveSymbolsInObject(object.index);
918931
919932 // continue loop for any remaining undefined symbols that still exist
920933 // after resolving last object file
......@@ -1176,9 +1189,10 @@ fn validateFeatures(
11761189
11771190 // extract all the used, disallowed and required features from each
11781191 // linked object file so we can test them.
1179 for (wasm.objects.items, 0..) |object, object_index| {
1192 for (wasm.objects.items) |file_index| {
1193 const object: Object = wasm.files.items(.data)[file_index].object;
11801194 for (object.features) |feature| {
1181 const value = @as(u16, @intCast(object_index)) << 1 | @as(u1, 1);
1195 const value = @as(u16, @intFromEnum(file_index)) << 1 | @as(u1, 1);
11821196 switch (feature.prefix) {
11831197 .used => {
11841198 used[@intFromEnum(feature.tag)] = value;
......@@ -1210,7 +1224,7 @@ fn validateFeatures(
12101224 emit_features_count.* += @intFromBool(is_enabled);
12111225 } else if (is_enabled and !allowed[used_index]) {
12121226 log.err("feature '{}' not allowed, but used by linked object", .{@as(types.Feature.Tag, @enumFromInt(used_index))});
1213 log.err(" defined in '{s}'", .{wasm.objects.items[used_set >> 1].name});
1227 log.err(" defined in '{s}'", .{wasm.files.items(.data)[used_set >> 1].object.path});
12141228 valid_feature_set = false;
12151229 }
12161230 }
......@@ -1224,7 +1238,7 @@ fn validateFeatures(
12241238 if (@as(u1, @truncate(disallowed_feature)) != 0) {
12251239 log.err(
12261240 "shared-memory is disallowed by '{s}' because it wasn't compiled with 'atomics' and 'bulk-memory' features enabled",
1227 .{wasm.objects.items[disallowed_feature >> 1].name},
1241 .{wasm.files.items(.data)[disallowed_feature >> 1].object.path},
12281242 );
12291243 valid_feature_set = false;
12301244 }
......@@ -1244,16 +1258,17 @@ fn validateFeatures(
12441258 }
12451259 }
12461260 // For each linked object, validate the required and disallowed features
1247 for (wasm.objects.items) |object| {
1261 for (wasm.objects.items) |file_index| {
12481262 var object_used_features = [_]bool{false} ** known_features_count;
1263 const object = wasm.files.items(.data)[file_index].object;
12491264 for (object.features) |feature| {
12501265 if (feature.prefix == .disallowed) continue; // already defined in 'disallowed' set.
12511266 // from here a feature is always used
12521267 const disallowed_feature = disallowed[@intFromEnum(feature.tag)];
12531268 if (@as(u1, @truncate(disallowed_feature)) != 0) {
12541269 log.err("feature '{}' is disallowed, but used by linked object", .{feature.tag});
1255 log.err(" disallowed by '{s}'", .{wasm.objects.items[disallowed_feature >> 1].name});
1256 log.err(" used in '{s}'", .{object.name});
1270 log.err(" disallowed by '{s}'", .{wasm.files.items(.data)[disallowed_feature >> 1].object.path});
1271 log.err(" used in '{s}'", .{object.path});
12571272 valid_feature_set = false;
12581273 }
12591274
......@@ -1265,8 +1280,8 @@ fn validateFeatures(
12651280 const is_required = @as(u1, @truncate(required_feature)) != 0;
12661281 if (is_required and !object_used_features[feature_index]) {
12671282 log.err("feature '{}' is required but not used in linked object", .{@as(types.Feature.Tag, @enumFromInt(feature_index))});
1268 log.err(" required by '{s}'", .{wasm.objects.items[required_feature >> 1].name});
1269 log.err(" missing in '{s}'", .{object.name});
1283 log.err(" required by '{s}'", .{wasm.files.items(.data)[required_feature >> 1].object.path});
1284 log.err(" missing in '{s}'", .{object.path});
12701285 valid_feature_set = false;
12711286 }
12721287 }
......@@ -1346,9 +1361,10 @@ fn checkUndefinedSymbols(wasm: *const Wasm) !void {
13461361 const symbol = undef.getSymbol(wasm);
13471362 if (symbol.tag == .data) {
13481363 found_undefined_symbols = true;
1349 const file_name = if (undef.file) |file_index| name: {
1350 break :name wasm.objects.items[file_index].name;
1351 } else wasm.name;
1364 const file_name = if (undef.file) |file_index|
1365 wasm.file(file_index).?.path()
1366 else
1367 wasm.name;
13521368 const symbol_name = undef.getName(wasm);
13531369 log.err("could not resolve undefined symbol '{s}'", .{symbol_name});
13541370 log.err(" defined in '{s}'", .{file_name});
......@@ -1369,8 +1385,11 @@ pub fn deinit(wasm: *Wasm) void {
13691385 for (wasm.segment_info.values()) |segment_info| {
13701386 gpa.free(segment_info.name);
13711387 }
1372 for (wasm.objects.items) |*object| {
1373 object.deinit(gpa);
1388 if (wasm.zigObjectPtr()) |zig_obj| {
1389 zig_obj.deinit(gpa);
1390 }
1391 for (wasm.objects.items) |obj_index| {
1392 wasm.file(obj_index).?.object.deinit(gpa);
13741393 }
13751394
13761395 for (wasm.archives.items) |*archive| {
......@@ -1441,12 +1460,11 @@ fn getGlobalType(wasm: *const Wasm, loc: SymbolLoc) std.wasm.GlobalType {
14411460 assert(symbol.tag == .global);
14421461 const is_undefined = symbol.isUndefined();
14431462 if (loc.file) |file_index| {
1444 const obj: Object = wasm.objects.items[file_index];
1463 const obj_file = wasm.file(@enumFromInt(file_index)).?;
14451464 if (is_undefined) {
1446 return obj.findImport(.global, symbol.index).kind.global;
1465 return obj_file.import(loc.index).kind.global;
14471466 }
1448 const import_global_count = obj.importedCountByKind(.global);
1449 return obj.globals[symbol.index - import_global_count].global_type;
1467 return obj_file.globals()[symbol.index - obj_file.importedGlobals()].global_type;
14501468 }
14511469 if (is_undefined) {
14521470 return wasm.imports.get(loc).?.kind.global;
......@@ -1461,14 +1479,13 @@ fn getFunctionSignature(wasm: *const Wasm, loc: SymbolLoc) std.wasm.Type {
14611479 assert(symbol.tag == .function);
14621480 const is_undefined = symbol.isUndefined();
14631481 if (loc.file) |file_index| {
1464 const obj: Object = wasm.objects.items[file_index];
1482 const obj_file = wasm.file(@enumFromInt(file_index)).?;
14651483 if (is_undefined) {
1466 const ty_index = obj.findImport(.function, symbol.index).kind.function;
1467 return obj.func_types[ty_index];
1484 const ty_index = obj_file.import(loc.index).kind.function;
1485 return obj_file.funcTypes()[ty_index];
14681486 }
1469 const import_function_count = obj.importedCountByKind(.function);
1470 const type_index = obj.functions[symbol.index - import_function_count].type_index;
1471 return obj.func_types[type_index];
1487 const type_index = obj_file.functions()[symbol.index - obj_file.importedFunctions()].type_index;
1488 return obj_file.funcTypes()[type_index];
14721489 }
14731490 if (is_undefined) {
14741491 const ty_index = wasm.imports.get(loc).?.kind.function;
......@@ -1606,10 +1623,10 @@ fn allocateAtoms(wasm: *Wasm) !void {
16061623 // Ensure we get the original symbol, so we verify the correct symbol on whether
16071624 // it is dead or not and ensure an atom is removed when dead.
16081625 // This is required as we may have parsed aliases into atoms.
1609 const sym = if (symbol_loc.file) |object_index| sym: {
1610 const object = wasm.objects.items[object_index];
1611 break :sym object.symtable[symbol_loc.index];
1612 } else wasm.synthetic_symbols.items[symbol_loc.index];
1626 const sym = if (symbol_loc.file) |object_index|
1627 wasm.file(object_index).?.symbol(symbol_loc.index).*
1628 else
1629 wasm.synthetic_symbols.items[symbol_loc.index];
16131630
16141631 // Dead symbols must be unlinked from the linked-list to prevent them
16151632 // from being emit into the binary.
......@@ -1655,9 +1672,10 @@ fn allocateVirtualAddresses(wasm: *Wasm) void {
16551672
16561673 const atom = wasm.getAtom(atom_index);
16571674 const merge_segment = wasm.base.comp.config.output_mode != .Obj;
1658 const segment_info = if (atom.file) |object_index| blk: {
1659 break :blk wasm.objects.items[object_index].segment_info;
1660 } else wasm.segment_info.values();
1675 const segment_info = if (atom.file) |object_index|
1676 wasm.file(object_index).?.segmentInfo()
1677 else
1678 wasm.segment_info.values();
16611679 const segment_name = segment_info[symbol.index].outputName(merge_segment);
16621680 const segment_index = wasm.data_segments.get(segment_name).?;
16631681 const segment = wasm.segments.items[segment_index];
......@@ -1713,7 +1731,8 @@ fn sortDataSegments(wasm: *Wasm) !void {
17131731/// contain any parameters.
17141732fn setupInitFunctions(wasm: *Wasm) !void {
17151733 const gpa = wasm.base.comp.gpa;
1716 for (wasm.objects.items, 0..) |object, file_index| {
1734 for (wasm.objects.items) |file_index| {
1735 const object = wasm.files.items(.data)[file_index].object;
17171736 try wasm.init_funcs.ensureUnusedCapacity(gpa, object.init_funcs.len);
17181737 for (object.init_funcs) |init_func| {
17191738 const symbol = object.symtable[init_func.symbol_index];
......@@ -1961,7 +1980,7 @@ fn setupImports(wasm: *Wasm) !void {
19611980
19621981 for (wasm.resolved_symbols.keys()) |symbol_loc| {
19631982 const file_index = symbol_loc.file orelse {
1964 // imports generated by Zig code are already in the `import` section
1983 // Synthetic symbols will already exist in the `import` section
19651984 continue;
19661985 };
19671986
......@@ -1974,14 +1993,14 @@ fn setupImports(wasm: *Wasm) !void {
19741993 }
19751994
19761995 log.debug("Symbol '{s}' will be imported from the host", .{symbol_loc.getName(wasm)});
1977 const object = wasm.objects.items[file_index];
1978 const import = object.findImport(symbol.tag.externalType(), symbol.index);
1996 const obj_file = wasm.file(file_index).?;
1997 const import = obj_file.import(symbol_loc.index);
19791998
19801999 // We copy the import to a new import to ensure the names contain references
19812000 // to the internal string table, rather than of the object file.
19822001 const new_imp: types.Import = .{
1983 .module_name = try wasm.string_table.put(gpa, object.string_table.get(import.module_name)),
1984 .name = try wasm.string_table.put(gpa, object.string_table.get(import.name)),
2002 .module_name = try wasm.string_table.put(gpa, obj_file.string(import.module_name)),
2003 .name = try wasm.string_table.put(gpa, obj_file.string(import.name)),
19852004 .kind = import.kind,
19862005 };
19872006 // TODO: De-duplicate imports when they contain the same names and type
......@@ -2032,28 +2051,23 @@ fn mergeSections(wasm: *Wasm) !void {
20322051 defer removed_duplicates.deinit();
20332052
20342053 for (wasm.resolved_symbols.keys()) |sym_loc| {
2035 if (sym_loc.file == null) {
2054 const file_index = sym_loc.file orelse {
20362055 // Zig code-generated symbols are already within the sections and do not
20372056 // require to be merged
20382057 continue;
2039 }
2058 };
20402059
2041 const object = &wasm.objects.items[sym_loc.file.?];
2042 const symbol = &object.symtable[sym_loc.index];
2060 const obj_file = wasm.file(@enumFromInt(file_index)).?;
2061 const symbol = obj_file.symbol[sym_loc.index];
20432062
2044 if (symbol.isDead() or
2045 symbol.isUndefined() or
2046 (symbol.tag != .function and symbol.tag != .global and symbol.tag != .table))
2047 {
2063 if (symbol.isDead() or symbol.isUndefined()) {
20482064 // Skip undefined symbols as they go in the `import` section
2049 // Also skip symbols that do not need to have a section merged.
20502065 continue;
20512066 }
20522067
2053 const offset = object.importedCountByKind(symbol.tag.externalType());
2054 const index = symbol.index - offset;
20552068 switch (symbol.tag) {
20562069 .function => {
2070 const index = symbol.index - obj_file.importedFunctions();
20572071 const gop = try wasm.functions.getOrPut(
20582072 gpa,
20592073 .{ .file = sym_loc.file, .index = symbol.index },
......@@ -2071,20 +2085,24 @@ fn mergeSections(wasm: *Wasm) !void {
20712085 try removed_duplicates.append(sym_loc);
20722086 continue;
20732087 }
2074 gop.value_ptr.* = .{ .func = object.functions[index], .sym_index = sym_loc.index };
2088 gop.value_ptr.* = .{ .func = obj_file.functions()[index], .sym_index = sym_loc.index };
20752089 symbol.index = @as(u32, @intCast(gop.index)) + wasm.imported_functions_count;
20762090 },
20772091 .global => {
2078 const original_global = object.globals[index];
2092 const index = symbol.index - obj_file.importedFunctions();
2093 const original_global = obj_file.globals()[index];
20792094 symbol.index = @as(u32, @intCast(wasm.wasm_globals.items.len)) + wasm.imported_globals_count;
20802095 try wasm.wasm_globals.append(gpa, original_global);
20812096 },
20822097 .table => {
2083 const original_table = object.tables[index];
2098 const index = symbol.index - obj_file.importedFunctions();
2099 // assert it's a regular relocatable object file as `ZigObject` will never
2100 // contain a table.
2101 const original_table = obj_file.object.tables[index];
20842102 symbol.index = @as(u32, @intCast(wasm.tables.items.len)) + wasm.imported_tables_count;
20852103 try wasm.tables.append(gpa, original_table);
20862104 },
2087 else => unreachable,
2105 else => continue,
20882106 }
20892107 }
20902108
......@@ -2111,12 +2129,13 @@ fn mergeTypes(wasm: *Wasm) !void {
21112129 defer dirty.deinit();
21122130
21132131 for (wasm.resolved_symbols.keys()) |sym_loc| {
2114 if (sym_loc.file == null) {
2132 const file_index = sym_loc.file orelse {
21152133 // zig code-generated symbols are already present in final type section
21162134 continue;
2117 }
2118 const object = wasm.objects.items[sym_loc.file.?];
2119 const symbol = object.symtable[sym_loc.index];
2135 };
2136
2137 const obj_file = wasm.file(@enumFromInt(file_index)).?;
2138 const symbol = obj_file.symbol(sym_loc.index);
21202139 if (symbol.tag != .function or symbol.isDead()) {
21212140 // Only functions have types. Only retrieve the type of referenced functions.
21222141 continue;
......@@ -2125,12 +2144,12 @@ fn mergeTypes(wasm: *Wasm) !void {
21252144 if (symbol.isUndefined()) {
21262145 log.debug("Adding type from extern function '{s}'", .{sym_loc.getName(wasm)});
21272146 const import: *types.Import = wasm.imports.getPtr(sym_loc) orelse continue;
2128 const original_type = object.func_types[import.kind.function];
2147 const original_type = obj_file.funcTypes()[import.kind.function];
21292148 import.kind.function = try wasm.putOrGetFuncType(original_type);
21302149 } else if (!dirty.contains(symbol.index)) {
21312150 log.debug("Adding type from function '{s}'", .{sym_loc.getName(wasm)});
21322151 const func = &wasm.functions.values()[symbol.index - wasm.imported_functions_count].func;
2133 func.type_index = try wasm.putOrGetFuncType(object.func_types[func.type_index]);
2152 func.type_index = try wasm.putOrGetFuncType(obj_file.funcTypes()[func.type_index]);
21342153 dirty.putAssumeCapacityNoClobber(symbol.index, {});
21352154 }
21362155 }
......@@ -2240,11 +2259,18 @@ fn setupMemory(wasm: *Wasm) !void {
22402259
22412260 const is_obj = comp.config.output_mode == .Obj;
22422261
2262 const stack_ptr = if (wasm.findGlobalSymbol("__stack_pointer")) |loc| index: {
2263 const sym = loc.getSymbol(wasm);
2264 break :index sym.index - wasm.imported_globals_count;
2265 } else null;
2266
22432267 if (place_stack_first and !is_obj) {
22442268 memory_ptr = stack_alignment.forward(memory_ptr);
22452269 memory_ptr += wasm.base.stack_size;
22462270 // We always put the stack pointer global at index 0
2247 wasm.wasm_globals.items[0].init.i32_const = @as(i32, @bitCast(@as(u32, @intCast(memory_ptr))));
2271 if (stack_ptr) |index| {
2272 wasm.wasm_globals.items[index].init.i32_const = @as(i32, @bitCast(@as(u32, @intCast(memory_ptr))));
2273 }
22482274 }
22492275
22502276 var offset: u32 = @as(u32, @intCast(memory_ptr));
......@@ -2290,7 +2316,9 @@ fn setupMemory(wasm: *Wasm) !void {
22902316 if (!place_stack_first and !is_obj) {
22912317 memory_ptr = stack_alignment.forward(memory_ptr);
22922318 memory_ptr += wasm.base.stack_size;
2293 wasm.wasm_globals.items[0].init.i32_const = @as(i32, @bitCast(@as(u32, @intCast(memory_ptr))));
2319 if (stack_ptr) |index| {
2320 wasm.wasm_globals.items[index].init.i32_const = @as(i32, @bitCast(@as(u32, @intCast(memory_ptr))));
2321 }
22942322 }
22952323
22962324 // One of the linked object files has a reference to the __heap_base symbol.
......@@ -2355,17 +2383,17 @@ fn setupMemory(wasm: *Wasm) !void {
23552383/// From a given object's index and the index of the segment, returns the corresponding
23562384/// index of the segment within the final data section. When the segment does not yet
23572385/// exist, a new one will be initialized and appended. The new index will be returned in that case.
2358pub fn getMatchingSegment(wasm: *Wasm, object_index: u16, symbol_index: u32) !u32 {
2386pub fn getMatchingSegment(wasm: *Wasm, file_index: File.Index, symbol_index: u32) !u32 {
23592387 const comp = wasm.base.comp;
23602388 const gpa = comp.gpa;
2361 const object: Object = wasm.objects.items[object_index];
2362 const symbol = object.symtable[symbol_index];
2389 const obj_file = wasm.file(file_index).?;
2390 const symbol = obj_file.symbols()[symbol_index];
23632391 const index: u32 = @intCast(wasm.segments.items.len);
23642392 const shared_memory = comp.config.shared_memory;
23652393
23662394 switch (symbol.tag) {
23672395 .data => {
2368 const segment_info = object.segment_info[symbol.index];
2396 const segment_info = obj_file.segmentInfo()[symbol.index];
23692397 const merge_segment = comp.config.output_mode != .Obj;
23702398 const result = try wasm.data_segments.getOrPut(gpa, segment_info.outputName(merge_segment));
23712399 if (!result.found_existing) {
......@@ -2394,7 +2422,7 @@ pub fn getMatchingSegment(wasm: *Wasm, object_index: u16, symbol_index: u32) !u3
23942422 break :blk index;
23952423 },
23962424 .section => {
2397 const section_name = object.string_table.get(symbol.name);
2425 const section_name = file.symbolName(symbol.index);
23982426 if (mem.eql(u8, section_name, ".debug_info")) {
23992427 return wasm.debug_info_index orelse blk: {
24002428 wasm.debug_info_index = index;
......@@ -4291,12 +4319,10 @@ fn markReferences(wasm: *Wasm) !void {
42914319 // Debug sections may require to be parsed and marked when it contains
42924320 // relocations to alive symbols.
42934321 if (sym.tag == .section and comp.config.debug_format != .strip) {
4294 const file = sym_loc.file orelse continue; // Incremental debug info is done independently
4295 const object = &wasm.objects.items[file];
4296 const atom_index = try Object.parseSymbolIntoAtom(object, file, sym_loc.index, wasm);
4297 const atom = wasm.getAtom(atom_index);
4298 const atom_sym = atom.symbolLoc().getSymbol(wasm);
4299 atom_sym.mark();
4322 const file_index = sym_loc.file orelse continue; // Incremental debug info is done independently
4323 const obj_file = wasm.file(@enumFromInt(file_index)).?;
4324 _ = try obj_file.parseSymbolIntoAtom(wasm, sym_loc.index);
4325 sym.mark();
43004326 }
43014327 }
43024328}
......@@ -4319,9 +4345,8 @@ fn mark(wasm: *Wasm, loc: SymbolLoc) !void {
43194345 }
43204346
43214347 const atom_index = if (loc.file) |file_index| idx: {
4322 const object = &wasm.objects.items[file_index];
4323 const atom_index = try object.parseSymbolIntoAtom(file_index, loc.index, wasm);
4324 break :idx atom_index;
4348 const obj_file = wasm.file(@enumFromInt(file_index)).?;
4349 break :idx try obj_file.parseSymbolIntoAtom(wasm, loc.index);
43254350 } else wasm.symbol_atom.get(loc) orelse return;
43264351
43274352 const atom = wasm.getAtom(atom_index);
src/link/Wasm/Object.zig+39-36
......@@ -9,6 +9,7 @@ const std = @import("std");
99const Wasm = @import("../Wasm.zig");
1010const Symbol = @import("Symbol.zig");
1111const Alignment = types.Alignment;
12const File = @import("file.zig").File;
1213
1314const Allocator = std.mem.Allocator;
1415const leb = std.leb;
......@@ -16,12 +17,14 @@ const meta = std.meta;
1617
1718const log = std.log.scoped(.link);
1819
20/// Index into the list of relocatable object files within the linker driver.
21index: File.Index = .null,
1922/// Wasm spec version used for this `Object`
2023version: u32 = 0,
2124/// The file descriptor that represents the wasm object file.
2225file: ?std.fs.File = null,
2326/// Name (read path) of the object file.
24name: []const u8,
27path: []const u8,
2528/// Parsed type section
2629func_types: []const std.wasm.Type = &.{},
2730/// A list of all imports for this module
......@@ -64,6 +67,12 @@ relocatable_data: std.AutoHashMapUnmanaged(RelocatableData.Tag, []RelocatableDat
6467/// import name, module name and export names. Each string will be deduplicated
6568/// and returns an offset into the table.
6669string_table: Wasm.StringTable = .{},
70/// Amount of functions in the `import` sections.
71imported_functions_count: u32 = 0,
72/// Amount of globals in the `import` section.
73imported_globals_count: u32 = 0,
74/// Amount of tables in the `import` section.
75imported_tables_count: u32 = 0,
6776
6877/// Represents a single item within a section (depending on its `type`)
6978const RelocatableData = struct {
......@@ -121,7 +130,7 @@ pub const InitError = error{NotObjectFile} || ParseError || std.fs.File.ReadErro
121130pub fn create(gpa: Allocator, file: std.fs.File, name: []const u8, maybe_max_size: ?usize) InitError!Object {
122131 var object: Object = .{
123132 .file = file,
124 .name = try gpa.dupe(u8, name),
133 .path = try gpa.dupe(u8, name),
125134 };
126135
127136 var is_object_file: bool = false;
......@@ -199,29 +208,17 @@ pub fn deinit(object: *Object, gpa: Allocator) void {
199208
200209/// Finds the import within the list of imports from a given kind and index of that kind.
201210/// Asserts the import exists
202pub fn findImport(object: *const Object, import_kind: std.wasm.ExternalKind, index: u32) types.Import {
211pub fn findImport(object: *const Object, index: u32) types.Import {
212 const sym = object.symtable[index];
203213 var i: u32 = 0;
204214 return for (object.imports) |import| {
205 if (std.meta.activeTag(import.kind) == import_kind) {
215 if (std.meta.activeTag(import.kind) == sym.tag) {
206216 if (i == index) return import;
207217 i += 1;
208218 }
209219 } else unreachable; // Only existing imports are allowed to be found
210220}
211221
212/// Counts the entries of imported `kind` and returns the result
213pub fn importedCountByKind(object: *const Object, kind: std.wasm.ExternalKind) u32 {
214 var i: u32 = 0;
215 return for (object.imports) |imp| {
216 if (@as(std.wasm.ExternalKind, imp.kind) == kind) i += 1;
217 } else i;
218}
219
220/// From a given `RelocatableDate`, find the corresponding debug section name
221pub fn getDebugName(object: *const Object, relocatable_data: RelocatableData) []const u8 {
222 return object.string_table.get(relocatable_data.index);
223}
224
225222/// Checks if the object file is an MVP version.
226223/// When that's the case, we check if there's an import table definiton with its name
227224/// set to '__indirect_function_table". When that's also the case,
......@@ -427,16 +424,25 @@ fn Parser(comptime ReaderType: type) type {
427424
428425 const kind = try readEnum(std.wasm.ExternalKind, reader);
429426 const kind_value: std.wasm.Import.Kind = switch (kind) {
430 .function => .{ .function = try readLeb(u32, reader) },
427 .function => val: {
428 parser.object.imported_functions_count += 1;
429 break :val .{ .function = try readLeb(u32, reader) };
430 },
431431 .memory => .{ .memory = try readLimits(reader) },
432 .global => .{ .global = .{
433 .valtype = try readEnum(std.wasm.Valtype, reader),
434 .mutable = (try reader.readByte()) == 0x01,
435 } },
436 .table => .{ .table = .{
437 .reftype = try readEnum(std.wasm.RefType, reader),
438 .limits = try readLimits(reader),
439 } },
432 .global => val: {
433 parser.object.imported_globals_count += 1;
434 break :val .{ .global = .{
435 .valtype = try readEnum(std.wasm.Valtype, reader),
436 .mutable = (try reader.readByte()) == 0x01,
437 } };
438 },
439 .table => val: {
440 parser.object.imported_tables_count += 1;
441 break :val .{ .table = .{
442 .reftype = try readEnum(std.wasm.RefType, reader),
443 .limits = try readLimits(reader),
444 } };
445 },
440446 };
441447
442448 import.* = .{
......@@ -904,7 +910,7 @@ fn assertEnd(reader: anytype) !void {
904910}
905911
906912/// Parses an object file into atoms, for code and data sections
907pub fn parseSymbolIntoAtom(object: *Object, object_index: u16, symbol_index: u32, wasm: *Wasm) !Atom.Index {
913pub fn parseSymbolIntoAtom(object: *Object, wasm: *Wasm, symbol_index: u32) !Atom.Index {
908914 const comp = wasm.base.comp;
909915 const gpa = comp.gpa;
910916 const symbol = &object.symtable[symbol_index];
......@@ -922,19 +928,16 @@ pub fn parseSymbolIntoAtom(object: *Object, object_index: u16, symbol_index: u32
922928 },
923929 else => unreachable,
924930 };
925 const final_index = try wasm.getMatchingSegment(object_index, symbol_index);
926 const atom_index = @as(Atom.Index, @intCast(wasm.managed_atoms.items.len));
927 const atom = try wasm.managed_atoms.addOne(gpa);
928 atom.* = Atom.empty;
931 const final_index = try wasm.getMatchingSegment(object.index, symbol_index);
932 const atom_index = try wasm.createAtom(symbol_index, object.index);
929933 try wasm.appendAtomAtIndex(final_index, atom_index);
930934
931 atom.sym_index = symbol_index;
932 atom.file = object_index;
935 const atom = wasm.getAtomPtr(atom_index);
933936 atom.size = relocatable_data.size;
934937 atom.alignment = relocatable_data.getAlignment(object);
935938 atom.code = std.ArrayListUnmanaged(u8).fromOwnedSlice(relocatable_data.data[0..relocatable_data.size]);
936939 atom.original_offset = relocatable_data.offset;
937 try wasm.symbol_atom.putNoClobber(gpa, atom.symbolLoc(), atom_index);
940
938941 const segment: *Wasm.Segment = &wasm.segments.items[final_index];
939942 if (relocatable_data.type == .data) { //code section and custom sections are 1-byte aligned
940943 segment.alignment = segment.alignment.max(atom.alignment);
......@@ -952,7 +955,7 @@ pub fn parseSymbolIntoAtom(object: *Object, object_index: u16, symbol_index: u32
952955 .R_WASM_TABLE_INDEX_SLEB64,
953956 => {
954957 try wasm.function_table.put(gpa, .{
955 .file = object_index,
958 .file = object.index,
956959 .index = reloc.index,
957960 }, 0);
958961 },
......@@ -963,7 +966,7 @@ pub fn parseSymbolIntoAtom(object: *Object, object_index: u16, symbol_index: u32
963966 if (sym.tag != .global) {
964967 try wasm.got_symbols.append(
965968 gpa,
966 .{ .file = object_index, .index = reloc.index },
969 .{ .file = object.index, .index = reloc.index },
967970 );
968971 }
969972 },
src/link/Wasm/ZigObject.zig+36
......@@ -11,6 +11,9 @@ index: File.Index,
1111decls: std.AutoHashMapUnmanaged(InternPool.DeclIndex, Atom.Index) = .{},
1212/// List of function type signatures for this Zig module.
1313func_types: std.ArrayListUnmanaged(std.wasm.Type) = .{},
14/// List of `std.wasm.Func`. Each entry contains the function signature,
15/// rather than the actual body.
16functions: std.ArrayListUnmanaged(std.wasm.Func) = .{},
1417/// Map of symbol locations, represented by its `types.Import`.
1518imports: std.AutoHashMapUnmanaged(u32, types.Import) = .{},
1619/// List of WebAssembly globals.
......@@ -1152,6 +1155,39 @@ pub fn storeDeclType(zig_object: *ZigObject, gpa: std.mem.Allocator, decl_index:
11521155 return index;
11531156}
11541157
1158/// The symbols in ZigObject are already represented by an atom as we need to store its data.
1159/// So rather than creating a new Atom and returning its index, we use this oppertunity to scan
1160/// its relocations and create any GOT symbols or function table indexes it may require.
1161pub fn parseSymbolIntoAtom(zig_object: *ZigObject, wasm_file: *Wasm, index: u32) Atom.Index {
1162 const gpa = wasm_file.base.comp.gpa;
1163 const loc: Wasm.SymbolLoc = .{ .file = @intFromEnum(zig_object.index), .index = index };
1164 const final_index = try wasm_file.getMatchingSegment(zig_object.index, index);
1165 const atom_index = wasm_file.symbol_atom.get(loc).?;
1166 try wasm_file.appendAtomAtIndex(final_index, atom_index);
1167 const atom = wasm_file.getAtom(atom_index);
1168 for (atom.relocs.items) |reloc| {
1169 switch (reloc.relocation_type) {
1170 .R_WASM_TABLE_INDEX_I32,
1171 .R_WASM_TABLE_INDEX_I64,
1172 .R_WASM_TABLE_INDEX_SLEB,
1173 .R_WASM_TABLE_INDEX_SLEB64,
1174 => {
1175 try wasm_file.function_table.put(gpa, loc, 0);
1176 },
1177 .R_WASM_GLOBAL_INDEX_I32,
1178 .R_WASM_GLOBAL_INDEX_LEB,
1179 => {
1180 const sym = zig_object.symbol(reloc.index);
1181 if (sym.tag != .global) {
1182 try wasm_file.got_symbols.append(gpa, loc);
1183 }
1184 },
1185 else => {},
1186 }
1187 }
1188 return atom_index;
1189}
1190
11551191const build_options = @import("build_options");
11561192const builtin = @import("builtin");
11571193const codegen = @import("../../codegen.zig");