Skip to content

Commit

Permalink
Fixes to native Linux backtrace.
Browse files Browse the repository at this point in the history
  • Loading branch information
lerno committed Nov 12, 2023
1 parent f39aa1a commit 1d00526
Show file tree
Hide file tree
Showing 15 changed files with 364 additions and 110 deletions.
1 change: 1 addition & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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: |
Expand Down
4 changes: 2 additions & 2 deletions lib/std/core/builtin.c3
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,11 @@ struct CallstackElement
uint line;
}

fn bool print_backtrace(String message, int backtraces_to_ignore) @if(env::DARWIN)
fn bool print_backtrace(String message, int backtraces_to_ignore) @if(env::DARWIN || env::LINUX)
{
@pool()
{
BacktraceList! backtrace = darwin::backtrace_load(mem::temp());
BacktraceList! backtrace = backtrace::backtrace_load(mem::temp());
if (catch backtrace) return false;
if (backtrace.len() <= backtraces_to_ignore) return false;
io::eprint("\nERROR: '");
Expand Down
3 changes: 3 additions & 0 deletions lib/std/os/backtrace.c3
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ fn void Backtrace.free(&self)
self.allocator.free(self.file);
}

def backtrace_load = darwin::backtrace_load @if(env::DARWIN);
def backtrace_load = linux::backtrace_load @if(env::LINUX);

fn Backtrace* Backtrace.init(&self, uptr offset, String function, String object_file, String file = "", uint line = 0, Allocator* using = mem::heap())
{
if (!using)
Expand Down
200 changes: 200 additions & 0 deletions lib/std/os/linux/linux.c3
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
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(<Backtrace>);

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 ulong! elf_module_image_base(String path) @local
{
File file = file::open(path, "rb")!;
defer (void)file.close();
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((usz)file_header.e_phoff + (usz)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 + (usz)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 Backtrace! backtrace_load_element(void* addr, Allocator* allocator = mem::heap()) @local
{
@pool(allocator)
{
if (!addr) return backtrace::BACKTRACE_UNKNOWN;
char[] buf = mem::temp_array(char, 1024);
Linux_Dl_info info;
if (dladdr(addr, &info) == 0) return backtrace::BACKTRACE_UNKNOWN;
void* obj_address = addr - (uptr)info.dli_fbase + (uptr)elf_module_image_base(info.dli_fname.str_view())!;
ZString obj_path = info.dli_fname;
String s = process::execute_stdout_to_buffer(buf, { "addr2line", "-p", "-i", "-C", "-f", "-e", obj_path.str_view(), string::tformat("0x%x", obj_address) })!;
String[] parts = s.tsplit(" at ");
if (parts.len != 2)
{
return {
.function = info.dli_sname ? info.dli_sname.copy(allocator) : "???".copy(allocator),
.object_file = info.dli_fname.copy(allocator),
.offset = (uptr)addr,
.file = "".copy(allocator),
.line = 0
};
}
uint line = 0;
String source = "";
if (!parts[1].contains("?") && parts[1].contains(":"))
{
usz index = parts[1].rindex_of_char(':')!;
source = parts[1][:index];
line = parts[1][index + 1..].to_uint()!;
}
return {
.function = parts[0].copy(allocator),
.object_file = info.dli_fname.copy(allocator),
.file = source.copy(allocator),
.line = line,
.allocator = allocator
};
};
}

fn BacktraceList! backtrace_load(Allocator* allocator)
{
void*[256] bt_buffer;
CInt size = posix::backtrace(&bt_buffer, 256);
io::printfn("Backtrace list %s", size);
BacktraceList list;
list.init_new(size, allocator);
defer catch
{
foreach (trace : list)
{
trace.free();
}
list.free();
}
@pool(allocator)
{
for (usz i = 1; i < size; i++)
{
Backtrace trace = backtrace_load_element(bt_buffer[i], allocator)!;
list.append(trace);
}
};
return list;
}
9 changes: 3 additions & 6 deletions lib/std/os/macos/darwin.c3
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
7 changes: 7 additions & 0 deletions lib/std/os/subprocess.c3
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
30 changes: 30 additions & 0 deletions resources/linux_stack.c3
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
module test;
import std::io;
import std::collections::map;
import std::os;

fn void! test2()
{
//typeid y = int.typeid;
BacktraceList list = linux::backtrace_load(mem::heap())!;
foreach (Backtrace trace : list)
{
io::printfn(">%s", trace);
}
io::printn("Hello");
//builtin::panicf("FEHifej", "file", "fun", 123);
int x = 2;
int[1] y;
// int z = y[x];
//assert(false, "ofkef");
}

fn void test1()
{
io::printfn("Hello2");
(void)test2();
}
fn void main()
{
test1();
}
1 change: 1 addition & 0 deletions src/compiler/compiler_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -2367,6 +2367,7 @@ void codegen_setup_object_names(Module *module, const char **ir_filename, const
void target_setup(BuildTarget *build_target);
int target_alloca_addr_space();
bool os_is_apple(OsType os_type);
bool os_supports_stacktrace(OsType os_type);
bool arch_is_wasm(ArchType type);

const char *macos_sysroot(void);
Expand Down
8 changes: 8 additions & 0 deletions src/compiler/linker.c
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
3 changes: 2 additions & 1 deletion src/compiler/llvm_codegen_module.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -168,7 +169,7 @@ void gencontext_begin_module(GenContext *c)
LLVMStructSetBody(c->debug.stack_type, types, 5, false);
c->debug.current_stack_ptr = NULL;
c->debug.enable_stacktrace = true;
c->debug.emulated_stacktrace = !os_is_apple(platform_target.os);
c->debug.emulated_stacktrace = !os_supports_stacktrace(platform_target.os);
}
}
c->global_builder = LLVMCreateBuilder();
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/target.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ int target_alloca_addr_space()
return platform_target.alloca_address_space;
}

bool os_supports_stacktrace(OsType os_type)
{
return os_type == OS_TYPE_LINUX || os_is_apple(os_type);
}
bool os_is_apple(OsType os_type)
{
return os_type == OS_TYPE_TVOS || os_type == OS_TYPE_WATCHOS ||
Expand Down
2 changes: 1 addition & 1 deletion src/version.h
Original file line number Diff line number Diff line change
@@ -1 +1 @@
#define COMPILER_VERSION "0.4.699"
#define COMPILER_VERSION "0.4.700"
Loading

0 comments on commit 1d00526

Please sign in to comment.