mirror of
https://github.com/odin-lang/Odin.git
synced 2026-07-17 13:11:07 +00:00
106 lines
4.1 KiB
Odin
106 lines
4.1 KiB
Odin
// rexcode · Brendan Punsky (dotbmp@github), original author
|
|
// Ginger Bill (gingerBill@github)
|
|
|
|
package rexcode_wasm
|
|
|
|
// =============================================================================
|
|
// WebAssembly RELOCATIONS
|
|
// =============================================================================
|
|
//
|
|
// Per the cross-arch design (§2.4) each arch owns its Relocation_Type. WASM's
|
|
// relocations are the object-file ("linking") relocations: symbolic index
|
|
// references the linker fixes up. They are emitted, never PC-relative -- WASM
|
|
// control flow uses structured label depths, not byte offsets, so the encoder
|
|
// does not resolve these in a pass 2; it records them and leaves the patching
|
|
// to the linker. The relocatable LEB encodings are written as fixed-width
|
|
// 5-byte placeholders so the patched value always fits.
|
|
//
|
|
// The subset modelled mirrors the names from the tool-conventions linking
|
|
// spec used by LLVM / wasm-ld.
|
|
|
|
Relocation_Type :: enum u8 {
|
|
NONE = 0,
|
|
|
|
FUNCTION_INDEX_LEB, // funcidx, 5-byte ULEB (call, ref.func)
|
|
TABLE_INDEX_SLEB, // 5-byte SLEB table element index
|
|
TABLE_INDEX_I32, // 4-byte LE table element index
|
|
MEMORY_ADDR_LEB, // linear-memory address, 5-byte ULEB
|
|
MEMORY_ADDR_SLEB, // linear-memory address, 5-byte SLEB
|
|
MEMORY_ADDR_I32, // linear-memory address, 4-byte LE
|
|
TYPE_INDEX_LEB, // typeidx, 5-byte ULEB (call_indirect)
|
|
GLOBAL_INDEX_LEB, // globalidx, 5-byte ULEB
|
|
TABLE_NUMBER_LEB, // tableidx, 5-byte ULEB
|
|
}
|
|
|
|
Relocation :: struct #packed {
|
|
offset: u32, // byte offset of the relocatable field
|
|
label_id: u32, // symbol / target label id
|
|
addend: i32,
|
|
type: Relocation_Type,
|
|
size: u8, // bytes occupied by the field (5 for LEB, 4 for I32)
|
|
inst_idx: u16,
|
|
}
|
|
#assert(size_of(Relocation) == 16)
|
|
|
|
// A set of relocations that apply to one section (parsed from a `reloc.<name>`
|
|
// custom section). Grouped by the section they target so the decoder can hand
|
|
// the CODE-section group to each function body it decodes.
|
|
Reloc_Group :: struct {
|
|
target_section: Section_Id,
|
|
relocs: []Relocation,
|
|
}
|
|
|
|
// The relocations (if any) that patch fields in the given section.
|
|
@(require_results)
|
|
relocations_for_section :: proc "contextless" (groups: []Reloc_Group, id: Section_Id) -> []Relocation {
|
|
for g in groups {
|
|
if g.target_section == id {
|
|
return g.relocs
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Object-file (tool-conventions) relocation wire format
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// Decode a relocation type byte from a `reloc.*` custom section. `ok` is false
|
|
// for a type this codec does not model (the entry is then skipped).
|
|
@(require_results)
|
|
reloc_type_from_wire :: proc "contextless" (code: u8) -> (Relocation_Type, bool) {
|
|
switch code {
|
|
case 0: return .FUNCTION_INDEX_LEB, true // R_WASM_FUNCTION_INDEX_LEB
|
|
case 1: return .TABLE_INDEX_SLEB, true // R_WASM_TABLE_INDEX_SLEB
|
|
case 2: return .TABLE_INDEX_I32, true // R_WASM_TABLE_INDEX_I32
|
|
case 3: return .MEMORY_ADDR_LEB, true // R_WASM_MEMORY_ADDR_LEB
|
|
case 4: return .MEMORY_ADDR_SLEB, true // R_WASM_MEMORY_ADDR_SLEB
|
|
case 5: return .MEMORY_ADDR_I32, true // R_WASM_MEMORY_ADDR_I32
|
|
case 6: return .TYPE_INDEX_LEB, true // R_WASM_TYPE_INDEX_LEB
|
|
case 7: return .GLOBAL_INDEX_LEB, true // R_WASM_GLOBAL_INDEX_LEB
|
|
case 20: return .TABLE_NUMBER_LEB, true // R_WASM_TABLE_NUMBER_LEB
|
|
}
|
|
return .NONE, false
|
|
}
|
|
|
|
// MEMORY_ADDR_* (3,4,5) and the *_OFFSET_I32 forms (8,9) carry a trailing
|
|
// signed-LEB addend; the index-type relocations do not.
|
|
@(require_results)
|
|
reloc_has_addend :: proc "contextless" (code: u8) -> bool {
|
|
switch code {
|
|
case 3, 4, 5, 8, 9:
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// On-wire field width of a relocation, in bytes.
|
|
@(require_results)
|
|
reloc_field_size :: proc "contextless" (t: Relocation_Type) -> u8 {
|
|
#partial switch t {
|
|
case .TABLE_INDEX_I32, .MEMORY_ADDR_I32:
|
|
return 4 // 4-byte LE field
|
|
}
|
|
return 5 // 5-byte padded (S)LEB field
|
|
}
|