mirror of
https://github.com/ziglang/zig.git
synced 2025-12-12 17:23:09 +00:00
wasm linker: implement data symbols
This commit is contained in:
parent
a4bee3009a
commit
abdbc38574
@ -123,9 +123,10 @@ object_init_funcs: std.ArrayListUnmanaged(InitFunc) = .empty,
|
||||
/// logically to an object file's .data section, or .rodata section. In
|
||||
/// the case of `-fdata-sections` there will be one segment per data symbol.
|
||||
object_data_segments: std.ArrayListUnmanaged(ObjectDataSegment) = .empty,
|
||||
/// Each segment has many data symbols. These correspond logically to global
|
||||
/// Each segment has many data symbols, which correspond logically to global
|
||||
/// constants.
|
||||
object_datas: std.ArrayListUnmanaged(ObjectData) = .empty,
|
||||
object_data_imports: std.AutoArrayHashMapUnmanaged(String, ObjectDataImport) = .empty,
|
||||
/// Non-synthetic section that can essentially be mem-cpy'd into place after performing relocations.
|
||||
object_custom_segments: std.AutoArrayHashMapUnmanaged(ObjectSectionIndex, CustomSegment) = .empty,
|
||||
|
||||
@ -227,6 +228,22 @@ functions_end_prelink: u32 = 0,
|
||||
/// symbol errors, or import section entries depending on the output mode.
|
||||
function_imports: std.AutoArrayHashMapUnmanaged(String, FunctionImportId) = .empty,
|
||||
|
||||
/// At the end of prelink, this is populated with data symbols needed by
|
||||
/// objects.
|
||||
///
|
||||
/// During the Zcu phase, entries are not deleted from this table
|
||||
/// because doing so would be irreversible when a `deleteExport` call is
|
||||
/// handled. However, entries are added during the Zcu phase when extern
|
||||
/// functions are passed to `updateNav`.
|
||||
///
|
||||
/// `flush` gets a copy of this table, and then Zcu exports are applied to
|
||||
/// remove elements from the table, and the remainder are either undefined
|
||||
/// symbol errors, or symbol table entries depending on the output mode.
|
||||
data_imports: std.AutoArrayHashMapUnmanaged(String, DataImportId) = .empty,
|
||||
/// Set of data symbols that will appear in the final binary. Used to populate
|
||||
/// `Flush.data_segments` before sorting.
|
||||
data_segments: std.AutoArrayHashMapUnmanaged(DataId, void) = .empty,
|
||||
|
||||
/// Ordered list of non-import globals that will appear in the final binary.
|
||||
/// Empty until prelink.
|
||||
globals: std.AutoArrayHashMapUnmanaged(GlobalImport.Resolution, void) = .empty,
|
||||
@ -251,6 +268,7 @@ error_name_table_ref_count: u32 = 0,
|
||||
/// value must be this OR'd with the same logic for zig functions
|
||||
/// (set to true if any threadlocal global is used).
|
||||
any_tls_relocs: bool = false,
|
||||
any_passive_inits: bool = false,
|
||||
|
||||
/// All MIR instructions for all Zcu functions.
|
||||
mir_instructions: std.MultiArrayList(Mir.Inst) = .{},
|
||||
@ -1499,6 +1517,50 @@ pub const ObjectData = extern struct {
|
||||
};
|
||||
};
|
||||
|
||||
pub const ObjectDataImport = extern struct {
|
||||
resolution: Resolution,
|
||||
flags: SymbolFlags,
|
||||
source_location: SourceLocation,
|
||||
|
||||
pub const Resolution = enum(u32) {
|
||||
__zig_error_names,
|
||||
__zig_error_name_table,
|
||||
__heap_base,
|
||||
__heap_end,
|
||||
unresolved = std.math.maxInt(u32),
|
||||
_,
|
||||
|
||||
comptime {
|
||||
assert(@intFromEnum(Resolution.__zig_error_names) == @intFromEnum(DataId.__zig_error_names));
|
||||
assert(@intFromEnum(Resolution.__zig_error_name_table) == @intFromEnum(DataId.__zig_error_name_table));
|
||||
assert(@intFromEnum(Resolution.__heap_base) == @intFromEnum(DataId.__heap_base));
|
||||
assert(@intFromEnum(Resolution.__heap_end) == @intFromEnum(DataId.__heap_end));
|
||||
}
|
||||
|
||||
pub fn toDataId(r: Resolution) ?DataId {
|
||||
if (r == .unresolved) return null;
|
||||
return @enumFromInt(@intFromEnum(r));
|
||||
}
|
||||
|
||||
pub fn fromObjectDataIndex(wasm: *const Wasm, object_data_index: ObjectData.Index) Resolution {
|
||||
return @enumFromInt(@intFromEnum(DataId.pack(wasm, .{ .object = object_data_index.ptr(wasm).segment })));
|
||||
}
|
||||
};
|
||||
|
||||
/// Points into `Wasm.object_data_imports`.
|
||||
pub const Index = enum(u32) {
|
||||
_,
|
||||
|
||||
pub fn value(i: @This(), wasm: *const Wasm) *ObjectDataImport {
|
||||
return &wasm.object_data_imports.values()[@intFromEnum(i)];
|
||||
}
|
||||
|
||||
pub fn fromSymbolName(wasm: *const Wasm, name: String) ?Index {
|
||||
return @enumFromInt(wasm.object_data_imports.getIndex(name) orelse return null);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
pub const DataPayload = extern struct {
|
||||
off: Off,
|
||||
/// The size in bytes of the data representing the segment within the section.
|
||||
@ -1524,12 +1586,17 @@ pub const DataPayload = extern struct {
|
||||
pub const DataId = enum(u32) {
|
||||
__zig_error_names,
|
||||
__zig_error_name_table,
|
||||
/// This and `__heap_end` are better retrieved via a global, but there is
|
||||
/// some suboptimal code out there (wasi libc) that additionally needs them
|
||||
/// as data symbols.
|
||||
__heap_base,
|
||||
__heap_end,
|
||||
/// First, an `ObjectDataSegment.Index`.
|
||||
/// Next, index into `uavs_obj` or `uavs_exe` depending on whether emitting an object.
|
||||
/// Next, index into `navs_obj` or `navs_exe` depending on whether emitting an object.
|
||||
_,
|
||||
|
||||
const first_object = @intFromEnum(DataId.__zig_error_name_table) + 1;
|
||||
const first_object = @intFromEnum(DataId.__heap_end) + 1;
|
||||
|
||||
pub const Category = enum {
|
||||
/// Thread-local variables.
|
||||
@ -1544,6 +1611,8 @@ pub const DataId = enum(u32) {
|
||||
pub const Unpacked = union(enum) {
|
||||
__zig_error_names,
|
||||
__zig_error_name_table,
|
||||
__heap_base,
|
||||
__heap_end,
|
||||
object: ObjectDataSegment.Index,
|
||||
uav_exe: UavsExeIndex,
|
||||
uav_obj: UavsObjIndex,
|
||||
@ -1555,6 +1624,8 @@ pub const DataId = enum(u32) {
|
||||
return switch (unpacked) {
|
||||
.__zig_error_names => .__zig_error_names,
|
||||
.__zig_error_name_table => .__zig_error_name_table,
|
||||
.__heap_base => .__heap_base,
|
||||
.__heap_end => .__heap_end,
|
||||
.object => |i| @enumFromInt(first_object + @intFromEnum(i)),
|
||||
inline .uav_exe, .uav_obj => |i| @enumFromInt(first_object + wasm.object_data_segments.items.len + @intFromEnum(i)),
|
||||
.nav_exe => |i| @enumFromInt(first_object + wasm.object_data_segments.items.len + wasm.uavs_exe.entries.len + @intFromEnum(i)),
|
||||
@ -1566,6 +1637,8 @@ pub const DataId = enum(u32) {
|
||||
return switch (id) {
|
||||
.__zig_error_names => .__zig_error_names,
|
||||
.__zig_error_name_table => .__zig_error_name_table,
|
||||
.__heap_base => .__heap_base,
|
||||
.__heap_end => .__heap_end,
|
||||
_ => {
|
||||
const object_index = @intFromEnum(id) - first_object;
|
||||
|
||||
@ -1601,7 +1674,7 @@ pub const DataId = enum(u32) {
|
||||
|
||||
pub fn category(id: DataId, wasm: *const Wasm) Category {
|
||||
return switch (unpack(id, wasm)) {
|
||||
.__zig_error_names, .__zig_error_name_table => .data,
|
||||
.__zig_error_names, .__zig_error_name_table, .__heap_base, .__heap_end => .data,
|
||||
.object => |i| {
|
||||
const ptr = i.ptr(wasm);
|
||||
if (ptr.flags.tls) return .tls;
|
||||
@ -1622,7 +1695,7 @@ pub const DataId = enum(u32) {
|
||||
|
||||
pub fn isTls(id: DataId, wasm: *const Wasm) bool {
|
||||
return switch (unpack(id, wasm)) {
|
||||
.__zig_error_names, .__zig_error_name_table => false,
|
||||
.__zig_error_names, .__zig_error_name_table, .__heap_base, .__heap_end => false,
|
||||
.object => |i| i.ptr(wasm).flags.tls,
|
||||
.uav_exe, .uav_obj => false,
|
||||
inline .nav_exe, .nav_obj => |i| {
|
||||
@ -1640,7 +1713,7 @@ pub const DataId = enum(u32) {
|
||||
|
||||
pub fn name(id: DataId, wasm: *const Wasm) []const u8 {
|
||||
return switch (unpack(id, wasm)) {
|
||||
.__zig_error_names, .__zig_error_name_table, .uav_exe, .uav_obj => ".data",
|
||||
.__zig_error_names, .__zig_error_name_table, .uav_exe, .uav_obj, .__heap_base, .__heap_end => ".data",
|
||||
.object => |i| i.ptr(wasm).name.unwrap().?.slice(wasm),
|
||||
inline .nav_exe, .nav_obj => |i| {
|
||||
const zcu = wasm.base.comp.zcu.?;
|
||||
@ -1654,7 +1727,7 @@ pub const DataId = enum(u32) {
|
||||
pub fn alignment(id: DataId, wasm: *const Wasm) Alignment {
|
||||
return switch (unpack(id, wasm)) {
|
||||
.__zig_error_names => .@"1",
|
||||
.__zig_error_name_table => wasm.pointerAlignment(),
|
||||
.__zig_error_name_table, .__heap_base, .__heap_end => wasm.pointerAlignment(),
|
||||
.object => |i| i.ptr(wasm).flags.alignment,
|
||||
inline .uav_exe, .uav_obj => |i| {
|
||||
const zcu = wasm.base.comp.zcu.?;
|
||||
@ -1683,7 +1756,7 @@ pub const DataId = enum(u32) {
|
||||
return switch (unpack(id, wasm)) {
|
||||
.__zig_error_names => @intCast(wasm.error_name_offs.items.len),
|
||||
.__zig_error_name_table => wasm.error_name_table_ref_count,
|
||||
.object, .uav_obj, .nav_obj => 0,
|
||||
.object, .uav_obj, .nav_obj, .__heap_base, .__heap_end => 0,
|
||||
inline .uav_exe, .nav_exe => |i| i.value(wasm).count,
|
||||
};
|
||||
}
|
||||
@ -1692,7 +1765,7 @@ pub const DataId = enum(u32) {
|
||||
const comp = wasm.base.comp;
|
||||
if (comp.config.import_memory and !id.isBss(wasm)) return true;
|
||||
return switch (unpack(id, wasm)) {
|
||||
.__zig_error_names, .__zig_error_name_table => false,
|
||||
.__zig_error_names, .__zig_error_name_table, .__heap_base, .__heap_end => false,
|
||||
.object => |i| i.ptr(wasm).flags.is_passive,
|
||||
.uav_exe, .uav_obj, .nav_exe, .nav_obj => false,
|
||||
};
|
||||
@ -1700,7 +1773,7 @@ pub const DataId = enum(u32) {
|
||||
|
||||
pub fn isEmpty(id: DataId, wasm: *const Wasm) bool {
|
||||
return switch (unpack(id, wasm)) {
|
||||
.__zig_error_names, .__zig_error_name_table => false,
|
||||
.__zig_error_names, .__zig_error_name_table, .__heap_base, .__heap_end => false,
|
||||
.object => |i| i.ptr(wasm).payload.off == .none,
|
||||
inline .uav_exe, .uav_obj, .nav_exe, .nav_obj => |i| i.value(wasm).code.off == .none,
|
||||
};
|
||||
@ -1716,6 +1789,7 @@ pub const DataId = enum(u32) {
|
||||
const elem_size = ZcuType.slice_const_u8_sentinel_0.abiSize(zcu);
|
||||
return @intCast(errors_len * elem_size);
|
||||
},
|
||||
.__heap_base, .__heap_end => wasm.pointerSize(),
|
||||
.object => |i| i.ptr(wasm).payload.len,
|
||||
inline .uav_exe, .uav_obj, .nav_exe, .nav_obj => |i| i.value(wasm).code.len,
|
||||
};
|
||||
@ -2110,6 +2184,55 @@ pub const GlobalImportId = enum(u32) {
|
||||
}
|
||||
};
|
||||
|
||||
/// 0. Index into `Wasm.object_data_imports`.
|
||||
/// 1. Index into `Wasm.imports`.
|
||||
pub const DataImportId = enum(u32) {
|
||||
_,
|
||||
|
||||
pub const Unpacked = union(enum) {
|
||||
object_data_import: ObjectDataImport.Index,
|
||||
zcu_import: ZcuImportIndex,
|
||||
};
|
||||
|
||||
pub fn pack(unpacked: Unpacked, wasm: *const Wasm) DataImportId {
|
||||
return switch (unpacked) {
|
||||
.object_data_import => |i| @enumFromInt(@intFromEnum(i)),
|
||||
.zcu_import => |i| @enumFromInt(@intFromEnum(i) - wasm.object_data_imports.entries.len),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn unpack(id: DataImportId, wasm: *const Wasm) Unpacked {
|
||||
const i = @intFromEnum(id);
|
||||
if (i < wasm.object_data_imports.entries.len) return .{ .object_data_import = @enumFromInt(i) };
|
||||
const zcu_import_i = i - wasm.object_data_imports.entries.len;
|
||||
return .{ .zcu_import = @enumFromInt(zcu_import_i) };
|
||||
}
|
||||
|
||||
pub fn fromZcuImport(zcu_import: ZcuImportIndex, wasm: *const Wasm) DataImportId {
|
||||
return pack(.{ .zcu_import = zcu_import }, wasm);
|
||||
}
|
||||
|
||||
pub fn fromObject(object_data_import: ObjectDataImport.Index, wasm: *const Wasm) DataImportId {
|
||||
return pack(.{ .object_data_import = object_data_import }, wasm);
|
||||
}
|
||||
|
||||
pub fn sourceLocation(id: DataImportId, wasm: *const Wasm) SourceLocation {
|
||||
switch (id.unpack(wasm)) {
|
||||
.object_data_import => |obj_data_index| {
|
||||
// TODO binary search
|
||||
for (wasm.objects.items, 0..) |o, i| {
|
||||
if (o.data_imports.off <= @intFromEnum(obj_data_index) and
|
||||
o.data_imports.off + o.data_imports.len > @intFromEnum(obj_data_index))
|
||||
{
|
||||
return .pack(.{ .object_index = @enumFromInt(i) }, wasm);
|
||||
}
|
||||
} else unreachable;
|
||||
},
|
||||
.zcu_import => return .zig_object_nofile, // TODO give a better source location
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// Index into `Wasm.symbol_table`.
|
||||
pub const SymbolTableIndex = enum(u32) {
|
||||
_,
|
||||
@ -2716,6 +2839,7 @@ pub fn deinit(wasm: *Wasm) void {
|
||||
wasm.object_memory_imports.deinit(gpa);
|
||||
wasm.object_memories.deinit(gpa);
|
||||
wasm.object_relocations.deinit(gpa);
|
||||
wasm.object_data_imports.deinit(gpa);
|
||||
wasm.object_data_segments.deinit(gpa);
|
||||
wasm.object_datas.deinit(gpa);
|
||||
wasm.object_custom_segments.deinit(gpa);
|
||||
@ -2734,6 +2858,8 @@ pub fn deinit(wasm: *Wasm) void {
|
||||
wasm.global_imports.deinit(gpa);
|
||||
wasm.table_imports.deinit(gpa);
|
||||
wasm.tables.deinit(gpa);
|
||||
wasm.data_imports.deinit(gpa);
|
||||
wasm.data_segments.deinit(gpa);
|
||||
wasm.symbol_table.deinit(gpa);
|
||||
wasm.out_relocs.deinit(gpa);
|
||||
wasm.uav_fixups.deinit(gpa);
|
||||
@ -2805,15 +2931,16 @@ pub fn updateNav(wasm: *Wasm, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index
|
||||
const name = try wasm.internString(ext.name.toSlice(ip));
|
||||
if (ext.lib_name.toSlice(ip)) |ext_name| _ = try wasm.internString(ext_name);
|
||||
try wasm.imports.ensureUnusedCapacity(gpa, 1);
|
||||
try wasm.function_imports.ensureUnusedCapacity(gpa, 1);
|
||||
try wasm.data_imports.ensureUnusedCapacity(gpa, 1);
|
||||
const zcu_import = wasm.addZcuImportReserved(ext.owner_nav);
|
||||
if (ip.isFunctionType(nav.typeOf(ip))) {
|
||||
try wasm.function_imports.ensureUnusedCapacity(gpa, 1);
|
||||
const zcu_import = wasm.addZcuImportReserved(ext.owner_nav);
|
||||
wasm.function_imports.putAssumeCapacity(name, .fromZcuImport(zcu_import, wasm));
|
||||
// Ensure there is a corresponding function type table entry.
|
||||
const fn_info = zcu.typeToFunc(.fromInterned(ext.ty)).?;
|
||||
_ = try internFunctionType(wasm, fn_info.cc, fn_info.param_types.get(ip), .fromInterned(fn_info.return_type), target);
|
||||
} else {
|
||||
@panic("TODO extern data");
|
||||
wasm.data_imports.putAssumeCapacity(name, .fromZcuImport(zcu_import, wasm));
|
||||
}
|
||||
return;
|
||||
},
|
||||
@ -3023,6 +3150,12 @@ pub fn prelink(wasm: *Wasm, prog_node: std.Progress.Node) link.File.FlushError!v
|
||||
try markTableImport(wasm, name, import, @enumFromInt(i));
|
||||
}
|
||||
}
|
||||
|
||||
for (wasm.object_data_imports.keys(), wasm.object_data_imports.values(), 0..) |name, *import, i| {
|
||||
if (import.flags.isIncluded(rdynamic)) {
|
||||
try markDataImport(wasm, name, import, @enumFromInt(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn markFunctionImport(
|
||||
@ -3163,13 +3296,45 @@ fn markTableImport(
|
||||
}
|
||||
|
||||
fn markDataSegment(wasm: *Wasm, segment_index: ObjectDataSegment.Index) link.File.FlushError!void {
|
||||
const comp = wasm.base.comp;
|
||||
const segment = segment_index.ptr(wasm);
|
||||
if (segment.flags.alive) return;
|
||||
segment.flags.alive = true;
|
||||
|
||||
wasm.any_passive_inits = wasm.any_passive_inits or segment.flags.is_passive or
|
||||
(comp.config.import_memory and !wasm.isBss(segment.name));
|
||||
|
||||
try wasm.data_segments.put(comp.gpa, .pack(wasm, .{ .object = segment_index }), {});
|
||||
try wasm.markRelocations(segment.relocations(wasm));
|
||||
}
|
||||
|
||||
fn markDataImport(
|
||||
wasm: *Wasm,
|
||||
name: String,
|
||||
import: *ObjectDataImport,
|
||||
data_index: ObjectDataImport.Index,
|
||||
) link.File.FlushError!void {
|
||||
if (import.flags.alive) return;
|
||||
import.flags.alive = true;
|
||||
|
||||
const comp = wasm.base.comp;
|
||||
const gpa = comp.gpa;
|
||||
|
||||
if (import.resolution == .unresolved) {
|
||||
if (name == wasm.preloaded_strings.__heap_base) {
|
||||
import.resolution = .__heap_base;
|
||||
wasm.data_segments.putAssumeCapacity(.__heap_base, {});
|
||||
} else if (name == wasm.preloaded_strings.__heap_end) {
|
||||
import.resolution = .__heap_end;
|
||||
wasm.data_segments.putAssumeCapacity(.__heap_end, {});
|
||||
} else {
|
||||
try wasm.data_imports.put(gpa, name, .fromObject(data_index, wasm));
|
||||
}
|
||||
} else {
|
||||
try markDataSegment(wasm, import.resolution.toDataId().?.unpack(wasm).object);
|
||||
}
|
||||
}
|
||||
|
||||
fn markRelocations(wasm: *Wasm, relocs: ObjectRelocation.IterableSlice) link.File.FlushError!void {
|
||||
for (relocs.slice.tags(wasm), relocs.slice.pointees(wasm), relocs.slice.offsets(wasm)) |tag, pointee, offset| {
|
||||
if (offset >= relocs.end) break;
|
||||
@ -3199,6 +3364,22 @@ fn markRelocations(wasm: *Wasm, relocs: ObjectRelocation.IterableSlice) link.Fil
|
||||
const i: TableImport.Index = @enumFromInt(wasm.object_table_imports.getIndex(name).?);
|
||||
try markTableImport(wasm, name, i.value(wasm), i);
|
||||
},
|
||||
.memory_addr_import_leb,
|
||||
.memory_addr_import_sleb,
|
||||
.memory_addr_import_i32,
|
||||
.memory_addr_import_rel_sleb,
|
||||
.memory_addr_import_leb64,
|
||||
.memory_addr_import_sleb64,
|
||||
.memory_addr_import_i64,
|
||||
.memory_addr_import_rel_sleb64,
|
||||
.memory_addr_import_tls_sleb,
|
||||
.memory_addr_import_locrel_i32,
|
||||
.memory_addr_import_tls_sleb64,
|
||||
=> {
|
||||
const name = pointee.symbol_name;
|
||||
const i = ObjectDataImport.Index.fromSymbolName(wasm, name).?;
|
||||
try markDataImport(wasm, name, i.value(wasm), i);
|
||||
},
|
||||
|
||||
.function_index_leb,
|
||||
.function_index_i32,
|
||||
@ -3220,26 +3401,6 @@ fn markRelocations(wasm: *Wasm, relocs: ObjectRelocation.IterableSlice) link.Fil
|
||||
.section_offset_i32 => {
|
||||
log.warn("TODO: ensure section {d} is included in output", .{pointee.section});
|
||||
},
|
||||
.memory_addr_import_leb,
|
||||
.memory_addr_import_sleb,
|
||||
.memory_addr_import_i32,
|
||||
.memory_addr_import_rel_sleb,
|
||||
.memory_addr_import_leb64,
|
||||
.memory_addr_import_sleb64,
|
||||
.memory_addr_import_i64,
|
||||
.memory_addr_import_rel_sleb64,
|
||||
.memory_addr_import_tls_sleb,
|
||||
.memory_addr_import_locrel_i32,
|
||||
.memory_addr_import_tls_sleb64,
|
||||
=> {
|
||||
const name = pointee.symbol_name;
|
||||
if (name == wasm.preloaded_strings.__heap_end or
|
||||
name == wasm.preloaded_strings.__heap_base)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
log.warn("TODO: ensure data symbol {s} is included in output", .{name.slice(wasm)});
|
||||
},
|
||||
|
||||
.memory_addr_leb,
|
||||
.memory_addr_sleb,
|
||||
@ -3309,6 +3470,7 @@ pub fn flushModule(
|
||||
try wasm.flush_buffer.missing_exports.reinit(gpa, wasm.missing_exports.keys(), &.{});
|
||||
try wasm.flush_buffer.function_imports.reinit(gpa, wasm.function_imports.keys(), wasm.function_imports.values());
|
||||
try wasm.flush_buffer.global_imports.reinit(gpa, wasm.global_imports.keys(), wasm.global_imports.values());
|
||||
try wasm.flush_buffer.data_imports.reinit(gpa, wasm.data_imports.keys(), wasm.data_imports.values());
|
||||
|
||||
return wasm.flush_buffer.finish(wasm) catch |err| switch (err) {
|
||||
error.OutOfMemory => return error.OutOfMemory,
|
||||
@ -4117,6 +4279,15 @@ fn pointerAlignment(wasm: *const Wasm) Alignment {
|
||||
};
|
||||
}
|
||||
|
||||
fn pointerSize(wasm: *const Wasm) u32 {
|
||||
const target = &wasm.base.comp.root_mod.resolved_target.result;
|
||||
return switch (target.cpu.arch) {
|
||||
.wasm32 => 4,
|
||||
.wasm64 => 8,
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
fn addZcuImportReserved(wasm: *Wasm, nav_index: InternPool.Nav.Index) ZcuImportIndex {
|
||||
const gop = wasm.imports.getOrPutAssumeCapacity(nav_index);
|
||||
gop.value_ptr.* = {};
|
||||
|
||||
@ -32,6 +32,7 @@ binary_bytes: std.ArrayListUnmanaged(u8) = .empty,
|
||||
missing_exports: std.AutoArrayHashMapUnmanaged(String, void) = .empty,
|
||||
function_imports: std.AutoArrayHashMapUnmanaged(String, Wasm.FunctionImportId) = .empty,
|
||||
global_imports: std.AutoArrayHashMapUnmanaged(String, Wasm.GlobalImportId) = .empty,
|
||||
data_imports: std.AutoArrayHashMapUnmanaged(String, Wasm.DataImportId) = .empty,
|
||||
|
||||
/// For debug purposes only.
|
||||
memory_layout_finished: bool = false,
|
||||
@ -50,6 +51,7 @@ pub fn deinit(f: *Flush, gpa: Allocator) void {
|
||||
f.missing_exports.deinit(gpa);
|
||||
f.function_imports.deinit(gpa);
|
||||
f.global_imports.deinit(gpa);
|
||||
f.data_imports.deinit(gpa);
|
||||
f.* = undefined;
|
||||
}
|
||||
|
||||
@ -108,7 +110,9 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
|
||||
.global_index = Wasm.GlobalIndex.fromIpNav(wasm, nav_export.nav_index).?,
|
||||
});
|
||||
_ = f.missing_exports.swapRemove(nav_export.name);
|
||||
_ = f.global_imports.swapRemove(nav_export.name);
|
||||
_ = f.data_imports.swapRemove(nav_export.name);
|
||||
// `f.global_imports` is ignored because Zcu has no way to
|
||||
// export wasm globals.
|
||||
}
|
||||
}
|
||||
|
||||
@ -139,6 +143,10 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
|
||||
const src_loc = table_import_id.value(wasm).source_location;
|
||||
src_loc.addError(wasm, "undefined table: {s}", .{name.slice(wasm)});
|
||||
}
|
||||
for (f.data_imports.keys(), f.data_imports.values()) |name, data_import_id| {
|
||||
const src_loc = data_import_id.sourceLocation(wasm);
|
||||
src_loc.addError(wasm, "undefined data: {s}", .{name.slice(wasm)});
|
||||
}
|
||||
}
|
||||
|
||||
if (diags.hasErrors()) return error.LinkFailure;
|
||||
@ -151,11 +159,9 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
|
||||
try wasm.functions.put(gpa, .__wasm_call_ctors, {});
|
||||
}
|
||||
|
||||
var any_passive_inits = false;
|
||||
|
||||
// Merge and order the data segments. Depends on garbage collection so that
|
||||
// unused segments can be omitted.
|
||||
try f.data_segments.ensureUnusedCapacity(gpa, wasm.object_data_segments.items.len +
|
||||
try f.data_segments.ensureUnusedCapacity(gpa, wasm.data_segments.entries.len +
|
||||
wasm.uavs_obj.entries.len + wasm.navs_obj.entries.len +
|
||||
wasm.uavs_exe.entries.len + wasm.navs_exe.entries.len + 2);
|
||||
if (is_obj) assert(wasm.uavs_exe.entries.len == 0);
|
||||
@ -174,18 +180,11 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
|
||||
for (0..wasm.navs_exe.entries.len) |navs_index| f.data_segments.putAssumeCapacityNoClobber(.pack(wasm, .{
|
||||
.nav_exe = @enumFromInt(navs_index),
|
||||
}), @as(u32, undefined));
|
||||
for (wasm.object_data_segments.items, 0..) |*ds, i| {
|
||||
if (!ds.flags.alive) continue;
|
||||
const obj_seg_index: Wasm.ObjectDataSegment.Index = @enumFromInt(i);
|
||||
any_passive_inits = any_passive_inits or ds.flags.is_passive or (import_memory and !wasm.isBss(ds.name));
|
||||
_ = f.data_segments.putAssumeCapacityNoClobber(.pack(wasm, .{
|
||||
.object = obj_seg_index,
|
||||
}), @as(u32, undefined));
|
||||
}
|
||||
if (wasm.error_name_table_ref_count > 0) {
|
||||
f.data_segments.putAssumeCapacity(.__zig_error_names, @as(u32, undefined));
|
||||
f.data_segments.putAssumeCapacity(.__zig_error_name_table, @as(u32, undefined));
|
||||
}
|
||||
for (wasm.data_segments.keys()) |data_id| f.data_segments.putAssumeCapacity(data_id, @as(u32, undefined));
|
||||
|
||||
try wasm.functions.ensureUnusedCapacity(gpa, 3);
|
||||
|
||||
@ -194,7 +193,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
|
||||
// dropped in __wasm_init_memory, which is registered as the start function
|
||||
// We also initialize bss segments (using memory.fill) as part of this
|
||||
// function.
|
||||
if (any_passive_inits) {
|
||||
if (wasm.any_passive_inits) {
|
||||
wasm.functions.putAssumeCapacity(.__wasm_init_memory, {});
|
||||
}
|
||||
|
||||
@ -349,7 +348,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
|
||||
if (category != .zero) try f.data_segment_groups.append(gpa, @intCast(memory_ptr));
|
||||
}
|
||||
|
||||
if (shared_memory and any_passive_inits) {
|
||||
if (shared_memory and wasm.any_passive_inits) {
|
||||
memory_ptr = pointer_alignment.forward(memory_ptr);
|
||||
virtual_addrs.init_memory_flag = @intCast(memory_ptr);
|
||||
memory_ptr += 4;
|
||||
@ -774,6 +773,8 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
|
||||
const code_start = binary_bytes.items.len;
|
||||
append: {
|
||||
const code = switch (segment_id.unpack(wasm)) {
|
||||
.__heap_base => @panic("TODO"),
|
||||
.__heap_end => @panic("TODO"),
|
||||
.__zig_error_names => {
|
||||
try binary_bytes.appendSlice(gpa, wasm.error_name_bytes.items);
|
||||
break :append;
|
||||
|
||||
@ -26,14 +26,16 @@ start_function: Wasm.OptionalObjectFunctionIndex,
|
||||
/// (or therefore missing) and must generate an error when another object uses
|
||||
/// features that are not supported by the other.
|
||||
features: Wasm.Feature.Set,
|
||||
/// Points into Wasm object_functions
|
||||
/// Points into `Wasm.object_functions`
|
||||
functions: RelativeSlice,
|
||||
/// Points into Wasm object_function_imports
|
||||
/// Points into `Wasm.object_function_imports`
|
||||
function_imports: RelativeSlice,
|
||||
/// Points into Wasm object_global_imports
|
||||
/// Points into `Wasm.object_global_imports`
|
||||
global_imports: RelativeSlice,
|
||||
/// Points into Wasm object_table_imports
|
||||
/// Points into `Wasm.object_table_imports`
|
||||
table_imports: RelativeSlice,
|
||||
// Points into `Wasm.object_data_imports`
|
||||
data_imports: RelativeSlice,
|
||||
/// Points into Wasm object_custom_segments
|
||||
custom_segments: RelativeSlice,
|
||||
/// Points into Wasm object_init_funcs
|
||||
@ -280,6 +282,7 @@ pub fn parse(
|
||||
const function_imports_start: u32 = @intCast(wasm.object_function_imports.entries.len);
|
||||
const global_imports_start: u32 = @intCast(wasm.object_global_imports.entries.len);
|
||||
const table_imports_start: u32 = @intCast(wasm.object_table_imports.entries.len);
|
||||
const data_imports_start: u32 = @intCast(wasm.object_data_imports.entries.len);
|
||||
const local_section_index_base = wasm.object_total_sections;
|
||||
const object_index: Wasm.ObjectIndex = @enumFromInt(wasm.objects.items.len);
|
||||
const source_location: Wasm.SourceLocation = .fromObject(object_index, wasm);
|
||||
@ -1087,6 +1090,19 @@ pub fn parse(
|
||||
gop.value_ptr.flags.ref_type = .from(ptr.ref_type);
|
||||
}
|
||||
},
|
||||
.data_import => {
|
||||
const name = symbol.name.unwrap().?;
|
||||
if (symbol.flags.binding == .local) {
|
||||
diags.addParseError(path, "local symbol '{s}' references import", .{name.slice(wasm)});
|
||||
continue;
|
||||
}
|
||||
const gop = try wasm.object_data_imports.getOrPut(gpa, name);
|
||||
if (!gop.found_existing) gop.value_ptr.* = .{
|
||||
.flags = symbol.flags,
|
||||
.source_location = source_location,
|
||||
.resolution = .unresolved,
|
||||
};
|
||||
},
|
||||
.function => |index| {
|
||||
assert(!symbol.flags.undefined);
|
||||
const ptr = index.ptr(wasm);
|
||||
@ -1134,12 +1150,13 @@ pub fn parse(
|
||||
}
|
||||
},
|
||||
.global => |index| {
|
||||
assert(!symbol.flags.undefined);
|
||||
const ptr = index.ptr(wasm);
|
||||
ptr.name = symbol.name;
|
||||
ptr.flags = symbol.flags;
|
||||
if (symbol.flags.binding == .local) continue; // No participation in symbol resolution.
|
||||
const new_ty = ptr.type();
|
||||
const name = symbol.name.unwrap().?;
|
||||
const new_ty = ptr.type();
|
||||
const gop = try wasm.object_global_imports.getOrPut(gpa, name);
|
||||
if (gop.found_existing) {
|
||||
const existing_ty = gop.value_ptr.type();
|
||||
@ -1192,12 +1209,42 @@ pub fn parse(
|
||||
}
|
||||
},
|
||||
.table => |i| {
|
||||
assert(!symbol.flags.undefined);
|
||||
const ptr = i.ptr(wasm);
|
||||
ptr.name = symbol.name;
|
||||
ptr.flags = symbol.flags;
|
||||
if (symbol.flags.undefined and symbol.flags.binding == .local) {
|
||||
const name = ptr.name.slice(wasm).?;
|
||||
diags.addParseError(path, "local symbol '{s}' references import", .{name});
|
||||
},
|
||||
.data => |index| {
|
||||
assert(!symbol.flags.undefined);
|
||||
const ptr = index.ptr(wasm);
|
||||
const name = ptr.name;
|
||||
assert(name.toOptional() == symbol.name);
|
||||
ptr.flags = symbol.flags;
|
||||
if (symbol.flags.binding == .local) continue; // No participation in symbol resolution.
|
||||
const gop = try wasm.object_data_imports.getOrPut(gpa, name);
|
||||
if (gop.found_existing) {
|
||||
if (gop.value_ptr.resolution == .unresolved or gop.value_ptr.flags.binding == .weak) {
|
||||
// Intentional: if they're both weak, take the last one.
|
||||
gop.value_ptr.source_location = source_location;
|
||||
gop.value_ptr.resolution = .fromObjectDataIndex(wasm, index);
|
||||
gop.value_ptr.flags = symbol.flags;
|
||||
continue;
|
||||
}
|
||||
if (ptr.flags.binding == .weak) {
|
||||
// Keep the existing one.
|
||||
continue;
|
||||
}
|
||||
var err = try diags.addErrorWithNotes(2);
|
||||
try err.addMsg("symbol collision: {s}", .{name.slice(wasm)});
|
||||
gop.value_ptr.source_location.addNote(&err, "exported here", .{});
|
||||
source_location.addNote(&err, "exported here", .{});
|
||||
continue;
|
||||
} else {
|
||||
gop.value_ptr.* = .{
|
||||
.flags = symbol.flags,
|
||||
.source_location = source_location,
|
||||
.resolution = .unresolved,
|
||||
};
|
||||
}
|
||||
},
|
||||
.section => |i| {
|
||||
@ -1210,13 +1257,6 @@ pub fn parse(
|
||||
diags.addParseError(path, "local symbol '{s}' references import", .{name});
|
||||
}
|
||||
},
|
||||
.data_import => {
|
||||
if (symbol.flags.undefined and symbol.flags.binding == .local) {
|
||||
const name = symbol.name.slice(wasm).?;
|
||||
diags.addParseError(path, "local symbol '{s}' references import", .{name});
|
||||
}
|
||||
},
|
||||
.data => continue, // `wasm.object_datas` has already been populated.
|
||||
};
|
||||
|
||||
// Apply export section info. This is done after the symbol table above so
|
||||
@ -1317,6 +1357,10 @@ pub fn parse(
|
||||
.off = table_imports_start,
|
||||
.len = @intCast(wasm.object_table_imports.entries.len - table_imports_start),
|
||||
},
|
||||
.data_imports = .{
|
||||
.off = data_imports_start,
|
||||
.len = @intCast(wasm.object_data_imports.entries.len - data_imports_start),
|
||||
},
|
||||
.init_funcs = .{
|
||||
.off = init_funcs_start,
|
||||
.len = @intCast(wasm.object_init_funcs.items.len - init_funcs_start),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user