131 lines
3.1 KiB
C
131 lines
3.1 KiB
C
// 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;
|
|
}
|