diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a8c4196ad..d43a42868 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -264,6 +264,7 @@ jobs: ../build/c3c compile-run examples/load_world.c3 ../build/c3c compile-run examples/process.c3 ../build/c3c compile-run examples/ls.c3 + ../build/c3c compile-run linux_stack.c3 - name: Compile run unit tests run: | diff --git a/lib/std/os/linux/linux.c3 b/lib/std/os/linux/linux.c3 new file mode 100644 index 000000000..d64d5bab3 --- /dev/null +++ b/lib/std/os/linux/linux.c3 @@ -0,0 +1,209 @@ +module std::os::linux @if(env::LINUX); +import libc; +import std::os::posix; +import std::io; +import std::collections::list; + +extern fn isz readlink(ZString path, char* buf, usz bufsize); + +def BacktraceList = List(); + +const PT_PHDR = 6; +const EI_NIDENT = 16; +def Elf32_Half = ushort; +def Elf32_Word = uint; +def Elf32_Addr = uint; +def Elf32_Off = uint; + +struct Elf32_Ehdr +{ + char[EI_NIDENT] e_ident; + Elf32_Half e_type; + Elf32_Half e_machine; + Elf32_Word e_version; + Elf32_Addr e_entry; + Elf32_Off e_phoff; + Elf32_Off e_shoff; + Elf32_Word e_flags; + Elf32_Half e_ehsize; + Elf32_Half e_phentsize; + Elf32_Half e_phnum; + Elf32_Half e_shentsize; + Elf32_Half e_shnum; + Elf32_Half e_shstrndx; +} + +struct Elf32_Phdr +{ + Elf32_Word p_type; + Elf32_Off p_offset; + Elf32_Addr p_vaddr; + Elf32_Addr p_paddr; + Elf32_Word p_filesz; + Elf32_Word p_memsz; + Elf32_Word p_flags; + Elf32_Word p_align; +} + +def Elf64_Addr = ulong; +def Elf64_Half = ushort; +def Elf64_Off = ulong; +def Elf64_Word = uint; +def Elf64_Sword = int; +def Elf64_Sxword = long; +def Elf64_Lword = ulong; +def Elf64_Xword = ulong; + +struct Elf64_Ehdr +{ + char[EI_NIDENT] e_ident; + Elf64_Half e_type; + Elf64_Half e_machine; + Elf64_Word e_version; + Elf64_Addr e_entry; + Elf64_Off e_phoff; + Elf64_Off e_shoff; + Elf64_Word e_flags; + Elf64_Half e_ehsize; + Elf64_Half e_phentsize; + Elf64_Half e_phnum; + Elf64_Half e_shentsize; + Elf64_Half e_shnum; + Elf64_Half e_shstrndx; +} + +struct Elf64_Phdr +{ + Elf64_Word p_type; + Elf64_Word p_flags; + Elf64_Off p_offset; + Elf64_Addr p_vaddr; + Elf64_Addr p_paddr; + Elf64_Xword p_filesz; + Elf64_Xword p_memsz; + Elf64_Xword p_align; +} + +extern fn CInt dladdr(void* addr, Linux_Dl_info* info); + +struct Linux_Dl_info +{ + ZString dli_fname; /* Pathname of shared object */ + void* dli_fbase; /* Base address of shared object */ + ZString dli_sname; /* Name of nearest symbol */ + void* dli_saddr; /* Address of nearest symbol */ +} + +fn bool! resolve_addr(void* addr, ZString* obj_path, ZString* name, void** obj_address) +{ + Linux_Dl_info info; + io::printfn("Trying to resolve %p", addr); + if (dladdr(addr, &info) == 0) return false; + io::printn("Success"); + *obj_path = info.dli_fname; + *obj_address = addr - (uptr)info.dli_fbase + elf_module_image_base(info.dli_fname.str_view())!; + *name = info.dli_sname; + return true; +} + +fn uptr! elf_module_image_base(String path) @local +{ + File file = file::open(path, "rb")!; + char[4] buffer; + io::read_all(&file, &buffer)!; + if (buffer != char[4]{ 0x7f, 'E', 'L', 'F'}) return BacktraceFault.IMAGE_NOT_FOUND?; + bool is_64 = file.read_byte()! == 2; + bool is_little_endian = file.read_byte()! == 1; + // Actually, not supported. + if (!is_little_endian) return BacktraceFault.IMAGE_NOT_FOUND?; + file.seek(0)!; + if (is_64) + { + Elf64_Ehdr file_header; + io::read_any(&file, &file_header)!; + if (file_header.e_ehsize != Elf64_Ehdr.sizeof) return BacktraceFault.IMAGE_NOT_FOUND?; + for (isz i = 0; i < file_header.e_phnum; i++) + { + Elf64_Phdr header; + file.seek(file_header.e_phoff + file_header.e_phentsize * i)!; + io::read_any(&file, &header)!; + if (header.p_type == PT_PHDR) return header.p_vaddr - header.p_offset; + } + return 0; + } + Elf32_Ehdr file_header; + io::read_any(&file, &file_header)!; + if (file_header.e_ehsize != Elf32_Ehdr.sizeof) return BacktraceFault.IMAGE_NOT_FOUND?; + for (isz i = 0; i < file_header.e_phnum; i++) + { + Elf32_Phdr header; + file.seek(file_header.e_phoff + file_header.e_phentsize * i)!; + io::read_any(&file, &header)!; + if (header.p_type == PT_PHDR) return (ulong)header.p_vaddr - header.p_offset; + } + return 0; +} + +fn BacktraceList! backtrace_load(Allocator* allocator) +{ + void*[256] bt_buffer; + CInt size = posix::backtrace(&bt_buffer, 256); + ZString* symbols = posix::backtrace_symbols(&bt_buffer, size); + BacktraceList list; + list.init_new(size, allocator); + defer catch + { + foreach (trace : list) + { + trace.free(); + } + list.free(); + } + bool has_addr = true; + @pool() + { + char[] buf = mem::temp_array(char, 1024); + while (size > 0) + { + String symbol = symbols[size - 1].str_view(); + io::printn(symbol); + size--; + String file; + String! hex = {| + usz index = symbol.index_of("(")!; + file = symbol[:index]; + String after = symbol[index + 1..]; + after = after[after.index_of("[0x")! + 1..]; + return after[:after.index_of("]")!]; + |}; + if (try hex) + { + ZString obj_path; + ZString name; + void* obj_address; + if (resolve_addr(bt_buffer[size], &obj_path, &name, &obj_address)!) + { + io::printfn("Got %s %s %s", obj_path, name, obj_address); + } + else + { + io::printfn("Resolve failed"); + } + (void)io::printfn("Image module offset %s", elf_module_image_base(file)); + io::printfn("%s %s %p", hex, file, bt_buffer[size]); + String! s = process::execute_stdout_to_buffer(buf, { "addr2line", "-p", "-i", "-C", "-f", "-e", file, hex }); + if (try s) + { + io::printn(s); + continue; + } + has_addr = false; + } + else + { + io::printn(symbol); + } + } + }; + return list; +} diff --git a/lib/std/os/macos/darwin.c3 b/lib/std/os/macos/darwin.c3 index 28b164a08..d8f6fb302 100644 --- a/lib/std/os/macos/darwin.c3 +++ b/lib/std/os/macos/darwin.c3 @@ -101,15 +101,12 @@ fn Backtrace! backtrace_load_element(String execpath, void* buffer, void* load_a { if (buffer) { - SubProcess process = process::create({ "atos", - "-o", execpath, "-arch", env::AARCH64 ? "arm64" : "x86_64", "-l", + char* buf = tmalloc(1024); + String s = process::execute_stdout_to_buffer(buf[:1024], + { "atos", "-o", execpath, "-arch", env::AARCH64 ? "arm64" : "x86_64", "-l", string::tformat("%p", load_address), string::tformat("%p", buffer), "-fullPath" })!; - process.join()!; - char* buf = tmalloc(1024); - usz len = process.read_stdout(buf, 1024)!; - String s = (String)buf[:len - 1]; String[] parts = s.tsplit(" "); if (parts.len == 4) { diff --git a/lib/std/os/subprocess.c3 b/lib/std/os/subprocess.c3 index 0305a6856..aac929d76 100644 --- a/lib/std/os/subprocess.c3 +++ b/lib/std/os/subprocess.c3 @@ -269,6 +269,13 @@ fn ZString* tcopy_env(String[] environment) @local @inline @if(env::POSIX) return copy; } +fn String! execute_stdout_to_buffer(char[] buffer, String[] command_line, SubProcessOptions options = {}, String[] environment = {}) +{ + SubProcess process = process::create(command_line, options, environment)!; + process.join()!; + usz len = process.read_stdout(buffer.ptr, buffer.len)!; + return (String)buffer[:len - 1]; +} /** * @require !environment || !options.inherit_environment diff --git a/resources/linux_stack.c3 b/resources/linux_stack.c3 new file mode 100644 index 000000000..940449105 --- /dev/null +++ b/resources/linux_stack.c3 @@ -0,0 +1,10 @@ +module test; +import std::io; +import std::collections::map; +import std::os; + +fn void main() +{ + int x = 2; + (void)linux::backtrace_load(mem::heap()); +} \ No newline at end of file diff --git a/src/compiler/linker.c b/src/compiler/linker.c index 8e487f82c..572789b21 100644 --- a/src/compiler/linker.c +++ b/src/compiler/linker.c @@ -386,9 +386,17 @@ static void linker_setup_linux(const char ***args_ref, LinkerType linker_type) { if (linker_type == LINKER_CC) { + if (active_target.debug_info == DEBUG_INFO_FULL) + { + add_arg("-rdynamic"); + } add_arg("-pthread"); return; } + if (active_target.debug_info == DEBUG_INFO_FULL) + { + add_arg("-export-dynamic"); + } if (is_no_pie(platform_target.reloc_model)) add_arg("-no-pie"); if (is_pie(platform_target.reloc_model)) add_arg("-pie"); if (platform_target.arch == ARCH_TYPE_X86_64) add_arg("--eh-frame-hdr"); diff --git a/src/compiler/llvm_codegen_module.c b/src/compiler/llvm_codegen_module.c index 44cc3e233..af98fd6a2 100644 --- a/src/compiler/llvm_codegen_module.c +++ b/src/compiler/llvm_codegen_module.c @@ -152,6 +152,7 @@ void gencontext_begin_module(GenContext *c) { llvm_set_module_flag(c, LLVMModuleFlagBehaviorWarning, "Dwarf Version", 4, type_uint); llvm_set_module_flag(c, LLVMModuleFlagBehaviorWarning, "Debug Info Version", 3, type_uint); + llvm_set_module_flag(c, LLVMModuleFlagBehaviorWarning, "frame-pointer", 2, type_uint); } llvm_set_module_flag(c, LLVMModuleFlagBehaviorError, "uwtable", 2, type_uint);