Make improvements

- Add GetCpuCount() API to redbean
- Add unix.gmtime() API to redbean
- Add unix.readlink() API to redbean
- Add unix.localtime() API to redbean
- Perfect the new redbean UNIX module APIs
- Integrate with Linux clock_gettime() vDSO
- Run Lua garbage collector when malloc() fails
- Fix another regression quirk with linenoise repl
- Fix GetProgramExecutableName() for systemwide installs
- Fix a build flake with test/libc/mem/test.mk SRCS list
This commit is contained in:
Justine Tunney 2022-04-25 21:16:05 -07:00
parent 860ea18a87
commit d57b81aac7
51 changed files with 3096 additions and 1395 deletions

View file

@ -65,6 +65,7 @@ $(LIBC_CALLS_A).pkg: \
$(LIBC_CALLS_A_OBJS) \
$(foreach x,$(LIBC_CALLS_A_DIRECTDEPS),$($(x)_A).pkg)
o/$(MODE)/libc/calls/vdsofunc.greg.o \
o/$(MODE)/libc/calls/directmap.o \
o/$(MODE)/libc/calls/directmap-nt.o \
o/$(MODE)/libc/calls/raise.o: \

View file

@ -26,6 +26,8 @@
#include "libc/nt/synchronization.h"
#include "libc/sysv/errfuns.h"
static typeof(sys_clock_gettime) *__clock_gettime = sys_clock_gettime;
/**
* Returns nanosecond time.
*
@ -52,7 +54,7 @@ noinstrument int clock_gettime(int clockid, struct timespec *ts) {
rc = einval();
} else if (!IsWindows()) {
e = errno;
if ((rc = sys_clock_gettime(clockid, ts))) {
if ((rc = __clock_gettime(clockid, ts))) {
errno = e;
ad = sys_gettimeofday((struct timeval *)ts, NULL, NULL);
assert(ad.ax != -1);
@ -72,3 +74,23 @@ noinstrument int clock_gettime(int clockid, struct timespec *ts) {
}
return rc;
}
/**
* Returns fast system clock_gettime() if it exists.
*/
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;
}
return result;
}
const void *const __clock_gettime_ctor[] initarray = {
__get_clock_gettime,
};

View file

@ -33,7 +33,7 @@
* file is a relative path, then file is opened relative to dirfd
* @param path is a filename or directory
* @param mode can be R_OK, W_OK, X_OK, F_OK
* @param flags should be 0
* @param flags can have AT_EACCESS, AT_SYMLINK_NOFOLLOW
* @return 0 if ok, or -1 and sets errno
* @asyncsignalsafe
*/

View file

@ -106,7 +106,7 @@ textwindows int sys_fstat_nt(int64_t handle, struct stat *st) {
st->st_size = (uint64_t)wst.nFileSizeHigh << 32 | wst.nFileSizeLow;
st->st_blksize = PAGESIZE;
st->st_dev = wst.dwVolumeSerialNumber;
st->st_rdev = wst.dwVolumeSerialNumber;
st->st_rdev = 0;
st->st_ino = (uint64_t)wst.nFileIndexHigh << 32 | wst.nFileIndexLow;
st->st_nlink = wst.nNumberOfLinks;
if (S_ISLNK(st->st_mode)) {

View file

@ -0,0 +1,121 @@
/*-*- 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
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/calls.h"
#include "libc/calls/internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/macros.internal.h"
#include "libc/nt/runtime.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/consts/ok.h"
#define SIZE 1024
#define CTL_KERN 1
#define KERN_PROC 14
#define KERN_PROC_PATHNAME_FREEBSD 12
#define KERN_PROC_PATHNAME_NETBSD 5
char program_executable_name[PATH_MAX + 1];
static inline void GetProgramExecutableNameImpl(char *p, char *e) {
char *q;
ssize_t rc;
size_t i, n;
union {
int cmd[4];
char16_t path16[PATH_MAX + 1];
} u;
if (IsWindows()) {
n = GetModuleFileName(0, u.path16, ARRAYLEN(u.path16));
for (i = 0; i < n; ++i) {
if (u.path16[i] == '\\') {
u.path16[i] = '/';
}
}
if (isalpha(u.path16[0]) && u.path16[1] == ':' && u.path16[2] == '/') {
p[0] = '/';
p[1] = '/';
p[2] = '?';
p[3] = '/';
p += 4;
}
tprecode16to8(p, e - p, u.path16);
return;
}
if (__argc && (q = __argv[0]) && !sys_faccessat(AT_FDCWD, q, F_OK, 0)) {
if (*q != '/') {
if (q[0] == '.' && q[1] == '/') {
q += 2;
}
if (getcwd(p, e - p)) {
while (*p) ++p;
*p++ = '/';
}
}
for (i = 0; *q && p + 1 < e; ++p, ++q) {
*p = *q;
}
*p = 0;
return;
}
if ((rc = sys_readlinkat(AT_FDCWD, "/proc/self/exe", p, e - p - 1)) > 0 ||
(rc = sys_readlinkat(AT_FDCWD, "/proc/curproc/file", p, e - p - 1)) > 0) {
p[rc] = 0;
return;
}
if (IsFreebsd() || IsNetbsd()) {
u.cmd[0] = CTL_KERN;
u.cmd[1] = KERN_PROC;
if (IsFreebsd()) {
u.cmd[2] = KERN_PROC_PATHNAME_FREEBSD;
} else {
u.cmd[2] = KERN_PROC_PATHNAME_NETBSD;
}
u.cmd[3] = -1; // current process
n = e - p;
if (sysctl(u.cmd, ARRAYLEN(u.cmd), p, &n, 0, 0) != -1) {
return;
}
}
}
/**
* Returns absolute path of executable.
*/
char *GetProgramExecutableName(void) {
int e;
static bool once;
if (!once) {
e = errno;
GetProgramExecutableNameImpl(
program_executable_name,
program_executable_name + sizeof(program_executable_name));
errno = e;
}
return program_executable_name;
}
const void *const GetProgramExecutableNameCtor[] initarray = {
GetProgramExecutableName,
};

View file

@ -25,6 +25,8 @@
#include "libc/time/struct/timezone.h"
#include "libc/time/time.h"
static typeof(sys_gettimeofday) *__gettimeofday = sys_gettimeofday;
/**
* Returns system wall time in microseconds.
*
@ -40,7 +42,7 @@ int gettimeofday(struct timeval *tv, struct timezone *tz) {
return efault();
}
if (!IsWindows() && !IsMetal()) {
ad = sys_gettimeofday(tv, tz, NULL);
ad = __gettimeofday(tv, tz, NULL);
assert(ad.ax != -1);
if (SupportsXnu() && ad.ax && tv) {
tv->tv_sec = ad.ax;
@ -53,3 +55,14 @@ int gettimeofday(struct timeval *tv, struct timezone *tz) {
return sys_gettimeofday_nt(tv, tz);
}
}
static textstartup void __gettimeofday_init(void) {
void *vdso;
if ((vdso = __vdsofunc("__vdso_gettimeofday"))) {
__gettimeofday = vdso;
}
}
const void *const __gettimeofday_ctor[] initarray = {
__gettimeofday_init,
};

View file

@ -245,6 +245,8 @@ void sys_exit(int) hidden;
*/
void __onfork(void) hidden;
void *__vdsofunc(const char *) hidden;
void *__get_clock_gettime(void) hidden;
i32 __fixupnewfd(i32, i32) hidden;
void __restore_rt() hidden;
int sys_utimensat_xnu(int, const char *, const struct timespec *, int) hidden;

View file

@ -31,9 +31,9 @@
#include "libc/time/time.h"
static struct Now {
bool once;
uint64_t k0;
long double r0, cpn;
typeof(sys_clock_gettime) *clock_gettime;
} g_now;
static long double GetTimeSample(void) {
@ -73,22 +73,39 @@ void RefreshTime(void) {
now.cpn = MeasureNanosPerCycle();
now.r0 = dtime(CLOCK_REALTIME);
now.k0 = rdtsc();
now.once = true;
memcpy(&g_now, &now, sizeof(now));
}
long double ConvertTicksToNanos(uint64_t ticks) {
if (!g_now.once) RefreshTime();
return ticks * g_now.cpn; /* pico scale */
}
long double nowl_sys(void) {
static long double nowl_sys(void) {
return dtime(CLOCK_REALTIME);
}
long double nowl_art(void) {
uint64_t ticks;
if (!g_now.once) RefreshTime();
ticks = unsignedsubtract(rdtsc(), g_now.k0);
static long double nowl_art(void) {
uint64_t ticks = rdtsc() - g_now.k0;
return g_now.r0 + (1 / 1e9L * (ticks * g_now.cpn));
}
static long double nowl_vdso(void) {
long double secs;
struct timespec tv;
g_now.clock_gettime(CLOCK_REALTIME, &tv);
secs = tv.tv_nsec;
secs *= 1 / 1e9L;
secs += tv.tv_sec;
return secs;
}
long double nowl_setup(void) {
uint64_t ticks;
if ((g_now.clock_gettime = __get_clock_gettime())) {
nowl = nowl_vdso;
} else if (X86_HAVE(INVTSC)) {
RefreshTime();
nowl = nowl_art;
} else {
nowl = nowl_sys;
}
return nowl();
}
long double (*nowl)(void) = nowl_setup;

View file

@ -1,5 +1,5 @@
/*-*- 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
/*-*- 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 2020 Justine Alexandra Roberts Tunney
@ -16,22 +16,68 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/nexgen32e/x86feature.h"
#include "libc/bits/bits.h"
#include "libc/bits/initializer.internal.h"
#include "libc/bits/safemacros.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/strace.internal.h"
#include "libc/dce.h"
#include "libc/macros.internal.h"
#include "libc/nexgen32e/rdtsc.h"
#include "libc/nexgen32e/x86feature.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/clock.h"
#include "libc/time/time.h"
// Returns timestamp without needing system calls.
//
// @return seconds since unix epoch in %st0
// @note uses microsecond scale fallback on k8 or vm
.initbss 202,_init_nowl
nowl: .quad 0
.endobj nowl,globl
.previous
static struct Now {
bool once;
uint64_t k0;
long double r0, cpn;
} g_now;
.init.start 202,_init_nowl
ezlea nowl_sys,ax
ezlea nowl_art,cx
testb X86_HAVE(INVTSC)+kCpuids(%rip)
cmovnz %rcx,%rax
stosq
.init.end 202,_init_nowl
static long double GetTimeSample(void) {
uint64_t tick1, tick2;
long double time1, time2;
sched_yield();
time1 = dtime(CLOCK_REALTIME);
tick1 = rdtsc();
nanosleep(&(struct timespec){0, 1000000}, NULL);
time2 = dtime(CLOCK_REALTIME);
tick2 = rdtsc();
return (time2 - time1) * 1e9 / MAX(1, tick2 - tick1);
}
static long double MeasureNanosPerCycle(void) {
bool tc;
int i, n;
long double avg, samp;
tc = __time_critical;
__time_critical = true;
if (IsWindows()) {
n = 30;
} else {
n = 20;
}
for (avg = 1.0L, i = 1; i < n; ++i) {
samp = GetTimeSample();
avg += (samp - avg) / i;
}
__time_critical = tc;
STRACE("MeasureNanosPerCycle cpn*1000=%d", (long)(avg * 1000));
return avg;
}
static void Refresh(void) {
struct Now now;
now.cpn = MeasureNanosPerCycle();
now.r0 = dtime(CLOCK_REALTIME);
now.k0 = rdtsc();
now.once = true;
memcpy(&g_now, &now, sizeof(now));
}
long double ConvertTicksToNanos(uint64_t ticks) {
if (!g_now.once) Refresh();
return ticks * g_now.cpn; /* pico scale */
}

View file

@ -23,6 +23,7 @@
#include "libc/nt/enum/fileflagandattributes.h"
#include "libc/nt/ipc.h"
#include "libc/nt/runtime.h"
#include "libc/sysv/consts/limits.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/errfuns.h"
@ -42,9 +43,9 @@ textwindows int sys_pipe_nt(int pipefd[2], unsigned flags) {
} else {
mode = kNtPipeTypeMessage | kNtPipeReadmodeMessage;
}
if ((hin = CreateNamedPipe(pipename,
kNtPipeAccessInbound | kNtFileFlagOverlapped, mode,
1, 512, 512, 0, &kNtIsInheritable)) != -1) {
if ((hin = CreateNamedPipe(
pipename, kNtPipeAccessInbound | kNtFileFlagOverlapped, mode, 1,
PIPE_BUF, PIPE_BUF, 0, &kNtIsInheritable)) != -1) {
if ((hout = CreateFile(pipename, kNtGenericWrite, 0, &kNtIsInheritable,
kNtOpenExisting, kNtFileFlagOverlapped, 0)) != -1) {
g_fds.p[reader].kind = kFdFile;

View file

@ -1,148 +0,0 @@
/*-*- 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
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/assert.h"
#include "libc/bits/bits.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/strace.internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/spinlock.h"
#include "libc/log/libfatal.internal.h"
#include "libc/macros.internal.h"
#include "libc/mem/alloca.h"
#include "libc/nt/runtime.h"
#include "libc/runtime/runtime.h"
#include "libc/str/path.h"
#include "libc/str/str.h"
#include "libc/str/tpenc.h"
#include "libc/str/utf16.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/consts/auxv.h"
#include "libc/sysv/consts/ok.h"
#include "libc/sysv/consts/prot.h"
#define SIZE 1024
#define CTL_KERN 1
#define KERN_PROC 14
#define KERN_PROC_PATHNAME_FREEBSD 12
#define KERN_PROC_PATHNAME_NETBSD 5
char program_executable_name[SIZE];
static textwindows bool GetNtExePath(char exe[SIZE]) {
bool32 rc;
uint64_t w;
wint_t x, y;
uint32_t i, j;
char16_t p[PATH_MAX + 1];
p[0] = 0;
rc = GetModuleFileName(0, p, ARRAYLEN(p));
NTTRACE("GetModuleFileName(0, [%#hs]) → %hhhd", p, rc);
if (!rc) return false;
j = 0;
if (p[0] != '\\' || p[1] != '\\' || p[2] != '?' || p[3] != '\\') {
exe[j++] = '/';
exe[j++] = '/';
exe[j++] = '?';
exe[j++] = '/';
}
for (i = 0; (x = p[i++] & 0xffff);) {
if (!IsUcs2(x)) {
y = p[i++] & 0xffff;
x = MergeUtf16(x, y);
}
if (x == '\\') x = '/';
w = tpenc(x);
do {
exe[j] = w;
if (++j == SIZE) {
return false;
}
} while ((w >>= 8));
}
exe[j] = 0;
return true;
}
static void ReadProgramExecutableName(char exe[SIZE], char *argv0,
uintptr_t *auxv) {
int e;
size_t m;
ssize_t n;
int cmd[4];
char *p, *t;
e = errno;
if (!IsWindows() || !GetNtExePath(exe)) {
for (p = 0; *auxv; auxv += 2) {
if (*auxv == AT_EXECFN) {
p = (char *)auxv[1];
break;
}
}
n = 0;
if (!p) p = argv0;
if (p) {
if (!_isabspath(p)) {
if (getcwd(exe, SIZE - 1)) {
n = strlen(exe);
exe[n++] = '/';
}
}
for (; *p; ++p) {
if (n + 1 < SIZE) {
exe[n++] = *p;
}
}
}
exe[n] = 0;
}
errno = e;
}
/**
* Returns absolute path of executable.
*
* This variable is initialized automatically at startup. The path is
* basically `argv[0]` except some extra vetting is done to provide
* stronger assurance that the path can be counted upon to exist.
*
* For example, if your program is executed as a relative path and then
* your program calls `chdir()`, then `argv[0]` will be incorrect; but
* `program_executable_name` will work, because it prefixed `getcwd()`
* early in the initialization phase.
*
* @see GetInterpreterExecutableName()
* @see program_invocation_short_name
* @see program_invocation_name
*/
char *GetProgramExecutableName(void) {
static bool once;
if (!once) {
ReadProgramExecutableName(program_executable_name, __argv[0], __auxv);
once = true;
}
return program_executable_name;
}
// try our best to memoize it before a chdir() happens
const void *const program_executable_name_init_ctor[] initarray = {
GetProgramExecutableName,
};

View file

@ -22,13 +22,14 @@
/**
* Adds signal to set.
*
* @return true, false, or -1 w/ errno
* @return 0 on success, or -1 w/ errno
* @raises EINVAL if `1 sig NSIG` isn't the case
* @asyncsignalsafe
*/
int sigaddset(sigset_t *set, int sig) {
unsigned i = sig - 1;
if (i < sizeof(set->__bits) * 8) {
set->__bits[i >> 6] |= 1ull << (i & 63);
_Static_assert(sizeof(set->__bits[0]) * CHAR_BIT == 64, "");
if (1 <= sig && sig <= NSIG) {
set->__bits[(sig - 1) >> 6] |= 1ull << ((sig - 1) & 63);
return 0;
} else {
return einval();

View file

@ -23,12 +23,13 @@
* Removes signal from set.
*
* @return 0 on success, or -1 w/ errno
* @raises EINVAL if `1 sig NSIG` isn't the case
* @asyncsignalsafe
*/
int sigdelset(sigset_t *set, int sig) {
unsigned i = sig - 1;
if (i < sizeof(set->__bits) * 8) {
set->__bits[i >> 6] &= ~(1ull << (i & 63));
_Static_assert(sizeof(set->__bits[0]) * CHAR_BIT == 64, "");
if (1 <= sig && sig <= NSIG) {
set->__bits[(sig - 1) >> 6] &= ~(1ull << ((sig - 1) & 63));
return 0;
} else {
return einval();

View file

@ -22,14 +22,15 @@
/**
* Returns true if signal is member of set.
*
* @return true, false, or -1 w/ errno
* @return 1 if set, 0 if not set, or -1 w/ errno
* @raises EINVAL if `1 sig NSIG` isn't the case
* @asyncsignalsafe
* @vforksafe
*/
int sigismember(const sigset_t *set, int sig) {
unsigned i = sig - 1;
if (i < sizeof(set->__bits) * 8) {
return (set->__bits[i >> 6] >> (i & 63)) & 1;
_Static_assert(sizeof(set->__bits[0]) * CHAR_BIT == 64, "");
if (1 <= sig && sig <= NSIG) {
return !!(set->__bits[(sig - 1) >> 6] & (1ull << ((sig - 1) & 63)));
} else {
return einval();
}

View file

@ -10,7 +10,7 @@ struct stat { /* cosmo abi */
uint32_t st_mode; /* 24: octal file mask thing */
uint32_t st_uid; /* 28: user id of owner */
uint32_t st_gid; /* group id of owning group */
uint32_t st_flags; /* flags (bsd-only) */
uint32_t st_flags; /* nt/xnu/bsd-only */
uint64_t st_rdev; /* id of device if a special file */
int64_t st_size; /* bytes in file */
int64_t st_blksize; /* preferred chunking for underlying filesystem */
@ -19,7 +19,7 @@ struct stat { /* cosmo abi */
struct timespec st_mtim; /* modified time */
struct timespec st_ctim; /* complicated time */
struct timespec st_birthtim;
uint64_t st_gen;
uint64_t st_gen; /* xnu/bsd only */
};
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -0,0 +1,94 @@
/*-*- 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/bits/bits.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/elf/scalar.h"
#include "libc/elf/struct/ehdr.h"
#include "libc/elf/struct/shdr.h"
#include "libc/elf/struct/sym.h"
#include "libc/log/libfatal.internal.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 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);
}
}
return 0;
}
/**
* Returns Linux Kernel Virtual Dynamic Shared Object function address.
*/
void *__vdsofunc(const char *name) {
size_t m;
char *names;
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);
}
}
}
return 0;
}