aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLuuk de Gram <luuk@degram.dev>2023-06-13 21:08:02 +0200
committerLuuk de Gram <luuk@degram.dev>2023-06-16 17:16:55 +0200
commit729f822e311f3bce1e7bd99bcf71937145451a4c (patch)
tree48e61ad54dd9203cfcf73e4b63266066d56f4d4a /src
parent5d9e8f27d0dc131e0b4154c5f65376f2fb9f3500 (diff)
downloadzig-729f822e311f3bce1e7bd99bcf71937145451a4c.tar.gz
zig-729f822e311f3bce1e7bd99bcf71937145451a4c.zip
wasm-linker: correctly resolve exported symbols
When compiling Zig code using the Wasm backend, we would previously incorrectly resolve exported symbols as it would not correctly remove existing symbols if they were to be overwritten. This meant that undefined symbols could cause collisions although they should be resolved by the exported symbol.
Diffstat (limited to 'src')
-rw-r--r--src/link/Wasm.zig81
1 files changed, 45 insertions, 36 deletions
diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig
index fdac7dfa63..f593f95c3d 100644
--- a/src/link/Wasm.zig
+++ b/src/link/Wasm.zig
@@ -1703,6 +1703,7 @@ pub fn updateDeclExports(
const decl = mod.declPtr(decl_index);
const atom_index = try wasm.getOrCreateAtomForDecl(decl_index);
const atom = wasm.getAtom(atom_index);
+ const atom_sym = atom.symbolLoc().getSymbol(wasm).*;
const gpa = mod.gpa;
for (exports) |exp| {
@@ -1716,43 +1717,21 @@ pub fn updateDeclExports(
continue;
}
- const export_name = try wasm.string_table.put(wasm.base.allocator, mod.intern_pool.stringToSlice(exp.opts.name));
- if (wasm.globals.getPtr(export_name)) |existing_loc| {
- if (existing_loc.index == atom.sym_index) continue;
- const existing_sym: Symbol = existing_loc.getSymbol(wasm).*;
-
- const exp_is_weak = exp.opts.linkage == .Internal or exp.opts.linkage == .Weak;
- // When both the to-be-exported symbol and the already existing symbol
- // are strong symbols, we have a linker error.
- // In the other case we replace one with the other.
- if (!exp_is_weak and !existing_sym.isWeak()) {
- try mod.failed_exports.put(gpa, exp, try Module.ErrorMsg.create(
- gpa,
- decl.srcLoc(mod),
- \\LinkError: symbol '{}' defined multiple times
- \\ first definition in '{s}'
- \\ next definition in '{s}'
- ,
- .{ exp.opts.name.fmt(&mod.intern_pool), wasm.name, wasm.name },
- ));
- continue;
- } else if (exp_is_weak) {
- continue; // to-be-exported symbol is weak, so we keep the existing symbol
- } else {
- // TODO: Revisit this, why was this needed?
- existing_loc.index = atom.sym_index;
- existing_loc.file = null;
- // exp.link.wasm.sym_index = existing_loc.index;
- }
- }
-
const exported_atom_index = try wasm.getOrCreateAtomForDecl(exp.exported_decl);
const exported_atom = wasm.getAtom(exported_atom_index);
+ const export_name = try wasm.string_table.put(wasm.base.allocator, mod.intern_pool.stringToSlice(exp.opts.name));
const sym_loc = exported_atom.symbolLoc();
const symbol = sym_loc.getSymbol(wasm);
+ symbol.setGlobal(true);
+ symbol.setUndefined(false);
+ symbol.index = atom_sym.index;
+ symbol.tag = atom_sym.tag;
+ symbol.name = atom_sym.name;
+
switch (exp.opts.linkage) {
.Internal => {
symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
+ symbol.setFlag(.WASM_SYM_BINDING_WEAK);
},
.Weak => {
symbol.setFlag(.WASM_SYM_BINDING_WEAK);
@@ -1768,22 +1747,52 @@ pub fn updateDeclExports(
continue;
},
}
+
+ if (wasm.globals.get(export_name)) |existing_loc| {
+ if (existing_loc.index == atom.sym_index) continue;
+ const existing_sym: Symbol = existing_loc.getSymbol(wasm).*;
+
+ if (!existing_sym.isUndefined()) blk: {
+ if (symbol.isWeak()) {
+ try wasm.discarded.put(wasm.base.allocator, existing_loc, sym_loc);
+ continue; // to-be-exported symbol is weak, so we keep the existing symbol
+ }
+
+ // new symbol is not weak while existing is, replace existing symbol
+ if (existing_sym.isWeak()) {
+ break :blk;
+ }
+ // When both the to-be-exported symbol and the already existing symbol
+ // are strong symbols, we have a linker error.
+ // In the other case we replace one with the other.
+ try mod.failed_exports.put(gpa, exp, try Module.ErrorMsg.create(
+ gpa,
+ decl.srcLoc(mod),
+ \\LinkError: symbol '{}' defined multiple times
+ \\ first definition in '{s}'
+ \\ next definition in '{s}'
+ ,
+ .{ exp.opts.name.fmt(&mod.intern_pool), wasm.name, wasm.name },
+ ));
+ continue;
+ }
+
+ // in this case the existing symbol must be replaced either because it's weak or undefined.
+ try wasm.discarded.put(wasm.base.allocator, existing_loc, sym_loc);
+ _ = wasm.imports.remove(existing_loc);
+ _ = wasm.undefs.swapRemove(existing_sym.name);
+ }
+
// Ensure the symbol will be exported using the given name
if (!mod.intern_pool.stringEqlSlice(exp.opts.name, sym_loc.getName(wasm))) {
try wasm.export_names.put(wasm.base.allocator, sym_loc, export_name);
}
- symbol.setGlobal(true);
- symbol.setUndefined(false);
try wasm.globals.put(
wasm.base.allocator,
export_name,
sym_loc,
);
-
- // if the symbol was previously undefined, remove it as an import
- _ = wasm.imports.remove(sym_loc);
- _ = wasm.undefs.swapRemove(export_name);
}
}