getting the started

  • sudo apt-get install git gcc gawk automake libtool texlive-extra-utils
    git clone git://git.sv.gnu.org/libunwind.git
    cd libunwind
    autoreconf -i
    ./configure CFLAGS="-U_FORTIFY_SOURCE" --enable-maintainer-mode --enable-debug
    make -j`getconf _NPROCESSORS_ONLN`
    make check CFLAGS="-funwind-tables" UNW_ARM_UNWIND_METHOD=4
    make install prefix=$(pwd)/../libunwind-install

unwinding in general

unwinding on ARM

  • In case a GCC >= 4.5.x is used and the -funwind-tables option is enabled the compiler generates an ELF file containing the .ARM.exidx and .ARM.extab sections.

exception index table entry

  • The index section contains a sorted list of key-value pairs. The 'key' is a prel31 offset to the start of a function. The 'value' contains one of the following three formats:
    1. If bit 31 is zero: this is a prel31 offset of the start of the table entry for this function
    2. If bit 31 is one: this is a table entry itself
    3. If set to 0x1: the function cannot be unwound (EXIDX_CANTUNWIND)
    The index table entry consists of two words:
              one
    +-+----------------------+
    |0| prel31_offset_to_fnc |
    +-+----------------------+
    31 30                   0
                                         two
    EXIDX_CANTUNWIND           +----------------------+-+
                               |                      |1|
                               +----------------------+-+
                               31                    1 0
                                         two
    The ex table entry itself  +-+----------------------+
    encoded in 31bit           |1|     ex_tbl_entry     |
                               +-+----------------------+
                               31 30                   0
                                         two
    prel31 offset of the start +-+----------------------+
    of the table entry for     |0|   tbl_entry_offset   |
    this function              +-+----------------------+
                               31 30                   0

exception table entry

  • The format of an exception table entry has to be either the 'generic' or the 'compact' model. The compact model is used if bit 31 of the first word is set. In this case the bits 24-27 selects one of the default personality routines. Exception Table Entry could be encoded using either the compact model or the generic model.
         generic model:
    +-+----------------------+ +-----------------------
    |0|    prs_fnc_offset    | | prs_data 
    +-+----------------------+ +-----------------------
    31 30                   0
    
         compact model:
    +-+-----+-----+----------+ +-----------------------
    |1|  0  | idx | prs_data | | optional_prs_data 
    +-+-----+-----+----------+ +-----------------------
    31 30-28 27-24 23       0
    Currently there are three routines defined on ARM:

    index

    name

    description

    0

    Su16 / __aeabi_unwind_cpp_pr0

    Short frame unwinding description followed by descriptors with 16-bit scope

    1

    Lu16 / __aeabi_unwind_cpp_pr1

    Long frame unwinding description followed by descriptors with 16-bit scope

    2

    Lu32 / __aeabi_unwind_cpp_pr2

    Long frame unwinding description followed by descriptors with 32-bit scope

    The short frame unwinding routine (Su16) expects a fixes set of three unwind instructions encoded into bits 0-23. The long frame unwinding routine (Lu16) encode the number of unwind instructions in bits 16-23. The .ARM.extab section can be omitted if the "compact model" is used in conjunction with the short frame unwinding description:
    short:
    +-+-----+-----+----------+
    |1|  0  |  0  | prs_data |
    +-+-----+-----+----------+
    31 30-28 2724/ 23       0 \
                /              \
                +----+----+----+
                |insn|insn|insn|
                +----+----+----+
                23-16 15-8 7 - 0
    long:
    +-+-----+-----+----------+ +----------------------
    |1|  0  | 1or2| prs_data | |prs_data
    +-+-----+-----+----------+ +----------------------
    31 30-28 2724/ 23       0                         \
                /                                      \
                +----+----+----++----+----+----+----+---
                |  N |insn|insn||insn|insn|insn|insn|...
                +----+----+----++----+----+----+----+---
                23-16 15-8 7 - 0
    With 'N' specifying the number of additional insn

Frame unwinding instructions

  • the start value of the vsp (virtual sp) is the value of the sp that identifies the frame being unwound

    Instruction

    Mnemonic

    Explanation

    00xxxxxx

    ARM_EXIDX_CMD_DATA_POP

    vsp = vsp + (xxxxxx << 2) + 4. Covers range 0x04-0x100 inclusive

    01xxxxxx

    ARM_EXIDX_CMD_DATA_PUSH

    vsp = vsp – (xxxxxx << 2) - 4. Covers range 0x04-0x100 inclusive

    10000000 00000000

    ARM_EXIDX_CMD_REFUSED

    refuse to unwind (0x80 followed by 0x00)

    1000iiii iiiiiiii

    ARM_EXIDX_CMD_REG_POP

    Pop up to 12 integer registers under masks {r15-r12}, {r11-r4}

    1001nnnn

    ARM_EXIDX_CMD_REG_TO_SP

    Set vsp = r[nnnn]

    10100nnn

    ARM_EXIDX_CMD_REG_POP

    Pop r4-r[4+nnn]

    10101nnn

    ARM_EXIDX_CMD_REG_POP

    Pop r4-r[4+nnn], r14

    10110000

    ARM_EXIDX_CMD_FINISH

    Finish

    10110001 0000iiii

    ARM_EXIDX_CMD_REG_POP

    Pop integer registers under mask {r3, r2, r1, r0}

    10110010 uleb128

    ARM_EXIDX_CMD_DATA_POP

    vsp = vsp + 0x204 + (uleb128 << 2)

    10110011 sssscccc

    ARM_EXIDX_CMD_VFP_POP

    Pop VFP double-precision registers D[ssss]-D[ssss+cccc] saved (as if) by FSTMFDX

    10111nnn

    ARM_EXIDX_CMD_VFP_POP

    Pop VFP double-precision registers D[8]-D[8+nnn] saved (as if) by FSTMFDX

    11010nnn

    ARM_EXIDX_CMD_VFP_POP

    Pop VFP double-precision registers D[8]-D[8+nnn] saved (as if) by FSTMFDD

    11000nnn

    ARM_EXIDX_CMD_WREG_POP

    Intel Wireless MMX pop

    11000111 0000iiii

    ARM_EXIDX_CMD_WCGR_POP

    Intel Wireless MMX pop wCGR registers under mask {wCGR3,2,1,0}

    all other

    ARM_EXIDX_CMD_RESERVED

    Reserved or Spare

overhead of the ARM specific unwind-tables

  • arithmetic mean of the text size increase:

    3.50%

    geometric mean of the text size increase:

    2.86%

    arithmetic mean of the file size increase:

    2.08%

    geometric mean of the file size increase:

    1.08%

    OverheadOfTheARMspecificUnwindTables.png

  • measured with gcc version 4.5.2 (Ubuntu/Linaro 4.5.2-8ubuntu4)

    ELF binary

    text size (1)

    text size (2)

    text size increase

    file size (1)

    file size (2)

    file size increase

    firefox-4.0.1/firefox-bin

    36252

    36974

    1.99 %

    42520

    42572

    1.22 %

    firefox-4.0.1/libfreebl3.so

    286214

    290558

    1.52 %

    292200

    296400

    1.44 %

    firefox-4.0.1/libmozalloc.so

    2721

    3015

    10.8 %

    5344

    5448

    1.95 %

    firefox-4.0.1/libmozsqlite3.so

    350740

    358936

    2.34 %

    360416

    368660

    2.29 %

    firefox-4.0.1/libnspr4.so

    136920

    142504

    4.08 %

    146048

    150196

    2.84 %

    firefox-4.0.1/libnss3.so

    592533

    611641

    3.23 %

    609700

    630232

    3.37 %

    firefox-4.0.1/libnssckbi.so

    287618

    290266

    0.92 %

    339800

    344000

    1.24 %

    firefox-4.0.1/libnssdbm3.so

    76354

    79818

    4.54 %

    83284

    83388

    0.13 %

    firefox-4.0.1/libnssutil3.so

    56192

    57820

    2.9 %

    70988

    71092

    0.15 %

    firefox-4.0.1/libplc4.so

    8406

    8829

    5.03 %

    13600

    13652

    0.38 %

    firefox-4.0.1/libplds4.so

    4800

    5160

    7.5 %

    9520

    9624

    1.01 %

    firefox-4.0.1/libsmime3.so

    79997

    82857

    3.58 %

    91868

    96068

    4.57 %

    firefox-4.0.1/libsoftokn3.so

    127693

    132293

    3.6 %

    132964

    141260

    6.24 %

    firefox-4.0.1/libssl3.so

    109271

    113435

    3.81 %

    116760

    125056

    7.11 %

    firefox-4.0.1/libxpcom.so

    7549

    7715

    2.2 %

    13540

    13640

    0.74 %

    firefox-4.0.1/libxul.so

    14983671

    15756607

    5.16 %

    16385156

    17159300

    4.72 %

    python3.1

    1910044

    1938104

    1.47 %

    2256924

    2285596

    1.27 %

    python2.7

    1729833

    1757757

    1.61%

    2055072

    2083796

    1.34 %

    libunwind.so.7.0.0

    49016

    50524

    3.08 %

    69682

    69906

    0.32 %

    libunwind-arm.so.7.0.0

    55691

    56086

    0.71 %

    74749

    74877

    0.17 %

    • (1) size in bytes without -funwind-tables
    • (2) size in bytes with -funwind-tables

libunwind ARM unwind methods

  • There are several unwinding methods on ARM (see include/tdep-arm/libunwind_i.h) that can be selected using the UNW_ARM_UNWIND_METHOD environment variable.

UNW_ARM_METHOD_DWARF 0x01

  • This method uses DWARF information stored in the .debug_frame section of the ELF file. This section contains Frame Description Entries (FDEs) that can be used to unwind the stack. GCC generates the .debug_frame section if the -g option is used. This section is not loaded into memory and it is not even part of the binary on several Linux distributions (Ubuntu, Fedora) but comes with the corresponding debug package. pointers:

UNW_ARM_METHOD_FRAME 0x02

  • This method supports unwindind using the old ARM Procedure Call Standard. In case gcc -mapcs-frame option is specified the compiler generates a stack frame that is APCS compliant. This allows libunwind to follow the call chain. Since the systems targeted by Linaro are using the ARM EABI the APCS frames won't help.

UNW_ARM_METHOD_EXIDX 0x04

  • This method is using the informations provided by the .ARM.exidx and .ARM.extbl sections as mentioned above.

prologue/epilogue-parsing

  • As a fallback prologue/epilogue-parsing could be used to unwind the stack (like gdb does) but this gets tricky when compilers are optimizing.

libunwind internals

library interdependencies

  • The build process creates a couple of libraries that have interdependencies:

    attachment:libunwind-lib-deps.dot

libunwind.so

  • This one contains functionality for local (UNW_LOCAL_ONLY) unwinding only (L.*\.c files). It depends on:
    • libunwind-dwarf-local.a libunwind-elf32.a
    consists of:
    • os-linux.c
    • mi/init.c mi/flush_cache.c mi/mempool.c mi/strerror.c arm/is_fpreg.c arm/regname.c mi/backtrace.c mi/dyn-cancel.c mi/dyn-info-list.c mi/dyn-register.c mi/Ldyn-extract.c mi/Lfind_dynamic_proc_info.c mi/Lget_accessors.c mi/Lget_proc_info_by_ip.c mi/Lget_proc_name.c mi/Lput_dynamic_unwind_info.c mi/Ldestroy_addr_space.c mi/Lget_reg.c mi/Lset_reg.c mi/Lget_fpreg.c mi/Lset_fpreg.c mi/Lset_caching_policy.c
    • arm/getcontext.c arm/Lcreate_addr_space.c arm/Lget_proc_info.c arm/Lget_save_loc.c arm/Lglobal.c arm/Linit.c arm/Linit_local.c arm/Linit_remote.c arm/Lis_signal_frame.c arm/Lregs.c arm/Lresume.c arm/Lstep.c

libunwind-arm.so

  • This one contains functionality for generic (remote?) (G.*\.c files) and local unwinding. It depends on:
    • libunwind-dwarf-generic.a libunwind-elf32.a libunwind.so
    consists of:
    • src/os-linux.c
    • mi/init.c mi/flush_cache.c mi/mempool.c mi/strerror.c mi/Gdyn-extract.c mi/Gdyn-remote.c mi/Gfind_dynamic_proc_info.c mi/Gget_accessors.c mi/Gget_proc_info_by_ip.c mi/Gget_proc_name.c mi/Gput_dynamic_unwind_info.c mi/Gdestroy_addr_space.c mi/Gget_reg.c mi/Gset_reg.c mi/Gget_fpreg.c mi/Gset_fpreg.c mi/Gset_caching_policy.c
    • arm/is_fpreg.c arm/regname.c arm/Gcreate_addr_space.c arm/Gget_proc_info.c arm/Gget_save_loc.c arm/Gglobal.c arm/Ginit.c arm/Ginit_local.c arm/Ginit_remote.c arm/Gis_signal_frame.c arm/Gregs.c arm/Gresume.c arm/Gstep.c

libunwind-setjmp.so

  • This is supposed to be a drop-in replacement for the system-provided setjmp longjmp routines. It depends on:
    • libunwind-elf32.a libunwind-arm.so libunwind.so
    consists of:
    • setjmp/longjmp.c setjmp/siglongjmp.c

libunwind-dwarf-local.a

  • depends on:
    • libunwind-dwarf-common.a
    consists of:
    • dwarf/Lexpr.c dwarf/Lfde.c dwarf/Lparser.c dwarf/Lpe.c dwarf/Lstep.c dwarf/Lfind_proc_info-lsb.c

libunwind-dwarf-generic.a

  • depends on:
    • libunwind-dwarf-common.a
    consists of:
    • dwarf/Gexpr.c dwarf/Gfde.c dwarf/Gparser.c dwarf/Gpe.c dwarf/Gstep.c dwarf/Gfind_proc_info-lsb.c

libunwind-dwarf-common.a

  • consists of:
    • dwarf/global.c

libunwind-elf32.a

  • consists of:
    • elf32.c

libunwind-ptrace.a

  • Provides the _UPT_accessors for libunwind's remote interface in order to backtrace another process via ptrace. It depends on:
    • libunwind-arm.so
    consists of:
    • ptrace/_UPT_elf.c ptrace/_UPT_accessors.c ptrace/_UPT_access_fpreg.c ptrace/_UPT_access_mem.c ptrace/_UPT_access_reg.c ptrace/_UPT_create.c ptrace/_UPT_destroy.c ptrace/_UPT_find_proc_info.c ptrace/_UPT_get_dyn_info_list_addr.c ptrace/_UPT_put_unwind_info.c ptrace/_UPT_get_proc_name.c ptrace/_UPT_reg_offset.c ptrace/_UPT_resume.c

testsuite results on ARM

  • make check- results:

    • Hardware: PandaBoard A1 ES2.1

    • OS: Linaro 11.05 snapshot
      • kernel: 2.6.38-1002-linaro-omap
      • gcc: 4.5.2 (Ubuntu/Linaro 4.5.2-8ubuntu4)
      • glibc: libc6 2.13-0ubuntu13
      • libunwind: snapshot git://git.linaro.org/people/kwerner/libunwind.git
      • ARM unwind method: UNW_ARM_UNWIND_METHOD=4
    PASS: test-proc-info
    PASS: test-static-link
    PASS: test-strerror
    PASS: Gtest-bt
    PASS: Ltest-bt
    PASS: Gtest-exc
    PASS: Ltest-exc
    PASS: Gtest-init
    PASS: Ltest-init
    PASS: Gtest-concurrent
    PASS: Ltest-concurrent
    PASS: Gtest-resume-sig
    PASS: Ltest-resume-sig
    FAIL: Gtest-dyn1 (FAILURE: expected 13, not 1 frames below signal frame)
    FAIL: Ltest-dyn1 (FAILURE: expected 13, not 1 frames below signal frame)
    PASS: Gtest-trace
    PASS: Ltest-trace
    FAIL: test-async-sig (deadlocks because libunwind isn't async signal safe)
    PASS: test-flush-cache
    PASS: test-init-remote
    PASS: test-mem
    FAIL: test-setjmp
    FAIL: test-ptrace (FAILURE: unw_step() returned -1 for ip=0 (start ip=4000f6f6))
    PASS: Ltest-nomalloc
    PASS: Ltest-nocalloc
    FAIL: rs-race
    PASS: run-check-namespace
    FAIL: run-ptrace-mapper (FAILURE: unw_step() returned -1 for ip=ffffffff (start ip=40060c90))
    FAIL: run-ptrace-misc (FAILURE: unw_step() returned -1 for ip=ffffffff (start ip=40060c90))
    ===========================================
    8 of 29 tests failed

pointers


CategoryHowTo

KenWerner/Sandbox/libunwind (last modified 2012-04-16 07:29:07)