/* vim: set sw=4 sts=4 et foldmethod=syntax : */ /* * Copyright (c) 2007 David Leverton * * This file is part of the Paludis package manager. Paludis is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #include "elf_linkage_checker.hh" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace paludis; namespace { struct ElfArchitecture { // not in elf.h; from glibc-2.5/sysdeps/s390/s390-32/dl-machine.h static const unsigned S390_OLD = 0xA390; // not in FreeBSD elf.h static const unsigned MIPS_ABI2 = 32; unsigned _machine; unsigned char _class; bool _bigendian, _mips_n32; bool operator< (const ElfArchitecture &) const PALUDIS_ATTRIBUTE((warn_unused_result)); static unsigned normalise_arch(unsigned arch) { switch (arch) { case EM_MIPS_RS3_LE: return EM_MIPS; case S390_OLD: return EM_S390; } return arch; } template ElfArchitecture(const ElfObject & elf) : _machine(normalise_arch(elf.get_arch())), _class(ElfType_::elf_class), _bigendian(elf.is_big_endian()), _mips_n32(EM_MIPS == _machine && MIPS_ABI2 & elf.get_flags()) { } }; bool ElfArchitecture::operator< (const ElfArchitecture & other) const { if (_machine != other._machine) return _machine < other._machine; if (_class != other._class) return _class < other._class; if (_bigendian != other._bigendian) return _bigendian < other._bigendian; return _mips_n32 < other._mips_n32; } } typedef std::multimap Symlinks; typedef std::map > > Needed; namespace paludis { template <> struct Imp { FSPath root; std::set check_libraries; Mutex mutex; std::map seen; Symlinks symlinks; std::map > libraries; Needed needed; std::vector extra_lib_dirs; template bool check_elf(const FSPath &, std::istream &); void handle_library(const FSPath &, const ElfArchitecture &); template bool check_extra_elf(const FSPath &, std::istream &, std::set &); Imp(const FSPath & the_root, const std::shared_ptr> & the_libraries) : root(the_root) { for (auto it(the_libraries->begin()), it_end(the_libraries->end()); it_end != it; ++it) check_libraries.insert(*it); } }; } ElfLinkageChecker::ElfLinkageChecker(const FSPath & root, const std::shared_ptr> & libraries) : _imp(root, libraries) { } ElfLinkageChecker::~ElfLinkageChecker() { } bool ElfLinkageChecker::check_file(const FSPath & file) { std::string basename(file.basename()); if (! (std::string::npos != basename.find(".so.") || (3 <= basename.length() && ".so" == basename.substr(basename.length() - 3)) || (0 != (file.stat().permissions() & S_IXUSR)))) return false; SafeIFStream stream(file); return _imp->check_elf(file, stream) || _imp->check_elf(file, stream); } template bool Imp::check_elf(const FSPath & file, std::istream & stream) { if (! ElfObject::is_valid_elf(stream)) return false; try { Context ctx("When checking '" + stringify(file) + "' as a " + stringify(ElfType_::elf_class * 32) + "-bit ELF file:"); ElfObject elf(stream); if (ET_EXEC != elf.get_type() && ET_DYN != elf.get_type()) { Log::get_instance()->message("broken_linkage_finder.not_interesting", ll_debug, lc_context) << "File is not an executable or shared library"; return true; } ElfArchitecture arch(elf); elf.resolve_all_strings(); Lock l(mutex); if (check_libraries.empty() && ET_DYN == elf.get_type()) handle_library(file, arch); for (typename ElfObject::SectionIterator sec_it(elf.section_begin()), sec_it_end(elf.section_end()); sec_it_end != sec_it; ++sec_it) { const DynamicSection * dyn_sec(visitor_cast >(*sec_it)); if (0 != dyn_sec) for (typename DynamicSection::EntryIterator ent_it(dyn_sec->entry_begin()), ent_it_end(dyn_sec->entry_end()); ent_it_end != ent_it; ++ent_it) { const DynamicEntryString * ent_str(visitor_cast >(*ent_it)); if (0 != ent_str && "NEEDED" == ent_str->tag_name()) { const std::string & req((*ent_str)()); if (check_libraries.empty() || check_libraries.end() != check_libraries.find(req)) { Log::get_instance()->message("broken_linkage_finder.depends", ll_debug, lc_context) << "File depends on " << req; needed[arch][req].push_back(file); } } } } } catch (const InvalidElfFileError & e) { Log::get_instance()->message("broken_linkage_finder.invalid", ll_warning, lc_no_context) << "'" << file << "' appears to be invalid or corrupted: " << e.message(); } return true; } void Imp::handle_library(const FSPath & file, const ElfArchitecture & arch) { seen.insert(std::make_pair(file, arch)); std::pair range(symlinks.equal_range(file)); libraries[arch].push_back(file.basename()); if (range.first != range.second) { Log::get_instance()->message("broken_linkage_finder.known_symlinks_are", ll_debug, lc_context) << "Known symlinks are " << join(second_iterator(range.first), second_iterator(range.second), " "); std::transform(second_iterator(range.first), second_iterator(range.second), std::back_inserter(libraries[arch]), std::mem_fn(&FSPath::basename)); } } void ElfLinkageChecker::note_symlink(const FSPath & link, const FSPath & target) { if (_imp->check_libraries.empty()) { Lock l(_imp->mutex); std::map::const_iterator it(_imp->seen.find(target)); if (_imp->seen.end() != it) { Log::get_instance()->message("broken_linkage_finder.note_symlink", ll_debug, lc_context) << "'" << link << "' is a symlink to known library '" << target << "'"; _imp->libraries[it->second].push_back(link.basename()); } else _imp->symlinks.insert(std::make_pair(target, link)); } } void ElfLinkageChecker::add_extra_lib_dir(const FSPath & dir) { _imp->extra_lib_dirs.push_back(dir); } void ElfLinkageChecker::need_breakage_added( const std::function & callback) { using namespace std::placeholders; typedef std::map > AllMissing; AllMissing all_missing; for (Needed::iterator arch_it(_imp->needed.begin()), arch_it_end(_imp->needed.end()); arch_it_end != arch_it; ++arch_it) { std::sort(_imp->libraries[arch_it->first].begin(), _imp->libraries[arch_it->first].end()); _imp->libraries[arch_it->first].erase( std::unique(_imp->libraries[arch_it->first].begin(), _imp->libraries[arch_it->first].end()), _imp->libraries[arch_it->first].end()); std::vector missing; std::set_difference(first_iterator(arch_it->second.begin()), first_iterator(arch_it->second.end()), _imp->libraries[arch_it->first].begin(), _imp->libraries[arch_it->first].end(), std::back_inserter(missing)); for (std::vector::const_iterator it(missing.begin()), it_end(missing.end()); it_end != it; ++it) all_missing[*it].insert(arch_it->first); } for (std::vector::const_iterator dir_it(_imp->extra_lib_dirs.begin()), dir_it_end(_imp->extra_lib_dirs.end()); dir_it_end != dir_it; ++dir_it) { Context ctx("When searching for missing libraries in '" + stringify(*dir_it) + "':"); for (AllMissing::iterator missing_it(all_missing.begin()), missing_it_end(all_missing.end()); missing_it_end != missing_it; ++missing_it) { if (missing_it->second.empty()) continue; FSPath file(dereference_with_root(*dir_it / missing_it->first, _imp->root)); if (! file.stat().is_regular_file()) { Log::get_instance()->message("broken_linkage_finder.missing", ll_debug, lc_context) << "'" << file << "' is missing or not a regular file"; continue; } try { SafeIFStream stream(file); if (! (_imp->check_extra_elf(file, stream, missing_it->second) || _imp->check_extra_elf(file, stream, missing_it->second))) Log::get_instance()->message("broken_linkage_finder.not_an_elf", ll_debug, lc_no_context) << "'" << file << "' is not an ELF file"; } catch (const SafeIFStreamError & e) { Log::get_instance()->message("broken_linkage_finder.failure", ll_warning, lc_no_context) << "Error opening '" << file << "': '" << e.message() << "' (" << e.what() << ")"; continue; } } } for (AllMissing::const_iterator missing_it(all_missing.begin()), missing_it_end(all_missing.end()); missing_it_end != missing_it; ++missing_it) for (std::set::const_iterator arch_it(missing_it->second.begin()), arch_it_end(missing_it->second.end()); arch_it_end != arch_it; ++arch_it) std::for_each(_imp->needed[*arch_it][missing_it->first].begin(), _imp->needed[*arch_it][missing_it->first].end(), std::bind(callback, _1, missing_it->first)); } template bool Imp::check_extra_elf(const FSPath & file, std::istream & stream, std::set & arches) { if (! ElfObject::is_valid_elf(stream)) return false; Context ctx("When checking '" + stringify(file) + "' as a " + stringify(ElfType_::elf_class * 32) + "-bit ELF file"); try { ElfObject elf(stream); if (ET_DYN == elf.get_type()) { Log::get_instance()->message("broken_linkage_finder.is_library", ll_debug, lc_context) << "'" << file << "' is a library"; arches.erase(ElfArchitecture(elf)); } else Log::get_instance()->message("broken_linkage_finder.is_not_library", ll_debug, lc_context) << "'" << file << "' is not a library"; } catch (const InvalidElfFileError & e) { Log::get_instance()->message("broken_linkage_finder.invalid", ll_warning, lc_no_context) << "'" << file << "' appears to be invalid or corrupted: " << e.message(); } return true; }