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}