Make improvements

- Get threads working on NetBSD
- Get threads working on OpenBSD
- Fix Emacs config for Emacs v28
- Improve --strace logging of sigset_t
- Improve --strace logging of struct stat
- Improve memory safety of DescribeThing functions
- Refactor auto stack allocation into LIBC_RUNTIME
- Introduce shell.com example which works on Windows
- Refactor __strace_thing into DescribeThing functions
- Document the CHECK macros and improve them in NDEBUG mode
- Rewrite MAP_STACK so it uses FreeBSD behavior across platforms
- Deprecate and discourage the use of MAP_GROWSDOWN (it's weird)
This commit is contained in:
Justine Tunney 2022-05-12 06:43:59 -07:00
parent dd9ab01d25
commit e7611a8476
101 changed files with 967 additions and 464 deletions

88
examples/check.c Normal file
View file

@ -0,0 +1,88 @@
#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/log/check.h"
/**
* @fileoverview Check Macros
*
* The simplest assertion is:
*
* assert(123 == x);
*
* This has some downsides:
*
* 1. It's nice to know what `x` is when it crashes
* 2. It's sometimes nice to have the check always take effect.
* 3. It's problematic that assert() can't do __builtin_assume()
*
* Cosmopolitan provides alternative macros like:
*
* - `CHECK(EXPR, ...)`
* - `CHECK_EQ(WANT, GOT, ...)`
* - `CHECK_NE(WANT, GOT, ...)`
* - `CHECK_GT(WANT, GOT, ...)`
* - `CHECK_LT(WANT, GOT, ...)`
* - `CHECK_NOTNULL(EXPR, ...)`
*
* The CHECK macros always happen. They always kill the process when
* they fail. Printf formatting information may be provided as extra
* arguments. On the other hand, there exists:
*
* - `DCHECK(EXPR, ...)`
* - `DCHECK_EQ(WANT, GOT, ...)`
* - `DCHECK_NE(WANT, GOT, ...)`
* - `DCHECK_GT(WANT, GOT, ...)`
* - `DCHECK_LT(WANT, GOT, ...)`
* - `DCHECK_NOTNULL(EXPR, ...)`
*
* The DCHECK macros always happen when NDEBUG isn't defined. When
* NDEBUG is defined, they still happen, but in a special way that
* causes the compiler to recognize their failure as undefined behavior.
* What this means is that, if the provided expressions are pure without
* side-effects, then the code compiles down to nothing and the compiler
* may be able to use the knowledge of something being the case in order
* to optimize other code adjacent to your DCHECK.
*
* In the default build modes, this prints lots of information:
*
* error:examples/check.c:23:check.com: check failed on nightmare pid 15412
* CHECK_EQ(123, some_source_code);
* 0x7b (123)
* == 0x64 (some_source_code)
* extra info: hello
* EUNKNOWN/0/No error information
* ./o//examples/check.com
* 0x0000000000407404: __die at libc/log/die.c:42
* 0x0000000000407340: __check_fail at libc/log/checkfail.c:73
* 0x00000000004071d0: main at examples/check.c:23
* 0x000000000040256e: cosmo at libc/runtime/cosmo.S:69
* 0x000000000040217d: _start at libc/crt/crt.S:85
*
* In NDEBUG mode (e.g. MODE=rel, MODE=tiny, etc.) this prints a much
* simpler message that, most importantly, doesn't include any source
* code, although it still includes the file name for reference.
*
* error:examples/check.c:14: check failed: 123 == 100: extra info: hello
*
* That way your release binaries won't leak CI. You may optionally link
* the following functions to further expand the information shown by
* the NDEBUG check failure:
*
* STATIC_YOINK("__die");
* STATIC_YOINK("strerror");
*
* Please note that backtraces aren't ever available in MODE=tiny.
*/
int main(int argc, char *argv[]) {
int some_source_code = 100;
CHECK_EQ(123, some_source_code, "extra info: %s", "hello");
return 0;
}

View file

@ -22,7 +22,7 @@
* o//examples/crashreport.com
*/
int main(int argc, char *argv[]) {
noubsan int main(int argc, char *argv[]) {
volatile int64_t x;
ShowCrashReports();
return 1 / (x = 0);

14
examples/exit.c Normal file
View file

@ -0,0 +1,14 @@
#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/fmt/conv.h"
int main(int argc, char *argv[]) {
return atoi(argc > 1 ? argv[1] : "0");
}

View file

@ -11,6 +11,7 @@
#include "libc/calls/strace.internal.h"
#include "libc/calls/struct/rlimit.h"
#include "libc/errno.h"
#include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/kprintf.h"
#include "libc/log/color.internal.h"
#include "libc/macros.internal.h"
@ -37,12 +38,12 @@ static void SetLimit(int resource, uint64_t soft, uint64_t hard) {
lim.rlim_cur = MIN(soft, lim.rlim_max);
if (!setrlimit(resource, &lim)) {
fprintf(stderr, "%sNOTE: SETRLIMIT(%s) DOWNGRADED TO {%,ld, %,ld}\n",
__strace_rlimit_name(resource), lim.rlim_cur, lim.rlim_max);
DescribeRlimitName(resource), lim.rlim_cur, lim.rlim_max);
return;
}
}
fprintf(stderr, "ERROR: SETRLIMIT(%s, %,ld, %,ld) FAILED %m%n",
__strace_rlimit_name(resource), soft, hard);
DescribeRlimitName(resource), soft, hard);
exit(1);
}
}
@ -63,9 +64,8 @@ int main(int argc, char *argv[]) {
for (i = 0; i < RLIM_NLIMITS; ++i) {
rc = getrlimit(i, &rlim);
printf("SETRLIMIT(%-20s, %,16ld, %,16ld) → %d %s\n",
__strace_rlimit_name(i), rlim.rlim_cur, rlim.rlim_max, rc,
!rc ? "" : strerror(errno));
printf("SETRLIMIT(%-20s, %,16ld, %,16ld) → %d %s\n", DescribeRlimitName(i),
rlim.rlim_cur, rlim.rlim_max, rc, !rc ? "" : strerror(errno));
}
return 0;

172
examples/shell.c Normal file
View file

@ -0,0 +1,172 @@
#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/calls/sigbits.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/calls/struct/sigset.h"
#include "libc/fmt/fmt.h"
#include "libc/fmt/itoa.h"
#include "libc/log/internal.h"
#include "libc/macros.internal.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/dt.h"
#include "libc/sysv/consts/sig.h"
#include "libc/x/x.h"
#include "third_party/linenoise/linenoise.h"
/**
* @fileoverview Shell that works on Windows.
*
* This doesn't have script language features like UNBOURNE.COM but it
* works on Windows and, unlike CMD.EXE, actually has CTRL-P and CTRL-R
* which alone make it so much better.
*
* One day we'll have UNBOURNE.COM working on Windows but the code isn't
* very maintainable sadly.
*/
static void AddUniqueCompletion(linenoiseCompletions *c, char *s) {
size_t i;
if (!s) return;
for (i = 0; i < c->len; ++i) {
if (!strcmp(s, c->cvec[i])) {
return;
}
}
c->cvec = realloc(c->cvec, ++c->len * sizeof(*c->cvec));
c->cvec[c->len - 1] = s;
}
static void CompleteFilename(const char *p, const char *q, const char *b,
linenoiseCompletions *c) {
DIR *d;
char *buf;
const char *g;
struct dirent *e;
if ((buf = malloc(512))) {
if ((g = memrchr(p, '/', q - p))) {
*(char *)mempcpy(buf, p, MIN(g - p, 511)) = 0;
p = ++g;
} else {
strcpy(buf, ".");
}
if ((d = opendir(buf))) {
while ((e = readdir(d))) {
if (!strcmp(e->d_name, ".")) continue;
if (!strcmp(e->d_name, "..")) continue;
if (!strncmp(e->d_name, p, q - p)) {
snprintf(buf, 512, "%.*s%s%s", p - b, b, e->d_name,
e->d_type == DT_DIR ? "/" : "");
AddUniqueCompletion(c, strdup(buf));
}
}
closedir(d);
}
free(buf);
}
}
static void ShellCompletion(const char *p, linenoiseCompletions *c) {
bool slashed;
const char *q, *b;
for (slashed = false, b = p, q = (p += strlen(p)); p > b; --p) {
if (p[-1] == '/' && p[-1] == '\\') slashed = true;
if (!isalnum(p[-1]) &&
(p[-1] != '.' && p[-1] != '_' && p[-1] != '-' && p[-1] != '+' &&
p[-1] != '[' && p[-1] != '/' && p[-1] != '\\')) {
break;
}
}
CompleteFilename(p, q, b, c);
}
static char *ShellHint(const char *p, const char **ansi1, const char **ansi2) {
char *h = 0;
linenoiseCompletions c = {0};
ShellCompletion(p, &c);
if (c.len == 1) {
h = strdup(c.cvec[0] + strlen(p));
}
linenoiseFreeCompletions(&c);
return h;
}
int main(int argc, char *argv[]) {
int n, ws, pid;
char *prog, path[PATH_MAX];
sigset_t chldmask, savemask;
struct sigaction ignore, saveint, savequit;
char *p, *line, **args, *arg, *start, *state, prompt[64];
linenoiseSetFreeHintsCallback(free);
linenoiseSetHintsCallback(ShellHint);
linenoiseSetCompletionCallback(ShellCompletion);
stpcpy(prompt, "$ ");
while ((line = linenoiseWithHistory(prompt, "cmd"))) {
n = 0;
start = line;
args = xcalloc(1, sizeof(*args));
while ((arg = strtok_r(start, " \t\r\n", &state))) {
args = xrealloc(args, (++n + 1) * sizeof(*args));
args[n - 1] = arg;
args[n - 0] = 0;
start = 0;
}
if (n > 0) {
if ((prog = commandv(args[0], path, sizeof(path)))) {
ignore.sa_flags = 0;
ignore.sa_handler = SIG_IGN;
sigemptyset(&ignore.sa_mask);
sigaction(SIGINT, &ignore, &saveint);
sigaction(SIGQUIT, &ignore, &savequit);
sigemptyset(&chldmask);
sigaddset(&chldmask, SIGCHLD);
sigprocmask(SIG_BLOCK, &chldmask, &savemask);
if (!fork()) {
sigaction(SIGINT, &saveint, 0);
sigaction(SIGQUIT, &savequit, 0);
sigprocmask(SIG_SETMASK, &savemask, 0);
execv(prog, args);
_Exit(127);
}
wait(&ws);
p = prompt;
if (WIFEXITED(ws)) {
if (WEXITSTATUS(ws)) {
if (!__nocolor) p = stpcpy(p, "\e[1;31m");
p = stpcpy(p, "rc=");
p = FormatInt32(p, WEXITSTATUS(ws));
if (!__nocolor) p = stpcpy(p, "\e[0m");
*p++ = ' ';
}
} else {
if (!__nocolor) p = stpcpy(p, "\e[1;31m");
p = stpcpy(p, "rc=");
p = stpcpy(p, strsignal(WTERMSIG(ws)));
if (!__nocolor) p = stpcpy(p, "\e[0m");
*p++ = ' ';
}
p = stpcpy(p, "$ ");
sigaction(SIGINT, &saveint, 0);
sigaction(SIGQUIT, &savequit, 0);
sigprocmask(SIG_SETMASK, &savemask, 0);
} else {
fprintf(stderr, "%s: %s: command not found\n", argv[0], args[0]);
}
}
free(line);
free(args);
}
}