关于DWARF

在开始的地方,首先要介绍下DWARF

DWARF is a debugging file format used by many compilers and debuggers to support source level debugging. It addresses the requirements of a number of procedural languages, such as C, C++, and Fortran, and is designed to be extensible to other languages. DWARF is architecture independent and applicable to any processor or operating system. It is widely used on Unix, Linux and other operating systems, as well as in stand-alone environments.

DWARF翻译过来是侏儒的意思,类似于Windows下的COFF格式(存在于pdb文件中?),也是一种调试信息的格式,只不是它是体系结构和操作系统中立的。用gcc编译源代码时加上-g选项,就会在elf文件(一般是末尾)加上若干名为.debug.xxx的段,这里面记录的就是该elf文件的调试信息。

11:01:27 testvm:~/test # objdump -h ./with_dwarf   
./with_dwarf:     file format elf64-x86-64
Sections:
......
27 .debug_aranges 00000030  0000000000000000  0000000000000000  00000988  2**0
    CONTENTS, READONLY, DEBUGGING
28 .debug_info   00000108  0000000000000000  0000000000000000  000009b8  2**0
    CONTENTS, READONLY, DEBUGGING
29 .debug_abbrev 0000006d  0000000000000000  0000000000000000  00000ac0  2**0
    CONTENTS, READONLY, DEBUGGING
30 .debug_line   0000004d  0000000000000000  0000000000000000  00000b2d  2**0
    CONTENTS, READONLY, DEBUGGING
31 .debug_str    00000097  0000000000000000  0000000000000000  00000b7a  2**0
    CONTENTS, READONLY, DEBUGGING
32 .debug_loc    00000060  0000000000000000  0000000000000000  00000c11  2**0
    CONTENTS, READONLY, DEBUGGING

安装dwarfdump后,即可以用该命令dump出某个elf文件中调试信息段的内容。至于每个section里面到底放了什么东西,可以参考DWARF的文档

DWARF2 contains .debug_frame information for helping debuggers figure out how to unwind frames (i.e. how to restore the stack to the previous frame from any instruction executing using the current frame.

在这些section当中,有一个尤为重要,即为.debug_frame。有了.debug_frame,我们可以在执行到使用当前call stack frame的任何一条指令时,推断出上一个call stack frame的内容。x86_64的ABI默认不保存恢复stack frame pointer,所以唯有借助.debug_frame段才可以进行栈回溯。关于.debug_frame的具体构造,可以参考这里

关于libunwind

以上DWARF只是定义了应该在目标文件中存储的调试信息的格式,至于利用这些调试信息来达到堆栈展开或者栈回溯的目的,则是libunwind做的事情。当然libunwind能做的也不仅仅局限于这些,利用libunwind还可以实现non-local goto和efficient setjmp,后面会给出相应的例子。然而,当用ldd查看gdb时,发现gdb并没有依赖于libunwind,于是推测gdb应该是自己实现了这堆功能。

The primary goal of this project is to define a portable and efficient C programming interface (API) to determine the call-chain of a program. The API additionally provides the means to manipulate the preserved (callee-saved) state of each call-frame and to resume execution at any point in the call-chain (non-local goto). The API supports both local (same-process) and remote (across-process) operation. As such, the API is useful in a number of applications.

stack unwinding

利用libunwind来做栈回溯(gcc也原生提供了类似的backtrace)。

#define UNW_LOCAL_ONLY
#include <libunwind.h>

void show_backtrace (void) {
    unw_cursor_t cursor; unw_context_t uc;
    unw_word_t ip, sp;

    unw_getcontext(&uc);
    unw_init_local(&cursor, &uc);
    while (unw_step(&cursor) > 0) {
        unw_get_reg(&cursor, UNW_REG_IP, &ip);
        unw_get_reg(&cursor, UNW_REG_SP, &sp);
        printf ("ip = %lx, sp = %lx\n", (long) ip, (long) sp);
    }
}

effcient setjmp()

setjmp个人认为其实就是跨函数的goto,能实现异常处理等高级功能。

libunwind中的setjmp和longjmp实现非常奇怪,在导出的头文件中并没有包含这两个函数,在生成的so文件中倒包含了longjmp函数的实现,并且与glibc中的函数同名。在链接的时候需要-lunwind-setjmp,难道是覆盖了glibc中的实现?不需要LD_PRELOAD?

#include <stdio.h>  
#include <setjmp.h>  
   
static jmp_buf buf;  
      
void second(void) {  
    printf("second\n");         // 打印  
    longjmp(buf,1);             // 跳回setjmp的调用处 - 使得setjmp返回值为1  
}  
         
void first(void) {  
    second();  
    printf("first\n");          // 不可能执行到此行  
}  
            
int main() {     
    if ( ! setjmp(buf) ) {  
        first();                // 进入此行前,setjmp返回0  
    } else {                    // 当longjmp跳转回,setjmp返回1,因此进入此行  
        printf("main\n");       // 打印  
    }  
    return 0;  
}  

exception handing

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <setjmp.h>

void first(void);
void second(void);

/* This program's output is:
 *
 *  calling first
 *  calling second
 *  entering second
 *  second failed with type 3 exception; remapping to type 1.
 *  first failed, exception type 1
 *
 *   */

/* Use a file scoped static variable for the exception stack so we can access
 *  * it anywhere within this translation unit. */
static jmp_buf exception_env;
static int exception_type;

int main() {
    void *volatile mem_buffer;

    mem_buffer = NULL;
    if (setjmp(exception_env)) {
        /* if we get here there was an exception */
        printf("first failed, exception type %d\n", exception_type);
    } else {
        /* Run code that may signal failure via longjmp. */
        printf("calling first\n");
        first();
        mem_buffer = malloc(300); /* allocate a resource */
        printf(strcpy((char*) mem_buffer, "first succeeded!")); /* ... this will not happen */
    }
    if (mem_buffer)
        free((void*) mem_buffer); /* carefully deallocate resource */
    return 0;
}

void first(void) {
    jmp_buf my_env;

    printf("calling second\n");
    memcpy(my_env, exception_env, sizeof(jmp_buf));
    switch (setjmp(exception_env)) {
    case 3:
        /* if we get here there was an exception. */
        printf("second failed with type 3 exception; remapping to type 1.\n");
        exception_type = 1;

    default: /* fall through */
        memcpy(exception_env, my_env, sizeof(jmp_buf)); /* restore exception stack */
        longjmp(exception_env, exception_type); /* continue handling the exception */

    case 0:
        /* normal, desired operation */
        second();
        printf("second succeeded\n");  /* not reached */
    }
    memcpy(exception_env, my_env, sizeof(jmp_buf)); /* restore exception stack */
}

void second(void) {
    printf("entering second\n" ); /* reached */
    exception_type = 3;
    longjmp(exception_env, exception_type); /* declare that the program has failed */
    printf("leaving second\n"); /* not reached */
}

C++中的exception handing

这个pdf讲得非常好。说来惭愧,自己貌似把什么语言都当成c来写,c++也不例外,因此也从来没有用过exception这个特性。不过想想都头痛(exception这个feature得给编译器增加多少复杂度啊!!!),还是老老实实去用if-else和swith-case吧。。。




blog comments powered by Disqus

Published

26 November 2013

Tags