commit edd1067fdee49d8a445ecbbad6d36449a6b5d6a8 Author: A.Olokhtonov Date: Sat Jul 10 21:29:04 2021 +0300 Initital trace tests. Source-level stepping works! (but with a little secret) diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..afed320 --- /dev/null +++ b/Makefile @@ -0,0 +1,2 @@ +debug: + gcc main.c -g -O0 -o build/trace -Wall -Wextra -Wno-unused-variable -Wno-unused-function -Wno-switch diff --git a/dwarf.c b/dwarf.c new file mode 100644 index 0000000..edf2a4a --- /dev/null +++ b/dwarf.c @@ -0,0 +1,675 @@ +static u64 +get_section_offset(u8 *file, char *name) +{ + struct elf_header_x64 header = { 0 }; + memcpy(&header, file, sizeof(header)); + + for (int i = 0; i < header.header_table_entry_count; ++i) { + struct elf_header_table_entry_x64 header_entry = { 0 }; + u64 offset = header.header_table_offset + header.header_table_entry_size * i; + memcpy(&header_entry, file + offset, sizeof(header_entry)); + int a = 1; + } + + struct elf_section_table_entry_x64 shstrtab_header = { 0 }; + u64 shstrtab_header_offset = header.section_table_offset + header.section_table_entry_size * header.section_names_table_index; + memcpy(&shstrtab_header, file + shstrtab_header_offset, sizeof(shstrtab_header)); + + u64 shstrtab_offset = shstrtab_header.offset_in_file; + u64 debug_info_offset = 0; + + for (int i = 0; i < header.section_table_entry_count; ++i) { + struct elf_section_table_entry_x64 section_entry = { 0 }; + u64 offset = header.section_table_offset + header.section_table_entry_size * i; + memcpy(§ion_entry, file + offset, sizeof(section_entry)); + u64 section_name_offset = shstrtab_offset + section_entry.name_offset; + + if (strncmp((char *) file + section_name_offset, name, strlen(name) + 1) == 0) { + debug_info_offset = section_entry.offset_in_file; + } + + } + + return(debug_info_offset); +} + +static int +dwarf_section_contribution_is_64(u8 *file, u64 offset) +{ + u32 length; + memcpy(&length, file + offset, 4); + return(length >= 0xFFFFFFF0); +} + +static int +decode_leb128(u8 *at, u32 *dest) +{ + int offset = 0; + + u64 result = 0; + u64 shift = 0; + + while (1) { + u8 byte = at[offset++]; + + result |= ((byte & 127) << shift); + + if ((byte & 128) == 0) { + break; + } + + shift += 7; + } + + if (dest) { + *dest = result; + } + + return(offset); +} + +static int +decode_leb128s(u8 *at, s32 *dest) +{ + int offset = 0; + + s64 result = 0; + u64 shift = 0; + u32 size = 32; + u8 byte; + + while (1) { + byte = at[offset++]; + + result |= ((byte & 127) << shift); + + if ((byte & 128) == 0) { + break; + } + + shift += 7; + } + + if ((shift < size) && (byte & 128)) { + result |= -(1 << shift); + } + + if (dest) { + *dest = result; + } + + return(offset); +} + +static u64 +abbrev_entry_offset(u8 *file, u64 abbrev_offset, u32 requested_code) +{ + u32 code, tag; + u32 offset = 0; + + do { + offset += decode_leb128(file + abbrev_offset + offset, &code); + offset += decode_leb128(file + abbrev_offset + offset, &tag); + + u32 has_children = file[abbrev_offset + offset++]; + + if (code == requested_code) { + return(abbrev_offset); + } + + if (code == 0) { + /* Abbreviation code not found, this should not happen */ + assert(0); + return(0); + } + + u32 attribute, form; + + do { + offset += decode_leb128(file + abbrev_offset + offset, &form); + offset += decode_leb128(file + abbrev_offset + offset, &attribute); + } while (attribute != 0 || form != 0); + + abbrev_offset += offset; + offset = 0; + } while (code != 0); + + assert(0); + + return(0); +} + +static u64 +find_subroutine_offset(u8 *file, u64 header_size, u8 address_size, + u64 string_offset, u64 abbrev_offset, u64 data_offset, + char *subroutine) +{ + u32 code, tag; + u64 schema_offset; + u32 depth = 0; + u64 original_data_offset = data_offset; + + int found_sr = 0; + + do { + data_offset += decode_leb128(file + data_offset, &code); + + if (code == 0) { + if (depth > 1) { + --depth; + continue; + } else { + break; + } + } + + schema_offset = abbrev_entry_offset(file, abbrev_offset, code); + schema_offset += decode_leb128(file + schema_offset, NULL); + schema_offset += decode_leb128(file + schema_offset, &tag); + + //printf("%d %s\n", code, tag_to_str(tag)); + + u32 has_children = file[schema_offset++]; + if (has_children) { + ++depth; + } + + u32 attribute, form; + + do { + schema_offset += decode_leb128(file + schema_offset, &attribute); + schema_offset += decode_leb128(file + schema_offset, &form); + + if (attribute) { + //printf("\t%s ", attribute_to_str(attribute)); + } + + switch (form) { + case DW_FORM_sec_offset: + case DW_FORM_strp: { + u32 data = file[data_offset]; + data_offset += 4; // 8 bytes for x64 DWARF! + + if (form == DW_FORM_strp) { + char *str = (char *) file + string_offset + data; + //printf("(indirect string, offset: %#x): %s\n", data, str); + if (tag == DW_TAG_subprogram) { + if (strcmp(str, subroutine) == 0) { + found_sr = 1; + } + } + } else { + //printf("%#x\n", data); + } + + break; + } + + case DW_FORM_addr: { + u64 data = 0; + memcpy(&data, file + data_offset, address_size); + data_offset += address_size; + //printf("%#lx\n", data); + + if (tag == DW_TAG_subprogram && found_sr == 1 && attribute == DW_AT_low_pc) { + return(data); + } + + break; + }; + + case DW_FORM_string: { + char *data = (char *) file + data_offset; + data_offset += strlen(data) + 1; + //printf("%s\n", data); + break; + } + + case DW_FORM_flag_present: { + int data = 1; + //printf("Flag = 1\n"); + break; + } + + case DW_FORM_ref4: { + u32 data = file[data_offset]; + data_offset += 4; + //printf("%#x\n", data); + u32 referenced_data = file[original_data_offset - header_size + data]; + break; + } + + case DW_FORM_exprloc: { + u32 length; + data_offset += decode_leb128(file + data_offset, &length); + //printf("%d byte block:", length); + + for (u32 i = 0; i < length; ++i) { + //printf(" %x", file[data_offset + i]); + } + //printf("\n"); + + data_offset += length; + + break; + } + + case DW_FORM_data1: { + u8 data = file[data_offset]; + data_offset += 1; + //printf("%#x\n", data); + break; + }; + + case DW_FORM_data2: { + u16 data = file[data_offset]; + data_offset += 2; + //printf("%#x\n", data); + break; + }; + + case DW_FORM_data4: { + u32 data = file[data_offset]; + data_offset += 4; + //printf("%#x\n", data); + break; + }; + + case DW_FORM_data8: { + u64 data = file[data_offset]; + data_offset += 8; + //printf("%#lx\n", data); + break; + }; + + default: { + if (form) { + //printf("unknown attribute form %d\n", form); + } + } + } + } while (attribute != 0 || form != 0); + } while (1); + + return(0); +} + +static u64 +read_line_number_info(u8 *file, u64 dl_offset, u64 pc) +{ + struct dwarf_debug_line_header_v3_x32 header = { 0 }; + memcpy(&header, file + dl_offset, 15); /* all fixed-size info */ + + dl_offset += 15; + + header.standard_opcode_lengths = malloc(header.opcode_base - 1); + memcpy(header.standard_opcode_lengths, file + dl_offset, header.opcode_base - 1); + + dl_offset += header.opcode_base - 1; + + /* "Each entry is a null-terminated string containing a full path name. The last entry +is followed by a single null byte." */ + u8 ndirs = 0; + u8 nfiles = 0; + + u8 *p = file + dl_offset; + + while (*p != 0) { + ++ndirs; + while (*p != 0) { + ++p; + } + } + + header.ndirs = ndirs; + header.include_directories = 0; // malloc(ndirs * sizeof(char *)); + + dl_offset += (p - (file + dl_offset)) + 1; + + + p = file + dl_offset; + while (*p != 0) { + ++nfiles; + + /* null-terminated string */ + while (*p != 0) { + ++p; + } + + ++p; + + u64 offset = 0; + u32 dummy = 0; + + offset += decode_leb128(p, &dummy); + offset += decode_leb128(p, &dummy); + offset += decode_leb128(p, &dummy); + + p += offset; + } + + header.files = malloc(nfiles * sizeof(struct dwarf_debug_line_file_info)); + header.nfiles = nfiles; + + struct dwarf_debug_line_file_info *f = header.files; + + p = file + dl_offset; + while (*p != 0) { + + /* null-terminated string */ + f->name = (char *) p; + + while (*p != 0) { + ++p; + } + + ++p; + + u64 offset = 0; + u32 dummy = 0; + + offset += decode_leb128(p, &f->directory_index); + offset += decode_leb128(p, &f->time_modified); + offset += decode_leb128(p, &f->file_size); + + p += offset; + + ++f; + } + + dl_offset = p - file + 1; + + u8 opcode; + enum dwarf_lnp_opcode opcode_regular; + enum dwarf_lnp_opcode_extended opcode_extended; + + p = file + dl_offset; + + struct dwarf_line_number_state state = { 0 }; + + state.file = 1; + state.line = 1; + state.is_stmt = header.default_is_stmt; + + + u64 last_line = 0; + + do { + opcode = *p; + ++p; + + u8 nops = 0; + if (opcode) { + if (opcode <= header.opcode_base) { + /* standart opcode */ + opcode_regular = opcode; + + switch (opcode_regular) { + case DW_LNS_copy: { + state.basic_block = 0; + state.prologue_end = 0; + state.epilogue_begin = 0; + break; + } + + case DW_LNS_advance_pc: { + u32 operand; + p += decode_leb128(p, &operand); + operand *= header.minimum_instruction_length; + state.pc += operand; + break; + } + + case DW_LNS_advance_line: { + s32 operand; + p += decode_leb128s(p, &operand); + last_line = state.line; + state.line += operand; + break; + } + + case DW_LNS_set_file: { + u32 operand; + p += decode_leb128(p, &operand); + operand *= header.minimum_instruction_length; + state.file = operand; + + // printf("Switch to file %s in directory %d\n", header.files[state.file - 1].name, header.files[state.file - 1].directory_index); + + break; + } + + case DW_LNS_set_column: { + u32 operand; + p += decode_leb128(p, &operand); + operand *= header.minimum_instruction_length; + state.column = operand; + break; + } + + case DW_LNS_negate_stmt: { + state.is_stmt = 1 - state.is_stmt; + break; + } + + case DW_LNS_set_basic_block: { + state.basic_block = 1; + break; + } + + case DW_LNS_const_add_pc: { + u8 adjusted = 255 - header.opcode_base; + s32 address_increment = (adjusted / header.line_range) * header.minimum_instruction_length; + state.pc += address_increment; + break; + } + + case DW_LNS_fixed_advance_pc: { + u16 operand; + memcpy(&operand, p, 2); + p += 2; + state.pc += operand; + break; + } + + case DW_LNS_set_prologue_end: { + state.prologue_end = 1; + break; + } + + case DW_LNS_set_epilogue_begin: { + state.epilogue_begin = 1; + break; + } + + case DW_LNS_set_isa: { + u32 operand; + p += decode_leb128(p, &operand); + operand *= header.minimum_instruction_length; + state.isa = operand; + break; + } + } + } else { + /* special opcode */ + u8 adjusted = opcode - header.opcode_base; + s32 address_increment = (adjusted / header.line_range) * header.minimum_instruction_length; + s32 line_increment = header.line_base + (adjusted % header.line_range); + + last_line = state.line; + + state.pc += address_increment; + state.line += line_increment; + state.basic_block = 0; + state.prologue_end = 0; + state.epilogue_begin = 0; + state.discriminator = 0; + } + } else { + /* extended opcode */ + u32 instruction_length; + p += decode_leb128(p, &instruction_length); + opcode = *p; + opcode_extended = opcode; + + ++p; + + switch (opcode_extended) { + case DW_LNE_end_sequence: { + state.end_sequence = 1; + //printf("END: %lx -> %d\n", state.pc, state.line); + memset(&state, 0, sizeof(state)); + state.file = 1; + state.line = 1; + state.is_stmt = header.default_is_stmt; + break; + } + + case DW_LNE_set_address: { + u64 address; + memcpy(&address, p, 8); + state.pc = address; + p += 8; + break; + } + + case DW_LNE_define_file: { + struct dwarf_debug_line_file_info f = { 0 }; + f.name = (char *) p; + + while (*p != 0) { + ++p; + } + ++p; + + p += decode_leb128(p, &f.directory_index); + p += decode_leb128(p, &f.time_modified); + p += decode_leb128(p, &f.file_size); + + break; + } + + case DW_LNE_set_discriminator: { + u32 operand; + p += decode_leb128(p, &operand); + operand *= header.minimum_instruction_length; + state.discriminator = operand; + break; + break; + } + } + } + + // TODO: this is off by one? + if (state.pc >= pc) { + return(last_line); + } + + } while (opcode_extended != DW_LNE_end_sequence); + + return(0); +} + +static u64 +get_executable_base_address(u8 *elf_file, int pid) +{ + char path[256] = { 0 }; + + snprintf(path, 256, "/proc/%d/maps", pid); + + FILE *maps_file = fopen(path, "rb"); + if (!maps_file) { + DIE("proc map not found\n"); + } + + struct elf_header_x64 header = { 0 }; + memcpy(&header, elf_file, sizeof(header)); + + u64 elf_offset = 0; + + for (int i = 0; i < header.header_table_entry_count; ++i) { + struct elf_header_table_entry_x64 header_entry = { 0 }; + u64 offset = header.header_table_offset + header.header_table_entry_size * i; + memcpy(&header_entry, elf_file + offset, sizeof(header_entry)); + + //printf("%#018lx %s\n", header_entry.segment_offset, header_entry.flags & PF_X ? "E" : ""); + + if (header_entry.flags & PF_X) { + elf_offset = header_entry.segment_offset; + break; + } + } + + if (!elf_offset) { + return(0); + } + + size_t len; + char *line = malloc(4096); + + while ((len = getline(&line, &len, maps_file)) != -1UL) { + u64 base; + u64 end; + u64 offset; + + char *at = line; + + base = strtoll(at, &at, 16); + + return(base); + + end = strtoll(at + 1, &at, 16); + (void) end; + + while (*at < '0' || *at > '9') ++at; + + offset = strtoll(at, &at, 16); + + if (offset == elf_offset) { + return(base); + } + } + + free(line); + + return(0); +} + +static u64 +get_address_of_subroutine(u8 *file, char *sr) +{ + u64 debug_info_offset = get_section_offset(file, ".debug_info"); + printf("Found .debug_info at offset %#lx\n", debug_info_offset); + + u64 debug_line_offset = get_section_offset(file, ".debug_line"); + printf("Found .debug_line at offset %#lx\n", debug_line_offset); + + u64 debug_abbrev_offset = get_section_offset(file, ".debug_abbrev"); + printf("Found .debug_abbrev at offset %#lx\n", debug_abbrev_offset); + + u64 debug_str_offset = get_section_offset(file, ".debug_str"); + printf("Found .debug_str at offset %#lx\n", debug_str_offset); + + struct dwarf_debug_info_header_x32 di_header = { 0 }; + memcpy(&di_header, file + debug_info_offset, sizeof(di_header)); + + u64 abbrev_offset = debug_abbrev_offset + di_header.debug_abbrev_offset; + u64 data_offset = debug_info_offset + sizeof(di_header); + + u64 result = find_subroutine_offset(file, sizeof(di_header), di_header.address_size, + debug_str_offset, abbrev_offset, data_offset, sr); + + //read_line_number_info(file, debug_line_offset); + + + return(result); +} + +static u64 +pc_to_line_number(u8 *file, u64 pc) +{ + u64 debug_line_offset = get_section_offset(file, ".debug_line"); + //printf("Found .debug_line at offset %#lx\n", debug_line_offset); + u64 result = read_line_number_info(file, debug_line_offset, pc); + return(result); +} \ No newline at end of file diff --git a/main.c b/main.c new file mode 100644 index 0000000..65eeff7 --- /dev/null +++ b/main.c @@ -0,0 +1,375 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; + +typedef int8_t s8; +typedef int16_t s16; +typedef int32_t s32; +typedef int64_t s64; + +#define NT_PRSTATUS 1 +#define DIE(string) do { fprintf(stderr, string); exit(1); } while (0) + +#include "structs.h" + +struct mi_process { + pid_t pid; + + char *source; + u64 source_size; + + u8 *elf; + u64 elf_size; + + u64 base_address; + u64 main_address; +}; + +struct mi_registers { + u64 rbp; + u64 rbx; + u64 rax; + u64 rcx; + u64 rdx; + u64 rsi; + u64 rdi; + u64 rip; + u64 rsp; + + struct user_regs_struct _sys; +}; + +#include "dwarf.c" + + +static void +print_word_at_address(int child, u64 address) +{ + long word = ptrace(PTRACE_PEEKDATA, child, address, NULL); + u8 nb[4]; + memcpy(nb, &word, 4); + printf("word at %#018lx: {%x %x %x %x}\n", address, nb[0], nb[1], nb[2], nb[3]); +} + +static void +print_current_line(struct mi_process proc, u64 line) +{ + char *source = proc.source; + u64 size = proc.source_size; + u64 current_line = 1; + + for (u32 i = 0; i < size; ++i) { + if (current_line == line) { + int len = 0; + int start = 0; + int leading = 1; + + for (u32 j = i; j < size; ++j) { + if (leading && (source[j] == '\t' || source[j] == ' ')) { + ++start; + } else { + leading = 0; + } + + if (source[j] == '\n') { + break; + } + + ++len; + } + + + printf("%.*s\n", len - start, source + i + start); + return; + } + + if (source[i] == '\n') { + current_line++; + } + } +} + +static struct mi_process +process_create(char *path) +{ + struct mi_process result = { 0 }; + + /* disable ASLR */ + syscall(SYS_personality, ADDR_NO_RANDOMIZE); + + pid_t pid = fork(); + + if (pid == -1) { + DIE("fork error\n"); + } + + if (pid == 0) { + int rt = ptrace(PTRACE_TRACEME, 0, 0, 0); + assert(rt == 0); + char *args[] = { path, NULL }; + rt = execvp(path, args); + if (rt == -1) { + DIE("exevp error\n"); + } + } + + u64 source_size; + u64 elf_size; + u8 *elf; + char *source; + struct stat s; + int fd; + + { + fd = open(path, O_RDONLY); + assert(fd != -1); + fstat(fd, &s); + elf_size = s.st_size; + elf = mmap(0, s.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + close(fd); + } + + { + fd = open("/code/trace-test/traceme.c", O_RDONLY); // TODO + assert(fd != -1); + fstat(fd, &s); + source_size = s.st_size; + source = mmap(0, source_size, PROT_READ, MAP_PRIVATE, fd, 0); + close(fd); + } + + result.elf = elf; + result.elf_size = elf_size; + result.source = source; + result.source_size = source_size; + result.pid = pid; + + return(result); +} + +static struct mi_registers +get_process_registers(struct mi_process proc) +{ + struct user_regs_struct regs = { 0 }; + struct iovec iov = { ®s, sizeof(regs) }; + struct mi_registers result = { 0 }; + + ptrace(PTRACE_GETREGSET, proc.pid, NT_PRSTATUS, &iov); + + result.rax = regs.rax; + result.rip = regs.rip; + result.rbp = regs.rbp; + result.rbx = regs.rbx; + result.rcx = regs.rcx; + result.rdx = regs.rdx; + result.rsi = regs.rsi; + result.rdi = regs.rdi; + result.rsp = regs.rsp; + + result._sys = regs; + + return(result); +} + +static void +set_process_registers(struct mi_process proc, struct mi_registers regs) +{ + struct iovec iov = { ®s._sys, sizeof(regs._sys) }; + ptrace(PTRACE_GETREGSET, proc.pid, NT_PRSTATUS, &iov); +} + +static void +print_current_instruction(struct mi_process proc) +{ + struct mi_registers regs = get_process_registers(proc); + + long wordb = ptrace(PTRACE_PEEKDATA, proc.pid, regs.rip - 4, NULL); + long word = ptrace(PTRACE_PEEKDATA, proc.pid, regs.rip, NULL); + long worda = ptrace(PTRACE_PEEKDATA, proc.pid, regs.rip + 4, NULL); + + u8 nb[12]; + memcpy(nb, &wordb, 4); + memcpy(nb + 4, &word, 4); + memcpy(nb + 8, &worda, 4); + + printf("PC = %#018lx: %x %x %x %x [%x] %x %x %x %x %x %x %x\n", regs.rip, + nb[0], nb[1], nb[2], nb[3], nb[4], nb[5], nb[6], nb[7], + nb[8], nb[9], nb[10], nb[11]); +} + + +static void +command_regs(struct mi_process proc) +{ + struct mi_registers regs = get_process_registers(proc); + printf("rax = %#018lx rcx = %#018lx\n" + "rbp = %#018lx rbx = %#018lx\n" + "rdx = %#018lx rsi = %#018lx\n" + "rdi = %#018lx rip = %#018lx\n" + "rsp = %#018lx\n", + regs.rax, regs.rcx, regs.rbp, regs.rbx, + regs.rdx, regs.rsi, regs.rdi, regs.rip, + regs.rsp); +} + +static void +command_step(struct mi_process proc) +{ + ptrace(PTRACE_SINGLESTEP, proc.pid, 0, 0); + waitpid(proc.pid, 0, 0); + +#if 1 + print_current_instruction(proc); + struct mi_registers regs = get_process_registers(proc); + u64 line_number = pc_to_line_number(proc.elf, regs.rip - proc.base_address); + print_current_line(proc, line_number); +#endif +} + +static void +command_start(struct mi_process proc) +{ + u64 main_address = proc.base_address + proc.main_address; + + long saved_instruction = ptrace(PTRACE_PEEKDATA, proc.pid, main_address, NULL); + long int3_byte = 0x000000cc; + long int3_word = (saved_instruction & 0xFFFFFF00) | int3_byte; + + /* write 0xcc */ + ptrace(PTRACE_POKEDATA, proc.pid, main_address, int3_word); + + /* wait till child hits the interrupt */ + ptrace(PTRACE_CONT, proc.pid, 0, 0); + waitpid(proc.pid, 0, 0); + + /* restore original instrucion */ + ptrace(PTRACE_POKEDATA, proc.pid, main_address, saved_instruction); + + /* step back to original instruction */ + struct mi_registers regs = get_process_registers(proc); + regs.rip -= 1; + set_process_registers(proc, regs); +} + +static void +command_next(struct mi_process proc) +{ + struct mi_registers regs = get_process_registers(proc); + + u64 line_number = pc_to_line_number(proc.elf, regs.rip - proc.base_address); + u64 next_line_number; + + do { + ptrace(PTRACE_SINGLESTEP, proc.pid, 0, 0); + waitpid(proc.pid, 0, 0); + regs = get_process_registers(proc); + //printf("%#lx\n", regs.rip - proc.base_address); + next_line_number= pc_to_line_number(proc.elf, regs.rip - proc.base_address); + // TODO: skip call, repXXX, etc + } while (next_line_number == line_number); + + print_current_line(proc, next_line_number); +} + +static void +command_list(struct mi_process proc) +{ + struct mi_registers regs = get_process_registers(proc); + u64 line_number = pc_to_line_number(proc.elf, regs.rip - proc.base_address); + print_current_line(proc, line_number); +} + +static void +command_cont(struct mi_process proc) +{ + ptrace(PTRACE_CONT, proc.pid, 0, 0); + // TODO +} + +static void +command_stop(struct mi_process proc) +{ + kill(proc.pid, SIGINT); + // TODO +} + +int +main(int argc, char *argv[]) +{ + if (argc != 2) { + printf("Usage: %s executable\n", argv[0]); + return(1); + } + + struct mi_process process = process_create(argv[1]); + printf("pid of child = %d\n", process.pid); + + size_t max_command_length = 1023; + int command_length = 0; + char *command = malloc(max_command_length + 1); + + process.base_address = 0x555555554000UL; // get_executable_base_address(file, proc.pid); + process.main_address = get_address_of_subroutine(process.elf, "main"); + + printf("Base address: %#lx\n", process.base_address); + printf("Main address: %#lx\n", process.main_address); + + printf("> "); + fflush(stdout); + + while ((command_length = getline(&command, &max_command_length, stdin))) { + if (0 == strncmp(command, "exit\n", command_length) || 0 == strncmp(command, "q\n", command_length)) { + break; + } else if (strncmp(command, "regs\n", command_length) == 0) { + command_regs(process); + } else if (strncmp(command, "step\n", command_length) == 0) { + command_step(process); + } else if (strncmp(command, "start\n", command_length) == 0) { + command_start(process); + } else if (strncmp(command, "next\n", command_length) == 0) { + command_next(process); + } else if (strncmp(command, "line\n", command_length) == 0) { + command_list(process); + } else if (strncmp(command, "cont\n", command_length) == 0) { + command_cont(process); + } else if (strncmp(command, "stop\n", command_length) == 0) { + command_stop(process); + } else { + printf("Unknown command: %*s", command_length, command); + } + + int wstatus; + int rt = waitpid(process.pid, &wstatus, WNOHANG); + + if (rt > 0 && WIFEXITED(wstatus)) { + printf("Child exited\n"); + break; + } + + printf("> "); + } + + return(0); +} \ No newline at end of file diff --git a/project.4coder b/project.4coder new file mode 100644 index 0000000..2dab6f9 --- /dev/null +++ b/project.4coder @@ -0,0 +1,37 @@ +version(1); + +project_name = "trace-test"; + +patterns = { + "*.c", + "*.h", + "*.4coder", +}; + +blacklist_patterns = { + ".*", + "res", + "lib", + "build/*" +}; + +load_paths_base = { + { ".", .relative = true, .recursive = true, } +}; + +load_paths = { + { load_paths_base, .os = "linux", } +}; + +command_list = { + { + .name = "build debug", + .out = "*compilation*", + .footer_panel = false, + .save_dirty_files = true, + .cursor_at_end = true, + .cmd = {{ "make debug", .os = "linux" }}, + }, +}; + +fkey_command[1] = "build debug"; diff --git a/structs.h b/structs.h new file mode 100644 index 0000000..ba2e391 --- /dev/null +++ b/structs.h @@ -0,0 +1,586 @@ +#define PF_X (1 << 0) +#define PF_W (1 << 1) +#define PF_R (1 << 2) + +enum elf_abi { + SYSTEM_V = 0x00, + HP_UX = 0x01, + NETBSD = 0x02, + LINUX = 0x03, + GNU_HURD = 0x04, + SOLARIS = 0x06, + AIX = 0x07, + IRIX = 0x08, + FREEBSD = 0x09, + TRU64 = 0x0A, + NOVELL_MODESTO = 0x0B, + OPENBSD = 0x0D, + OPENVMS = 0x0E, + NONTSTOP_KERNEL = 0x0E, + AROS = 0x0F, + FENIX_OS = 0x10, + CLOUDABI = 0x11, + STRATUS_TECHNOLOGIES_OPENVOS = 0x12 +}; + +enum elf_file_type { + ET_NONE = 0x00, + ET_REL = 0x01, + ET_EXEC = 0x02, + ET_DYN = 0x03, + ET_CORE = 0x04, + ET_LOOS = 0xFE00, + ET_HIOS = 0xFEFF, + ET_LOPROC = 0xFF00, + ET_HIPROC = 0xFFFF +}; + +enum elf_isa { + NO_SPECIFIC_INSTRUCTION_SET = 0x00, + ATNT_WE_32100 = 0x01, + SPARC = 0x02, + X86 = 0x03, + MOTOROLA_68000 = 0x04, + MOTOROLA_88000 = 0x05, + INTEL_MCU = 0x06, + INTEL_80860 = 0x07, + MIPS = 0x08, + IBM_SYSTEM_370 = 0x09, + MIPS_RS3000_LITTLE_ENDIAN = 0x0A, + HEWLETT_PACKARD_PA_RISC = 0x0E, + INTEL_80960 = 0x13, + POWERPC = 0x14, + POWERPC_64 = 0x15, + S390_S390X = 0x16, + ARM = 0x28, + SUPERH = 0x2A, + IA_64 = 0x32, + AMD64 = 0x3E, + TMS320C6000_FAMILY = 0x8C, + ARM_64 = 0xB7, + RISC_V = 0xF3, + BERKELEY_PACKET_FILTER = 0xF7, + WDC_65C816 = 0x10 +}; + +enum elf_section_type { + SHT_NULL = 0, // No associated section (inactive entry). + SHT_PROGBITS = 1, // Program-defined contents. + SHT_SYMTAB = 2, // Symbol table. + SHT_STRTAB = 3, // String table. + SHT_RELA = 4, // Relocation entries; explicit addends. + SHT_HASH = 5, // Symbol hash table. + SHT_DYNAMIC = 6, // Information for dynamic linking. + SHT_NOTE = 7, // Information about the file. + SHT_NOBITS = 8, // Data occupies no space in the file. + SHT_REL = 9, // Relocation entries; no explicit addends. + SHT_SHLIB = 10, // Reserved. + SHT_DYNSYM = 11, // Symbol table. + SHT_INIT_ARRAY = 14, // Pointers to initialization functions. + SHT_FINI_ARRAY = 15, // Pointers to termination functions. + SHT_PREINIT_ARRAY = 16, // Pointers to pre-init functions. + SHT_GROUP = 17, // Section group. + SHT_SYMTAB_SHNDX = 18, // Indices for SHN_XINDEX entries. + + SHT_RELR = 19, // Relocation entries; only offsets. + SHT_LOOS = 0x60000000, // Lowest operating system-specific type. + + SHT_HIOS = 0x6fffffff, // Highest operating system-specific type. + SHT_LOPROC = 0x70000000, // Lowest processor arch-specific type. + SHT_HEX_ORDERED = 0x70000000, + SHT_X86_64_UNWIND = 0x70000001, // Unwind information + + SHT_HIPROC = 0x7fffffff, // Highest processor arch-specific type. + SHT_LOUSER = 0x80000000, // Lowest type reserved for applications. + SHT_HIUSER = 0xffffffff // Highest type reserved for applications. +}; + +enum dwarf_die_tag { + DW_TAG_array_type = 0x01, + DW_TAG_class_type = 0x02, + DW_TAG_entry_point = 0x03, + DW_TAG_enumeration_type = 0x04, + DW_TAG_formal_parameter = 0x05, + DW_TAG_imported_declaration = 0x08, + DW_TAG_label = 0x0a, + DW_TAG_lexical_block = 0x0b, + DW_TAG_member = 0x0d, + DW_TAG_pointer_type = 0x0f, + DW_TAG_reference_type = 0x10, + DW_TAG_compile_unit = 0x11, + DW_TAG_string_type = 0x12, + DW_TAG_structure_type = 0x13, + DW_TAG_subroutine_type = 0x15, + DW_TAG_typedef = 0x16, + DW_TAG_union_type = 0x17, + DW_TAG_unspecified_parameters = 0x18, + DW_TAG_variant = 0x19, + DW_TAG_common_block = 0x1a, + DW_TAG_common_inclusion = 0x1b, + DW_TAG_inheritance = 0x1c, + DW_TAG_inlined_subroutine = 0x1d, + DW_TAG_module = 0x1e, + DW_TAG_ptr_to_member_type = 0x1f, + DW_TAG_set_type = 0x20, + DW_TAG_subrange_type = 0x21, + DW_TAG_with_stmt = 0x22, + DW_TAG_access_declaration = 0x23, + DW_TAG_base_type = 0x24, + DW_TAG_catch_block = 0x25, + DW_TAG_const_type = 0x26, + DW_TAG_constant = 0x27, + DW_TAG_enumerator = 0x28, + DW_TAG_file_type = 0x29, + DW_TAG_friend = 0x2a, + DW_TAG_namelist = 0x2b, + DW_TAG_namelist_item = 0x2c, + DW_TAG_namelist_items = 0x2c, + DW_TAG_packed_type = 0x2d, + DW_TAG_subprogram = 0x2e, + DW_TAG_thrown_type = 0x31, + DW_TAG_try_block = 0x32, + DW_TAG_variant_part = 0x33, + DW_TAG_variable = 0x34, + DW_TAG_volatile_type = 0x35, + DW_TAG_lo_user = 0x4080, + DW_TAG_MIPS_loop = 0x4081, + DW_TAG_hi_user = 0xffff +}; + +enum dwarf_die_attribute { + DW_AT_sibling = 0x01, + DW_AT_location = 0x02, + DW_AT_name = 0x03, + DW_AT_ordering = 0x09, + DW_AT_subscr_data = 0x0a, + DW_AT_byte_size = 0x0b, + DW_AT_bit_offset = 0x0c, + DW_AT_bit_size = 0x0d, + DW_AT_element_list = 0x0f, + DW_AT_stmt_list = 0x10, + DW_AT_low_pc = 0x11, + DW_AT_high_pc = 0x12, + DW_AT_language = 0x13, + DW_AT_member = 0x14, + DW_AT_discr = 0x15, + DW_AT_discr_value = 0x16, + DW_AT_visibility = 0x17, + DW_AT_import = 0x18, + DW_AT_string_length = 0x19, + DW_AT_common_reference = 0x1a, + DW_AT_comp_dir = 0x1b, + DW_AT_const_value = 0x1c, + DW_AT_containing_type = 0x1d, + DW_AT_default_value = 0x1e, + DW_AT_inline = 0x20, + DW_AT_is_optional = 0x21, + DW_AT_lower_bound = 0x22, + DW_AT_producer = 0x25, + DW_AT_prototyped = 0x27, + DW_AT_return_addr = 0x2a, + DW_AT_start_scope = 0x2c, + DW_AT_upper_bound = 0x2f, + DW_AT_abstract_origin = 0x31, + DW_AT_accessibility = 0x32, + DW_AT_address_class = 0x33, + DW_AT_artificial = 0x34, + DW_AT_base_types = 0x35, + DW_AT_calling_convention = 0x36, + DW_AT_count = 0x37, + DW_AT_data_member_location = 0x38, + DW_AT_decl_column = 0x39, + DW_AT_decl_file = 0x3a, + DW_AT_decl_line = 0x3b, + DW_AT_declaration = 0x3c, + DW_AT_encoding = 0x3e, + DW_AT_external = 0x3f, + DW_AT_frame_base = 0x40, + DW_AT_friend = 0x41, + DW_AT_identifier_case = 0x42, + DW_AT_macro_info = 0x43, + DW_AT_namelist_item = 0x44, + DW_AT_priority = 0x45, + DW_AT_segment = 0x46, + DW_AT_specification = 0x47, + DW_AT_static_link = 0x48, + DW_AT_type = 0x49, + DW_AT_use_location = 0x4a, + DW_AT_variable_parameter = 0x4b, + DW_AT_virtuality = 0x4c, + DW_AT_vtable_elem_location = 0x4d, + DW_AT_signature = 0x69, + DW_AT_main_subprogram = 0x6a, + DW_AT_data_bit_offset = 0x6b, + DW_AT_const_expr = 0x6c, + DW_AT_enum_class = 0x6d, + DW_AT_linkage_name = 0x6e, + DW_AT_string_length_bit_size = 0x6f, + DW_AT_string_length_byte_size = 0x70, + DW_AT_rank = 0x71, + DW_AT_str_offsets_base = 0x72, + DW_AT_addr_base = 0x73, + DW_AT_rnglists_base = 0x74, + DW_AT_dwo_id = 0x75, + DW_AT_dwo_name = 0x76, + DW_AT_reference = 0x77, + DW_AT_rvalue_reference = 0x78, + DW_AT_macros = 0x79, + DW_AT_call_all_calls = 0x7a, + DW_AT_call_all_source_calls = 0x7b, + DW_AT_call_all_tail_calls = 0x7c, + DW_AT_call_return_pc = 0x7d, + DW_AT_call_value = 0x7e, + DW_AT_call_origin = 0x7f, + DW_AT_call_parameter = 0x80, + DW_AT_call_pc = 0x81, + DW_AT_call_tail_call = 0x82, + DW_AT_call_target = 0x83, + DW_AT_call_target_clobbered = 0x84, + DW_AT_call_data_location = 0x85, + DW_AT_call_data_value = 0x86, + DW_AT_noreturn = 0x87, + DW_AT_alignment = 0x88, + DW_AT_export_symbols = 0x89, + DW_AT_deleted = 0x8a, + DW_AT_defaulted = 0x8b, + DW_AT_loclists_base = 0x8c +}; + +enum dwarf_attribute_form { + DW_FORM_addr = 0x01, + DW_FORM_block2 = 0x03, + DW_FORM_block4 = 0x04, + DW_FORM_data2 = 0x05, + DW_FORM_data4 = 0x06, + DW_FORM_data8 = 0x07, + DW_FORM_string = 0x08, + DW_FORM_block = 0x09, + DW_FORM_block1 = 0x0a, + DW_FORM_data1 = 0x0b, + DW_FORM_flag = 0x0c, + DW_FORM_sdata = 0x0d, + DW_FORM_strp = 0x0e, + DW_FORM_udata = 0x0f, + DW_FORM_ref_addr = 0x10, + DW_FORM_ref1 = 0x11, + DW_FORM_ref2 = 0x12, + DW_FORM_ref4 = 0x13, + DW_FORM_ref8 = 0x14, + DW_FORM_ref_udata = 0x15, + DW_FORM_indirect = 0x16, + DW_FORM_sec_offset = 0x17, + DW_FORM_exprloc = 0x18, + DW_FORM_flag_present = 0x19, + DW_FORM_strx = 0x1a, + DW_FORM_addrx = 0x1b, + DW_FORM_ref_sup4 = 0x1c, + DW_FORM_strp_sup = 0x1d, + DW_FORM_data16 = 0x1e, + DW_FORM_line_strp = 0x1f, + DW_FORM_ref_sig8 = 0x20, + DW_FORM_implicit_const = 0x21, + DW_FORM_loclistx = 0x22, + DW_FORM_rnglistx = 0x23, + DW_FORM_ref_sup8 = 0x24, + DW_FORM_strx1 = 0x25, + DW_FORM_strx2 = 0x26, + DW_FORM_strx3 = 0x27, + DW_FORM_strx4 = 0x28, + DW_FORM_addrx1 = 0x29, + DW_FORM_addrx2 = 0x2a, + DW_FORM_addrx3 = 0x2b, + DW_FORM_addrx4 = 0x2c +}; + +enum dwarf_lnp_opcode { + DW_LNS_copy = 0x01, + DW_LNS_advance_pc = 0x02, + DW_LNS_advance_line = 0x03, + DW_LNS_set_file = 0x04, + DW_LNS_set_column = 0x05, + DW_LNS_negate_stmt = 0x06, + DW_LNS_set_basic_block = 0x07, + DW_LNS_const_add_pc = 0x08, + DW_LNS_fixed_advance_pc = 0x09, + DW_LNS_set_prologue_end = 0x0a, + DW_LNS_set_epilogue_begin = 0x0b, + DW_LNS_set_isa = 0x0c, +}; + +enum dwarf_lnp_opcode_extended { + DW_LNE_end_sequence = 0x01, + DW_LNE_set_address = 0x02, + DW_LNE_define_file = 0x03, + DW_LNE_set_discriminator = 0x04, +}; + +#pragma pack(1) +struct elf_header_x64 { + char magic[4]; + u8 bitness; + u8 endianness; + u8 ei_version; + u8 abi; + u8 abi_version; + u8 pad[7]; + u16 file_type; + u16 isa; + u32 version; + u64 entry_point_offset; + u64 header_table_offset; + u64 section_table_offset; + u32 flags; + u16 this_header_size; + u16 header_table_entry_size; + u16 header_table_entry_count; + u16 section_table_entry_size; + u16 section_table_entry_count; + u16 section_names_table_index; +}; + +#pragma pack(1) +struct elf_header_table_entry_x64 { + u32 type; + u32 flags; + u64 segment_offset; + u64 virtual_address; + u64 reserved_physical_address; + u64 segment_file_size; + u64 segment_memory_size; + u64 alignment; +}; + +#pragma pack(1) +struct elf_section_table_entry_x64 { + u32 name_offset; + u32 type; + u64 flags; + u64 virtual_address; + u64 offset_in_file; + u64 size; + u32 link; + u32 info; + u64 alignment; + u64 entry_size; +}; + +#pragma pack(1) +struct dwarf_debug_info_header_x64 { + u32 pad; + u64 length; + u16 version; + u64 debug_abbrev_offset; + u8 adress_size; +}; + +#pragma pack(1) +struct dwarf_debug_info_header_x32 { + u32 length; + u16 version; + u32 debug_abbrev_offset; + u8 address_size; +}; + +struct dwarf_debug_line_file_info { + char *name; + u32 directory_index; + u32 time_modified; + u32 file_size; +}; + +#pragma pack(1) +struct dwarf_debug_line_header_v3_x32 { + u32 length; + u16 version; + u32 header_length; + u8 minimum_instruction_length; + u8 default_is_stmt; + s8 line_base; + u8 line_range; + u8 opcode_base; + u8 *standard_opcode_lengths; + u8 ndirs; + u8 nfiles; + char *include_directories; + struct dwarf_debug_line_file_info *files; +}; + +struct dwarf_line_number_state { + u64 pc; + u32 file; + u32 line; + u32 column; + + u8 is_stmt; + u8 basic_block; + u8 end_sequence; + u8 prologue_end; + u8 epilogue_begin; + + u32 isa; + u32 discriminator; +}; + + +static char * +attribute_to_str(enum dwarf_die_tag attr) +{ + switch (attr) { + case DW_AT_sibling: { return "DW_AT_sibling"; } + case DW_AT_location: { return "DW_AT_location"; } + case DW_AT_name: { return "DW_AT_name"; } + case DW_AT_ordering: { return "DW_AT_ordering"; } + case DW_AT_subscr_data: { return "DW_AT_subscr_data"; } + case DW_AT_byte_size: { return "DW_AT_byte_size"; } + case DW_AT_bit_offset: { return "DW_AT_bit_offset"; } + case DW_AT_bit_size: { return "DW_AT_bit_size"; } + case DW_AT_element_list: { return "DW_AT_element_list"; } + case DW_AT_stmt_list: { return "DW_AT_stmt_list"; } + case DW_AT_low_pc: { return "DW_AT_low_pc"; } + case DW_AT_high_pc: { return "DW_AT_high_pc"; } + case DW_AT_language: { return "DW_AT_language"; } + case DW_AT_member: { return "DW_AT_member"; } + case DW_AT_discr: { return "DW_AT_discr"; } + case DW_AT_discr_value: { return "DW_AT_discr_value"; } + case DW_AT_visibility: { return "DW_AT_visibility"; } + case DW_AT_import: { return "DW_AT_import"; } + case DW_AT_string_length: { return "DW_AT_string_length"; } + case DW_AT_common_reference: { return "DW_AT_common_reference"; } + case DW_AT_comp_dir: { return "DW_AT_comp_dir"; } + case DW_AT_const_value: { return "DW_AT_const_value"; } + case DW_AT_containing_type: { return "DW_AT_containing_type"; } + case DW_AT_default_value: { return "DW_AT_default_value"; } + case DW_AT_inline: { return "DW_AT_inline"; } + case DW_AT_is_optional: { return "DW_AT_is_optional"; } + case DW_AT_lower_bound: { return "DW_AT_lower_bound"; } + case DW_AT_producer: { return "DW_AT_producer"; } + case DW_AT_prototyped: { return "DW_AT_prototyped"; } + case DW_AT_return_addr: { return "DW_AT_return_addr"; } + case DW_AT_start_scope: { return "DW_AT_start_scope"; } + case DW_AT_upper_bound: { return "DW_AT_upper_bound"; } + case DW_AT_abstract_origin: { return "DW_AT_abstract_origin"; } + case DW_AT_accessibility: { return "DW_AT_accessibility"; } + case DW_AT_address_class: { return "DW_AT_address_class"; } + case DW_AT_artificial: { return "DW_AT_artificial"; } + case DW_AT_base_types: { return "DW_AT_base_types"; } + case DW_AT_calling_convention: { return "DW_AT_calling_convention"; } + case DW_AT_count: { return "DW_AT_count"; } + case DW_AT_data_member_location: { return "DW_AT_data_member_location"; } + case DW_AT_decl_column: { return "DW_AT_decl_column"; } + case DW_AT_decl_file: { return "DW_AT_decl_file"; } + case DW_AT_decl_line: { return "DW_AT_decl_line"; } + case DW_AT_declaration: { return "DW_AT_declaration"; } + case DW_AT_encoding: { return "DW_AT_encoding"; } + case DW_AT_external: { return "DW_AT_external"; } + case DW_AT_frame_base: { return "DW_AT_frame_base"; } + case DW_AT_friend: { return "DW_AT_friend"; } + case DW_AT_identifier_case: { return "DW_AT_identifier_case"; } + case DW_AT_macro_info: { return "DW_AT_macro_info"; } + case DW_AT_namelist_item: { return "DW_AT_namelist_item"; } + case DW_AT_priority: { return "DW_AT_priority"; } + case DW_AT_segment: { return "DW_AT_segment"; } + case DW_AT_specification: { return "DW_AT_specification"; } + case DW_AT_static_link: { return "DW_AT_static_link"; } + case DW_AT_type: { return "DW_AT_type"; } + case DW_AT_use_location: { return "DW_AT_use_location"; } + case DW_AT_variable_parameter: { return "DW_AT_variable_parameter"; } + case DW_AT_virtuality: { return "DW_AT_virtuality"; } + case DW_AT_vtable_elem_location: { return "DW_AT_vtable_elem_location"; } + case DW_AT_signature: { return "DW_AT_signature"; } + case DW_AT_main_subprogram: { return "DW_AT_main_subprogram"; } + case DW_AT_data_bit_offset: { return "DW_AT_data_bit_offset"; } + case DW_AT_const_expr: { return "DW_AT_const_expr"; } + case DW_AT_enum_class: { return "DW_AT_enum_class"; } + case DW_AT_linkage_name: { return "DW_AT_linkage_name"; } + case DW_AT_string_length_bit_size: { return "DW_AT_string_length_bit_size"; } + case DW_AT_string_length_byte_size: { return "DW_AT_string_length_byte_size"; } + case DW_AT_rank: { return "DW_AT_rank"; } + case DW_AT_str_offsets_base: { return "DW_AT_str_offsets_base"; } + case DW_AT_addr_base: { return "DW_AT_addr_base"; } + case DW_AT_rnglists_base: { return "DW_AT_rnglists_base"; } + case DW_AT_dwo_id: { return "DW_AT_dwo_id"; } + case DW_AT_dwo_name: { return "DW_AT_dwo_name"; } + case DW_AT_reference: { return "DW_AT_reference"; } + case DW_AT_rvalue_reference: { return "DW_AT_rvalue_reference"; } + case DW_AT_macros: { return "DW_AT_macros"; } + case DW_AT_call_all_calls: { return "DW_AT_call_all_calls"; } + case DW_AT_call_all_source_calls: { return "DW_AT_call_all_source_calls"; } + case DW_AT_call_all_tail_calls: { return "DW_AT_call_all_tail_calls"; } + case DW_AT_call_return_pc: { return "DW_AT_call_return_pc"; } + case DW_AT_call_value: { return "DW_AT_call_value"; } + case DW_AT_call_origin: { return "DW_AT_call_origin"; } + case DW_AT_call_parameter: { return "DW_AT_call_parameter"; } + case DW_AT_call_pc: { return "DW_AT_call_pc"; } + case DW_AT_call_tail_call: { return "DW_AT_call_tail_call"; } + case DW_AT_call_target: { return "DW_AT_call_target"; } + case DW_AT_call_target_clobbered: { return "DW_AT_call_target_clobbered"; } + case DW_AT_call_data_location: { return "DW_AT_call_data_location"; } + case DW_AT_call_data_value: { return "DW_AT_call_data_value"; } + case DW_AT_noreturn: { return "DW_AT_noreturn"; } + case DW_AT_alignment: { return "DW_AT_alignment"; } + case DW_AT_export_symbols: { return "DW_AT_export_symbols"; } + case DW_AT_deleted: { return "DW_AT_deleted"; } + case DW_AT_defaulted: { return "DW_AT_defaulted"; } + case DW_AT_loclists_base: { return "DW_AT_loclists_base"; } + } + + return(NULL); +} + +static char * +tag_to_str(enum dwarf_die_tag tag) { + switch (tag) { + case DW_TAG_array_type: { return "DW_TAG_array_type"; } + case DW_TAG_class_type: { return "DW_TAG_class_type"; } + case DW_TAG_entry_point: { return "DW_TAG_entry_point"; } + case DW_TAG_enumeration_type: { return "DW_TAG_enumeration_type"; } + case DW_TAG_formal_parameter: { return "DW_TAG_formal_parameter"; } + case DW_TAG_imported_declaration: { return "DW_TAG_imported_declaration"; } + case DW_TAG_label: { return "DW_TAG_label"; } + case DW_TAG_lexical_block: { return "DW_TAG_lexical_block"; } + case DW_TAG_member: { return "DW_TAG_member"; } + case DW_TAG_pointer_type: { return "DW_TAG_pointer_type"; } + case DW_TAG_reference_type: { return "DW_TAG_reference_type"; } + case DW_TAG_compile_unit: { return "DW_TAG_compile_unit"; } + case DW_TAG_string_type: { return "DW_TAG_string_type"; } + case DW_TAG_structure_type: { return "DW_TAG_structure_type"; } + case DW_TAG_subroutine_type: { return "DW_TAG_subroutine_type"; } + case DW_TAG_typedef: { return "DW_TAG_typedef"; } + case DW_TAG_union_type: { return "DW_TAG_union_type"; } + case DW_TAG_unspecified_parameters: { return "DW_TAG_unspecified_parameters"; } + case DW_TAG_variant: { return "DW_TAG_variant"; } + case DW_TAG_common_block: { return "DW_TAG_common_block"; } + case DW_TAG_common_inclusion: { return "DW_TAG_common_inclusion"; } + case DW_TAG_inheritance: { return "DW_TAG_inheritance"; } + case DW_TAG_inlined_subroutine: { return "DW_TAG_inlined_subroutine"; } + case DW_TAG_module: { return "DW_TAG_module"; } + case DW_TAG_ptr_to_member_type: { return "DW_TAG_ptr_to_member_type"; } + case DW_TAG_set_type: { return "DW_TAG_set_type"; } + case DW_TAG_subrange_type: { return "DW_TAG_subrange_type"; } + case DW_TAG_with_stmt: { return "DW_TAG_with_stmt"; } + case DW_TAG_access_declaration: { return "DW_TAG_access_declaration"; } + case DW_TAG_base_type: { return "DW_TAG_base_type"; } + case DW_TAG_catch_block: { return "DW_TAG_catch_block"; } + case DW_TAG_const_type: { return "DW_TAG_const_type"; } + case DW_TAG_constant: { return "DW_TAG_constant"; } + case DW_TAG_enumerator: { return "DW_TAG_enumerator"; } + case DW_TAG_file_type: { return "DW_TAG_file_type"; } + case DW_TAG_friend: { return "DW_TAG_friend"; } + case DW_TAG_namelist: { return "DW_TAG_namelist"; } + case DW_TAG_namelist_item: { return "DW_TAG_namelist_item"; } + case DW_TAG_packed_type: { return "DW_TAG_packed_type"; } + case DW_TAG_subprogram: { return "DW_TAG_subprogram"; } + case DW_TAG_thrown_type: { return "DW_TAG_thrown_type"; } + case DW_TAG_try_block: { return "DW_TAG_try_block"; } + case DW_TAG_variant_part: { return "DW_TAG_variant_part"; } + case DW_TAG_variable: { return "DW_TAG_variable"; } + case DW_TAG_volatile_type: { return "DW_TAG_volatile_type"; } + case DW_TAG_lo_user: { return "DW_TAG_lo_user"; } + case DW_TAG_MIPS_loop: { return "DW_TAG_MIPS_loop"; } + default: { return "unknown tag"; } + } + + assert(0); + + return(NULL); +} diff --git a/trace_include.c b/trace_include.c new file mode 100644 index 0000000..b83e057 --- /dev/null +++ b/trace_include.c @@ -0,0 +1,5 @@ +int foo(int a) +{ + int b = a + 1; + return(b / 10); +} \ No newline at end of file diff --git a/traceme.c b/traceme.c new file mode 100644 index 0000000..0f67353 --- /dev/null +++ b/traceme.c @@ -0,0 +1,23 @@ +#include +#include + +#include "trace_include.c" + +int main() +{ + int i; + int arr[10]; + + for(i = 0;i < 10; ++i) { + arr[i] = i * i * i / 10; + } + + int pipa = 0; + int boba = 1; + int peppa = pipa / boba + 2 * (boba / 2) - 1 + + pipa * pipa / boba; + + foo(3); + + return 0; +}