master
1const std = @import("std");
2const builtin = @import("builtin");
3const elf = std.elf;
4const assert = std.debug.assert;
5
6const R_ALPHA_RELATIVE = 27;
7const R_AMD64_RELATIVE = 8;
8const R_386_RELATIVE = 8;
9const R_ARC_RELATIVE = 56;
10const R_ARM_RELATIVE = 23;
11const R_AARCH64_RELATIVE = 1027;
12const R_CSKY_RELATIVE = 9;
13const R_HEXAGON_RELATIVE = 35;
14const R_KVX_RELATIVE = 39;
15const R_LARCH_RELATIVE = 3;
16const R_68K_RELATIVE = 22;
17const R_MICROBLAZE_REL = 16;
18const R_MIPS_RELATIVE = 128;
19const R_OR1K_RELATIVE = 21;
20const R_PPC_RELATIVE = 22;
21const R_RISCV_RELATIVE = 3;
22const R_390_RELATIVE = 12;
23const R_SH_RELATIVE = 165;
24const R_SPARC_RELATIVE = 22;
25
26const R_RELATIVE = switch (builtin.cpu.arch) {
27 .x86 => R_386_RELATIVE,
28 .x86_64 => R_AMD64_RELATIVE,
29 .arc, .arceb => R_ARC_RELATIVE,
30 .arm, .armeb, .thumb, .thumbeb => R_ARM_RELATIVE,
31 .aarch64, .aarch64_be => R_AARCH64_RELATIVE,
32 .alpha => R_ALPHA_RELATIVE,
33 .csky => R_CSKY_RELATIVE,
34 .hexagon => R_HEXAGON_RELATIVE,
35 .kvx => R_KVX_RELATIVE,
36 .loongarch32, .loongarch64 => R_LARCH_RELATIVE,
37 .m68k => R_68K_RELATIVE,
38 .microblaze, .microblazeel => R_MICROBLAZE_REL,
39 .mips, .mipsel, .mips64, .mips64el => R_MIPS_RELATIVE,
40 .or1k => R_OR1K_RELATIVE,
41 .powerpc, .powerpcle, .powerpc64, .powerpc64le => R_PPC_RELATIVE,
42 .riscv32, .riscv32be, .riscv64, .riscv64be => R_RISCV_RELATIVE,
43 .s390x => R_390_RELATIVE,
44 .sh, .sheb => R_SH_RELATIVE,
45 .sparc, .sparc64 => R_SPARC_RELATIVE,
46 else => @compileError("Missing R_RELATIVE definition for this target"),
47};
48
49// Obtain a pointer to the _DYNAMIC array.
50// We have to compute its address as a PC-relative quantity not to require a
51// relocation that, at this point, is not yet applied.
52inline fn getDynamicSymbol() [*]const elf.Dyn {
53 return switch (builtin.zig_backend) {
54 else => switch (builtin.cpu.arch) {
55 .x86 => asm volatile (
56 \\ .weak _DYNAMIC
57 \\ .hidden _DYNAMIC
58 \\ call 1f
59 \\1:
60 \\ pop %[ret]
61 \\ lea _DYNAMIC - 1b(%[ret]), %[ret]
62 : [ret] "=r" (-> [*]const elf.Dyn),
63 ),
64 .x86_64 => asm volatile (
65 \\ .weak _DYNAMIC
66 \\ .hidden _DYNAMIC
67 \\ lea _DYNAMIC(%%rip), %[ret]
68 : [ret] "=r" (-> [*]const elf.Dyn),
69 ),
70 .arc, .arceb => asm volatile (
71 \\ .weak _DYNAMIC
72 \\ .hidden _DYNAMIC
73 \\ add %[ret], pcl, _DYNAMIC@pcl
74 : [ret] "=r" (-> [*]const elf.Dyn),
75 ),
76 // Work around the limited offset range of `ldr`
77 .arm, .armeb, .thumb, .thumbeb => asm volatile (
78 \\ .weak _DYNAMIC
79 \\ .hidden _DYNAMIC
80 \\ ldr %[ret], 1f
81 \\ add %[ret], pc
82 \\ b 2f
83 \\1:
84 \\ .word _DYNAMIC-1b
85 \\2:
86 : [ret] "=r" (-> [*]const elf.Dyn),
87 ),
88 // A simple `adr` is not enough as it has a limited offset range
89 .aarch64, .aarch64_be => asm volatile (
90 \\ .weak _DYNAMIC
91 \\ .hidden _DYNAMIC
92 \\ adrp %[ret], _DYNAMIC
93 \\ add %[ret], %[ret], #:lo12:_DYNAMIC
94 : [ret] "=r" (-> [*]const elf.Dyn),
95 ),
96 // The compiler is not required to load the GP register, so do it ourselves.
97 .alpha => asm volatile (
98 \\ br $29, 1f
99 \\1:
100 \\ ldgp $29, 0($29)
101 \\ ldq %[ret], -0x8000($29)
102 : [ret] "=r" (-> [*]const elf.Dyn),
103 :
104 : .{ .r26 = true, .r29 = true }),
105 // The CSKY ABI requires the gb register to point to the GOT. Additionally, the first
106 // entry in the GOT is defined to hold the address of _DYNAMIC.
107 .csky => asm volatile (
108 \\ mov %[ret], gb
109 \\ ldw %[ret], %[ret]
110 : [ret] "=r" (-> [*]const elf.Dyn),
111 ),
112 .hexagon => asm volatile (
113 \\ .weak _DYNAMIC
114 \\ .hidden _DYNAMIC
115 \\ jump 1f
116 \\ .word _DYNAMIC - .
117 \\1:
118 \\ r1 = pc
119 \\ r1 = add(r1, #-4)
120 \\ %[ret] = memw(r1)
121 \\ %[ret] = add(r1, %[ret])
122 : [ret] "=r" (-> [*]const elf.Dyn),
123 :
124 : .{ .r1 = true }),
125 .kvx => asm volatile (
126 \\ .weak _DYNAMIC
127 \\ .hidden _DYNAMIC
128 \\ pcrel %[ret] = @pcrel(_DYNAMIC)
129 : [ret] "=r" (-> [*]const elf.Dyn),
130 ),
131 .loongarch32, .loongarch64 => asm volatile (
132 \\ .weak _DYNAMIC
133 \\ .hidden _DYNAMIC
134 \\ la.local %[ret], _DYNAMIC
135 : [ret] "=r" (-> [*]const elf.Dyn),
136 ),
137 // Note that the - 8 is needed because pc in the second lea instruction points into the
138 // middle of that instruction. (The first lea is 6 bytes, the second is 4 bytes.)
139 .m68k => asm volatile (
140 \\ .weak _DYNAMIC
141 \\ .hidden _DYNAMIC
142 \\ lea _DYNAMIC - . - 8, %[ret]
143 \\ lea (%[ret], %%pc), %[ret]
144 : [ret] "=r" (-> [*]const elf.Dyn),
145 ),
146 .microblaze, .microblazeel => asm volatile (
147 \\ lwi %[ret], r20, 0
148 : [ret] "=r" (-> [*]const elf.Dyn),
149 ),
150 .mips, .mipsel => asm volatile (
151 \\ .weak _DYNAMIC
152 \\ .hidden _DYNAMIC
153 \\ bal 1f
154 \\ .gpword _DYNAMIC
155 \\1:
156 \\ lw %[ret], 0($ra)
157 \\ nop
158 \\ addu %[ret], %[ret], $gp
159 : [ret] "=r" (-> [*]const elf.Dyn),
160 :
161 : .{ .lr = true }),
162 .mips64, .mips64el => switch (builtin.abi) {
163 .gnuabin32, .muslabin32 => asm volatile (
164 \\ .weak _DYNAMIC
165 \\ .hidden _DYNAMIC
166 \\ bal 1f
167 \\ .gpword _DYNAMIC
168 \\1:
169 \\ lw %[ret], 0($ra)
170 \\ addu %[ret], %[ret], $gp
171 : [ret] "=r" (-> [*]const elf.Dyn),
172 :
173 : .{ .lr = true }),
174 else => asm volatile (
175 \\ .weak _DYNAMIC
176 \\ .hidden _DYNAMIC
177 \\ .balign 8
178 \\ bal 1f
179 \\ .gpdword _DYNAMIC
180 \\1:
181 \\ ld %[ret], 0($ra)
182 \\ daddu %[ret], %[ret], $gp
183 : [ret] "=r" (-> [*]const elf.Dyn),
184 :
185 : .{ .lr = true }),
186 },
187 .or1k => asm volatile (
188 \\ .weak _DYNAMIC
189 \\ .hidden _DYNAMIC
190 \\ l.jal 1f
191 \\ .word _DYNAMIC - .
192 \\1:
193 \\ l.lwz %[ret], 0(r9)
194 \\ l.add %[ret], %[ret], r9
195 : [ret] "=r" (-> [*]const elf.Dyn),
196 :
197 : .{ .r9 = true }),
198 .powerpc, .powerpcle => asm volatile (
199 \\ .weak _DYNAMIC
200 \\ .hidden _DYNAMIC
201 \\ bl 1f
202 \\ .long _DYNAMIC - .
203 \\1:
204 \\ mflr %[ret]
205 \\ lwz 4, 0(%[ret])
206 \\ add %[ret], 4, %[ret]
207 : [ret] "=r" (-> [*]const elf.Dyn),
208 :
209 : .{ .lr = true, .r4 = true }),
210 .powerpc64, .powerpc64le => asm volatile (
211 \\ .weak _DYNAMIC
212 \\ .hidden _DYNAMIC
213 \\ bl 1f
214 \\ .quad _DYNAMIC - .
215 \\1:
216 \\ mflr %[ret]
217 \\ ld 4, 0(%[ret])
218 \\ add %[ret], 4, %[ret]
219 : [ret] "=r" (-> [*]const elf.Dyn),
220 :
221 : .{ .lr = true, .r4 = true }),
222 .riscv32, .riscv32be, .riscv64, .riscv64be => asm volatile (
223 \\ .weak _DYNAMIC
224 \\ .hidden _DYNAMIC
225 \\ lla %[ret], _DYNAMIC
226 : [ret] "=r" (-> [*]const elf.Dyn),
227 ),
228 .s390x => asm volatile (
229 \\ .weak _DYNAMIC
230 \\ .hidden _DYNAMIC
231 \\ larl %[ret], 1f
232 \\ ag %[ret], 0(%[ret])
233 \\ jg 2f
234 \\1:
235 \\ .quad _DYNAMIC - .
236 \\2:
237 : [ret] "=a" (-> [*]const elf.Dyn),
238 ),
239 .sh, .sheb => asm volatile (
240 \\ .weak _DYNAMIC
241 \\ .hidden _DYNAMIC
242 \\ mova 1f, r0
243 \\ mov.l 1f, %[ret]
244 \\ add r0, %[ret]
245 \\ bra 2f
246 \\1:
247 \\ .balign 4
248 \\ .long DYNAMIC - .
249 \\2:
250 : [ret] "=r" (-> [*]const elf.Dyn),
251 :
252 : .{ .r0 = true }),
253 // The compiler does not necessarily have any obligation to load the `l7` register (pointing
254 // to the GOT), so do it ourselves just in case.
255 .sparc, .sparc64 => asm volatile (
256 \\ sethi %%hi(_GLOBAL_OFFSET_TABLE_ - 4), %%l7
257 \\ call 1f
258 \\ add %%l7, %%lo(_GLOBAL_OFFSET_TABLE_ + 4), %%l7
259 \\1:
260 \\ add %%l7, %%o7, %[ret]
261 : [ret] "=r" (-> [*]const elf.Dyn),
262 :
263 : .{ .l7 = true }),
264 else => {
265 @compileError("PIE startup is not yet supported for this target!");
266 },
267 },
268 .stage2_x86_64 => @extern([*]const elf.Dyn, .{
269 .name = "_DYNAMIC",
270 .linkage = .weak,
271 .visibility = .hidden,
272 .relocation = .pcrel,
273 }).?,
274 };
275}
276
277pub fn relocate(phdrs: []const elf.Phdr) void {
278 @setRuntimeSafety(false);
279 @disableInstrumentation();
280
281 const dynv = getDynamicSymbol();
282
283 // Recover the delta applied by the loader by comparing the effective and
284 // the theoretical load addresses for the `_DYNAMIC` symbol.
285 const base_addr = base: {
286 for (phdrs) |*phdr| {
287 if (phdr.p_type != elf.PT_DYNAMIC) continue;
288 break :base @intFromPtr(dynv) - phdr.p_vaddr;
289 }
290 // This is not supposed to happen for well-formed binaries.
291 @trap();
292 };
293
294 var sorted_dynv: [elf.DT_NUM]elf.Addr = undefined;
295
296 // Zero-initialized this way to prevent the compiler from turning this into
297 // `memcpy` or `memset` calls (which can require relocations).
298 for (&sorted_dynv) |*dyn| {
299 const pdyn: *volatile elf.Addr = @ptrCast(dyn);
300 pdyn.* = 0;
301 }
302
303 {
304 // `dynv` has no defined order. Fix that.
305 var i: usize = 0;
306 while (dynv[i].d_tag != elf.DT_NULL) : (i += 1) {
307 if (dynv[i].d_tag < elf.DT_NUM) sorted_dynv[@bitCast(dynv[i].d_tag)] = dynv[i].d_val;
308 }
309 }
310
311 // Deal with the GOT relocations that MIPS uses first.
312 if (builtin.cpu.arch.isMIPS()) {
313 const count: elf.Addr = blk: {
314 // This is an architecture-specific tag, so not part of `sorted_dynv`.
315 var i: usize = 0;
316 while (dynv[i].d_tag != elf.DT_NULL) : (i += 1) {
317 if (dynv[i].d_tag == elf.DT_MIPS_LOCAL_GOTNO) break :blk dynv[i].d_val;
318 }
319
320 break :blk 0;
321 };
322
323 const got: [*]usize = @ptrFromInt(base_addr + sorted_dynv[elf.DT_PLTGOT]);
324
325 for (0..count) |i| {
326 got[i] += base_addr;
327 }
328 }
329
330 // Apply normal relocations.
331
332 const rel = sorted_dynv[elf.DT_REL];
333 if (rel != 0) {
334 const rels: []const elf.Rel = @ptrCast(@alignCast(
335 @as([*]align(@alignOf(elf.Rel)) const u8, @ptrFromInt(base_addr + rel))[0..sorted_dynv[elf.DT_RELSZ]],
336 ));
337 for (rels) |r| {
338 if (r.r_type() != R_RELATIVE) continue;
339 @as(*usize, @ptrFromInt(base_addr + r.r_offset)).* += base_addr;
340 }
341 }
342
343 const rela = sorted_dynv[elf.DT_RELA];
344 if (rela != 0) {
345 const relas: []const elf.Rela = @ptrCast(@alignCast(
346 @as([*]align(@alignOf(elf.Rela)) const u8, @ptrFromInt(base_addr + rela))[0..sorted_dynv[elf.DT_RELASZ]],
347 ));
348 for (relas) |r| {
349 if (r.r_type() != R_RELATIVE) continue;
350 @as(*usize, @ptrFromInt(base_addr + r.r_offset)).* = base_addr + @as(usize, @bitCast(r.r_addend));
351 }
352 }
353
354 const relr = sorted_dynv[elf.DT_RELR];
355 if (relr != 0) {
356 const relrs: []const elf.Relr = @ptrCast(
357 @as([*]align(@alignOf(elf.Relr)) const u8, @ptrFromInt(base_addr + relr))[0..sorted_dynv[elf.DT_RELRSZ]],
358 );
359 var current: [*]usize = undefined;
360 for (relrs) |r| {
361 if ((r & 1) == 0) {
362 current = @ptrFromInt(base_addr + r);
363 current[0] += base_addr;
364 current += 1;
365 } else {
366 // Skip the first bit; there are 63 locations in the bitmap.
367 var i: if (@sizeOf(usize) == 8) u6 else u5 = 1;
368 while (i < @bitSizeOf(elf.Relr)) : (i += 1) {
369 if (((r >> i) & 1) != 0) current[i] += base_addr;
370 }
371
372 current += @bitSizeOf(elf.Relr) - 1;
373 }
374 }
375 }
376}