From a00feef713edc4d586fb41da30e40c22ad3641ce Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Fri, 21 Jul 2023 17:16:46 -0700 Subject: [PATCH] Support debugging eXecute-only ELF segments --- blink/abort.c | 20 +++++++++++++++----- blink/bios.c | 2 +- blink/blink.c | 3 +++ blink/blinkenlights.c | 18 +++++++++++------- blink/checked.h | 9 +++++++++ blink/debug.c | 6 +++--- blink/dis.c | 4 ++-- blink/machine.h | 6 ++++-- blink/memory.c | 23 +++++++++++++++++++++-- blink/syscall.c | 2 ++ blink/util.h | 4 +++- blink/watch.c | 2 +- 12 files changed, 75 insertions(+), 24 deletions(-) diff --git a/blink/abort.c b/blink/abort.c index defc46345..3d0dbea66 100644 --- a/blink/abort.c +++ b/blink/abort.c @@ -18,20 +18,30 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include +#include "blink/assert.h" +#include "blink/macros.h" #include "blink/stats.h" #include "blink/util.h" -bool g_exitdontabort; +static struct AbortHooks { + int n; + aborthook_f *p[4]; +} g_aborthooks; + +void AtAbort(aborthook_f *hook) { + unassert(g_aborthooks.n < ARRAYLEN(g_aborthooks.p)); + g_aborthooks.p[g_aborthooks.n++] = hook; +} void Abort(void) { + int i; #ifndef NDEBUG if (FLAG_statistics) { PrintStats(); } #endif - if (g_exitdontabort) { - exit(1); - } else { - abort(); + for (i = g_aborthooks.n; i--;) { + g_aborthooks.p[i](); } + abort(); } diff --git a/blink/bios.c b/blink/bios.c index 4b2d1fe38..c03d998d6 100644 --- a/blink/bios.c +++ b/blink/bios.c @@ -475,7 +475,7 @@ static void VidyaServiceWriteTeletype(u8 ch) { void VidyaServiceSetMode(int mode) { int cols, lines; vidya = mode; - if (LookupAddress(m, 0xB0000)) { + if (SpyAddress(m, 0xB0000)) { ptyisenabled = true; lines = 25; switch (mode) { diff --git a/blink/blink.c b/blink/blink.c index 89b473277..3dde8b7d4 100644 --- a/blink/blink.c +++ b/blink/blink.c @@ -364,6 +364,9 @@ void exit(int status) { int main(int argc, char *argv[]) { SetupWeb(); GetStartDir(); +#ifndef NDEBUG + AtAbort(PrintStats); +#endif #ifndef DISABLE_STRACE setlocale(LC_ALL, ""); #endif diff --git a/blink/blinkenlights.c b/blink/blinkenlights.c index 07d562902..559bf79e6 100644 --- a/blink/blinkenlights.c +++ b/blink/blinkenlights.c @@ -664,7 +664,7 @@ static int VirtualBing(i64 v) { int rc; jmp_buf busted; onbusted = &busted; - if ((p = (u8 *)LookupAddress(m, v))) { + if ((p = SpyAddress(m, v))) { if (!setjmp(busted)) { rc = kCp437[p[0] & 255]; } else { @@ -686,7 +686,7 @@ static int VirtualShadow(i64 v) { jmp_buf busted; if (IsShadow(v)) return -2; onbusted = &busted; - if ((p = (char *)LookupAddress(m, (v >> 3) + 0x7fff8000))) { + if ((p = (char *)SpyAddress(m, (v >> 3) + 0x7fff8000))) { if (!setjmp(busted)) { rc = p[0] & 0xff; } else { @@ -954,6 +954,7 @@ void TuiSetup(void) { CommonSetup(); VfsTcgetattr(ttyout, &oldterm); atexit(TtyRestore); + AtAbort(TtyRestore); once = true; report = true; } @@ -1870,7 +1871,7 @@ static void DrawFrames(struct Panel *p) { } ++i; if (((m->ss.base + bp) & 0xfff) > 0xff0) break; - if (!(r = LookupAddress(m, m->ss.base + bp))) { + if (!(r = SpyAddress(m, m->ss.base + bp))) { AppendPanel(p, i - framesstart, "CORRUPT FRAME POINTER"); break; } @@ -1889,7 +1890,7 @@ static void CheckFramePointerImpl(void) { lastbp = bp; rp = m->ip; while (bp) { - if (!(r = LookupAddress(m, m->ss.base + bp))) { + if (!(r = SpyAddress(m, m->ss.base + bp))) { LOGF("corrupt frame: %0*" PRIx64 "", GetAddrHexWidth(), bp); ThrowProtectionFault(m); } @@ -2132,8 +2133,8 @@ void Redraw(bool force) { ShowHistory(); return; } - LookupAddress(m, m->ip); - LookupAddress(m, Get64(m->sp)); + LookupAddress(m, m->ip); // want page fault + LookupAddress(m, Get64(m->sp)); // want page fault BEGIN_NO_PAGE_FAULTS; start_draw = GetTime(); execsecs = ToNanoseconds(SubtractTime(start_draw, last_draw)) * 1e-9; @@ -3679,9 +3680,12 @@ int main(int argc, char *argv[]) { struct System *s; static struct sigaction sa; setlocale(LC_ALL, ""); - g_exitdontabort = true; SetupWeb(); GetStartDir(); + AtAbort(PrintStats); +#ifndef NDEBUG + AtAbort(PrintStats); +#endif // Ensure utf-8 is printed correctly on windows, this method // has issues(http://stackoverflow.com/a/10884364/4279) but // should work for at least windows 7 and newer. diff --git a/blink/checked.h b/blink/checked.h index 88cd2951e..8bd9134b7 100644 --- a/blink/checked.h +++ b/blink/checked.h @@ -6,9 +6,18 @@ #define __STDC_VERSION_STDCKDINT_H__ 202311L +#if ((defined(__GNUC__) && __GNUC__ >= 5 && !defined(__ICC)) || \ + (defined(__has_builtin) && (__has_builtin(__builtin_add_overflow) && \ + __has_builtin(__builtin_sub_overflow) && \ + __has_builtin(__builtin_mul_overflow)))) #define ckd_add(res, x, y) __builtin_add_overflow((x), (y), (res)) #define ckd_sub(res, x, y) __builtin_sub_overflow((x), (y), (res)) #define ckd_mul(res, x, y) __builtin_mul_overflow((x), (y), (res)) +#else +#define ckd_add(res, x, y) (*(res) = (x) + (y), 0) +#define ckd_sub(res, x, y) (*(res) = (x) - (y), 0) +#define ckd_mul(res, x, y) (*(res) = (x) * (y), 0) +#endif #endif /* */ #endif /* EASYCKDINT_H_ */ diff --git a/blink/debug.c b/blink/debug.c index 68bfa2502..e5029fcd3 100644 --- a/blink/debug.c +++ b/blink/debug.c @@ -136,14 +136,14 @@ int GetInstruction(struct Machine *m, i64 pc, struct XedDecodedInst *x) { int i, rc, err; u8 copy[15], *toil, *addr; BEGIN_NO_PAGE_FAULTS; - if ((addr = LookupAddress(m, pc))) { + if ((addr = SpyAddress(m, pc))) { if ((i = 4096 - (pc & 4095)) >= 15) { if (!DecodeInstruction(x, addr, 15, m->mode.omode)) { rc = 0; } else { rc = kMachineDecodeError; } - } else if ((toil = LookupAddress(m, pc + i))) { + } else if ((toil = SpyAddress(m, pc + i))) { memcpy(copy, addr, i); memcpy(copy + i, toil, 15 - i); if (!DecodeInstruction(x, copy, 15, m->mode.omode)) { @@ -287,7 +287,7 @@ const char *GetBacktrace(struct Machine *m) { } ++i; if (((m->ss.base + bp) & 0xfff) > 0xff0) break; - if (!(r = LookupAddress(m, m->ss.base + bp))) { + if (!(r = SpyAddress(m, m->ss.base + bp))) { APPEND(" [CORRUPT FRAME POINTER]"); break; } diff --git a/blink/dis.c b/blink/dis.c index 3488c3728..dd2e4db7e 100644 --- a/blink/dis.c +++ b/blink/dis.c @@ -229,14 +229,14 @@ static long DisAppendOpLines(struct Dis *d, struct Machine *m, i64 addr) { } } n = MAX(1, MIN(15, n)); - if (!(r = LookupAddress(m, addr))) return -1; + if (!(r = SpyAddress(m, addr))) return -1; k = 0x1000 - (addr & 0xfff); if (n <= k) { p = (u8 *)r; } else { p = b; memcpy(b, r, k); - if ((r = LookupAddress(m, addr + k))) { + if ((r = SpyAddress(m, addr + k))) { memcpy(b + k, r, n - k); } else { n = k; diff --git a/blink/machine.h b/blink/machine.h index 4c1319f71..b3ea70b61 100644 --- a/blink/machine.h +++ b/blink/machine.h @@ -510,6 +510,7 @@ bool HasPageLock(const struct Machine *, i64) nosideeffect; void CollectPageLocks(struct Machine *); u8 *LookupAddress(struct Machine *, i64); u8 *LookupAddress2(struct Machine *, i64, u64, u64); +u8 *SpyAddress(struct Machine *, i64); u8 *Load(struct Machine *, i64, size_t, u8 *); u8 *MallocPage(void); u8 *RealAddress(struct Machine *, i64); @@ -806,8 +807,9 @@ void LogCodOp(struct Machine *, const char *); #endif MICRO_OP_SAFE u8 Cpl(struct Machine *m) { - return !m->metal ? 3u : - m->mode.genmode != XED_GEN_MODE_REAL ? (m->cs.sel & 3u) : 0u; + return !m->metal ? 3u + : m->mode.genmode != XED_GEN_MODE_REAL ? (m->cs.sel & 3u) + : 0u; } #define BEGIN_NO_PAGE_FAULTS \ diff --git a/blink/memory.c b/blink/memory.c index 08fac137d..1119aa53e 100644 --- a/blink/memory.c +++ b/blink/memory.c @@ -263,8 +263,7 @@ u64 FindPageTableEntry(struct Machine *m, u64 page) { u8 *LookupAddress2(struct Machine *m, i64 virt, u64 mask, u64 need) { u8 *host; u64 entry; - if (!m->metal || - m->mode.omode == XED_MODE_LONG || + if (!m->metal || m->mode.omode == XED_MODE_LONG || (m->mode.genmode != XED_GEN_MODE_REAL && (m->system->cr0 & CR0_PG))) { if (!(entry = FindPageTableEntry(m, virt & -4096))) { return 0; @@ -307,6 +306,26 @@ flattencalls u8 *GetAddress(struct Machine *m, i64 v) { return LookupAddress(m, v); } +/** + * Translates virtual address into pointer. + * + * This function bypasses memory protection, since it's used to display + * memory in the debugger tui. That's useful, for example, if debugging + * programs that specify an eXecute-only program header. + * + * It's recommended that the caller use: + * + * BEGIN_NO_PAGE_FAULTS; + * i64 address = ...; + * u8 *pointer = SpyAddress(m, address); + * END_NO_PAGE_FAULTS; + * + * When calling this function. + */ +u8 *SpyAddress(struct Machine *m, i64 virt) { + return LookupAddress2(m, virt, 0, 0); +} + u8 *ResolveAddress(struct Machine *m, i64 v) { u8 *r; if ((r = GetAddress(m, v))) return r; diff --git a/blink/syscall.c b/blink/syscall.c index 73d0785a8..a4883e57d 100644 --- a/blink/syscall.c +++ b/blink/syscall.c @@ -538,9 +538,11 @@ static int SysSpawn(struct Machine *m, u64 flags, u64 stack, u64 ptid, u64 ctid, } if (((flags & CLONE_PARENT_SETTID_LINUX) && ((ptid & (sizeof(int) - 1)) || + !IsValidMemory(m, ptid, 4, PROT_READ | PROT_WRITE) || !(ptid_ptr = (_Atomic(int) *)LookupAddress(m, ptid)))) || ((flags & CLONE_CHILD_SETTID_LINUX) && ((ctid & (sizeof(int) - 1)) || + !IsValidMemory(m, ctid, 4, PROT_READ | PROT_WRITE) || !(ctid_ptr = (_Atomic(int) *)LookupAddress(m, ctid))))) { LOGF("bad clone() ptid / ctid pointers: %#" PRIx64, flags); return efault(); diff --git a/blink/util.h b/blink/util.h index c1d01338a..e85f770c6 100644 --- a/blink/util.h +++ b/blink/util.h @@ -10,12 +10,14 @@ #include "blink/builtin.h" #include "blink/types.h" +typedef void aborthook_f(void); + extern int optind_; extern char *optarg_; extern const short kCp437[256]; -extern bool g_exitdontabort; _Noreturn void Abort(void); +void AtAbort(aborthook_f *); char *GetStartDir(void); int GetOpt(int, char *const[], const char *); u64 tpenc(uint32_t); diff --git a/blink/watch.c b/blink/watch.c index b39c375cd..e57c42c6d 100644 --- a/blink/watch.c +++ b/blink/watch.c @@ -87,7 +87,7 @@ ssize_t IsAtWatchpoint(struct Watchpoints *wps, struct Machine *m) { m->ip = oldip; // TODO(jart): Handle case of overlapping page boundary. // TODO(jart): Possibly track munmap() type cases. - if ((r = LookupAddress(m, wps->p[i].addr))) { + if ((r = SpyAddress(m, wps->p[i].addr))) { w = Read64(r); if (!wps->p[i].initialized) { wps->p[i].oldvalue = w;