| 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); |
| 3 | const elf = std.elf; |
| 4 | const assert = std.debug.assert; |
| 5 | |
| 6 | const R_ALPHA_RELATIVE = 27; |
| 7 | const R_AMD64_RELATIVE = 8; |
| 8 | const R_386_RELATIVE = 8; |
| 9 | const R_ARC_RELATIVE = 56; |
| 10 | const R_ARM_RELATIVE = 23; |
| 11 | const R_AARCH64_RELATIVE = 1027; |
| 12 | const R_CSKY_RELATIVE = 9; |
| 13 | const R_HEXAGON_RELATIVE = 35; |
| 14 | const R_KVX_RELATIVE = 39; |
| 15 | const R_LARCH_RELATIVE = 3; |
| 16 | const R_68K_RELATIVE = 22; |
| 17 | const R_MICROBLAZE_REL = 16; |
| 18 | const R_MIPS_RELATIVE = 128; |
| 19 | const R_OR1K_RELATIVE = 21; |
| 20 | const R_PPC_RELATIVE = 22; |
| 21 | const R_RISCV_RELATIVE = 3; |
| 22 | const R_390_RELATIVE = 12; |
| 23 | const R_SH_RELATIVE = 165; |
| 24 | const R_SPARC_RELATIVE = 22; |
| 25 | const R_XTENSA_RELATIVE = 5; |
| 26 | |
| 27 | const 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. |
| 54 | inline 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 | |
| 317 | pub 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 | } |