about summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
authorrajdakin <rajdakin@gmail.com>2022-02-14 13:13:12 +0100
committerrajdakin <rajdakin@gmail.com>2022-02-14 13:13:12 +0100
commit77925f264fbb60d67fb83e9f2d7d27f5898f7982 (patch)
tree655d59df7b98083499875594603b887fb43e13a5 /src
parent97857d55a02c41ab3b34f9c811ebd7b82f9a1894 (diff)
downloadbox64-77925f264fbb60d67fb83e9f2d7d27f5898f7982.tar.gz
box64-77925f264fbb60d67fb83e9f2d7d27f5898f7982.zip
Fixed the backtrace wrapper (uses eh_frame information only)
Diffstat (limited to 'src')
-rwxr-xr-xsrc/dynarec/dynarec.c2
-rw-r--r--src/elfs/elfdwarf_private.c717
-rw-r--r--src/elfs/elfdwarf_private.h19
-rwxr-xr-xsrc/elfs/elfloader_private.h5
-rwxr-xr-xsrc/elfs/elfparser.c11
-rwxr-xr-xsrc/emu/x64emu.c2
-rwxr-xr-xsrc/libtools/threads.c5
-rwxr-xr-xsrc/wrapped/wrappedlibc.c68
8 files changed, 795 insertions, 34 deletions
diff --git a/src/dynarec/dynarec.c b/src/dynarec/dynarec.c
index 8edefa58..36fa85ad 100755
--- a/src/dynarec/dynarec.c
+++ b/src/dynarec/dynarec.c
@@ -111,6 +111,8 @@ void DynaCall(x64emu_t* emu, uintptr_t addr)
         uint64_t old_rsi = R_RSI;
         uint64_t old_rbp = R_RBP;
         uint64_t old_rip = R_RIP;
+        Push64(emu, GetRBP(emu));   // set frame pointer
+        SetRBP(emu, GetRSP(emu));   // save RSP
         PushExit(emu);
         R_RIP = addr;
         emu->df = d_none;
diff --git a/src/elfs/elfdwarf_private.c b/src/elfs/elfdwarf_private.c
new file mode 100644
index 00000000..7c5b3c92
--- /dev/null
+++ b/src/elfs/elfdwarf_private.c
@@ -0,0 +1,717 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "debug.h"
+#include "elfloader_private.h"
+#include "elfdwarf_private.h"
+
+typedef struct dwarf_unwind_constr_s {
+    uint8_t reg_count;
+    uint64_t *table;
+    uint8_t *statuses;
+#define GET_STATUS(n,i) (((n).statuses[(i) >> 1] >> (((i) & 1) << 2)) & 0xF)
+#define SET_STATUS(n,i,v) (n).statuses[(i) >> 1] = ((n).statuses[(i) >> 1] & (0xF0 >> (((i) & 1) << 2))) | (v << (((i) & 1) << 2))
+#define REGSTATUS_undefined     0b0000
+#define REGSTATUS_same_val      0b0101
+#define REGSTATUS_offset        0b0010
+#define REGSTATUS_val_offset    0b0011
+#define REGSTATUS_register      0b0100
+} dwarf_unwind_constr_t;
+
+#define DW_EH_PE_uptr       0x00
+#define DW_EH_PE_uleb128    0x01
+#define DW_EH_PE_udata2     0x02
+#define DW_EH_PE_udata4     0x03
+#define DW_EH_PE_udata8     0x04
+#define DW_EH_PE_SIZE_MASK  0x07
+#define DW_EH_PE_signed     0x08
+#define DW_EH_PE_absptr     0x00
+#define DW_EH_PE_pcrel      0x10
+#define DW_EH_PE_textrel    0x20 // Unsupported
+#define DW_EH_PE_datarel    0x30 // Unsupported
+#define DW_EH_PE_indirect   0x80 // May crash
+#define DW_EH_PE_omit       0xff
+
+#define READ_U1(var, ptr) do { (var) = *( uint8_t*)(ptr); (ptr) += 1; } while (0)
+#define READ_S1(var, ptr) do { (var) = *(  int8_t*)(ptr); (ptr) += 1; } while (0)
+#define READ_U2(var, ptr) do { (var) = *(uint16_t*)(ptr); (ptr) += 2; } while (0)
+#define READ_S2(var, ptr) do { (var) = *( int16_t*)(ptr); (ptr) += 2; } while (0)
+#define READ_U4(var, ptr) do { (var) = *(uint32_t*)(ptr); (ptr) += 4; } while (0)
+#define READ_S4(var, ptr) do { (var) = *( int32_t*)(ptr); (ptr) += 4; } while (0)
+#define READ_U8(var, ptr) do { (var) = *(uint64_t*)(ptr); (ptr) += 8; } while (0)
+#define READ_S8(var, ptr) do { (var) = *( int64_t*)(ptr); (ptr) += 8; } while (0)
+#define READ_ULEB128(var, ptr) readULEB(&(var), &(ptr))
+#define READ_SLEB128(var, ptr) readSLEB((int64_t*)&(var), &(ptr))
+// Seems like DW_EH_PE_signed is actually an invalid encoding
+#define READ_ENCODED(var, ptr, enc, isptr) do { uintptr_t optr = (uintptr_t)(ptr); \
+         if ((((enc) & DW_EH_PE_SIZE_MASK) == DW_EH_PE_uleb128) &&  ((enc) & DW_EH_PE_signed)) READ_SLEB128(var, ptr); \
+    else if ((((enc) & DW_EH_PE_SIZE_MASK) == DW_EH_PE_uleb128) && !((enc) & DW_EH_PE_signed)) READ_ULEB128(var, ptr); \
+    else if ((((enc) & DW_EH_PE_SIZE_MASK) == DW_EH_PE_udata2 ) &&  ((enc) & DW_EH_PE_signed)) READ_S2     (var, ptr); \
+    else if ((((enc) & DW_EH_PE_SIZE_MASK) == DW_EH_PE_udata2 ) && !((enc) & DW_EH_PE_signed)) READ_U2     (var, ptr); \
+    else if ((((enc) & DW_EH_PE_SIZE_MASK) == DW_EH_PE_udata4 ) &&  ((enc) & DW_EH_PE_signed)) READ_S4     (var, ptr); \
+    else if ((((enc) & DW_EH_PE_SIZE_MASK) == DW_EH_PE_udata4 ) && !((enc) & DW_EH_PE_signed)) READ_U4     (var, ptr); \
+    else if ((((enc) & DW_EH_PE_SIZE_MASK) == DW_EH_PE_udata8 ) &&  ((enc) & DW_EH_PE_signed)) READ_S8     (var, ptr); \
+    else if ((((enc) & DW_EH_PE_SIZE_MASK) == DW_EH_PE_udata8 ) && !((enc) & DW_EH_PE_signed)) READ_U8     (var, ptr); \
+    else if ((((enc) & DW_EH_PE_SIZE_MASK) == DW_EH_PE_uptr   ) &&  ((enc) & DW_EH_PE_signed)) READ_S8     (var, ptr); \
+    else if ((((enc) & DW_EH_PE_SIZE_MASK) == DW_EH_PE_uptr   ) && !((enc) & DW_EH_PE_signed)) READ_U8     (var, ptr); \
+    else { printf_log(LOG_DEBUG, "Invalid encoding 0x%02X\n", (enc)); (var) = 0; } \
+    if (((enc) & DW_EH_PE_pcrel) && isptr) (var) += optr; \
+    if ((enc) & DW_EH_PE_indirect) (var) = *(uint64_t*)(var); \
+} while (0)
+#define SKIP_1(ptr) (ptr) += 1
+#define SKIP_2(ptr) (ptr) += 2
+#define SKIP_4(ptr) (ptr) += 4
+#define SKIP_8(ptr) (ptr) += 8
+#define SKIP_LEB128(ptr) do { ++(ptr); } while (((*(((unsigned char*)(ptr))-1))) & (1 << 7))
+#define SKIP_ENCODED(ptr, enc) \
+    do { if (((enc) & DW_EH_PE_SIZE_MASK) == DW_EH_PE_uleb128) SKIP_LEB128(ptr); \
+    else if (((enc) & DW_EH_PE_SIZE_MASK) == DW_EH_PE_udata2 ) SKIP_2     (ptr); \
+    else if (((enc) & DW_EH_PE_SIZE_MASK) == DW_EH_PE_udata4 ) SKIP_4     (ptr); \
+    else if (((enc) & DW_EH_PE_SIZE_MASK) == DW_EH_PE_udata8 ) SKIP_8     (ptr); \
+    else if (((enc) & DW_EH_PE_SIZE_MASK) == DW_EH_PE_uptr   ) SKIP_8     (ptr); \
+    else printf_log(LOG_DEBUG, "Invalid encoding 0x%02X\n", (enc)); \
+} while (0)
+
+// LEB128 wil only work on 64 bits numbers (unsigned long, like the Linux kernel)
+void readULEB(uint64_t *var, unsigned char **ptr) {
+    *var = 0;
+    int shift = 0;
+    while (1) {
+        unsigned char byte = **ptr; ++*ptr;
+        *var |= ((byte & 0x7F) << shift);
+        if (!(byte & (1 << 7))) break;
+        shift += 7;
+    }
+}
+void readSLEB(int64_t *var, unsigned char **ptr) {
+    uint64_t tmp = 0;
+    int shift = 0;
+    unsigned char byte;
+    while (1) {
+        byte = **ptr; ++*ptr;
+        tmp |= ((byte & 0x7F) << shift);
+        if (!(byte & (1 << 7))) break;
+        shift += 7;
+    }
+    // Sign extend
+    if (byte & (1 << 6)) {
+        *(uint64_t*)var = tmp | (0xFFFFFFFFFFFFFFFFull << (shift + 7));
+    } else {
+        *var = tmp;
+    }
+}
+
+uintptr_t get_parent_registers(dwarf_unwind_t *unwind, const elfheader_t *ehdr, uintptr_t addr, char *success) {
+    if (!ehdr) {
+        *success = 0;
+        return 0;
+    }
+    unsigned char ehfh_version = *(unsigned char*)ehdr->ehframehdr;
+    if (ehfh_version != 1) {
+        *success = 0;
+        return 0;
+    }
+
+    // printf_log(LOG_NONE, "Address: 0x%016lX\nEH frame address: 0x%016lX\n", addr, ehdr->ehframe);
+
+    // Not using the binary search table (for now)
+
+    unsigned char *cur_addr = (unsigned char*)ehdr->ehframe;
+    unsigned char *end_addr = (unsigned char*)ehdr->ehframe_end;
+
+#define AUG_EHDATA      (1 << 0)
+#define AUG_AUGDATA     (1 << 1)
+#define AUG_LSDA        (1 << 2)
+#define AUG_PARG        (1 << 3)
+#define AUG_FDEENC      (1 << 4)
+#define AUG_SIGHDLER    (1 << 5)
+    unsigned short aug_fields = 0;
+    unsigned char lsda_encoding = DW_EH_PE_omit;
+    unsigned char fde_encoding  = DW_EH_PE_omit;
+    uint64_t eh_data = 0;               // Note: these are never reset, so use aug_fields & AUG_EHDATA
+    uint64_t code_alignment_factor = 0;
+    int64_t data_alignment_factor = 0;
+    uint64_t cie_aug_data_len = 0;      // Note: these are never reset, so use aug_fields & AUG_AUGDATA
+    unsigned char *cie_aug_data = NULL; // Note: these are never reset, so use aug_fields & AUG_AUGDATA
+    unsigned char *initCIEinstr = NULL; // Never NULL if initialized
+    unsigned char *endCIEinstr = NULL;  // Never NULL if initialized
+    uint64_t return_addr_reg = 0;       // Description absent in the LSBCS 5.0, but still present
+    while (cur_addr < end_addr) {
+        // Length
+        uint64_t len; int extended = 0;
+        READ_U4(len, cur_addr);
+        if (!len) {
+            // Terminator, end parsing
+            *success = 0;
+            return 0;
+        } else if (len == 0xFFFFFFFF) {
+            // Extended Length (optional)
+            extended = 1;
+            READ_U8(len, cur_addr);
+        }
+        (void)extended;
+        // printf_log(LOG_NONE, "[???] Length          %02X\n", len);
+        // printf_log(LOG_NONE, "[???] Extended        %c\n", extended ? 'Y' : 'N');
+        unsigned char *next_addr = cur_addr + len;
+
+        // CIE ID (always 0) or CIE Pointer (never 0)
+        uint32_t cie_ptr;
+        READ_U4(cie_ptr, cur_addr);
+        if (cie_ptr == 0) {
+            // Current block is a CIE
+            // printf_log(LOG_NONE, "[CIE] ID              %02X\n", cie_ptr);
+
+            // Version (always 1 or 3)
+            uint8_t cie_version;
+            READ_U1(cie_version, cur_addr);
+            // printf_log(LOG_NONE, "[CIE] Version         %01X\n", cie_version);
+            if ((cie_version != 1) && (cie_version != 3)) { // Only 1 is in the LSBCS 5.0 but I found 3 exists as well
+                // Invalid CIE version
+                printf_log(LOG_DEBUG, "Invalid CIE version %d (should be 1 or 3), stopping parsing\n", cie_version);
+                // Failed to process CIE
+                *success = 0;
+                return 0;
+            }
+
+            // Augmentation String
+            char *aug_str = (char*)cur_addr; cur_addr += strlen(aug_str) + 1;
+            // printf_log(LOG_NONE, "[CIE] Augmentation st %s\n", aug_str);
+            aug_fields = (aug_str[0] == 'z') ? AUG_AUGDATA : 0;
+            for (char *aug_str2 = aug_str + ((aug_fields & AUG_AUGDATA) ? 1 : 0); *aug_str2; ++aug_str2) {
+                if ((aug_str2[0] == 'e') && (aug_str2[1] == 'h')) {
+                    // Use has also been left untold in the Linux Standard Base Core Specification 3.0RC1
+                    printf_log(LOG_DEBUG, "Warning: EH data detected but this was removed from the standard\n");
+                    aug_fields |= AUG_EHDATA; ++aug_str2;
+                } else if (aug_fields & AUG_AUGDATA) {
+                    if (aug_str2[0] == 'L') {
+                        aug_fields |= AUG_LSDA;
+                    } else if (aug_str2[0] == 'P') {
+                        aug_fields |= AUG_PARG;
+                        printf_log(LOG_DEBUG, "Warning: augmentation string attribute 'P' unsupported\n");
+                    } else if (aug_str2[0] == 'S') {
+                        aug_fields |= AUG_SIGHDLER;
+                        printf_log(LOG_DEBUG, "Warning: augmentation string attribute 'S' ignored\n");
+                    } else if (aug_str2[0] == 'R') {
+                        aug_fields |= AUG_FDEENC;
+                    } else {
+                        printf_log(LOG_DEBUG, "Error: invalid augmentation string %s\n", aug_str);
+                        // Failed to process augmentation string
+                        break;
+                    }
+                } else {
+                    printf_log(LOG_DEBUG, "Error: invalid augmentation string %s\n", aug_str);
+                    // Failed to process augmentation string
+                    break;
+                }
+            }
+            // printf_log(LOG_NONE, "[CIE] Augmentation fs %02x\n", aug_fields);
+
+            // Code Alignment Factor
+            READ_ULEB128(code_alignment_factor, cur_addr);
+            // printf_log(LOG_NONE, "[CIE] Code AF         %d\n", code_alignment_factor);
+            // Data Alignment Factor
+            READ_SLEB128(data_alignment_factor, cur_addr);
+            // printf_log(LOG_NONE, "[CIE] Data AF         %d\n", data_alignment_factor);
+
+            if (cie_version == 1) {
+                READ_U1(return_addr_reg, cur_addr);
+            } else {
+                READ_ULEB128(return_addr_reg, cur_addr);
+            }
+            // printf_log(LOG_NONE, "[CIE] Return addr reg %01X\n", return_addr_reg);
+
+            lsda_encoding = DW_EH_PE_omit;
+            fde_encoding  = DW_EH_PE_absptr;
+            if (aug_fields & AUG_AUGDATA) {
+                // Augmentation Data Length (optional)
+                READ_ULEB128(cie_aug_data_len, cur_addr);
+                // printf_log(LOG_NONE, "[CIE] Aug data len    %01X\n", cie_aug_data_len);
+                // Augmentation Data (optional)
+                cie_aug_data = cur_addr; cur_addr += cie_aug_data_len;
+                char *cie_aug_data2 = (char*)cie_aug_data;
+                for (char *aug_str2 = aug_str + ((aug_fields & AUG_AUGDATA) ? 1 : 0); *aug_str2; ++aug_str2) {
+                    if ((aug_str2[0] == 'e') && (aug_str2[1] == 'h')) {
+                        break; // Unknown usage
+                    } else if (aug_fields & AUG_AUGDATA) {
+                        if (aug_str2[0] == 'L') {
+                            READ_U1(lsda_encoding, cie_aug_data2);
+                            if (lsda_encoding != DW_EH_PE_omit) printf_log(LOG_DEBUG, "Warning: LSDA unsupported\n");
+                        } else if (aug_str2[0] == 'P') {
+                            unsigned char pencoding;
+                            READ_U1(pencoding, cie_aug_data2);
+                            SKIP_ENCODED(cie_aug_data2, pencoding);
+                        } else if (aug_str2[0] == 'S') {
+                        } else if (aug_str2[0] == 'R') {
+                            READ_U1(fde_encoding, cie_aug_data2);
+                            // printf_log(LOG_NONE, "[CIE] FDE encoding    %02X\n", fde_encoding);
+                            if (fde_encoding == DW_EH_PE_omit) printf_log(LOG_DEBUG, "Error: FDE encoding set to 'omit'\n");
+                        } else {
+                            break;
+                        }
+                    } else {
+                        break;
+                    }
+                }
+            }
+        
+            // Initial Instructions
+            initCIEinstr = cur_addr;
+            endCIEinstr = next_addr;
+        } else if (initCIEinstr) {
+            // Current block is a FDE
+            // printf_log(LOG_NONE, "[FDE] Ptr             %02X\n", cie_ptr);
+
+            // PC Begin
+            uintptr_t pc_begin;
+            READ_ENCODED(pc_begin, cur_addr, fde_encoding, 1);
+            // printf_log(LOG_NONE, "[FDE] PC begin        %016lX\n", pc_begin);
+            // PC Range
+            uint64_t pc_range;
+            READ_ENCODED(pc_range, cur_addr, fde_encoding, 0);
+            // printf_log(LOG_NONE, "[FDE] PC end          %016lX\n", pc_begin + pc_range);
+            
+            uint64_t aug_data_len;
+            unsigned char *aug_data;
+            if (aug_fields & AUG_AUGDATA) {
+                // Augmentation Data Length (optional)
+                READ_ULEB128(aug_data_len, cur_addr);
+                // printf_log(LOG_NONE, "[FDE] Aug data len    %01X\n", aug_data_len);
+                // Augmentation Data (optional)
+                aug_data = cur_addr; cur_addr += aug_data_len;
+                (void)aug_data;
+            }
+
+            if (lsda_encoding != DW_EH_PE_omit) {
+                SKIP_ENCODED(cur_addr, lsda_encoding);
+            }
+
+            // Call Frame Instructions
+            if ((pc_begin <= addr) && (addr < pc_begin + pc_range)) {
+                // Corresponding Frame Descriptor Entry found!
+
+#define setmax(a, b) a = (a < (b) ? (b) : a)
+                dwarf_unwind_constr_t unwind_constr;
+                unwind_constr.reg_count = return_addr_reg;
+                uint64_t maxstacksize = 0;
+                uint64_t curstacksize = 0;
+                unsigned char *cur_inst = initCIEinstr;
+                uint64_t nreg;
+#define PARSE_INST \
+        switch (inst >> 6) {                                        \
+        case 0b01:                                                  \
+            break;                                                  \
+        case 0b10:                                                  \
+            setmax(unwind_constr.reg_count, inst & 0x3F);           \
+            SKIP_LEB128(cur_inst);                                  \
+            break;                                                  \
+        case 0b11:                                                  \
+            setmax(unwind_constr.reg_count, inst & 0x3F);           \
+            break;                                                  \
+        case 0b00:                                                  \
+            switch (inst & 0x3F) {                                  \
+            case 0b000000:                                          \
+                break;                                              \
+            case 0b001010:                                          \
+                ++curstacksize;                                     \
+                setmax(maxstacksize, curstacksize);                 \
+                break;                                              \
+            case 0b001011:                                          \
+                if (!curstacksize) {                                \
+                    printf_log(LOG_DEBUG, "Negative stack size during CFI execution\n"); \
+                    *success = 0;                                   \
+                    return 0;                                       \
+                }                                                   \
+                --curstacksize;                                     \
+                break;                                              \
+            case 0b000010:                                          \
+                SKIP_1(cur_inst);                                   \
+                break;                                              \
+            case 0b000011:                                          \
+                SKIP_2(cur_inst);                                   \
+                break;                                              \
+            case 0b000100:                                          \
+                SKIP_4(cur_inst);                                   \
+                break;                                              \
+            case 0b000001:                                          \
+                SKIP_8(cur_inst);                                   \
+                break;                                              \
+            case 0b001110:                                          \
+            case 0b010011:                                          \
+                SKIP_LEB128(cur_inst);                              \
+                break;                                              \
+            case 0b000110:                                          \
+            case 0b000111:                                          \
+            case 0b001000:                                          \
+            case 0b001101:                                          \
+                READ_ULEB128(nreg, cur_inst);                       \
+                setmax(unwind_constr.reg_count, nreg);              \
+                break;                                              \
+            case 0b000101:                                          \
+            case 0b001100:                                          \
+            case 0b010001:                                          \
+            case 0b010010:                                          \
+            case 0b010100:                                          \
+            case 0b010101:                                          \
+                READ_ULEB128(nreg, cur_inst);                       \
+                setmax(unwind_constr.reg_count, nreg);              \
+                SKIP_LEB128(cur_inst);                              \
+                break;                                              \
+            case 0b001001:                                          \
+                READ_ULEB128(nreg, cur_inst);                       \
+                setmax(unwind_constr.reg_count, nreg);              \
+                READ_ULEB128(nreg, cur_inst);                       \
+                setmax(unwind_constr.reg_count, nreg);              \
+                break;                                              \
+            default:                                                \
+                /* Contains undefined, user and expression CFIs */  \
+                printf_log(LOG_DEBUG, "Unknown CFI 0x%02X\n", inst); \
+                *success = 0;                                       \
+                return 0;                                        \
+            }                                                       \
+            break;                                                  \
+        }
+                while (cur_inst < endCIEinstr) {
+                    unsigned char inst; READ_U1(inst, cur_inst);
+                    PARSE_INST
+                }
+                cur_inst = cur_addr;
+                while (cur_inst < next_addr) {
+                    unsigned char inst; READ_U1(inst, cur_inst);
+                    PARSE_INST
+                }
+#undef PARSE_INST
+
+                uint64_t cfa_reg = -1;
+                unsigned char cfa_signed;
+                union { uint64_t uoff; int64_t soff; } cfa_offset = { .uoff = 0 };
+                ++unwind_constr.reg_count;
+                size_t tablelen = unwind_constr.reg_count * sizeof(uint64_t);
+                size_t statuseslen = ((unwind_constr.reg_count+1) >> 1) * sizeof(uint8_t);
+                unwind_constr.table = (uint64_t*)malloc(tablelen);
+                unwind_constr.statuses = (uint8_t*)calloc((unwind_constr.reg_count+1) >> 1, sizeof(uint8_t));
+                // ~~undefined is 0: no initialization needed~~ still initialize the first 17 to same_val
+                for (int i = 0; (i < 17) && (i <= unwind_constr.reg_count); ++i) {
+                    SET_STATUS(unwind_constr, i, REGSTATUS_same_val);
+                }
+
+                curstacksize = 0;
+                uint64_t **table_stack = (uint64_t**)malloc(maxstacksize * sizeof(uint64_t*));
+                for (uint64_t i = 0; i < maxstacksize; ++i) {
+                    table_stack[i] = (uint64_t*)malloc(tablelen);
+                }
+                cur_inst = initCIEinstr;
+                uintptr_t cur_pointed_addr = pc_begin;
+                // Missing:
+                /* DW_CFA_def_cfa_expression    0   0x0f      BLOCK                              */
+                /* DW_CFA_expression            0   0x10      ULEB128 register  BLOCK            */
+                /* DW_CFA_val_expression        0   0x16      ULEB128 BLOCK                      */
+                /* DW_CFA_lo_user               0   0x1c                                         */
+                /* DW_CFA_GNU_args_size         0   0x2e      ULEB128 argsize                    */
+                /* DW_CFA_GNU_negative_offset_extended 0 0x2f ULEB128 register  ULEB128 offset (obsoleted by DW_CFA_offset_extended_sf) */
+                /* DW_CFA_hi_user               0   0x3f                                         */
+                // Known "bug": DW_CFA_set_loc can go backwards, this is ignored
+                uint64_t tmpreg;
+                uint64_t tmpuval;
+                int64_t tmpsval;
+#define PARSE_INST \
+        switch (inst >> 6) {                                                                                    \
+        case 0b01:         /* DW_CFA_advance_loc           0x1 delta                                        */  \
+            cur_pointed_addr += (inst & 0x3F) * code_alignment_factor;                                          \
+            break;                                                                                              \
+        case 0b10:         /* DW_CFA_offset                0x2 register  ULEB128 offset                     */  \
+            READ_ULEB128(tmpuval, cur_inst);                                                                    \
+            SET_STATUS(unwind_constr, inst & 0x3F, REGSTATUS_offset);                                           \
+            unwind_constr.table[inst & 0x3F] = (int64_t)tmpuval * data_alignment_factor;                        \
+            break;                                                                                              \
+        case 0b11:         /* DW_CFA_restore               0x3 register                                     */  \
+            RESTORE_REG(inst & 0x3F)                                                                            \
+            break;                                                                                              \
+        case 0b00:                                                                                              \
+            switch (inst & 0x3F) {                                                                              \
+            case 0b000001: /* DW_CFA_set_loc               0   0x01      address                            */  \
+                READ_U8(cur_pointed_addr, cur_inst);                                                            \
+                break;                                                                                          \
+            case 0b000010: /* DW_CFA_advance_loc1          0   0x02      1-byte delta                       */  \
+                READ_U1(tmpuval, cur_inst);                                                                     \
+                cur_pointed_addr += tmpuval * code_alignment_factor;                                            \
+                break;                                                                                          \
+            case 0b000011: /* DW_CFA_advance_loc2          0   0x03      2-byte delta                       */  \
+                READ_U2(tmpuval, cur_inst);                                                                     \
+                cur_pointed_addr += tmpuval * code_alignment_factor;                                            \
+                break;                                                                                          \
+            case 0b000100: /* DW_CFA_advance_loc4          0   0x04      4-byte delta                       */  \
+                READ_U4(tmpuval, cur_inst);                                                                     \
+                cur_pointed_addr += tmpuval * code_alignment_factor;                                            \
+                break;                                                                                          \
+            \
+            case 0b001100: /* DW_CFA_def_cfa               0   0x0c      ULEB128 register  ULEB128 offset   */  \
+                READ_ULEB128(cfa_reg, cur_inst);                                                                \
+                cfa_signed = 0;                                                                                 \
+                READ_ULEB128(cfa_offset.uoff, cur_inst);                                                        \
+                break;                                                                                          \
+            case 0b010010: /* DW_CFA_def_cfa_sf            0   0x12      ULEB128 register  SLEB128 offset   */  \
+                READ_ULEB128(cfa_reg, cur_inst);                                                                \
+                cfa_signed = 1;                                                                                 \
+                READ_SLEB128(cfa_offset.soff, cur_inst);                                                        \
+                cfa_offset.soff *= data_alignment_factor;                                                       \
+                break;                                                                                          \
+            case 0b001101: /* DW_CFA_def_cfa_register      0   0x0d      ULEB128 register                   */  \
+                READ_ULEB128(cfa_reg, cur_inst);                                                                \
+                break;                                                                                          \
+            case 0b001110: /* DW_CFA_def_cfa_offset        0   0x0e      ULEB128 offset                     */  \
+                cfa_signed = 0;                                                                                 \
+                READ_ULEB128(cfa_offset.uoff, cur_inst);                                                        \
+                break;                                                                                          \
+            case 0b010011: /* DW_CFA_def_cfa_offset_sf     0   0x13      SLEB128 offset                     */  \
+                READ_SLEB128(cfa_offset.soff, cur_inst);                                                        \
+                cfa_offset.soff *= data_alignment_factor;                                                       \
+                break;                                                                                          \
+            /* DW_CFA_def_cfa_expression */ \
+            \
+            case 0b000111: /* DW_CFA_undefined             0   0x07      ULEB128 register                   */  \
+                READ_ULEB128(tmpreg, cur_inst);                                                                 \
+                SET_STATUS(unwind_constr, tmpreg, REGSTATUS_undefined);                                         \
+                break;                                                                                          \
+            case 0b001000: /* DW_CFA_same_value            0   0x08      ULEB128 register                   */  \
+                READ_ULEB128(tmpreg, cur_inst);                                                                 \
+                SET_STATUS(unwind_constr, tmpreg, REGSTATUS_same_val);                                          \
+                break;                                                                                          \
+            case 0b000101: /* DW_CFA_offset_extended       0   0x05      ULEB128 register  ULEB128 offset   */  \
+                READ_ULEB128(tmpreg, cur_inst);                                                                 \
+                READ_ULEB128(tmpuval, cur_inst);                                                                \
+                SET_STATUS(unwind_constr, tmpreg, REGSTATUS_offset);                                            \
+                unwind_constr.table[tmpreg] = (int64_t)tmpuval * data_alignment_factor;                         \
+                break;                                                                                          \
+            case 0b010001: /* DW_CFA_offset_extended_sf    0   0x11      ULEB128 register  SLEB128 offset   */  \
+                READ_ULEB128(tmpreg, cur_inst);                                                                 \
+                READ_SLEB128(tmpsval, cur_inst);                                                                \
+                SET_STATUS(unwind_constr, tmpreg, REGSTATUS_offset);                                            \
+                unwind_constr.table[tmpreg] = tmpsval * data_alignment_factor;                                  \
+                break;                                                                                          \
+            case 0b010100: /* DW_CFA_val_offset            0   0x14      ULEB128 register  ULEB128 offset   */  \
+                READ_ULEB128(tmpreg, cur_inst);                                                                 \
+                READ_ULEB128(tmpuval, cur_inst);                                                                \
+                SET_STATUS(unwind_constr, tmpreg, REGSTATUS_val_offset);                                        \
+                unwind_constr.table[tmpreg] = (int64_t)tmpuval * data_alignment_factor;                         \
+                break;                                                                                          \
+            case 0b010101: /* DW_CFA_val_offset_sf         0   0x15      ULEB128 register  SLEB128 offset   */  \
+                READ_ULEB128(tmpreg, cur_inst);                                                                 \
+                READ_SLEB128(tmpsval, cur_inst);                                                                \
+                SET_STATUS(unwind_constr, tmpreg, REGSTATUS_val_offset);                                        \
+                unwind_constr.table[tmpreg] = tmpsval * data_alignment_factor;                                  \
+                break;                                                                                          \
+            case 0b001001: /* DW_CFA_register              0   0x09      ULEB128 register  ULEB128 register */  \
+                READ_ULEB128(tmpreg, cur_inst);                                                                 \
+                READ_ULEB128(tmpuval, cur_inst);                                                                \
+                SET_STATUS(unwind_constr, tmpreg, REGSTATUS_register);                                          \
+                unwind_constr.table[tmpreg] = tmpuval;                                                          \
+                break;                                                                                          \
+            /* DW_CFA_expression */ \
+            /* DW_CFA_val_expression */ \
+            case 0b000110: /* DW_CFA_restore_extended      0   0x06      ULEB128 register                   */  \
+                READ_ULEB128(tmpreg, cur_inst);                                                                 \
+                RESTORE_REG(tmpreg)                                                                             \
+                break;                                                                                          \
+            \
+            case 0b001010: /* DW_CFA_remember_state        0   0x0a                                         */  \
+                memcpy(table_stack[curstacksize], unwind_constr.table, tablelen);                               \
+                ++curstacksize;                                                                                 \
+                break;                                                                                          \
+            case 0b001011: /* DW_CFA_restore_state         0   0x0b                                         */  \
+                --curstacksize;                                                                                 \
+                memcpy(unwind_constr.table, table_stack[curstacksize], tablelen);                               \
+                break;                                                                                          \
+            \
+            case 0b000000: /* DW_CFA_nop                   0   0                                            */  \
+                break;                                                                                          \
+            default:                                                                                            \
+                /* Contains undefined, user and expression CFIs */                                              \
+                printf_log(LOG_DEBUG, "Unknown CFI 0x%02X\n", inst);                                             \
+                FAILED                                                                                          \
+            }                                                                                                   \
+            break;                                                                                              \
+        }
+#define RESTORE_REG(reg) \
+    printf_log(LOG_DEBUG, "Trying to restore register 0x%02lX while in the intial CFIs\n", (uint64_t)reg); \
+    FAILED
+#define FAILED \
+    free(unwind_constr.statuses);                                       \
+    free(unwind_constr.table);                                          \
+    for (uint64_t i = 0; i < maxstacksize; ++i) free(table_stack[i]);   \
+    free(table_stack);                                                  \
+    *success = 0; return 0;
+                while (cur_inst < endCIEinstr) {
+                    unsigned char inst; READ_U1(inst, cur_inst);
+                    // printf_log(LOG_NONE, "Executing pre 0x%02X\n", inst);
+                    PARSE_INST
+                }
+#undef FAILED
+#undef RESTORE_REG
+                uint64_t *init_table = (uint64_t*)malloc(tablelen);
+                memcpy(init_table, unwind_constr.table, tablelen);
+                uint8_t *init_statuses = (uint8_t*)malloc(statuseslen);
+                memcpy(init_statuses, unwind_constr.statuses, statuseslen);
+                cur_inst = cur_addr;
+#define RESTORE_REG(reg) \
+    unwind_constr.table[reg] = init_table[reg]; \
+    SET_STATUS(unwind_constr, (reg), ((init_statuses[(reg) >> 1] >> (((reg) & 1) << 2)) & 0xF));
+#define FAILED \
+    free(init_statuses);                                                \
+    free(init_table);                                                   \
+    free(unwind_constr.statuses);                                       \
+    free(unwind_constr.table);                                          \
+    for (uint64_t i = 0; i < maxstacksize; ++i) free(table_stack[i]);   \
+    free(table_stack);                                                  \
+    *success = 0; return 0;
+                while ((cur_inst < next_addr) && (cur_pointed_addr <= addr)) {
+                    unsigned char inst; READ_U1(inst, cur_inst);
+                    // printf_log(LOG_NONE, "Executing post 0x%02X\n", inst);
+                    PARSE_INST
+                }
+#undef FAILED
+#undef RESTORE_REG
+#undef PARSE_INST
+                free(init_statuses);
+                free(init_table);
+                for (int i = 0; i < maxstacksize; ++i) {
+                    free(table_stack[i]);
+                }
+                free(table_stack);
+
+                dwarf_unwind_t new_unwind;
+                new_unwind.reg_count = unwind_constr.reg_count;
+                new_unwind.regs = calloc(unwind_constr.reg_count, sizeof(uint64_t));
+                uintptr_t cfa = unwind->regs[cfa_reg];
+                if (cfa_signed) cfa += cfa_offset.soff;
+                else            cfa += cfa_offset.uoff;
+                
+                // printf_log(LOG_NONE, "Done, rewriting registers (CFA at r%d(0x%016lX) + %lld (%c) -> 0x%016lX\n", cfa_reg, unwind->regs[cfa_reg], cfa_offset.soff, cfa_signed ? 's' : 'u', cfa);
+                for (uint64_t i = 0; i < unwind_constr.reg_count; ++i) {
+                    switch (GET_STATUS(unwind_constr, i)) {
+                    case REGSTATUS_undefined:
+                        // printf_log(LOG_NONE, "Register %02lX: (undefined)\n", i);
+                        break;
+                    case REGSTATUS_same_val:
+                        if (i >= unwind->reg_count) {
+                            printf_log(LOG_DEBUG, "Invalid register status (value copied from register 0x%02lX)\n", i);
+                            free(unwind_constr.statuses);
+                            free(unwind_constr.table);
+                            free(new_unwind.regs);
+                            *success = 0;
+                            return 0;
+                        }
+                        new_unwind.regs[i] = unwind->regs[i];
+                        // printf_log(LOG_NONE, "Register %02lX: copy   %016lX\n", i, new_unwind.regs[i]);
+                        break;
+                    case REGSTATUS_offset:
+                        new_unwind.regs[i] = *(uint64_t*)(cfa + (int64_t)unwind_constr.table[i]);
+                        // printf_log(LOG_NONE, "Register %02lX: offset %016lX [%016lX + %lld]\n", i, new_unwind.regs[i], cfa, (int64_t)unwind_constr.table[i]);
+                        break;
+                    case REGSTATUS_val_offset:
+                        new_unwind.regs[i] = (uint64_t)(cfa + (int64_t)unwind_constr.table[i]);
+                        // printf_log(LOG_NONE, "Register %02lX: voff   %016lX\n", i, new_unwind.regs[i]);
+                        break;
+                    case REGSTATUS_register:
+                        if (unwind_constr.table[i] >= unwind->reg_count) {
+                            printf_log(LOG_DEBUG, "Invalid register status (value copied from register 0x%02lX)\n", unwind_constr.table[i]);
+                            free(unwind_constr.statuses);
+                            free(unwind_constr.table);
+                            free(new_unwind.regs);
+                            *success = 0;
+                            return 0;
+                        }
+                        new_unwind.regs[i] = unwind->regs[unwind_constr.table[i]];
+                        // printf_log(LOG_NONE, "Register %02lX: reg    %016lX\n", i, new_unwind.regs[i]);
+                        break;
+                    }
+                }
+                *success = (GET_STATUS(unwind_constr, return_addr_reg) == REGSTATUS_undefined) ? 0 : ((aug_fields & AUG_SIGHDLER) ? 2 : 1);
+                free(unwind_constr.statuses);
+                free(unwind_constr.table);
+                
+                free(unwind->regs);
+                unwind->reg_count = new_unwind.reg_count;
+                unwind->regs = new_unwind.regs;
+
+                // Maybe?
+                unwind->regs[cfa_reg] = cfa;
+
+                // printf_log(LOG_NONE, "Returning %016lX\n", unwind->regs[return_addr_reg]);
+                return unwind->regs[return_addr_reg];
+            }
+        } else {
+            // printf_log(LOG_NONE, "[FDE] Ptr             %02X\n", cie_ptr);
+            printf_log(LOG_DEBUG, "Unexpected FDE, corresponding CIE missing\n");
+            return 0;
+        }
+
+        cur_addr = next_addr;
+    }
+    *success = 0;
+    return 0;
+}
+
+dwarf_unwind_t *init_dwarf_unwind_registers(x64emu_t *emu) {
+    dwarf_unwind_t *unwind_struct = (dwarf_unwind_t*)malloc(sizeof(dwarf_unwind_t));
+    unwind_struct->reg_count = 17;
+    unwind_struct->regs = (uint64_t*)malloc(17*sizeof(uint64_t));
+    /* x86_64-abi-0.99.pdf
+     * Register Name                    | Number | Abbreviation
+     * General Purpose Register RAX     | 0      | %rax
+     * General Purpose Register RDX     | 1      | %rdx
+     * General Purpose Register RCX     | 2      | %rcx
+     * General Purpose Register RBX     | 3      | %rbx
+     * General Purpose Register RSI     | 4      | %rsi
+     * General Purpose Register RDI     | 5      | %rdi
+     * Frame Pointer Register   RBP     | 6      | %rbp
+     * Stack Pointer Register   RSP     | 7      | %rsp
+     * Extended Integer Registers 8-15  | 8-15   | %r8-%r15
+     * Return Address RA                | 16     |
+     * Vector Registers 0-7             | 17-24  | %xmm0-%xmm7
+     * Extended Vector Registers 8-15   | 25-32  | %xmm8-%xmm15
+     * Floating Point Registers 0-7     | 33-40  | %st0-%st7
+     * MMX Registers 0-7                | 41-48  | %mm0-%mm7
+     * Flag Register                    | 49     | %rFLAGS
+     * Segment Register ES              | 50     | %es
+     * Segment Register CS              | 51     | %cs
+     * Segment Register SS              | 52     | %ss
+     * Segment Register DS              | 53     | %ds
+     * Segment Register FS              | 54     | %fs
+     * Segment Register GS              | 55     | %gs
+     * Reserved                         | 56-57  |
+     * FS Base address                  | 58     | %fs.base
+     * GS Base address                  | 59     | %gs.base
+     * Reserved                         | 60-61  |
+     * Task Register                    | 62     | %tr
+     * LDT Register                     | 63     | %ldtr
+     * 128-bit Media Control and Status | 64     | %mxcsr
+     * x87 Control Word                 | 65     | %fcw
+     * x87 Status Word                  | 66     | %fsw
+     */
+    unwind_struct->regs[ 0] = emu->regs[_RAX].q[0];
+    unwind_struct->regs[ 1] = emu->regs[_RDX].q[0];
+    unwind_struct->regs[ 2] = emu->regs[_RCX].q[0];
+    unwind_struct->regs[ 3] = emu->regs[_RBX].q[0];
+    unwind_struct->regs[ 4] = emu->regs[_RSI].q[0];
+    unwind_struct->regs[ 5] = emu->regs[_RDI].q[0];
+    unwind_struct->regs[ 6] = emu->regs[_RBP].q[0];
+    unwind_struct->regs[ 7] = emu->regs[_RSP].q[0] + 8;
+    unwind_struct->regs[ 8] = emu->regs[_R8 ].q[0];
+    unwind_struct->regs[ 9] = emu->regs[_R9 ].q[0];
+    unwind_struct->regs[10] = emu->regs[_R10].q[0];
+    unwind_struct->regs[11] = emu->regs[_R11].q[0];
+    unwind_struct->regs[12] = emu->regs[_R12].q[0];
+    unwind_struct->regs[13] = emu->regs[_R13].q[0];
+    unwind_struct->regs[14] = emu->regs[_R14].q[0];
+    unwind_struct->regs[15] = emu->regs[_R15].q[0];
+    unwind_struct->regs[16] = emu->ip.q[0];
+    return unwind_struct;
+}
+
+void free_dwarf_unwind_registers(dwarf_unwind_t **unwind_struct) {
+    free((*unwind_struct)->regs);
+    free(*unwind_struct);
+    *unwind_struct = NULL;
+}
diff --git a/src/elfs/elfdwarf_private.h b/src/elfs/elfdwarf_private.h
new file mode 100644
index 00000000..2a1c39b0
--- /dev/null
+++ b/src/elfs/elfdwarf_private.h
@@ -0,0 +1,19 @@
+#ifndef __ELFDWARF_PRIVATE_H_
+#define __ELFDWARF_PRIVATE_H_
+
+#include "emu/x64emu_private.h"
+
+typedef struct dwarf_unwind_s {
+    uint8_t reg_count;
+    uint64_t *regs;
+} dwarf_unwind_t;
+typedef struct elfheader_s elfheader_t;
+
+dwarf_unwind_t *init_dwarf_unwind_registers(x64emu_t *emu);
+void free_dwarf_unwind_registers(dwarf_unwind_t **unwind_struct);
+
+// Returns the callee's address on success or NULL on failure (may be NULL regardless).
+// If success equals 2, the frame is a signal frame.
+uintptr_t get_parent_registers(dwarf_unwind_t *emu, const elfheader_t *ehdr, uintptr_t addr, char *success);
+
+#endif // __ELFDWARF_PRIVATE_H_
diff --git a/src/elfs/elfloader_private.h b/src/elfs/elfloader_private.h
index 18671d75..97319305 100755
--- a/src/elfs/elfloader_private.h
+++ b/src/elfs/elfloader_private.h
@@ -9,6 +9,8 @@ typedef struct library_s library_t;
 typedef struct needed_libs_s needed_libs_t;
 
 #include <pthread.h>
+#include <elf.h>
+#include "elfloader.h"
 
 struct elfheader_s {
     char*       name;
@@ -66,6 +68,9 @@ struct elfheader_s {
     uintptr_t   plt_end;
     uintptr_t   text;
     size_t      textsz;
+    uintptr_t   ehframe;
+    uintptr_t   ehframe_end;
+    uintptr_t   ehframehdr;
 
     uintptr_t   paddr;
     uintptr_t   vaddr;
diff --git a/src/elfs/elfparser.c b/src/elfs/elfparser.c
index 07599df6..c92d7e4c 100755
--- a/src/elfs/elfparser.c
+++ b/src/elfs/elfparser.c
@@ -349,6 +349,17 @@ elfheader_t* ParseElfHeader(FILE* f, const char* name, int exec)
             h->textsz = h->SHEntries[ii].sh_size;
             printf_log(LOG_DEBUG, "The .text is at address %p, and is %zu big\n", (void*)h->text, h->textsz);
         }
+        ii = FindSection(h->SHEntries, h->numSHEntries, h->SHStrTab, ".eh_frame");
+        if(ii) {
+            h->ehframe = (uintptr_t)(h->SHEntries[ii].sh_addr);
+            h->ehframe_end = h->ehframe + h->SHEntries[ii].sh_size;
+            printf_log(LOG_DEBUG, "The .eh_frame section is at address %p..%p\n", (void*)h->ehframe, (void*)h->ehframe_end);
+        }
+        ii = FindSection(h->SHEntries, h->numSHEntries, h->SHStrTab, ".eh_frame_hdr");
+        if(ii) {
+            h->ehframehdr = (uintptr_t)(h->SHEntries[ii].sh_addr);
+            printf_log(LOG_DEBUG, "The .eh_frame_hdr section is at address %p\n", (void*)h->ehframehdr);
+        }
 
         LoadNamedSection(f, h->SHEntries, h->numSHEntries, h->SHStrTab, ".dynstr", "DynSym Strings", SHT_STRTAB, (void**)&h->DynStr, NULL);
         LoadNamedSection(f, h->SHEntries, h->numSHEntries, h->SHStrTab, ".dynsym", "DynSym", SHT_DYNSYM, (void**)&h->DynSym, &h->numDynSym);
diff --git a/src/emu/x64emu.c b/src/emu/x64emu.c
index 907c0acc..d27fb5fd 100755
--- a/src/emu/x64emu.c
+++ b/src/emu/x64emu.c
@@ -459,6 +459,8 @@ void EmuCall(x64emu_t* emu, uintptr_t addr)
     uint64_t old_rsi = R_RSI;
     uint64_t old_rbp = R_RBP;
     uint64_t old_rip = R_RIP;
+    Push64(emu, GetRBP(emu));   // set frame pointer
+    SetRBP(emu, GetRSP(emu));   // save RSP
     PushExit(emu);
     R_RIP = addr;
     emu->df = d_none;
diff --git a/src/libtools/threads.c b/src/libtools/threads.c
index 8f94692a..f16a7b08 100755
--- a/src/libtools/threads.c
+++ b/src/libtools/threads.c
@@ -234,9 +234,10 @@ static void* pthread_routine(void* p)
 	et->emu->type = EMUTYPE_MAIN;
 	// setup callstack and run...
 	x64emu_t* emu = et->emu;
-	Push64(emu, 0);		// PUSH BP
+	Push64(emu, 0);	// PUSH 0 (backtrace marker: return address is 0)
+	Push64(emu, 0);	// PUSH BP
 	R_RBP = R_RSP;	// MOV BP, SP
-	R_RSP -= 56;	// Gard zone
+	R_RSP -= 56;	// Guard zone
 	PushExit(emu);
 	R_RIP = et->fnc;
 	R_RDI = (uintptr_t)et->arg;
diff --git a/src/wrapped/wrappedlibc.c b/src/wrapped/wrappedlibc.c
index 81726f3f..2537a68b 100755
--- a/src/wrapped/wrappedlibc.c
+++ b/src/wrapped/wrappedlibc.c
@@ -2502,35 +2502,34 @@ EXPORT int my_semctl(int semid, int semnum, int cmd, union semun b)
 }
 
 // Backtrace stuff
-typedef struct i386_layout_s
-{
-  struct i386_layout_s  *next;
-  void                  *return_address;
-} i386_layout_t;
 
+#include "elfs/elfdwarf_private.h"
 EXPORT int my_backtrace(x64emu_t* emu, void** buffer, int size)
 {
-    // Get current Framepointer
-    i386_layout_t *fp = (i386_layout_t*)R_RBP;
-    if((uintptr_t)fp == (uintptr_t)buffer)  // cannot find the FramePointer if it's not set in BP properly
-        return 0;
-    uintptr_t stack_end = (uintptr_t)(emu->init_stack) + emu->size_stack;
-    uintptr_t stack_start = (uintptr_t)(emu->init_stack);
-    // check if fp is on another stack (in case of beeing call from a signal with altstack)
-    x64emu_t *thread_emu = thread_get_emu();
-    if(emu!=thread_emu && (((uintptr_t)fp>(uintptr_t)(thread_emu->init_stack)) && ((uintptr_t)fp<((uintptr_t)(thread_emu->init_stack) + thread_emu->size_stack)))) {
-        stack_end = (uintptr_t)(thread_emu->init_stack) + thread_emu->size_stack;
-        stack_start = (uintptr_t)(thread_emu->init_stack);        
-    }
-    int idx=0;
-    while(idx<size) {
-        if(!(uintptr_t)fp || ((uintptr_t)fp+sizeof(void*)>=stack_end) || ((uintptr_t)fp<=stack_start)) {
-            return idx;
-        }
-        buffer[idx] = fp->return_address;
-        fp = fp->next;
-        ++idx;
+    if (!size) return 0;
+    dwarf_unwind_t *unwind = init_dwarf_unwind_registers(emu);
+    int idx = 0;
+    char success = 0;
+    uintptr_t addr = *(uintptr_t*)R_RSP;
+    buffer[0] = (void*)addr;
+    while (++idx < size) {
+        uintptr_t ret_addr = get_parent_registers(unwind, FindElfAddress(my_context, addr), addr, &success);
+        if (ret_addr == (uintptr_t)GetExit()) {
+            // TODO: do something to be able to get the function name
+            buffer[idx] = (void*)ret_addr;
+            success = 2;
+            // See elfdwarf_private.c for the register mapping
+            unwind->regs[6] = unwind->regs[7]; // mov rsp, rbp
+            unwind->regs[7] = *(uint64_t*)unwind->regs[6]; // pop rbp
+            unwind->regs[6] += 8;
+            ret_addr = *(uint64_t*)unwind->regs[6]; // ret
+            unwind->regs[6] += 8;
+            if (++idx < size) buffer[idx] = (void*)ret_addr;
+        } else if (!success) break;
+        else buffer[idx] = (void*)ret_addr;
+        addr = ret_addr;
     }
+    free_dwarf_unwind_registers(&unwind);
     return idx;
 }
 
@@ -2538,16 +2537,21 @@ EXPORT char** my_backtrace_symbols(x64emu_t* emu, uintptr_t* buffer, int size)
 {
     (void)emu;
     char** ret = (char**)calloc(1, size*sizeof(char*) + size*100);  // capping each strings to 100 chars
-    char* s = (char*)(ret+size*sizeof(char*));
+    char* s = (char*)(ret+size);
     for (int i=0; i<size; ++i) {
         uintptr_t start = 0;
         uint64_t sz = 0;
-        const char* symbname = FindNearestSymbolName(FindElfAddress(my_context, buffer[i]), (void*)buffer[i], &start, &sz);
-        if(symbname && buffer[i]>=start && (buffer[i]<(start+sz) || !sz))
-            snprintf(s, 100, "%s+%ld [%p]\n", symbname, buffer[i] - start, (void*)buffer[i]);
-        else 
-            snprintf(s, 100, "??? [%p]\n", (void*)buffer[i]);
-        s+=100;
+        elfheader_t *hdr = FindElfAddress(my_context, buffer[i]);
+        const char* symbname = FindNearestSymbolName(hdr, (void*)buffer[i], &start, &sz);
+        if (symbname && buffer[i]>=start && (buffer[i]<(start+sz) || !sz)) {
+            snprintf(s, 100, "%s(%s+%lx) [%p]", ElfName(hdr), symbname, buffer[i] - start, (void*)buffer[i]);
+        } else if (hdr) {
+            snprintf(s, 100, "%s+%lx [%p]", ElfName(hdr), buffer[i] - (uintptr_t)GetBaseAddress(hdr), (void*)buffer[i]);
+        } else {
+            snprintf(s, 100, "??? [%p]", (void*)buffer[i]);
+        }
+        ret[i] = s;
+        s += 100;
     }
     return ret;
 }