From 8e9eb84785806d76f35bec4494620a51dccc1f46 Mon Sep 17 00:00:00 2001 From: Romain Thomas Date: Sat, 30 Sep 2023 06:04:29 +0200 Subject: [PATCH] Resolve #970 --- src/ELF/Builder.tcc | 99 ++++++++++++++--------- src/ELF/ExeLayout.hpp | 162 ++++++++++++++++++++------------------ src/ELF/Parser.tcc | 23 +++--- src/logging.hpp | 8 ++ tests/elf/test_builder.py | 17 ++++ 5 files changed, 183 insertions(+), 126 deletions(-) diff --git a/src/ELF/Builder.tcc b/src/ELF/Builder.tcc index 3869132702..d223408dd2 100644 --- a/src/ELF/Builder.tcc +++ b/src/ELF/Builder.tcc @@ -14,7 +14,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include #include +#include #include #include @@ -1606,65 +1608,84 @@ ok_error_t Builder::build_symbol_definition() { return make_error_code(lief_errors::not_found); } - const Elf_Addr svd_va = dt_verdef->value(); - const uint32_t svd_nb = dt_verdefnum->value(); + const Elf_Addr svd_va = dt_verdef->value(); + const uint32_t svd_nb = dt_verdefnum->value(); if (svd_nb != binary_->symbol_version_definition_.size()) { LIEF_WARN("The number of symbol version definition entries " "in the binary differ from the value in DT_VERDEFNUM"); } - vector_iostream svd_raw(should_swap()); + auto sym_verdef = binary_->symbols_version_definition(); - uint32_t svd_idx = 0; const auto& sym_name_offset = static_cast(layout_.get())->dynstr_map(); - for (const SymbolVersionDefinition& svd: binary_->symbols_version_definition()) { + auto& verdef_info = static_cast(layout_.get())->verdef_info(); - SymbolVersionDefinition::it_const_version_aux svas = svd.symbols_aux(); + vector_iostream svd_aux_raw(should_swap()); + { + for (const auto& names : verdef_info.names_list) { + for (size_t i = 0; i < names.size(); ++i) { + const std::string& sva_name = names[i]; + uint64_t dynstr_offset = 0; + + const auto it_name_offset = sym_name_offset.find(sva_name); + if (it_name_offset == std::end(sym_name_offset)) { + LIEF_ERR("Can't find dynstr offset for '{}'", sva_name); + return make_error_code(lief_errors::not_found); + } - Elf_Off next_symbol_offset = 0; + dynstr_offset = it_name_offset->second; + const uint64_t next_offset = i < (names.size() - 1) ? + sizeof(Elf_Verdaux) : 0; - if (svd_idx < (svd_nb - 1)) { - next_symbol_offset = sizeof(Elf_Verdef) + svas.size() * sizeof(Elf_Verdaux); + Elf_Verdaux aux_header; + aux_header.vda_name = static_cast(dynstr_offset); + aux_header.vda_next = static_cast(next_offset); + verdef_info.names_offset[&names] = svd_aux_raw.tellp(); + svd_aux_raw.write_conv(aux_header); + } } + } - Elf_Verdef header; - header.vd_version = static_cast(svd.version()); - header.vd_flags = static_cast(svd.flags()); - header.vd_ndx = static_cast(svd.ndx()); - header.vd_cnt = static_cast(svas.size()); - header.vd_hash = static_cast(svd.hash()); - header.vd_aux = static_cast(!svas.empty() ? sizeof(Elf_Verdef) : 0); - header.vd_next = static_cast(next_symbol_offset); - - svd_raw.write_conv(header); - - - uint32_t sva_idx = 0; - for (const SymbolVersionAux& sva : svas) { - const std::string& sva_name = sva.name(); + const uint64_t svd_aux_offset = sizeof(Elf_Verdef) * sym_verdef.size(); - Elf_Off sva_name_offset = 0; - const auto& it_name_offset = sym_name_offset.find(sva_name); - if (it_name_offset != std::end(sym_name_offset)) { - sva_name_offset = it_name_offset->second; - } else { - LIEF_ERR("Can't find dynstr offset for '{}'", sva_name); - continue; + vector_iostream svd_raw(should_swap()); + { + for (size_t i = 0; i < sym_verdef.size(); ++i) { + const SymbolVersionDefinition& svd = sym_verdef[i]; + const uint64_t next_offset = i < (sym_verdef.size() - 1) ? + sizeof(Elf_Verdef) : 0; + + auto it_names = verdef_info.def_to_names.find(&svd); + if (it_names == verdef_info.def_to_names.end()) { + LIEF_ERR("Can't find list of names"); + return make_error_code(lief_errors::not_found); } - Elf_Verdaux aux_header; - aux_header.vda_name = static_cast(sva_name_offset); - aux_header.vda_next = static_cast(sva_idx < (svas.size() - 1) ? sizeof(Elf_Verdaux) : 0); - - svd_raw.write_conv(aux_header); + auto it_offset = verdef_info.names_offset.find(it_names->second); + if (it_offset == verdef_info.names_offset.end()) { + LIEF_ERR("Can't find names offset"); + return make_error_code(lief_errors::not_found); + } - ++sva_idx; + uint64_t aux_offset = svd_aux_offset + it_offset->second; + // This is a **relative** offset + aux_offset -= svd_raw.tellp(); + + Elf_Verdef header; + header.vd_version = static_cast(svd.version()); + header.vd_flags = static_cast(svd.flags()); + header.vd_ndx = static_cast(svd.ndx()); + header.vd_cnt = static_cast(svd.symbols_aux().size()); + header.vd_hash = static_cast(svd.hash()); + header.vd_aux = static_cast(aux_offset); + header.vd_next = static_cast(next_offset); + svd_raw.write_conv(header); } - ++svd_idx; } - binary_->patch_address(svd_va, svd_raw.raw()); + binary_->patch_address(svd_va, svd_raw.raw()); + binary_->patch_address(svd_va + svd_aux_offset, svd_aux_raw.raw()); return ok(); } diff --git a/src/ELF/ExeLayout.hpp b/src/ELF/ExeLayout.hpp index 5caf2bdcc9..0015fdacad 100644 --- a/src/ELF/ExeLayout.hpp +++ b/src/ELF/ExeLayout.hpp @@ -38,6 +38,8 @@ #include #include #include +#include +#include #include "ELF/Structures.hpp" #include "internal_utils.hpp" @@ -54,7 +56,14 @@ class Note; //! needed to rebuild the ELF file. class LIEF_LOCAL ExeLayout : public Layout { public: + struct sym_verdef_info_t { + using list_names_t = std::vector; + std::set names_list; + std::unordered_map def_to_names; + std::unordered_map names_offset; + }; using Layout::Layout; + ExeLayout(const ExeLayout&) = delete; ExeLayout& operator=(const ExeLayout&) = delete; @@ -85,97 +94,83 @@ class LIEF_LOCAL ExeLayout : public Layout { // Start with dynamic entries: NEEDED / SONAME etc vector_iostream raw_dynstr; raw_dynstr.write(0); + + std::vector opt_list; + + std::transform(binary_->dynamic_symbols_.begin(), + binary_->dynamic_symbols_.end(), + std::back_inserter(opt_list), + [] (const std::unique_ptr& sym) { + return sym->name(); + }); + for (std::unique_ptr& entry : binary_->dynamic_entries_) { switch (entry->tag()) { case DYNAMIC_TAGS::DT_NEEDED: { const std::string& name = entry->as()->name(); - offset_name_map_[name] = raw_dynstr.tellp(); - raw_dynstr.write(name); + opt_list.push_back(name); break; } case DYNAMIC_TAGS::DT_SONAME: { const std::string& name = entry->as()->name(); - offset_name_map_[name] = raw_dynstr.tellp(); - raw_dynstr.write(name); + opt_list.push_back(name); break; } case DYNAMIC_TAGS::DT_RPATH: { const std::string& name = entry->as()->name(); - offset_name_map_[name] = raw_dynstr.tellp(); - raw_dynstr.write(name); + opt_list.push_back(name); break; } case DYNAMIC_TAGS::DT_RUNPATH: { const std::string& name = entry->as()->name(); - offset_name_map_[name] = raw_dynstr.tellp(); - raw_dynstr.write(name); + opt_list.push_back(name); break; } default: {} } } - - // Dynamic symbols names - size_t offset_counter = raw_dynstr.tellp(); - std::vector string_table_optimized = optimize(binary_->dynamic_symbols_, - [] (const std::unique_ptr& sym) { - return sym->name(); - }, - offset_counter, &offset_name_map_); - for (const std::string& name : string_table_optimized) { - raw_dynstr.write(name); - } - // Symbol definition for (const SymbolVersionDefinition& svd: binary_->symbols_version_definition()) { - for (const SymbolVersionAux& sva : svd.symbols_aux()) { + sym_verdef_info_t::list_names_t aux_names; + auto saux = svd.symbols_aux(); + aux_names.reserve(saux.size()); + for (const SymbolVersionAux& sva : saux) { const std::string& sva_name = sva.name(); - auto it = offset_name_map_.find(sva_name); - if (it != std::end(offset_name_map_)) { - continue; - } - offset_name_map_[sva_name] = raw_dynstr.tellp(); - raw_dynstr.write(sva_name); + aux_names.push_back(sva_name); + opt_list.push_back(sva_name); } + auto res = verdef_info_.names_list.insert(std::move(aux_names)); + verdef_info_.def_to_names[&svd] = &*res.first; } + // Symbol version requirement for (const SymbolVersionRequirement& svr: binary_->symbols_version_requirement()) { const std::string& libname = svr.name(); - auto it = offset_name_map_.find(libname); - if (it == std::end(offset_name_map_)) { - offset_name_map_[libname] = raw_dynstr.tellp(); - raw_dynstr.write(libname); - } + opt_list.push_back(libname); for (const SymbolVersionAuxRequirement& svar : svr.auxiliary_symbols()) { const std::string& name = svar.name(); - auto it = offset_name_map_.find(name); - if (it != std::end(offset_name_map_)) { - continue; - } - offset_name_map_[name] = raw_dynstr.tellp(); - raw_dynstr.write(name); + opt_list.push_back(name); } } - // Symbol version definition - for (const SymbolVersionDefinition& svd: binary_->symbols_version_definition()) { - for (const SymbolVersionAux& svar : svd.symbols_aux()) { - const std::string& name = svar.name(); - auto it = offset_name_map_.find(name); - if (it != std::end(offset_name_map_)) { - continue; - } - offset_name_map_[name] = raw_dynstr.tellp(); - raw_dynstr.write(name); - } + + size_t offset_counter = raw_dynstr.tellp(); + + std::vector string_table_optimized = optimize(opt_list, + [] (const std::string& name) { return name; }, + offset_counter, &offset_name_map_); + + for (const std::string& name : string_table_optimized) { + raw_dynstr.write(name); } + raw_dynstr.move(raw_dynstr_); return raw_dynstr_.size(); } @@ -436,9 +431,12 @@ class LIEF_LOCAL ExeLayout : public Layout { size_t symbol_vdef_size() { using Elf_Verdef = typename ELF_T::Elf_Verdef; using Elf_Verdaux = typename ELF_T::Elf_Verdaux; - size_t computed_size = 0; - for (const SymbolVersionDefinition& svd: binary_->symbols_version_definition()) { - computed_size += sizeof(Elf_Verdef) + svd.symbols_aux().size() * sizeof(Elf_Verdaux); + CHECK_FATAL(!binary_->symbols_version_definition().empty() && + verdef_info_.def_to_names.empty(), "Inconsistent state"); + size_t computed_size = binary_->symbols_version_definition().size() * sizeof(Elf_Verdef); + + for (const sym_verdef_info_t::list_names_t& names : verdef_info_.names_list) { + computed_size += sizeof(Elf_Verdaux) * names.size(); } return computed_size; } @@ -461,94 +459,102 @@ class LIEF_LOCAL ExeLayout : public Layout { return binary_->interpreter_.size() + 1; } - inline void relocate_dynamic(uint64_t size) { + void relocate_dynamic(uint64_t size) { dynamic_size_ = size; } - inline void relocate_dynstr(bool val) { + void relocate_dynstr(bool val) { relocate_dynstr_ = val; } - inline void relocate_shstr(bool val) { + void relocate_shstr(bool val) { relocate_shstrtab_ = val; } - inline void relocate_strtab(bool val) { + void relocate_strtab(bool val) { relocate_strtab_ = val; } - inline void relocate_gnu_hash(bool val) { + void relocate_gnu_hash(bool val) { relocate_gnu_hash_ = val; } - inline void relocate_sysv_hash(uint64_t size) { + void relocate_sysv_hash(uint64_t size) { sysv_size_ = size; } - inline void relocate_dynsym(uint64_t size) { + void relocate_dynsym(uint64_t size) { dynsym_size_ = size; } - inline void relocate_symver(uint64_t size) { + void relocate_symver(uint64_t size) { sver_size_ = size; } - inline void relocate_symverd(uint64_t size) { + void relocate_symverd(uint64_t size) { sverd_size_ = size; } - inline void relocate_symverr(uint64_t size) { + void relocate_symverr(uint64_t size) { sverr_size_ = size; } - inline void relocate_preinit_array(uint64_t size) { + void relocate_preinit_array(uint64_t size) { preinit_size_ = size; } - inline void relocate_init_array(uint64_t size) { + void relocate_init_array(uint64_t size) { init_size_ = size; } - inline void relocate_fini_array(uint64_t size) { + void relocate_fini_array(uint64_t size) { fini_size_ = size; } - inline void relocate_dyn_reloc(uint64_t size) { + void relocate_dyn_reloc(uint64_t size) { dynamic_reloc_size_ = size; } - inline void relocate_plt_reloc(uint64_t size) { + void relocate_plt_reloc(uint64_t size) { pltgot_reloc_size_ = size; } - inline void relocate_interpreter(uint64_t size) { + void relocate_interpreter(uint64_t size) { interp_size_ = size; } - inline void relocate_notes(bool value) { + void relocate_notes(bool value) { relocate_notes_ = value; } - inline void relocate_symtab(size_t size) { + void relocate_symtab(size_t size) { symtab_size_ = size; } - inline const std::vector& raw_dynstr() const { + const std::vector& raw_dynstr() const { return raw_dynstr_; } - inline const std::vector& raw_shstr() const override { + const std::vector& raw_shstr() const override { return raw_shstrtab_; } - inline const std::vector& raw_gnuhash() const { + const std::vector& raw_gnuhash() const { return raw_gnu_hash_; } - inline const std::vector& raw_notes() const { + const std::vector& raw_notes() const { return raw_notes_; } + sym_verdef_info_t& verdef_info() { + return verdef_info_; + } + + const sym_verdef_info_t& verdef_info() const { + return verdef_info_; + } + result relocate() { /* PT_INTERP segment (optional) * @@ -1337,15 +1343,15 @@ class LIEF_LOCAL ExeLayout : public Layout { return true; } - inline const std::unordered_map& dynstr_map() const { + const std::unordered_map& dynstr_map() const { return offset_name_map_; } - inline const std::unordered_map& note_off_map() const { + const std::unordered_map& note_off_map() const { return notes_off_map_; } - inline uint32_t sysv_nchain() const { + uint32_t sysv_nchain() const { return nchain_; } @@ -1356,6 +1362,8 @@ class LIEF_LOCAL ExeLayout : public Layout { std::unordered_map offset_name_map_; std::unordered_map notes_off_map_; + sym_verdef_info_t verdef_info_; + std::vector raw_notes_; bool relocate_notes_{false}; diff --git a/src/ELF/Parser.tcc b/src/ELF/Parser.tcc index f603341da1..3e22db8ff0 100644 --- a/src/ELF/Parser.tcc +++ b/src/ELF/Parser.tcc @@ -16,6 +16,7 @@ #include #include #include +#include "BinaryStream/BinaryStream.hpp" #include "logging.hpp" #include "LIEF/utils.hpp" @@ -1517,11 +1518,12 @@ ok_error_t Parser::parse_symbol_version_definition(uint64_t offset, uint32_t nb_ using Elf_Verdaux = typename ELF_T::Elf_Verdaux; const uint64_t string_offset = get_dynamic_string_table(); - uint32_t next_symbol_offset = 0; + ScopedStream sscoped(*stream_, offset); + uint64_t def_size = 0; for (size_t i = 0; i < nb_entries; ++i) { - const uint64_t struct_offset = offset + next_symbol_offset; - const auto svd_header = stream_->peek_conv(struct_offset); + const auto svd_header = sscoped->peek_conv(); + def_size = std::max(def_size, sscoped->pos() - offset + sizeof(Elf_Verdef)); if (!svd_header) { break; } @@ -1529,9 +1531,11 @@ ok_error_t Parser::parse_symbol_version_definition(uint64_t offset, uint32_t nb_ auto symbol_version_definition = std::make_unique(*svd_header); uint32_t nb_aux_symbols = svd_header->vd_cnt; uint32_t next_aux_offset = 0; + for (size_t j = 0; j < nb_aux_symbols; ++j) { - const uint64_t struct_offset = offset + next_symbol_offset + svd_header->vd_aux + next_aux_offset; - const auto svda_header = stream_->peek_conv(struct_offset); + ScopedStream aux_stream(*stream_, sscoped->pos() + svd_header->vd_aux); + const auto svda_header = aux_stream->peek_conv(); + def_size = std::max(def_size, aux_stream->pos() - offset + sizeof(Elf_Verdaux)); if (!svda_header) { break; } @@ -1547,7 +1551,7 @@ ok_error_t Parser::parse_symbol_version_definition(uint64_t offset, uint32_t nb_ if (svda_header->vda_next == 0) { break; } - + aux_stream->increment_pos(svda_header->vda_next); next_aux_offset += svda_header->vda_next; } @@ -1557,17 +1561,16 @@ ok_error_t Parser::parse_symbol_version_definition(uint64_t offset, uint32_t nb_ if (svd_header->vd_next == 0) { break; } - - next_symbol_offset += svd_header->vd_next; + sscoped->increment_pos(svd_header->vd_next); } + binary_->sizing_info_->verdef = def_size; + // Associate Symbol Version with auxiliary symbol // We mask the 15th bit because it sets if this symbol is a hidden on or not // but we don't care for (std::unique_ptr& svd : binary_->symbol_version_definition_) { - binary_->sizing_info_->verdef += sizeof(Elf_Verdef); for (std::unique_ptr& sva : svd->symbol_version_aux_) { - binary_->sizing_info_->verdef += sizeof(Elf_Verdaux); for (std::unique_ptr& sv : binary_->symbol_version_table_) { if (svd->ndx() > 1 && (sv->value() & 0x7FFF) == svd->ndx() && !sv->symbol_aux_) { sv->symbol_aux_ = sva.get(); diff --git a/src/logging.hpp b/src/logging.hpp index badfbdd709..bb1eca5cf1 100644 --- a/src/logging.hpp +++ b/src/logging.hpp @@ -38,6 +38,14 @@ } while (false) +#define CHECK_FATAL(X, ...) \ + do { \ + if ((X)) { \ + LIEF_ERR(__VA_ARGS__); \ + std::abort(); \ + } \ + } while (false) + namespace LIEF { namespace logging { diff --git a/tests/elf/test_builder.py b/tests/elf/test_builder.py index 658e13606a..f11cb5b09b 100644 --- a/tests/elf/test_builder.py +++ b/tests/elf/test_builder.py @@ -241,3 +241,20 @@ def test_go_files(tmp_path): stdout = proc.stdout.read() proc.poll() assert "done" in normalize(stdout) + +def test_issue_970(tmp_path: Path): + lib = lief.ELF.parse(get_sample("ELF/libcudart.so.12")) + out = tmp_path / "libcudart.so" + + lib.write(out.as_posix()) + new = lief.ELF.parse(out.as_posix()) + + assert len(new.symbols_version_definition) == 2 + svd_0 = new.symbols_version_definition[0] + svd_1 = new.symbols_version_definition[1] + + assert len(svd_0.auxiliary_symbols) == 1 + assert len(svd_1.auxiliary_symbols) == 1 + + assert svd_0.auxiliary_symbols[0].name == "libcudart.so.12" + assert svd_1.auxiliary_symbols[0].name == "libcudart.so.12"