Rewrite brk() and sbrk()

This change reduces the .bss memory requirement for all executables by
O(64kb). The brk system calls are now fully tested and figured out and
might be useful for tiny programs that only target System Five.
This commit is contained in:
Justine Tunney 2022-10-01 23:11:56 -07:00
parent bc8532688b
commit 5005f2e446
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
23 changed files with 643 additions and 294 deletions

View file

@ -427,7 +427,7 @@ SECTIONS {
KEEP(*(.bssepilogue)) KEEP(*(.bssepilogue))
/*END: NT FORK COPYING */ /*END: NT FORK COPYING */
. = ALIGN(FRAMESIZE); /* for brk()/sbrk() allocation */ . = ALIGN(PAGESIZE);
HIDDEN(_end = .); HIDDEN(_end = .);
PROVIDE_HIDDEN(end = .); PROVIDE_HIDDEN(end = .);
} :Bss } :Bss

View file

@ -73,33 +73,25 @@ IMAGE_BASE_VIRTUAL ?= 0x400000
IGNORE := $(shell $(ECHO) -2 ♥cosmo) IGNORE := $(shell $(ECHO) -2 ♥cosmo)
IGNORE := $(shell $(MKDIR) o/tmp) IGNORE := $(shell $(MKDIR) o/tmp)
ifneq ("$(wildcard o/third_party/gcc/bin/x86_64-pc-linux-gnu-as.exe)","") ifneq ("$(wildcard o/third_party/gcc/bin/x86_64-pc-linux-gnu-*)","")
AS = o/third_party/gcc/bin/x86_64-pc-linux-gnu-as.exe PREFIX = o/third_party/gcc/bin/x86_64-pc-linux-gnu-
CC = o/third_party/gcc/bin/x86_64-pc-linux-gnu-gcc.exe
CXX = o/third_party/gcc/bin/x86_64-pc-linux-gnu-g++.exe
CXXFILT = o/third_party/gcc/bin/x86_64-pc-linux-gnu-c++filt.exe
LD = o/third_party/gcc/bin/x86_64-pc-linux-gnu-ld.bfd.exe
NM = o/third_party/gcc/bin/x86_64-pc-linux-gnu-nm.exe
GCC = o/third_party/gcc/bin/x86_64-pc-linux-gnu-gcc.exe
STRIP = o/third_party/gcc/bin/x86_64-pc-linux-gnu-strip.exe
OBJCOPY = o/third_party/gcc/bin/x86_64-pc-linux-gnu-objcopy.exe
OBJDUMP = o/third_party/gcc/bin/x86_64-pc-linux-gnu-objdump.exe
ADDR2LINE = $(PWD)/o/third_party/gcc/bin/x86_64-pc-linux-gnu-addr2line.exe
else else
IGNORE := $(shell build/bootstrap/unbundle.com) IGNORE := $(shell build/bootstrap/unbundle.com)
AS = o/third_party/gcc/bin/x86_64-linux-musl-as PREFIX = o/third_party/gcc/bin/x86_64-linux-musl-
CC = o/third_party/gcc/bin/x86_64-linux-musl-gcc
CXX = o/third_party/gcc/bin/x86_64-linux-musl-g++
CXXFILT = o/third_party/gcc/bin/x86_64-linux-musl-c++filt
LD = o/third_party/gcc/bin/x86_64-linux-musl-ld.bfd
NM = o/third_party/gcc/bin/x86_64-linux-musl-nm
GCC = o/third_party/gcc/bin/x86_64-linux-musl-gcc
STRIP = o/third_party/gcc/bin/x86_64-linux-musl-strip
OBJCOPY = o/third_party/gcc/bin/x86_64-linux-musl-objcopy
OBJDUMP = o/third_party/gcc/bin/x86_64-linux-musl-objdump
ADDR2LINE = $(PWD)/o/third_party/gcc/bin/x86_64-linux-musl-addr2line
endif endif
AS = $(PREFIX)as
CC = $(PREFIX)gcc
CXX = $(PREFIX)g++
CXXFILT = $(PREFIX)c++filt
LD = $(PREFIX)ld.bfd
NM = $(PREFIX)nm
GCC = $(PREFIX)gcc
STRIP = $(PREFIX)strip
OBJCOPY = $(PREFIX)objcopy
OBJDUMP = $(PREFIX)objdump
ADDR2LINE = $(PWD)/$(PREFIX)addr2line
export ADDR2LINE export ADDR2LINE
export LC_ALL export LC_ALL
export MKDIR export MKDIR

View file

@ -1,45 +0,0 @@
#ifndef COSMOPOLITAN_LIBC_BITS_BITOP_H_
#define COSMOPOLITAN_LIBC_BITS_BITOP_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
#define bts(MEM, BIT) __BitOp("bts", BIT, MEM) /** bit test and set */
#define btr(MEM, BIT) __BitOp("btr", BIT, MEM) /** bit test and reset */
#define btc(MEM, BIT) __BitOp("btc", BIT, MEM) /** bit test and complement */
#define lockbts(MEM, BIT) __BitOp("lock bts", BIT, MEM)
#define lockbtr(MEM, BIT) __BitOp("lock btr", BIT, MEM)
#define lockbtc(MEM, BIT) __BitOp("lock btc", BIT, MEM)
#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
#define __BitOp(OP, BIT, MEM) \
({ \
bool OldBit; \
if (__builtin_constant_p(BIT)) { \
asm(CFLAG_ASM(OP "%z1\t%2,%1") \
: CFLAG_CONSTRAINT(OldBit), \
"+m"((MEM)[(BIT) / (sizeof((MEM)[0]) * CHAR_BIT)]) \
: "J"((BIT) % (sizeof((MEM)[0]) * CHAR_BIT)) \
: "cc"); \
} else if (sizeof((MEM)[0]) == 2) { \
asm(CFLAG_ASM(OP "\t%w2,%1") \
: CFLAG_CONSTRAINT(OldBit), "+m"((MEM)[0]) \
: "r"(BIT) \
: "cc"); \
} else if (sizeof((MEM)[0]) == 4) { \
asm(CFLAG_ASM(OP "\t%k2,%1") \
: CFLAG_CONSTRAINT(OldBit), "+m"((MEM)[0]) \
: "r"(BIT) \
: "cc"); \
} else if (sizeof((MEM)[0]) == 8) { \
asm(CFLAG_ASM(OP "\t%q2,%1") \
: CFLAG_CONSTRAINT(OldBit), "+m"((MEM)[0]) \
: "r"(BIT) \
: "cc"); \
} \
OldBit; \
})
#endif /* __GNUC__ && !__STRICT_ANSI__ */
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_BITS_BITOP_H_ */

View file

@ -19,7 +19,9 @@
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/intrin/describeflags.internal.h" #include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/kprintf.h" #include "libc/intrin/kprintf.h"
#include "libc/intrin/weaken.h"
#include "libc/macros.internal.h" #include "libc/macros.internal.h"
#include "libc/runtime/brk.internal.h"
#include "libc/runtime/memtrack.internal.h" #include "libc/runtime/memtrack.internal.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
#include "libc/runtime/winargs.internal.h" #include "libc/runtime/winargs.internal.h"
@ -28,6 +30,11 @@
#define UNSHADOW(x) ((int64_t)(MAX(0, (x)-0x7fff8000)) << 3) #define UNSHADOW(x) ((int64_t)(MAX(0, (x)-0x7fff8000)) << 3)
#define FRAME(x) ((int)((x) >> 16)) #define FRAME(x) ((int)((x) >> 16))
forceinline pureconst bool IsBrkFrame(int x) {
unsigned char *p = (unsigned char *)((intptr_t)((uintptr_t)x << 32) >> 16);
return _weaken(__brk) && p >= _end && p < _weaken(__brk)->p;
}
static const char *GetFrameName(int x) { static const char *GetFrameName(int x) {
if (!x) { if (!x) {
return "null"; return "null";
@ -41,6 +48,8 @@ static const char *GetFrameName(int x) {
return "arena"; return "arena";
} else if (IsStaticStackFrame(x)) { } else if (IsStaticStackFrame(x)) {
return "stack"; return "stack";
} else if (IsBrkFrame(x)) {
return "brk";
} else if (IsGfdsFrame(x)) { } else if (IsGfdsFrame(x)) {
return "g_fds"; return "g_fds";
} else if (IsZiposFrame(x)) { } else if (IsZiposFrame(x)) {
@ -68,7 +77,7 @@ static const char *GetFrameName(int x) {
const char *(DescribeFrame)(char buf[32], int x) { const char *(DescribeFrame)(char buf[32], int x) {
char *p; char *p;
if (IsShadowFrame(x)) { if (IsShadowFrame(x)) {
ksnprintf(buf, 64, "%s %s %.8x", GetFrameName(x), ksnprintf(buf, 32, "%s %s %.8x", GetFrameName(x),
GetFrameName(FRAME(UNSHADOW(ADDR(x)))), FRAME(UNSHADOW(ADDR(x)))); GetFrameName(FRAME(UNSHADOW(ADDR(x)))), FRAME(UNSHADOW(ADDR(x))));
return buf; return buf;
} else { } else {

View file

@ -0,0 +1,37 @@
/*-*- 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 2022 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/midpoint.h"
#include "libc/runtime/memtrack.internal.h"
noasan unsigned FindMemoryInterval(const struct MemoryIntervals *mm, int x) {
unsigned l, m, r;
l = 0;
r = mm->i;
while (l < r) {
m = _midpoint(l, r);
if (mm->p[m].y < x) {
l = m + 1;
} else {
r = m;
}
}
_unassert(l == mm->i || x <= mm->p[l].y);
return l;
}

View file

@ -43,6 +43,7 @@
#include "libc/nt/runtime.h" #include "libc/nt/runtime.h"
#include "libc/nt/thunk/msabi.h" #include "libc/nt/thunk/msabi.h"
#include "libc/nt/winsock.h" #include "libc/nt/winsock.h"
#include "libc/runtime/brk.internal.h"
#include "libc/runtime/internal.h" #include "libc/runtime/internal.h"
#include "libc/runtime/memtrack.internal.h" #include "libc/runtime/memtrack.internal.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
@ -103,8 +104,17 @@ privileged static inline bool kistextpointer(const void *p) {
return _base <= (const unsigned char *)p && (const unsigned char *)p < _etext; return _base <= (const unsigned char *)p && (const unsigned char *)p < _etext;
} }
privileged static inline unsigned char *kend(void) {
unsigned char *p;
if (_weaken(__brk) && (p = _weaken(__brk)->p)) {
return p;
} else {
return _end;
}
}
privileged static inline bool kisimagepointer(const void *p) { privileged static inline bool kisimagepointer(const void *p) {
return _base <= (const unsigned char *)p && (const unsigned char *)p < _end; return _base <= (const unsigned char *)p && (const unsigned char *)p < kend();
} }
privileged static inline bool kischarmisaligned(const char *p, signed char t) { privileged static inline bool kischarmisaligned(const char *p, signed char t) {

View file

@ -12,6 +12,7 @@
#define kNtMemTopDown 0x100000 #define kNtMemTopDown 0x100000
#define kNtMemWriteWatch 0x200000 #define kNtMemWriteWatch 0x200000
#define kNtMemPhysical 0x400000 #define kNtMemPhysical 0x400000
#define kNtMemImage 0x1000000
#define kNtMemLargePages 0x20000000 #define kNtMemLargePages 0x20000000
#define kNtMem4mbPages 0x80000000 #define kNtMem4mbPages 0x80000000

View file

@ -3567,7 +3567,7 @@ imp 'WakeByAddressSingle' WakeByAddressSingle API-MS-Win-Core-Synch-l1-2
# “The functions and structures in [for these APIs] are internal to # “The functions and structures in [for these APIs] are internal to
# the operating system and subject to change from one release of # the operating system and subject to change from one release of
# Windows to the next, and possibly even between service packs for # Windows to the next, and possibly even between service packs for
# each release.” ──Quoth MSDN */ # each release.” ──Quoth MSDN
# #
# Name Actual DLL Hint Arity # Name Actual DLL Hint Arity
imp 'AlpcAdjustCompletionListConcurrencyCount' AlpcAdjustCompletionListConcurrencyCount ntdll 12 imp 'AlpcAdjustCompletionListConcurrencyCount' AlpcAdjustCompletionListConcurrencyCount ntdll 12

View file

@ -1,10 +1,9 @@
#ifndef COSMOPOLITAN_LIBC_NT_STRUCT_IMAGESECTIONHEADER_H_ #ifndef COSMOPOLITAN_LIBC_NT_STRUCT_IMAGESECTIONHEADER_H_
#define COSMOPOLITAN_LIBC_NT_STRUCT_IMAGESECTIONHEADER_H_ #define COSMOPOLITAN_LIBC_NT_STRUCT_IMAGESECTIONHEADER_H_
#include "libc/nt/pedef.internal.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0) #if !(__ASSEMBLER__ + __LINKER__ + 0)
struct NtImageSectionHeader { struct NtImageSectionHeader {
uint8_t Name[kNtImageSizeofShortName]; uint8_t Name[8];
union { union {
uint32_t PhysicalAddress; uint32_t PhysicalAddress;
uint32_t VirtualSize; uint32_t VirtualSize;

View file

@ -18,45 +18,122 @@
*/ */
#include "libc/assert.h" #include "libc/assert.h"
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/intrin/nopl.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/macros.internal.h" #include "libc/macros.internal.h"
#include "libc/runtime/brk.internal.h"
#include "libc/runtime/directmap.internal.h"
#include "libc/runtime/memtrack.internal.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
#include "libc/sysv/consts/map.h" #include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/prot.h" #include "libc/sysv/consts/prot.h"
#include "libc/sysv/errfuns.h" #include "libc/sysv/errfuns.h"
#include "libc/thread/thread.h"
#include "libc/thread/tls.h"
uintptr_t __break; struct Brk __brk;
static bool OverlapsMmappedMemory(unsigned char *p, size_t n) {
int a, b, i;
_unassert(n);
a = (intptr_t)p >> 16;
b = (intptr_t)(p + n - 1) >> 16;
i = FindMemoryInterval(&_mmi, a);
if (i < _mmi.i) {
if (a <= _mmi.p[i].x && _mmi.p[i].x <= b) return true;
if (a <= _mmi.p[i].y && _mmi.p[i].y <= b) return true;
if (_mmi.p[i].x <= a && b <= _mmi.p[i].y) return true;
}
return false;
}
static unsigned char *brk_unlocked(unsigned char *p) {
int rc;
struct DirectMap dm;
_unassert(!((intptr_t)__brk.p & (PAGESIZE - 1)));
if (p >= __brk.p) {
p = (unsigned char *)ROUNDUP((intptr_t)p, PAGESIZE);
} else {
p = (unsigned char *)ROUNDDOWN((intptr_t)p, PAGESIZE);
}
if (IsWindows()) {
rc = enosys();
} else if (p < _end) {
rc = einval();
} else if (p > __brk.p) {
if (!OverlapsMmappedMemory(__brk.p, p - __brk.p)) {
// we always polyfill this system call because
// 1. Linux has brk() but its behavior is poorly documented
// 2. FreeBSD has sbrk(int):int but it's foreseeable it could go away
// 3. XNU/OpenBSD/NetBSD have all deleted this interface in the kernel
dm = sys_mmap(__brk.p, p - __brk.p, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
rc = (int)(intptr_t)dm.addr; // safe b/c __brk.p is page-aligned
} else {
rc = eexist();
}
} else if (p < __brk.p) {
rc = sys_munmap(p, __brk.p - p);
} else {
rc = 0;
}
if (rc != -1) {
__brk.p = p;
return 0;
} else {
return (unsigned char *)-1;
}
}
int brk_lock(void) {
pthread_mutex_lock(&__brk.m);
return 0;
}
void brk_unlock(void) {
pthread_mutex_unlock(&__brk.m);
}
#ifdef _NOPL0
#define brk_lock() _NOPL0("__threadcalls", brk_lock)
#define brk_unlock() _NOPL0("__threadcalls", brk_unlock)
#else
#define brk_lock() (__threaded ? brk_lock() : 0)
#define brk_unlock() (__threaded ? brk_unlock() : 0)
#endif
/** /**
* Sets end of data section. * Sets end of data section.
* *
* This can be used to allocate and deallocate memory. It won't * Your program break starts right after end of `.bss` as defined
* conflict with malloc() and mmap(NULL, ...) allocations since * by the external linker-defined variable `end`. Setting it to a
* APE binaries load the image at 0x440000 and does allocations * higher address will allocate more memory. After using this you
* starting at 0x100080040000. You should consult _end, or call * may dealocate memory by specifying it back to a lower address.
* sbrk(NULL), to figure out where the existing break is first.
* *
* @return 0 on success or -1 w/ errno * The only virtue of brk(), and sbrk(), aside from compatibility
* @see mmap(), sbrk(), _end * with legacy software, is it's tinier than mmap() because since
* this API only supports Unix, we don't bother doing the complex
* memory interval tracking that mmap() does.
*
* @param neu is the new end address of data segment, which shall
* be rounded automatically to a 4096-byte granularity
* @return 0 on success, or -1 w/ errno
* @raise EINVAL if `neu` is less than the `end` of `.bss`
* @raise EEXIST if expanded break would overlap existing mmap() memory
* @raise ENOMEM if `RLIMIT_DATA` / `RLIMIT_AS` / `RLIMIT_RSS` is exceeded
* @raise ENOSYS on Windows because WIN32 puts random stuff after your break
* @threadsafe
*/ */
int brk(void *end) { int brk(void *neu) {
int rc; unsigned char *rc;
uintptr_t x; brk_lock();
if (!__break) __break = (uintptr_t)_end; if (!__brk.p) __brk.p = _end;
x = (uintptr_t)end; rc = brk_unlocked(neu);
if (x < (uintptr_t)_end) x = (uintptr_t)_end; brk_unlock();
x = ROUNDUP(x, FRAMESIZE); STRACE("brk(%p) → %d% m", neu, rc);
if (x == __break) return 0; return (int)(intptr_t)rc;
/* allocate one frame at a time due to nt pickiness */
for (; x > __break; __break += FRAMESIZE) {
if (mmap((void *)__break, FRAMESIZE, PROT_READ | PROT_WRITE,
MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) == MAP_FAILED) {
return -1;
}
}
for (rc = 0; x < __break; __break -= FRAMESIZE) {
rc |= munmap((void *)(__break - FRAMESIZE), FRAMESIZE);
}
return 0;
} }
/** /**
@ -64,14 +141,41 @@ int brk(void *end) {
* *
* This shrinks or increases the program break by delta bytes. On * This shrinks or increases the program break by delta bytes. On
* success, the previous program break is returned. It's possible * success, the previous program break is returned. It's possible
* to pass zero to this function to get the current program break * to pass 0 to this function to obtain the current program break
* which is initially set to the linker-defined external variable
* `end` which is the end of the `.bss` segment. Your allocations
* are rounded automatically to a 4096-byte granularity.
* *
* @return old break on success or -1 w/ errno * The only virtue of sbrk(), and brk(), aside from compatibility
* @see mmap(), brk(), _end * with legacy software, is it's tinier than mmap() because since
* this API only supports Unix, we don't bother doing the complex
* memory interval tracking that mmap() does.
*
* @param delta is the number of bytes to allocate (or free if negative)
* noting that your delta may be tuned to a number further from zero
* to accommodate the page size granularity of this allocator
* @return previous break on success, or `(void *)-1` w/ errno
* @raise EINVAL if new break would be less than the `end` of `.bss`
* @raise EEXIST if expanded break would overlap existing mmap() memory
* @raise EOVERFLOW if `delta` added to break overflows the address space
* @raise ENOMEM if `RLIMIT_DATA` / `RLIMIT_AS` / `RLIMIT_RSS` is exceeded
* @raise ENOSYS on Windows because WIN32 puts random stuff after your break
* @threadsafe
*/ */
void *sbrk(intptr_t delta) { void *sbrk(intptr_t delta) {
uintptr_t oldbreak; intptr_t neu;
if (!__break) __break = (uintptr_t)_end; unsigned char *rc, *old;
oldbreak = __break; brk_lock();
return (void *)(brk((void *)(__break + delta)) != -1 ? oldbreak : -1); if (!__brk.p) __brk.p = _end;
old = __brk.p;
if (!__builtin_add_overflow((intptr_t)__brk.p, delta, &neu) &&
IsLegalPointer((unsigned char *)neu)) {
rc = brk_unlocked((unsigned char *)neu);
if (!rc) rc = old;
} else {
rc = (void *)eoverflow();
}
brk_unlock();
STRACE("sbrk(%'ld) → %p% m", delta, rc);
return rc;
} }

View file

@ -0,0 +1,16 @@
#ifndef COSMOPOLITAN_LIBC_RUNTIME_BRK_INTERNAL_H_
#define COSMOPOLITAN_LIBC_RUNTIME_BRK_INTERNAL_H_
#include "libc/thread/thread.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
struct Brk {
unsigned char *p;
pthread_mutex_t m;
};
extern struct Brk __brk;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_RUNTIME_BRK_INTERNAL_H_ */

View file

@ -1,8 +1,6 @@
#ifndef COSMOPOLITAN_LIBC_RUNTIME_MEMTRACK_H_ #ifndef COSMOPOLITAN_LIBC_RUNTIME_MEMTRACK_H_
#define COSMOPOLITAN_LIBC_RUNTIME_MEMTRACK_H_ #define COSMOPOLITAN_LIBC_RUNTIME_MEMTRACK_H_
#include "libc/assert.h"
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/intrin/midpoint.h"
#include "libc/intrin/nopl.internal.h" #include "libc/intrin/nopl.internal.h"
#include "libc/macros.internal.h" #include "libc/macros.internal.h"
#include "libc/nt/version.h" #include "libc/nt/version.h"
@ -31,9 +29,9 @@ struct MemoryInterval {
int y; int y;
long h; long h;
long size; long size;
int prot;
int flags;
long offset; long offset;
int flags;
char prot;
bool iscow; bool iscow;
bool readonlyfile; bool readonlyfile;
}; };
@ -44,20 +42,21 @@ struct MemoryIntervals {
struct MemoryInterval s[OPEN_MAX]; struct MemoryInterval s[OPEN_MAX];
}; };
extern hidden struct MemoryIntervals _mmi; extern struct MemoryIntervals _mmi;
void __mmi_lock(void) hidden; void __mmi_lock(void);
void __mmi_unlock(void) hidden; void __mmi_unlock(void);
bool IsMemtracked(int, int) hidden; bool IsMemtracked(int, int);
void PrintSystemMappings(int) hidden; void PrintSystemMappings(int);
bool AreMemoryIntervalsOk(const struct MemoryIntervals *) nosideeffect hidden; unsigned FindMemoryInterval(const struct MemoryIntervals *, int) nosideeffect;
void PrintMemoryIntervals(int, const struct MemoryIntervals *) hidden; bool AreMemoryIntervalsOk(const struct MemoryIntervals *) nosideeffect;
void PrintMemoryIntervals(int, const struct MemoryIntervals *);
int TrackMemoryInterval(struct MemoryIntervals *, int, int, long, int, int, int TrackMemoryInterval(struct MemoryIntervals *, int, int, long, int, int,
bool, bool, long, long) hidden; bool, bool, long, long);
int ReleaseMemoryIntervals(struct MemoryIntervals *, int, int, int ReleaseMemoryIntervals(struct MemoryIntervals *, int, int,
void (*)(struct MemoryIntervals *, int, int)) hidden; void (*)(struct MemoryIntervals *, int, int));
void ReleaseMemoryNt(struct MemoryIntervals *, int, int) hidden; void ReleaseMemoryNt(struct MemoryIntervals *, int, int);
int UntrackMemoryIntervals(void *, size_t) hidden; int UntrackMemoryIntervals(void *, size_t);
size_t GetMemtrackSize(struct MemoryIntervals *); size_t GetMemtrackSize(struct MemoryIntervals *);
#ifdef _NOPL0 #ifdef _NOPL0
@ -172,23 +171,6 @@ forceinline pureconst bool OverlapsShadowSpace(const void *p, size_t n) {
} }
} }
forceinline unsigned FindMemoryInterval(const struct MemoryIntervals *mm,
int x) {
unsigned l, m, r;
l = 0;
r = mm->i;
while (l < r) {
m = _midpoint(l, r);
if (mm->p[m].y < x) {
l = m + 1;
} else {
r = m;
}
}
assert(l == mm->i || x <= mm->p[l].y);
return l;
}
COSMOPOLITAN_C_END_ COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_RUNTIME_MEMTRACK_H_ */ #endif /* COSMOPOLITAN_LIBC_RUNTIME_MEMTRACK_H_ */

View file

@ -71,7 +71,7 @@ static wontreturn void OnUnrecoverableMmapError(const char *s) {
static noasan inline bool OverlapsExistingMapping(char *p, size_t n) { static noasan inline bool OverlapsExistingMapping(char *p, size_t n) {
int a, b, i; int a, b, i;
assert(n > 0); _unassert(n > 0);
a = FRAME(p); a = FRAME(p);
b = FRAME(p + (n - 1)); b = FRAME(p + (n - 1));
i = FindMemoryInterval(&_mmi, a); i = FindMemoryInterval(&_mmi, a);
@ -86,7 +86,7 @@ static noasan inline bool OverlapsExistingMapping(char *p, size_t n) {
static noasan bool ChooseMemoryInterval(int x, int n, int align, int *res) { static noasan bool ChooseMemoryInterval(int x, int n, int align, int *res) {
// TODO: improve performance // TODO: improve performance
int i, start, end; int i, start, end;
assert(align > 0); _unassert(align > 0);
if (_mmi.i) { if (_mmi.i) {
// find the start of the automap memory region // find the start of the automap memory region
@ -202,8 +202,8 @@ static textwindows dontinline noasan void *MapMemories(char *addr, size_t size,
struct DirectMap dm; struct DirectMap dm;
bool iscow, readonlyfile; bool iscow, readonlyfile;
m = (size_t)(n - 1) << 16; m = (size_t)(n - 1) << 16;
assert(m < size); _unassert(m < size);
assert(m + FRAMESIZE >= size); _unassert(m + FRAMESIZE >= size);
oi = fd == -1 ? 0 : off + m; oi = fd == -1 ? 0 : off + m;
sz = size - m; sz = size - m;
dm = sys_mmap(addr + m, sz, prot, f, fd, oi); dm = sys_mmap(addr + m, sz, prot, f, fd, oi);

View file

@ -17,7 +17,6 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/intrin/strace.internal.h"
#include "libc/calls/struct/rlimit.h" #include "libc/calls/struct/rlimit.h"
#include "libc/calls/struct/sched_param.h" #include "libc/calls/struct/sched_param.h"
#include "libc/calls/struct/sigset.h" #include "libc/calls/struct/sigset.h"
@ -31,6 +30,7 @@
#include "libc/intrin/describeflags.internal.h" #include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/kprintf.h" #include "libc/intrin/kprintf.h"
#include "libc/intrin/promises.internal.h" #include "libc/intrin/promises.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/macros.internal.h" #include "libc/macros.internal.h"
#include "libc/nexgen32e/cpuid4.internal.h" #include "libc/nexgen32e/cpuid4.internal.h"
#include "libc/nexgen32e/kcpuids.h" #include "libc/nexgen32e/kcpuids.h"
@ -139,8 +139,8 @@ static noasan void PrintDependencies(const char *prologue) {
do { do {
const struct NtLdrDataTableEntry *dll = const struct NtLdrDataTableEntry *dll =
(const struct NtLdrDataTableEntry *)ldr; (const struct NtLdrDataTableEntry *)ldr;
PRINT(" ☼ %.*!hs (%'zukb)", dll->FullDllName.Length, dll->FullDllName.Data, PRINT(" ☼ %.*!hs (%'zukb @ %p)", dll->FullDllName.Length,
dll->SizeOfImage / 1024); dll->FullDllName.Data, dll->SizeOfImage / 1024, dll->DllBase);
} while ((ldr = ldr->Next) && ldr != head); } while ((ldr = ldr->Next) && ldr != head);
} }

View file

@ -24,12 +24,12 @@ extern uint64_t kStartTsc; /* RII */
extern char kTmpPath[]; /* RII */ extern char kTmpPath[]; /* RII */
extern const char kNtSystemDirectory[]; /* RII */ extern const char kNtSystemDirectory[]; /* RII */
extern const char kNtWindowsDirectory[]; /* RII */ extern const char kNtWindowsDirectory[]; /* RII */
extern unsigned char _base[] forcealign(PAGESIZE); /* αpε */ extern unsigned char _base[]; /* αpε */
extern unsigned char _ehead[] forcealign(PAGESIZE); /* αpε */ extern unsigned char _ehead[]; /* αpε */
extern unsigned char _etext[] forcealign(PAGESIZE); /* αpε */ extern unsigned char _etext[]; /* αpε */
extern unsigned char _edata[] forcealign(PAGESIZE); /* αpε */ extern unsigned char _edata[]; /* αpε */
extern unsigned char _ezip[]; /* αpε */ extern unsigned char _ezip[]; /* αpε */
extern unsigned char _end[] forcealign(FRAMESIZE); /* αpε */ extern unsigned char _end[]; /* αpε */
extern unsigned char _ereal[]; /* αpε */ extern unsigned char _ereal[]; /* αpε */
extern unsigned char __privileged_start[]; /* αpε */ extern unsigned char __privileged_start[]; /* αpε */
extern unsigned char __privileged_addr[]; /* αpε */ extern unsigned char __privileged_addr[]; /* αpε */

View file

@ -0,0 +1,63 @@
/*-*- 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 2022 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/atomic.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/calls/struct/siginfo.h"
#include "libc/calls/ucontext.h"
#include "libc/intrin/atomic.h"
#include "libc/sysv/consts/sa.h"
#include "libc/sysv/consts/sig.h"
#include "libc/testlib/testlib.h"
#include "third_party/xed/x86.h"
static volatile _Thread_local int gotsignal;
static void ContinueOnError(int sig, siginfo_t *si, void *vctx) {
struct XedDecodedInst xedd;
struct ucontext *ctx = vctx;
xed_decoded_inst_zero_set_mode(&xedd, XED_MACHINE_MODE_LONG_64);
xed_instruction_length_decode(&xedd, (void *)ctx->uc_mcontext.rip, 15);
ctx->uc_mcontext.rip += xedd.length;
gotsignal = sig;
}
/**
* Returns true if byte at address `p` is readable.
*
* This function temporarily catches `SIGSEGV` and `SIGBUS` to recover
* on error. It then attempts a volatile read and if it faults, then
* this function shall return false. The value at `p` isn't considered.
*/
noasan bool testlib_memoryexists(const void *p) {
volatile char c;
const atomic_char *mem = p;
struct sigaction old[2];
struct sigaction sa = {
.sa_sigaction = ContinueOnError,
.sa_flags = SA_SIGINFO,
};
gotsignal = 0;
_npassert(!sigaction(SIGSEGV, &sa, old + 0));
_npassert(!sigaction(SIGBUS, &sa, old + 1));
c = atomic_load(mem);
_npassert(!sigaction(SIGBUS, old + 1, 0));
_npassert(!sigaction(SIGSEGV, old + 0, 0));
return !gotsignal;
}

View file

@ -382,6 +382,7 @@ void testlib_seterrno(int);
void testlib_runalltests(void); void testlib_runalltests(void);
const char *testlib_strerror(void); const char *testlib_strerror(void);
void testlib_runallbenchmarks(void); void testlib_runallbenchmarks(void);
bool testlib_memoryexists(const void *);
void testlib_runtestcases(testfn_t *, testfn_t *, testfn_t); void testlib_runtestcases(testfn_t *, testfn_t *, testfn_t);
void testlib_runcombos(testfn_t *, testfn_t *, const struct TestFixture *, void testlib_runcombos(testfn_t *, testfn_t *, const struct TestFixture *,
const struct TestFixture *); const struct TestFixture *);

View file

@ -76,6 +76,7 @@ LIBC_TESTLIB_A_SRCS_C = \
libc/testlib/globals.c \ libc/testlib/globals.c \
libc/testlib/hexequals.c \ libc/testlib/hexequals.c \
libc/testlib/incrementfailed.c \ libc/testlib/incrementfailed.c \
libc/testlib/memoryexists.c \
libc/testlib/quota.c \ libc/testlib/quota.c \
libc/testlib/seterrno.c \ libc/testlib/seterrno.c \
libc/testlib/shoulddebugbreak.c \ libc/testlib/shoulddebugbreak.c \
@ -117,7 +118,8 @@ LIBC_TESTLIB_A_DIRECTDEPS = \
LIBC_X \ LIBC_X \
LIBC_ZIPOS \ LIBC_ZIPOS \
THIRD_PARTY_DLMALLOC \ THIRD_PARTY_DLMALLOC \
THIRD_PARTY_GDTOA THIRD_PARTY_GDTOA \
THIRD_PARTY_XED
LIBC_TESTLIB_A_DEPS := \ LIBC_TESTLIB_A_DEPS := \
$(call uniq,$(foreach x,$(LIBC_TESTLIB_A_DIRECTDEPS),$($(x)))) $(call uniq,$(foreach x,$(LIBC_TESTLIB_A_DIRECTDEPS),$($(x))))

View file

@ -0,0 +1,91 @@
/*-*- 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 2022 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/calls/struct/rlimit.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/limits.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/prot.h"
#include "libc/sysv/consts/rlimit.h"
#include "libc/testlib/subprocess.h"
#include "libc/testlib/testlib.h"
void SetUp(void) {
if (IsWindows()) {
ASSERT_SYS(ENOSYS, -1, brk(0));
ASSERT_SYS(ENOSYS, MAP_FAILED, sbrk(0));
exit(0);
}
}
TEST(sbrk, testReportCurrentBreak) {
ASSERT_SYS(0, _end, sbrk(0));
}
TEST(sbrk, hugeDelta_returnsEoverflow) {
ASSERT_SYS(EOVERFLOW, MAP_FAILED, sbrk(INTPTR_MAX));
}
TEST(brk, underflowsEnd_returnsEinval) {
ASSERT_SYS(EINVAL, -1, brk(0));
}
TEST(sbrk, underflowsEnd_returnsEinval) {
ASSERT_SYS(EINVAL, MAP_FAILED, sbrk(-PAGESIZE));
}
TEST(sbrk, giantDelta_returnsEnomem) {
if (IsXnu()) return; // mmap polyfills this but brk doesn't right now
SPAWN(fork);
struct rlimit rl = {1024 * 1024, 1024 * 1024};
ASSERT_SYS(0, 0, setrlimit(RLIMIT_AS, &rl));
ASSERT_SYS(ENOMEM, MAP_FAILED, sbrk(1024 * 1024 * 4));
EXITS(0);
}
TEST(sbrk, overlapsExistingMapping_failsWithEexist) {
char *p = (char *)ROUNDUP((intptr_t)_end, FRAMESIZE);
ASSERT_EQ(p, mmap(p, FRAMESIZE, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0));
ASSERT_SYS(EEXIST, MAP_FAILED, sbrk(FRAMESIZE));
ASSERT_SYS(0, 0, munmap(p, FRAMESIZE));
}
TEST(sbrk, testGrowAndShrink) {
SPAWN(fork);
ASSERT_FALSE(testlib_memoryexists(_end));
ASSERT_SYS(0, _end, sbrk(PAGESIZE));
ASSERT_SYS(0, _end + PAGESIZE, sbrk(0));
ASSERT_TRUE(testlib_memoryexists(_end));
ASSERT_FALSE(testlib_memoryexists(_end + PAGESIZE));
ASSERT_SYS(0, _end + PAGESIZE, sbrk(-PAGESIZE));
ASSERT_FALSE(testlib_memoryexists(_end));
EXITS(0);
}
TEST(brk, testGrowAndShrink) {
SPAWN(fork);
ASSERT_FALSE(testlib_memoryexists(_end));
ASSERT_EQ(0, brk(_end + PAGESIZE));
ASSERT_TRUE(testlib_memoryexists(_end));
ASSERT_FALSE(testlib_memoryexists(_end + PAGESIZE));
ASSERT_EQ(0, brk(_end));
EXITS(0);
}

View file

@ -17,54 +17,21 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/calls/struct/sigaction.h" #include "libc/dce.h"
#include "libc/calls/struct/siginfo.h"
#include "libc/calls/ucontext.h"
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/intrin/atomic.h"
#include "libc/runtime/memtrack.internal.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/map.h" #include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/o.h" #include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/prot.h" #include "libc/sysv/consts/prot.h"
#include "libc/sysv/consts/sa.h"
#include "libc/testlib/testlib.h" #include "libc/testlib/testlib.h"
#include "third_party/xed/x86.h"
volatile int gotsignal;
char testlib_enable_tmp_setup_teardown; char testlib_enable_tmp_setup_teardown;
void ContinueOnError(int sig, siginfo_t *si, void *vctx) {
struct XedDecodedInst xedd;
struct ucontext *ctx = vctx;
xed_decoded_inst_zero_set_mode(&xedd, XED_MACHINE_MODE_LONG_64);
xed_instruction_length_decode(&xedd, (void *)ctx->uc_mcontext.rip, 15);
ctx->uc_mcontext.rip += xedd.length;
gotsignal = sig;
}
noasan bool MemoryExists(char *p) {
volatile char c;
struct sigaction old[2];
struct sigaction sa = {
.sa_sigaction = ContinueOnError,
.sa_flags = SA_SIGINFO,
};
gotsignal = 0;
sigaction(SIGSEGV, &sa, old + 0);
sigaction(SIGBUS, &sa, old + 1);
c = atomic_load(p);
sigaction(SIGBUS, old + 1, 0);
sigaction(SIGSEGV, old + 0, 0);
return !gotsignal;
}
TEST(munmap, doesntExist_doesntCare) { TEST(munmap, doesntExist_doesntCare) {
EXPECT_SYS(0, 0, munmap(0, FRAMESIZE * 8)); EXPECT_SYS(0, 0, munmap(0, FRAMESIZE * 8));
if (IsAsan()) { if (IsAsan()) {
// make sure it didn't unmap the null pointer shadow memory // make sure it didn't unmap the null pointer shadow memory
EXPECT_TRUE(MemoryExists((char *)0x7fff8000)); EXPECT_TRUE(testlib_memoryexists((char *)0x7fff8000));
} }
} }
@ -78,27 +45,27 @@ TEST(munmap, test) {
char *p; char *p;
ASSERT_NE(MAP_FAILED, (p = mmap(0, FRAMESIZE, PROT_READ | PROT_WRITE, ASSERT_NE(MAP_FAILED, (p = mmap(0, FRAMESIZE, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0))); MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)));
EXPECT_TRUE(MemoryExists(p)); EXPECT_TRUE(testlib_memoryexists(p));
EXPECT_SYS(0, 0, munmap(p, FRAMESIZE)); EXPECT_SYS(0, 0, munmap(p, FRAMESIZE));
EXPECT_FALSE(MemoryExists(p)); EXPECT_FALSE(testlib_memoryexists(p));
} }
TEST(munmap, punchHoleInMemory) { TEST(munmap, punchHoleInMemory) {
char *p; char *p;
ASSERT_NE(MAP_FAILED, (p = mmap(0, FRAMESIZE * 3, PROT_READ | PROT_WRITE, ASSERT_NE(MAP_FAILED, (p = mmap(0, FRAMESIZE * 3, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0))); MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)));
EXPECT_TRUE(MemoryExists(p + FRAMESIZE * 0)); EXPECT_TRUE(testlib_memoryexists(p + FRAMESIZE * 0));
EXPECT_TRUE(MemoryExists(p + FRAMESIZE * 1)); EXPECT_TRUE(testlib_memoryexists(p + FRAMESIZE * 1));
EXPECT_TRUE(MemoryExists(p + FRAMESIZE * 2)); EXPECT_TRUE(testlib_memoryexists(p + FRAMESIZE * 2));
EXPECT_SYS(0, 0, munmap(p + FRAMESIZE, FRAMESIZE)); EXPECT_SYS(0, 0, munmap(p + FRAMESIZE, FRAMESIZE));
EXPECT_TRUE(MemoryExists(p + FRAMESIZE * 0)); EXPECT_TRUE(testlib_memoryexists(p + FRAMESIZE * 0));
EXPECT_FALSE(MemoryExists(p + FRAMESIZE * 1)); EXPECT_FALSE(testlib_memoryexists(p + FRAMESIZE * 1));
EXPECT_TRUE(MemoryExists(p + FRAMESIZE * 2)); EXPECT_TRUE(testlib_memoryexists(p + FRAMESIZE * 2));
EXPECT_SYS(0, 0, munmap(p, FRAMESIZE)); EXPECT_SYS(0, 0, munmap(p, FRAMESIZE));
EXPECT_SYS(0, 0, munmap(p + FRAMESIZE * 2, FRAMESIZE)); EXPECT_SYS(0, 0, munmap(p + FRAMESIZE * 2, FRAMESIZE));
EXPECT_FALSE(MemoryExists(p + FRAMESIZE * 0)); EXPECT_FALSE(testlib_memoryexists(p + FRAMESIZE * 0));
EXPECT_FALSE(MemoryExists(p + FRAMESIZE * 1)); EXPECT_FALSE(testlib_memoryexists(p + FRAMESIZE * 1));
EXPECT_FALSE(MemoryExists(p + FRAMESIZE * 2)); EXPECT_FALSE(testlib_memoryexists(p + FRAMESIZE * 2));
} }
TEST(munmap, memoryHasHole) { TEST(munmap, memoryHasHole) {
@ -106,59 +73,59 @@ TEST(munmap, memoryHasHole) {
ASSERT_NE(MAP_FAILED, (p = mmap(0, FRAMESIZE * 3, PROT_READ | PROT_WRITE, ASSERT_NE(MAP_FAILED, (p = mmap(0, FRAMESIZE * 3, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0))); MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)));
EXPECT_SYS(0, 0, munmap(p + FRAMESIZE, FRAMESIZE)); EXPECT_SYS(0, 0, munmap(p + FRAMESIZE, FRAMESIZE));
EXPECT_TRUE(MemoryExists(p + FRAMESIZE * 0)); EXPECT_TRUE(testlib_memoryexists(p + FRAMESIZE * 0));
EXPECT_FALSE(MemoryExists(p + FRAMESIZE * 1)); EXPECT_FALSE(testlib_memoryexists(p + FRAMESIZE * 1));
EXPECT_TRUE(MemoryExists(p + FRAMESIZE * 2)); EXPECT_TRUE(testlib_memoryexists(p + FRAMESIZE * 2));
EXPECT_SYS(0, 0, munmap(p, FRAMESIZE * 3)); EXPECT_SYS(0, 0, munmap(p, FRAMESIZE * 3));
EXPECT_FALSE(MemoryExists(p + FRAMESIZE * 0)); EXPECT_FALSE(testlib_memoryexists(p + FRAMESIZE * 0));
EXPECT_FALSE(MemoryExists(p + FRAMESIZE * 1)); EXPECT_FALSE(testlib_memoryexists(p + FRAMESIZE * 1));
EXPECT_FALSE(MemoryExists(p + FRAMESIZE * 2)); EXPECT_FALSE(testlib_memoryexists(p + FRAMESIZE * 2));
} }
TEST(munmap, blanketFree) { TEST(munmap, blanketFree) {
char *p; char *p;
ASSERT_NE(MAP_FAILED, (p = mmap(0, FRAMESIZE * 3, PROT_READ | PROT_WRITE, ASSERT_NE(MAP_FAILED, (p = mmap(0, FRAMESIZE * 3, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0))); MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)));
EXPECT_TRUE(MemoryExists(p + FRAMESIZE * 0)); EXPECT_TRUE(testlib_memoryexists(p + FRAMESIZE * 0));
EXPECT_TRUE(MemoryExists(p + FRAMESIZE * 1)); EXPECT_TRUE(testlib_memoryexists(p + FRAMESIZE * 1));
EXPECT_TRUE(MemoryExists(p + FRAMESIZE * 2)); EXPECT_TRUE(testlib_memoryexists(p + FRAMESIZE * 2));
EXPECT_SYS(0, 0, munmap(p + FRAMESIZE * 0, FRAMESIZE)); EXPECT_SYS(0, 0, munmap(p + FRAMESIZE * 0, FRAMESIZE));
EXPECT_SYS(0, 0, munmap(p + FRAMESIZE * 2, FRAMESIZE)); EXPECT_SYS(0, 0, munmap(p + FRAMESIZE * 2, FRAMESIZE));
EXPECT_FALSE(MemoryExists(p + FRAMESIZE * 0)); EXPECT_FALSE(testlib_memoryexists(p + FRAMESIZE * 0));
EXPECT_TRUE(MemoryExists(p + FRAMESIZE * 1)); EXPECT_TRUE(testlib_memoryexists(p + FRAMESIZE * 1));
EXPECT_FALSE(MemoryExists(p + FRAMESIZE * 2)); EXPECT_FALSE(testlib_memoryexists(p + FRAMESIZE * 2));
EXPECT_SYS(0, 0, munmap(p, FRAMESIZE * 3)); EXPECT_SYS(0, 0, munmap(p, FRAMESIZE * 3));
EXPECT_FALSE(MemoryExists(p + FRAMESIZE * 0)); EXPECT_FALSE(testlib_memoryexists(p + FRAMESIZE * 0));
EXPECT_FALSE(MemoryExists(p + FRAMESIZE * 1)); EXPECT_FALSE(testlib_memoryexists(p + FRAMESIZE * 1));
EXPECT_FALSE(MemoryExists(p + FRAMESIZE * 2)); EXPECT_FALSE(testlib_memoryexists(p + FRAMESIZE * 2));
} }
TEST(munmap, trimLeft) { TEST(munmap, trimLeft) {
char *p; char *p;
ASSERT_NE(MAP_FAILED, (p = mmap(0, FRAMESIZE * 2, PROT_READ | PROT_WRITE, ASSERT_NE(MAP_FAILED, (p = mmap(0, FRAMESIZE * 2, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0))); MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)));
EXPECT_TRUE(MemoryExists(p + FRAMESIZE * 0)); EXPECT_TRUE(testlib_memoryexists(p + FRAMESIZE * 0));
EXPECT_TRUE(MemoryExists(p + FRAMESIZE * 1)); EXPECT_TRUE(testlib_memoryexists(p + FRAMESIZE * 1));
EXPECT_SYS(0, 0, munmap(p, FRAMESIZE)); EXPECT_SYS(0, 0, munmap(p, FRAMESIZE));
EXPECT_FALSE(MemoryExists(p + FRAMESIZE * 0)); EXPECT_FALSE(testlib_memoryexists(p + FRAMESIZE * 0));
EXPECT_TRUE(MemoryExists(p + FRAMESIZE * 1)); EXPECT_TRUE(testlib_memoryexists(p + FRAMESIZE * 1));
EXPECT_SYS(0, 0, munmap(p, FRAMESIZE * 2)); EXPECT_SYS(0, 0, munmap(p, FRAMESIZE * 2));
EXPECT_FALSE(MemoryExists(p + FRAMESIZE * 0)); EXPECT_FALSE(testlib_memoryexists(p + FRAMESIZE * 0));
EXPECT_FALSE(MemoryExists(p + FRAMESIZE * 1)); EXPECT_FALSE(testlib_memoryexists(p + FRAMESIZE * 1));
} }
TEST(munmap, trimRight) { TEST(munmap, trimRight) {
char *p; char *p;
ASSERT_NE(MAP_FAILED, (p = mmap(0, FRAMESIZE * 2, PROT_READ | PROT_WRITE, ASSERT_NE(MAP_FAILED, (p = mmap(0, FRAMESIZE * 2, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0))); MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)));
EXPECT_TRUE(MemoryExists(p + FRAMESIZE * 0)); EXPECT_TRUE(testlib_memoryexists(p + FRAMESIZE * 0));
EXPECT_TRUE(MemoryExists(p + FRAMESIZE * 1)); EXPECT_TRUE(testlib_memoryexists(p + FRAMESIZE * 1));
EXPECT_SYS(0, 0, munmap(p + FRAMESIZE, FRAMESIZE)); EXPECT_SYS(0, 0, munmap(p + FRAMESIZE, FRAMESIZE));
EXPECT_TRUE(MemoryExists(p + FRAMESIZE * 0)); EXPECT_TRUE(testlib_memoryexists(p + FRAMESIZE * 0));
EXPECT_FALSE(MemoryExists(p + FRAMESIZE * 1)); EXPECT_FALSE(testlib_memoryexists(p + FRAMESIZE * 1));
EXPECT_SYS(0, 0, munmap(p, FRAMESIZE * 2)); EXPECT_SYS(0, 0, munmap(p, FRAMESIZE * 2));
EXPECT_FALSE(MemoryExists(p + FRAMESIZE * 0)); EXPECT_FALSE(testlib_memoryexists(p + FRAMESIZE * 0));
EXPECT_FALSE(MemoryExists(p + FRAMESIZE * 1)); EXPECT_FALSE(testlib_memoryexists(p + FRAMESIZE * 1));
} }
TEST(munmap, memoryGone) { TEST(munmap, memoryGone) {
@ -174,9 +141,9 @@ TEST(munmap, testTooSmallToUnmapAsan) {
char *p; char *p;
ASSERT_NE(MAP_FAILED, (p = mmap(0, FRAMESIZE, PROT_READ | PROT_WRITE, ASSERT_NE(MAP_FAILED, (p = mmap(0, FRAMESIZE, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0))); MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)));
EXPECT_TRUE(MemoryExists((char *)(((intptr_t)p >> 3) + 0x7fff8000))); EXPECT_TRUE(testlib_memoryexists((char *)(((intptr_t)p >> 3) + 0x7fff8000)));
EXPECT_SYS(0, 0, munmap(p, FRAMESIZE)); EXPECT_SYS(0, 0, munmap(p, FRAMESIZE));
EXPECT_TRUE(MemoryExists((char *)(((intptr_t)p >> 3) + 0x7fff8000))); EXPECT_TRUE(testlib_memoryexists((char *)(((intptr_t)p >> 3) + 0x7fff8000)));
} }
TEST(munmap, testLargeEnoughToUnmapAsan) { TEST(munmap, testLargeEnoughToUnmapAsan) {
@ -195,7 +162,7 @@ TEST(munmap, testLargeEnoughToUnmapAsan) {
EXPECT_SYS(0, 0, munmap(p, n)); EXPECT_SYS(0, 0, munmap(p, n));
#if 0 #if 0
EXPECT_FALSE( EXPECT_FALSE(
MemoryExists((char *)(((intptr_t)(p + n / 2) >> 3) + 0x7fff8000))); testlib_memoryexists((char *)(((intptr_t)(p + n / 2) >> 3) + 0x7fff8000)));
#endif #endif
} }
@ -207,12 +174,12 @@ TEST(munmap, tinyFile_roundupUnmapSize) {
ASSERT_SYS(0, 3, open("doge", O_RDONLY)); ASSERT_SYS(0, 3, open("doge", O_RDONLY));
ASSERT_NE(MAP_FAILED, (p = mmap(0, 5, PROT_READ, MAP_PRIVATE, 3, 0))); ASSERT_NE(MAP_FAILED, (p = mmap(0, 5, PROT_READ, MAP_PRIVATE, 3, 0)));
ASSERT_SYS(0, 0, close(3)); ASSERT_SYS(0, 0, close(3));
EXPECT_TRUE(MemoryExists(p)); EXPECT_TRUE(testlib_memoryexists(p));
// some kernels/versions support this, some don't // some kernels/versions support this, some don't
EXPECT_FALSE(MemoryExists(p + PAGESIZE)); EXPECT_FALSE(testlib_memoryexists(p + PAGESIZE));
EXPECT_SYS(0, 0, munmap(p, FRAMESIZE)); EXPECT_SYS(0, 0, munmap(p, FRAMESIZE));
EXPECT_FALSE(MemoryExists(p)); EXPECT_FALSE(testlib_memoryexists(p));
EXPECT_FALSE(MemoryExists(p + 5)); EXPECT_FALSE(testlib_memoryexists(p + 5));
} }
TEST(munmap, tinyFile_preciseUnmapSize) { TEST(munmap, tinyFile_preciseUnmapSize) {
@ -224,13 +191,13 @@ TEST(munmap, tinyFile_preciseUnmapSize) {
ASSERT_NE(MAP_FAILED, (p = mmap(0, 5, PROT_READ, MAP_PRIVATE, 3, 0))); ASSERT_NE(MAP_FAILED, (p = mmap(0, 5, PROT_READ, MAP_PRIVATE, 3, 0)));
ASSERT_NE(MAP_FAILED, (q = mmap(0, 5, PROT_READ, MAP_PRIVATE, 3, 0))); ASSERT_NE(MAP_FAILED, (q = mmap(0, 5, PROT_READ, MAP_PRIVATE, 3, 0)));
ASSERT_SYS(0, 0, close(3)); ASSERT_SYS(0, 0, close(3));
EXPECT_TRUE(MemoryExists(p)); EXPECT_TRUE(testlib_memoryexists(p));
EXPECT_TRUE(MemoryExists(q)); EXPECT_TRUE(testlib_memoryexists(q));
EXPECT_SYS(0, 0, munmap(p, 5)); EXPECT_SYS(0, 0, munmap(p, 5));
EXPECT_FALSE(MemoryExists(p)); EXPECT_FALSE(testlib_memoryexists(p));
EXPECT_TRUE(MemoryExists(q)); EXPECT_TRUE(testlib_memoryexists(q));
EXPECT_SYS(0, 0, munmap(q, 5)); EXPECT_SYS(0, 0, munmap(q, 5));
EXPECT_FALSE(MemoryExists(q)); EXPECT_FALSE(testlib_memoryexists(q));
} }
// clang-format off // clang-format off
@ -242,14 +209,14 @@ TEST(munmap, tinyFile_mapThriceUnmapOnce) {
ASSERT_NE(MAP_FAILED, mmap(p+FRAMESIZE*1, 5, PROT_READ, MAP_PRIVATE|MAP_FIXED, 3, 0)); ASSERT_NE(MAP_FAILED, mmap(p+FRAMESIZE*1, 5, PROT_READ, MAP_PRIVATE|MAP_FIXED, 3, 0));
ASSERT_NE(MAP_FAILED, mmap(p+FRAMESIZE*3, 5, PROT_READ, MAP_PRIVATE|MAP_FIXED, 3, 0)); ASSERT_NE(MAP_FAILED, mmap(p+FRAMESIZE*3, 5, PROT_READ, MAP_PRIVATE|MAP_FIXED, 3, 0));
ASSERT_SYS(0, 0, close(3)); ASSERT_SYS(0, 0, close(3));
EXPECT_TRUE(MemoryExists(p+FRAMESIZE*0)); EXPECT_TRUE(testlib_memoryexists(p+FRAMESIZE*0));
EXPECT_TRUE(MemoryExists(p+FRAMESIZE*1)); EXPECT_TRUE(testlib_memoryexists(p+FRAMESIZE*1));
EXPECT_FALSE(MemoryExists(p+FRAMESIZE*2)); EXPECT_FALSE(testlib_memoryexists(p+FRAMESIZE*2));
EXPECT_TRUE(MemoryExists(p+FRAMESIZE*3)); EXPECT_TRUE(testlib_memoryexists(p+FRAMESIZE*3));
EXPECT_SYS(0, 0, munmap(p, FRAMESIZE*5)); EXPECT_SYS(0, 0, munmap(p, FRAMESIZE*5));
EXPECT_FALSE(MemoryExists(p+FRAMESIZE*0)); EXPECT_FALSE(testlib_memoryexists(p+FRAMESIZE*0));
EXPECT_FALSE(MemoryExists(p+FRAMESIZE*1)); EXPECT_FALSE(testlib_memoryexists(p+FRAMESIZE*1));
EXPECT_FALSE(MemoryExists(p+FRAMESIZE*2)); EXPECT_FALSE(testlib_memoryexists(p+FRAMESIZE*2));
EXPECT_FALSE(MemoryExists(p+FRAMESIZE*3)); EXPECT_FALSE(testlib_memoryexists(p+FRAMESIZE*3));
} }
// clang-format on // clang-format on

View file

@ -506,12 +506,9 @@ static void PrintDox(struct Dox *dox, FILE *f) {
<!doctype html>\n\ <!doctype html>\n\
<html lang=\"en\">\n\ <html lang=\"en\">\n\
<meta charset=\"utf-8\">\n\ <meta charset=\"utf-8\">\n\
<script async src=\"https://www.googletagmanager.com/gtag/js?id=UA-43182592-5\"></script>\n\
<script>window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'UA-43182592-5');</script>\n\
<title>Cosmopolitan C Library</title>\n\ <title>Cosmopolitan C Library</title>\n\
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n\ <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n\
<link rel=\"canonical\" href=\"https://justine.lol/cosmopolitan/documentation.html\">\n\ <link rel=\"canonical\" href=\"https://justine.lol/cosmopolitan/documentation.html\">\n\
<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css2?family=Roboto&family=Roboto+Mono&display=swap\">\n\
<link rel=\"stylesheet\" href=\"style.css\">\n\ <link rel=\"stylesheet\" href=\"style.css\">\n\
<link rel=\"apple-touch-icon\" sizes=\"180x180\" href=\"/apple-touch-icon.png\">\n\ <link rel=\"apple-touch-icon\" sizes=\"180x180\" href=\"/apple-touch-icon.png\">\n\
<link rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/favicon-32x32.png\">\n\ <link rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/favicon-32x32.png\">\n\
@ -626,7 +623,7 @@ static void PrintDox(struct Dox *dox, FILE *f) {
\n\ \n\
<header>\n\ <header>\n\
<img width=\"196\" height=\"105\"\n\ <img width=\"196\" height=\"105\"\n\
src=\"//storage.googleapis.com/justine/cosmopolitan/cosmopolitan.png\"\n\ src=\"//worker.jart.workers.dev/cosmopolitan/cosmopolitan.png\"\n\
title=\"cosmopolitan honeybadger\"\n\ title=\"cosmopolitan honeybadger\"\n\
alt=\"honeybadger\">\n\ alt=\"honeybadger\">\n\
<h1>cosmopolitan libc</h1>\n\ <h1>cosmopolitan libc</h1>\n\
@ -637,6 +634,7 @@ static void PrintDox(struct Dox *dox, FILE *f) {
<ul>\n\ <ul>\n\
<li><a href=\"index.html\">Intro</a>\n\ <li><a href=\"index.html\">Intro</a>\n\
<li><a href=\"download.html\">Download</a>\n\ <li><a href=\"download.html\">Download</a>\n\
<li><a href=\"functions.html\">Functions</a>\n\
<li><a class=\"active\" href=\"documentation.html\">Documentation</a>\n\ <li><a class=\"active\" href=\"documentation.html\">Documentation</a>\n\
<li><a href=\"tutorials.html\">Tutorials</a>\n\ <li><a href=\"tutorials.html\">Tutorials</a>\n\
<li><a href=\"https://github.com/jart/cosmopolitan\">GitHub</a>\n\ <li><a href=\"https://github.com/jart/cosmopolitan\">GitHub</a>\n\

View file

@ -21,11 +21,13 @@
#include "libc/fmt/conv.h" #include "libc/fmt/conv.h"
#include "libc/intrin/bits.h" #include "libc/intrin/bits.h"
#include "libc/intrin/safemacros.internal.h" #include "libc/intrin/safemacros.internal.h"
#include "libc/mem/gc.h"
#include "libc/nt/struct/imagedosheader.internal.h" #include "libc/nt/struct/imagedosheader.internal.h"
#include "libc/nt/struct/imagentheaders.internal.h" #include "libc/nt/struct/imagentheaders.internal.h"
#include "libc/nt/struct/imageoptionalheader.internal.h" #include "libc/nt/struct/imageoptionalheader.internal.h"
#include "libc/mem/gc.h" #include "libc/nt/struct/imagesectionheader.internal.h"
#include "libc/stdio/stdio.h" #include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/map.h" #include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/o.h" #include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/prot.h" #include "libc/sysv/consts/prot.h"
@ -249,6 +251,47 @@ static void ShowIat(char *iat, size_t size) {
} }
} }
static void ShowSection(struct NtImageSectionHeader *s) {
char name[9] = {0};
memcpy(name, s->Name, 8);
printf("\n");
printf("\t.ascin\t\"%'s\",8\n", name);
printf("\t.long\t%#x\t\t# VirtualSize\n", s->Misc.VirtualSize);
printf("\t.long\t%#x\t\t# VirtualAddress\n", s->VirtualAddress);
printf("\t.long\t%#x\t\t# SizeOfRawData\n", s->SizeOfRawData);
printf("\t.long\t%#x\t\t# PointerToRawData\n", s->PointerToRawData);
printf("\t.long\t%#x\t\t# PointerToRelocations\n", s->PointerToRelocations);
printf("\t.long\t%#x\t\t# PointerToLinenumbers\n", s->PointerToLinenumbers);
printf("\t.short\t%#x\t\t# NumberOfRelocations\n", s->NumberOfRelocations);
printf("\t.short\t%#x\t\t# NumberOfLinenumbers\n", s->NumberOfLinenumbers);
printf("\
// 31:Writeable \n\
// 30:Readable PE Section Flags \n\
// 29:Executable \n\
// 28:Shareable o for object files \n\
// 27:Unpageable r reserved \n\
// 26:Uncacheable \n\
// 25:Discardable\n\
// 24:Contains Extended Relocations\n\
// 15:Contains Global Pointer (GP) Relative Data\n\
// 7:Contains Uninitialized Data\n\
// 6:Contains Initialized Data\n\
// o 5:Contains Code\n\
// rrrr oororrorrr\n\
\t.long\t0b%.32b\t\t# Characteristics\n",
s->Characteristics);
}
static void ShowSections(struct NtImageSectionHeader *s, size_t n) {
size_t i;
printf("\n");
showtitle(basename(path), "windows", "sections", 0, 0);
for (i = 0; i < n; ++i) {
ShowSection(s + i);
}
}
static void showpeheader(struct NtImageNtHeaders *pe) { static void showpeheader(struct NtImageNtHeaders *pe) {
showtitle(basename(path), "windows", "pe header", NULL, NULL); showtitle(basename(path), "windows", "pe header", NULL, NULL);
printf("\n"); printf("\n");
@ -273,6 +316,10 @@ static void showpeheader(struct NtImageNtHeaders *pe) {
printf("\n"); printf("\n");
showpeoptionalheader(pecheckaddress(mz, mzsize, &pe->OptionalHeader, showpeoptionalheader(pecheckaddress(mz, mzsize, &pe->OptionalHeader,
pe->FileHeader.SizeOfOptionalHeader)); pe->FileHeader.SizeOfOptionalHeader));
ShowSections(pecheckaddress(mz, mzsize, pe + 1,
pe->FileHeader.NumberOfSections *
sizeof(struct NtImageSectionHeader)),
pe->FileHeader.NumberOfSections);
ShowIat( ShowIat(
(void *)((intptr_t)mz + (void *)((intptr_t)mz +
pe->OptionalHeader.DataDirectory[kNtImageDirectoryEntryImport] pe->OptionalHeader.DataDirectory[kNtImageDirectoryEntryImport]

75
tool/viz/virtualquery.c Normal file
View file

@ -0,0 +1,75 @@
/*-*- 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 2022 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/dce.h"
#include "libc/fmt/conv.h"
#include "libc/intrin/describeflags.internal.h"
#include "libc/macros.internal.h"
#include "libc/nt/enum/memflags.h"
#include "libc/nt/memory.h"
#include "libc/nt/struct/memorybasicinformation.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
/**
* @fileoverview WIN32 Virtual Memory Layout Dump Utility
*/
static const struct DescribeFlags kNtMemState[] = {
{kNtMemCommit, "Commit"}, //
{kNtMemFree, "Free"}, //
{kNtMemReserve, "Reserve"}, //
};
const char *DescribeNtMemState(char buf[64], uint32_t x) {
return DescribeFlags(buf, 64, kNtMemState, ARRAYLEN(kNtMemState), "kNtMem",
x);
}
static const struct DescribeFlags kNtMemType[] = {
{kNtMemImage, "Image"}, //
{kNtMemMapped, "Mapped"}, //
{kNtMemPrivate, "Private"}, //
};
const char *DescribeNtMemType(char buf[64], uint32_t x) {
return DescribeFlags(buf, 64, kNtMemType, ARRAYLEN(kNtMemType), "kNtMem", x);
}
int main(int argc, char *argv[]) {
char *p, b[5][64];
struct NtMemoryBasicInformation mi;
if (!IsWindows()) {
fprintf(stderr, "error: %s is intended for windows\n",
program_invocation_short_name);
exit(1);
}
printf("%-12s %-12s %10s %16s %16s %32s %32s\n", "Allocation", "BaseAddress",
"RegionSize", "State", "Type", "AllocationProtect", "Protect");
for (p = 0;; p = (char *)mi.BaseAddress + mi.RegionSize) {
bzero(&mi, sizeof(mi));
if (!VirtualQuery(p, &mi, sizeof(mi))) break;
sizefmt(b[0], mi.RegionSize, 1024);
printf("%.12lx %.12lx %10s %16s %16s %32s %32s\n", mi.AllocationBase,
mi.BaseAddress, b[0], DescribeNtMemState(b[1], mi.State),
DescribeNtMemType(b[2], mi.Type),
(DescribeNtPageFlags)(b[3], mi.AllocationProtect),
(DescribeNtPageFlags)(b[4], mi.Protect));
}
}