diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..41f5cb4 --- /dev/null +++ b/build.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +set -e +set -x + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +pushd $SCRIPT_DIR + +if [[ $# = 0 ]]; then + pushd src + mkdir -p ../build + gcc main.c -g -O0 -o ../build/trace -Wall -Wextra -Wno-unused-variable -Wno-unused-function -Wno-switch # -fsanitize=address + popd +elif [[ $1 = "examples" ]]; then + pushd res + gcc -g -o 001-simple 001-simple.c + gcc -g -o 002-compilation-units 002-compilation-units.c 002-compilation-units-impl.c + gcc -g -o 003-recursion 003-recursion.c + gcc -g -o 004-scoped-variables 004-scoped-variables.c + gcc -g -o 005-base-types 005-base-types.c + gcc -g -o 006-composite-types 006-composite-types.c + popd +elif [[ $1 = "tests" ]]; then + pushd test/ + gcc test_main.c -g -o ../build/test -Wall -Wextra -Wno-unused-function -Wno-unused-variable + popd +elif [[ $1 = "clean" ]]; then + rm build/* +else + echo "No such target '$1'" +fi + +popd \ No newline at end of file diff --git a/project.4coder b/project.4coder index bfdf018..ef5fc97 100644 --- a/project.4coder +++ b/project.4coder @@ -11,6 +11,7 @@ patterns = { blacklist_patterns = { ".*", "res", + "test", "lib", "build/*" }; @@ -30,8 +31,26 @@ command_list = { .footer_panel = false, .save_dirty_files = true, .cursor_at_end = true, - .cmd = {{ "cd src && make debug && cd ..", .os = "linux" }}, + .cmd = {{ "./build.sh", .os = "linux" }}, + }, + { + .name = "build examples", + .out = "*compilation*", + .footer_panel = false, + .save_dirty_files = true, + .cursor_at_end = true, + .cmd = {{ "./build.sh examples", .os = "linux" }}, + }, + { + .name = "build tests", + .out = "*compilation*", + .footer_panel = false, + .save_dirty_files = true, + .cursor_at_end = true, + .cmd = {{ "./build.sh tests", .os = "linux" }}, }, }; fkey_command[1] = "build debug"; +fkey_command[2] = "build examples"; +fkey_command[3] = "build tests"; diff --git a/res/002-compilation-units.c b/res/002-compilation-units.c index 09b0031..c02febb 100644 --- a/res/002-compilation-units.c +++ b/res/002-compilation-units.c @@ -5,5 +5,5 @@ main(void) { int a = 3; int b = foo(a); - return(b); + return(0); } diff --git a/res/Makefile b/res/Makefile deleted file mode 100644 index 2f79fb7..0000000 --- a/res/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -all: - gcc -g -o 001-simple 001-simple.c - gcc -g -o 002-compilation-units 002-compilation-units.c 002-compilation-units-impl.c - gcc -g -o 003-recursion 003-recursion.c - gcc -g -o 004-scoped-variables 004-scoped-variables.c - gcc -g -o 005-base-types 005-base-types.c - gcc -g -o 006-composite-types 006-composite-types.c diff --git a/res/traceme b/res/traceme deleted file mode 100755 index fdc818f..0000000 Binary files a/res/traceme and /dev/null differ diff --git a/src/Makefile b/src/Makefile deleted file mode 100644 index c64ff5d..0000000 --- a/src/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -debug: - gcc main.c -g -O0 -o ../build/trace -Wall -Wextra -Wno-unused-variable -Wno-unused-function -Wno-switch # -fsanitize=address diff --git a/src/command.c b/src/command.c index a1841fd..17ffd68 100644 --- a/src/command.c +++ b/src/command.c @@ -155,6 +155,8 @@ command_print(struct mi_process proc, char *name, int name_length) u64 address = cfa + variable->location; u64 raw_value = ptrace(PTRACE_PEEKDATA, proc.pid, address, NULL); + printf("\"%s\", %ld\n", type.name, variable->location); + u8 raw_value1; u16 raw_value2; u32 raw_value4; diff --git a/src/main.c b/src/main.c index 8f6ffba..1bcc0a0 100644 --- a/src/main.c +++ b/src/main.c @@ -21,8 +21,6 @@ main(int argc, char *argv[]) char *command = malloc(max_command_length + 1); char *last_command = malloc(max_command_length + 1); - process.base_address = 0x555555554000UL; // get_executable_base_address(file, proc.pid); - parse_debug_info(process.elf, &process.debug); parse_debug_line(process.elf, &process.debug); diff --git a/src/util.c b/src/util.c index 41472dc..22a987b 100644 --- a/src/util.c +++ b/src/util.c @@ -202,7 +202,7 @@ print_sourcepoint(struct mi_process proc, int comp_unit, struct mi_sourcepoint * } - printf("%.*s\n", len - start, source + i + start); + printf("%d:%d] %.*s\n", sp->line, sp->column, len - start, source + i + start); return; } @@ -236,11 +236,17 @@ process_create(char *path) } } + // TODO: readup man (2) ptrace: how to wait for execve + //waitpid(pid, 0, 0); + //ptrace(PTRACE_CONT, pid, 0, 0); + //waitpid(pid, 0, 0); + struct mi_buffer file = read_file_mmap(path); result.elf = file.data; result.elf_size = file.size; result.pid = pid; + result.base_address = 0x555555554000UL; // get_executable_base_address(file, proc.pid); return(result); } diff --git a/test/test_main.c b/test/test_main.c new file mode 100644 index 0000000..20743fe --- /dev/null +++ b/test/test_main.c @@ -0,0 +1,211 @@ +#include "../src/common.h" + +#include "../src/dwarf.c" +#include "../src/eh_frame.c" +#include "../src/util.c" +#include "../src/command.c" + +#define DO_TEST(title, call) do { \ +int rt = call; \ +if (rt == 0) { \ +fprintf(stderr, "Test '%s' \033[1m\033[32msuccess\033[0m...\n", title); \ +} else { \ +fprintf(stderr, "Test '%s' \033[1m\033[31mfail\033[0m\n", title); \ +exit(1); \ +} \ +} while(0) + +static int +test_start(char *path) +{ + struct mi_process proc = process_create(path); + waitpid(proc.pid, 0, 0); + parse_debug_info(proc.elf, &proc.debug); + proc.main_address = get_address_of_subroutine(proc, "main"); + command_start(proc); + struct mi_registers regs = get_process_registers(proc); + + int result = 1; + + if (proc.main_address == regs.rip - proc.base_address) { + result = 0; + } + + command_kill(proc); + waitpid(proc.pid, 0, 0); + + return(result); +} + +static int +test_regs(char *path) +{ + struct mi_process proc = process_create(path); + waitpid(proc.pid, 0, 0); + parse_debug_info(proc.elf, &proc.debug); + proc.main_address = get_address_of_subroutine(proc, "main"); + command_start(proc); + struct mi_registers regs = get_process_registers(proc); + + int result = 1; + if (regs.rbp > 0 && regs.rsp > 0 && regs.rip > 0) { + result = 0; + } + + command_kill(proc); + waitpid(proc.pid, 0, 0); + + return(result); +} + +static int +test_step(char *path, int steps, u64 pc) +{ + struct mi_process proc = process_create(path); + waitpid(proc.pid, 0, 0); + parse_debug_info(proc.elf, &proc.debug); + parse_debug_line(proc.elf, &proc.debug); + proc.main_address = get_address_of_subroutine(proc, "main"); + command_start(proc); + for (int i = 0; i < steps; ++i) { + command_step(proc); + } + struct mi_registers regs = get_process_registers(proc); + int result = 1; + if ((regs.rip - proc.base_address) == pc) { + result = 0; + } + command_kill(proc); + waitpid(proc.pid, 0, 0); + return(result); +} + +static int +test_next(char *path, int nexts, u64 pc) +{ + struct mi_process proc = process_create(path); + waitpid(proc.pid, 0, 0); + parse_debug_info(proc.elf, &proc.debug); + parse_debug_line(proc.elf, &proc.debug); + proc.main_address = get_address_of_subroutine(proc, "main"); + command_start(proc); + for (int i = 0; i < nexts; ++i) { + command_next(proc); + } + struct mi_registers regs = get_process_registers(proc); + int result = 1; + if ((regs.rip - proc.base_address) == pc) { + result = 0; + } + command_kill(proc); + waitpid(proc.pid, 0, 0); + return(result); +} + +static int +test_list(char *path, int steps, int line, int column) +{ + struct mi_process proc = process_create(path); + waitpid(proc.pid, 0, 0); + parse_debug_info(proc.elf, &proc.debug); + parse_debug_line(proc.elf, &proc.debug); + proc.main_address = get_address_of_subroutine(proc, "main"); + command_start(proc); + for (int i = 0; i < steps; ++i) { + command_step(proc); + } + + int comp_unit; + struct mi_registers regs = get_process_registers(proc); + struct mi_sourcepoint *sp = pc_to_sourcepoint(proc, regs.rip - proc.base_address, &comp_unit); + + int result = 1; + if (sp->line == line && sp->column == column) { + result = 0; + } + command_kill(proc); + waitpid(proc.pid, 0, 0); + return(result); +} + +static int +test_print(char *path, int steps, char *name, char *type, int location) +{ + struct mi_process proc = process_create(path); + waitpid(proc.pid, 0, 0); + parse_debug_info(proc.elf, &proc.debug); + parse_debug_line(proc.elf, &proc.debug); + proc.main_address = get_address_of_subroutine(proc, "main"); + command_start(proc); + for (int i = 0; i < steps; ++i) { + command_step(proc); + } + + struct mi_registers regs = get_process_registers(proc); + u64 pc = regs.rip - proc.base_address; + u64 cfa = get_cfa_at_pc(proc, pc); + + struct mi_variable *variable = get_variable(proc, name, strlen(name), pc); + struct mi_type real_type = proc.debug.types[variable->type]; + + int result = 1; + int type_length = strlen(type); + if (0 == strncmp(type, real_type.name, type_length) && variable->location == location) { + result = 0; + } + command_kill(proc); + waitpid(proc.pid, 0, 0); + return(result); +} + +int +main(void) +{ + DO_TEST("start 001-simple", test_start("res/001-simple")); + DO_TEST("start 002-compilation-units", test_start("res/002-compilation-units")); + DO_TEST("start 003-recursion", test_start("res/003-recursion")); + DO_TEST("start 004-scoped-variables", test_start("res/004-scoped-variables")); + DO_TEST("start 005-base-types", test_start("res/005-base-types")); + DO_TEST("start 006-composite-types", test_start("res/006-composite-types")); + + DO_TEST("regs 001-simple", test_regs("res/001-simple")); + DO_TEST("regs 002-compilation-units", test_regs("res/002-compilation-units")); + DO_TEST("regs 003-recursion", test_regs("res/003-recursion")); + DO_TEST("regs 004-scoped-variables", test_regs("res/004-scoped-variables")); + DO_TEST("regs 005-base-types", test_regs("res/005-base-types")); + DO_TEST("regs 006-composite-types", test_regs("res/006-composite-types")); + + DO_TEST("step 001-simple", test_step("res/001-simple", 4, 0x1166)); + DO_TEST("step 002-compilation-units", test_step("res/002-compilation-units", 4, 0x114f)); + DO_TEST("step 003-recursion", test_step("res/003-recursion", 9, 0x1130)); + DO_TEST("step 004-scoped-variables", test_step("res/004-scoped-variables", 7, 0x1145)); + DO_TEST("step 005-base-types", test_step("res/005-base-types", 3, 0x113e)); + DO_TEST("step 006-composite-types", test_step("res/006-composite-types", 5, 0x114c)); + + DO_TEST("next 001-simple", test_next("res/001-simple", 4, 0x1166)); + DO_TEST("next 002-compilation-units", test_next("res/002-compilation-units", 4, 0x1146)); + DO_TEST("next 003-recursion", test_next("res/003-recursion", 2, 0x116b)); + DO_TEST("next 004-scoped-variables", test_next("res/004-scoped-variables", 4, 0x11a3)); + DO_TEST("next 005-base-types", test_next("res/005-base-types", 3, 0x113e)); + DO_TEST("next 006-composite-types", test_next("res/006-composite-types", 5, 0x114c)); + + DO_TEST("list 001-simple", test_list("res/001-simple", 4, 12, 20)); + DO_TEST("list 002-compilation-units", test_list("res/002-compilation-units", 4, 5, 6)); + DO_TEST("list 003-recursion", test_list("res/003-recursion", 9, 4, 5)); + DO_TEST("list 004-scoped-variables", test_list("res/004-scoped-variables", 7, 15, 6)); + DO_TEST("list 005-base-types", test_list("res/005-base-types", 3, 6, 7)); + DO_TEST("list 006-composite-types", test_list("res/006-composite-types", 5, 18, 14)); + + DO_TEST("print 001-simple", test_print("res/001-simple", 4, "i", "int", -28)); + DO_TEST("print 002-compilation-units", test_print("res/002-compilation-units", 5, "sq", "int", -20)); + DO_TEST("print 003-recursion", test_print("res/003-recursion", 28, "result", "int", -20)); + DO_TEST("print 004-scoped-variables", test_print("res/004-scoped-variables", 9, "b_f2", "int", -24)); + DO_TEST("print 005-base-types long", test_print("res/005-base-types", 5, "var_unsigned_long", "long unsigned int", -40)); + DO_TEST("print 005-base-types char", test_print("res/005-base-types", 4, "var_char", "char", -41)); + DO_TEST("print 005-base-types float", test_print("res/005-base-types", 8, "var_float", "float", -48)); + DO_TEST("print 005-base-types double", test_print("res/005-base-types", 9, "var_double", "double", -56)); + + fprintf(stderr, "\033[1m\033[32mAll tests succeeded!\033[0m\n"); + + return(0); +} \ No newline at end of file