blob: 8a3c003d9ed01262f55548a29c90366e480c501e (
plain) (
blame)
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
|
// Copyright (c) 2021 Johannes Stoelp
#include <syscall.h>
#include <auxv.h>
#include <common.h>
#include <io.h>
#include <stdint.h>
#include <asm/unistd.h>
#if !defined(__linux__) || !defined(__x86_64__)
# error "Only supported in linux(x86_64)!"
#endif
void dl_entry(const uint64_t* prctx) {
// Interpret data on the stack passed by the OS kernel as specified in the
// x86_64 SysV ABI.
uint64_t argc = *prctx;
const char** argv = (const char**)(prctx + 1);
const char** envv = (const char**)(argv + argc + 1);
// Count the number of environment variables in the `ENVP` segment.
int envc = 0;
for (const char** env = envv; *env; ++env) {
++envc;
}
uint64_t auxv[AT_MAX_CNT];
for (unsigned i = 0; i < AT_MAX_CNT; ++i) {
auxv[i] = 0;
}
// Read the `AUXV` auxiliary vector segment.
const Auxv64Entry* auxvp = (const Auxv64Entry*)(envv + envc + 1);
for (; auxvp->tag != AT_NULL; ++auxvp) {
if (auxvp->tag < AT_MAX_CNT) {
auxv[auxvp->tag] = auxvp->val;
}
}
// Get address of the entrypoint for the user executable and
// transfer control.
// Requirements for the user executable:
// - no dependencies
// - no relocations
pfmt("[dynld]: Running %s @ %s\n", __FUNCTION__, __FILE__);
// Either `AT_EXECFD` or `AT_PHDR` must be specified, we only
// support `AT_PHDR` here.
//
// From the X86_64 SystemV ABI:
// AT_EXECFD
// At process creation the system may pass control to an
// interpreter program. When this happens, the system places
// either an entry of type `AT_EXECFD` or one of type `AT_PHDR`
// in the auxiliary vector. The entry for type `AT_EXECFD`
// contains a file descriptor open to read the application
// program’s object file.
//
// AT_PHDR
// The system may create the memory image of the application
// program before passing control to the interpreter
// program. When this happens the `AT_PHDR` entry tells the
// interpreter where to find the program header table in the
// memory image.
ERROR_ON(auxv[AT_PHDR] == 0 || auxv[AT_EXECFD] != 0, "[dynld]: ERROR, expected Linux Kernel to map user executable!\n");
// Entrypoint must be defined.
ERROR_ON(auxv[AT_ENTRY] == 0, "[dynld]: ERROR, AT_ENTRY not found in auxiliary vector!\n");
// Transfer control to user executable.
void (*user_entry)() = (void (*)())auxv[AT_ENTRY];
pfmt("[dynld]: Got user entrypoint @0x%x\n", user_entry);
user_entry();
syscall1(__NR_exit, 0);
}
|