mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-07 06:53:33 +00:00
Introduce dlopen() support
Every program built using Cosmopolitan is statically-linked. However there are some cases, e.g. GUIs and video drivers, where linking the host platform libraries is desirable. So what we do in such cases is launch a stub executable using the host platform's libc, and longjmp back into this executable. The stub executable passes back to us the platform-specific dlopen() implementation, which we shall then wrap. Here's the list of platforms that are supported so far: - x86-64 Linux w/ Glibc - x86-64 Linux w/ Musl Libc - x86-64 FreeBSD - x86-64 Windows - aarch64 Linux w/ Glibc - aarch64 MacOS What this means is your Cosmo programs can call foreign functions on your host operating system. However, it's important to note that any foreign library you link won't have the ability to call functions in your Cosmopolitan program. For example it's now technically possible that Lua can load a module, however that almost certainly won't work since the Lua module won't have access to Cosmo's Lua API. Kudos to @jacereda for figuring out how to do this.
This commit is contained in:
parent
1eb6484c9c
commit
5e8c928f1a
27 changed files with 753 additions and 53 deletions
1
Makefile
1
Makefile
|
@ -176,6 +176,7 @@ include libc/calls/calls.mk #─┐
|
|||
include libc/irq/irq.mk # ├──SYSTEMS RUNTIME
|
||||
include third_party/nsync/nsync.mk # │ You can issue system calls
|
||||
include libc/runtime/runtime.mk # │
|
||||
include libc/dlopen/dlopen.mk # │
|
||||
include third_party/double-conversion/dc.mk # │
|
||||
include libc/crt/crt.mk # │
|
||||
include third_party/dlmalloc/dlmalloc.mk #─┘
|
||||
|
|
12
ape/ape-m1.c
12
ape/ape-m1.c
|
@ -31,10 +31,11 @@
|
|||
#include <sys/uio.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#define pagesz 16384
|
||||
#define SYSLIB_MAGIC ('s' | 'l' << 8 | 'i' << 16 | 'b' << 24)
|
||||
#define SYSLIB_VERSION 5
|
||||
#define SYSLIB_VERSION 6
|
||||
|
||||
struct Syslib {
|
||||
int magic;
|
||||
|
@ -91,6 +92,11 @@ struct Syslib {
|
|||
long (*sem_trywait)(int *);
|
||||
long (*getrlimit)(int, struct rlimit *);
|
||||
long (*setrlimit)(int, const struct rlimit *);
|
||||
// v6 (2023-11-03)
|
||||
void *(*dlopen)(const char *, int);
|
||||
void *(*dlsym)(void *, const char *);
|
||||
int (*dlclose)(void *);
|
||||
char *(*dlerror)(void);
|
||||
};
|
||||
|
||||
#define ELFCLASS32 1
|
||||
|
@ -943,6 +949,10 @@ int main(int argc, char **argv, char **envp) {
|
|||
M->lib.sem_trywait = sys_sem_trywait;
|
||||
M->lib.getrlimit = sys_getrlimit;
|
||||
M->lib.setrlimit = sys_setrlimit;
|
||||
M->lib.dlopen = dlopen;
|
||||
M->lib.dlsym = dlsym;
|
||||
M->lib.dlclose = dlclose;
|
||||
M->lib.dlerror = dlerror;
|
||||
|
||||
/* getenv("_") is close enough to at_execfn */
|
||||
execfn = argc > 0 ? argv[0] : 0;
|
||||
|
|
55
examples/dlopen.c
Normal file
55
examples/dlopen.c
Normal file
|
@ -0,0 +1,55 @@
|
|||
#if 0
|
||||
/*─────────────────────────────────────────────────────────────────╗
|
||||
│ To the extent possible under law, Justine Tunney has waived │
|
||||
│ all copyright and related or neighboring rights to this file, │
|
||||
│ as it is written in the following disclaimers: │
|
||||
│ • http://unlicense.org/ │
|
||||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#endif
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/dlopen/dlfcn.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/nt/thunk/msabi.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
|
||||
/**
|
||||
* @fileoverview cosmopolitan dynamic runtime linking demo
|
||||
*
|
||||
* Our dlopen() interface currently supports:
|
||||
*
|
||||
* - x86-64 Linux w/ Glibc
|
||||
* - x86-64 Linux w/ Musl Libc
|
||||
* - x86-64 FreeBSD
|
||||
* - x86-64 Windows
|
||||
* - aarch64 Linux w/ Glibc
|
||||
* - aarch64 MacOS
|
||||
*
|
||||
*/
|
||||
|
||||
int main(int argc, char **argv, char **envp) {
|
||||
|
||||
// open the host system's zlib library
|
||||
void *libc = dlopen("libz.so", RTLD_LAZY);
|
||||
if (!libc) {
|
||||
tinyprint(2, dlerror(), "\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// load crc() function address
|
||||
unsigned (*crc32)(unsigned, void *, int) = dlsym(libc, "crc32");
|
||||
if (!crc32) {
|
||||
tinyprint(2, dlerror(), "\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// compute a checksum and print the result
|
||||
char ibuf[12];
|
||||
FormatInt32(ibuf, crc32(0, "hi", 2));
|
||||
tinyprint(1, "crc(hi) = ", ibuf, "\n", NULL);
|
||||
|
||||
// mop up
|
||||
dlclose(libc);
|
||||
exit(0);
|
||||
}
|
|
@ -43,6 +43,7 @@ EXAMPLES_DIRECTDEPS = \
|
|||
DSP_SCALE \
|
||||
DSP_TTY \
|
||||
LIBC_CALLS \
|
||||
LIBC_DLOPEN \
|
||||
LIBC_DNS \
|
||||
LIBC_FMT \
|
||||
LIBC_INTRIN \
|
||||
|
|
BIN
libc/dlopen/.dlopen.aarch64.glibc.elf
Executable file
BIN
libc/dlopen/.dlopen.aarch64.glibc.elf
Executable file
Binary file not shown.
BIN
libc/dlopen/.dlopen.x86_64.freebsd.elf
Executable file
BIN
libc/dlopen/.dlopen.x86_64.freebsd.elf
Executable file
Binary file not shown.
BIN
libc/dlopen/.dlopen.x86_64.glibc.elf
Executable file
BIN
libc/dlopen/.dlopen.x86_64.glibc.elf
Executable file
Binary file not shown.
BIN
libc/dlopen/.dlopen.x86_64.musl.elf
Executable file
BIN
libc/dlopen/.dlopen.x86_64.musl.elf
Executable file
Binary file not shown.
|
@ -1,5 +1,5 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_RUNTIME_DLFCN_H_
|
||||
#define COSMOPOLITAN_LIBC_RUNTIME_DLFCN_H_
|
||||
#ifndef COSMOPOLITAN_LIBC_DLFCN_H_
|
||||
#define COSMOPOLITAN_LIBC_DLFCN_H_
|
||||
|
||||
#define RTLD_LOCAL 0
|
||||
#define RTLD_LAZY 1
|
||||
|
@ -20,4 +20,4 @@ int dl_iterate_phdr(int (*)(void *, size_t, void *), void *);
|
|||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_RUNTIME_DLFCN_H_ */
|
||||
#endif /* COSMOPOLITAN_LIBC_DLFCN_H_ */
|
582
libc/dlopen/dlopen.c
Normal file
582
libc/dlopen/dlopen.c
Normal file
|
@ -0,0 +1,582 @@
|
|||
/*-*- 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 2023 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/atomic.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/cosmo.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/dlopen/dlfcn.h"
|
||||
#include "libc/elf/def.h"
|
||||
#include "libc/elf/elf.h"
|
||||
#include "libc/elf/struct/auxv.h"
|
||||
#include "libc/elf/struct/ehdr.h"
|
||||
#include "libc/elf/struct/phdr.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/bits.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/nt/dll.h"
|
||||
#include "libc/nt/enum/filemapflags.h"
|
||||
#include "libc/nt/enum/pageflags.h"
|
||||
#include "libc/nt/errors.h"
|
||||
#include "libc/nt/memory.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/runtime/elf_loader.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/syslib.internal.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/stdio/sysparam.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/auxv.h"
|
||||
#include "libc/sysv/consts/map.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/prot.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
/**
|
||||
* @fileoverview Cosmopolitan Dynamic Linker.
|
||||
*
|
||||
* Every program built using Cosmopolitan is statically-linked. However
|
||||
* there are some cases, e.g. GUIs and video drivers, where linking the
|
||||
* host platform libraries is desirable. So what we do in such cases is
|
||||
* launch a stub executable using the host platform's libc, and longjmp
|
||||
* back into this executable. The stub executable passes back to us the
|
||||
* platform-specific dlopen() implementation, which we shall then wrap.
|
||||
*
|
||||
* @kudos jacereda for figuring out how to do this
|
||||
*/
|
||||
|
||||
__static_yoink(".dlopen.x86_64.musl.elf");
|
||||
__static_yoink(".dlopen.x86_64.glibc.elf");
|
||||
__static_yoink(".dlopen.x86_64.freebsd.elf");
|
||||
__static_yoink(".dlopen.aarch64.glibc.elf");
|
||||
|
||||
#define XNU_RTLD_LAZY 1
|
||||
#define XNU_RTLD_NOW 2
|
||||
#define XNU_RTLD_LOCAL 4
|
||||
#define XNU_RTLD_GLOBAL 8
|
||||
|
||||
#define BEGIN_FOREIGN \
|
||||
{ \
|
||||
struct CosmoTib *cosmo_tib = __get_tls(); \
|
||||
pthread_mutex_lock(&foreign.lock); \
|
||||
__set_tls(foreign.tib)
|
||||
|
||||
#define END_FOREIGN \
|
||||
__set_tls(cosmo_tib); \
|
||||
pthread_mutex_unlock(&foreign.lock); \
|
||||
}
|
||||
|
||||
struct loaded {
|
||||
char *base;
|
||||
char *entry;
|
||||
Elf64_Ehdr eh;
|
||||
Elf64_Phdr ph[32];
|
||||
};
|
||||
|
||||
static struct {
|
||||
atomic_uint once;
|
||||
bool is_supported;
|
||||
struct CosmoTib *tib;
|
||||
pthread_mutex_t lock;
|
||||
void *(*dlopen)(const char *, int);
|
||||
void *(*dlsym)(void *, const char *);
|
||||
int (*dlclose)(void *);
|
||||
char *(*dlerror)(void);
|
||||
jmp_buf jb;
|
||||
} foreign;
|
||||
|
||||
static _Thread_local const char *last_dlerror;
|
||||
|
||||
static const unsigned align_mask = 4095;
|
||||
|
||||
static uintptr_t pgtrunc(uintptr_t x) {
|
||||
return x & ~align_mask;
|
||||
}
|
||||
|
||||
static uintptr_t pground(uintptr_t x) {
|
||||
return pgtrunc(x + align_mask);
|
||||
}
|
||||
|
||||
static unsigned pflags(unsigned x) {
|
||||
unsigned r = 0;
|
||||
if (x & PF_R) r += PROT_READ;
|
||||
if (x & PF_W) r += PROT_WRITE;
|
||||
if (x & PF_X) r += PROT_EXEC;
|
||||
return r;
|
||||
}
|
||||
|
||||
static char *elf_map(int fd, Elf64_Ehdr *ehdr, Elf64_Phdr *phdr) {
|
||||
uintptr_t minva = -1;
|
||||
uintptr_t maxva = 0;
|
||||
for (Elf64_Phdr *p = phdr; p < &phdr[ehdr->e_phnum]; p++) {
|
||||
if (p->p_type != PT_LOAD) continue;
|
||||
if (p->p_vaddr < minva) minva = p->p_vaddr;
|
||||
if (p->p_vaddr + p->p_memsz > maxva) maxva = p->p_vaddr + p->p_memsz;
|
||||
}
|
||||
minva = pgtrunc(minva);
|
||||
maxva = pground(maxva);
|
||||
uint8_t *base = __sys_mmap(0, maxva - minva, PROT_NONE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0, 0);
|
||||
if (base == MAP_FAILED) return MAP_FAILED;
|
||||
__sys_munmap(base, maxva - minva);
|
||||
for (Elf64_Phdr *p = phdr; p < &phdr[ehdr->e_phnum]; p++) {
|
||||
if (p->p_type != PT_LOAD) continue;
|
||||
uintptr_t off = p->p_vaddr & align_mask;
|
||||
uint8_t *start = base;
|
||||
start += pgtrunc(p->p_vaddr);
|
||||
size_t sz = pground(p->p_memsz + off);
|
||||
uint8_t *m = __sys_mmap(start, sz, PROT_WRITE,
|
||||
MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0, 0);
|
||||
if (m == MAP_FAILED) return MAP_FAILED;
|
||||
ssize_t rr = pread(fd, m + off, p->p_filesz, p->p_offset);
|
||||
if (rr != (ssize_t)p->p_filesz) return MAP_FAILED;
|
||||
sys_mprotect(m, sz, pflags(p->p_flags));
|
||||
}
|
||||
return (void *)base;
|
||||
}
|
||||
|
||||
static int elf_open(const char *file) {
|
||||
return open(file, O_RDONLY | O_CLOEXEC);
|
||||
}
|
||||
|
||||
static bool elf_slurp(struct loaded *l, int fd, const char *file) {
|
||||
if (pread(fd, &l->eh, 64, 0) != 64) return false;
|
||||
if (!IsElf64Binary(&l->eh, 64)) return false;
|
||||
if (l->eh.e_phnum > ARRAYLEN(l->ph)) return false;
|
||||
int bytes = l->eh.e_phnum * sizeof(l->ph[0]);
|
||||
if (pread(fd, l->ph, bytes, l->eh.e_phoff) != bytes) return false;
|
||||
l->entry = (char *)l->eh.e_entry;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool elf_load(struct loaded *l, const char *file) {
|
||||
int fd;
|
||||
if ((fd = elf_open(file)) == -1) return false;
|
||||
if (!elf_slurp(l, fd, file)) {
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
if ((l->base = elf_map(fd, &l->eh, l->ph)) == MAP_FAILED) {
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
l->entry += (uintptr_t)l->base;
|
||||
close(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool elf_interp(char *buf, size_t bsz, const char *file) {
|
||||
int fd;
|
||||
if ((fd = elf_open(file)) == -1) return false;
|
||||
struct loaded l;
|
||||
if (!elf_slurp(&l, fd, file)) {
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
for (unsigned i = 0; i < l.eh.e_phnum; i++) {
|
||||
if (l.ph[i].p_type == PT_INTERP) {
|
||||
if (l.ph[i].p_filesz >= bsz ||
|
||||
pread(fd, buf, l.ph[i].p_filesz, l.ph[i].p_offset) !=
|
||||
l.ph[i].p_filesz) {
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
static long *push_strs(long *sp, char **list, int count) {
|
||||
*--sp = 0;
|
||||
while (count) *--sp = (long)list[--count];
|
||||
return sp;
|
||||
}
|
||||
|
||||
static void elf_exec(const char *file, const char *iinterp, int argc,
|
||||
char **argv, char **envp) {
|
||||
|
||||
// get elf information
|
||||
struct loaded prog;
|
||||
if (!elf_load(&prog, file)) return;
|
||||
struct loaded interp;
|
||||
if (!elf_load(&interp, iinterp)) return;
|
||||
|
||||
// count environment variables
|
||||
int envc = 0;
|
||||
while (envp[envc]) envc++;
|
||||
|
||||
// create a stack for the new process just beneath our stack
|
||||
long *sp = (long *)__builtin_frame_address(0) - 256;
|
||||
|
||||
// push auxiliary values
|
||||
*--sp = 0;
|
||||
Elf64_auxv_t *av;
|
||||
unsigned long key, val;
|
||||
for (av = (Elf64_auxv_t *)__auxv; (key = av->a_type); ++av) {
|
||||
val = av->a_un.a_val;
|
||||
if (key == AT_PHDR) val = (long)(prog.base + prog.eh.e_phoff);
|
||||
if (key == AT_PHENT) val = prog.eh.e_phentsize;
|
||||
if (key == AT_PHNUM) val = prog.eh.e_phnum;
|
||||
if (key == AT_PAGESZ) val = 0x1000;
|
||||
if (key == AT_BASE) val = (long)interp.base;
|
||||
if (key == AT_FLAGS) val = 0;
|
||||
if (key == AT_ENTRY) val = (long)prog.entry;
|
||||
if (key == AT_EXECFN) val = (long)argv[0];
|
||||
*--sp = val;
|
||||
*--sp = key;
|
||||
}
|
||||
|
||||
// push main() arguments
|
||||
sp = push_strs(sp, envp, envc);
|
||||
sp = push_strs(sp, argv, argc);
|
||||
*--sp = argc;
|
||||
|
||||
#ifdef __x86_64__
|
||||
struct ps_strings {
|
||||
char **argv;
|
||||
int argc;
|
||||
char **envp;
|
||||
int envc;
|
||||
} pss = {argv, argc, envp, envc};
|
||||
asm volatile("mov\t%2,%%rsp\n\t"
|
||||
"jmpq\t*%1"
|
||||
: /* no outputs */
|
||||
: "D"(IsFreebsd() ? sp : 0), "S"(interp.entry), "d"(sp),
|
||||
"b"(IsNetbsd() ? &pss : 0)
|
||||
: "memory");
|
||||
__builtin_unreachable();
|
||||
#elif defined(__aarch64__)
|
||||
register long x0 asm("x0") = IsFreebsd() ? (long)sp : 0;
|
||||
register long x9 asm("x9") = (long)sp;
|
||||
register long x16 asm("x16") = (long)interp.entry;
|
||||
asm volatile("mov\tsp,x9\n\t"
|
||||
"br\tx16"
|
||||
: /* no outputs */
|
||||
: "r"(x0), "r"(x9), "r"(x16)
|
||||
: "memory");
|
||||
__builtin_unreachable();
|
||||
#else
|
||||
#error "unsupported architecture"
|
||||
#endif
|
||||
}
|
||||
|
||||
static wontreturn void foreign_helper(void **p) {
|
||||
foreign.dlopen = p[0];
|
||||
foreign.dlsym = p[1];
|
||||
foreign.dlclose = p[2];
|
||||
foreign.dlerror = p[3];
|
||||
longjmp(foreign.jb, 1);
|
||||
}
|
||||
|
||||
static void foreign_setup(void) {
|
||||
char interp[256] = {0};
|
||||
if (!elf_interp(interp, sizeof(interp), "/usr/bin/env")) return;
|
||||
const char *dlopen_helper = 0;
|
||||
#ifdef __x86_64__
|
||||
if (IsFreebsd()) {
|
||||
dlopen_helper = "/zip/.dlopen.x86_64.freebsd.elf";
|
||||
} else if (IsLinux()) {
|
||||
if (fileexists("/lib64/ld-linux-x86-64.so.2")) {
|
||||
dlopen_helper = "/zip/.dlopen.x86_64.glibc.elf";
|
||||
} else {
|
||||
dlopen_helper = "/zip/.dlopen.x86_64.musl.elf";
|
||||
}
|
||||
}
|
||||
#elif defined(__aarch64__)
|
||||
if (IsLinux()) {
|
||||
dlopen_helper = "/zip/.dlopen.aarch64.glibc.elf";
|
||||
}
|
||||
#endif
|
||||
if (!dlopen_helper) {
|
||||
return; // this platform isn't supported yet
|
||||
}
|
||||
struct CosmoTib *cosmo_tib = __get_tls();
|
||||
if (!setjmp(foreign.jb)) {
|
||||
elf_exec(dlopen_helper, interp, 2,
|
||||
(char *[]){
|
||||
program_invocation_name,
|
||||
(char *)foreign_helper,
|
||||
NULL,
|
||||
},
|
||||
environ);
|
||||
return; // if it returns then it failed
|
||||
}
|
||||
foreign.tib = __get_tls();
|
||||
__set_tls(cosmo_tib);
|
||||
foreign.is_supported = true;
|
||||
}
|
||||
|
||||
static bool foreign_init(void) {
|
||||
bool res;
|
||||
cosmo_once(&foreign.once, foreign_setup);
|
||||
if (!(res = foreign.is_supported)) {
|
||||
last_dlerror = "dlopen() isn't supported on this platform";
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int dlclose_nt(void *handle) {
|
||||
int res;
|
||||
if (FreeLibrary((uintptr_t)handle)) {
|
||||
res = 0;
|
||||
} else {
|
||||
last_dlerror = "FreeLibrary() failed";
|
||||
res = -1;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static void *dlopen_nt(const char *path, int mode) {
|
||||
int n;
|
||||
uintptr_t handle;
|
||||
char16_t path16[PATH_MAX + 2];
|
||||
if (mode & ~(RTLD_LOCAL | RTLD_LAZY | RTLD_NOW)) {
|
||||
last_dlerror = "invalid mode";
|
||||
return 0;
|
||||
}
|
||||
if ((n = __mkntpath(path, path16)) == -1) {
|
||||
last_dlerror = "path invalid";
|
||||
return 0;
|
||||
}
|
||||
if (n > 3 && //
|
||||
path16[n - 3] == '.' && //
|
||||
path16[n - 2] == 's' && //
|
||||
path16[n - 1] == 'o') {
|
||||
path16[n - 2] = 'd';
|
||||
path16[n - 1] = 'l';
|
||||
path16[n + 0] = 'l';
|
||||
path16[n + 1] = 0;
|
||||
}
|
||||
if (!(handle = LoadLibrary(path16))) {
|
||||
last_dlerror = "library not found";
|
||||
}
|
||||
return (void *)handle;
|
||||
}
|
||||
|
||||
static char *dlsym_nt_alloc_rwx_block(void) {
|
||||
uintptr_t h;
|
||||
char *p = 0;
|
||||
if ((h = CreateFileMapping(-1, 0, kNtPageExecuteReadwrite, 0, 65536, 0)) &&
|
||||
(p = MapViewOfFileEx(h, kNtFileMapWrite | kNtFileMapExecute, 0, 0, 65536,
|
||||
0))) {
|
||||
WRITE32LE(p, 4); // store used index
|
||||
} else {
|
||||
last_dlerror = "out of memory";
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
static void *dlsym_nt_alloc_rwx(size_t n) {
|
||||
void *res;
|
||||
static char *block;
|
||||
pthread_mutex_lock(&foreign.lock);
|
||||
if (!block || READ32LE(block) + n > 65536) {
|
||||
if (!(block = dlsym_nt_alloc_rwx_block())) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
res = block + READ32LE(block);
|
||||
WRITE32LE(block, READ32LE(block) + n);
|
||||
pthread_mutex_unlock(&foreign.lock);
|
||||
return res;
|
||||
}
|
||||
|
||||
static void *dlsym_nt_thunk(void *func, void *tramp) {
|
||||
unsigned char *code;
|
||||
if (!(code = dlsym_nt_alloc_rwx(27))) return 0;
|
||||
// push %rbp
|
||||
code[0] = 0x55;
|
||||
// mov %rsp,%rbp
|
||||
code[1] = 0x48;
|
||||
code[2] = 0x89;
|
||||
code[3] = 0xe5;
|
||||
// movabs $func,%rax
|
||||
code[4] = 0x48;
|
||||
code[5] = 0xb8;
|
||||
WRITE64LE(code + 6, (uintptr_t)func);
|
||||
// movabs $tramp,%r10
|
||||
code[14] = 0x49;
|
||||
code[15] = 0xba;
|
||||
WRITE64LE(code + 16, (uintptr_t)tramp);
|
||||
// jmp *%r10
|
||||
code[24] = 0x41;
|
||||
code[25] = 0xff;
|
||||
code[26] = 0xe2;
|
||||
return code;
|
||||
}
|
||||
|
||||
static void *dlsym_nt(void *handle, const char *name) {
|
||||
long __sysv2nt14();
|
||||
void *x64_abi_func;
|
||||
void *sysv_abi_func = 0;
|
||||
if ((x64_abi_func = GetProcAddress((uintptr_t)handle, name))) {
|
||||
sysv_abi_func = dlsym_nt_thunk(x64_abi_func, __sysv2nt14);
|
||||
} else {
|
||||
last_dlerror = "symbol not found";
|
||||
}
|
||||
return sysv_abi_func;
|
||||
}
|
||||
|
||||
static void *dlopen_silicon(const char *path, int mode) {
|
||||
int n;
|
||||
int xnu_mode = 0;
|
||||
char path2[PATH_MAX + 5];
|
||||
if (mode & ~(RTLD_LOCAL | RTLD_LAZY | RTLD_NOW | RTLD_GLOBAL)) {
|
||||
xnu_mode = -1; // punt error to system dlerror() impl
|
||||
}
|
||||
if (!(mode & RTLD_GLOBAL)) {
|
||||
xnu_mode |= XNU_RTLD_LOCAL; // unlike Linux, XNU defaults to RTLD_GLOBAL
|
||||
}
|
||||
if (mode & RTLD_NOW) {
|
||||
xnu_mode |= XNU_RTLD_NOW;
|
||||
}
|
||||
if (mode & RTLD_LAZY) {
|
||||
xnu_mode |= XNU_RTLD_LAZY;
|
||||
}
|
||||
if ((n = strlen(path)) < PATH_MAX && n > 3 && //
|
||||
path[n - 3] == '.' && //
|
||||
path[n - 2] == 's' && //
|
||||
path[n - 1] == 'o') {
|
||||
memcpy(path2, path, n);
|
||||
path2[n - 2] = 'd';
|
||||
path2[n - 1] = 'y';
|
||||
path2[n + 0] = 'l';
|
||||
path2[n + 1] = 'i';
|
||||
path2[n + 2] = 'b';
|
||||
path2[n + 3] = 0;
|
||||
path = path2;
|
||||
}
|
||||
return __syslib->__dlopen(path, xnu_mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens dynamic shared object using host platform libc.
|
||||
*
|
||||
* If a `path` ending with `.so` is passed on Windows or MacOS, then
|
||||
* this wrapper will automatically change it to `.dll` or `.dylib` to
|
||||
* increase its chance of successfully loading.
|
||||
*
|
||||
* @param mode is a bitmask that can contain:
|
||||
* - `RTLD_LOCAL` (default)
|
||||
* - `RTLD_GLOBAL` (not supported on Windows)
|
||||
* - `RTLD_LAZY`
|
||||
* - `RTLD_NOW`
|
||||
* @return dso handle, or NULL w/ dlerror()
|
||||
*/
|
||||
void *dlopen(const char *path, int mode) {
|
||||
void *res;
|
||||
if (IsWindows()) {
|
||||
res = dlopen_nt(path, mode);
|
||||
} else if (IsXnuSilicon()) {
|
||||
res = dlopen_silicon(path, mode);
|
||||
} else if (IsXnu()) {
|
||||
last_dlerror = "dlopen() isn't supported on x86-64 MacOS";
|
||||
res = 0;
|
||||
} else if (foreign_init()) {
|
||||
BEGIN_FOREIGN;
|
||||
res = foreign.dlopen(path, mode);
|
||||
END_FOREIGN;
|
||||
} else {
|
||||
res = 0;
|
||||
}
|
||||
STRACE("dlopen(%#s, %d) → %p", path, mode, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains address of symbol from dynamic shared object.
|
||||
*
|
||||
* On Windows you can only use this to lookup function addresses.
|
||||
* Returned functions are trampolined to conform to System V ABI.
|
||||
*
|
||||
* @param handle was opened by dlopen()
|
||||
* @return address of symbol, or NULL w/ dlerror()
|
||||
*/
|
||||
void *dlsym(void *handle, const char *name) {
|
||||
void *res;
|
||||
if (IsWindows()) {
|
||||
res = dlsym_nt(handle, name);
|
||||
} else if (IsXnuSilicon()) {
|
||||
res = __syslib->__dlsym(handle, name);
|
||||
} else if (IsXnu()) {
|
||||
last_dlerror = "dlopen() isn't supported on x86-64 MacOS";
|
||||
res = 0;
|
||||
} else if (foreign_init()) {
|
||||
BEGIN_FOREIGN;
|
||||
res = foreign.dlsym(handle, name);
|
||||
END_FOREIGN;
|
||||
} else {
|
||||
res = 0;
|
||||
}
|
||||
STRACE("dlsym(%p, %#s) → %p", handle, name, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes dynamic shared object.
|
||||
*
|
||||
* @param handle was opened by dlopen()
|
||||
* @return 0 on success, or -1 w/ dlerror()
|
||||
*/
|
||||
int dlclose(void *handle) {
|
||||
int res;
|
||||
if (IsWindows()) {
|
||||
res = dlclose_nt(handle);
|
||||
} else if (IsXnuSilicon()) {
|
||||
res = __syslib->__dlclose(handle);
|
||||
} else if (IsXnu()) {
|
||||
last_dlerror = "dlopen() isn't supported on x86-64 MacOS";
|
||||
res = -1;
|
||||
} else if (foreign_init()) {
|
||||
BEGIN_FOREIGN;
|
||||
res = foreign.dlclose(handle);
|
||||
END_FOREIGN;
|
||||
} else {
|
||||
res = -1;
|
||||
}
|
||||
STRACE("dlclose(%p) → %d", handle, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns string describing last dlopen/dlsym/dlclose error.
|
||||
*/
|
||||
char *dlerror(void) {
|
||||
char *res;
|
||||
if (IsXnuSilicon()) {
|
||||
res = __syslib->__dlerror();
|
||||
} else if (IsWindows() || IsXnu()) {
|
||||
res = (char *)last_dlerror;
|
||||
} else if (foreign_init()) {
|
||||
BEGIN_FOREIGN;
|
||||
res = foreign.dlerror();
|
||||
END_FOREIGN;
|
||||
} else {
|
||||
res = (char *)last_dlerror;
|
||||
}
|
||||
STRACE("dlerror() → %#s", res);
|
||||
return res;
|
||||
}
|
71
libc/dlopen/dlopen.mk
Normal file
71
libc/dlopen/dlopen.mk
Normal file
|
@ -0,0 +1,71 @@
|
|||
#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐
|
||||
#───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘
|
||||
|
||||
PKGS += LIBC_DLOPEN
|
||||
|
||||
LIBC_DLOPEN_ARTIFACTS += LIBC_DLOPEN_A
|
||||
LIBC_DLOPEN = $(LIBC_DLOPEN_A_DEPS) $(LIBC_DLOPEN_A)
|
||||
LIBC_DLOPEN_A = o/$(MODE)/libc/dlopen/dlopen.a
|
||||
LIBC_DLOPEN_A_FILES := $(wildcard libc/dlopen/*)
|
||||
LIBC_DLOPEN_A_HDRS = $(filter %.h,$(LIBC_DLOPEN_A_FILES))
|
||||
LIBC_DLOPEN_A_SRCS_S = $(filter %.S,$(LIBC_DLOPEN_A_FILES))
|
||||
LIBC_DLOPEN_A_SRCS_C = $(filter %.c,$(LIBC_DLOPEN_A_FILES))
|
||||
|
||||
LIBC_DLOPEN_A_SRCS = \
|
||||
$(LIBC_DLOPEN_A_SRCS_S) \
|
||||
$(LIBC_DLOPEN_A_SRCS_C)
|
||||
|
||||
LIBC_DLOPEN_DSOS = \
|
||||
o/$(MODE)/libc/dlopen/.dlopen.aarch64.glibc.elf.zip.o \
|
||||
o/$(MODE)/libc/dlopen/.dlopen.x86_64.freebsd.elf.zip.o \
|
||||
o/$(MODE)/libc/dlopen/.dlopen.x86_64.glibc.elf.zip.o \
|
||||
o/$(MODE)/libc/dlopen/.dlopen.x86_64.musl.elf.zip.o
|
||||
|
||||
LIBC_DLOPEN_A_OBJS = \
|
||||
$(LIBC_DLOPEN_A_SRCS_S:%.S=o/$(MODE)/%.o) \
|
||||
$(LIBC_DLOPEN_A_SRCS_C:%.c=o/$(MODE)/%.o) \
|
||||
$(LIBC_DLOPEN_DSOS)
|
||||
|
||||
LIBC_DLOPEN_A_CHECKS = \
|
||||
$(LIBC_DLOPEN_A).pkg \
|
||||
$(LIBC_DLOPEN_A_HDRS:%=o/$(MODE)/%.ok)
|
||||
|
||||
LIBC_DLOPEN_A_DIRECTDEPS = \
|
||||
LIBC_CALLS \
|
||||
LIBC_ELF \
|
||||
LIBC_INTRIN \
|
||||
LIBC_NEXGEN32E \
|
||||
LIBC_NT_KERNEL32 \
|
||||
LIBC_RUNTIME \
|
||||
LIBC_SYSV \
|
||||
LIBC_SYSV_CALLS \
|
||||
LIBC_STR \
|
||||
|
||||
LIBC_DLOPEN_A_DEPS := \
|
||||
$(call uniq,$(foreach x,$(LIBC_DLOPEN_A_DIRECTDEPS),$($(x))))
|
||||
|
||||
$(LIBC_DLOPEN_A): \
|
||||
libc/dlopen/ \
|
||||
$(LIBC_DLOPEN_A).pkg \
|
||||
$(LIBC_DLOPEN_A_OBJS)
|
||||
|
||||
$(LIBC_DLOPEN_A).pkg: \
|
||||
$(LIBC_DLOPEN_A_OBJS) \
|
||||
$(foreach x,$(LIBC_DLOPEN_A_DIRECTDEPS),$($(x)_A).pkg)
|
||||
|
||||
$(LIBC_DLOPEN_A_OBJS): private \
|
||||
COPTS += \
|
||||
-Wframe-larger-than=4096 \
|
||||
-Walloca-larger-than=4096
|
||||
|
||||
$(LIBC_DLOPEN_DSOS): private ZIPOBJ_FLAGS += -B
|
||||
|
||||
LIBC_DLOPEN_LIBS = $(foreach x,$(LIBC_DLOPEN_ARTIFACTS),$($(x)))
|
||||
LIBC_DLOPEN_SRCS = $(foreach x,$(LIBC_DLOPEN_ARTIFACTS),$($(x)_SRCS))
|
||||
LIBC_DLOPEN_HDRS = $(foreach x,$(LIBC_DLOPEN_ARTIFACTS),$($(x)_HDRS))
|
||||
LIBC_DLOPEN_CHECKS = $(foreach x,$(LIBC_DLOPEN_ARTIFACTS),$($(x)_CHECKS))
|
||||
LIBC_DLOPEN_OBJS = $(foreach x,$(LIBC_DLOPEN_ARTIFACTS),$($(x)_OBJS))
|
||||
$(LIBC_DLOPEN_OBJS): $(BUILD_FILES) libc/dlopen/dlopen.mk
|
||||
|
||||
.PHONY: o/$(MODE)/libc/dlopen
|
||||
o/$(MODE)/libc/dlopen: $(LIBC_DLOPEN_CHECKS)
|
10
libc/dlopen/misc/helper.c
Normal file
10
libc/dlopen/misc/helper.c
Normal file
|
@ -0,0 +1,10 @@
|
|||
#include <dlfcn.h>
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
return ((int (*)(void *))argv[1])((void *[]){
|
||||
dlopen,
|
||||
dlsym,
|
||||
dlclose,
|
||||
dlerror,
|
||||
});
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
#ifndef _DLFCN_H
|
||||
#define _DLFCN_H
|
||||
#include "libc/runtime/dlfcn.h"
|
||||
#include "libc/dlopen/dlfcn.h"
|
||||
#endif /* _DLFCN_H */
|
||||
|
|
|
@ -16,6 +16,7 @@ endif
|
|||
.PHONY: o/$(MODE)/libc
|
||||
o/$(MODE)/libc: o/$(MODE)/libc/calls \
|
||||
o/$(MODE)/libc/crt \
|
||||
o/$(MODE)/libc/dlopen \
|
||||
o/$(MODE)/libc/dns \
|
||||
o/$(MODE)/libc/elf \
|
||||
o/$(MODE)/libc/fmt \
|
||||
|
|
0
libc/runtime/elf_loader.h
Executable file
0
libc/runtime/elf_loader.h
Executable file
|
@ -1,40 +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/runtime/dlfcn.h"
|
||||
|
||||
char *dlerror(void) {
|
||||
return "cosmopolitan doesn't support dsos";
|
||||
}
|
||||
|
||||
void *dlopen(const char *file, int mode) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *dlsym(void *handle, const char *name) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int dlclose(void *handle) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int dl_iterate_phdr(int callback(void *info, size_t size, void *data),
|
||||
void *data) {
|
||||
return -1;
|
||||
}
|
|
@ -12,7 +12,7 @@ COSMOPOLITAN_C_START_
|
|||
*/
|
||||
|
||||
#define SYSLIB_MAGIC ('s' | 'l' << 8 | 'i' << 16 | 'b' << 24)
|
||||
#define SYSLIB_VERSION 4
|
||||
#define SYSLIB_VERSION 6
|
||||
|
||||
typedef uint64_t dispatch_time_t;
|
||||
typedef uint64_t dispatch_semaphore_t;
|
||||
|
@ -70,6 +70,11 @@ struct Syslib {
|
|||
long (*__sem_trywait)(int *);
|
||||
long (*__getrlimit)(int, void *);
|
||||
long (*__setrlimit)(int, const void *);
|
||||
// v6 (2023-11-03)
|
||||
void *(*__dlopen)(const char *, int);
|
||||
void *(*__dlsym)(void *, const char *);
|
||||
int (*__dlclose)(void *);
|
||||
char *(*__dlerror)(void);
|
||||
};
|
||||
|
||||
extern struct Syslib *__syslib;
|
||||
|
|
1
third_party/lua/loadlib.c
vendored
1
third_party/lua/loadlib.c
vendored
|
@ -27,6 +27,7 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#define loadlib_c
|
||||
#define LUA_LIB
|
||||
#include "libc/dlopen/dlfcn.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "third_party/lua/lauxlib.h"
|
||||
|
|
1
third_party/lua/lua.mk
vendored
1
third_party/lua/lua.mk
vendored
|
@ -117,6 +117,7 @@ THIRD_PARTY_LUA_A_OBJS = \
|
|||
|
||||
THIRD_PARTY_LUA_A_DIRECTDEPS = \
|
||||
LIBC_CALLS \
|
||||
LIBC_DLOPEN \
|
||||
LIBC_FMT \
|
||||
LIBC_INTRIN \
|
||||
LIBC_MEM \
|
||||
|
|
4
third_party/lua/luaconf.h
vendored
4
third_party/lua/luaconf.h
vendored
|
@ -58,10 +58,10 @@
|
|||
#endif
|
||||
|
||||
|
||||
#if defined(LUA_USE_LINUX)
|
||||
//#if defined(LUA_USE_LINUX)
|
||||
#define LUA_USE_POSIX
|
||||
#define LUA_USE_DLOPEN /* needs an extra library: -ldl */
|
||||
#endif
|
||||
//#endif
|
||||
|
||||
|
||||
#if defined(LUA_USE_MACOSX)
|
||||
|
|
2
third_party/python/Modules/_threadmodule.c
vendored
2
third_party/python/Modules/_threadmodule.c
vendored
|
@ -35,7 +35,7 @@
|
|||
#include "libc/nt/enum/sw.h"
|
||||
#include "libc/nt/files.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/runtime/dlfcn.h"
|
||||
#include "libc/dlopen/dlfcn.h"
|
||||
#include "libc/runtime/pathconf.h"
|
||||
#include "libc/runtime/sysconf.h"
|
||||
#include "libc/sock/sendfile.internal.h"
|
||||
|
|
2
third_party/python/Modules/posixmodule.c
vendored
2
third_party/python/Modules/posixmodule.c
vendored
|
@ -37,7 +37,7 @@
|
|||
#include "libc/nt/enum/sw.h"
|
||||
#include "libc/nt/files.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/runtime/dlfcn.h"
|
||||
#include "libc/dlopen/dlfcn.h"
|
||||
#include "libc/runtime/pathconf.h"
|
||||
#include "libc/runtime/sysconf.h"
|
||||
#include "libc/sock/sendfile.internal.h"
|
||||
|
|
2
third_party/python/Python/dynload_shlib.c
vendored
2
third_party/python/Python/dynload_shlib.c
vendored
|
@ -5,7 +5,7 @@
|
|||
│ https://docs.python.org/3/license.html │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/weirdtypes.h"
|
||||
#include "libc/runtime/dlfcn.h"
|
||||
#include "libc/dlopen/dlfcn.h"
|
||||
#include "third_party/python/Include/fileutils.h"
|
||||
#include "third_party/python/Include/modsupport.h"
|
||||
#include "third_party/python/Include/pyerrors.h"
|
||||
|
|
2
third_party/python/Python/pystate.c
vendored
2
third_party/python/Python/pystate.c
vendored
|
@ -7,7 +7,7 @@
|
|||
#include "third_party/python/Include/pystate.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/pushpop.internal.h"
|
||||
#include "libc/runtime/dlfcn.h"
|
||||
#include "libc/dlopen/dlfcn.h"
|
||||
#include "third_party/python/Include/ceval.h"
|
||||
#include "third_party/python/Include/dictobject.h"
|
||||
#include "third_party/python/Include/listobject.h"
|
||||
|
|
1
third_party/python/python.mk
vendored
1
third_party/python/python.mk
vendored
|
@ -455,6 +455,7 @@ THIRD_PARTY_PYTHON_STAGE1_A_SRCS = \
|
|||
THIRD_PARTY_PYTHON_STAGE1_A_DIRECTDEPS = \
|
||||
DSP_SCALE \
|
||||
LIBC_CALLS \
|
||||
LIBC_DLOPEN \
|
||||
LIBC_FMT \
|
||||
LIBC_INTRIN \
|
||||
LIBC_LOG \
|
||||
|
|
2
third_party/quickjs/quickjs-libc.c
vendored
2
third_party/quickjs/quickjs-libc.c
vendored
|
@ -37,7 +37,7 @@
|
|||
#include "libc/limits.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/runtime/dlfcn.h"
|
||||
#include "libc/dlopen/dlfcn.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/sysconf.h"
|
||||
#include "libc/sock/select.h"
|
||||
|
|
1
third_party/quickjs/quickjs.mk
vendored
1
third_party/quickjs/quickjs.mk
vendored
|
@ -79,6 +79,7 @@ THIRD_PARTY_QUICKJS_A_OBJS = \
|
|||
|
||||
THIRD_PARTY_QUICKJS_A_DIRECTDEPS = \
|
||||
LIBC_CALLS \
|
||||
LIBC_DLOPEN \
|
||||
LIBC_FMT \
|
||||
LIBC_INTRIN \
|
||||
LIBC_LOG \
|
||||
|
|
Loading…
Reference in a new issue