libdwarf

repository·main·Indexed 42 days ago

https://github.com/davea42/libdwarf-code

A portable, ABI-independent library providing robust access to DWARF2 through DWARF5 debugging information. It is designed to handle corrupted DWARF data without runtime crashes or memory leaks and supports reading DWARF data from disk or memory via the dwarf_object_init_b() workflow.

Tokens
14.7K
Snippets
58
Records
87
Agent score
52%

What's inside libdwarf

  1. What is libdwarf and its core goals

    main

    libdwarf provides portable, ABI-independent access to DWARF2 through DWARF5 data. Its primary goals are:

    • Data Integrity: Detecting and reporting corrupted DWARF data while avoiding runtime crashes or memory leaks.
    • Portability: Providing a consistent interface across different ABIs.
    • Future Proofing: Adding support for new standards (like DWARF6) as soon as they are released.

    Note: libdwarf reads files from disk; it does not read running programs or shared objects directly.

  2. Link against the static libdwarf.a library

    main

    When linking your own code against the static library libdwarf.a, you must define the LIBDWARF_STATIC macro in your code whenever you #include "libdwarf.h".

    #define LIBDWARF_STATIC
    #include "libdwarf.h"
  3. Naming conventions and namespaces

    main

    To avoid collisions with user code or other libraries, Libdwarf uses specific prefixes:

    • DW_: From the DWARF Standard (used in dwarf.h).
    • Dwarf_: Used for types.
    • dwarf_: Used for public functions.
    • _dwarf_: Used for non-static functions internal to the library (not for user calls).
    • dw_: Used for public function argument names (to support Doxygen).

    Other naming rules:

    • Functions: All lower-case with underscores (under_score).
    • Local Variables: All lower-case with underscores.
    • Struct Members: Should have a 2-character prefix specific to that struct followed by an underscore (e.g., ns_x in struct Dwarf_Nothing_Special_s) to facilitate grep searches.
  4. Read DWARF data directly from memory

    main

    If DWARF data is located in memory or in a custom file system that libdwarf does not natively support, use the dwarf_object_init_b() workflow. This requires implementing source functions and data for three specific interface structures to provide access to the DWARF data.

    Workflow:

    1. Initialize the object with dwarf_object_init_b().
    2. Perform your required libdwarf operations.
    3. Finalize the object with dwarf_object_finish().

    Required Interface Structures:

    • struct Dwarf_Obj_Access_Interface_a_s
    • struct Dwarf_Obj_Access_Methods_a_s
    • struct Dwarf_Obj_Access_Section_a_s

    For a complete implementation example, refer to src/bin/dwarfexample/jitreader.c in the repository.

    dwarf_object_init_b();
    // ... call libdwarf functions ...
    dwarf_object_finish();
  5. Understand the Libdwarf LHS/RHS architecture

    main

    Libdwarf is architecturally split into two conceptual sides:

    1. Left-Hand Side (LHS): Contains all the public DWARF-related API functions. The LHS is agnostic of the specific object file format.
    2. Right-Hand Side (RHS): Contains the object-file-specific logic (ELF, PE, or MacOS Mach-O). The RHS provides a set of function pointers that the LHS uses to access the object file.

    This separation allows the API functions to work on any object regardless of the ABI, and all functions automatically account for endianness. All code blocks (Elf, PE, and MacOS) are available at runtime; there is no selection required at library-build time.

  6. Set up MSYS2 for libdwarf development on Windows

    main

    To build and test libdwarf on Windows, it is recommended to use the MSYS2 environment with the Meson build system.

    1. Download and install MSYS2 from msys2.org.
    2. Use the MSYS2 MINGW64 shell (which uses gcc) or MSYS2 CLANG64 (which uses clang).
    3. Install the necessary dependencies using pacman:
    # Update system
    pacman -Suy
    
    # Install build essentials and tools
    pacman -S base-devel git autoconf automake libtool
    pacman -S mingw-w64-x86_64-python3
    pacman -S mingw-w64-x86_64-toolchain
    pacman -S mingw-w64-x86_64-zlib
    pacman -S mingw-w64-x86_64-zstd
    pacman -S mingw-w64-x86_64-doxygen
    
    # Install build systems (Meson/CMake)
    pacman -S mingw-w64-x86_64-meson
    pacman -S mingw-w64-x86_64-cmake
    pacman -S mingw-w64-x86_64-python3-pip
    
    # Install xz for distributions
    pacman -S mingw-w64-x86_64-xz
    pacman -S base-devel git autoconf automake libtool mingw-w64-x86_64-python3 mingw-w64-x86_64-toolchain mingw-w64-x86_64-zlib mingw-w64-x86_64-zstd mingw-w64-x86_64-meson mingw-w64-x86_64-cmake mingw-w64-x86_64-xz
  7. Configure shared vs static library builds

    main

    The default build type depends on the build tool used. Note that you should avoid using the -DWALL option in cmake or --enable-wall in configure, as it causes a gcc error regarding <b>I64u</b>.

    CMake

    By default, CMake builds a static library. To build a shared library, use:

    -DBUILD_SHARED=YES -DBUILD_NON_SHARED=NO

    Use -G Ninja for faster builds.

    Meson

    By default, Meson builds a shared library.

    • To build a static archive: --default-library static
    • To explicitly choose shared: --default-library shared

    Configure

    By default, configure builds a static library (note: building a static libdwarf.a via configure is not supported). To build a shared library, use:

    --enable-shared --disable-static
    # CMake shared build
    cmake -DBUILD_SHARED=YES -DBUILD_NON_SHARED=NO -G Ninja ..
    
    # Meson static build
    meson build --default-library static
    
    # Configure shared build
    ./configure --enable-shared --disable-static
  8. Refactoring deeply nested blocks

    main

    Deeply nested blocks make code hard to read and often indicate that functionality should be split into separate functions.

    Avoid Gratuitous Nesting: Instead of nesting the entire logic inside an if (pointer_is_valid) block, handle the error case early and return. This keeps the primary logic at the top level of the function.

    Bad Pattern (Gratuitous Nesting):

    foo = malloc (sizeof (foo_t));
    if (foo) {
        /* lots of code to initialize foo */
        return DW_DLV_OK;
    }
    _dwarf_error(dbg,error,DW_DLE_MALLOC_RETURNS_NULL);
    return DW_DLV_ERROR;

    Good Pattern (Early Return):

    foo = malloc (sizeof (foo_t));
    if (!foo) {
        _dwarf_error(dbg,error,DW_DLE_MALLOC_RETURNS_NULL);
        return DW_DLV_ERROR;
    }
    /* lots of code to initialize foo */
    return DW_DLV_OK;
    foo = malloc (sizeof (foo_t));
    if (!foo) {
        _dwarf_error(dbg,error,DW_DLE_MALLOC_RETURNS_NULL);
        return DW_DLV_ERROR;
    }
    /* lots of code to initialize foo */
    return DW_DLV_OK;
  9. Configure LIBDWARF_STATIC in Visual Studio

    main

    To pass the LIBDWARF_STATIC macro to the preprocessor in Visual Studio:

    1. Right-click your project in the Solution Explorer.
    2. Select Properties.
    3. Navigate to C/C++ > Preprocessor.
    4. Click on Preprocessor Definitions, click the down arrow, and select Modify.
    5. Add LIBDWARF_STATIC to the list and click OK.
  10. Rebuild name definitions with make rebuild

    main

    Library maintainers use this program to update name definitions whenever a DWARF tag (e.g., DW_TAG_subprogram), attribute (e.g., DW_AT), or other identifier is added, deleted, or changed.

    To run the process, execute the following command from the top level of the repository:

    make rebuild

    Important: After a successful rebuild, you must re-run configure on the project because critical headers may have changed.

  11. Validate libdwarf builds on MacOS

    main

    Once dependencies are installed via MacPorts, standard build systems like configure, cmake, or meson should function correctly, including make check.

    To run a simplified build process from the source directory, you can use the provided shell script. Note that the full libdwarf-regressiontests suite is available but typically not required for standard validation.

    sh scripts/allsimplebuilds.sh