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