Commit 9ccf500508

Andrew Kelley <andrew@ziglang.org>
2024-12-31 03:49:40
implement function relocations
not all relocation types are implemented yet
1 parent 5b18af8
Changed files (2)
src
src/link/Wasm/Flush.zig
@@ -683,10 +683,12 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
             .__wasm_init_memory => @panic("TODO lower __wasm_init_memory "),
             .__wasm_init_tls => @panic("TODO lower __wasm_init_tls "),
             .object_function => |i| {
-                _ = i;
-                @panic("TODO lower object function code and apply relocations");
-                //try leb.writeUleb128(binary_writer, atom.code.len);
-                //try binary_bytes.appendSlice(gpa, atom.code.slice(wasm));
+                const ptr = i.ptr(wasm);
+                const code = ptr.code.slice(wasm);
+                try leb.writeUleb128(binary_writer, code.len);
+                const code_start = binary_bytes.items.len;
+                try binary_bytes.appendSlice(gpa, code);
+                if (!is_obj) applyRelocs(binary_bytes.items[code_start..], ptr.offset, ptr.relocations(wasm), wasm);
             },
             .zcu_func => |i| {
                 const code_start = try reserveSize(gpa, binary_bytes);
@@ -699,7 +701,6 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
         };
 
         replaceVecSectionHeader(binary_bytes, header_offset, .code, @intCast(wasm.functions.entries.len));
-        if (is_obj) @panic("TODO apply offset to code relocs");
         code_section_index = section_index;
         section_index += 1;
     }
@@ -801,7 +802,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
     }
 
     if (is_obj) {
-        @panic("TODO emit link section for object file and apply relocations");
+        @panic("TODO emit link section for object file and emit modified relocations");
         //var symbol_table = std.AutoArrayHashMap(SymbolLoc, u32).init(arena);
         //try wasm.emitLinkSection(binary_bytes, &symbol_table);
         //if (code_section_index) |code_index| {
@@ -1420,3 +1421,141 @@ fn emitErrorNameTable(
         mem.writeInt(Int, code.addManyAsArrayAssumeCapacity(ptr_size_bytes), name_len, .little);
     }
 }
+
+fn applyRelocs(code: []u8, code_offset: u32, relocs: Wasm.ObjectRelocation.IterableSlice, wasm: *const Wasm) void {
+    for (
+        relocs.slice.tags(wasm),
+        relocs.slice.pointees(wasm),
+        relocs.slice.offsets(wasm),
+        relocs.slice.addends(wasm),
+    ) |tag, pointee, offset, *addend| {
+        if (offset >= relocs.end) break;
+        const sliced_code = code[offset - code_offset ..];
+        switch (tag) {
+            .function_index_i32 => reloc_u32_function(sliced_code, .fromObjectFunction(wasm, pointee.function)),
+            .function_index_leb => reloc_leb_function(sliced_code, .fromObjectFunction(wasm, pointee.function)),
+            .function_offset_i32 => @panic("TODO this value is not known yet"),
+            .function_offset_i64 => @panic("TODO this value is not known yet"),
+            .table_index_i32 => @panic("TODO indirect function table needs to support object functions too"),
+            .table_index_i64 => @panic("TODO indirect function table needs to support object functions too"),
+            .table_index_rel_sleb => @panic("TODO indirect function table needs to support object functions too"),
+            .table_index_rel_sleb64 => @panic("TODO indirect function table needs to support object functions too"),
+            .table_index_sleb => @panic("TODO indirect function table needs to support object functions too"),
+            .table_index_sleb64 => @panic("TODO indirect function table needs to support object functions too"),
+
+            .function_import_index_i32 => reloc_u32_function(sliced_code, .fromSymbolName(wasm, pointee.symbol_name)),
+            .function_import_index_leb => reloc_leb_function(sliced_code, .fromSymbolName(wasm, pointee.symbol_name)),
+            .function_import_offset_i32 => @panic("TODO this value is not known yet"),
+            .function_import_offset_i64 => @panic("TODO this value is not known yet"),
+            .table_import_index_i32 => @panic("TODO indirect function table needs to support object functions too"),
+            .table_import_index_i64 => @panic("TODO indirect function table needs to support object functions too"),
+            .table_import_index_rel_sleb => @panic("TODO indirect function table needs to support object functions too"),
+            .table_import_index_rel_sleb64 => @panic("TODO indirect function table needs to support object functions too"),
+            .table_import_index_sleb => @panic("TODO indirect function table needs to support object functions too"),
+            .table_import_index_sleb64 => @panic("TODO indirect function table needs to support object functions too"),
+
+            .global_index_i32 => reloc_u32_global(sliced_code, .fromObjectGlobal(wasm, pointee.global)),
+            .global_index_leb => reloc_leb_global(sliced_code, .fromObjectGlobal(wasm, pointee.global)),
+
+            .global_import_index_i32 => reloc_u32_global(sliced_code, .fromSymbolName(wasm, pointee.symbol_name)),
+            .global_import_index_leb => reloc_leb_global(sliced_code, .fromSymbolName(wasm, pointee.symbol_name)),
+
+            .memory_addr_i32 => reloc_u32_addr(sliced_code, .fromObjectData(wasm, pointee.data, addend.*)),
+            .memory_addr_i64 => reloc_u64_addr(sliced_code, .fromObjectData(wasm, pointee.data, addend.*)),
+            .memory_addr_leb => reloc_leb_addr(sliced_code, .fromObjectData(wasm, pointee.data, addend.*)),
+            .memory_addr_leb64 => reloc_leb64_addr(sliced_code, .fromObjectData(wasm, pointee.data, addend.*)),
+            .memory_addr_locrel_i32 => @panic("TODO implement relocation memory_addr_locrel_i32"),
+            .memory_addr_rel_sleb => @panic("TODO implement relocation memory_addr_rel_sleb"),
+            .memory_addr_rel_sleb64 => @panic("TODO implement relocation memory_addr_rel_sleb64"),
+            .memory_addr_sleb => reloc_sleb_addr(sliced_code, .fromObjectData(wasm, pointee.data, addend.*)),
+            .memory_addr_sleb64 => reloc_sleb64_addr(sliced_code, .fromObjectData(wasm, pointee.data, addend.*)),
+            .memory_addr_tls_sleb => @panic("TODO implement relocation memory_addr_tls_sleb"),
+            .memory_addr_tls_sleb64 => @panic("TODO implement relocation memory_addr_tls_sleb64"),
+
+            .memory_addr_import_i32 => reloc_u32_addr(sliced_code, .fromSymbolName(wasm, pointee.symbol_name, addend.*)),
+            .memory_addr_import_i64 => reloc_u64_addr(sliced_code, .fromSymbolName(wasm, pointee.symbol_name, addend.*)),
+            .memory_addr_import_leb => reloc_leb_addr(sliced_code, .fromSymbolName(wasm, pointee.symbol_name, addend.*)),
+            .memory_addr_import_leb64 => reloc_leb64_addr(sliced_code, .fromSymbolName(wasm, pointee.symbol_name, addend.*)),
+            .memory_addr_import_locrel_i32 => @panic("TODO implement relocation memory_addr_import_locrel_i32"),
+            .memory_addr_import_rel_sleb => @panic("TODO implement relocation memory_addr_import_rel_sleb"),
+            .memory_addr_import_rel_sleb64 => @panic("TODO implement memory_addr_import_rel_sleb64"),
+            .memory_addr_import_sleb => reloc_sleb_addr(sliced_code, .fromSymbolName(wasm, pointee.symbol_name, addend.*)),
+            .memory_addr_import_sleb64 => reloc_sleb64_addr(sliced_code, .fromSymbolName(wasm, pointee.symbol_name, addend.*)),
+            .memory_addr_import_tls_sleb => @panic("TODO"),
+            .memory_addr_import_tls_sleb64 => @panic("TODO"),
+
+            .section_offset_i32 => @panic("TODO this value is not known yet"),
+
+            .table_number_leb => reloc_leb_table(sliced_code, .fromObjectTable(wasm, pointee.table)),
+            .table_import_number_leb => reloc_leb_table(sliced_code, .fromSymbolName(wasm, pointee.symbol_name)),
+
+            .type_index_leb => reloc_leb_type(sliced_code, pointee.type_index),
+        }
+    }
+}
+
+fn reloc_u32_function(code: []u8, function: Wasm.OutputFunctionIndex) void {
+    mem.writeInt(u32, code[0..4], @intFromEnum(function), .little);
+}
+
+fn reloc_leb_function(code: []u8, function: Wasm.OutputFunctionIndex) void {
+    leb.writeUnsignedFixed(5, code[0..5], @intFromEnum(function));
+}
+
+fn reloc_u32_global(code: []u8, global: Wasm.GlobalIndex) void {
+    mem.writeInt(u32, code[0..4], @intFromEnum(global), .little);
+}
+
+fn reloc_leb_global(code: []u8, global: Wasm.GlobalIndex) void {
+    leb.writeUnsignedFixed(5, code[0..5], @intFromEnum(global));
+}
+
+const RelocAddr = struct {
+    addr: u32,
+
+    fn fromObjectData(wasm: *const Wasm, i: Wasm.ObjectData.Index, addend: i32) RelocAddr {
+        const ptr = i.ptr(wasm);
+        const f = &wasm.flush_buffer;
+        const addr = f.data_segments.get(.fromObjectDataSegment(wasm, ptr.segment)).?;
+        return .{ .addr = @intCast(@as(i64, addr) + addend) };
+    }
+
+    fn fromSymbolName(wasm: *const Wasm, name: String, addend: i32) RelocAddr {
+        _ = wasm;
+        _ = name;
+        _ = addend;
+        @panic("TODO implement data symbol resolution");
+    }
+};
+
+fn reloc_u32_addr(code: []u8, ra: RelocAddr) void {
+    mem.writeInt(u32, code[0..4], ra.addr, .little);
+}
+
+fn reloc_u64_addr(code: []u8, ra: RelocAddr) void {
+    mem.writeInt(u64, code[0..8], ra.addr, .little);
+}
+
+fn reloc_leb_addr(code: []u8, ra: RelocAddr) void {
+    leb.writeUnsignedFixed(5, code[0..5], ra.addr);
+}
+
+fn reloc_leb64_addr(code: []u8, ra: RelocAddr) void {
+    leb.writeUnsignedFixed(11, code[0..11], ra.addr);
+}
+
+fn reloc_sleb_addr(code: []u8, ra: RelocAddr) void {
+    leb.writeSignedFixed(5, code[0..5], ra.addr);
+}
+
+fn reloc_sleb64_addr(code: []u8, ra: RelocAddr) void {
+    leb.writeSignedFixed(11, code[0..11], ra.addr);
+}
+
+fn reloc_leb_table(code: []u8, table: Wasm.TableIndex) void {
+    leb.writeUnsignedFixed(5, code[0..5], @intFromEnum(table));
+}
+
+fn reloc_leb_type(code: []u8, index: Wasm.FunctionType.Index) void {
+    leb.writeUnsignedFixed(5, code[0..5], @intFromEnum(index));
+}
src/link/Wasm.zig
@@ -349,6 +349,10 @@ pub const OutputFunctionIndex = enum(u32) {
         return @enumFromInt(wasm.function_imports.entries.len + @intFromEnum(index));
     }
 
+    pub fn fromObjectFunction(wasm: *const Wasm, index: ObjectFunctionIndex) OutputFunctionIndex {
+        return fromResolution(wasm, .fromObjectFunction(wasm, index)).?;
+    }
+
     pub fn fromIpIndex(wasm: *const Wasm, ip_index: InternPool.Index) OutputFunctionIndex {
         const zcu = wasm.base.comp.zcu.?;
         const ip = &zcu.intern_pool;
@@ -401,6 +405,33 @@ pub const GlobalIndex = enum(u32) {
         const i = wasm.globals.getIndex(.fromIpNav(wasm, nav_index)) orelse return null;
         return @enumFromInt(i);
     }
+
+    pub fn fromObjectGlobal(wasm: *const Wasm, i: ObjectGlobalIndex) GlobalIndex {
+        return @enumFromInt(wasm.globals.getIndex(.fromObjectGlobal(wasm, i)).?);
+    }
+
+    pub fn fromSymbolName(wasm: *const Wasm, name: String) GlobalIndex {
+        const import = wasm.object_global_imports.getPtr(name).?;
+        return @enumFromInt(wasm.globals.getIndex(import.resolution).?);
+    }
+};
+
+/// Index into `tables`.
+pub const TableIndex = enum(u32) {
+    _,
+
+    pub fn ptr(index: TableIndex, f: *const Flush) *Wasm.TableImport.Resolution {
+        return &f.tables.items[@intFromEnum(index)];
+    }
+
+    pub fn fromObjectTable(wasm: *const Wasm, i: ObjectTableIndex) TableIndex {
+        return @enumFromInt(wasm.tables.getIndex(.fromObjectTable(i)).?);
+    }
+
+    pub fn fromSymbolName(wasm: *const Wasm, name: String) TableIndex {
+        const import = wasm.object_table_imports.getPtr(name).?;
+        return @enumFromInt(wasm.tables.getIndex(import.resolution).?);
+    }
 };
 
 /// The first N indexes correspond to input objects (`objects`) array.
@@ -1019,7 +1050,7 @@ pub const ObjectFunction = extern struct {
 
     pub const Code = DataPayload;
 
-    fn relocations(of: *const ObjectFunction, wasm: *const Wasm) ObjectRelocation.IterableSlice {
+    pub fn relocations(of: *const ObjectFunction, wasm: *const Wasm) ObjectRelocation.IterableSlice {
         const code_section_index = of.object_index.ptr(wasm).code_section_index.?;
         const relocs = wasm.object_relocations_table.get(code_section_index) orelse return .empty;
         return .init(relocs, of.offset, of.code.len, wasm);
@@ -1181,7 +1212,7 @@ pub const ObjectGlobal = extern struct {
         mutable: bool,
     };
 
-    fn relocations(og: *const ObjectGlobal, wasm: *const Wasm) ObjectRelocation.IterableSlice {
+    pub fn relocations(og: *const ObjectGlobal, wasm: *const Wasm) ObjectRelocation.IterableSlice {
         const global_section_index = og.object_index.ptr(wasm).global_section_index.?;
         const relocs = wasm.object_relocations_table.get(global_section_index) orelse return .empty;
         return .init(relocs, og.offset, og.size, wasm);
@@ -1440,7 +1471,7 @@ pub const ObjectDataSegment = extern struct {
         }
     };
 
-    fn relocations(ods: *const ObjectDataSegment, wasm: *const Wasm) ObjectRelocation.IterableSlice {
+    pub fn relocations(ods: *const ObjectDataSegment, wasm: *const Wasm) ObjectRelocation.IterableSlice {
         const data_section_index = ods.object_index.ptr(wasm).data_section_index.?;
         const relocs = wasm.object_relocations_table.get(data_section_index) orelse return .empty;
         return .init(relocs, ods.offset, ods.payload.len, wasm);
@@ -1563,6 +1594,10 @@ pub const DataId = enum(u32) {
         };
     }
 
+    pub fn fromObjectDataSegment(wasm: *const Wasm, object_data_segment: ObjectDataSegment.Index) DataId {
+        return pack(wasm, .{ .object = object_data_segment });
+    }
+
     pub fn category(id: DataId, wasm: *const Wasm) Category {
         return switch (unpack(id, wasm)) {
             .__zig_error_names, .__zig_error_name_table => .data,
@@ -2258,19 +2293,19 @@ pub const ObjectRelocation = struct {
 
         const empty: Slice = .{ .off = 0, .len = 0 };
 
-        fn tags(s: Slice, wasm: *const Wasm) []const ObjectRelocation.Tag {
+        pub fn tags(s: Slice, wasm: *const Wasm) []const ObjectRelocation.Tag {
             return wasm.object_relocations.items(.tag)[s.off..][0..s.len];
         }
 
-        fn offsets(s: Slice, wasm: *const Wasm) []const u32 {
+        pub fn offsets(s: Slice, wasm: *const Wasm) []const u32 {
             return wasm.object_relocations.items(.offset)[s.off..][0..s.len];
         }
 
-        fn pointees(s: Slice, wasm: *const Wasm) []const Pointee {
+        pub fn pointees(s: Slice, wasm: *const Wasm) []const Pointee {
             return wasm.object_relocations.items(.pointee)[s.off..][0..s.len];
         }
 
-        fn addends(s: Slice, wasm: *const Wasm) []const i32 {
+        pub fn addends(s: Slice, wasm: *const Wasm) []const i32 {
             return wasm.object_relocations.items(.addend)[s.off..][0..s.len];
         }
     };
@@ -3131,7 +3166,7 @@ fn markDataSegment(wasm: *Wasm, segment_index: ObjectDataSegment.Index) link.Fil
 }
 
 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| {
+    for (relocs.slice.tags(wasm), relocs.slice.pointees(wasm), relocs.slice.offsets(wasm)) |tag, pointee, offset| {
         if (offset >= relocs.end) break;
         switch (tag) {
             .function_import_index_leb,