Commit 48157aa3 authored by Ard Biesheuvel's avatar Ard Biesheuvel Committed by Catalin Marinas

arm64: kernel: Manage absolute relocations in code built under pi/

The mini C runtime runs before relocations are processed, and so it
cannot rely on statically initialized pointer variables.

Add a check to ensure that such code does not get introduced by
accident, by going over the relocations in each object, identifying the
ones that operate on data sections that are part of the executable
image, and raising an error if any relocations of type R_AARCH64_ABS64
exist. Note that such relocations are permitted in other places (e.g.,
debug sections) and will never occur in compiler generated code sections
when using the small code model, so only check sections that have
SHF_ALLOC set and SHF_EXECINSTR cleared.

To accommodate cases where statically initialized symbol references are
unavoidable, introduce a special case for ELF input data sections that
have ".rodata.prel64" in their names, and in these cases, instead of
rejecting any encountered ABS64 relocations, convert them into PREL64
relocations, which don't require any runtime fixups. Note that the code
in question must still be modified to deal with this, as it needs to
convert the 64-bit signed offsets into absolute addresses before use.
Signed-off-by: default avatarArd Biesheuvel <ardb@kernel.org>
Link: https://lore.kernel.org/r/20240214122845.2033971-46-ardb+git@google.comSigned-off-by: default avatarCatalin Marinas <catalin.marinas@arm.com>
parent 3567fa63
...@@ -22,11 +22,16 @@ KCSAN_SANITIZE := n ...@@ -22,11 +22,16 @@ KCSAN_SANITIZE := n
UBSAN_SANITIZE := n UBSAN_SANITIZE := n
KCOV_INSTRUMENT := n KCOV_INSTRUMENT := n
hostprogs := relacheck
quiet_cmd_piobjcopy = $(quiet_cmd_objcopy)
cmd_piobjcopy = $(cmd_objcopy) && $(obj)/relacheck $(@) $(<)
$(obj)/%.pi.o: OBJCOPYFLAGS := --prefix-symbols=__pi_ \ $(obj)/%.pi.o: OBJCOPYFLAGS := --prefix-symbols=__pi_ \
--remove-section=.note.gnu.property \ --remove-section=.note.gnu.property \
--prefix-alloc-sections=.init --prefix-alloc-sections=.init
$(obj)/%.pi.o: $(obj)/%.o FORCE $(obj)/%.pi.o: $(obj)/%.o $(obj)/relacheck FORCE
$(call if_changed,objcopy) $(call if_changed,piobjcopy)
$(obj)/lib-%.o: $(srctree)/lib/%.c FORCE $(obj)/lib-%.o: $(srctree)/lib/%.c FORCE
$(call if_changed_rule,cc_o_c) $(call if_changed_rule,cc_o_c)
......
// SPDX-License-Identifier: GPL-2.0-only
// Copyright 2023 Google LLC
// Author: Ard Biesheuvel <ardb@google.com>
#define __prel64_initconst __section(".init.rodata.prel64")
#define PREL64(type, name) union { type *name; prel64_t name ## _prel; }
#define prel64_pointer(__d) (typeof(__d))prel64_to_pointer(&__d##_prel)
typedef volatile signed long prel64_t;
static inline void *prel64_to_pointer(const prel64_t *offset)
{
if (!*offset)
return NULL;
return (void *)offset + *offset;
}
// 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;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment