diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 56d2f062cc..d6f11ef8c9 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/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, diff --git a/src/arch/riscv64/Emit.zig b/src/arch/riscv64/Emit.zig index ec256fefb3..6f136e7fb4 100644 --- a/src/arch/riscv64/Emit.zig +++ b/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); diff --git a/src/arch/riscv64/Encoding.zig b/src/arch/riscv64/Encoding.zig index cccf0c8aac..3a0ef90584 100644 --- a/src/arch/riscv64/Encoding.zig +++ b/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, diff --git a/src/arch/riscv64/Lower.zig b/src/arch/riscv64/Lower.zig index 4b77f9cdee..dba902eaa4 100644 --- a/src/arch/riscv64/Lower.zig +++ b/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)}), }, } diff --git a/src/arch/riscv64/Mir.zig b/src/arch/riscv64/Mir.zig index 064aff0415..5d21719da2 100644 --- a/src/arch/riscv64/Mir.zig +++ b/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. diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 770d483e98..6464ac73e2 100644 --- a/src/link/Elf.zig +++ b/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); } diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index 239186ffaa..1c303980c3 100644 --- a/src/link/Elf/Atom.zig +++ b/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 => {