authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-09-14 01:45:23+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-09-14 01:45:23+02:00
log8fb4a4efbabbe3fe98521201eac41feee5a9a50a
tree398abef45faa8065836c6a5f8cad167ae8adb4ed
parent223f62acbd32c04db3169906f33869f43e5258f7
parent59a586a8785b8aea417824e5b6b8d6b8a1b2b695
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #17146 from ziglang/elf-linker

elf: upstream zld/ELF functionality, part 2

15 files changed, 954 insertions(+), 47 deletions(-)

CMakeLists.txt+2
......@@ -555,6 +555,7 @@ set(ZIG_STAGE2_SOURCES
555555 "${CMAKE_SOURCE_DIR}/src/arch/wasm/Emit.zig"
556556 "${CMAKE_SOURCE_DIR}/src/arch/wasm/Mir.zig"
557557 "${CMAKE_SOURCE_DIR}/src/arch/x86_64/CodeGen.zig"
558 "${CMAKE_SOURCE_DIR}/src/arch/x86_64/Disassembler.zig"
558559 "${CMAKE_SOURCE_DIR}/src/arch/x86_64/Emit.zig"
559560 "${CMAKE_SOURCE_DIR}/src/arch/x86_64/Encoding.zig"
560561 "${CMAKE_SOURCE_DIR}/src/arch/x86_64/Mir.zig"
......@@ -584,6 +585,7 @@ set(ZIG_STAGE2_SOURCES
584585 "${CMAKE_SOURCE_DIR}/src/link/Coff/Object.zig"
585586 "${CMAKE_SOURCE_DIR}/src/link/Coff/lld.zig"
586587 "${CMAKE_SOURCE_DIR}/src/link/Elf.zig"
588 "${CMAKE_SOURCE_DIR}/src/link/Elf/Archive.zig"
587589 "${CMAKE_SOURCE_DIR}/src/link/Elf/Atom.zig"
588590 "${CMAKE_SOURCE_DIR}/src/link/Elf/LinkerDefined.zig"
589591 "${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
43164316 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
43174317 const sym_index = try elf_file.getOrCreateMetadataForDecl(func.owner_decl);
43184318 const sym = elf_file.symbol(sym_index);
4319 _ = try sym.getOrCreateGotEntry(elf_file);
4319 _ = try sym.getOrCreateGotEntry(sym_index, elf_file);
43204320 const got_addr = @as(u32, @intCast(sym.gotAddress(elf_file)));
43214321 try self.genSetReg(Type.usize, .x30, .{ .memory = got_addr });
43224322 } 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
42964296 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
42974297 const sym_index = try elf_file.getOrCreateMetadataForDecl(func.owner_decl);
42984298 const sym = elf_file.symbol(sym_index);
4299 _ = try sym.getOrCreateGotEntry(elf_file);
4299 _ = try sym.getOrCreateGotEntry(sym_index, elf_file);
43004300 const got_addr = @as(u32, @intCast(sym.gotAddress(elf_file)));
43014301 try self.genSetReg(Type.usize, .lr, .{ .memory = got_addr });
43024302 } 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
17491749 .func => |func| {
17501750 const sym_index = try elf_file.getOrCreateMetadataForDecl(func.owner_decl);
17511751 const sym = elf_file.symbol(sym_index);
1752 _ = try sym.getOrCreateGotEntry(elf_file);
1752 _ = try sym.getOrCreateGotEntry(sym_index, elf_file);
17531753 const got_addr = @as(u32, @intCast(sym.gotAddress(elf_file)));
17541754 try self.genSetReg(Type.usize, .ra, .{ .memory = got_addr });
17551755 _ = 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
13511351 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
13521352 const sym_index = try elf_file.getOrCreateMetadataForDecl(func.owner_decl);
13531353 const sym = elf_file.symbol(sym_index);
1354 _ = try sym.getOrCreateGotEntry(elf_file);
1354 _ = try sym.getOrCreateGotEntry(sym_index, elf_file);
13551355 break :blk @as(u32, @intCast(sym.gotAddress(elf_file)));
13561356 } else unreachable;
13571357
src/arch/x86_64/CodeGen.zig+2-2
......@@ -8157,7 +8157,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
81578157 const sym_index = try elf_file.getOrCreateMetadataForDecl(owner_decl);
81588158 const sym = elf_file.symbol(sym_index);
81598159 sym.flags.needs_got = true;
8160 _ = try sym.getOrCreateGotEntry(elf_file);
8160 _ = try sym.getOrCreateGotEntry(sym_index, elf_file);
81618161 const got_addr = sym.gotAddress(elf_file);
81628162 try self.asmMemory(.{ ._, .call }, Memory.sib(.qword, .{
81638163 .base = .{ .reg = .ds },
......@@ -10236,7 +10236,7 @@ fn genLazySymbolRef(
1023610236 return self.fail("{s} creating lazy symbol", .{@errorName(err)});
1023710237 const sym = elf_file.symbol(sym_index);
1023810238 sym.flags.needs_got = true;
10239 _ = try sym.getOrCreateGotEntry(elf_file);
10239 _ = try sym.getOrCreateGotEntry(sym_index, elf_file);
1024010240 const got_addr = sym.gotAddress(elf_file);
1024110241 const got_mem =
1024210242 Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = @intCast(got_addr) });
src/arch/x86_64/Disassembler.zig created+475
......@@ -0,0 +1,475 @@
1const Disassembler = @This();
2
3const std = @import("std");
4const assert = std.debug.assert;
5const math = std.math;
6
7const bits = @import("bits.zig");
8const encoder = @import("encoder.zig");
9
10const Encoding = @import("Encoding.zig");
11const Immediate = bits.Immediate;
12const Instruction = encoder.Instruction;
13const LegacyPrefixes = encoder.LegacyPrefixes;
14const Memory = bits.Memory;
15const Register = bits.Register;
16const Rex = encoder.Rex;
17
18pub const Error = error{
19 EndOfStream,
20 LegacyPrefixAfterRex,
21 UnknownOpcode,
22 Overflow,
23 Todo,
24};
25
26code: []const u8,
27pos: usize = 0,
28
29pub fn init(code: []const u8) Disassembler {
30 return .{ .code = code };
31}
32
33pub 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
230fn 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
246const Prefixes = struct {
247 legacy: LegacyPrefixes = .{},
248 rex: Rex = .{},
249 // TODO add support for VEX prefix
250};
251
252fn 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
304fn 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
346fn 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
358fn 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
376fn 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
384const 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
402fn 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
412fn 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
421const 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
443fn 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
453fn 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(
861861 const sym_index = try elf_file.getOrCreateMetadataForDecl(decl_index);
862862 const sym = elf_file.symbol(sym_index);
863863 sym.flags.needs_got = true;
864 _ = try sym.getOrCreateGotEntry(elf_file);
864 _ = try sym.getOrCreateGotEntry(sym_index, elf_file);
865865 return GenResult.mcv(.{ .memory = sym.gotAddress(elf_file) });
866866 } else if (bin_file.cast(link.File.MachO)) |macho_file| {
867867 const atom_index = try macho_file.getOrCreateAtomForDecl(decl_index);
src/link/Elf.zig+194-23
......@@ -40,6 +40,8 @@ phdr_got_index: ?u16 = null,
4040phdr_load_ro_index: ?u16 = null,
4141/// The index into the program headers of a PT_LOAD program header with Write flag
4242phdr_load_rw_index: ?u16 = null,
43/// The index into the program headers of a PT_LOAD program header with zerofill data.
44phdr_load_zerofill_index: ?u16 = null,
4345
4446entry_addr: ?u64 = null,
4547page_size: u32,
......@@ -56,6 +58,7 @@ got: GotSection = .{},
5658text_section_index: ?u16 = null,
5759rodata_section_index: ?u16 = null,
5860data_section_index: ?u16 = null,
61bss_section_index: ?u16 = null,
5962eh_frame_section_index: ?u16 = null,
6063eh_frame_hdr_section_index: ?u16 = null,
6164dynamic_section_index: ?u16 = null,
......@@ -532,6 +535,26 @@ pub fn populateMissingMetadata(self: *Elf) !void {
532535 self.phdr_table_dirty = true;
533536 }
534537
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
535558 if (self.shstrtab_section_index == null) {
536559 self.shstrtab_section_index = @as(u16, @intCast(self.shdrs.items.len));
537560 assert(self.shstrtab.buffer.items.len == 0);
......@@ -655,6 +678,26 @@ pub fn populateMissingMetadata(self: *Elf) !void {
655678 self.shdr_table_dirty = true;
656679 }
657680
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
658701 if (self.symtab_section_index == null) {
659702 self.symtab_section_index = @as(u16, @intCast(self.shdrs.items.len));
660703 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 {
868911 const shdr = &self.shdrs.items[shdr_index];
869912 const phdr_index = self.phdr_to_shdr_table.get(shdr_index).?;
870913 const phdr = &self.phdrs.items[phdr_index];
914 const is_zerofill = shdr.sh_type == elf.SHT_NOBITS;
871915
872 if (needed_size > self.allocatedSize(shdr.sh_offset)) {
916 if (needed_size > self.allocatedSize(shdr.sh_offset) and !is_zerofill) {
873917 // Must move the entire section.
874918 const new_offset = self.findFreeSpace(needed_size, self.page_size);
875919 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 {
893937
894938 shdr.sh_size = needed_size;
895939 phdr.p_memsz = needed_size;
896 phdr.p_filesz = needed_size;
940
941 if (!is_zerofill) {
942 phdr.p_filesz = needed_size;
943 }
897944
898945 self.markDirty(shdr_index, phdr_index);
899946}
......@@ -1005,13 +1052,6 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
10051052 const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type.
10061053 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});
10071054
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
10151055 // Here we will parse input positional and library files (if referenced).
10161056 // This will roughly match in any linker backend we support.
10171057 var positionals = std.ArrayList(Compilation.LinkObject).init(arena);
......@@ -1037,6 +1077,15 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
10371077 try positionals.append(.{ .path = key.status.success.object_path });
10381078 }
10391079
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
10401089 for (positionals.items) |obj| {
10411090 const in_file = try std.fs.cwd().openFile(obj.path, .{});
10421091 defer in_file.close();
......@@ -1093,7 +1142,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
10931142 // input Object files.
10941143 // Any qualifing unresolved symbol will be upgraded to an absolute, weak
10951144 // symbol for potential resolution at load-time.
1096 self.resolveSymbols();
1145 try self.resolveSymbols();
10971146 self.markImportsExports();
10981147 self.claimUnresolved();
10991148
......@@ -1356,6 +1405,7 @@ const ParseError = error{
13561405 EndOfStream,
13571406 FileSystem,
13581407 NotSupported,
1408 InvalidCharacter,
13591409} || std.os.SeekError || std.fs.File.OpenError || std.fs.File.ReadError;
13601410
13611411fn parsePositional(
......@@ -1367,10 +1417,32 @@ fn parsePositional(
13671417) ParseError!void {
13681418 const tracy = trace(@src());
13691419 defer tracy.end();
1370 _ = must_link;
13711420
13721421 if (Object.isObject(in_file)) {
13731422 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
1432fn 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);
13741446 } else return error.UnknownFileType;
13751447}
13761448
......@@ -1395,15 +1467,109 @@ fn parseObject(self: *Elf, in_file: std.fs.File, path: []const u8, ctx: *ParseEr
13951467 if (ctx.detected_cpu_arch != self.base.options.target.cpu.arch) return error.InvalidCpuArch;
13961468}
13971469
1398fn resolveSymbols(self: *Elf) void {
1399 if (self.zig_module_index) |index| {
1400 const zig_module = self.file(index).?.zig_module;
1401 zig_module.resolveSymbols(self);
1470fn parseArchive(
1471 self: *Elf,
1472 in_file: std.fs.File,
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.
1507fn 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 }
14021541 }
14031542
14041543 for (self.objects.items) |index| {
14051544 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.
1569fn 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);
14071573 }
14081574}
14091575
......@@ -1477,11 +1643,11 @@ fn scanRelocs(self: *Elf) !void {
14771643
14781644 try self.reportUndefined(&undefs);
14791645
1480 for (self.symbols.items) |*sym| {
1646 for (self.symbols.items, 0..) |*sym, sym_index| {
14811647 if (sym.flags.needs_got) {
14821648 log.debug("'{s}' needs GOT", .{sym.name(self)});
14831649 // 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);
14851651 try self.got.writeEntry(self, gop.index);
14861652 }
14871653 }
......@@ -1500,7 +1666,7 @@ fn allocateObjects(self: *Elf) !void {
15001666 const local = self.symbol(local_index);
15011667 const atom_ptr = local.atom(self) orelse continue;
15021668 if (!atom_ptr.alive) continue;
1503 local.value = atom_ptr.value;
1669 local.value += atom_ptr.value;
15041670 }
15051671
15061672 for (object.globals()) |global_index| {
......@@ -1508,7 +1674,7 @@ fn allocateObjects(self: *Elf) !void {
15081674 const atom_ptr = global.atom(self) orelse continue;
15091675 if (!atom_ptr.alive) continue;
15101676 if (global.file_index == index) {
1511 global.value = atom_ptr.value;
1677 global.value += atom_ptr.value;
15121678 }
15131679 }
15141680 }
......@@ -1524,7 +1690,11 @@ fn writeObjects(self: *Elf) !void {
15241690 if (!atom_ptr.alive) continue;
15251691
15261692 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
15271696 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 });
15281698 const code = try atom_ptr.codeInObjectUncompressAlloc(self);
15291699 defer gpa.free(code);
15301700
......@@ -2529,7 +2699,7 @@ fn updateDeclCode(
25292699 esym.st_value = atom_ptr.value;
25302700
25312701 sym.flags.needs_got = true;
2532 const gop = try sym.getOrCreateGotEntry(self);
2702 const gop = try sym.getOrCreateGotEntry(sym_index, self);
25332703 try self.got.writeEntry(self, gop.index);
25342704 }
25352705
......@@ -2764,7 +2934,7 @@ fn updateLazySymbol(self: *Elf, sym: link.File.LazySymbol, symbol_index: Symbol.
27642934 local_esym.st_value = atom_ptr.value;
27652935
27662936 local_sym.flags.needs_got = true;
2767 const gop = try local_sym.getOrCreateGotEntry(self);
2937 const gop = try local_sym.getOrCreateGotEntry(symbol_index, self);
27682938 try self.got.writeEntry(self, gop.index);
27692939
27702940 const section_offset = atom_ptr.value - self.phdrs.items[phdr_index].p_vaddr;
......@@ -3635,7 +3805,7 @@ pub fn addSymbol(self: *Elf) !Symbol.Index {
36353805 break :blk index;
36363806 }
36373807 };
3638 self.symbols.items[index] = .{ .index = index };
3808 self.symbols.items[index] = .{};
36393809 return index;
36403810}
36413811
......@@ -4012,6 +4182,7 @@ const synthetic_sections = @import("Elf/synthetic_sections.zig");
40124182
40134183const Air = @import("../Air.zig");
40144184const Allocator = std.mem.Allocator;
4185const Archive = @import("Elf/Archive.zig");
40154186pub const Atom = @import("Elf/Atom.zig");
40164187const Cache = std.Build.Cache;
40174188const Compilation = @import("../Compilation.zig");
src/link/Elf/Archive.zig created+153
......@@ -0,0 +1,153 @@
1path: []const u8,
2data: []const u8,
3
4objects: std.ArrayListUnmanaged(Object) = .{},
5strtab: []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.
11pub const ARMAG: *const [SARMAG:0]u8 = "!<arch>\n";
12/// Size of that string.
13pub const SARMAG: u4 = 8;
14
15/// String in ar_fmag at the end of each header.
16const ARFMAG: *const [2:0]u8 = "`\n";
17
18const SYM64NAME: *const [7:0]u8 = "/SYM64/";
19
20const 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
65pub 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
73pub fn deinit(self: *Archive, allocator: Allocator) void {
74 allocator.free(self.data);
75 self.objects.deinit(allocator);
76}
77
78pub 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
138fn 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
143const std = @import("std");
144const assert = std.debug.assert;
145const elf = std.elf;
146const fs = std.fs;
147const log = std.log.scoped(.link);
148const mem = std.mem;
149
150const Allocator = mem.Allocator;
151const Archive = @This();
152const Elf = @import("../Elf.zig");
153const 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 {
322322
323323 if (rel.r_type() == elf.R_X86_64_NONE) continue;
324324
325 const symbol = switch (file_ptr) {
326 .zig_module => |x| elf_file.symbol(x.symbol(rel.r_sym())),
327 .object => |x| elf_file.symbol(x.symbols.items[rel.r_sym()]),
325 const symbol_index = switch (file_ptr) {
326 .zig_module => |x| x.symbol(rel.r_sym()),
327 .object => |x| x.symbols.items[rel.r_sym()],
328328 else => unreachable,
329329 };
330 const symbol = elf_file.symbol(symbol_index);
330331
331332 // Check for violation of One Definition Rule for COMDATs.
332333 if (symbol.file(elf_file) == null) {
......@@ -340,7 +341,7 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf, undefs: anytype) !void {
340341 }
341342
342343 // Report an undefined symbol.
343 try self.reportUndefined(elf_file, symbol, rel, undefs);
344 try self.reportUndefined(elf_file, symbol, symbol_index, rel, undefs);
344345
345346 // While traversing relocations, mark symbols that require special handling such as
346347 // 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 {
379380}
380381
381382// This function will report any undefined non-weak symbols that are not imports.
382fn reportUndefined(self: Atom, elf_file: *Elf, sym: *const Symbol, rel: elf.Elf64_Rela, undefs: anytype) !void {
383fn 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 {
383391 const rel_esym = switch (elf_file.file(self.file_index).?) {
384392 .zig_module => |x| x.elfSym(rel.r_sym()).*,
385393 .object => |x| x.symtab[rel.r_sym()],
......@@ -392,7 +400,7 @@ fn reportUndefined(self: Atom, elf_file: *Elf, sym: *const Symbol, rel: elf.Elf6
392400 !sym.flags.import and
393401 esym.st_shndx == elf.SHN_UNDEF)
394402 {
395 const gop = try undefs.getOrPut(sym.index);
403 const gop = try undefs.getOrPut(sym_index);
396404 if (!gop.found_existing) {
397405 gop.value_ptr.* = std.ArrayList(Atom.Index).init(elf_file.base.allocator);
398406 }
......@@ -417,6 +425,7 @@ pub fn resolveRelocs(self: Atom, elf_file: *Elf, code: []u8) !void {
417425 .object => |x| elf_file.symbol(x.symbols.items[rel.r_sym()]),
418426 else => unreachable,
419427 };
428 const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow;
420429
421430 // We will use equation format to resolve relocations:
422431 // 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 {
446455
447456 relocs_log.debug(" {s}: {x}: [{x} => {x}] G({x}) ({s})", .{
448457 fmtRelocType(r_type),
449 rel.r_offset,
458 r_offset,
450459 P,
451460 S + A,
452461 G + GOT + A,
453462 target.name(elf_file),
454463 });
455464
456 try stream.seekTo(rel.r_offset);
465 try stream.seekTo(r_offset);
457466
458467 switch (rel.r_type()) {
459468 elf.R_X86_64_NONE => unreachable,
460469
461470 elf.R_X86_64_64 => try cwriter.writeIntLittle(i64, S + A),
462471
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
463475 elf.R_X86_64_PLT32,
464476 elf.R_X86_64_PC32,
465477 => try cwriter.writeIntLittle(i32, @as(i32, @intCast(S + A - P))),
466478
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
467501 else => {
468502 log.err("TODO: unhandled relocation type {}", .{fmtRelocType(rel.r_type())});
469503 @panic("TODO unhandled relocation type");
......@@ -591,6 +625,58 @@ fn format2(
591625// future.
592626pub const Index = u16;
593627
628const 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
594680const std = @import("std");
595681const assert = std.debug.assert;
596682const elf = std.elf;
src/link/Elf/Object.zig+3-3
......@@ -485,9 +485,9 @@ pub fn claimUnresolved(self: *Object, elf_file: *Elf) void {
485485pub fn resetGlobals(self: *Object, elf_file: *Elf) void {
486486 for (self.globals()) |index| {
487487 const global = elf_file.symbol(index);
488 const name = global.name;
488 const off = global.name_offset;
489489 global.* = .{};
490 global.name = name;
490 global.name_offset = off;
491491 }
492492}
493493
......@@ -499,7 +499,7 @@ pub fn markLive(self: *Object, elf_file: *Elf) void {
499499 if (sym.st_bind() == elf.STB_WEAK) continue;
500500
501501 const global = elf_file.symbol(index);
502 const file = global.getFile(elf_file) orelse continue;
502 const file = global.file(elf_file) orelse continue;
503503 const should_keep = sym.st_shndx == elf.SHN_UNDEF or
504504 (sym.st_shndx == elf.SHN_COMMON and global.elfSym(elf_file).st_shndx != elf.SHN_COMMON);
505505 if (should_keep and !file.isAlive()) {
src/link/Elf/Symbol.zig+3-5
......@@ -1,7 +1,5 @@
11//! Represents a defined symbol.
22
3index: Index = 0,
4
53/// Allocated address value of this symbol.
64value: u64 = 0,
75
......@@ -117,10 +115,10 @@ const GetOrCreateGotEntryResult = struct {
117115 index: GotSection.Index,
118116};
119117
120pub fn getOrCreateGotEntry(symbol: *Symbol, elf_file: *Elf) !GetOrCreateGotEntryResult {
118pub fn getOrCreateGotEntry(symbol: *Symbol, symbol_index: Index, elf_file: *Elf) !GetOrCreateGotEntryResult {
121119 assert(symbol.flags.needs_got);
122120 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);
124122 symbol.flags.has_got = true;
125123 return .{ .found_existing = false, .index = index };
126124}
......@@ -270,7 +268,7 @@ fn format2(
270268 _ = options;
271269 _ = unused_fmt_string;
272270 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 });
274272 if (symbol.file(ctx.elf_file)) |file_ptr| {
275273 if (symbol.isAbs(ctx.elf_file)) {
276274 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 {
148148 }
149149}
150150
151pub 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
151160pub fn updateSymtabSize(self: *ZigModule, elf_file: *Elf) void {
152161 for (self.locals()) |local_index| {
153162 const local = elf_file.symbol(local_index);
src/link/Elf/file.zig+14-1
......@@ -62,6 +62,19 @@ pub const File = union(enum) {
6262 return (@as(u32, base) << 24) + file.index();
6363 }
6464
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
6578 pub fn setAlive(file: File) void {
6679 switch (file) {
6780 .zig_module, .linker_defined => {},
......@@ -71,7 +84,7 @@ pub const File = union(enum) {
7184
7285 pub fn markLive(file: File, elf_file: *Elf) void {
7386 switch (file) {
74 .zig_module, .linker_defined => {},
87 .zig_module, .linker_defined => unreachable,
7588 inline else => |x| x.markLive(elf_file),
7689 }
7790 }