//===- GarbageCollection.cpp ----------------------------------------------===// // // The MCLinker Project // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if !defined(MCLD_ON_WIN32) #include #define fnmatch0(pattern,string) (fnmatch(pattern,string,0) == 0) #else #include #include #define fnmatch0(pattern,string) (PathMatchSpec(string, pattern) == true) #endif using namespace mcld; //===----------------------------------------------------------------------===// // Non-member functions //===----------------------------------------------------------------------===// // FIXME: these rules should be added into SectionMap, while currently adding to // SectionMap will cause the output order change in .text section and leads to // the .ARM.exidx order incorrect. We should sort the .ARM.exidx. static const char* pattern_to_keep[] = { ".text*personality*", ".data*personality*", ".gnu.linkonce.d*personality*", ".sdata*personality*" }; /// shouldKeep - check the section name for the keep sections static bool shouldKeep(const std::string& pName) { static const unsigned int pattern_size = sizeof(pattern_to_keep) / sizeof(pattern_to_keep[0]); for (unsigned int i=0; i < pattern_size; ++i) { if (fnmatch0(pattern_to_keep[i], pName.c_str())) return true; } return false; } /// shouldProcessGC - check if the section kind is handled in GC static bool mayProcessGC(const LDSection& pSection) { if (pSection.kind() == LDFileFormat::Regular || pSection.kind() == LDFileFormat::BSS || pSection.kind() == LDFileFormat::GCCExceptTable) return true; return false; } //===----------------------------------------------------------------------===// // GarbageCollection::SectionReachedListMap //===----------------------------------------------------------------------===// void GarbageCollection::SectionReachedListMap::addReference(const LDSection& pFrom, const LDSection& pTo) { m_ReachedSections[&pFrom].insert(&pTo); } GarbageCollection::SectionListTy& GarbageCollection::SectionReachedListMap::getReachedList( const LDSection& pSection) { return m_ReachedSections[&pSection]; } GarbageCollection::SectionListTy* GarbageCollection::SectionReachedListMap::findReachedList( const LDSection& pSection) { ReachedSectionsTy::iterator it = m_ReachedSections.find(&pSection); if (it == m_ReachedSections.end()) return NULL; return &it->second; } //===----------------------------------------------------------------------===// // GarbageCollection //===----------------------------------------------------------------------===// GarbageCollection::GarbageCollection(const LinkerConfig& pConfig, const TargetLDBackend& pBackend, Module& pModule) : m_Config(pConfig), m_Backend(pBackend), m_Module(pModule) { } GarbageCollection::~GarbageCollection() { } bool GarbageCollection::run() { // 1. traverse all the relocations to set up the reached sections of each // section setUpReachedSections(); m_Backend.setUpReachedSectionsForGC(m_Module, m_SectionReachedListMap); // 2. get all sections defined the entry point SectionVecTy entry; getEntrySections(entry); // 3. find all the referenced sections those can be reached by entry findReferencedSections(entry); // 4. stripSections - set the unreached sections to Ignore stripSections(); return true; } void GarbageCollection::setUpReachedSections() { // traverse all the input relocations to setup the reached sections Module::obj_iterator input, inEnd = m_Module.obj_end(); for (input = m_Module.obj_begin(); input != inEnd; ++input) { LDContext::sect_iterator rs, rsEnd = (*input)->context()->relocSectEnd(); for (rs = (*input)->context()->relocSectBegin(); rs != rsEnd; ++rs) { // bypass the discarded relocation section // 1. its section kind is changed to Ignore. (The target section is a // discarded group section.) // 2. it has no reloc data. (All symbols in the input relocs are in the // discarded group sections) LDSection* reloc_sect = *rs; LDSection* apply_sect = reloc_sect->getLink(); if ((LDFileFormat::Ignore == reloc_sect->kind()) || (!reloc_sect->hasRelocData())) continue; // bypass the apply target sections which are not handled by gc if (!mayProcessGC(*apply_sect)) continue; bool add_first = false; SectionListTy* reached_sects = NULL; RelocData::iterator reloc_it, rEnd = reloc_sect->getRelocData()->end(); for (reloc_it = reloc_sect->getRelocData()->begin(); reloc_it != rEnd; ++reloc_it) { Relocation* reloc = llvm::cast(reloc_it); ResolveInfo* sym = reloc->symInfo(); // only the target symbols defined in the input fragments can make the // reference if (NULL == sym) continue; if (!sym->isDefine() || !sym->outSymbol()->hasFragRef()) continue; // only the target symbols defined in the concerned sections can make // the reference const LDSection* target_sect = &sym->outSymbol()->fragRef()->frag()->getParent()->getSection(); if (!mayProcessGC(*target_sect)) continue; // setup the reached list, if we first add the element to reached list // of this section, create an entry in ReachedSections map if (!add_first) { reached_sects = &m_SectionReachedListMap.getReachedList(*apply_sect); add_first = true; } reached_sects->insert(target_sect); } reached_sects = NULL; add_first = false; } } } void GarbageCollection::getEntrySections(SectionVecTy& pEntry) { // all the KEEP sections defined in ldscript are entries, traverse all the // input sections and check the SectionMap to find the KEEP sections Module::obj_iterator obj, objEnd = m_Module.obj_end(); SectionMap& sect_map = m_Module.getScript().sectionMap(); for (obj = m_Module.obj_begin(); obj != objEnd; ++obj) { const std::string input_name = (*obj)->name(); LDContext::sect_iterator sect, sectEnd = (*obj)->context()->sectEnd(); for (sect = (*obj)->context()->sectBegin(); sect != sectEnd; ++sect) { LDSection* section = *sect; if (!mayProcessGC(*section)) continue; SectionMap::Input* sm_input = sect_map.find(input_name, section->name()).second; if (((sm_input != NULL) && (InputSectDesc::Keep == sm_input->policy())) || shouldKeep(section->name())) pEntry.push_back(section); } } // get the sections those the entry symbols defined in if (LinkerConfig::Exec == m_Config.codeGenType() || m_Config.options().isPIE()) { // when building executable // 1. the entry symbol is the entry LDSymbol* entry_sym = m_Module.getNamePool().findSymbol(m_Backend.getEntry(m_Module)); assert(NULL != entry_sym); pEntry.push_back(&entry_sym->fragRef()->frag()->getParent()->getSection()); // 2. the symbols have been seen in dynamice objects are entries NamePool::syminfo_iterator info_it, info_end = m_Module.getNamePool().syminfo_end(); for (info_it = m_Module.getNamePool().syminfo_begin(); info_it != info_end; ++info_it) { ResolveInfo* info = info_it.getEntry(); if (!info->isDefine() || info->isLocal()) continue; if (!info->isInDyn()) continue; LDSymbol* sym = info->outSymbol(); if (NULL == sym || !sym->hasFragRef()) continue; // only the target symbols defined in the concerned sections can be // entries const LDSection* sect = &sym->fragRef()->frag()->getParent()->getSection(); if (!mayProcessGC(*sect)) continue; pEntry.push_back(sect); } } else { // when building shared objects, the global define symbols are entries NamePool::syminfo_iterator info_it, info_end = m_Module.getNamePool().syminfo_end(); for (info_it = m_Module.getNamePool().syminfo_begin(); info_it != info_end; ++info_it) { ResolveInfo* info = info_it.getEntry(); if (!info->isDefine() || info->isLocal() || info->shouldForceLocal(m_Config)) continue; LDSymbol* sym = info->outSymbol(); if (NULL == sym || !sym->hasFragRef()) continue; // only the target symbols defined in the concerned sections can be // entries const LDSection* sect = &sym->fragRef()->frag()->getParent()->getSection(); if (!mayProcessGC(*sect)) continue; pEntry.push_back(sect); } } } void GarbageCollection::findReferencedSections(SectionVecTy& pEntry) { // list of sections waiting to be processed typedef std::queue WorkListTy; WorkListTy work_list; // start from each entry, resolve the transitive closure SectionVecTy::iterator entry_it, entry_end = pEntry.end(); for (entry_it = pEntry.begin(); entry_it != entry_end; ++entry_it) { // add entry point to work list work_list.push(*entry_it); // add section from the work_list to the referencedSections until every // reached sections are added while (!work_list.empty()) { const LDSection* sect = work_list.front(); work_list.pop(); // add section to the ReferencedSections, if the section has been put into // referencedSections, skip this section if (!m_ReferencedSections.insert(sect).second) continue; // get the section reached list, if the section do not has one, which // means no referenced between it and other sections, then skip it SectionListTy* reach_list = m_SectionReachedListMap.findReachedList(*sect); if (NULL == reach_list) continue; // put the reached sections to work list, skip the one already be in // referencedSections SectionListTy::iterator it, end = reach_list->end(); for (it = reach_list->begin(); it != end; ++it) { if (m_ReferencedSections.find(*it) == m_ReferencedSections.end()) work_list.push(*it); } } } } void GarbageCollection::stripSections() { // Traverse all the input Regular and BSS sections, if a section is not found // in the ReferencedSections, then it should be garbage collected Module::obj_iterator obj, objEnd = m_Module.obj_end(); for (obj = m_Module.obj_begin(); obj != objEnd; ++obj) { LDContext::sect_iterator sect, sectEnd = (*obj)->context()->sectEnd(); for (sect = (*obj)->context()->sectBegin(); sect != sectEnd; ++sect) { LDSection* section = *sect; if (!mayProcessGC(*section)) continue; if (m_ReferencedSections.find(section) == m_ReferencedSections.end()) section->setKind(LDFileFormat::Ignore); } } // Traverse all the relocation sections, if its target section is set to // Ignore, then set the relocation section to Ignore as well Module::obj_iterator input, inEnd = m_Module.obj_end(); for (input = m_Module.obj_begin(); input != inEnd; ++input) { LDContext::sect_iterator rs, rsEnd = (*input)->context()->relocSectEnd(); for (rs = (*input)->context()->relocSectBegin(); rs != rsEnd; ++rs) { LDSection* reloc_sect = *rs; if (LDFileFormat::Ignore == reloc_sect->getLink()->kind()) reloc_sect->setKind(LDFileFormat::Ignore); } } }