summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorthe lemons <citrons@mondecitronne.com>2023-02-05 19:06:16 -0600
committerthe lemons <citrons@mondecitronne.com>2023-02-05 19:06:16 -0600
commit3af50b956b8887b64c3c29b30a0008be866b24c4 (patch)
treeaf2a6f736505585192f852342ae988a297024133
initial commit
-rw-r--r--Makefile18
-rw-r--r--core.c150
-rw-r--r--memview.c124
-rw-r--r--memview.h28
-rw-r--r--procfs.c28
-rw-r--r--procfs.h15
6 files changed, 363 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..50a35f4
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,18 @@
+SDL_HEADERS=/usr/include/SDL2
+
+CFLAGS=-g -Wall -I$(SDL_HEADERS)
+LFLAGS=-lSDL2
+
+core: core.o procfs.o memview.o
+ $(CC) -o $@ $^ $(LFLAGS)
+
+core.c: procfs.h memview.h
+memview.c: procfs.h memview.h
+procfs.c: procfs.h
+
+.SUFFIXES: .c .o
+.c.o:
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+clean:
+ rm -f *.o core
diff --git a/core.c b/core.c
new file mode 100644
index 0000000..96ba866
--- /dev/null
+++ b/core.c
@@ -0,0 +1,150 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <assert.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;
+
+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 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 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 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:
+ if (e.motion.state & SDL_BUTTON_RMASK) {
+ pos_x -= (double) e.motion.xrel / scale;
+ pos_y -= (double) e.motion.yrel / scale;
+ refresh = true;
+ }
+ mouse_x = e.motion.x;
+ mouse_y = e.motion.y;
+ break;
+ case SDL_MOUSEWHEEL:
+ if (e.wheel.y == 0) break;
+ add_scale((double) e.wheel.y / 2);
+ refresh = true;
+ break;
+ default:
+ break;
+ }
+ }
+ 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;
+ }
+ int 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(argv[0]);
+ viewer = create_viewer(mem_fd, renderer);
+ if (!viewer) abort();
+ atexit(cleanup);
+
+ goto_addr(0x7fea87727000);
+
+ 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/memview.c b/memview.c
new file mode 100644
index 0000000..c2af3ac
--- /dev/null
+++ b/memview.c
@@ -0,0 +1,124 @@
+#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];
+};
+
+enum color {
+ BLACK = 0x00000000,
+ WHITE = 0xFFFFFFFF,
+ GREY = 0xFF303030,
+};
+
+#define BIT_OF(X, I) (((X) >> (I)) & 1ULL)
+
+static uintptr_t zorder(int x, int y) {
+ uintptr_t z = 0;
+ unsigned int ux, uy;
+ memcpy(&ux, &x, sizeof(unsigned int));
+ memcpy(&uy, &y, sizeof(unsigned int));
+ // interleaving bits results in a point on the z-order curve
+ for (int i = 0; i < sizeof(x) * CHAR_BIT; i++)
+ z |= (BIT_OF(ux, i) << i*2) | (BIT_OF(uy, i) << (i*2 + 1));
+ return z;
+}
+
+static void unzorder(uintptr_t z, int *x, int *y) {
+ unsigned int ux, uy = 0;
+ for (int i = 0; i < sizeof(z) * CHAR_BIT / 2; i++) {
+ ux |= BIT_OF(z, i*2) << i;
+ uy |= BIT_OF(z, i*2 + 1) << i;
+ }
+ memcpy(x, &ux, sizeof(int));
+ memcpy(y, &uy, sizeof(int));
+}
+
+uintptr_t to_addr(int x, int y) {
+ uintptr_t page = zorder(x / PAGE_WIDTH, y / PAGE_HEIGHT);
+ int offset = x % PAGE_WIDTH + y % PAGE_HEIGHT * PAGE_WIDTH;
+ 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;
+ *x = px * PAGE_WIDTH + offset % PAGE_WIDTH;
+ *y = py * PAGE_HEIGHT + offset / PAGE_WIDTH;
+}
+
+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 ? WHITE : BLACK;
+ }
+ }
+ } else {
+ for (int i = 0; i < PAGE_WIDTH * PAGE_HEIGHT; i++)
+ pixels[i] = GREY;
+ }
+ SDL_UnlockTexture(tex);
+}
+
+void 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;
+
+ 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;
+ // TODO: unless refresh is true, do not redraw if page unaltered
+ render_page(v->page_texture, success ? v->page_buffer : NULL);
+ SDL_RenderCopy(v->renderer, v->page_texture, &src, &dest);
+ }
+ }
+}
diff --git a/memview.h b/memview.h
new file mode 100644
index 0000000..186bfa8
--- /dev/null
+++ b/memview.h
@@ -0,0 +1,28 @@
+#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);
+
+void render_pages(struct viewer *v,
+ int x, int y, int width, int height, double scale, bool refresh);
+void pencil(struct viewer *v, int x1, int y1, int x2, int y2, bool bit);
+
+#endif
diff --git a/procfs.c b/procfs.c
new file mode 100644
index 0000000..63aef3a
--- /dev/null
+++ b/procfs.c
@@ -0,0 +1,28 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "procfs.h"
+
+int procfs_open(pid_t pid) {
+ char path[2048];
+ sprintf(path, "/proc/%d/mem", pid);
+ return open(path, O_RDWR);
+}
+
+int read_page(int fd, uintptr_t index, uint8_t *data) {
+ if (lseek(fd, index * PAGE_SIZE, SEEK_SET) == -1)
+ return -1;
+ size_t n = 0;
+ while (n < PAGE_SIZE) {
+ ssize_t result = read(fd, data + n, PAGE_SIZE - n);
+ if (result == -1) return -1;
+ n += result;
+ }
+ return 0;
+}
diff --git a/procfs.h b/procfs.h
new file mode 100644
index 0000000..7b81a13
--- /dev/null
+++ b/procfs.h
@@ -0,0 +1,15 @@
+#ifndef CORE_PROCFS_H
+#define CORE_PROCFS_H
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <sys/types.h>
+
+#ifndef PAGE_SIZE
+#define PAGE_SIZE (4096)
+#endif
+
+int procfs_open(pid_t pid);
+int read_page(int fd, uintptr_t index, uint8_t *data);
+
+#endif