mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
198 lines
5.2 KiB
C
198 lines
5.2 KiB
C
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);
|
|
|
|
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;
|
|
ZString sname = info.dli_sname ? info.dli_sname : (ZString)"???";
|
|
String s = process::execute_stdout_to_buffer(buf, { "addr2line", "-p", "-i", "-C", "-f", "-e", obj_path.str_view(), string::tformat("0x%x", obj_address - 1) })!;
|
|
String[] parts = s.tsplit(" at ");
|
|
if (parts.len != 2)
|
|
{
|
|
return {
|
|
.function = sname.copy(allocator),
|
|
.object_file = info.dli_fname.copy(allocator),
|
|
.offset = (uptr)addr,
|
|
.file = "".copy(allocator),
|
|
.line = 0,
|
|
.allocator = allocator
|
|
};
|
|
}
|
|
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! symbolize_backtrace(void*[] backtrace, Allocator* allocator)
|
|
{
|
|
BacktraceList list;
|
|
list.init_new(backtrace.len, allocator);
|
|
defer catch
|
|
{
|
|
foreach (trace : list)
|
|
{
|
|
trace.free();
|
|
}
|
|
list.free();
|
|
}
|
|
@pool(allocator)
|
|
{
|
|
foreach (addr : backtrace)
|
|
{
|
|
Backtrace trace = backtrace_load_element(addr, allocator)!;
|
|
list.append(trace);
|
|
}
|
|
};
|
|
return list;
|
|
}
|