Add Linux ptrace() tutorial

This commit is contained in:
Justine Tunney 2022-03-24 08:00:21 -07:00
parent 98909b1391
commit 3b9e66ecba
14 changed files with 1290 additions and 23 deletions

View file

@ -202,7 +202,7 @@ int wait3(int *, int, struct rusage *);
int wait4(int, int *, int, struct rusage *);
int waitpid(int, int *, int);
intptr_t syscall(int, ...);
long ptrace(int, int, void *, void *);
long ptrace(int, ...);
long telldir(DIR *);
long times(struct tms *);
size_t GetFileSize(const char *);

View file

@ -21,7 +21,6 @@
#include "libc/nt/enum/pageflags.h"
#include "libc/nt/memory.h"
#include "libc/nt/runtime.h"
#include "libc/nt/struct/overlapped.h"
#include "libc/runtime/directmap.internal.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/prot.h"

View file

@ -17,16 +17,73 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/internal.h"
#include "libc/calls/strace.internal.h"
#include "libc/dce.h"
#include "libc/sysv/consts/ptrace.h"
#include "libc/sysv/errfuns.h"
static const char *__ptrace_describe_request(int x) {
if (x == -1) return "-1";
if (x == PTRACE_TRACEME) return "PTRACE_TRACEME";
if (x == PTRACE_PEEKDATA) return "PTRACE_PEEKDATA";
if (x == PTRACE_GETFPREGS) return "PTRACE_GETFPREGS";
if (x == PTRACE_PEEKTEXT) return "PTRACE_PEEKTEXT";
if (x == PTRACE_POKEDATA) return "PTRACE_POKEDATA";
if (x == PTRACE_PEEKUSER) return "PTRACE_PEEKUSER";
if (x == PTRACE_POKETEXT) return "PTRACE_POKETEXT";
if (x == PTRACE_POKEUSER) return "PTRACE_POKEUSER";
if (x == PTRACE_GETREGS) return "PTRACE_GETREGS";
if (x == PTRACE_GETREGSET) return "PTRACE_GETREGSET";
if (x == PTRACE_SETFPREGS) return "PTRACE_SETFPREGS";
if (x == PTRACE_SETREGS) return "PTRACE_SETREGS";
if (x == PTRACE_SETREGSET) return "PTRACE_SETREGSET";
if (x == PTRACE_GETSIGINFO) return "PTRACE_GETSIGINFO";
if (x == PTRACE_SETSIGINFO) return "PTRACE_SETSIGINFO";
if (x == PTRACE_PEEKSIGINFO) return "PTRACE_PEEKSIGINFO";
if (x == PTRACE_GETSIGMASK) return "PTRACE_GETSIGMASK";
if (x == PTRACE_SETSIGMASK) return "PTRACE_SETSIGMASK";
if (x == PTRACE_SETOPTIONS) return "PTRACE_SETOPTIONS";
if (x == PTRACE_GETEVENTMSG) return "PTRACE_GETEVENTMSG";
if (x == PTRACE_CONT) return "PTRACE_CONT";
if (x == PTRACE_SINGLESTEP) return "PTRACE_SINGLESTEP";
if (x == PTRACE_SYSCALL) return "PTRACE_SYSCALL";
if (x == PTRACE_LISTEN) return "PTRACE_LISTEN";
if (x == PTRACE_KILL) return "PTRACE_KILL";
if (x == PTRACE_INTERRUPT) return "PTRACE_INTERRUPT";
if (x == PTRACE_ATTACH) return "PTRACE_ATTACH";
if (x == PTRACE_SEIZE) return "PTRACE_SEIZE";
if (x == PTRACE_SECCOMP_GET_FILTER) return "PTRACE_SECCOMP_GET_FILTER";
if (x == PTRACE_DETACH) return "PTRACE_DETACH";
return "PTRACE_WUT";
}
/**
* Traces process.
*
* @param request can be PTRACE_xxx
* @note de facto linux only atm
*/
long ptrace(int request, int pid, void *addr, void *data) {
/* TODO(jart): FreeBSD addr and data args are different */
if (request == -1) return einval(); /* see consts.sh */
return sys_ptrace(request, pid, addr, data);
long ptrace(int request, ...) {
// TODO(jart): FreeBSD addr and data args are different
int pid;
va_list va;
bool ispeek;
long rc, peek;
void *addr, *data;
va_start(va, request);
pid = va_arg(va, int);
addr = va_arg(va, void *);
data = va_arg(va, void *);
va_end(va);
if (request == -1) {
rc = einval(); /* see consts.sh */
} else {
ispeek = IsLinux() && request - 1u < 3;
if (ispeek) data = &peek;
rc = sys_ptrace(request, pid, addr, data);
if (rc != -1 && ispeek) rc = peek;
}
STRACE("ptrace(%s, %d, %p, %p) → %ld% m", __ptrace_describe_request(request),
pid, addr, data);
return rc;
}

View file

@ -41,6 +41,7 @@
} while (0)
extern bool __nomultics;
extern bool __replmode;
static const char kSpecialFloats[2][2][4] = {{"INF", "inf"}, {"NAN", "nan"}};
@ -377,8 +378,15 @@ hidden int __fmt(void *fn, void *arg, const char *format, va_list va) {
goto FormatString;
case 'r':
flags |= FLAGS_REPR;
/* fallthrough */
// undocumented %r specifier
// used for good carriage return
// helps integrate loggers with repls
if (!__replmode) {
break;
} else {
p = "\r\e[K";
goto FormatString;
}
case 'q':
flags |= FLAGS_QUOTE;
@ -393,6 +401,8 @@ hidden int __fmt(void *fn, void *arg, const char *format, va_list va) {
break;
case 'n':
// nonstandard %n specifier
// used to print newlines that work in raw terminal modes
if (__nomultics) PUT('\r');
PUT('\n');
break;

View file

@ -468,6 +468,7 @@ privileged static size_t kformat(char *b, size_t n, const char *fmt, va_list va,
if (cols) --cols; /* end quote */
}
goto EmitChar;
case 'm':
if (!(x = errno) && sign == ' ' &&
(!IsWindows() || !__imp_GetLastError())) {
@ -479,7 +480,10 @@ privileged static size_t kformat(char *b, size_t n, const char *fmt, va_list va,
} else {
goto FormatDecimal;
}
case 'n':
// nonstandard %n specifier
// used to print newlines that work in raw terminal modes
if (__nomultics) {
if (p < e) *p = '\r';
++p;
@ -487,13 +491,18 @@ privileged static size_t kformat(char *b, size_t n, const char *fmt, va_list va,
if (p < e) *p = '\n';
++p;
break;
case 'r':
// undocumented %r specifier
// used for good carriage return
// helps integrate loggers with repls
if (!__replmode) {
break;
} else {
s = "\r\033[K";
goto FormatString;
}
case 'S':
c = 's';
type = 1;
@ -695,10 +704,12 @@ privileged static size_t kformat(char *b, size_t n, const char *fmt, va_list va,
privileged size_t ksnprintf(char *b, size_t n, const char *fmt, ...) {
size_t m;
va_list v;
struct Timestamps t = {0};
struct Timestamps t;
t = kenter();
va_start(v, fmt);
m = kformat(b, n, fmt, v, t);
va_end(v);
kleave(t);
return m;
}
@ -713,8 +724,12 @@ privileged size_t ksnprintf(char *b, size_t n, const char *fmt, ...) {
* @vforksafe
*/
privileged size_t kvsnprintf(char *b, size_t n, const char *fmt, va_list v) {
struct Timestamps t = {0};
return kformat(b, n, fmt, v, t);
size_t m;
struct Timestamps t;
t = kenter();
m = kformat(b, n, fmt, v, t);
kleave(t);
return m;
}
/**

View file

@ -98,7 +98,7 @@ void(vflogf)(unsigned level, const char *file, int line, FILE *f,
prog = basename(program_invocation_name);
bufmode = f->bufmode;
if (bufmode == _IOLBF) f->bufmode = _IOFBF;
if ((fprintf)(f, "%c%s%06ld:%s:%d:%.*s:%d] ", "FEWIVDNT"[level & 7], buf32,
if ((fprintf)(f, "%r%c%s%06ld:%s:%d:%.*s:%d] ", "FEWIVDNT"[level & 7], buf32,
rem1000000int64(div1000int64(dots)), file, line,
strchrnul(prog, '.') - prog, prog, getpid()) <= 0) {
vflogf_onfail(f);

View file

@ -19,6 +19,8 @@ ssize_t appendw(char **, uint64_t);
ssize_t appends(char **, const char *);
ssize_t appendf(char **, const char *, ...);
ssize_t vappendf(char **, const char *, va_list);
ssize_t kappendf(char **, const char *, ...);
ssize_t kvappendf(char **, const char *, va_list);
#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
#define appendf(BUF, FMT, ...) appendf(BUF, PFLINK(FMT), ##__VA_ARGS__)

View file

@ -56,21 +56,18 @@ static int openpathname(const char *pathname, int flags, bool *out_noclose) {
* @note microsoft unilaterally deprecated this function lool
*/
FILE *fopen(const char *pathname, const char *mode) {
FILE *f;
FILE *f = 0;
bool noclose;
int fd, flags;
STRACE("fopen(%s)", pathname);
flags = fopenflags(mode);
pathname = fixpathname(pathname, flags);
if ((fd = openpathname(pathname, flags, &noclose)) != -1) {
if ((f = fdopen(fd, mode)) != NULL) {
f->noclose = noclose;
return f;
} else {
if (!noclose) close(fd);
return NULL;
} else if (!noclose) {
close(fd);
}
} else {
return NULL;
}
STRACE("fopen(%#s, %#s) → %p% m", pathname, mode, f);
return f;
}

40
libc/stdio/kappendf.c Normal file
View file

@ -0,0 +1,40 @@
/*-*- 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/stdio/append.internal.h"
/**
* Appends formatted string to buffer w/ kprintf, e.g.
*
* char *b = 0;
* kappendf(&b, "hello %d\n", 123);
* free(b);
*
* @return bytes appended or -1 if `ENOMEM`
* @see appendz(b).i to get buffer length
* @note O(1) amortized buffer growth
* @see kprintf()
*/
ssize_t kappendf(char **b, const char *fmt, ...) {
int n;
va_list va;
va_start(va, fmt);
n = kvappendf(b, fmt, va);
va_end(va);
return n;
}

68
libc/stdio/kvappendf.c Normal file
View file

@ -0,0 +1,68 @@
/*-*- 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/intrin/kprintf.h"
#include "libc/macros.internal.h"
#include "libc/mem/mem.h"
#include "libc/stdio/append.internal.h"
#define W sizeof(size_t)
/**
* Appends formatted string to buffer.
*
* This is an alternative to vappendf() that uses the kprintf()
* formatting facility. This has some advantages in terms of
* performance, code size, and memory safety. There's also
* disadvantages, such as lack of floating-point directives.
*
* @see kprintf()
*/
ssize_t kvappendf(char **b, const char *f, va_list v) {
char *p;
int r, s;
size_t n;
va_list w;
struct appendz z;
z = appendz((p = *b));
va_copy(w, v);
if ((r = kvsnprintf(p + z.i, z.n ? z.n - W - z.i : 0, f, v)) >= 0) {
n = ROUNDUP(z.i + r + 1, 8) + W;
if (n > z.n) {
if (!z.n) z.n = W * 2;
while (n > z.n) z.n += z.n >> 1;
z.n = ROUNDUP(z.n, W);
if ((p = realloc(p, z.n))) {
z.n = malloc_usable_size(p);
assert(!(z.n & (W - 1)));
s = kvsnprintf(p + z.i, z.n - W - z.i, f, w);
assert(s == r);
*b = p;
} else {
va_end(w);
return -1;
}
}
z.i += r;
if (!IsTiny() && W == 8) z.i |= (size_t)APPEND_COOKIE << 48;
*(size_t *)(p + z.n - W) = z.i;
}
va_end(w);
return r;
}

View file

@ -18,7 +18,6 @@
*/
#include "libc/bits/safemacros.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/strace.internal.h"
#include "libc/fmt/fmt.h"
#include "libc/intrin/kprintf.h"
#include "libc/macros.internal.h"

1048
tool/build/strace.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -22,6 +22,7 @@ TOOL_NET_COMS = \
o/$(MODE)/tool/net/redbean-static.com \
o/$(MODE)/tool/net/redbean-unsecure.com \
o/$(MODE)/tool/net/redbean-original.com \
o/$(MODE)/tool/net/redbean-assimilate.com \
o/$(MODE)/tool/net/echoserver.com \
o/$(MODE)/tool/net/wb.com
@ -314,6 +315,33 @@ o/$(MODE)/tool/net/redbean-original.com.dbg: \
o/$(MODE)/tool/net/redbean-original.o: tool/net/redbean.c o/$(MODE)/tool/net/redbean.o
@$(COMPILE) -AOBJECTIFY.c $(OBJECTIFY.c) -DSTATIC -DUNSECURE -DREDBEAN=\"redbean-original\" $(OUTPUT_OPTION) $<
# REDBEAN-ASSIMILATE.COM
#
# Same as REDBEAN.COM except without no-modify-self behavior.
o/$(MODE)/tool/net/redbean-assimilate.com.dbg: \
o/$(MODE)/tool/net/redbean.com.dbg
@cp -f $< $@
o/$(MODE)/tool/net/redbean-assimilate.com: \
o/$(MODE)/tool/net/redbean-assimilate.com.dbg \
o/$(MODE)/third_party/infozip/zip.com \
o/$(MODE)/tool/build/symtab.com \
tool/net/net.mk \
tool/net/help.txt \
tool/net/.init.lua \
tool/net/favicon.ico \
tool/net/redbean.png
@$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@
@$(COMPILE) -AMKDIR -T$@ mkdir -p o/$(MODE)/tool/net/.redbean-assimilate
@$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com -o o/$(MODE)/tool/net/.redbean-assimilate/.symtab $<
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/infozip/zip.com -9qj $@ \
o/$(MODE)/tool/net/.redbean-assimilate/.symtab \
tool/net/help.txt \
tool/net/.init.lua \
tool/net/favicon.ico \
tool/net/redbean.png
.PHONY: o/$(MODE)/tool/net
o/$(MODE)/tool/net: \
$(TOOL_NET_BINS) \

View file

@ -2674,7 +2674,7 @@ static void LaunchBrowser(const char *path) {
sigprocmask(SIG_BLOCK, &chldmask, &savemask);
CHECK_NE(-1, (pid = fork()));
if (!pid) {
setpgid(getpid(), getpid());
setpgid(getpid(), getpid()); // ctrl-c'ing redbean shouldn't kill browser
sigaction(SIGINT, &saveint, 0);
sigaction(SIGQUIT, &savequit, 0);
sigprocmask(SIG_SETMASK, &savemask, 0);
@ -6605,7 +6605,7 @@ static void RestoreApe(void) {
WARNF("(srvr) can't restore .ape");
free(p);
} else {
WARNF("(srvr) /.ape not found");
INFOF("(srvr) /.ape not found");
}
}
@ -6907,6 +6907,10 @@ void RedBean(int argc, char *argv[]) {
if (daemonize) {
Daemonize();
} else {
// xxx: create process group to make it easier to propagate SIGTERM
// to children. the downside to doing this seems to be that
// ctrl-c isn't propagating as expected when running redbean
// underneath strace.com :|
setpgid(getpid(), getpid());
if (logpath) {
close(2);