From 8cd5838112dfd95f40eb7b96af5b7dcd874110c3 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 13 Jul 2026 15:30:00 +0100 Subject: [PATCH] wasm: parse custom sections --- core/rexcode/ir/wasm/module.odin | 75 +++++++++++++++++++++ core/rexcode/ir/wasm/parse.odin | 112 +++++++++++++++++++++++++++++-- 2 files changed, 182 insertions(+), 5 deletions(-) diff --git a/core/rexcode/ir/wasm/module.odin b/core/rexcode/ir/wasm/module.odin index b45bf3a94..388e06aff 100644 --- a/core/rexcode/ir/wasm/module.odin +++ b/core/rexcode/ir/wasm/module.odin @@ -106,6 +106,8 @@ Module :: struct { exports: []Export, start: i64, // -1 if absent, else the start funcidx + customs: []Custom_Section, + // --- side tables parallel to the ir core arrays --- // A WASM function's declared locals (the code section's local groups), kept // parallel to base.functions since ir.Function has no locals slot. @@ -127,3 +129,76 @@ make_module :: proc "contextless" () -> Module { m.start = -1 return m } + + +// ----------------------------------------------------------------------------- +// Custom Section Layout +// ----------------------------------------------------------------------------- + +Custom_Section :: struct { + section: Section, + payload: []byte, // borrowed, m.data[section.offset:][:section.size] + variant: union { + Custom_Section_Name, + Custom_Section_Target_Features, + }, +} + +Custom_Section_Name_Function :: struct { + id: u32, + name: string, // borrowed +} + +Custom_Section_Name_Local :: struct { + idx: u32, + name: string, // borrowed +} + +Custom_Section_Name_Function_Locals :: struct { + func_idx: u32, + locals: []Custom_Section_Name_Local, +} + +Custom_Section_Name :: struct { + module_name: string, + functions: []Custom_Section_Name_Function, + locals: []Custom_Section_Name_Function_Locals, +} + +Custom_Section_Target_Feature_Prefix :: enum u8 { + Used = '+', + Disallowed = '-', + Required = '=', +} + +Custom_Section_Target_Feature :: struct { + prefix: Custom_Section_Target_Feature_Prefix, + feature: string, // borrowed +} + + +Custom_Section_Target_Features :: struct { + features: []Custom_Section_Target_Feature, +} + + +@(require_results) +section_name :: #force_inline proc "contextless" (id: Section_Id) -> string { + switch id { + case .CUSTOM: return "custom" + case .TYPE: return "type" + case .IMPORT: return "import" + case .FUNCTION: return "function" + case .TABLE: return "table" + case .MEMORY: return "memory" + case .GLOBAL: return "global" + case .EXPORT: return "export" + case .START: return "start" + case .ELEMENT: return "element" + case .CODE: return "code" + case .DATA: return "data" + case .DATA_COUNT: return "data.count" + } + return "unknown" +} + diff --git a/core/rexcode/ir/wasm/parse.odin b/core/rexcode/ir/wasm/parse.odin index bf73f9bc5..6f7a58d76 100644 --- a/core/rexcode/ir/wasm/parse.odin +++ b/core/rexcode/ir/wasm/parse.odin @@ -133,6 +133,8 @@ parse_container :: proc(data: []u8, m: ^Module, errors: ^[dynamic]Error, allocat } } + parse_custom_sections(m, allocator) or_return + // Relocations must be parsed before the bodies: the CODE group is threaded // into body decode so relocatable index fields become symbolic refs. m.relocations = parse_relocations(m^, allocator) or_return @@ -194,7 +196,7 @@ rd_u32le_block :: proc "contextless" (r: ^Reader) -> (u32, Parse_Error) { rd_uleb :: proc "contextless" (r: ^Reader) -> (u64, Parse_Error) { shift: uint = 0 value: u64 = 0 - for _ in 0 ..< 10 { + for _ in 0..<10 { if r.off >= u32(len(r.data)) { return 0, .TRUNCATED } b := r.data[r.off] r.off += 1 @@ -210,7 +212,7 @@ rd_sleb :: proc "contextless" (r: ^Reader) -> (i64, Parse_Error) { shift: uint = 0 value: i64 = 0 b: u8 = 0 - for _ in 0 ..< 10 { + for _ in 0..<10 { if r.off >= u32(len(r.data)) { return 0, .TRUNCATED } b = r.data[r.off] r.off += 1 @@ -361,6 +363,104 @@ parse_code :: proc(r: ^Reader, allocator: runtime.Allocator) -> (out: []Code_Bod return } + +@(private="file", require_results) +parse_custom_sections :: proc(m: ^Module, allocator: runtime.Allocator) -> Reader_Error { + custom_count := 0 + for &sec in m.sections { + if sec.id == .CUSTOM { + custom_count += 1 + } + } + m.customs = make([]Custom_Section, custom_count, allocator) or_return + custom_index := 0 + for &sec in m.sections { + if sec.id != .CUSTOM { + continue + } + + custom := &m.customs[custom_index] + custom_index += 1 + + custom.section = sec + + custom.payload = m.data[sec.offset:][:sec.size] + + r := reader(custom.payload, 0) + sec_name := rd_name(&r) or_continue + assert(sec_name == sec.name) + + if strings.has_prefix(sec.name, ".debug_") { + // DWARF debug stuff + } else if strings.has_prefix(sec.name, "reloc.") { + // other relocation stuff + } + + custom_block: switch sec.name { + case "linking": + // not yet handled + case "producers": + // not yet handled + case "dynlink.0": + // not yet handled + case "external_debug_info", "sourceMappingURL": + // not yet handled, but debugging related + case "metadata.code.branch_hint": + // not yet handled + case "name": + cname: Custom_Section_Name + defer custom.variant = cname + + for r.off < u32(len(r.data)) { + id := rd_byte(&r) or_return + size := rd_u32(&r) or_return + end_off := r.off+size + defer r.off = end_off + + switch id { + case 0: // module + cname.module_name = rd_name(&r) or_return + case 1: // functions + count := rd_u32(&r) or_return + cname.functions = make([]Custom_Section_Name_Function, count, allocator) or_return + for &func in cname.functions { + func.id = rd_u32(&r) or_return + func.name = rd_name(&r) or_return + } + case 2: // locals + count := rd_u32(&r) or_return + + cname.locals = make([]Custom_Section_Name_Function_Locals, count, allocator) or_return + + for &local_func in cname.locals { + local_func.func_idx = rd_u32(&r) or_return + local_count := rd_u32(&r) or_return + local_func.locals = make([]Custom_Section_Name_Local, local_count, allocator) or_return + for &local in local_func.locals { + local.idx = rd_u32(&r) or_return + local.name = rd_name(&r) or_return + } + } + } + } + + case "target_features": + target_features: Custom_Section_Target_Features + defer custom.variant = target_features + + count := rd_u32(&r) or_return + target_features.features = make([]Custom_Section_Target_Feature, count, allocator) or_return + + for &feature in target_features.features { + feature.prefix = Custom_Section_Target_Feature_Prefix(rd_byte(&r) or_return) + feature.feature = rd_name(&r) or_return + } + } + } + + return nil +} + // ============================================================================= // Function index space (imports ++ defined) with eagerly-decoded bodies // ============================================================================= @@ -415,7 +515,9 @@ build_functions :: proc( flat := make([]Value_Type, nlocals, allocator) or_return w := 0 for g in cb.locals { - for _ in 0 ..< int(g.count) { flat[w] = g.type; w += 1 } + for _ in 0.. (groups_ou out := make([]Relocation, int(count), allocator) or_return w := 0 - for _ in 0 ..< count { + for _ in 0..