diff --git a/src/Builtin.zig b/src/Builtin.zig index 6e573d843f5f..58abcb7a4bec 100644 --- a/src/Builtin.zig +++ b/src/Builtin.zig @@ -218,7 +218,7 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void { if (opts.is_test) { try buffer.appendSlice( - \\pub var test_functions: []const std.builtin.TestFn = undefined; // overwritten later + \\pub var test_functions: []const std.builtin.TestFn = &.{}; // overwritten later \\ ); } diff --git a/src/Compilation.zig b/src/Compilation.zig index d7dabd51640e..0beb960d022a 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -3092,6 +3092,10 @@ pub fn totalErrorCount(comp: *Compilation) u32 { if (zcu.intern_pool.global_error_set.getNamesFromMainThread().len > zcu.error_limit) { total += 1; } + + for (zcu.failed_codegen.keys()) |_| { + total += 1; + } } // The "no entry point found" error only counts if there are no semantic analysis errors. @@ -3237,6 +3241,9 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { } } } + for (zcu.failed_codegen.values()) |error_msg| { + try addModuleErrorMsg(zcu, &bundle, error_msg.*, &all_references); + } for (zcu.failed_exports.values()) |value| { try addModuleErrorMsg(zcu, &bundle, value.*, &all_references); } diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 2810b6b52189..231db8e98c53 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -4352,24 +4352,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier // on linking. if (try self.air.value(callee, pt)) |func_value| switch (ip.indexToKey(func_value.toIntern())) { .func => |func| { - if (self.bin_file.cast(.elf)) |elf_file| { - const zo = elf_file.zigObjectPtr().?; - const sym_index = try zo.getOrCreateMetadataForNav(elf_file, func.owner_nav); - const sym = zo.symbol(sym_index); - _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file); - const got_addr = @as(u32, @intCast(sym.zigGotAddress(elf_file))); - try self.genSetReg(Type.usize, .x30, .{ .memory = got_addr }); - } else if (self.bin_file.cast(.macho)) |macho_file| { - _ = macho_file; - @panic("TODO airCall"); - // const atom = try macho_file.getOrCreateAtomForNav(func.owner_nav); - // const sym_index = macho_file.getAtom(atom).getSymbolIndex().?; - // try self.genSetReg(Type.u64, .x30, .{ - // .linker_load = .{ - // .type = .got, - // .sym_index = sym_index, - // }, - // }); + if (self.bin_file.cast(.elf)) |_| { + return self.fail("TODO implement calling functions for Elf", .{}); + } else if (self.bin_file.cast(.macho)) |_| { + return self.fail("TODO implement calling functions for MachO", .{}); } else if (self.bin_file.cast(.coff)) |coff_file| { const atom = try coff_file.getOrCreateAtomForNav(func.owner_nav); const sym_index = coff_file.getAtom(atom).getSymbolIndex().?; @@ -4393,21 +4379,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier .@"extern" => |@"extern"| { const nav_name = ip.getNav(@"extern".owner_nav).name.toSlice(ip); const lib_name = @"extern".lib_name.toSlice(ip); - if (self.bin_file.cast(.macho)) |macho_file| { - _ = macho_file; - @panic("TODO airCall"); - // const sym_index = try macho_file.getGlobalSymbol(nav_name, lib_name); - // const atom = try macho_file.getOrCreateAtomForNav(self.owner_nav); - // const atom_index = macho_file.getAtom(atom).getSymbolIndex().?; - // _ = try self.addInst(.{ - // .tag = .call_extern, - // .data = .{ - // .relocation = .{ - // .atom_index = atom_index, - // .sym_index = sym_index, - // }, - // }, - // }); + if (self.bin_file.cast(.macho)) |_| { + return self.fail("TODO implement calling extern functions for MachO", .{}); } else if (self.bin_file.cast(.coff)) |coff_file| { const sym_index = try coff_file.getGlobalSymbol(nav_name, lib_name); try self.genSetReg(Type.u64, .x30, .{ @@ -6234,7 +6207,7 @@ fn genTypedValue(self: *Self, val: Value) InnerError!MCValue { .memory => |addr| .{ .memory = addr }, .load_got => |sym_index| .{ .linker_load = .{ .type = .got, .sym_index = sym_index } }, .load_direct => |sym_index| .{ .linker_load = .{ .type = .direct, .sym_index = sym_index } }, - .load_symbol, .load_tlv => unreachable, // TODO + .load_symbol, .load_tlv, .lea_symbol => unreachable, // TODO }, .fail => |msg| { self.err_msg = msg; diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index e4d106921ed4..1c0369955208 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -4333,22 +4333,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier // Due to incremental compilation, how function calls are generated depends // on linking. if (try self.air.value(callee, pt)) |func_value| switch (ip.indexToKey(func_value.toIntern())) { - .func => |func| { - if (self.bin_file.cast(.elf)) |elf_file| { - const zo = elf_file.zigObjectPtr().?; - const sym_index = try zo.getOrCreateMetadataForNav(elf_file, func.owner_nav); - const sym = zo.symbol(sym_index); - _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file); - const got_addr: u32 = @intCast(sym.zigGotAddress(elf_file)); - try self.genSetReg(Type.usize, .lr, .{ .memory = got_addr }); - } else if (self.bin_file.cast(.macho)) |_| { - unreachable; // unsupported architecture for MachO - } else { - return self.fail("TODO implement call on {s} for {s}", .{ - @tagName(self.bin_file.tag), - @tagName(self.target.cpu.arch), - }); - } + .func => { + return self.fail("TODO implement calling functions", .{}); }, .@"extern" => { return self.fail("TODO implement calling extern functions", .{}); @@ -6184,7 +6170,7 @@ fn genTypedValue(self: *Self, val: Value) InnerError!MCValue { .mcv => |mcv| switch (mcv) { .none => .none, .undef => .undef, - .load_got, .load_symbol, .load_direct, .load_tlv => unreachable, // TODO + .load_got, .load_symbol, .load_direct, .load_tlv, .lea_symbol => unreachable, // TODO .immediate => |imm| .{ .immediate = @truncate(imm) }, .memory => |addr| .{ .memory = addr }, }, diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index b5384542e73f..942c56249652 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -4937,7 +4937,7 @@ fn genCall( if (func.mod.pic) { return func.fail("TODO: genCall pic", .{}); } else { - try func.genSetReg(Type.u64, .ra, .{ .load_symbol = .{ .sym = sym_index } }); + try func.genSetReg(Type.u64, .ra, .{ .lea_symbol = .{ .sym = sym_index } }); _ = try func.addInst(.{ .tag = .jalr, .data = .{ .i_type = .{ @@ -6120,7 +6120,7 @@ fn airAsm(func: *Func, inst: Air.Inst.Index) !void { arg_map.get(op_str["%[".len .. mod_index orelse op_str.len - "]".len]) orelse return func.fail("no matching constraint: '{s}'", .{op_str}) ]) { - .load_symbol => |sym_off| if (mem.eql(u8, modifier, "plt")) blk: { + .lea_symbol => |sym_off| if (mem.eql(u8, modifier, "plt")) blk: { assert(sym_off.off == 0); break :blk .{ .sym = sym_off }; } else return func.fail("invalid modifier: '{s}'", .{modifier}), @@ -6388,7 +6388,7 @@ fn genCopy(func: *Func, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void { ty, src_mcv, ), - .load_tlv => { + .load_symbol, .load_tlv => { const addr_reg, const addr_lock = try func.allocReg(.int); defer func.register_manager.unlockReg(addr_lock); @@ -6433,7 +6433,7 @@ fn genCopy(func: *Func, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void { part_disp += @intCast(dst_ty.abiSize(func.pt)); } }, - else => return func.fail("TODO: genCopy to {s} from {s}", .{ @tagName(dst_mcv), @tagName(src_mcv) }), + else => return std.debug.panic("TODO: genCopy to {s} from {s}", .{ @tagName(dst_mcv), @tagName(src_mcv) }), } } @@ -6594,7 +6594,7 @@ fn genInlineMemset( .tag = .beq, .data = .{ .b_type = .{ - .inst = @intCast(func.mir_instructions.len + 4), // points after the last inst + .inst = @intCast(func.mir_instructions.len + 3), // points after the last inst .rs1 = count, .rs2 = .zero, }, @@ -8026,6 +8026,7 @@ fn genTypedValue(func: *Func, val: Value) InnerError!MCValue { .mcv => |mcv| switch (mcv) { .none => .none, .undef => unreachable, + .lea_symbol => |sym_index| .{ .lea_symbol = .{ .sym = sym_index } }, .load_symbol => |sym_index| .{ .load_symbol = .{ .sym = sym_index } }, .load_tlv => |sym_index| .{ .lea_tlv = sym_index }, .immediate => |imm| .{ .immediate = imm }, diff --git a/src/arch/riscv64/Emit.zig b/src/arch/riscv64/Emit.zig index 137bc395721e..a4fadad52655 100644 --- a/src/arch/riscv64/Emit.zig +++ b/src/arch/riscv64/Emit.zig @@ -43,31 +43,19 @@ pub fn emitMir(emit: *Emit) Error!void { .fmt = std.meta.activeTag(lowered_inst), }), .load_symbol_reloc => |symbol| { - const is_obj_or_static_lib = switch (emit.lower.output_mode) { - .Exe => false, - .Obj => true, - .Lib => emit.lower.link_mode == .static, - }; - const elf_file = emit.bin_file.cast(.elf).?; const zo = elf_file.zigObjectPtr().?; const atom_ptr = zo.symbol(symbol.atom_index).atom(elf_file).?; const sym = zo.symbol(symbol.sym_index); - var hi_r_type: u32 = @intFromEnum(std.elf.R_RISCV.HI20); - var lo_r_type: u32 = @intFromEnum(std.elf.R_RISCV.LO12_I); - - if (sym.flags.needs_zig_got and !is_obj_or_static_lib) { - _ = try sym.getOrCreateZigGotEntry(symbol.sym_index, elf_file); - - hi_r_type = Elf.R_ZIG_GOT_HI20; - lo_r_type = Elf.R_ZIG_GOT_LO12; - } else if (sym.flags.needs_got) { - hi_r_type = Elf.R_GOT_HI20_STATIC; // TODO: rework this #20887 - lo_r_type = Elf.R_GOT_LO12_I_STATIC; // TODO: rework this #20887 + if (sym.flags.is_extern_ptr and emit.lower.pic) { + return emit.fail("emit GOT relocation for symbol '{s}'", .{sym.name(elf_file)}); } + const hi_r_type: u32 = @intFromEnum(std.elf.R_RISCV.HI20); + const lo_r_type: u32 = @intFromEnum(std.elf.R_RISCV.LO12_I); + try atom_ptr.addReloc(elf_file, .{ .r_offset = start_offset, .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | hi_r_type, diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index bba8cdec3481..d0b0eedc5756 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -1349,34 +1349,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier // Due to incremental compilation, how function calls are generated depends // on linking. if (try self.air.value(callee, pt)) |func_value| switch (ip.indexToKey(func_value.toIntern())) { - .func => |func| { - const got_addr = if (self.bin_file.cast(.elf)) |elf_file| blk: { - const zo = elf_file.zigObjectPtr().?; - const sym_index = try zo.getOrCreateMetadataForNav(elf_file, func.owner_nav); - const sym = zo.symbol(sym_index); - _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file); - break :blk @as(u32, @intCast(sym.zigGotAddress(elf_file))); - } else @panic("TODO SPARCv9 currently does not support non-ELF binaries"); - - try self.genSetReg(Type.usize, .o7, .{ .memory = got_addr }); - - _ = try self.addInst(.{ - .tag = .jmpl, - .data = .{ - .arithmetic_3op = .{ - .is_imm = false, - .rd = .o7, - .rs1 = .o7, - .rs2_or_imm = .{ .rs2 = .g0 }, - }, - }, - }); - - // TODO Find a way to fill this delay slot - _ = try self.addInst(.{ - .tag = .nop, - .data = .{ .nop = {} }, - }); + .func => { + return self.fail("TODO implement calling functions", .{}); }, .@"extern" => { return self.fail("TODO implement calling extern functions", .{}); @@ -4153,7 +4127,7 @@ fn genTypedValue(self: *Self, val: Value) InnerError!MCValue { .mcv => |mcv| switch (mcv) { .none => .none, .undef => .undef, - .load_got, .load_symbol, .load_direct, .load_tlv => unreachable, // TODO + .load_got, .load_symbol, .load_direct, .load_tlv, .lea_symbol => unreachable, // TODO .immediate => |imm| .{ .immediate = imm }, .memory => |addr| .{ .memory = addr }, }, diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index a36bd981bcc8..bfcc29348cb9 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -1379,14 +1379,22 @@ fn asmImmediate(self: *Self, tag: Mir.Inst.FixedTag, imm: Immediate) !void { .ops = switch (imm) { .signed => .i_s, .unsigned => .i_u, + .reloc => .rel, }, - .data = .{ .i = .{ - .fixes = tag[0], - .i = switch (imm) { - .signed => |s| @bitCast(s), - .unsigned => |u| @intCast(u), + .data = switch (imm) { + .reloc => |x| reloc: { + assert(tag[0] == ._); + break :reloc .{ .reloc = x }; }, - } }, + .signed, .unsigned => .{ .i = .{ + .fixes = tag[0], + .i = switch (imm) { + .signed => |s| @bitCast(s), + .unsigned => |u| @intCast(u), + .reloc => unreachable, + }, + } }, + }, }); } @@ -1406,6 +1414,7 @@ fn asmRegisterImmediate(self: *Self, tag: Mir.Inst.FixedTag, reg: Register, imm: const ops: Mir.Inst.Ops = switch (imm) { .signed => .ri_s, .unsigned => |u| if (math.cast(u32, u)) |_| .ri_u else .ri64, + .reloc => unreachable, }; _ = try self.addInst(.{ .tag = tag[1], @@ -1417,6 +1426,7 @@ fn asmRegisterImmediate(self: *Self, tag: Mir.Inst.FixedTag, reg: Register, imm: .i = switch (imm) { .signed => |s| @bitCast(s), .unsigned => |u| @intCast(u), + .reloc => unreachable, }, } }, .ri64 => .{ .rx = .{ @@ -1488,6 +1498,7 @@ fn asmRegisterRegisterRegisterImmediate( .i = switch (imm) { .signed => |s| @bitCast(@as(i8, @intCast(s))), .unsigned => |u| @intCast(u), + .reloc => unreachable, }, } }, }); @@ -1505,6 +1516,7 @@ fn asmRegisterRegisterImmediate( .ops = switch (imm) { .signed => .rri_s, .unsigned => .rri_u, + .reloc => unreachable, }, .data = .{ .rri = .{ .fixes = tag[0], @@ -1513,6 +1525,7 @@ fn asmRegisterRegisterImmediate( .i = switch (imm) { .signed => |s| @bitCast(s), .unsigned => |u| @intCast(u), + .reloc => unreachable, }, } }, }); @@ -1610,6 +1623,7 @@ fn asmRegisterMemoryImmediate( if (switch (imm) { .signed => |s| if (math.cast(i16, s)) |x| @as(u16, @bitCast(x)) else null, .unsigned => |u| math.cast(u16, u), + .reloc => unreachable, }) |small_imm| { _ = try self.addInst(.{ .tag = tag[1], @@ -1625,6 +1639,7 @@ fn asmRegisterMemoryImmediate( const payload = try self.addExtra(Mir.Imm32{ .imm = switch (imm) { .signed => |s| @bitCast(s), .unsigned => unreachable, + .reloc => unreachable, } }); assert(payload + 1 == try self.addExtra(Mir.Memory.encode(m))); _ = try self.addInst(.{ @@ -1632,6 +1647,7 @@ fn asmRegisterMemoryImmediate( .ops = switch (imm) { .signed => .rmi_s, .unsigned => .rmi_u, + .reloc => unreachable, }, .data = .{ .rx = .{ .fixes = tag[0], @@ -1679,6 +1695,7 @@ fn asmMemoryImmediate(self: *Self, tag: Mir.Inst.FixedTag, m: Memory, imm: Immed const payload = try self.addExtra(Mir.Imm32{ .imm = switch (imm) { .signed => |s| @bitCast(s), .unsigned => |u| @intCast(u), + .reloc => unreachable, } }); assert(payload + 1 == try self.addExtra(Mir.Memory.encode(m))); _ = try self.addInst(.{ @@ -1686,6 +1703,7 @@ fn asmMemoryImmediate(self: *Self, tag: Mir.Inst.FixedTag, m: Memory, imm: Immed .ops = switch (imm) { .signed => .mi_s, .unsigned => .mi_u, + .reloc => unreachable, }, .data = .{ .x = .{ .fixes = tag[0], @@ -12310,33 +12328,10 @@ fn genCall(self: *Self, info: union(enum) { if (self.bin_file.cast(.elf)) |elf_file| { const zo = elf_file.zigObjectPtr().?; const sym_index = try zo.getOrCreateMetadataForNav(elf_file, func.owner_nav); - if (self.mod.pic) { - const callee_reg: Register = switch (resolved_cc) { - .SysV => callee: { - if (!fn_info.is_var_args) break :callee .rax; - const param_regs = abi.getCAbiIntParamRegs(resolved_cc); - break :callee if (call_info.gp_count < param_regs.len) - param_regs[call_info.gp_count] - else - .r10; - }, - .Win64 => .rax, - else => unreachable, - }; - try self.genSetReg( - callee_reg, - Type.usize, - .{ .load_symbol = .{ .sym = sym_index } }, - .{}, - ); - try self.asmRegister(.{ ._, .call }, callee_reg); - } else try self.asmMemory(.{ ._, .call }, .{ - .base = .{ .reloc = .{ - .atom_index = try self.owner.getSymbolIndex(self), - .sym_index = sym_index, - } }, - .mod = .{ .rm = .{ .size = .qword } }, - }); + try self.asmImmediate(.{ ._, .call }, Immediate.rel(.{ + .atom_index = try self.owner.getSymbolIndex(self), + .sym_index = sym_index, + })); } else if (self.bin_file.cast(.coff)) |coff_file| { const atom = try coff_file.getOrCreateAtomForNav(func.owner_nav); const sym_index = coff_file.getAtom(atom).getSymbolIndex().?; @@ -12365,7 +12360,16 @@ fn genCall(self: *Self, info: union(enum) { }); } else unreachable; }, - .@"extern" => |@"extern"| try self.genExternSymbolRef( + .@"extern" => |@"extern"| if (self.bin_file.cast(.elf)) |elf_file| { + const target_sym_index = try elf_file.getGlobalSymbol( + @"extern".name.toSlice(ip), + @"extern".lib_name.toSlice(ip), + ); + try self.asmImmediate(.{ ._, .call }, Immediate.rel(.{ + .atom_index = try self.owner.getSymbolIndex(self), + .sym_index = target_sym_index, + })); + } else try self.genExternSymbolRef( .call, @"extern".lib_name.toSlice(ip), @"extern".name.toSlice(ip), @@ -12377,7 +12381,13 @@ fn genCall(self: *Self, info: union(enum) { try self.genSetReg(.rax, Type.usize, .{ .air_ref = callee }, .{}); try self.asmRegister(.{ ._, .call }, .rax); }, - .lib => |lib| try self.genExternSymbolRef(.call, lib.lib, lib.callee), + .lib => |lib| if (self.bin_file.cast(.elf)) |elf_file| { + const target_sym_index = try elf_file.getGlobalSymbol(lib.callee, lib.lib); + try self.asmImmediate(.{ ._, .call }, Immediate.rel(.{ + .atom_index = try self.owner.getSymbolIndex(self), + .sym_index = target_sym_index, + })); + } else try self.genExternSymbolRef(.call, lib.lib, lib.callee), } return call_info.return_value.short; } @@ -14096,8 +14106,8 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { .{ .reg = try self.copyToTmpRegister(Type.usize, .{ .lea_got = sym_index }) } else return self.fail("invalid modifier: '{s}'", .{modifier}), - .load_symbol => |sym_off| if (mem.eql(u8, modifier, "P")) - .{ .reg = try self.copyToTmpRegister(Type.usize, .{ .load_symbol = sym_off }) } + .lea_symbol => |sym_off| if (mem.eql(u8, modifier, "P")) + .{ .reg = try self.copyToTmpRegister(Type.usize, .{ .lea_symbol = sym_off }) } else return self.fail("invalid modifier: '{s}'", .{modifier}), else => return self.fail("invalid constraint: '{s}'", .{op_str}), @@ -15253,16 +15263,7 @@ fn genExternSymbolRef( callee: []const u8, ) InnerError!void { const atom_index = try self.owner.getSymbolIndex(self); - if (self.bin_file.cast(.elf)) |elf_file| { - _ = try self.addInst(.{ - .tag = tag, - .ops = .extern_fn_reloc, - .data = .{ .reloc = .{ - .atom_index = atom_index, - .sym_index = try elf_file.getGlobalSymbol(callee, lib), - } }, - }); - } else if (self.bin_file.cast(.coff)) |coff_file| { + if (self.bin_file.cast(.coff)) |coff_file| { const global_index = try coff_file.getGlobalSymbol(callee, lib); _ = try self.addInst(.{ .tag = .mov, @@ -15306,7 +15307,7 @@ fn genLazySymbolRef( if (self.mod.pic) { switch (tag) { .lea, .call => try self.genSetReg(reg, Type.usize, .{ - .load_symbol = .{ .sym = sym_index }, + .lea_symbol = .{ .sym = sym_index }, }, .{}), .mov => try self.genSetReg(reg, Type.usize, .{ .load_symbol = .{ .sym = sym_index }, @@ -15324,14 +15325,11 @@ fn genLazySymbolRef( .sym_index = sym_index, }; switch (tag) { - .lea, .mov => try self.asmRegisterMemory(.{ ._, .mov }, reg.to64(), .{ - .base = .{ .reloc = reloc }, - .mod = .{ .rm = .{ .size = .qword } }, - }), - .call => try self.asmMemory(.{ ._, .call }, .{ + .lea, .mov => try self.asmRegisterMemory(.{ ._, tag }, reg.to64(), .{ .base = .{ .reloc = reloc }, .mod = .{ .rm = .{ .size = .qword } }, }), + .call => try self.asmImmediate(.{ ._, .call }, Immediate.rel(reloc)), else => unreachable, } } @@ -18790,6 +18788,7 @@ fn genTypedValue(self: *Self, val: Value) InnerError!MCValue { .immediate => |imm| .{ .immediate = imm }, .memory => |addr| .{ .memory = addr }, .load_symbol => |sym_index| .{ .load_symbol = .{ .sym = sym_index } }, + .lea_symbol => |sym_index| .{ .lea_symbol = .{ .sym = sym_index } }, .load_direct => |sym_index| .{ .load_direct = sym_index }, .load_got => |sym_index| .{ .lea_got = sym_index }, .load_tlv => |sym_index| .{ .lea_tlv = sym_index }, diff --git a/src/arch/x86_64/Disassembler.zig b/src/arch/x86_64/Disassembler.zig index e0117fa17b78..d21cec28ab20 100644 --- a/src/arch/x86_64/Disassembler.zig +++ b/src/arch/x86_64/Disassembler.zig @@ -8,7 +8,7 @@ const bits = @import("bits.zig"); const encoder = @import("encoder.zig"); const Encoding = @import("Encoding.zig"); -const Immediate = bits.Immediate; +const Immediate = Instruction.Immediate; const Instruction = encoder.Instruction; const LegacyPrefixes = encoder.LegacyPrefixes; const Memory = Instruction.Memory; diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index b6c1fbb96882..693dca1da65c 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -110,21 +110,11 @@ pub fn emitMir(emit: *Emit) Error!void { }); }, .linker_reloc => |data| if (emit.lower.bin_file.cast(.elf)) |elf_file| { - const is_obj_or_static_lib = switch (emit.lower.output_mode) { - .Exe => false, - .Obj => true, - .Lib => emit.lower.link_mode == .static, - }; const zo = elf_file.zigObjectPtr().?; const atom = zo.symbol(data.atom_index).atom(elf_file).?; const sym = zo.symbol(data.sym_index); - if (sym.flags.needs_zig_got and !is_obj_or_static_lib) { - _ = try sym.getOrCreateZigGotEntry(data.sym_index, elf_file); - } if (emit.lower.pic) { - const r_type: u32 = if (sym.flags.needs_zig_got and !is_obj_or_static_lib) - link.File.Elf.R_ZIG_GOTPCREL - else if (sym.flags.needs_got) + const r_type: u32 = if (sym.flags.is_extern_ptr) @intFromEnum(std.elf.R_X86_64.GOTPCREL) else @intFromEnum(std.elf.R_X86_64.PC32); @@ -134,28 +124,15 @@ pub fn emitMir(emit: *Emit) Error!void { .r_addend = -4, }); } else { - if (lowered_inst.encoding.mnemonic == .call and sym.flags.needs_zig_got and is_obj_or_static_lib) { - const r_type = @intFromEnum(std.elf.R_X86_64.PC32); - try atom.addReloc(elf_file, .{ - .r_offset = end_offset - 4, - .r_info = (@as(u64, @intCast(data.sym_index)) << 32) | r_type, - .r_addend = -4, - }); - } else { - const r_type: u32 = if (sym.flags.needs_zig_got and !is_obj_or_static_lib) - link.File.Elf.R_ZIG_GOT32 - else if (sym.flags.needs_got) - @intFromEnum(std.elf.R_X86_64.GOT32) - else if (sym.flags.is_tls) - @intFromEnum(std.elf.R_X86_64.TPOFF32) - else - @intFromEnum(std.elf.R_X86_64.@"32"); - try atom.addReloc(elf_file, .{ - .r_offset = end_offset - 4, - .r_info = (@as(u64, @intCast(data.sym_index)) << 32) | r_type, - .r_addend = 0, - }); - } + const r_type: u32 = if (sym.flags.is_tls) + @intFromEnum(std.elf.R_X86_64.TPOFF32) + else + @intFromEnum(std.elf.R_X86_64.@"32"); + try atom.addReloc(elf_file, .{ + .r_offset = end_offset - 4, + .r_info = (@as(u64, @intCast(data.sym_index)) << 32) | r_type, + .r_addend = 0, + }); } } else if (emit.lower.bin_file.cast(.macho)) |macho_file| { const is_obj_or_static_lib = switch (emit.lower.output_mode) { diff --git a/src/arch/x86_64/Lower.zig b/src/arch/x86_64/Lower.zig index 11fe279dd9dd..15322ce4f6b6 100644 --- a/src/arch/x86_64/Lower.zig +++ b/src/arch/x86_64/Lower.zig @@ -397,33 +397,40 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) } _ = lower.reloc(.{ .linker_reloc = sym }); - break :op if (lower.pic) switch (mnemonic) { + if (lower.pic) switch (mnemonic) { .lea => { + if (elf_sym.flags.is_extern_ptr) emit_mnemonic = .mov; break :op .{ .mem = Memory.rip(mem_op.sib.ptr_size, 0) }; }, .mov => { - if (is_obj_or_static_lib and elf_sym.flags.needs_zig_got) emit_mnemonic = .lea; + if (elf_sym.flags.is_extern_ptr) { + const reg = ops[0].reg; + lower.result_insts[lower.result_insts_len] = + try Instruction.new(.none, .mov, &[_]Operand{ + .{ .reg = reg.to64() }, + .{ .mem = Memory.rip(.qword, 0) }, + }); + lower.result_insts_len += 1; + break :op .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{ .base = .{ + .reg = reg.to64(), + } }) }; + } break :op .{ .mem = Memory.rip(mem_op.sib.ptr_size, 0) }; }, else => unreachable, } else switch (mnemonic) { - .call => break :op if (is_obj_or_static_lib and elf_sym.flags.needs_zig_got) .{ - .imm = Immediate.s(0), - } else .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{ + .call => break :op .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{ .base = .{ .reg = .ds }, }) }, .lea => { emit_mnemonic = .mov; break :op .{ .imm = Immediate.s(0) }; }, - .mov => { - if (is_obj_or_static_lib and elf_sym.flags.needs_zig_got) emit_mnemonic = .lea; - break :op .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{ - .base = .{ .reg = .ds }, - }) }; - }, + .mov => break :op .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{ + .base = .{ .reg = .ds }, + }) }, else => unreachable, - }; + } } else if (lower.bin_file.cast(.macho)) |macho_file| { const zo = macho_file.getZigObject().?; const macho_sym = zo.symbols.items[sym.sym_index]; @@ -485,7 +492,7 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void { .rrmi => inst.data.rrix.fixes, .mi_u, .mi_s => inst.data.x.fixes, .m => inst.data.x.fixes, - .extern_fn_reloc, .got_reloc, .direct_reloc, .import_reloc, .tlv_reloc => ._, + .extern_fn_reloc, .got_reloc, .direct_reloc, .import_reloc, .tlv_reloc, .rel => ._, else => return lower.fail("TODO lower .{s}", .{@tagName(inst.ops)}), }; try lower.emit(switch (fixes) { @@ -617,7 +624,7 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void { .{ .mem = lower.mem(inst.data.rrix.payload) }, .{ .imm = lower.imm(inst.ops, inst.data.rrix.i) }, }, - .extern_fn_reloc => &.{ + .extern_fn_reloc, .rel => &.{ .{ .imm = lower.reloc(.{ .linker_extern_fn = inst.data.reloc }) }, }, .got_reloc, .direct_reloc, .import_reloc => ops: { @@ -660,7 +667,7 @@ const std = @import("std"); const Air = @import("../../Air.zig"); const Allocator = std.mem.Allocator; const ErrorMsg = Zcu.ErrorMsg; -const Immediate = bits.Immediate; +const Immediate = Instruction.Immediate; const Instruction = encoder.Instruction; const Lower = @This(); const Memory = Instruction.Memory; diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index d2dd6237a5e6..08f2cc3b253a 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -769,7 +769,7 @@ pub const Inst = struct { /// Uses `imm` payload. i_u, /// Relative displacement operand. - /// Uses `imm` payload. + /// Uses `reloc` payload. rel, /// Register, memory operands. /// Uses `rx` payload with extra data of type `Memory`. diff --git a/src/arch/x86_64/bits.zig b/src/arch/x86_64/bits.zig index 6896d6c2e72a..79f9b20924bf 100644 --- a/src/arch/x86_64/bits.zig +++ b/src/arch/x86_64/bits.zig @@ -569,6 +569,7 @@ pub const Memory = struct { pub const Immediate = union(enum) { signed: i32, unsigned: u64, + reloc: Symbol, pub fn u(x: u64) Immediate { return .{ .unsigned = x }; @@ -578,39 +579,19 @@ pub const Immediate = union(enum) { return .{ .signed = x }; } - pub fn asSigned(imm: Immediate, bit_size: u64) i64 { - return switch (imm) { - .signed => |x| switch (bit_size) { - 1, 8 => @as(i8, @intCast(x)), - 16 => @as(i16, @intCast(x)), - 32, 64 => x, - else => unreachable, - }, - .unsigned => |x| switch (bit_size) { - 1, 8 => @as(i8, @bitCast(@as(u8, @intCast(x)))), - 16 => @as(i16, @bitCast(@as(u16, @intCast(x)))), - 32 => @as(i32, @bitCast(@as(u32, @intCast(x)))), - 64 => @bitCast(x), - else => unreachable, - }, - }; + pub fn rel(symbol: Symbol) Immediate { + return .{ .reloc = symbol }; } - pub fn asUnsigned(imm: Immediate, bit_size: u64) u64 { - return switch (imm) { - .signed => |x| switch (bit_size) { - 1, 8 => @as(u8, @bitCast(@as(i8, @intCast(x)))), - 16 => @as(u16, @bitCast(@as(i16, @intCast(x)))), - 32, 64 => @as(u32, @bitCast(x)), - else => unreachable, - }, - .unsigned => |x| switch (bit_size) { - 1, 8 => @as(u8, @intCast(x)), - 16 => @as(u16, @intCast(x)), - 32 => @as(u32, @intCast(x)), - 64 => x, - else => unreachable, - }, - }; + pub fn format( + imm: Immediate, + comptime fmt: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, + ) @TypeOf(writer).Error!void { + switch (imm) { + .reloc => |x| try std.fmt.formatType(x, fmt, options, writer, 0), + inline else => |x| try writer.print("{d}", .{x}), + } } }; diff --git a/src/arch/x86_64/encoder.zig b/src/arch/x86_64/encoder.zig index 5f49443934fa..b525b0e11e8e 100644 --- a/src/arch/x86_64/encoder.zig +++ b/src/arch/x86_64/encoder.zig @@ -7,7 +7,6 @@ const testing = std.testing; const bits = @import("bits.zig"); const Encoding = @import("Encoding.zig"); const FrameIndex = bits.FrameIndex; -const Immediate = bits.Immediate; const Register = bits.Register; const Symbol = bits.Symbol; @@ -28,6 +27,55 @@ pub const Instruction = struct { repnz, }; + pub const Immediate = union(enum) { + signed: i32, + unsigned: u64, + + pub fn u(x: u64) Immediate { + return .{ .unsigned = x }; + } + + pub fn s(x: i32) Immediate { + return .{ .signed = x }; + } + + pub fn asSigned(imm: Immediate, bit_size: u64) i64 { + return switch (imm) { + .signed => |x| switch (bit_size) { + 1, 8 => @as(i8, @intCast(x)), + 16 => @as(i16, @intCast(x)), + 32, 64 => x, + else => unreachable, + }, + .unsigned => |x| switch (bit_size) { + 1, 8 => @as(i8, @bitCast(@as(u8, @intCast(x)))), + 16 => @as(i16, @bitCast(@as(u16, @intCast(x)))), + 32 => @as(i32, @bitCast(@as(u32, @intCast(x)))), + 64 => @bitCast(x), + else => unreachable, + }, + }; + } + + pub fn asUnsigned(imm: Immediate, bit_size: u64) u64 { + return switch (imm) { + .signed => |x| switch (bit_size) { + 1, 8 => @as(u8, @bitCast(@as(i8, @intCast(x)))), + 16 => @as(u16, @bitCast(@as(i16, @intCast(x)))), + 32, 64 => @as(u32, @bitCast(x)), + else => unreachable, + }, + .unsigned => |x| switch (bit_size) { + 1, 8 => @as(u8, @intCast(x)), + 16 => @as(u16, @intCast(x)), + 32 => @as(u32, @intCast(x)), + 64 => x, + else => unreachable, + }, + }; + } + }; + pub const Memory = union(enum) { sib: Sib, rip: Rip, @@ -1119,7 +1167,7 @@ test "encode" { const inst = try Instruction.new(.none, .mov, &.{ .{ .reg = .rbx }, - .{ .imm = Immediate.u(4) }, + .{ .imm = Instruction.Immediate.u(4) }, }); try inst.encode(buf.writer(), .{}); try testing.expectEqualSlices(u8, &.{ 0x48, 0xc7, 0xc3, 0x4, 0x0, 0x0, 0x0 }, buf.items); @@ -1129,47 +1177,47 @@ test "lower I encoding" { var enc = TestEncode{}; try enc.encode(.push, &.{ - .{ .imm = Immediate.u(0x10) }, + .{ .imm = Instruction.Immediate.u(0x10) }, }); try expectEqualHexStrings("\x6A\x10", enc.code(), "push 0x10"); try enc.encode(.push, &.{ - .{ .imm = Immediate.u(0x1000) }, + .{ .imm = Instruction.Immediate.u(0x1000) }, }); try expectEqualHexStrings("\x66\x68\x00\x10", enc.code(), "push 0x1000"); try enc.encode(.push, &.{ - .{ .imm = Immediate.u(0x10000000) }, + .{ .imm = Instruction.Immediate.u(0x10000000) }, }); try expectEqualHexStrings("\x68\x00\x00\x00\x10", enc.code(), "push 0x10000000"); try enc.encode(.adc, &.{ .{ .reg = .rax }, - .{ .imm = Immediate.u(0x10000000) }, + .{ .imm = Instruction.Immediate.u(0x10000000) }, }); try expectEqualHexStrings("\x48\x15\x00\x00\x00\x10", enc.code(), "adc rax, 0x10000000"); try enc.encode(.add, &.{ .{ .reg = .al }, - .{ .imm = Immediate.u(0x10) }, + .{ .imm = Instruction.Immediate.u(0x10) }, }); try expectEqualHexStrings("\x04\x10", enc.code(), "add al, 0x10"); try enc.encode(.add, &.{ .{ .reg = .rax }, - .{ .imm = Immediate.u(0x10) }, + .{ .imm = Instruction.Immediate.u(0x10) }, }); try expectEqualHexStrings("\x48\x83\xC0\x10", enc.code(), "add rax, 0x10"); try enc.encode(.sbb, &.{ .{ .reg = .ax }, - .{ .imm = Immediate.u(0x10) }, + .{ .imm = Instruction.Immediate.u(0x10) }, }); try expectEqualHexStrings("\x66\x1D\x10\x00", enc.code(), "sbb ax, 0x10"); try enc.encode(.xor, &.{ .{ .reg = .al }, - .{ .imm = Immediate.u(0x10) }, + .{ .imm = Instruction.Immediate.u(0x10) }, }); try expectEqualHexStrings("\x34\x10", enc.code(), "xor al, 0x10"); } @@ -1179,43 +1227,43 @@ test "lower MI encoding" { try enc.encode(.mov, &.{ .{ .reg = .r12 }, - .{ .imm = Immediate.u(0x1000) }, + .{ .imm = Instruction.Immediate.u(0x1000) }, }); try expectEqualHexStrings("\x49\xC7\xC4\x00\x10\x00\x00", enc.code(), "mov r12, 0x1000"); try enc.encode(.mov, &.{ .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .r12 } }) }, - .{ .imm = Immediate.u(0x10) }, + .{ .imm = Instruction.Immediate.u(0x10) }, }); try expectEqualHexStrings("\x41\xC6\x04\x24\x10", enc.code(), "mov BYTE PTR [r12], 0x10"); try enc.encode(.mov, &.{ .{ .reg = .r12 }, - .{ .imm = Immediate.u(0x1000) }, + .{ .imm = Instruction.Immediate.u(0x1000) }, }); try expectEqualHexStrings("\x49\xC7\xC4\x00\x10\x00\x00", enc.code(), "mov r12, 0x1000"); try enc.encode(.mov, &.{ .{ .reg = .r12 }, - .{ .imm = Immediate.u(0x1000) }, + .{ .imm = Instruction.Immediate.u(0x1000) }, }); try expectEqualHexStrings("\x49\xC7\xC4\x00\x10\x00\x00", enc.code(), "mov r12, 0x1000"); try enc.encode(.mov, &.{ .{ .reg = .rax }, - .{ .imm = Immediate.u(0x10) }, + .{ .imm = Instruction.Immediate.u(0x10) }, }); try expectEqualHexStrings("\x48\xc7\xc0\x10\x00\x00\x00", enc.code(), "mov rax, 0x10"); try enc.encode(.mov, &.{ .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .r11 } }) }, - .{ .imm = Immediate.u(0x10) }, + .{ .imm = Instruction.Immediate.u(0x10) }, }); try expectEqualHexStrings("\x41\xc7\x03\x10\x00\x00\x00", enc.code(), "mov DWORD PTR [r11], 0x10"); try enc.encode(.mov, &.{ .{ .mem = Instruction.Memory.rip(.qword, 0x10) }, - .{ .imm = Immediate.u(0x10) }, + .{ .imm = Instruction.Immediate.u(0x10) }, }); try expectEqualHexStrings( "\x48\xC7\x05\x10\x00\x00\x00\x10\x00\x00\x00", @@ -1225,19 +1273,19 @@ test "lower MI encoding" { try enc.encode(.mov, &.{ .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .rbp }, .disp = -8 }) }, - .{ .imm = Immediate.u(0x10) }, + .{ .imm = Instruction.Immediate.u(0x10) }, }); try expectEqualHexStrings("\x48\xc7\x45\xf8\x10\x00\x00\x00", enc.code(), "mov QWORD PTR [rbp - 8], 0x10"); try enc.encode(.mov, &.{ .{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp }, .disp = -2 }) }, - .{ .imm = Immediate.s(-16) }, + .{ .imm = Instruction.Immediate.s(-16) }, }); try expectEqualHexStrings("\x66\xC7\x45\xFE\xF0\xFF", enc.code(), "mov WORD PTR [rbp - 2], -16"); try enc.encode(.mov, &.{ .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .rbp }, .disp = -1 }) }, - .{ .imm = Immediate.u(0x10) }, + .{ .imm = Instruction.Immediate.u(0x10) }, }); try expectEqualHexStrings("\xC6\x45\xFF\x10", enc.code(), "mov BYTE PTR [rbp - 1], 0x10"); @@ -1247,7 +1295,7 @@ test "lower MI encoding" { .disp = 0x10000000, .scale_index = .{ .scale = 2, .index = .rcx }, }) }, - .{ .imm = Immediate.u(0x10) }, + .{ .imm = Instruction.Immediate.u(0x10) }, }); try expectEqualHexStrings( "\x48\xC7\x04\x4D\x00\x00\x00\x10\x10\x00\x00\x00", @@ -1257,43 +1305,43 @@ test "lower MI encoding" { try enc.encode(.adc, &.{ .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .rbp }, .disp = -0x10 }) }, - .{ .imm = Immediate.u(0x10) }, + .{ .imm = Instruction.Immediate.u(0x10) }, }); try expectEqualHexStrings("\x80\x55\xF0\x10", enc.code(), "adc BYTE PTR [rbp - 0x10], 0x10"); try enc.encode(.adc, &.{ .{ .mem = Instruction.Memory.rip(.qword, 0) }, - .{ .imm = Immediate.u(0x10) }, + .{ .imm = Instruction.Immediate.u(0x10) }, }); try expectEqualHexStrings("\x48\x83\x15\x00\x00\x00\x00\x10", enc.code(), "adc QWORD PTR [rip], 0x10"); try enc.encode(.adc, &.{ .{ .reg = .rax }, - .{ .imm = Immediate.u(0x10) }, + .{ .imm = Instruction.Immediate.u(0x10) }, }); try expectEqualHexStrings("\x48\x83\xD0\x10", enc.code(), "adc rax, 0x10"); try enc.encode(.add, &.{ .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .rdx }, .disp = -8 }) }, - .{ .imm = Immediate.u(0x10) }, + .{ .imm = Instruction.Immediate.u(0x10) }, }); try expectEqualHexStrings("\x83\x42\xF8\x10", enc.code(), "add DWORD PTR [rdx - 8], 0x10"); try enc.encode(.add, &.{ .{ .reg = .rax }, - .{ .imm = Immediate.u(0x10) }, + .{ .imm = Instruction.Immediate.u(0x10) }, }); try expectEqualHexStrings("\x48\x83\xC0\x10", enc.code(), "add rax, 0x10"); try enc.encode(.add, &.{ .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .rbp }, .disp = -0x10 }) }, - .{ .imm = Immediate.s(-0x10) }, + .{ .imm = Instruction.Immediate.s(-0x10) }, }); try expectEqualHexStrings("\x48\x83\x45\xF0\xF0", enc.code(), "add QWORD PTR [rbp - 0x10], -0x10"); try enc.encode(.@"and", &.{ .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) }, - .{ .imm = Immediate.u(0x10) }, + .{ .imm = Instruction.Immediate.u(0x10) }, }); try expectEqualHexStrings( "\x83\x24\x25\x00\x00\x00\x10\x10", @@ -1303,7 +1351,7 @@ test "lower MI encoding" { try enc.encode(.@"and", &.{ .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .es }, .disp = 0x10000000 }) }, - .{ .imm = Immediate.u(0x10) }, + .{ .imm = Instruction.Immediate.u(0x10) }, }); try expectEqualHexStrings( "\x26\x83\x24\x25\x00\x00\x00\x10\x10", @@ -1313,7 +1361,7 @@ test "lower MI encoding" { try enc.encode(.@"and", &.{ .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .r12 }, .disp = 0x10000000 }) }, - .{ .imm = Immediate.u(0x10) }, + .{ .imm = Instruction.Immediate.u(0x10) }, }); try expectEqualHexStrings( "\x41\x83\xA4\x24\x00\x00\x00\x10\x10", @@ -1323,7 +1371,7 @@ test "lower MI encoding" { try enc.encode(.sub, &.{ .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .r11 }, .disp = 0x10000000 }) }, - .{ .imm = Immediate.u(0x10) }, + .{ .imm = Instruction.Immediate.u(0x10) }, }); try expectEqualHexStrings( "\x41\x83\xAB\x00\x00\x00\x10\x10", @@ -1542,14 +1590,14 @@ test "lower RMI encoding" { try enc.encode(.imul, &.{ .{ .reg = .r11 }, .{ .reg = .r12 }, - .{ .imm = Immediate.s(-2) }, + .{ .imm = Instruction.Immediate.s(-2) }, }); try expectEqualHexStrings("\x4D\x6B\xDC\xFE", enc.code(), "imul r11, r12, -2"); try enc.encode(.imul, &.{ .{ .reg = .r11 }, .{ .mem = Instruction.Memory.rip(.qword, -16) }, - .{ .imm = Immediate.s(-1024) }, + .{ .imm = Instruction.Immediate.s(-1024) }, }); try expectEqualHexStrings( "\x4C\x69\x1D\xF0\xFF\xFF\xFF\x00\xFC\xFF\xFF", @@ -1560,7 +1608,7 @@ test "lower RMI encoding" { try enc.encode(.imul, &.{ .{ .reg = .bx }, .{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp }, .disp = -16 }) }, - .{ .imm = Immediate.s(-1024) }, + .{ .imm = Instruction.Immediate.s(-1024) }, }); try expectEqualHexStrings( "\x66\x69\x5D\xF0\x00\xFC", @@ -1571,7 +1619,7 @@ test "lower RMI encoding" { try enc.encode(.imul, &.{ .{ .reg = .bx }, .{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp }, .disp = -16 }) }, - .{ .imm = Immediate.u(1024) }, + .{ .imm = Instruction.Immediate.u(1024) }, }); try expectEqualHexStrings( "\x66\x69\x5D\xF0\x00\x04", @@ -1687,7 +1735,7 @@ test "lower M encoding" { try expectEqualHexStrings("\x65\xFF\x14\x25\x00\x00\x00\x00", enc.code(), "call gs:0x0"); try enc.encode(.call, &.{ - .{ .imm = Immediate.s(0) }, + .{ .imm = Instruction.Immediate.s(0) }, }); try expectEqualHexStrings("\xE8\x00\x00\x00\x00", enc.code(), "call 0x0"); @@ -1746,7 +1794,7 @@ test "lower OI encoding" { try enc.encode(.mov, &.{ .{ .reg = .rax }, - .{ .imm = Immediate.u(0x1000000000000000) }, + .{ .imm = Instruction.Immediate.u(0x1000000000000000) }, }); try expectEqualHexStrings( "\x48\xB8\x00\x00\x00\x00\x00\x00\x00\x10", @@ -1756,7 +1804,7 @@ test "lower OI encoding" { try enc.encode(.mov, &.{ .{ .reg = .r11 }, - .{ .imm = Immediate.u(0x1000000000000000) }, + .{ .imm = Instruction.Immediate.u(0x1000000000000000) }, }); try expectEqualHexStrings( "\x49\xBB\x00\x00\x00\x00\x00\x00\x00\x10", @@ -1766,19 +1814,19 @@ test "lower OI encoding" { try enc.encode(.mov, &.{ .{ .reg = .r11d }, - .{ .imm = Immediate.u(0x10000000) }, + .{ .imm = Instruction.Immediate.u(0x10000000) }, }); try expectEqualHexStrings("\x41\xBB\x00\x00\x00\x10", enc.code(), "mov r11d, 0x10000000"); try enc.encode(.mov, &.{ .{ .reg = .r11w }, - .{ .imm = Immediate.u(0x1000) }, + .{ .imm = Instruction.Immediate.u(0x1000) }, }); try expectEqualHexStrings("\x66\x41\xBB\x00\x10", enc.code(), "mov r11w, 0x1000"); try enc.encode(.mov, &.{ .{ .reg = .r11b }, - .{ .imm = Immediate.u(0x10) }, + .{ .imm = Instruction.Immediate.u(0x10) }, }); try expectEqualHexStrings("\x41\xB3\x10", enc.code(), "mov r11b, 0x10"); } @@ -1900,7 +1948,7 @@ test "invalid instruction" { .{ .reg = .r12d }, }); try invalidInstruction(.push, &.{ - .{ .imm = Immediate.u(0x1000000000000000) }, + .{ .imm = Instruction.Immediate.u(0x1000000000000000) }, }); } @@ -2213,7 +2261,7 @@ const Assembler = struct { .immediate => { const is_neg = if (as.expect(.minus)) |_| true else |_| false; const imm_tok = try as.expect(.numeral); - const imm: Immediate = if (is_neg) blk: { + const imm: Instruction.Immediate = if (is_neg) blk: { const imm = try std.fmt.parseInt(i32, as.source(imm_tok), 0); break :blk .{ .signed = imm * -1 }; } else .{ .unsigned = try std.fmt.parseInt(u64, as.source(imm_tok), 0) }; diff --git a/src/codegen.zig b/src/codegen.zig index f2fa60fdf8be..89b4a8a29174 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -828,6 +828,9 @@ pub const GenResult = union(enum) { /// Reference to memory location but deferred until linker allocated the Decl in memory. /// Traditionally, this corresponds to emitting a relocation in a relocatable object file. load_symbol: u32, + /// Reference to memory location but deferred until linker allocated the Decl in memory. + /// Traditionally, this corresponds to emitting a relocation in a relocatable object file. + lea_symbol: u32, }; fn mcv(val: MCValue) GenResult { @@ -895,16 +898,15 @@ fn genNavRef( if (lf.cast(.elf)) |elf_file| { const zo = elf_file.zigObjectPtr().?; if (is_extern) { - // TODO audit this const sym_index = try elf_file.getGlobalSymbol(name.toSlice(ip), lib_name.toSlice(ip)); - zo.symbol(sym_index).flags.needs_got = true; - return GenResult.mcv(.{ .load_symbol = sym_index }); + zo.symbol(sym_index).flags.is_extern_ptr = true; + return GenResult.mcv(.{ .lea_symbol = sym_index }); } const sym_index = try zo.getOrCreateMetadataForNav(elf_file, nav_index); if (!single_threaded and is_threadlocal) { return GenResult.mcv(.{ .load_tlv = sym_index }); } - return GenResult.mcv(.{ .load_symbol = sym_index }); + return GenResult.mcv(.{ .lea_symbol = sym_index }); } else if (lf.cast(.macho)) |macho_file| { const zo = macho_file.getZigObject().?; if (is_extern) { diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 103c69202bad..0ef1e9862d4a 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -64,9 +64,6 @@ phdrs: std.ArrayListUnmanaged(elf.Elf64_Phdr) = .{}, /// Tracked loadable segments during incremental linking. /// The index into the program headers of a PT_LOAD program header with Read and Execute flags phdr_zig_load_re_index: ?u16 = null, -/// The index into the program headers of the global offset table. -/// It needs PT_LOAD and Read flags. -phdr_zig_got_index: ?u16 = null, /// The index into the program headers of a PT_LOAD program header with Read flag phdr_zig_load_ro_index: ?u16 = null, /// The index into the program headers of a PT_LOAD program header with Write flag @@ -130,8 +127,6 @@ plt_got: PltGotSection = .{}, copy_rel: CopyRelSection = .{}, /// .rela.plt section rela_plt: std.ArrayListUnmanaged(elf.Elf64_Rela) = .{}, -/// .got.zig section -zig_got: ZigGotSection = .{}, /// SHT_GROUP sections /// Applies only to a relocatable. comdat_group_sections: std.ArrayListUnmanaged(ComdatGroupSection) = .{}, @@ -142,7 +137,6 @@ zig_text_section_index: ?u32 = null, zig_data_rel_ro_section_index: ?u32 = null, zig_data_section_index: ?u32 = null, zig_bss_section_index: ?u32 = null, -zig_got_section_index: ?u32 = null, debug_info_section_index: ?u32 = null, debug_abbrev_section_index: ?u32 = null, @@ -474,7 +468,6 @@ pub fn deinit(self: *Elf) void { self.copy_rel.deinit(gpa); self.rela_dyn.deinit(gpa); self.rela_plt.deinit(gpa); - self.zig_got.deinit(gpa); self.comdat_group_sections.deinit(gpa); } @@ -611,28 +604,13 @@ pub fn initMetadata(self: *Elf, options: InitMetadataOptions) !void { .type = elf.PT_LOAD, .offset = off, .filesz = filesz, - .addr = if (ptr_bit_width >= 32) 0x8000000 else 0x8000, + .addr = if (ptr_bit_width >= 32) 0x4000000 else 0x4000, .memsz = filesz, .@"align" = self.page_size, .flags = elf.PF_X | elf.PF_R | elf.PF_W, }); } - if (self.phdr_zig_got_index == null) { - const alignment = self.page_size; - const filesz = @as(u64, ptr_size) * options.symbol_count_hint; - const off = self.findFreeSpace(filesz, alignment); - self.phdr_zig_got_index = try self.addPhdr(.{ - .type = elf.PT_LOAD, - .offset = off, - .filesz = filesz, - .addr = if (ptr_bit_width >= 32) 0x4000000 else 0x4000, - .memsz = filesz, - .@"align" = alignment, - .flags = elf.PF_R | elf.PF_W, - }); - } - if (self.phdr_zig_load_ro_index == null) { const alignment = self.page_size; const filesz: u64 = 1024; @@ -701,27 +679,6 @@ pub fn initMetadata(self: *Elf, options: InitMetadataOptions) !void { try self.last_atom_and_free_list_table.putNoClobber(gpa, self.zig_text_section_index.?, .{}); } - if (self.zig_got_section_index == null and !self.base.isRelocatable()) { - self.zig_got_section_index = try self.addSection(.{ - .name = try self.insertShString(".got.zig"), - .type = elf.SHT_PROGBITS, - .addralign = ptr_size, - .flags = elf.SHF_ALLOC | elf.SHF_WRITE, - .offset = std.math.maxInt(u64), - }); - const shdr = &self.shdrs.items[self.zig_got_section_index.?]; - const phndx = self.phdr_zig_got_index.?; - const phdr = self.phdrs.items[phndx]; - shdr.sh_addr = phdr.p_vaddr; - shdr.sh_offset = phdr.p_offset; - shdr.sh_size = phdr.p_memsz; - try self.phdr_to_shdr_table.putNoClobber( - gpa, - self.zig_got_section_index.?, - self.phdr_zig_got_index.?, - ); - } - if (self.zig_data_rel_ro_section_index == null) { self.zig_data_rel_ro_section_index = try self.addSection(.{ .name = try self.insertShString(".data.rel.ro.zig"), @@ -900,6 +857,11 @@ pub fn growAllocSection(self: *Elf, shdr_index: u32, needed_size: u64) !void { const shdr = &self.shdrs.items[shdr_index]; const maybe_phdr = if (self.phdr_to_shdr_table.get(shdr_index)) |phndx| &self.phdrs.items[phndx] else null; const is_zerofill = shdr.sh_type == elf.SHT_NOBITS; + log.debug("allocated size {x} of {s}, needed size {x}", .{ + self.allocatedSize(shdr.sh_offset), + self.getShString(shdr.sh_name), + needed_size, + }); if (needed_size > self.allocatedSize(shdr.sh_offset) and !is_zerofill) { const existing_size = shdr.sh_size; @@ -3156,8 +3118,8 @@ fn initSyntheticSections(self: *Elf) !void { }); const needs_rela_dyn = blk: { - if (self.got.flags.needs_rela or self.got.flags.needs_tlsld or - self.zig_got.flags.needs_rela or self.copy_rel.symbols.items.len > 0) break :blk true; + if (self.got.flags.needs_rela or self.got.flags.needs_tlsld or self.copy_rel.symbols.items.len > 0) + break :blk true; if (self.zigObjectPtr()) |zig_object| { if (zig_object.num_dynrelocs > 0) break :blk true; } @@ -3562,7 +3524,6 @@ fn sortPhdrs(self: *Elf) error{OutOfMemory}!void { for (&[_]*?u16{ &self.phdr_zig_load_re_index, - &self.phdr_zig_got_index, &self.phdr_zig_load_ro_index, &self.phdr_zig_load_zerofill_index, &self.phdr_table_index, @@ -3694,7 +3655,6 @@ fn resetShdrIndexes(self: *Elf, backlinks: []const u32) !void { &self.versym_section_index, &self.verneed_section_index, &self.zig_text_section_index, - &self.zig_got_section_index, &self.zig_data_rel_ro_section_index, &self.zig_data_section_index, &self.zig_bss_section_index, @@ -3893,7 +3853,7 @@ fn updateSectionSizes(self: *Elf) !void { } if (self.rela_dyn_section_index) |shndx| { - var num = self.got.numRela(self) + self.copy_rel.numRela() + self.zig_got.numRela(); + var num = self.got.numRela(self) + self.copy_rel.numRela(); if (self.zigObjectPtr()) |zig_object| { num += zig_object.num_dynrelocs; } @@ -4431,15 +4391,6 @@ pub fn updateSymtabSize(self: *Elf) !void { strsize += ctx.strsize; } - if (self.zigObjectPtr()) |_| { - if (self.zig_got_section_index) |_| { - self.zig_got.output_symtab_ctx.ilocal = nlocals + 1; - self.zig_got.updateSymtabSize(self); - nlocals += self.zig_got.output_symtab_ctx.nlocals; - strsize += self.zig_got.output_symtab_ctx.strsize; - } - } - if (self.got_section_index) |_| { self.got.output_symtab_ctx.ilocal = nlocals + 1; self.got.updateSymtabSize(self); @@ -4576,9 +4527,6 @@ fn writeSyntheticSections(self: *Elf) !void { const shdr = self.shdrs.items[shndx]; try self.got.addRela(self); try self.copy_rel.addRela(self); - if (self.zigObjectPtr()) |_| { - try self.zig_got.addRela(self); - } self.sortRelaDyn(); try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.rela_dyn.items), shdr.sh_offset); } @@ -4674,10 +4622,6 @@ pub fn writeSymtab(self: *Elf) !void { obj.asFile().writeSymtab(self); } - if (self.zig_got_section_index) |_| { - self.zig_got.writeSymtab(self); - } - if (self.got_section_index) |_| { self.got.writeSymtab(self); } @@ -5085,7 +5029,6 @@ pub fn isZigSection(self: Elf, shndx: u32) bool { self.zig_data_rel_ro_section_index, self.zig_data_section_index, self.zig_bss_section_index, - self.zig_got_section_index, }) |maybe_index| { if (maybe_index) |index| { if (index == shndx) return true; @@ -5657,10 +5600,11 @@ fn fmtDumpState( if (self.zigObjectPtr()) |zig_object| { try writer.print("zig_object({d}) : {s}\n", .{ zig_object.index, zig_object.path }); - try writer.print("{}{}\n", .{ + try writer.print("{}{}", .{ zig_object.fmtAtoms(self), zig_object.fmtSymtab(self), }); + try writer.writeByte('\n'); } for (self.objects.items) |index| { @@ -5700,7 +5644,6 @@ fn fmtDumpState( } } - try writer.print("{}\n", .{self.zig_got.fmt(self)}); try writer.print("{}\n", .{self.got.fmt(self)}); try writer.print("{}\n", .{self.plt.fmt(self)}); @@ -5991,41 +5934,6 @@ const RelaSection = struct { }; const RelaSectionTable = std.AutoArrayHashMapUnmanaged(u32, RelaSection); -pub const R_ZIG_GOT32: u32 = 0xff00; -pub const R_ZIG_GOTPCREL: u32 = 0xff01; -pub const R_ZIG_GOT_HI20: u32 = 0xff02; -pub const R_ZIG_GOT_LO12: u32 = 0xff03; -pub const R_GOT_HI20_STATIC: u32 = 0xff04; -pub const R_GOT_LO12_I_STATIC: u32 = 0xff05; - -// Comptime asserts that no Zig relocs overlap with another ISA's reloc number -comptime { - const zig_relocs = .{ - R_ZIG_GOT32, - R_ZIG_GOT_HI20, - R_ZIG_GOT_LO12, - R_ZIG_GOTPCREL, - R_GOT_HI20_STATIC, - R_GOT_LO12_I_STATIC, - }; - - const other_relocs = .{ - elf.R_X86_64, - elf.R_AARCH64, - elf.R_RISCV, - elf.R_PPC64, - }; - - @setEvalBranchQuota(@min(other_relocs.len * zig_relocs.len * 256, 6200)); - for (other_relocs) |relocs| { - for (@typeInfo(relocs).Enum.fields) |reloc| { - for (zig_relocs) |zig_reloc| { - assert(reloc.value != zig_reloc); - } - } - } -} - fn defaultEntrySymbolName(cpu_arch: std.Target.Cpu.Arch) []const u8 { return switch (cpu_arch) { .mips, .mipsel, .mips64, .mips64el => "__start", @@ -6095,6 +6003,5 @@ const StringTable = @import("StringTable.zig"); const Thunk = thunks.Thunk; const Value = @import("../Value.zig"); const VerneedSection = synthetic_sections.VerneedSection; -const ZigGotSection = synthetic_sections.ZigGotSection; const ZigObject = @import("Elf/ZigObject.zig"); const riscv = @import("riscv.zig"); diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index f2757f570b0c..fe31712f30be 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -746,12 +746,10 @@ pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) RelocError!voi const P = self.address(elf_file) + @as(i64, @intCast(rel.r_offset)); // Addend from the relocation. const A = rel.r_addend; - // Address of the target symbol - can be address of the symbol within an atom or address of PLT stub. + // Address of the target symbol - can be address of the symbol within an atom or address of PLT stub, or address of a Zig trampoline. const S = target.address(.{}, elf_file); // Address of the global offset table. const GOT = elf_file.gotAddress(); - // Address of the .zig.got table entry if any. - const ZIG_GOT = target.zigGotAddress(elf_file); // Relative offset to the start of the global offset table. const G = target.gotAddress(elf_file) - GOT; // // Address of the thread pointer. @@ -759,19 +757,18 @@ pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) RelocError!voi // Address of the dynamic thread pointer. const DTP = elf_file.dtpAddress(); - relocs_log.debug(" {s}: {x}: [{x} => {x}] G({x}) ZG({x}) ({s})", .{ + relocs_log.debug(" {s}: {x}: [{x} => {x}] GOT({x}) ({s})", .{ relocation.fmtRelocType(rel.r_type(), cpu_arch), r_offset, P, S + A, G + GOT + A, - ZIG_GOT + A, target.name(elf_file), }); try stream.seekTo(r_offset); - const args = ResolveArgs{ P, A, S, GOT, G, TP, DTP, ZIG_GOT }; + const args = ResolveArgs{ P, A, S, GOT, G, TP, DTP }; switch (cpu_arch) { .x86_64 => x86_64.resolveRelocAlloc(self, elf_file, rel, target, args, &it, code, &stream) catch |err| switch (err) { @@ -956,7 +953,7 @@ pub fn resolveRelocsNonAlloc(self: Atom, elf_file: *Elf, code: []u8, undefs: any // Address of the dynamic thread pointer. const DTP = elf_file.dtpAddress(); - const args = ResolveArgs{ P, A, S, GOT, 0, 0, DTP, 0 }; + const args = ResolveArgs{ P, A, S, GOT, 0, 0, DTP }; relocs_log.debug(" {}: {x}: [{x} => {x}] ({s})", .{ relocation.fmtRelocType(rel.r_type(), cpu_arch), @@ -1025,7 +1022,7 @@ pub fn format( _ = unused_fmt_string; _ = options; _ = writer; - @compileError("do not format symbols directly"); + @compileError("do not format Atom directly"); } pub fn fmt(atom: Atom, elf_file: *Elf) std.fmt.Formatter(format2) { @@ -1051,8 +1048,8 @@ fn format2( const atom = ctx.atom; const elf_file = ctx.elf_file; try writer.print("atom({d}) : {s} : @{x} : shdr({d}) : align({x}) : size({x})", .{ - atom.atom_index, atom.name(elf_file), atom.address(elf_file), - atom.output_section_index, atom.alignment, atom.size, + atom.atom_index, atom.name(elf_file), atom.address(elf_file), + atom.output_section_index, atom.alignment.toByteUnits() orelse 0, atom.size, }); if (atom.fdes(elf_file).len > 0) { try writer.writeAll(" : fdes{ "); @@ -1180,16 +1177,7 @@ const x86_64 = struct { .TLSDESC_CALL, => {}, - else => |x| switch (@intFromEnum(x)) { - // Zig custom relocations - Elf.R_ZIG_GOT32, - Elf.R_ZIG_GOTPCREL, - => { - assert(symbol.flags.has_zig_got); - }, - - else => try atom.reportUnhandledRelocError(rel, elf_file), - }, + else => try atom.reportUnhandledRelocError(rel, elf_file), } } @@ -1209,7 +1197,7 @@ const x86_64 = struct { const cwriter = stream.writer(); - const P, const A, const S, const GOT, const G, const TP, const DTP, const ZIG_GOT = args; + const P, const A, const S, const GOT, const G, const TP, const DTP = args; switch (r_type) { .NONE => unreachable, @@ -1224,9 +1212,8 @@ const x86_64 = struct { ); }, - .PLT32, - .PC32, - => try cwriter.writeInt(i32, @as(i32, @intCast(S + A - P)), .little), + .PLT32 => try cwriter.writeInt(i32, @as(i32, @intCast(S + A - P)), .little), + .PC32 => try cwriter.writeInt(i32, @as(i32, @intCast(S + A - P)), .little), .GOTPCREL => try cwriter.writeInt(i32, @as(i32, @intCast(G + GOT + A - P)), .little), .GOTPC32 => try cwriter.writeInt(i32, @as(i32, @intCast(GOT + A - P)), .little), @@ -1327,15 +1314,9 @@ const x86_64 = struct { } }, - .GOT32 => try cwriter.writeInt(i32, @as(i32, @intCast(G + GOT + A)), .little), - - else => |x| switch (@intFromEnum(x)) { - // Zig custom relocations - Elf.R_ZIG_GOT32 => try cwriter.writeInt(u32, @as(u32, @intCast(ZIG_GOT + A)), .little), - Elf.R_ZIG_GOTPCREL => try cwriter.writeInt(i32, @as(i32, @intCast(ZIG_GOT + A - P)), .little), + .GOT32 => try cwriter.writeInt(i32, @as(i32, @intCast(G + A)), .little), - else => try atom.reportUnhandledRelocError(rel, elf_file), - }, + else => try atom.reportUnhandledRelocError(rel, elf_file), } } @@ -1355,7 +1336,7 @@ const x86_64 = struct { const r_type: elf.R_X86_64 = @enumFromInt(rel.r_type()); const cwriter = stream.writer(); - _, const A, const S, const GOT, _, _, const DTP, _ = args; + _, const A, const S, const GOT, _, _, const DTP = args; switch (r_type) { .NONE => unreachable, @@ -1629,7 +1610,7 @@ const x86_64 = struct { const bits = @import("../../arch/x86_64/bits.zig"); const encoder = @import("../../arch/x86_64/encoder.zig"); const Disassembler = @import("../../arch/x86_64/Disassembler.zig"); - const Immediate = bits.Immediate; + const Immediate = Instruction.Immediate; const Instruction = encoder.Instruction; }; @@ -1738,9 +1719,8 @@ const aarch64 = struct { const code = code_buffer[r_offset..][0..4]; const file_ptr = atom.file(elf_file).?; - const P, const A, const S, const GOT, const G, const TP, const DTP, const ZIG_GOT = args; + const P, const A, const S, const GOT, const G, const TP, const DTP = args; _ = DTP; - _ = ZIG_GOT; switch (r_type) { .NONE => unreachable, @@ -1942,7 +1922,7 @@ const aarch64 = struct { const r_type: elf.R_AARCH64 = @enumFromInt(rel.r_type()); const cwriter = stream.writer(); - _, const A, const S, _, _, _, _, _ = args; + _, const A, const S, _, _, _, _ = args; switch (r_type) { .NONE => unreachable, @@ -1998,19 +1978,7 @@ const riscv = struct { .SUB32, => {}, - else => |x| switch (@intFromEnum(x)) { - Elf.R_ZIG_GOT_HI20, - Elf.R_ZIG_GOT_LO12, - => { - assert(symbol.flags.has_zig_got); - }, - - Elf.R_GOT_HI20_STATIC, - Elf.R_GOT_LO12_I_STATIC, - => symbol.flags.needs_got = true, - - else => try atom.reportUnhandledRelocError(rel, elf_file), - }, + else => try atom.reportUnhandledRelocError(rel, elf_file), } } @@ -2028,7 +1996,7 @@ const riscv = struct { const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow; const cwriter = stream.writer(); - const P, const A, const S, const GOT, const G, const TP, const DTP, const ZIG_GOT = args; + const P, const A, const S, const GOT, const G, const TP, const DTP = args; _ = TP; _ = DTP; @@ -2147,34 +2115,7 @@ const riscv = struct { // 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 => { - assert(target.flags.has_zig_got); - const disp: u32 = @bitCast(math.cast(i32, ZIG_GOT + A) orelse return error.Overflow); - riscv_util.writeInstU(code[r_offset..][0..4], disp); - }, - - Elf.R_ZIG_GOT_LO12 => { - assert(target.flags.has_zig_got); - const value: u32 = @bitCast(math.cast(i32, ZIG_GOT + A) orelse return error.Overflow); - riscv_util.writeInstI(code[r_offset..][0..4], value); - }, - - Elf.R_GOT_HI20_STATIC => { - assert(target.flags.has_got); - const disp: u32 = @bitCast(math.cast(i32, G + GOT + A) orelse return error.Overflow); - riscv_util.writeInstU(code[r_offset..][0..4], disp); - }, - - Elf.R_GOT_LO12_I_STATIC => { - assert(target.flags.has_got); - const disp: u32 = @bitCast(math.cast(i32, G + GOT + A) orelse return error.Overflow); - riscv_util.writeInstI(code[r_offset..][0..4], disp); - }, - - else => try atom.reportUnhandledRelocError(rel, elf_file), - }, + else => try atom.reportUnhandledRelocError(rel, elf_file), } } @@ -2194,7 +2135,7 @@ const riscv = struct { const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow; const cwriter = stream.writer(); - _, const A, const S, const GOT, _, _, const DTP, _ = args; + _, const A, const S, const GOT, _, _, const DTP = args; _ = GOT; _ = DTP; @@ -2230,7 +2171,7 @@ const riscv = struct { const riscv_util = @import("../riscv.zig"); }; -const ResolveArgs = struct { i64, i64, i64, i64, i64, i64, i64, i64 }; +const ResolveArgs = struct { i64, i64, i64, i64, i64, i64, i64 }; const RelocError = error{ Overflow, diff --git a/src/link/Elf/Symbol.zig b/src/link/Elf/Symbol.zig index ce6b94a185c4..4f6c2b8c7e34 100644 --- a/src/link/Elf/Symbol.zig +++ b/src/link/Elf/Symbol.zig @@ -101,7 +101,7 @@ pub fn symbolRank(symbol: Symbol, elf_file: *Elf) u32 { return file_ptr.symbolRank(sym, in_archive); } -pub fn address(symbol: Symbol, opts: struct { plt: bool = true }, elf_file: *Elf) i64 { +pub fn address(symbol: Symbol, opts: struct { plt: bool = true, trampoline: bool = true }, elf_file: *Elf) i64 { if (symbol.mergeSubsection(elf_file)) |msub| { if (!msub.alive) return 0; return msub.address(elf_file) + symbol.value; @@ -109,6 +109,9 @@ pub fn address(symbol: Symbol, opts: struct { plt: bool = true }, elf_file: *Elf if (symbol.flags.has_copy_rel) { return symbol.copyRelAddress(elf_file); } + if (symbol.flags.has_trampoline and opts.trampoline) { + return symbol.trampolineAddress(elf_file); + } if (symbol.flags.has_plt and opts.plt) { if (!symbol.flags.is_canonical and symbol.flags.has_got) { // We have a non-lazy bound function pointer, use that! @@ -217,23 +220,11 @@ pub fn tlsDescAddress(symbol: Symbol, elf_file: *Elf) i64 { return entry.address(elf_file); } -const GetOrCreateZigGotEntryResult = struct { - found_existing: bool, - index: ZigGotSection.Index, -}; - -pub fn getOrCreateZigGotEntry(symbol: *Symbol, symbol_index: Index, elf_file: *Elf) !GetOrCreateZigGotEntryResult { - assert(!elf_file.base.isRelocatable()); - assert(symbol.flags.needs_zig_got); - if (symbol.flags.has_zig_got) return .{ .found_existing = true, .index = symbol.extra(elf_file).zig_got }; - const index = try elf_file.zig_got.addSymbol(symbol_index, elf_file); - return .{ .found_existing = false, .index = index }; -} - -pub fn zigGotAddress(symbol: Symbol, elf_file: *Elf) i64 { - if (!symbol.flags.has_zig_got) return 0; - const extras = symbol.extra(elf_file); - return elf_file.zig_got.entryAddress(extras.zig_got, elf_file); +pub fn trampolineAddress(symbol: Symbol, elf_file: *Elf) i64 { + if (!symbol.flags.has_trampoline) return 0; + const zo = elf_file.zigObjectPtr().?; + const index = symbol.extra(elf_file).trampoline; + return zo.symbol(index).address(.{}, elf_file); } pub fn dsoAlignment(symbol: Symbol, elf_file: *Elf) !u64 { @@ -259,7 +250,7 @@ const AddExtraOpts = struct { tlsgd: ?u32 = null, gottp: ?u32 = null, tlsdesc: ?u32 = null, - zig_got: ?u32 = null, + trampoline: ?u32 = null, }; pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, elf_file: *Elf) void { @@ -312,7 +303,7 @@ pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void { const shdr = elf_file.shdrs.items[st_shndx]; if (shdr.sh_flags & elf.SHF_TLS != 0 and file_ptr != .linker_defined) break :blk symbol.address(.{ .plt = false }, elf_file) - elf_file.tlsAddress(); - break :blk symbol.address(.{ .plt = false }, elf_file); + break :blk symbol.address(.{ .plt = false, .trampoline = false }, elf_file); }; out.st_info = (st_bind << 4) | st_type; out.st_other = esym.st_other; @@ -331,7 +322,7 @@ pub fn format( _ = unused_fmt_string; _ = options; _ = writer; - @compileError("do not format symbols directly"); + @compileError("do not format Symbol directly"); } const FormatContext = struct { @@ -388,7 +379,7 @@ fn format2( try writer.print("%{d} : {s} : @{x}", .{ symbol.esym_index, symbol.fmtName(elf_file), - symbol.address(.{}, elf_file), + symbol.address(.{ .plt = false, .trampoline = false }, elf_file), }); if (symbol.file(elf_file)) |file_ptr| { if (symbol.isAbs(elf_file)) { @@ -456,17 +447,18 @@ pub const Flags = packed struct { needs_tlsdesc: bool = false, has_tlsdesc: bool = false, - /// Whether the symbol contains .zig.got indirection. - needs_zig_got: bool = false, - has_zig_got: bool = false, + /// Whether the symbol is a merge subsection. + merge_subsection: bool = false, + + /// ZigObject specific flags + /// Whether the symbol has a trampoline. + has_trampoline: bool = false, /// Whether the symbol is a TLS variable. - /// TODO this is really not needed if only we operated on esyms between - /// codegen and ZigObject. is_tls: bool = false, - /// Whether the symbol is a merge subsection. - merge_subsection: bool = false, + /// Whether the symbol is an extern pointer (as opposed to function). + is_extern_ptr: bool = false, }; pub const Extra = struct { @@ -479,8 +471,8 @@ pub const Extra = struct { tlsgd: u32 = 0, gottp: u32 = 0, tlsdesc: u32 = 0, - zig_got: u32 = 0, merge_section: u32 = 0, + trampoline: u32 = 0, }; pub const Index = u32; diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index ef3e2ed77c78..b72946c0f9ab 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -102,23 +102,17 @@ pub fn deinit(self: *ZigObject, allocator: Allocator) void { } self.relocs.deinit(allocator); - { - var it = self.navs.iterator(); - while (it.next()) |entry| { - entry.value_ptr.exports.deinit(allocator); - } - self.navs.deinit(allocator); + for (self.navs.values()) |*meta| { + meta.exports.deinit(allocator); } + self.navs.deinit(allocator); self.lazy_syms.deinit(allocator); - { - var it = self.uavs.iterator(); - while (it.next()) |entry| { - entry.value_ptr.exports.deinit(allocator); - } - self.uavs.deinit(allocator); + for (self.uavs.values()) |*meta| { + meta.exports.deinit(allocator); } + self.uavs.deinit(allocator); for (self.tls_variables.values()) |*tlv| { tlv.deinit(allocator); @@ -161,6 +155,16 @@ pub fn flushModule(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) !voi if (metadata.rodata_state != .unused) metadata.rodata_state = .flushed; } + if (build_options.enable_logging) { + const pt: Zcu.PerThread = .{ .zcu = elf_file.base.comp.module.?, .tid = tid }; + for (self.navs.keys(), self.navs.values()) |nav_index, meta| { + checkNavAllocated(pt, nav_index, meta); + } + for (self.uavs.keys(), self.uavs.values()) |uav_index, meta| { + checkUavAllocated(pt, uav_index, meta); + } + } + if (self.dwarf) |*dw| { const pt: Zcu.PerThread = .{ .zcu = elf_file.base.comp.module.?, .tid = tid }; try dw.flushModule(pt); @@ -698,6 +702,7 @@ pub fn lowerUav( else => explicit_alignment, }; if (self.uavs.get(uav)) |metadata| { + assert(metadata.allocated); const sym = self.symbol(metadata.symbol_index); const existing_alignment = sym.atom(elf_file).?.alignment; if (uav_alignment.order(existing_alignment).compare(.lte)) @@ -729,7 +734,7 @@ pub fn lowerUav( .ok => |sym_index| sym_index, .fail => |em| return .{ .fail = em }, }; - try self.uavs.put(gpa, uav, .{ .symbol_index = sym_index }); + try self.uavs.put(gpa, uav, .{ .symbol_index = sym_index, .allocated = true }); return .{ .mcv = .{ .load_symbol = sym_index } }; } @@ -747,13 +752,7 @@ pub fn getOrCreateMetadataForLazySymbol( .const_data => .{ &gop.value_ptr.rodata_symbol_index, &gop.value_ptr.rodata_state }, }; switch (state_ptr.*) { - .unused => { - const gpa = elf_file.base.comp.gpa; - const symbol_index = try self.newSymbolWithAtom(gpa, 0); - const sym = self.symbol(symbol_index); - sym.flags.needs_zig_got = true; - symbol_index_ptr.* = symbol_index; - }, + .unused => symbol_index_ptr.* = try self.newSymbolWithAtom(pt.zcu.gpa, 0), .pending_flush => return symbol_index_ptr.*, .flushed => {}, } @@ -807,9 +806,6 @@ pub fn getOrCreateMetadataForNav( sym.flags.is_tls = true; } } - if (!sym.flags.is_tls) { - sym.flags.needs_zig_got = true; - } gop.value_ptr.* = .{ .symbol_index = symbol_index }; } return gop.value_ptr.symbol_index; @@ -820,9 +816,9 @@ fn getNavShdrIndex( elf_file: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index, + sym_index: Symbol.Index, code: []const u8, ) error{OutOfMemory}!u32 { - _ = self; const ip = &zcu.intern_pool; const any_non_single_threaded = elf_file.base.comp.config.any_non_single_threaded; const nav_val = zcu.navValue(nav_index); @@ -832,10 +828,12 @@ fn getNavShdrIndex( .@"extern" => |@"extern"| .{ @"extern".is_const, @"extern".is_threadlocal, .none }, else => .{ true, false, nav_val.toIntern() }, }; + const has_relocs = self.symbol(sym_index).atom(elf_file).?.relocs(elf_file).len > 0; if (any_non_single_threaded and is_threadlocal) { - for (code) |byte| { - if (byte != 0) break; - } else return elf_file.sectionByName(".tbss") orelse try elf_file.addSection(.{ + const is_bss = !has_relocs and for (code) |byte| { + if (byte != 0) break false; + } else true; + if (is_bss) return elf_file.sectionByName(".tbss") orelse try elf_file.addSection(.{ .type = elf.SHT_NOBITS, .flags = elf.SHF_ALLOC | elf.SHF_WRITE | elf.SHF_TLS, .name = try elf_file.insertShString(".tbss"), @@ -854,9 +852,10 @@ fn getNavShdrIndex( .Debug, .ReleaseSafe => elf_file.zig_data_section_index.?, .ReleaseFast, .ReleaseSmall => elf_file.zig_bss_section_index.?, }; - for (code) |byte| { - if (byte != 0) break; - } else return elf_file.zig_bss_section_index.?; + const is_bss = !has_relocs and for (code) |byte| { + if (byte != 0) break false; + } else true; + if (is_bss) return elf_file.zig_bss_section_index.?; return elf_file.zig_data_section_index.?; } @@ -909,13 +908,6 @@ fn updateNavCode( if (old_vaddr != atom_ptr.value) { sym.value = 0; esym.st_value = 0; - - if (!elf_file.base.isRelocatable()) { - log.debug(" (writing new offset table entry)", .{}); - assert(sym.flags.has_zig_got); - const extra = sym.extra(elf_file); - try elf_file.zig_got.writeOne(elf_file, extra.zig_got); - } } } else if (code.len < old_size) { atom_ptr.shrink(elf_file); @@ -925,15 +917,11 @@ fn updateNavCode( errdefer self.freeNavMetadata(elf_file, sym_index); sym.value = 0; - sym.flags.needs_zig_got = true; esym.st_value = 0; - - if (!elf_file.base.isRelocatable()) { - const gop = try sym.getOrCreateZigGotEntry(sym_index, elf_file); - try elf_file.zig_got.writeOne(elf_file, gop.index); - } } + self.navs.getPtr(nav_index).?.allocated = true; + if (elf_file.base.child_pid) |pid| { switch (builtin.os.tag) { .linux => { @@ -959,6 +947,7 @@ fn updateNavCode( if (shdr.sh_type != elf.SHT_NOBITS) { const file_offset = shdr.sh_offset + @as(u64, @intCast(atom_ptr.value)); try elf_file.base.file.?.pwriteAll(code, file_offset); + log.debug("writing {} from 0x{x} to 0x{x}", .{ nav.fqn.fmt(ip), file_offset, file_offset + code.len }); } } @@ -1001,6 +990,8 @@ fn updateTlv( atom_ptr.alignment = required_alignment; atom_ptr.size = code.len; + self.navs.getPtr(nav_index).?.allocated = true; + { const gop = try self.tls_variables.getOrPut(gpa, atom_ptr.atom_index); assert(!gop.found_existing); // TODO incremental updates @@ -1065,8 +1056,21 @@ pub fn updateFunc( }, }; - const shndx = try self.getNavShdrIndex(elf_file, zcu, func.owner_nav, code); + const shndx = try self.getNavShdrIndex(elf_file, zcu, func.owner_nav, sym_index, code); + log.debug("setting shdr({x},{s}) for {}", .{ + shndx, + elf_file.getShString(elf_file.shdrs.items[shndx].sh_name), + ip.getNav(func.owner_nav).fqn.fmt(ip), + }); + const old_rva, const old_alignment = blk: { + const atom_ptr = self.symbol(sym_index).atom(elf_file).?; + break :blk .{ atom_ptr.value, atom_ptr.alignment }; + }; try self.updateNavCode(elf_file, pt, func.owner_nav, sym_index, shndx, code, elf.STT_FUNC); + const new_rva, const new_alignment = blk: { + const atom_ptr = self.symbol(sym_index).atom(elf_file).?; + break :blk .{ atom_ptr.value, atom_ptr.alignment }; + }; if (dwarf_state) |*ds| { const sym = self.symbol(sym_index); @@ -1080,6 +1084,41 @@ pub fn updateFunc( } // Exports will be updated by `Zcu.processExports` after the update. + + if (old_rva != new_rva and old_rva > 0) { + // If we had to reallocate the function, we re-use the existing slot for a trampoline. + // In the rare case that the function has been further overaligned we skip creating a + // trampoline and update all symbols referring this function. + if (old_alignment.order(new_alignment) == .lt) { + @panic("TODO update all symbols referring this function"); + } + + // Create a trampoline to the new location at `old_rva`. + if (!self.symbol(sym_index).flags.has_trampoline) { + const name = try std.fmt.allocPrint(gpa, "{s}$trampoline", .{ + self.symbol(sym_index).name(elf_file), + }); + defer gpa.free(name); + const name_off = try self.addString(gpa, name); + const tr_size = trampolineSize(elf_file.getTarget().cpu.arch); + const tr_sym_index = try self.newSymbolWithAtom(gpa, name_off); + const tr_sym = self.symbol(tr_sym_index); + const tr_esym = &self.symtab.items(.elf_sym)[tr_sym.esym_index]; + tr_esym.st_info |= elf.STT_OBJECT; + tr_esym.st_size = tr_size; + const tr_atom_ptr = tr_sym.atom(elf_file).?; + tr_atom_ptr.value = old_rva; + tr_atom_ptr.alive = true; + tr_atom_ptr.alignment = old_alignment; + tr_atom_ptr.output_section_index = elf_file.zig_text_section_index.?; + tr_atom_ptr.size = tr_size; + const target_sym = self.symbol(sym_index); + target_sym.addExtra(.{ .trampoline = tr_sym_index }, elf_file); + target_sym.flags.has_trampoline = true; + } + const target_sym = self.symbol(sym_index); + try writeTrampoline(self.symbol(target_sym.extra(elf_file).trampoline).*, target_sym.*, elf_file); + } } pub fn updateNav( @@ -1102,13 +1141,12 @@ pub fn updateNav( .variable => |variable| Value.fromInterned(variable.init), .@"extern" => |@"extern"| { if (ip.isFunctionType(@"extern".ty)) return; - // Extern variable gets a .got entry only. const sym_index = try self.getGlobalSymbol( elf_file, nav.name.toSlice(ip), @"extern".lib_name.toSlice(ip), ); - self.symbol(sym_index).flags.needs_got = true; + self.symbol(sym_index).flags.is_extern_ptr = true; return; }, else => nav_val, @@ -1142,7 +1180,12 @@ pub fn updateNav( }, }; - const shndx = try self.getNavShdrIndex(elf_file, zcu, nav_index, code); + const shndx = try self.getNavShdrIndex(elf_file, zcu, nav_index, sym_index, code); + log.debug("setting shdr({x},{s}) for {}", .{ + shndx, + elf_file.getShString(elf_file.shdrs.items[shndx].sh_name), + nav.fqn.fmt(ip), + }); if (elf_file.shdrs.items[shndx].sh_flags & elf.SHF_TLS != 0) try self.updateTlv(elf_file, pt, nav_index, sym_index, shndx, code) else @@ -1225,14 +1268,8 @@ fn updateLazySymbol( errdefer self.freeNavMetadata(elf_file, symbol_index); local_sym.value = 0; - local_sym.flags.needs_zig_got = true; local_esym.st_value = 0; - if (!elf_file.base.isRelocatable()) { - const gop = try local_sym.getOrCreateZigGotEntry(symbol_index, elf_file); - try elf_file.zig_got.writeOne(elf_file, gop.index); - } - const shdr = elf_file.shdrs.items[output_section_index]; const file_offset = shdr.sh_offset + @as(u64, @intCast(atom_ptr.value)); try elf_file.base.file.?.pwriteAll(code, file_offset); @@ -1440,6 +1477,52 @@ pub fn getGlobalSymbol(self: *ZigObject, elf_file: *Elf, name: []const u8, lib_n return lookup_gop.value_ptr.*; } +const max_trampoline_len = 12; + +fn trampolineSize(cpu_arch: std.Target.Cpu.Arch) u64 { + const len = switch (cpu_arch) { + .x86_64 => 5, // jmp rel32 + else => @panic("TODO implement trampoline size for this CPU arch"), + }; + comptime assert(len <= max_trampoline_len); + return len; +} + +fn writeTrampoline(tr_sym: Symbol, target: Symbol, elf_file: *Elf) !void { + const atom_ptr = tr_sym.atom(elf_file).?; + const shdr = elf_file.shdrs.items[atom_ptr.output_section_index]; + const fileoff = shdr.sh_offset + @as(u64, @intCast(atom_ptr.value)); + const source_addr = tr_sym.address(.{}, elf_file); + const target_addr = target.address(.{ .trampoline = false }, elf_file); + var buf: [max_trampoline_len]u8 = undefined; + const out = switch (elf_file.getTarget().cpu.arch) { + .x86_64 => try x86_64.writeTrampolineCode(source_addr, target_addr, &buf), + else => @panic("TODO implement write trampoline for this CPU arch"), + }; + try elf_file.base.file.?.pwriteAll(out, fileoff); + + if (elf_file.base.child_pid) |pid| { + switch (builtin.os.tag) { + .linux => { + var local_vec: [1]std.posix.iovec_const = .{.{ + .base = out.ptr, + .len = out.len, + }}; + var remote_vec: [1]std.posix.iovec_const = .{.{ + .base = @as([*]u8, @ptrFromInt(@as(usize, @intCast(source_addr)))), + .len = out.len, + }}; + const rc = std.os.linux.process_vm_writev(pid, &local_vec, &remote_vec, 0); + switch (std.os.linux.E.init(rc)) { + .SUCCESS => assert(rc == out.len), + else => |errno| log.warn("process_vm_writev failure: {s}", .{@tagName(errno)}), + } + }, + else => return error.HotSwapUnavailableOnHostOperatingSystem, + } + } +} + pub fn asFile(self: *ZigObject) File { return .{ .zig_object = self }; } @@ -1662,6 +1745,8 @@ const AvMetadata = struct { symbol_index: Symbol.Index, /// A list of all exports aliases of this Av. exports: std.ArrayListUnmanaged(Symbol.Index) = .{}, + /// Set to true if the AV has been initialized and allocated. + allocated: bool = false, fn @"export"(m: AvMetadata, zig_object: *ZigObject, name: []const u8) ?*u32 { for (m.exports.items) |*exp| { @@ -1672,6 +1757,32 @@ const AvMetadata = struct { } }; +fn checkNavAllocated(pt: Zcu.PerThread, index: InternPool.Nav.Index, meta: AvMetadata) void { + if (!meta.allocated) { + const zcu = pt.zcu; + const ip = &zcu.intern_pool; + const nav = ip.getNav(index); + log.err("NAV {}({d}) assigned symbol {d} but not allocated!", .{ + nav.fqn.fmt(ip), + index, + meta.symbol_index, + }); + } +} + +fn checkUavAllocated(pt: Zcu.PerThread, index: InternPool.Index, meta: AvMetadata) void { + if (!meta.allocated) { + const zcu = pt.zcu; + const uav = Value.fromInterned(index); + const ty = uav.typeOf(zcu); + log.err("UAV {}({d}) assigned symbol {d} but not allocated!", .{ + ty.fmt(pt), + index, + meta.symbol_index, + }); + } +} + const TlsVariable = struct { symbol_index: Symbol.Index, code: []const u8 = &[0]u8{}, @@ -1682,12 +1793,26 @@ const TlsVariable = struct { }; const AtomList = std.ArrayListUnmanaged(Atom.Index); -const NavTable = std.AutoHashMapUnmanaged(InternPool.Nav.Index, AvMetadata); -const UavTable = std.AutoHashMapUnmanaged(InternPool.Index, AvMetadata); +const NavTable = std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, AvMetadata); +const UavTable = std.AutoArrayHashMapUnmanaged(InternPool.Index, AvMetadata); const LazySymbolTable = std.AutoArrayHashMapUnmanaged(InternPool.Index, LazySymbolMetadata); const TlsTable = std.AutoArrayHashMapUnmanaged(Atom.Index, TlsVariable); +const x86_64 = struct { + fn writeTrampolineCode(source_addr: i64, target_addr: i64, buf: *[max_trampoline_len]u8) ![]u8 { + const disp = @as(i64, @intCast(target_addr)) - source_addr - 5; + var bytes = [_]u8{ + 0xe9, 0x00, 0x00, 0x00, 0x00, // jmp rel32 + }; + assert(bytes.len == trampolineSize(.x86_64)); + mem.writeInt(i32, bytes[1..][0..4], @intCast(disp), .little); + @memcpy(buf[0..bytes.len], &bytes); + return buf[0..bytes.len]; + } +}; + const assert = std.debug.assert; +const build_options = @import("build_options"); const builtin = @import("builtin"); const codegen = @import("../../codegen.zig"); const elf = std.elf; diff --git a/src/link/Elf/relocation.zig b/src/link/Elf/relocation.zig index 5f6810d6f95f..e1a317f67d46 100644 --- a/src/link/Elf/relocation.zig +++ b/src/link/Elf/relocation.zig @@ -112,19 +112,11 @@ fn formatRelocType( _ = unused_fmt_string; _ = options; const r_type = ctx.r_type; - switch (r_type) { - Elf.R_ZIG_GOT32 => try writer.writeAll("R_ZIG_GOT32"), - Elf.R_ZIG_GOTPCREL => try writer.writeAll("R_ZIG_GOTPCREL"), - Elf.R_ZIG_GOT_HI20 => try writer.writeAll("R_ZIG_GOT_HI20"), - Elf.R_ZIG_GOT_LO12 => try writer.writeAll("R_ZIG_GOT_LO12"), - Elf.R_GOT_HI20_STATIC => try writer.writeAll("R_GOT_HI20_STATIC"), - Elf.R_GOT_LO12_I_STATIC => try writer.writeAll("R_GOT_LO12_I_STATIC"), - else => switch (ctx.cpu_arch) { - .x86_64 => try writer.print("R_X86_64_{s}", .{@tagName(@as(elf.R_X86_64, @enumFromInt(r_type)))}), - .aarch64 => try writer.print("R_AARCH64_{s}", .{@tagName(@as(elf.R_AARCH64, @enumFromInt(r_type)))}), - .riscv64 => try writer.print("R_RISCV_{s}", .{@tagName(@as(elf.R_RISCV, @enumFromInt(r_type)))}), - else => unreachable, - }, + switch (ctx.cpu_arch) { + .x86_64 => try writer.print("R_X86_64_{s}", .{@tagName(@as(elf.R_X86_64, @enumFromInt(r_type)))}), + .aarch64 => try writer.print("R_AARCH64_{s}", .{@tagName(@as(elf.R_AARCH64, @enumFromInt(r_type)))}), + .riscv64 => try writer.print("R_RISCV_{s}", .{@tagName(@as(elf.R_RISCV, @enumFromInt(r_type)))}), + else => unreachable, } } diff --git a/src/link/Elf/synthetic_sections.zig b/src/link/Elf/synthetic_sections.zig index e1ec90139eb5..40cb37b9671f 100644 --- a/src/link/Elf/synthetic_sections.zig +++ b/src/link/Elf/synthetic_sections.zig @@ -223,215 +223,6 @@ pub const DynamicSection = struct { } }; -pub const ZigGotSection = struct { - entries: std.ArrayListUnmanaged(Symbol.Index) = .{}, - output_symtab_ctx: Elf.SymtabCtx = .{}, - flags: Flags = .{}, - - const Flags = packed struct { - needs_rela: bool = false, - dirty: bool = false, - }; - - pub const Index = u32; - - pub fn deinit(zig_got: *ZigGotSection, allocator: Allocator) void { - zig_got.entries.deinit(allocator); - } - - fn allocateEntry(zig_got: *ZigGotSection, allocator: Allocator) !Index { - try zig_got.entries.ensureUnusedCapacity(allocator, 1); - // TODO add free list - const index = @as(Index, @intCast(zig_got.entries.items.len)); - _ = zig_got.entries.addOneAssumeCapacity(); - zig_got.flags.dirty = true; - return index; - } - - pub fn addSymbol(zig_got: *ZigGotSection, sym_index: Symbol.Index, elf_file: *Elf) !Index { - const comp = elf_file.base.comp; - const gpa = comp.gpa; - const zo = elf_file.zigObjectPtr().?; - const index = try zig_got.allocateEntry(gpa); - const entry = &zig_got.entries.items[index]; - entry.* = sym_index; - const symbol = zo.symbol(sym_index); - symbol.flags.has_zig_got = true; - if (elf_file.isEffectivelyDynLib() or (elf_file.base.isExe() and comp.config.pie)) { - zig_got.flags.needs_rela = true; - } - symbol.addExtra(.{ .zig_got = index }, elf_file); - return index; - } - - pub fn entryOffset(zig_got: ZigGotSection, index: Index, elf_file: *Elf) u64 { - _ = zig_got; - const entry_size = elf_file.archPtrWidthBytes(); - const shdr = elf_file.shdrs.items[elf_file.zig_got_section_index.?]; - return shdr.sh_offset + @as(u64, entry_size) * index; - } - - pub fn entryAddress(zig_got: ZigGotSection, index: Index, elf_file: *Elf) i64 { - _ = zig_got; - const entry_size = elf_file.archPtrWidthBytes(); - const shdr = elf_file.shdrs.items[elf_file.zig_got_section_index.?]; - return @as(i64, @intCast(shdr.sh_addr)) + entry_size * index; - } - - pub fn size(zig_got: ZigGotSection, elf_file: *Elf) usize { - return elf_file.archPtrWidthBytes() * zig_got.entries.items.len; - } - - pub fn writeOne(zig_got: *ZigGotSection, elf_file: *Elf, index: Index) !void { - const zo = elf_file.zigObjectPtr().?; - if (zig_got.flags.dirty) { - const needed_size = zig_got.size(elf_file); - try elf_file.growAllocSection(elf_file.zig_got_section_index.?, needed_size); - zig_got.flags.dirty = false; - } - const entry_size: u16 = elf_file.archPtrWidthBytes(); - const target = elf_file.getTarget(); - const endian = target.cpu.arch.endian(); - const off = zig_got.entryOffset(index, elf_file); - const vaddr: u64 = @intCast(zig_got.entryAddress(index, elf_file)); - const entry = zig_got.entries.items[index]; - const value = zo.symbol(entry).address(.{}, elf_file); - switch (entry_size) { - 2 => { - var buf: [2]u8 = undefined; - std.mem.writeInt(u16, &buf, @intCast(value), endian); - try elf_file.base.file.?.pwriteAll(&buf, off); - }, - 4 => { - var buf: [4]u8 = undefined; - std.mem.writeInt(u32, &buf, @intCast(value), endian); - try elf_file.base.file.?.pwriteAll(&buf, off); - }, - 8 => { - var buf: [8]u8 = undefined; - std.mem.writeInt(u64, &buf, @intCast(value), endian); - try elf_file.base.file.?.pwriteAll(&buf, off); - - if (elf_file.base.child_pid) |pid| { - switch (builtin.os.tag) { - .linux => { - var local_vec: [1]std.posix.iovec_const = .{.{ - .base = &buf, - .len = buf.len, - }}; - var remote_vec: [1]std.posix.iovec_const = .{.{ - .base = @as([*]u8, @ptrFromInt(@as(usize, @intCast(vaddr)))), - .len = buf.len, - }}; - const rc = std.os.linux.process_vm_writev(pid, &local_vec, &remote_vec, 0); - switch (std.os.linux.E.init(rc)) { - .SUCCESS => assert(rc == buf.len), - else => |errno| log.warn("process_vm_writev failure: {s}", .{@tagName(errno)}), - } - }, - else => return error.HotSwapUnavailableOnHostOperatingSystem, - } - } - }, - else => unreachable, - } - } - - pub fn writeAll(zig_got: ZigGotSection, elf_file: *Elf, writer: anytype) !void { - const zo = elf_file.zigObjectPtr().?; - for (zig_got.entries.items) |entry| { - const symbol = zo.symbol(entry); - const value = symbol.address(.{ .plt = false }, elf_file); - try writeInt(value, elf_file, writer); - } - } - - pub fn numRela(zig_got: ZigGotSection) usize { - return zig_got.entries.items.len; - } - - pub fn addRela(zig_got: ZigGotSection, elf_file: *Elf) !void { - const comp = elf_file.base.comp; - const gpa = comp.gpa; - const cpu_arch = elf_file.getTarget().cpu.arch; - const zo = elf_file.zigObjectPtr().?; - try elf_file.rela_dyn.ensureUnusedCapacity(gpa, zig_got.numRela()); - for (zig_got.entries.items) |entry| { - const symbol = zo.symbol(entry); - const offset = symbol.zigGotAddress(elf_file); - elf_file.addRelaDynAssumeCapacity(.{ - .offset = @intCast(offset), - .type = relocation.encode(.rel, cpu_arch), - .addend = symbol.address(.{ .plt = false }, elf_file), - }); - } - } - - pub fn updateSymtabSize(zig_got: *ZigGotSection, elf_file: *Elf) void { - const zo = elf_file.zigObjectPtr().?; - zig_got.output_symtab_ctx.nlocals = @as(u32, @intCast(zig_got.entries.items.len)); - for (zig_got.entries.items) |entry| { - const name = zo.symbol(entry).name(elf_file); - zig_got.output_symtab_ctx.strsize += @as(u32, @intCast(name.len + "$ziggot".len)) + 1; - } - } - - pub fn writeSymtab(zig_got: ZigGotSection, elf_file: *Elf) void { - const zo = elf_file.zigObjectPtr().?; - for (zig_got.entries.items, zig_got.output_symtab_ctx.ilocal.., 0..) |entry, ilocal, index| { - const symbol = zo.symbol(entry); - const symbol_name = symbol.name(elf_file); - const st_name = @as(u32, @intCast(elf_file.strtab.items.len)); - elf_file.strtab.appendSliceAssumeCapacity(symbol_name); - elf_file.strtab.appendSliceAssumeCapacity("$ziggot"); - elf_file.strtab.appendAssumeCapacity(0); - const st_value = zig_got.entryAddress(@intCast(index), elf_file); - const st_size = elf_file.archPtrWidthBytes(); - elf_file.symtab.items[ilocal] = .{ - .st_name = st_name, - .st_info = elf.STT_OBJECT, - .st_other = 0, - .st_shndx = @intCast(elf_file.zig_got_section_index.?), - .st_value = @intCast(st_value), - .st_size = st_size, - }; - } - } - - const FormatCtx = struct { - zig_got: ZigGotSection, - elf_file: *Elf, - }; - - pub fn fmt(zig_got: ZigGotSection, elf_file: *Elf) std.fmt.Formatter(format2) { - return .{ .data = .{ .zig_got = zig_got, .elf_file = elf_file } }; - } - - pub fn format2( - ctx: FormatCtx, - comptime unused_fmt_string: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, - ) !void { - _ = options; - _ = unused_fmt_string; - const zig_got = ctx.zig_got; - const elf_file = ctx.elf_file; - try writer.writeAll(".zig.got\n"); - for (zig_got.entries.items, 0..) |entry, index| { - const zo = elf_file.zigObjectPtr().?; - const symbol = zo.symbol(entry); - try writer.print(" {d}@0x{x} => {d}@0x{x} ({s})\n", .{ - index, - zig_got.entryAddress(@intCast(index), elf_file), - entry, - symbol.address(.{}, elf_file), - symbol.name(elf_file), - }); - } - } -}; - pub const GotSection = struct { entries: std.ArrayListUnmanaged(Entry) = .{}, output_symtab_ctx: Elf.SymtabCtx = .{}, diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index 90d12e6858bf..9d3474260468 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -733,7 +733,6 @@ test "extern variable with non-pointer opaque type" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest; if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; @export(var_to_export, .{ .name = "opaque_extern_var" }); try expect(@as(*align(1) u32, @ptrCast(&opaque_extern_var)).* == 42); diff --git a/test/behavior/export_builtin.zig b/test/behavior/export_builtin.zig index 547a9b990af8..25b6e2527ec3 100644 --- a/test/behavior/export_builtin.zig +++ b/test/behavior/export_builtin.zig @@ -48,7 +48,6 @@ test "exporting using field access" { test "exporting comptime-known value" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 and (builtin.target.ofmt != .elf and builtin.target.ofmt != .macho and @@ -68,7 +67,6 @@ test "exporting comptime-known value" { test "exporting comptime var" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 and (builtin.target.ofmt != .elf and builtin.target.ofmt != .macho and diff --git a/test/behavior/extern.zig b/test/behavior/extern.zig index cd80c545ce14..0ef3e4935337 100644 --- a/test/behavior/extern.zig +++ b/test/behavior/extern.zig @@ -7,7 +7,6 @@ test "anyopaque extern symbol" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest; if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const a = @extern(*anyopaque, .{ .name = "a_mystery_symbol" }); const b: *i32 = @alignCast(@ptrCast(a)); diff --git a/test/behavior/fn.zig b/test/behavior/fn.zig index 1e4039c1bb07..ab7aca6ed6b4 100644 --- a/test/behavior/fn.zig +++ b/test/behavior/fn.zig @@ -429,7 +429,6 @@ test "implicit cast function to function ptr" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S1 = struct { export fn someFunctionThatReturnsAValue() c_int { diff --git a/test/behavior/pointers.zig b/test/behavior/pointers.zig index 42e3ea0ae970..f8260d1f0d83 100644 --- a/test/behavior/pointers.zig +++ b/test/behavior/pointers.zig @@ -45,7 +45,6 @@ test "pointer-integer arithmetic" { test "pointer subtraction" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; { const a: *u8 = @ptrFromInt(100); diff --git a/test/link/elf.zig b/test/link/elf.zig index 71b722a9f983..5539638ba7b6 100644 --- a/test/link/elf.zig +++ b/test/link/elf.zig @@ -1773,25 +1773,41 @@ fn testImportingDataDynamic(b: *Build, opts: Options) *Step { .use_llvm = true, }, .{ .name = "a", - .c_source_bytes = "int foo = 42;", + .c_source_bytes = + \\#include + \\int foo = 42; + \\void printFoo() { fprintf(stderr, "lib foo=%d\n", foo); } + , }); + dso.linkLibC(); const main = addExecutable(b, opts, .{ .name = "main", .zig_source_bytes = + \\const std = @import("std"); \\extern var foo: i32; + \\extern fn printFoo() void; \\pub fn main() void { - \\ @import("std").debug.print("{d}\n", .{foo}); + \\ std.debug.print("exe foo={d}\n", .{foo}); + \\ printFoo(); + \\ foo += 1; + \\ std.debug.print("exe foo={d}\n", .{foo}); + \\ printFoo(); \\} , .strip = true, // TODO temp hack }); main.pie = true; main.linkLibrary(dso); - main.linkLibC(); const run = addRunArtifact(main); - run.expectStdErrEqual("42\n"); + run.expectStdErrEqual( + \\exe foo=42 + \\lib foo=42 + \\exe foo=43 + \\lib foo=43 + \\ + ); test_step.dependOn(&run.step); return test_step;