From 836620bfbb789e609470f2fecdd343056ddd1a1b Mon Sep 17 00:00:00 2001 From: Romain Thomas Date: Sun, 29 Jun 2025 17:34:58 +0200 Subject: [PATCH] Add API to remove a version requirement from a symbol --- api/python/lief/ELF/__init__.pyi | 6 ++++ .../src/ELF/objects/pySymbolVersion.cpp | 10 ++++++ api/rust/cargo/lief/src/elf/dynamic.rs | 8 +++++ .../cargo/lief/src/elf/symbol_versioning.rs | 19 +++++++++++ .../LIEF/rust/ELF/DynamicEntryFlags.hpp | 10 ++++++ .../include/LIEF/rust/ELF/SymbolVersion.hpp | 10 +++++- include/LIEF/ELF/SymbolVersion.hpp | 32 +++++++++++++++++-- include/LIEF/ELF/SymbolVersionRequirement.hpp | 1 + tests/elf/test_symbol_versions.py | 17 ++++++++++ 9 files changed, 110 insertions(+), 3 deletions(-) diff --git a/api/python/lief/ELF/__init__.pyi b/api/python/lief/ELF/__init__.pyi index bcaa752b..1c5dff23 100644 --- a/api/python/lief/ELF/__init__.pyi +++ b/api/python/lief/ELF/__init__.pyi @@ -432,6 +432,12 @@ class SymbolVersion(lief.Object): symbol_version_auxiliary: SymbolVersionAux + def drop_version(self, value: int) -> None: ... + + def as_global(self) -> None: ... + + def as_local(self) -> None: ... + def __str__(self) -> str: ... class Binary(lief.Binary): diff --git a/api/python/src/ELF/objects/pySymbolVersion.cpp b/api/python/src/ELF/objects/pySymbolVersion.cpp index b43b7d40..46f3d1cc 100644 --- a/api/python/src/ELF/objects/pySymbolVersion.cpp +++ b/api/python/src/ELF/objects/pySymbolVersion.cpp @@ -76,6 +76,16 @@ void create(nb::module_& m) { )delim"_doc, nb::rv_policy::reference_internal) + .def("drop_version", &SymbolVersion::drop_version, + "Drop the versioning requirement and replace the value (local/global)"_doc, + "value"_a) + + .def("as_global", &SymbolVersion::as_global, + "Redefine this version as global by dropping its auxiliary version"_doc) + + .def("as_local", &SymbolVersion::as_local, + "Redefine this version as local by dropping its auxiliary version"_doc) + LIEF_DEFAULT_STR(SymbolVersion); } diff --git a/api/rust/cargo/lief/src/elf/dynamic.rs b/api/rust/cargo/lief/src/elf/dynamic.rs index 6c8a60a7..7097caef 100644 --- a/api/rust/cargo/lief/src/elf/dynamic.rs +++ b/api/rust/cargo/lief/src/elf/dynamic.rs @@ -1077,6 +1077,14 @@ impl Flags<'_> { DtFlags::from(self.ptr.flags()) } + pub fn add_flag(&mut self, flag: DtFlags) { + self.ptr.pin_mut().add_flag(flag.into()) + } + + pub fn remove_flag(&mut self, flag: DtFlags) { + self.ptr.pin_mut().remove_flag(flag.into()) + } + pub fn create_dt_flag(value: u64) -> Self { Self::from_ffi(ffi::ELF_DynamicEntryFlags::create_dt_flag(value)) } diff --git a/api/rust/cargo/lief/src/elf/symbol_versioning.rs b/api/rust/cargo/lief/src/elf/symbol_versioning.rs index 94fc0b28..ad0b397a 100644 --- a/api/rust/cargo/lief/src/elf/symbol_versioning.rs +++ b/api/rust/cargo/lief/src/elf/symbol_versioning.rs @@ -26,6 +26,25 @@ impl SymbolVersion<'_> { pub fn symbol_version_auxiliary(&self) -> Option { into_optional(self.ptr.symbol_version_auxiliary()) } + + /// Drop the versioning requirement and replace the value (local/global) + pub fn drop_version(&mut self, value: u16) { + self.ptr.pin_mut().drop_version(value) + } + + /// Redefine this version as local by dropping its auxiliary version + /// + /// See: [`SymbolVersion::as_global`], [`SymbolVersion::drop_version`] + pub fn as_local(&mut self) { + self.ptr.pin_mut().as_local() + } + + /// Redefine this version as global by dropping its auxiliary version + /// + /// See: [`SymbolVersion::as_local`], [`SymbolVersion::drop_version`] + pub fn as_global(&mut self) { + self.ptr.pin_mut().as_global() + } } impl fmt::Debug for SymbolVersion<'_> { diff --git a/api/rust/include/LIEF/rust/ELF/DynamicEntryFlags.hpp b/api/rust/include/LIEF/rust/ELF/DynamicEntryFlags.hpp index 68842f6b..ae370c35 100644 --- a/api/rust/include/LIEF/rust/ELF/DynamicEntryFlags.hpp +++ b/api/rust/include/LIEF/rust/ELF/DynamicEntryFlags.hpp @@ -22,10 +22,19 @@ class ELF_DynamicEntryFlags : public ELF_DynamicEntry { ELF_DynamicEntryFlags(const lief_t& impl) : ELF_DynamicEntry(impl.clone()) {} + auto flags() const { return impl().raw_flags(); } + void add_flag(uint64_t f) { + impl().add(lief_t::FLAG(f)); + } + + void remove_flag(uint64_t f) { + impl().remove(lief_t::FLAG(f)); + } + static bool classof(const ELF_DynamicEntry& entry) { return lief_t::classof(&entry.get()); } @@ -40,4 +49,5 @@ class ELF_DynamicEntryFlags : public ELF_DynamicEntry { private: const lief_t& impl() const { return as(this); } + lief_t& impl() { return as(this); } }; diff --git a/api/rust/include/LIEF/rust/ELF/SymbolVersion.hpp b/api/rust/include/LIEF/rust/ELF/SymbolVersion.hpp index 94ef8fc7..fc8afea2 100644 --- a/api/rust/include/LIEF/rust/ELF/SymbolVersion.hpp +++ b/api/rust/include/LIEF/rust/ELF/SymbolVersion.hpp @@ -22,8 +22,16 @@ class ELF_SymbolVersion : private Mirror { using lief_t = LIEF::ELF::SymbolVersion; using Mirror::Mirror; - uint16_t value() const { return get().value(); } + auto value() const { return get().value(); } + auto symbol_version_auxiliary() const { return details::try_unique(get().symbol_version_auxiliary()); } + + auto drop_version(uint16_t value) { + get().drop_version(value); + } + + auto as_local() { get().as_local(); } + auto as_global() { get().as_global(); } }; diff --git a/include/LIEF/ELF/SymbolVersion.hpp b/include/LIEF/ELF/SymbolVersion.hpp index ab99ecaa..ad92e212 100644 --- a/include/LIEF/ELF/SymbolVersion.hpp +++ b/include/LIEF/ELF/SymbolVersion.hpp @@ -17,6 +17,7 @@ #define LIEF_ELF_SYMBOL_VERSION_H #include #include +#include #include "LIEF/Object.hpp" #include "LIEF/visibility.h" @@ -33,6 +34,9 @@ class LIEF_API SymbolVersion : public Object { friend class Parser; public: + static constexpr auto LOCAL_VERSION = 0; + static constexpr auto GLOBAL_VERSION = 1; + SymbolVersion(uint16_t value) : value_(value) {} @@ -40,12 +44,12 @@ class LIEF_API SymbolVersion : public Object { /// Generate a *local* SymbolVersion static SymbolVersion local() { - return SymbolVersion(0); + return SymbolVersion(LOCAL_VERSION); } /// Generate a *global* SymbolVersion static SymbolVersion global() { - return SymbolVersion(1); + return SymbolVersion(GLOBAL_VERSION); } ~SymbolVersion() override = default; @@ -86,6 +90,30 @@ class LIEF_API SymbolVersion : public Object { /// SymbolVersionRequirement::add_aux_requirement void symbol_version_auxiliary(SymbolVersionAuxRequirement& svauxr); + /// Drop the versioning requirement and replace the value (local/global) + void drop_version(uint16_t value) { + if (symbol_aux_ == nullptr) { + return; + } + assert(value == LOCAL_VERSION || value == GLOBAL_VERSION); + value_ = value; + symbol_aux_ = nullptr; + } + + /// Redefine this version as global by dropping its auxiliary version + /// + /// \see as_local() drop_version() + void as_global() { + return drop_version(GLOBAL_VERSION); + } + + /// Redefine this version as local by dropping its auxiliary version + /// + /// \see as_global() drop_version() + void as_local() { + return drop_version(LOCAL_VERSION); + } + void value(uint16_t v) { value_ = v; } diff --git a/include/LIEF/ELF/SymbolVersionRequirement.hpp b/include/LIEF/ELF/SymbolVersionRequirement.hpp index 7ed91e53..8bbf0b35 100644 --- a/include/LIEF/ELF/SymbolVersionRequirement.hpp +++ b/include/LIEF/ELF/SymbolVersionRequirement.hpp @@ -16,6 +16,7 @@ #ifndef LIEF_ELF_SYMBOL_VERSION_REQUIREMENTS_H #define LIEF_ELF_SYMBOL_VERSION_REQUIREMENTS_H +#include #include #include #include diff --git a/tests/elf/test_symbol_versions.py b/tests/elf/test_symbol_versions.py index 650073bc..3760fcc8 100644 --- a/tests/elf/test_symbol_versions.py +++ b/tests/elf/test_symbol_versions.py @@ -44,3 +44,20 @@ def test_issue_1014(tmp_path: Path): lib.write(out.as_posix()) new_lib = lief.ELF.parse(out.as_posix()) check_lib(new_lib) + +def test_remove_symbol(tmp_path: Path): + elf = lief.ELF.parse(get_sample('ELF/lib_symbol_versions.so')) + + sym: lief.ELF.Symbol = elf.get_symbol("puts") + + version: lief.ELF.SymbolVersion = sym.symbol_version + assert version is not None + assert str(version) == "GLIBC_2.2.5(4)" + version.as_global() + + output = tmp_path / "lib_symbol_versions.so" + + elf.write(output.as_posix()) + + new = lief.ELF.parse(output) + assert str(new.get_symbol("puts").symbol_version) == "* Global *"