diff --git a/api/python/lief/__init__.pyi b/api/python/lief/__init__.pyi index 46d55e1c..4915649f 100644 --- a/api/python/lief/__init__.pyi +++ b/api/python/lief/__init__.pyi @@ -421,6 +421,8 @@ class Binary(Object): @property def page_size(self) -> int: ... + def load_debug_info(self, path: Union[str | os.PathLike]) -> DebugInfo: ... + def __str__(self) -> str: ... class Section(Object): @@ -544,6 +546,8 @@ class DebugInfo: @property def format(self) -> DebugInfo.FORMAT: ... +def is_pdb(file: str) -> bool: ... + @overload def is_elf(filename: str) -> bool: ... diff --git a/api/python/src/Abstract/pyBinary.cpp b/api/python/src/Abstract/pyBinary.cpp index 73618f3d..0b52681a 100644 --- a/api/python/src/Abstract/pyBinary.cpp +++ b/api/python/src/Abstract/pyBinary.cpp @@ -24,6 +24,7 @@ #include "pyErr.hpp" #include "pySafeString.hpp" #include "nanobind/extra/stl/lief_span.h" +#include "nanobind/extra/stl/pathlike.h" #include "pyIterator.hpp" #include "nanobind/utils.hpp" @@ -40,35 +41,42 @@ #include "LIEF/asm/Engine.hpp" #include "LIEF/asm/Instruction.hpp" +#include "Abstract/pyDebugInfoTyHook.hpp" + namespace LIEF::py { template<> void create(nb::module_& m) { nb::class_ pybinary(m, "Binary", - R"delim( - File format abstract representation. + R"doc( + Generic interface representing a binary executable. - This object represents the abstraction of an executable file format. - It enables to access common features (like the :attr:`~lief.Binary.entrypoint`) regardless - of the concrete format (e.g. :attr:`lief.ELF.Binary.entrypoint`) - )delim"_doc); + This class provides a unified interface across multiple binary formats + such as ELF, PE, Mach-O, and others. It enables users to access binary + components like headers, sections, symbols, relocations, + and functions in a format-agnostic way. -# define ENTRY(X) .value(to_string(Binary::VA_TYPES::X), Binary::VA_TYPES::X) - nb::enum_(pybinary, "VA_TYPES") - ENTRY(AUTO) - ENTRY(VA) - ENTRY(RVA) - ; -# undef ENTRY + Subclasses (like :class:`lief.PE.Binary`) implement format-specific API + )doc"_doc); + + nb::enum_(pybinary, "VA_TYPES", + "Enumeration of virtual address types used for patching and memory access."_doc + ) + .value("AUTO", Binary::VA_TYPES::AUTO, + "Automatically determine if the address is absolute or relative (default behavior)"_doc + ) + .value("RVA", Binary::VA_TYPES::RVA, + "Relative Virtual Address (RVA), offset from image base."_doc + ) + .value("VA", Binary::VA_TYPES::VA, + "Absolute Virtual Address." + ); -# define ENTRY(X) .value(to_string(Binary::FORMATS::X), Binary::FORMATS::X) nb::enum_(pybinary, "FORMATS") - ENTRY(UNKNOWN) - ENTRY(ELF) - ENTRY(PE) - ENTRY(MACHO) - ENTRY(OAT) - ; -# undef ENTRY + .value("UNKNOWN", Binary::FORMATS::UNKNOWN) + .value("ELF", Binary::FORMATS::ELF) + .value("PE", Binary::FORMATS::PE) + .value("MACHO", Binary::FORMATS::MACHO) + .value("OAT", Binary::FORMATS::OAT); init_ref_iterator(pybinary, "it_sections"); init_ref_iterator(pybinary, "it_symbols"); @@ -313,7 +321,7 @@ void create(nb::module_& m) { nb::type(), "instructions_it", insts); }, "address"_a, "size"_a, nb::keep_alive<0, 1>(), R"doc( - Disassemble code starting a the given virtual address and with the given + Disassemble code starting at the given virtual address and with the given size. .. code-block:: python @@ -399,6 +407,30 @@ void create(nb::module_& m) { )doc"_doc ) + .def("load_debug_info", [] (Binary& self, const nb::PathLike& pathlike) { + return self.load_debug_info(pathlike); + }, "path"_a, nb::rv_policy::reference_internal, + R"doc( + Load and associate an external debug file (e.g., DWARF or PDB) with this + binary. + + This method attempts to load the debug information from the file located + at the given path, and binds it to the current binary instance. If + successful, it returns the loaded :class:`~.DebugInfo` object. + + .. warning:: + + It is the caller's responsibility to ensure that the debug file is + compatible with the binary. Incorrect associations may lead to + inconsistent or invalid results. + + .. note:: + + This function does not verify that the debug file matches the binary's + unique identifier (e.g., build ID, GUID). + )doc"_doc + ) + LIEF_DEFAULT_STR(Binary); } diff --git a/api/python/src/Abstract/pyDebugInfo.cpp b/api/python/src/Abstract/pyDebugInfo.cpp index 97e5a9f1..1146a5c5 100644 --- a/api/python/src/Abstract/pyDebugInfo.cpp +++ b/api/python/src/Abstract/pyDebugInfo.cpp @@ -1,3 +1,4 @@ +#include "pyLIEF.hpp" #include "LIEF/Abstract/DebugInfo.hpp" #include "Abstract/init.hpp" @@ -13,9 +14,9 @@ void create(nb::module_& m) { .value("PDB", DebugInfo::FORMAT::PDB); dbg_info.def_prop_ro("format", &DebugInfo::format, - R"delim( + R"doc( Debug format (PDB/DWARF) - )delim" + )doc"_doc ); } diff --git a/api/python/src/Abstract/pyDebugInfoTyHook.hpp b/api/python/src/Abstract/pyDebugInfoTyHook.hpp new file mode 100644 index 00000000..b2f9ebf4 --- /dev/null +++ b/api/python/src/Abstract/pyDebugInfoTyHook.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include + +#include "LIEF/DWARF/DebugInfo.hpp" +#include "LIEF/PDB/DebugInfo.hpp" + +namespace nanobind::detail { +template<> struct type_hook { + static const std::type_info* get(const LIEF::DebugInfo *src) { + if (src) { + if (LIEF::dwarf::DebugInfo::classof(src)) { + return &typeid(LIEF::dwarf::DebugInfo); + } + + if (LIEF::pdb::DebugInfo::classof(src)) { + return &typeid(LIEF::pdb::DebugInfo); + } + } + return &typeid(LIEF::dwarf::Type); + } +}; +} diff --git a/api/python/src/nanobind/extra/stl/pathlike.h b/api/python/src/nanobind/extra/stl/pathlike.h new file mode 100644 index 00000000..3aae1f10 --- /dev/null +++ b/api/python/src/nanobind/extra/stl/pathlike.h @@ -0,0 +1,35 @@ +#pragma once + +#include +#include + +#include + +#include "typing.hpp" +#include "pyutils.hpp" + +NAMESPACE_BEGIN(NB_NAMESPACE) + +struct PathLike : public nanobind::object { + LIEF_PY_DEFAULT_CTOR(PathLike, nanobind::object); + + NB_OBJECT_DEFAULT(PathLike, object, "Union[str | os.PathLike]", check) + + std::string to_string() const { + if (nb::isinstance(*this)) { + return nb::cast(*this); + } + auto path_str = LIEF::py::path_to_str(*this); + assert(path_str); + return *path_str; + } + + operator std::string() const { return to_string(); } + + static bool check(handle h) { + return nb::isinstance(h) || + LIEF::py::path_to_str(nb::object(h, nb::detail::borrow_t{})); + } +}; + +NAMESPACE_END(NB_NAMESPACE) diff --git a/api/rust/cargo/lief/src/generic.rs b/api/rust/cargo/lief/src/generic.rs index 65c281d7..04dd3e8c 100644 --- a/api/rust/cargo/lief/src/generic.rs +++ b/api/rust/cargo/lief/src/generic.rs @@ -117,6 +117,12 @@ impl std::fmt::Debug for &dyn Relocation { } } +/// Generic interface representing a binary executable. +/// +/// This trait provides a unified interface across multiple binary formats +/// such as ELF, PE, Mach-O, and others. It enables users to access binary +/// components like headers, sections, symbols, relocations, +/// and functions in a format-agnostic way. pub trait Binary { #[doc(hidden)] fn as_generic(&self) -> &ffi::AbstractBinary; @@ -167,7 +173,7 @@ pub trait Binary { into_optional(self.as_generic().debug_info()) } - /// Disassemble code starting a the given virtual address and with the given + /// Disassemble code starting at the given virtual address and with the given /// size. /// /// ``` @@ -249,6 +255,26 @@ pub trait Binary { fn page_size(&self) -> u64 { self.as_generic().page_size() } + + /// Load and associate an external debug file (e.g., DWARF or PDB) with this binary. + /// + /// This method attempts to load the debug information from the file located at the given path, + /// and binds it to the current binary instance. If successful, it returns the + /// loaded [`crate::DebugInfo`] object. + /// + ///
+ /// It is the caller's responsibility to ensure that the debug file is + /// compatible with the binary. Incorrect associations may lead to + /// inconsistent or invalid results. + ///
+ /// + ///
+ /// This function does not verify that the debug file matches the binary's unique + /// identifier (e.g., build ID, GUID). + ///
+ fn load_debug_info(&mut self, path: &std::path::Path) -> Option { + into_optional(self.as_pin_mut_generic().load_debug_info(path.to_str().unwrap())) + } } pub trait DebugInfo { diff --git a/api/rust/include/LIEF/rust/Abstract/Binary.hpp b/api/rust/include/LIEF/rust/Abstract/Binary.hpp index a09d5082..26ddf310 100644 --- a/api/rust/include/LIEF/rust/Abstract/Binary.hpp +++ b/api/rust/include/LIEF/rust/Abstract/Binary.hpp @@ -111,6 +111,10 @@ class AbstractBinary : public Mirror { return get().assemble(address, Asm, *config); } + auto load_debug_info(std::string file) { + return details::try_unique(get().load_debug_info(file)); + } + uint64_t page_size() const { return get().page_size(); } diff --git a/doc/sphinx/_cross_api.rst b/doc/sphinx/_cross_api.rst index e68d60c5..cde5e7a9 100644 --- a/doc/sphinx/_cross_api.rst +++ b/doc/sphinx/_cross_api.rst @@ -595,6 +595,12 @@ :py:attr:`lief.Binary.page_size` :cpp:func:`LIEF::Binary::page_size` +.. |lief-abstract-binary-load_debug_info| lief-api:: lief.abstract.Binary.load_debug_info() + + :rust:method:`lief::generic::Binary::load_debug_info [trait]` + :py:meth:`lief.Binary.load_debug_info` + :cpp:func:`LIEF::Binary::load_debug_info` + .. ELF ========================================================================= .. |lief-elf-symbol-demangled_name| lief-api:: lief.ELF.Symbol.demangled_name() diff --git a/doc/sphinx/api/error_handling/index.rst b/doc/sphinx/api/error_handling/index.rst index 8b747648..71e987a7 100644 --- a/doc/sphinx/api/error_handling/index.rst +++ b/doc/sphinx/api/error_handling/index.rst @@ -70,23 +70,17 @@ C++ ++++++++ -.. doxygentypedef:: LIEF::result - :project: lief +.. doxygenclass:: LIEF::result .. doxygenfunction:: LIEF::as_lief_err - :project: lief .. doxygenenum:: lief_errors - :project: lief -.. doxygentypedef:: LIEF::ok_error_t - :project: lief +.. doxygenclass:: LIEF::ok_error_t .. doxygenfunction:: LIEF::ok - :project: lief .. doxygenstruct:: LIEF::ok_t - :project: lief Python ++++++++ diff --git a/doc/sphinx/changelog.rst b/doc/sphinx/changelog.rst index 51594192..cef52ec2 100644 --- a/doc/sphinx/changelog.rst +++ b/doc/sphinx/changelog.rst @@ -211,6 +211,10 @@ :Abstraction: * Expose |lief-abstract-binary-page_size| + * Add |lief-abstract-binary-load_debug_info| to attach an external debug file + to a |lief-abstract-binary|. See these sections for more details: + :ref:`DWARF: Loading an external debug file ` + :ref:`PDB: Loading an external debug file ` :Extended: diff --git a/doc/sphinx/extended/dwarf/index.rst b/doc/sphinx/extended/dwarf/index.rst index cd36ff10..f4f924dd 100644 --- a/doc/sphinx/extended/dwarf/index.rst +++ b/doc/sphinx/extended/dwarf/index.rst @@ -16,11 +16,13 @@ Introduction ************ -DWARF debug info can be embedded in the binary itself (default behavior for ELF) -or externalized in a dedicated file. +DWARF debug information can be included directly in the binary +(which is the default behavior for ELF binaries) or stored in a separate +dedicated file. -If the DWARF debug info are embedded in the binary itself, one can use the -attribute: |lief-dwarf-binary-debug-info| to access an instance of |lief-dwarf-debug-info|: +When the DWARF debug information is embedded within the binary, +you can access it using the following attribute: |lief-dwarf-binary-debug-info|. +This attribute returns a |lief-dwarf-debug-info|: .. tabs:: @@ -55,8 +57,8 @@ attribute: |lief-dwarf-binary-debug-info| to access an instance of |lief-dwarf-d // DWARF debug info } -On the other hand, we can also use the function: |lief-dwarf-load| to load a -DWARF file regardless whether it is embedded or not: +Additionally, we can use the function: |lief-dwarf-load| to load a +DWARF file, regardless of whether it is embedded or not: .. tabs:: @@ -182,6 +184,98 @@ instantiated debug info: dbg.variable_by_name("std::out_of_range::out_of_range(char const*)"); dbg.variable_by_addr(0x137a70); + +.. _extended-dwarf-load-ext: + +In the case of an external DWARF file, you can bind this debug file to +a |lief-abstract-binary| by using the function: |lief-abstract-binary-load_debug_info|. + +Here's an example: + +.. tabs:: + + .. tab:: :fa:`brands fa-python` Python + + .. code-block:: python + + import lief + + binary: lief.Binary = ... # Can be an ELF/PE/Mach-O [...] + + dbg: lief.DebugInfo = binary.load_debug_info("/home/romain/dev/LIEF/some.dwo") + + .. tab:: :fa:`regular fa-file-code` C++ + + .. code-block:: cpp + + std::unique_ptr binary; // Can be an ELF/PE/Mach-O + + binary->load_debug_info("/home/romain/dev/LIEF/some.dwo"); + + .. tab:: :fa:`brands fa-rust` Rust + + .. code-block:: rust + + bin: &mut dyn lief::generic::Binary = ...; + + let path = PathBuf::from("/home/romain/dev/LIEF/some.dwo"); + + bin.load_debug_info(&path); + +This external loading API is useful for adding debug information that might not +already be present in the binary. For instance, the |lief-disassemble| function +can leverage this additional debug information to disassemble functions +defined in the debug file previously loaded: + +.. tabs:: + + .. tab:: :fa:`brands fa-python` Python + + .. code-block:: python + + import lief + + binary: lief.Binary = ... # Can be an ELF/PE/Mach-O [...] + + dbg: lief.DebugInfo = binary.load_debug_info("/home/romain/dev/LIEF/some.dwo") + + # The location (address/size) of `my_function` is defined in some.dwo + for inst in binary.disassemble("my_function"): + print(inst) + + .. tab:: :fa:`regular fa-file-code` C++ + + .. code-block:: cpp + + std::unique_ptr binary; // Can be an ELF/PE/Mach-O + + binary->load_debug_info("/home/romain/dev/LIEF/some.dwo"); + + // The location (address/size) of `my_function` is defined in some.dwo + for (std::unique_ptr inst : binary->disassemble("my_function")) { + std::cout << *inst << '\n'; + } + + .. tab:: :fa:`brands fa-rust` Rust + + .. code-block:: rust + + bin: &mut dyn lief::generic::Binary = ...; + + let path = PathBuf::from("/home/romain/dev/LIEF/some.dwo"); + + bin.load_debug_info(&path); + + // The location (address/size) of `my_function` is defined in some.dwo + for inst in bin.disassemble_symbol("my_function") { + println!("{inst}"); + } + +Additionally, you may want to check out the +:ref:`BinaryNinja ` and +:ref:`Ghidra ` DWARF export plugin which can generate +debug information based on the analyses performed by these frameworks. + .. _extended-dwarf-editor: DWARF Editor diff --git a/doc/sphinx/extended/pdb/cpp.rst b/doc/sphinx/extended/pdb/cpp.rst index b9a31efa..a1632d1e 100644 --- a/doc/sphinx/extended/pdb/cpp.rst +++ b/doc/sphinx/extended/pdb/cpp.rst @@ -152,4 +152,4 @@ Union Utilities ********* -.. doxygenfunction:: LIEF::PDB::is_pdb(const std::string&) +.. doxygenfunction:: bool LIEF::pdb::is_pdb(const std::string&) diff --git a/doc/sphinx/extended/pdb/index.rst b/doc/sphinx/extended/pdb/index.rst index 8bfde1f3..bdb883c1 100644 --- a/doc/sphinx/extended/pdb/index.rst +++ b/doc/sphinx/extended/pdb/index.rst @@ -161,6 +161,98 @@ the PDB debug info: } } +.. _extended-pdb-load-ext: + +You can also use the function |lief-abstract-binary-load_debug_info| to bind +an PDB file to an existing |lief-abstract-binary|: + +.. tabs:: + + .. tab:: :fa:`brands fa-python` Python + + .. code-block:: python + + import lief + + binary: lief.Binary = ... # Can be an ELF/PE/Mach-O [...] + + dbg: lief.DebugInfo = binary.load_debug_info(r"C:\Users\romain\LIEF.pdb") + + .. tab:: :fa:`regular fa-file-code` C++ + + .. code-block:: cpp + + std::unique_ptr binary; // Can be an ELF/PE/Mach-O + + binary->load_debug_info("C:\\Users\\romain\\LIEF.pdb"); + + .. tab:: :fa:`brands fa-rust` Rust + + .. code-block:: rust + + bin: &mut dyn lief::generic::Binary = ...; + + let path = PathBuf::from("C:\\Users\\romain\\LIEF.pdb"); + + bin.load_debug_info(&path); + +Note that |lief-abstract-binary-load_debug_info| can also attach an external +DWARF file on a PE binary even if this is not the regular use case. +For instance, :ref:`BinaryNinja ` and +:ref:`Ghidra ` DWARF export plugin can generate +a DWARF file based on the analyses performed by these frameworks for a PE +binary. + +This external loading API is useful for adding debug information that might not +already be present in the binary. For instance, the |lief-disassemble| function +can leverage this additional debug information to disassemble functions +defined in the debug file previously loaded: + +.. tabs:: + + .. tab:: :fa:`brands fa-python` Python + + .. code-block:: python + + import lief + + binary: lief.Binary = ... # Can be an ELF/PE/Mach-O [...] + + dbg: lief.DebugInfo = binary.load_debug_info(r"C:\Users\romain\LIEF.pdb") + + # The location (address/size) of `my_function` is defined in LIEF.pdb + for inst in binary.disassemble("my_function"): + print(inst) + + .. tab:: :fa:`regular fa-file-code` C++ + + .. code-block:: cpp + + std::unique_ptr binary; // Can be an ELF/PE/Mach-O + + binary->load_debug_info("C:\\Users\\romain\\LIEF.pdb"); + + // The location (address/size) of `my_function` is defined in LIEF.pdb + for (std::unique_ptr inst : binary->disassemble("my_function")) { + std::cout << *inst << '\n'; + } + + .. tab:: :fa:`brands fa-rust` Rust + + .. code-block:: rust + + bin: &mut dyn lief::generic::Binary = ...; + + let path = PathBuf::from("C:\\Users\\romain\\LIEF.pdb"); + + bin.load_debug_info(&path); + + // The location (address/size) of `my_function` is defined in LIEF.pdb + for inst in bin.disassemble_symbol("my_function") { + println!("{inst}"); + } + + ---- API diff --git a/include/LIEF/Abstract/Binary.hpp b/include/LIEF/Abstract/Binary.hpp index 61003acc..8e4f738a 100644 --- a/include/LIEF/Abstract/Binary.hpp +++ b/include/LIEF/Abstract/Binary.hpp @@ -48,16 +48,28 @@ namespace assembly { class Engine; } -/// Abstract binary that exposes an uniform API for the -/// different executable file formats +/// Generic interface representing a binary executable. +/// +/// This class provides a unified interface across multiple binary formats +/// such as ELF, PE, Mach-O, and others. It enables users to access binary +/// components like headers, sections, symbols, relocations, +/// and functions in a format-agnostic way. +/// +/// Subclasses like LIEF::PE::Binary implement format-specific API class LIEF_API Binary : public Object { public: - /// Type of a virtual address + /// Enumeration of virtual address types used for patching and memory access. enum class VA_TYPES { - AUTO = 0, ///< Try to guess if it's relative or not - RVA = 1, ///< Relative - VA = 2, ///< Absolute + /// Automatically determine if the address is absolute or relative + /// (default behavior). + AUTO = 0, + + /// Relative Virtual Address (RVA), offset from image base. + RVA = 1, + + /// Absolute Virtual Address. + VA = 2 }; enum FORMATS { @@ -285,7 +297,7 @@ class LIEF_API Binary : public Object { /// **always** return a nullptr DebugInfo* debug_info() const; - /// Disassemble code starting a the given virtual address and with the given + /// Disassemble code starting at the given virtual address and with the given /// size. /// /// ```cpp @@ -298,7 +310,7 @@ class LIEF_API Binary : public Object { /// \see LIEF::assembly::Instruction instructions_it disassemble(uint64_t address, size_t size) const; - /// Disassemble code starting a the given virtual address + /// Disassemble code starting at the given virtual address /// /// ```cpp /// auto insts = binary->disassemble(0xacde); @@ -381,6 +393,23 @@ class LIEF_API Binary : public Object { /// the format of the current binary virtual uint64_t page_size() const; + /// Load and associate an external debug file (e.g., DWARF or PDB) with this binary. + /// + /// This method attempts to load the debug information from the file located at the given path, + /// and binds it to the current binary instance. If successful, it returns a pointer to the + /// loaded DebugInfo object. + /// + /// \param path Path to the external debug file (e.g., `.dwarf`, `.pdb`) + /// \return Pointer to the loaded DebugInfo object on success, or `nullptr` on failure. + /// + /// \warning It is the caller's responsibility to ensure that the debug file is + /// compatible with the binary. Incorrect associations may lead to + /// inconsistent or invalid results. + /// + /// \note This function does not verify that the debug file matches the binary's unique + /// identifier (e.g., build ID, GUID). + DebugInfo* load_debug_info(const std::string& path); + protected: FORMATS format_ = FORMATS::UNKNOWN; mutable std::unique_ptr debug_info_; diff --git a/include/LIEF/Abstract/DebugInfo.hpp b/include/LIEF/Abstract/DebugInfo.hpp index 4ccf6aa6..928d1367 100644 --- a/include/LIEF/Abstract/DebugInfo.hpp +++ b/include/LIEF/Abstract/DebugInfo.hpp @@ -18,12 +18,15 @@ #include "LIEF/visibility.h" namespace LIEF { +class Binary; + namespace details { class DebugInfo; } class LIEF_API DebugInfo { public: + friend class Binary; enum class FORMAT { UNKNOWN = 0, DWARF, PDB, diff --git a/src/Abstract/Binary.cpp b/src/Abstract/Binary.cpp index 31b73636..725a3369 100644 --- a/src/Abstract/Binary.cpp +++ b/src/Abstract/Binary.cpp @@ -70,11 +70,11 @@ std::vector Binary::xref(uint64_t address) const { return result; } - uint64_t Binary::page_size() const { return get_pagesize(*this); } + void Binary::accept(Visitor& visitor) const { visitor.visit(*this); } diff --git a/src/Abstract/debug_info.cpp b/src/Abstract/debug_info.cpp index 0c75d20f..9cfd133e 100644 --- a/src/Abstract/debug_info.cpp +++ b/src/Abstract/debug_info.cpp @@ -31,6 +31,12 @@ DebugInfo* Binary::debug_info() const { return nullptr; } +DebugInfo* Binary::load_debug_info(const std::string& /*path*/) { + LIEF_ERR(DEBUG_FMT_NOT_SUPPORTED); + return nullptr; +} + + // ---------------------------------------------------------------------------- // DebugInfo/DebugInfo.hpp // ---------------------------------------------------------------------------- diff --git a/tests/dwarf/test_binaryninja.py b/tests/dwarf/test_binaryninja.py index 1b6f6f58..9b6872f3 100644 --- a/tests/dwarf/test_binaryninja.py +++ b/tests/dwarf/test_binaryninja.py @@ -87,3 +87,9 @@ def test_variables(): assert binaryninja_liblinker.find_variable("protected_lib").address == 0x30000 assert binaryninja_liblinker.find_variable(0x30000).name == "protected_lib" + +def test_external_load(): + elf = lief.ELF.parse(get_sample("private/DWARF/binaryninja/dexprotector/libdp.so")) + assert len(list(elf.disassemble("dp_sys_mprotect"))) == 0 + elf.load_debug_info(get_sample("private/DWARF/binaryninja/dexprotector/libdp.dwarf")) + assert len(list(elf.disassemble("dp_sys_mprotect"))) == 17 diff --git a/tests/dwarf/test_macho_dsym.py b/tests/dwarf/test_macho_dsym.py index 9f1fce6d..dc038b15 100644 --- a/tests/dwarf/test_macho_dsym.py +++ b/tests/dwarf/test_macho_dsym.py @@ -27,3 +27,11 @@ def test_lief(): assert variables[0].name == "None" # static Relocation None; assert variables[0].address == 0x370710 assert variables[0].size == 80 + +def test_external_load(): + macho = lief.MachO.parse(get_sample("DWARF/dSYM/example")).at(0) + + assert len(list(macho.disassemble("main"))) == 0 + macho.load_debug_info(get_sample("DWARF/dSYM/example.dSYM/Contents/Resources/DWARF/example")) + + assert len(list(macho.disassemble("main"))) == 375 diff --git a/tests/pdb/test_api.py b/tests/pdb/test_api.py index 38a23c43..c9555d92 100644 --- a/tests/pdb/test_api.py +++ b/tests/pdb/test_api.py @@ -109,3 +109,12 @@ def test_libobjc2(): loc = functions[3].debug_location assert loc.file == r"C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Tools\MSVC\14.16.27023\include\xstring" assert loc.line == 4004 + +def test_external_load(): + pe = lief.PE.parse(get_sample("private/PE/LIEF-arm64.dll")) + assert pe is not None + pe.load_debug_info(get_sample("private/PDB/LIEF-arm64.pdb")) + assert pe.debug_info is not None + assert isinstance(pe.debug_info, lief.pdb.DebugInfo) + + assert len(list(pe.disassemble("??_7Header@ELF@LIEF@@6B@"))) == 19