xref: /linux/arch/arm64/kernel/pi/relacheck.c (revision e6a901a00822659181c93c86d8bbc2a17779fddc)
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 
24 static Elf64_Ehdr *ehdr;
25 static Elf64_Shdr *shdr;
26 static const char *strtab;
27 static bool swap;
28 
29 static uint64_t swab_elfxword(uint64_t val)
30 {
31 	return swap ? __builtin_bswap64(val) : val;
32 }
33 
34 static uint32_t swab_elfword(uint32_t val)
35 {
36 	return swap ? __builtin_bswap32(val) : val;
37 }
38 
39 static uint16_t swab_elfhword(uint16_t val)
40 {
41 	return swap ? __builtin_bswap16(val) : val;
42 }
43 
44 int 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