wasm linker: flush implemented up to the export section

This commit is contained in:
Andrew Kelley 2024-12-11 22:18:53 -08:00
parent e21a42723b
commit bf88059591
3 changed files with 320 additions and 192 deletions

View File

@ -404,7 +404,7 @@ pub const SymbolFlags = packed struct(u32) {
/// Zig-specific. Data segments only.
alignment: Alignment = .none,
/// Zig-specific. Globals only.
global_type: Global.Type = .zero,
global_type: GlobalType4 = .zero,
/// Zig-specific. Tables only.
limits_has_max: bool = false,
/// Zig-specific. Tables only.
@ -481,6 +481,48 @@ pub const SymbolFlags = packed struct(u32) {
}
};
pub const GlobalType4 = packed struct(u4) {
valtype: Valtype3,
mutable: bool,
pub const zero: GlobalType4 = @bitCast(@as(u4, 0));
pub fn to(gt: GlobalType4) Global.Type {
return .{
.valtype = gt.valtype.to(),
.mutable = gt.mutable,
};
}
};
pub const Valtype3 = enum(u3) {
i32,
i64,
f32,
f64,
v128,
pub fn from(v: std.wasm.Valtype) Valtype3 {
return switch (v) {
.i32 => .i32,
.i64 => .i64,
.f32 => .f32,
.f64 => .f64,
.v128 => .v128,
};
}
pub fn to(v: Valtype3) std.wasm.Valtype {
return switch (v) {
.i32 => .i32,
.i64 => .i64,
.f32 => .f32,
.f64 => .f64,
.v128 => .v128,
};
}
};
pub const NavObj = extern struct {
code: DataSegment.Payload,
/// Empty if not emitting an object.
@ -591,7 +633,7 @@ pub const FunctionImport = extern struct {
__zig_error_names,
object_function: ObjectFunctionIndex,
nav_exe: NavExe.Index,
nav_obj: NavExe.Index,
nav_obj: NavObj.Index,
};
pub fn unpack(r: Resolution, wasm: *const Wasm) Unpacked {
@ -649,6 +691,20 @@ pub const FunctionImport = extern struct {
else => false,
};
}
pub fn typeIndex(r: Resolution, wasm: *const Wasm) FunctionType.Index {
return switch (unpack(r, wasm)) {
.unresolved => unreachable,
.__wasm_apply_global_tls_relocs => @panic("TODO"),
.__wasm_call_ctors => @panic("TODO"),
.__wasm_init_memory => @panic("TODO"),
.__wasm_init_tls => @panic("TODO"),
.__zig_error_names => @panic("TODO"),
.object_function => |i| i.ptr(wasm).type_index,
.nav_exe => @panic("TODO"),
.nav_obj => @panic("TODO"),
};
}
};
/// Index into `object_function_imports`.
@ -731,11 +787,13 @@ pub const GlobalImport = extern struct {
pub fn unpack(r: Resolution, wasm: *const Wasm) Unpacked {
return switch (r) {
.unresolved => .unresolved,
.__wasm_apply_global_tls_relocs => .__wasm_apply_global_tls_relocs,
.__wasm_call_ctors => .__wasm_call_ctors,
.__wasm_init_memory => .__wasm_init_memory,
.__wasm_init_tls => .__wasm_init_tls,
.__zig_error_names => .__zig_error_names,
.__heap_base => .__heap_base,
.__heap_end => .__heap_end,
.__stack_pointer => .__stack_pointer,
.__tls_align => .__tls_align,
.__tls_base => .__tls_base,
.__tls_size => .__tls_size,
.__zig_error_name_table => .__zig_error_name_table,
_ => {
const i: u32 = @intFromEnum(r);
const object_global_index = i - first_object_global;
@ -780,12 +838,28 @@ pub const GlobalImport = extern struct {
}
};
/// Index into `object_global_imports`.
/// Index into `Wasm.object_global_imports`.
pub const Index = enum(u32) {
_,
pub fn ptr(index: Index, wasm: *const Wasm) *GlobalImport {
return &wasm.object_global_imports.items[@intFromEnum(index)];
pub fn key(index: Index, wasm: *const Wasm) *String {
return &wasm.object_global_imports.keys()[@intFromEnum(index)];
}
pub fn value(index: Index, wasm: *const Wasm) *GlobalImport {
return &wasm.object_global_imports.values()[@intFromEnum(index)];
}
pub fn name(index: Index, wasm: *const Wasm) String {
return index.key(wasm).*;
}
pub fn moduleName(index: Index, wasm: *const Wasm) String {
return index.value(wasm).module_name;
}
pub fn globalType(index: Index, wasm: *const Wasm) Global.Type {
return value(index, wasm).flags.global_type.to();
}
};
};
@ -796,39 +870,9 @@ pub const Global = extern struct {
flags: SymbolFlags,
expr: Expr,
pub const Type = packed struct(u4) {
valtype: Valtype,
pub const Type = struct {
valtype: std.wasm.Valtype,
mutable: bool,
pub const zero: Type = @bitCast(@as(u4, 0));
};
pub const Valtype = enum(u3) {
i32,
i64,
f32,
f64,
v128,
pub fn from(v: std.wasm.Valtype) Valtype {
return switch (v) {
.i32 => .i32,
.i64 => .i64,
.f32 => .f32,
.f64 => .f64,
.v128 => .v128,
};
}
pub fn to(v: Valtype) std.wasm.Valtype {
return switch (v) {
.i32 => .i32,
.i64 => .i64,
.f32 => .f32,
.f64 => .f64,
.v128 => .v128,
};
}
};
};
@ -865,6 +909,38 @@ pub const TableImport = extern struct {
__indirect_function_table,
// Next, index into `object_tables`.
_,
const first_object_table = @intFromEnum(Resolution.__indirect_function_table) + 1;
pub const Unpacked = union(enum) {
unresolved,
__indirect_function_table,
object_table: ObjectTableIndex,
};
pub fn unpack(r: Resolution) Unpacked {
return switch (r) {
.unresolved => .unresolved,
.__indirect_function_table => .__indirect_function_table,
_ => .{ .object_table = @enumFromInt(@intFromEnum(r) - first_object_table) },
};
}
pub fn refType(r: Resolution, wasm: *const Wasm) std.wasm.RefType {
return switch (unpack(r)) {
.unresolved => unreachable,
.__indirect_function_table => @panic("TODO"),
.object_table => |i| i.ptr(wasm).flags.ref_type.to(),
};
}
pub fn limits(r: Resolution, wasm: *const Wasm) std.wasm.Limits {
return switch (unpack(r)) {
.unresolved => unreachable,
.__indirect_function_table => @panic("TODO"),
.object_table => |i| i.ptr(wasm).limits(),
};
}
};
/// Index into `object_table_imports`.
@ -878,7 +954,26 @@ pub const TableImport = extern struct {
pub fn value(index: Index, wasm: *const Wasm) *TableImport {
return &wasm.object_table_imports.values()[@intFromEnum(index)];
}
pub fn name(index: Index, wasm: *const Wasm) String {
return index.key(wasm).*;
}
pub fn moduleName(index: Index, wasm: *const Wasm) String {
return index.value(wasm).module_name;
}
};
pub fn limits(ti: *const TableImport) std.wasm.Limits {
return .{
.flags = .{
.has_max = ti.flags.limits_has_max,
.is_shared = ti.flags.limits_is_shared,
},
.min = ti.limits_min,
.max = ti.limits_max,
};
}
};
pub const Table = extern struct {
@ -887,6 +982,17 @@ pub const Table = extern struct {
flags: SymbolFlags,
limits_min: u32,
limits_max: u32,
pub fn limits(t: *const Table) std.wasm.Limits {
return .{
.flags = .{
.has_max = t.flags.limits_has_max,
.is_shared = t.flags.limits_is_shared,
},
.min = t.limits_min,
.max = t.limits_max,
};
}
};
/// Uniquely identifies a section across all objects. By subtracting
@ -910,9 +1016,13 @@ pub const GlobalImportIndex = enum(u32) {
_,
};
/// Index into `object_globals`.
/// Index into `Wasm.object_globals`.
pub const ObjectGlobalIndex = enum(u32) {
_,
pub fn ptr(index: ObjectGlobalIndex, wasm: *const Wasm) *Global {
return &wasm.object_globals.items[@intFromEnum(index)];
}
};
/// Index into `Wasm.object_memories`.
@ -992,6 +1102,16 @@ pub const CustomSegment = extern struct {
/// An index into string_bytes where a wasm expression is found.
pub const Expr = enum(u32) {
_,
pub const end = @intFromEnum(std.wasm.Opcode.end);
pub fn slice(index: Expr, wasm: *const Wasm) [:end]const u8 {
const start_slice = wasm.string_bytes.items[@intFromEnum(index)..];
const end_pos = Object.exprEndPos(start_slice, 0) catch |err| switch (err) {
error.InvalidInitOpcode => unreachable,
};
return start_slice[0..end_pos :end];
}
};
pub const FunctionType = extern struct {
@ -1162,6 +1282,12 @@ pub const ZcuImportIndex = enum(u32) {
const fn_info = zcu.typeToFunc(.fromInterned(ext.ty)).?;
return getExistingFunctionType(wasm, fn_info.cc, fn_info.param_types.get(ip), .fromInterned(fn_info.return_type), target).?;
}
pub fn globalType(index: ZcuImportIndex, wasm: *const Wasm) Global.Type {
_ = index;
_ = wasm;
unreachable; // Zig has no way to create Wasm globals yet.
}
};
/// 0. Index into `object_function_imports`.
@ -1274,6 +1400,24 @@ pub const GlobalImportId = enum(u32) {
.zcu_import => return .zig_object_nofile, // TODO give a better source location
}
}
pub fn name(id: GlobalImportId, wasm: *const Wasm) String {
return switch (unpack(id, wasm)) {
inline .object_global_import, .zcu_import => |i| i.name(wasm),
};
}
pub fn moduleName(id: GlobalImportId, wasm: *const Wasm) String {
return switch (unpack(id, wasm)) {
inline .object_global_import, .zcu_import => |i| i.moduleName(wasm),
};
}
pub fn globalType(id: GlobalImportId, wasm: *Wasm) Global.Type {
return switch (unpack(id, wasm)) {
inline .object_global_import, .zcu_import => |i| i.globalType(wasm),
};
}
};
/// Index into `Wasm.symbol_table`.
@ -1384,6 +1528,17 @@ pub const MemoryImport = extern struct {
limits_has_max: bool,
limits_is_shared: bool,
padding: [2]u8 = .{ 0, 0 },
pub fn limits(mi: *const MemoryImport) std.wasm.Limits {
return .{
.flags = .{
.has_max = mi.limits_has_max,
.is_shared = mi.limits_is_shared,
},
.min = mi.limits_min,
.max = mi.limits_max,
};
}
};
pub const Alignment = InternPool.Alignment;

View File

@ -335,8 +335,6 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {
log.debug("maximum memory pages: {?d}", .{wasm.memories.limits.max});
}
// Size of each section header
const header_size = 5 + 1;
var section_index: u32 = 0;
// Index of the code section. Used to tell relocation table where the section lives.
var code_section_index: ?u32 = null;
@ -369,54 +367,50 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {
}
}
try writeVecSectionHeader(
binary_bytes.items,
header_offset,
.type,
@intCast(binary_bytes.items.len - header_offset - header_size),
@intCast(wasm.func_types.entries.len),
);
replaceVecSectionHeader(binary_bytes, header_offset, .type, @intCast(wasm.func_types.entries.len));
section_index += 1;
}
// Import section
const total_imports_len = wasm.function_imports.entries.len + wasm.global_imports.entries.len +
wasm.table_imports.entries.len + wasm.object_memory_imports.items.len + @intFromBool(import_memory);
if (total_imports_len > 0) {
{
var total_imports: usize = 0;
const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
for (wasm.function_imports.values()) |*function_import| {
const module_name = function_import.moduleName(wasm).slice(wasm);
for (wasm.function_imports.values()) |id| {
const module_name = id.moduleName(wasm).slice(wasm);
try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len)));
try binary_writer.writeAll(module_name);
const name = function_import.name(wasm).slice(wasm);
const name = id.name(wasm).slice(wasm);
try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len)));
try binary_writer.writeAll(name);
try binary_writer.writeByte(@intFromEnum(std.wasm.ExternalKind.function));
try leb.writeUleb128(binary_writer, @intFromEnum(function_import.functionType(wasm)));
try leb.writeUleb128(binary_writer, @intFromEnum(id.functionType(wasm)));
}
total_imports += wasm.function_imports.entries.len;
for (wasm.table_imports.values()) |*table_import| {
const module_name = table_import.moduleName(wasm).slice(wasm);
for (wasm.table_imports.values()) |id| {
const table_import = id.value(wasm);
const module_name = table_import.module_name.slice(wasm);
try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len)));
try binary_writer.writeAll(module_name);
const name = table_import.name(wasm).slice(wasm);
const name = id.key(wasm).slice(wasm);
try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len)));
try binary_writer.writeAll(name);
try binary_writer.writeByte(@intFromEnum(std.wasm.ExternalKind.table));
try leb.writeUleb128(binary_writer, std.wasm.reftype(table_import.reftype));
try emitLimits(binary_writer, table_import.limits);
try leb.writeUleb128(binary_writer, @intFromEnum(@as(std.wasm.RefType, table_import.flags.ref_type.to())));
try emitLimits(gpa, binary_bytes, table_import.limits());
}
total_imports += wasm.table_imports.entries.len;
for (wasm.object_memory_imports.items) |*memory_import| {
try emitMemoryImport(wasm, binary_writer, memory_import);
try emitMemoryImport(wasm, binary_bytes, memory_import);
total_imports += 1;
} else if (import_memory) {
try emitMemoryImport(wasm, binary_writer, &.{
try emitMemoryImport(wasm, binary_bytes, &.{
.module_name = wasm.host_name,
.name = if (is_obj) wasm.preloaded_strings.__linear_memory else wasm.preloaded_strings.memory,
.limits_min = wasm.memories.limits.min,
@ -424,100 +418,87 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {
.limits_has_max = wasm.memories.limits.flags.has_max,
.limits_is_shared = wasm.memories.limits.flags.is_shared,
});
total_imports += 1;
}
for (wasm.global_imports.values()) |*global_import| {
const module_name = global_import.module_name.slice(wasm);
for (wasm.global_imports.values()) |id| {
const module_name = id.moduleName(wasm).slice(wasm);
try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len)));
try binary_writer.writeAll(module_name);
const name = global_import.name.slice(wasm);
const name = id.name(wasm).slice(wasm);
try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len)));
try binary_writer.writeAll(name);
try binary_writer.writeByte(@intFromEnum(std.wasm.ExternalKind.global));
try leb.writeUleb128(binary_writer, @intFromEnum(global_import.valtype));
try binary_writer.writeByte(@intFromBool(global_import.mutable));
const global_type = id.globalType(wasm);
try leb.writeUleb128(binary_writer, @intFromEnum(@as(std.wasm.Valtype, global_type.valtype)));
try binary_writer.writeByte(@intFromBool(global_type.mutable));
}
total_imports += wasm.global_imports.entries.len;
try writeVecSectionHeader(
binary_bytes.items,
header_offset,
.import,
@intCast(binary_bytes.items.len - header_offset - header_size),
@intCast(total_imports_len),
);
replaceVecSectionHeader(binary_bytes, header_offset, .import, @intCast(total_imports));
section_index += 1;
}
// Function section
if (wasm.functions.count() != 0) {
const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
for (wasm.functions.values()) |function| {
try leb.writeUleb128(binary_writer, function.func.type_index);
for (wasm.functions.keys()) |function| {
try leb.writeUleb128(binary_writer, @intFromEnum(function.typeIndex(wasm)));
}
try writeVecSectionHeader(
binary_bytes.items,
header_offset,
.function,
@intCast(binary_bytes.items.len - header_offset - header_size),
@intCast(wasm.functions.count()),
);
replaceVecSectionHeader(binary_bytes, header_offset, .function, @intCast(wasm.functions.count()));
section_index += 1;
}
// Table section
if (wasm.tables.items.len > 0) {
if (wasm.tables.entries.len > 0) {
const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
for (wasm.tables.items) |table| {
try leb.writeUleb128(binary_writer, std.wasm.reftype(table.reftype));
try emitLimits(binary_writer, table.limits);
for (wasm.tables.keys()) |table| {
try leb.writeUleb128(binary_writer, @intFromEnum(@as(std.wasm.RefType, table.refType(wasm))));
try emitLimits(gpa, binary_bytes, table.limits(wasm));
}
try writeVecSectionHeader(
binary_bytes.items,
header_offset,
.table,
@intCast(binary_bytes.items.len - header_offset - header_size),
@intCast(wasm.tables.items.len),
);
replaceVecSectionHeader(binary_bytes, header_offset, .table, @intCast(wasm.tables.entries.len));
section_index += 1;
}
// Memory section
// Memory section. wasm currently only supports 1 linear memory segment.
if (!import_memory) {
const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
try emitLimits(binary_writer, wasm.memories.limits);
try writeVecSectionHeader(
binary_bytes.items,
header_offset,
.memory,
@intCast(binary_bytes.items.len - header_offset - header_size),
1, // wasm currently only supports 1 linear memory segment
);
try emitLimits(gpa, binary_bytes, wasm.memories.limits);
replaceVecSectionHeader(binary_bytes, header_offset, .memory, 1);
section_index += 1;
}
// Global section (used to emit stack pointer)
if (wasm.output_globals.items.len > 0) {
if (wasm.globals.entries.len > 0) {
const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
for (wasm.output_globals.items) |global| {
try binary_writer.writeByte(@intFromEnum(global.global_type.valtype));
try binary_writer.writeByte(@intFromBool(global.global_type.mutable));
try emitInit(binary_writer, global.init);
for (wasm.globals.keys()) |global_resolution| {
switch (global_resolution.unpack(wasm)) {
.unresolved => unreachable,
.__heap_base => @panic("TODO"),
.__heap_end => @panic("TODO"),
.__stack_pointer => @panic("TODO"),
.__tls_align => @panic("TODO"),
.__tls_base => @panic("TODO"),
.__tls_size => @panic("TODO"),
.__zig_error_name_table => @panic("TODO"),
.object_global => |i| {
const global = i.ptr(wasm);
try binary_writer.writeByte(@intFromEnum(@as(std.wasm.Valtype, global.flags.global_type.valtype.to())));
try binary_writer.writeByte(@intFromBool(global.flags.global_type.mutable));
try emitExpr(wasm, binary_bytes, global.expr);
},
.nav_exe => @panic("TODO"),
.nav_obj => @panic("TODO"),
}
}
try writeVecSectionHeader(
binary_bytes.items,
header_offset,
.global,
@intCast(binary_bytes.items.len - header_offset - header_size),
@intCast(wasm.output_globals.items.len),
);
replaceVecSectionHeader(binary_bytes, header_offset, .global, @intCast(wasm.globals.entries.len));
section_index += 1;
}
@ -540,25 +521,14 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {
try leb.writeUleb128(binary_writer, @as(u32, 0));
}
try writeVecSectionHeader(
binary_bytes.items,
header_offset,
.@"export",
@intCast(binary_bytes.items.len - header_offset - header_size),
@intCast(wasm.exports.items.len + @intFromBool(export_memory)),
);
const n_items: u32 = @intCast(wasm.exports.items.len + @intFromBool(export_memory));
replaceVecSectionHeader(binary_bytes, header_offset, .@"export", n_items);
section_index += 1;
}
if (wasm.entry) |entry_index| {
if (Wasm.FunctionIndex.fromResolution(wasm.entry_resolution)) |entry_index| {
const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
try writeVecSectionHeader(
binary_bytes.items,
header_offset,
.start,
@intCast(binary_bytes.items.len - header_offset - header_size),
entry_index,
);
replaceVecSectionHeader(binary_bytes, header_offset, .start, @intFromEnum(entry_index));
}
// element section (function table)
@ -586,26 +556,14 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {
try leb.writeUleb128(binary_writer, sym.index);
}
try writeVecSectionHeader(
binary_bytes.items,
header_offset,
.element,
@intCast(binary_bytes.items.len - header_offset - header_size),
1,
);
replaceVecSectionHeader(binary_bytes, header_offset, .element, 1);
section_index += 1;
}
// When the shared-memory option is enabled, we *must* emit the 'data count' section.
if (f.data_segment_groups.items.len > 0 and shared_memory) {
const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
try writeVecSectionHeader(
binary_bytes.items,
header_offset,
.data_count,
@intCast(binary_bytes.items.len - header_offset - header_size),
@intCast(f.data_segment_groups.items.len),
);
replaceVecSectionHeader(binary_bytes, header_offset, .data_count, @intCast(f.data_segment_groups.items.len));
}
// Code section.
@ -636,13 +594,7 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {
},
};
try writeVecSectionHeader(
binary_bytes.items,
header_offset,
.code,
@intCast(binary_bytes.items.len - header_offset - header_size),
@intCast(wasm.functions.count()),
);
replaceVecSectionHeader(binary_bytes, header_offset, .code, @intCast(wasm.functions.entries.len));
code_section_index = section_index;
section_index += 1;
}
@ -682,13 +634,7 @@ pub fn finish(f: *Flush, wasm: *Wasm, arena: Allocator) !void {
}
assert(group_index == f.data_segment_groups.items.len);
try writeVecSectionHeader(
binary_bytes.items,
header_offset,
.data,
@intCast(binary_bytes.items.len - header_offset - header_size),
group_index,
);
replaceVecSectionHeader(binary_bytes, header_offset, .data, group_index);
data_section_index = section_index;
section_index += 1;
}
@ -768,7 +714,7 @@ fn emitNameSection(wasm: *Wasm, binary_bytes: *std.ArrayListUnmanaged(u8), arena
};
var globals: std.MultiArrayList(NamedIndex) = .empty;
try globals.ensureTotalCapacityPrecise(arena, wasm.output_globals.items.len + wasm.global_imports.items.len);
try globals.ensureTotalCapacityPrecise(arena, wasm.globals.items.len + wasm.global_imports.items.len);
var segments: std.MultiArrayList(NamedIndex) = .empty;
try segments.ensureTotalCapacityPrecise(arena, wasm.data_segments.count());
@ -844,10 +790,8 @@ fn writeCustomSectionHeader(buffer: []u8, offset: u32, size: u32) !void {
}
fn reserveCustomSectionHeader(gpa: Allocator, bytes: *std.ArrayListUnmanaged(u8)) Allocator.Error!u32 {
// unlike regular section, we don't emit the count
const header_size = 1 + 5;
try bytes.appendNTimes(gpa, 0, header_size);
return @intCast(bytes.items.len - header_size);
try bytes.appendNTimes(gpa, 0, section_header_size);
return @intCast(bytes.items.len - section_header_size);
}
fn emitNameSubsection(
@ -1106,38 +1050,57 @@ fn wantSegmentMerge(wasm: *const Wasm, a_index: Wasm.DataSegment.Index, b_index:
return a_prefix.len > 0 and mem.eql(u8, a_prefix, b_prefix);
}
/// section id + fixed leb contents size + fixed leb vector length
const section_header_reserve_size = 1 + 5 + 5;
const section_header_size = 5 + 1;
fn reserveVecSectionHeader(gpa: Allocator, bytes: *std.ArrayListUnmanaged(u8)) Allocator.Error!u32 {
// section id + fixed leb contents size + fixed leb vector length
const header_size = 1 + 5 + 5;
try bytes.appendNTimes(gpa, 0, header_size);
return @intCast(bytes.items.len - header_size);
try bytes.appendNTimes(gpa, 0, section_header_reserve_size);
return @intCast(bytes.items.len - section_header_reserve_size);
}
fn writeVecSectionHeader(buffer: []u8, offset: u32, section: std.wasm.Section, size: u32, items: u32) !void {
var buf: [1 + 5 + 5]u8 = undefined;
buf[0] = @intFromEnum(section);
leb.writeUnsignedFixed(5, buf[1..6], size);
leb.writeUnsignedFixed(5, buf[6..], items);
buffer[offset..][0..buf.len].* = buf;
fn replaceVecSectionHeader(
bytes: *std.ArrayListUnmanaged(u8),
offset: u32,
section: std.wasm.Section,
n_items: u32,
) void {
const size: u32 = @intCast(bytes.items.len - offset - section_header_size);
var buf: [section_header_reserve_size]u8 = undefined;
var fbw = std.io.fixedBufferStream(&buf);
const w = fbw.writer();
w.writeByte(@intFromEnum(section)) catch unreachable;
leb.writeUleb128(w, size) catch unreachable;
leb.writeUleb128(w, n_items) catch unreachable;
bytes.replaceRangeAssumeCapacity(offset, section_header_reserve_size, fbw.getWritten());
}
fn emitLimits(writer: anytype, limits: std.wasm.Limits) !void {
try writer.writeByte(limits.flags);
try leb.writeUleb128(writer, limits.min);
if (limits.flags.has_max) try leb.writeUleb128(writer, limits.max);
fn emitLimits(
gpa: Allocator,
binary_bytes: *std.ArrayListUnmanaged(u8),
limits: std.wasm.Limits,
) Allocator.Error!void {
try binary_bytes.append(gpa, @bitCast(limits.flags));
try leb.writeUleb128(binary_bytes.writer(gpa), limits.min);
if (limits.flags.has_max) try leb.writeUleb128(binary_bytes.writer(gpa), limits.max);
}
fn emitMemoryImport(wasm: *Wasm, writer: anytype, memory_import: *const Wasm.MemoryImport) Allocator.Error!void {
fn emitMemoryImport(
wasm: *Wasm,
binary_bytes: *std.ArrayListUnmanaged(u8),
memory_import: *const Wasm.MemoryImport,
) Allocator.Error!void {
const gpa = wasm.base.comp.gpa;
const module_name = memory_import.module_name.slice(wasm);
try leb.writeUleb128(writer, @as(u32, @intCast(module_name.len)));
try writer.writeAll(module_name);
try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(module_name.len)));
try binary_bytes.appendSlice(gpa, module_name);
const name = memory_import.name.slice(wasm);
try leb.writeUleb128(writer, @as(u32, @intCast(name.len)));
try writer.writeAll(name);
try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len)));
try binary_bytes.appendSlice(gpa, name);
try writer.writeByte(@intFromEnum(std.wasm.ExternalKind.memory));
try emitLimits(writer, memory_import.limits());
try binary_bytes.append(gpa, @intFromEnum(std.wasm.ExternalKind.memory));
try emitLimits(gpa, binary_bytes, memory_import.limits());
}
pub fn emitInit(writer: anytype, init_expr: std.wasm.InitExpression) !void {
@ -1166,6 +1129,12 @@ pub fn emitInit(writer: anytype, init_expr: std.wasm.InitExpression) !void {
try writer.writeByte(@intFromEnum(std.wasm.Opcode.end));
}
pub fn emitExpr(wasm: *const Wasm, binary_bytes: *std.ArrayListUnmanaged(u8), expr: Wasm.Expr) Allocator.Error!void {
const gpa = wasm.base.comp.gpa;
const slice = expr.slice(wasm);
try binary_bytes.appendSlice(gpa, slice[0 .. slice.len + 1]); // +1 to include end opcode
}
//fn emitLinkSection(
// wasm: *Wasm,
// binary_bytes: *std.ArrayListUnmanaged(u8),

View File

@ -1040,9 +1040,9 @@ fn readInit(wasm: *Wasm, bytes: []const u8, pos: usize) !struct { Wasm.Expr, usi
return .{ try wasm.addExpr(bytes[pos..end_pos]), end_pos };
}
fn skipInit(bytes: []const u8, pos: usize) !usize {
pub fn exprEndPos(bytes: []const u8, pos: usize) error{InvalidInitOpcode}!usize {
const opcode = bytes[pos];
const end_pos = switch (@as(std.wasm.Opcode, @enumFromInt(opcode))) {
return switch (@as(std.wasm.Opcode, @enumFromInt(opcode))) {
.i32_const => readLeb(i32, bytes, pos + 1)[1],
.i64_const => readLeb(i64, bytes, pos + 1)[1],
.f32_const => pos + 5,
@ -1050,6 +1050,10 @@ fn skipInit(bytes: []const u8, pos: usize) !usize {
.global_get => readLeb(u32, bytes, pos + 1)[1],
else => return error.InvalidInitOpcode,
};
}
fn skipInit(bytes: []const u8, pos: usize) !usize {
const end_pos = try exprEndPos(bytes, pos);
const op, const final_pos = readEnum(std.wasm.Opcode, bytes, end_pos);
if (op != .end) return error.InitExprMissingEnd;
return final_pos;