authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-01-08 16:25:00-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-01-15 15:11:36-08:00
log5186c6c4ee520f7f1c87134d45cd7a33f8b2aaea
tree690966b310aac635845e518884d06124ddeb2123
parent3474057e5e5002c950758a8416e786d6195f058c

wasm linker: distinguish symbol name vs import name, and implement weak


3 files changed, 115 insertions(+), 30 deletions(-)

src/link/Wasm.zig+76-11
......@@ -91,6 +91,7 @@ objects: std.ArrayListUnmanaged(Object) = .{},
9191func_types: std.AutoArrayHashMapUnmanaged(FunctionType, void) = .empty,
9292/// Provides a mapping of both imports and provided functions to symbol name.
9393/// Local functions may be unnamed.
94/// Key is symbol name, however the `FunctionImport` may have an name override for the import name.
9495object_function_imports: std.AutoArrayHashMapUnmanaged(String, FunctionImport) = .empty,
9596/// All functions for all objects.
9697object_functions: std.ArrayListUnmanaged(ObjectFunction) = .empty,
......@@ -164,7 +165,7 @@ object_host_name: OptionalString,
164165/// Memory section
165166memories: std.wasm.Memory = .{ .limits = .{
166167 .min = 0,
167 .max = undefined,
168 .max = 0,
168169 .flags = .{ .has_max = false, .is_shared = false },
169170} },
170171
......@@ -371,6 +372,17 @@ pub const OutputFunctionIndex = enum(u32) {
371372 return fromResolution(wasm, .fromObjectFunction(wasm, index)).?;
372373 }
373374
375 pub fn fromObjectFunctionHandlingWeak(wasm: *const Wasm, index: ObjectFunctionIndex) OutputFunctionIndex {
376 const ptr = index.ptr(wasm);
377 if (ptr.flags.binding == .weak) {
378 const name = ptr.name.unwrap().?;
379 const import = wasm.object_function_imports.getPtr(name).?;
380 assert(import.resolution != .unresolved);
381 return fromResolution(wasm, import.resolution).?;
382 }
383 return fromResolution(wasm, .fromObjectFunction(wasm, index)).?;
384 }
385
374386 pub fn fromIpIndex(wasm: *const Wasm, ip_index: InternPool.Index) OutputFunctionIndex {
375387 const zcu = wasm.base.comp.zcu.?;
376388 const ip = &zcu.intern_pool;
......@@ -923,6 +935,8 @@ const DebugSection = struct {};
923935pub const FunctionImport = extern struct {
924936 flags: SymbolFlags,
925937 module_name: OptionalString,
938 /// May be different than the key which is a symbol name.
939 name: String,
926940 source_location: SourceLocation,
927941 resolution: Resolution,
928942 type: FunctionType.Index,
......@@ -1042,10 +1056,14 @@ pub const FunctionImport = extern struct {
10421056 return &wasm.object_function_imports.values()[@intFromEnum(index)];
10431057 }
10441058
1045 pub fn name(index: Index, wasm: *const Wasm) String {
1059 pub fn symbolName(index: Index, wasm: *const Wasm) String {
10461060 return index.key(wasm).*;
10471061 }
10481062
1063 pub fn importName(index: Index, wasm: *const Wasm) String {
1064 return index.value(wasm).name;
1065 }
1066
10491067 pub fn moduleName(index: Index, wasm: *const Wasm) OptionalString {
10501068 return index.value(wasm).module_name;
10511069 }
......@@ -1079,6 +1097,8 @@ pub const ObjectFunction = extern struct {
10791097pub const GlobalImport = extern struct {
10801098 flags: SymbolFlags,
10811099 module_name: OptionalString,
1100 /// May be different than the key which is a symbol name.
1101 name: String,
10821102 source_location: SourceLocation,
10831103 resolution: Resolution,
10841104
......@@ -1194,10 +1214,14 @@ pub const GlobalImport = extern struct {
11941214 return &wasm.object_global_imports.values()[@intFromEnum(index)];
11951215 }
11961216
1197 pub fn name(index: Index, wasm: *const Wasm) String {
1217 pub fn symbolName(index: Index, wasm: *const Wasm) String {
11981218 return index.key(wasm).*;
11991219 }
12001220
1221 pub fn importName(index: Index, wasm: *const Wasm) String {
1222 return index.value(wasm).name;
1223 }
1224
12011225 pub fn moduleName(index: Index, wasm: *const Wasm) OptionalString {
12021226 return index.value(wasm).module_name;
12031227 }
......@@ -1260,6 +1284,8 @@ pub const RefType1 = enum(u1) {
12601284pub const TableImport = extern struct {
12611285 flags: SymbolFlags,
12621286 module_name: String,
1287 /// May be different than the key which is a symbol name.
1288 name: String,
12631289 source_location: SourceLocation,
12641290 resolution: Resolution,
12651291 limits_min: u32,
......@@ -1387,6 +1413,15 @@ pub const ObjectTableIndex = enum(u32) {
13871413 pub fn ptr(index: ObjectTableIndex, wasm: *const Wasm) *Table {
13881414 return &wasm.object_tables.items[@intFromEnum(index)];
13891415 }
1416
1417 pub fn chaseWeak(i: ObjectTableIndex, wasm: *const Wasm) ObjectTableIndex {
1418 const table = ptr(i, wasm);
1419 if (table.flags.binding != .weak) return i;
1420 const name = table.name.unwrap().?;
1421 const import = wasm.object_table_imports.getPtr(name).?;
1422 assert(import.resolution != .unresolved); // otherwise it should resolve to this one.
1423 return import.resolution.unpack().object_table;
1424 }
13901425};
13911426
13921427/// Index into `Wasm.object_globals`.
......@@ -1400,6 +1435,15 @@ pub const ObjectGlobalIndex = enum(u32) {
14001435 pub fn name(index: ObjectGlobalIndex, wasm: *const Wasm) OptionalString {
14011436 return index.ptr(wasm).name;
14021437 }
1438
1439 pub fn chaseWeak(i: ObjectGlobalIndex, wasm: *const Wasm) ObjectGlobalIndex {
1440 const global = ptr(i, wasm);
1441 if (global.flags.binding != .weak) return i;
1442 const import_name = global.name.unwrap().?;
1443 const import = wasm.object_global_imports.getPtr(import_name).?;
1444 assert(import.resolution != .unresolved); // otherwise it should resolve to this one.
1445 return import.resolution.unpack(wasm).object_global;
1446 }
14031447};
14041448
14051449pub const ObjectMemory = extern struct {
......@@ -1442,6 +1486,15 @@ pub const ObjectFunctionIndex = enum(u32) {
14421486 assert(result != .none);
14431487 return result;
14441488 }
1489
1490 pub fn chaseWeak(i: ObjectFunctionIndex, wasm: *const Wasm) ObjectFunctionIndex {
1491 const func = ptr(i, wasm);
1492 if (func.flags.binding != .weak) return i;
1493 const name = func.name.unwrap().?;
1494 const import = wasm.object_function_imports.getPtr(name).?;
1495 assert(import.resolution != .unresolved); // otherwise it should resolve to this one.
1496 return import.resolution.unpack(wasm).object_function;
1497 }
14451498};
14461499
14471500/// Index into `object_functions`, or null.
......@@ -2131,7 +2184,7 @@ pub const ZcuImportIndex = enum(u32) {
21312184 return &wasm.imports.keys()[@intFromEnum(index)];
21322185 }
21332186
2134 pub fn name(index: ZcuImportIndex, wasm: *const Wasm) String {
2187 pub fn importName(index: ZcuImportIndex, wasm: *const Wasm) String {
21352188 const zcu = wasm.base.comp.zcu.?;
21362189 const ip = &zcu.intern_pool;
21372190 const nav_index = index.ptr(wasm).*;
......@@ -2217,9 +2270,9 @@ pub const FunctionImportId = enum(u32) {
22172270 }
22182271 }
22192272
2220 pub fn name(id: FunctionImportId, wasm: *const Wasm) String {
2273 pub fn importName(id: FunctionImportId, wasm: *const Wasm) String {
22212274 return switch (unpack(id, wasm)) {
2222 inline .object_function_import, .zcu_import => |i| i.name(wasm),
2275 inline .object_function_import, .zcu_import => |i| i.importName(wasm),
22232276 };
22242277 }
22252278
......@@ -2300,9 +2353,9 @@ pub const GlobalImportId = enum(u32) {
23002353 }
23012354 }
23022355
2303 pub fn name(id: GlobalImportId, wasm: *const Wasm) String {
2356 pub fn importName(id: GlobalImportId, wasm: *const Wasm) String {
23042357 return switch (unpack(id, wasm)) {
2305 inline .object_global_import, .zcu_import => |i| i.name(wasm),
2358 inline .object_global_import, .zcu_import => |i| i.importName(wasm),
23062359 };
23072360 }
23082361
......@@ -3297,6 +3350,14 @@ pub fn prelink(wasm: *Wasm, prog_node: std.Progress.Node) link.File.FlushError!v
32973350 try markDataImport(wasm, name, import, @enumFromInt(i));
32983351 }
32993352 }
3353
3354 // This is a wild ass guess at how to merge memories, haven't checked yet
3355 // what the proper way to do this is.
3356 for (wasm.object_memory_imports.values()) |*memory_import| {
3357 wasm.memories.limits.min = @min(wasm.memories.limits.min, memory_import.limits_min);
3358 wasm.memories.limits.max = @max(wasm.memories.limits.max, memory_import.limits_max);
3359 wasm.memories.limits.flags.has_max = wasm.memories.limits.flags.has_max or memory_import.limits_has_max;
3360 }
33003361}
33013362
33023363fn markFunctionImport(
......@@ -3532,12 +3593,12 @@ fn markRelocations(wasm: *Wasm, relocs: ObjectRelocation.IterableSlice) link.Fil
35323593 .table_index_i64,
35333594 .table_index_rel_sleb,
35343595 .table_index_rel_sleb64,
3535 => try markFunction(wasm, pointee.function),
3596 => try markFunction(wasm, pointee.function.chaseWeak(wasm)),
35363597 .global_index_leb,
35373598 .global_index_i32,
3538 => try markGlobal(wasm, pointee.global),
3599 => try markGlobal(wasm, pointee.global.chaseWeak(wasm)),
35393600 .table_number_leb,
3540 => try wasm.tables.put(wasm.base.comp.gpa, .fromObjectTable(pointee.table), {}),
3601 => try markTable(wasm, pointee.table.chaseWeak(wasm)),
35413602
35423603 .section_offset_i32 => {
35433604 log.warn("TODO: ensure section {d} is included in output", .{pointee.section});
......@@ -3561,6 +3622,10 @@ fn markRelocations(wasm: *Wasm, relocs: ObjectRelocation.IterableSlice) link.Fil
35613622 }
35623623}
35633624
3625fn markTable(wasm: *Wasm, i: ObjectTableIndex) link.File.FlushError!void {
3626 try wasm.tables.put(wasm.base.comp.gpa, .fromObjectTable(i), {});
3627}
3628
35643629pub fn flushModule(
35653630 wasm: *Wasm,
35663631 arena: Allocator,
src/link/Wasm/Flush.zig+6-9
......@@ -459,7 +459,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
459459 try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len)));
460460 try binary_writer.writeAll(module_name);
461461
462 const name = id.name(wasm).slice(wasm);
462 const name = id.importName(wasm).slice(wasm);
463463 try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len)));
464464 try binary_writer.writeAll(name);
465465
......@@ -474,7 +474,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
474474 try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len)));
475475 try binary_writer.writeAll(module_name);
476476
477 const name = id.key(wasm).slice(wasm);
477 const name = table_import.name.slice(wasm);
478478 try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len)));
479479 try binary_writer.writeAll(name);
480480
......@@ -484,10 +484,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
484484 }
485485 total_imports += wasm.table_imports.entries.len;
486486
487 for (wasm.object_memory_imports.keys(), wasm.object_memory_imports.values()) |name, *memory_import| {
488 try emitMemoryImport(wasm, binary_bytes, name, memory_import);
489 total_imports += 1;
490 } else if (import_memory) {
487 if (import_memory) {
491488 const name = if (is_obj) wasm.preloaded_strings.__linear_memory else wasm.preloaded_strings.memory;
492489 try emitMemoryImport(wasm, binary_bytes, name, &.{
493490 // TODO the import_memory option needs to specify from which module
......@@ -506,7 +503,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
506503 try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len)));
507504 try binary_writer.writeAll(module_name);
508505
509 const name = id.name(wasm).slice(wasm);
506 const name = id.importName(wasm).slice(wasm);
510507 try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len)));
511508 try binary_writer.writeAll(name);
512509
......@@ -1458,8 +1455,8 @@ fn applyRelocs(code: []u8, code_offset: u32, relocs: Wasm.ObjectRelocation.Itera
14581455 if (offset >= relocs.end) break;
14591456 const sliced_code = code[offset - code_offset ..];
14601457 switch (tag) {
1461 .function_index_i32 => reloc_u32_function(sliced_code, .fromObjectFunction(wasm, pointee.function)),
1462 .function_index_leb => reloc_leb_function(sliced_code, .fromObjectFunction(wasm, pointee.function)),
1458 .function_index_i32 => reloc_u32_function(sliced_code, .fromObjectFunctionHandlingWeak(wasm, pointee.function)),
1459 .function_index_leb => reloc_leb_function(sliced_code, .fromObjectFunctionHandlingWeak(wasm, pointee.function)),
14631460 .function_offset_i32 => @panic("TODO this value is not known yet"),
14641461 .function_offset_i64 => @panic("TODO this value is not known yet"),
14651462 .table_index_i32 => @panic("TODO indirect function table needs to support object functions too"),
src/link/Wasm/Object.zig+33-10
......@@ -972,7 +972,7 @@ pub fn parse(
972972 for (ss.symbol_table.items) |symbol| switch (symbol.pointee) {
973973 .function_import => |index| {
974974 const ptr = index.ptr(ss);
975 const name = symbol.name.unwrap().?;
975 const name = symbol.name.unwrap() orelse ptr.name;
976976 if (symbol.flags.binding == .local) {
977977 diags.addParseError(path, "local symbol '{s}' references import", .{name.slice(wasm)});
978978 continue;
......@@ -1000,10 +1000,18 @@ pub fn parse(
10001000 source_location.addNote(&err, "module '{s}' here", .{ptr.module_name.slice(wasm)});
10011001 continue;
10021002 }
1003 if (gop.value_ptr.name != ptr.name) {
1004 var err = try diags.addErrorWithNotes(2);
1005 try err.addMsg("symbol '{s}' mismatching import names", .{name.slice(wasm)});
1006 gop.value_ptr.source_location.addNote(&err, "imported as '{s}' here", .{gop.value_ptr.name.slice(wasm)});
1007 source_location.addNote(&err, "imported as '{s}' here", .{ptr.name.slice(wasm)});
1008 continue;
1009 }
10031010 } else {
10041011 gop.value_ptr.* = .{
10051012 .flags = symbol.flags,
10061013 .module_name = ptr.module_name.toOptional(),
1014 .name = ptr.name,
10071015 .source_location = source_location,
10081016 .resolution = .unresolved,
10091017 .type = fn_ty_index,
......@@ -1012,7 +1020,7 @@ pub fn parse(
10121020 },
10131021 .global_import => |index| {
10141022 const ptr = index.ptr(ss);
1015 const name = symbol.name.unwrap().?;
1023 const name = symbol.name.unwrap() orelse ptr.name;
10161024 if (symbol.flags.binding == .local) {
10171025 diags.addParseError(path, "local symbol '{s}' references import", .{name.slice(wasm)});
10181026 continue;
......@@ -1049,10 +1057,18 @@ pub fn parse(
10491057 source_location.addNote(&err, "module '{s}' here", .{ptr.module_name.slice(wasm)});
10501058 continue;
10511059 }
1060 if (gop.value_ptr.name != ptr.name) {
1061 var err = try diags.addErrorWithNotes(2);
1062 try err.addMsg("symbol '{s}' mismatching import names", .{name.slice(wasm)});
1063 gop.value_ptr.source_location.addNote(&err, "imported as '{s}' here", .{gop.value_ptr.name.slice(wasm)});
1064 source_location.addNote(&err, "imported as '{s}' here", .{ptr.name.slice(wasm)});
1065 continue;
1066 }
10521067 } else {
10531068 gop.value_ptr.* = .{
10541069 .flags = symbol.flags,
10551070 .module_name = ptr.module_name.toOptional(),
1071 .name = ptr.name,
10561072 .source_location = source_location,
10571073 .resolution = .unresolved,
10581074 };
......@@ -1064,7 +1080,7 @@ pub fn parse(
10641080 },
10651081 .table_import => |index| {
10661082 const ptr = index.ptr(ss);
1067 const name = symbol.name.unwrap().?;
1083 const name = symbol.name.unwrap() orelse ptr.name;
10681084 if (symbol.flags.binding == .local) {
10691085 diags.addParseError(path, "local symbol '{s}' references import", .{name.slice(wasm)});
10701086 continue;
......@@ -1088,6 +1104,13 @@ pub fn parse(
10881104 source_location.addNote(&err, "module '{s}' here", .{ptr.module_name.slice(wasm)});
10891105 continue;
10901106 }
1107 if (gop.value_ptr.name != ptr.name) {
1108 var err = try diags.addErrorWithNotes(2);
1109 try err.addMsg("symbol '{s}' mismatching import names", .{name.slice(wasm)});
1110 gop.value_ptr.source_location.addNote(&err, "imported as '{s}' here", .{gop.value_ptr.name.slice(wasm)});
1111 source_location.addNote(&err, "imported as '{s}' here", .{ptr.name.slice(wasm)});
1112 continue;
1113 }
10911114 if (symbol.flags.binding == .strong) gop.value_ptr.flags.binding = .strong;
10921115 if (!symbol.flags.visibility_hidden) gop.value_ptr.flags.visibility_hidden = false;
10931116 if (symbol.flags.no_strip) gop.value_ptr.flags.no_strip = true;
......@@ -1095,6 +1118,7 @@ pub fn parse(
10951118 gop.value_ptr.* = .{
10961119 .flags = symbol.flags,
10971120 .module_name = ptr.module_name,
1121 .name = ptr.name,
10981122 .source_location = source_location,
10991123 .resolution = .unresolved,
11001124 .limits_min = ptr.limits_min,
......@@ -1158,6 +1182,7 @@ pub fn parse(
11581182 gop.value_ptr.* = .{
11591183 .flags = symbol.flags,
11601184 .module_name = host_name,
1185 .name = name,
11611186 .source_location = source_location,
11621187 .resolution = .fromObjectFunction(wasm, index),
11631188 .type = ptr.type_index,
......@@ -1214,8 +1239,9 @@ pub fn parse(
12141239 gop.value_ptr.* = .{
12151240 .flags = symbol.flags,
12161241 .module_name = .none,
1242 .name = name,
12171243 .source_location = source_location,
1218 .resolution = .unresolved,
1244 .resolution = .fromObjectGlobal(wasm, index),
12191245 };
12201246 gop.value_ptr.flags.global_type = .{
12211247 .valtype = .from(new_ty.valtype),
......@@ -1258,7 +1284,7 @@ pub fn parse(
12581284 gop.value_ptr.* = .{
12591285 .flags = symbol.flags,
12601286 .source_location = source_location,
1261 .resolution = .unresolved,
1287 .resolution = .fromObjectDataIndex(wasm, index),
12621288 };
12631289 }
12641290 },
......@@ -1280,11 +1306,8 @@ pub fn parse(
12801306 switch (exp.pointee) {
12811307 inline .function, .table, .memory, .global => |index| {
12821308 const ptr = index.ptr(wasm);
1283 if (ptr.name == .none) {
1284 // Missing symbol table entry; use defaults for exported things.
1285 ptr.name = exp.name.toOptional();
1286 ptr.flags.exported = true;
1287 }
1309 ptr.name = exp.name.toOptional();
1310 ptr.flags.exported = true;
12881311 },
12891312 }
12901313 }