Make improvements

- Implement openpty()
- Add `--assimilate` flag to APE bootloader
- Restore Linux vDSO clock_gettime() support
- Use `$(APE_NO_MODIFY_SELF)` on more programs
This commit is contained in:
Justine Tunney 2022-05-24 10:19:39 -07:00
parent cef50f2a6b
commit d44ff6ce1f
33 changed files with 600 additions and 251 deletions

View file

@ -28,21 +28,57 @@ printf 'main() { printf("hello world\\n"); }\n' >hello.c
gcc -g -Os -static -nostdlib -nostdinc -fno-pie -no-pie -mno-red-zone \
-fno-omit-frame-pointer -pg -mnop-mcount \
-o hello.com.dbg hello.c -fuse-ld=bfd -Wl,-T,ape.lds \
-include cosmopolitan.h crt.o ape.o cosmopolitan.a
-include cosmopolitan.h crt.o ape-no-modify-self.o cosmopolitan.a
objcopy -S -O binary hello.com.dbg hello.com
```
You now have a portable program. Please note that your APE binary will
assimilate itself as a conventional resident of your platform after the
first run, so it can be fast and efficient for subsequent executions.
You now have a portable program.
```sh
./hello.com
bash -c './hello.com' # zsh/fish workaround (we upstreamed patches)
bash -c './hello.com' # zsh/fish workaround (we patched them in 2021)
```
So if you intend to copy the binary to Windows or Mac then please do
that before you run it, not after.
Since we used the `ape-no-modify-self.o` bootloader (rather than
`ape.o`) your executable will not modify itself when it's run. What
it'll instead do, is extract a 4kb program to `${TMPDIR:-/tmp}` that
maps your program into memory without needing to copy it. It's possible
to install the APE loader systemwide as follows.
```sh
# (1) linux systems that want binfmt_misc
ape/apeinstall.sh
# (2) for linux/freebsd/netbsd/openbsd systems
cp build/bootstrap/ape.elf /usr/bin/ape
# (3) for mac os x systems
cp build/bootstrap/ape.macho /usr/bin/ape
```
If you followed steps (2) and (3) then there's going to be a slight
constant-time startup latency each time you run an APE binary. Your
system might also prevent your APE program from being installed to a
system directory as a setuid binary or a script interpreter. To solve
that, you can use the following flag to turn your binary into the
platform local format (ELF or Mach-O):
```sh
./hello.com --assimilate
```
There's also some other useful flags that get baked into your binary by
default:
```sh
./hello.com --strace
./hello.com --ftrace
```
If you want your `hello.com` program to be much tinier, more on the
order of 16kb rather than 60kb, then all you have to do is use
<https://justine.lol/cosmopolitan/cosmopolitan-tiny.zip> instead. See
<https://justine.lol/cosmopolitan/download.html>.
### MacOS

View file

@ -542,34 +542,37 @@ apesh: .ascii "'\n#'\"\n" # sixth edition shebang
// present two choices.
.ascii "o=\"$(command -v \"$0\")\"\n"
// Try to use a system-wide APE loader.
.ascii "type ape >/dev/null 2>&1 && "
.ascii "exec ape \"$o\" \"$@\"\n"
.ascii "[ x\"$1\" != x--assimilate ] && "
.ascii "type ape >/dev/null 2>&1 && "
.ascii "exec ape \"$o\" \"$@\"\n"
#ifdef APE_LOADER
// There is no system-wide APE loader, but there is one
// embedded inside the APE. So if the system is not MacOs,
// extract the loader into a temp folder, and use it to
// load the APE without modifying it.
.ascii "t=\"${TMPDIR:-/tmp}/ape\"\n"
.ascii "[ -x \"$t\" ] || {\n"
.ascii "dd if=\"$o\" of=\"$t.$$\" skip=\""
.shstub ape_loader_dd_skip,2
.ascii "\" count=\""
.shstub ape_loader_dd_count,2
.ascii "\" bs=64 2>/dev/null\n"
.ascii "[ x\"$1\" != x--assimilate ] && {\n"
.ascii "t=\"${TMPDIR:-/tmp}/ape\"\n"
.ascii "[ -x \"$t\" ] || {\n"
.ascii "dd if=\"$o\" of=\"$t.$$\" skip=\""
.shstub ape_loader_dd_skip,2
.ascii "\" count=\""
.shstub ape_loader_dd_count,2
.ascii "\" bs=64 2>/dev/null\n"
#if SupportsXnu()
.ascii "[ -d /Applications ] && "
.ascii "dd if=\"$t.$$\""
.ascii " of=\"$t.$$\""
.ascii " skip=6"
.ascii " count=10"
.ascii " bs=64"
.ascii " conv=notrunc"
.ascii " 2>/dev/null\n"
.ascii "[ -d /Applications ] && "
.ascii "dd if=\"$t.$$\""
.ascii " of=\"$t.$$\""
.ascii " skip=6"
.ascii " count=10"
.ascii " bs=64"
.ascii " conv=notrunc"
.ascii " 2>/dev/null\n"
#endif /* SupportsXnu() */
.ascii "chmod 755 \"$t.$$\"\n"
.ascii "mv -f \"$t.$$\" \"$t\"\n"
.ascii "chmod 755 \"$t.$$\"\n"
.ascii "mv -f \"$t.$$\" \"$t\"\n"
.ascii "}\n"
.ascii "exec \"$t\" \"$o\" \"$@\"\n"
.ascii "}\n"
.ascii "exec \"$t\" \"$o\" \"$@\"\n"
#endif /* APE_LOADER */
#ifndef APE_NO_MODIFY_SELF
// The default behavior is: to overwrite the header in place.
@ -590,11 +593,13 @@ apesh: .ascii "'\n#'\"\n" # sixth edition shebang
// since only root is able to set the sticky bit, which can
// be addressed simply by overriding the TMPDIR environment
.ascii "t=\"${TMPDIR:-/tmp}/$0\"\n"
.ascii "[ -e \"$t\" ] || {\n"
.ascii "mkdir -p \"${t%/*}\" 2>/dev/null\n"
.ascii "cp -f \"$o\" \"$t.$$\" &&\n"
.ascii "mv -f \"$t.$$\" \"$t\" || exit 120\n"
.ascii "o=\"$t\"\n"
.ascii "[ x\"$1\" != x--assimilate ] || [ ! -e \"$t\" ] && {\n"
.ascii "[ x\"$1\" != x--assimilate ] && {\n"
.ascii "mkdir -p \"${t%/*}\" 2>/dev/null\n"
.ascii "cp -f \"$o\" \"$t.$$\" &&\n"
.ascii "mv -f \"$t.$$\" \"$t\" || exit 120\n"
.ascii "o=\"$t\"\n"
.ascii "}\n"
#endif /* APE_NO_MODIFY_SELF */
.ascii "exec 7<> \"$o\" || exit 121\n"
.ascii "printf '"
@ -632,6 +637,7 @@ apesh: .ascii "'\n#'\"\n" # sixth edition shebang
.shstub ape_macho_dd_count,2
.ascii "\" conv=notrunc 2>/dev/null\n"
#endif /* XNU */
.ascii "[ x\"$1\" = x--assimilate ] && exit 0\n"
#ifndef APE_NO_MODIFY_SELF
.ascii "exec \"$0\" \"$@\"\n" # try to preserve argv[0]
#else

View file

@ -112,6 +112,14 @@ o/$(MODE)/examples/nomodifyself.com.dbg: \
$(APE_NO_MODIFY_SELF)
@$(APELINK)
o/$(MODE)/examples/greenbean.com.dbg: \
$(EXAMPLES_DEPS) \
o/$(MODE)/examples/greenbean.o \
o/$(MODE)/examples/examples.pkg \
$(CRT) \
$(APE_NO_MODIFY_SELF)
@$(APELINK)
o/$(MODE)/examples/hellolua.com.dbg: \
$(EXAMPLES_DEPS) \
o/$(MODE)/examples/hellolua.o \
@ -162,4 +170,5 @@ usr/share/dict/words: usr/share/dict/words.gz
.PHONY: o/$(MODE)/examples
o/$(MODE)/examples: \
o/$(MODE)/examples/package \
o/$(MODE)/examples/pyapp \
$(EXAMPLES_BINS)

View file

@ -70,20 +70,20 @@
* Like redbean, greenbean has superior performance too, with an
* advantage on benchmarks biased towards high connection counts
*
* $ sudo wrk -c 300 -t 32 --latency http://10.10.10.124:8080/
* $ wrk -c 300 -t 32 --latency http://10.10.10.124:8080/
* Running 10s test @ http://10.10.10.124:8080/
* 32 threads and 300 connections
* Thread Stats Avg Stdev Max +/- Stdev
* Latency 1.07ms 8.27ms 138.55ms 98.58%
* Req/Sec 37.98k 12.61k 117.65k 80.11%
* Latency 661.06us 5.11ms 96.22ms 98.85%
* Req/Sec 42.38k 8.90k 90.47k 84.65%
* Latency Distribution
* 50% 200.00us
* 75% 227.00us
* 90% 303.00us
* 99% 32.46ms
* 10033090 requests in 8.31s, 2.96GB read
* Requests/sec: 1207983.58
* Transfer/sec: 365.19MB
* 50% 184.00us
* 75% 201.00us
* 90% 224.00us
* 99% 11.99ms
* 10221978 requests in 7.60s, 3.02GB read
* Requests/sec: 1345015.69
* Transfer/sec: 406.62MB
*
*/
@ -97,6 +97,7 @@
"Referrer-Policy: origin\r\n" \
"Cache-Control: private; max-age=0\r\n"
int threads;
_Atomic(int) workers;
_Atomic(int) messages;
_Atomic(int) listening;
@ -114,7 +115,9 @@ int Worker(void *id) {
server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (server == -1) {
if (LOGGING) kprintf("%s() failed %m\n", "socket");
kprintf("socket() failed %m\n"
" try running: sudo prlimit --pid=$$ --nofile=%d\n",
threads * 2);
goto WorkerFinished;
}
@ -127,8 +130,8 @@ int Worker(void *id) {
errno = 0;
if (bind(server, &addr, sizeof(addr)) == -1) {
if (LOGGING) kprintf("%s() failed %m\n", "socket");
goto WorkerFinished;
kprintf("%s() failed %m\n", "socket");
goto CloseWorker;
}
listen(server, 1);
@ -246,8 +249,9 @@ int Worker(void *id) {
--listening;
// inform the parent that this clone has finished
WorkerFinished:
CloseWorker:
close(server);
WorkerFinished:
--workers;
return 0;
}
@ -267,9 +271,9 @@ void PrintStatus(void) {
}
int main(int argc, char *argv[]) {
int i;
char **tls;
char **stack;
int i, threads;
uint32_t *hostips;
// ShowCrashReports();
@ -295,7 +299,6 @@ int main(int argc, char *argv[]) {
if ((1 <= threads && threads <= INT_MAX) &&
(tls = malloc(threads * sizeof(*tls))) &&
(stack = malloc(threads * sizeof(*stack)))) {
if (!threads) threads = GetCpuCount();
for (i = 0; i < threads; ++i) {
if ((tls[i] = __initialize_tls(malloc(64))) &&
(stack[i] = mmap(0, GetStackSize(), PROT_READ | PROT_WRITE,

View file

@ -21,7 +21,7 @@
int main(int argc, char *argv[]) {
if (_base[0] == 'M' && _base[1] == 'Z') {
printf("success: %s spawned without needing to modify its "
"executable header, thanks to APE loader\n",
"executable header",
argv[0]);
if (!IsWindows()) {
printf(", thanks to APE loader!\n");

View file

@ -1,5 +1,5 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney
@ -16,36 +16,15 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/dce.h"
#include "libc/log/check.h"
#include "libc/runtime/runtime.h"
#include "libc/testlib/testlib.h"
#include "libc/macros.internal.h"
void SetUp(void) {
if (getenv("_WEIRDENV")) {
for (char **e = environ; *e; ++e) {
if (!strcmp(*e, "WEIRD")) {
exit(0);
}
}
exit(7);
}
}
.initbss 201,_init___clock_gettime
__clock_gettime:
.quad 0
.endobj __clock_gettime,globl,hidden
.previous
TEST(execve, testWeirdEnvironmentVariable) {
char *prog;
int pid, ws;
if (IsWindows()) return;
if (IsOpenbsd()) return;
prog = GetProgramExecutableName();
ASSERT_NE(-1, (pid = fork()));
if (!pid) {
execve(prog, (char *const[]){prog, 0},
(char *const[]){"_WEIRDENV=1", "WEIRD", 0});
_Exit(127);
}
ASSERT_NE(-1, wait(&ws));
EXPECT_TRUE(WIFEXITED(ws));
EXPECT_EQ(0, WEXITSTATUS(ws));
}
.init.start 201,_init___clock_gettime
ezlea __clock_gettime_init,ax
stosq
.init.end 201,_init___clock_gettime

View file

@ -0,0 +1,19 @@
#ifndef COSMOPOLITAN_LIBC_CALLS_ASAN_INTERNAL_H_
#define COSMOPOLITAN_LIBC_CALLS_ASAN_INTERNAL_H_
#include "libc/bits/asmflag.h"
#include "libc/calls/struct/timespec.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
forceinline bool __asan_is_valid_timespec(const struct timespec *ts) {
bool zf;
asm(ZFLAG_ASM("cmpw\t$0,0x7fff8000(%1)")
: ZFLAG_CONSTRAINT(zf)
: "r"((intptr_t)ts >> 3)
: "memory");
return zf;
}
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_CALLS_ASAN_INTERNAL_H_ */

View file

@ -27,6 +27,7 @@ textwindows int sys_clock_gettime_nt(int clockid, struct timespec *ts) {
struct timespec res;
struct NtFileTime ft;
static struct timespec mono;
if (!ts) return efault();
if (clockid == CLOCK_REALTIME) {
GetSystemTimeAsFileTime(&ft);
*ts = FileTimeToTimeSpec(ft);

View file

@ -1,7 +1,7 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2021 Justine Alexandra Roberts Tunney
Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
@ -16,24 +16,20 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/struct/timespec.h"
#include "libc/nexgen32e/rdtscp.h"
#include "libc/sysv/consts/clock.h"
#include "libc/testlib/ezbench.h"
#include "libc/testlib/testlib.h"
#include "libc/time/time.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
TEST(nowl, testIsMonotonic) {
long double a = nowl();
long double b = nowl();
EXPECT_TRUE(b > a);
}
BENCH(nowl, bench) {
volatile int64_t c;
volatile long double x;
volatile struct timespec ts;
EZBENCH2("rdtsc", donothing, c = rdtsc());
EZBENCH2("nowl", donothing, x = nowl());
EZBENCH2("clock_gettime", donothing, clock_gettime(CLOCK_MONOTONIC, &ts));
int sys_clock_gettime_xnu(int clockid, struct timespec *ts) {
axdx_t ad;
ad = sys_gettimeofday((struct timeval *)ts, NULL, NULL);
if (ad.ax != -1) {
if (ad.ax) {
ts->tv_sec = ad.ax;
ts->tv_nsec = ad.dx;
}
ts->tv_nsec *= 1000;
return 0;
} else {
return -1;
}
}

View file

@ -17,6 +17,10 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/bits/asmflag.h"
#include "libc/bits/bits.h"
#include "libc/calls/asan.internal.h"
#include "libc/calls/clock_gettime.h"
#include "libc/calls/internal.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/strace.internal.h"
@ -26,11 +30,10 @@
#include "libc/fmt/conv.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/describeflags.internal.h"
#include "libc/mem/alloca.h"
#include "libc/nt/synchronization.h"
#include "libc/sysv/errfuns.h"
static typeof(sys_clock_gettime) *__clock_gettime = sys_clock_gettime;
/**
* Returns nanosecond time.
*
@ -38,6 +41,13 @@ static typeof(sys_clock_gettime) *__clock_gettime = sys_clock_gettime;
* time. Among the more popular is CLOCK_MONOTONIC. This function has a
* zero syscall implementation of that on modern x86.
*
* nowl l: 45𝑐 15𝑛𝑠
* rdtsc l: 13𝑐 4𝑛𝑠
* gettimeofday l: 44𝑐 14𝑛𝑠
* clock_gettime l: 40𝑐 13𝑛𝑠
* __clock_gettime l: 35𝑐 11𝑛𝑠
* sys_clock_gettime l: 220𝑐 71𝑛𝑠
*
* @param clockid can be CLOCK_REALTIME, CLOCK_MONOTONIC, etc.
* @param ts is where the result is stored
* @return 0 on success, or -1 w/ errno
@ -46,54 +56,49 @@ static typeof(sys_clock_gettime) *__clock_gettime = sys_clock_gettime;
* @asyncsignalsafe
*/
noinstrument int clock_gettime(int clockid, struct timespec *ts) {
int rc, e;
axdx_t ad;
char buf[45];
if (!ts) {
int rc;
char *buf;
if (IsAsan() && !__asan_is_valid_timespec(ts)) {
rc = efault();
} else if (IsAsan() && !__asan_is_valid(ts, sizeof(*ts))) {
rc = efault();
} else if (clockid == -1) {
rc = einval();
} else if (!IsWindows()) {
e = errno;
if ((rc = __clock_gettime(clockid, ts))) {
errno = e;
ad = sys_gettimeofday((struct timeval *)ts, NULL, NULL);
assert(ad.ax != -1);
if (SupportsXnu() && ad.ax) {
ts->tv_sec = ad.ax;
ts->tv_nsec = ad.dx;
}
ts->tv_nsec *= 1000;
rc = 0;
}
} else {
rc = sys_clock_gettime_nt(clockid, ts);
rc = __clock_gettime(clockid, ts);
}
#if SYSDEBUG
if (!__time_critical) {
buf = alloca(45);
STRACE("clock_gettime(%d, [%s]) → %d% m", clockid,
DescribeTimespec(buf, sizeof(buf), rc, ts), rc);
DescribeTimespec(buf, 45, rc, ts), rc);
}
#endif
return rc;
}
/**
* Returns fast system clock_gettime() if it exists.
* Returns pointer to fastest clock_gettime().
*/
void *__get_clock_gettime(void) {
void *vdso;
static bool once;
static void *result;
if (!once) {
if ((vdso = __vdsofunc("__vdso_clock_gettime"))) {
__clock_gettime = result = vdso;
}
once = true;
clock_gettime_f *__get_clock_gettime(bool *opt_out_isfast) {
bool isfast;
clock_gettime_f *res;
if (IsLinux() && (res = __vdsosym("LINUX_2.6", "__vdso_clock_gettime"))) {
isfast = true;
} else if (IsXnu()) {
isfast = false;
res = sys_clock_gettime_xnu;
} else if (IsWindows()) {
isfast = true;
res = sys_clock_gettime_nt;
} else {
isfast = false;
res = sys_clock_gettime;
}
return result;
if (opt_out_isfast) {
*opt_out_isfast = isfast;
}
return res;
}
const void *const __clock_gettime_ctor[] initarray = {
__get_clock_gettime,
};
hidden int __clock_gettime_init(int clockid, struct timespec *ts) {
clock_gettime_f *gettime;
__clock_gettime = gettime = __get_clock_gettime(0);
return gettime(clockid, ts);
}

View file

@ -0,0 +1,15 @@
#ifndef COSMOPOLITAN_LIBC_CALLS_CLOCK_GETTIME_H_
#define COSMOPOLITAN_LIBC_CALLS_CLOCK_GETTIME_H_
#include "libc/calls/struct/timespec.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
typedef int clock_gettime_f(int, struct timespec *);
extern clock_gettime_f *__clock_gettime;
hidden clock_gettime_f __clock_gettime_init;
hidden clock_gettime_f *__get_clock_gettime(bool *);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_CALLS_CLOCK_GETTIME_H_ */

View file

@ -59,7 +59,7 @@ int gettimeofday(struct timeval *tv, struct timezone *tz) {
static textstartup void __gettimeofday_init(void) {
void *vdso;
if ((vdso = __vdsofunc("__vdso_gettimeofday"))) {
if ((vdso = __vdsosym("LINUX_2.6", "__vdso_gettimeofday"))) {
__gettimeofday = vdso;
}
}

View file

@ -118,6 +118,7 @@ i32 __sys_sigprocmask(i32, const sigset *, sigset *, u64) hidden;
i32 __sys_utimensat(i32, const char *, const struct timespec *, i32) hidden;
i32 __sys_wait4(i32, i32 *, i32, struct rusage *) hidden;
i32 sys_clock_gettime(i32, struct timespec *) hidden;
i32 sys_clock_gettime_xnu(i32, struct timespec *) hidden;
i32 sys_fstat(i32, struct stat *) hidden;
i32 sys_fstatat(i32, const char *, struct stat *, i32) hidden;
i32 sys_futimes(i32, const struct timeval *) hidden;

View file

@ -83,7 +83,7 @@ noasan textreal uint64_t *__get_virtual(struct mman *mm, uint64_t *t,
/**
* Sorts, rounds, and filters BIOS memory map.
*/
static noasan texthead void __normalize_e820(struct mman *mm) {
static noasan textreal void __normalize_e820(struct mman *mm) {
uint64_t a, b;
uint64_t x, y;
unsigned i, j, n;
@ -113,7 +113,7 @@ static noasan texthead void __normalize_e820(struct mman *mm) {
/**
* Identity maps all usable physical memory to its negative address.
*/
static noasan texthead void __invert_memory(struct mman *mm, uint64_t *pml4t) {
static noasan textreal void __invert_memory(struct mman *mm, uint64_t *pml4t) {
uint64_t i, j, *m, p, pe;
for (i = 0; i < mm->e820n; ++i) {
for (p = mm->e820[i].addr, pe = mm->e820[i].addr + mm->e820[i].size;
@ -126,7 +126,7 @@ static noasan texthead void __invert_memory(struct mman *mm, uint64_t *pml4t) {
}
}
noasan texthead void __setup_mman(struct mman *mm, uint64_t *pml4t) {
noasan textreal void __setup_mman(struct mman *mm, uint64_t *pml4t) {
__normalize_e820(mm);
__invert_memory(mm, pml4t);
}

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/asan.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/state.internal.h"
@ -31,7 +32,8 @@
noinstrument int nanosleep(const struct timespec *req, struct timespec *rem) {
int rc;
char buf[2][45];
if (!req) {
if (!req || (IsAsan() && (!__asan_is_valid_timespec(req) ||
(rem && !__asan_is_valid_timespec(rem))))) {
rc = efault();
} else if (req->tv_sec < 0 ||
!(0 <= req->tv_nsec && req->tv_nsec <= 999999999)) {

View file

@ -20,6 +20,7 @@
#include "libc/bits/initializer.internal.h"
#include "libc/bits/safemacros.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/clock_gettime.h"
#include "libc/calls/internal.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/strace.internal.h"
@ -32,10 +33,11 @@
#include "libc/sysv/consts/clock.h"
#include "libc/time/time.h"
static clock_gettime_f *__gettime;
static struct Now {
uint64_t k0;
long double r0, cpn;
typeof(sys_clock_gettime) *clock_gettime;
} g_now;
static long double GetTimeSample(void) {
@ -90,7 +92,7 @@ static long double nowl_art(void) {
static long double nowl_vdso(void) {
long double secs;
struct timespec tv;
g_now.clock_gettime(CLOCK_REALTIME, &tv);
__gettime(CLOCK_REALTIME, &tv);
secs = tv.tv_nsec;
secs *= 1 / 1e9L;
secs += tv.tv_sec;
@ -98,9 +100,10 @@ static long double nowl_vdso(void) {
}
long double nowl_setup(void) {
bool isfast;
uint64_t ticks;
if (0 && (g_now.clock_gettime = __get_clock_gettime())) {
// TODO(jart): Re-enable this.
__gettime = __get_clock_gettime(&isfast);
if (isfast) {
nowl = nowl_vdso;
} else if (X86_HAVE(INVTSC)) {
RefreshTime();

View file

@ -17,33 +17,44 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/ioctl.h"
#include "libc/calls/termios.h"
#include "libc/fmt/itoa.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/pty.h"
#include "libc/sysv/consts/termios.h"
#include "libc/sysv/errfuns.h"
/**
* Opens new pseudo teletypewriter.
*
* @param ilduce receives controlling tty rw fd on success
* @param aworker receives subordinate tty rw fd on success
* @param termp may be passed to tune a century of legacy behaviors
* @param winp may be passed to set terminal display dimensions
* @param mfd receives controlling tty rw fd on success
* @param sfd receives subordinate tty rw fd on success
* @param tio may be passed to tune a century of legacy behaviors
* @param wsz may be passed to set terminal display dimensions
* @params flags is usually O_RDWR|O_NOCTTY
* @return file descriptor, or -1 w/ errno
* @return 0 on success, or -1 w/ errno
*/
int openpty(int *ilduce, int *aworker, char *name, const struct termios *termp,
const struct winsize *winp) {
return enosys();
/* TODO(jart) */
/* int fd, flags; */
/* flags = O_RDWR | O_NOCTTY; */
/* if ((fd = posix_openpt(flags)) != -1) { */
/* if (ioctl(m, TIOCSPTLCK, &n) || ioctl(m, TIOCGPTN, &n)) { */
/* } else { */
/* close(fd); */
/* } */
/* } else { */
/* return -1; */
/* } */
int openpty(int *mfd, int *sfd, char *name, const struct termios *tio,
const struct winsize *wsz) {
int m, s, n;
char buf[20];
if ((m = open("/dev/ptmx", O_RDWR | O_NOCTTY)) != -1) {
n = 0;
if (!ioctl(m, TIOCSPTLCK, &n) || !ioctl(m, TIOCGPTN, &n)) {
if (!name) name = buf;
name[0] = '/', name[1] = 'd', name[2] = 'e', name[3] = 'v';
name[4] = '/', name[5] = 'p', name[6] = 't', name[7] = 's';
name[8] = '/', FormatInt32(name + 9, n);
if ((s = open(name, O_RDWR | O_NOCTTY)) != -1) {
if (tio) ioctl(s, TCSETS, tio);
if (wsz) ioctl(s, TIOCSWINSZ, wsz);
*mfd = m;
*sfd = s;
return 0;
}
}
close(m);
}
return -1;
}

View file

@ -12,8 +12,7 @@ int __notziposat(int, const char *);
int gethostname_bsd(char *, size_t) hidden;
int gethostname_linux(char *, size_t) hidden;
int gethostname_nt(char *, size_t, int) hidden;
void *__get_clock_gettime(void) hidden;
void *__vdsofunc(const char *) hidden;
void *__vdsosym(const char *, const char *) hidden;
void __onfork(void) hidden;
void __restore_rt() hidden;
void __restore_rt_netbsd(void) hidden;

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/bits/weaken.h"
#include "libc/calls/asan.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/strace.internal.h"
@ -39,7 +40,8 @@ int utimensat(int dirfd, const char *path, const struct timespec ts[2],
int rc;
char buf[12];
if (IsAsan() && (!__asan_is_valid(path, 1) ||
(ts && !__asan_is_valid(ts, sizeof(struct timespec) * 2)))) {
(ts && (!__asan_is_valid_timespec(ts + 0) ||
!__asan_is_valid_timespec(ts + 1))))) {
rc = efault();
} else if (weaken(__zipos_notat) && (rc = __zipos_notat(dirfd, path)) == -1) {
STRACE("zipos mkdirat not supported yet");

View file

@ -18,76 +18,136 @@
*/
#include "libc/bits/bits.h"
#include "libc/calls/calls.h"
#include "libc/calls/strace.internal.h"
#include "libc/elf/scalar.h"
#include "libc/elf/struct/ehdr.h"
#include "libc/elf/struct/phdr.h"
#include "libc/elf/struct/shdr.h"
#include "libc/elf/struct/sym.h"
#include "libc/log/libfatal.internal.h"
#include "libc/elf/struct/verdaux.h"
#include "libc/elf/struct/verdef.h"
#include "libc/intrin/kprintf.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/consts/auxv.h"
#define LAZY_RHEL7_RELOCATION 0xfffff
#define GetStr(tab, rva) ((char *)(tab) + (rva))
#define GetSection(e, s) ((void *)((intptr_t)(e) + (size_t)(s)->sh_offset))
#define GetShstrtab(e) GetSection(e, GetShdr(e, (e)->e_shstrndx))
#define GetSectionName(e, s) GetStr(GetShstrtab(e), (s)->sh_name)
#define GetPhdr(e, i) \
((Elf64_Phdr *)((intptr_t)(e) + (e)->e_phoff + \
(size_t)(e)->e_phentsize * (i)))
#define GetShdr(e, i) \
((Elf64_Shdr *)((intptr_t)(e) + (e)->e_shoff + \
(size_t)(e)->e_shentsize * (i)))
static char *GetDynamicStringTable(Elf64_Ehdr *e, size_t *n) {
char *name;
Elf64_Half i;
Elf64_Shdr *shdr;
for (i = 0; i < e->e_shnum; ++i) {
shdr = GetShdr(e, i);
name = GetSectionName(e, GetShdr(e, i));
if (shdr->sh_type == SHT_STRTAB) {
name = GetSectionName(e, GetShdr(e, i));
if (name && READ64LE(name) == READ64LE(".dynstr")) {
if (n) *n = shdr->sh_size;
return GetSection(e, shdr);
}
}
}
return 0;
static inline int CompareStrings(const char *l, const char *r) {
size_t i = 0;
while (l[i] == r[i] && r[i]) ++i;
return (l[i] & 255) - (r[i] & 255);
}
static Elf64_Sym *GetDynamicSymbolTable(Elf64_Ehdr *e, Elf64_Xword *n) {
Elf64_Half i;
Elf64_Shdr *shdr;
for (i = e->e_shnum; i > 0; --i) {
shdr = GetShdr(e, i - 1);
if (shdr->sh_type == SHT_DYNSYM) {
if (shdr->sh_entsize != sizeof(Elf64_Sym)) continue;
if (n) *n = shdr->sh_size / shdr->sh_entsize;
return GetSection(e, shdr);
static inline int CheckDsoSymbolVersion(Elf64_Verdef *vd, int sym,
const char *name, char *strtab) {
Elf64_Verdaux *aux;
for (;; vd = (Elf64_Verdef *)((char *)vd + vd->vd_next)) {
if (!(vd->vd_flags & VER_FLG_BASE) &&
(vd->vd_ndx & 0x7fff) == (sym & 0x7fff)) {
aux = (Elf64_Verdaux *)((char *)vd + vd->vd_aux);
return !CompareStrings(name, strtab + aux->vda_name);
}
if (!vd->vd_next) {
return 0;
}
}
return 0;
}
/**
* Returns Linux Kernel Virtual Dynamic Shared Object function address.
* Returns address of vDSO function.
*/
void *__vdsofunc(const char *name) {
size_t m;
char *names;
void *__vdsosym(const char *version, const char *name) {
void *p;
size_t i;
Elf64_Ehdr *ehdr;
Elf64_Xword i, n;
Elf64_Sym *symtab, *sym;
if ((ehdr = (Elf64_Ehdr *)getauxval(AT_SYSINFO_EHDR)) &&
(names = GetDynamicStringTable(ehdr, &m)) &&
(symtab = GetDynamicSymbolTable(ehdr, &n))) {
for (i = 0; i < n; ++i) {
if (!__strcmp(names + symtab[i].st_name, name)) {
return (char *)ehdr + (symtab[i].st_value & LAZY_RHEL7_RELOCATION);
}
Elf64_Phdr *phdr;
char *strtab = 0;
size_t *dyn, base;
unsigned long *ap;
Elf64_Sym *symtab = 0;
uint16_t *versym = 0;
Elf_Symndx *hashtab = 0;
Elf64_Verdef *verdef = 0;
for (ehdr = 0, ap = __auxv; ap[0]; ap += 2) {
if (ap[0] == AT_SYSINFO_EHDR) {
ehdr = (void *)ap[1];
break;
}
}
if (!ehdr || READ32LE(ehdr->e_ident) != READ32LE("\177ELF")) {
KERNTRACE("__vdsosym() → AT_SYSINFO_EHDR ELF not found");
return 0;
}
phdr = (void *)((char *)ehdr + ehdr->e_phoff);
for (base = -1, dyn = 0, i = 0; i < ehdr->e_phnum;
i++, phdr = (void *)((char *)phdr + ehdr->e_phentsize)) {
switch (phdr->p_type) {
case PT_LOAD:
// modern linux uses the base address zero, but elders
// e.g. rhel7 uses the base address 0xffffffffff700000
base = (size_t)ehdr + phdr->p_offset - phdr->p_vaddr;
break;
case PT_DYNAMIC:
dyn = (void *)((char *)ehdr + phdr->p_offset);
break;
default:
break;
}
}
if (!dyn || base == -1) {
KERNTRACE("__vdsosym() → missing program headers");
return 0;
}
for (i = 0; dyn[i]; i += 2) {
p = (void *)(base + dyn[i + 1]);
switch (dyn[i]) {
case DT_STRTAB:
strtab = p;
break;
case DT_SYMTAB:
symtab = p;
break;
case DT_HASH:
hashtab = p;
break;
case DT_VERSYM:
versym = p;
break;
case DT_VERDEF:
verdef = p;
break;
}
}
if (!strtab || !symtab || !hashtab) {
KERNTRACE("__vdsosym() → tables not found");
return 0;
}
if (!verdef) {
versym = 0;
}
for (i = 0; i < hashtab[1]; i++) {
if (ELF64_ST_TYPE(symtab[i].st_info) != STT_FUNC &&
ELF64_ST_TYPE(symtab[i].st_info) != STT_OBJECT &&
ELF64_ST_TYPE(symtab[i].st_info) != STT_NOTYPE) {
continue;
}
if (ELF64_ST_BIND(symtab[i].st_info) != STB_GLOBAL) {
continue;
}
if (!symtab[i].st_shndx) {
continue;
}
if (CompareStrings(name, strtab + symtab[i].st_name)) {
continue;
}
if (versym && !CheckDsoSymbolVersion(verdef, versym[i], version, strtab)) {
continue;
}
return (void *)(base + symtab[i].st_value);
}
KERNTRACE("__vdsosym() → symbol not found");
return 0;
}

View file

@ -17,12 +17,16 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/elf/elf.h"
#include "libc/intrin/kprintf.h"
#include "libc/runtime/runtime.h"
void CheckElfAddress(const Elf64_Ehdr *elf, size_t mapsize, intptr_t addr,
size_t addrsize) {
#if !(TRUSTWORTHY + ELF_TRUSTWORTHY + 0) || ELF_UNTRUSTWORTHY + 0
if (addr < (intptr_t)elf || addr + addrsize > (intptr_t)elf + mapsize) {
/* kprintf("%p-%p falls outside interval %p-%p", // */
/* addr, addr + addrsize, // */
/* elf, (char *)elf + mapsize); // */
abort();
}
#endif

View file

@ -2,15 +2,16 @@
#define COSMOPOLITAN_LIBC_ELF_SCALAR_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
#define Elf64_Half uint16_t
#define Elf64_Word uint32_t
#define Elf64_Sword int32_t
#define Elf64_Xword uint64_t
#define Elf64_Sxword int64_t
#define Elf64_Addr uint64_t
#define Elf64_Off uint64_t
#define Elf64_Addr uint64_t
#define Elf64_Half uint16_t
#define Elf64_Off uint64_t
#define Elf64_Section uint16_t
#define Elf64_Versym Elf64_Half
#define Elf64_Sword int32_t
#define Elf64_Sxword int64_t
#define Elf64_Versym Elf64_Half
#define Elf64_Word uint32_t
#define Elf64_Xword uint64_t
#define Elf_Symndx uint32_t
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_ELF_SCALAR_H_ */

View file

@ -1323,7 +1323,6 @@ syscon termios TIOCSETD 0x5423 0x8004741b 0x8004741b 0x8004741b 0x800474
syscon termios TIOCSIG 0x40045436 0x2000745f 0x2004745f 0x8004745f 0x8004745f 0 # boop
syscon termios TIOCSPGRP 0x5410 0x80047476 0x80047476 0x80047476 0x80047476 0 # boop
syscon termios TIOCSTI 0x5412 0x80017472 0x80017472 0 0 0 # boop
syscon termios TIOCGPTN 0x80045430 0 0x4004740f 0 0 0 # boop
syscon termios TIOCGSID 0x5429 0x40047463 0x40047463 0x40047463 0x40047463 0 # boop
syscon termios TABLDISC 0 0x3 0 0x3 0x3 0 # boop
syscon termios SLIPDISC 0 0x4 0x4 0x4 0x4 0 # boop
@ -1508,6 +1507,8 @@ syscon termios CSTOP 19 19 19 19 19 0 # unix consensus
# Pseudoteletypewriter Control
#
# group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary
syscon pty TIOCGPTN 0x80045430 0 0x4004740f 0 0 0 # boop
syscon pty TIOCSPTLCK 0x40045431 0 0 0 0 0 # boop
syscon pty TIOCPKT 0x5420 0x80047470 0x80047470 0x80047470 0x80047470 -1 # boop
syscon pty TIOCPKT_DATA 0 0 0 0 0 0 # consensus
syscon pty TIOCPKT_FLUSHREAD 1 1 1 1 1 1 # unix consensus
@ -1517,7 +1518,6 @@ syscon pty TIOCPKT_START 8 8 8 8 8 8 # unix consensus
syscon pty TIOCPKT_NOSTOP 16 16 16 16 16 16 # unix consensus
syscon pty TIOCPKT_DOSTOP 32 32 32 32 32 32 # unix consensus
syscon pty TIOCPKT_IOCTL 64 64 64 64 64 64 # unix consensus
syscon pty TIOCSPTLCK 0x40045431 0 0 0 0 -1 # boop
syscon pty PTMGET 0 0 0 0x40287401 0x40287401 -1 # for /dev/ptm
# Modem Control

View file

@ -154,11 +154,14 @@ intptr_t erfkill(void) relegated;
intptr_t ehwpoison(void) relegated;
#if defined(__MNO_RED_ZONE__) && defined(__GNUC__) && !defined(__STRICT_ANSI__)
#define __ERRFUN(FUNC) \
({ \
intptr_t NegOne; \
asm("call\t" FUNC : "=a"(NegOne), "=m"(errno)); \
NegOne; \
#define __ERRFUN(FUNC) \
({ \
intptr_t NegOne; \
asm volatile("call\t" FUNC \
: "=a"(NegOne) \
: /* no outputs */ \
: "rcx", "memory"); \
NegOne; \
})
#define einval() __ERRFUN("einval")
#define eperm() __ERRFUN("eperm")

View file

@ -16,14 +16,40 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/clock_gettime.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/timespec.h"
#include "libc/sysv/consts/clock.h"
#include "libc/calls/struct/timeval.h"
#include "libc/calls/syscall_support-sysv.internal.h"
#include "libc/nexgen32e/rdtsc.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/consts/auxv.h"
#include "libc/testlib/ezbench.h"
#include "libc/testlib/testlib.h"
#include "libc/time/time.h"
TEST(clock_gettime, test) {
bool isfast;
struct timespec ts = {0};
ASSERT_EQ(0, clock_gettime(0, &ts));
ASSERT_NE(0, ts.tv_sec);
ASSERT_NE(0, ts.tv_nsec);
if (__is_linux_2_6_23()) {
ASSERT_GT((intptr_t)__get_clock_gettime(&isfast),
getauxval(AT_SYSINFO_EHDR));
ASSERT_TRUE(isfast);
}
}
BENCH(clock_gettime, bench) {
struct timeval tv;
struct timespec ts;
gettimeofday(&tv, 0); // trigger init
clock_gettime(0, &ts); // trigger init
EZBENCH2("nowl", donothing, nowl());
EZBENCH2("clock_gettime", donothing, clock_gettime(CLOCK_REALTIME, &ts));
EZBENCH2("rdtsc", donothing, rdtsc());
EZBENCH2("gettimeofday", donothing, gettimeofday(&tv, 0));
EZBENCH2("clock_gettime", donothing, clock_gettime(0, &ts));
EZBENCH2("__clock_gettime", donothing, __clock_gettime(0, &ts));
EZBENCH2("sys_clock_gettime", donothing, sys_clock_gettime(0, &ts));
}

View file

@ -20,6 +20,7 @@
#include "libc/calls/sigbits.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/errno.h"
#include "libc/intrin/kprintf.h"
#include "libc/mem/io.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/consts/o.h"

View file

@ -51,7 +51,7 @@ o/$(MODE)/third_party/bzip2/bzip2.com.dbg: \
o/$(MODE)/third_party/bzip2/bzip2.o \
o/$(MODE)/third_party/bzip2/bzip2.a.pkg \
$(CRT) \
$(APE)
$(APE_NO_MODIFY_SELF)
@$(APELINK)
o/$(MODE)/third_party/bzip2/bzip2recover.com.dbg: \
@ -59,7 +59,7 @@ o/$(MODE)/third_party/bzip2/bzip2recover.com.dbg: \
o/$(MODE)/third_party/bzip2/bzip2recover.o \
o/$(MODE)/third_party/bzip2/bzip2.a.pkg \
$(CRT) \
$(APE)
$(APE_NO_MODIFY_SELF)
@$(APELINK)
$(THIRD_PARTY_BZIP2_A_OBJS): \

View file

@ -59,7 +59,7 @@ o/$(MODE)/third_party/lua/lua.com.dbg: \
$(THIRD_PARTY_LUA_A).pkg \
o/$(MODE)/third_party/lua/lua.main.o \
$(CRT) \
$(APE)
$(APE_NO_MODIFY_SELF)
@$(APELINK)
o/$(MODE)/third_party/lua/luac.com.dbg: \
@ -68,7 +68,7 @@ o/$(MODE)/third_party/lua/luac.com.dbg: \
$(THIRD_PARTY_LUA_A).pkg \
o/$(MODE)/third_party/lua/luac.main.o \
$(CRT) \
$(APE)
$(APE_NO_MODIFY_SELF)
@$(APELINK)
o/$(MODE)/third_party/lua/lua.com: \

View file

@ -56,7 +56,7 @@ o/$(MODE)/third_party/lz4cli/lz4cli.com.dbg: \
$(THIRD_PARTY_LZ4CLI_DEPS) \
$(THIRD_PARTY_LZ4CLI_OBJS) \
$(CRT) \
$(APE)
$(APE_NO_MODIFY_SELF)
@$(APELINK)
$(THIRD_PARTY_LZ4CLI_OBJS): \

View file

@ -512,6 +512,7 @@ int mbedtls_ecp_check_budget( const mbedtls_ecp_group *grp,
* SECP384R1 192 IANA, NIST, FRANCE, GERMANY, NSA
* X25519 112-128 IANA
* X448 224 IANA
* SECP256K1 128 BITCOIN
* BP384R1 GERMANY
* SECP521R1 FRANCE
* GC512A RUSSIA

View file

@ -145,7 +145,7 @@ o/$(MODE)/third_party/quickjs/qjs.com.dbg: \
o/$(MODE)/third_party/quickjs/repl.o \
o/$(MODE)/third_party/quickjs/qjscalc.o \
$(CRT) \
$(APE)
$(APE_NO_MODIFY_SELF)
@$(APELINK)
o/$(MODE)/third_party/quickjs/qjs.com: \

View file

@ -157,7 +157,8 @@ static void printelfsectionheader(int i, char *shstrtab) {
static void printelfsectionheaders(void) {
Elf64_Half i;
char *shstrtab = GetElfSectionNameStringTable(elf, st->st_size);
char *shstrtab;
shstrtab = GetElfSectionNameStringTable(elf, st->st_size);
if (shstrtab) {
printf("\n");
printf("\t.org\t%#x\n", elf->e_shoff);
@ -341,7 +342,6 @@ int main(int argc, char *argv[]) {
fprintf(stderr, "error: not an elf executable: %'s\n", path);
exit(1);
}
elf = (Elf64_Ehdr *)getauxval(AT_SYSINFO_EHDR);
startfile();
printelfehdr();
printelfsegmentheaders();

166
tool/viz/vdsosyms.c Normal file
View file

@ -0,0 +1,166 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/strace.internal.h"
#include "libc/elf/def.h"
#include "libc/elf/scalar.h"
#include "libc/elf/struct/ehdr.h"
#include "libc/elf/struct/phdr.h"
#include "libc/elf/struct/sym.h"
#include "libc/elf/struct/verdaux.h"
#include "libc/elf/struct/verdef.h"
#include "libc/intrin/kprintf.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/consts/auxv.h"
static inline void PrintDsoSymbolVersions(Elf64_Verdef *vd, int sym,
char *strtab) {
Elf64_Verdaux *aux;
for (;; vd = (Elf64_Verdef *)((char *)vd + vd->vd_next)) {
if (!(vd->vd_flags & VER_FLG_BASE) &&
(vd->vd_ndx & 0x7fff) == (sym & 0x7fff)) {
aux = (Elf64_Verdaux *)((char *)vd + vd->vd_aux);
kprintf(" %s", strtab + aux->vda_name);
}
if (!vd->vd_next) {
break;
}
}
}
int PrintVdsoSymbols(void) {
void *p;
size_t i;
Elf64_Ehdr *ehdr;
Elf64_Phdr *phdr;
char *strtab = 0;
size_t *dyn, base;
unsigned long *ap;
Elf64_Sym *symtab = 0;
uint16_t *versym = 0;
Elf_Symndx *hashtab = 0;
Elf64_Verdef *verdef = 0;
const char *typename, *bindname;
for (ehdr = 0, ap = __auxv; ap[0]; ap += 2) {
if (ap[0] == AT_SYSINFO_EHDR) {
ehdr = (void *)ap[1];
break;
}
}
if (!ehdr) {
kprintf("error: AT_SYSINFO_EHDR not found\n");
return 1;
}
phdr = (void *)((char *)ehdr + ehdr->e_phoff);
for (base = -1, dyn = 0, i = 0; i < ehdr->e_phnum;
i++, phdr = (void *)((char *)phdr + ehdr->e_phentsize)) {
switch (phdr->p_type) {
case PT_LOAD:
// modern linux uses the base address zero, but elders
// e.g. rhel7 uses the base address 0xffffffffff700000
base = (size_t)ehdr + phdr->p_offset - phdr->p_vaddr;
break;
case PT_DYNAMIC:
dyn = (void *)((char *)ehdr + phdr->p_offset);
break;
default:
break;
}
}
if (!dyn || base == -1) {
kprintf("error: missing program headers\n");
return 2;
}
for (i = 0; dyn[i]; i += 2) {
p = (void *)(base + dyn[i + 1]);
switch (dyn[i]) {
case DT_STRTAB:
strtab = p;
break;
case DT_SYMTAB:
symtab = p;
break;
case DT_HASH:
hashtab = p;
break;
case DT_VERSYM:
versym = p;
break;
case DT_VERDEF:
verdef = p;
break;
}
}
if (!verdef) {
versym = 0;
}
if (!strtab || !symtab || !hashtab) {
kprintf("error: strtab/symtab/hashtab not found\n");
return 3;
}
for (i = 0; i < hashtab[1]; i++) {
if (!symtab[i].st_shndx) {
continue;
}
switch (ELF64_ST_BIND(symtab[i].st_info)) {
case STB_LOCAL:
bindname = "locl";
break;
case STB_GLOBAL:
bindname = "glob";
break;
case STB_WEAK:
bindname = "weak";
break;
default:
bindname = "????";
break;
}
switch (ELF64_ST_TYPE(symtab[i].st_info)) {
case STT_FUNC:
typename = "func";
break;
case STT_OBJECT:
typename = " obj";
break;
case STT_NOTYPE:
typename = "none";
break;
default:
typename = "????";
break;
}
kprintf("%s %s %-40s", bindname, typename, strtab + symtab[i].st_name);
PrintDsoSymbolVersions(verdef, versym[i], strtab);
kprintf("\n");
}
return 0;
}
int main(int argc, char *argv[]) {
return PrintVdsoSymbols();
}