diff options
| author | raven <citrons@mondecitronne.com> | 2026-04-08 22:51:39 -0500 |
|---|---|---|
| committer | raven <citrons@mondecitronne.com> | 2026-04-08 22:51:39 -0500 |
| commit | 18a86e1038b20cb6f8922beead08dcc24ba2a4d3 (patch) | |
| tree | 85cb74210d67d42763a4709d18af93aa60a8f400 | |
| parent | 4a3429a96b5b5ea7468540349aeb4535d5738053 (diff) | |
rewrite and port to SDL3
| -rw-r--r-- | .gitignore | 3 | ||||
| -rw-r--r-- | Makefile | 34 | ||||
| -rw-r--r-- | core.c | 226 | ||||
| -rw-r--r-- | evilize.c | 65 | ||||
| -rw-r--r-- | memview.c | 161 | ||||
| -rw-r--r-- | memview.h | 29 | ||||
| -rw-r--r-- | src/curve.h | 25 | ||||
| -rw-r--r-- | src/hash.h | 15 | ||||
| -rw-r--r-- | src/main.c | 391 | ||||
| -rw-r--r-- | src/main.h | 7 | ||||
| -rw-r--r-- | src/memory.c | 140 | ||||
| -rw-r--r-- | src/memory.h | 44 | ||||
| -rw-r--r-- | src/panic.h | 28 | ||||
| -rw-r--r-- | src/procfs.c (renamed from procfs.c) | 58 | ||||
| -rw-r--r-- | src/procfs.h (renamed from procfs.h) | 6 |
15 files changed, 716 insertions, 516 deletions
@@ -1,2 +1 @@ -core -*.o +/build @@ -1,22 +1,26 @@ -SDL_HEADERS=/usr/include/SDL2 +name=core -#CFLAGS=-g -Wall -fsanitize=undefined -fsanitize=address -I$(SDL_HEADERS) -CFLAGS=-g -Wall -I$(SDL_HEADERS) -#LFLAGS=-lSDL2 -fsanitize=undefined -fsanitize=address -LFLAGS=-lSDL2 -lm +CFLAGS= -g \ + $(shell pkg-config --cflags sdl3) \ + $(pkg-config --cflags sdl3-ttf) -core: core.o procfs.o memview.o - $(CC) -o $@ $^ $(LFLAGS) +LDFLAGS= \ + $(shell pkg-config --libs sdl3) \ + $(shell pkg-config --libs sdl3-ttf) -core.c: procfs.h memview.h -memview.c: procfs.h memview.h -procfs.c: procfs.h +srcs=$(shell find src -name '*.c') +objs=$(srcs:%.c=build/%.o) +deps=$(objs:.o=.d) -evilize: evilize.o +./build/$(name): $(objs) + $(CC) $(objs) -o $@ $(LDFLAGS) -.SUFFIXES: .c .o -.c.o: - $(CC) $(CFLAGS) -c -o $@ $< +./build/%.o: %.c + mkdir -p $(dir $@) + $(CC) $(CFLAGS) -MMD -MP -c $< -o $@ +.PHONY: clean clean: - rm -f *.o core + rm -rf build + +-include $(deps) @@ -1,226 +0,0 @@ -#include <stdlib.h> -#include <stdio.h> -#include <stdint.h> -#include <stdbool.h> -#include <unistd.h> -#include <assert.h> -#include <signal.h> -#include <SDL.h> - -#include "procfs.h" -#include "memview.h" - -static int screen_width = 640; -static int screen_height = 480; - -static double scale = 5; -static double pos_x = 1024; -static double pos_y = 0; - -static int mouse_x = 0; -static int mouse_y = 0; - -SDL_Window *window = NULL; -SDL_Renderer *renderer = NULL; - -pid_t pid; -int mem_fd; -struct viewer *viewer; - -static void add_scale(double amount) { - pos_x += mouse_x / scale; - pos_y += mouse_y / scale; - scale += amount; - if (scale > 50) scale = 50; - else if (scale < 0.5) scale = 0.5; - pos_x -= mouse_x / scale; - pos_y -= mouse_y / scale; -} - -static void world_pos(double *x, double *y) { - *x = *x / scale + floor(pos_x); - *y = *y / scale + floor(pos_y); -} - -static void goto_addr(uintptr_t addr) { - int x, y; - to_pos(addr, &x, &y); - pos_x = x; pos_y = y; - pos_x -= (double) screen_width / 2 * scale; - pos_y -= (double) screen_height / 2 * scale; -} - -static size_t current_map = 0; - -static void next_map() { - struct procfs_map *maps; - int n = procfs_maps(pid, &maps); - if (n < 1) return; - for (int i = 0; i < n; i++) { - current_map = (current_map + 1) % n; - if (maps[current_map].prot & PROT_READ) - break; - } - goto_addr(maps[current_map].base); - free(maps); -} - -static void prev_map() { - struct procfs_map *maps; - int n = procfs_maps(pid, &maps); - if (n < 1) return; - for (int i = 0; i < n; i++) { - current_map = current_map > 1 ? current_map - 1 : n - 1; - if (maps[current_map].prot & PROT_READ) - break; - } - goto_addr(maps[current_map].base); - free(maps); -} - -static void report_error() { - fprintf(stderr, "SDL Error: %s\n", SDL_GetError()); - exit(-1); -} - -static void init_sdl(const char *title) { - if (SDL_Init(SDL_INIT_VIDEO) < 0) report_error(); - window = SDL_CreateWindow(title, - SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, - screen_width, screen_height, - SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE); - if (window == NULL) report_error(); - renderer = SDL_CreateRenderer(window, -1, 0); - if (renderer == NULL) report_error(); -} - -static void deinit_sdl() { - SDL_DestroyRenderer(renderer); - SDL_DestroyWindow(window); - SDL_Quit(); -} - -static bool held(SDL_Keycode k) { - const uint8_t *state = SDL_GetKeyboardState(NULL); - return state[SDL_GetScancodeFromKey(k)]; -} - -static void handle_events() { - bool refresh = false; - SDL_Event e; - while (SDL_PollEvent(&e)) { - switch (e.type) { - case SDL_QUIT: - exit(0); break; - case SDL_WINDOWEVENT: - switch (e.window.event) { - case SDL_WINDOWEVENT_RESIZED: - screen_width = e.window.data1; - screen_height = e.window.data2; - SDL_SetWindowSize(window, screen_width, screen_height); - refresh = true; - break; - case SDL_WINDOWEVENT_EXPOSED: - refresh = true; - break; - default: - break; - } - break; - case SDL_MOUSEMOTION:; - uint32_t state = e.motion.state; - if (state & SDL_BUTTON_MMASK || - (state & SDL_BUTTON_RMASK && held(SDLK_LSHIFT))) { - pos_x -= (double) e.motion.xrel / scale; - pos_y -= (double) e.motion.yrel / scale; - refresh = true; - } else if (state & (SDL_BUTTON_LMASK | SDL_BUTTON_RMASK)) { - double x1 = e.motion.x - e.motion.xrel; - double y1 = e.motion.y - e.motion.yrel; - double x2 = e.motion.x; double y2 = e.motion.y; - world_pos(&x1, &y1); world_pos(&x2, &y2); - pencil(viewer, - x1, y1, x2, y2, !(state & SDL_BUTTON_RMASK)); - refresh = true; - } - mouse_x = e.motion.x; - mouse_y = e.motion.y; - break; - case SDL_MOUSEBUTTONDOWN:; - double x = e.button.x; double y = e.button.y; - world_pos(&x, &y); - if (e.button.button == SDL_BUTTON_LEFT) - pencil(viewer, x, y, x, y, true); - else if (e.button.button == SDL_BUTTON_RIGHT) - pencil(viewer, x, y, x, y, false); - break; - case SDL_MOUSEWHEEL: - if (e.wheel.y == 0) break; - add_scale((double) e.wheel.y / 2); - refresh = true; - break; - case SDL_KEYDOWN: - switch (e.key.keysym.sym) { - case SDLK_RIGHT: - next_map(); - refresh = true; - break; - case SDLK_LEFT: - prev_map(); - refresh = true; - break; - case SDLK_SPACE:; - static bool stopped = false; - kill(pid, stopped ? SIGSTOP : SIGCONT); - stopped = !stopped; - break; - default: - break; - } - default: - break; - } - } - if (render_pages(viewer, pos_x, pos_y, - screen_width, screen_height, scale, refresh)) - SDL_RenderPresent(renderer); -} - -void cleanup() { - destroy_viewer(viewer); - deinit_sdl(); -} - -int main(int argc, char *argv[]) { - if (argc < 2) { - fprintf(stderr, "todo: show usage\n"); - return -1; - } - pid = atoi(argv[1]); - if (pid == 0) { - fprintf(stderr, "todo: show usage\n"); - return -1; - } - mem_fd = procfs_open(pid); - if (mem_fd == -1) perror("error attaching to process"); - - char title[1024]; - snprintf(title, 1024, "%s [%d]", argv[0], pid); - init_sdl(title); - viewer = create_viewer(mem_fd, renderer); - if (!viewer) abort(); - atexit(cleanup); - - prev_map(); - - int last_time = 0; - int current_time = 0; - while (1) { - handle_events(); - last_time = current_time; - current_time = SDL_GetTicks(); - int elapsed = current_time - last_time; - if (elapsed < 1000 / 30) - SDL_Delay(1000 / 30 - elapsed); - } -} diff --git a/evilize.c b/evilize.c deleted file mode 100644 index e3b13aa..0000000 --- a/evilize.c +++ /dev/null @@ -1,65 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <errno.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <unistd.h> -#include <signal.h> -#include <sys/ptrace.h> - -#define ERRCHECK(E)\ - if ((E) == -1) {perror(argv[0]); kill(pid, SIGHUP); return -1;} - -int main(int argc, char *argv[]) { - if (argc < 2) { - fprintf(stderr, "usage: %s COMMAND [ARGS]...\n" , argv[0]); - return -1; - } - pid_t pid = fork(); - if (pid == -1) { - perror(argv[0]); - return -1; - } - if (pid == 0) { - ptrace(PTRACE_TRACEME, 0, NULL, NULL); - raise(SIGSTOP); - if (execvp(argv[1], argv + 1) == -1) { - perror(argv[0]); - return -1; - } - } else { - ERRCHECK(ptrace(PTRACE_ATTACH, pid, NULL, NULL)); - int status; - pid_t w = waitpid(pid, &status, __WALL); - ERRCHECK(w); - ERRCHECK(ptrace(PTRACE_CONT, pid, NULL, NULL)); - while (1) { - pid_t w = waitpid(pid, &status, __WALL); - ERRCHECK(w); - if (WIFEXITED(status)) - return WEXITSTATUS(status); - if (WIFSTOPPED(status)) { - if (WIFSIGNALED(status)) { - errno = 0; - switch (WSTOPSIG(status)) { - case SIGABRT: - case SIGBUS: - case SIGFPE: - case SIGILL: - case SIGSEGV: - ptrace(PTRACE_CONT, pid, NULL, NULL); - break; - default: - ptrace(PTRACE_CONT, pid, NULL, WSTOPSIG(status)); - break; - } - if (errno && errno != ESRCH) { - perror("ptrace"); - kill(pid, SIGHUP); - return -1; - } - } else ptrace(PTRACE_CONT, pid, NULL, NULL); - } - } - } -} diff --git a/memview.c b/memview.c deleted file mode 100644 index 95a3c6c..0000000 --- a/memview.c +++ /dev/null @@ -1,161 +0,0 @@ -#include <stdlib.h> -#include <stdint.h> -#include <stdbool.h> -#include <assert.h> -#include <limits.h> -#include <string.h> -#include <SDL.h> - -#include "memview.h" -#include "procfs.h" - -struct viewer { - int fd; - SDL_Renderer *renderer; - SDL_Texture *page_texture; - uint8_t page_buffer[PAGE_SIZE]; -}; - -#define BIT_OF(X, I) (((X) >> (I)) & 1ULL) - -static uintptr_t zorder(unsigned int x, unsigned int y) { - uintptr_t z = 0; - // interleaving bits results in a point on the z-order curve - for (int i = 0; i < sizeof(x) * CHAR_BIT; i++) - z |= (BIT_OF(x, i) << i*2) | (BIT_OF(y, i) << (i*2 + 1)); - return z; -} - -static void unzorder(uintptr_t z, unsigned int *x, unsigned int *y) { - *x = 0; *y = 0; - for (int i = 0; i < sizeof(z) * CHAR_BIT / 2; i++) { - *x |= BIT_OF(z, i*2) << i; - *y |= BIT_OF(z, i*2 + 1) << i; - } -} - -uintptr_t to_addr(int x, int y) { - unsigned int ux, uy; - memcpy(&ux, &x, sizeof(unsigned int)); - memcpy(&uy, &y, sizeof(unsigned int)); - uintptr_t page = zorder(ux / PAGE_WIDTH, uy / PAGE_HEIGHT); - int offset = - x % PAGE_WIDTH / CHAR_BIT + y % PAGE_HEIGHT * PAGE_WIDTH / CHAR_BIT; - return page * PAGE_SIZE + offset; -} - -void to_pos(uintptr_t addr, int *x, int *y) { - unsigned int px, py; - unzorder(addr / PAGE_SIZE, &px, &py); - int offset = addr % PAGE_SIZE; - unsigned int ux = px * PAGE_WIDTH + offset % PAGE_WIDTH; - unsigned int uy = py * PAGE_HEIGHT + offset / PAGE_WIDTH; - memcpy(x, &ux, sizeof(int)); - memcpy(y, &uy, sizeof(int)); -} - -struct viewer *create_viewer(int fd, SDL_Renderer *renderer) { - struct viewer *v = calloc(1, sizeof(struct viewer)); - if (!v) return NULL; - v->fd = fd; v->renderer = renderer; - v->page_texture = SDL_CreateTexture(v->renderer, - SDL_PIXELFORMAT_RGBA32, - SDL_TEXTUREACCESS_STREAMING, - PAGE_WIDTH, PAGE_HEIGHT); - if (!v->page_texture) {free(v); return NULL;} - return v; -} - -void destroy_viewer(struct viewer *v) { - SDL_DestroyTexture(v->page_texture); - free(v); -} - -static void render_page(SDL_Texture *tex, uint8_t *data) { - uint32_t *pixels; int pitch; - assert(SDL_LockTexture(tex, NULL, (void **) &pixels, &pitch) != -1); - if (data) { - for (int i = 0; i < PAGE_SIZE; i++) { - for (int j = 0; j < CHAR_BIT; j++) { - uint8_t bit = (data[i] >> j) & 1; - pixels[i * CHAR_BIT + j] = bit ? 0xFFFFFFFF : 0xFF000000; - } - } - } else { - for (int i = 0; i < PAGE_WIDTH * PAGE_HEIGHT; i++) - pixels[i] = - ((i / 16) + i / PAGE_WIDTH / 16) % 2 ? 0xFF111111 : 0xFF333333; - } - SDL_UnlockTexture(tex); -} - -static uint64_t fnv(uint8_t *data, size_t size) { - uint64_t hash = 0xcbf29ce484222325; - for (size_t i = 0; i < size; i++) { - hash ^= data[i]; - hash *= 0x100000001b3; - } - return hash; -} - -static uint64_t hashes[1024] = {0}; - -bool render_pages(struct viewer *v, - int x, int y, int width, int height, double scale, bool refresh) { - int page_x = x / PAGE_WIDTH; - int page_y = y / PAGE_HEIGHT; - - int offs_x = x % PAGE_WIDTH * scale; - int offs_y = y % PAGE_HEIGHT * scale; - - int page_width = PAGE_WIDTH * scale; - int page_height = PAGE_HEIGHT * scale; - - int view_width = width / page_width; - int view_height = height / page_height; - - bool present = false; - for (int draw_y = -1; draw_y <= view_height + 1; draw_y++) { - for (int draw_x = -1; draw_x <= view_width + 1; draw_x++) { - SDL_Rect src = {0, 0, PAGE_WIDTH, PAGE_HEIGHT}; - SDL_Rect dest = { - draw_x * page_width - offs_x, draw_y * page_height - offs_y, - page_width, page_height - }; - uintptr_t page = zorder(page_x + draw_x, page_y + draw_y); - bool success = read_page(v->fd, page, v->page_buffer) != -1; - - uint64_t hash = fnv(v->page_buffer, success ? PAGE_SIZE : 0); - if (refresh || hashes[page % 1024] != hash) { - render_page(v->page_texture, success ? v->page_buffer : NULL); - SDL_RenderCopy(v->renderer, v->page_texture, &src, &dest); - present = true; - } - hashes[page % 1024] = hash; - } - } - return present; -} - -static void write_bit(int fd, int x, int y, bool bit) { - uintptr_t addr = to_addr(x, y); - int bit_offs = x % CHAR_BIT; - uint8_t byte; - if (read_mem(fd, addr, &byte, 1) == -1) return; - byte = (byte & ~(1 << bit_offs)) | (bit << bit_offs); - write_mem(fd, addr, &byte, 1); -} - -void pencil(struct viewer *v, - double x1, double y1, double x2, double y2, bool bit) { - double dx = (x2 - x1); - double dy = (y2 - y1); - int step = abs(dx) >= abs(dy) ? abs(dx) : abs(dy); - dx = dx / step; dy = dy / step; - double x = x1; double y = y1; - for (int i = 0; i <= step; i++) { - write_bit(v->fd, x, y, bit); - x = x + dx; - y = y + dy; - } -} diff --git a/memview.h b/memview.h deleted file mode 100644 index 47aab27..0000000 --- a/memview.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef CORE_MEMVIEW_H -#define CORE_MEMVIEW_H - -#include <stdlib.h> -#include <stdint.h> -#include <stdbool.h> -#include <SDL.h> - -#include "procfs.h" - -#ifndef PAGE_WIDTH -#define PAGE_WIDTH (256) -#endif - -#define PAGE_HEIGHT ((PAGE_SIZE * CHAR_BIT) / PAGE_WIDTH) - -uintptr_t to_addr(int x, int y); -void to_pos(uintptr_t addr, int *x, int *y); - -struct viewer; -struct viewer *create_viewer(int fd, SDL_Renderer *renderer); -void destroy_viewer(struct viewer *v); - -bool render_pages(struct viewer *v, - int x, int y, int width, int height, double scale, bool refresh); -void pencil(struct viewer *v, - double x1, double y1, double x2, double y2, bool bit); - -#endif diff --git a/src/curve.h b/src/curve.h new file mode 100644 index 0000000..dc003c1 --- /dev/null +++ b/src/curve.h @@ -0,0 +1,25 @@ +#ifndef CURVE_H +#define CURVE_H + +#include <limits.h> + +#define BIT_OF(X, I) (((X) >> (I)) & 1ULL) + +static uintptr_t zorder(int x, int y) { + uintptr_t z = 0; + // interleaving bits results in a point on the z-order curve + for (int i = 0; i < sizeof(x) * CHAR_BIT; i++) + z |= (BIT_OF(x, i) << i*2) | (BIT_OF(y, i) << (i*2 + 1)); + return z; +} + +static void unzorder(uintptr_t z, int *x, int *y) { + *x = 0; *y = 0; + for (int i = 0; i < sizeof(z) * CHAR_BIT / 2; i++) { + *x |= BIT_OF(z, i*2) << i; + *y |= BIT_OF(z, i*2 + 1) << i; + } +} + +#undef BIT_OF +#endif diff --git a/src/hash.h b/src/hash.h new file mode 100644 index 0000000..238b15b --- /dev/null +++ b/src/hash.h @@ -0,0 +1,15 @@ +#ifndef HASH_H +#define HASH_H + +#include <stdint.h> + +static uint64_t fnv(uint8_t *data, size_t size) { + uint64_t hash = 0xcbf29ce484222325; + for (size_t i = 0; i < size; i++) { + hash ^= data[i]; + hash *= 0x100000001b3; + } + return hash; +} + +#endif diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..a806692 --- /dev/null +++ b/src/main.c @@ -0,0 +1,391 @@ +#include <SDL3/SDL.h> +#include <stdlib.h> +#include <stdbool.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <errno.h> +#include "panic.h" +#include "curve.h" +#include "procfs.h" +#include "memory.h" + +SDL_Window *window; +SDL_Renderer *renderer; + +static int window_width = 640; +static int window_height = 480; + +static double scale = 1; +static double pos_x = 1024; +static double pos_y = 0; +static bool brush_bit = true; + +static pid_t pid = -1; +static bool is_child = false; +static bool stopped = false; +static page_list pages = {.fd = -1, .first = NULL}; +static procfs_map *mappings = NULL; +static int n_mappings = 0; + +static double get_scale() { + if (scale >= 1) { + return SDL_round(scale); + } + return scale; +} + +static void add_scale(double amount) { + float mx, my; + SDL_GetMouseState(&mx, &my); + double old_scale = get_scale(); + scale += amount; + if (scale > 50) scale = 50; + else if (scale < 0.125) scale = 0.125; + pos_x -= mx/get_scale() - mx/old_scale; + pos_y -= my/get_scale() - my/old_scale; +} + +static void absolute_pos(double *x, double *y) { + *x = *x / get_scale() + pos_x; + *y = *y / get_scale() + pos_y; +} + +static void go_to(uintptr_t addr) { + int ipos_x, ipos_y; + to_pos(addr, &ipos_x, &ipos_y); + pos_x = ipos_x; pos_y = ipos_y; + float mx, my; + SDL_GetMouseState(&mx, &my); + pos_x -= mx / get_scale(); + pos_y -= my / get_scale(); +} + +static void detach() { + if (pid == -1) return; + if (pages.fd != -1) { + int fd = pages.fd; + free_page_list(&pages); + close(fd); + } + if (is_child) { + kill(pid, SIGKILL); + while (true) { + int status; + pid_t w = waitpid(pid, &status, 0); + if (w == -1) { + break; + } + if (w == pid && WIFEXITED(status)) { + break; + } + } + } + pid = -1; + is_child = false; +} + +static bool attach(pid_t p) { + detach(); + pid = p; + int fd = procfs_open(pid); + if (fd == -1) { + pid = -1; + fprintf(stderr, "error attaching to pid %d: %s\n", p, strerror(errno)); + return false; + } + init_page_list(&pages, fd); + stopped = false; + n_mappings = procfs_maps(pid, &mappings); + for (int i = 0; i < n_mappings; i++) { + if (strcmp("[stack]", mappings[i].name) == 0) { + go_to(mappings[i].max); + } + } + return true; +} + +static bool spawn(char *const cmdline[]) { + detach(); + int fildes[2]; + if (pipe(fildes) == -1) { + fprintf(stderr, "error starting child: pipe: %d\n", strerror(errno)); + return false; + } + pid_t child = fork(); + if (child == -1) { + fprintf(stderr, "error starting child: %d\n", strerror(errno)); + close(fildes[0]); + close(fildes[1]); + return false; + } + if (child == 0) { + close(fildes[0]); + if (fcntl(fildes[1], F_SETFD, FD_CLOEXEC) == -1) { + perror("fcntl"); + exit(-1); + } + if (execvp(cmdline[0], cmdline) == -1) { + const char *err = strerror(errno); + write(fildes[1], err, strlen(err)); + exit(-1); + } + } + close(fildes[1]); + char child_err[2048] = {0}; + ssize_t n = read(fildes[0], child_err, 2047); + close(fildes[0]); + if (n == -1) { + fprintf(stderr, + "error starting child: read pipe: %d\n", strerror(errno) + ); + goto err; + } + if (n != 0) { + fprintf(stderr, "%s: %s\n", cmdline[0], child_err); + } + + if (attach(child)) { + is_child = true; + return true; + } +err: + kill(child, SIGKILL); + while (true) { + int status; + pid_t w = waitpid(child, &status, 0); + if (w == -1) { + break; + } + if (w == child && WIFEXITED(status)) { + break; + } + } + return false; +} + +static void handle_child() { + int status; + pid_t w = waitpid(pid, &status, WNOHANG); + if (w == -1) { + return; + } + if (w == pid && WIFEXITED(status)) { + is_child = false; + detach(); + fprintf( + stderr, "child exited with status %d\n", WEXITSTATUS(status) + ); + } +} + +static bool held(SDL_Keycode k) { + const bool *state = SDL_GetKeyboardState(NULL); + return state[SDL_GetScancodeFromKey(k, NULL)]; +} + +static void handle_key(SDL_Keycode key) { + switch (key) { + case SDLK_SPACE: + if (pid == -1) break; + kill(pid, stopped ? SIGSTOP : SIGCONT); + stopped = !stopped; + break; + case SDLK_E: + brush_bit = !brush_bit; + break; + default: + } +} + +static bool handle_event(SDL_Event e) { + switch (e.type) { + case SDL_EVENT_KEY_DOWN: + handle_key(e.key.key); + break; + case SDL_EVENT_MOUSE_MOTION: + SDL_MouseButtonFlags state = e.motion.state; + if (state & SDL_BUTTON_LMASK) { + pos_x -= (double) e.motion.xrel / get_scale(); + pos_y -= (double) e.motion.yrel / get_scale(); + } break; + case SDL_EVENT_MOUSE_WHEEL: + add_scale(e.wheel.y * get_scale() / 10); + break; + case SDL_EVENT_QUIT: + return false; + break; + default: + } + return true; +} + +static void update() { + SDL_GetWindowSize(window, &window_width, &window_height); + if (pid != -1) { + n_mappings = procfs_maps(pid, &mappings); + if (is_child) handle_child(); + } + if (held(SDLK_W)) { + pos_y -= 5 / get_scale(); + } + if (held(SDLK_S)) { + pos_y += 5 / get_scale(); + } + if (held(SDLK_A)) { + pos_x -= 5 / get_scale(); + } + if (held(SDLK_D)) { + pos_x += 5 / get_scale(); + } + float mx, my; + static bool mouse_held = false; + static double px, py; + SDL_MouseButtonFlags state = SDL_GetMouseState(&mx, &my); + if (state & SDL_BUTTON_RMASK) { + double x2 = mx, y2 = my; + absolute_pos(&x2, &y2); + double x1, y1; + if (mouse_held) { + x1 = px; y1 = py; + } else { + x1 = x2; y1 = y2; + } + px = x2; py = y2; + mouse_held = true; + draw_line(&pages, x1, y1, x2, y2, brush_bit); + } else { + mouse_held = false; + } +} + +static SDL_Texture *missing_texture(); + +static void show_pages() { + double s = get_scale(); + + int page_x = pos_x / PAGE_WIDTH; + int page_y = pos_y / PAGE_HEIGHT; + + double offs_x = SDL_fmod(pos_x, PAGE_WIDTH) * s; + double offs_y = SDL_fmod(pos_y, PAGE_HEIGHT) * s; + + double page_width = PAGE_WIDTH * s; + double page_height = PAGE_HEIGHT * s; + + double view_width = window_width / page_width; + double view_height = window_height / page_height; + + for (int draw_y = -1; draw_y <= view_height + 1; draw_y++) { + for (int draw_x = -1; draw_x <= view_width + 1; draw_x++) { + SDL_FRect src = {0, 0, PAGE_WIDTH, PAGE_HEIGHT}; + SDL_FRect dest = { + draw_x * page_width - offs_x, draw_y * page_height - offs_y, + page_width, page_height + }; + SDL_Texture *tex = NULL; + if (pid != -1) { + uintptr_t addr = zorder(page_x + draw_x, page_y + draw_y); + addr *= PAGE_SIZE; + page *p = get_page(&pages, addr); + tex = get_texture(p); + } + if (!tex) { + tex = missing_texture(); + } + if (s < 0.75) { + SDL_SetTextureScaleMode(tex, SDL_SCALEMODE_LINEAR); + } else { + SDL_SetTextureScaleMode(tex, SDL_SCALEMODE_NEAREST); + } + SDL_RenderTexture(renderer, tex, &src, &dest); + } + } + free_unused_pages(&pages); +} + +static SDL_Texture *missing_texture() { + static SDL_Texture *tex = NULL; + if (!tex) { + tex = must(SDL_CreateTexture(renderer, + SDL_PIXELFORMAT_RGBA32, + SDL_TEXTUREACCESS_STREAMING, + PAGE_WIDTH, PAGE_HEIGHT + )); + uint32_t *pixels; int pitch; + SDL_Rect rect = {0, 0, PAGE_WIDTH, PAGE_HEIGHT}; + must(SDL_LockTexture(tex, &rect, (void **) &pixels, &pitch)); + for (int i = 0; i < PAGE_WIDTH * PAGE_HEIGHT; i++) { + if (((i / 32) + i / PAGE_WIDTH / 32) % 2) { + pixels[i] = 0xFF151515; + } else { + pixels[i] = 0xFF222222; + } + } + SDL_UnlockTexture(tex); + } + return tex; +} + +static void main_loop() { + int last_time = 0, current_time = 0; + while (true) { + update(); + SDL_Event e; + while (SDL_PollEvent(&e)) { + if (!handle_event(e)) { + return; + } + } + SDL_GetWindowSize(window, &window_width, &window_height); + SDL_RenderClear(renderer); + show_pages(); + SDL_RenderPresent(renderer); + + last_time = current_time; + current_time = SDL_GetTicks(); + int elapsed = current_time - last_time; + if (elapsed < 5) SDL_Delay(5 - elapsed); + } +} + +static void usage(const char *name) { + fprintf(stderr, "usage: %s -p <pid>\n", name); + fprintf(stderr, " %s command [arg...]\n", name); + exit(-1); +} + +int main(int argc, char *argv[]) { + must(SDL_Init(SDL_INIT_VIDEO)); + must(SDL_CreateWindowAndRenderer("core", + window_width, window_height, SDL_WINDOW_RESIZABLE, &window, &renderer + )); + SDL_SetRenderVSync(renderer, 1); + + if (argc > 1) { + if (argv[1][0] == '-') { + if (strcmp(argv[1], "-p") == 0) { + if (argc < 3) { + usage(argv[0]); + } + pid_t arg = strtol(argv[2], NULL, 10); + if (arg == 0 && errno == EINVAL) { + usage(argv[0]); + } + attach(arg); + } else { + usage(argv[0]); + } + } else { + spawn(argv + 1); + } + } + main_loop(); + detach(); + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + SDL_Quit(); + return 0; +}
\ No newline at end of file diff --git a/src/main.h b/src/main.h new file mode 100644 index 0000000..83adbe8 --- /dev/null +++ b/src/main.h @@ -0,0 +1,7 @@ +#ifndef MAIN_H +#define MAIN_H + +extern SDL_Window *window; +extern SDL_Renderer *renderer; + +#endif diff --git a/src/memory.c b/src/memory.c new file mode 100644 index 0000000..373ca27 --- /dev/null +++ b/src/memory.c @@ -0,0 +1,140 @@ +#include <stdlib.h> +#include <stdint.h> +#include <limits.h> +#include <stdbool.h> +#include <SDL3/SDL.h> +#include <sys/mman.h> +#include "main.h" +#include "procfs.h" +#include "hash.h" +#include "curve.h" +#include "panic.h" +#include "memory.h" + +uintptr_t to_addr(int x, int y) { + unsigned int ux, uy; + memcpy(&ux, &x, sizeof(unsigned int)); + memcpy(&uy, &y, sizeof(unsigned int)); + uintptr_t page = zorder(ux / PAGE_WIDTH, uy / PAGE_HEIGHT); + int offset = + x % PAGE_WIDTH / CHAR_BIT + y % PAGE_HEIGHT * PAGE_WIDTH / CHAR_BIT; + return page * PAGE_SIZE + offset; +} + +void to_pos(uintptr_t addr, int *x, int *y) { + int px, py; + unzorder(addr / PAGE_SIZE, &px, &py); + int offset = addr % PAGE_SIZE; + int ux = px * PAGE_WIDTH + offset % PAGE_WIDTH; + int uy = py * PAGE_HEIGHT + offset / PAGE_WIDTH; + memcpy(x, &ux, sizeof(int)); + memcpy(y, &uy, sizeof(int)); +} + +int init_page_list(page_list *l, int fd) { + l->fd = fd; +} + +static void free_page(page *p) { + if (p->tex) { + SDL_DestroyTexture(p->tex); + } + free(p); +} + +void free_page_list(page_list *l) { + page *p = l->first; + while (p) { + page *next = p->next; + free_page(p); + p = next; + } + l->fd = -1; + l->first = NULL; +} + +void free_unused_pages(page_list *l) { + page **prev_link = &l->first; + page *p = l->first; + while (p) { + page *next = p->next; + if (!p->in_use) { + *prev_link = next; + free_page(p); + } else { + prev_link = &p->next; + p->in_use = false; + } + p = next; + } +} + +page *get_page(page_list *l, uintptr_t addr) { + addr = addr - addr % PAGE_SIZE; + for (page *p = l->first; p; p = p->next) { + if (p->address == addr) { + return p; + } + } + page *p = calloc(1, sizeof(page)); + if (!p) panic("out of memory"); + p->address = addr; + p->l = l; + p->next = l->first; + l->first = p; + return p; +} + +static SDL_Palette *texture_palette() { + SDL_Color colors[] = {{0x00, 0x00, 0x00, 0xFF}, {0xFF, 0xFF, 0xFF, 0xFF}}; + static SDL_Palette *palette = NULL; + if (!palette) { + palette = must(SDL_CreatePalette(2)); + must(SDL_SetPaletteColors(palette, colors, 0, 2)); + } + return palette; +} + +SDL_Texture *get_texture(page *p) { + static char data[PAGE_SIZE]; + if (read_mem(p->l->fd, p->address, data, PAGE_SIZE) != 0) { + return NULL; + } + uint32_t hash = fnv(data, PAGE_SIZE); + if (p->hash != hash) { + if (p->tex) SDL_DestroyTexture(p->tex); + SDL_Surface *surface = must(SDL_CreateSurfaceFrom( + PAGE_WIDTH, PAGE_HEIGHT, SDL_PIXELFORMAT_INDEX1LSB, data, + PAGE_WIDTH / 8 + )); + p->tex = must(SDL_CreateTextureFromSurface(renderer, surface)); + must(SDL_SetTexturePalette(p->tex, texture_palette())); + SDL_DestroySurface(surface); + } + p->hash = hash; + p->in_use = true; + return p->tex; +} + +static void write_bit(int fd, int x, int y, bool bit) { + uintptr_t addr = to_addr(x, y); + int bit_offs = x % CHAR_BIT; + uint8_t byte; + if (read_mem(fd, addr, &byte, 1) == -1) return; + byte = (byte & ~(1 << bit_offs)) | (bit << bit_offs); + write_mem(fd, addr, &byte, 1); +} + +void draw_line(page_list *l, + double x1, double y1, double x2, double y2, bool bit) { + double dx = (x2 - x1); + double dy = (y2 - y1); + int step = abs(dx) >= abs(dy) ? abs(dx) : abs(dy); + dx = dx / step; dy = dy / step; + double x = x1; double y = y1; + for (int i = 0; i <= step; i++) { + write_bit(l->fd, x, y, bit); + x = x + dx; + y = y + dy; + } +} diff --git a/src/memory.h b/src/memory.h new file mode 100644 index 0000000..b472d31 --- /dev/null +++ b/src/memory.h @@ -0,0 +1,44 @@ +#ifndef MEMORY_H +#define MEMORY_H + +#include <stdlib.h> +#include <stdint.h> +#include <limits.h> +#include <stdbool.h> +#include <SDL3/SDL.h> + +#ifndef PAGE_SIZE +#define PAGE_SIZE 4096 +#endif + +#ifndef PAGE_WIDTH +#define PAGE_WIDTH 256 +#endif +#define PAGE_HEIGHT (PAGE_SIZE * CHAR_BIT / PAGE_WIDTH) + +typedef struct page { + struct page_list *l; + uintptr_t address; + SDL_Texture *tex; + uint64_t hash; + bool in_use; + struct page *next; +} page; + +typedef struct page_list { + int fd; + page *first; +} page_list; + +uintptr_t to_addr(int x, int y); +void to_pos(uintptr_t addr, int *x, int *y); + +int init_page_list(page_list *l, int fd); +page *get_page(page_list *l, uintptr_t addr); +void free_unused_pages(page_list *l); +SDL_Texture *get_texture(page *p); +void free_page_list(page_list *l); + +void draw_line(page_list *l, + double x1, double y1, double x2, double y2, bool bit); +#endif diff --git a/src/panic.h b/src/panic.h new file mode 100644 index 0000000..fed75d8 --- /dev/null +++ b/src/panic.h @@ -0,0 +1,28 @@ +#ifndef PANIC_H +#define PANIC_H + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <stdbool.h> +#include <SDL3/SDL.h> + +static void panic(const char *why, ...) { + if (!why) return; + va_list ap; + va_start(ap, why); + vfprintf(stderr, why, ap); + fprintf(stderr, "\n"); + va_end(ap); + abort(); +} + +#define must(result) ({ \ + typeof(result) must_result = result; \ + if (!must_result) { \ + panic("SDL: %s", SDL_GetError()); \ + } \ + must_result; \ +}) + +#endif @@ -2,6 +2,7 @@ #include <stdio.h> #include <stddef.h> #include <stdint.h> +#include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/mman.h> @@ -24,14 +25,31 @@ ssize_t procfs_maps(pid_t pid, struct procfs_map **maps) { int n = 0; int capacity = 2; + if (*maps) { + free(*maps); + } *maps = calloc(capacity, sizeof(**maps)); if (!*maps) return -1; while (1) { void *base, *max; char prot_str[4]; - int matches = fscanf(f, "%p-%p %4c", &base, &max, prot_str); + char name[4096] = {0}; + void *unused; + int matches = fscanf( + f, "%p-%p %4c %p %p:%p %d", + &base, &max, &prot_str, &unused, &unused, &unused, &unused + ); if (matches == EOF) goto eof; - if (matches < 3) goto err; + if (matches < 7) goto err; + + int c = ' '; + while (c == ' ') { + c = fgetc(f); + } + if (c == EOF) goto err; + ungetc(c, f); + if (fgets(name, 4096, f) == NULL) goto err; + name[strlen(name) - 1] = '\0'; // remove newline int prot = 0; for (int i = 0; i < 4; i++) { @@ -51,9 +69,14 @@ ssize_t procfs_maps(pid_t pid, struct procfs_map **maps) { } if (prot == 0) prot = PROT_NONE; - int c; - while ((c = getc(f)) != '\n') - if (c == EOF) goto eof; + if (n > 0) { + procfs_map *prev = *maps + (n - 1); + if ((strlen(name) == 0 || strncmp(prev->name, name, 1024) == 0) && + prev->max == (uintptr_t)base) { + prev->max = (uintptr_t)max; + continue; + } + } n++; if (n > capacity) { @@ -62,25 +85,34 @@ ssize_t procfs_maps(pid_t pid, struct procfs_map **maps) { if (!r) goto err; *maps = r; } - (*maps)[n - 1] = (struct procfs_map) { - (uintptr_t) base, (uintptr_t) max, prot - }; + procfs_map *new = *maps + (n - 1); + new->base = (uintptr_t)base; + new->max = (uintptr_t)max; + new->prot = prot; + strncpy(new->name, name, sizeof(new->name) - 1); } eof: if (ferror(f)) goto err; - if (n == 0) free(*maps); + fclose(f); + if (n == 0) { + free(*maps); + *maps = NULL; + } return n; err: + fclose(f); free(*maps); + *maps = NULL; return -1; } +// mmap does not work on /proc/pid/mem, so we issue reads/writes instead int read_mem(int fd, uintptr_t addr, uint8_t *data, size_t size) { if (lseek(fd, addr, SEEK_SET) == -1) return -1; size_t n = 0; while (n < size) { ssize_t result = read(fd, data + n, size - n); - if (result == -1) return -1; + if (result <= 0) return -1; n += result; } return 0; @@ -91,12 +123,8 @@ int write_mem(int fd, uintptr_t addr, uint8_t *data, size_t size) { size_t n = 0; while (n < size) { ssize_t result = write(fd, data + n, size - n); - if (result == -1) return -1; + if (result <= 0) return -1; n += result; } return 0; } - -int read_page(int fd, uintptr_t index, uint8_t *data) { - return read_mem(fd, index * PAGE_SIZE, data, PAGE_SIZE); -} @@ -13,13 +13,13 @@ int procfs_open(pid_t pid); int read_mem(int fd, uintptr_t addr, uint8_t *data, size_t size); int write_mem(int fd, uintptr_t addr, uint8_t *data, size_t size); -int read_page(int fd, uintptr_t index, uint8_t *data); -struct procfs_map { +typedef struct procfs_map { uintptr_t base; uintptr_t max; + char name[1024]; int prot; -}; +} procfs_map; ssize_t procfs_maps(pid_t pid, struct procfs_map **maps); char procfs_state(pid_t pid); |
