mirror of
https://github.com/ziglang/zig.git
synced 2026-02-09 11:03:30 +00:00
Merge pull request #14006 from Luukdegram/wasm-no-lld
wasm-linker: allow usage of the in-house WebAssembly linker
This commit is contained in:
commit
dc22c3b9a5
@ -112,6 +112,8 @@ 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
|
||||
@ -311,13 +313,23 @@ pub const StringTable = struct {
|
||||
pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*Wasm {
|
||||
assert(options.target.ofmt == .wasm);
|
||||
|
||||
if (build_options.have_llvm and options.use_llvm) {
|
||||
if (build_options.have_llvm and options.use_llvm and options.use_lld) {
|
||||
return createEmpty(allocator, options);
|
||||
}
|
||||
|
||||
const wasm_bin = try createEmpty(allocator, options);
|
||||
errdefer wasm_bin.base.destroy();
|
||||
|
||||
// We are not using LLD at this point, so ensure we set the intermediary basename
|
||||
if (build_options.have_llvm and options.use_llvm and options.module != null) {
|
||||
// TODO this intermediary_basename isn't enough; in the case of `zig build-exe`,
|
||||
// we also want to put the intermediary object file in the cache while the
|
||||
// main emit directory is the cwd.
|
||||
wasm_bin.base.intermediary_basename = try std.fmt.allocPrint(allocator, "{s}{s}", .{
|
||||
options.emit.?.sub_path, options.target.ofmt.fileExt(options.target.cpu.arch),
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: read the file and keep valid parts instead of truncating
|
||||
const file = try options.emit.?.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true });
|
||||
wasm_bin.base.file = file;
|
||||
@ -613,7 +625,7 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void {
|
||||
try wasm.resolved_symbols.put(wasm.base.allocator, location, {});
|
||||
assert(wasm.resolved_symbols.swapRemove(existing_loc));
|
||||
if (existing_sym.isUndefined()) {
|
||||
assert(wasm.undefs.swapRemove(sym_name));
|
||||
_ = wasm.undefs.swapRemove(sym_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -624,8 +636,7 @@ fn resolveSymbolsInArchives(wasm: *Wasm) !void {
|
||||
log.debug("Resolving symbols in archives", .{});
|
||||
var index: u32 = 0;
|
||||
undef_loop: while (index < wasm.undefs.count()) {
|
||||
const undef_sym_loc = wasm.undefs.values()[index];
|
||||
const sym_name = undef_sym_loc.getName(wasm);
|
||||
const sym_name = wasm.undefs.keys()[index];
|
||||
|
||||
for (wasm.archives.items) |archive| {
|
||||
const offset = archive.toc.get(sym_name) orelse {
|
||||
@ -829,6 +840,7 @@ 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);
|
||||
@ -1556,9 +1568,13 @@ fn allocateAtoms(wasm: *Wasm) !void {
|
||||
var atom: *Atom = entry.value_ptr.*.getFirst();
|
||||
var offset: u32 = 0;
|
||||
while (true) {
|
||||
const symbol_loc = atom.symbolLoc();
|
||||
if (!wasm.resolved_symbols.contains(symbol_loc)) {
|
||||
atom = atom.next orelse break;
|
||||
continue;
|
||||
}
|
||||
offset = std.mem.alignForwardGeneric(u32, offset, atom.alignment);
|
||||
atom.offset = offset;
|
||||
const symbol_loc = atom.symbolLoc();
|
||||
log.debug("Atom '{s}' allocated from 0x{x:0>8} to 0x{x:0>8} size={d}", .{
|
||||
symbol_loc.getName(wasm),
|
||||
offset,
|
||||
@ -1566,7 +1582,7 @@ fn allocateAtoms(wasm: *Wasm) !void {
|
||||
atom.size,
|
||||
});
|
||||
offset += atom.size;
|
||||
try wasm.symbol_atom.put(wasm.base.allocator, atom.symbolLoc(), atom); // Update atom pointers
|
||||
try wasm.symbol_atom.put(wasm.base.allocator, symbol_loc, atom); // Update atom pointers
|
||||
atom = atom.next orelse break;
|
||||
}
|
||||
segment.size = std.mem.alignForwardGeneric(u32, offset, segment.alignment);
|
||||
@ -1702,7 +1718,7 @@ fn mergeSections(wasm: *Wasm) !void {
|
||||
continue;
|
||||
}
|
||||
|
||||
const object = wasm.objects.items[sym_loc.file.?];
|
||||
const object = &wasm.objects.items[sym_loc.file.?];
|
||||
const symbol = &object.symtable[sym_loc.index];
|
||||
if (symbol.isUndefined() or (symbol.tag != .function and symbol.tag != .global and symbol.tag != .table)) {
|
||||
// Skip undefined symbols as they go in the `import` section
|
||||
@ -1714,13 +1730,12 @@ fn mergeSections(wasm: *Wasm) !void {
|
||||
const index = symbol.index - offset;
|
||||
switch (symbol.tag) {
|
||||
.function => {
|
||||
const original_func = object.functions[index];
|
||||
const gop = try wasm.functions.getOrPut(
|
||||
wasm.base.allocator,
|
||||
.{ .file = sym_loc.file, .index = symbol.index },
|
||||
);
|
||||
if (!gop.found_existing) {
|
||||
gop.value_ptr.* = original_func;
|
||||
gop.value_ptr.* = object.functions[index];
|
||||
}
|
||||
symbol.index = @intCast(u32, gop.index) + wasm.imported_functions_count;
|
||||
},
|
||||
@ -1768,7 +1783,7 @@ fn mergeTypes(wasm: *Wasm) !void {
|
||||
|
||||
if (symbol.isUndefined()) {
|
||||
log.debug("Adding type from extern function '{s}'", .{sym_loc.getName(wasm)});
|
||||
const import: *types.Import = wasm.imports.getPtr(sym_loc).?;
|
||||
const import: *types.Import = wasm.imports.getPtr(sym_loc) orelse continue;
|
||||
const original_type = object.func_types[import.kind.function];
|
||||
import.kind.function = try wasm.putOrGetFuncType(original_type);
|
||||
} else if (!dirty.contains(symbol.index)) {
|
||||
@ -1794,7 +1809,15 @@ fn setupExports(wasm: *Wasm) !void {
|
||||
if (sym_loc.file == null) break :blk symbol.name;
|
||||
break :blk try wasm.string_table.put(wasm.base.allocator, sym_name);
|
||||
};
|
||||
const exp: types.Export = .{
|
||||
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);
|
||||
break :exp .{
|
||||
.name = export_name,
|
||||
.kind = .global,
|
||||
.index = global_index,
|
||||
};
|
||||
} else .{
|
||||
.name = export_name,
|
||||
.kind = symbol.tag.externalType(),
|
||||
.index = symbol.index,
|
||||
@ -2134,7 +2157,6 @@ pub fn createDebugSectionForIndex(wasm: *Wasm, index: *?u32, name: []const u8) !
|
||||
const new_index = @intCast(u32, wasm.segments.items.len);
|
||||
index.* = new_index;
|
||||
try wasm.appendDummySegment();
|
||||
// _ = index;
|
||||
|
||||
const sym_index = wasm.symbols_free_list.popOrNull() orelse idx: {
|
||||
const tmp_index = @intCast(u32, wasm.symbols.items.len);
|
||||
@ -2202,13 +2224,213 @@ pub fn flush(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) lin
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (build_options.have_llvm and wasm.base.options.use_lld) {
|
||||
return wasm.linkWithLLD(comp, prog_node);
|
||||
} else if (build_options.have_llvm and wasm.base.options.use_llvm and !wasm.base.options.use_lld) {
|
||||
return wasm.linkWithZld(comp, prog_node);
|
||||
} else {
|
||||
return wasm.flushModule(comp, prog_node);
|
||||
}
|
||||
}
|
||||
|
||||
/// Uses the in-house linker to link one or multiple object -and archive files into a WebAssembly binary.
|
||||
fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
const gpa = wasm.base.allocator;
|
||||
const options = wasm.base.options;
|
||||
|
||||
// Used for all temporary memory allocated during flushin
|
||||
var arena_instance = std.heap.ArenaAllocator.init(gpa);
|
||||
defer arena_instance.deinit();
|
||||
const arena = arena_instance.allocator();
|
||||
|
||||
const directory = options.emit.?.directory; // Just an alias to make it shorter to type.
|
||||
const full_out_path = try directory.join(arena, &[_][]const u8{options.emit.?.sub_path});
|
||||
|
||||
// If there is no Zig code to compile, then we should skip flushing the output file because it
|
||||
// will not be part of the linker line anyway.
|
||||
const module_obj_path: ?[]const u8 = if (options.module != null) blk: {
|
||||
assert(options.use_llvm); // `linkWithZld` should never be called when the Wasm backend is used
|
||||
try wasm.flushModule(comp, prog_node);
|
||||
|
||||
if (fs.path.dirname(full_out_path)) |dirname| {
|
||||
break :blk try fs.path.join(arena, &.{ dirname, wasm.base.intermediary_basename.? });
|
||||
} else {
|
||||
break :blk wasm.base.intermediary_basename.?;
|
||||
}
|
||||
} else null;
|
||||
|
||||
var sub_prog_node = prog_node.start("Wasm Flush", 0);
|
||||
sub_prog_node.activate();
|
||||
defer sub_prog_node.end();
|
||||
|
||||
const is_obj = options.output_mode == .Obj;
|
||||
const compiler_rt_path: ?[]const u8 = if (options.include_compiler_rt and !is_obj)
|
||||
comp.compiler_rt_lib.?.full_object_path
|
||||
else
|
||||
null;
|
||||
const id_symlink_basename = "zld.id";
|
||||
|
||||
var man: Cache.Manifest = undefined;
|
||||
defer if (!options.disable_lld_caching) man.deinit();
|
||||
var digest: [Cache.hex_digest_len]u8 = undefined;
|
||||
|
||||
// NOTE: The following section must be maintained to be equal
|
||||
// as the section defined in `linkWithLLD`
|
||||
if (!options.disable_lld_caching) {
|
||||
man = comp.cache_parent.obtain();
|
||||
|
||||
// We are about to obtain this lock, so here we give other processes a chance first.
|
||||
wasm.base.releaseLock();
|
||||
|
||||
comptime assert(Compilation.link_hash_implementation_version == 7);
|
||||
|
||||
for (options.objects) |obj| {
|
||||
_ = try man.addFile(obj.path, null);
|
||||
man.hash.add(obj.must_link);
|
||||
}
|
||||
for (comp.c_object_table.keys()) |key| {
|
||||
_ = try man.addFile(key.status.success.object_path, null);
|
||||
}
|
||||
try man.addOptionalFile(module_obj_path);
|
||||
try man.addOptionalFile(compiler_rt_path);
|
||||
man.hash.addOptionalBytes(options.entry);
|
||||
man.hash.addOptional(options.stack_size_override);
|
||||
man.hash.add(options.import_memory);
|
||||
man.hash.add(options.import_table);
|
||||
man.hash.add(options.export_table);
|
||||
man.hash.addOptional(options.initial_memory);
|
||||
man.hash.addOptional(options.max_memory);
|
||||
man.hash.add(options.shared_memory);
|
||||
man.hash.addOptional(options.global_base);
|
||||
man.hash.add(options.export_symbol_names.len);
|
||||
// strip does not need to go into the linker hash because it is part of the hash namespace
|
||||
for (options.export_symbol_names) |symbol_name| {
|
||||
man.hash.addBytes(symbol_name);
|
||||
}
|
||||
|
||||
// We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
|
||||
_ = try man.hit();
|
||||
digest = man.final();
|
||||
|
||||
var prev_digest_buf: [digest.len]u8 = undefined;
|
||||
const prev_digest: []u8 = Cache.readSmallFile(
|
||||
directory.handle,
|
||||
id_symlink_basename,
|
||||
&prev_digest_buf,
|
||||
) catch |err| blk: {
|
||||
log.debug("WASM LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) });
|
||||
// Handle this as a cache miss.
|
||||
break :blk prev_digest_buf[0..0];
|
||||
};
|
||||
if (mem.eql(u8, prev_digest, &digest)) {
|
||||
log.debug("WASM LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)});
|
||||
// Hot diggity dog! The output binary is already there.
|
||||
wasm.base.lock = man.toOwnedLock();
|
||||
return;
|
||||
}
|
||||
log.debug("WASM LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) });
|
||||
|
||||
// We are about to change the output file to be different, so we invalidate the build hash now.
|
||||
directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
|
||||
error.FileNotFound => {},
|
||||
else => |e| return e,
|
||||
};
|
||||
}
|
||||
|
||||
// Positional arguments to the linker such as object files and static archives.
|
||||
var positionals = std.ArrayList([]const u8).init(arena);
|
||||
try positionals.ensureUnusedCapacity(options.objects.len);
|
||||
|
||||
// When the target os is WASI, we allow linking with WASI-LIBC
|
||||
if (options.target.os.tag == .wasi) {
|
||||
const is_exe_or_dyn_lib = wasm.base.options.output_mode == .Exe or
|
||||
(wasm.base.options.output_mode == .Lib and wasm.base.options.link_mode == .Dynamic);
|
||||
if (is_exe_or_dyn_lib) {
|
||||
const wasi_emulated_libs = wasm.base.options.wasi_emulated_libs;
|
||||
for (wasi_emulated_libs) |crt_file| {
|
||||
try positionals.append(try comp.get_libc_crt_file(
|
||||
arena,
|
||||
wasi_libc.emulatedLibCRFileLibName(crt_file),
|
||||
));
|
||||
}
|
||||
|
||||
if (wasm.base.options.link_libc) {
|
||||
try positionals.append(try comp.get_libc_crt_file(
|
||||
arena,
|
||||
wasi_libc.execModelCrtFileFullName(wasm.base.options.wasi_exec_model),
|
||||
));
|
||||
try positionals.append(try comp.get_libc_crt_file(arena, "libc.a"));
|
||||
}
|
||||
|
||||
if (wasm.base.options.link_libcpp) {
|
||||
try positionals.append(comp.libcxx_static_lib.?.full_object_path);
|
||||
try positionals.append(comp.libcxxabi_static_lib.?.full_object_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (module_obj_path) |path| {
|
||||
try positionals.append(path);
|
||||
}
|
||||
|
||||
for (options.objects) |object| {
|
||||
try positionals.append(object.path);
|
||||
}
|
||||
|
||||
for (comp.c_object_table.keys()) |c_object| {
|
||||
try positionals.append(c_object.status.success.object_path);
|
||||
}
|
||||
|
||||
if (comp.compiler_rt_lib) |lib| {
|
||||
try positionals.append(lib.full_object_path);
|
||||
}
|
||||
|
||||
try wasm.parseInputFiles(positionals.items);
|
||||
|
||||
for (wasm.objects.items) |_, object_index| {
|
||||
try wasm.resolveSymbolsInObject(@intCast(u16, object_index));
|
||||
}
|
||||
|
||||
var emit_features_count: u32 = 0;
|
||||
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.setupStart();
|
||||
try wasm.setupImports();
|
||||
|
||||
for (wasm.objects.items) |*object, object_index| {
|
||||
try object.parseIntoAtoms(gpa, @intCast(u16, object_index), wasm);
|
||||
}
|
||||
|
||||
try wasm.allocateAtoms();
|
||||
try wasm.setupMemory();
|
||||
wasm.mapFunctionTable();
|
||||
try wasm.mergeSections();
|
||||
try wasm.mergeTypes();
|
||||
try wasm.setupExports();
|
||||
try wasm.writeToFile(enabled_features, emit_features_count, arena);
|
||||
|
||||
if (!wasm.base.options.disable_lld_caching) {
|
||||
// Update the file with the digest. If it fails we can continue; it only
|
||||
// means that the next invocation will have an unnecessary cache miss.
|
||||
Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| {
|
||||
log.warn("failed to save linking hash digest symlink: {s}", .{@errorName(err)});
|
||||
};
|
||||
// Again failure here only means an unnecessary cache miss.
|
||||
man.writeManifest() catch |err| {
|
||||
log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)});
|
||||
};
|
||||
// We hang on to this lock so that the output file path can be used without
|
||||
// other processes clobbering it.
|
||||
wasm.base.lock = man.toOwnedLock();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
@ -2219,20 +2441,13 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
|
||||
}
|
||||
}
|
||||
|
||||
var sub_prog_node = prog_node.start("WASM Flush", 0);
|
||||
var sub_prog_node = prog_node.start("Wasm Flush", 0);
|
||||
sub_prog_node.activate();
|
||||
defer sub_prog_node.end();
|
||||
|
||||
// ensure the error names table is populated when an error name is referenced
|
||||
try wasm.populateErrorNameTable();
|
||||
|
||||
// The amount of sections that will be written
|
||||
var section_count: u32 = 0;
|
||||
// Index of the code section. Used to tell relocation table where the section lives.
|
||||
var code_section_index: ?u32 = null;
|
||||
// Index of the data section. Used to tell relocation table where the section lives.
|
||||
var data_section_index: ?u32 = null;
|
||||
|
||||
// Used for all temporary memory allocated during flushin
|
||||
var arena_instance = std.heap.ArenaAllocator.init(wasm.base.allocator);
|
||||
defer arena_instance.deinit();
|
||||
@ -2312,9 +2527,25 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
|
||||
try wasm.mergeSections();
|
||||
try wasm.mergeTypes();
|
||||
try wasm.setupExports();
|
||||
try wasm.writeToFile(enabled_features, emit_features_count, arena);
|
||||
}
|
||||
|
||||
/// Writes the WebAssembly in-memory module to the file
|
||||
fn writeToFile(
|
||||
wasm: *Wasm,
|
||||
enabled_features: [@typeInfo(types.Feature.Tag).Enum.fields.len]bool,
|
||||
feature_count: u32,
|
||||
arena: Allocator,
|
||||
) !void {
|
||||
// Size of each section header
|
||||
const header_size = 5 + 1;
|
||||
const is_obj = wasm.base.options.output_mode == .Obj;
|
||||
// The amount of sections that will be written
|
||||
var section_count: u32 = 0;
|
||||
// Index of the code section. Used to tell relocation table where the section lives.
|
||||
var code_section_index: ?u32 = null;
|
||||
// Index of the data section. Used to tell relocation table where the section lives.
|
||||
var data_section_index: ?u32 = null;
|
||||
const is_obj = wasm.base.options.output_mode == .Obj or (!wasm.base.options.use_llvm and wasm.base.options.use_lld);
|
||||
|
||||
var binary_bytes = std.ArrayList(u8).init(wasm.base.allocator);
|
||||
defer binary_bytes.deinit();
|
||||
@ -2461,10 +2692,22 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
|
||||
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(
|
||||
@ -2472,7 +2715,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
|
||||
header_offset,
|
||||
.global,
|
||||
@intCast(u32, binary_bytes.items.len - header_offset - header_size),
|
||||
@intCast(u32, wasm.wasm_globals.items.len),
|
||||
@intCast(u32, global_count),
|
||||
);
|
||||
section_count += 1;
|
||||
}
|
||||
@ -2708,8 +2951,8 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
|
||||
}
|
||||
|
||||
try emitProducerSection(&binary_bytes);
|
||||
if (emit_features_count > 0) {
|
||||
try emitFeaturesSection(&binary_bytes, &enabled_features, emit_features_count);
|
||||
if (feature_count > 0) {
|
||||
try emitFeaturesSection(&binary_bytes, &enabled_features, feature_count);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2849,7 +3092,9 @@ 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: {
|
||||
break :blk wasm.string_table.get(wasm.imports.get(sym_loc).?.name);
|
||||
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);
|
||||
switch (symbol.tag) {
|
||||
.function => {
|
||||
@ -3607,15 +3852,23 @@ fn emitDataRelocations(
|
||||
try writeCustomSectionHeader(binary_bytes.items, header_offset, size);
|
||||
}
|
||||
|
||||
/// Searches for an a matching function signature, when not found
|
||||
/// a new entry will be made. The index of the existing/new signature will be returned.
|
||||
pub fn putOrGetFuncType(wasm: *Wasm, func_type: std.wasm.Type) !u32 {
|
||||
pub fn getTypeIndex(wasm: *const Wasm, func_type: std.wasm.Type) ?u32 {
|
||||
var index: u32 = 0;
|
||||
while (index < wasm.func_types.items.len) : (index += 1) {
|
||||
if (wasm.func_types.items[index].eql(func_type)) return index;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Searches for an a matching function signature, when not found
|
||||
/// a new entry will be made. The index of the existing/new signature will be returned.
|
||||
pub fn putOrGetFuncType(wasm: *Wasm, func_type: std.wasm.Type) !u32 {
|
||||
if (wasm.getTypeIndex(func_type)) |index| {
|
||||
return index;
|
||||
}
|
||||
|
||||
// functype does not exist.
|
||||
const index = @intCast(u32, wasm.func_types.items.len);
|
||||
const params = try wasm.base.allocator.dupe(std.wasm.Valtype, func_type.params);
|
||||
errdefer wasm.base.allocator.free(params);
|
||||
const returns = try wasm.base.allocator.dupe(std.wasm.Valtype, func_type.returns);
|
||||
|
||||
@ -157,13 +157,12 @@ fn parseTableOfContents(archive: *Archive, allocator: Allocator, reader: anytype
|
||||
};
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < sym_tab.len) {
|
||||
const string = mem.sliceTo(sym_tab[i..], 0);
|
||||
if (string.len == 0) {
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
i += string.len;
|
||||
var pos: usize = 0;
|
||||
while (i < num_symbols) : (i += 1) {
|
||||
const string = mem.sliceTo(sym_tab[pos..], 0);
|
||||
pos += string.len + 1;
|
||||
if (string.len == 0) continue;
|
||||
|
||||
const name = try allocator.dupe(u8, string);
|
||||
errdefer allocator.free(name);
|
||||
const gop = try archive.toc.getOrPut(allocator, name);
|
||||
@ -172,7 +171,7 @@ fn parseTableOfContents(archive: *Archive, allocator: Allocator, reader: anytype
|
||||
} else {
|
||||
gop.value_ptr.* = .{};
|
||||
}
|
||||
try gop.value_ptr.append(allocator, symbol_positions[gop.index]);
|
||||
try gop.value_ptr.append(allocator, symbol_positions[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -168,12 +168,13 @@ fn relocationValue(atom: Atom, relocation: types.Relocation, wasm_bin: *const Wa
|
||||
.R_WASM_TABLE_INDEX_SLEB,
|
||||
.R_WASM_TABLE_INDEX_SLEB64,
|
||||
=> return wasm_bin.function_table.get(target_loc) orelse 0,
|
||||
.R_WASM_TYPE_INDEX_LEB => return blk: {
|
||||
if (symbol.isUndefined()) {
|
||||
const imp = wasm_bin.imports.get(target_loc).?;
|
||||
break :blk imp.kind.function;
|
||||
}
|
||||
break :blk wasm_bin.functions.values()[symbol.index - wasm_bin.imported_functions_count].type_index;
|
||||
.R_WASM_TYPE_INDEX_LEB => {
|
||||
const file_index = atom.file orelse {
|
||||
return relocation.index;
|
||||
};
|
||||
|
||||
const original_type = wasm_bin.objects.items[file_index].func_types[relocation.index];
|
||||
return wasm_bin.getTypeIndex(original_type).?;
|
||||
},
|
||||
.R_WASM_GLOBAL_INDEX_I32,
|
||||
.R_WASM_GLOBAL_INDEX_LEB,
|
||||
@ -185,7 +186,11 @@ fn relocationValue(atom: Atom, relocation: types.Relocation, wasm_bin: *const Wa
|
||||
.R_WASM_MEMORY_ADDR_SLEB,
|
||||
.R_WASM_MEMORY_ADDR_SLEB64,
|
||||
=> {
|
||||
std.debug.assert(symbol.tag == .data and !symbol.isUndefined());
|
||||
std.debug.assert(symbol.tag == .data);
|
||||
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: {
|
||||
@ -204,18 +209,10 @@ fn relocationValue(atom: Atom, relocation: types.Relocation, wasm_bin: *const Wa
|
||||
return @intCast(u32, rel_value);
|
||||
},
|
||||
.R_WASM_FUNCTION_OFFSET_I32 => {
|
||||
const target_atom = wasm_bin.symbol_atom.get(target_loc).?;
|
||||
var current_atom = target_atom.getFirst();
|
||||
var offset: u32 = 0;
|
||||
// TODO: Calculate this during atom allocation, rather than
|
||||
// this linear calculation. For now it's done here as atoms
|
||||
// are being sorted after atom allocation, as functions aren't
|
||||
// merged until later.
|
||||
while (true) {
|
||||
offset += 5; // each atom uses 5 bytes to store its body's size
|
||||
if (current_atom == target_atom) break;
|
||||
current_atom = current_atom.next.?;
|
||||
}
|
||||
const target_atom = wasm_bin.symbol_atom.get(target_loc) orelse {
|
||||
return @bitCast(u32, @as(i32, -1));
|
||||
};
|
||||
const offset: u32 = 11 + Wasm.getULEB128Size(target_atom.size); // Header (11 bytes fixed-size) + body size (leb-encoded)
|
||||
const rel_value = @intCast(i32, target_atom.offset + offset) + relocation.addend;
|
||||
return @intCast(u32, rel_value);
|
||||
},
|
||||
|
||||
@ -923,7 +923,7 @@ pub fn parseIntoAtoms(object: *Object, gpa: Allocator, object_index: u16, wasm_b
|
||||
try atom.relocs.append(gpa, reloc);
|
||||
|
||||
if (relocation.isTableIndex()) {
|
||||
try wasm_bin.function_table.putNoClobber(gpa, .{
|
||||
try wasm_bin.function_table.put(gpa, .{
|
||||
.file = object_index,
|
||||
.index = relocation.index,
|
||||
}, 0);
|
||||
@ -938,17 +938,17 @@ pub fn parseIntoAtoms(object: *Object, gpa: Allocator, object_index: u16, wasm_b
|
||||
.index = relocatable_data.getIndex(),
|
||||
})) |symbols| {
|
||||
atom.sym_index = symbols.pop();
|
||||
try wasm_bin.symbol_atom.putNoClobber(gpa, atom.symbolLoc(), atom);
|
||||
|
||||
// symbols referencing the same atom will be added as alias
|
||||
// or as 'parent' when they are global.
|
||||
while (symbols.popOrNull()) |idx| {
|
||||
try wasm_bin.symbol_atom.putNoClobber(gpa, .{ .file = atom.file, .index = idx }, atom);
|
||||
const alias_symbol = object.symtable[idx];
|
||||
const symbol = object.symtable[atom.sym_index];
|
||||
if (alias_symbol.isGlobal() and symbol.isLocal()) {
|
||||
if (alias_symbol.isGlobal()) {
|
||||
atom.sym_index = idx;
|
||||
}
|
||||
}
|
||||
try wasm_bin.symbol_atom.putNoClobber(gpa, atom.symbolLoc(), atom);
|
||||
}
|
||||
|
||||
const segment: *Wasm.Segment = &wasm_bin.segments.items[final_index];
|
||||
|
||||
@ -38,7 +38,7 @@ pub const Tag = enum {
|
||||
return switch (tag) {
|
||||
.function => .function,
|
||||
.global => .global,
|
||||
.data => .memory,
|
||||
.data => unreachable, // Data symbols will generate a global
|
||||
.section => unreachable, // Not an external type
|
||||
.event => unreachable, // Not an external type
|
||||
.dead => unreachable, // Dead symbols should not be referenced
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user