mirror of
https://github.com/ziglang/zig.git
synced 2025-12-31 10:33:19 +00:00
Merge pull request #19121 from Luukdegram/wasm-linker-zigobject
wasm-linker: encapsulate Zig module in ZigObject
This commit is contained in:
commit
791e28bb68
@ -1286,8 +1286,9 @@ fn genFunc(func: *CodeGen) InnerError!void {
|
||||
var prologue = std.ArrayList(Mir.Inst).init(func.gpa);
|
||||
defer prologue.deinit();
|
||||
|
||||
const sp = @intFromEnum(func.bin_file.zigObjectPtr().?.stack_pointer_sym);
|
||||
// load stack pointer
|
||||
try prologue.append(.{ .tag = .global_get, .data = .{ .label = 0 } });
|
||||
try prologue.append(.{ .tag = .global_get, .data = .{ .label = sp } });
|
||||
// store stack pointer so we can restore it when we return from the function
|
||||
try prologue.append(.{ .tag = .local_tee, .data = .{ .label = func.initial_stack_value.local.value } });
|
||||
// get the total stack size
|
||||
@ -1303,7 +1304,7 @@ fn genFunc(func: *CodeGen) InnerError!void {
|
||||
try prologue.append(.{ .tag = .local_tee, .data = .{ .label = func.bottom_stack_value.local.value } });
|
||||
// Store the current stack pointer value into the global stack pointer so other function calls will
|
||||
// start from this value instead and not overwrite the current stack.
|
||||
try prologue.append(.{ .tag = .global_set, .data = .{ .label = 0 } });
|
||||
try prologue.append(.{ .tag = .global_set, .data = .{ .label = sp } });
|
||||
|
||||
// reserve space and insert all prologue instructions at the front of the instruction list
|
||||
// We insert them in reserve order as there is no insertSlice in multiArrayList.
|
||||
@ -1502,7 +1503,7 @@ fn restoreStackPointer(func: *CodeGen) !void {
|
||||
try func.emitWValue(func.initial_stack_value);
|
||||
|
||||
// save its value in the global stack pointer
|
||||
try func.addLabel(.global_set, 0);
|
||||
try func.addLabel(.global_set, @intFromEnum(func.bin_file.zigObjectPtr().?.stack_pointer_sym));
|
||||
}
|
||||
|
||||
/// From a given type, will create space on the virtual stack to store the value of such type.
|
||||
@ -2205,7 +2206,7 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif
|
||||
const type_index = try func.bin_file.storeDeclType(extern_func.decl, func_type);
|
||||
try func.bin_file.addOrUpdateImport(
|
||||
mod.intern_pool.stringToSlice(ext_decl.name),
|
||||
atom.getSymbolIndex().?,
|
||||
atom.sym_index,
|
||||
mod.intern_pool.stringToSliceUnwrap(ext_decl.getOwnedExternFunc(mod).?.lib_name),
|
||||
type_index,
|
||||
);
|
||||
@ -2239,8 +2240,8 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif
|
||||
}
|
||||
|
||||
if (callee) |direct| {
|
||||
const atom_index = func.bin_file.decls.get(direct).?;
|
||||
try func.addLabel(.call, func.bin_file.getAtom(atom_index).sym_index);
|
||||
const atom_index = func.bin_file.zigObjectPtr().?.decls_map.get(direct).?.atom;
|
||||
try func.addLabel(.call, @intFromEnum(func.bin_file.getAtom(atom_index).sym_index));
|
||||
} else {
|
||||
// in this case we call a function pointer
|
||||
// so load its value onto the stack
|
||||
@ -2251,7 +2252,7 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif
|
||||
var fn_type = try genFunctype(func.gpa, fn_info.cc, fn_info.param_types.get(ip), Type.fromInterned(fn_info.return_type), mod);
|
||||
defer fn_type.deinit(func.gpa);
|
||||
|
||||
const fn_type_index = try func.bin_file.putOrGetFuncType(fn_type);
|
||||
const fn_type_index = try func.bin_file.zigObjectPtr().?.putOrGetFuncType(func.gpa, fn_type);
|
||||
try func.addLabel(.call_indirect, fn_type_index);
|
||||
}
|
||||
|
||||
@ -3157,8 +3158,8 @@ fn lowerAnonDeclRef(
|
||||
return error.CodegenFail;
|
||||
},
|
||||
}
|
||||
const target_atom_index = func.bin_file.anon_decls.get(decl_val).?;
|
||||
const target_sym_index = func.bin_file.getAtom(target_atom_index).getSymbolIndex().?;
|
||||
const target_atom_index = func.bin_file.zigObjectPtr().?.anon_decls.get(decl_val).?;
|
||||
const target_sym_index = @intFromEnum(func.bin_file.getAtom(target_atom_index).sym_index);
|
||||
if (is_fn_body) {
|
||||
return WValue{ .function_index = target_sym_index };
|
||||
} else if (offset == 0) {
|
||||
@ -3189,9 +3190,8 @@ fn lowerDeclRefValue(func: *CodeGen, tv: TypedValue, decl_index: InternPool.Decl
|
||||
const atom_index = try func.bin_file.getOrCreateAtomForDecl(decl_index);
|
||||
const atom = func.bin_file.getAtom(atom_index);
|
||||
|
||||
const target_sym_index = atom.sym_index;
|
||||
const target_sym_index = @intFromEnum(atom.sym_index);
|
||||
if (decl.ty.zigTypeTag(mod) == .Fn) {
|
||||
try func.bin_file.addTableFunction(target_sym_index);
|
||||
return WValue{ .function_index = target_sym_index };
|
||||
} else if (offset == 0) {
|
||||
return WValue{ .memory = target_sym_index };
|
||||
@ -3712,7 +3712,7 @@ fn airCmpLtErrorsLen(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
|
||||
const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
|
||||
const operand = try func.resolveInst(un_op);
|
||||
const sym_index = try func.bin_file.getGlobalSymbol("__zig_errors_len", null);
|
||||
const errors_len = WValue{ .memory = sym_index };
|
||||
const errors_len = WValue{ .memory = @intFromEnum(sym_index) };
|
||||
|
||||
try func.emitWValue(operand);
|
||||
const mod = func.bin_file.base.comp.module.?;
|
||||
@ -7154,7 +7154,7 @@ fn callIntrinsic(
|
||||
args: []const WValue,
|
||||
) InnerError!WValue {
|
||||
assert(param_types.len == args.len);
|
||||
const symbol_index = func.bin_file.base.getGlobalSymbol(name, null) catch |err| {
|
||||
const symbol_index = func.bin_file.getGlobalSymbol(name, null) catch |err| {
|
||||
return func.fail("Could not find or create global symbol '{s}'", .{@errorName(err)});
|
||||
};
|
||||
|
||||
@ -7162,7 +7162,7 @@ fn callIntrinsic(
|
||||
const mod = func.bin_file.base.comp.module.?;
|
||||
var func_type = try genFunctype(func.gpa, .C, param_types, return_type, mod);
|
||||
defer func_type.deinit(func.gpa);
|
||||
const func_type_index = try func.bin_file.putOrGetFuncType(func_type);
|
||||
const func_type_index = try func.bin_file.zigObjectPtr().?.putOrGetFuncType(func.gpa, func_type);
|
||||
try func.bin_file.addOrUpdateImport(name, symbol_index, null, func_type_index);
|
||||
|
||||
const want_sret_param = firstParamSRet(.C, return_type, mod);
|
||||
@ -7182,7 +7182,7 @@ fn callIntrinsic(
|
||||
}
|
||||
|
||||
// Actually call our intrinsic
|
||||
try func.addLabel(.call, symbol_index);
|
||||
try func.addLabel(.call, @intFromEnum(symbol_index));
|
||||
|
||||
if (!return_type.hasRuntimeBitsIgnoreComptime(mod)) {
|
||||
return WValue.none;
|
||||
@ -7225,7 +7225,7 @@ fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 {
|
||||
|
||||
// check if we already generated code for this.
|
||||
if (func.bin_file.findGlobalSymbol(func_name)) |loc| {
|
||||
return loc.index;
|
||||
return @intFromEnum(loc.index);
|
||||
}
|
||||
|
||||
const int_tag_ty = enum_ty.intTagType(mod);
|
||||
@ -7365,7 +7365,8 @@ fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 {
|
||||
|
||||
const slice_ty = Type.slice_const_u8_sentinel_0;
|
||||
const func_type = try genFunctype(arena, .Unspecified, &.{int_tag_ty.ip_index}, slice_ty, mod);
|
||||
return func.bin_file.createFunction(func_name, func_type, &body_list, &relocs);
|
||||
const sym_index = try func.bin_file.createFunction(func_name, func_type, &body_list, &relocs);
|
||||
return @intFromEnum(sym_index);
|
||||
}
|
||||
|
||||
fn airErrorSetHasValue(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
|
||||
|
||||
@ -310,7 +310,7 @@ fn emitGlobal(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void {
|
||||
const global_offset = emit.offset();
|
||||
try emit.code.appendSlice(&buf);
|
||||
|
||||
const atom_index = emit.bin_file.decls.get(emit.decl_index).?;
|
||||
const atom_index = emit.bin_file.zigObjectPtr().?.decls_map.get(emit.decl_index).?.atom;
|
||||
const atom = emit.bin_file.getAtomPtr(atom_index);
|
||||
try atom.relocs.append(gpa, .{
|
||||
.index = label,
|
||||
@ -370,7 +370,7 @@ fn emitCall(emit: *Emit, inst: Mir.Inst.Index) !void {
|
||||
try emit.code.appendSlice(&buf);
|
||||
|
||||
if (label != 0) {
|
||||
const atom_index = emit.bin_file.decls.get(emit.decl_index).?;
|
||||
const atom_index = emit.bin_file.zigObjectPtr().?.decls_map.get(emit.decl_index).?.atom;
|
||||
const atom = emit.bin_file.getAtomPtr(atom_index);
|
||||
try atom.relocs.append(gpa, .{
|
||||
.offset = call_offset,
|
||||
@ -385,7 +385,19 @@ fn emitCallIndirect(emit: *Emit, inst: Mir.Inst.Index) !void {
|
||||
try emit.code.append(std.wasm.opcode(.call_indirect));
|
||||
// NOTE: If we remove unused function types in the future for incremental
|
||||
// linking, we must also emit a relocation for this `type_index`
|
||||
try leb128.writeULEB128(emit.code.writer(), type_index);
|
||||
const call_offset = emit.offset();
|
||||
var buf: [5]u8 = undefined;
|
||||
leb128.writeUnsignedFixed(5, &buf, type_index);
|
||||
try emit.code.appendSlice(&buf);
|
||||
if (type_index != 0) {
|
||||
const atom_index = emit.bin_file.zigObjectPtr().?.decls_map.get(emit.decl_index).?.atom;
|
||||
const atom = emit.bin_file.getAtomPtr(atom_index);
|
||||
try atom.relocs.append(emit.bin_file.base.comp.gpa, .{
|
||||
.offset = call_offset,
|
||||
.index = type_index,
|
||||
.relocation_type = .R_WASM_TYPE_INDEX_LEB,
|
||||
});
|
||||
}
|
||||
try leb128.writeULEB128(emit.code.writer(), @as(u32, 0)); // TODO: Emit relocation for table index
|
||||
}
|
||||
|
||||
@ -400,7 +412,7 @@ fn emitFunctionIndex(emit: *Emit, inst: Mir.Inst.Index) !void {
|
||||
try emit.code.appendSlice(&buf);
|
||||
|
||||
if (symbol_index != 0) {
|
||||
const atom_index = emit.bin_file.decls.get(emit.decl_index).?;
|
||||
const atom_index = emit.bin_file.zigObjectPtr().?.decls_map.get(emit.decl_index).?.atom;
|
||||
const atom = emit.bin_file.getAtomPtr(atom_index);
|
||||
try atom.relocs.append(gpa, .{
|
||||
.offset = index_offset,
|
||||
@ -431,7 +443,7 @@ fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void {
|
||||
}
|
||||
|
||||
if (mem.pointer != 0) {
|
||||
const atom_index = emit.bin_file.decls.get(emit.decl_index).?;
|
||||
const atom_index = emit.bin_file.zigObjectPtr().?.decls_map.get(emit.decl_index).?.atom;
|
||||
const atom = emit.bin_file.getAtomPtr(atom_index);
|
||||
try atom.relocs.append(gpa, .{
|
||||
.offset = mem_offset,
|
||||
|
||||
@ -1297,9 +1297,9 @@ pub fn commitDeclState(
|
||||
}
|
||||
},
|
||||
.wasm => {
|
||||
const wasm_file = self.bin_file.cast(File.Wasm).?;
|
||||
const debug_line = wasm_file.getAtomPtr(wasm_file.debug_line_atom.?).code;
|
||||
writeDbgLineNopsBuffered(debug_line.items, src_fn.off, 0, &.{}, src_fn.len);
|
||||
// const wasm_file = self.bin_file.cast(File.Wasm).?;
|
||||
// const debug_line = wasm_file.getAtomPtr(wasm_file.debug_line_atom.?).code;
|
||||
// writeDbgLineNopsBuffered(debug_line.items, src_fn.off, 0, &.{}, src_fn.len);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
@ -1390,26 +1390,26 @@ pub fn commitDeclState(
|
||||
},
|
||||
|
||||
.wasm => {
|
||||
const wasm_file = self.bin_file.cast(File.Wasm).?;
|
||||
const atom = wasm_file.getAtomPtr(wasm_file.debug_line_atom.?);
|
||||
const debug_line = &atom.code;
|
||||
const segment_size = debug_line.items.len;
|
||||
if (needed_size != segment_size) {
|
||||
log.debug(" needed size does not equal allocated size: {d}", .{needed_size});
|
||||
if (needed_size > segment_size) {
|
||||
log.debug(" allocating {d} bytes for 'debug line' information", .{needed_size - segment_size});
|
||||
try debug_line.resize(self.allocator, needed_size);
|
||||
@memset(debug_line.items[segment_size..], 0);
|
||||
}
|
||||
debug_line.items.len = needed_size;
|
||||
}
|
||||
writeDbgLineNopsBuffered(
|
||||
debug_line.items,
|
||||
src_fn.off,
|
||||
prev_padding_size,
|
||||
dbg_line_buffer.items,
|
||||
next_padding_size,
|
||||
);
|
||||
// const wasm_file = self.bin_file.cast(File.Wasm).?;
|
||||
// const atom = wasm_file.getAtomPtr(wasm_file.debug_line_atom.?);
|
||||
// const debug_line = &atom.code;
|
||||
// const segment_size = debug_line.items.len;
|
||||
// if (needed_size != segment_size) {
|
||||
// log.debug(" needed size does not equal allocated size: {d}", .{needed_size});
|
||||
// if (needed_size > segment_size) {
|
||||
// log.debug(" allocating {d} bytes for 'debug line' information", .{needed_size - segment_size});
|
||||
// try debug_line.resize(self.allocator, needed_size);
|
||||
// @memset(debug_line.items[segment_size..], 0);
|
||||
// }
|
||||
// debug_line.items.len = needed_size;
|
||||
// }
|
||||
// writeDbgLineNopsBuffered(
|
||||
// debug_line.items,
|
||||
// src_fn.off,
|
||||
// prev_padding_size,
|
||||
// dbg_line_buffer.items,
|
||||
// next_padding_size,
|
||||
// );
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
@ -1553,10 +1553,10 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, atom_index: Atom.Index, len: u32)
|
||||
}
|
||||
},
|
||||
.wasm => {
|
||||
const wasm_file = self.bin_file.cast(File.Wasm).?;
|
||||
const debug_info_index = wasm_file.debug_info_atom.?;
|
||||
const debug_info = &wasm_file.getAtomPtr(debug_info_index).code;
|
||||
try writeDbgInfoNopsToArrayList(gpa, debug_info, atom.off, 0, &.{0}, atom.len, false);
|
||||
// const wasm_file = self.bin_file.cast(File.Wasm).?;
|
||||
// const debug_info_index = wasm_file.debug_info_atom.?;
|
||||
// const debug_info = &wasm_file.getAtomPtr(debug_info_index).code;
|
||||
// try writeDbgInfoNopsToArrayList(gpa, debug_info, atom.off, 0, &.{0}, atom.len, false);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
@ -1594,7 +1594,6 @@ fn writeDeclDebugInfo(self: *Dwarf, atom_index: Atom.Index, dbg_info_buf: []cons
|
||||
// This logic is nearly identical to the logic above in `updateDecl` for
|
||||
// `SrcFn` and the line number programs. If you are editing this logic, you
|
||||
// probably need to edit that logic too.
|
||||
const gpa = self.allocator;
|
||||
|
||||
const atom = self.getAtom(.di_atom, atom_index);
|
||||
const last_decl_index = self.di_atom_last_index.?;
|
||||
@ -1665,31 +1664,31 @@ fn writeDeclDebugInfo(self: *Dwarf, atom_index: Atom.Index, dbg_info_buf: []cons
|
||||
},
|
||||
|
||||
.wasm => {
|
||||
const wasm_file = self.bin_file.cast(File.Wasm).?;
|
||||
const info_atom = wasm_file.debug_info_atom.?;
|
||||
const debug_info = &wasm_file.getAtomPtr(info_atom).code;
|
||||
const segment_size = debug_info.items.len;
|
||||
if (needed_size != segment_size) {
|
||||
log.debug(" needed size does not equal allocated size: {d}", .{needed_size});
|
||||
if (needed_size > segment_size) {
|
||||
log.debug(" allocating {d} bytes for 'debug info' information", .{needed_size - segment_size});
|
||||
try debug_info.resize(self.allocator, needed_size);
|
||||
@memset(debug_info.items[segment_size..], 0);
|
||||
}
|
||||
debug_info.items.len = needed_size;
|
||||
}
|
||||
log.debug(" writeDbgInfoNopsToArrayList debug_info_len={d} offset={d} content_len={d} next_padding_size={d}", .{
|
||||
debug_info.items.len, atom.off, dbg_info_buf.len, next_padding_size,
|
||||
});
|
||||
try writeDbgInfoNopsToArrayList(
|
||||
gpa,
|
||||
debug_info,
|
||||
atom.off,
|
||||
prev_padding_size,
|
||||
dbg_info_buf,
|
||||
next_padding_size,
|
||||
trailing_zero,
|
||||
);
|
||||
// const wasm_file = self.bin_file.cast(File.Wasm).?;
|
||||
// const info_atom = wasm_file.debug_info_atom.?;
|
||||
// const debug_info = &wasm_file.getAtomPtr(info_atom).code;
|
||||
// const segment_size = debug_info.items.len;
|
||||
// if (needed_size != segment_size) {
|
||||
// log.debug(" needed size does not equal allocated size: {d}", .{needed_size});
|
||||
// if (needed_size > segment_size) {
|
||||
// log.debug(" allocating {d} bytes for 'debug info' information", .{needed_size - segment_size});
|
||||
// try debug_info.resize(self.allocator, needed_size);
|
||||
// @memset(debug_info.items[segment_size..], 0);
|
||||
// }
|
||||
// debug_info.items.len = needed_size;
|
||||
// }
|
||||
// log.debug(" writeDbgInfoNopsToArrayList debug_info_len={d} offset={d} content_len={d} next_padding_size={d}", .{
|
||||
// debug_info.items.len, atom.off, dbg_info_buf.len, next_padding_size,
|
||||
// });
|
||||
// try writeDbgInfoNopsToArrayList(
|
||||
// gpa,
|
||||
// debug_info,
|
||||
// atom.off,
|
||||
// prev_padding_size,
|
||||
// dbg_info_buf,
|
||||
// next_padding_size,
|
||||
// trailing_zero,
|
||||
// );
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
@ -1735,10 +1734,10 @@ pub fn updateDeclLineNumber(self: *Dwarf, mod: *Module, decl_index: InternPool.D
|
||||
}
|
||||
},
|
||||
.wasm => {
|
||||
const wasm_file = self.bin_file.cast(File.Wasm).?;
|
||||
const offset = atom.off + self.getRelocDbgLineOff();
|
||||
const line_atom_index = wasm_file.debug_line_atom.?;
|
||||
wasm_file.getAtomPtr(line_atom_index).code.items[offset..][0..data.len].* = data;
|
||||
// const wasm_file = self.bin_file.cast(File.Wasm).?;
|
||||
// const offset = atom.off + self.getRelocDbgLineOff();
|
||||
// const line_atom_index = wasm_file.debug_line_atom.?;
|
||||
// wasm_file.getAtomPtr(line_atom_index).code.items[offset..][0..data.len].* = data;
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
@ -1803,7 +1802,6 @@ pub fn freeDecl(self: *Dwarf, decl_index: InternPool.DeclIndex) void {
|
||||
}
|
||||
|
||||
pub fn writeDbgAbbrev(self: *Dwarf) !void {
|
||||
const gpa = self.allocator;
|
||||
// These are LEB encoded but since the values are all less than 127
|
||||
// we can simply append these bytes.
|
||||
// zig fmt: off
|
||||
@ -1960,10 +1958,10 @@ pub fn writeDbgAbbrev(self: *Dwarf) !void {
|
||||
}
|
||||
},
|
||||
.wasm => {
|
||||
const wasm_file = self.bin_file.cast(File.Wasm).?;
|
||||
const debug_abbrev = &wasm_file.getAtomPtr(wasm_file.debug_abbrev_atom.?).code;
|
||||
try debug_abbrev.resize(gpa, needed_size);
|
||||
debug_abbrev.items[0..abbrev_buf.len].* = abbrev_buf;
|
||||
// const wasm_file = self.bin_file.cast(File.Wasm).?;
|
||||
// const debug_abbrev = &wasm_file.getAtomPtr(wasm_file.debug_abbrev_atom.?).code;
|
||||
// try debug_abbrev.resize(gpa, needed_size);
|
||||
// debug_abbrev.items[0..abbrev_buf.len].* = abbrev_buf;
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
@ -2055,9 +2053,9 @@ pub fn writeDbgInfoHeader(self: *Dwarf, zcu: *Module, low_pc: u64, high_pc: u64)
|
||||
}
|
||||
},
|
||||
.wasm => {
|
||||
const wasm_file = self.bin_file.cast(File.Wasm).?;
|
||||
const debug_info = &wasm_file.getAtomPtr(wasm_file.debug_info_atom.?).code;
|
||||
try writeDbgInfoNopsToArrayList(self.allocator, debug_info, 0, 0, di_buf.items, jmp_amt, false);
|
||||
// const wasm_file = self.bin_file.cast(File.Wasm).?;
|
||||
// const debug_info = &wasm_file.getAtomPtr(wasm_file.debug_info_atom.?).code;
|
||||
// try writeDbgInfoNopsToArrayList(self.allocator, debug_info, 0, 0, di_buf.items, jmp_amt, false);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
@ -2318,7 +2316,6 @@ fn writeDbgInfoNopsToArrayList(
|
||||
|
||||
pub fn writeDbgAranges(self: *Dwarf, addr: u64, size: u64) !void {
|
||||
const comp = self.bin_file.comp;
|
||||
const gpa = comp.gpa;
|
||||
const target = comp.root_mod.resolved_target.result;
|
||||
const target_endian = target.cpu.arch.endian();
|
||||
const ptr_width_bytes = self.ptrWidthBytes();
|
||||
@ -2391,10 +2388,10 @@ pub fn writeDbgAranges(self: *Dwarf, addr: u64, size: u64) !void {
|
||||
}
|
||||
},
|
||||
.wasm => {
|
||||
const wasm_file = self.bin_file.cast(File.Wasm).?;
|
||||
const debug_ranges = &wasm_file.getAtomPtr(wasm_file.debug_ranges_atom.?).code;
|
||||
try debug_ranges.resize(gpa, needed_size);
|
||||
@memcpy(debug_ranges.items[0..di_buf.items.len], di_buf.items);
|
||||
// const wasm_file = self.bin_file.cast(File.Wasm).?;
|
||||
// const debug_ranges = &wasm_file.getAtomPtr(wasm_file.debug_ranges_atom.?).code;
|
||||
// try debug_ranges.resize(gpa, needed_size);
|
||||
// @memcpy(debug_ranges.items[0..di_buf.items.len], di_buf.items);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
@ -2548,14 +2545,15 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void {
|
||||
}
|
||||
},
|
||||
.wasm => {
|
||||
const wasm_file = self.bin_file.cast(File.Wasm).?;
|
||||
const debug_line = &wasm_file.getAtomPtr(wasm_file.debug_line_atom.?).code;
|
||||
{
|
||||
const src = debug_line.items[first_fn.off..];
|
||||
@memcpy(buffer[0..src.len], src);
|
||||
}
|
||||
try debug_line.resize(self.allocator, debug_line.items.len + delta);
|
||||
@memcpy(debug_line.items[first_fn.off + delta ..][0..buffer.len], buffer);
|
||||
_ = &buffer;
|
||||
// const wasm_file = self.bin_file.cast(File.Wasm).?;
|
||||
// const debug_line = &wasm_file.getAtomPtr(wasm_file.debug_line_atom.?).code;
|
||||
// {
|
||||
// const src = debug_line.items[first_fn.off..];
|
||||
// @memcpy(buffer[0..src.len], src);
|
||||
// }
|
||||
// try debug_line.resize(self.allocator, debug_line.items.len + delta);
|
||||
// @memcpy(debug_line.items[first_fn.off + delta ..][0..buffer.len], buffer);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
@ -2604,9 +2602,9 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void {
|
||||
}
|
||||
},
|
||||
.wasm => {
|
||||
const wasm_file = self.bin_file.cast(File.Wasm).?;
|
||||
const debug_line = &wasm_file.getAtomPtr(wasm_file.debug_line_atom.?).code;
|
||||
writeDbgLineNopsBuffered(debug_line.items, 0, 0, di_buf.items, jmp_amt);
|
||||
// const wasm_file = self.bin_file.cast(File.Wasm).?;
|
||||
// const debug_line = &wasm_file.getAtomPtr(wasm_file.debug_line_atom.?).code;
|
||||
// writeDbgLineNopsBuffered(debug_line.items, 0, 0, di_buf.items, jmp_amt);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
@ -2754,9 +2752,9 @@ pub fn flushModule(self: *Dwarf, module: *Module) !void {
|
||||
}
|
||||
},
|
||||
.wasm => {
|
||||
const wasm_file = self.bin_file.cast(File.Wasm).?;
|
||||
const debug_info = wasm_file.getAtomPtr(wasm_file.debug_info_atom.?).code;
|
||||
debug_info.items[atom.off + reloc.offset ..][0..buf.len].* = buf;
|
||||
// const wasm_file = self.bin_file.cast(File.Wasm).?;
|
||||
// const debug_info = wasm_file.getAtomPtr(wasm_file.debug_info_atom.?).code;
|
||||
// debug_info.items[atom.off + reloc.offset ..][0..buf.len].* = buf;
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
|
||||
2216
src/link/Wasm.zig
2216
src/link/Wasm.zig
File diff suppressed because it is too large
Load Diff
@ -1,14 +1,3 @@
|
||||
const Archive = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const fs = std.fs;
|
||||
const log = std.log.scoped(.archive);
|
||||
const mem = std.mem;
|
||||
|
||||
const Allocator = mem.Allocator;
|
||||
const Object = @import("Object.zig");
|
||||
|
||||
file: fs.File,
|
||||
name: []const u8,
|
||||
|
||||
@ -151,10 +140,7 @@ fn parseTableOfContents(archive: *Archive, allocator: Allocator, reader: anytype
|
||||
const sym_tab = try allocator.alloc(u8, sym_tab_size - 4 - (4 * num_symbols));
|
||||
defer allocator.free(sym_tab);
|
||||
|
||||
reader.readNoEof(sym_tab) catch {
|
||||
log.err("incomplete symbol table: expected symbol table of length 0x{x}", .{sym_tab.len});
|
||||
return error.MalformedArchive;
|
||||
};
|
||||
reader.readNoEof(sym_tab) catch return error.IncompleteSymbolTable;
|
||||
|
||||
var i: usize = 0;
|
||||
var pos: usize = 0;
|
||||
@ -178,12 +164,10 @@ fn parseTableOfContents(archive: *Archive, allocator: Allocator, reader: anytype
|
||||
fn parseNameTable(archive: *Archive, allocator: Allocator, reader: anytype) !void {
|
||||
const header: ar_hdr = try reader.readStruct(ar_hdr);
|
||||
if (!mem.eql(u8, &header.ar_fmag, ARFMAG)) {
|
||||
log.err("invalid header delimiter: expected '{s}', found '{s}'", .{ ARFMAG, header.ar_fmag });
|
||||
return error.MalformedArchive;
|
||||
return error.InvalidHeaderDelimiter;
|
||||
}
|
||||
if (!mem.eql(u8, header.ar_name[0..2], "//")) {
|
||||
log.err("invalid archive. Long name table missing", .{});
|
||||
return error.MalformedArchive;
|
||||
return error.MissingTableName;
|
||||
}
|
||||
const table_size = try header.size();
|
||||
const long_file_names = try allocator.alloc(u8, table_size);
|
||||
@ -194,7 +178,8 @@ fn parseNameTable(archive: *Archive, allocator: Allocator, reader: anytype) !voi
|
||||
|
||||
/// From a given file offset, starts reading for a file header.
|
||||
/// When found, parses the object file into an `Object` and returns it.
|
||||
pub fn parseObject(archive: Archive, allocator: Allocator, file_offset: u32) !Object {
|
||||
pub fn parseObject(archive: Archive, wasm_file: *const Wasm, file_offset: u32) !Object {
|
||||
const gpa = wasm_file.base.comp.gpa;
|
||||
try archive.file.seekTo(file_offset);
|
||||
const reader = archive.file.reader();
|
||||
const header = try reader.readStruct(ar_hdr);
|
||||
@ -202,22 +187,33 @@ pub fn parseObject(archive: Archive, allocator: Allocator, file_offset: u32) !Ob
|
||||
try archive.file.seekTo(0);
|
||||
|
||||
if (!mem.eql(u8, &header.ar_fmag, ARFMAG)) {
|
||||
log.err("invalid header delimiter: expected '{s}', found '{s}'", .{ ARFMAG, header.ar_fmag });
|
||||
return error.MalformedArchive;
|
||||
return error.InvalidHeaderDelimiter;
|
||||
}
|
||||
|
||||
const object_name = try archive.parseName(header);
|
||||
const name = name: {
|
||||
var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
|
||||
const path = try std.os.realpath(archive.name, &buffer);
|
||||
break :name try std.fmt.allocPrint(allocator, "{s}({s})", .{ path, object_name });
|
||||
break :name try std.fmt.allocPrint(gpa, "{s}({s})", .{ path, object_name });
|
||||
};
|
||||
defer allocator.free(name);
|
||||
defer gpa.free(name);
|
||||
|
||||
const object_file = try std.fs.cwd().openFile(archive.name, .{});
|
||||
errdefer object_file.close();
|
||||
|
||||
const object_file_size = try header.size();
|
||||
try object_file.seekTo(current_offset);
|
||||
return Object.create(allocator, object_file, name, object_file_size);
|
||||
return Object.create(wasm_file, object_file, name, object_file_size);
|
||||
}
|
||||
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const fs = std.fs;
|
||||
const log = std.log.scoped(.archive);
|
||||
const mem = std.mem;
|
||||
|
||||
const Allocator = mem.Allocator;
|
||||
const Object = @import("Object.zig");
|
||||
const Wasm = @import("../Wasm.zig");
|
||||
|
||||
const Archive = @This();
|
||||
|
||||
@ -1,53 +1,34 @@
|
||||
const Atom = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const types = @import("types.zig");
|
||||
const Wasm = @import("../Wasm.zig");
|
||||
const Symbol = @import("Symbol.zig");
|
||||
|
||||
const leb = std.leb;
|
||||
const log = std.log.scoped(.link);
|
||||
const mem = std.mem;
|
||||
const Allocator = mem.Allocator;
|
||||
|
||||
/// Represents the index of the file this atom was generated from.
|
||||
/// This is 'null' when the atom was generated by a synthetic linker symbol.
|
||||
file: FileIndex,
|
||||
/// symbol index of the symbol representing this atom
|
||||
sym_index: u32,
|
||||
sym_index: Symbol.Index,
|
||||
/// Size of the atom, used to calculate section sizes in the final binary
|
||||
size: u32,
|
||||
size: u32 = 0,
|
||||
/// List of relocations belonging to this atom
|
||||
relocs: std.ArrayListUnmanaged(types.Relocation) = .{},
|
||||
/// Contains the binary data of an atom, which can be non-relocated
|
||||
code: std.ArrayListUnmanaged(u8) = .{},
|
||||
/// For code this is 1, for data this is set to the highest value of all segments
|
||||
alignment: Wasm.Alignment,
|
||||
alignment: Wasm.Alignment = .@"1",
|
||||
/// Offset into the section where the atom lives, this already accounts
|
||||
/// for alignment.
|
||||
offset: u32,
|
||||
offset: u32 = 0,
|
||||
/// The original offset within the object file. This value is substracted from
|
||||
/// relocation offsets to determine where in the `data` to rewrite the value
|
||||
original_offset: u32,
|
||||
/// Represents the index of the file this atom was generated from.
|
||||
/// This is 'null' when the atom was generated by a Decl from Zig code.
|
||||
file: ?u16,
|
||||
original_offset: u32 = 0,
|
||||
/// Previous atom in relation to this atom.
|
||||
/// is null when this atom is the first in its order
|
||||
prev: ?Atom.Index,
|
||||
prev: Atom.Index = .null,
|
||||
/// Contains atoms local to a decl, all managed by this `Atom`.
|
||||
/// When the parent atom is being freed, it will also do so for all local atoms.
|
||||
locals: std.ArrayListUnmanaged(Atom.Index) = .{},
|
||||
|
||||
/// Alias to an unsigned 32-bit integer
|
||||
pub const Index = u32;
|
||||
|
||||
/// Represents a default empty wasm `Atom`
|
||||
pub const empty: Atom = .{
|
||||
.alignment = .@"1",
|
||||
.file = null,
|
||||
.offset = 0,
|
||||
.prev = null,
|
||||
.size = 0,
|
||||
.sym_index = 0,
|
||||
.original_offset = 0,
|
||||
/// Represents the index of an Atom where `null` is considered
|
||||
/// an invalid atom.
|
||||
pub const Index = enum(u32) {
|
||||
null = std.math.maxInt(u32),
|
||||
_,
|
||||
};
|
||||
|
||||
/// Frees all resources owned by this `Atom`.
|
||||
@ -69,7 +50,7 @@ pub fn format(atom: Atom, comptime fmt: []const u8, options: std.fmt.FormatOptio
|
||||
_ = fmt;
|
||||
_ = options;
|
||||
try writer.print("Atom{{ .sym_index = {d}, .alignment = {d}, .size = {d}, .offset = 0x{x:0>8} }}", .{
|
||||
atom.sym_index,
|
||||
@intFromEnum(atom.sym_index),
|
||||
atom.alignment,
|
||||
atom.size,
|
||||
atom.offset,
|
||||
@ -81,11 +62,6 @@ pub fn symbolLoc(atom: Atom) Wasm.SymbolLoc {
|
||||
return .{ .file = atom.file, .index = atom.sym_index };
|
||||
}
|
||||
|
||||
pub fn getSymbolIndex(atom: Atom) ?u32 {
|
||||
if (atom.sym_index == 0) return null;
|
||||
return atom.sym_index;
|
||||
}
|
||||
|
||||
/// Resolves the relocations within the atom, writing the new value
|
||||
/// at the calculated offset.
|
||||
pub fn resolveRelocs(atom: *Atom, wasm_bin: *const Wasm) void {
|
||||
@ -99,7 +75,7 @@ pub fn resolveRelocs(atom: *Atom, wasm_bin: *const Wasm) void {
|
||||
for (atom.relocs.items) |reloc| {
|
||||
const value = atom.relocationValue(reloc, wasm_bin);
|
||||
log.debug("Relocating '{s}' referenced in '{s}' offset=0x{x:0>8} value={d}", .{
|
||||
(Wasm.SymbolLoc{ .file = atom.file, .index = reloc.index }).getName(wasm_bin),
|
||||
(Wasm.SymbolLoc{ .file = atom.file, .index = @enumFromInt(reloc.index) }).getName(wasm_bin),
|
||||
symbol_name,
|
||||
reloc.offset,
|
||||
value,
|
||||
@ -138,7 +114,7 @@ pub fn resolveRelocs(atom: *Atom, wasm_bin: *const Wasm) void {
|
||||
/// All values will be represented as a `u64` as all values can fit within it.
|
||||
/// 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 target_loc = (Wasm.SymbolLoc{ .file = atom.file, .index = @enumFromInt(relocation.index) }).finalLoc(wasm_bin);
|
||||
const symbol = target_loc.getSymbol(wasm_bin);
|
||||
if (relocation.relocation_type != .R_WASM_TYPE_INDEX_LEB and
|
||||
symbol.tag != .section and
|
||||
@ -154,13 +130,10 @@ fn relocationValue(atom: Atom, relocation: types.Relocation, wasm_bin: *const Wa
|
||||
.R_WASM_TABLE_INDEX_I64,
|
||||
.R_WASM_TABLE_INDEX_SLEB,
|
||||
.R_WASM_TABLE_INDEX_SLEB64,
|
||||
=> return wasm_bin.function_table.get(.{ .file = atom.file, .index = relocation.index }) orelse 0,
|
||||
=> return wasm_bin.function_table.get(.{ .file = atom.file, .index = @enumFromInt(relocation.index) }) orelse 0,
|
||||
.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];
|
||||
const obj_file = wasm_bin.file(atom.file) orelse return relocation.index;
|
||||
const original_type = obj_file.funcTypes()[relocation.index];
|
||||
return wasm_bin.getTypeIndex(original_type).?;
|
||||
},
|
||||
.R_WASM_GLOBAL_INDEX_I32,
|
||||
@ -217,3 +190,15 @@ fn thombstone(atom: Atom, wasm: *const Wasm) ?i64 {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const leb = std.leb;
|
||||
const log = std.log.scoped(.link);
|
||||
const mem = std.mem;
|
||||
const std = @import("std");
|
||||
const types = @import("types.zig");
|
||||
|
||||
const Allocator = mem.Allocator;
|
||||
const Atom = @This();
|
||||
const FileIndex = @import("file.zig").File.Index;
|
||||
const Symbol = @import("Symbol.zig");
|
||||
const Wasm = @import("../Wasm.zig");
|
||||
|
||||
@ -9,19 +9,22 @@ const std = @import("std");
|
||||
const Wasm = @import("../Wasm.zig");
|
||||
const Symbol = @import("Symbol.zig");
|
||||
const Alignment = types.Alignment;
|
||||
const File = @import("file.zig").File;
|
||||
|
||||
const Allocator = std.mem.Allocator;
|
||||
const leb = std.leb;
|
||||
const meta = std.meta;
|
||||
|
||||
const log = std.log.scoped(.link);
|
||||
const log = std.log.scoped(.object);
|
||||
|
||||
/// Index into the list of relocatable object files within the linker driver.
|
||||
index: File.Index = .null,
|
||||
/// Wasm spec version used for this `Object`
|
||||
version: u32 = 0,
|
||||
/// The file descriptor that represents the wasm object file.
|
||||
file: ?std.fs.File = null,
|
||||
/// Name (read path) of the object file.
|
||||
name: []const u8,
|
||||
path: []const u8,
|
||||
/// Parsed type section
|
||||
func_types: []const std.wasm.Type = &.{},
|
||||
/// A list of all imports for this module
|
||||
@ -64,6 +67,12 @@ relocatable_data: std.AutoHashMapUnmanaged(RelocatableData.Tag, []RelocatableDat
|
||||
/// import name, module name and export names. Each string will be deduplicated
|
||||
/// and returns an offset into the table.
|
||||
string_table: Wasm.StringTable = .{},
|
||||
/// Amount of functions in the `import` sections.
|
||||
imported_functions_count: u32 = 0,
|
||||
/// Amount of globals in the `import` section.
|
||||
imported_globals_count: u32 = 0,
|
||||
/// Amount of tables in the `import` section.
|
||||
imported_tables_count: u32 = 0,
|
||||
|
||||
/// Represents a single item within a section (depending on its `type`)
|
||||
const RelocatableData = struct {
|
||||
@ -118,15 +127,16 @@ pub const InitError = error{NotObjectFile} || ParseError || std.fs.File.ReadErro
|
||||
/// This also parses and verifies the object file.
|
||||
/// When a max size is given, will only parse up to the given size,
|
||||
/// else will read until the end of the file.
|
||||
pub fn create(gpa: Allocator, file: std.fs.File, name: []const u8, maybe_max_size: ?usize) InitError!Object {
|
||||
pub fn create(wasm_file: *const Wasm, file: std.fs.File, name: []const u8, maybe_max_size: ?usize) InitError!Object {
|
||||
const gpa = wasm_file.base.comp.gpa;
|
||||
var object: Object = .{
|
||||
.file = file,
|
||||
.name = try gpa.dupe(u8, name),
|
||||
.path = try gpa.dupe(u8, name),
|
||||
};
|
||||
|
||||
var is_object_file: bool = false;
|
||||
const size = maybe_max_size orelse size: {
|
||||
errdefer gpa.free(object.name);
|
||||
errdefer gpa.free(object.path);
|
||||
const stat = try file.stat();
|
||||
break :size @as(usize, @intCast(stat.size));
|
||||
};
|
||||
@ -142,7 +152,7 @@ pub fn create(gpa: Allocator, file: std.fs.File, name: []const u8, maybe_max_siz
|
||||
}
|
||||
var fbs = std.io.fixedBufferStream(file_contents);
|
||||
|
||||
try object.parse(gpa, fbs.reader(), &is_object_file);
|
||||
try object.parse(gpa, wasm_file, fbs.reader(), &is_object_file);
|
||||
errdefer object.deinit(gpa);
|
||||
if (!is_object_file) return error.NotObjectFile;
|
||||
|
||||
@ -193,68 +203,59 @@ pub fn deinit(object: *Object, gpa: Allocator) void {
|
||||
}
|
||||
object.relocatable_data.deinit(gpa);
|
||||
object.string_table.deinit(gpa);
|
||||
gpa.free(object.name);
|
||||
gpa.free(object.path);
|
||||
object.* = undefined;
|
||||
}
|
||||
|
||||
/// Finds the import within the list of imports from a given kind and index of that kind.
|
||||
/// Asserts the import exists
|
||||
pub fn findImport(object: *const Object, import_kind: std.wasm.ExternalKind, index: u32) types.Import {
|
||||
pub fn findImport(object: *const Object, sym: Symbol) types.Import {
|
||||
var i: u32 = 0;
|
||||
return for (object.imports) |import| {
|
||||
if (std.meta.activeTag(import.kind) == import_kind) {
|
||||
if (i == index) return import;
|
||||
if (std.meta.activeTag(import.kind) == sym.tag.externalType()) {
|
||||
if (i == sym.index) return import;
|
||||
i += 1;
|
||||
}
|
||||
} else unreachable; // Only existing imports are allowed to be found
|
||||
}
|
||||
|
||||
/// Counts the entries of imported `kind` and returns the result
|
||||
pub fn importedCountByKind(object: *const Object, kind: std.wasm.ExternalKind) u32 {
|
||||
var i: u32 = 0;
|
||||
return for (object.imports) |imp| {
|
||||
if (@as(std.wasm.ExternalKind, imp.kind) == kind) i += 1;
|
||||
} else i;
|
||||
}
|
||||
|
||||
/// From a given `RelocatableDate`, find the corresponding debug section name
|
||||
pub fn getDebugName(object: *const Object, relocatable_data: RelocatableData) []const u8 {
|
||||
return object.string_table.get(relocatable_data.index);
|
||||
}
|
||||
|
||||
/// Checks if the object file is an MVP version.
|
||||
/// When that's the case, we check if there's an import table definiton with its name
|
||||
/// set to '__indirect_function_table". When that's also the case,
|
||||
/// we initialize a new table symbol that corresponds to that import and return that symbol.
|
||||
///
|
||||
/// When the object file is *NOT* MVP, we return `null`.
|
||||
fn checkLegacyIndirectFunctionTable(object: *Object) !?Symbol {
|
||||
fn checkLegacyIndirectFunctionTable(object: *Object, wasm_file: *const Wasm) !?Symbol {
|
||||
var table_count: usize = 0;
|
||||
for (object.symtable) |sym| {
|
||||
if (sym.tag == .table) table_count += 1;
|
||||
}
|
||||
|
||||
const import_table_count = object.importedCountByKind(.table);
|
||||
|
||||
// For each import table, we also have a symbol so this is not a legacy object file
|
||||
if (import_table_count == table_count) return null;
|
||||
if (object.imported_tables_count == table_count) return null;
|
||||
|
||||
if (table_count != 0) {
|
||||
log.err("Expected a table entry symbol for each of the {d} table(s), but instead got {d} symbols.", .{
|
||||
import_table_count,
|
||||
var err = try wasm_file.addErrorWithNotes(1);
|
||||
try err.addMsg(wasm_file, "Expected a table entry symbol for each of the {d} table(s), but instead got {d} symbols.", .{
|
||||
object.imported_tables_count,
|
||||
table_count,
|
||||
});
|
||||
try err.addNote(wasm_file, "defined in '{s}'", .{object.path});
|
||||
return error.MissingTableSymbols;
|
||||
}
|
||||
|
||||
// MVP object files cannot have any table definitions, only imports (for the indirect function table).
|
||||
if (object.tables.len > 0) {
|
||||
log.err("Unexpected table definition without representing table symbols.", .{});
|
||||
var err = try wasm_file.addErrorWithNotes(1);
|
||||
try err.addMsg(wasm_file, "Unexpected table definition without representing table symbols.", .{});
|
||||
try err.addNote(wasm_file, "defined in '{s}'", .{object.path});
|
||||
return error.UnexpectedTable;
|
||||
}
|
||||
|
||||
if (import_table_count != 1) {
|
||||
log.err("Found more than one table import, but no representing table symbols", .{});
|
||||
if (object.imported_tables_count != 1) {
|
||||
var err = try wasm_file.addErrorWithNotes(1);
|
||||
try err.addMsg(wasm_file, "Found more than one table import, but no representing table symbols", .{});
|
||||
try err.addNote(wasm_file, "defined in '{s}'", .{object.path});
|
||||
return error.MissingTableSymbols;
|
||||
}
|
||||
|
||||
@ -265,7 +266,9 @@ fn checkLegacyIndirectFunctionTable(object: *Object) !?Symbol {
|
||||
} else unreachable;
|
||||
|
||||
if (!std.mem.eql(u8, object.string_table.get(table_import.name), "__indirect_function_table")) {
|
||||
log.err("Non-indirect function table import '{s}' is missing a corresponding symbol", .{object.string_table.get(table_import.name)});
|
||||
var err = try wasm_file.addErrorWithNotes(1);
|
||||
try err.addMsg(wasm_file, "Non-indirect function table import '{s}' is missing a corresponding symbol", .{object.string_table.get(table_import.name)});
|
||||
try err.addNote(wasm_file, "defined in '{s}'", .{object.path});
|
||||
return error.MissingTableSymbols;
|
||||
}
|
||||
|
||||
@ -318,8 +321,8 @@ pub const ParseError = error{
|
||||
UnknownFeature,
|
||||
};
|
||||
|
||||
fn parse(object: *Object, gpa: Allocator, reader: anytype, is_object_file: *bool) Parser(@TypeOf(reader)).Error!void {
|
||||
var parser = Parser(@TypeOf(reader)).init(object, reader);
|
||||
fn parse(object: *Object, gpa: Allocator, wasm_file: *const Wasm, reader: anytype, is_object_file: *bool) Parser(@TypeOf(reader)).Error!void {
|
||||
var parser = Parser(@TypeOf(reader)).init(object, wasm_file, reader);
|
||||
return parser.parseObject(gpa, is_object_file);
|
||||
}
|
||||
|
||||
@ -331,9 +334,11 @@ fn Parser(comptime ReaderType: type) type {
|
||||
reader: std.io.CountingReader(ReaderType),
|
||||
/// Object file we're building
|
||||
object: *Object,
|
||||
/// Read-only reference to the WebAssembly linker
|
||||
wasm_file: *const Wasm,
|
||||
|
||||
fn init(object: *Object, reader: ReaderType) ObjectParser {
|
||||
return .{ .object = object, .reader = std.io.countingReader(reader) };
|
||||
fn init(object: *Object, wasm_file: *const Wasm, reader: ReaderType) ObjectParser {
|
||||
return .{ .object = object, .wasm_file = wasm_file, .reader = std.io.countingReader(reader) };
|
||||
}
|
||||
|
||||
/// Verifies that the first 4 bytes contains \0Asm
|
||||
@ -427,16 +432,25 @@ fn Parser(comptime ReaderType: type) type {
|
||||
|
||||
const kind = try readEnum(std.wasm.ExternalKind, reader);
|
||||
const kind_value: std.wasm.Import.Kind = switch (kind) {
|
||||
.function => .{ .function = try readLeb(u32, reader) },
|
||||
.function => val: {
|
||||
parser.object.imported_functions_count += 1;
|
||||
break :val .{ .function = try readLeb(u32, reader) };
|
||||
},
|
||||
.memory => .{ .memory = try readLimits(reader) },
|
||||
.global => .{ .global = .{
|
||||
.valtype = try readEnum(std.wasm.Valtype, reader),
|
||||
.mutable = (try reader.readByte()) == 0x01,
|
||||
} },
|
||||
.table => .{ .table = .{
|
||||
.reftype = try readEnum(std.wasm.RefType, reader),
|
||||
.limits = try readLimits(reader),
|
||||
} },
|
||||
.global => val: {
|
||||
parser.object.imported_globals_count += 1;
|
||||
break :val .{ .global = .{
|
||||
.valtype = try readEnum(std.wasm.Valtype, reader),
|
||||
.mutable = (try reader.readByte()) == 0x01,
|
||||
} };
|
||||
},
|
||||
.table => val: {
|
||||
parser.object.imported_tables_count += 1;
|
||||
break :val .{ .table = .{
|
||||
.reftype = try readEnum(std.wasm.RefType, reader),
|
||||
.limits = try readLimits(reader),
|
||||
} };
|
||||
},
|
||||
};
|
||||
|
||||
import.* = .{
|
||||
@ -513,7 +527,7 @@ fn Parser(comptime ReaderType: type) type {
|
||||
const start = reader.context.bytes_left;
|
||||
var index: u32 = 0;
|
||||
const count = try readLeb(u32, reader);
|
||||
const imported_function_count = parser.object.importedCountByKind(.function);
|
||||
const imported_function_count = parser.object.imported_functions_count;
|
||||
var relocatable_data = try std.ArrayList(RelocatableData).initCapacity(gpa, count);
|
||||
defer relocatable_data.deinit();
|
||||
while (index < count) : (index += 1) {
|
||||
@ -582,7 +596,9 @@ fn Parser(comptime ReaderType: type) type {
|
||||
try reader.readNoEof(name);
|
||||
|
||||
const tag = types.known_features.get(name) orelse {
|
||||
log.err("Object file contains unknown feature: {s}", .{name});
|
||||
var err = try parser.wasm_file.addErrorWithNotes(1);
|
||||
try err.addMsg(parser.wasm_file, "Object file contains unknown feature: {s}", .{name});
|
||||
try err.addNote(parser.wasm_file, "defined in '{s}'", .{parser.object.path});
|
||||
return error.UnknownFeature;
|
||||
};
|
||||
feature.* = .{
|
||||
@ -751,7 +767,7 @@ fn Parser(comptime ReaderType: type) type {
|
||||
|
||||
// we found all symbols, check for indirect function table
|
||||
// in case of an MVP object file
|
||||
if (try parser.object.checkLegacyIndirectFunctionTable()) |symbol| {
|
||||
if (try parser.object.checkLegacyIndirectFunctionTable(parser.wasm_file)) |symbol| {
|
||||
try symbols.append(symbol);
|
||||
log.debug("Found legacy indirect function table. Created symbol", .{});
|
||||
}
|
||||
@ -830,7 +846,7 @@ fn Parser(comptime ReaderType: type) type {
|
||||
defer gpa.free(name);
|
||||
try reader.readNoEof(name);
|
||||
break :name try parser.object.string_table.put(gpa, name);
|
||||
} else parser.object.findImport(symbol.tag.externalType(), symbol.index).name;
|
||||
} else parser.object.findImport(symbol).name;
|
||||
},
|
||||
}
|
||||
return symbol;
|
||||
@ -904,12 +920,12 @@ fn assertEnd(reader: anytype) !void {
|
||||
}
|
||||
|
||||
/// Parses an object file into atoms, for code and data sections
|
||||
pub fn parseSymbolIntoAtom(object: *Object, object_index: u16, symbol_index: u32, wasm: *Wasm) !Atom.Index {
|
||||
pub fn parseSymbolIntoAtom(object: *Object, wasm: *Wasm, symbol_index: Symbol.Index) !Atom.Index {
|
||||
const comp = wasm.base.comp;
|
||||
const gpa = comp.gpa;
|
||||
const symbol = &object.symtable[symbol_index];
|
||||
const symbol = &object.symtable[@intFromEnum(symbol_index)];
|
||||
const relocatable_data: RelocatableData = switch (symbol.tag) {
|
||||
.function => object.relocatable_data.get(.code).?[symbol.index - object.importedCountByKind(.function)],
|
||||
.function => object.relocatable_data.get(.code).?[symbol.index - object.imported_functions_count],
|
||||
.data => object.relocatable_data.get(.data).?[symbol.index],
|
||||
.section => blk: {
|
||||
const data = object.relocatable_data.get(.custom).?;
|
||||
@ -922,19 +938,16 @@ pub fn parseSymbolIntoAtom(object: *Object, object_index: u16, symbol_index: u32
|
||||
},
|
||||
else => unreachable,
|
||||
};
|
||||
const final_index = try wasm.getMatchingSegment(object_index, symbol_index);
|
||||
const atom_index = @as(Atom.Index, @intCast(wasm.managed_atoms.items.len));
|
||||
const atom = try wasm.managed_atoms.addOne(gpa);
|
||||
atom.* = Atom.empty;
|
||||
const final_index = try wasm.getMatchingSegment(object.index, symbol_index);
|
||||
const atom_index = try wasm.createAtom(symbol_index, object.index);
|
||||
try wasm.appendAtomAtIndex(final_index, atom_index);
|
||||
|
||||
atom.sym_index = symbol_index;
|
||||
atom.file = object_index;
|
||||
const atom = wasm.getAtomPtr(atom_index);
|
||||
atom.size = relocatable_data.size;
|
||||
atom.alignment = relocatable_data.getAlignment(object);
|
||||
atom.code = std.ArrayListUnmanaged(u8).fromOwnedSlice(relocatable_data.data[0..relocatable_data.size]);
|
||||
atom.original_offset = relocatable_data.offset;
|
||||
try wasm.symbol_atom.putNoClobber(gpa, atom.symbolLoc(), atom_index);
|
||||
|
||||
const segment: *Wasm.Segment = &wasm.segments.items[final_index];
|
||||
if (relocatable_data.type == .data) { //code section and custom sections are 1-byte aligned
|
||||
segment.alignment = segment.alignment.max(atom.alignment);
|
||||
@ -952,8 +965,8 @@ pub fn parseSymbolIntoAtom(object: *Object, object_index: u16, symbol_index: u32
|
||||
.R_WASM_TABLE_INDEX_SLEB64,
|
||||
=> {
|
||||
try wasm.function_table.put(gpa, .{
|
||||
.file = object_index,
|
||||
.index = reloc.index,
|
||||
.file = object.index,
|
||||
.index = @enumFromInt(reloc.index),
|
||||
}, 0);
|
||||
},
|
||||
.R_WASM_GLOBAL_INDEX_I32,
|
||||
@ -961,10 +974,7 @@ pub fn parseSymbolIntoAtom(object: *Object, object_index: u16, symbol_index: u32
|
||||
=> {
|
||||
const sym = object.symtable[reloc.index];
|
||||
if (sym.tag != .global) {
|
||||
try wasm.got_symbols.append(
|
||||
gpa,
|
||||
.{ .file = object_index, .index = reloc.index },
|
||||
);
|
||||
try wasm.got_symbols.append(gpa, .{ .file = object.index, .index = @enumFromInt(reloc.index) });
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
|
||||
@ -1,12 +1,8 @@
|
||||
//! Represents a wasm symbol. Containing all of its properties,
|
||||
//! Represents a WebAssembly symbol. Containing all of its properties,
|
||||
//! as well as providing helper methods to determine its functionality
|
||||
//! and how it will/must be linked.
|
||||
//! The name of the symbol can be found by providing the offset, found
|
||||
//! on the `name` field, to a string table in the wasm binary or object file.
|
||||
const Symbol = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const types = @import("types.zig");
|
||||
|
||||
/// Bitfield containings flags for a symbol
|
||||
/// Can contain any of the flags defined in `Flag`
|
||||
@ -24,6 +20,12 @@ tag: Tag,
|
||||
/// This differs from the offset of an `Atom` which is relative to the start of a segment.
|
||||
virtual_address: u32,
|
||||
|
||||
/// Represents a symbol index where `null` represents an invalid index.
|
||||
pub const Index = enum(u32) {
|
||||
null,
|
||||
_,
|
||||
};
|
||||
|
||||
pub const Tag = enum {
|
||||
function,
|
||||
data,
|
||||
@ -202,3 +204,7 @@ pub fn format(symbol: Symbol, comptime fmt: []const u8, options: std.fmt.FormatO
|
||||
.{ kind_fmt, binding, visible, symbol.index, symbol.name, undef },
|
||||
);
|
||||
}
|
||||
|
||||
const std = @import("std");
|
||||
const types = @import("types.zig");
|
||||
const Symbol = @This();
|
||||
|
||||
1248
src/link/Wasm/ZigObject.zig
Normal file
1248
src/link/Wasm/ZigObject.zig
Normal file
File diff suppressed because it is too large
Load Diff
132
src/link/Wasm/file.zig
Normal file
132
src/link/Wasm/file.zig
Normal file
@ -0,0 +1,132 @@
|
||||
pub const File = union(enum) {
|
||||
zig_object: *ZigObject,
|
||||
object: *Object,
|
||||
|
||||
pub const Index = enum(u16) {
|
||||
null = std.math.maxInt(u16),
|
||||
_,
|
||||
};
|
||||
|
||||
pub fn path(file: File) []const u8 {
|
||||
return switch (file) {
|
||||
inline else => |obj| obj.path,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn segmentInfo(file: File) []const types.Segment {
|
||||
return switch (file) {
|
||||
.zig_object => |obj| obj.segment_info.items,
|
||||
.object => |obj| obj.segment_info,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn symbol(file: File, index: Symbol.Index) *Symbol {
|
||||
return switch (file) {
|
||||
.zig_object => |obj| &obj.symbols.items[@intFromEnum(index)],
|
||||
.object => |obj| &obj.symtable[@intFromEnum(index)],
|
||||
};
|
||||
}
|
||||
|
||||
pub fn symbols(file: File) []const Symbol {
|
||||
return switch (file) {
|
||||
.zig_object => |obj| obj.symbols.items,
|
||||
.object => |obj| obj.symtable,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn symbolName(file: File, index: Symbol.Index) []const u8 {
|
||||
switch (file) {
|
||||
.zig_object => |obj| {
|
||||
const sym = obj.symbols.items[@intFromEnum(index)];
|
||||
return obj.string_table.get(sym.name).?;
|
||||
},
|
||||
.object => |obj| {
|
||||
const sym = obj.symtable[@intFromEnum(index)];
|
||||
return obj.string_table.get(sym.name);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parseSymbolIntoAtom(file: File, wasm_file: *Wasm, index: Symbol.Index) !AtomIndex {
|
||||
return switch (file) {
|
||||
inline else => |obj| obj.parseSymbolIntoAtom(wasm_file, index),
|
||||
};
|
||||
}
|
||||
|
||||
/// For a given symbol index, find its corresponding import.
|
||||
/// Asserts import exists.
|
||||
pub fn import(file: File, symbol_index: Symbol.Index) types.Import {
|
||||
return switch (file) {
|
||||
.zig_object => |obj| obj.imports.get(symbol_index).?,
|
||||
.object => |obj| obj.findImport(obj.symtable[@intFromEnum(symbol_index)]),
|
||||
};
|
||||
}
|
||||
|
||||
/// For a given offset, returns its string value.
|
||||
/// Asserts string exists in the object string table.
|
||||
pub fn string(file: File, offset: u32) []const u8 {
|
||||
return switch (file) {
|
||||
.zig_object => |obj| obj.string_table.get(offset).?,
|
||||
.object => |obj| obj.string_table.get(offset),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn importedGlobals(file: File) u32 {
|
||||
return switch (file) {
|
||||
inline else => |obj| obj.imported_globals_count,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn importedFunctions(file: File) u32 {
|
||||
return switch (file) {
|
||||
inline else => |obj| obj.imported_functions_count,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn importedTables(file: File) u32 {
|
||||
return switch (file) {
|
||||
inline else => |obj| obj.imported_tables_count,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn function(file: File, sym_index: Symbol.Index) std.wasm.Func {
|
||||
switch (file) {
|
||||
.zig_object => |obj| {
|
||||
const sym = obj.symbols.items[@intFromEnum(sym_index)];
|
||||
return obj.functions.items[sym.index];
|
||||
},
|
||||
.object => |obj| {
|
||||
const sym = obj.symtable[@intFromEnum(sym_index)];
|
||||
return obj.functions[sym.index - obj.imported_functions_count];
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn globals(file: File) []const std.wasm.Global {
|
||||
return switch (file) {
|
||||
.zig_object => |obj| obj.globals.items,
|
||||
.object => |obj| obj.globals,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn funcTypes(file: File) []const std.wasm.Type {
|
||||
return switch (file) {
|
||||
.zig_object => |obj| obj.func_types.items,
|
||||
.object => |obj| obj.func_types,
|
||||
};
|
||||
}
|
||||
|
||||
pub const Entry = union(enum) {
|
||||
zig_object: ZigObject,
|
||||
object: Object,
|
||||
};
|
||||
};
|
||||
|
||||
const std = @import("std");
|
||||
const types = @import("types.zig");
|
||||
|
||||
const AtomIndex = @import("Atom.zig").Index;
|
||||
const Object = @import("Object.zig");
|
||||
const Symbol = @import("Symbol.zig");
|
||||
const Wasm = @import("../Wasm.zig");
|
||||
const ZigObject = @import("ZigObject.zig");
|
||||
@ -35,11 +35,10 @@ pub const cases = [_]Case{
|
||||
},
|
||||
|
||||
// WASM Cases
|
||||
// https://github.com/ziglang/zig/issues/16938
|
||||
//.{
|
||||
// .build_root = "test/link/wasm/archive",
|
||||
// .import = @import("link/wasm/archive/build.zig"),
|
||||
//},
|
||||
.{
|
||||
.build_root = "test/link/wasm/archive",
|
||||
.import = @import("link/wasm/archive/build.zig"),
|
||||
},
|
||||
.{
|
||||
.build_root = "test/link/wasm/basic-features",
|
||||
.import = @import("link/wasm/basic-features/build.zig"),
|
||||
@ -52,11 +51,10 @@ pub const cases = [_]Case{
|
||||
.build_root = "test/link/wasm/export",
|
||||
.import = @import("link/wasm/export/build.zig"),
|
||||
},
|
||||
// https://github.com/ziglang/zig/issues/16937
|
||||
//.{
|
||||
// .build_root = "test/link/wasm/export-data",
|
||||
// .import = @import("link/wasm/export-data/build.zig"),
|
||||
//},
|
||||
.{
|
||||
.build_root = "test/link/wasm/export-data",
|
||||
.import = @import("link/wasm/export-data/build.zig"),
|
||||
},
|
||||
.{
|
||||
.build_root = "test/link/wasm/extern",
|
||||
.import = @import("link/wasm/extern/build.zig"),
|
||||
@ -81,6 +79,10 @@ pub const cases = [_]Case{
|
||||
.build_root = "test/link/wasm/segments",
|
||||
.import = @import("link/wasm/segments/build.zig"),
|
||||
},
|
||||
.{
|
||||
.build_root = "test/link/wasm/shared-memory",
|
||||
.import = @import("link/wasm/shared-memory/build.zig"),
|
||||
},
|
||||
.{
|
||||
.build_root = "test/link/wasm/stack_pointer",
|
||||
.import = @import("link/wasm/stack_pointer/build.zig"),
|
||||
|
||||
@ -19,12 +19,13 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
|
||||
.name = "main",
|
||||
.root_source_file = .{ .path = "main.zig" },
|
||||
.optimize = optimize,
|
||||
.target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
|
||||
.target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
|
||||
.strip = false,
|
||||
});
|
||||
lib.entry = .disabled;
|
||||
lib.use_llvm = false;
|
||||
lib.use_lld = false;
|
||||
lib.root_module.export_symbol_names = &.{"foo"};
|
||||
|
||||
const check = lib.checkObject();
|
||||
check.checkInHeaders();
|
||||
|
||||
@ -13,7 +13,7 @@ pub fn build(b: *std.Build) void {
|
||||
.name = "lib",
|
||||
.root_source_file = .{ .path = "lib.zig" },
|
||||
.optimize = .ReleaseSafe, // to make the output deterministic in address positions
|
||||
.target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
|
||||
.target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
|
||||
});
|
||||
lib.entry = .disabled;
|
||||
lib.use_lld = false;
|
||||
|
||||
@ -11,37 +11,39 @@ pub fn build(b: *std.Build) void {
|
||||
}
|
||||
|
||||
fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.OptimizeMode) void {
|
||||
const lib = b.addExecutable(.{
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "lib",
|
||||
.root_source_file = .{ .path = "lib.zig" },
|
||||
.target = .{
|
||||
.target = b.resolveTargetQuery(.{
|
||||
.cpu_arch = .wasm32,
|
||||
.cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp },
|
||||
.cpu_features_add = std.Target.wasm.featureSet(&.{ .atomics, .bulk_memory }),
|
||||
.os_tag = .freestanding,
|
||||
},
|
||||
}),
|
||||
.optimize = optimize_mode,
|
||||
.strip = false,
|
||||
.single_threaded = false,
|
||||
});
|
||||
lib.entry = .disabled;
|
||||
lib.use_lld = false;
|
||||
lib.import_memory = true;
|
||||
lib.export_memory = true;
|
||||
lib.shared_memory = true;
|
||||
lib.max_memory = 67108864;
|
||||
lib.root_module.export_symbol_names = &.{"foo"};
|
||||
exe.entry = .disabled;
|
||||
exe.use_lld = false;
|
||||
exe.import_memory = true;
|
||||
exe.export_memory = true;
|
||||
exe.shared_memory = true;
|
||||
exe.max_memory = 67108864;
|
||||
exe.root_module.export_symbol_names = &.{"foo"};
|
||||
|
||||
const check_lib = lib.checkObject();
|
||||
const check_exe = exe.checkObject();
|
||||
|
||||
check_lib.checkStart("Section import");
|
||||
check_lib.checkNext("entries 1");
|
||||
check_lib.checkNext("module env");
|
||||
check_lib.checkNext("name memory"); // ensure we are importing memory
|
||||
check_exe.checkInHeaders();
|
||||
check_exe.checkExact("Section import");
|
||||
check_exe.checkExact("entries 1");
|
||||
check_exe.checkExact("module env");
|
||||
check_exe.checkExact("name memory"); // ensure we are importing memory
|
||||
|
||||
check_lib.checkStart("Section export");
|
||||
check_lib.checkNext("entries 2");
|
||||
check_lib.checkNext("name memory"); // ensure we also export memory again
|
||||
check_exe.checkInHeaders();
|
||||
check_exe.checkExact("Section export");
|
||||
check_exe.checkExact("entries 2");
|
||||
check_exe.checkExact("name memory"); // ensure we also export memory again
|
||||
|
||||
// This section *must* be emit as the start function is set to the index
|
||||
// of __wasm_init_memory
|
||||
@ -49,49 +51,42 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.Opt
|
||||
// This means we won't have __wasm_init_memory in such case, and therefore
|
||||
// should also not have a section "start"
|
||||
if (optimize_mode == .Debug) {
|
||||
check_lib.checkStart("Section start");
|
||||
check_exe.checkInHeaders();
|
||||
check_exe.checkExact("Section start");
|
||||
}
|
||||
|
||||
// This section is only and *must* be emit when shared-memory is enabled
|
||||
// release modes will have the TLS segment optimized out in our test-case.
|
||||
if (optimize_mode == .Debug) {
|
||||
check_lib.checkStart("Section data_count");
|
||||
check_lib.checkNext("count 3");
|
||||
check_exe.checkInHeaders();
|
||||
check_exe.checkExact("Section data_count");
|
||||
check_exe.checkExact("count 1");
|
||||
}
|
||||
|
||||
check_lib.checkStart("Section custom");
|
||||
check_lib.checkNext("name name");
|
||||
check_lib.checkNext("type function");
|
||||
check_exe.checkInHeaders();
|
||||
check_exe.checkExact("Section custom");
|
||||
check_exe.checkExact("name name");
|
||||
check_exe.checkExact("type function");
|
||||
if (optimize_mode == .Debug) {
|
||||
check_lib.checkNext("name __wasm_init_memory");
|
||||
check_exe.checkExact("name __wasm_init_memory");
|
||||
}
|
||||
check_lib.checkNext("name __wasm_init_tls");
|
||||
check_lib.checkNext("type global");
|
||||
check_exe.checkExact("name __wasm_init_tls");
|
||||
check_exe.checkExact("type global");
|
||||
|
||||
// In debug mode the symbol __tls_base is resolved to an undefined symbol
|
||||
// from the object file, hence its placement differs than in release modes
|
||||
// where the entire tls segment is optimized away, and tls_base will have
|
||||
// its original position.
|
||||
check_exe.checkExact("name __tls_base");
|
||||
check_exe.checkExact("name __tls_size");
|
||||
check_exe.checkExact("name __tls_align");
|
||||
|
||||
check_exe.checkExact("type data_segment");
|
||||
if (optimize_mode == .Debug) {
|
||||
check_lib.checkNext("name __tls_size");
|
||||
check_lib.checkNext("name __tls_align");
|
||||
check_lib.checkNext("name __tls_base");
|
||||
} else {
|
||||
check_lib.checkNext("name __tls_base");
|
||||
check_lib.checkNext("name __tls_size");
|
||||
check_lib.checkNext("name __tls_align");
|
||||
check_exe.checkExact("names 1");
|
||||
check_exe.checkExact("index 0");
|
||||
check_exe.checkExact("name .tdata");
|
||||
}
|
||||
|
||||
check_lib.checkNext("type data_segment");
|
||||
if (optimize_mode == .Debug) {
|
||||
check_lib.checkNext("names 3");
|
||||
check_lib.checkNext("index 0");
|
||||
check_lib.checkNext("name .rodata");
|
||||
check_lib.checkNext("index 1");
|
||||
check_lib.checkNext("name .bss");
|
||||
check_lib.checkNext("index 2");
|
||||
check_lib.checkNext("name .tdata");
|
||||
}
|
||||
|
||||
test_step.dependOn(&check_lib.step);
|
||||
test_step.dependOn(&check_exe.step);
|
||||
}
|
||||
|
||||
@ -13,31 +13,32 @@ pub fn build(b: *std.Build) void {
|
||||
}
|
||||
|
||||
fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
|
||||
const lib = b.addExecutable(.{
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "lib",
|
||||
.root_source_file = .{ .path = "lib.zig" },
|
||||
.target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
|
||||
.optimize = optimize,
|
||||
.strip = false,
|
||||
});
|
||||
lib.entry = .disabled;
|
||||
lib.use_llvm = false;
|
||||
lib.use_lld = false;
|
||||
b.installArtifact(lib);
|
||||
exe.entry = .disabled;
|
||||
exe.use_llvm = false;
|
||||
exe.use_lld = false;
|
||||
exe.root_module.export_symbol_names = &.{"foo"};
|
||||
b.installArtifact(exe);
|
||||
|
||||
const check_lib = lib.checkObject();
|
||||
check_lib.checkInHeaders();
|
||||
check_lib.checkExact("Section type");
|
||||
const check_exe = exe.checkObject();
|
||||
check_exe.checkInHeaders();
|
||||
check_exe.checkExact("Section type");
|
||||
// only 2 entries, although we have more functions.
|
||||
// This is to test functions with the same function signature
|
||||
// have their types deduplicated.
|
||||
check_lib.checkExact("entries 2");
|
||||
check_lib.checkExact("params 1");
|
||||
check_lib.checkExact("type i32");
|
||||
check_lib.checkExact("returns 1");
|
||||
check_lib.checkExact("type i64");
|
||||
check_lib.checkExact("params 0");
|
||||
check_lib.checkExact("returns 0");
|
||||
check_exe.checkExact("entries 2");
|
||||
check_exe.checkExact("params 1");
|
||||
check_exe.checkExact("type i32");
|
||||
check_exe.checkExact("returns 1");
|
||||
check_exe.checkExact("type i64");
|
||||
check_exe.checkExact("params 0");
|
||||
check_exe.checkExact("returns 0");
|
||||
|
||||
test_step.dependOn(&check_lib.step);
|
||||
test_step.dependOn(&check_exe.step);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user