Tool to generate FDE and CIE tables using libdwarf
authorSimon Hausmann <simon.hausmann@digia.com>
Fri, 22 Feb 2013 16:15:58 +0000 (17:15 +0100)
committerSimon Hausmann <simon.hausmann@digia.com>
Wed, 27 Feb 2013 15:56:22 +0000 (16:56 +0100)
This makes it easier to generate the right magic bits and bytes
across different architectures.

Change-Id: I83cf8f348f4ea92febfe463e1ffd627808e1bb44
Reviewed-by: Erik Verbruggen <erik.verbruggen@digia.com>
.gitignore
tools/fdegen/fdegen.pro [new file with mode: 0644]
tools/fdegen/main.cpp [new file with mode: 0644]

index 5a6d2ed..a592d28 100644 (file)
@@ -15,3 +15,4 @@ mkspecs
 src/v4/Qt5V4_resource.rc
 src/v4/Qt5V4d_resource.rc
 *.pdb
+tools/fdegen/fdegen
diff --git a/tools/fdegen/fdegen.pro b/tools/fdegen/fdegen.pro
new file mode 100644 (file)
index 0000000..a525332
--- /dev/null
@@ -0,0 +1,8 @@
+TEMPLATE = app
+TARGET = fdegen
+INCLUDEPATH += .
+
+# Input
+SOURCES += main.cpp
+
+LIBS += -ldwarf -lelf
diff --git a/tools/fdegen/main.cpp b/tools/fdegen/main.cpp
new file mode 100644 (file)
index 0000000..313be40
--- /dev/null
@@ -0,0 +1,372 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <libdwarf.h>
+#include <dwarf.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#define DEBUG
+
+#ifdef DEBUG
+#include <libelf.h>
+#endif
+
+#include <qglobal.h>
+
+enum DwarfRegs {
+#if defined(Q_PROCESSOR_X86_64)
+    // X86-64
+    RAX = 0,
+    RDX = 1,
+    RCX = 2,
+    RBX = 3,
+    RSI = 4,
+    RDI = 5,
+    RBP = 6,
+    RSP = 7,
+    R8 = 8,
+    R9 = 9,
+    R10 = 10,
+    R11 = 11,
+    R12 = 12,
+    R13 = 13,
+    R14 = 14,
+    R15 = 15,
+    RIP = 16,
+
+    InstructionPointerRegister = RIP,
+    StackPointerRegister = RSP,
+    StackFrameRegister = RBP
+#elif defined(Q_PROCESSOR_X86)
+    // x86
+    EAX = 0,
+    EDX = 1,
+    ECX = 2,
+    EBX = 3,
+    ESI = 4,
+    EDI = 5,
+    EBP = 6,
+    ESP = 7,
+    EIP = 8,
+
+    InstructionPointerRegister = EIP,
+    StackPointerRegister = ESP,
+    StackFrameRegister = EBP
+#else
+#error Not ported yet
+#endif
+};
+
+static const DwarfRegs calleeSavedRegisters[] = {
+#if defined(Q_PROCESSOR_X86_64)
+    R12,
+    R14
+#endif
+};
+static const int calleeSavedRegisterCount = sizeof(calleeSavedRegisters) / sizeof(calleeSavedRegisters[0]);
+
+#if QT_POINTER_SIZE == 8
+#define Elf_Ehdr Elf64_Ehdr
+#define elf_newehdr elf64_newehdr
+#define Elf_Shdr Elf64_Shdr
+#define elf_getshdr elf64_getshdr
+#else
+#define Elf_Ehdr Elf32_Ehdr
+#define elf_newehdr elf32_newehdr
+#define Elf_Shdr Elf32_Shdr
+#define elf_getshdr elf32_getshdr
+#endif
+
+static void die(const char *msg)
+{
+    fprintf(stderr, "error: %s\n", msg);
+    exit(1);
+}
+
+static int createSectionCallback(
+        char *name,
+        int size,
+        Dwarf_Unsigned  /*type*/,
+        Dwarf_Unsigned  /*flags*/,
+        Dwarf_Unsigned  /*link*/,
+        Dwarf_Unsigned  /*info*/,
+        Dwarf_Unsigned* /*sect_name_index*/,
+        void *          /*user_data*/,
+        int*            /*error*/)
+{
+    if (strcmp(name, ".debug_frame"))
+        return 0;
+    fprintf(stderr, "createsection called with %s and size %d\n", name, size);
+    return 1;
+}
+
+static unsigned char cie_init_instructions[] = {
+    DW_CFA_def_cfa, StackPointerRegister, /*offset in bytes */QT_POINTER_SIZE,
+    DW_CFA_offset | InstructionPointerRegister, 1,
+};
+
+int main()
+{
+    Dwarf_Error error = 0;
+    Dwarf_P_Debug dw = dwarf_producer_init_c(DW_DLC_WRITE | DW_DLC_SIZE_64,
+                                             createSectionCallback,
+                                             /* error handler */0,
+                                             /* error arg */0,
+                                             /* user data */0,
+                                             &error);
+    if (error != 0)
+        die("dwarf_producer_init_c failed");
+
+    Dwarf_Unsigned cie = dwarf_add_frame_cie(dw,
+                                             "",
+                                             /* code alignment factor */QT_POINTER_SIZE,
+                                             /* data alignment factor */-QT_POINTER_SIZE,
+                                             /* return address reg*/InstructionPointerRegister,
+                                             cie_init_instructions,
+                                             sizeof(cie_init_instructions),
+                                             &error);
+    if (error != 0)
+        die("dwarf_add_frame_cie failed");
+
+    Dwarf_P_Fde fde = dwarf_new_fde(dw, &error);
+    if (error != 0)
+        die("dwarf_new_fde failed");
+
+    /* New entry in state machine for code offset 1 after push rbp instruction */
+    dwarf_add_fde_inst(fde,
+                       DW_CFA_advance_loc,
+                       /*offset in code alignment units*/ 1,
+                       /* unused*/ 0,
+                       &error);
+
+    /* After "push rbp" the offset to the CFA is now 2 instead of 1 */
+    dwarf_add_fde_inst(fde,
+                       DW_CFA_def_cfa_offset_sf,
+                       /*offset in code alignment units*/ -2,
+                       /* unused*/ 0,
+                       &error);
+
+    /* After "push rbp" the value of rbp is now stored at offset 1 from CFA */
+    dwarf_add_fde_inst(fde,
+                       DW_CFA_offset,
+                       StackFrameRegister,
+                       2,
+                       &error);
+
+    /* New entry in state machine for code offset 3 for mov rbp, rsp instruction */
+    dwarf_add_fde_inst(fde,
+                       DW_CFA_advance_loc,
+                       /*offset in code alignment units*/ 3,
+                       /* unused */ 0,
+                       &error);
+
+    /* After "mov rbp, rsp" the cfa is reachable via rbp */
+    dwarf_add_fde_inst(fde,
+                       DW_CFA_def_cfa_register,
+                       StackFrameRegister,
+                       /* unused */0,
+                       &error);
+
+    /* Callee saved registers */
+    for (int i = 0; i < calleeSavedRegisterCount; ++i) {
+        dwarf_add_fde_inst(fde,
+                           DW_CFA_offset,
+                           calleeSavedRegisters[i],
+                           i + 3,
+                           &error);
+    }
+
+    dwarf_add_frame_fde(dw, fde,
+                        /* die */0,
+                        cie,
+                        /*virt addr */0,
+                        /* length of code */0,
+                        /* symbol index */0,
+                        &error);
+    if (error != 0)
+        die("dwarf_add_frame_fde failed");
+
+    dwarf_transform_to_disk_form(dw, &error);
+    if (error != 0)
+        die("dwarf_transform_to_disk_form failed");
+
+    Dwarf_Unsigned len = 0;
+    Dwarf_Signed elfIdx = 0;
+    unsigned char *bytes = (unsigned char *)dwarf_get_section_bytes(dw, /* section */1,
+                                              &elfIdx, &len, &error);
+    if (error != 0)
+        die("dwarf_get_section_bytes failed");
+
+    // libdwarf doesn't add a terminating FDE with zero length, so let's add one
+    // ourselves.
+    unsigned char *newBytes = (unsigned char *)alloca(len + 4);
+    memcpy(newBytes, bytes, len);
+    newBytes[len] = 0;
+    newBytes[len + 1] = 0;
+    newBytes[len + 2] = 0;
+    newBytes[len + 3] = 0;
+    newBytes[len + 4] = 0;
+    bytes = newBytes;
+    len += 4;
+
+    // Reset CIE-ID back to 0 as expected for .eh_frames
+    bytes[4] = 0;
+    bytes[5] = 0;
+    bytes[6] = 0;
+    bytes[7] = 0;
+
+    unsigned fde_offset = bytes[0] + 4;
+
+    bytes[fde_offset + 4] = fde_offset + 4;
+
+    printf("static const unsigned char cie_fde_data[] = {\n");
+    int i = 0;
+    while (i < len) {
+        printf("    ");
+        for (int j = 0; i < len && j < 8; ++j, ++i)
+            printf("0x%x, ", bytes[i]);
+        printf("\n");
+    }
+    printf("};\n");
+
+    printf("static const int fde_offset = %d;\n", fde_offset);
+    printf("static const int initial_location_offset = %d;\n", fde_offset + 8);
+    printf("static const int address_range_offset = %d;\n", fde_offset + 8 + QT_POINTER_SIZE);
+
+#ifdef DEBUG
+    {
+        if (elf_version(EV_CURRENT) == EV_NONE)
+            die("wrong elf version");
+        int fd = open("debug.o", O_WRONLY | O_CREAT, 0777);
+        if (fd < 0)
+            die("cannot create debug.o");
+
+        Elf *e = elf_begin(fd, ELF_C_WRITE, 0);
+        if (!e)
+            die("elf_begin failed");
+
+        Elf_Ehdr *ehdr = elf_newehdr(e);
+        if (!ehdr)
+            die(elf_errmsg(-1));
+
+        ehdr->e_ident[EI_DATA] = ELFDATA2LSB;
+#if defined(Q_PROCESSOR_X86_64)
+        ehdr->e_machine = EM_X86_64;
+#elif defined(Q_PROCESSOR_X86)
+        ehdr->e_machine = EM_386;
+#else
+#error port me :)
+#endif
+        ehdr->e_type = ET_EXEC;
+        ehdr->e_version = EV_CURRENT;
+
+        Elf_Scn *section = elf_newscn(e);
+        if (!section)
+            die("elf_newscn failed");
+
+        Elf_Data *data = elf_newdata(section);
+        if (!data)
+            die(elf_errmsg(-1));
+        data->d_align = 4;
+        data->d_off = 0;
+        data->d_buf = bytes;
+        data->d_size = len;
+        data->d_type = ELF_T_BYTE;
+        data->d_version = EV_CURRENT;
+
+        Elf_Shdr *shdr = elf_getshdr(section);
+        if (!shdr)
+            die(elf_errmsg(-1));
+
+        shdr->sh_name = 1;
+        shdr->sh_type = SHT_PROGBITS;
+        shdr->sh_entsize = 0;
+
+        char stringTable[] = {
+            0,
+            '.', 'e', 'h', '_', 'f', 'r', 'a', 'm', 'e', 0,
+            '.', 's', 'h', 's', 't', 'r', 't', 'a', 'b', 0
+        };
+
+        section = elf_newscn(e);
+        if (!section)
+            die("elf_newscn failed");
+
+        data = elf_newdata(section);
+        if (!data)
+            die(elf_errmsg(-1));
+        data->d_align = 1;
+        data->d_off = 0;
+        data->d_buf = stringTable;
+        data->d_size = sizeof(stringTable);
+        data->d_type = ELF_T_BYTE;
+        data->d_version = EV_CURRENT;
+
+        shdr = elf_getshdr(section);
+        if (!shdr)
+            die(elf_errmsg(-1));
+
+        shdr->sh_name = 11;
+        shdr->sh_type = SHT_STRTAB;
+        shdr->sh_flags = SHF_STRINGS | SHF_ALLOC;
+        shdr->sh_entsize = 0;
+
+        ehdr->e_shstrndx = elf_ndxscn(section);
+
+        if (elf_update(e, ELF_C_WRITE) < 0)
+            die(elf_errmsg(-1));
+
+        elf_end(e);
+        close(fd);
+    }
+#endif
+
+    dwarf_producer_finish(dw, &error);
+    if (error != 0)
+        die("dwarf_producer_finish failed");
+    return 0;
+}