summaryrefslogtreecommitdiff
path: root/arch/arm64/kernel/pi/relacheck.c
blob: b0cd4d0d275bee0d6f90629034de8af1348ba3b1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// 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;
}