summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorraven <citrons@mondecitronne.com>2026-04-08 22:51:39 -0500
committerraven <citrons@mondecitronne.com>2026-04-08 22:51:39 -0500
commit18a86e1038b20cb6f8922beead08dcc24ba2a4d3 (patch)
tree85cb74210d67d42763a4709d18af93aa60a8f400
parent4a3429a96b5b5ea7468540349aeb4535d5738053 (diff)
rewrite and port to SDL3
-rw-r--r--.gitignore3
-rw-r--r--Makefile34
-rw-r--r--core.c226
-rw-r--r--evilize.c65
-rw-r--r--memview.c161
-rw-r--r--memview.h29
-rw-r--r--src/curve.h25
-rw-r--r--src/hash.h15
-rw-r--r--src/main.c391
-rw-r--r--src/main.h7
-rw-r--r--src/memory.c140
-rw-r--r--src/memory.h44
-rw-r--r--src/panic.h28
-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
diff --git a/.gitignore b/.gitignore
index 46e4cb0..796b96d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1 @@
-core
-*.o
+/build
diff --git a/Makefile b/Makefile
index 903b7e9..f6d01c7 100644
--- a/Makefile
+++ b/Makefile
@@ -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)
diff --git a/core.c b/core.c
deleted file mode 100644
index de685b6..0000000
--- a/core.c
+++ /dev/null
@@ -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
diff --git a/procfs.c b/src/procfs.c
index 1e70274..d377e02 100644
--- a/procfs.c
+++ b/src/procfs.c
@@ -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);
-}
diff --git a/procfs.h b/src/procfs.h
index 70a652e..7196a24 100644
--- a/procfs.h
+++ b/src/procfs.h
@@ -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);