//! This file contains all constants and related to wasm's object format. const std = @import("std"); pub const Relocation = struct { /// Represents the type of the `Relocation` relocation_type: RelocationType, /// Offset of the value to rewrite relative to the relevant section's contents. /// When `offset` is zero, its position is immediately after the id and size of the section. offset: u32, /// The index of the symbol used. /// When the type is `R_WASM_TYPE_INDEX_LEB`, it represents the index of the type. index: u32, /// Addend to add to the address. /// This field is only non-zero for `R_WASM_MEMORY_ADDR_*`, `R_WASM_FUNCTION_OFFSET_I32` and `R_WASM_SECTION_OFFSET_I32`. addend: i32 = 0, /// All possible relocation types currently existing. /// This enum is exhaustive as the spec is WIP and new types /// can be added which means that a generated binary will be invalid, /// so instead we will show an error in such cases. pub const RelocationType = enum(u8) { R_WASM_FUNCTION_INDEX_LEB = 0, R_WASM_TABLE_INDEX_SLEB = 1, R_WASM_TABLE_INDEX_I32 = 2, R_WASM_MEMORY_ADDR_LEB = 3, R_WASM_MEMORY_ADDR_SLEB = 4, R_WASM_MEMORY_ADDR_I32 = 5, R_WASM_TYPE_INDEX_LEB = 6, R_WASM_GLOBAL_INDEX_LEB = 7, R_WASM_FUNCTION_OFFSET_I32 = 8, R_WASM_SECTION_OFFSET_I32 = 9, R_WASM_EVENT_INDEX_LEB = 10, R_WASM_GLOBAL_INDEX_I32 = 13, R_WASM_MEMORY_ADDR_LEB64 = 14, R_WASM_MEMORY_ADDR_SLEB64 = 15, R_WASM_MEMORY_ADDR_I64 = 16, R_WASM_TABLE_INDEX_SLEB64 = 18, R_WASM_TABLE_INDEX_I64 = 19, R_WASM_TABLE_NUMBER_LEB = 20, R_WASM_MEMORY_ADDR_TLS_SLEB = 21, R_WASM_MEMORY_ADDR_TLS_SLEB64 = 25, /// Returns true for relocation types where the `addend` field is present. pub fn addendIsPresent(self: RelocationType) bool { return switch (self) { .R_WASM_MEMORY_ADDR_LEB, .R_WASM_MEMORY_ADDR_SLEB, .R_WASM_MEMORY_ADDR_I32, .R_WASM_MEMORY_ADDR_LEB64, .R_WASM_MEMORY_ADDR_SLEB64, .R_WASM_MEMORY_ADDR_I64, .R_WASM_MEMORY_ADDR_TLS_SLEB, .R_WASM_MEMORY_ADDR_TLS_SLEB64, .R_WASM_FUNCTION_OFFSET_I32, .R_WASM_SECTION_OFFSET_I32, => true, else => false, }; } }; /// Verifies the relocation type of a given `Relocation` and returns /// true when the relocation references a function call or address to a function. pub fn isFunction(self: Relocation) bool { return switch (self.relocation_type) { .R_WASM_FUNCTION_INDEX_LEB, .R_WASM_TABLE_INDEX_SLEB, => true, else => false, }; } pub fn format(self: Relocation, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { _ = fmt; _ = options; try writer.print("{s} offset=0x{x:0>6} symbol={d}", .{ @tagName(self.relocation_type), self.offset, self.index, }); } }; /// Unlike the `Import` object defined by the wasm spec, and existing /// in the std.wasm namespace, this construct saves the 'module name' and 'name' /// of the import using offsets into a string table, rather than the slices itself. /// This saves us (potentially) 24 bytes per import on 64bit machines. pub const Import = struct { module_name: u32, name: u32, kind: std.wasm.Import.Kind, }; /// Unlike the `Export` object defined by the wasm spec, and existing /// in the std.wasm namespace, this construct saves the 'name' /// of the export using offsets into a string table, rather than the slice itself. /// This saves us (potentially) 12 bytes per export on 64bit machines. pub const Export = struct { name: u32, index: u32, kind: std.wasm.ExternalKind, }; pub const SubsectionType = enum(u8) { WASM_SEGMENT_INFO = 5, WASM_INIT_FUNCS = 6, WASM_COMDAT_INFO = 7, WASM_SYMBOL_TABLE = 8, }; pub const Alignment = @import("../../InternPool.zig").Alignment; pub const Segment = struct { /// Segment's name, encoded as UTF-8 bytes. name: []const u8, /// The required alignment of the segment, encoded as a power of 2 alignment: Alignment, /// Bitfield containing flags for a segment flags: u32, pub fn isTLS(segment: Segment) bool { return segment.flags & @intFromEnum(Flags.WASM_SEG_FLAG_TLS) != 0; } /// Returns the name as how it will be output into the final object /// file or binary. When `merge_segments` is true, this will return the /// short name. i.e. ".rodata". When false, it returns the entire name instead. pub fn outputName(segment: Segment, merge_segments: bool) []const u8 { if (segment.isTLS()) { return ".tdata"; } else if (!merge_segments) { return segment.name; } else if (std.mem.startsWith(u8, segment.name, ".rodata.")) { return ".rodata"; } else if (std.mem.startsWith(u8, segment.name, ".text.")) { return ".text"; } else if (std.mem.startsWith(u8, segment.name, ".data.")) { return ".data"; } else if (std.mem.startsWith(u8, segment.name, ".bss.")) { return ".bss"; } return segment.name; } pub const Flags = enum(u32) { WASM_SEG_FLAG_STRINGS = 0x1, WASM_SEG_FLAG_TLS = 0x2, }; }; pub const InitFunc = struct { /// Priority of the init function priority: u32, /// The symbol index of init function (not the function index). symbol_index: u32, }; pub const Comdat = struct { name: []const u8, /// Must be zero, no flags are currently defined by the tool-convention. flags: u32, symbols: []const ComdatSym, }; pub const ComdatSym = struct { kind: Type, /// Index of the data segment/function/global/event/table within a WASM module. /// The object must not be an import. index: u32, pub const Type = enum(u8) { WASM_COMDAT_DATA = 0, WASM_COMDAT_FUNCTION = 1, WASM_COMDAT_GLOBAL = 2, WASM_COMDAT_EVENT = 3, WASM_COMDAT_TABLE = 4, WASM_COMDAT_SECTION = 5, }; }; pub const Feature = struct { /// Provides information about the usage of the feature. /// - '0x2b' (+): Object uses this feature, and the link fails if feature is not in the allowed set. /// - '0x2d' (-): Object does not use this feature, and the link fails if this feature is in the allowed set. /// - '0x3d' (=): Object uses this feature, and the link fails if this feature is not in the allowed set, /// or if any object does not use this feature. prefix: Prefix, /// Type of the feature, must be unique in the sequence of features. tag: Tag, /// Unlike `std.Target.wasm.Feature` this also contains linker-features such as shared-mem pub const Tag = enum { atomics, bulk_memory, exception_handling, extended_const, multivalue, mutable_globals, nontrapping_fptoint, reference_types, relaxed_simd, sign_ext, simd128, tail_call, shared_mem, /// From a given cpu feature, returns its linker feature pub fn fromCpuFeature(feature: std.Target.wasm.Feature) Tag { return @as(Tag, @enumFromInt(@intFromEnum(feature))); } pub fn format(tag: Tag, comptime fmt: []const u8, opt: std.fmt.FormatOptions, writer: anytype) !void { _ = fmt; _ = opt; try writer.writeAll(switch (tag) { .atomics => "atomics", .bulk_memory => "bulk-memory", .exception_handling => "exception-handling", .extended_const => "extended-const", .multivalue => "multivalue", .mutable_globals => "mutable-globals", .nontrapping_fptoint => "nontrapping-fptoint", .reference_types => "reference-types", .relaxed_simd => "relaxed-simd", .sign_ext => "sign-ext", .simd128 => "simd128", .tail_call => "tail-call", .shared_mem => "shared-mem", }); } }; pub const Prefix = enum(u8) { used = '+', disallowed = '-', required = '=', }; pub fn format(feature: Feature, comptime fmt: []const u8, opt: std.fmt.FormatOptions, writer: anytype) !void { _ = opt; _ = fmt; try writer.print("{c} {}", .{ feature.prefix, feature.tag }); } }; pub const known_features = std.ComptimeStringMap(Feature.Tag, .{ .{ "atomics", .atomics }, .{ "bulk-memory", .bulk_memory }, .{ "exception-handling", .exception_handling }, .{ "extended-const", .extended_const }, .{ "multivalue", .multivalue }, .{ "mutable-globals", .mutable_globals }, .{ "nontrapping-fptoint", .nontrapping_fptoint }, .{ "reference-types", .reference_types }, .{ "relaxed-simd", .relaxed_simd }, .{ "sign-ext", .sign_ext }, .{ "simd128", .simd128 }, .{ "tail-call", .tail_call }, .{ "shared-mem", .shared_mem }, });