| // SPDX-License-Identifier: GPL-2.0-only |
| /* |
| * Copyright (C) 2023 - Google LLC |
| * Author: Ard Biesheuvel <ardb@google.com> |
| */ |
| |
| #include <elf.h> |
| #include <fcntl.h> |
| #include <stdbool.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/mman.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| |
| #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ |
| #define HOST_ORDER ELFDATA2LSB |
| #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ |
| #define HOST_ORDER ELFDATA2MSB |
| #endif |
| |
| static Elf64_Ehdr *ehdr; |
| static Elf64_Shdr *shdr; |
| static const char *strtab; |
| static bool swap; |
| |
| static uint64_t swab_elfxword(uint64_t val) |
| { |
| return swap ? __builtin_bswap64(val) : val; |
| } |
| |
| static uint32_t swab_elfword(uint32_t val) |
| { |
| return swap ? __builtin_bswap32(val) : val; |
| } |
| |
| static uint16_t swab_elfhword(uint16_t val) |
| { |
| return swap ? __builtin_bswap16(val) : val; |
| } |
| |
| int main(int argc, char *argv[]) |
| { |
| struct stat stat; |
| int fd, ret; |
| |
| if (argc < 3) { |
| fprintf(stderr, "file arguments missing\n"); |
| exit(EXIT_FAILURE); |
| } |
| |
| fd = open(argv[1], O_RDWR); |
| if (fd < 0) { |
| fprintf(stderr, "failed to open %s\n", argv[1]); |
| exit(EXIT_FAILURE); |
| } |
| |
| ret = fstat(fd, &stat); |
| if (ret < 0) { |
| fprintf(stderr, "failed to stat() %s\n", argv[1]); |
| exit(EXIT_FAILURE); |
| } |
| |
| ehdr = mmap(0, stat.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); |
| if (ehdr == MAP_FAILED) { |
| fprintf(stderr, "failed to mmap() %s\n", argv[1]); |
| exit(EXIT_FAILURE); |
| } |
| |
| swap = ehdr->e_ident[EI_DATA] != HOST_ORDER; |
| shdr = (void *)ehdr + swab_elfxword(ehdr->e_shoff); |
| strtab = (void *)ehdr + |
| swab_elfxword(shdr[swab_elfhword(ehdr->e_shstrndx)].sh_offset); |
| |
| for (int i = 0; i < swab_elfhword(ehdr->e_shnum); i++) { |
| unsigned long info, flags; |
| bool prel64 = false; |
| Elf64_Rela *rela; |
| int numrela; |
| |
| if (swab_elfword(shdr[i].sh_type) != SHT_RELA) |
| continue; |
| |
| /* only consider RELA sections operating on data */ |
| info = swab_elfword(shdr[i].sh_info); |
| flags = swab_elfxword(shdr[info].sh_flags); |
| if ((flags & (SHF_ALLOC | SHF_EXECINSTR)) != SHF_ALLOC) |
| continue; |
| |
| /* |
| * We generally don't permit ABS64 relocations in the code that |
| * runs before relocation processing occurs. If statically |
| * initialized absolute symbol references are unavoidable, they |
| * may be emitted into a *.rodata.prel64 section and they will |
| * be converted to place-relative 64-bit references. This |
| * requires special handling in the referring code. |
| */ |
| if (strstr(strtab + swab_elfword(shdr[info].sh_name), |
| ".rodata.prel64")) { |
| prel64 = true; |
| } |
| |
| rela = (void *)ehdr + swab_elfxword(shdr[i].sh_offset); |
| numrela = swab_elfxword(shdr[i].sh_size) / sizeof(*rela); |
| |
| for (int j = 0; j < numrela; j++) { |
| uint64_t info = swab_elfxword(rela[j].r_info); |
| |
| if (ELF64_R_TYPE(info) != R_AARCH64_ABS64) |
| continue; |
| |
| if (prel64) { |
| /* convert ABS64 into PREL64 */ |
| info ^= R_AARCH64_ABS64 ^ R_AARCH64_PREL64; |
| rela[j].r_info = swab_elfxword(info); |
| } else { |
| fprintf(stderr, |
| "Unexpected absolute relocations detected in %s\n", |
| argv[2]); |
| close(fd); |
| unlink(argv[1]); |
| exit(EXIT_FAILURE); |
| } |
| } |
| } |
| close(fd); |
| return 0; |
| } |