You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
358 lines
13 KiB
358 lines
13 KiB
//===- GarbageCollection.cpp ----------------------------------------------===//
|
|
//
|
|
// The MCLinker Project
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
#include "mcld/LD/GarbageCollection.h"
|
|
|
|
#include "mcld/Fragment/Fragment.h"
|
|
#include "mcld/Fragment/Relocation.h"
|
|
#include "mcld/LD/LDContext.h"
|
|
#include "mcld/LD/LDFileFormat.h"
|
|
#include "mcld/LD/LDSection.h"
|
|
#include "mcld/LD/LDSymbol.h"
|
|
#include "mcld/LD/SectionData.h"
|
|
#include "mcld/LD/RelocData.h"
|
|
#include "mcld/LinkerConfig.h"
|
|
#include "mcld/LinkerScript.h"
|
|
#include "mcld/Module.h"
|
|
#include "mcld/Support/MsgHandling.h"
|
|
#include "mcld/Target/TargetLDBackend.h"
|
|
|
|
#include <llvm/Support/Casting.h>
|
|
|
|
#include <queue>
|
|
#if !defined(MCLD_ON_WIN32)
|
|
#include <fnmatch.h>
|
|
#define fnmatch0(pattern, string) (fnmatch(pattern, string, 0) == 0)
|
|
#else
|
|
#include <windows.h>
|
|
#include <shlwapi.h>
|
|
#define fnmatch0(pattern, string) (PathMatchSpec(string, pattern) == true)
|
|
#endif
|
|
|
|
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::TEXT ||
|
|
pSection.kind() == LDFileFormat::DATA ||
|
|
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<Relocation>(reloc_it);
|
|
ResolveInfo* sym = reloc->symInfo();
|
|
// only the target symbols defined in the input fragments can make the
|
|
// reference
|
|
if (sym == NULL)
|
|
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);
|
|
}
|
|
}
|
|
|
|
// when building shared object or the --export-dynamic has been given, the
|
|
// global define symbols are entries
|
|
if (LinkerConfig::DynObj == m_Config.codeGenType() ||
|
|
m_Config.options().exportDynamic()) {
|
|
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 (sym == NULL || !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);
|
|
}
|
|
}
|
|
|
|
// when building executable or PIE
|
|
if (LinkerConfig::Exec == m_Config.codeGenType() ||
|
|
m_Config.options().isPIE()) {
|
|
// 1. the entry symbol is the entry
|
|
LDSymbol* entry_sym =
|
|
m_Module.getNamePool().findSymbol(m_Backend.getEntry(m_Module));
|
|
assert(entry_sym != NULL);
|
|
pEntry.push_back(&entry_sym->fragRef()->frag()->getParent()->getSection());
|
|
|
|
// 2. the symbols have been seen in dynamic objects are entries. If
|
|
// --export-dynamic is set, then these sections already been added. No need
|
|
// to add them again
|
|
if (!m_Config.options().exportDynamic()) {
|
|
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 (sym == NULL || !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);
|
|
}
|
|
}
|
|
}
|
|
|
|
// symbols set by -u should not be garbage collected. Set them entries.
|
|
GeneralOptions::const_undef_sym_iterator usym;
|
|
GeneralOptions::const_undef_sym_iterator usymEnd =
|
|
m_Config.options().undef_sym_end();
|
|
for (usym = m_Config.options().undef_sym_begin(); usym != usymEnd; ++usym) {
|
|
LDSymbol* sym = m_Module.getNamePool().findSymbol(*usym);
|
|
assert(sym);
|
|
ResolveInfo* info = sym->resolveInfo();
|
|
assert(info);
|
|
if (!info->isDefine() || !sym->hasFragRef())
|
|
continue;
|
|
// only the 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<const LDSection*> 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 (reach_list == NULL)
|
|
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);
|
|
debug(diag::debug_print_gc_sections) << section->name()
|
|
<< (*obj)->name();
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace mcld
|