mirror of
https://github.com/ziglang/zig.git
synced 2026-02-21 16:54:52 +00:00
Merge pull request #14157 from Luukdegram/wasm-linker-misc
wasm-linker: various fixes & improvements
This commit is contained in:
commit
7935135767
@ -112,8 +112,6 @@ func_types: std.ArrayListUnmanaged(std.wasm.Type) = .{},
|
||||
functions: std.AutoArrayHashMapUnmanaged(struct { file: ?u16, index: u32 }, std.wasm.Func) = .{},
|
||||
/// Output global section
|
||||
wasm_globals: std.ArrayListUnmanaged(std.wasm.Global) = .{},
|
||||
/// Global symbols for exported data symbols
|
||||
address_globals: std.ArrayListUnmanaged(SymbolLoc) = .{},
|
||||
/// Memory section
|
||||
memories: std.wasm.Memory = .{ .limits = .{ .min = 0, .max = null } },
|
||||
/// Output table section
|
||||
@ -335,41 +333,64 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
|
||||
wasm_bin.base.file = file;
|
||||
wasm_bin.name = sub_path;
|
||||
|
||||
// As sym_index '0' is reserved, we use it for our stack pointer symbol
|
||||
const sym_name = try wasm_bin.string_table.put(allocator, "__stack_pointer");
|
||||
const symbol = try wasm_bin.symbols.addOne(allocator);
|
||||
symbol.* = .{
|
||||
.name = sym_name,
|
||||
.tag = .global,
|
||||
.flags = 0,
|
||||
.index = 0,
|
||||
};
|
||||
const loc: SymbolLoc = .{ .file = null, .index = 0 };
|
||||
try wasm_bin.resolved_symbols.putNoClobber(allocator, loc, {});
|
||||
try wasm_bin.globals.putNoClobber(allocator, sym_name, loc);
|
||||
// create stack pointer symbol
|
||||
{
|
||||
const loc = try wasm_bin.createSyntheticSymbol("__stack_pointer", .global);
|
||||
const symbol = loc.getSymbol(wasm_bin);
|
||||
// For object files we will import the stack pointer symbol
|
||||
if (options.output_mode == .Obj) {
|
||||
symbol.setUndefined(true);
|
||||
symbol.index = @intCast(u32, wasm_bin.imported_globals_count);
|
||||
wasm_bin.imported_globals_count += 1;
|
||||
try wasm_bin.imports.putNoClobber(
|
||||
allocator,
|
||||
loc,
|
||||
.{
|
||||
.module_name = try wasm_bin.string_table.put(allocator, wasm_bin.host_name),
|
||||
.name = symbol.name,
|
||||
.kind = .{ .global = .{ .valtype = .i32, .mutable = true } },
|
||||
},
|
||||
);
|
||||
} else {
|
||||
symbol.index = @intCast(u32, wasm_bin.imported_globals_count + wasm_bin.wasm_globals.items.len);
|
||||
symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
|
||||
const global = try wasm_bin.wasm_globals.addOne(allocator);
|
||||
global.* = .{
|
||||
.global_type = .{
|
||||
.valtype = .i32,
|
||||
.mutable = true,
|
||||
},
|
||||
.init = .{ .i32_const = 0 },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// For object files we will import the stack pointer symbol
|
||||
if (options.output_mode == .Obj) {
|
||||
symbol.setUndefined(true);
|
||||
try wasm_bin.imports.putNoClobber(
|
||||
allocator,
|
||||
.{ .file = null, .index = 0 },
|
||||
.{
|
||||
.module_name = try wasm_bin.string_table.put(allocator, wasm_bin.host_name),
|
||||
.name = sym_name,
|
||||
.kind = .{ .global = .{ .valtype = .i32, .mutable = true } },
|
||||
},
|
||||
);
|
||||
} else {
|
||||
symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
|
||||
const global = try wasm_bin.wasm_globals.addOne(allocator);
|
||||
global.* = .{
|
||||
.global_type = .{
|
||||
.valtype = .i32,
|
||||
.mutable = true,
|
||||
},
|
||||
.init = .{ .i32_const = 0 },
|
||||
// create indirect function pointer symbol
|
||||
{
|
||||
const loc = try wasm_bin.createSyntheticSymbol("__indirect_function_table", .table);
|
||||
const symbol = loc.getSymbol(wasm_bin);
|
||||
const table: std.wasm.Table = .{
|
||||
.limits = .{ .min = 0, .max = null }, // will be overwritten during `mapFunctionTable`
|
||||
.reftype = .funcref,
|
||||
};
|
||||
if (options.output_mode == .Obj or options.import_table) {
|
||||
symbol.setUndefined(true);
|
||||
symbol.index = @intCast(u32, wasm_bin.imported_tables_count);
|
||||
wasm_bin.imported_tables_count += 1;
|
||||
try wasm_bin.imports.put(allocator, loc, .{
|
||||
.module_name = try wasm_bin.string_table.put(allocator, wasm_bin.host_name),
|
||||
.name = symbol.name,
|
||||
.kind = .{ .table = table },
|
||||
});
|
||||
} else {
|
||||
symbol.index = @intCast(u32, wasm_bin.imported_tables_count + wasm_bin.tables.items.len);
|
||||
try wasm_bin.tables.append(allocator, table);
|
||||
if (options.export_table) {
|
||||
symbol.setFlag(.WASM_SYM_EXPORTED);
|
||||
} else {
|
||||
symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!options.strip and options.module != null) {
|
||||
@ -400,6 +421,22 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Wasm {
|
||||
return wasm;
|
||||
}
|
||||
|
||||
/// For a given name, creates a new global synthetic symbol.
|
||||
/// Leaves index undefined and the default flags (0).
|
||||
fn createSyntheticSymbol(wasm: *Wasm, name: []const u8, tag: Symbol.Tag) !SymbolLoc {
|
||||
const name_offset = try wasm.string_table.put(wasm.base.allocator, name);
|
||||
const sym_index = @intCast(u32, wasm.symbols.items.len);
|
||||
const loc: SymbolLoc = .{ .index = sym_index, .file = null };
|
||||
try wasm.symbols.append(wasm.base.allocator, .{
|
||||
.name = name_offset,
|
||||
.flags = 0,
|
||||
.tag = tag,
|
||||
.index = undefined,
|
||||
});
|
||||
try wasm.resolved_symbols.putNoClobber(wasm.base.allocator, loc, {});
|
||||
try wasm.globals.putNoClobber(wasm.base.allocator, name_offset, loc);
|
||||
return loc;
|
||||
}
|
||||
/// Initializes symbols and atoms for the debug sections
|
||||
/// Initialization is only done when compiling Zig code.
|
||||
/// When Zig is invoked as a linker instead, the atoms
|
||||
@ -766,6 +803,7 @@ fn validateFeatures(
|
||||
|
||||
fn checkUndefinedSymbols(wasm: *const Wasm) !void {
|
||||
if (wasm.base.options.output_mode == .Obj) return;
|
||||
if (wasm.base.options.import_symbols) return;
|
||||
|
||||
var found_undefined_symbols = false;
|
||||
for (wasm.undefs.values()) |undef| {
|
||||
@ -775,7 +813,12 @@ fn checkUndefinedSymbols(wasm: *const Wasm) !void {
|
||||
const file_name = if (undef.file) |file_index| name: {
|
||||
break :name wasm.objects.items[file_index].name;
|
||||
} else wasm.name;
|
||||
log.err("could not resolve undefined symbol '{s}'", .{undef.getName(wasm)});
|
||||
const import_name = if (undef.file) |file_index| name: {
|
||||
const obj = wasm.objects.items[file_index];
|
||||
const name_index = obj.findImport(symbol.tag.externalType(), symbol.index).name;
|
||||
break :name obj.string_table.get(name_index);
|
||||
} else wasm.string_table.get(wasm.imports.get(undef).?.name);
|
||||
log.err("could not resolve undefined symbol '{s}'", .{import_name});
|
||||
log.err(" defined in '{s}'", .{file_name});
|
||||
}
|
||||
}
|
||||
@ -840,7 +883,6 @@ pub fn deinit(wasm: *Wasm) void {
|
||||
wasm.func_types.deinit(gpa);
|
||||
wasm.functions.deinit(gpa);
|
||||
wasm.wasm_globals.deinit(gpa);
|
||||
wasm.address_globals.deinit(gpa);
|
||||
wasm.function_table.deinit(gpa);
|
||||
wasm.tables.deinit(gpa);
|
||||
wasm.exports.deinit(gpa);
|
||||
@ -1249,7 +1291,7 @@ pub fn updateDeclExports(
|
||||
const existing_sym: Symbol = existing_loc.getSymbol(wasm).*;
|
||||
|
||||
const exp_is_weak = exp.options.linkage == .Internal or exp.options.linkage == .Weak;
|
||||
// When both the to-bo-exported symbol and the already existing symbol
|
||||
// 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()) {
|
||||
@ -1361,6 +1403,19 @@ fn mapFunctionTable(wasm: *Wasm) void {
|
||||
while (it.next()) |value_ptr| : (index += 1) {
|
||||
value_ptr.* = index;
|
||||
}
|
||||
|
||||
if (wasm.base.options.import_table or wasm.base.options.output_mode == .Obj) {
|
||||
const sym_loc = wasm.globals.get(wasm.string_table.getOffset("__indirect_function_table").?).?;
|
||||
const import = wasm.imports.getPtr(sym_loc).?;
|
||||
import.kind.table.limits.min = index - 1; // we start at index 1.
|
||||
} else if (index > 1) {
|
||||
log.debug("Appending indirect function table", .{});
|
||||
const offset = wasm.string_table.getOffset("__indirect_function_table").?;
|
||||
const sym_with_loc = wasm.globals.get(offset).?;
|
||||
const symbol = sym_with_loc.getSymbol(wasm);
|
||||
const table = &wasm.tables.items[symbol.index - wasm.imported_tables_count];
|
||||
table.limits = .{ .min = index, .max = index };
|
||||
}
|
||||
}
|
||||
|
||||
/// Either creates a new import, or updates one if existing.
|
||||
@ -1380,18 +1435,31 @@ pub fn addOrUpdateImport(
|
||||
type_index: ?u32,
|
||||
) !void {
|
||||
assert(symbol_index != 0);
|
||||
// For the import name itwasm, we use the decl's name, rather than the fully qualified name
|
||||
const decl_name_index = try wasm.string_table.put(wasm.base.allocator, name);
|
||||
// For the import name, we use the decl's name, rather than the fully qualified name
|
||||
// Also mangle the name when the lib name is set and not equal to "C" so imports with the same
|
||||
// name but different module can be resolved correctly.
|
||||
const mangle_name = lib_name != null and
|
||||
!std.mem.eql(u8, std.mem.sliceTo(lib_name.?, 0), "c");
|
||||
const full_name = if (mangle_name) full_name: {
|
||||
break :full_name try std.fmt.allocPrint(wasm.base.allocator, "{s}|{s}", .{ name, lib_name.? });
|
||||
} else name;
|
||||
defer if (mangle_name) wasm.base.allocator.free(full_name);
|
||||
|
||||
const decl_name_index = try wasm.string_table.put(wasm.base.allocator, full_name);
|
||||
const symbol: *Symbol = &wasm.symbols.items[symbol_index];
|
||||
symbol.setUndefined(true);
|
||||
symbol.setGlobal(true);
|
||||
symbol.name = decl_name_index;
|
||||
if (mangle_name) {
|
||||
// we specified a specific name for the symbol that does not match the import name
|
||||
symbol.setFlag(.WASM_SYM_EXPLICIT_NAME);
|
||||
}
|
||||
const global_gop = try wasm.globals.getOrPut(wasm.base.allocator, decl_name_index);
|
||||
if (!global_gop.found_existing) {
|
||||
const loc: SymbolLoc = .{ .file = null, .index = symbol_index };
|
||||
global_gop.value_ptr.* = loc;
|
||||
try wasm.resolved_symbols.put(wasm.base.allocator, loc, {});
|
||||
try wasm.undefs.putNoClobber(wasm.base.allocator, name, loc);
|
||||
try wasm.undefs.putNoClobber(wasm.base.allocator, full_name, loc);
|
||||
}
|
||||
|
||||
if (type_index) |ty_index| {
|
||||
@ -1402,7 +1470,7 @@ pub fn addOrUpdateImport(
|
||||
if (!gop.found_existing) {
|
||||
gop.value_ptr.* = .{
|
||||
.module_name = try wasm.string_table.put(wasm.base.allocator, module_name),
|
||||
.name = decl_name_index,
|
||||
.name = try wasm.string_table.put(wasm.base.allocator, name),
|
||||
.kind = .{ .function = ty_index },
|
||||
};
|
||||
}
|
||||
@ -1700,17 +1768,6 @@ fn setupImports(wasm: *Wasm) !void {
|
||||
/// Takes the global, function and table section from each linked object file
|
||||
/// and merges it into a single section for each.
|
||||
fn mergeSections(wasm: *Wasm) !void {
|
||||
// append the indirect function table if initialized
|
||||
if (wasm.string_table.getOffset("__indirect_function_table")) |offset| {
|
||||
const sym_loc = wasm.globals.get(offset).?;
|
||||
const table: std.wasm.Table = .{
|
||||
.limits = .{ .min = @intCast(u32, wasm.function_table.count()), .max = null },
|
||||
.reftype = .funcref,
|
||||
};
|
||||
sym_loc.getSymbol(wasm).index = @intCast(u32, wasm.tables.items.len) + wasm.imported_tables_count;
|
||||
try wasm.tables.append(wasm.base.allocator, table);
|
||||
}
|
||||
|
||||
for (wasm.resolved_symbols.keys()) |sym_loc| {
|
||||
if (sym_loc.file == null) {
|
||||
// Zig code-generated symbols are already within the sections and do not
|
||||
@ -1800,9 +1857,36 @@ fn setupExports(wasm: *Wasm) !void {
|
||||
if (wasm.base.options.output_mode == .Obj) return;
|
||||
log.debug("Building exports from symbols", .{});
|
||||
|
||||
const force_exp_names = wasm.base.options.export_symbol_names;
|
||||
if (force_exp_names.len > 0) {
|
||||
var failed_exports = try std.ArrayList([]const u8).initCapacity(wasm.base.allocator, force_exp_names.len);
|
||||
defer failed_exports.deinit();
|
||||
|
||||
for (force_exp_names) |exp_name| {
|
||||
const name_index = wasm.string_table.getOffset(exp_name) orelse {
|
||||
failed_exports.appendAssumeCapacity(exp_name);
|
||||
continue;
|
||||
};
|
||||
const loc = wasm.globals.get(name_index) orelse {
|
||||
failed_exports.appendAssumeCapacity(exp_name);
|
||||
continue;
|
||||
};
|
||||
|
||||
const symbol = loc.getSymbol(wasm);
|
||||
symbol.setFlag(.WASM_SYM_EXPORTED);
|
||||
}
|
||||
|
||||
if (failed_exports.items.len > 0) {
|
||||
for (failed_exports.items) |exp_name| {
|
||||
log.err("could not export '{s}', symbol not found", .{exp_name});
|
||||
}
|
||||
return error.MissingSymbol;
|
||||
}
|
||||
}
|
||||
|
||||
for (wasm.resolved_symbols.keys()) |sym_loc| {
|
||||
const symbol = sym_loc.getSymbol(wasm);
|
||||
if (!symbol.isExported()) continue;
|
||||
if (!symbol.isExported(wasm.base.options.rdynamic)) continue;
|
||||
|
||||
const sym_name = sym_loc.getName(wasm);
|
||||
const export_name = if (wasm.export_names.get(sym_loc)) |name| name else blk: {
|
||||
@ -1810,8 +1894,13 @@ fn setupExports(wasm: *Wasm) !void {
|
||||
break :blk try wasm.string_table.put(wasm.base.allocator, sym_name);
|
||||
};
|
||||
const exp: types.Export = if (symbol.tag == .data) exp: {
|
||||
const global_index = @intCast(u32, wasm.wasm_globals.items.len + wasm.address_globals.items.len);
|
||||
try wasm.address_globals.append(wasm.base.allocator, sym_loc);
|
||||
const atom = wasm.symbol_atom.get(sym_loc).?;
|
||||
const va = atom.getVA(wasm, symbol);
|
||||
const global_index = @intCast(u32, wasm.imported_globals_count + wasm.wasm_globals.items.len);
|
||||
try wasm.wasm_globals.append(wasm.base.allocator, .{
|
||||
.global_type = .{ .valtype = .i32, .mutable = false },
|
||||
.init = .{ .i32_const = @intCast(i32, va) },
|
||||
});
|
||||
break :exp .{
|
||||
.name = export_name,
|
||||
.kind = .global,
|
||||
@ -2399,6 +2488,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l
|
||||
var enabled_features: [@typeInfo(types.Feature.Tag).Enum.fields.len]bool = undefined;
|
||||
try wasm.validateFeatures(&enabled_features, &emit_features_count);
|
||||
try wasm.resolveSymbolsInArchives();
|
||||
try wasm.checkUndefinedSymbols();
|
||||
|
||||
try wasm.setupStart();
|
||||
try wasm.setupImports();
|
||||
@ -2586,28 +2676,9 @@ fn writeToFile(
|
||||
|
||||
// Import section
|
||||
const import_memory = wasm.base.options.import_memory or is_obj;
|
||||
const import_table = wasm.base.options.import_table or is_obj;
|
||||
if (wasm.imports.count() != 0 or import_memory or import_table) {
|
||||
if (wasm.imports.count() != 0 or import_memory) {
|
||||
const header_offset = try reserveVecSectionHeader(&binary_bytes);
|
||||
|
||||
// import table is always first table so emit that first
|
||||
if (import_table) {
|
||||
const table_imp: types.Import = .{
|
||||
.module_name = try wasm.string_table.put(wasm.base.allocator, wasm.host_name),
|
||||
.name = try wasm.string_table.put(wasm.base.allocator, "__indirect_function_table"),
|
||||
.kind = .{
|
||||
.table = .{
|
||||
.limits = .{
|
||||
.min = @intCast(u32, wasm.function_table.count()),
|
||||
.max = null,
|
||||
},
|
||||
.reftype = .funcref,
|
||||
},
|
||||
},
|
||||
};
|
||||
try wasm.emitImport(binary_writer, table_imp);
|
||||
}
|
||||
|
||||
var it = wasm.imports.iterator();
|
||||
while (it.next()) |entry| {
|
||||
assert(entry.key_ptr.*.getSymbol(wasm).isUndefined());
|
||||
@ -2630,7 +2701,7 @@ fn writeToFile(
|
||||
header_offset,
|
||||
.import,
|
||||
@intCast(u32, binary_bytes.items.len - header_offset - header_size),
|
||||
@intCast(u32, wasm.imports.count() + @boolToInt(import_memory) + @boolToInt(import_table)),
|
||||
@intCast(u32, wasm.imports.count() + @boolToInt(import_memory)),
|
||||
);
|
||||
section_count += 1;
|
||||
}
|
||||
@ -2653,22 +2724,20 @@ fn writeToFile(
|
||||
}
|
||||
|
||||
// Table section
|
||||
const export_table = wasm.base.options.export_table;
|
||||
if (!import_table and wasm.function_table.count() != 0) {
|
||||
if (wasm.tables.items.len > 0) {
|
||||
const header_offset = try reserveVecSectionHeader(&binary_bytes);
|
||||
|
||||
try leb.writeULEB128(binary_writer, std.wasm.reftype(.funcref));
|
||||
try emitLimits(binary_writer, .{
|
||||
.min = @intCast(u32, wasm.function_table.count()) + 1,
|
||||
.max = null,
|
||||
});
|
||||
for (wasm.tables.items) |table| {
|
||||
try leb.writeULEB128(binary_writer, std.wasm.reftype(table.reftype));
|
||||
try emitLimits(binary_writer, table.limits);
|
||||
}
|
||||
|
||||
try writeVecSectionHeader(
|
||||
binary_bytes.items,
|
||||
header_offset,
|
||||
.table,
|
||||
@intCast(u32, binary_bytes.items.len - header_offset - header_size),
|
||||
@as(u32, 1),
|
||||
@intCast(u32, wasm.tables.items.len),
|
||||
);
|
||||
section_count += 1;
|
||||
}
|
||||
@ -2692,22 +2761,10 @@ fn writeToFile(
|
||||
if (wasm.wasm_globals.items.len > 0) {
|
||||
const header_offset = try reserveVecSectionHeader(&binary_bytes);
|
||||
|
||||
var global_count: u32 = 0;
|
||||
for (wasm.wasm_globals.items) |global| {
|
||||
try binary_writer.writeByte(std.wasm.valtype(global.global_type.valtype));
|
||||
try binary_writer.writeByte(@boolToInt(global.global_type.mutable));
|
||||
try emitInit(binary_writer, global.init);
|
||||
global_count += 1;
|
||||
}
|
||||
|
||||
for (wasm.address_globals.items) |sym_loc| {
|
||||
const atom = wasm.symbol_atom.get(sym_loc).?;
|
||||
try binary_writer.writeByte(std.wasm.valtype(.i32));
|
||||
try binary_writer.writeByte(0); // immutable
|
||||
try emitInit(binary_writer, .{
|
||||
.i32_const = @bitCast(i32, atom.offset),
|
||||
});
|
||||
global_count += 1;
|
||||
}
|
||||
|
||||
try writeVecSectionHeader(
|
||||
@ -2715,13 +2772,13 @@ fn writeToFile(
|
||||
header_offset,
|
||||
.global,
|
||||
@intCast(u32, binary_bytes.items.len - header_offset - header_size),
|
||||
@intCast(u32, global_count),
|
||||
@intCast(u32, wasm.wasm_globals.items.len),
|
||||
);
|
||||
section_count += 1;
|
||||
}
|
||||
|
||||
// Export section
|
||||
if (wasm.exports.items.len != 0 or export_table or !import_memory) {
|
||||
if (wasm.exports.items.len != 0 or !import_memory) {
|
||||
const header_offset = try reserveVecSectionHeader(&binary_bytes);
|
||||
|
||||
for (wasm.exports.items) |exp| {
|
||||
@ -2732,13 +2789,6 @@ fn writeToFile(
|
||||
try leb.writeULEB128(binary_writer, exp.index);
|
||||
}
|
||||
|
||||
if (export_table) {
|
||||
try leb.writeULEB128(binary_writer, @intCast(u32, "__indirect_function_table".len));
|
||||
try binary_writer.writeAll("__indirect_function_table");
|
||||
try binary_writer.writeByte(std.wasm.externalKind(.table));
|
||||
try leb.writeULEB128(binary_writer, @as(u32, 0)); // function table is always the first table
|
||||
}
|
||||
|
||||
if (!import_memory) {
|
||||
try leb.writeULEB128(binary_writer, @intCast(u32, "memory".len));
|
||||
try binary_writer.writeAll("memory");
|
||||
@ -2751,7 +2801,7 @@ fn writeToFile(
|
||||
header_offset,
|
||||
.@"export",
|
||||
@intCast(u32, binary_bytes.items.len - header_offset - header_size),
|
||||
@intCast(u32, wasm.exports.items.len) + @boolToInt(export_table) + @boolToInt(!import_memory),
|
||||
@intCast(u32, wasm.exports.items.len) + @boolToInt(!import_memory),
|
||||
);
|
||||
section_count += 1;
|
||||
}
|
||||
@ -2760,11 +2810,18 @@ fn writeToFile(
|
||||
if (wasm.function_table.count() > 0) {
|
||||
const header_offset = try reserveVecSectionHeader(&binary_bytes);
|
||||
|
||||
var flags: u32 = 0x2; // Yes we have a table
|
||||
const table_loc = wasm.globals.get(wasm.string_table.getOffset("__indirect_function_table").?).?;
|
||||
const table_sym = table_loc.getSymbol(wasm);
|
||||
|
||||
var flags: u32 = if (table_sym.index == 0) 0x0 else 0x02; // passive with implicit 0-index table or set table index manually
|
||||
try leb.writeULEB128(binary_writer, flags);
|
||||
try leb.writeULEB128(binary_writer, @as(u32, 0)); // index of that table. TODO: Store synthetic symbols
|
||||
if (flags == 0x02) {
|
||||
try leb.writeULEB128(binary_writer, table_sym.index);
|
||||
}
|
||||
try emitInit(binary_writer, .{ .i32_const = 1 }); // We start at index 1, so unresolved function pointers are invalid
|
||||
try leb.writeULEB128(binary_writer, @as(u8, 0));
|
||||
if (flags == 0x02) {
|
||||
try leb.writeULEB128(binary_writer, @as(u8, 0)); // represents funcref
|
||||
}
|
||||
try leb.writeULEB128(binary_writer, @intCast(u32, wasm.function_table.count()));
|
||||
var symbol_it = wasm.function_table.keyIterator();
|
||||
while (symbol_it.next()) |symbol_loc_ptr| {
|
||||
@ -3091,11 +3148,7 @@ fn emitNameSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), arena: std.mem
|
||||
|
||||
for (wasm.resolved_symbols.keys()) |sym_loc| {
|
||||
const symbol = sym_loc.getSymbol(wasm).*;
|
||||
const name = if (symbol.isUndefined()) blk: {
|
||||
if (symbol.tag == .data) continue;
|
||||
const imp = wasm.imports.get(sym_loc) orelse continue;
|
||||
break :blk wasm.string_table.get(imp.name);
|
||||
} else sym_loc.getName(wasm);
|
||||
const name = sym_loc.getName(wasm);
|
||||
switch (symbol.tag) {
|
||||
.function => {
|
||||
const gop = funcs.getOrPutAssumeCapacity(symbol.index);
|
||||
|
||||
@ -90,24 +90,26 @@ pub fn getFirst(atom: *Atom) *Atom {
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/// Unlike `getFirst` this returns the first `*Atom` that was
|
||||
/// produced from Zig code, rather than an object file.
|
||||
/// This is useful for debug sections where we want to extend
|
||||
/// the bytes, and don't want to overwrite existing Atoms.
|
||||
pub fn getFirstZigAtom(atom: *Atom) *Atom {
|
||||
if (atom.file == null) return atom;
|
||||
var tmp = atom;
|
||||
return while (tmp.prev) |prev| {
|
||||
if (prev.file == null) break prev;
|
||||
tmp = prev;
|
||||
} else unreachable; // must allocate an Atom first!
|
||||
}
|
||||
|
||||
/// Returns the location of the symbol that represents this `Atom`
|
||||
pub fn symbolLoc(atom: Atom) Wasm.SymbolLoc {
|
||||
return .{ .file = atom.file, .index = atom.sym_index };
|
||||
}
|
||||
|
||||
/// Returns the virtual address of the `Atom`. This is the address starting
|
||||
/// from the first entry within a section.
|
||||
pub fn getVA(atom: Atom, wasm: *const Wasm, symbol: *const Symbol) u32 {
|
||||
if (symbol.tag == .function) return atom.offset;
|
||||
std.debug.assert(symbol.tag == .data);
|
||||
const merge_segment = wasm.base.options.output_mode != .Obj;
|
||||
const segment_info = if (atom.file) |object_index| blk: {
|
||||
break :blk wasm.objects.items[object_index].segment_info;
|
||||
} else wasm.segment_info.values();
|
||||
const segment_name = segment_info[symbol.index].outputName(merge_segment);
|
||||
const segment_index = wasm.data_segments.get(segment_name).?;
|
||||
const segment = wasm.segments.items[segment_index];
|
||||
return segment.offset + atom.offset;
|
||||
}
|
||||
|
||||
/// Resolves the relocations within the atom, writing the new value
|
||||
/// at the calculated offset.
|
||||
pub fn resolveRelocs(atom: *Atom, wasm_bin: *const Wasm) void {
|
||||
@ -159,7 +161,7 @@ pub fn resolveRelocs(atom: *Atom, wasm_bin: *const Wasm) void {
|
||||
/// The final value must be casted to the correct size.
|
||||
fn relocationValue(atom: Atom, relocation: types.Relocation, wasm_bin: *const Wasm) u64 {
|
||||
const target_loc = (Wasm.SymbolLoc{ .file = atom.file, .index = relocation.index }).finalLoc(wasm_bin);
|
||||
const symbol = target_loc.getSymbol(wasm_bin).*;
|
||||
const symbol = target_loc.getSymbol(wasm_bin);
|
||||
switch (relocation.relocation_type) {
|
||||
.R_WASM_FUNCTION_INDEX_LEB => return symbol.index,
|
||||
.R_WASM_TABLE_NUMBER_LEB => return symbol.index,
|
||||
@ -190,17 +192,9 @@ fn relocationValue(atom: Atom, relocation: types.Relocation, wasm_bin: *const Wa
|
||||
if (symbol.isUndefined()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const merge_segment = wasm_bin.base.options.output_mode != .Obj;
|
||||
const target_atom = wasm_bin.symbol_atom.get(target_loc).?;
|
||||
const segment_info = if (target_atom.file) |object_index| blk: {
|
||||
break :blk wasm_bin.objects.items[object_index].segment_info;
|
||||
} else wasm_bin.segment_info.values();
|
||||
const segment_name = segment_info[symbol.index].outputName(merge_segment);
|
||||
const segment_index = wasm_bin.data_segments.get(segment_name).?;
|
||||
const segment = wasm_bin.segments.items[segment_index];
|
||||
const rel_value = @intCast(i32, target_atom.offset + segment.offset) + relocation.addend;
|
||||
return @intCast(u32, rel_value);
|
||||
const va = @intCast(i32, target_atom.getVA(wasm_bin, symbol));
|
||||
return @intCast(u32, va + relocation.addend);
|
||||
},
|
||||
.R_WASM_EVENT_INDEX_LEB => return symbol.index,
|
||||
.R_WASM_SECTION_OFFSET_I32 => {
|
||||
|
||||
@ -139,12 +139,10 @@ pub fn isNoStrip(symbol: Symbol) bool {
|
||||
return symbol.flags & @enumToInt(Flag.WASM_SYM_NO_STRIP) != 0;
|
||||
}
|
||||
|
||||
pub fn isExported(symbol: Symbol) bool {
|
||||
pub fn isExported(symbol: Symbol, is_dynamic: bool) bool {
|
||||
if (symbol.isUndefined() or symbol.isLocal()) return false;
|
||||
if (symbol.isHidden()) return false;
|
||||
if (symbol.hasFlag(.WASM_SYM_EXPORTED)) return true;
|
||||
if (symbol.hasFlag(.WASM_SYM_BINDING_WEAK)) return false;
|
||||
return true;
|
||||
if (is_dynamic and symbol.isVisible()) return true;
|
||||
return symbol.hasFlag(.WASM_SYM_EXPORTED);
|
||||
}
|
||||
|
||||
pub fn isWeak(symbol: Symbol) bool {
|
||||
|
||||
@ -42,6 +42,16 @@ fn addWasmCases(cases: *tests.StandaloneContext) void {
|
||||
.requires_stage2 = true,
|
||||
});
|
||||
|
||||
cases.addBuildFile("test/link/wasm/export/build.zig", .{
|
||||
.build_modes = true,
|
||||
.requires_stage2 = true,
|
||||
});
|
||||
|
||||
// TODO: Fix open handle in wasm-linker refraining rename from working on Windows.
|
||||
if (builtin.os.tag != .windows) {
|
||||
cases.addBuildFile("test/link/wasm/export-data/build.zig", .{});
|
||||
}
|
||||
|
||||
cases.addBuildFile("test/link/wasm/extern/build.zig", .{
|
||||
.build_modes = true,
|
||||
.requires_stage2 = true,
|
||||
@ -53,6 +63,11 @@ fn addWasmCases(cases: *tests.StandaloneContext) void {
|
||||
.requires_stage2 = true,
|
||||
});
|
||||
|
||||
cases.addBuildFile("test/link/wasm/function-table/build.zig", .{
|
||||
.build_modes = true,
|
||||
.requires_stage2 = true,
|
||||
});
|
||||
|
||||
cases.addBuildFile("test/link/wasm/infer-features/build.zig", .{
|
||||
.requires_stage2 = true,
|
||||
});
|
||||
|
||||
@ -26,8 +26,7 @@ pub fn build(b: *Builder) void {
|
||||
check_lib.checkNext("name memory"); // as per linker specification
|
||||
|
||||
// since we are importing memory, ensure it's not exported
|
||||
check_lib.checkStart("Section export");
|
||||
check_lib.checkNext("entries 1"); // we're exporting function 'foo' so only 1 entry
|
||||
check_lib.checkNotPresent("Section export");
|
||||
|
||||
// validate the name of the stack pointer
|
||||
check_lib.checkStart("Section custom");
|
||||
|
||||
39
test/link/wasm/export-data/build.zig
Normal file
39
test/link/wasm/export-data/build.zig
Normal file
@ -0,0 +1,39 @@
|
||||
const std = @import("std");
|
||||
const Builder = std.build.Builder;
|
||||
|
||||
pub fn build(b: *Builder) void {
|
||||
const test_step = b.step("test", "Test");
|
||||
test_step.dependOn(b.getInstallStep());
|
||||
|
||||
const lib = b.addSharedLibrary("lib", "lib.zig", .unversioned);
|
||||
lib.setBuildMode(.ReleaseSafe); // to make the output deterministic in address positions
|
||||
lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
|
||||
lib.use_lld = false;
|
||||
lib.export_symbol_names = &.{ "foo", "bar" };
|
||||
lib.global_base = 0; // put data section at address 0 to make data symbols easier to parse
|
||||
|
||||
const check_lib = lib.checkObject(.wasm);
|
||||
|
||||
check_lib.checkStart("Section global");
|
||||
check_lib.checkNext("entries 3");
|
||||
check_lib.checkNext("type i32"); // stack pointer so skip other fields
|
||||
check_lib.checkNext("type i32");
|
||||
check_lib.checkNext("mutable false");
|
||||
check_lib.checkNext("i32.const {foo_address}");
|
||||
check_lib.checkNext("type i32");
|
||||
check_lib.checkNext("mutable false");
|
||||
check_lib.checkNext("i32.const {bar_address}");
|
||||
check_lib.checkComputeCompare("foo_address", .{ .op = .eq, .value = .{ .literal = 0 } });
|
||||
check_lib.checkComputeCompare("bar_address", .{ .op = .eq, .value = .{ .literal = 4 } });
|
||||
|
||||
check_lib.checkStart("Section export");
|
||||
check_lib.checkNext("entries 3");
|
||||
check_lib.checkNext("name foo");
|
||||
check_lib.checkNext("kind global");
|
||||
check_lib.checkNext("index 1");
|
||||
check_lib.checkNext("name bar");
|
||||
check_lib.checkNext("kind global");
|
||||
check_lib.checkNext("index 2");
|
||||
|
||||
test_step.dependOn(&check_lib.step);
|
||||
}
|
||||
2
test/link/wasm/export-data/lib.zig
Normal file
2
test/link/wasm/export-data/lib.zig
Normal file
@ -0,0 +1,2 @@
|
||||
export const foo: u32 = 0xbbbbbbbb;
|
||||
export const bar: u32 = 0xbbbbbbbb;
|
||||
48
test/link/wasm/export/build.zig
Normal file
48
test/link/wasm/export/build.zig
Normal file
@ -0,0 +1,48 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub fn build(b: *std.build.Builder) void {
|
||||
const mode = b.standardReleaseOptions();
|
||||
|
||||
const no_export = b.addSharedLibrary("no-export", "main.zig", .unversioned);
|
||||
no_export.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
|
||||
no_export.setBuildMode(mode);
|
||||
no_export.use_llvm = false;
|
||||
no_export.use_lld = false;
|
||||
|
||||
const dynamic_export = b.addSharedLibrary("dynamic", "main.zig", .unversioned);
|
||||
dynamic_export.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
|
||||
dynamic_export.setBuildMode(mode);
|
||||
dynamic_export.rdynamic = true;
|
||||
dynamic_export.use_llvm = false;
|
||||
dynamic_export.use_lld = false;
|
||||
|
||||
const force_export = b.addSharedLibrary("force", "main.zig", .unversioned);
|
||||
force_export.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
|
||||
force_export.setBuildMode(mode);
|
||||
force_export.export_symbol_names = &.{"foo"};
|
||||
force_export.use_llvm = false;
|
||||
force_export.use_lld = false;
|
||||
|
||||
const check_no_export = no_export.checkObject(.wasm);
|
||||
check_no_export.checkStart("Section export");
|
||||
check_no_export.checkNext("entries 1");
|
||||
check_no_export.checkNext("name memory");
|
||||
check_no_export.checkNext("kind memory");
|
||||
|
||||
const check_dynamic_export = dynamic_export.checkObject(.wasm);
|
||||
check_dynamic_export.checkStart("Section export");
|
||||
check_dynamic_export.checkNext("entries 2");
|
||||
check_dynamic_export.checkNext("name foo");
|
||||
check_dynamic_export.checkNext("kind function");
|
||||
|
||||
const check_force_export = force_export.checkObject(.wasm);
|
||||
check_force_export.checkStart("Section export");
|
||||
check_force_export.checkNext("entries 2");
|
||||
check_force_export.checkNext("name foo");
|
||||
check_force_export.checkNext("kind function");
|
||||
|
||||
const test_step = b.step("test", "Run linker test");
|
||||
test_step.dependOn(&check_no_export.step);
|
||||
test_step.dependOn(&check_dynamic_export.step);
|
||||
test_step.dependOn(&check_force_export.step);
|
||||
}
|
||||
1
test/link/wasm/export/main.zig
Normal file
1
test/link/wasm/export/main.zig
Normal file
@ -0,0 +1 @@
|
||||
export fn foo() void {}
|
||||
63
test/link/wasm/function-table/build.zig
Normal file
63
test/link/wasm/function-table/build.zig
Normal file
@ -0,0 +1,63 @@
|
||||
const std = @import("std");
|
||||
const Builder = std.build.Builder;
|
||||
|
||||
pub fn build(b: *Builder) void {
|
||||
const mode = b.standardReleaseOptions();
|
||||
|
||||
const test_step = b.step("test", "Test");
|
||||
test_step.dependOn(b.getInstallStep());
|
||||
|
||||
const import_table = b.addSharedLibrary("lib", "lib.zig", .unversioned);
|
||||
import_table.setBuildMode(mode);
|
||||
import_table.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
|
||||
import_table.use_llvm = false;
|
||||
import_table.use_lld = false;
|
||||
import_table.import_table = true;
|
||||
|
||||
const export_table = b.addSharedLibrary("lib", "lib.zig", .unversioned);
|
||||
export_table.setBuildMode(mode);
|
||||
export_table.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
|
||||
export_table.use_llvm = false;
|
||||
export_table.use_lld = false;
|
||||
export_table.export_table = true;
|
||||
|
||||
const regular_table = b.addSharedLibrary("lib", "lib.zig", .unversioned);
|
||||
regular_table.setBuildMode(mode);
|
||||
regular_table.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
|
||||
regular_table.use_llvm = false;
|
||||
regular_table.use_lld = false;
|
||||
|
||||
const check_import = import_table.checkObject(.wasm);
|
||||
const check_export = export_table.checkObject(.wasm);
|
||||
const check_regular = regular_table.checkObject(.wasm);
|
||||
|
||||
check_import.checkStart("Section import");
|
||||
check_import.checkNext("entries 1");
|
||||
check_import.checkNext("module env");
|
||||
check_import.checkNext("name __indirect_function_table");
|
||||
check_import.checkNext("kind table");
|
||||
check_import.checkNext("type funcref");
|
||||
check_import.checkNext("min 1"); // 1 function pointer
|
||||
check_import.checkNotPresent("max"); // when importing, we do not provide a max
|
||||
check_import.checkNotPresent("Section table"); // we're importing it
|
||||
|
||||
check_export.checkStart("Section export");
|
||||
check_export.checkNext("entries 2");
|
||||
check_export.checkNext("name __indirect_function_table"); // as per linker specification
|
||||
check_export.checkNext("kind table");
|
||||
|
||||
check_regular.checkStart("Section table");
|
||||
check_regular.checkNext("entries 1");
|
||||
check_regular.checkNext("type funcref");
|
||||
check_regular.checkNext("min 2"); // index starts at 1 & 1 function pointer = 2.
|
||||
check_regular.checkNext("max 2");
|
||||
check_regular.checkStart("Section element");
|
||||
check_regular.checkNext("entries 1");
|
||||
check_regular.checkNext("table index 0");
|
||||
check_regular.checkNext("i32.const 1"); // we want to start function indexes at 1
|
||||
check_regular.checkNext("indexes 1"); // 1 function pointer
|
||||
|
||||
test_step.dependOn(&check_import.step);
|
||||
test_step.dependOn(&check_export.step);
|
||||
test_step.dependOn(&check_regular.step);
|
||||
}
|
||||
7
test/link/wasm/function-table/lib.zig
Normal file
7
test/link/wasm/function-table/lib.zig
Normal file
@ -0,0 +1,7 @@
|
||||
var func: *const fn () void = &bar;
|
||||
|
||||
export fn foo() void {
|
||||
func();
|
||||
}
|
||||
|
||||
fn bar() void {}
|
||||
Loading…
x
Reference in New Issue
Block a user