Skip to content

Commit 33e0cb3

Browse files
committed
Make mmap() work better
ELF loading now works properly, by delegating to the underlying mmap() system call (rather than copying the executable into memory). This reduces the memory required to emulate GCC by 40mb. Here's the latest benchmark for running GCC: RL: took 416,277µs wall time RL: ballooned to 107,028kb in size RL: needed 415,958µs cpu (7% kernel) RL: caused 21,681 page faults (100% memcpy) RL: 3 context switches (33% consensual) RL: performed 0 reads and 8 write i/o operations We're now outperforming Qemu by 33% and we finally have comparable memory usage. This represents a big improvement compared to yesterday: https://twitter.com/JustineTunney/status/1604049358340902912 Some fine tuning is also performed by this change, to help ensure error messages are reliably printed when things fall apart.
1 parent cb5cfa2 commit 33e0cb3

File tree

15 files changed

+369
-134
lines changed

15 files changed

+369
-134
lines changed

blink/assert.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@
1616
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
1717
│ PERFORMANCE OF THIS SOFTWARE. │
1818
╚─────────────────────────────────────────────────────────────────────────────*/
19+
#include <errno.h>
1920
#include <limits.h>
2021
#include <stdio.h>
2122
#include <stdlib.h>
23+
#include <string.h>
2224

2325
#include "blink/assert.h"
2426
#include "blink/debug.h"
@@ -39,7 +41,8 @@ static void PrintBacktraceUsingAsan(void) {
3941
void AssertFailed(const char *file, int line, const char *msg) {
4042
_Thread_local static bool noreentry;
4143
char b[512];
42-
snprintf(b, sizeof(b), "%s:%d: assertion failed: %s\n", file, line, msg);
44+
snprintf(b, sizeof(b), "%s:%d: assertion failed: %s (%s)\n", file, line, msg,
45+
strerror(errno));
4346
b[sizeof(b) - 1] = 0;
4447
WriteErrorString(b);
4548
if (g_machine && !noreentry) {

blink/blink.c

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
#include "blink/assert.h"
2727
#include "blink/case.h"
28+
#include "blink/debug.h"
2829
#include "blink/dll.h"
2930
#include "blink/endian.h"
3031
#include "blink/jit.h"
@@ -55,6 +56,29 @@ static void OnSigSys(int sig) {
5556
// do nothing
5657
}
5758

59+
_Noreturn void TerminateSignal(struct Machine *m, int sig) {
60+
int syssig;
61+
struct sigaction sa;
62+
LOGF("terminating due to signal %s", DescribeSignal(sig));
63+
if ((syssig = XlatSignal(sig)) == -1) syssig = SIGTERM;
64+
LOCK(&m->system->sig_lock);
65+
sa.sa_flags = 0;
66+
sa.sa_handler = SIG_DFL;
67+
sigemptyset(&sa.sa_mask);
68+
unassert(!sigaction(syssig, &sa, 0));
69+
unassert(!kill(getpid(), syssig));
70+
abort();
71+
}
72+
73+
static void OnSigSegv(int sig, siginfo_t *si, void *uc) {
74+
RestoreIp(g_machine);
75+
g_machine->faultaddr = ToGuest(si->si_addr);
76+
LOGF("SEGMENTATION FAULT AT ADDRESS %" PRIx64 "\n\t%s", g_machine->faultaddr,
77+
GetBacktrace(g_machine));
78+
DeliverSignalToUser(g_machine, SIGSEGV_LINUX);
79+
siglongjmp(g_machine->onhalt, kMachineSegmentationFault);
80+
}
81+
5882
static int Exec(char *prog, char **argv, char **envp) {
5983
struct Machine *old;
6084
if ((old = g_machine)) KillOtherThreads(old->system);
@@ -79,7 +103,7 @@ static int Exec(char *prog, char **argv, char **envp) {
79103
FreeMachine(old);
80104
}
81105
for (;;) {
82-
if (!setjmp(g_machine->onhalt)) {
106+
if (!sigsetjmp(g_machine->onhalt, 1)) {
83107
g_machine->canhalt = true;
84108
Actor(g_machine);
85109
}
@@ -119,19 +143,22 @@ static void GetOpts(int argc, char *argv[]) {
119143
}
120144
}
121145

122-
static void HandleSigSys(void) {
146+
static void HandleSigs(void) {
123147
struct sigaction sa;
124148
sigfillset(&sa.sa_mask);
125149
sa.sa_flags = 0;
126150
sa.sa_handler = OnSigSys;
127151
unassert(!sigaction(SIGSYS, &sa, 0));
152+
sa.sa_sigaction = OnSigSegv;
153+
sa.sa_flags = SA_SIGINFO;
154+
unassert(!sigaction(SIGSEGV, &sa, 0));
128155
}
129156

130157
int main(int argc, char *argv[], char **envp) {
131158
g_blink_path = argc > 0 ? argv[0] : 0;
132159
GetOpts(argc, argv);
133160
if (optind_ == argc) PrintUsage(argc, argv, 48, 2);
134161
WriteErrorInit();
135-
HandleSigSys();
162+
HandleSigs();
136163
return Exec(argv[optind_], argv + optind_, envp);
137164
}

blink/cpucount.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@
1717
│ PERFORMANCE OF THIS SOFTWARE. │
1818
╚─────────────────────────────────────────────────────────────────────────────*/
1919
#include <stdatomic.h>
20+
#include <sys/types.h>
2021
#ifdef __linux
2122
#include <sched.h>
2223
#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
2324
#include <sys/sysctl.h>
24-
#include <sys/types.h>
2525
#define HAVE_SYSCTL
2626
#elif defined(__NetBSD__)
2727
#include <sys/param.h>

blink/loader.c

Lines changed: 104 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,9 @@ static bool CouldJit(struct Machine *m) {
4646
}
4747

4848
static i64 LoadElfLoadSegment(struct Machine *m, void *image, size_t imagesize,
49-
const Elf64_Phdr *phdr, i64 last_end) {
49+
const Elf64_Phdr *phdr, i64 last_end, int fd) {
50+
i64 bulk;
51+
struct System *s = m->system;
5052
u32 flags = Read32(phdr->p_flags);
5153
i64 vaddr = Read64(phdr->p_vaddr);
5254
i64 memsz = Read64(phdr->p_memsz);
@@ -55,6 +57,20 @@ static i64 LoadElfLoadSegment(struct Machine *m, void *image, size_t imagesize,
5557
long pagesize = GetSystemPageSize();
5658
i64 start = ROUNDDOWN(vaddr, pagesize);
5759
i64 end = ROUNDUP(vaddr + memsz, pagesize);
60+
long skew = vaddr & (pagesize - 1);
61+
u64 key = (flags & PF_R ? PAGE_U : 0) | //
62+
(flags & PF_W ? PAGE_RW : 0) | //
63+
(flags & PF_X ? 0 : PAGE_XD);
64+
65+
ELF_LOGF("PROGRAM HEADER");
66+
ELF_LOGF(" vaddr = %" PRIx64, vaddr);
67+
ELF_LOGF(" memsz = %" PRIx64, memsz);
68+
ELF_LOGF(" offset = %" PRIx64, offset);
69+
ELF_LOGF(" filesz = %" PRIx64, filesz);
70+
ELF_LOGF(" pagesize = %" PRIx64, pagesize);
71+
ELF_LOGF(" start = %" PRIx64, start);
72+
ELF_LOGF(" end = %" PRIx64, end);
73+
ELF_LOGF(" skew = %lx", skew);
5874

5975
if (offset > imagesize) {
6076
LOGF("bad phdr offset");
@@ -72,39 +88,97 @@ static i64 LoadElfLoadSegment(struct Machine *m, void *image, size_t imagesize,
7288
LOGF("program headers aren't ordered");
7389
exit(127);
7490
}
75-
if (memsz <= 0) {
76-
return last_end;
91+
if (skew != (offset & (pagesize - 1))) {
92+
LOGF("p_vaddr p_offset skew unequal w.r.t. host page size");
93+
exit(127);
7794
}
7895

7996
// on systems with a page size greater than the elf executable (e.g.
8097
// apple m1) it's possible for the second program header load to end
8198
// up overlapping the previous one.
82-
if (memsz > 0 && start < last_end) {
99+
if (start < last_end) {
83100
start += last_end - start;
84-
memsz = end - start;
101+
}
102+
if (start >= end) {
103+
return last_end;
85104
}
86105

87-
if (memsz > 0) {
88-
if (ReserveVirtual(m->system, start, end - start,
89-
(flags & PF_R ? PAGE_U : 0) |
90-
(flags & PF_W ? PAGE_RW : 0) |
91-
(flags & PF_X ? 0 : PAGE_XD),
92-
-1, false) == -1) {
93-
LOGF("failed to reserve virtual memory for elf program header");
94-
exit(127);
106+
// mmap() always returns an address that page-size aligned, but elf
107+
// program headers can start at any address. therefore the first page
108+
// needs to be loaded with special care, if the phdr is skewed. the
109+
// only real rule in this situation is that the skew of of the virtual
110+
// address and the file offset need to be the same.
111+
if (skew) {
112+
if (vaddr > start && start + pagesize <= end) {
113+
unassert(vaddr < start + pagesize);
114+
ELF_LOGF("alloc %" PRIx64 "-%" PRIx64, start, start + pagesize);
115+
if (ReserveVirtual(s, start, pagesize, key, -1, 0, 0) == -1) {
116+
LOGF("failed to allocate program header skew");
117+
exit(127);
118+
}
119+
start += pagesize;
120+
}
121+
ELF_LOGF("copy %" PRIx64 "-%" PRIx64 " from %" PRIx64 "-%" PRIx64, vaddr,
122+
vaddr + (pagesize - skew), offset,
123+
offset + MIN(filesz, pagesize - skew));
124+
CopyToUser(m, vaddr, (u8 *)image + offset, MIN(filesz, pagesize - skew));
125+
vaddr += pagesize - skew;
126+
offset += pagesize - skew;
127+
filesz -= MIN(filesz, pagesize - skew);
128+
}
129+
130+
// load the aligned program header.
131+
unassert(start <= end);
132+
unassert(vaddr == start);
133+
unassert(vaddr + filesz <= end);
134+
unassert(!(vaddr & (pagesize - 1)));
135+
unassert(!(start & (pagesize - 1)));
136+
unassert(!(offset & (pagesize - 1)));
137+
if (start < end) {
138+
bulk = ROUNDDOWN(filesz, pagesize);
139+
unassert(bulk >= 0);
140+
if (bulk) {
141+
// map the bulk of .text directly into memory without copying.
142+
ELF_LOGF("load %" PRIx64 "-%" PRIx64 " from %" PRIx64 "-%" PRIx64, start,
143+
start + bulk, offset, offset + bulk);
144+
if (ReserveVirtual(s, start, bulk, key, fd, offset, 0) == -1) {
145+
LOGF("failed to map elf program header file data");
146+
exit(127);
147+
}
148+
if (!HasLinearMapping(m->system) && fd != -1) {
149+
SyncVirtual(m, start, bulk, fd, offset);
150+
}
151+
}
152+
start += bulk;
153+
offset += bulk;
154+
filesz -= bulk;
155+
if (start < end) {
156+
// allocate .bss zero-initialized memory.
157+
ELF_LOGF("alloc %" PRIx64 "-%" PRIx64, start, end);
158+
if (ReserveVirtual(s, start, end - start, key, -1, 0, 0) == -1) {
159+
LOGF("failed to allocate program header bss");
160+
exit(127);
161+
}
162+
// copy the tail skew.
163+
if (filesz) {
164+
ELF_LOGF("copy %" PRIx64 "-%" PRIx64 " from %" PRIx64 "-%" PRIx64,
165+
start, start + filesz, offset, offset + filesz);
166+
CopyToUser(m, start, (u8 *)image + offset, filesz);
167+
}
168+
} else {
169+
unassert(!filesz);
95170
}
96171
}
97172

98-
CopyToUser(m, vaddr, (u8 *)image + offset, filesz);
99-
m->system->brk = MAX(m->system->brk, end);
173+
s->brk = MAX(s->brk, end);
100174

101175
#ifdef HAVE_JIT
102176
if ((flags & PF_X) && CouldJit(m)) {
103-
if (!m->system->codesize) {
104-
m->system->codestart = vaddr;
105-
m->system->codesize = memsz;
106-
} else if (vaddr == m->system->codestart + m->system->codesize) {
107-
m->system->codesize += memsz;
177+
if (!s->codesize) {
178+
s->codestart = vaddr;
179+
s->codesize = memsz;
180+
} else if (vaddr == s->codestart + s->codesize) {
181+
s->codesize += memsz;
108182
} else {
109183
LOGF("elf has multiple executable program headers at noncontiguous "
110184
"addresses; only the first region will benefit from jitting");
@@ -129,20 +203,20 @@ bool IsSupportedExecutable(const char *path, void *image) {
129203
}
130204

131205
static void LoadFlatExecutable(struct Machine *m, intptr_t base,
132-
const char *prog, void *image,
133-
size_t imagesize) {
206+
const char *prog, void *image, size_t imagesize,
207+
int fd) {
134208
Elf64_Phdr phdr;
135209
Write32(phdr.p_type, PT_LOAD);
136210
Write32(phdr.p_flags, PF_X | PF_R | PF_W);
137211
Write64(phdr.p_offset, 0);
138212
Write64(phdr.p_vaddr, base);
139213
Write64(phdr.p_filesz, imagesize);
140214
Write64(phdr.p_memsz, ROUNDUP(imagesize + kRealSize, 4096));
141-
LoadElfLoadSegment(m, image, imagesize, &phdr, 0);
215+
LoadElfLoadSegment(m, image, imagesize, &phdr, 0, fd);
142216
m->ip = base;
143217
}
144218

145-
static bool LoadElf(struct Machine *m, struct Elf *elf) {
219+
static bool LoadElf(struct Machine *m, struct Elf *elf, int fd) {
146220
int i;
147221
Elf64_Phdr *phdr;
148222
i64 end = INT64_MIN;
@@ -153,7 +227,7 @@ static bool LoadElf(struct Machine *m, struct Elf *elf) {
153227
switch (Read32(phdr->p_type)) {
154228
case PT_LOAD:
155229
elf->base = MIN(elf->base, (i64)Read64(phdr->p_vaddr));
156-
end = LoadElfLoadSegment(m, elf->ehdr, elf->size, phdr, end);
230+
end = LoadElfLoadSegment(m, elf->ehdr, elf->size, phdr, end, fd);
157231
break;
158232
case PT_GNU_STACK:
159233
execstack = false;
@@ -270,7 +344,6 @@ void LoadProgram(struct Machine *m, char *prog, char **args, char **vars) {
270344
WriteErrorString("\n");
271345
exit(127);
272346
};
273-
unassert(!close(fd));
274347
if (!IsSupportedExecutable(prog, elf->map)) {
275348
WriteErrorString("\
276349
error: unsupported executable; we need:\n\
@@ -290,25 +363,25 @@ error: unsupported executable; we need:\n\
290363
if (READ32(elf->map) == READ32("\177ELF")) {
291364
elf->ehdr = (Elf64_Ehdr *)elf->map;
292365
elf->size = elf->mapsize;
293-
execstack = LoadElf(m, elf);
366+
execstack = LoadElf(m, elf, fd);
294367
} else if (READ64(elf->map) == READ64("MZqFpD='") ||
295368
READ64(elf->map) == READ64("jartsr='")) {
296369
if (GetElfHeader(ehdr, prog, elf->map) == -1) exit(127);
297370
memcpy(elf->map, ehdr, 64);
298371
elf->ehdr = (Elf64_Ehdr *)elf->map;
299372
elf->size = elf->mapsize;
300-
execstack = LoadElf(m, elf);
373+
execstack = LoadElf(m, elf, fd);
301374
} else {
302375
elf->base = 0x400000;
303376
elf->ehdr = NULL;
304377
elf->size = 0;
305-
LoadFlatExecutable(m, elf->base, prog, elf->map, elf->mapsize);
378+
LoadFlatExecutable(m, elf->base, prog, elf->map, elf->mapsize, fd);
306379
execstack = true;
307380
}
308381
sp = kStackTop;
309382
Put64(m->sp, sp);
310383
if (ReserveVirtual(m->system, sp - kStackSize, kStackSize,
311-
PAGE_U | PAGE_RW | (execstack ? 0 : PAGE_XD), -1,
384+
PAGE_U | PAGE_RW | (execstack ? 0 : PAGE_XD), -1, 0,
312385
false) == -1) {
313386
LOGF("failed to reserve stack memory");
314387
exit(127);
@@ -319,4 +392,5 @@ error: unsupported executable; we need:\n\
319392
pagesize = sysconf(_SC_PAGESIZE);
320393
pagesize = MAX(4096, pagesize);
321394
m->system->brk = ROUNDUP(m->system->brk, pagesize);
395+
unassert(!close(fd));
322396
}

blink/log.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#define LOG_JIX 0
1717
#define LOG_MEM 0
1818
#define LOG_THR 0
19+
#define LOG_ELF 0
1920

2021
#if LOG_ENABLED
2122
#define LOGF(...) Log(__FILE__, __LINE__, __VA_ARGS__)
@@ -65,6 +66,12 @@
6566
#define THR_LOGF(...) (void)0
6667
#endif
6768

69+
#if LOG_ELF
70+
#define ELF_LOGF(...) Log(__FILE__, __LINE__, "(elf) " __VA_ARGS__)
71+
#else
72+
#define ELF_LOGF(...) (void)0
73+
#endif
74+
6875
#if LOG_ENABLED
6976
#define LOG_ONCE(x) \
7077
do { \

0 commit comments

Comments
 (0)