The Pedigree Project 0.1
library.c
1/* Copyright (c) 2026, Pedigree Developers. */
2#define _GNU_SOURCE
3#include <dlfcn.h>
4#include <fcntl.h>
5#include <signal.h>
6#include <stdlib.h>
7#include <string.h>
8#include <unistd.h>
9
10#include "contract.h"
11#include <sys/mman.h>
12#include <sys/stat.h>
13
14int ef_load(const char* path, int version) {
15 int failed = 0;
16 void* library = dlopen(path, RTLD_NOW | RTLD_LOCAL);
17 if (!library)
18 fprintf(stderr, "EXECUTABLE-FILE-CONTRACT: dlopen %s: %s\n", path, dlerror());
19 CHECK(library);
20 int (*warm)(void) = (int (*)(void))dlsym(library, "executable_fixture_warm");
21 int (*cold)(void) = (int (*)(void))dlsym(library, "executable_fixture_cold");
22 CHECK(warm && cold && warm() == version && cold() == version);
23out:
24 if (library)
25 dlclose(library);
26 return failed;
27}
28
29int ef_library(void) {
30 int failed = 0, fd = -1, probe = -1;
31 char path[PATH_MAX], staged[PATH_MAX];
32 void* library = NULL;
33 pid_t child = -1;
34 snprintf(path, sizeof(path), "%s/current.so", ef_directory);
35 snprintf(staged, sizeof(staged), "%s/staged.so", ef_directory);
36 CHECK(!ef_copy("/libraries/libexecutable-file-fixture-v1.so", path));
37 CHECK((fd = open(path, O_RDWR | O_CLOEXEC)) >= 0);
38 struct stat before, after;
39 CHECK(!fstat(fd, &before));
40 library = dlopen(path, RTLD_NOW | RTLD_LOCAL);
41 if (!library)
42 fprintf(stderr, "EXECUTABLE-FILE-CONTRACT: dlopen %s: %s\n", path, dlerror());
43 CHECK(library);
44 int (*warm)(void) = (int (*)(void))dlsym(library, "executable_fixture_warm");
45 int (*cold)(void) = (int (*)(void))dlsym(library, "executable_fixture_cold");
46 const volatile unsigned char* data = dlsym(library, "executable_fixture_data");
47 CHECK(warm && cold && data && warm() == 1);
48 CHECK(pwrite(fd, "x", 1, 0) == -1 && errno == ETXTBSY);
49 CHECK(ftruncate(fd, 0) == -1 && errno == ETXTBSY);
50 probe = open(path, O_WRONLY | O_TRUNC);
51 CHECK(probe == -1 && errno == ETXTBSY);
52 CHECK(!fstat(fd, &after) && before.st_size == after.st_size);
53 char magic[4];
54 CHECK(pread(fd, magic, sizeof(magic), 0) == sizeof(magic) && !memcmp(magic, "\177ELF", 4));
55 CHECK(!ef_copy("/libraries/libexecutable-file-fixture-v2.so", staged));
56 uintptr_t code_page = (uintptr_t)cold / ef_page * ef_page;
57 uintptr_t data_page = (uintptr_t)(data + 8192) / ef_page * ef_page;
58 CHECK(code_page != (uintptr_t)warm / ef_page * ef_page);
59 // Retire process mappings so the old inode must satisfy faults after replacement.
60 CHECK(!madvise((void*)code_page, ef_page, MADV_DONTNEED));
61 CHECK(!madvise((void*)data_page, ef_page, MADV_DONTNEED));
62 CHECK(!rename(staged, path));
63 CHECK(!stat(path, &after) && before.st_ino != after.st_ino);
64 CHECK(cold() == 1 && data[8192] == 1 && warm() == 1);
65 CHECK((child = fork()) >= 0);
66 if (!child) {
67 execl(ef_self, ef_self, "--load", path, "2", NULL);
68 _exit(80);
69 }
70 int status = ef_reap(child);
71 child = -1;
72 CHECK(!status);
73out:
74 if (child > 0) {
75 kill(child, SIGKILL);
76 ef_reap(child);
77 }
78 if (library)
79 dlclose(library);
80 if (fd >= 0)
81 close(fd);
82 if (probe >= 0)
83 close(probe);
84 unlink(staged);
85 unlink(path);
86 return failed;
87}