1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * Copyright (C) 2023 - Google LLC
4 * Author: Ard Biesheuvel <ardb@google.com>
5 */
6
7#include <elf.h>
8#include <fcntl.h>
9#include <stdbool.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#include <sys/mman.h>
14#include <sys/stat.h>
15#include <sys/types.h>
16#include <unistd.h>
17
18#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
19#define HOST_ORDER ELFDATA2LSB
20#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
21#define HOST_ORDER ELFDATA2MSB
22#endif
23
24static Elf64_Ehdr *ehdr;
25static Elf64_Shdr *shdr;
26static const char *strtab;
27static bool swap;
28
29static uint64_t swab_elfxword(uint64_t val)
30{
31	return swap ? __builtin_bswap64(val) : val;
32}
33
34static uint32_t swab_elfword(uint32_t val)
35{
36	return swap ? __builtin_bswap32(val) : val;
37}
38
39static uint16_t swab_elfhword(uint16_t val)
40{
41	return swap ? __builtin_bswap16(val) : val;
42}
43
44int main(int argc, char *argv[])
45{
46	struct stat stat;
47	int fd, ret;
48
49	if (argc < 3) {
50		fprintf(stderr, "file arguments missing\n");
51		exit(EXIT_FAILURE);
52	}
53
54	fd = open(argv[1], O_RDWR);
55	if (fd < 0) {
56		fprintf(stderr, "failed to open %s\n", argv[1]);
57		exit(EXIT_FAILURE);
58	}
59
60	ret = fstat(fd, &stat);
61	if (ret < 0) {
62		fprintf(stderr, "failed to stat() %s\n", argv[1]);
63		exit(EXIT_FAILURE);
64	}
65
66	ehdr = mmap(0, stat.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
67	if (ehdr == MAP_FAILED) {
68		fprintf(stderr, "failed to mmap() %s\n", argv[1]);
69		exit(EXIT_FAILURE);
70	}
71
72	swap = ehdr->e_ident[EI_DATA] != HOST_ORDER;
73	shdr = (void *)ehdr + swab_elfxword(ehdr->e_shoff);
74	strtab = (void *)ehdr +
75		 swab_elfxword(shdr[swab_elfhword(ehdr->e_shstrndx)].sh_offset);
76
77	for (int i = 0; i < swab_elfhword(ehdr->e_shnum); i++) {
78		unsigned long info, flags;
79		bool prel64 = false;
80		Elf64_Rela *rela;
81		int numrela;
82
83		if (swab_elfword(shdr[i].sh_type) != SHT_RELA)
84			continue;
85
86		/* only consider RELA sections operating on data */
87		info = swab_elfword(shdr[i].sh_info);
88		flags = swab_elfxword(shdr[info].sh_flags);
89		if ((flags & (SHF_ALLOC | SHF_EXECINSTR)) != SHF_ALLOC)
90			continue;
91
92		/*
93		 * We generally don't permit ABS64 relocations in the code that
94		 * runs before relocation processing occurs. If statically
95		 * initialized absolute symbol references are unavoidable, they
96		 * may be emitted into a *.rodata.prel64 section and they will
97		 * be converted to place-relative 64-bit references. This
98		 * requires special handling in the referring code.
99		 */
100		if (strstr(strtab + swab_elfword(shdr[info].sh_name),
101			   ".rodata.prel64")) {
102			prel64 = true;
103		}
104
105		rela = (void *)ehdr + swab_elfxword(shdr[i].sh_offset);
106		numrela = swab_elfxword(shdr[i].sh_size) / sizeof(*rela);
107
108		for (int j = 0; j < numrela; j++) {
109			uint64_t info = swab_elfxword(rela[j].r_info);
110
111			if (ELF64_R_TYPE(info) != R_AARCH64_ABS64)
112				continue;
113
114			if (prel64) {
115				/* convert ABS64 into PREL64 */
116				info ^= R_AARCH64_ABS64 ^ R_AARCH64_PREL64;
117				rela[j].r_info = swab_elfxword(info);
118			} else {
119				fprintf(stderr,
120					"Unexpected absolute relocations detected in %s\n",
121					argv[2]);
122				close(fd);
123				unlink(argv[1]);
124				exit(EXIT_FAILURE);
125			}
126		}
127	}
128	close(fd);
129	return 0;
130}
131