| //===- MipsGOT.cpp --------------------------------------------------------===// |
| // |
| // The MCLinker Project |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include <llvm/Support/Casting.h> |
| #include <llvm/Support/ELF.h> |
| |
| #include <mcld/LD/ResolveInfo.h> |
| #include <mcld/Support/MemoryRegion.h> |
| #include <mcld/Support/MsgHandling.h> |
| #include <mcld/Target/OutputRelocSection.h> |
| |
| #include "MipsGOT.h" |
| #include "MipsRelocator.h" |
| |
| namespace { |
| const size_t MipsGOT0Num = 1; |
| const size_t MipsGOTGpOffset = 0x7FF0; |
| const size_t MipsGOTSize = MipsGOTGpOffset + 0x7FFF; |
| } |
| |
| using namespace mcld; |
| |
| //===----------------------------------------------------------------------===// |
| // MipsGOTEntry |
| //===----------------------------------------------------------------------===// |
| MipsGOTEntry::MipsGOTEntry(uint64_t pContent, SectionData* pParent) |
| : GOT::Entry<4>(pContent, pParent) |
| {} |
| |
| //===----------------------------------------------------------------------===// |
| // MipsGOT::GOTMultipart |
| //===----------------------------------------------------------------------===// |
| MipsGOT::GOTMultipart::GOTMultipart(size_t local, size_t global) |
| : m_LocalNum(local), |
| m_GlobalNum(global), |
| m_ConsumedLocal(0), |
| m_ConsumedGlobal(0), |
| m_pLastLocal(NULL), |
| m_pLastGlobal(NULL) |
| { |
| } |
| |
| bool MipsGOT::GOTMultipart::isConsumed() const |
| { |
| return m_LocalNum == m_ConsumedLocal && |
| m_GlobalNum == m_ConsumedGlobal; |
| } |
| |
| void MipsGOT::GOTMultipart::consumeLocal() |
| { |
| assert(m_ConsumedLocal < m_LocalNum && |
| "Consumed too many local GOT entries"); |
| ++m_ConsumedLocal; |
| m_pLastLocal = llvm::cast<MipsGOTEntry>(m_pLastLocal->getNextNode()); |
| } |
| |
| void MipsGOT::GOTMultipart::consumeGlobal() |
| { |
| assert(m_ConsumedGlobal < m_GlobalNum && |
| "Consumed too many global GOT entries"); |
| ++m_ConsumedGlobal; |
| m_pLastGlobal = llvm::cast<MipsGOTEntry>(m_pLastGlobal->getNextNode()); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // MipsGOT |
| //===----------------------------------------------------------------------===// |
| MipsGOT::MipsGOT(LDSection& pSection) |
| : GOT(pSection), |
| m_pInput(NULL), |
| m_CurrentGOTPart(0) |
| { |
| } |
| |
| SizeTraits<32>::Address MipsGOT::getGPDispAddress() const |
| { |
| return addr() + MipsGOTGpOffset; |
| } |
| |
| void MipsGOT::reserve(size_t pNum) |
| { |
| for (size_t i = 0; i < pNum; i++) { |
| new MipsGOTEntry(0, m_SectionData); |
| } |
| } |
| |
| bool MipsGOT::hasGOT1() const |
| { |
| return !m_MultipartList.empty(); |
| } |
| |
| bool MipsGOT::hasMultipleGOT() const |
| { |
| return m_MultipartList.size() > 1; |
| } |
| |
| void MipsGOT::finalizeScanning(OutputRelocSection& pRelDyn) |
| { |
| for (MultipartListType::iterator it = m_MultipartList.begin(); |
| it != m_MultipartList.end(); ++it) { |
| reserve(MipsGOT0Num); |
| it->m_pLastLocal = llvm::cast<MipsGOTEntry>(&m_SectionData->back()); |
| reserve(it->m_LocalNum); |
| it->m_pLastGlobal = llvm::cast<MipsGOTEntry>(&m_SectionData->back()); |
| reserve(it->m_GlobalNum); |
| |
| if (it == m_MultipartList.begin()) |
| // Reserve entries in the second part of the primary GOT. |
| // These entries correspond to the global symbols in all |
| // non-primary GOTs. |
| reserve(getGlobalNum() - it->m_GlobalNum); |
| else { |
| // Reserve reldyn entries for R_MIPS_REL32 relocations |
| // for all global entries of secondary GOTs. |
| // FIXME: (simon) Do not count local entries for non-pic. |
| size_t count = it->m_GlobalNum + it->m_LocalNum; |
| for (size_t i = 0; i < count; ++i) |
| pRelDyn.reserveEntry(); |
| } |
| } |
| } |
| |
| bool MipsGOT::dynSymOrderCompare(const LDSymbol* pX, const LDSymbol* pY) const |
| { |
| SymbolOrderMapType::const_iterator itX = m_SymbolOrderMap.find(pX); |
| SymbolOrderMapType::const_iterator itY = m_SymbolOrderMap.find(pY); |
| |
| if (itX != m_SymbolOrderMap.end() && itY != m_SymbolOrderMap.end()) |
| return itX->second < itY->second; |
| |
| return itX == m_SymbolOrderMap.end() && itY != m_SymbolOrderMap.end(); |
| } |
| |
| uint64_t MipsGOT::emit(MemoryRegion& pRegion) |
| { |
| uint32_t* buffer = reinterpret_cast<uint32_t*>(pRegion.getBuffer()); |
| |
| uint64_t result = 0; |
| for (iterator it = begin(), ie = end(); |
| it != ie; ++it, ++buffer) { |
| MipsGOTEntry* got = &(llvm::cast<MipsGOTEntry>((*it))); |
| *buffer = static_cast<uint32_t>(got->getValue()); |
| result += got->size(); |
| } |
| return result; |
| } |
| |
| void MipsGOT::initGOTList() |
| { |
| m_SymbolOrderMap.clear(); |
| |
| m_MultipartList.clear(); |
| m_MultipartList.push_back(GOTMultipart()); |
| |
| m_MultipartList.back().m_Inputs.insert(m_pInput); |
| |
| m_MergedGlobalSymbols.clear(); |
| m_InputGlobalSymbols.clear(); |
| m_MergedLocalSymbols.clear(); |
| m_InputLocalSymbols.clear(); |
| } |
| |
| void MipsGOT::changeInput() |
| { |
| m_MultipartList.back().m_Inputs.insert(m_pInput); |
| |
| for (SymbolSetType::iterator it = m_InputLocalSymbols.begin(), |
| end = m_InputLocalSymbols.end(); |
| it != end; ++it) |
| m_MergedLocalSymbols.insert(*it); |
| |
| m_InputLocalSymbols.clear(); |
| |
| for (SymbolUniqueMapType::iterator it = m_InputGlobalSymbols.begin(), |
| end = m_InputGlobalSymbols.end(); |
| it != end; ++it) |
| m_MergedGlobalSymbols.insert(it->first); |
| |
| m_InputGlobalSymbols.clear(); |
| } |
| |
| bool MipsGOT::isGOTFull() const |
| { |
| uint64_t gotCount = MipsGOT0Num + |
| m_MultipartList.back().m_LocalNum + |
| m_MultipartList.back().m_GlobalNum; |
| |
| gotCount += 1; |
| |
| return (gotCount * mcld::MipsGOTEntry::EntrySize) > MipsGOTSize; |
| } |
| |
| void MipsGOT::split() |
| { |
| m_MergedLocalSymbols.clear(); |
| m_MergedGlobalSymbols.clear(); |
| |
| size_t uniqueCount = 0; |
| for (SymbolUniqueMapType::const_iterator it = m_InputGlobalSymbols.begin(), |
| end = m_InputGlobalSymbols.end(); |
| it != end; ++it) { |
| if (it->second) |
| ++uniqueCount; |
| } |
| |
| m_MultipartList.back().m_LocalNum -= m_InputLocalSymbols.size(); |
| m_MultipartList.back().m_GlobalNum -= uniqueCount; |
| m_MultipartList.back().m_Inputs.erase(m_pInput); |
| |
| m_MultipartList.push_back(GOTMultipart(m_InputLocalSymbols.size(), |
| m_InputGlobalSymbols.size())); |
| m_MultipartList.back().m_Inputs.insert(m_pInput); |
| } |
| |
| void MipsGOT::initializeScan(const Input& pInput) |
| { |
| if (m_pInput == NULL) { |
| m_pInput = &pInput; |
| initGOTList(); |
| } |
| else { |
| m_pInput = &pInput; |
| changeInput(); |
| } |
| } |
| |
| void MipsGOT::finalizeScan(const Input& pInput) |
| { |
| } |
| |
| bool MipsGOT::reserveLocalEntry(ResolveInfo& pInfo) |
| { |
| if (pInfo.type() != ResolveInfo::Section) { |
| if (m_InputLocalSymbols.count(&pInfo)) |
| return false; |
| |
| if (m_MergedLocalSymbols.count(&pInfo)) { |
| m_InputLocalSymbols.insert(&pInfo); |
| return false; |
| } |
| } |
| |
| if (isGOTFull()) |
| split(); |
| |
| if (pInfo.type() != ResolveInfo::Section) |
| m_InputLocalSymbols.insert(&pInfo); |
| |
| ++m_MultipartList.back().m_LocalNum; |
| return true; |
| } |
| |
| bool MipsGOT::reserveGlobalEntry(ResolveInfo& pInfo) |
| { |
| if (m_InputGlobalSymbols.count(&pInfo)) |
| return false; |
| |
| if (m_MergedGlobalSymbols.count(&pInfo)) { |
| m_InputGlobalSymbols[&pInfo] = false; |
| return false; |
| } |
| |
| if (isGOTFull()) |
| split(); |
| |
| m_InputGlobalSymbols[&pInfo] = true; |
| ++m_MultipartList.back().m_GlobalNum; |
| |
| if (!(pInfo.reserved() & MipsRelocator::ReserveGot)) { |
| m_SymbolOrderMap[pInfo.outSymbol()] = m_SymbolOrderMap.size(); |
| pInfo.setReserved(pInfo.reserved() | MipsRelocator::ReserveGot); |
| } |
| |
| return true; |
| } |
| |
| bool MipsGOT::isPrimaryGOTConsumed() |
| { |
| return m_CurrentGOTPart > 0; |
| } |
| |
| MipsGOTEntry* MipsGOT::consumeLocal() |
| { |
| assert(m_CurrentGOTPart < m_MultipartList.size() && "GOT number is out of range!"); |
| |
| if (m_MultipartList[m_CurrentGOTPart].isConsumed()) |
| ++m_CurrentGOTPart; |
| |
| m_MultipartList[m_CurrentGOTPart].consumeLocal(); |
| |
| return m_MultipartList[m_CurrentGOTPart].m_pLastLocal; |
| } |
| |
| MipsGOTEntry* MipsGOT::consumeGlobal() |
| { |
| assert(m_CurrentGOTPart < m_MultipartList.size() && "GOT number is out of range!"); |
| |
| if (m_MultipartList[m_CurrentGOTPart].isConsumed()) |
| ++m_CurrentGOTPart; |
| |
| m_MultipartList[m_CurrentGOTPart].consumeGlobal(); |
| |
| return m_MultipartList[m_CurrentGOTPart].m_pLastGlobal; |
| } |
| |
| SizeTraits<32>::Address MipsGOT::getGPAddr(const Input& pInput) const |
| { |
| uint64_t gotSize = 0; |
| for (MultipartListType::const_iterator it = m_MultipartList.begin(); |
| it != m_MultipartList.end(); ++it) { |
| if (it->m_Inputs.count(&pInput)) |
| break; |
| |
| gotSize += (MipsGOT0Num + it->m_LocalNum + it->m_GlobalNum); |
| if (it == m_MultipartList.begin()) |
| gotSize += getGlobalNum() - it->m_GlobalNum; |
| } |
| |
| return addr() + gotSize * MipsGOTEntry::EntrySize + MipsGOTGpOffset; |
| } |
| |
| SizeTraits<32>::Offset MipsGOT::getGPRelOffset(const Input& pInput, |
| const MipsGOTEntry& pEntry) const |
| { |
| SizeTraits<32>::Address gpAddr = getGPAddr(pInput); |
| return addr() + pEntry.getOffset() - gpAddr; |
| } |
| |
| void MipsGOT::recordEntry(const ResolveInfo* pInfo, MipsGOTEntry* pEntry) |
| { |
| GotEntryKey key; |
| key.m_GOTPage = m_CurrentGOTPart; |
| key.m_pInfo = pInfo; |
| m_GotEntriesMap[key] = pEntry; |
| } |
| |
| MipsGOTEntry* MipsGOT::lookupEntry(const ResolveInfo* pInfo) |
| { |
| GotEntryKey key; |
| key.m_GOTPage= m_CurrentGOTPart; |
| key.m_pInfo = pInfo; |
| GotEntryMapType::iterator it = m_GotEntriesMap.find(key); |
| |
| if (it == m_GotEntriesMap.end()) |
| return NULL; |
| |
| return it->second; |
| } |
| |
| size_t MipsGOT::getLocalNum() const |
| { |
| assert(!m_MultipartList.empty() && "GOT is empty!"); |
| return m_MultipartList[0].m_LocalNum + MipsGOT0Num; |
| } |
| |
| size_t MipsGOT::getGlobalNum() const |
| { |
| return m_SymbolOrderMap.size(); |
| } |