Linkloader improvement: mclinker.
Change-Id: I8805e39ccbc2ee204234fb3e71c70c906f3990bb
diff --git a/lib/Target/Mips/Android.mk b/lib/Target/Mips/Android.mk
new file mode 100644
index 0000000..cc5a211
--- /dev/null
+++ b/lib/Target/Mips/Android.mk
@@ -0,0 +1,39 @@
+LOCAL_PATH:= $(call my-dir)
+
+mcld_mips_target_SRC_FILES := \
+ MipsAndroidSectLinker.cpp \
+ MipsELFDynamic.cpp \
+ MipsELFSectLinker.cpp \
+ MipsGOT.cpp \
+ MipsLDBackend.cpp \
+ MipsRelocationFactory.cpp \
+ MipsSectLinker.cpp \
+ MipsTargetMachine.cpp
+
+# For the host
+# =====================================================
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(mcld_mips_target_SRC_FILES)
+LOCAL_MODULE:= libmcldMipsTarget
+
+LOCAL_MODULE_TAGS := optional
+
+include $(MCLD_HOST_BUILD_MK)
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+# For the device
+# =====================================================
+ifeq ($(TARGET_ARCH),mips)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(mcld_mips_target_SRC_FILES)
+LOCAL_MODULE:= libmcldMipsTarget
+
+LOCAL_MODULE_TAGS := optional
+
+include $(MCLD_DEVICE_BUILD_MK)
+include $(BUILD_STATIC_LIBRARY)
+
+endif
diff --git a/lib/Target/Mips/Mips.h b/lib/Target/Mips/Mips.h
new file mode 100644
index 0000000..a63be45
--- /dev/null
+++ b/lib/Target/Mips/Mips.h
@@ -0,0 +1,20 @@
+//===- Mips.h -------------------------------------------------------------===//
+//
+// The MCLinker Project
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+#ifndef MCLD_MIPS_H
+#define MCLD_MIPS_H
+
+#include "mcld/Support/TargetRegistry.h"
+
+namespace mcld {
+
+extern mcld::Target TheMipselTarget;
+
+} // namespace of mcld
+
+#endif
diff --git a/lib/Target/Mips/MipsAndroidSectLinker.cpp b/lib/Target/Mips/MipsAndroidSectLinker.cpp
new file mode 100644
index 0000000..e697fbc
--- /dev/null
+++ b/lib/Target/Mips/MipsAndroidSectLinker.cpp
@@ -0,0 +1,33 @@
+//===- MipsAndroidSectLinker.cpp ------------------------------------------===//
+//
+// The MCLinker Project
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MipsAndroidSectLinker.h"
+
+#include <mcld/CodeGen/SectLinkerOption.h>
+
+using namespace mcld;
+
+MipsAndroidSectLinker::MipsAndroidSectLinker(SectLinkerOption &pOption,
+ TargetLDBackend &pLDBackend)
+ : AndroidSectLinker(pOption,
+ pLDBackend) {
+ MCLDInfo &info = pOption.info();
+ // set up target-dependent constraints of attibutes
+ info.attrFactory().constraint().disableWholeArchive();
+ info.attrFactory().constraint().disableAsNeeded();
+ info.attrFactory().constraint().setSharedSystem();
+
+ // set up the predefined attributes
+ info.attrFactory().predefined().unsetWholeArchive();
+ info.attrFactory().predefined().setDynamic();
+}
+
+MipsAndroidSectLinker::~MipsAndroidSectLinker()
+{
+}
diff --git a/lib/Target/Mips/MipsAndroidSectLinker.h b/lib/Target/Mips/MipsAndroidSectLinker.h
new file mode 100644
index 0000000..ba216e4
--- /dev/null
+++ b/lib/Target/Mips/MipsAndroidSectLinker.h
@@ -0,0 +1,36 @@
+//===- MipsAndroidSectLinker.h --------------------------------------------===//
+//
+// The MCLinker Project
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+#ifndef MIPS_ANDROIDSECTLINKER_H
+#define MIPS_ANDROIDSECTLINKER_H
+
+#ifdef ENABLE_UNITTEST
+#include <gtest.h>
+#endif
+
+#include "mcld/Target/AndroidSectLinker.h"
+
+namespace mcld
+{
+
+/** \class MipsAndroidSectLinker
+ * \brief MipsAndroidSectLinker sets up the environment for linking.
+ *
+ */
+class MipsAndroidSectLinker : public AndroidSectLinker
+{
+public:
+ MipsAndroidSectLinker(SectLinkerOption &pOption,
+ mcld::TargetLDBackend &pLDBackend);
+
+ ~MipsAndroidSectLinker();
+};
+
+} // namespace of mcld
+
+#endif
diff --git a/lib/Target/Mips/MipsELFDynamic.cpp b/lib/Target/Mips/MipsELFDynamic.cpp
new file mode 100644
index 0000000..cd692ac
--- /dev/null
+++ b/lib/Target/Mips/MipsELFDynamic.cpp
@@ -0,0 +1,90 @@
+//===- MipsELFDynamic.cpp -------------------------------------------------===//
+//
+// The MCLinker Project
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include <mcld/LD/ELFFileFormat.h>
+#include <mcld/Target/GNULDBackend.h>
+#include "MipsELFDynamic.h"
+#include "MipsLDBackend.h"
+
+using namespace mcld;
+
+// MIPS mandatory dynamic section entries
+enum {
+ MIPS_RLD_VERSION = 0x70000001,
+ MIPS_FLAGS = 0x70000005,
+ MIPS_BASE_ADDRESS = 0x70000006,
+ MIPS_LOCAL_GOTNO = 0x7000000a,
+ MIPS_SYMTABNO = 0x70000011,
+ MIPS_GOTSYM = 0x70000013,
+};
+
+MipsELFDynamic::MipsELFDynamic(const MipsGNULDBackend& pParent)
+ : ELFDynamic(pParent),
+ m_pParent(pParent)
+{
+}
+
+MipsELFDynamic::~MipsELFDynamic()
+{
+}
+
+void MipsELFDynamic::reserveTargetEntries(const ELFFileFormat& pFormat)
+{
+ // reservePLTGOT
+ if (pFormat.hasGOT())
+ reserveOne(llvm::ELF::DT_PLTGOT);
+
+ reserveOne(MIPS_RLD_VERSION);
+ reserveOne(MIPS_FLAGS);
+ reserveOne(MIPS_BASE_ADDRESS);
+ reserveOne(MIPS_LOCAL_GOTNO);
+ reserveOne(MIPS_SYMTABNO);
+ reserveOne(MIPS_GOTSYM);
+}
+
+void MipsELFDynamic::applyTargetEntries(const ELFFileFormat& pFormat)
+{
+ // applyPLTGOT
+ if (pFormat.hasGOT())
+ applyOne(llvm::ELF::DT_PLTGOT, pFormat.getGOT().addr());
+
+ applyOne(MIPS_RLD_VERSION, 1);
+ applyOne(MIPS_FLAGS, 0);
+ applyOne(MIPS_BASE_ADDRESS, 0);
+ applyOne(MIPS_LOCAL_GOTNO, getLocalGotNum(pFormat));
+ applyOne(MIPS_SYMTABNO, getSymTabNum(pFormat));
+ applyOne(MIPS_GOTSYM, getGotSym(pFormat));
+}
+
+size_t MipsELFDynamic::getSymTabNum(const ELFFileFormat& pFormat) const
+{
+ if (!pFormat.hasDynSymTab())
+ return 0;
+
+ const LDSection& dynsym = pFormat.getDynSymTab();
+ return dynsym.size() / symbolSize();
+}
+
+size_t MipsELFDynamic::getGotSym(const ELFFileFormat& pFormat) const
+{
+ if (!pFormat.hasGOT())
+ return 0;
+
+ return getSymTabNum(pFormat) -
+ m_pParent.getGOT().getTotalNum() +
+ m_pParent.getGOT().getLocalNum();
+}
+
+size_t MipsELFDynamic::getLocalGotNum(const ELFFileFormat& pFormat) const
+{
+ if (!pFormat.hasGOT())
+ return 0;
+
+ return m_pParent.getGOT().getLocalNum();
+}
diff --git a/lib/Target/Mips/MipsELFDynamic.h b/lib/Target/Mips/MipsELFDynamic.h
new file mode 100644
index 0000000..118e4f9
--- /dev/null
+++ b/lib/Target/Mips/MipsELFDynamic.h
@@ -0,0 +1,41 @@
+//===- MipsELFDynamic.h ---------------------------------------------------===//
+//
+// The MCLinker Project
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+#ifndef MCLD_Mips_ELFDYNAMIC_SECTION_H
+#define MCLD_Mips_ELFDYNAMIC_SECTION_H
+#ifdef ENABLE_UNITTEST
+#include <gtest.h>
+#endif
+
+#include <mcld/Target/ELFDynamic.h>
+
+namespace mcld {
+
+class MipsGNULDBackend;
+
+class MipsELFDynamic : public ELFDynamic
+{
+public:
+ MipsELFDynamic(const MipsGNULDBackend& pParent);
+ ~MipsELFDynamic();
+
+private:
+ const MipsGNULDBackend& m_pParent;
+
+private:
+ void reserveTargetEntries(const ELFFileFormat& pFormat);
+ void applyTargetEntries(const ELFFileFormat& pFormat);
+
+ size_t getSymTabNum(const ELFFileFormat& pFormat) const;
+ size_t getGotSym(const ELFFileFormat& pFormat) const;
+ size_t getLocalGotNum(const ELFFileFormat& pFormat) const;
+};
+
+} // namespace of mcld
+
+#endif
diff --git a/lib/Target/Mips/MipsELFSectLinker.cpp b/lib/Target/Mips/MipsELFSectLinker.cpp
new file mode 100644
index 0000000..aa41f36
--- /dev/null
+++ b/lib/Target/Mips/MipsELFSectLinker.cpp
@@ -0,0 +1,33 @@
+//===- MipsELFSectLinker.cpp ----------------------------------------------===//
+//
+// The MCLinker Project
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MipsELFSectLinker.h"
+
+#include <mcld/CodeGen/SectLinkerOption.h>
+
+using namespace mcld;
+
+MipsELFSectLinker::MipsELFSectLinker(SectLinkerOption &pOption,
+ TargetLDBackend &pLDBackend)
+ : SectLinker(pOption,
+ pLDBackend) {
+ MCLDInfo &info = pOption.info();
+ // set up target-dependent constraints of attibutes
+ info.attrFactory().constraint().enableWholeArchive();
+ info.attrFactory().constraint().disableAsNeeded();
+ info.attrFactory().constraint().setSharedSystem();
+
+ // set up the predefined attributes
+ info.attrFactory().predefined().setWholeArchive();
+ info.attrFactory().predefined().setDynamic();
+}
+
+MipsELFSectLinker::~MipsELFSectLinker()
+{
+}
diff --git a/lib/Target/Mips/MipsELFSectLinker.h b/lib/Target/Mips/MipsELFSectLinker.h
new file mode 100644
index 0000000..82bec4c
--- /dev/null
+++ b/lib/Target/Mips/MipsELFSectLinker.h
@@ -0,0 +1,34 @@
+//===- MipsELFSectLinker.h ------------------------------------------------===//
+//
+// The MCLinker Project
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+#ifndef MIPS_ELFSECTLINKER_H
+#define MIPS_ELFSECTLINKER_H
+#ifdef ENABLE_UNITTEST
+#include <gtest.h>
+#endif
+#include <mcld/CodeGen/SectLinker.h>
+
+namespace mcld
+{
+
+/** \class MipsELFSectLinker
+ * \brief MipsELFSectLinker sets up the environment for linking.
+ */
+class MipsELFSectLinker : public SectLinker
+{
+public:
+ MipsELFSectLinker(SectLinkerOption &pOption,
+ mcld::TargetLDBackend &pLDBackend);
+
+ ~MipsELFSectLinker();
+};
+
+} // namespace of mcld
+
+#endif
+
diff --git a/lib/Target/Mips/MipsGOT.cpp b/lib/Target/Mips/MipsGOT.cpp
new file mode 100644
index 0000000..f9d5bb4
--- /dev/null
+++ b/lib/Target/Mips/MipsGOT.cpp
@@ -0,0 +1,148 @@
+//===- 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/ErrorHandling.h>
+#include <mcld/LD/ResolveInfo.h>
+#include <mcld/Support/MemoryRegion.h>
+#include "MipsGOT.h"
+
+namespace {
+ const size_t MipsGOTEntrySize = 4;
+ const size_t MipsGOT0Num = 1;
+}
+
+using namespace mcld;
+
+//===----------------------------------------------------------------------===//
+// MipsGOT
+MipsGOT::MipsGOT(LDSection& pSection, llvm::MCSectionData& pSectionData)
+ : GOT(pSection, pSectionData, MipsGOTEntrySize),
+ m_pLocalNum(0)
+{
+ // Create GOT0 entries.
+ for (size_t i = 0; i < MipsGOT0Num; ++i) {
+ GOTEntry* entry =
+ new (std::nothrow) GOTEntry(0, MipsGOTEntrySize, &m_SectionData);
+
+ if (NULL == entry)
+ llvm::report_fatal_error("Allocating GOT0 entries failed!");
+
+ m_Section.setSize(m_Section.size() + MipsGOTEntrySize);
+ }
+
+ // Skip GOT0 entries.
+ iterator it = m_SectionData.begin();
+ iterator ie = m_SectionData.end();
+
+ for (size_t i = 1; i < MipsGOT0Num; ++i) {
+ if (it == ie)
+ llvm::report_fatal_error("Generation of GOT0 entries is incomplete!");
+
+ ++it;
+ }
+
+ m_LocalGOTIterator = it;
+ m_GlobalGOTIterator = it;
+ m_pLocalNum = MipsGOT0Num;
+}
+
+MipsGOT::iterator MipsGOT::begin()
+{
+ return m_SectionData.getFragmentList().begin();
+}
+
+MipsGOT::iterator MipsGOT::end()
+{
+ return m_SectionData.getFragmentList().end();
+}
+
+MipsGOT::const_iterator MipsGOT::begin() const
+{
+ return m_SectionData.getFragmentList().begin();
+}
+
+MipsGOT::const_iterator MipsGOT::end() const
+{
+ return m_SectionData.getFragmentList().end();
+}
+
+uint64_t MipsGOT::emit(MemoryRegion& pRegion)
+{
+ uint32_t* buffer = reinterpret_cast<uint32_t*>(pRegion.getBuffer());
+
+ size_t entry_size = getEntrySize();
+
+ uint64_t result = 0;
+ for (iterator it = begin(), ie = end();
+ it != ie; ++it, ++buffer) {
+ GOTEntry* got = &(llvm::cast<GOTEntry>((*it)));
+ *buffer = static_cast<uint32_t>(got->getContent());
+ result += entry_size;
+ }
+ return result;
+}
+
+void MipsGOT::reserveEntry(size_t pNum)
+{
+ for (size_t i = 0; i < pNum; ++i) {
+ GOTEntry* entry =
+ new (std::nothrow) GOTEntry(0, MipsGOTEntrySize, &m_SectionData);
+
+ if (NULL == entry)
+ llvm::report_fatal_error("Allocating new GOTEntry failed");
+
+ m_Section.setSize(m_Section.size() + MipsGOTEntrySize);
+ }
+}
+
+void MipsGOT::reserveLocalEntry()
+{
+ reserveEntry(1);
+ ++m_pLocalNum;
+
+ // Move global entries iterator forward.
+ // We need to put global GOT entries after all local ones.
+ ++m_GlobalGOTIterator;
+}
+
+void MipsGOT::reserveGlobalEntry()
+{
+ reserveEntry(1);
+}
+
+GOTEntry* MipsGOT::getEntry(const ResolveInfo& pInfo, bool& pExist)
+{
+ GOTEntry*& entry = m_GeneralGOTMap[&pInfo];
+
+ pExist = NULL != entry;
+
+ if (!pExist) {
+ iterator& it = pInfo.isLocal() ? m_LocalGOTIterator : m_GlobalGOTIterator;
+
+ ++it;
+
+ assert(it != m_SectionData.getFragmentList().end() &&
+ "The number of GOT Entries and ResolveInfo doesn't match");
+
+ entry = llvm::cast<GOTEntry>(&(*it));
+ }
+
+ return entry;
+}
+
+size_t MipsGOT::getTotalNum() const
+{
+ return m_SectionData.getFragmentList().size();
+}
+
+size_t MipsGOT::getLocalNum() const
+{
+ return m_pLocalNum;
+}
+
diff --git a/lib/Target/Mips/MipsGOT.h b/lib/Target/Mips/MipsGOT.h
new file mode 100644
index 0000000..dccc7ac
--- /dev/null
+++ b/lib/Target/Mips/MipsGOT.h
@@ -0,0 +1,67 @@
+//===- MipsGOT.h ----------------------------------------------------------===//
+//
+// The MCLinker Project
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+#ifndef MCLD_MIPS_GOT_H
+#define MCLD_MIPS_GOT_H
+#ifdef ENABLE_UNITTEST
+#include <gtest.h>
+#endif
+
+#include <mcld/Target/GOT.h>
+
+namespace mcld
+{
+class LDSection;
+class MemoryRegion;
+
+/** \class MipsGOT
+ * \brief Mips Global Offset Table.
+ */
+class MipsGOT : public GOT
+{
+private:
+ typedef llvm::DenseMap<const ResolveInfo*, GOTEntry*> SymbolIndexMapType;
+
+public:
+ typedef llvm::MCSectionData::iterator iterator;
+ typedef llvm::MCSectionData::const_iterator const_iterator;
+
+public:
+ MipsGOT(LDSection& pSection, llvm::MCSectionData& pSectionData);
+
+ iterator begin();
+ iterator end();
+
+ const_iterator begin() const;
+ const_iterator end() const;
+
+ uint64_t emit(MemoryRegion& pRegion);
+
+ void reserveLocalEntry();
+ void reserveGlobalEntry();
+
+ GOTEntry* getEntry(const ResolveInfo& pInfo, bool& pExist);
+
+ size_t getTotalNum() const;
+ size_t getLocalNum() const;
+
+private:
+ SymbolIndexMapType m_GeneralGOTMap;
+ iterator m_LocalGOTIterator; // last local GOT entries
+ iterator m_GlobalGOTIterator; // last global GOT entries
+ size_t m_pLocalNum;
+
+private:
+ // Use reserveLocalEntry()/reserveGlobalEntry() instead of this routine.
+ void reserveEntry(size_t pNum = 1);
+};
+
+} // namespace of mcld
+
+#endif
+
diff --git a/lib/Target/Mips/MipsLDBackend.cpp b/lib/Target/Mips/MipsLDBackend.cpp
new file mode 100644
index 0000000..c7a6b23
--- /dev/null
+++ b/lib/Target/Mips/MipsLDBackend.cpp
@@ -0,0 +1,894 @@
+//===- MipsLDBackend.cpp --------------------------------------------------===//
+//
+// The MCLinker Project
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include <llvm/ADT/Triple.h>
+#include <llvm/Support/ELF.h>
+
+#include <mcld/LD/SectionMap.h>
+#include <mcld/MC/MCLDInfo.h>
+#include <mcld/MC/MCLinker.h>
+#include <mcld/Support/MemoryRegion.h>
+#include <mcld/Support/TargetRegistry.h>
+#include <mcld/Target/OutputRelocSection.h>
+
+#include "Mips.h"
+#include "MipsELFDynamic.h"
+#include "MipsLDBackend.h"
+#include "MipsRelocationFactory.h"
+
+enum {
+ // The original o32 abi.
+ E_MIPS_ABI_O32 = 0x00001000,
+ // O32 extended to work on 64 bit architectures.
+ E_MIPS_ABI_O64 = 0x00002000,
+ // EABI in 32 bit mode.
+ E_MIPS_ABI_EABI32 = 0x00003000,
+ // EABI in 64 bit mode.
+ E_MIPS_ABI_EABI64 = 0x00004000
+};
+
+namespace mcld {
+
+MipsGNULDBackend::MipsGNULDBackend()
+ : m_pRelocFactory(NULL),
+ m_pGOT(NULL),
+ m_pRelDyn(NULL),
+ m_pDynamic(NULL),
+ m_pGOTSymbol(NULL),
+ m_pGpDispSymbol(NULL)
+{
+}
+
+MipsGNULDBackend::~MipsGNULDBackend()
+{
+ if (NULL != m_pRelocFactory)
+ delete m_pRelocFactory;
+ if (NULL != m_pGOT)
+ delete m_pGOT;
+ if (NULL != m_pRelDyn)
+ delete m_pRelDyn;
+ if (NULL != m_pDynamic)
+ delete m_pDynamic;
+}
+
+bool MipsGNULDBackend::initTargetSectionMap(SectionMap& pSectionMap)
+{
+ // Nothing to do because we do not support
+ // any MIPS specific sections now.
+ return true;
+}
+
+void MipsGNULDBackend::initTargetSections(MCLinker& pLinker)
+{
+ // Nothing to do because we do not support
+ // any MIPS specific sections now.
+}
+
+void MipsGNULDBackend::initTargetSymbols(MCLinker& pLinker)
+{
+ // Define the symbol _GLOBAL_OFFSET_TABLE_ if there is a symbol with the
+ // same name in input
+ m_pGOTSymbol = pLinker.defineSymbol<MCLinker::AsRefered, MCLinker::Resolve>(
+ "_GLOBAL_OFFSET_TABLE_",
+ false,
+ ResolveInfo::Object,
+ ResolveInfo::Define,
+ ResolveInfo::Local,
+ 0x0, // size
+ 0x0, // value
+ NULL, // FragRef
+ ResolveInfo::Hidden);
+
+ m_pGpDispSymbol = pLinker.defineSymbol<MCLinker::AsRefered, MCLinker::Resolve>(
+ "_gp_disp",
+ false,
+ ResolveInfo::Section,
+ ResolveInfo::Define,
+ ResolveInfo::Absolute,
+ 0x0, // size
+ 0x0, // value
+ NULL, // FragRef
+ ResolveInfo::Default);
+
+ if (NULL != m_pGpDispSymbol) {
+ m_pGpDispSymbol->resolveInfo()->setReserved(ReserveGpDisp);
+ }
+}
+
+bool MipsGNULDBackend::initRelocFactory(const MCLinker& pLinker)
+{
+ if (NULL == m_pRelocFactory) {
+ m_pRelocFactory = new MipsRelocationFactory(1024, *this);
+ m_pRelocFactory->setLayout(pLinker.getLayout());
+ }
+ return true;
+}
+
+RelocationFactory* MipsGNULDBackend::getRelocFactory()
+{
+ assert(NULL != m_pRelocFactory);
+ return m_pRelocFactory;
+}
+
+void MipsGNULDBackend::scanRelocation(Relocation& pReloc,
+ const LDSymbol& pInputSym,
+ MCLinker& pLinker,
+ const MCLDInfo& pLDInfo,
+ const Output& pOutput)
+{
+ // rsym - The relocation target symbol
+ ResolveInfo* rsym = pReloc.symInfo();
+ assert(NULL != rsym && "ResolveInfo of relocation not set while scanRelocation");
+
+ // A refernece to symbol _GLOBAL_OFFSET_TABLE_ implies
+ // that a .got section is needed.
+ if (NULL == m_pGOT && NULL != m_pGOTSymbol) {
+ if (rsym == m_pGOTSymbol->resolveInfo()) {
+ createGOT(pLinker, pOutput);
+ }
+ }
+
+ if (rsym->isLocal())
+ scanLocalReloc(pReloc, pInputSym, pLinker, pLDInfo, pOutput);
+ else
+ scanGlobalReloc(pReloc, pInputSym, pLinker, pLDInfo, pOutput);
+}
+
+uint32_t MipsGNULDBackend::machine() const
+{
+ return llvm::ELF::EM_MIPS;
+}
+
+uint8_t MipsGNULDBackend::OSABI() const
+{
+ return llvm::ELF::ELFOSABI_NONE;
+}
+
+uint8_t MipsGNULDBackend::ABIVersion() const
+{
+ return 0;
+}
+
+uint64_t MipsGNULDBackend::flags() const
+{
+ // TODO: (simon) The correct flag's set depend on command line
+ // arguments and flags from input .o files.
+ return llvm::ELF::EF_MIPS_ARCH_32R2 |
+ llvm::ELF::EF_MIPS_NOREORDER |
+ llvm::ELF::EF_MIPS_PIC |
+ llvm::ELF::EF_MIPS_CPIC |
+ E_MIPS_ABI_O32;
+}
+
+bool MipsGNULDBackend::isLittleEndian() const
+{
+ // Now we support little endian (mipsel) target only.
+ return true;
+}
+
+unsigned int MipsGNULDBackend::bitclass() const
+{
+ return 32;
+}
+
+void MipsGNULDBackend::doPreLayout(const Output& pOutput,
+ const MCLDInfo& pInfo,
+ MCLinker& pLinker)
+{
+ // when building shared object, the .got section is must.
+ if (pOutput.type() == Output::DynObj && NULL == m_pGOT) {
+ createGOT(pLinker, pOutput);
+ }
+}
+
+void MipsGNULDBackend::doPostLayout(const Output& pOutput,
+ const MCLDInfo& pInfo,
+ MCLinker& pLinker)
+{
+ // emit program headers
+ if (pOutput.type() == Output::DynObj || pOutput.type() == Output::Exec)
+ emitProgramHdrs(pLinker.getLDInfo().output());
+}
+
+/// dynamic - the dynamic section of the target machine.
+/// Use co-variant return type to return its own dynamic section.
+MipsELFDynamic& MipsGNULDBackend::dynamic()
+{
+ if (NULL == m_pDynamic)
+ m_pDynamic = new MipsELFDynamic(*this);
+
+ return *m_pDynamic;
+}
+
+/// dynamic - the dynamic section of the target machine.
+/// Use co-variant return type to return its own dynamic section.
+const MipsELFDynamic& MipsGNULDBackend::dynamic() const
+{
+ assert( NULL != m_pDynamic);
+ return *m_pDynamic;
+}
+
+uint64_t MipsGNULDBackend::emitSectionData(const Output& pOutput,
+ const LDSection& pSection,
+ const MCLDInfo& pInfo,
+ MemoryRegion& pRegion) const
+{
+ assert(pRegion.size() && "Size of MemoryRegion is zero!");
+
+ ELFFileFormat* file_format = getOutputFormat(pOutput);
+
+ if (&pSection == &(file_format->getGOT())) {
+ assert(NULL != m_pGOT && "emitSectionData failed, m_pGOT is NULL!");
+ uint64_t result = m_pGOT->emit(pRegion);
+ return result;
+ }
+
+ llvm::report_fatal_error(llvm::Twine("Unable to emit section `") +
+ pSection.name() +
+ llvm::Twine("'.\n"));
+ return 0;
+}
+/// isGOTSymbol - return true if the symbol is the GOT entry.
+bool MipsGNULDBackend::isGOTSymbol(const LDSymbol& pSymbol) const
+{
+ return std::find(m_LocalGOTSyms.begin(),
+ m_LocalGOTSyms.end(), &pSymbol) != m_LocalGOTSyms.end() ||
+ std::find(m_GlobalGOTSyms.begin(),
+ m_GlobalGOTSyms.end(), &pSymbol) != m_GlobalGOTSyms.end();
+}
+
+/// emitDynamicSymbol - emit dynamic symbol.
+void MipsGNULDBackend::emitDynamicSymbol(llvm::ELF::Elf32_Sym& sym32,
+ Output& pOutput,
+ LDSymbol& pSymbol,
+ const Layout& pLayout,
+ char* strtab,
+ size_t strtabsize,
+ size_t symtabIdx)
+{
+ // maintain output's symbol and index map
+ bool sym_exist = false;
+ HashTableType::entry_type* entry = 0;
+ entry = m_pSymIndexMap->insert(&pSymbol, sym_exist);
+ entry->setValue(symtabIdx);
+
+ // FIXME: check the endian between host and target
+ // write out symbol
+ sym32.st_name = strtabsize;
+ sym32.st_value = pSymbol.value();
+ sym32.st_size = getSymbolSize(pSymbol);
+ sym32.st_info = getSymbolInfo(pSymbol);
+ sym32.st_other = pSymbol.visibility();
+ sym32.st_shndx = getSymbolShndx(pSymbol, pLayout);
+ // write out string
+ strcpy((strtab + strtabsize), pSymbol.name());
+}
+
+/// emitNamePools - emit dynamic name pools - .dyntab, .dynstr, .hash
+///
+/// the size of these tables should be computed before layout
+/// layout should computes the start offset of these tables
+void MipsGNULDBackend::emitDynNamePools(Output& pOutput,
+ SymbolCategory& pSymbols,
+ const Layout& pLayout,
+ const MCLDInfo& pLDInfo)
+{
+ assert(pOutput.hasMemArea());
+ ELFFileFormat* file_format = getOutputFormat(pOutput);
+
+ LDSection& symtab_sect = file_format->getDynSymTab();
+ LDSection& strtab_sect = file_format->getDynStrTab();
+ LDSection& hash_sect = file_format->getHashTab();
+ LDSection& dyn_sect = file_format->getDynamic();
+
+ MemoryRegion* symtab_region = pOutput.memArea()->request(symtab_sect.offset(),
+ symtab_sect.size());
+ MemoryRegion* strtab_region = pOutput.memArea()->request(strtab_sect.offset(),
+ strtab_sect.size());
+ MemoryRegion* hash_region = pOutput.memArea()->request(hash_sect.offset(),
+ hash_sect.size());
+ MemoryRegion* dyn_region = pOutput.memArea()->request(dyn_sect.offset(),
+ dyn_sect.size());
+ // set up symtab_region
+ llvm::ELF::Elf32_Sym* symtab32 = NULL;
+ symtab32 = (llvm::ELF::Elf32_Sym*)symtab_region->start();
+
+ symtab32[0].st_name = 0;
+ symtab32[0].st_value = 0;
+ symtab32[0].st_size = 0;
+ symtab32[0].st_info = 0;
+ symtab32[0].st_other = 0;
+ symtab32[0].st_shndx = 0;
+
+ // set up strtab_region
+ char* strtab = (char*)strtab_region->start();
+ strtab[0] = '\0';
+
+ bool sym_exist = false;
+ HashTableType::entry_type* entry = 0;
+
+ // add index 0 symbol into SymIndexMap
+ entry = m_pSymIndexMap->insert(NULL, sym_exist);
+ entry->setValue(0);
+
+ size_t symtabIdx = 1;
+ size_t strtabsize = 1;
+
+ // emit of .dynsym, and .dynstr except GOT entries
+ for (SymbolCategory::iterator symbol = pSymbols.begin(),
+ sym_end = pSymbols.end(); symbol != sym_end; ++symbol) {
+ if (!isDynamicSymbol(**symbol, pOutput))
+ continue;
+
+ if (isGOTSymbol(**symbol))
+ continue;
+
+ emitDynamicSymbol(symtab32[symtabIdx], pOutput, **symbol, pLayout, strtab,
+ strtabsize, symtabIdx);
+
+ // sum up counters
+ ++symtabIdx;
+ strtabsize += (*symbol)->nameSize() + 1;
+ }
+
+ // emit global GOT
+ for (std::vector<LDSymbol*>::const_iterator symbol = m_GlobalGOTSyms.begin(),
+ symbol_end = m_GlobalGOTSyms.end();
+ symbol != symbol_end; ++symbol) {
+
+ emitDynamicSymbol(symtab32[symtabIdx], pOutput, **symbol, pLayout, strtab,
+ strtabsize, symtabIdx);
+
+ // sum up counters
+ ++symtabIdx;
+ strtabsize += (*symbol)->nameSize() + 1;
+ }
+
+ // emit DT_NEED
+ // add DT_NEED strings into .dynstr
+ // Rules:
+ // 1. ignore --no-add-needed
+ // 2. force count in --no-as-needed
+ // 3. judge --as-needed
+ ELFDynamic::iterator dt_need = dynamic().needBegin();
+ InputTree::const_bfs_iterator input, inputEnd = pLDInfo.inputs().bfs_end();
+ for (input = pLDInfo.inputs().bfs_begin(); input != inputEnd; ++input) {
+ if (Input::DynObj == (*input)->type()) {
+ // --add-needed
+ if ((*input)->attribute()->isAddNeeded()) {
+ // --no-as-needed
+ if (!(*input)->attribute()->isAsNeeded()) {
+ strcpy((strtab + strtabsize), (*input)->name().c_str());
+ (*dt_need)->setValue(llvm::ELF::DT_NEEDED, strtabsize);
+ strtabsize += (*input)->name().size() + 1;
+ ++dt_need;
+ }
+ // --as-needed
+ else if ((*input)->isNeeded()) {
+ strcpy((strtab + strtabsize), (*input)->name().c_str());
+ (*dt_need)->setValue(llvm::ELF::DT_NEEDED, strtabsize);
+ strtabsize += (*input)->name().size() + 1;
+ ++dt_need;
+ }
+ }
+ }
+ } // for
+
+ // emit soname
+ // initialize value of ELF .dynamic section
+ dynamic().applySoname(strtabsize);
+ dynamic().applyEntries(pLDInfo, *file_format);
+ dynamic().emit(dyn_sect, *dyn_region);
+
+ strcpy((strtab + strtabsize), pOutput.name().c_str());
+ strtabsize += pOutput.name().size() + 1;
+
+ // emit hash table
+ // FIXME: this verion only emit SVR4 hash section.
+ // Please add GNU new hash section
+
+ // both 32 and 64 bits hash table use 32-bit entry
+ // set up hash_region
+ uint32_t* word_array = (uint32_t*)hash_region->start();
+ uint32_t& nbucket = word_array[0];
+ uint32_t& nchain = word_array[1];
+
+ nbucket = getHashBucketCount(symtabIdx, false);
+ nchain = symtabIdx;
+
+ uint32_t* bucket = (word_array + 2);
+ uint32_t* chain = (bucket + nbucket);
+
+ // initialize bucket
+ bzero((void*)bucket, nbucket);
+
+ StringHash<ELF> hash_func;
+
+ for (size_t sym_idx=0; sym_idx < symtabIdx; ++sym_idx) {
+ llvm::StringRef name(strtab + symtab32[sym_idx].st_name);
+ size_t bucket_pos = hash_func(name) % nbucket;
+ chain[sym_idx] = bucket[bucket_pos];
+ bucket[bucket_pos] = sym_idx;
+ }
+
+}
+
+MipsGOT& MipsGNULDBackend::getGOT()
+{
+ assert(NULL != m_pGOT);
+ return *m_pGOT;
+}
+
+const MipsGOT& MipsGNULDBackend::getGOT() const
+{
+ assert(NULL != m_pGOT);
+ return *m_pGOT;
+}
+
+OutputRelocSection& MipsGNULDBackend::getRelDyn()
+{
+ assert(NULL != m_pRelDyn);
+ return *m_pRelDyn;
+}
+
+const OutputRelocSection& MipsGNULDBackend::getRelDyn() const
+{
+ assert(NULL != m_pRelDyn);
+ return *m_pRelDyn;
+}
+
+unsigned int
+MipsGNULDBackend::getTargetSectionOrder(const Output& pOutput,
+ const LDSection& pSectHdr) const
+{
+ ELFFileFormat* file_format = getOutputFormat(pOutput);
+
+ if (&pSectHdr == &file_format->getGOT())
+ return SHO_DATA;
+
+ return SHO_UNDEFINED;
+}
+
+/// finalizeSymbol - finalize the symbol value
+/// If the symbol's reserved field is not zero, MCLinker will call back this
+/// function to ask the final value of the symbol
+bool MipsGNULDBackend::finalizeSymbol(LDSymbol& pSymbol) const
+{
+ if (&pSymbol == m_pGpDispSymbol) {
+ m_pGpDispSymbol->setValue(m_pGOT->getSection().addr() + 0x7FF0);
+ return true;
+ }
+ return false;
+}
+
+/// allocateCommonSymbols - allocate common symbols in the corresponding
+/// sections.
+/// @refer Google gold linker: common.cc: 214
+/// FIXME: Mips needs to allocate small common symbol
+bool
+MipsGNULDBackend::allocateCommonSymbols(const MCLDInfo& pInfo, MCLinker& pLinker) const
+{
+ // SymbolCategory contains all symbols that must emit to the output files.
+ // We are not like Google gold linker, we don't remember symbols before symbol
+ // resolution. All symbols in SymbolCategory are already resolved. Therefore, we
+ // don't need to care about some symbols may be changed its category due to symbol
+ // resolution.
+ SymbolCategory& symbol_list = pLinker.getOutputSymbols();
+
+ if (symbol_list.emptyCommons() && symbol_list.emptyLocals())
+ return true;
+
+ // addralign := max value of all common symbols
+ uint64_t addralign = 0x0;
+
+ // Due to the visibility, some common symbols may be forcefully local.
+ SymbolCategory::iterator com_sym, com_end = symbol_list.localEnd();
+ for (com_sym = symbol_list.localBegin(); com_sym != com_end; ++com_sym) {
+ if (ResolveInfo::Common == (*com_sym)->desc()) {
+ if ((*com_sym)->value() > addralign)
+ addralign = (*com_sym)->value();
+ }
+ }
+
+ // global common symbols.
+ com_end = symbol_list.commonEnd();
+ for (com_sym = symbol_list.commonBegin(); com_sym != com_end; ++com_sym) {
+ if ((*com_sym)->value() > addralign)
+ addralign = (*com_sym)->value();
+ }
+
+ // FIXME: If the order of common symbols is defined, then sort common symbols
+ // com_sym = symbol_list.commonBegin();
+ // std::sort(com_sym, com_end, some kind of order);
+
+ // get or create corresponding BSS LDSection
+ LDSection* bss_sect_hdr = NULL;
+ if (ResolveInfo::ThreadLocal == (*com_sym)->type()) {
+ bss_sect_hdr = &pLinker.getOrCreateOutputSectHdr(
+ ".tbss",
+ LDFileFormat::BSS,
+ llvm::ELF::SHT_NOBITS,
+ llvm::ELF::SHF_WRITE | llvm::ELF::SHF_ALLOC);
+ }
+ else {
+ bss_sect_hdr = &pLinker.getOrCreateOutputSectHdr(".bss",
+ LDFileFormat::BSS,
+ llvm::ELF::SHT_NOBITS,
+ llvm::ELF::SHF_WRITE | llvm::ELF::SHF_ALLOC);
+ }
+
+ // get or create corresponding BSS MCSectionData
+ assert(NULL != bss_sect_hdr);
+ llvm::MCSectionData& bss_section = pLinker.getOrCreateSectData(*bss_sect_hdr);
+
+ // allocate all common symbols
+ uint64_t offset = bss_sect_hdr->size();
+
+ // allocate all local common symbols
+ com_end = symbol_list.localEnd();
+ for (com_sym = symbol_list.localBegin(); com_sym != com_end; ++com_sym) {
+ if (ResolveInfo::Common == (*com_sym)->desc()) {
+ alignAddress(offset, (*com_sym)->value());
+ // We have to reset the description of the symbol here. When doing
+ // incremental linking, the output relocatable object may have common
+ // symbols. Therefore, we can not treat common symbols as normal symbols
+ // when emitting the regular name pools. We must change the symbols'
+ // description here.
+ (*com_sym)->resolveInfo()->setDesc(ResolveInfo::Define);
+ llvm::MCFragment* frag = new llvm::MCFillFragment(0x0, 1, (*com_sym)->size(), &bss_section);
+ (*com_sym)->setFragmentRef(new MCFragmentRef(*frag, 0));
+ offset += (*com_sym)->size();
+ }
+ }
+
+ // allocate all global common symbols
+ com_end = symbol_list.commonEnd();
+ for (com_sym = symbol_list.commonBegin(); com_sym != com_end; ++com_sym) {
+ alignAddress(offset, (*com_sym)->value());
+
+ // We have to reset the description of the symbol here. When doing
+ // incremental linking, the output relocatable object may have common
+ // symbols. Therefore, we can not treat common symbols as normal symbols
+ // when emitting the regular name pools. We must change the symbols'
+ // description here.
+ (*com_sym)->resolveInfo()->setDesc(ResolveInfo::Define);
+ llvm::MCFragment* frag = new llvm::MCFillFragment(0x0, 1, (*com_sym)->size(), &bss_section);
+ (*com_sym)->setFragmentRef(new MCFragmentRef(*frag, 0));
+ offset += (*com_sym)->size();
+ }
+
+ bss_sect_hdr->setSize(offset);
+ symbol_list.changeCommonsToGlobal();
+ return true;
+}
+
+void MipsGNULDBackend::updateAddend(Relocation& pReloc,
+ const LDSymbol& pInputSym,
+ const Layout& pLayout) const
+{
+ // Update value keep in addend if we meet a section symbol
+ if(pReloc.symInfo()->type() == ResolveInfo::Section) {
+ pReloc.setAddend(pLayout.getOutputOffset(
+ *pInputSym.fragRef()) + pReloc.addend());
+ }
+}
+
+void MipsGNULDBackend::scanLocalReloc(Relocation& pReloc,
+ const LDSymbol& pInputSym,
+ MCLinker& pLinker,
+ const MCLDInfo& pLDInfo,
+ const Output& pOutput)
+{
+ ResolveInfo* rsym = pReloc.symInfo();
+
+ updateAddend(pReloc, pInputSym, pLinker.getLayout());
+
+ switch (pReloc.type()){
+ case llvm::ELF::R_MIPS_NONE:
+ case llvm::ELF::R_MIPS_16:
+ break;
+ case llvm::ELF::R_MIPS_32:
+ if (Output::DynObj == pOutput.type()) {
+ // TODO: (simon) The gold linker does not create an entry in .rel.dyn
+ // section if the symbol section flags contains SHF_EXECINSTR.
+ // 1. Find the reason of this condition.
+ // 2. Check this condition here.
+ if (NULL == m_pRelDyn)
+ createRelDyn(pLinker, pOutput);
+
+ m_pRelDyn->reserveEntry(*m_pRelocFactory);
+ rsym->setReserved(rsym->reserved() | ReserveRel);
+ }
+ break;
+ case llvm::ELF::R_MIPS_REL32:
+ case llvm::ELF::R_MIPS_26:
+ case llvm::ELF::R_MIPS_HI16:
+ case llvm::ELF::R_MIPS_LO16:
+ case llvm::ELF::R_MIPS_PC16:
+ case llvm::ELF::R_MIPS_SHIFT5:
+ case llvm::ELF::R_MIPS_SHIFT6:
+ case llvm::ELF::R_MIPS_64:
+ case llvm::ELF::R_MIPS_GOT_PAGE:
+ case llvm::ELF::R_MIPS_GOT_OFST:
+ case llvm::ELF::R_MIPS_SUB:
+ case llvm::ELF::R_MIPS_INSERT_A:
+ case llvm::ELF::R_MIPS_INSERT_B:
+ case llvm::ELF::R_MIPS_DELETE:
+ case llvm::ELF::R_MIPS_HIGHER:
+ case llvm::ELF::R_MIPS_HIGHEST:
+ case llvm::ELF::R_MIPS_SCN_DISP:
+ case llvm::ELF::R_MIPS_REL16:
+ case llvm::ELF::R_MIPS_ADD_IMMEDIATE:
+ case llvm::ELF::R_MIPS_PJUMP:
+ case llvm::ELF::R_MIPS_RELGOT:
+ case llvm::ELF::R_MIPS_JALR:
+ case llvm::ELF::R_MIPS_GLOB_DAT:
+ case llvm::ELF::R_MIPS_COPY:
+ case llvm::ELF::R_MIPS_JUMP_SLOT:
+ break;
+ case llvm::ELF::R_MIPS_GOT16:
+ case llvm::ELF::R_MIPS_CALL16:
+ if (NULL == m_pGOT)
+ createGOT(pLinker, pOutput);
+
+ if (!(rsym->reserved() & MipsGNULDBackend::ReserveGot)) {
+ m_pGOT->reserveLocalEntry();
+ rsym->setReserved(rsym->reserved() | ReserveGot);
+ m_LocalGOTSyms.push_back(rsym->outSymbol());
+ }
+ break;
+ case llvm::ELF::R_MIPS_GPREL32:
+ case llvm::ELF::R_MIPS_GPREL16:
+ case llvm::ELF::R_MIPS_LITERAL:
+ break;
+ case llvm::ELF::R_MIPS_GOT_DISP:
+ case llvm::ELF::R_MIPS_GOT_HI16:
+ case llvm::ELF::R_MIPS_CALL_HI16:
+ case llvm::ELF::R_MIPS_GOT_LO16:
+ case llvm::ELF::R_MIPS_CALL_LO16:
+ break;
+ case llvm::ELF::R_MIPS_TLS_DTPMOD32:
+ case llvm::ELF::R_MIPS_TLS_DTPREL32:
+ case llvm::ELF::R_MIPS_TLS_DTPMOD64:
+ case llvm::ELF::R_MIPS_TLS_DTPREL64:
+ case llvm::ELF::R_MIPS_TLS_GD:
+ case llvm::ELF::R_MIPS_TLS_LDM:
+ case llvm::ELF::R_MIPS_TLS_DTPREL_HI16:
+ case llvm::ELF::R_MIPS_TLS_DTPREL_LO16:
+ case llvm::ELF::R_MIPS_TLS_GOTTPREL:
+ case llvm::ELF::R_MIPS_TLS_TPREL32:
+ case llvm::ELF::R_MIPS_TLS_TPREL64:
+ case llvm::ELF::R_MIPS_TLS_TPREL_HI16:
+ case llvm::ELF::R_MIPS_TLS_TPREL_LO16:
+ break;
+ default:
+ llvm::report_fatal_error(llvm::Twine("Unknown relocation ") +
+ llvm::Twine(pReloc.type()) +
+ llvm::Twine("for the local symbol `") +
+ pReloc.symInfo()->name() +
+ llvm::Twine("'."));
+ }
+}
+
+void MipsGNULDBackend::scanGlobalReloc(Relocation& pReloc,
+ const LDSymbol& pInputSym,
+ MCLinker& pLinker,
+ const MCLDInfo& pLDInfo,
+ const Output& pOutput)
+{
+ ResolveInfo* rsym = pReloc.symInfo();
+
+ switch (pReloc.type()){
+ case llvm::ELF::R_MIPS_NONE:
+ case llvm::ELF::R_MIPS_INSERT_A:
+ case llvm::ELF::R_MIPS_INSERT_B:
+ case llvm::ELF::R_MIPS_DELETE:
+ case llvm::ELF::R_MIPS_TLS_DTPMOD64:
+ case llvm::ELF::R_MIPS_TLS_DTPREL64:
+ case llvm::ELF::R_MIPS_REL16:
+ case llvm::ELF::R_MIPS_ADD_IMMEDIATE:
+ case llvm::ELF::R_MIPS_PJUMP:
+ case llvm::ELF::R_MIPS_RELGOT:
+ case llvm::ELF::R_MIPS_TLS_TPREL64:
+ break;
+ case llvm::ELF::R_MIPS_32:
+ case llvm::ELF::R_MIPS_64:
+ case llvm::ELF::R_MIPS_HI16:
+ case llvm::ELF::R_MIPS_LO16:
+ if (isSymbolNeedsDynRel(*rsym, pOutput)) {
+ if (NULL == m_pRelDyn)
+ createRelDyn(pLinker, pOutput);
+
+ m_pRelDyn->reserveEntry(*m_pRelocFactory);
+ rsym->setReserved(rsym->reserved() | ReserveRel);
+ }
+ break;
+ case llvm::ELF::R_MIPS_GOT16:
+ case llvm::ELF::R_MIPS_CALL16:
+ case llvm::ELF::R_MIPS_GOT_DISP:
+ case llvm::ELF::R_MIPS_GOT_HI16:
+ case llvm::ELF::R_MIPS_CALL_HI16:
+ case llvm::ELF::R_MIPS_GOT_LO16:
+ case llvm::ELF::R_MIPS_CALL_LO16:
+ case llvm::ELF::R_MIPS_GOT_PAGE:
+ case llvm::ELF::R_MIPS_GOT_OFST:
+ if (NULL == m_pGOT)
+ createGOT(pLinker, pOutput);
+
+ if (!(rsym->reserved() & MipsGNULDBackend::ReserveGot)) {
+ m_pGOT->reserveGlobalEntry();
+ rsym->setReserved(rsym->reserved() | ReserveGot);
+ m_GlobalGOTSyms.push_back(rsym->outSymbol());
+ }
+ break;
+ case llvm::ELF::R_MIPS_LITERAL:
+ case llvm::ELF::R_MIPS_GPREL32:
+ llvm::report_fatal_error(llvm::Twine("Relocation ") +
+ llvm::Twine(pReloc.type()) +
+ llvm::Twine(" is not defined for the "
+ "global symbol `") +
+ pReloc.symInfo()->name() +
+ llvm::Twine("'."));
+ break;
+ case llvm::ELF::R_MIPS_GPREL16:
+ break;
+ case llvm::ELF::R_MIPS_26:
+ case llvm::ELF::R_MIPS_PC16:
+ break;
+ case llvm::ELF::R_MIPS_16:
+ case llvm::ELF::R_MIPS_SHIFT5:
+ case llvm::ELF::R_MIPS_SHIFT6:
+ case llvm::ELF::R_MIPS_SUB:
+ case llvm::ELF::R_MIPS_HIGHER:
+ case llvm::ELF::R_MIPS_HIGHEST:
+ case llvm::ELF::R_MIPS_SCN_DISP:
+ break;
+ case llvm::ELF::R_MIPS_TLS_DTPREL32:
+ case llvm::ELF::R_MIPS_TLS_GD:
+ case llvm::ELF::R_MIPS_TLS_LDM:
+ case llvm::ELF::R_MIPS_TLS_DTPREL_HI16:
+ case llvm::ELF::R_MIPS_TLS_DTPREL_LO16:
+ case llvm::ELF::R_MIPS_TLS_GOTTPREL:
+ case llvm::ELF::R_MIPS_TLS_TPREL32:
+ case llvm::ELF::R_MIPS_TLS_TPREL_HI16:
+ case llvm::ELF::R_MIPS_TLS_TPREL_LO16:
+ break;
+ case llvm::ELF::R_MIPS_REL32:
+ break;
+ case llvm::ELF::R_MIPS_JALR:
+ break;
+ case llvm::ELF::R_MIPS_COPY:
+ case llvm::ELF::R_MIPS_GLOB_DAT:
+ case llvm::ELF::R_MIPS_JUMP_SLOT:
+ llvm::report_fatal_error(llvm::Twine("Relocation ") +
+ llvm::Twine(pReloc.type()) +
+ llvm::Twine("for the global symbol `") +
+ pReloc.symInfo()->name() +
+ llvm::Twine("' should only be seen "
+ "by the dynamic linker"));
+ break;
+ default:
+ llvm::report_fatal_error(llvm::Twine("Unknown relocation ") +
+ llvm::Twine(pReloc.type()) +
+ llvm::Twine("for the global symbol `") +
+ pReloc.symInfo()->name() +
+ llvm::Twine("'."));
+ }
+}
+
+bool MipsGNULDBackend::isSymbolNeedsPLT(ResolveInfo& pSym,
+ const Output& pOutput) const
+{
+ return (Output::DynObj == pOutput.type() &&
+ ResolveInfo::Function == pSym.type() &&
+ (pSym.isDyn() || pSym.isUndef()));
+}
+
+bool MipsGNULDBackend::isSymbolNeedsDynRel(ResolveInfo& pSym,
+ const Output& pOutput) const
+{
+ if(pSym.isUndef() && Output::Exec == pOutput.type())
+ return false;
+ if(pSym.isAbsolute())
+ return false;
+ if(Output::DynObj == pOutput.type())
+ return true;
+ if(pSym.isDyn() || pSym.isUndef())
+ return true;
+
+ return false;
+}
+
+void MipsGNULDBackend::createGOT(MCLinker& pLinker, const Output& pOutput)
+{
+ ELFFileFormat* file_format = getOutputFormat(pOutput);
+
+ LDSection& got = file_format->getGOT();
+ m_pGOT = new MipsGOT(got, pLinker.getOrCreateSectData(got));
+
+ // define symbol _GLOBAL_OFFSET_TABLE_ when .got create
+ if( m_pGOTSymbol != NULL ) {
+ pLinker.defineSymbol<MCLinker::Force, MCLinker::Unresolve>(
+ "_GLOBAL_OFFSET_TABLE_",
+ false,
+ ResolveInfo::Object,
+ ResolveInfo::Define,
+ ResolveInfo::Local,
+ 0x0, // size
+ 0x0, // value
+ pLinker.getLayout().getFragmentRef(*(m_pGOT->begin()), 0x0),
+ ResolveInfo::Hidden);
+ }
+ else {
+ m_pGOTSymbol = pLinker.defineSymbol<MCLinker::Force, MCLinker::Resolve>(
+ "_GLOBAL_OFFSET_TABLE_",
+ false,
+ ResolveInfo::Object,
+ ResolveInfo::Define,
+ ResolveInfo::Local,
+ 0x0, // size
+ 0x0, // value
+ pLinker.getLayout().getFragmentRef(*(m_pGOT->begin()), 0x0),
+ ResolveInfo::Hidden);
+ }
+}
+
+void MipsGNULDBackend::createRelDyn(MCLinker& pLinker, const Output& pOutput)
+{
+ ELFFileFormat* file_format = getOutputFormat(pOutput);
+
+ // get .rel.dyn LDSection and create MCSectionData
+ LDSection& reldyn = file_format->getRelDyn();
+ // create MCSectionData and ARMRelDynSection
+ m_pRelDyn = new OutputRelocSection(reldyn,
+ pLinker.getOrCreateSectData(reldyn),
+ 8);
+}
+
+ELFFileFormat* MipsGNULDBackend::getOutputFormat(const Output& pOutput) const
+{
+ switch (pOutput.type()) {
+ case Output::DynObj:
+ return getDynObjFileFormat();
+ case Output::Exec:
+ return getExecFileFormat();
+ case Output::Object:
+ return NULL;
+ default:
+ llvm::report_fatal_error(llvm::Twine("Unsupported output file format: ") +
+ llvm::Twine(pOutput.type()));
+ return NULL;
+ }
+}
+
+//===----------------------------------------------------------------------===//
+/// createMipsLDBackend - the help funtion to create corresponding MipsLDBackend
+///
+static TargetLDBackend* createMipsLDBackend(const llvm::Target& pTarget,
+ const std::string& pTriple)
+{
+ llvm::Triple theTriple(pTriple);
+ if (theTriple.isOSDarwin()) {
+ assert(0 && "MachO linker is not supported yet");
+ }
+ if (theTriple.isOSWindows()) {
+ assert(0 && "COFF linker is not supported yet");
+ }
+ return new MipsGNULDBackend();
+}
+
+} // namespace of mcld
+
+//=============================
+// Force static initialization.
+extern "C" void LLVMInitializeMipsLDBackend() {
+ // Register the linker backend
+ mcld::TargetRegistry::RegisterTargetLDBackend(mcld::TheMipselTarget,
+ mcld::createMipsLDBackend);
+}
diff --git a/lib/Target/Mips/MipsLDBackend.h b/lib/Target/Mips/MipsLDBackend.h
new file mode 100644
index 0000000..cd9e3cd
--- /dev/null
+++ b/lib/Target/Mips/MipsLDBackend.h
@@ -0,0 +1,200 @@
+//===- MipsLDBackend.h ----------------------------------------------------===//
+//
+// The MCLinker Project
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+#ifndef MIPS_LDBACKEND_H
+#define MIPS_LDBACKEND_H
+#include "mcld/Target/GNULDBackend.h"
+#include "MipsELFDynamic.h"
+#include "MipsGOT.h"
+
+namespace mcld {
+
+class MCLinker;
+class OutputRelocSection;
+class SectionMap;
+
+//===----------------------------------------------------------------------===//
+/// MipsGNULDBackend - linker backend of Mips target of GNU ELF format
+///
+class MipsGNULDBackend : public GNULDBackend
+{
+public:
+ enum ReservedEntryType {
+ None = 0, // no reserved entry
+ ReserveRel = 1, // reserve a dynamic relocation entry
+ ReserveGot = 2, // reserve a GOT entry
+ ReserveGpDisp = 8 // reserve _gp_disp symbol
+ };
+
+public:
+ MipsGNULDBackend();
+ ~MipsGNULDBackend();
+
+public:
+ /// initTargetSectionMap - initialize target dependent section mapping.
+ bool initTargetSectionMap(SectionMap& pSectionMap);
+
+ /// initTargetSections - initialize target dependent sections in output
+ void initTargetSections(MCLinker& pLinker);
+
+ /// initTargetSymbols - initialize target dependent symbols in output.
+ void initTargetSymbols(MCLinker& pLinker);
+
+ /// initRelocFactory - create and initialize RelocationFactory.
+ bool initRelocFactory(const MCLinker& pLinker);
+
+ /// getRelocFactory - return relocation factory.
+ RelocationFactory* getRelocFactory();
+
+ /// scanRelocation - determine the empty entries are needed or not and
+ /// create the empty entries if needed.
+ /// For Mips, the GOT, GP, and dynamic relocation entries are check to create.
+ void scanRelocation(Relocation& pReloc,
+ const LDSymbol& pInputSym,
+ MCLinker& pLinker,
+ const MCLDInfo& pLDInfo,
+ const Output& pOutput);
+
+ uint32_t machine() const;
+
+ /// OSABI - the value of e_ident[EI_OSABI]
+ uint8_t OSABI() const;
+
+ /// ABIVersion - the value of e_ident[EI_ABIVRESION]
+ uint8_t ABIVersion() const;
+
+ /// flags - the value of ElfXX_Ehdr::e_flags
+ uint64_t flags() const;
+
+ bool isLittleEndian() const;
+
+ unsigned int bitclass() const;
+
+ /// preLayout - Backend can do any needed modification before layout
+ void doPreLayout(const Output& pOutput,
+ const MCLDInfo& pInfo,
+ MCLinker& pLinker);
+
+ /// postLayout -Backend can do any needed modification after layout
+ void doPostLayout(const Output& pOutput,
+ const MCLDInfo& pInfo,
+ MCLinker& pLinker);
+
+ /// dynamic - the dynamic section of the target machine.
+ /// Use co-variant return type to return its own dynamic section.
+ MipsELFDynamic& dynamic();
+
+ /// dynamic - the dynamic section of the target machine.
+ /// Use co-variant return type to return its own dynamic section.
+ const MipsELFDynamic& dynamic() const;
+
+ /// emitSectionData - write out the section data into the memory region.
+ /// When writers get a LDSection whose kind is LDFileFormat::Target, writers
+ /// call back target backend to emit the data.
+ ///
+ /// Backends handle the target-special tables (plt, gp,...) by themselves.
+ /// Backend can put the data of the tables in MCSectionData directly
+ /// - LDSection.getSectionData can get the section data.
+ /// Or, backend can put the data into special data structure
+ /// - backend can maintain its own map<LDSection, table> to get the table
+ /// from given LDSection.
+ ///
+ /// @param pOutput - the output file
+ /// @param pSection - the given LDSection
+ /// @param pInfo - all options in the command line.
+ /// @param pRegion - the region to write out data
+ /// @return the size of the table in the file.
+ uint64_t emitSectionData(const Output& pOutput,
+ const LDSection& pSection,
+ const MCLDInfo& pInfo,
+ MemoryRegion& pRegion) const;
+
+ /// emitNamePools - emit dynamic name pools - .dyntab, .dynstr, .hash
+ virtual void emitDynNamePools(Output& pOutput,
+ SymbolCategory& pSymbols,
+ const Layout& pLayout,
+ const MCLDInfo& pLDInfo);
+
+ MipsGOT& getGOT();
+ const MipsGOT& getGOT() const;
+
+ OutputRelocSection& getRelDyn();
+ const OutputRelocSection& getRelDyn() const;
+
+ /// getTargetSectionOrder - compute the layout order of ARM target sections
+ unsigned int getTargetSectionOrder(const Output& pOutput,
+ const LDSection& pSectHdr) const;
+
+ /// finalizeSymbol - finalize the symbol value
+ /// If the symbol's reserved field is not zero, MCLinker will call back this
+ /// function to ask the final value of the symbol
+ bool finalizeSymbol(LDSymbol& pSymbol) const;
+
+ /// allocateCommonSymbols - allocate common symbols in the corresponding
+ /// sections.
+ bool allocateCommonSymbols(const MCLDInfo& pLDInfo, MCLinker& pLinker) const;
+
+private:
+ void scanLocalReloc(Relocation& pReloc,
+ const LDSymbol& pInputSym,
+ MCLinker& pLinker,
+ const MCLDInfo& pLDInfo,
+ const Output& pOutput);
+
+ void scanGlobalReloc(Relocation& pReloc,
+ const LDSymbol& pInputSym,
+ MCLinker& pLinker,
+ const MCLDInfo& pLDInfo,
+ const Output& pOutput);
+
+ bool isSymbolNeedsPLT(ResolveInfo& pSym, const Output& pOutput) const;
+ bool isSymbolNeedsDynRel(ResolveInfo& pSym, const Output& pOutput) const;
+
+ void createGOT(MCLinker& pLinker, const Output& pOutput);
+ void createRelDyn(MCLinker& pLinker, const Output& pOutput);
+
+ ELFFileFormat* getOutputFormat(const Output& pOutput) const;
+
+ /// updateAddend - update addend value of the relocation if the
+ /// the target symbol is a section symbol. Addend is the offset
+ /// in the section. This value should be updated after section
+ /// merged.
+ void updateAddend(Relocation& pReloc,
+ const LDSymbol& pInputSym,
+ const Layout& pLayout) const;
+
+private:
+ RelocationFactory* m_pRelocFactory;
+
+ MipsGOT* m_pGOT; // .got
+ OutputRelocSection* m_pRelDyn; // .rel.dyn
+
+ MipsELFDynamic* m_pDynamic;
+ LDSymbol* m_pGOTSymbol;
+ LDSymbol* m_pGpDispSymbol;
+
+ std::vector<LDSymbol*> m_LocalGOTSyms;
+ std::vector<LDSymbol*> m_GlobalGOTSyms;
+
+private:
+ /// isGOTSymbol - return true if the symbol is the GOT entry.
+ bool isGOTSymbol(const LDSymbol& pSymbol) const;
+ /// emitDynamicSymbol - emit dynamic symbol.
+ void emitDynamicSymbol(llvm::ELF::Elf32_Sym& sym32,
+ Output& pOutput,
+ LDSymbol& pSymbol,
+ const Layout& pLayout,
+ char* strtab,
+ size_t strtabsize,
+ size_t symtabIdx);
+};
+
+} // namespace of mcld
+
+#endif
+
diff --git a/lib/Target/Mips/MipsRelocationFactory.cpp b/lib/Target/Mips/MipsRelocationFactory.cpp
new file mode 100644
index 0000000..e3a7793
--- /dev/null
+++ b/lib/Target/Mips/MipsRelocationFactory.cpp
@@ -0,0 +1,363 @@
+//===- MipsRelocationFactory.cpp -----------------------------------------===//
+//
+// The MCLinker Project
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include <llvm/ADT/Twine.h>
+#include <llvm/Support/ELF.h>
+#include <llvm/Support/ErrorHandling.h>
+#include <mcld/LD/Layout.h>
+#include <mcld/Target/OutputRelocSection.h>
+
+#include "MipsRelocationFactory.h"
+#include "MipsRelocationFunctions.h"
+
+using namespace mcld;
+
+DECL_MIPS_APPLY_RELOC_FUNCS
+
+//==========================
+// MipsRelocationFactory
+MipsRelocationFactory::MipsRelocationFactory(size_t pNum,
+ MipsGNULDBackend& pParent)
+ : RelocationFactory(pNum),
+ m_Target(pParent),
+ m_AHL(0)
+{
+}
+
+void MipsRelocationFactory::applyRelocation(Relocation& pRelocation,
+ const MCLDInfo& pLDInfo)
+
+{
+ /// the prototype of applying function
+ typedef Result (*ApplyFunctionType)(Relocation&,
+ const MCLDInfo& pLDInfo,
+ MipsRelocationFactory&);
+
+ // the table entry of applying functions
+ struct ApplyFunctionTriple {
+ ApplyFunctionType func;
+ unsigned int type;
+ const char* name;
+ };
+
+ // declare the table of applying functions
+ static ApplyFunctionTriple apply_functions[] = {
+ DECL_MIPS_APPLY_RELOC_FUNC_PTRS
+ };
+
+ Relocation::Type type = pRelocation.type();
+
+ if (type >= sizeof(apply_functions) / sizeof(apply_functions[0])) {
+ llvm::report_fatal_error(llvm::Twine("Unknown relocation type. "
+ "To symbol `") +
+ pRelocation.symInfo()->name() +
+ llvm::Twine("'."));
+ }
+
+ // apply the relocation
+ Result result = apply_functions[type].func(pRelocation, pLDInfo, *this);
+
+ // check result
+ if (Overflow == result) {
+ llvm::report_fatal_error(llvm::Twine("Applying relocation `") +
+ llvm::Twine(apply_functions[type].name) +
+ llvm::Twine("' causes overflow. on symbol: `") +
+ llvm::Twine(pRelocation.symInfo()->name()) +
+ llvm::Twine("'."));
+ return;
+ }
+
+ if (BadReloc == result) {
+ llvm::report_fatal_error(llvm::Twine("Applying relocation `") +
+ llvm::Twine(apply_functions[type].name) +
+ llvm::Twine("' encounters unexpected opcode. "
+ "on symbol: `") +
+ llvm::Twine(pRelocation.symInfo()->name()) +
+ llvm::Twine("'."));
+ return;
+ }
+}
+
+//=========================================//
+// Relocation helper function //
+//=========================================//
+
+static const char * const GP_DISP_NAME = "_gp_disp";
+
+// Get an relocation entry in .rel.dyn and set its type to R_MIPS_REL32,
+// its FragmentRef to pReloc->targetFrag() and its ResolveInfo
+// to pReloc->symInfo()
+static
+void helper_SetRelDynEntry(Relocation& pReloc,
+ MipsRelocationFactory& pParent)
+{
+ // rsym - The relocation target symbol
+ ResolveInfo* rsym = pReloc.symInfo();
+ MipsGNULDBackend& ld_backend = pParent.getTarget();
+
+ bool exist;
+ Relocation& rel_entry =
+ *ld_backend.getRelDyn().getEntry(*rsym, false, exist);
+
+ rel_entry.setType(llvm::ELF::R_MIPS_REL32);
+ rel_entry.targetRef() = pReloc.targetRef();
+ rel_entry.setSymInfo(0);
+}
+
+// Find next R_MIPS_LO16 relocation paired to pReloc.
+static
+Relocation* helper_FindLo16Reloc(Relocation& pReloc)
+{
+ Relocation* reloc = static_cast<Relocation*>(pReloc.getNextNode());
+ while (NULL != reloc)
+ {
+ if (llvm::ELF::R_MIPS_LO16 == reloc->type() &&
+ reloc->symInfo() == pReloc.symInfo())
+ return reloc;
+
+ reloc = static_cast<Relocation*>(reloc->getNextNode());
+ }
+ return NULL;
+}
+
+// Check the symbol is _gp_disp.
+static
+bool helper_isGpDisp(const Relocation& pReloc)
+{
+ const ResolveInfo* rsym = pReloc.symInfo();
+ return 0 == strcmp(GP_DISP_NAME, rsym->name());
+}
+
+static
+RelocationFactory::Address helper_GetGP(MipsRelocationFactory& pParent)
+{
+ return pParent.getTarget().getGOT().getSection().addr() + 0x7FF0;
+}
+
+static
+GOTEntry& helper_GetGOTEntry(Relocation& pReloc,
+ MipsRelocationFactory& pParent)
+{
+ // rsym - The relocation target symbol
+ ResolveInfo* rsym = pReloc.symInfo();
+ MipsGNULDBackend& ld_backend = pParent.getTarget();
+
+ bool exist;
+ GOTEntry& got_entry = *ld_backend.getGOT().getEntry(*rsym, exist);
+
+ if (exist)
+ return got_entry;
+
+ // If we first get this GOT entry, we should initialize it.
+ if (rsym->reserved() & MipsGNULDBackend::ReserveGot) {
+ got_entry.setContent(pReloc.symValue());
+ }
+ else {
+ llvm::report_fatal_error("No GOT entry reserved for GOT type relocation!");
+ }
+
+ return got_entry;
+}
+
+static
+RelocationFactory::Address helper_GetGOTOffset(Relocation& pReloc,
+ MipsRelocationFactory& pParent)
+{
+ GOTEntry& got_entry = helper_GetGOTEntry(pReloc, pParent);
+ return pParent.getLayout().getOutputOffset(got_entry) - 0x7FF0;
+}
+
+static
+int32_t helper_CalcAHL(const Relocation& pHiReloc, const Relocation& pLoReloc)
+{
+ assert((pHiReloc.type() == llvm::ELF::R_MIPS_HI16 ||
+ pHiReloc.type() == llvm::ELF::R_MIPS_GOT16) &&
+ pLoReloc.type() == llvm::ELF::R_MIPS_LO16 &&
+ "Incorrect type of relocation for AHL calculation");
+
+ // Note the addend is section symbol offset here
+ assert (pHiReloc.addend() == pLoReloc.addend());
+
+ int32_t AHI = pHiReloc.target();
+ int32_t ALO = pLoReloc.target();
+ int32_t AHL = ((AHI & 0xFFFF) << 16) + (int16_t)(ALO & 0xFFFF) + pLoReloc.addend();
+ return AHL;
+}
+
+static
+void helper_DynRel(Relocation& pReloc,
+ MipsRelocationFactory& pParent)
+{
+ ResolveInfo* rsym = pReloc.symInfo();
+ MipsGNULDBackend& ld_backend = pParent.getTarget();
+
+ bool exist;
+ Relocation& rel_entry =
+ *ld_backend.getRelDyn().getEntry(*rsym, false, exist);
+
+ rel_entry.setType(llvm::ELF::R_MIPS_REL32);
+ rel_entry.targetRef() = pReloc.targetRef();
+ rel_entry.setSymInfo(rsym->isLocal() ? NULL : rsym);
+}
+
+//=========================================//
+// Relocation functions implementation //
+//=========================================//
+
+// R_MIPS_NONE and those unsupported/deprecated relocation type
+static
+MipsRelocationFactory::Result none(Relocation& pReloc,
+ const MCLDInfo& pLDInfo,
+ MipsRelocationFactory& pParent)
+{
+ return MipsRelocationFactory::OK;
+}
+
+// R_MIPS_32: S + A
+static
+MipsRelocationFactory::Result abs32(Relocation& pReloc,
+ const MCLDInfo& pLDInfo,
+ MipsRelocationFactory& pParent)
+{
+ ResolveInfo* rsym = pReloc.symInfo();
+
+ if (rsym->reserved() & MipsGNULDBackend::ReserveRel) {
+ helper_DynRel(pReloc, pParent);
+ }
+
+ RelocationFactory::DWord A = pReloc.target() + pReloc.addend();
+ RelocationFactory::DWord S = pReloc.symValue();
+
+ pReloc.target() |= (S + A);
+
+ return MipsRelocationFactory::OK;
+}
+
+// R_MIPS_HI16:
+// local/external: ((AHL + S) - (short)(AHL + S)) >> 16
+// _gp_disp : ((AHL + GP - P) - (short)(AHL + GP - P)) >> 16
+static
+MipsRelocationFactory::Result hi16(Relocation& pReloc,
+ const MCLDInfo& pLDInfo,
+ MipsRelocationFactory& pParent)
+{
+ Relocation* lo_reloc = helper_FindLo16Reloc(pReloc);
+ assert(NULL != lo_reloc && "There is no paired R_MIPS_LO16 for R_MIPS_HI16");
+
+ int32_t AHL = helper_CalcAHL(pReloc, *lo_reloc);
+ int32_t res = 0;
+
+ pParent.setAHL(AHL);
+
+ if (helper_isGpDisp(pReloc)) {
+ int32_t P = pReloc.place(pParent.getLayout());
+ int32_t GP = helper_GetGP(pParent);
+ res = ((AHL + GP - P) - (int16_t)(AHL + GP - P)) >> 16;
+ }
+ else {
+ int32_t S = pReloc.symValue();
+ res = ((AHL + S) - (int16_t)(AHL + S)) >> 16;
+ }
+
+ pReloc.target() &= 0xFFFF0000;
+ pReloc.target() |= (res & 0xFFFF);
+
+ return MipsRelocationFactory::OK;
+}
+
+// R_MIPS_LO16:
+// local/external: AHL + S
+// _gp_disp : AHL + GP - P + 4
+static
+MipsRelocationFactory::Result lo16(Relocation& pReloc,
+ const MCLDInfo& pLDInfo,
+ MipsRelocationFactory& pParent)
+{
+ int32_t AHL = pParent.getAHL();
+ int32_t res = 0;
+
+ if (helper_isGpDisp(pReloc)) {
+ int32_t P = pReloc.place(pParent.getLayout());
+ int32_t GP = helper_GetGP(pParent);
+ res = AHL + GP - P + 4;
+ }
+ else {
+ int32_t S = pReloc.symValue();
+ res = AHL + S;
+ }
+
+ pReloc.target() &= 0xFFFF0000;
+ pReloc.target() |= (res & 0xFFFF);
+
+ return MipsRelocationFactory::OK;
+}
+
+// R_MIPS_GOT16:
+// local : G (calculate AHL and put high 16 bit to GOT)
+// external: G
+static
+MipsRelocationFactory::Result got16(Relocation& pReloc,
+ const MCLDInfo& pLDInfo,
+ MipsRelocationFactory& pParent)
+{
+ ResolveInfo* rsym = pReloc.symInfo();
+
+ if (rsym->isLocal()) {
+ Relocation* lo_reloc = helper_FindLo16Reloc(pReloc);
+ assert(NULL != lo_reloc && "There is no paired R_MIPS_LO16 for R_MIPS_GOT16");
+
+ int32_t AHL = helper_CalcAHL(pReloc, *lo_reloc);
+ int32_t S = pReloc.symValue();
+
+ pParent.setAHL(AHL);
+
+ GOTEntry& got_entry = helper_GetGOTEntry(pReloc, pParent);
+
+ int32_t res = (AHL + S + 0x8000) & 0xFFFF0000;
+ got_entry.setContent(res);
+ }
+
+ RelocationFactory::Address G = helper_GetGOTOffset(pReloc, pParent);
+
+ pReloc.target() &= 0xFFFF0000;
+ pReloc.target() |= (G & 0xFFFF);
+
+ return MipsRelocationFactory::OK;
+}
+
+// R_MIPS_CALL16: G
+static
+MipsRelocationFactory::Result call16(Relocation& pReloc,
+ const MCLDInfo& pLDInfo,
+ MipsRelocationFactory& pParent)
+{
+ RelocationFactory::Address G = helper_GetGOTOffset(pReloc, pParent);
+
+ pReloc.target() &= 0xFFFF0000;
+ pReloc.target() |= (G & 0xFFFF);
+
+ return MipsRelocationFactory::OK;
+}
+
+// R_MIPS_GPREL32: A + S + GP0 - GP
+static
+MipsRelocationFactory::Result gprel32(Relocation& pReloc,
+ const MCLDInfo& pLDInfo,
+ MipsRelocationFactory& pParent)
+{
+ int32_t A = pReloc.target();
+ int32_t S = pReloc.symValue();
+ int32_t GP = helper_GetGP(pParent);
+
+ // llvm does not emits SHT_MIPS_REGINFO section.
+ // Assume that GP0 is zero.
+ pReloc.target() = (A + S - GP) & 0xFFFFFFFF;
+
+ return MipsRelocationFactory::OK;
+}
diff --git a/lib/Target/Mips/MipsRelocationFactory.h b/lib/Target/Mips/MipsRelocationFactory.h
new file mode 100644
index 0000000..8f3bf93
--- /dev/null
+++ b/lib/Target/Mips/MipsRelocationFactory.h
@@ -0,0 +1,64 @@
+//===- MipsRelocationFactory.h --------------------------------------------===//
+//
+// The MCLinker Project
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+#ifndef MIPS_RELOCATION_FACTORY_H
+#define MIPS_RELOCATION_FACTORY_H
+#ifdef ENABLE_UNITTEST
+#include <gtest.h>
+#endif
+
+#include <mcld/LD/RelocationFactory.h>
+#include <mcld/Support/GCFactory.h>
+#include "MipsLDBackend.h"
+
+namespace mcld
+{
+
+/** \class MipsRelocationFactory
+ * \brief MipsRelocationFactory creates and destroys the Mips relocations.
+ */
+class MipsRelocationFactory : public RelocationFactory
+{
+public:
+ /** \enum Reloc
+ * \brief Reloc is the result of applying functions.
+ */
+ enum Result
+ {
+ OK,
+ Overflow,
+ BadReloc
+ };
+
+public:
+ MipsRelocationFactory(size_t pNum, MipsGNULDBackend& pParent);
+
+ void applyRelocation(Relocation& pRelocation, const MCLDInfo& pLDInfo);
+
+ MipsGNULDBackend& getTarget()
+ { return m_Target; }
+
+ const MipsGNULDBackend& getTarget() const
+ { return m_Target; }
+
+ // Get last calculated AHL.
+ int32_t getAHL() const
+ { return m_AHL; }
+
+ // Set last calculated AHL.
+ void setAHL(int32_t pAHL)
+ { m_AHL = pAHL; }
+
+private:
+ MipsGNULDBackend& m_Target;
+ int32_t m_AHL;
+};
+
+} // namespace of mcld
+
+#endif
diff --git a/lib/Target/Mips/MipsRelocationFunctions.h b/lib/Target/Mips/MipsRelocationFunctions.h
new file mode 100644
index 0000000..4b60d10
--- /dev/null
+++ b/lib/Target/Mips/MipsRelocationFunctions.h
@@ -0,0 +1,76 @@
+//===- MipsRelocationFunction.h -------------------------------------------===//
+//
+// The MCLinker Project
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#define DECL_MIPS_APPLY_RELOC_FUNC(Name) \
+static MipsRelocationFactory::Result Name(Relocation& pEntry, \
+ const MCLDInfo& pLDInfo, \
+ MipsRelocationFactory& pParent);
+
+#define DECL_MIPS_APPLY_RELOC_FUNCS \
+DECL_MIPS_APPLY_RELOC_FUNC(none) \
+DECL_MIPS_APPLY_RELOC_FUNC(abs32) \
+DECL_MIPS_APPLY_RELOC_FUNC(hi16) \
+DECL_MIPS_APPLY_RELOC_FUNC(lo16) \
+DECL_MIPS_APPLY_RELOC_FUNC(got16) \
+DECL_MIPS_APPLY_RELOC_FUNC(call16) \
+DECL_MIPS_APPLY_RELOC_FUNC(gprel32)
+
+#define DECL_MIPS_APPLY_RELOC_FUNC_PTRS \
+ { &none, 0, "R_MIPS_NONE" }, \
+ { &none, 1, "R_MIPS_16" }, \
+ { &abs32, 2, "R_MIPS_32" }, \
+ { &none, 3, "R_MIPS_REL32" }, \
+ { &none, 4, "R_MIPS_26" }, \
+ { &hi16, 5, "R_MIPS_HI16" }, \
+ { &lo16, 6, "R_MIPS_LO16" }, \
+ { &none, 7, "R_MIPS_GPREL16" }, \
+ { &none, 8, "R_MIPS_LITERAL" }, \
+ { &got16, 9, "R_MIPS_GOT16" }, \
+ { &none, 10, "R_MIPS_PC16" }, \
+ { &call16, 11, "R_MIPS_CALL16" }, \
+ { &gprel32, 12, "R_MIPS_GPREL32" }, \
+ { &none, 13, "R_MIPS_UNUSED1" }, \
+ { &none, 14, "R_MIPS_UNUSED2" }, \
+ { &none, 15, "R_MIPS_UNUSED3" }, \
+ { &none, 16, "R_MIPS_SHIFT5" }, \
+ { &none, 17, "R_MIPS_SHIFT6" }, \
+ { &none, 18, "R_MIPS_64" }, \
+ { &none, 19, "R_MIPS_GOT_DISP" }, \
+ { &none, 20, "R_MIPS_GOT_PAGE" }, \
+ { &none, 21, "R_MIPS_GOT_OFST" }, \
+ { &none, 22, "R_MIPS_GOT_HI16" }, \
+ { &none, 23, "R_MIPS_GOT_LO16" }, \
+ { &none, 24, "R_MIPS_SUB" }, \
+ { &none, 25, "R_MIPS_INSERT_A" }, \
+ { &none, 26, "R_MIPS_INSERT_B" }, \
+ { &none, 27, "R_MIPS_DELETE" }, \
+ { &none, 28, "R_MIPS_HIGHER" }, \
+ { &none, 29, "R_MIPS_HIGHEST" }, \
+ { &none, 30, "R_MIPS_CALL_HI16" }, \
+ { &none, 31, "R_MIPS_CALL_LO16" }, \
+ { &none, 32, "R_MIPS_SCN_DISP" }, \
+ { &none, 33, "R_MIPS_REL16" }, \
+ { &none, 34, "R_MIPS_ADD_IMMEDIATE" }, \
+ { &none, 35, "R_MIPS_PJUMP" }, \
+ { &none, 36, "R_MIPS_RELGOT" }, \
+ { &none, 37, "R_MIPS_JALR" }, \
+ { &none, 38, "R_MIPS_TLS_DTPMOD32" }, \
+ { &none, 39, "R_MIPS_TLS_DTPREL32" }, \
+ { &none, 40, "R_MIPS_TLS_DTPMOD64" }, \
+ { &none, 41, "R_MIPS_TLS_DTPREL64" }, \
+ { &none, 42, "R_MIPS_TLS_GD" }, \
+ { &none, 43, "R_MIPS_TLS_LDM" }, \
+ { &none, 44, "R_MIPS_TLS_DTPREL_HI16" }, \
+ { &none, 45, "R_MIPS_TLS_DTPREL_LO16" }, \
+ { &none, 46, "R_MIPS_TLS_GOTTPREL" }, \
+ { &none, 47, "R_MIPS_TLS_TPREL32" }, \
+ { &none, 48, "R_MIPS_TLS_TPREL64" }, \
+ { &none, 49, "R_MIPS_TLS_TPREL_HI16" }, \
+ { &none, 50, "R_MIPS_TLS_TPREL_LO16" }, \
+ { &none, 51, "R_MIPS_GLOB_DAT" }
diff --git a/lib/Target/Mips/MipsSectLinker.cpp b/lib/Target/Mips/MipsSectLinker.cpp
new file mode 100644
index 0000000..af543c9
--- /dev/null
+++ b/lib/Target/Mips/MipsSectLinker.cpp
@@ -0,0 +1,47 @@
+//===- MipsSectLinker.cpp -------------------------------------------------===//
+//
+// The MCLinker Project
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include <llvm/ADT/Triple.h>
+#include <mcld/Support/TargetRegistry.h>
+#include "Mips.h"
+#include "MipsAndroidSectLinker.h"
+
+using namespace mcld;
+
+namespace mcld {
+//===----------------------------------------------------------------------===//
+/// createMipsSectLinker - the help funtion to create
+/// corresponding MipsSectLinker
+///
+SectLinker* createMipsSectLinker(const std::string &pTriple,
+ SectLinkerOption &pOption,
+ mcld::TargetLDBackend &pLDBackend)
+{
+ llvm::Triple theTriple(pTriple);
+ if (theTriple.isOSDarwin()) {
+ assert(0 && "MachO linker has not supported yet");
+ }
+ if (theTriple.isOSWindows()) {
+ assert(0 && "COFF linker has not supported yet");
+ }
+
+ // For now, use Android SectLinker directly
+ return new MipsAndroidSectLinker(pOption,
+ pLDBackend);
+}
+
+} // namespace of mcld
+
+//==========================
+// MipsSectLinker
+extern "C" void LLVMInitializeMipsSectLinker() {
+ // Register the linker frontend
+ mcld::TargetRegistry::RegisterSectLinker(TheMipselTarget,
+ createMipsSectLinker);
+}
diff --git a/lib/Target/Mips/MipsTargetMachine.cpp b/lib/Target/Mips/MipsTargetMachine.cpp
new file mode 100644
index 0000000..0987585
--- /dev/null
+++ b/lib/Target/Mips/MipsTargetMachine.cpp
@@ -0,0 +1,34 @@
+//===- MipsTargetMachine.cpp ----------------------------------------------===//
+//
+// The MCLinker Project
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MipsTargetMachine.h"
+#include "mcld/Target/TargetMachine.h"
+#include "mcld/Support/TargetRegistry.h"
+#include "mcld/MC/MCLDInfo.h"
+#include "Mips.h"
+
+extern "C" void LLVMInitializeMipsLDTarget() {
+ // Register createTargetMachine function pointer to mcld::Target
+ mcld::RegisterTargetMachine<mcld::MipsBaseTargetMachine>
+ X(mcld::TheMipselTarget);
+}
+
+mcld::MipsBaseTargetMachine::MipsBaseTargetMachine(llvm::TargetMachine& pPM,
+ const mcld::Target &pTarget,
+ const std::string& pTriple)
+ : mcld::LLVMTargetMachine(pPM, pTarget, pTriple) {
+ // arg1 - the number of total attributes
+ // arg2 - the most possible number of input files
+ m_pLDInfo = new MCLDInfo(pTriple, 32, 64);
+}
+
+mcld::MipsBaseTargetMachine::~MipsBaseTargetMachine()
+{
+ delete m_pLDInfo;
+}
diff --git a/lib/Target/Mips/MipsTargetMachine.h b/lib/Target/Mips/MipsTargetMachine.h
new file mode 100644
index 0000000..e9bd5da
--- /dev/null
+++ b/lib/Target/Mips/MipsTargetMachine.h
@@ -0,0 +1,38 @@
+//===- MipsTargetMachine.h ------------------------------------------------===//
+//
+// The MCLinker Project
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+#ifndef MIPS_TARGET_MACHINE_H
+#define MIPS_TARGET_MACHINE_H
+#include "mcld/Target/TargetMachine.h"
+#include "Mips.h"
+
+namespace mcld
+{
+
+class MipsBaseTargetMachine : public LLVMTargetMachine
+{
+protected:
+ MCLDInfo *m_pLDInfo;
+
+public:
+ MipsBaseTargetMachine(llvm::TargetMachine &pTM,
+ const mcld::Target &pTarget,
+ const std::string &pTriple);
+
+ virtual ~MipsBaseTargetMachine();
+
+ mcld::MCLDInfo& getLDInfo()
+ { return *m_pLDInfo; }
+
+ const mcld::MCLDInfo& getLDInfo() const
+ { return *m_pLDInfo; }
+};
+
+} // namespace of mcld
+
+#endif
diff --git a/lib/Target/Mips/TargetInfo/Android.mk b/lib/Target/Mips/TargetInfo/Android.mk
new file mode 100644
index 0000000..cf8ae38
--- /dev/null
+++ b/lib/Target/Mips/TargetInfo/Android.mk
@@ -0,0 +1,32 @@
+LOCAL_PATH:= $(call my-dir)
+
+mcld_mips_info_SRC_FILES := \
+ MipsTargetInfo.cpp
+
+# For the host
+# =====================================================
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(mcld_mips_info_SRC_FILES)
+LOCAL_MODULE:= libmcldMipsInfo
+
+LOCAL_MODULE_TAGS := optional
+
+include $(MCLD_HOST_BUILD_MK)
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+# For the device
+# =====================================================
+ifeq ($(TARGET_ARCH),mips)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(mcld_mips_info_SRC_FILES)
+LOCAL_MODULE:= libmcldMipsInfo
+
+LOCAL_MODULE_TAGS := optional
+
+include $(MCLD_DEVICE_BUILD_MK)
+include $(BUILD_STATIC_LIBRARY)
+
+endif
diff --git a/lib/Target/Mips/TargetInfo/MipsTargetInfo.cpp b/lib/Target/Mips/TargetInfo/MipsTargetInfo.cpp
new file mode 100644
index 0000000..0889973
--- /dev/null
+++ b/lib/Target/Mips/TargetInfo/MipsTargetInfo.cpp
@@ -0,0 +1,21 @@
+//===- MipsTargetInfo.cpp -------------------------------------------------===//
+//
+// The MCLinker Project
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+#include "mcld/Target/TargetMachine.h"
+#include "mcld/Support/TargetRegistry.h"
+
+namespace mcld {
+
+mcld::Target TheMipselTarget;
+
+extern "C" void LLVMInitializeMipsLDTargetInfo() {
+ // register into mcld::TargetRegistry
+ mcld::RegisterTarget X(TheMipselTarget, "mipsel");
+}
+
+} // namespace of mcld