mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 11:37:35 +00:00
957c61cbbf
This change upgrades to GCC 12.3 and GNU binutils 2.42. The GNU linker appears to have changed things so that only a single de-duplicated str table is present in the binary, and it gets placed wherever the linker wants, regardless of what the linker script says. To cope with that we need to stop using .ident to embed licenses. As such, this change does significant work to revamp how third party licenses are defined in the codebase, using `.section .notice,"aR",@progbits`. This new GCC 12.3 toolchain has support for GNU indirect functions. It lets us support __target_clones__ for the first time. This is used for optimizing the performance of libc string functions such as strlen and friends so far on x86, by ensuring AVX systems favor a second codepath that uses VEX encoding. It shaves some latency off certain operations. It's a useful feature to have for scientific computing for the reasons explained by the test/libcxx/openmp_test.cc example which compiles for fifteen different microarchitectures. Thanks to the upgrades, it's now also possible to use newer instruction sets, such as AVX512FP16, VNNI. Cosmo now uses the %gs register on x86 by default for TLS. Doing it is helpful for any program that links `cosmo_dlopen()`. Such programs had to recompile their binaries at startup to change the TLS instructions. That's not great, since it means every page in the executable needs to be faulted. The work of rewriting TLS-related x86 opcodes, is moved to fixupobj.com instead. This is great news for MacOS x86 users, since we previously needed to morph the binary every time for that platform but now that's no longer necessary. The only platforms where we need fixup of TLS x86 opcodes at runtime are now Windows, OpenBSD, and NetBSD. On Windows we morph TLS to point deeper into the TIB, based on a TlsAlloc assignment, and on OpenBSD/NetBSD we morph %gs back into %fs since the kernels do not allow us to specify a value for the %gs register. OpenBSD users are now required to use APE Loader to run Cosmo binaries and assimilation is no longer possible. OpenBSD kernel needs to change to allow programs to specify a value for the %gs register, or it needs to stop marking executable pages loaded by the kernel as mimmutable(). This release fixes __constructor__, .ctor, .init_array, and lastly the .preinit_array so they behave the exact same way as glibc. We no longer use hex constants to define math.h symbols like M_PI.
411 lines
14 KiB
C
411 lines
14 KiB
C
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
|
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
|
╚──────────────────────────────────────────────────────────────────────────────╝
|
|
│ │
|
|
│ Lua │
|
|
│ Copyright © 2004-2021 Lua.org, PUC-Rio. │
|
|
│ │
|
|
│ Permission is hereby granted, free of charge, to any person obtaining │
|
|
│ a copy of this software and associated documentation files (the │
|
|
│ "Software"), to deal in the Software without restriction, including │
|
|
│ without limitation the rights to use, copy, modify, merge, publish, │
|
|
│ distribute, sublicense, and/or sell copies of the Software, and to │
|
|
│ permit persons to whom the Software is furnished to do so, subject to │
|
|
│ the following conditions: │
|
|
│ │
|
|
│ The above copyright notice and this permission notice shall be │
|
|
│ included in all copies or substantial portions of the Software. │
|
|
│ │
|
|
│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │
|
|
│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │
|
|
│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │
|
|
│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │
|
|
│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │
|
|
│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │
|
|
│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │
|
|
│ │
|
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
|
#define lua_c
|
|
#include "third_party/lua/lua.h"
|
|
#include "libc/calls/calls.h"
|
|
#include "libc/calls/struct/sigaction.h"
|
|
#include "libc/dce.h"
|
|
#include "libc/errno.h"
|
|
#include "libc/log/log.h"
|
|
#include "libc/mem/gc.h"
|
|
#include "libc/runtime/runtime.h"
|
|
#include "libc/runtime/stack.h"
|
|
#include "libc/sock/sock.h"
|
|
#include "libc/sock/struct/pollfd.h"
|
|
#include "libc/str/str.h"
|
|
#include "libc/sysv/consts/exit.h"
|
|
#include "libc/sysv/consts/poll.h"
|
|
#include "libc/sysv/consts/sa.h"
|
|
#include "libc/x/x.h"
|
|
#include "third_party/linenoise/linenoise.h"
|
|
#include "third_party/lua/cosmo.h"
|
|
#include "third_party/lua/lauxlib.h"
|
|
#include "third_party/lua/lprefix.h"
|
|
#include "third_party/lua/lrepl.h"
|
|
#include "third_party/lua/lualib.h"
|
|
#include "third_party/lua/lunix.h"
|
|
#include "tool/args/args.h"
|
|
__static_yoink("lua_notice");
|
|
|
|
STATIC_STACK_ALIGN(GetStackSize());
|
|
|
|
#if !defined(LUA_PROGNAME)
|
|
#define LUA_PROGNAME "lua"
|
|
#endif
|
|
|
|
#if !defined(LUA_INIT_VAR)
|
|
#define LUA_INIT_VAR "LUA_INIT"
|
|
#endif
|
|
|
|
#define LUA_INITVARVERSION LUA_INIT_VAR LUA_VERSUFFIX
|
|
|
|
|
|
static bool lua_stdin_is_tty(void) {
|
|
return isatty(0);
|
|
}
|
|
|
|
|
|
static void print_usage (const char *badoption) {
|
|
lua_writestringerror("%s: ", lua_progname);
|
|
if (badoption[1] == 'e' || badoption[1] == 'l')
|
|
lua_writestringerror("'%s' needs argument\n", badoption);
|
|
else
|
|
lua_writestringerror("unrecognized option '%s'\n", badoption);
|
|
lua_writestringerror(
|
|
"usage: %s [options] [script [args]]\n"
|
|
"Available options are:\n"
|
|
" -e stat execute string 'stat'\n"
|
|
" -i enter interactive mode after executing 'script'\n"
|
|
" -l name require library 'name' into global 'name'\n"
|
|
" -v show version information\n"
|
|
" -E ignore environment variables\n"
|
|
" -W turn warnings on\n"
|
|
" -- stop handling options\n"
|
|
" - stop handling options and execute stdin\n"
|
|
,
|
|
lua_progname);
|
|
}
|
|
|
|
|
|
static void print_version (void) {
|
|
lua_writestring(LUA_COPYRIGHT, strlen(LUA_COPYRIGHT));
|
|
lua_writeline();
|
|
}
|
|
|
|
|
|
/*
|
|
** Create the 'arg' table, which stores all arguments from the
|
|
** command line ('argv'). It should be aligned so that, at index 0,
|
|
** it has 'argv[script]', which is the script name. The arguments
|
|
** to the script (everything after 'script') go to positive indices;
|
|
** other arguments (before the script name) go to negative indices.
|
|
** If there is no script name, assume interpreter's name as base.
|
|
*/
|
|
static void createargtable (lua_State *L, char **argv, int argc, int script) {
|
|
int i, narg;
|
|
if (script == argc) script = 0; /* no script name? */
|
|
narg = argc - (script + 1); /* number of positive indices */
|
|
lua_createtable(L, narg, script + 1);
|
|
for (i = 0; i < argc; i++) {
|
|
lua_pushstring(L, argv[i]);
|
|
lua_rawseti(L, -2, i - script);
|
|
}
|
|
lua_setglobal(L, "arg");
|
|
}
|
|
|
|
|
|
static int dochunk (lua_State *L, int status) {
|
|
if (status == LUA_OK) status = lua_runchunk(L, 0, 0);
|
|
return lua_report(L, status);
|
|
}
|
|
|
|
|
|
static int dofile (lua_State *L, const char *name) {
|
|
return dochunk(L, luaL_loadfile(L, name));
|
|
}
|
|
|
|
|
|
static int dostring (lua_State *L, const char *s, const char *name) {
|
|
return dochunk(L, luaL_loadbuffer(L, s, strlen(s), name));
|
|
}
|
|
|
|
|
|
/*
|
|
** Calls 'require(name)' and stores the result in a global variable
|
|
** with the given name.
|
|
*/
|
|
static int dolibrary (lua_State *L, const char *name) {
|
|
int status;
|
|
lua_getglobal(L, "require");
|
|
lua_pushstring(L, name);
|
|
status = lua_runchunk(L, 1, 1); /* call 'require(name)' */
|
|
if (status == LUA_OK)
|
|
lua_setglobal(L, name); /* global[name] = require return */
|
|
return lua_report(L, status);
|
|
}
|
|
|
|
|
|
/*
|
|
** Push on the stack the contents of table 'arg' from 1 to #arg
|
|
*/
|
|
static int pushargs (lua_State *L) {
|
|
int i, n;
|
|
if (lua_getglobal(L, "arg") != LUA_TTABLE)
|
|
luaL_error(L, "'arg' is not a table");
|
|
n = (int)luaL_len(L, -1);
|
|
luaL_checkstack(L, n + 3, "too many arguments to script");
|
|
for (i = 1; i <= n; i++)
|
|
lua_rawgeti(L, -i, i);
|
|
lua_remove(L, -i); /* remove table from the stack */
|
|
return n;
|
|
}
|
|
|
|
|
|
static int handle_script (lua_State *L, char **argv) {
|
|
int status;
|
|
const char *fname = argv[0];
|
|
if (strcmp(fname, "-") == 0 && strcmp(argv[-1], "--") != 0)
|
|
fname = NULL; /* stdin */
|
|
status = luaL_loadfile(L, fname);
|
|
if (status == LUA_OK) {
|
|
int n = pushargs(L); /* push arguments to script */
|
|
status = lua_runchunk(L, n, LUA_MULTRET);
|
|
}
|
|
return lua_report(L, status);
|
|
}
|
|
|
|
|
|
/* bits of various argument indicators in 'args' */
|
|
#define has_error 1 /* bad option */
|
|
#define has_i 2 /* -i */
|
|
#define has_v 4 /* -v */
|
|
#define has_e 8 /* -e */
|
|
#define has_E 16 /* -E */
|
|
|
|
|
|
/*
|
|
** Traverses all arguments from 'argv', returning a mask with those
|
|
** needed before running any Lua code (or an error code if it finds
|
|
** any invalid argument). 'first' returns the first not-handled argument
|
|
** (either the script name or a bad argument in case of error).
|
|
*/
|
|
static int collectargs (char **argv, int *first) {
|
|
int args = 0;
|
|
int i;
|
|
for (i = 1; argv[i] != NULL; i++) {
|
|
*first = i;
|
|
if (argv[i][0] != '-') /* not an option? */
|
|
return args; /* stop handling options */
|
|
switch (argv[i][1]) { /* else check option */
|
|
case '-': /* '--' */
|
|
if (argv[i][2] != '\0') /* extra characters after '--'? */
|
|
return has_error; /* invalid option */
|
|
*first = i + 1;
|
|
return args;
|
|
case '\0': /* '-' */
|
|
return args; /* script "name" is '-' */
|
|
case 'E':
|
|
if (argv[i][2] != '\0') /* extra characters? */
|
|
return has_error; /* invalid option */
|
|
args |= has_E;
|
|
break;
|
|
case 'W':
|
|
if (argv[i][2] != '\0') /* extra characters? */
|
|
return has_error; /* invalid option */
|
|
break;
|
|
case 'i':
|
|
args |= has_i; /* (-i implies -v) *//* FALLTHROUGH */
|
|
case 'v':
|
|
if (argv[i][2] != '\0') /* extra characters? */
|
|
return has_error; /* invalid option */
|
|
args |= has_v;
|
|
break;
|
|
case 'e':
|
|
args |= has_e; /* FALLTHROUGH */
|
|
case 'l': /* both options need an argument */
|
|
if (argv[i][2] == '\0') { /* no concatenated argument? */
|
|
i++; /* try next 'argv' */
|
|
if (argv[i] == NULL || argv[i][0] == '-')
|
|
return has_error; /* no next argument or it is another option */
|
|
}
|
|
break;
|
|
default: /* invalid option */
|
|
return has_error;
|
|
}
|
|
}
|
|
*first = i; /* no script name */
|
|
return args;
|
|
}
|
|
|
|
|
|
/*
|
|
** Processes options 'e' and 'l', which involve running Lua code, and
|
|
** 'W', which also affects the state.
|
|
** Returns 0 if some code raises an error.
|
|
*/
|
|
static int runargs (lua_State *L, char **argv, int n) {
|
|
int i;
|
|
for (i = 1; i < n; i++) {
|
|
int option = argv[i][1];
|
|
lua_assert(argv[i][0] == '-'); /* already checked */
|
|
switch (option) {
|
|
case 'e': case 'l': {
|
|
int status;
|
|
const char *extra = argv[i] + 2; /* both options need an argument */
|
|
if (*extra == '\0') extra = argv[++i];
|
|
lua_assert(extra != NULL);
|
|
status = (option == 'e')
|
|
? dostring(L, extra, "=(command line)")
|
|
: dolibrary(L, extra);
|
|
if (status != LUA_OK) return 0;
|
|
break;
|
|
}
|
|
case 'W':
|
|
lua_warning(L, "@on", 0); /* warnings on */
|
|
break;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
static int handle_luainit (lua_State *L) {
|
|
const char *name = "=" LUA_INITVARVERSION;
|
|
const char *init = getenv(name + 1);
|
|
if (init == NULL) {
|
|
name = "=" LUA_INIT_VAR;
|
|
init = getenv(name + 1); /* try alternative name */
|
|
}
|
|
if (init == NULL) return LUA_OK;
|
|
else if (init[0] == '@')
|
|
return dofile(L, init+1);
|
|
else
|
|
return dostring(L, init, name);
|
|
}
|
|
|
|
|
|
/*
|
|
** Do the REPL: repeatedly read (load) a line, evaluate (call) it, and
|
|
** print any results.
|
|
*/
|
|
static void doREPL (lua_State *L) {
|
|
int status;
|
|
const char *oldprogname = lua_progname;
|
|
lua_progname = NULL; /* no 'progname' on errors in interactive mode */
|
|
lua_initrepl(L);
|
|
for (;;) {
|
|
if (lua_repl_isterminal)
|
|
linenoiseEnableRawMode(0);
|
|
TryAgain:
|
|
status = lua_loadline(L);
|
|
if (status == -2 && errno == EAGAIN) {
|
|
errno = 0;
|
|
poll(&(struct pollfd){0, POLLIN}, 1, -1);
|
|
goto TryAgain;
|
|
}
|
|
if (lua_repl_isterminal)
|
|
linenoiseDisableRawMode();
|
|
if (status == -1) {
|
|
break;
|
|
} else if (status == -2) {
|
|
lua_pushfstring(L, "read error: %s", strerror(errno));
|
|
lua_report(L, status);
|
|
lua_freerepl();
|
|
lua_progname = oldprogname;
|
|
return;
|
|
}
|
|
if (status == LUA_OK)
|
|
status = lua_runchunk(L, 0, LUA_MULTRET);
|
|
if (status == LUA_OK) {
|
|
lua_l_print(L);
|
|
} else {
|
|
lua_report(L, status);
|
|
}
|
|
}
|
|
lua_freerepl();
|
|
lua_settop(L, 0); /* clear stack */
|
|
lua_progname = oldprogname;
|
|
}
|
|
|
|
/* }================================================================== */
|
|
|
|
|
|
/*
|
|
** Main body of stand-alone interpreter (to be called in protected mode).
|
|
** Reads the options and handles them all.
|
|
*/
|
|
static int pmain (lua_State *L) {
|
|
int argc = (int)lua_tointeger(L, 1);
|
|
char **argv = (char **)lua_touserdata(L, 2);
|
|
int script;
|
|
int args = collectargs(argv, &script);
|
|
luaL_checkversion(L); /* check that interpreter has correct version */
|
|
lua_progname = LUA_PROGNAME;
|
|
if (argv[0] && argv[0][0]) lua_progname = argv[0];
|
|
if (args == has_error) { /* bad arg? */
|
|
print_usage(argv[script]); /* 'script' has index of bad arg. */
|
|
return 0;
|
|
}
|
|
if (args & has_v) /* option '-v'? */
|
|
print_version();
|
|
if (args & has_E) { /* option '-E'? */
|
|
lua_pushboolean(L, 1); /* signal for libraries to ignore env. vars. */
|
|
lua_setfield(L, LUA_REGISTRYINDEX, "LUA_NOENV");
|
|
}
|
|
luaL_openlibs(L); /* open standard libraries */
|
|
luaL_requiref(L, "unix", LuaUnix, 1);
|
|
lua_pop(L, 1);
|
|
createargtable(L, argv, argc, script); /* create table 'arg' */
|
|
lua_gc(L, LUA_GCGEN, 0, 0); /* GC in generational mode */
|
|
if (!(args & has_E)) { /* no option '-E'? */
|
|
if (handle_luainit(L) != LUA_OK) /* run LUA_INIT */
|
|
return 0; /* error running LUA_INIT */
|
|
}
|
|
if (!runargs(L, argv, script)) /* execute arguments -e and -l */
|
|
return 0; /* something failed */
|
|
if (script < argc && /* execute main script (if there is one) */
|
|
handle_script(L, argv + script) != LUA_OK)
|
|
return 0;
|
|
if (args & has_i) /* -i option? */
|
|
doREPL(L); /* do read-eval-print loop */
|
|
else if (script == argc && !(args & (has_e | has_v))) { /* no arguments? */
|
|
if (lua_stdin_is_tty()) { /* running in interactive mode? */
|
|
print_version();
|
|
doREPL(L); /* do read-eval-print loop */
|
|
}
|
|
else dofile(L, NULL); /* executes stdin as a file */
|
|
}
|
|
lua_pushboolean(L, 1); /* signal no errors */
|
|
return 1;
|
|
}
|
|
|
|
|
|
int main (int argc, char **argv) {
|
|
lua_State *L;
|
|
int status, result;
|
|
LoadZipArgs(&argc, &argv);
|
|
if (IsModeDbg()) {
|
|
ShowCrashReports();
|
|
}
|
|
L = luaL_newstate(); /* create state */
|
|
if (L == NULL) {
|
|
lua_l_message(argv[0], "cannot create state: not enough memory");
|
|
return EXIT_FAILURE;
|
|
}
|
|
lua_pushcfunction(L, &pmain); /* to call 'pmain' in protected mode */
|
|
lua_pushinteger(L, argc); /* 1st argument */
|
|
lua_pushlightuserdata(L, argv); /* 2nd argument */
|
|
status = lua_pcall(L, 2, 1, 0); /* do the call */
|
|
result = lua_toboolean(L, -1); /* get result */
|
|
lua_report(L, status);
|
|
lua_close(L);
|
|
if (IsModeDbg()) {
|
|
CheckForMemoryLeaks();
|
|
}
|
|
return (result && status == LUA_OK) ? EXIT_SUCCESS : EXIT_FAILURE;
|
|
}
|