| author | |
| committer | |
| log | 8fb4a4efbabbe3fe98521201eac41feee5a9a50a |
| tree | 398abef45faa8065836c6a5f8cad167ae8adb4ed |
| parent | 223f62acbd32c04db3169906f33869f43e5258f7 |
| parent | 59a586a8785b8aea417824e5b6b8d6b8a1b2b695 |
| signature |
elf: upstream zld/ELF functionality, part 215 files changed, 954 insertions(+), 47 deletions(-)
CMakeLists.txt+2| ... | @@ -555,6 +555,7 @@ set(ZIG_STAGE2_SOURCES | ... | @@ -555,6 +555,7 @@ set(ZIG_STAGE2_SOURCES |
| 555 | "${CMAKE_SOURCE_DIR}/src/arch/wasm/Emit.zig" | 555 | "${CMAKE_SOURCE_DIR}/src/arch/wasm/Emit.zig" |
| 556 | "${CMAKE_SOURCE_DIR}/src/arch/wasm/Mir.zig" | 556 | "${CMAKE_SOURCE_DIR}/src/arch/wasm/Mir.zig" |
| 557 | "${CMAKE_SOURCE_DIR}/src/arch/x86_64/CodeGen.zig" | 557 | "${CMAKE_SOURCE_DIR}/src/arch/x86_64/CodeGen.zig" |
| 558 | "${CMAKE_SOURCE_DIR}/src/arch/x86_64/Disassembler.zig" | ||
| 558 | "${CMAKE_SOURCE_DIR}/src/arch/x86_64/Emit.zig" | 559 | "${CMAKE_SOURCE_DIR}/src/arch/x86_64/Emit.zig" |
| 559 | "${CMAKE_SOURCE_DIR}/src/arch/x86_64/Encoding.zig" | 560 | "${CMAKE_SOURCE_DIR}/src/arch/x86_64/Encoding.zig" |
| 560 | "${CMAKE_SOURCE_DIR}/src/arch/x86_64/Mir.zig" | 561 | "${CMAKE_SOURCE_DIR}/src/arch/x86_64/Mir.zig" |
| ... | @@ -584,6 +585,7 @@ set(ZIG_STAGE2_SOURCES | ... | @@ -584,6 +585,7 @@ set(ZIG_STAGE2_SOURCES |
| 584 | "${CMAKE_SOURCE_DIR}/src/link/Coff/Object.zig" | 585 | "${CMAKE_SOURCE_DIR}/src/link/Coff/Object.zig" |
| 585 | "${CMAKE_SOURCE_DIR}/src/link/Coff/lld.zig" | 586 | "${CMAKE_SOURCE_DIR}/src/link/Coff/lld.zig" |
| 586 | "${CMAKE_SOURCE_DIR}/src/link/Elf.zig" | 587 | "${CMAKE_SOURCE_DIR}/src/link/Elf.zig" |
| 588 | "${CMAKE_SOURCE_DIR}/src/link/Elf/Archive.zig" | ||
| 587 | "${CMAKE_SOURCE_DIR}/src/link/Elf/Atom.zig" | 589 | "${CMAKE_SOURCE_DIR}/src/link/Elf/Atom.zig" |
| 588 | "${CMAKE_SOURCE_DIR}/src/link/Elf/LinkerDefined.zig" | 590 | "${CMAKE_SOURCE_DIR}/src/link/Elf/LinkerDefined.zig" |
| 589 | "${CMAKE_SOURCE_DIR}/src/link/Elf/Object.zig" | 591 | "${CMAKE_SOURCE_DIR}/src/link/Elf/Object.zig" |
src/arch/aarch64/CodeGen.zig+1-1| ... | @@ -4316,7 +4316,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier | ... | @@ -4316,7 +4316,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier |
| 4316 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { | 4316 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { |
| 4317 | const sym_index = try elf_file.getOrCreateMetadataForDecl(func.owner_decl); | 4317 | const sym_index = try elf_file.getOrCreateMetadataForDecl(func.owner_decl); |
| 4318 | const sym = elf_file.symbol(sym_index); | 4318 | const sym = elf_file.symbol(sym_index); |
| 4319 | _ = try sym.getOrCreateGotEntry(elf_file); | 4319 | _ = try sym.getOrCreateGotEntry(sym_index, elf_file); |
| 4320 | const got_addr = @as(u32, @intCast(sym.gotAddress(elf_file))); | 4320 | const got_addr = @as(u32, @intCast(sym.gotAddress(elf_file))); |
| 4321 | try self.genSetReg(Type.usize, .x30, .{ .memory = got_addr }); | 4321 | try self.genSetReg(Type.usize, .x30, .{ .memory = got_addr }); |
| 4322 | } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { | 4322 | } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { |
src/arch/arm/CodeGen.zig+1-1| ... | @@ -4296,7 +4296,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier | ... | @@ -4296,7 +4296,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier |
| 4296 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { | 4296 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { |
| 4297 | const sym_index = try elf_file.getOrCreateMetadataForDecl(func.owner_decl); | 4297 | const sym_index = try elf_file.getOrCreateMetadataForDecl(func.owner_decl); |
| 4298 | const sym = elf_file.symbol(sym_index); | 4298 | const sym = elf_file.symbol(sym_index); |
| 4299 | _ = try sym.getOrCreateGotEntry(elf_file); | 4299 | _ = try sym.getOrCreateGotEntry(sym_index, elf_file); |
| 4300 | const got_addr = @as(u32, @intCast(sym.gotAddress(elf_file))); | 4300 | const got_addr = @as(u32, @intCast(sym.gotAddress(elf_file))); |
| 4301 | try self.genSetReg(Type.usize, .lr, .{ .memory = got_addr }); | 4301 | try self.genSetReg(Type.usize, .lr, .{ .memory = got_addr }); |
| 4302 | } else if (self.bin_file.cast(link.File.MachO)) |_| { | 4302 | } else if (self.bin_file.cast(link.File.MachO)) |_| { |
src/arch/riscv64/CodeGen.zig+1-1| ... | @@ -1749,7 +1749,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier | ... | @@ -1749,7 +1749,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier |
| 1749 | .func => |func| { | 1749 | .func => |func| { |
| 1750 | const sym_index = try elf_file.getOrCreateMetadataForDecl(func.owner_decl); | 1750 | const sym_index = try elf_file.getOrCreateMetadataForDecl(func.owner_decl); |
| 1751 | const sym = elf_file.symbol(sym_index); | 1751 | const sym = elf_file.symbol(sym_index); |
| 1752 | _ = try sym.getOrCreateGotEntry(elf_file); | 1752 | _ = try sym.getOrCreateGotEntry(sym_index, elf_file); |
| 1753 | const got_addr = @as(u32, @intCast(sym.gotAddress(elf_file))); | 1753 | const got_addr = @as(u32, @intCast(sym.gotAddress(elf_file))); |
| 1754 | try self.genSetReg(Type.usize, .ra, .{ .memory = got_addr }); | 1754 | try self.genSetReg(Type.usize, .ra, .{ .memory = got_addr }); |
| 1755 | _ = try self.addInst(.{ | 1755 | _ = try self.addInst(.{ |
src/arch/sparc64/CodeGen.zig+1-1| ... | @@ -1351,7 +1351,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier | ... | @@ -1351,7 +1351,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier |
| 1351 | const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: { | 1351 | const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: { |
| 1352 | const sym_index = try elf_file.getOrCreateMetadataForDecl(func.owner_decl); | 1352 | const sym_index = try elf_file.getOrCreateMetadataForDecl(func.owner_decl); |
| 1353 | const sym = elf_file.symbol(sym_index); | 1353 | const sym = elf_file.symbol(sym_index); |
| 1354 | _ = try sym.getOrCreateGotEntry(elf_file); | 1354 | _ = try sym.getOrCreateGotEntry(sym_index, elf_file); |
| 1355 | break :blk @as(u32, @intCast(sym.gotAddress(elf_file))); | 1355 | break :blk @as(u32, @intCast(sym.gotAddress(elf_file))); |
| 1356 | } else unreachable; | 1356 | } else unreachable; |
| 1357 | 1357 |
src/arch/x86_64/CodeGen.zig+2-2| ... | @@ -8157,7 +8157,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier | ... | @@ -8157,7 +8157,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier |
| 8157 | const sym_index = try elf_file.getOrCreateMetadataForDecl(owner_decl); | 8157 | const sym_index = try elf_file.getOrCreateMetadataForDecl(owner_decl); |
| 8158 | const sym = elf_file.symbol(sym_index); | 8158 | const sym = elf_file.symbol(sym_index); |
| 8159 | sym.flags.needs_got = true; | 8159 | sym.flags.needs_got = true; |
| 8160 | _ = try sym.getOrCreateGotEntry(elf_file); | 8160 | _ = try sym.getOrCreateGotEntry(sym_index, elf_file); |
| 8161 | const got_addr = sym.gotAddress(elf_file); | 8161 | const got_addr = sym.gotAddress(elf_file); |
| 8162 | try self.asmMemory(.{ ._, .call }, Memory.sib(.qword, .{ | 8162 | try self.asmMemory(.{ ._, .call }, Memory.sib(.qword, .{ |
| 8163 | .base = .{ .reg = .ds }, | 8163 | .base = .{ .reg = .ds }, |
| ... | @@ -10236,7 +10236,7 @@ fn genLazySymbolRef( | ... | @@ -10236,7 +10236,7 @@ fn genLazySymbolRef( |
| 10236 | return self.fail("{s} creating lazy symbol", .{@errorName(err)}); | 10236 | return self.fail("{s} creating lazy symbol", .{@errorName(err)}); |
| 10237 | const sym = elf_file.symbol(sym_index); | 10237 | const sym = elf_file.symbol(sym_index); |
| 10238 | sym.flags.needs_got = true; | 10238 | sym.flags.needs_got = true; |
| 10239 | _ = try sym.getOrCreateGotEntry(elf_file); | 10239 | _ = try sym.getOrCreateGotEntry(sym_index, elf_file); |
| 10240 | const got_addr = sym.gotAddress(elf_file); | 10240 | const got_addr = sym.gotAddress(elf_file); |
| 10241 | const got_mem = | 10241 | const got_mem = |
| 10242 | Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = @intCast(got_addr) }); | 10242 | Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = @intCast(got_addr) }); |
src/arch/x86_64/Disassembler.zig created+475| ... | @@ -0,0 +1,475 @@ | ||
| 1 | const Disassembler = @This(); | ||
| 2 | |||
| 3 | const std = @import("std"); | ||
| 4 | const assert = std.debug.assert; | ||
| 5 | const math = std.math; | ||
| 6 | |||
| 7 | const bits = @import("bits.zig"); | ||
| 8 | const encoder = @import("encoder.zig"); | ||
| 9 | |||
| 10 | const Encoding = @import("Encoding.zig"); | ||
| 11 | const Immediate = bits.Immediate; | ||
| 12 | const Instruction = encoder.Instruction; | ||
| 13 | const LegacyPrefixes = encoder.LegacyPrefixes; | ||
| 14 | const Memory = bits.Memory; | ||
| 15 | const Register = bits.Register; | ||
| 16 | const Rex = encoder.Rex; | ||
| 17 | |||
| 18 | pub const Error = error{ | ||
| 19 | EndOfStream, | ||
| 20 | LegacyPrefixAfterRex, | ||
| 21 | UnknownOpcode, | ||
| 22 | Overflow, | ||
| 23 | Todo, | ||
| 24 | }; | ||
| 25 | |||
| 26 | code: []const u8, | ||
| 27 | pos: usize = 0, | ||
| 28 | |||
| 29 | pub fn init(code: []const u8) Disassembler { | ||
| 30 | return .{ .code = code }; | ||
| 31 | } | ||
| 32 | |||
| 33 | pub fn next(dis: *Disassembler) Error!?Instruction { | ||
| 34 | const prefixes = dis.parsePrefixes() catch |err| switch (err) { | ||
| 35 | error.EndOfStream => return null, | ||
| 36 | else => |e| return e, | ||
| 37 | }; | ||
| 38 | |||
| 39 | const enc = try dis.parseEncoding(prefixes) orelse return error.UnknownOpcode; | ||
| 40 | switch (enc.data.op_en) { | ||
| 41 | .np => return inst(enc, .{}), | ||
| 42 | .d, .i => { | ||
| 43 | const imm = try dis.parseImm(enc.data.ops[0]); | ||
| 44 | return inst(enc, .{ | ||
| 45 | .op1 = .{ .imm = imm }, | ||
| 46 | }); | ||
| 47 | }, | ||
| 48 | .zi => { | ||
| 49 | const imm = try dis.parseImm(enc.data.ops[1]); | ||
| 50 | return inst(enc, .{ | ||
| 51 | .op1 = .{ .reg = Register.rax.toBitSize(enc.data.ops[0].regBitSize()) }, | ||
| 52 | .op2 = .{ .imm = imm }, | ||
| 53 | }); | ||
| 54 | }, | ||
| 55 | .o, .oi => { | ||
| 56 | const reg_low_enc = @as(u3, @truncate(dis.code[dis.pos - 1])); | ||
| 57 | const op2: Instruction.Operand = if (enc.data.op_en == .oi) .{ | ||
| 58 | .imm = try dis.parseImm(enc.data.ops[1]), | ||
| 59 | } else .none; | ||
| 60 | return inst(enc, .{ | ||
| 61 | .op1 = .{ .reg = parseGpRegister(reg_low_enc, prefixes.rex.b, prefixes.rex, enc.data.ops[0].regBitSize()) }, | ||
| 62 | .op2 = op2, | ||
| 63 | }); | ||
| 64 | }, | ||
| 65 | .m, .mi, .m1, .mc => { | ||
| 66 | const modrm = try dis.parseModRmByte(); | ||
| 67 | const act_enc = Encoding.findByOpcode(enc.opcode(), .{ | ||
| 68 | .legacy = prefixes.legacy, | ||
| 69 | .rex = prefixes.rex, | ||
| 70 | }, modrm.op1) orelse return error.UnknownOpcode; | ||
| 71 | const sib = if (modrm.sib()) try dis.parseSibByte() else null; | ||
| 72 | |||
| 73 | if (modrm.direct()) { | ||
| 74 | const op2: Instruction.Operand = switch (act_enc.data.op_en) { | ||
| 75 | .mi => .{ .imm = try dis.parseImm(act_enc.data.ops[1]) }, | ||
| 76 | .m1 => .{ .imm = Immediate.u(1) }, | ||
| 77 | .mc => .{ .reg = .cl }, | ||
| 78 | .m => .none, | ||
| 79 | else => unreachable, | ||
| 80 | }; | ||
| 81 | return inst(act_enc, .{ | ||
| 82 | .op1 = .{ .reg = parseGpRegister(modrm.op2, prefixes.rex.b, prefixes.rex, act_enc.data.ops[0].regBitSize()) }, | ||
| 83 | .op2 = op2, | ||
| 84 | }); | ||
| 85 | } | ||
| 86 | |||
| 87 | const disp = try dis.parseDisplacement(modrm, sib); | ||
| 88 | const op2: Instruction.Operand = switch (act_enc.data.op_en) { | ||
| 89 | .mi => .{ .imm = try dis.parseImm(act_enc.data.ops[1]) }, | ||
| 90 | .m1 => .{ .imm = Immediate.u(1) }, | ||
| 91 | .mc => .{ .reg = .cl }, | ||
| 92 | .m => .none, | ||
| 93 | else => unreachable, | ||
| 94 | }; | ||
| 95 | |||
| 96 | if (modrm.rip()) { | ||
| 97 | return inst(act_enc, .{ | ||
| 98 | .op1 = .{ .mem = Memory.rip(Memory.PtrSize.fromBitSize(act_enc.data.ops[0].memBitSize()), disp) }, | ||
| 99 | .op2 = op2, | ||
| 100 | }); | ||
| 101 | } | ||
| 102 | |||
| 103 | const scale_index = if (sib) |info| info.scaleIndex(prefixes.rex) else null; | ||
| 104 | const base = if (sib) |info| | ||
| 105 | info.baseReg(modrm, prefixes) | ||
| 106 | else | ||
| 107 | parseGpRegister(modrm.op2, prefixes.rex.b, prefixes.rex, 64); | ||
| 108 | return inst(act_enc, .{ | ||
| 109 | .op1 = .{ .mem = Memory.sib(Memory.PtrSize.fromBitSize(act_enc.data.ops[0].memBitSize()), .{ | ||
| 110 | .base = if (base) |base_reg| .{ .reg = base_reg } else .none, | ||
| 111 | .scale_index = scale_index, | ||
| 112 | .disp = disp, | ||
| 113 | }) }, | ||
| 114 | .op2 = op2, | ||
| 115 | }); | ||
| 116 | }, | ||
| 117 | .fd => { | ||
| 118 | const seg = segmentRegister(prefixes.legacy); | ||
| 119 | const offset = try dis.parseOffset(); | ||
| 120 | return inst(enc, .{ | ||
| 121 | .op1 = .{ .reg = Register.rax.toBitSize(enc.data.ops[0].regBitSize()) }, | ||
| 122 | .op2 = .{ .mem = Memory.moffs(seg, offset) }, | ||
| 123 | }); | ||
| 124 | }, | ||
| 125 | .td => { | ||
| 126 | const seg = segmentRegister(prefixes.legacy); | ||
| 127 | const offset = try dis.parseOffset(); | ||
| 128 | return inst(enc, .{ | ||
| 129 | .op1 = .{ .mem = Memory.moffs(seg, offset) }, | ||
| 130 | .op2 = .{ .reg = Register.rax.toBitSize(enc.data.ops[1].regBitSize()) }, | ||
| 131 | }); | ||
| 132 | }, | ||
| 133 | .mr, .mri, .mrc => { | ||
| 134 | const modrm = try dis.parseModRmByte(); | ||
| 135 | const sib = if (modrm.sib()) try dis.parseSibByte() else null; | ||
| 136 | const src_bit_size = enc.data.ops[1].regBitSize(); | ||
| 137 | |||
| 138 | if (modrm.direct()) { | ||
| 139 | return inst(enc, .{ | ||
| 140 | .op1 = .{ .reg = parseGpRegister(modrm.op2, prefixes.rex.b, prefixes.rex, enc.data.ops[0].regBitSize()) }, | ||
| 141 | .op2 = .{ .reg = parseGpRegister(modrm.op1, prefixes.rex.x, prefixes.rex, src_bit_size) }, | ||
| 142 | }); | ||
| 143 | } | ||
| 144 | |||
| 145 | const dst_bit_size = enc.data.ops[0].memBitSize(); | ||
| 146 | const disp = try dis.parseDisplacement(modrm, sib); | ||
| 147 | const op3: Instruction.Operand = switch (enc.data.op_en) { | ||
| 148 | .mri => .{ .imm = try dis.parseImm(enc.data.ops[2]) }, | ||
| 149 | .mrc => .{ .reg = .cl }, | ||
| 150 | .mr => .none, | ||
| 151 | else => unreachable, | ||
| 152 | }; | ||
| 153 | |||
| 154 | if (modrm.rip()) { | ||
| 155 | return inst(enc, .{ | ||
| 156 | .op1 = .{ .mem = Memory.rip(Memory.PtrSize.fromBitSize(dst_bit_size), disp) }, | ||
| 157 | .op2 = .{ .reg = parseGpRegister(modrm.op1, prefixes.rex.r, prefixes.rex, src_bit_size) }, | ||
| 158 | .op3 = op3, | ||
| 159 | }); | ||
| 160 | } | ||
| 161 | |||
| 162 | const scale_index = if (sib) |info| info.scaleIndex(prefixes.rex) else null; | ||
| 163 | const base = if (sib) |info| | ||
| 164 | info.baseReg(modrm, prefixes) | ||
| 165 | else | ||
| 166 | parseGpRegister(modrm.op2, prefixes.rex.b, prefixes.rex, 64); | ||
| 167 | return inst(enc, .{ | ||
| 168 | .op1 = .{ .mem = Memory.sib(Memory.PtrSize.fromBitSize(dst_bit_size), .{ | ||
| 169 | .base = if (base) |base_reg| .{ .reg = base_reg } else .none, | ||
| 170 | .scale_index = scale_index, | ||
| 171 | .disp = disp, | ||
| 172 | }) }, | ||
| 173 | .op2 = .{ .reg = parseGpRegister(modrm.op1, prefixes.rex.r, prefixes.rex, src_bit_size) }, | ||
| 174 | .op3 = op3, | ||
| 175 | }); | ||
| 176 | }, | ||
| 177 | .rm, .rmi => { | ||
| 178 | const modrm = try dis.parseModRmByte(); | ||
| 179 | const sib = if (modrm.sib()) try dis.parseSibByte() else null; | ||
| 180 | const dst_bit_size = enc.data.ops[0].regBitSize(); | ||
| 181 | |||
| 182 | if (modrm.direct()) { | ||
| 183 | const op3: Instruction.Operand = switch (enc.data.op_en) { | ||
| 184 | .rm => .none, | ||
| 185 | .rmi => .{ .imm = try dis.parseImm(enc.data.ops[2]) }, | ||
| 186 | else => unreachable, | ||
| 187 | }; | ||
| 188 | return inst(enc, .{ | ||
| 189 | .op1 = .{ .reg = parseGpRegister(modrm.op1, prefixes.rex.x, prefixes.rex, dst_bit_size) }, | ||
| 190 | .op2 = .{ .reg = parseGpRegister(modrm.op2, prefixes.rex.b, prefixes.rex, enc.data.ops[1].regBitSize()) }, | ||
| 191 | .op3 = op3, | ||
| 192 | }); | ||
| 193 | } | ||
| 194 | |||
| 195 | const src_bit_size = if (enc.data.ops[1] == .m) dst_bit_size else enc.data.ops[1].memBitSize(); | ||
| 196 | const disp = try dis.parseDisplacement(modrm, sib); | ||
| 197 | const op3: Instruction.Operand = switch (enc.data.op_en) { | ||
| 198 | .rmi => .{ .imm = try dis.parseImm(enc.data.ops[2]) }, | ||
| 199 | .rm => .none, | ||
| 200 | else => unreachable, | ||
| 201 | }; | ||
| 202 | |||
| 203 | if (modrm.rip()) { | ||
| 204 | return inst(enc, .{ | ||
| 205 | .op1 = .{ .reg = parseGpRegister(modrm.op1, prefixes.rex.r, prefixes.rex, dst_bit_size) }, | ||
| 206 | .op2 = .{ .mem = Memory.rip(Memory.PtrSize.fromBitSize(src_bit_size), disp) }, | ||
| 207 | .op3 = op3, | ||
| 208 | }); | ||
| 209 | } | ||
| 210 | |||
| 211 | const scale_index = if (sib) |info| info.scaleIndex(prefixes.rex) else null; | ||
| 212 | const base = if (sib) |info| | ||
| 213 | info.baseReg(modrm, prefixes) | ||
| 214 | else | ||
| 215 | parseGpRegister(modrm.op2, prefixes.rex.b, prefixes.rex, 64); | ||
| 216 | return inst(enc, .{ | ||
| 217 | .op1 = .{ .reg = parseGpRegister(modrm.op1, prefixes.rex.r, prefixes.rex, dst_bit_size) }, | ||
| 218 | .op2 = .{ .mem = Memory.sib(Memory.PtrSize.fromBitSize(src_bit_size), .{ | ||
| 219 | .base = if (base) |base_reg| .{ .reg = base_reg } else .none, | ||
| 220 | .scale_index = scale_index, | ||
| 221 | .disp = disp, | ||
| 222 | }) }, | ||
| 223 | .op3 = op3, | ||
| 224 | }); | ||
| 225 | }, | ||
| 226 | .rm0, .vmi, .rvm, .rvmr, .rvmi, .mvr => unreachable, // TODO | ||
| 227 | } | ||
| 228 | } | ||
| 229 | |||
| 230 | fn inst(encoding: Encoding, args: struct { | ||
| 231 | prefix: Instruction.Prefix = .none, | ||
| 232 | op1: Instruction.Operand = .none, | ||
| 233 | op2: Instruction.Operand = .none, | ||
| 234 | op3: Instruction.Operand = .none, | ||
| 235 | op4: Instruction.Operand = .none, | ||
| 236 | }) Instruction { | ||
| 237 | var i = Instruction{ .encoding = encoding, .prefix = args.prefix, .ops = .{ | ||
| 238 | args.op1, | ||
| 239 | args.op2, | ||
| 240 | args.op3, | ||
| 241 | args.op4, | ||
| 242 | } }; | ||
| 243 | return i; | ||
| 244 | } | ||
| 245 | |||
| 246 | const Prefixes = struct { | ||
| 247 | legacy: LegacyPrefixes = .{}, | ||
| 248 | rex: Rex = .{}, | ||
| 249 | // TODO add support for VEX prefix | ||
| 250 | }; | ||
| 251 | |||
| 252 | fn parsePrefixes(dis: *Disassembler) !Prefixes { | ||
| 253 | const rex_prefix_mask: u4 = 0b0100; | ||
| 254 | var stream = std.io.fixedBufferStream(dis.code[dis.pos..]); | ||
| 255 | const reader = stream.reader(); | ||
| 256 | |||
| 257 | var res: Prefixes = .{}; | ||
| 258 | |||
| 259 | while (true) { | ||
| 260 | const next_byte = try reader.readByte(); | ||
| 261 | dis.pos += 1; | ||
| 262 | |||
| 263 | switch (next_byte) { | ||
| 264 | 0xf0, 0xf2, 0xf3, 0x2e, 0x36, 0x26, 0x64, 0x65, 0x3e, 0x66, 0x67 => { | ||
| 265 | // Legacy prefix | ||
| 266 | if (res.rex.present) return error.LegacyPrefixAfterRex; | ||
| 267 | switch (next_byte) { | ||
| 268 | 0xf0 => res.legacy.prefix_f0 = true, | ||
| 269 | 0xf2 => res.legacy.prefix_f2 = true, | ||
| 270 | 0xf3 => res.legacy.prefix_f3 = true, | ||
| 271 | 0x2e => res.legacy.prefix_2e = true, | ||
| 272 | 0x36 => res.legacy.prefix_36 = true, | ||
| 273 | 0x26 => res.legacy.prefix_26 = true, | ||
| 274 | 0x64 => res.legacy.prefix_64 = true, | ||
| 275 | 0x65 => res.legacy.prefix_65 = true, | ||
| 276 | 0x3e => res.legacy.prefix_3e = true, | ||
| 277 | 0x66 => res.legacy.prefix_66 = true, | ||
| 278 | 0x67 => res.legacy.prefix_67 = true, | ||
| 279 | else => unreachable, | ||
| 280 | } | ||
| 281 | }, | ||
| 282 | else => { | ||
| 283 | if (rex_prefix_mask == @as(u4, @truncate(next_byte >> 4))) { | ||
| 284 | // REX prefix | ||
| 285 | res.rex.w = next_byte & 0b1000 != 0; | ||
| 286 | res.rex.r = next_byte & 0b100 != 0; | ||
| 287 | res.rex.x = next_byte & 0b10 != 0; | ||
| 288 | res.rex.b = next_byte & 0b1 != 0; | ||
| 289 | res.rex.present = true; | ||
| 290 | continue; | ||
| 291 | } | ||
| 292 | |||
| 293 | // TODO VEX prefix | ||
| 294 | |||
| 295 | dis.pos -= 1; | ||
| 296 | break; | ||
| 297 | }, | ||
| 298 | } | ||
| 299 | } | ||
| 300 | |||
| 301 | return res; | ||
| 302 | } | ||
| 303 | |||
| 304 | fn parseEncoding(dis: *Disassembler, prefixes: Prefixes) !?Encoding { | ||
| 305 | const o_mask: u8 = 0b1111_1000; | ||
| 306 | |||
| 307 | var opcode: [3]u8 = .{ 0, 0, 0 }; | ||
| 308 | var stream = std.io.fixedBufferStream(dis.code[dis.pos..]); | ||
| 309 | const reader = stream.reader(); | ||
| 310 | |||
| 311 | comptime var opc_count = 0; | ||
| 312 | inline while (opc_count < 3) : (opc_count += 1) { | ||
| 313 | const byte = try reader.readByte(); | ||
| 314 | opcode[opc_count] = byte; | ||
| 315 | dis.pos += 1; | ||
| 316 | |||
| 317 | if (byte == 0x0f) { | ||
| 318 | // Multi-byte opcode | ||
| 319 | } else if (opc_count > 0) { | ||
| 320 | // Multi-byte opcode | ||
| 321 | if (Encoding.findByOpcode(opcode[0 .. opc_count + 1], .{ | ||
| 322 | .legacy = prefixes.legacy, | ||
| 323 | .rex = prefixes.rex, | ||
| 324 | }, null)) |mnemonic| { | ||
| 325 | return mnemonic; | ||
| 326 | } | ||
| 327 | } else { | ||
| 328 | // Single-byte opcode | ||
| 329 | if (Encoding.findByOpcode(opcode[0..1], .{ | ||
| 330 | .legacy = prefixes.legacy, | ||
| 331 | .rex = prefixes.rex, | ||
| 332 | }, null)) |mnemonic| { | ||
| 333 | return mnemonic; | ||
| 334 | } else { | ||
| 335 | // Try O* encoding | ||
| 336 | return Encoding.findByOpcode(&.{opcode[0] & o_mask}, .{ | ||
| 337 | .legacy = prefixes.legacy, | ||
| 338 | .rex = prefixes.rex, | ||
| 339 | }, null); | ||
| 340 | } | ||
| 341 | } | ||
| 342 | } | ||
| 343 | return null; | ||
| 344 | } | ||
| 345 | |||
| 346 | fn parseGpRegister(low_enc: u3, is_extended: bool, rex: Rex, bit_size: u64) Register { | ||
| 347 | const reg_id: u4 = @as(u4, @intCast(@intFromBool(is_extended))) << 3 | low_enc; | ||
| 348 | const reg = @as(Register, @enumFromInt(reg_id)).toBitSize(bit_size); | ||
| 349 | return switch (reg) { | ||
| 350 | .spl => if (rex.present or rex.isSet()) .spl else .ah, | ||
| 351 | .dil => if (rex.present or rex.isSet()) .dil else .bh, | ||
| 352 | .bpl => if (rex.present or rex.isSet()) .bpl else .ch, | ||
| 353 | .sil => if (rex.present or rex.isSet()) .sil else .dh, | ||
| 354 | else => reg, | ||
| 355 | }; | ||
| 356 | } | ||
| 357 | |||
| 358 | fn parseImm(dis: *Disassembler, kind: Encoding.Op) !Immediate { | ||
| 359 | var stream = std.io.fixedBufferStream(dis.code[dis.pos..]); | ||
| 360 | var creader = std.io.countingReader(stream.reader()); | ||
| 361 | const reader = creader.reader(); | ||
| 362 | const imm = switch (kind) { | ||
| 363 | .imm8s, .rel8 => Immediate.s(try reader.readInt(i8, .Little)), | ||
| 364 | .imm16s, .rel16 => Immediate.s(try reader.readInt(i16, .Little)), | ||
| 365 | .imm32s, .rel32 => Immediate.s(try reader.readInt(i32, .Little)), | ||
| 366 | .imm8 => Immediate.u(try reader.readInt(u8, .Little)), | ||
| 367 | .imm16 => Immediate.u(try reader.readInt(u16, .Little)), | ||
| 368 | .imm32 => Immediate.u(try reader.readInt(u32, .Little)), | ||
| 369 | .imm64 => Immediate.u(try reader.readInt(u64, .Little)), | ||
| 370 | else => unreachable, | ||
| 371 | }; | ||
| 372 | dis.pos += std.math.cast(usize, creader.bytes_read) orelse return error.Overflow; | ||
| 373 | return imm; | ||
| 374 | } | ||
| 375 | |||
| 376 | fn parseOffset(dis: *Disassembler) !u64 { | ||
| 377 | var stream = std.io.fixedBufferStream(dis.code[dis.pos..]); | ||
| 378 | const reader = stream.reader(); | ||
| 379 | const offset = try reader.readInt(u64, .Little); | ||
| 380 | dis.pos += 8; | ||
| 381 | return offset; | ||
| 382 | } | ||
| 383 | |||
| 384 | const ModRm = packed struct { | ||
| 385 | mod: u2, | ||
| 386 | op1: u3, | ||
| 387 | op2: u3, | ||
| 388 | |||
| 389 | inline fn direct(self: ModRm) bool { | ||
| 390 | return self.mod == 0b11; | ||
| 391 | } | ||
| 392 | |||
| 393 | inline fn rip(self: ModRm) bool { | ||
| 394 | return self.mod == 0 and self.op2 == 0b101; | ||
| 395 | } | ||
| 396 | |||
| 397 | inline fn sib(self: ModRm) bool { | ||
| 398 | return !self.direct() and self.op2 == 0b100; | ||
| 399 | } | ||
| 400 | }; | ||
| 401 | |||
| 402 | fn parseModRmByte(dis: *Disassembler) !ModRm { | ||
| 403 | if (dis.code[dis.pos..].len == 0) return error.EndOfStream; | ||
| 404 | const modrm_byte = dis.code[dis.pos]; | ||
| 405 | dis.pos += 1; | ||
| 406 | const mod: u2 = @as(u2, @truncate(modrm_byte >> 6)); | ||
| 407 | const op1: u3 = @as(u3, @truncate(modrm_byte >> 3)); | ||
| 408 | const op2: u3 = @as(u3, @truncate(modrm_byte)); | ||
| 409 | return ModRm{ .mod = mod, .op1 = op1, .op2 = op2 }; | ||
| 410 | } | ||
| 411 | |||
| 412 | fn segmentRegister(prefixes: LegacyPrefixes) Register { | ||
| 413 | if (prefixes.prefix_2e) return .cs; | ||
| 414 | if (prefixes.prefix_36) return .ss; | ||
| 415 | if (prefixes.prefix_26) return .es; | ||
| 416 | if (prefixes.prefix_64) return .fs; | ||
| 417 | if (prefixes.prefix_65) return .gs; | ||
| 418 | return .ds; | ||
| 419 | } | ||
| 420 | |||
| 421 | const Sib = packed struct { | ||
| 422 | scale: u2, | ||
| 423 | index: u3, | ||
| 424 | base: u3, | ||
| 425 | |||
| 426 | fn scaleIndex(self: Sib, rex: Rex) ?Memory.ScaleIndex { | ||
| 427 | if (self.index == 0b100 and !rex.x) return null; | ||
| 428 | return .{ | ||
| 429 | .scale = @as(u4, 1) << self.scale, | ||
| 430 | .index = parseGpRegister(self.index, rex.x, rex, 64), | ||
| 431 | }; | ||
| 432 | } | ||
| 433 | |||
| 434 | fn baseReg(self: Sib, modrm: ModRm, prefixes: Prefixes) ?Register { | ||
| 435 | if (self.base == 0b101 and modrm.mod == 0) { | ||
| 436 | if (self.scaleIndex(prefixes.rex)) |_| return null; | ||
| 437 | return segmentRegister(prefixes.legacy); | ||
| 438 | } | ||
| 439 | return parseGpRegister(self.base, prefixes.rex.b, prefixes.rex, 64); | ||
| 440 | } | ||
| 441 | }; | ||
| 442 | |||
| 443 | fn parseSibByte(dis: *Disassembler) !Sib { | ||
| 444 | if (dis.code[dis.pos..].len == 0) return error.EndOfStream; | ||
| 445 | const sib_byte = dis.code[dis.pos]; | ||
| 446 | dis.pos += 1; | ||
| 447 | const scale: u2 = @as(u2, @truncate(sib_byte >> 6)); | ||
| 448 | const index: u3 = @as(u3, @truncate(sib_byte >> 3)); | ||
| 449 | const base: u3 = @as(u3, @truncate(sib_byte)); | ||
| 450 | return Sib{ .scale = scale, .index = index, .base = base }; | ||
| 451 | } | ||
| 452 | |||
| 453 | fn parseDisplacement(dis: *Disassembler, modrm: ModRm, sib: ?Sib) !i32 { | ||
| 454 | var stream = std.io.fixedBufferStream(dis.code[dis.pos..]); | ||
| 455 | var creader = std.io.countingReader(stream.reader()); | ||
| 456 | const reader = creader.reader(); | ||
| 457 | const disp = disp: { | ||
| 458 | if (sib) |info| { | ||
| 459 | if (info.base == 0b101 and modrm.mod == 0) { | ||
| 460 | break :disp try reader.readInt(i32, .Little); | ||
| 461 | } | ||
| 462 | } | ||
| 463 | if (modrm.rip()) { | ||
| 464 | break :disp try reader.readInt(i32, .Little); | ||
| 465 | } | ||
| 466 | break :disp switch (modrm.mod) { | ||
| 467 | 0b00 => 0, | ||
| 468 | 0b01 => try reader.readInt(i8, .Little), | ||
| 469 | 0b10 => try reader.readInt(i32, .Little), | ||
| 470 | 0b11 => unreachable, | ||
| 471 | }; | ||
| 472 | }; | ||
| 473 | dis.pos += std.math.cast(usize, creader.bytes_read) orelse return error.Overflow; | ||
| 474 | return disp; | ||
| 475 | } | ||
src/codegen.zig+1-1| ... | @@ -861,7 +861,7 @@ fn genDeclRef( | ... | @@ -861,7 +861,7 @@ fn genDeclRef( |
| 861 | const sym_index = try elf_file.getOrCreateMetadataForDecl(decl_index); | 861 | const sym_index = try elf_file.getOrCreateMetadataForDecl(decl_index); |
| 862 | const sym = elf_file.symbol(sym_index); | 862 | const sym = elf_file.symbol(sym_index); |
| 863 | sym.flags.needs_got = true; | 863 | sym.flags.needs_got = true; |
| 864 | _ = try sym.getOrCreateGotEntry(elf_file); | 864 | _ = try sym.getOrCreateGotEntry(sym_index, elf_file); |
| 865 | return GenResult.mcv(.{ .memory = sym.gotAddress(elf_file) }); | 865 | return GenResult.mcv(.{ .memory = sym.gotAddress(elf_file) }); |
| 866 | } else if (bin_file.cast(link.File.MachO)) |macho_file| { | 866 | } else if (bin_file.cast(link.File.MachO)) |macho_file| { |
| 867 | const atom_index = try macho_file.getOrCreateAtomForDecl(decl_index); | 867 | const atom_index = try macho_file.getOrCreateAtomForDecl(decl_index); |
src/link/Elf.zig+194-23| ... | @@ -40,6 +40,8 @@ phdr_got_index: ?u16 = null, | ... | @@ -40,6 +40,8 @@ phdr_got_index: ?u16 = null, |
| 40 | phdr_load_ro_index: ?u16 = null, | 40 | phdr_load_ro_index: ?u16 = null, |
| 41 | /// The index into the program headers of a PT_LOAD program header with Write flag | 41 | /// The index into the program headers of a PT_LOAD program header with Write flag |
| 42 | phdr_load_rw_index: ?u16 = null, | 42 | phdr_load_rw_index: ?u16 = null, |
| 43 | /// The index into the program headers of a PT_LOAD program header with zerofill data. | ||
| 44 | phdr_load_zerofill_index: ?u16 = null, | ||
| 43 | 45 | ||
| 44 | entry_addr: ?u64 = null, | 46 | entry_addr: ?u64 = null, |
| 45 | page_size: u32, | 47 | page_size: u32, |
| ... | @@ -56,6 +58,7 @@ got: GotSection = .{}, | ... | @@ -56,6 +58,7 @@ got: GotSection = .{}, |
| 56 | text_section_index: ?u16 = null, | 58 | text_section_index: ?u16 = null, |
| 57 | rodata_section_index: ?u16 = null, | 59 | rodata_section_index: ?u16 = null, |
| 58 | data_section_index: ?u16 = null, | 60 | data_section_index: ?u16 = null, |
| 61 | bss_section_index: ?u16 = null, | ||
| 59 | eh_frame_section_index: ?u16 = null, | 62 | eh_frame_section_index: ?u16 = null, |
| 60 | eh_frame_hdr_section_index: ?u16 = null, | 63 | eh_frame_hdr_section_index: ?u16 = null, |
| 61 | dynamic_section_index: ?u16 = null, | 64 | dynamic_section_index: ?u16 = null, |
| ... | @@ -532,6 +535,26 @@ pub fn populateMissingMetadata(self: *Elf) !void { | ... | @@ -532,6 +535,26 @@ pub fn populateMissingMetadata(self: *Elf) !void { |
| 532 | self.phdr_table_dirty = true; | 535 | self.phdr_table_dirty = true; |
| 533 | } | 536 | } |
| 534 | 537 | ||
| 538 | if (self.phdr_load_zerofill_index == null) { | ||
| 539 | self.phdr_load_zerofill_index = @as(u16, @intCast(self.phdrs.items.len)); | ||
| 540 | const p_align = if (self.base.options.target.os.tag == .linux) self.page_size else @as(u16, ptr_size); | ||
| 541 | const off = self.phdrs.items[self.phdr_load_rw_index.?].p_offset; | ||
| 542 | log.debug("found PT_LOAD zerofill free space 0x{x} to 0x{x}", .{ off, off }); | ||
| 543 | // TODO Same as for GOT | ||
| 544 | const addr: u32 = if (self.base.options.target.ptrBitWidth() >= 32) 0x14000000 else 0xf000; | ||
| 545 | try self.phdrs.append(gpa, .{ | ||
| 546 | .p_type = elf.PT_LOAD, | ||
| 547 | .p_offset = off, | ||
| 548 | .p_filesz = 0, | ||
| 549 | .p_vaddr = addr, | ||
| 550 | .p_paddr = addr, | ||
| 551 | .p_memsz = 0, | ||
| 552 | .p_align = p_align, | ||
| 553 | .p_flags = elf.PF_R | elf.PF_W, | ||
| 554 | }); | ||
| 555 | self.phdr_table_dirty = true; | ||
| 556 | } | ||
| 557 | |||
| 535 | if (self.shstrtab_section_index == null) { | 558 | if (self.shstrtab_section_index == null) { |
| 536 | self.shstrtab_section_index = @as(u16, @intCast(self.shdrs.items.len)); | 559 | self.shstrtab_section_index = @as(u16, @intCast(self.shdrs.items.len)); |
| 537 | assert(self.shstrtab.buffer.items.len == 0); | 560 | assert(self.shstrtab.buffer.items.len == 0); |
| ... | @@ -655,6 +678,26 @@ pub fn populateMissingMetadata(self: *Elf) !void { | ... | @@ -655,6 +678,26 @@ pub fn populateMissingMetadata(self: *Elf) !void { |
| 655 | self.shdr_table_dirty = true; | 678 | self.shdr_table_dirty = true; |
| 656 | } | 679 | } |
| 657 | 680 | ||
| 681 | if (self.bss_section_index == null) { | ||
| 682 | self.bss_section_index = @as(u16, @intCast(self.shdrs.items.len)); | ||
| 683 | const phdr = &self.phdrs.items[self.phdr_load_zerofill_index.?]; | ||
| 684 | try self.shdrs.append(gpa, .{ | ||
| 685 | .sh_name = try self.shstrtab.insert(gpa, ".bss"), | ||
| 686 | .sh_type = elf.SHT_NOBITS, | ||
| 687 | .sh_flags = elf.SHF_WRITE | elf.SHF_ALLOC, | ||
| 688 | .sh_addr = phdr.p_vaddr, | ||
| 689 | .sh_offset = phdr.p_offset, | ||
| 690 | .sh_size = phdr.p_filesz, | ||
| 691 | .sh_link = 0, | ||
| 692 | .sh_info = 0, | ||
| 693 | .sh_addralign = @as(u16, ptr_size), | ||
| 694 | .sh_entsize = 0, | ||
| 695 | }); | ||
| 696 | try self.phdr_to_shdr_table.putNoClobber(gpa, self.bss_section_index.?, self.phdr_load_zerofill_index.?); | ||
| 697 | try self.last_atom_and_free_list_table.putNoClobber(gpa, self.bss_section_index.?, .{}); | ||
| 698 | self.shdr_table_dirty = true; | ||
| 699 | } | ||
| 700 | |||
| 658 | if (self.symtab_section_index == null) { | 701 | if (self.symtab_section_index == null) { |
| 659 | self.symtab_section_index = @as(u16, @intCast(self.shdrs.items.len)); | 702 | self.symtab_section_index = @as(u16, @intCast(self.shdrs.items.len)); |
| 660 | const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym); | 703 | const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym); |
| ... | @@ -868,8 +911,9 @@ pub fn growAllocSection(self: *Elf, shdr_index: u16, needed_size: u64) !void { | ... | @@ -868,8 +911,9 @@ pub fn growAllocSection(self: *Elf, shdr_index: u16, needed_size: u64) !void { |
| 868 | const shdr = &self.shdrs.items[shdr_index]; | 911 | const shdr = &self.shdrs.items[shdr_index]; |
| 869 | const phdr_index = self.phdr_to_shdr_table.get(shdr_index).?; | 912 | const phdr_index = self.phdr_to_shdr_table.get(shdr_index).?; |
| 870 | const phdr = &self.phdrs.items[phdr_index]; | 913 | const phdr = &self.phdrs.items[phdr_index]; |
| 914 | const is_zerofill = shdr.sh_type == elf.SHT_NOBITS; | ||
| 871 | 915 | ||
| 872 | if (needed_size > self.allocatedSize(shdr.sh_offset)) { | 916 | if (needed_size > self.allocatedSize(shdr.sh_offset) and !is_zerofill) { |
| 873 | // Must move the entire section. | 917 | // Must move the entire section. |
| 874 | const new_offset = self.findFreeSpace(needed_size, self.page_size); | 918 | const new_offset = self.findFreeSpace(needed_size, self.page_size); |
| 875 | const existing_size = if (self.last_atom_and_free_list_table.get(shdr_index)) |meta| blk: { | 919 | const existing_size = if (self.last_atom_and_free_list_table.get(shdr_index)) |meta| blk: { |
| ... | @@ -893,7 +937,10 @@ pub fn growAllocSection(self: *Elf, shdr_index: u16, needed_size: u64) !void { | ... | @@ -893,7 +937,10 @@ pub fn growAllocSection(self: *Elf, shdr_index: u16, needed_size: u64) !void { |
| 893 | 937 | ||
| 894 | shdr.sh_size = needed_size; | 938 | shdr.sh_size = needed_size; |
| 895 | phdr.p_memsz = needed_size; | 939 | phdr.p_memsz = needed_size; |
| 896 | phdr.p_filesz = needed_size; | 940 | |
| 941 | if (!is_zerofill) { | ||
| 942 | phdr.p_filesz = needed_size; | ||
| 943 | } | ||
| 897 | 944 | ||
| 898 | self.markDirty(shdr_index, phdr_index); | 945 | self.markDirty(shdr_index, phdr_index); |
| 899 | } | 946 | } |
| ... | @@ -1005,13 +1052,6 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node | ... | @@ -1005,13 +1052,6 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node |
| 1005 | const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type. | 1052 | const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type. |
| 1006 | const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path}); | 1053 | const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path}); |
| 1007 | 1054 | ||
| 1008 | const compiler_rt_path: ?[]const u8 = blk: { | ||
| 1009 | if (comp.compiler_rt_lib) |x| break :blk x.full_object_path; | ||
| 1010 | if (comp.compiler_rt_obj) |x| break :blk x.full_object_path; | ||
| 1011 | break :blk null; | ||
| 1012 | }; | ||
| 1013 | _ = compiler_rt_path; | ||
| 1014 | |||
| 1015 | // Here we will parse input positional and library files (if referenced). | 1055 | // Here we will parse input positional and library files (if referenced). |
| 1016 | // This will roughly match in any linker backend we support. | 1056 | // This will roughly match in any linker backend we support. |
| 1017 | var positionals = std.ArrayList(Compilation.LinkObject).init(arena); | 1057 | var positionals = std.ArrayList(Compilation.LinkObject).init(arena); |
| ... | @@ -1037,6 +1077,15 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node | ... | @@ -1037,6 +1077,15 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node |
| 1037 | try positionals.append(.{ .path = key.status.success.object_path }); | 1077 | try positionals.append(.{ .path = key.status.success.object_path }); |
| 1038 | } | 1078 | } |
| 1039 | 1079 | ||
| 1080 | const compiler_rt_path: ?[]const u8 = blk: { | ||
| 1081 | if (comp.compiler_rt_lib) |x| break :blk x.full_object_path; | ||
| 1082 | if (comp.compiler_rt_obj) |x| break :blk x.full_object_path; | ||
| 1083 | break :blk null; | ||
| 1084 | }; | ||
| 1085 | if (compiler_rt_path) |path| { | ||
| 1086 | try positionals.append(.{ .path = path }); | ||
| 1087 | } | ||
| 1088 | |||
| 1040 | for (positionals.items) |obj| { | 1089 | for (positionals.items) |obj| { |
| 1041 | const in_file = try std.fs.cwd().openFile(obj.path, .{}); | 1090 | const in_file = try std.fs.cwd().openFile(obj.path, .{}); |
| 1042 | defer in_file.close(); | 1091 | defer in_file.close(); |
| ... | @@ -1093,7 +1142,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node | ... | @@ -1093,7 +1142,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node |
| 1093 | // input Object files. | 1142 | // input Object files. |
| 1094 | // Any qualifing unresolved symbol will be upgraded to an absolute, weak | 1143 | // Any qualifing unresolved symbol will be upgraded to an absolute, weak |
| 1095 | // symbol for potential resolution at load-time. | 1144 | // symbol for potential resolution at load-time. |
| 1096 | self.resolveSymbols(); | 1145 | try self.resolveSymbols(); |
| 1097 | self.markImportsExports(); | 1146 | self.markImportsExports(); |
| 1098 | self.claimUnresolved(); | 1147 | self.claimUnresolved(); |
| 1099 | 1148 | ||
| ... | @@ -1356,6 +1405,7 @@ const ParseError = error{ | ... | @@ -1356,6 +1405,7 @@ const ParseError = error{ |
| 1356 | EndOfStream, | 1405 | EndOfStream, |
| 1357 | FileSystem, | 1406 | FileSystem, |
| 1358 | NotSupported, | 1407 | NotSupported, |
| 1408 | InvalidCharacter, | ||
| 1359 | } || std.os.SeekError || std.fs.File.OpenError || std.fs.File.ReadError; | 1409 | } || std.os.SeekError || std.fs.File.OpenError || std.fs.File.ReadError; |
| 1360 | 1410 | ||
| 1361 | fn parsePositional( | 1411 | fn parsePositional( |
| ... | @@ -1367,10 +1417,32 @@ fn parsePositional( | ... | @@ -1367,10 +1417,32 @@ fn parsePositional( |
| 1367 | ) ParseError!void { | 1417 | ) ParseError!void { |
| 1368 | const tracy = trace(@src()); | 1418 | const tracy = trace(@src()); |
| 1369 | defer tracy.end(); | 1419 | defer tracy.end(); |
| 1370 | _ = must_link; | ||
| 1371 | 1420 | ||
| 1372 | if (Object.isObject(in_file)) { | 1421 | if (Object.isObject(in_file)) { |
| 1373 | try self.parseObject(in_file, path, ctx); | 1422 | try self.parseObject(in_file, path, ctx); |
| 1423 | } else { | ||
| 1424 | try self.parseLibrary(in_file, path, .{ | ||
| 1425 | .path = null, | ||
| 1426 | .needed = false, | ||
| 1427 | .weak = false, | ||
| 1428 | }, must_link, ctx); | ||
| 1429 | } | ||
| 1430 | } | ||
| 1431 | |||
| 1432 | fn parseLibrary( | ||
| 1433 | self: *Elf, | ||
| 1434 | in_file: std.fs.File, | ||
| 1435 | path: []const u8, | ||
| 1436 | lib: link.SystemLib, | ||
| 1437 | must_link: bool, | ||
| 1438 | ctx: *ParseErrorCtx, | ||
| 1439 | ) ParseError!void { | ||
| 1440 | const tracy = trace(@src()); | ||
| 1441 | defer tracy.end(); | ||
| 1442 | _ = lib; | ||
| 1443 | |||
| 1444 | if (Archive.isArchive(in_file)) { | ||
| 1445 | try self.parseArchive(in_file, path, must_link, ctx); | ||
| 1374 | } else return error.UnknownFileType; | 1446 | } else return error.UnknownFileType; |
| 1375 | } | 1447 | } |
| 1376 | 1448 | ||
| ... | @@ -1395,15 +1467,109 @@ fn parseObject(self: *Elf, in_file: std.fs.File, path: []const u8, ctx: *ParseEr | ... | @@ -1395,15 +1467,109 @@ fn parseObject(self: *Elf, in_file: std.fs.File, path: []const u8, ctx: *ParseEr |
| 1395 | if (ctx.detected_cpu_arch != self.base.options.target.cpu.arch) return error.InvalidCpuArch; | 1467 | if (ctx.detected_cpu_arch != self.base.options.target.cpu.arch) return error.InvalidCpuArch; |
| 1396 | } | 1468 | } |
| 1397 | 1469 | ||
| 1398 | fn resolveSymbols(self: *Elf) void { | 1470 | fn parseArchive( |
| 1399 | if (self.zig_module_index) |index| { | 1471 | self: *Elf, |
| 1400 | const zig_module = self.file(index).?.zig_module; | 1472 | in_file: std.fs.File, |
| 1401 | zig_module.resolveSymbols(self); | 1473 | path: []const u8, |
| 1474 | must_link: bool, | ||
| 1475 | ctx: *ParseErrorCtx, | ||
| 1476 | ) ParseError!void { | ||
| 1477 | const tracy = trace(@src()); | ||
| 1478 | defer tracy.end(); | ||
| 1479 | |||
| 1480 | const gpa = self.base.allocator; | ||
| 1481 | const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32)); | ||
| 1482 | var archive = Archive{ .path = path, .data = data }; | ||
| 1483 | defer archive.deinit(gpa); | ||
| 1484 | try archive.parse(self); | ||
| 1485 | |||
| 1486 | for (archive.objects.items) |extracted| { | ||
| 1487 | const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); | ||
| 1488 | self.files.set(index, .{ .object = extracted }); | ||
| 1489 | const object = &self.files.items(.data)[index].object; | ||
| 1490 | object.index = index; | ||
| 1491 | object.alive = must_link; | ||
| 1492 | try object.parse(self); | ||
| 1493 | try self.objects.append(gpa, index); | ||
| 1494 | |||
| 1495 | ctx.detected_cpu_arch = object.header.?.e_machine.toTargetCpuArch().?; | ||
| 1496 | if (ctx.detected_cpu_arch != self.base.options.target.cpu.arch) return error.InvalidCpuArch; | ||
| 1497 | } | ||
| 1498 | } | ||
| 1499 | |||
| 1500 | /// When resolving symbols, we approach the problem similarly to `mold`. | ||
| 1501 | /// 1. Resolve symbols across all objects (including those preemptively extracted archives). | ||
| 1502 | /// 2. Resolve symbols across all shared objects. | ||
| 1503 | /// 3. Mark live objects (see `Elf.markLive`) | ||
| 1504 | /// 4. Reset state of all resolved globals since we will redo this bit on the pruned set. | ||
| 1505 | /// 5. Remove references to dead objects/shared objects | ||
| 1506 | /// 6. Re-run symbol resolution on pruned objects and shared objects sets. | ||
| 1507 | fn resolveSymbols(self: *Elf) error{Overflow}!void { | ||
| 1508 | // Resolve symbols in the ZigModule. For now, we assume that it's always live. | ||
| 1509 | if (self.zig_module_index) |index| self.file(index).?.resolveSymbols(self); | ||
| 1510 | // Resolve symbols on the set of all objects and shared objects (even if some are unneeded). | ||
| 1511 | for (self.objects.items) |index| self.file(index).?.resolveSymbols(self); | ||
| 1512 | |||
| 1513 | // Mark live objects. | ||
| 1514 | self.markLive(); | ||
| 1515 | |||
| 1516 | // Reset state of all globals after marking live objects. | ||
| 1517 | if (self.zig_module_index) |index| self.file(index).?.resetGlobals(self); | ||
| 1518 | for (self.objects.items) |index| self.file(index).?.resetGlobals(self); | ||
| 1519 | |||
| 1520 | // Prune dead objects and shared objects. | ||
| 1521 | var i: usize = 0; | ||
| 1522 | while (i < self.objects.items.len) { | ||
| 1523 | const index = self.objects.items[i]; | ||
| 1524 | if (!self.file(index).?.isAlive()) { | ||
| 1525 | _ = self.objects.orderedRemove(i); | ||
| 1526 | } else i += 1; | ||
| 1527 | } | ||
| 1528 | |||
| 1529 | // Dedup comdat groups. | ||
| 1530 | for (self.objects.items) |index| { | ||
| 1531 | const object = self.file(index).?.object; | ||
| 1532 | for (object.comdat_groups.items) |cg_index| { | ||
| 1533 | const cg = self.comdatGroup(cg_index); | ||
| 1534 | const cg_owner = self.comdatGroupOwner(cg.owner); | ||
| 1535 | const owner_file_index = if (self.file(cg_owner.file)) |file_ptr| | ||
| 1536 | file_ptr.object.index | ||
| 1537 | else | ||
| 1538 | std.math.maxInt(File.Index); | ||
| 1539 | cg_owner.file = @min(owner_file_index, index); | ||
| 1540 | } | ||
| 1402 | } | 1541 | } |
| 1403 | 1542 | ||
| 1404 | for (self.objects.items) |index| { | 1543 | for (self.objects.items) |index| { |
| 1405 | const object = self.file(index).?.object; | 1544 | const object = self.file(index).?.object; |
| 1406 | object.resolveSymbols(self); | 1545 | for (object.comdat_groups.items) |cg_index| { |
| 1546 | const cg = self.comdatGroup(cg_index); | ||
| 1547 | const cg_owner = self.comdatGroupOwner(cg.owner); | ||
| 1548 | if (cg_owner.file != index) { | ||
| 1549 | for (try object.comdatGroupMembers(cg.shndx)) |shndx| { | ||
| 1550 | const atom_index = object.atoms.items[shndx]; | ||
| 1551 | if (self.atom(atom_index)) |atom_ptr| { | ||
| 1552 | atom_ptr.alive = false; | ||
| 1553 | // atom_ptr.markFdesDead(self); | ||
| 1554 | } | ||
| 1555 | } | ||
| 1556 | } | ||
| 1557 | } | ||
| 1558 | } | ||
| 1559 | |||
| 1560 | // Re-resolve the symbols. | ||
| 1561 | if (self.zig_module_index) |index| self.file(index).?.resolveSymbols(self); | ||
| 1562 | for (self.objects.items) |index| self.file(index).?.resolveSymbols(self); | ||
| 1563 | } | ||
| 1564 | |||
| 1565 | /// Traverses all objects and shared objects marking any object referenced by | ||
| 1566 | /// a live object/shared object as alive itself. | ||
| 1567 | /// This routine will prune unneeded objects extracted from archives and | ||
| 1568 | /// unneeded shared objects. | ||
| 1569 | fn markLive(self: *Elf) void { | ||
| 1570 | for (self.objects.items) |index| { | ||
| 1571 | const file_ptr = self.file(index).?; | ||
| 1572 | if (file_ptr.isAlive()) file_ptr.markLive(self); | ||
| 1407 | } | 1573 | } |
| 1408 | } | 1574 | } |
| 1409 | 1575 | ||
| ... | @@ -1477,11 +1643,11 @@ fn scanRelocs(self: *Elf) !void { | ... | @@ -1477,11 +1643,11 @@ fn scanRelocs(self: *Elf) !void { |
| 1477 | 1643 | ||
| 1478 | try self.reportUndefined(&undefs); | 1644 | try self.reportUndefined(&undefs); |
| 1479 | 1645 | ||
| 1480 | for (self.symbols.items) |*sym| { | 1646 | for (self.symbols.items, 0..) |*sym, sym_index| { |
| 1481 | if (sym.flags.needs_got) { | 1647 | if (sym.flags.needs_got) { |
| 1482 | log.debug("'{s}' needs GOT", .{sym.name(self)}); | 1648 | log.debug("'{s}' needs GOT", .{sym.name(self)}); |
| 1483 | // TODO how can we tell we need to write it again, aka the entry is dirty? | 1649 | // TODO how can we tell we need to write it again, aka the entry is dirty? |
| 1484 | const gop = try sym.getOrCreateGotEntry(self); | 1650 | const gop = try sym.getOrCreateGotEntry(@intCast(sym_index), self); |
| 1485 | try self.got.writeEntry(self, gop.index); | 1651 | try self.got.writeEntry(self, gop.index); |
| 1486 | } | 1652 | } |
| 1487 | } | 1653 | } |
| ... | @@ -1500,7 +1666,7 @@ fn allocateObjects(self: *Elf) !void { | ... | @@ -1500,7 +1666,7 @@ fn allocateObjects(self: *Elf) !void { |
| 1500 | const local = self.symbol(local_index); | 1666 | const local = self.symbol(local_index); |
| 1501 | const atom_ptr = local.atom(self) orelse continue; | 1667 | const atom_ptr = local.atom(self) orelse continue; |
| 1502 | if (!atom_ptr.alive) continue; | 1668 | if (!atom_ptr.alive) continue; |
| 1503 | local.value = atom_ptr.value; | 1669 | local.value += atom_ptr.value; |
| 1504 | } | 1670 | } |
| 1505 | 1671 | ||
| 1506 | for (object.globals()) |global_index| { | 1672 | for (object.globals()) |global_index| { |
| ... | @@ -1508,7 +1674,7 @@ fn allocateObjects(self: *Elf) !void { | ... | @@ -1508,7 +1674,7 @@ fn allocateObjects(self: *Elf) !void { |
| 1508 | const atom_ptr = global.atom(self) orelse continue; | 1674 | const atom_ptr = global.atom(self) orelse continue; |
| 1509 | if (!atom_ptr.alive) continue; | 1675 | if (!atom_ptr.alive) continue; |
| 1510 | if (global.file_index == index) { | 1676 | if (global.file_index == index) { |
| 1511 | global.value = atom_ptr.value; | 1677 | global.value += atom_ptr.value; |
| 1512 | } | 1678 | } |
| 1513 | } | 1679 | } |
| 1514 | } | 1680 | } |
| ... | @@ -1524,7 +1690,11 @@ fn writeObjects(self: *Elf) !void { | ... | @@ -1524,7 +1690,11 @@ fn writeObjects(self: *Elf) !void { |
| 1524 | if (!atom_ptr.alive) continue; | 1690 | if (!atom_ptr.alive) continue; |
| 1525 | 1691 | ||
| 1526 | const shdr = &self.shdrs.items[atom_ptr.output_section_index]; | 1692 | const shdr = &self.shdrs.items[atom_ptr.output_section_index]; |
| 1693 | if (shdr.sh_type == elf.SHT_NOBITS) continue; | ||
| 1694 | if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue; // TODO we don't yet know how to handle non-alloc sections | ||
| 1695 | |||
| 1527 | const file_offset = shdr.sh_offset + atom_ptr.value - shdr.sh_addr; | 1696 | const file_offset = shdr.sh_offset + atom_ptr.value - shdr.sh_addr; |
| 1697 | log.debug("writing atom({d}) at 0x{x}", .{ atom_ptr.atom_index, file_offset }); | ||
| 1528 | const code = try atom_ptr.codeInObjectUncompressAlloc(self); | 1698 | const code = try atom_ptr.codeInObjectUncompressAlloc(self); |
| 1529 | defer gpa.free(code); | 1699 | defer gpa.free(code); |
| 1530 | 1700 | ||
| ... | @@ -2529,7 +2699,7 @@ fn updateDeclCode( | ... | @@ -2529,7 +2699,7 @@ fn updateDeclCode( |
| 2529 | esym.st_value = atom_ptr.value; | 2699 | esym.st_value = atom_ptr.value; |
| 2530 | 2700 | ||
| 2531 | sym.flags.needs_got = true; | 2701 | sym.flags.needs_got = true; |
| 2532 | const gop = try sym.getOrCreateGotEntry(self); | 2702 | const gop = try sym.getOrCreateGotEntry(sym_index, self); |
| 2533 | try self.got.writeEntry(self, gop.index); | 2703 | try self.got.writeEntry(self, gop.index); |
| 2534 | } | 2704 | } |
| 2535 | 2705 | ||
| ... | @@ -2764,7 +2934,7 @@ fn updateLazySymbol(self: *Elf, sym: link.File.LazySymbol, symbol_index: Symbol. | ... | @@ -2764,7 +2934,7 @@ fn updateLazySymbol(self: *Elf, sym: link.File.LazySymbol, symbol_index: Symbol. |
| 2764 | local_esym.st_value = atom_ptr.value; | 2934 | local_esym.st_value = atom_ptr.value; |
| 2765 | 2935 | ||
| 2766 | local_sym.flags.needs_got = true; | 2936 | local_sym.flags.needs_got = true; |
| 2767 | const gop = try local_sym.getOrCreateGotEntry(self); | 2937 | const gop = try local_sym.getOrCreateGotEntry(symbol_index, self); |
| 2768 | try self.got.writeEntry(self, gop.index); | 2938 | try self.got.writeEntry(self, gop.index); |
| 2769 | 2939 | ||
| 2770 | const section_offset = atom_ptr.value - self.phdrs.items[phdr_index].p_vaddr; | 2940 | const section_offset = atom_ptr.value - self.phdrs.items[phdr_index].p_vaddr; |
| ... | @@ -3635,7 +3805,7 @@ pub fn addSymbol(self: *Elf) !Symbol.Index { | ... | @@ -3635,7 +3805,7 @@ pub fn addSymbol(self: *Elf) !Symbol.Index { |
| 3635 | break :blk index; | 3805 | break :blk index; |
| 3636 | } | 3806 | } |
| 3637 | }; | 3807 | }; |
| 3638 | self.symbols.items[index] = .{ .index = index }; | 3808 | self.symbols.items[index] = .{}; |
| 3639 | return index; | 3809 | return index; |
| 3640 | } | 3810 | } |
| 3641 | 3811 | ||
| ... | @@ -4012,6 +4182,7 @@ const synthetic_sections = @import("Elf/synthetic_sections.zig"); | ... | @@ -4012,6 +4182,7 @@ const synthetic_sections = @import("Elf/synthetic_sections.zig"); |
| 4012 | 4182 | ||
| 4013 | const Air = @import("../Air.zig"); | 4183 | const Air = @import("../Air.zig"); |
| 4014 | const Allocator = std.mem.Allocator; | 4184 | const Allocator = std.mem.Allocator; |
| 4185 | const Archive = @import("Elf/Archive.zig"); | ||
| 4015 | pub const Atom = @import("Elf/Atom.zig"); | 4186 | pub const Atom = @import("Elf/Atom.zig"); |
| 4016 | const Cache = std.Build.Cache; | 4187 | const Cache = std.Build.Cache; |
| 4017 | const Compilation = @import("../Compilation.zig"); | 4188 | const Compilation = @import("../Compilation.zig"); |
src/link/Elf/Archive.zig created+153| ... | @@ -0,0 +1,153 @@ | ||
| 1 | path: []const u8, | ||
| 2 | data: []const u8, | ||
| 3 | |||
| 4 | objects: std.ArrayListUnmanaged(Object) = .{}, | ||
| 5 | strtab: []const u8 = &[0]u8{}, | ||
| 6 | |||
| 7 | // Archive files start with the ARMAG identifying string. Then follows a | ||
| 8 | // `struct ar_hdr', and as many bytes of member file data as its `ar_size' | ||
| 9 | // member indicates, for each member file. | ||
| 10 | /// String that begins an archive file. | ||
| 11 | pub const ARMAG: *const [SARMAG:0]u8 = "!<arch>\n"; | ||
| 12 | /// Size of that string. | ||
| 13 | pub const SARMAG: u4 = 8; | ||
| 14 | |||
| 15 | /// String in ar_fmag at the end of each header. | ||
| 16 | const ARFMAG: *const [2:0]u8 = "`\n"; | ||
| 17 | |||
| 18 | const SYM64NAME: *const [7:0]u8 = "/SYM64/"; | ||
| 19 | |||
| 20 | const ar_hdr = extern struct { | ||
| 21 | /// Member file name, sometimes / terminated. | ||
| 22 | ar_name: [16]u8, | ||
| 23 | |||
| 24 | /// File date, decimal seconds since Epoch. | ||
| 25 | ar_date: [12]u8, | ||
| 26 | |||
| 27 | /// User ID, in ASCII format. | ||
| 28 | ar_uid: [6]u8, | ||
| 29 | |||
| 30 | /// Group ID, in ASCII format. | ||
| 31 | ar_gid: [6]u8, | ||
| 32 | |||
| 33 | /// File mode, in ASCII octal. | ||
| 34 | ar_mode: [8]u8, | ||
| 35 | |||
| 36 | /// File size, in ASCII decimal. | ||
| 37 | ar_size: [10]u8, | ||
| 38 | |||
| 39 | /// Always contains ARFMAG. | ||
| 40 | ar_fmag: [2]u8, | ||
| 41 | |||
| 42 | fn date(self: ar_hdr) !u64 { | ||
| 43 | const value = getValue(&self.ar_date); | ||
| 44 | return std.fmt.parseInt(u64, value, 10); | ||
| 45 | } | ||
| 46 | |||
| 47 | fn size(self: ar_hdr) !u32 { | ||
| 48 | const value = getValue(&self.ar_size); | ||
| 49 | return std.fmt.parseInt(u32, value, 10); | ||
| 50 | } | ||
| 51 | |||
| 52 | fn getValue(raw: []const u8) []const u8 { | ||
| 53 | return mem.trimRight(u8, raw, &[_]u8{@as(u8, 0x20)}); | ||
| 54 | } | ||
| 55 | |||
| 56 | fn isStrtab(self: ar_hdr) bool { | ||
| 57 | return mem.eql(u8, getValue(&self.ar_name), "//"); | ||
| 58 | } | ||
| 59 | |||
| 60 | fn isSymtab(self: ar_hdr) bool { | ||
| 61 | return mem.eql(u8, getValue(&self.ar_name), "/"); | ||
| 62 | } | ||
| 63 | }; | ||
| 64 | |||
| 65 | pub fn isArchive(file: std.fs.File) bool { | ||
| 66 | const reader = file.reader(); | ||
| 67 | const magic = reader.readBytesNoEof(Archive.SARMAG) catch return false; | ||
| 68 | defer file.seekTo(0) catch {}; | ||
| 69 | if (!mem.eql(u8, &magic, ARMAG)) return false; | ||
| 70 | return true; | ||
| 71 | } | ||
| 72 | |||
| 73 | pub fn deinit(self: *Archive, allocator: Allocator) void { | ||
| 74 | allocator.free(self.data); | ||
| 75 | self.objects.deinit(allocator); | ||
| 76 | } | ||
| 77 | |||
| 78 | pub fn parse(self: *Archive, elf_file: *Elf) !void { | ||
| 79 | const gpa = elf_file.base.allocator; | ||
| 80 | |||
| 81 | var stream = std.io.fixedBufferStream(self.data); | ||
| 82 | const reader = stream.reader(); | ||
| 83 | _ = try reader.readBytesNoEof(SARMAG); | ||
| 84 | |||
| 85 | while (true) { | ||
| 86 | if (stream.pos % 2 != 0) { | ||
| 87 | stream.pos += 1; | ||
| 88 | } | ||
| 89 | |||
| 90 | const hdr = reader.readStruct(ar_hdr) catch break; | ||
| 91 | |||
| 92 | if (!mem.eql(u8, &hdr.ar_fmag, ARFMAG)) { | ||
| 93 | // TODO convert into an error | ||
| 94 | log.debug( | ||
| 95 | "{s}: invalid header delimiter: expected '{s}', found '{s}'", | ||
| 96 | .{ self.path, std.fmt.fmtSliceEscapeLower(ARFMAG), std.fmt.fmtSliceEscapeLower(&hdr.ar_fmag) }, | ||
| 97 | ); | ||
| 98 | return; | ||
| 99 | } | ||
| 100 | |||
| 101 | const size = try hdr.size(); | ||
| 102 | defer { | ||
| 103 | _ = stream.seekBy(size) catch {}; | ||
| 104 | } | ||
| 105 | |||
| 106 | if (hdr.isSymtab()) continue; | ||
| 107 | if (hdr.isStrtab()) { | ||
| 108 | self.strtab = self.data[stream.pos..][0..size]; | ||
| 109 | continue; | ||
| 110 | } | ||
| 111 | |||
| 112 | const name = ar_hdr.getValue(&hdr.ar_name); | ||
| 113 | |||
| 114 | if (mem.eql(u8, name, "__.SYMDEF") or mem.eql(u8, name, "__.SYMDEF SORTED")) continue; | ||
| 115 | |||
| 116 | const object_name = blk: { | ||
| 117 | if (name[0] == '/') { | ||
| 118 | const off = try std.fmt.parseInt(u32, name[1..], 10); | ||
| 119 | break :blk self.getString(off); | ||
| 120 | } | ||
| 121 | break :blk name; | ||
| 122 | }; | ||
| 123 | |||
| 124 | const object = Object{ | ||
| 125 | .archive = self.path, | ||
| 126 | .path = try gpa.dupe(u8, object_name[0 .. object_name.len - 1]), // To account for trailing '/' | ||
| 127 | .data = try gpa.dupe(u8, self.data[stream.pos..][0..size]), | ||
| 128 | .index = undefined, | ||
| 129 | .alive = false, | ||
| 130 | }; | ||
| 131 | |||
| 132 | log.debug("extracting object '{s}' from archive '{s}'", .{ object.path, self.path }); | ||
| 133 | |||
| 134 | try self.objects.append(gpa, object); | ||
| 135 | } | ||
| 136 | } | ||
| 137 | |||
| 138 | fn getString(self: Archive, off: u32) []const u8 { | ||
| 139 | assert(off < self.strtab.len); | ||
| 140 | return mem.sliceTo(@as([*:'\n']const u8, @ptrCast(self.strtab.ptr + off)), 0); | ||
| 141 | } | ||
| 142 | |||
| 143 | const std = @import("std"); | ||
| 144 | const assert = std.debug.assert; | ||
| 145 | const elf = std.elf; | ||
| 146 | const fs = std.fs; | ||
| 147 | const log = std.log.scoped(.link); | ||
| 148 | const mem = std.mem; | ||
| 149 | |||
| 150 | const Allocator = mem.Allocator; | ||
| 151 | const Archive = @This(); | ||
| 152 | const Elf = @import("../Elf.zig"); | ||
| 153 | const Object = @import("Object.zig"); | ||
src/link/Elf/Atom.zig+94-8| ... | @@ -322,11 +322,12 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf, undefs: anytype) !void { | ... | @@ -322,11 +322,12 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf, undefs: anytype) !void { |
| 322 | 322 | ||
| 323 | if (rel.r_type() == elf.R_X86_64_NONE) continue; | 323 | if (rel.r_type() == elf.R_X86_64_NONE) continue; |
| 324 | 324 | ||
| 325 | const symbol = switch (file_ptr) { | 325 | const symbol_index = switch (file_ptr) { |
| 326 | .zig_module => |x| elf_file.symbol(x.symbol(rel.r_sym())), | 326 | .zig_module => |x| x.symbol(rel.r_sym()), |
| 327 | .object => |x| elf_file.symbol(x.symbols.items[rel.r_sym()]), | 327 | .object => |x| x.symbols.items[rel.r_sym()], |
| 328 | else => unreachable, | 328 | else => unreachable, |
| 329 | }; | 329 | }; |
| 330 | const symbol = elf_file.symbol(symbol_index); | ||
| 330 | 331 | ||
| 331 | // Check for violation of One Definition Rule for COMDATs. | 332 | // Check for violation of One Definition Rule for COMDATs. |
| 332 | if (symbol.file(elf_file) == null) { | 333 | if (symbol.file(elf_file) == null) { |
| ... | @@ -340,7 +341,7 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf, undefs: anytype) !void { | ... | @@ -340,7 +341,7 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf, undefs: anytype) !void { |
| 340 | } | 341 | } |
| 341 | 342 | ||
| 342 | // Report an undefined symbol. | 343 | // Report an undefined symbol. |
| 343 | try self.reportUndefined(elf_file, symbol, rel, undefs); | 344 | try self.reportUndefined(elf_file, symbol, symbol_index, rel, undefs); |
| 344 | 345 | ||
| 345 | // While traversing relocations, mark symbols that require special handling such as | 346 | // While traversing relocations, mark symbols that require special handling such as |
| 346 | // pointer indirection via GOT, or a stub trampoline via PLT. | 347 | // pointer indirection via GOT, or a stub trampoline via PLT. |
| ... | @@ -379,7 +380,14 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf, undefs: anytype) !void { | ... | @@ -379,7 +380,14 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf, undefs: anytype) !void { |
| 379 | } | 380 | } |
| 380 | 381 | ||
| 381 | // This function will report any undefined non-weak symbols that are not imports. | 382 | // This function will report any undefined non-weak symbols that are not imports. |
| 382 | fn reportUndefined(self: Atom, elf_file: *Elf, sym: *const Symbol, rel: elf.Elf64_Rela, undefs: anytype) !void { | 383 | fn reportUndefined( |
| 384 | self: Atom, | ||
| 385 | elf_file: *Elf, | ||
| 386 | sym: *const Symbol, | ||
| 387 | sym_index: Symbol.Index, | ||
| 388 | rel: elf.Elf64_Rela, | ||
| 389 | undefs: anytype, | ||
| 390 | ) !void { | ||
| 383 | const rel_esym = switch (elf_file.file(self.file_index).?) { | 391 | const rel_esym = switch (elf_file.file(self.file_index).?) { |
| 384 | .zig_module => |x| x.elfSym(rel.r_sym()).*, | 392 | .zig_module => |x| x.elfSym(rel.r_sym()).*, |
| 385 | .object => |x| x.symtab[rel.r_sym()], | 393 | .object => |x| x.symtab[rel.r_sym()], |
| ... | @@ -392,7 +400,7 @@ fn reportUndefined(self: Atom, elf_file: *Elf, sym: *const Symbol, rel: elf.Elf6 | ... | @@ -392,7 +400,7 @@ fn reportUndefined(self: Atom, elf_file: *Elf, sym: *const Symbol, rel: elf.Elf6 |
| 392 | !sym.flags.import and | 400 | !sym.flags.import and |
| 393 | esym.st_shndx == elf.SHN_UNDEF) | 401 | esym.st_shndx == elf.SHN_UNDEF) |
| 394 | { | 402 | { |
| 395 | const gop = try undefs.getOrPut(sym.index); | 403 | const gop = try undefs.getOrPut(sym_index); |
| 396 | if (!gop.found_existing) { | 404 | if (!gop.found_existing) { |
| 397 | gop.value_ptr.* = std.ArrayList(Atom.Index).init(elf_file.base.allocator); | 405 | gop.value_ptr.* = std.ArrayList(Atom.Index).init(elf_file.base.allocator); |
| 398 | } | 406 | } |
| ... | @@ -417,6 +425,7 @@ pub fn resolveRelocs(self: Atom, elf_file: *Elf, code: []u8) !void { | ... | @@ -417,6 +425,7 @@ pub fn resolveRelocs(self: Atom, elf_file: *Elf, code: []u8) !void { |
| 417 | .object => |x| elf_file.symbol(x.symbols.items[rel.r_sym()]), | 425 | .object => |x| elf_file.symbol(x.symbols.items[rel.r_sym()]), |
| 418 | else => unreachable, | 426 | else => unreachable, |
| 419 | }; | 427 | }; |
| 428 | const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow; | ||
| 420 | 429 | ||
| 421 | // We will use equation format to resolve relocations: | 430 | // We will use equation format to resolve relocations: |
| 422 | // https://intezer.com/blog/malware-analysis/executable-and-linkable-format-101-part-3-relocations/ | 431 | // https://intezer.com/blog/malware-analysis/executable-and-linkable-format-101-part-3-relocations/ |
| ... | @@ -446,24 +455,49 @@ pub fn resolveRelocs(self: Atom, elf_file: *Elf, code: []u8) !void { | ... | @@ -446,24 +455,49 @@ pub fn resolveRelocs(self: Atom, elf_file: *Elf, code: []u8) !void { |
| 446 | 455 | ||
| 447 | relocs_log.debug(" {s}: {x}: [{x} => {x}] G({x}) ({s})", .{ | 456 | relocs_log.debug(" {s}: {x}: [{x} => {x}] G({x}) ({s})", .{ |
| 448 | fmtRelocType(r_type), | 457 | fmtRelocType(r_type), |
| 449 | rel.r_offset, | 458 | r_offset, |
| 450 | P, | 459 | P, |
| 451 | S + A, | 460 | S + A, |
| 452 | G + GOT + A, | 461 | G + GOT + A, |
| 453 | target.name(elf_file), | 462 | target.name(elf_file), |
| 454 | }); | 463 | }); |
| 455 | 464 | ||
| 456 | try stream.seekTo(rel.r_offset); | 465 | try stream.seekTo(r_offset); |
| 457 | 466 | ||
| 458 | switch (rel.r_type()) { | 467 | switch (rel.r_type()) { |
| 459 | elf.R_X86_64_NONE => unreachable, | 468 | elf.R_X86_64_NONE => unreachable, |
| 460 | 469 | ||
| 461 | elf.R_X86_64_64 => try cwriter.writeIntLittle(i64, S + A), | 470 | elf.R_X86_64_64 => try cwriter.writeIntLittle(i64, S + A), |
| 462 | 471 | ||
| 472 | elf.R_X86_64_32 => try cwriter.writeIntLittle(u32, @as(u32, @truncate(@as(u64, @intCast(S + A))))), | ||
| 473 | elf.R_X86_64_32S => try cwriter.writeIntLittle(i32, @as(i32, @truncate(S + A))), | ||
| 474 | |||
| 463 | elf.R_X86_64_PLT32, | 475 | elf.R_X86_64_PLT32, |
| 464 | elf.R_X86_64_PC32, | 476 | elf.R_X86_64_PC32, |
| 465 | => try cwriter.writeIntLittle(i32, @as(i32, @intCast(S + A - P))), | 477 | => try cwriter.writeIntLittle(i32, @as(i32, @intCast(S + A - P))), |
| 466 | 478 | ||
| 479 | elf.R_X86_64_GOTPCREL => try cwriter.writeIntLittle(i32, @as(i32, @intCast(G + GOT + A - P))), | ||
| 480 | elf.R_X86_64_GOTPC32 => try cwriter.writeIntLittle(i32, @as(i32, @intCast(GOT + A - P))), | ||
| 481 | elf.R_X86_64_GOTPC64 => try cwriter.writeIntLittle(i64, GOT + A - P), | ||
| 482 | |||
| 483 | elf.R_X86_64_GOTPCRELX => { | ||
| 484 | if (!target.flags.import and !target.isIFunc(elf_file) and !target.isAbs(elf_file)) blk: { | ||
| 485 | x86_64.relaxGotpcrelx(code[r_offset - 2 ..]) catch break :blk; | ||
| 486 | try cwriter.writeIntLittle(i32, @as(i32, @intCast(S + A - P))); | ||
| 487 | continue; | ||
| 488 | } | ||
| 489 | try cwriter.writeIntLittle(i32, @as(i32, @intCast(G + GOT + A - P))); | ||
| 490 | }, | ||
| 491 | |||
| 492 | elf.R_X86_64_REX_GOTPCRELX => { | ||
| 493 | if (!target.flags.import and !target.isIFunc(elf_file) and !target.isAbs(elf_file)) blk: { | ||
| 494 | x86_64.relaxRexGotpcrelx(code[r_offset - 3 ..]) catch break :blk; | ||
| 495 | try cwriter.writeIntLittle(i32, @as(i32, @intCast(S + A - P))); | ||
| 496 | continue; | ||
| 497 | } | ||
| 498 | try cwriter.writeIntLittle(i32, @as(i32, @intCast(G + GOT + A - P))); | ||
| 499 | }, | ||
| 500 | |||
| 467 | else => { | 501 | else => { |
| 468 | log.err("TODO: unhandled relocation type {}", .{fmtRelocType(rel.r_type())}); | 502 | log.err("TODO: unhandled relocation type {}", .{fmtRelocType(rel.r_type())}); |
| 469 | @panic("TODO unhandled relocation type"); | 503 | @panic("TODO unhandled relocation type"); |
| ... | @@ -591,6 +625,58 @@ fn format2( | ... | @@ -591,6 +625,58 @@ fn format2( |
| 591 | // future. | 625 | // future. |
| 592 | pub const Index = u16; | 626 | pub const Index = u16; |
| 593 | 627 | ||
| 628 | const x86_64 = struct { | ||
| 629 | pub fn relaxGotpcrelx(code: []u8) !void { | ||
| 630 | const old_inst = disassemble(code) orelse return error.RelaxFail; | ||
| 631 | const inst = switch (old_inst.encoding.mnemonic) { | ||
| 632 | .call => try Instruction.new(old_inst.prefix, .call, &.{ | ||
| 633 | // TODO: hack to force imm32s in the assembler | ||
| 634 | .{ .imm = Immediate.s(-129) }, | ||
| 635 | }), | ||
| 636 | .jmp => try Instruction.new(old_inst.prefix, .jmp, &.{ | ||
| 637 | // TODO: hack to force imm32s in the assembler | ||
| 638 | .{ .imm = Immediate.s(-129) }, | ||
| 639 | }), | ||
| 640 | else => return error.RelaxFail, | ||
| 641 | }; | ||
| 642 | relocs_log.debug(" relaxing {} => {}", .{ old_inst.encoding, inst.encoding }); | ||
| 643 | const nop = try Instruction.new(.none, .nop, &.{}); | ||
| 644 | encode(&.{ nop, inst }, code) catch return error.RelaxFail; | ||
| 645 | } | ||
| 646 | |||
| 647 | pub fn relaxRexGotpcrelx(code: []u8) !void { | ||
| 648 | const old_inst = disassemble(code) orelse return error.RelaxFail; | ||
| 649 | switch (old_inst.encoding.mnemonic) { | ||
| 650 | .mov => { | ||
| 651 | const inst = try Instruction.new(old_inst.prefix, .lea, &old_inst.ops); | ||
| 652 | relocs_log.debug(" relaxing {} => {}", .{ old_inst.encoding, inst.encoding }); | ||
| 653 | encode(&.{inst}, code) catch return error.RelaxFail; | ||
| 654 | }, | ||
| 655 | else => return error.RelaxFail, | ||
| 656 | } | ||
| 657 | } | ||
| 658 | |||
| 659 | fn disassemble(code: []const u8) ?Instruction { | ||
| 660 | var disas = Disassembler.init(code); | ||
| 661 | const inst = disas.next() catch return null; | ||
| 662 | return inst; | ||
| 663 | } | ||
| 664 | |||
| 665 | fn encode(insts: []const Instruction, code: []u8) !void { | ||
| 666 | var stream = std.io.fixedBufferStream(code); | ||
| 667 | const writer = stream.writer(); | ||
| 668 | for (insts) |inst| { | ||
| 669 | try inst.encode(writer, .{}); | ||
| 670 | } | ||
| 671 | } | ||
| 672 | |||
| 673 | const bits = @import("../../arch/x86_64/bits.zig"); | ||
| 674 | const encoder = @import("../../arch/x86_64/encoder.zig"); | ||
| 675 | const Disassembler = @import("../../arch/x86_64/Disassembler.zig"); | ||
| 676 | const Immediate = bits.Immediate; | ||
| 677 | const Instruction = encoder.Instruction; | ||
| 678 | }; | ||
| 679 | |||
| 594 | const std = @import("std"); | 680 | const std = @import("std"); |
| 595 | const assert = std.debug.assert; | 681 | const assert = std.debug.assert; |
| 596 | const elf = std.elf; | 682 | const elf = std.elf; |
src/link/Elf/Object.zig+3-3| ... | @@ -485,9 +485,9 @@ pub fn claimUnresolved(self: *Object, elf_file: *Elf) void { | ... | @@ -485,9 +485,9 @@ pub fn claimUnresolved(self: *Object, elf_file: *Elf) void { |
| 485 | pub fn resetGlobals(self: *Object, elf_file: *Elf) void { | 485 | pub fn resetGlobals(self: *Object, elf_file: *Elf) void { |
| 486 | for (self.globals()) |index| { | 486 | for (self.globals()) |index| { |
| 487 | const global = elf_file.symbol(index); | 487 | const global = elf_file.symbol(index); |
| 488 | const name = global.name; | 488 | const off = global.name_offset; |
| 489 | global.* = .{}; | 489 | global.* = .{}; |
| 490 | global.name = name; | 490 | global.name_offset = off; |
| 491 | } | 491 | } |
| 492 | } | 492 | } |
| 493 | 493 | ||
| ... | @@ -499,7 +499,7 @@ pub fn markLive(self: *Object, elf_file: *Elf) void { | ... | @@ -499,7 +499,7 @@ pub fn markLive(self: *Object, elf_file: *Elf) void { |
| 499 | if (sym.st_bind() == elf.STB_WEAK) continue; | 499 | if (sym.st_bind() == elf.STB_WEAK) continue; |
| 500 | 500 | ||
| 501 | const global = elf_file.symbol(index); | 501 | const global = elf_file.symbol(index); |
| 502 | const file = global.getFile(elf_file) orelse continue; | 502 | const file = global.file(elf_file) orelse continue; |
| 503 | const should_keep = sym.st_shndx == elf.SHN_UNDEF or | 503 | const should_keep = sym.st_shndx == elf.SHN_UNDEF or |
| 504 | (sym.st_shndx == elf.SHN_COMMON and global.elfSym(elf_file).st_shndx != elf.SHN_COMMON); | 504 | (sym.st_shndx == elf.SHN_COMMON and global.elfSym(elf_file).st_shndx != elf.SHN_COMMON); |
| 505 | if (should_keep and !file.isAlive()) { | 505 | if (should_keep and !file.isAlive()) { |
src/link/Elf/Symbol.zig+3-5| ... | @@ -1,7 +1,5 @@ | ... | @@ -1,7 +1,5 @@ |
| 1 | //! Represents a defined symbol. | 1 | //! Represents a defined symbol. |
| 2 | 2 | ||
| 3 | index: Index = 0, | ||
| 4 | |||
| 5 | /// Allocated address value of this symbol. | 3 | /// Allocated address value of this symbol. |
| 6 | value: u64 = 0, | 4 | value: u64 = 0, |
| 7 | 5 | ||
| ... | @@ -117,10 +115,10 @@ const GetOrCreateGotEntryResult = struct { | ... | @@ -117,10 +115,10 @@ const GetOrCreateGotEntryResult = struct { |
| 117 | index: GotSection.Index, | 115 | index: GotSection.Index, |
| 118 | }; | 116 | }; |
| 119 | 117 | ||
| 120 | pub fn getOrCreateGotEntry(symbol: *Symbol, elf_file: *Elf) !GetOrCreateGotEntryResult { | 118 | pub fn getOrCreateGotEntry(symbol: *Symbol, symbol_index: Index, elf_file: *Elf) !GetOrCreateGotEntryResult { |
| 121 | assert(symbol.flags.needs_got); | 119 | assert(symbol.flags.needs_got); |
| 122 | if (symbol.flags.has_got) return .{ .found_existing = true, .index = symbol.extra(elf_file).?.got }; | 120 | if (symbol.flags.has_got) return .{ .found_existing = true, .index = symbol.extra(elf_file).?.got }; |
| 123 | const index = try elf_file.got.addGotSymbol(symbol.index, elf_file); | 121 | const index = try elf_file.got.addGotSymbol(symbol_index, elf_file); |
| 124 | symbol.flags.has_got = true; | 122 | symbol.flags.has_got = true; |
| 125 | return .{ .found_existing = false, .index = index }; | 123 | return .{ .found_existing = false, .index = index }; |
| 126 | } | 124 | } |
| ... | @@ -270,7 +268,7 @@ fn format2( | ... | @@ -270,7 +268,7 @@ fn format2( |
| 270 | _ = options; | 268 | _ = options; |
| 271 | _ = unused_fmt_string; | 269 | _ = unused_fmt_string; |
| 272 | const symbol = ctx.symbol; | 270 | const symbol = ctx.symbol; |
| 273 | try writer.print("%{d} : {s} : @{x}", .{ symbol.index, symbol.fmtName(ctx.elf_file), symbol.value }); | 271 | try writer.print("%{d} : {s} : @{x}", .{ symbol.esym_index, symbol.fmtName(ctx.elf_file), symbol.value }); |
| 274 | if (symbol.file(ctx.elf_file)) |file_ptr| { | 272 | if (symbol.file(ctx.elf_file)) |file_ptr| { |
| 275 | if (symbol.isAbs(ctx.elf_file)) { | 273 | if (symbol.isAbs(ctx.elf_file)) { |
| 276 | if (symbol.elfSym(ctx.elf_file).st_shndx == elf.SHN_UNDEF) { | 274 | if (symbol.elfSym(ctx.elf_file).st_shndx == elf.SHN_UNDEF) { |
src/link/Elf/ZigModule.zig+9| ... | @@ -148,6 +148,15 @@ pub fn scanRelocs(self: *ZigModule, elf_file: *Elf, undefs: anytype) !void { | ... | @@ -148,6 +148,15 @@ pub fn scanRelocs(self: *ZigModule, elf_file: *Elf, undefs: anytype) !void { |
| 148 | } | 148 | } |
| 149 | } | 149 | } |
| 150 | 150 | ||
| 151 | pub fn resetGlobals(self: *ZigModule, elf_file: *Elf) void { | ||
| 152 | for (self.globals()) |index| { | ||
| 153 | const global = elf_file.symbol(index); | ||
| 154 | const off = global.name_offset; | ||
| 155 | global.* = .{}; | ||
| 156 | global.name_offset = off; | ||
| 157 | } | ||
| 158 | } | ||
| 159 | |||
| 151 | pub fn updateSymtabSize(self: *ZigModule, elf_file: *Elf) void { | 160 | pub fn updateSymtabSize(self: *ZigModule, elf_file: *Elf) void { |
| 152 | for (self.locals()) |local_index| { | 161 | for (self.locals()) |local_index| { |
| 153 | const local = elf_file.symbol(local_index); | 162 | const local = elf_file.symbol(local_index); |
src/link/Elf/file.zig+14-1| ... | @@ -62,6 +62,19 @@ pub const File = union(enum) { | ... | @@ -62,6 +62,19 @@ pub const File = union(enum) { |
| 62 | return (@as(u32, base) << 24) + file.index(); | 62 | return (@as(u32, base) << 24) + file.index(); |
| 63 | } | 63 | } |
| 64 | 64 | ||
| 65 | pub fn resolveSymbols(file: File, elf_file: *Elf) void { | ||
| 66 | switch (file) { | ||
| 67 | inline else => |x| x.resolveSymbols(elf_file), | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | pub fn resetGlobals(file: File, elf_file: *Elf) void { | ||
| 72 | switch (file) { | ||
| 73 | .linker_defined => unreachable, | ||
| 74 | inline else => |x| x.resetGlobals(elf_file), | ||
| 75 | } | ||
| 76 | } | ||
| 77 | |||
| 65 | pub fn setAlive(file: File) void { | 78 | pub fn setAlive(file: File) void { |
| 66 | switch (file) { | 79 | switch (file) { |
| 67 | .zig_module, .linker_defined => {}, | 80 | .zig_module, .linker_defined => {}, |
| ... | @@ -71,7 +84,7 @@ pub const File = union(enum) { | ... | @@ -71,7 +84,7 @@ pub const File = union(enum) { |
| 71 | 84 | ||
| 72 | pub fn markLive(file: File, elf_file: *Elf) void { | 85 | pub fn markLive(file: File, elf_file: *Elf) void { |
| 73 | switch (file) { | 86 | switch (file) { |
| 74 | .zig_module, .linker_defined => {}, | 87 | .zig_module, .linker_defined => unreachable, |
| 75 | inline else => |x| x.markLive(elf_file), | 88 | inline else => |x| x.markLive(elf_file), |
| 76 | } | 89 | } |
| 77 | } | 90 | } |