#include #include #include #include #include #include #include #include #include #include #include "panic.h" #include "curve.h" #include "procfs.h" #include "memory.h" #include "font.h" #include "menu.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 menu *current_menu = NULL; 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; pos_x -= window_width / 2 / get_scale(); pos_y -= window_height / 2 / 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 void replace_menu(menu *new) { if (current_menu) close_menu(current_menu); current_menu = new; } static void map_menu_selected(void *data) { go_to((uintptr_t) data); } static void open_map_menu() { menu_entry *entries = calloc(n_mappings, sizeof(menu_entry)); for (int i = 0; i < n_mappings; i++) { entries[i].action = map_menu_selected; entries[i].data = (void *) mappings[i].base; snprintf( entries[i].name, sizeof(entries[i].name), "%p %s", mappings[i].base, mappings[i].name ); } float mx, my; SDL_GetMouseState(&mx, &my); replace_menu(create_menu(entries, n_mappings, mx, my)); } 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) { if (current_menu) { if (!menu_handle_event(current_menu, e)) { close_menu(current_menu); current_menu = NULL; } return true; } 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_BUTTON_DOWN: uint8_t button = e.button.button; if (button == SDL_BUTTON_MIDDLE) { open_map_menu(); } else if (button == SDL_BUTTON_RIGHT && held(SDLK_LSHIFT)) { open_map_menu(); } else if (button == SDL_BUTTON_RIGHT && held(SDLK_RSHIFT)) { open_map_menu(); } 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 (current_menu) return; 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) { SDL_Event e; while (SDL_PollEvent(&e)) { if (!handle_event(e)) { return; } } update(); SDL_GetWindowSize(window, &window_width, &window_height); SDL_RenderClear(renderer); show_pages(); SDL_RenderPresent(renderer); if (current_menu) menu_render(current_menu); 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 \n", name); fprintf(stderr, " %s command [arg...]\n", name); exit(-1); } int main(int argc, char *argv[]) { must(TTF_Init()); load_font(); 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; }