RustForge

rust-kernel

Rustbelt: a hobby x86_64 kernel written in Rust

rust-kernel / user / piehello.c
2723 bytes ยท raw
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
/*
 * piehello.c -- stage 19: "hello" as a static PIE, built with the stock
 * `musl-gcc -static-pie` (no special flags). The file's addresses start
 * at 0; the kernel loads it at a random base and tells us where through
 * the auxiliary vector. musl's start-up code (rcrt1.o) then fixes up the
 * program's own pointers before main runs.
 *
 * Exit code = number of failed checks. `piehello where` prints the load
 * address of main (tests/static_pie.rs runs it twice: different bases).
 */
#define _GNU_SOURCE
#include <elf.h>
#include <link.h>
#include <stdio.h>
#include <string.h>
#include <sys/auxv.h>

static int fails;
#define CHECK(cond, what)                                    \
	do {                                                 \
		if (!(cond)) {                               \
			fails++;                             \
			printf("piehello: FAIL %s\n", what); \
		}                                            \
	} while (0)

/* Pointers in data: these need fixing up (relocating) at start-up. */
static const char *greeting = "hello from a static PIE";
static int (*say)(const char *) = puts;

extern char _start[];
extern const Elf64_Ehdr __ehdr_start; /* the linker's name for our ELF header */

int main(int argc, char **argv)
{
	if (argc > 1 && strcmp(argv[1], "where") == 0) {
		printf("%p\n", (void *)main);
		return 0;
	}
	say(greeting);
	unsigned long phdr = getauxval(AT_PHDR), entry = getauxval(AT_ENTRY);
	CHECK(getauxval(AT_BASE) == 0, "AT_BASE is 0 (no dynamic loader)");
	CHECK(entry == (unsigned long)_start, "AT_ENTRY is where _start is");
	CHECK(getauxval(AT_PHENT) == sizeof(Elf64_Phdr), "AT_PHENT");
	/* Our ELF header is loaded too, at the load bias (the file's lowest
	 * address is 0), and the program headers follow it, where AT_PHDR says. */
	unsigned long bias = (unsigned long)&__ehdr_start;
	CHECK(phdr == bias + __ehdr_start.e_phoff, "AT_PHDR points at the program headers");
	const Elf64_Phdr *ph = (const Elf64_Phdr *)phdr;
	int has_tls = 0, has_relro = 0, has_interp = 0;
	for (unsigned long i = 0; i < getauxval(AT_PHNUM); i++) {
		has_tls |= ph[i].p_type == PT_TLS;
		has_relro |= ph[i].p_type == PT_GNU_RELRO;
		has_interp |= ph[i].p_type == PT_INTERP;
	}
	CHECK(bias != 0 && bias % 4096 == 0, "a page-aligned, non-zero load bias");
	CHECK(bias >= 0x8000000000UL, "loaded in Rustbelt's user space");
	CHECK(has_relro && !has_interp, "PT_GNU_RELRO, no PT_INTERP");
	(void)has_tls;
	/* The relocated pointers point into the loaded image. */
	CHECK((unsigned long)greeting > bias, "relocated string pointer");
	/* RELRO: musl made it read-only after relocating; mprotect worked. */
	printf("argv[0] = %s, %d argument(s), bias %#lx\n", argv[0], argc, bias);
	return fails;
}