Commit fcafaae747

David Rubin <daviru007@icloud.com>
2024-04-23 05:40:55
riscv: get basic libc interop
1 parent 004d0c8
Changed files (7)
src/arch/riscv64/CodeGen.zig
@@ -3590,22 +3590,42 @@ fn genCall(
                             const sym_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, func.owner_decl);
                             const sym = elf_file.symbol(sym_index);
 
-                            _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file);
-                            const got_addr = sym.zigGotAddress(elf_file);
-                            try self.genSetReg(Type.usize, .ra, .{ .memory = @intCast(got_addr) });
+                            if (self.mod.pic) {
+                                return self.fail("TODO: genCall pic", .{});
+                            } else {
+                                _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file);
+                                const got_addr = sym.zigGotAddress(elf_file);
+                                try self.genSetReg(Type.usize, .ra, .{ .memory = @intCast(got_addr) });
+
+                                _ = try self.addInst(.{
+                                    .tag = .jalr,
+                                    .ops = .rri,
+                                    .data = .{ .i_type = .{
+                                        .rd = .ra,
+                                        .rs1 = .ra,
+                                        .imm12 = Immediate.s(0),
+                                    } },
+                                });
+                            }
+                        } else unreachable; // not a valid riscv64 format
+                    },
+                    .extern_func => |extern_func| {
+                        const owner_decl = zcu.declPtr(extern_func.decl);
+                        const lib_name = extern_func.lib_name.toSlice(&zcu.intern_pool);
+                        const decl_name = owner_decl.name.toSlice(&zcu.intern_pool);
+                        const atom_index = try self.symbolIndex();
 
+                        if (self.bin_file.cast(link.File.Elf)) |elf_file| {
                             _ = try self.addInst(.{
-                                .tag = .jalr,
-                                .ops = .rri,
-                                .data = .{ .i_type = .{
-                                    .rd = .ra,
-                                    .rs1 = .ra,
-                                    .imm12 = Immediate.s(0),
+                                .tag = .pseudo,
+                                .ops = .pseudo_extern_fn_reloc,
+                                .data = .{ .reloc = .{
+                                    .atom_index = atom_index,
+                                    .sym_index = try elf_file.getGlobalSymbol(decl_name, lib_name),
                                 } },
                             });
-                        } else unreachable;
+                        } else unreachable; // not a valid riscv64 format
                     },
-                    .extern_func => return self.fail("TODO: extern func calls", .{}),
                     else => return self.fail("TODO implement calling bitcasted functions", .{}),
                 }
             } else {
@@ -3613,6 +3633,7 @@ fn genCall(
                 const addr_reg, const addr_lock = try self.allocReg();
                 defer self.register_manager.unlockReg(addr_lock);
                 try self.genSetReg(Type.usize, addr_reg, .{ .air_ref = callee });
+
                 _ = try self.addInst(.{
                     .tag = .jalr,
                     .ops = .rri,
src/arch/riscv64/Emit.zig
@@ -70,6 +70,19 @@ pub fn emitMir(emit: *Emit) Error!void {
                         });
                     } else return emit.fail("TODO: load_symbol_reloc non-ELF", .{});
                 },
+                .call_extern_fn_reloc => |symbol| {
+                    if (emit.lower.bin_file.cast(link.File.Elf)) |elf_file| {
+                        const atom_ptr = elf_file.symbol(symbol.atom_index).atom(elf_file).?;
+
+                        const r_type: u32 = @intFromEnum(std.elf.R_RISCV.CALL_PLT);
+
+                        try atom_ptr.addReloc(elf_file, .{
+                            .r_offset = start_offset,
+                            .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | r_type,
+                            .r_addend = 0,
+                        });
+                    } else return emit.fail("TODO: call_extern_fn_reloc non-ELF", .{});
+                },
             };
         }
         std.debug.assert(lowered_relocs.len == 0);
src/arch/riscv64/Encoding.zig
@@ -22,6 +22,7 @@ pub const Mnemonic = enum {
 
     // U Type
     lui,
+    auipc,
 
     // S Type
     sd,
@@ -78,6 +79,7 @@ pub const Mnemonic = enum {
             .srai   => .{ .opcode = 0b0010011, .funct3 = 0b101, .funct7 = null,   .offset = 1 << 10  },
 
             .lui    => .{ .opcode = 0b0110111, .funct3 = null,  .funct7 = null      },
+            .auipc  => .{ .opcode = 0b0010111, .funct3 = null,  .funct7 = null      },
 
             .sd     => .{ .opcode = 0b0100011, .funct3 = 0b011, .funct7 = null      },
             .sw     => .{ .opcode = 0b0100011, .funct3 = 0b010, .funct7 = null      },
@@ -133,6 +135,7 @@ pub const InstEnc = enum {
             => .I,
 
             .lui,
+            .auipc,
             => .U,
 
             .sd,
src/arch/riscv64/Lower.zig
@@ -32,8 +32,10 @@ pub const Reloc = struct {
     const Target = union(enum) {
         inst: Mir.Inst.Index,
 
-        /// Relocs the lowered_inst_index and the next one.
+        /// Relocs the lowered_inst_index and the next instruction.
         load_symbol_reloc: bits.Symbol,
+        /// Relocs the lowered_inst_index and the next instruction.
+        call_extern_fn_reloc: bits.Symbol,
     };
 };
 
@@ -247,6 +249,26 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct {
                 });
             },
 
+            .pseudo_extern_fn_reloc => {
+                const inst_reloc = inst.data.reloc;
+
+                try lower.emit(.auipc, &.{
+                    .{ .reg = .ra },
+                    .{ .imm = lower.reloc(
+                        .{ .call_extern_fn_reloc = .{
+                            .atom_index = inst_reloc.atom_index,
+                            .sym_index = inst_reloc.sym_index,
+                        } },
+                    ) },
+                });
+
+                try lower.emit(.jalr, &.{
+                    .{ .reg = .ra },
+                    .{ .reg = .ra },
+                    .{ .imm = Immediate.s(0) },
+                });
+            },
+
             else => return lower.fail("TODO lower: psuedo {s}", .{@tagName(inst.ops)}),
         },
     }
src/arch/riscv64/Mir.zig
@@ -202,6 +202,11 @@ pub const Inst = struct {
                 lte,
             },
         },
+
+        reloc: struct {
+            atom_index: u32,
+            sym_index: u32,
+        },
     };
 
     pub const Ops = enum {
@@ -214,10 +219,7 @@ pub const Inst = struct {
 
         /// Two registers + immediate, uses the i_type payload.
         rri,
-        /// Two registers + Two Immediates
-        rrii,
-
-        /// Two registers + another instruction.
+        //extern_fn_reloc/ Two registers + another instruction.
         rr_inst,
 
         /// Register + Memory
@@ -283,6 +285,9 @@ pub const Inst = struct {
 
         pseudo_compare,
         pseudo_not,
+
+        /// Generates an auipc + jalr pair, with a R_RISCV_CALL_PLT reloc
+        pseudo_extern_fn_reloc,
     };
 
     // Make sure we don't accidentally make instructions bigger than expected.
src/link/Elf/Atom.zig
@@ -1409,11 +1409,11 @@ const x86_64 = struct {
             .GOTPC64 => try cwriter.writeInt(i64, GOT + A, .little),
             .SIZE32 => {
                 const size = @as(i64, @intCast(target.elfSym(elf_file).st_size));
-                try cwriter.writeInt(u32, @as(u32, @bitCast(@as(i32, @intCast(size + A)))), .little);
+                try cwriter.writeInt(u32, @bitCast(@as(i32, @intCast(size + A))), .little);
             },
             .SIZE64 => {
                 const size = @as(i64, @intCast(target.elfSym(elf_file).st_size));
-                try cwriter.writeInt(i64, @as(i64, @intCast(size + A)), .little);
+                try cwriter.writeInt(i64, @intCast(size + A), .little);
             },
             else => try atom.reportUnhandledRelocError(rel, elf_file),
         }
@@ -2001,26 +2001,25 @@ const riscv = struct {
         const r_type: elf.R_RISCV = @enumFromInt(rel.r_type());
 
         switch (r_type) {
-            .@"64" => {
-                try atom.scanReloc(symbol, rel, dynAbsRelocAction(symbol, elf_file), elf_file);
-            },
-
-            .HI20 => {
-                try atom.scanReloc(symbol, rel, absRelocAction(symbol, elf_file), elf_file);
-            },
+            .@"32" => try atom.scanReloc(symbol, rel, absRelocAction(symbol, elf_file), elf_file),
+            .@"64" => try atom.scanReloc(symbol, rel, dynAbsRelocAction(symbol, elf_file), elf_file),
+            .HI20 => try atom.scanReloc(symbol, rel, absRelocAction(symbol, elf_file), elf_file),
 
             .CALL_PLT => if (symbol.flags.import) {
                 symbol.flags.needs_plt = true;
             },
+            .GOT_HI20 => symbol.flags.needs_got = true,
 
-            .GOT_HI20 => {
-                symbol.flags.needs_got = true;
-            },
+            .TPREL_HI20,
+            .TPREL_LO12_I,
+            .TPREL_LO12_S,
+            .TPREL_ADD,
 
             .PCREL_HI20,
             .PCREL_LO12_I,
             .PCREL_LO12_S,
             .LO12_I,
+            .LO12_S,
             .ADD32,
             .SUB32,
             => {},
@@ -2058,6 +2057,8 @@ const riscv = struct {
         switch (r_type) {
             .NONE => unreachable,
 
+            .@"32" => try cwriter.writeInt(u32, @as(u32, @truncate(@as(u64, @intCast(S + A)))), .little),
+
             .@"64" => {
                 try atom.resolveDynAbsReloc(
                     target,
@@ -2076,11 +2077,6 @@ const riscv = struct {
                 riscv_util.writeInstU(code[r_offset..][0..4], value);
             },
 
-            .LO12_I => {
-                const value: u32 = @bitCast(math.cast(i32, S + A) orelse return error.Overflow);
-                riscv_util.writeInstI(code[r_offset..][0..4], value);
-            },
-
             .GOT_HI20 => {
                 assert(target.flags.has_got);
                 const disp: u32 = @bitCast(math.cast(i32, G + GOT + A - P) orelse return error.Overflow);
@@ -2143,6 +2139,39 @@ const riscv = struct {
                 }
             },
 
+            .LO12_I,
+            .LO12_S,
+            => {
+                const disp: u32 = @bitCast(math.cast(i32, S + A) orelse return error.Overflow);
+                switch (r_type) {
+                    .LO12_I => riscv_util.writeInstI(code[r_offset..][0..4], disp),
+                    .LO12_S => riscv_util.writeInstS(code[r_offset..][0..4], disp),
+                    else => unreachable,
+                }
+            },
+
+            .TPREL_HI20 => {
+                const target_addr: u32 = @intCast(target.address(.{}, elf_file));
+                const val: i32 = @intCast(S + A - target_addr);
+                riscv_util.writeInstU(code[r_offset..][0..4], @bitCast(val));
+            },
+
+            .TPREL_LO12_I,
+            .TPREL_LO12_S,
+            => {
+                const target_addr: u32 = @intCast(target.address(.{}, elf_file));
+                const val: i32 = @intCast(S + A - target_addr);
+                switch (r_type) {
+                    .TPREL_LO12_I => riscv_util.writeInstI(code[r_offset..][0..4], @bitCast(val)),
+                    .TPREL_LO12_S => riscv_util.writeInstS(code[r_offset..][0..4], @bitCast(val)),
+                    else => unreachable,
+                }
+            },
+
+            .TPREL_ADD => {
+                // TODO: annotates an ADD instruction that can be removed when TPREL is relaxed
+            },
+
             else => |x| switch (@intFromEnum(x)) {
                 // Zig custom relocations
                 Elf.R_ZIG_GOT_HI20 => {
src/link/Elf.zig
@@ -5842,7 +5842,8 @@ pub fn tpAddress(self: *Elf) i64 {
     const addr = switch (self.getTarget().cpu.arch) {
         .x86_64 => mem.alignForward(u64, phdr.p_vaddr + phdr.p_memsz, phdr.p_align),
         .aarch64 => mem.alignBackward(u64, phdr.p_vaddr - 16, phdr.p_align),
-        else => @panic("TODO implement getTpAddress for this arch"),
+        .riscv64 => phdr.p_vaddr,
+        else => |arch| std.debug.panic("TODO implement getTpAddress for {s}", .{@tagName(arch)}),
     };
     return @intCast(addr);
 }