diff options
author | Stephen Hines <srhines@google.com> | 2014-07-15 18:33:32 -0700 |
---|---|---|
committer | Stephen Hines <srhines@google.com> | 2014-07-25 00:47:58 -0700 |
commit | a790f0a8f3175183bea088389b3e4ae41813e192 (patch) | |
tree | 80cb1165eb946f770210ef2c662c6459af15ffb0 | |
parent | 717b462e8648e6e62ab9d39128a2f7e776bdf8ab (diff) | |
download | mclinker-a790f0a8f3175183bea088389b3e4ae41813e192.tar.gz |
Update mclinker for LLVM rebase.
commit 6824c791204cf5daabdfe008ee8808799f348815
Author: Pete Chou <petechou@gmail.com>
Date: Tue Jul 15 10:15:12 2014 +0800
Fix typo in README.
Change-Id: Id7a525732ba33b5ac81a0da4c8d8f02d1f8c3a16
82 files changed, 6020 insertions, 1890 deletions
diff --git a/include/mcld/Config/Config.h b/include/mcld/Config/Config.h index 9e52d54..2973426 100644 --- a/include/mcld/Config/Config.h +++ b/include/mcld/Config/Config.h @@ -27,11 +27,14 @@ #define MCLD_DEFAULT_TARGET_TRIPLE "x86_64-unknown-linux-gnu" /* MCLINKER version */ -#define MCLD_VERSION "2.3.0.RC-WhiteStone" +#define MCLD_VERSION "2.9.0.dev-" + +/* Name of package */ +#define PACKAGE "mclinker" /* Version number of package */ -#define VERSION "WhiteStone" +#define VERSION "dev" #define MCLD_REGION_CHUNK_SIZE 32 diff --git a/include/mcld/Config/Config.h.in b/include/mcld/Config/Config.h.in index 39df682..4a38cbf 100644 --- a/include/mcld/Config/Config.h.in +++ b/include/mcld/Config/Config.h.in @@ -108,6 +108,7 @@ #define MCLD_SECTIONS_PER_INPUT 16 #define MCLD_SYMBOLS_PER_INPUT 128 #define MCLD_RELOCATIONS_PER_INPUT 1024 +#define MCLD_SEGMENTS_PER_OUTPUT 8 #endif diff --git a/include/mcld/GeneralOptions.h b/include/mcld/GeneralOptions.h index 226e5d1..1d6c9a3 100644 --- a/include/mcld/GeneralOptions.h +++ b/include/mcld/GeneralOptions.h @@ -8,10 +8,11 @@ //===----------------------------------------------------------------------===// #ifndef MCLD_GENERALOPTIONS_H #define MCLD_GENERALOPTIONS_H -#include <string> -#include <vector> #include <mcld/Support/RealPath.h> #include <mcld/Support/FileSystem.h> +#include <string> +#include <vector> +#include <set> namespace mcld { @@ -39,6 +40,12 @@ public: Both = 0x3 }; + enum ICF { + ICF_None, + ICF_All, + ICF_Safe + }; + typedef std::vector<std::string> RpathList; typedef RpathList::iterator rpath_iterator; typedef RpathList::const_iterator const_rpath_iterator; @@ -51,6 +58,12 @@ public: typedef AuxiliaryList::iterator aux_iterator; typedef AuxiliaryList::const_iterator const_aux_iterator; + typedef std::vector<std::string> UndefSymList; + typedef UndefSymList::iterator undef_sym_iterator; + typedef UndefSymList::const_iterator const_undef_sym_iterator; + + typedef std::set<std::string> ExcludeLIBS; + public: GeneralOptions(); ~GeneralOptions(); @@ -284,6 +297,13 @@ public: bool GCSections() const { return m_bGCSections; } + // --print-gc-sections + void setPrintGCSections(bool pEnable = true) + { m_bPrintGCSections = pEnable; } + + bool getPrintGCSections() const + { return m_bPrintGCSections; } + // --ld-generated-unwind-info void setGenUnwindInfo(bool pEnable = true) { m_bGenUnwindInfo = pEnable; } @@ -303,6 +323,21 @@ public: void setHashStyle(unsigned int pStyle) { m_HashStyle = pStyle; } + ICF getICFMode() const { return m_ICF; } + + void setICFMode(ICF pMode) + { m_ICF = pMode; } + + size_t getICFIterations() const { return m_ICFIterations; } + + void setICFIterations(size_t pNum) + { m_ICFIterations = pNum; } + + bool printICFSections() const { return m_bPrintICFSections; } + + void setPrintICFSections(bool pPrintICFSections) + { m_bPrintICFSections = pPrintICFSections; } + // ----- link-in rpath ----- // const RpathList& getRpathList() const { return m_RpathList; } RpathList& getRpathList() { return m_RpathList; } @@ -321,6 +356,20 @@ public: const_script_iterator script_end () const { return m_ScriptList.end(); } script_iterator script_end () { return m_ScriptList.end(); } + // ----- -u/--undefined, undefined symbols ----- // + const UndefSymList& getUndefSymList() const { return m_UndefSymList; } + UndefSymList& getUndefSymList() { return m_UndefSymList; } + + const_undef_sym_iterator undef_sym_begin() const + { return m_UndefSymList.begin(); } + undef_sym_iterator undef_sym_begin() + { return m_UndefSymList.begin(); } + + const_undef_sym_iterator undef_sym_end() const + { return m_UndefSymList.end(); } + undef_sym_iterator undef_sym_end() + { return m_UndefSymList.end(); } + // ----- filter and auxiliary filter ----- // void setFilter(const std::string& pFilter) { m_Filter = pFilter; } @@ -339,6 +388,13 @@ public: const_aux_iterator aux_end () const { return m_AuxiliaryList.end(); } aux_iterator aux_end () { return m_AuxiliaryList.end(); } + // ----- exclude libs ----- // + ExcludeLIBS& excludeLIBS() + { return m_ExcludeLIBS; } + + bool isInExcludeLIBS(const Input& pInput) const; + + private: enum status { YES, @@ -389,14 +445,20 @@ private: bool m_bPrintMap: 1; // --print-map bool m_bWarnMismatch: 1; // --no-warn-mismatch bool m_bGCSections: 1; // --gc-sections + bool m_bPrintGCSections:1; // --print-gc-sections bool m_bGenUnwindInfo: 1; // --ld-generated-unwind-info + bool m_bPrintICFSections: 1; // --print-icf-sections + ICF m_ICF; + size_t m_ICFIterations; uint32_t m_GPSize; // -G, --gpsize StripSymbolMode m_StripSymbols; RpathList m_RpathList; ScriptList m_ScriptList; + UndefSymList m_UndefSymList; // -u [symbol], --undefined [symbol] unsigned int m_HashStyle; std::string m_Filter; AuxiliaryList m_AuxiliaryList; + ExcludeLIBS m_ExcludeLIBS; }; } // namespace of mcld diff --git a/include/mcld/LD/BinaryReader.h b/include/mcld/LD/BinaryReader.h index b713b06..26dab6f 100644 --- a/include/mcld/LD/BinaryReader.h +++ b/include/mcld/LD/BinaryReader.h @@ -9,7 +9,6 @@ #ifndef MCLD_LD_BINARYREADER_H #define MCLD_LD_BINARYREADER_H #include "mcld/LD/LDReader.h" -#include <llvm/Support/system_error.h> namespace mcld { diff --git a/include/mcld/LD/BranchIsland.h b/include/mcld/LD/BranchIsland.h index 09ac9dc..8733ddb 100644 --- a/include/mcld/LD/BranchIsland.h +++ b/include/mcld/LD/BranchIsland.h @@ -9,14 +9,15 @@ #ifndef MCLD_LD_BRANCHISLAND_H #define MCLD_LD_BRANCHISLAND_H -#include <llvm/Support/DataTypes.h> -#include <llvm/ADT/StringRef.h> #include <mcld/ADT/HashEntry.h> #include <mcld/ADT/HashTable.h> #include <mcld/ADT/StringHash.h> #include <mcld/LD/SectionData.h> #include <mcld/LD/LDSymbol.h> +#include <mcld/Fragment/FragmentRef.h> #include <mcld/Fragment/Stub.h> +#include <llvm/Support/DataTypes.h> +#include <llvm/ADT/StringRef.h> #include <string> namespace mcld { @@ -133,9 +134,25 @@ private: { bool operator() (const Key& KEY1, const Key& KEY2) const { - return (KEY1.prototype() == KEY2.prototype()) && - (KEY1.symbol() == KEY2.symbol()) && - (KEY1.addend() == KEY2.addend()); + bool res = false; + if ((KEY1.prototype() == KEY2.prototype()) && + (KEY1.addend() == KEY2.addend())) { + + if (KEY1.symbol() == KEY2.symbol()) { + res = true; + } else { + // Folded symbols may use the existing stub. + if (KEY1.symbol()->hasFragRef() && KEY2.symbol()->hasFragRef()) { + const FragmentRef* ref1 = KEY1.symbol()->fragRef(); + const FragmentRef* ref2 = KEY2.symbol()->fragRef(); + if ((ref1->offset() == ref2->offset()) && + (ref1->frag()->getOffset() == ref2->frag()->getOffset())) { + res = true; + } + } + } + } + return res; } }; diff --git a/include/mcld/LD/BranchIslandFactory.h b/include/mcld/LD/BranchIslandFactory.h index ef263b0..61a13ad 100644 --- a/include/mcld/LD/BranchIslandFactory.h +++ b/include/mcld/LD/BranchIslandFactory.h @@ -27,11 +27,13 @@ class BranchIslandFactory : public GCFactory<BranchIsland, 0> { public: /// ctor - /// @param pMaxBranchRange - the max branch range of the target backend + /// @param pMaxFwdBranchRange - the max forward branch range of the target + /// @param pMaxBwdBranchRange - the max backward branch range of the target /// @param pMaxIslandSize - a predifned value (64KB here) to decide the max /// size of the island - BranchIslandFactory(uint64_t pMaxBranchRange, - uint64_t pMaxIslandSize = 65536U); + BranchIslandFactory(int64_t pMaxFwdBranchRange, + int64_t pMaxBwdBranchRange, + size_t pMaxIslandSize = 65536U); ~BranchIslandFactory(); @@ -43,13 +45,15 @@ public: /// @param pFragment - the fragment needs a branch island BranchIsland* produce(Fragment& pFragment); - /// find - find a island for the given fragment - /// @param pFragment - the fragment needs a branch isladn - BranchIsland* find(const Fragment& pFragment); + /// getIsland - find fwd and bwd islands for the fragment + /// @param pFragment - the fragment needs a branch island + /// @return - return the pair of <fwd island, bwd island> + std::pair<BranchIsland*, BranchIsland*> getIslands(const Fragment& pFragment); private: - uint64_t m_MaxBranchRange; - uint64_t m_MaxIslandSize; + int64_t m_MaxFwdBranchRange; + int64_t m_MaxBwdBranchRange; + size_t m_MaxIslandSize; }; } // namespace of mcld diff --git a/include/mcld/LD/DiagCommonKinds.inc b/include/mcld/LD/DiagCommonKinds.inc index 539eafb..6bb1461 100644 --- a/include/mcld/LD/DiagCommonKinds.inc +++ b/include/mcld/LD/DiagCommonKinds.inc @@ -51,3 +51,4 @@ DIAG(err_invalid_emulation, DiagnosticEngine::Error, "Invalid target emulation: DIAG(err_cannot_find_scriptfile, DiagnosticEngine::Fatal, "cannot open %0 file %1", "cannot open %0 file %1") DIAG(err_unsupported_archive, DiagnosticEngine::Error, "Unsupported archive type.", "Unsupported archive type.") DIAG(unexpected_frag_type, DiagnosticEngine::Unreachable, "Unexpected fragment type `%0' when constructing FG", "Unexpected fragment type `%0' when constructing FG") +DIAG(debug_print_gc_sections, DiagnosticEngine::Debug, "removing unused section from '%0' in file '%1'", "removing unused section from '%0' in file '%1'") diff --git a/include/mcld/LD/DiagLayouts.inc b/include/mcld/LD/DiagLayouts.inc index fac7a32..513ee33 100644 --- a/include/mcld/LD/DiagLayouts.inc +++ b/include/mcld/LD/DiagLayouts.inc @@ -4,3 +4,5 @@ DIAG(err_section_not_laid_out, DiagnosticEngine::Unreachable, "section %0 has no DIAG(warn_duplicate_std_sectmap, DiagnosticEngine::Warning, "Duplicated definition of section map \"from %0 to %0\".", "Duplicated definition of section map \"from %0 to %0\".") DIAG(warn_rules_check_failed, DiagnosticEngine::Warning, "Illegal section mapping rule: %0 -> %1. (conflict with %2 -> %3)", "Illegal section mapping rule: %0 -> %1. (conflict with %2 -> %3)") DIAG(err_cannot_merge_section, DiagnosticEngine::Error, "Cannot merge section %0 of %1", "Cannot merge section %0 of %1") +DIAG(debug_icf_iterations, DiagnosticEngine::Debug, "ICF converged after `%0' iteration(s).", "ICF converged after `%0' iteration(s).") +DIAG(debug_icf_folded_section, DiagnosticEngine::Debug, "ICF folding section `%0' of `%1' into `%2' of `%3'", "ICF folding section `%0' of `%1' into `%2' of `%3'") diff --git a/include/mcld/LD/DynObjReader.h b/include/mcld/LD/DynObjReader.h index 56cf5e9..1817c4a 100644 --- a/include/mcld/LD/DynObjReader.h +++ b/include/mcld/LD/DynObjReader.h @@ -9,7 +9,6 @@ #ifndef MCLD_LD_DYNOBJREADER_H #define MCLD_LD_DYNOBJREADER_H #include "mcld/LD/LDReader.h" -#include <llvm/Support/system_error.h> namespace mcld { diff --git a/include/mcld/LD/ELFDynObjReader.h b/include/mcld/LD/ELFDynObjReader.h index b534fc1..0beeecd 100644 --- a/include/mcld/LD/ELFDynObjReader.h +++ b/include/mcld/LD/ELFDynObjReader.h @@ -9,7 +9,6 @@ #ifndef MCLD_LD_ELFDYNOBJREADER_H #define MCLD_LD_ELFDYNOBJREADER_H #include <mcld/LD/DynObjReader.h> -#include <llvm/Support/system_error.h> namespace mcld { diff --git a/include/mcld/LD/ELFObjectWriter.h b/include/mcld/LD/ELFObjectWriter.h index 7ab73fd..b83fb01 100644 --- a/include/mcld/LD/ELFObjectWriter.h +++ b/include/mcld/LD/ELFObjectWriter.h @@ -12,7 +12,6 @@ #include <cassert> #include <mcld/Support/FileOutputBuffer.h> -#include <llvm/Support/system_error.h> namespace mcld { @@ -39,7 +38,7 @@ public: ~ELFObjectWriter(); - llvm::error_code writeObject(Module& pModule, FileOutputBuffer& pOutput); + std::error_code writeObject(Module& pModule, FileOutputBuffer& pOutput); size_t getOutputSize(const Module& pModule) const; diff --git a/include/mcld/LD/IdenticalCodeFolding.h b/include/mcld/LD/IdenticalCodeFolding.h new file mode 100644 index 0000000..e26315e --- /dev/null +++ b/include/mcld/LD/IdenticalCodeFolding.h @@ -0,0 +1,79 @@ +//===- IdenticalCodeFolding.h ---------------------------------------------===// +// +// The MCLinker Project +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifndef MCLD_LD_IDENTICALCODEFOLDING_H +#define MCLD_LD_IDENTICALCODEFOLDING_H + +#include <llvm/ADT/MapVector.h> +#include <string> +#include <vector> + +namespace mcld { +class Input; +class LDSection; +class LinkerConfig; +class Module; +class Relocation; +class TargetLDBackend; + +/** \class IdenticalCodeFolding + * \brief Implementation of identical code folding for --icf=[none|all|safe] + * @ref Safe ICF: Pointer Safe and Unwinding Aware Identical Code Folding in + * Gold, http://research.google.com/pubs/pub36912.html + */ +class IdenticalCodeFolding { +public: + typedef std::pair<Input*, size_t> ObjectAndId; + typedef llvm::MapVector<LDSection*, ObjectAndId> KeptSections; + +private: + class FoldingCandidate { + public: + FoldingCandidate() + : sect(NULL), reloc_sect(NULL), obj(NULL) + { } + FoldingCandidate(LDSection* pCode, LDSection* pReloc, Input* pInput) + : sect(pCode), reloc_sect(pReloc), obj(pInput) + { } + + void initConstantContent(const TargetLDBackend& pBackend, + const IdenticalCodeFolding::KeptSections& pKeptSections); + std::string getContentWithVariables(const TargetLDBackend& pBackend, + const IdenticalCodeFolding::KeptSections& pKeptSections); + + LDSection* sect; + LDSection* reloc_sect; + Input* obj; + std::string content; + std::vector<Relocation*> variable_relocs; + }; + + typedef std::vector<FoldingCandidate> FoldingCandidates; + +public: + IdenticalCodeFolding(const LinkerConfig& pConfig, + const TargetLDBackend& pBackend, + Module& pModule); + + void foldIdenticalCode(); + +private: + void findCandidates(FoldingCandidates& pCandidateList); + + bool matchCandidates(FoldingCandidates& pCandidateList); + +private: + const LinkerConfig& m_Config; + const TargetLDBackend& m_Backend; + Module& m_Module; + KeptSections m_KeptSections; +}; + +} // namespace of mcld + +#endif diff --git a/include/mcld/LD/LDFileFormat.h b/include/mcld/LD/LDFileFormat.h index 41ee0d8..44d3bfc 100644 --- a/include/mcld/LD/LDFileFormat.h +++ b/include/mcld/LD/LDFileFormat.h @@ -25,7 +25,8 @@ class LDFileFormat public: enum Kind { Null, - Regular, + TEXT, // Executable regular sections + DATA, // Non-executable regular sections BSS, NamePool, Relocation, @@ -41,7 +42,8 @@ public: LinkOnce, StackNote, Ignore, - Exclude + Exclude, + Folded }; protected: diff --git a/include/mcld/LD/LDSymbol.h b/include/mcld/LD/LDSymbol.h index 5cfb2ef..45c10a8 100644 --- a/include/mcld/LD/LDSymbol.h +++ b/include/mcld/LD/LDSymbol.h @@ -105,6 +105,9 @@ public: const FragmentRef* fragRef() const { return m_pFragRef; } + FragmentRef* fragRef() + { return m_pFragRef; } + SizeType size() const { return m_pResolveInfo->size(); } diff --git a/include/mcld/LD/ObjectReader.h b/include/mcld/LD/ObjectReader.h index 7ecbd9d..32a8c0a 100644 --- a/include/mcld/LD/ObjectReader.h +++ b/include/mcld/LD/ObjectReader.h @@ -9,7 +9,6 @@ #ifndef MCLD_LD_OBJECTREADER_H #define MCLD_LD_OBJECTREADER_H #include "mcld/LD/LDReader.h" -#include <llvm/Support/system_error.h> #include <mcld/ADT/HashTable.h> #include <mcld/ADT/StringHash.h> #include <mcld/LD/ResolveInfo.h> diff --git a/include/mcld/LD/ObjectWriter.h b/include/mcld/LD/ObjectWriter.h index d5d124b..b58661a 100644 --- a/include/mcld/LD/ObjectWriter.h +++ b/include/mcld/LD/ObjectWriter.h @@ -8,7 +8,7 @@ //===----------------------------------------------------------------------===// #ifndef MCLD_LD_OBJECTWRITER_H #define MCLD_LD_OBJECTWRITER_H -#include <llvm/Support/system_error.h> +#include <system_error> namespace mcld { @@ -26,8 +26,8 @@ protected: public: virtual ~ObjectWriter(); - virtual llvm::error_code writeObject(Module& pModule, - FileOutputBuffer& pOutput) = 0; + virtual std::error_code writeObject(Module& pModule, + FileOutputBuffer& pOutput) = 0; virtual size_t getOutputSize(const Module& pModule) const = 0; }; diff --git a/include/mcld/LD/Relocator.h b/include/mcld/LD/Relocator.h index 8c9dfb6..82bdf8a 100644 --- a/include/mcld/LD/Relocator.h +++ b/include/mcld/LD/Relocator.h @@ -115,6 +115,13 @@ public: /// getSize - get the size of a relocation in bit virtual Size getSize(Type pType) const = 0; + /// mayHaveFunctionPointerAccess - check if the given reloc would possibly + /// access a function pointer. + /// Note: Each target relocator should override this function, or be + /// conservative and return true to avoid getting folded. + virtual bool mayHaveFunctionPointerAccess(const Relocation& pReloc) const + { return true; } + protected: const LinkerConfig& config() const { return m_Config; } diff --git a/include/mcld/MC/Input.h b/include/mcld/MC/Input.h index 1c8ccba..da3a647 100644 --- a/include/mcld/MC/Input.h +++ b/include/mcld/MC/Input.h @@ -94,6 +94,12 @@ public: void setNeeded() { m_bNeeded = true; } + bool noExport() const + { return m_bNoExport; } + + void setNoExport() + { m_bNoExport = true; } + off_t fileOffset() const { return m_fileOffset; } @@ -126,6 +132,7 @@ private: sys::fs::Path m_Path; Attribute *m_pAttr; bool m_bNeeded; + bool m_bNoExport; off_t m_fileOffset; MemoryArea* m_pMemArea; LDContext* m_pContext; diff --git a/include/mcld/Object/ObjectLinker.h b/include/mcld/Object/ObjectLinker.h index 0462414..9d54d2e 100644 --- a/include/mcld/Object/ObjectLinker.h +++ b/include/mcld/Object/ObjectLinker.h @@ -45,6 +45,9 @@ public: /// initStdSections - initialize standard sections of the output file. bool initStdSections(); + /// addUndefinedSymbols - add symbols set by -u + void addUndefinedSymbols(); + /// normalize - normalize the input files void normalize(); diff --git a/include/mcld/Script/FlexLexer.h b/include/mcld/Script/FlexLexer.h index 1e49b83..f09ab20 100644 --- a/include/mcld/Script/FlexLexer.h +++ b/include/mcld/Script/FlexLexer.h @@ -141,10 +141,6 @@ public: protected: virtual int LexerInput( char* buf, int max_size ); virtual void LexerOutput( const char* buf, int size ); - /* BEGIN ANDROID WAR - Mac builds use size_t until we switch to prebuilts */ - virtual size_t LexerInput( char* buf, size_t max_size ); - virtual void LexerOutput( const char* buf, size_t size ); - /* END ANDROID WAR */ virtual void LexerError( const char* msg ); void yyunput( int c, char* buf_ptr ); diff --git a/include/mcld/Support/CXADemangle.tcc b/include/mcld/Support/CXADemangle.tcc new file mode 100644 index 0000000..4e66f84 --- /dev/null +++ b/include/mcld/Support/CXADemangle.tcc @@ -0,0 +1,4976 @@ +//===- CXADemangle.tcc ----------------------------------------------------===// +// +// The MCLinker Project +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +/* + * Note: This file is imported from cxa_demangle.cpp in llvm libcxxabi. + */ +#include <vector> +#include <algorithm> +#include <string> +#include <numeric> +#include <cstdlib> +#include <cstring> +#include <cctype> + +namespace mcld +{ + +enum +{ + unknown_error = -4, + invalid_args = -3, + invalid_mangled_name, + memory_alloc_failure, + success +}; + +template <class C> + const char* parse_type(const char* first, const char* last, C& db); +template <class C> + const char* parse_encoding(const char* first, const char* last, C& db); +template <class C> + const char* parse_name(const char* first, const char* last, C& db, + bool* ends_with_template_args = 0); +template <class C> + const char* parse_expression(const char* first, const char* last, C& db); +template <class C> + const char* parse_template_args(const char* first, const char* last, C& db); +template <class C> + const char* parse_operator_name(const char* first, const char* last, C& db); +template <class C> + const char* parse_unqualified_name(const char* first, const char* last, C& db); +template <class C> + const char* parse_decltype(const char* first, const char* last, C& db); + +template <class C> +void +print_stack(const C& db) +{ + printf("---------\n"); + printf("names:\n"); + for (auto& s : db.names) + printf("{%s#%s}\n", s.first.c_str(), s.second.c_str()); + int i = -1; + printf("subs:\n"); + for (auto& v : db.subs) + { + if (i >= 0) + printf("S%i_ = {", i); + else + printf("S_ = {"); + for (auto& s : v) + printf("{%s#%s}", s.first.c_str(), s.second.c_str()); + printf("}\n"); + ++i; + } + printf("template_param:\n"); + for (auto& t : db.template_param) + { + printf("--\n"); + i = -1; + for (auto& v : t) + { + if (i >= 0) + printf("T%i_ = {", i); + else + printf("T_ = {"); + for (auto& s : v) + printf("{%s#%s}", s.first.c_str(), s.second.c_str()); + printf("}\n"); + ++i; + } + } + printf("---------\n\n"); +} + +template <class C> +void +print_state(const char* msg, const char* first, const char* last, const C& db) +{ + printf("%s: ", msg); + for (; first != last; ++first) + printf("%c", *first); + printf("\n"); + print_stack(db); +} + +// <number> ::= [n] <non-negative decimal integer> + +const char* +parse_number(const char* first, const char* last) +{ + if (first != last) + { + const char* t = first; + if (*t == 'n') + ++t; + if (t != last) + { + if (*t == '0') + { + first = t+1; + } + else if ('1' <= *t && *t <= '9') + { + first = t+1; + while (first != last && std::isdigit(*first)) + ++first; + } + } + } + return first; +} + +template <class Float> +struct float_data; + +template <> +struct float_data<float> +{ + static const size_t mangled_size = 8; + static const size_t max_demangled_size = 24; + static constexpr const char* spec = "%af"; +}; + +constexpr const char* float_data<float>::spec; + +template <> +struct float_data<double> +{ + static const size_t mangled_size = 16; + static const size_t max_demangled_size = 32; + static constexpr const char* spec = "%a"; +}; + +constexpr const char* float_data<double>::spec; + +template <> +struct float_data<long double> +{ +#if defined(__arm__) + static const size_t mangled_size = 16; +#else + static const size_t mangled_size = 20; // May need to be adjusted to 16 or 24 on other platforms +#endif + static const size_t max_demangled_size = 40; + static constexpr const char* spec = "%LaL"; +}; + +constexpr const char* float_data<long double>::spec; + +template <class Float, class C> +const char* +parse_floating_number(const char* first, const char* last, C& db) +{ + const size_t N = float_data<Float>::mangled_size; + if (static_cast<std::size_t>(last - first) > N) + { + last = first + N; + union + { + Float value; + char buf[sizeof(Float)]; + }; + const char* t = first; + char* e = buf; + for (; t != last; ++t, ++e) + { + if (!isxdigit(*t)) + return first; + unsigned d1 = isdigit(*t) ? static_cast<unsigned>(*t - '0') : + static_cast<unsigned>(*t - 'a' + 10); + ++t; + unsigned d0 = isdigit(*t) ? static_cast<unsigned>(*t - '0') : + static_cast<unsigned>(*t - 'a' + 10); + *e = static_cast<char>((d1 << 4) + d0); + } + if (*t == 'E') + { +#if __LITTLE_ENDIAN__ + std::reverse(buf, e); +#endif + char num[float_data<Float>::max_demangled_size] = {0}; + int n = snprintf(num, sizeof(num), float_data<Float>::spec, value); + if (static_cast<std::size_t>(n) >= sizeof(num)) + return first; + db.names.push_back(typename C::String(num, static_cast<std::size_t>(n))); + first = t+1; + } + } + return first; +} + +// <source-name> ::= <positive length number> <identifier> + +template <class C> +const char* +parse_source_name(const char* first, const char* last, C& db) +{ + if (first != last) + { + char c = *first; + if (isdigit(c) && first+1 != last) + { + const char* t = first+1; + size_t n = static_cast<size_t>(c - '0'); + for (c = *t; isdigit(c); c = *t) + { + n = n * 10 + static_cast<size_t>(c - '0'); + if (++t == last) + return first; + } + if (static_cast<size_t>(last - t) >= n) + { + typename C::String r(t, n); + if (r.substr(0, 10) == "_GLOBAL__N") + db.names.push_back("(anonymous namespace)"); + else + db.names.push_back(std::move(r)); + first = t + n; + } + } + } + return first; +} + +// <substitution> ::= S <seq-id> _ +// ::= S_ +// <substitution> ::= Sa # ::std::allocator +// <substitution> ::= Sb # ::std::basic_string +// <substitution> ::= Ss # ::std::basic_string < char, +// ::std::char_traits<char>, +// ::std::allocator<char> > +// <substitution> ::= Si # ::std::basic_istream<char, std::char_traits<char> > +// <substitution> ::= So # ::std::basic_ostream<char, std::char_traits<char> > +// <substitution> ::= Sd # ::std::basic_iostream<char, std::char_traits<char> > + +template <class C> +const char* +parse_substitution(const char* first, const char* last, C& db) +{ + if (last - first >= 2) + { + if (*first == 'S') + { + switch (first[1]) + { + case 'a': + db.names.push_back("std::allocator"); + first += 2; + break; + case 'b': + db.names.push_back("std::basic_string"); + first += 2; + break; + case 's': + db.names.push_back("std::string"); + first += 2; + break; + case 'i': + db.names.push_back("std::istream"); + first += 2; + break; + case 'o': + db.names.push_back("std::ostream"); + first += 2; + break; + case 'd': + db.names.push_back("std::iostream"); + first += 2; + break; + case '_': + if (!db.subs.empty()) + { + for (const auto& n : db.subs.front()) + db.names.push_back(n); + first += 2; + } + break; + default: + if (std::isdigit(first[1]) || std::isupper(first[1])) + { + size_t sub = 0; + const char* t = first+1; + if (std::isdigit(*t)) + sub = static_cast<size_t>(*t - '0'); + else + sub = static_cast<size_t>(*t - 'A') + 10; + for (++t; t != last && (std::isdigit(*t) || std::isupper(*t)); ++t) + { + sub *= 36; + if (std::isdigit(*t)) + sub += static_cast<size_t>(*t - '0'); + else + sub += static_cast<size_t>(*t - 'A') + 10; + } + if (t == last || *t != '_') + return first; + ++sub; + if (sub < db.subs.size()) + { + for (const auto& n : db.subs[sub]) + db.names.push_back(n); + first = t+1; + } + } + break; + } + } + } + return first; +} + +// <builtin-type> ::= v # void +// ::= w # wchar_t +// ::= b # bool +// ::= c # char +// ::= a # signed char +// ::= h # unsigned char +// ::= s # short +// ::= t # unsigned short +// ::= i # int +// ::= j # unsigned int +// ::= l # long +// ::= m # unsigned long +// ::= x # long long, __int64 +// ::= y # unsigned long long, __int64 +// ::= n # __int128 +// ::= o # unsigned __int128 +// ::= f # float +// ::= d # double +// ::= e # long double, __float80 +// ::= g # __float128 +// ::= z # ellipsis +// ::= Dd # IEEE 754r decimal floating point (64 bits) +// ::= De # IEEE 754r decimal floating point (128 bits) +// ::= Df # IEEE 754r decimal floating point (32 bits) +// ::= Dh # IEEE 754r half-precision floating point (16 bits) +// ::= Di # char32_t +// ::= Ds # char16_t +// ::= Da # auto (in dependent new-expressions) +// ::= Dc # decltype(auto) +// ::= Dn # std::nullptr_t (i.e., decltype(nullptr)) +// ::= u <source-name> # vendor extended type + +template <class C> +const char* +parse_builtin_type(const char* first, const char* last, C& db) +{ + if (first != last) + { + switch (*first) + { + case 'v': + db.names.push_back("void"); + ++first; + break; + case 'w': + db.names.push_back("wchar_t"); + ++first; + break; + case 'b': + db.names.push_back("bool"); + ++first; + break; + case 'c': + db.names.push_back("char"); + ++first; + break; + case 'a': + db.names.push_back("signed char"); + ++first; + break; + case 'h': + db.names.push_back("unsigned char"); + ++first; + break; + case 's': + db.names.push_back("short"); + ++first; + break; + case 't': + db.names.push_back("unsigned short"); + ++first; + break; + case 'i': + db.names.push_back("int"); + ++first; + break; + case 'j': + db.names.push_back("unsigned int"); + ++first; + break; + case 'l': + db.names.push_back("long"); + ++first; + break; + case 'm': + db.names.push_back("unsigned long"); + ++first; + break; + case 'x': + db.names.push_back("long long"); + ++first; + break; + case 'y': + db.names.push_back("unsigned long long"); + ++first; + break; + case 'n': + db.names.push_back("__int128"); + ++first; + break; + case 'o': + db.names.push_back("unsigned __int128"); + ++first; + break; + case 'f': + db.names.push_back("float"); + ++first; + break; + case 'd': + db.names.push_back("double"); + ++first; + break; + case 'e': + db.names.push_back("long double"); + ++first; + break; + case 'g': + db.names.push_back("__float128"); + ++first; + break; + case 'z': + db.names.push_back("..."); + ++first; + break; + case 'u': + { + const char*t = parse_source_name(first+1, last, db); + if (t != first+1) + first = t; + } + break; + case 'D': + if (first+1 != last) + { + switch (first[1]) + { + case 'd': + db.names.push_back("decimal64"); + first += 2; + break; + case 'e': + db.names.push_back("decimal128"); + first += 2; + break; + case 'f': + db.names.push_back("decimal32"); + first += 2; + break; + case 'h': + db.names.push_back("decimal16"); + first += 2; + break; + case 'i': + db.names.push_back("char32_t"); + first += 2; + break; + case 's': + db.names.push_back("char16_t"); + first += 2; + break; + case 'a': + db.names.push_back("auto"); + first += 2; + break; + case 'c': + db.names.push_back("decltype(auto)"); + first += 2; + break; + case 'n': + db.names.push_back("std::nullptr_t"); + first += 2; + break; + } + } + break; + } + } + return first; +} + +// <CV-qualifiers> ::= [r] [V] [K] + +const char* +parse_cv_qualifiers(const char* first, const char* last, unsigned& cv) +{ + cv = 0; + if (first != last) + { + if (*first == 'r') + { + cv |= 4; + ++first; + } + if (*first == 'V') + { + cv |= 2; + ++first; + } + if (*first == 'K') + { + cv |= 1; + ++first; + } + } + return first; +} + +// <template-param> ::= T_ # first template parameter +// ::= T <parameter-2 non-negative number> _ + +template <class C> +const char* +parse_template_param(const char* first, const char* last, C& db) +{ + if (last - first >= 2) + { + if (*first == 'T') + { + if (first[1] == '_') + { + if (db.template_param.empty()) + return first; + if (!db.template_param.back().empty()) + { + for (auto& t : db.template_param.back().front()) + db.names.push_back(t); + first += 2; + } + else + { + db.names.push_back("T_"); + first += 2; + db.fix_forward_references = true; + } + } + else if (isdigit(first[1])) + { + const char* t = first+1; + size_t sub = static_cast<size_t>(*t - '0'); + for (++t; t != last && isdigit(*t); ++t) + { + sub *= 10; + sub += static_cast<size_t>(*t - '0'); + } + if (t == last || *t != '_' || db.template_param.empty()) + return first; + ++sub; + if (sub < db.template_param.back().size()) + { + for (auto& temp : db.template_param.back()[sub]) + db.names.push_back(temp); + first = t+1; + } + else + { + db.names.push_back(typename C::String(first, t+1)); + first = t+1; + db.fix_forward_references = true; + } + } + } + } + return first; +} + +// cc <type> <expression> # const_cast<type> (expression) + +template <class C> +const char* +parse_const_cast_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 'c' && first[1] == 'c') + { + const char* t = parse_type(first+2, last, db); + if (t != first+2) + { + const char* t1 = parse_expression(t, last, db); + if (t1 != t) + { + if (db.names.size() < 2) + return first; + auto expr = db.names.back().move_full(); + db.names.pop_back(); + db.names.back() = "const_cast<" + db.names.back().move_full() + ">(" + expr + ")"; + first = t1; + } + } + } + return first; +} + +// dc <type> <expression> # dynamic_cast<type> (expression) + +template <class C> +const char* +parse_dynamic_cast_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 'd' && first[1] == 'c') + { + const char* t = parse_type(first+2, last, db); + if (t != first+2) + { + const char* t1 = parse_expression(t, last, db); + if (t1 != t) + { + if (db.names.size() < 2) + return first; + auto expr = db.names.back().move_full(); + db.names.pop_back(); + db.names.back() = "dynamic_cast<" + db.names.back().move_full() + ">(" + expr + ")"; + first = t1; + } + } + } + return first; +} + +// rc <type> <expression> # reinterpret_cast<type> (expression) + +template <class C> +const char* +parse_reinterpret_cast_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 'r' && first[1] == 'c') + { + const char* t = parse_type(first+2, last, db); + if (t != first+2) + { + const char* t1 = parse_expression(t, last, db); + if (t1 != t) + { + if (db.names.size() < 2) + return first; + auto expr = db.names.back().move_full(); + db.names.pop_back(); + db.names.back() = "reinterpret_cast<" + db.names.back().move_full() + ">(" + expr + ")"; + first = t1; + } + } + } + return first; +} + +// sc <type> <expression> # static_cast<type> (expression) + +template <class C> +const char* +parse_static_cast_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 's' && first[1] == 'c') + { + const char* t = parse_type(first+2, last, db); + if (t != first+2) + { + const char* t1 = parse_expression(t, last, db); + if (t1 != t) + { + if (db.names.size() < 2) + return first; + auto expr = db.names.back().move_full(); + db.names.pop_back(); + db.names.back() = "static_cast<" + db.names.back().move_full() + ">(" + expr + ")"; + first = t1; + } + } + } + return first; +} + +// sp <expression> # pack expansion + +template <class C> +const char* +parse_pack_expansion(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 's' && first[1] == 'p') + { + const char* t = parse_expression(first+2, last, db); + if (t != first+2) + first = t; + } + return first; +} + +// st <type> # sizeof (a type) + +template <class C> +const char* +parse_sizeof_type_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 's' && first[1] == 't') + { + const char* t = parse_type(first+2, last, db); + if (t != first+2) + { + if (db.names.empty()) + return first; + db.names.back() = "sizeof (" + db.names.back().move_full() + ")"; + first = t; + } + } + return first; +} + +// sz <expr> # sizeof (a expression) + +template <class C> +const char* +parse_sizeof_expr_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 's' && first[1] == 'z') + { + const char* t = parse_expression(first+2, last, db); + if (t != first+2) + { + if (db.names.empty()) + return first; + db.names.back() = "sizeof (" + db.names.back().move_full() + ")"; + first = t; + } + } + return first; +} + +// sZ <template-param> # size of a parameter pack + +template <class C> +const char* +parse_sizeof_param_pack_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 's' && first[1] == 'Z' && first[2] == 'T') + { + size_t k0 = db.names.size(); + const char* t = parse_template_param(first+2, last, db); + size_t k1 = db.names.size(); + if (t != first+2) + { + typename C::String tmp("sizeof...("); + size_t k = k0; + if (k != k1) + { + tmp += db.names[k].move_full(); + for (++k; k != k1; ++k) + tmp += ", " + db.names[k].move_full(); + } + tmp += ")"; + for (; k1 != k0; --k1) + db.names.pop_back(); + db.names.push_back(std::move(tmp)); + first = t; + } + } + return first; +} + +// <function-param> ::= fp <top-level CV-qualifiers> _ # L == 0, first parameter +// ::= fp <top-level CV-qualifiers> <parameter-2 non-negative number> _ # L == 0, second and later parameters +// ::= fL <L-1 non-negative number> p <top-level CV-qualifiers> _ # L > 0, first parameter +// ::= fL <L-1 non-negative number> p <top-level CV-qualifiers> <parameter-2 non-negative number> _ # L > 0, second and later parameters + +template <class C> +const char* +parse_function_param(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && *first == 'f') + { + if (first[1] == 'p') + { + unsigned cv; + const char* t = parse_cv_qualifiers(first+2, last, cv); + const char* t1 = parse_number(t, last); + if (t1 != last && *t1 == '_') + { + db.names.push_back("fp" + typename C::String(t, t1)); + first = t1+1; + } + } + else if (first[1] == 'L') + { + unsigned cv; + const char* t0 = parse_number(first+2, last); + if (t0 != last && *t0 == 'p') + { + ++t0; + const char* t = parse_cv_qualifiers(t0, last, cv); + const char* t1 = parse_number(t, last); + if (t1 != last && *t1 == '_') + { + db.names.push_back("fp" + typename C::String(t, t1)); + first = t1+1; + } + } + } + } + return first; +} + +// sZ <function-param> # size of a function parameter pack + +template <class C> +const char* +parse_sizeof_function_param_pack_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 's' && first[1] == 'Z' && first[2] == 'f') + { + const char* t = parse_function_param(first+2, last, db); + if (t != first+2) + { + if (db.names.empty()) + return first; + db.names.back() = "sizeof...(" + db.names.back().move_full() + ")"; + first = t; + } + } + return first; +} + +// te <expression> # typeid (expression) +// ti <type> # typeid (type) + +template <class C> +const char* +parse_typeid_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 't' && (first[1] == 'e' || first[1] == 'i')) + { + const char* t; + if (first[1] == 'e') + t = parse_expression(first+2, last, db); + else + t = parse_type(first+2, last, db); + if (t != first+2) + { + if (db.names.empty()) + return first; + db.names.back() = "typeid(" + db.names.back().move_full() + ")"; + first = t; + } + } + return first; +} + +// tw <expression> # throw expression + +template <class C> +const char* +parse_throw_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 't' && first[1] == 'w') + { + const char* t = parse_expression(first+2, last, db); + if (t != first+2) + { + if (db.names.empty()) + return first; + db.names.back() = "throw " + db.names.back().move_full(); + first = t; + } + } + return first; +} + +// ds <expression> <expression> # expr.*expr + +template <class C> +const char* +parse_dot_star_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 'd' && first[1] == 's') + { + const char* t = parse_expression(first+2, last, db); + if (t != first+2) + { + const char* t1 = parse_expression(t, last, db); + if (t1 != t) + { + if (db.names.size() < 2) + return first; + auto expr = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += ".*" + expr; + first = t1; + } + } + } + return first; +} + +// <simple-id> ::= <source-name> [ <template-args> ] + +template <class C> +const char* +parse_simple_id(const char* first, const char* last, C& db) +{ + if (first != last) + { + const char* t = parse_source_name(first, last, db); + if (t != first) + { + const char* t1 = parse_template_args(t, last, db); + if (t1 != t) + { + if (db.names.size() < 2) + return first; + auto args = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += std::move(args); + } + first = t1; + } + else + first = t; + } + return first; +} + +// <unresolved-type> ::= <template-param> +// ::= <decltype> +// ::= <substitution> + +template <class C> +const char* +parse_unresolved_type(const char* first, const char* last, C& db) +{ + if (first != last) + { + const char* t = first; + switch (*first) + { + case 'T': + { + size_t k0 = db.names.size(); + t = parse_template_param(first, last, db); + size_t k1 = db.names.size(); + if (t != first && k1 == k0 + 1) + { + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + first = t; + } + else + { + for (; k1 != k0; --k1) + db.names.pop_back(); + } + break; + } + case 'D': + t = parse_decltype(first, last, db); + if (t != first) + { + if (db.names.empty()) + return first; + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + first = t; + } + break; + case 'S': + t = parse_substitution(first, last, db); + if (t != first) + first = t; + else + { + if (last - first > 2 && first[1] == 't') + { + t = parse_unqualified_name(first+2, last, db); + if (t != first+2) + { + if (db.names.empty()) + return first; + db.names.back().first.insert(0, "std::"); + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + first = t; + } + } + } + break; + } + } + return first; +} + +// <destructor-name> ::= <unresolved-type> # e.g., ~T or ~decltype(f()) +// ::= <simple-id> # e.g., ~A<2*N> + +template <class C> +const char* +parse_destructor_name(const char* first, const char* last, C& db) +{ + if (first != last) + { + const char* t = parse_unresolved_type(first, last, db); + if (t == first) + t = parse_simple_id(first, last, db); + if (t != first) + { + if (db.names.empty()) + return first; + db.names.back().first.insert(0, "~"); + first = t; + } + } + return first; +} + +// <base-unresolved-name> ::= <simple-id> # unresolved name +// extension ::= <operator-name> # unresolved operator-function-id +// extension ::= <operator-name> <template-args> # unresolved operator template-id +// ::= on <operator-name> # unresolved operator-function-id +// ::= on <operator-name> <template-args> # unresolved operator template-id +// ::= dn <destructor-name> # destructor or pseudo-destructor; +// # e.g. ~X or ~X<N-1> + +template <class C> +const char* +parse_base_unresolved_name(const char* first, const char* last, C& db) +{ + if (last - first >= 2) + { + if ((first[0] == 'o' || first[0] == 'd') && first[1] == 'n') + { + if (first[0] == 'o') + { + const char* t = parse_operator_name(first+2, last, db); + if (t != first+2) + { + first = parse_template_args(t, last, db); + if (first != t) + { + if (db.names.size() < 2) + return first; + auto args = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += std::move(args); + } + } + } + else + { + const char* t = parse_destructor_name(first+2, last, db); + if (t != first+2) + first = t; + } + } + else + { + const char* t = parse_simple_id(first, last, db); + if (t == first) + { + t = parse_operator_name(first, last, db); + if (t != first) + { + first = parse_template_args(t, last, db); + if (first != t) + { + if (db.names.size() < 2) + return first; + auto args = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += std::move(args); + } + } + } + else + first = t; + } + } + return first; +} + +// <unresolved-qualifier-level> ::= <simple-id> + +template <class C> +const char* +parse_unresolved_qualifier_level(const char* first, const char* last, C& db) +{ + return parse_simple_id(first, last, db); +} + +// <unresolved-name> +// extension ::= srN <unresolved-type> [<template-args>] <unresolved-qualifier-level>* E <base-unresolved-name> +// ::= [gs] <base-unresolved-name> # x or (with "gs") ::x +// ::= [gs] sr <unresolved-qualifier-level>+ E <base-unresolved-name> +// # A::x, N::y, A<T>::z; "gs" means leading "::" +// ::= sr <unresolved-type> <base-unresolved-name> # T::x / decltype(p)::x +// extension ::= sr <unresolved-type> <template-args> <base-unresolved-name> +// # T::N::x /decltype(p)::N::x +// (ignored) ::= srN <unresolved-type> <unresolved-qualifier-level>+ E <base-unresolved-name> + +template <class C> +const char* +parse_unresolved_name(const char* first, const char* last, C& db) +{ + if (last - first > 2) + { + const char* t = first; + bool global = false; + if (t[0] == 'g' && t[1] == 's') + { + global = true; + t += 2; + } + const char* t2 = parse_base_unresolved_name(t, last, db); + if (t2 != t) + { + if (global) + { + if (db.names.empty()) + return first; + db.names.back().first.insert(0, "::"); + } + first = t2; + } + else if (last - t > 2 && t[0] == 's' && t[1] == 'r') + { + if (t[2] == 'N') + { + t += 3; + const char* t1 = parse_unresolved_type(t, last, db); + if (t1 == t || t1 == last) + return first; + t = t1; + t1 = parse_template_args(t, last, db); + if (t1 != t) + { + if (db.names.size() < 2) + return first; + auto args = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += std::move(args); + t = t1; + if (t == last) + { + db.names.pop_back(); + return first; + } + } + while (*t != 'E') + { + t1 = parse_unresolved_qualifier_level(t, last, db); + if (t1 == t || t1 == last || db.names.size() < 2) + return first; + auto s = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += "::" + std::move(s); + t = t1; + } + ++t; + t1 = parse_base_unresolved_name(t, last, db); + if (t1 == t) + { + if (!db.names.empty()) + db.names.pop_back(); + return first; + } + if (db.names.size() < 2) + return first; + auto s = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += "::" + std::move(s); + first = t1; + } + else + { + t += 2; + const char* t1 = parse_unresolved_type(t, last, db); + if (t1 != t) + { + t = t1; + t1 = parse_template_args(t, last, db); + if (t1 != t) + { + if (db.names.size() < 2) + return first; + auto args = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += std::move(args); + t = t1; + } + t1 = parse_base_unresolved_name(t, last, db); + if (t1 == t) + { + if (!db.names.empty()) + db.names.pop_back(); + return first; + } + if (db.names.size() < 2) + return first; + auto s = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += "::" + std::move(s); + first = t1; + } + else + { + t1 = parse_unresolved_qualifier_level(t, last, db); + if (t1 == t || t1 == last) + return first; + t = t1; + if (global) + { + if (db.names.empty()) + return first; + db.names.back().first.insert(0, "::"); + } + while (*t != 'E') + { + t1 = parse_unresolved_qualifier_level(t, last, db); + if (t1 == t || t1 == last || db.names.size() < 2) + return first; + auto s = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += "::" + std::move(s); + t = t1; + } + ++t; + t1 = parse_base_unresolved_name(t, last, db); + if (t1 == t) + { + if (!db.names.empty()) + db.names.pop_back(); + return first; + } + if (db.names.size() < 2) + return first; + auto s = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += "::" + std::move(s); + first = t1; + } + } + } + } + return first; +} + +// dt <expression> <unresolved-name> # expr.name + +template <class C> +const char* +parse_dot_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 'd' && first[1] == 't') + { + const char* t = parse_expression(first+2, last, db); + if (t != first+2) + { + const char* t1 = parse_unresolved_name(t, last, db); + if (t1 != t) + { + if (db.names.size() < 2) + return first; + auto name = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += "." + name; + first = t1; + } + } + } + return first; +} + +// cl <expression>+ E # call + +template <class C> +const char* +parse_call_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 4 && first[0] == 'c' && first[1] == 'l') + { + const char* t = parse_expression(first+2, last, db); + if (t != first+2) + { + if (t == last) + return first; + if (db.names.empty()) + return first; + db.names.back().first += db.names.back().second; + db.names.back().second = typename C::String(); + db.names.back().first.append("("); + bool first_expr = true; + while (*t != 'E') + { + const char* t1 = parse_expression(t, last, db); + if (t1 == t || t1 == last) + return first; + if (db.names.empty()) + return first; + auto tmp = db.names.back().move_full(); + db.names.pop_back(); + if (!tmp.empty()) + { + if (db.names.empty()) + return first; + if (!first_expr) + { + db.names.back().first.append(", "); + first_expr = false; + } + db.names.back().first.append(tmp); + } + t = t1; + } + ++t; + if (db.names.empty()) + return first; + db.names.back().first.append(")"); + first = t; + } + } + return first; +} + +// [gs] nw <expression>* _ <type> E # new (expr-list) type +// [gs] nw <expression>* _ <type> <initializer> # new (expr-list) type (init) +// [gs] na <expression>* _ <type> E # new[] (expr-list) type +// [gs] na <expression>* _ <type> <initializer> # new[] (expr-list) type (init) +// <initializer> ::= pi <expression>* E # parenthesized initialization + +template <class C> +const char* +parse_new_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 4) + { + const char* t = first; + bool parsed_gs = false; + if (t[0] == 'g' && t[1] == 's') + { + t += 2; + parsed_gs = true; + } + if (t[0] == 'n' && (t[1] == 'w' || t[1] == 'a')) + { + bool is_array = t[1] == 'a'; + t += 2; + if (t == last) + return first; + bool has_expr_list = false; + bool first_expr = true; + while (*t != '_') + { + const char* t1 = parse_expression(t, last, db); + if (t1 == t || t1 == last) + return first; + has_expr_list = true; + if (!first_expr) + { + if (db.names.empty()) + return first; + auto tmp = db.names.back().move_full(); + db.names.pop_back(); + if (!tmp.empty()) + { + if (db.names.empty()) + return first; + db.names.back().first.append(", "); + db.names.back().first.append(tmp); + first_expr = false; + } + } + t = t1; + } + ++t; + const char* t1 = parse_type(t, last, db); + if (t1 == t || t1 == last) + return first; + t = t1; + bool has_init = false; + if (last - t >= 3 && t[0] == 'p' && t[1] == 'i') + { + t += 2; + has_init = true; + first_expr = true; + while (*t != 'E') + { + t1 = parse_expression(t, last, db); + if (t1 == t || t1 == last) + return first; + if (!first_expr) + { + if (db.names.empty()) + return first; + auto tmp = db.names.back().move_full(); + db.names.pop_back(); + if (!tmp.empty()) + { + if (db.names.empty()) + return first; + db.names.back().first.append(", "); + db.names.back().first.append(tmp); + first_expr = false; + } + } + t = t1; + } + } + if (*t != 'E') + return first; + typename C::String init_list; + if (has_init) + { + if (db.names.empty()) + return first; + init_list = db.names.back().move_full(); + db.names.pop_back(); + } + if (db.names.empty()) + return first; + auto type = db.names.back().move_full(); + db.names.pop_back(); + typename C::String expr_list; + if (has_expr_list) + { + if (db.names.empty()) + return first; + expr_list = db.names.back().move_full(); + db.names.pop_back(); + } + typename C::String r; + if (parsed_gs) + r = "::"; + if (is_array) + r += "[] "; + else + r += " "; + if (has_expr_list) + r += "(" + expr_list + ") "; + r += type; + if (has_init) + r += " (" + init_list + ")"; + db.names.push_back(std::move(r)); + first = t+1; + } + } + return first; +} + +// cv <type> <expression> # conversion with one argument +// cv <type> _ <expression>* E # conversion with a different number of arguments + +template <class C> +const char* +parse_conversion_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 'c' && first[1] == 'v') + { + bool try_to_parse_template_args = db.try_to_parse_template_args; + db.try_to_parse_template_args = false; + const char* t = parse_type(first+2, last, db); + db.try_to_parse_template_args = try_to_parse_template_args; + if (t != first+2 && t != last) + { + if (*t != '_') + { + const char* t1 = parse_expression(t, last, db); + if (t1 == t) + return first; + t = t1; + } + else + { + ++t; + if (t == last) + return first; + if (*t == 'E') + db.names.emplace_back(); + else + { + bool first_expr = true; + while (*t != 'E') + { + const char* t1 = parse_expression(t, last, db); + if (t1 == t || t1 == last) + return first; + if (!first_expr) + { + if (db.names.empty()) + return first; + auto tmp = db.names.back().move_full(); + db.names.pop_back(); + if (!tmp.empty()) + { + if (db.names.empty()) + return first; + db.names.back().first.append(", "); + db.names.back().first.append(tmp); + first_expr = false; + } + } + t = t1; + } + } + ++t; + } + if (db.names.size() < 2) + return first; + auto tmp = db.names.back().move_full(); + db.names.pop_back(); + db.names.back() = "(" + db.names.back().move_full() + ")(" + tmp + ")"; + first = t; + } + } + return first; +} + +// pt <expression> <expression> # expr->name + +template <class C> +const char* +parse_arrow_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 'p' && first[1] == 't') + { + const char* t = parse_expression(first+2, last, db); + if (t != first+2) + { + const char* t1 = parse_expression(t, last, db); + if (t1 != t) + { + if (db.names.size() < 2) + return first; + auto tmp = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += "->"; + db.names.back().first += tmp; + first = t1; + } + } + } + return first; +} + +// <ref-qualifier> ::= R # & ref-qualifier +// <ref-qualifier> ::= O # && ref-qualifier + +// <function-type> ::= F [Y] <bare-function-type> [<ref-qualifier>] E + +template <class C> +const char* +parse_function_type(const char* first, const char* last, C& db) +{ + if (first != last && *first == 'F') + { + const char* t = first+1; + if (t != last) + { + bool externC = false; + if (*t == 'Y') + { + externC = true; + if (++t == last) + return first; + } + const char* t1 = parse_type(t, last, db); + if (t1 != t) + { + t = t1; + typename C::String sig("("); + int ref_qual = 0; + while (true) + { + if (t == last) + { + db.names.pop_back(); + return first; + } + if (*t == 'E') + { + ++t; + break; + } + if (*t == 'v') + { + ++t; + continue; + } + if (*t == 'R' && t+1 != last && t[1] == 'E') + { + ref_qual = 1; + ++t; + continue; + } + if (*t == 'O' && t+1 != last && t[1] == 'E') + { + ref_qual = 2; + ++t; + continue; + } + size_t k0 = db.names.size(); + t1 = parse_type(t, last, db); + size_t k1 = db.names.size(); + if (t1 == t || t1 == last) + return first; + for (size_t k = k0; k < k1; ++k) + { + if (sig.size() > 1) + sig += ", "; + sig += db.names[k].move_full(); + } + for (size_t k = k0; k < k1; ++k) + db.names.pop_back(); + t = t1; + } + sig += ")"; + switch (ref_qual) + { + case 1: + sig += " &"; + break; + case 2: + sig += " &&"; + break; + } + if (db.names.empty()) + return first; + db.names.back().first += " "; + db.names.back().second.insert(0, sig); + first = t; + } + } + } + return first; +} + +// <pointer-to-member-type> ::= M <class type> <member type> + +template <class C> +const char* +parse_pointer_to_member_type(const char* first, const char* last, C& db) +{ + if (first != last && *first == 'M') + { + const char* t = parse_type(first+1, last, db); + if (t != first+1) + { + const char* t2 = parse_type(t, last, db); + if (t2 != t) + { + if (db.names.size() < 2) + return first; + auto func = std::move(db.names.back()); + db.names.pop_back(); + auto class_type = std::move(db.names.back()); + if (func.second.front() == '(') + { + db.names.back().first = std::move(func.first) + "(" + class_type.move_full() + "::*"; + db.names.back().second = ")" + std::move(func.second); + } + else + { + db.names.back().first = std::move(func.first) + " " + class_type.move_full() + "::*"; + db.names.back().second = std::move(func.second); + } + first = t2; + } + } + } + return first; +} + +// <array-type> ::= A <positive dimension number> _ <element type> +// ::= A [<dimension expression>] _ <element type> + +template <class C> +const char* +parse_array_type(const char* first, const char* last, C& db) +{ + if (first != last && *first == 'A' && first+1 != last) + { + if (first[1] == '_') + { + const char* t = parse_type(first+2, last, db); + if (t != first+2) + { + if (db.names.empty()) + return first; + if (db.names.back().second.substr(0, 2) == " [") + db.names.back().second.erase(0, 1); + db.names.back().second.insert(0, " []"); + first = t; + } + } + else if ('1' <= first[1] && first[1] <= '9') + { + const char* t = parse_number(first+1, last); + if (t != last && *t == '_') + { + const char* t2 = parse_type(t+1, last, db); + if (t2 != t+1) + { + if (db.names.empty()) + return first; + if (db.names.back().second.substr(0, 2) == " [") + db.names.back().second.erase(0, 1); + db.names.back().second.insert(0, " [" + typename C::String(first+1, t) + "]"); + first = t2; + } + } + } + else + { + const char* t = parse_expression(first+1, last, db); + if (t != first+1 && t != last && *t == '_') + { + const char* t2 = parse_type(++t, last, db); + if (t2 != t) + { + if (db.names.size() < 2) + return first; + auto type = std::move(db.names.back()); + db.names.pop_back(); + auto expr = std::move(db.names.back()); + db.names.back().first = std::move(type.first); + if (type.second.substr(0, 2) == " [") + type.second.erase(0, 1); + db.names.back().second = " [" + expr.move_full() + "]" + std::move(type.second); + first = t2; + } + } + } + } + return first; +} + +// <decltype> ::= Dt <expression> E # decltype of an id-expression or class member access (C++0x) +// ::= DT <expression> E # decltype of an expression (C++0x) + +template <class C> +const char* +parse_decltype(const char* first, const char* last, C& db) +{ + if (last - first >= 4 && first[0] == 'D') + { + switch (first[1]) + { + case 't': + case 'T': + { + const char* t = parse_expression(first+2, last, db); + if (t != first+2 && t != last && *t == 'E') + { + if (db.names.empty()) + return first; + db.names.back() = "decltype(" + db.names.back().move_full() + ")"; + first = t+1; + } + } + break; + } + } + return first; +} + +// extension: +// <vector-type> ::= Dv <positive dimension number> _ +// <extended element type> +// ::= Dv [<dimension expression>] _ <element type> +// <extended element type> ::= <element type> +// ::= p # AltiVec vector pixel + +template <class C> +const char* +parse_vector_type(const char* first, const char* last, C& db) +{ + if (last - first > 3 && first[0] == 'D' && first[1] == 'v') + { + if ('1' <= first[2] && first[2] <= '9') + { + const char* t = parse_number(first+2, last); + if (t == last || *t != '_') + return first; + const char* num = first + 2; + size_t sz = static_cast<size_t>(t - num); + if (++t != last) + { + if (*t != 'p') + { + const char* t1 = parse_type(t, last, db); + if (t1 != t) + { + if (db.names.empty()) + return first; + db.names.back().first += " vector[" + typename C::String(num, sz) + "]"; + first = t1; + } + } + else + { + ++t; + db.names.push_back("pixel vector[" + typename C::String(num, sz) + "]"); + first = t; + } + } + } + else + { + typename C::String num; + const char* t1 = first+2; + if (*t1 != '_') + { + const char* t = parse_expression(t1, last, db); + if (t != t1) + { + if (db.names.empty()) + return first; + num = db.names.back().move_full(); + db.names.pop_back(); + t1 = t; + } + } + if (t1 != last && *t1 == '_' && ++t1 != last) + { + const char* t = parse_type(t1, last, db); + if (t != t1) + { + if (db.names.empty()) + return first; + db.names.back().first += " vector[" + num + "]"; + first = t; + } + } + } + } + return first; +} + +// <type> ::= <builtin-type> +// ::= <function-type> +// ::= <class-enum-type> +// ::= <array-type> +// ::= <pointer-to-member-type> +// ::= <template-param> +// ::= <template-template-param> <template-args> +// ::= <decltype> +// ::= <substitution> +// ::= <CV-qualifiers> <type> +// ::= P <type> # pointer-to +// ::= R <type> # reference-to +// ::= O <type> # rvalue reference-to (C++0x) +// ::= C <type> # complex pair (C 2000) +// ::= G <type> # imaginary (C 2000) +// ::= Dp <type> # pack expansion (C++0x) +// ::= U <source-name> <type> # vendor extended type qualifier +// extension := U <objc-name> <objc-type> # objc-type<identifier> +// extension := <vector-type> # <vector-type> starts with Dv + +// <objc-name> ::= <k0 number> objcproto <k1 number> <identifier> # k0 = 9 + <number of digits in k1> + k1 +// <objc-type> := <source-name> # PU<11+>objcproto 11objc_object<source-name> 11objc_object -> id<source-name> + +template <class C> +const char* +parse_type(const char* first, const char* last, C& db) +{ + if (first != last) + { + switch (*first) + { + case 'r': + case 'V': + case 'K': + { + unsigned cv = 0; + const char* t = parse_cv_qualifiers(first, last, cv); + if (t != first) + { + bool is_function = *t == 'F'; + size_t k0 = db.names.size(); + const char* t1 = parse_type(t, last, db); + size_t k1 = db.names.size(); + if (t1 != t) + { + if (is_function) + db.subs.pop_back(); + db.subs.emplace_back(db.names.get_allocator()); + for (size_t k = k0; k < k1; ++k) + { + if (is_function) + { + size_t p = db.names[k].second.size(); + if (db.names[k].second[p-2] == '&') + p -= 3; + else if (db.names[k].second.back() == '&') + p -= 2; + if (cv & 1) + { + db.names[k].second.insert(p, " const"); + p += 6; + } + if (cv & 2) + { + db.names[k].second.insert(p, " volatile"); + p += 9; + } + if (cv & 4) + db.names[k].second.insert(p, " restrict"); + } + else + { + if (cv & 1) + db.names[k].first.append(" const"); + if (cv & 2) + db.names[k].first.append(" volatile"); + if (cv & 4) + db.names[k].first.append(" restrict"); + } + db.subs.back().push_back(db.names[k]); + } + first = t1; + } + } + } + break; + default: + { + const char* t = parse_builtin_type(first, last, db); + if (t != first) + { + first = t; + } + else + { + switch (*first) + { + case 'A': + t = parse_array_type(first, last, db); + if (t != first) + { + if (db.names.empty()) + return first; + first = t; + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + } + break; + case 'C': + t = parse_type(first+1, last, db); + if (t != first+1) + { + if (db.names.empty()) + return first; + db.names.back().first.append(" complex"); + first = t; + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + } + break; + case 'F': + t = parse_function_type(first, last, db); + if (t != first) + { + if (db.names.empty()) + return first; + first = t; + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + } + break; + case 'G': + t = parse_type(first+1, last, db); + if (t != first+1) + { + if (db.names.empty()) + return first; + db.names.back().first.append(" imaginary"); + first = t; + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + } + break; + case 'M': + t = parse_pointer_to_member_type(first, last, db); + if (t != first) + { + if (db.names.empty()) + return first; + first = t; + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + } + break; + case 'O': + { + size_t k0 = db.names.size(); + t = parse_type(first+1, last, db); + size_t k1 = db.names.size(); + if (t != first+1) + { + db.subs.emplace_back(db.names.get_allocator()); + for (size_t k = k0; k < k1; ++k) + { + if (db.names[k].second.substr(0, 2) == " [") + { + db.names[k].first += " ("; + db.names[k].second.insert(0, ")"); + } + else if (db.names[k].second.front() == '(') + { + db.names[k].first += "("; + db.names[k].second.insert(0, ")"); + } + db.names[k].first.append("&&"); + db.subs.back().push_back(db.names[k]); + } + first = t; + } + break; + } + case 'P': + { + size_t k0 = db.names.size(); + t = parse_type(first+1, last, db); + size_t k1 = db.names.size(); + if (t != first+1) + { + db.subs.emplace_back(db.names.get_allocator()); + for (size_t k = k0; k < k1; ++k) + { + if (db.names[k].second.substr(0, 2) == " [") + { + db.names[k].first += " ("; + db.names[k].second.insert(0, ")"); + } + else if (db.names[k].second.front() == '(') + { + db.names[k].first += "("; + db.names[k].second.insert(0, ")"); + } + if (first[1] != 'U' || db.names[k].first.substr(0, 12) != "objc_object<") + { + db.names[k].first.append("*"); + } + else + { + db.names[k].first.replace(0, 11, "id"); + } + db.subs.back().push_back(db.names[k]); + } + first = t; + } + break; + } + case 'R': + { + size_t k0 = db.names.size(); + t = parse_type(first+1, last, db); + size_t k1 = db.names.size(); + if (t != first+1) + { + db.subs.emplace_back(db.names.get_allocator()); + for (size_t k = k0; k < k1; ++k) + { + if (db.names[k].second.substr(0, 2) == " [") + { + db.names[k].first += " ("; + db.names[k].second.insert(0, ")"); + } + else if (db.names[k].second.front() == '(') + { + db.names[k].first += "("; + db.names[k].second.insert(0, ")"); + } + db.names[k].first.append("&"); + db.subs.back().push_back(db.names[k]); + } + first = t; + } + break; + } + case 'T': + { + size_t k0 = db.names.size(); + t = parse_template_param(first, last, db); + size_t k1 = db.names.size(); + if (t != first) + { + db.subs.emplace_back(db.names.get_allocator()); + for (size_t k = k0; k < k1; ++k) + db.subs.back().push_back(db.names[k]); + if (db.try_to_parse_template_args && k1 == k0+1) + { + const char* t1 = parse_template_args(t, last, db); + if (t1 != t) + { + auto args = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += std::move(args); + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + t = t1; + } + } + first = t; + } + break; + } + case 'U': + if (first+1 != last) + { + t = parse_source_name(first+1, last, db); + if (t != first+1) + { + const char* t2 = parse_type(t, last, db); + if (t2 != t) + { + if (db.names.size() < 2) + return first; + auto type = db.names.back().move_full(); + db.names.pop_back(); + if (db.names.back().first.substr(0, 9) != "objcproto") + { + db.names.back() = type + " " + db.names.back().move_full(); + } + else + { + auto proto = db.names.back().move_full(); + db.names.pop_back(); + t = parse_source_name(proto.data() + 9, proto.data() + proto.size(), db); + if (t != proto.data() + 9) + { + db.names.back() = type + "<" + db.names.back().move_full() + ">"; + } + else + { + db.names.push_back(type + " " + proto); + } + } + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + first = t2; + } + } + } + break; + case 'S': + if (first+1 != last && first[1] == 't') + { + t = parse_name(first, last, db); + if (t != first) + { + if (db.names.empty()) + return first; + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + first = t; + } + } + else + { + t = parse_substitution(first, last, db); + if (t != first) + { + first = t; + // Parsed a substitution. If the substitution is a + // <template-param> it might be followed by <template-args>. + t = parse_template_args(first, last, db); + if (t != first) + { + if (db.names.size() < 2) + return first; + auto template_args = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += template_args; + // Need to create substitution for <template-template-param> <template-args> + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + first = t; + } + } + } + break; + case 'D': + if (first+1 != last) + { + switch (first[1]) + { + case 'p': + { + size_t k0 = db.names.size(); + t = parse_type(first+2, last, db); + size_t k1 = db.names.size(); + if (t != first+2) + { + db.subs.emplace_back(db.names.get_allocator()); + for (size_t k = k0; k < k1; ++k) + db.subs.back().push_back(db.names[k]); + first = t; + return first; + } + break; + } + case 't': + case 'T': + t = parse_decltype(first, last, db); + if (t != first) + { + if (db.names.empty()) + return first; + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + first = t; + return first; + } + break; + case 'v': + t = parse_vector_type(first, last, db); + if (t != first) + { + if (db.names.empty()) + return first; + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + first = t; + return first; + } + break; + } + } + // drop through + default: + // must check for builtin-types before class-enum-types to avoid + // ambiguities with operator-names + t = parse_builtin_type(first, last, db); + if (t != first) + { + first = t; + } + else + { + t = parse_name(first, last, db); + if (t != first) + { + if (db.names.empty()) + return first; + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + first = t; + } + } + break; + } + } + break; + } + } + } + return first; +} + +// <operator-name> +// ::= aa # && +// ::= ad # & (unary) +// ::= an # & +// ::= aN # &= +// ::= aS # = +// ::= cl # () +// ::= cm # , +// ::= co # ~ +// ::= cv <type> # (cast) +// ::= da # delete[] +// ::= de # * (unary) +// ::= dl # delete +// ::= dv # / +// ::= dV # /= +// ::= eo # ^ +// ::= eO # ^= +// ::= eq # == +// ::= ge # >= +// ::= gt # > +// ::= ix # [] +// ::= le # <= +// ::= li <source-name> # operator "" +// ::= ls # << +// ::= lS # <<= +// ::= lt # < +// ::= mi # - +// ::= mI # -= +// ::= ml # * +// ::= mL # *= +// ::= mm # -- (postfix in <expression> context) +// ::= na # new[] +// ::= ne # != +// ::= ng # - (unary) +// ::= nt # ! +// ::= nw # new +// ::= oo # || +// ::= or # | +// ::= oR # |= +// ::= pm # ->* +// ::= pl # + +// ::= pL # += +// ::= pp # ++ (postfix in <expression> context) +// ::= ps # + (unary) +// ::= pt # -> +// ::= qu # ? +// ::= rm # % +// ::= rM # %= +// ::= rs # >> +// ::= rS # >>= +// ::= v <digit> <source-name> # vendor extended operator + +template <class C> +const char* +parse_operator_name(const char* first, const char* last, C& db) +{ + if (last - first >= 2) + { + switch (first[0]) + { + case 'a': + switch (first[1]) + { + case 'a': + db.names.push_back("operator&&"); + first += 2; + break; + case 'd': + case 'n': + db.names.push_back("operator&"); + first += 2; + break; + case 'N': + db.names.push_back("operator&="); + first += 2; + break; + case 'S': + db.names.push_back("operator="); + first += 2; + break; + } + break; + case 'c': + switch (first[1]) + { + case 'l': + db.names.push_back("operator()"); + first += 2; + break; + case 'm': + db.names.push_back("operator,"); + first += 2; + break; + case 'o': + db.names.push_back("operator~"); + first += 2; + break; + case 'v': + { + bool try_to_parse_template_args = db.try_to_parse_template_args; + db.try_to_parse_template_args = false; + const char* t = parse_type(first+2, last, db); + db.try_to_parse_template_args = try_to_parse_template_args; + if (t != first+2) + { + if (db.names.empty()) + return first; + db.names.back().first.insert(0, "operator "); +#if UPSTREAM_CODE + db.parsed_ctor_dtor_cv = true; +#else + db.parsed_ctor_dtor_cv = false; +#endif + first = t; + } + } + break; + } + break; + case 'd': + switch (first[1]) + { + case 'a': + db.names.push_back("operator delete[]"); + first += 2; + break; + case 'e': + db.names.push_back("operator*"); + first += 2; + break; + case 'l': + db.names.push_back("operator delete"); + first += 2; + break; + case 'v': + db.names.push_back("operator/"); + first += 2; + break; + case 'V': + db.names.push_back("operator/="); + first += 2; + break; + } + break; + case 'e': + switch (first[1]) + { + case 'o': + db.names.push_back("operator^"); + first += 2; + break; + case 'O': + db.names.push_back("operator^="); + first += 2; + break; + case 'q': + db.names.push_back("operator=="); + first += 2; + break; + } + break; + case 'g': + switch (first[1]) + { + case 'e': + db.names.push_back("operator>="); + first += 2; + break; + case 't': + db.names.push_back("operator>"); + first += 2; + break; + } + break; + case 'i': + if (first[1] == 'x') + { + db.names.push_back("operator[]"); + first += 2; + } + break; + case 'l': + switch (first[1]) + { + case 'e': + db.names.push_back("operator<="); + first += 2; + break; + case 'i': + { + const char* t = parse_source_name(first+2, last, db); + if (t != first+2) + { + if (db.names.empty()) + return first; + db.names.back().first.insert(0, "operator\"\" "); + first = t; + } + } + break; + case 's': + db.names.push_back("operator<<"); + first += 2; + break; + case 'S': + db.names.push_back("operator<<="); + first += 2; + break; + case 't': + db.names.push_back("operator<"); + first += 2; + break; + } + break; + case 'm': + switch (first[1]) + { + case 'i': + db.names.push_back("operator-"); + first += 2; + break; + case 'I': + db.names.push_back("operator-="); + first += 2; + break; + case 'l': + db.names.push_back("operator*"); + first += 2; + break; + case 'L': + db.names.push_back("operator*="); + first += 2; + break; + case 'm': + db.names.push_back("operator--"); + first += 2; + break; + } + break; + case 'n': + switch (first[1]) + { + case 'a': + db.names.push_back("operator new[]"); + first += 2; + break; + case 'e': + db.names.push_back("operator!="); + first += 2; + break; + case 'g': + db.names.push_back("operator-"); + first += 2; + break; + case 't': + db.names.push_back("operator!"); + first += 2; + break; + case 'w': + db.names.push_back("operator new"); + first += 2; + break; + } + break; + case 'o': + switch (first[1]) + { + case 'o': + db.names.push_back("operator||"); + first += 2; + break; + case 'r': + db.names.push_back("operator|"); + first += 2; + break; + case 'R': + db.names.push_back("operator|="); + first += 2; + break; + } + break; + case 'p': + switch (first[1]) + { + case 'm': + db.names.push_back("operator->*"); + first += 2; + break; + case 'l': + db.names.push_back("operator+"); + first += 2; + break; + case 'L': + db.names.push_back("operator+="); + first += 2; + break; + case 'p': + db.names.push_back("operator++"); + first += 2; + break; + case 's': + db.names.push_back("operator+"); + first += 2; + break; + case 't': + db.names.push_back("operator->"); + first += 2; + break; + } + break; + case 'q': + if (first[1] == 'u') + { + db.names.push_back("operator?"); + first += 2; + } + break; + case 'r': + switch (first[1]) + { + case 'm': + db.names.push_back("operator%"); + first += 2; + break; + case 'M': + db.names.push_back("operator%="); + first += 2; + break; + case 's': + db.names.push_back("operator>>"); + first += 2; + break; + case 'S': + db.names.push_back("operator>>="); + first += 2; + break; + } + break; + case 'v': + if (std::isdigit(first[1])) + { + const char* t = parse_source_name(first+2, last, db); + if (t != first+2) + { + if (db.names.empty()) + return first; + db.names.back().first.insert(0, "operator "); + first = t; + } + } + break; + } + } + return first; +} + +template <class C> +const char* +parse_integer_literal(const char* first, const char* last, const typename C::String& lit, C& db) +{ + const char* t = parse_number(first, last); + if (t != first && t != last && *t == 'E') + { + if (lit.size() > 3) + db.names.push_back("(" + lit + ")"); + else + db.names.emplace_back(); + if (*first == 'n') + { + db.names.back().first += '-'; + ++first; + } + db.names.back().first.append(first, t); + if (lit.size() <= 3) + db.names.back().first += lit; + first = t+1; + } + return first; +} + +// <expr-primary> ::= L <type> <value number> E # integer literal +// ::= L <type> <value float> E # floating literal +// ::= L <string type> E # string literal +// ::= L <nullptr type> E # nullptr literal (i.e., "LDnE") +// ::= L <type> <real-part float> _ <imag-part float> E # complex floating point literal (C 2000) +// ::= L <mangled-name> E # external name + +template <class C> +const char* +parse_expr_primary(const char* first, const char* last, C& db) +{ + if (last - first >= 4 && *first == 'L') + { + switch (first[1]) + { + case 'w': + { + const char* t = parse_integer_literal(first+2, last, "wchar_t", db); + if (t != first+2) + first = t; + } + break; + case 'b': + if (first[3] == 'E') + { + switch (first[2]) + { + case '0': + db.names.push_back("false"); + first += 4; + break; + case '1': + db.names.push_back("true"); + first += 4; + break; + } + } + break; + case 'c': + { + const char* t = parse_integer_literal(first+2, last, "char", db); + if (t != first+2) + first = t; + } + break; + case 'a': + { + const char* t = parse_integer_literal(first+2, last, "signed char", db); + if (t != first+2) + first = t; + } + break; + case 'h': + { + const char* t = parse_integer_literal(first+2, last, "unsigned char", db); + if (t != first+2) + first = t; + } + break; + case 's': + { + const char* t = parse_integer_literal(first+2, last, "short", db); + if (t != first+2) + first = t; + } + break; + case 't': + { + const char* t = parse_integer_literal(first+2, last, "unsigned short", db); + if (t != first+2) + first = t; + } + break; + case 'i': + { + const char* t = parse_integer_literal(first+2, last, "", db); + if (t != first+2) + first = t; + } + break; + case 'j': + { + const char* t = parse_integer_literal(first+2, last, "u", db); + if (t != first+2) + first = t; + } + break; + case 'l': + { + const char* t = parse_integer_literal(first+2, last, "l", db); + if (t != first+2) + first = t; + } + break; + case 'm': + { + const char* t = parse_integer_literal(first+2, last, "ul", db); + if (t != first+2) + first = t; + } + break; + case 'x': + { + const char* t = parse_integer_literal(first+2, last, "ll", db); + if (t != first+2) + first = t; + } + break; + case 'y': + { + const char* t = parse_integer_literal(first+2, last, "ull", db); + if (t != first+2) + first = t; + } + break; + case 'n': + { + const char* t = parse_integer_literal(first+2, last, "__int128", db); + if (t != first+2) + first = t; + } + break; + case 'o': + { + const char* t = parse_integer_literal(first+2, last, "unsigned __int128", db); + if (t != first+2) + first = t; + } + break; + case 'f': + { + const char* t = parse_floating_number<float>(first+2, last, db); + if (t != first+2) + first = t; + } + break; + case 'd': + { + const char* t = parse_floating_number<double>(first+2, last, db); + if (t != first+2) + first = t; + } + break; + case 'e': + { + const char* t = parse_floating_number<long double>(first+2, last, db); + if (t != first+2) + first = t; + } + break; + case '_': + if (first[2] == 'Z') + { + const char* t = parse_encoding(first+3, last, db); + if (t != first+3 && t != last && *t == 'E') + first = t+1; + } + break; + case 'T': + // Invalid mangled name per + // http://sourcerytools.com/pipermail/cxx-abi-dev/2011-August/002422.html + break; + default: + { + // might be named type + const char* t = parse_type(first+1, last, db); + if (t != first+1 && t != last) + { + if (*t != 'E') + { + const char* n = t; + for (; n != last && isdigit(*n); ++n) + ; + if (n != t && n != last && *n == 'E') + { + if (db.names.empty()) + return first; + db.names.back() = "(" + db.names.back().move_full() + ")" + typename C::String(t, n); + first = n+1; + break; + } + } + else + { + first = t+1; + break; + } + } + } + } + } + return first; +} + +template <class String> +String +base_name(String& s) +{ + if (s.empty()) + return s; + if (s == "std::string") + { + s = "std::basic_string<char, std::char_traits<char>, std::allocator<char> >"; + return "basic_string"; + } + if (s == "std::istream") + { + s = "std::basic_istream<char, std::char_traits<char> >"; + return "basic_istream"; + } + if (s == "std::ostream") + { + s = "std::basic_ostream<char, std::char_traits<char> >"; + return "basic_ostream"; + } + if (s == "std::iostream") + { + s = "std::basic_iostream<char, std::char_traits<char> >"; + return "basic_iostream"; + } + const char* const pf = s.data(); + const char* pe = pf + s.size(); + if (pe[-1] == '>') + { + unsigned c = 1; + while (true) + { + if (--pe == pf) + return String(); + if (pe[-1] == '<') + { + if (--c == 0) + { + --pe; + break; + } + } + else if (pe[-1] == '>') + ++c; + } + } + const char* p0 = pe - 1; + for (; p0 != pf; --p0) + { + if (*p0 == ':') + { + ++p0; + break; + } + } + return String(p0, pe); +} + +// <ctor-dtor-name> ::= C1 # complete object constructor +// ::= C2 # base object constructor +// ::= C3 # complete object allocating constructor +// extension ::= C5 # ? +// ::= D0 # deleting destructor +// ::= D1 # complete object destructor +// ::= D2 # base object destructor +// extension ::= D5 # ? + +template <class C> +const char* +parse_ctor_dtor_name(const char* first, const char* last, C& db) +{ + if (last-first >= 2 && !db.names.empty()) + { + switch (first[0]) + { + case 'C': + switch (first[1]) + { + case '1': + case '2': + case '3': + case '5': + if (db.names.empty()) + return first; + db.names.push_back(base_name(db.names.back().first)); + first += 2; + db.parsed_ctor_dtor_cv = true; + break; + } + break; + case 'D': + switch (first[1]) + { + case '0': + case '1': + case '2': + case '5': + if (db.names.empty()) + return first; + db.names.push_back("~" + base_name(db.names.back().first)); + first += 2; + db.parsed_ctor_dtor_cv = true; + break; + } + break; + } + } + return first; +} + +// <unnamed-type-name> ::= Ut [ <nonnegative number> ] _ +// ::= <closure-type-name> +// +// <closure-type-name> ::= Ul <lambda-sig> E [ <nonnegative number> ] _ +// +// <lambda-sig> ::= <parameter type>+ # Parameter types or "v" if the lambda has no parameters + +template <class C> +const char* +parse_unnamed_type_name(const char* first, const char* last, C& db) +{ + if (last - first > 2 && first[0] == 'U') + { + char type = first[1]; + switch (type) + { + case 't': + { + db.names.push_back(typename C::String("'unnamed")); + const char* t0 = first+2; + if (t0 == last) + { + db.names.pop_back(); + return first; + } + if (std::isdigit(*t0)) + { + const char* t1 = t0 + 1; + while (t1 != last && std::isdigit(*t1)) + ++t1; + db.names.back().first.append(t0, t1); + t0 = t1; + } + db.names.back().first.push_back('\''); + if (t0 == last || *t0 != '_') + { + db.names.pop_back(); + return first; + } + first = t0 + 1; + } + break; + case 'l': + { + db.names.push_back(typename C::String("'lambda'(")); + const char* t0 = first+2; + if (first[2] == 'v') + { + db.names.back().first += ')'; + ++t0; + } + else + { + const char* t1 = parse_type(t0, last, db); + if (t1 == t0) + { + db.names.pop_back(); + return first; + } + if (db.names.size() < 2) + return first; + auto tmp = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first.append(tmp); + t0 = t1; + while (true) + { + t1 = parse_type(t0, last, db); + if (t1 == t0) + break; + if (db.names.size() < 2) + return first; + tmp = db.names.back().move_full(); + db.names.pop_back(); + if (!tmp.empty()) + { + db.names.back().first.append(", "); + db.names.back().first.append(tmp); + } + t0 = t1; + } + db.names.back().first.append(")"); + } + if (t0 == last || *t0 != 'E') + { + db.names.pop_back(); + return first; + } + ++t0; + if (t0 == last) + { + db.names.pop_back(); + return first; + } + if (std::isdigit(*t0)) + { + const char* t1 = t0 + 1; + while (t1 != last && std::isdigit(*t1)) + ++t1; + db.names.back().first.insert(db.names.back().first.begin()+7, t0, t1); + t0 = t1; + } + if (t0 == last || *t0 != '_') + { + db.names.pop_back(); + return first; + } + first = t0 + 1; + } + break; + } + } + return first; +} + +// <unqualified-name> ::= <operator-name> +// ::= <ctor-dtor-name> +// ::= <source-name> +// ::= <unnamed-type-name> + +template <class C> +const char* +parse_unqualified_name(const char* first, const char* last, C& db) +{ + if (first != last) + { + const char* t; + switch (*first) + { + case 'C': + case 'D': + t = parse_ctor_dtor_name(first, last, db); + if (t != first) + first = t; + break; + case 'U': + t = parse_unnamed_type_name(first, last, db); + if (t != first) + first = t; + break; + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + t = parse_source_name(first, last, db); + if (t != first) + first = t; + break; + default: + t = parse_operator_name(first, last, db); + if (t != first) + first = t; + break; + }; + } + return first; +} + +// <unscoped-name> ::= <unqualified-name> +// ::= St <unqualified-name> # ::std:: +// extension ::= StL<unqualified-name> + +template <class C> +const char* +parse_unscoped_name(const char* first, const char* last, C& db) +{ + if (last - first >= 2) + { + const char* t0 = first; + bool St = false; + if (first[0] == 'S' && first[1] == 't') + { + t0 += 2; + St = true; + if (t0 != last && *t0 == 'L') + ++t0; + } + const char* t1 = parse_unqualified_name(t0, last, db); + if (t1 != t0) + { + if (St) + { + if (db.names.empty()) + return first; + db.names.back().first.insert(0, "std::"); + } + first = t1; + } + } + return first; +} + +// at <type> # alignof (a type) + +template <class C> +const char* +parse_alignof_type(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 'a' && first[1] == 't') + { + const char* t = parse_type(first+2, last, db); + if (t != first+2) + { + if (db.names.empty()) + return first; + db.names.back().first = "alignof (" + db.names.back().move_full() + ")"; + first = t; + } + } + return first; +} + +// az <expression> # alignof (a expression) + +template <class C> +const char* +parse_alignof_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 'a' && first[1] == 'z') + { + const char* t = parse_expression(first+2, last, db); + if (t != first+2) + { + if (db.names.empty()) + return first; + db.names.back().first = "alignof (" + db.names.back().move_full() + ")"; + first = t; + } + } + return first; +} + +template <class C> +const char* +parse_noexcept_expression(const char* first, const char* last, C& db) +{ + const char* t1 = parse_expression(first, last, db); + if (t1 != first) + { + if (db.names.empty()) + return first; + db.names.back().first = "noexcept (" + db.names.back().move_full() + ")"; + first = t1; + } + return first; +} + +template <class C> +const char* +parse_prefix_expression(const char* first, const char* last, const typename C::String& op, C& db) +{ + const char* t1 = parse_expression(first, last, db); + if (t1 != first) + { + if (db.names.empty()) + return first; + db.names.back().first = op + "(" + db.names.back().move_full() + ")"; + first = t1; + } + return first; +} + +template <class C> +const char* +parse_binary_expression(const char* first, const char* last, const typename C::String& op, C& db) +{ + const char* t1 = parse_expression(first, last, db); + if (t1 != first) + { + const char* t2 = parse_expression(t1, last, db); + if (t2 != t1) + { + if (db.names.size() < 2) + return first; + auto op2 = db.names.back().move_full(); + db.names.pop_back(); + auto op1 = db.names.back().move_full(); + auto& nm = db.names.back().first; + nm.clear(); + if (op == ">") + nm += '('; + nm += "(" + op1 + ") " + op + " (" + op2 + ")"; + if (op == ">") + nm += ')'; + first = t2; + } + else + db.names.pop_back(); + } + return first; +} + +// <expression> ::= <unary operator-name> <expression> +// ::= <binary operator-name> <expression> <expression> +// ::= <ternary operator-name> <expression> <expression> <expression> +// ::= cl <expression>+ E # call +// ::= cv <type> <expression> # conversion with one argument +// ::= cv <type> _ <expression>* E # conversion with a different number of arguments +// ::= [gs] nw <expression>* _ <type> E # new (expr-list) type +// ::= [gs] nw <expression>* _ <type> <initializer> # new (expr-list) type (init) +// ::= [gs] na <expression>* _ <type> E # new[] (expr-list) type +// ::= [gs] na <expression>* _ <type> <initializer> # new[] (expr-list) type (init) +// ::= [gs] dl <expression> # delete expression +// ::= [gs] da <expression> # delete[] expression +// ::= pp_ <expression> # prefix ++ +// ::= mm_ <expression> # prefix -- +// ::= ti <type> # typeid (type) +// ::= te <expression> # typeid (expression) +// ::= dc <type> <expression> # dynamic_cast<type> (expression) +// ::= sc <type> <expression> # static_cast<type> (expression) +// ::= cc <type> <expression> # const_cast<type> (expression) +// ::= rc <type> <expression> # reinterpret_cast<type> (expression) +// ::= st <type> # sizeof (a type) +// ::= sz <expression> # sizeof (an expression) +// ::= at <type> # alignof (a type) +// ::= az <expression> # alignof (an expression) +// ::= nx <expression> # noexcept (expression) +// ::= <template-param> +// ::= <function-param> +// ::= dt <expression> <unresolved-name> # expr.name +// ::= pt <expression> <unresolved-name> # expr->name +// ::= ds <expression> <expression> # expr.*expr +// ::= sZ <template-param> # size of a parameter pack +// ::= sZ <function-param> # size of a function parameter pack +// ::= sp <expression> # pack expansion +// ::= tw <expression> # throw expression +// ::= tr # throw with no operand (rethrow) +// ::= <unresolved-name> # f(p), N::f(p), ::f(p), +// # freestanding dependent name (e.g., T::x), +// # objectless nonstatic member reference +// ::= <expr-primary> + +template <class C> +const char* +parse_expression(const char* first, const char* last, C& db) +{ + if (last - first >= 2) + { + const char* t = first; + bool parsed_gs = false; + if (last - first >= 4 && t[0] == 'g' && t[1] == 's') + { + t += 2; + parsed_gs = true; + } + switch (*t) + { + case 'L': + first = parse_expr_primary(first, last, db); + break; + case 'T': + first = parse_template_param(first, last, db); + break; + case 'f': + first = parse_function_param(first, last, db); + break; + case 'a': + switch (t[1]) + { + case 'a': + t = parse_binary_expression(first+2, last, "&&", db); + if (t != first+2) + first = t; + break; + case 'd': + t = parse_prefix_expression(first+2, last, "&", db); + if (t != first+2) + first = t; + break; + case 'n': + t = parse_binary_expression(first+2, last, "&", db); + if (t != first+2) + first = t; + break; + case 'N': + t = parse_binary_expression(first+2, last, "&=", db); + if (t != first+2) + first = t; + break; + case 'S': + t = parse_binary_expression(first+2, last, "=", db); + if (t != first+2) + first = t; + break; + case 't': + first = parse_alignof_type(first, last, db); + break; + case 'z': + first = parse_alignof_expr(first, last, db); + break; + } + break; + case 'c': + switch (t[1]) + { + case 'c': + first = parse_const_cast_expr(first, last, db); + break; + case 'l': + first = parse_call_expr(first, last, db); + break; + case 'm': + t = parse_binary_expression(first+2, last, ",", db); + if (t != first+2) + first = t; + break; + case 'o': + t = parse_prefix_expression(first+2, last, "~", db); + if (t != first+2) + first = t; + break; + case 'v': + first = parse_conversion_expr(first, last, db); + break; + } + break; + case 'd': + switch (t[1]) + { + case 'a': + { + const char* t1 = parse_expression(t+2, last, db); + if (t1 != t+2) + { + if (db.names.empty()) + return first; + db.names.back().first = (parsed_gs ? typename C::String("::") : typename C::String()) + + "delete[] " + db.names.back().move_full(); + first = t1; + } + } + break; + case 'c': + first = parse_dynamic_cast_expr(first, last, db); + break; + case 'e': + t = parse_prefix_expression(first+2, last, "*", db); + if (t != first+2) + first = t; + break; + case 'l': + { + const char* t1 = parse_expression(t+2, last, db); + if (t1 != t+2) + { + if (db.names.empty()) + return first; + db.names.back().first = (parsed_gs ? typename C::String("::") : typename C::String()) + + "delete " + db.names.back().move_full(); + first = t1; + } + } + break; + case 'n': + return parse_unresolved_name(first, last, db); + case 's': + first = parse_dot_star_expr(first, last, db); + break; + case 't': + first = parse_dot_expr(first, last, db); + break; + case 'v': + t = parse_binary_expression(first+2, last, "/", db); + if (t != first+2) + first = t; + break; + case 'V': + t = parse_binary_expression(first+2, last, "/=", db); + if (t != first+2) + first = t; + break; + } + break; + case 'e': + switch (t[1]) + { + case 'o': + t = parse_binary_expression(first+2, last, "^", db); + if (t != first+2) + first = t; + break; + case 'O': + t = parse_binary_expression(first+2, last, "^=", db); + if (t != first+2) + first = t; + break; + case 'q': + t = parse_binary_expression(first+2, last, "==", db); + if (t != first+2) + first = t; + break; + } + break; + case 'g': + switch (t[1]) + { + case 'e': + t = parse_binary_expression(first+2, last, ">=", db); + if (t != first+2) + first = t; + break; + case 't': + t = parse_binary_expression(first+2, last, ">", db); + if (t != first+2) + first = t; + break; + } + break; + case 'i': + if (t[1] == 'x') + { + const char* t1 = parse_expression(first+2, last, db); + if (t1 != first+2) + { + const char* t2 = parse_expression(t1, last, db); + if (t2 != t1) + { + if (db.names.size() < 2) + return first; + auto op2 = db.names.back().move_full(); + db.names.pop_back(); + auto op1 = db.names.back().move_full(); + db.names.back() = "(" + op1 + ")[" + op2 + "]"; + first = t2; + } + else + db.names.pop_back(); + } + } + break; + case 'l': + switch (t[1]) + { + case 'e': + t = parse_binary_expression(first+2, last, "<=", db); + if (t != first+2) + first = t; + break; + case 's': + t = parse_binary_expression(first+2, last, "<<", db); + if (t != first+2) + first = t; + break; + case 'S': + t = parse_binary_expression(first+2, last, "<<=", db); + if (t != first+2) + first = t; + break; + case 't': + t = parse_binary_expression(first+2, last, "<", db); + if (t != first+2) + first = t; + break; + } + break; + case 'm': + switch (t[1]) + { + case 'i': + t = parse_binary_expression(first+2, last, "-", db); + if (t != first+2) + first = t; + break; + case 'I': + t = parse_binary_expression(first+2, last, "-=", db); + if (t != first+2) + first = t; + break; + case 'l': + t = parse_binary_expression(first+2, last, "*", db); + if (t != first+2) + first = t; + break; + case 'L': + t = parse_binary_expression(first+2, last, "*=", db); + if (t != first+2) + first = t; + break; + case 'm': + if (first+2 != last && first[2] == '_') + { + t = parse_prefix_expression(first+3, last, "--", db); + if (t != first+3) + first = t; + } + else + { + const char* t1 = parse_expression(first+2, last, db); + if (t1 != first+2) + { + if (db.names.empty()) + return first; + db.names.back() = "(" + db.names.back().move_full() + ")--"; + first = t1; + } + } + break; + } + break; + case 'n': + switch (t[1]) + { + case 'a': + case 'w': + first = parse_new_expr(first, last, db); + break; + case 'e': + t = parse_binary_expression(first+2, last, "!=", db); + if (t != first+2) + first = t; + break; + case 'g': + t = parse_prefix_expression(first+2, last, "-", db); + if (t != first+2) + first = t; + break; + case 't': + t = parse_prefix_expression(first+2, last, "!", db); + if (t != first+2) + first = t; + break; + case 'x': + t = parse_noexcept_expression(first+2, last, db); + if (t != first+2) + first = t; + break; + } + break; + case 'o': + switch (t[1]) + { + case 'n': + return parse_unresolved_name(first, last, db); + case 'o': + t = parse_binary_expression(first+2, last, "||", db); + if (t != first+2) + first = t; + break; + case 'r': + t = parse_binary_expression(first+2, last, "|", db); + if (t != first+2) + first = t; + break; + case 'R': + t = parse_binary_expression(first+2, last, "|=", db); + if (t != first+2) + first = t; + break; + } + break; + case 'p': + switch (t[1]) + { + case 'm': + t = parse_binary_expression(first+2, last, "->*", db); + if (t != first+2) + first = t; + break; + case 'l': + t = parse_binary_expression(first+2, last, "+", db); + if (t != first+2) + first = t; + break; + case 'L': + t = parse_binary_expression(first+2, last, "+=", db); + if (t != first+2) + first = t; + break; + case 'p': + if (first+2 != last && first[2] == '_') + { + t = parse_prefix_expression(first+3, last, "++", db); + if (t != first+3) + first = t; + } + else + { + const char* t1 = parse_expression(first+2, last, db); + if (t1 != first+2) + { + if (db.names.empty()) + return first; + db.names.back() = "(" + db.names.back().move_full() + ")++"; + first = t1; + } + } + break; + case 's': + t = parse_prefix_expression(first+2, last, "+", db); + if (t != first+2) + first = t; + break; + case 't': + first = parse_arrow_expr(first, last, db); + break; + } + break; + case 'q': + if (t[1] == 'u') + { + const char* t1 = parse_expression(first+2, last, db); + if (t1 != first+2) + { + const char* t2 = parse_expression(t1, last, db); + if (t2 != t1) + { + const char* t3 = parse_expression(t2, last, db); + if (t3 != t2) + { + if (db.names.size() < 3) + return first; + auto op3 = db.names.back().move_full(); + db.names.pop_back(); + auto op2 = db.names.back().move_full(); + db.names.pop_back(); + auto op1 = db.names.back().move_full(); + db.names.back() = "(" + op1 + ") ? (" + op2 + ") : (" + op3 + ")"; + first = t3; + } + else + { + db.names.pop_back(); + db.names.pop_back(); + } + } + else + db.names.pop_back(); + } + } + break; + case 'r': + switch (t[1]) + { + case 'c': + first = parse_reinterpret_cast_expr(first, last, db); + break; + case 'm': + t = parse_binary_expression(first+2, last, "%", db); + if (t != first+2) + first = t; + break; + case 'M': + t = parse_binary_expression(first+2, last, "%=", db); + if (t != first+2) + first = t; + break; + case 's': + t = parse_binary_expression(first+2, last, ">>", db); + if (t != first+2) + first = t; + break; + case 'S': + t = parse_binary_expression(first+2, last, ">>=", db); + if (t != first+2) + first = t; + break; + } + break; + case 's': + switch (t[1]) + { + case 'c': + first = parse_static_cast_expr(first, last, db); + break; + case 'p': + first = parse_pack_expansion(first, last, db); + break; + case 'r': + return parse_unresolved_name(first, last, db); + case 't': + first = parse_sizeof_type_expr(first, last, db); + break; + case 'z': + first = parse_sizeof_expr_expr(first, last, db); + break; + case 'Z': + if (last - t >= 3) + { + switch (t[2]) + { + case 'T': + first = parse_sizeof_param_pack_expr(first, last, db); + break; + case 'f': + first = parse_sizeof_function_param_pack_expr(first, last, db); + break; + } + } + break; + } + break; + case 't': + switch (t[1]) + { + case 'e': + case 'i': + first = parse_typeid_expr(first, last, db); + break; + case 'r': + db.names.push_back("throw"); + first += 2; + break; + case 'w': + first = parse_throw_expr(first, last, db); + break; + } + break; + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return parse_unresolved_name(first, last, db); + } + } + return first; +} + +// <template-arg> ::= <type> # type or template +// ::= X <expression> E # expression +// ::= <expr-primary> # simple expressions +// ::= J <template-arg>* E # argument pack +// ::= LZ <encoding> E # extension + +template <class C> +const char* +parse_template_arg(const char* first, const char* last, C& db) +{ + if (first != last) + { + const char* t; + switch (*first) + { + case 'X': + t = parse_expression(first+1, last, db); + if (t != first+1) + { + if (t != last && *t == 'E') + first = t+1; + } + break; + case 'J': + t = first+1; + if (t == last) + return first; + while (*t != 'E') + { + const char* t1 = parse_template_arg(t, last, db); + if (t1 == t) + return first; + t = t1; + } + first = t+1; + break; + case 'L': + // <expr-primary> or LZ <encoding> E + if (first+1 != last && first[1] == 'Z') + { + t = parse_encoding(first+2, last, db); + if (t != first+2 && t != last && *t == 'E') + first = t+1; + } + else + first = parse_expr_primary(first, last, db); + break; + default: + // <type> + first = parse_type(first, last, db); + break; + } + } + return first; +} + +// <template-args> ::= I <template-arg>* E +// extension, the abi says <template-arg>+ + +template <class C> +const char* +parse_template_args(const char* first, const char* last, C& db) +{ + if (last - first >= 2 && *first == 'I') + { + if (db.tag_templates) + db.template_param.back().clear(); + const char* t = first+1; + typename C::String args("<"); + while (*t != 'E') + { + if (db.tag_templates) + db.template_param.emplace_back(db.names.get_allocator()); + size_t k0 = db.names.size(); + const char* t1 = parse_template_arg(t, last, db); + size_t k1 = db.names.size(); + if (db.tag_templates) + db.template_param.pop_back(); + if (t1 == t || t1 == last) + return first; + if (db.tag_templates) + { + db.template_param.back().emplace_back(db.names.get_allocator()); + for (size_t k = k0; k < k1; ++k) + db.template_param.back().back().push_back(db.names[k]); + } + for (size_t k = k0; k < k1; ++k) + { + if (args.size() > 1) + args += ", "; + args += db.names[k].move_full(); + } + for (; k1 != k0; --k1) + db.names.pop_back(); + t = t1; + } + first = t + 1; + if (args.back() != '>') + args += ">"; + else + args += " >"; + db.names.push_back(std::move(args)); + + } + return first; +} + +// <nested-name> ::= N [<CV-qualifiers>] [<ref-qualifier>] <prefix> <unqualified-name> E +// ::= N [<CV-qualifiers>] [<ref-qualifier>] <template-prefix> <template-args> E +// +// <prefix> ::= <prefix> <unqualified-name> +// ::= <template-prefix> <template-args> +// ::= <template-param> +// ::= <decltype> +// ::= # empty +// ::= <substitution> +// ::= <prefix> <data-member-prefix> +// extension ::= L +// +// <template-prefix> ::= <prefix> <template unqualified-name> +// ::= <template-param> +// ::= <substitution> + +template <class C> +const char* +parse_nested_name(const char* first, const char* last, C& db, + bool* ends_with_template_args) +{ + if (first != last && *first == 'N') + { + unsigned cv; + const char* t0 = parse_cv_qualifiers(first+1, last, cv); + if (t0 == last) + return first; + db.ref = 0; + if (*t0 == 'R') + { + db.ref = 1; + ++t0; + } + else if (*t0 == 'O') + { + db.ref = 2; + ++t0; + } + db.names.emplace_back(); + if (last - t0 >= 2 && t0[0] == 'S' && t0[1] == 't') + { + t0 += 2; + db.names.back().first = "std"; + } + if (t0 == last) + { + db.names.pop_back(); + return first; + } + bool pop_subs = false; + bool component_ends_with_template_args = false; + while (*t0 != 'E') + { + component_ends_with_template_args = false; + const char* t1; + switch (*t0) + { + case 'S': + if (t0 + 1 != last && t0[1] == 't') + goto do_parse_unqualified_name; + t1 = parse_substitution(t0, last, db); + if (t1 != t0 && t1 != last) + { + auto name = db.names.back().move_full(); + db.names.pop_back(); + if (!db.names.back().first.empty()) + { + db.names.back().first += "::" + name; + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + } + else + db.names.back().first = name; + pop_subs = true; + t0 = t1; + } + else + return first; + break; + case 'T': + t1 = parse_template_param(t0, last, db); + if (t1 != t0 && t1 != last) + { + auto name = db.names.back().move_full(); + db.names.pop_back(); + if (!db.names.back().first.empty()) + db.names.back().first += "::" + name; + else + db.names.back().first = name; + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + pop_subs = true; + t0 = t1; + } + else + return first; + break; + case 'D': + if (t0 + 1 != last && t0[1] != 't' && t0[1] != 'T') + goto do_parse_unqualified_name; + t1 = parse_decltype(t0, last, db); + if (t1 != t0 && t1 != last) + { + auto name = db.names.back().move_full(); + db.names.pop_back(); + if (!db.names.back().first.empty()) + db.names.back().first += "::" + name; + else + db.names.back().first = name; + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + pop_subs = true; + t0 = t1; + } + else + return first; + break; + case 'I': + t1 = parse_template_args(t0, last, db); + if (t1 != t0 && t1 != last) + { + auto name = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += name; + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + t0 = t1; + component_ends_with_template_args = true; + } + else + return first; + break; + case 'L': + if (++t0 == last) + return first; + break; + default: + do_parse_unqualified_name: + t1 = parse_unqualified_name(t0, last, db); + if (t1 != t0 && t1 != last) + { + auto name = db.names.back().move_full(); + db.names.pop_back(); + if (!db.names.back().first.empty()) + db.names.back().first += "::" + name; + else + db.names.back().first = name; + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + pop_subs = true; + t0 = t1; + } + else + return first; + } + } + first = t0 + 1; + db.cv = cv; + if (pop_subs && !db.subs.empty()) + db.subs.pop_back(); + if (ends_with_template_args) + *ends_with_template_args = component_ends_with_template_args; + } + return first; +} + +// <discriminator> := _ <non-negative number> # when number < 10 +// := __ <non-negative number> _ # when number >= 10 +// extension := decimal-digit+ + +const char* +parse_discriminator(const char* first, const char* last) +{ + // parse but ignore discriminator + if (first != last) + { + if (*first == '_') + { + const char* t1 = first+1; + if (t1 != last) + { + if (std::isdigit(*t1)) + first = t1+1; + else if (*t1 == '_') + { + for (++t1; t1 != last && std::isdigit(*t1); ++t1) + ; + if (t1 != last && *t1 == '_') + first = t1 + 1; + } + } + } + else if (std::isdigit(*first)) + { + const char* t1 = first+1; + for (; t1 != last && std::isdigit(*t1); ++t1) + ; + first = t1; + } + } + return first; +} + +// <local-name> := Z <function encoding> E <entity name> [<discriminator>] +// := Z <function encoding> E s [<discriminator>] +// := Z <function encoding> Ed [ <parameter number> ] _ <entity name> + +template <class C> +const char* +parse_local_name(const char* first, const char* last, C& db, + bool* ends_with_template_args) +{ + if (first != last && *first == 'Z') + { + const char* t = parse_encoding(first+1, last, db); + if (t != first+1 && t != last && *t == 'E' && ++t != last) + { + switch (*t) + { + case 's': + first = parse_discriminator(t+1, last); + if (db.names.empty()) + return first; + db.names.back().first.append("::string literal"); + break; + case 'd': + if (++t != last) + { + const char* t1 = parse_number(t, last); + if (t1 != last && *t1 == '_') + { + t = t1 + 1; + t1 = parse_name(t, last, db, + ends_with_template_args); + if (t1 != t) + { + if (db.names.size() < 2) + return first; + auto name = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first.append("::"); + db.names.back().first.append(name); + first = t1; + } + else + db.names.pop_back(); + } + } + break; + default: + { + const char* t1 = parse_name(t, last, db, + ends_with_template_args); + if (t1 != t) + { + // parse but ignore discriminator + first = parse_discriminator(t1, last); + if (db.names.size() < 2) + return first; + auto name = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first.append("::"); + db.names.back().first.append(name); + } + else + db.names.pop_back(); + } + break; + } + } + } + return first; +} + +// <name> ::= <nested-name> // N +// ::= <local-name> # See Scope Encoding below // Z +// ::= <unscoped-template-name> <template-args> +// ::= <unscoped-name> + +// <unscoped-template-name> ::= <unscoped-name> +// ::= <substitution> + +template <class C> +const char* +parse_name(const char* first, const char* last, C& db, + bool* ends_with_template_args) +{ + if (last - first >= 2) + { + const char* t0 = first; + // extension: ignore L here + if (*t0 == 'L') + ++t0; + switch (*t0) + { + case 'N': + { + const char* t1 = parse_nested_name(t0, last, db, + ends_with_template_args); + if (t1 != t0) + first = t1; + break; + } + case 'Z': + { + const char* t1 = parse_local_name(t0, last, db, + ends_with_template_args); + if (t1 != t0) + first = t1; + break; + } + default: + { + const char* t1 = parse_unscoped_name(t0, last, db); + if (t1 != t0) + { + if (t1 != last && *t1 == 'I') // <unscoped-template-name> <template-args> + { + if (db.names.empty()) + return first; + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + t0 = t1; + t1 = parse_template_args(t0, last, db); + if (t1 != t0) + { + if (db.names.size() < 2) + return first; + auto tmp = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += tmp; + first = t1; + if (ends_with_template_args) + *ends_with_template_args = true; + } + } + else // <unscoped-name> + first = t1; + } + else + { // try <substitution> <template-args> + t1 = parse_substitution(t0, last, db); + if (t1 != t0 && t1 != last && *t1 == 'I') + { + t0 = t1; + t1 = parse_template_args(t0, last, db); + if (t1 != t0) + { + if (db.names.size() < 2) + return first; + auto tmp = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += tmp; + first = t1; + if (ends_with_template_args) + *ends_with_template_args = true; + } + } + } + break; + } + } + } + return first; +} + +// <call-offset> ::= h <nv-offset> _ +// ::= v <v-offset> _ +// +// <nv-offset> ::= <offset number> +// # non-virtual base override +// +// <v-offset> ::= <offset number> _ <virtual offset number> +// # virtual base override, with vcall offset + +const char* +parse_call_offset(const char* first, const char* last) +{ + if (first != last) + { + switch (*first) + { + case 'h': + { + const char* t = parse_number(first + 1, last); + if (t != first + 1 && t != last && *t == '_') + first = t + 1; + } + break; + case 'v': + { + const char* t = parse_number(first + 1, last); + if (t != first + 1 && t != last && *t == '_') + { + const char* t2 = parse_number(++t, last); + if (t2 != t && t2 != last && *t2 == '_') + first = t2 + 1; + } + } + break; + } + } + return first; +} + +// <special-name> ::= TV <type> # virtual table +// ::= TT <type> # VTT structure (construction vtable index) +// ::= TI <type> # typeinfo structure +// ::= TS <type> # typeinfo name (null-terminated byte string) +// ::= Tc <call-offset> <call-offset> <base encoding> +// # base is the nominal target function of thunk +// # first call-offset is 'this' adjustment +// # second call-offset is result adjustment +// ::= T <call-offset> <base encoding> +// # base is the nominal target function of thunk +// ::= GV <object name> # Guard variable for one-time initialization +// # No <type> +// extension ::= TC <first type> <number> _ <second type> # construction vtable for second-in-first +// extension ::= GR <object name> # reference temporary for object + +template <class C> +const char* +parse_special_name(const char* first, const char* last, C& db) +{ + if (last - first > 2) + { + const char* t; + switch (*first) + { + case 'T': + switch (first[1]) + { + case 'V': + // TV <type> # virtual table + t = parse_type(first+2, last, db); + if (t != first+2) + { + if (db.names.empty()) + return first; + db.names.back().first.insert(0, "vtable for "); + first = t; + } + break; + case 'T': + // TT <type> # VTT structure (construction vtable index) + t = parse_type(first+2, last, db); + if (t != first+2) + { + if (db.names.empty()) + return first; + db.names.back().first.insert(0, "VTT for "); + first = t; + } + break; + case 'I': + // TI <type> # typeinfo structure + t = parse_type(first+2, last, db); + if (t != first+2) + { + if (db.names.empty()) + return first; + db.names.back().first.insert(0, "typeinfo for "); + first = t; + } + break; + case 'S': + // TS <type> # typeinfo name (null-terminated byte string) + t = parse_type(first+2, last, db); + if (t != first+2) + { + if (db.names.empty()) + return first; + db.names.back().first.insert(0, "typeinfo name for "); + first = t; + } + break; + case 'c': + // Tc <call-offset> <call-offset> <base encoding> + { + const char* t0 = parse_call_offset(first+2, last); + if (t0 == first+2) + break; + const char* t1 = parse_call_offset(t0, last); + if (t1 == t0) + break; + t = parse_encoding(t1, last, db); + if (t != t1) + { + if (db.names.empty()) + return first; + db.names.back().first.insert(0, "covariant return thunk to "); + first = t; + } + } + break; + case 'C': + // extension ::= TC <first type> <number> _ <second type> # construction vtable for second-in-first + t = parse_type(first+2, last, db); + if (t != first+2) + { + const char* t0 = parse_number(t, last); + if (t0 != t && t0 != last && *t0 == '_') + { + const char* t1 = parse_type(++t0, last, db); + if (t1 != t0) + { + if (db.names.size() < 2) + return first; + auto left = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first = "construction vtable for " + + std::move(left) + "-in-" + + db.names.back().move_full(); + first = t1; + } + } + } + break; + default: + // T <call-offset> <base encoding> + { + const char* t0 = parse_call_offset(first+1, last); + if (t0 == first+1) + break; + t = parse_encoding(t0, last, db); + if (t != t0) + { + if (db.names.empty()) + return first; + if (first[2] == 'v') + { + db.names.back().first.insert(0, "virtual thunk to "); + first = t; + } + else + { + db.names.back().first.insert(0, "non-virtual thunk to "); + first = t; + } + } + } + break; + } + break; + case 'G': + switch (first[1]) + { + case 'V': + // GV <object name> # Guard variable for one-time initialization + t = parse_name(first+2, last, db); + if (t != first+2) + { + if (db.names.empty()) + return first; + db.names.back().first.insert(0, "guard variable for "); + first = t; + } + break; + case 'R': + // extension ::= GR <object name> # reference temporary for object + t = parse_name(first+2, last, db); + if (t != first+2) + { + if (db.names.empty()) + return first; + db.names.back().first.insert(0, "reference temporary for "); + first = t; + } + break; + } + break; + } + } + return first; +} + +template <class T> +class save_value +{ + T& restore_; + T original_value_; +public: + save_value(T& restore) + : restore_(restore), + original_value_(restore) + {} + + ~save_value() + { + restore_ = std::move(original_value_); + } + + save_value(const save_value&) = delete; + save_value& operator=(const save_value&) = delete; +}; + +// <encoding> ::= <function name> <bare-function-type> +// ::= <data name> +// ::= <special-name> + +template <class C> +const char* +parse_encoding(const char* first, const char* last, C& db) +{ + if (first != last) + { + save_value<decltype(db.encoding_depth)> su(db.encoding_depth); + ++db.encoding_depth; + save_value<decltype(db.tag_templates)> sb(db.tag_templates); + if (db.encoding_depth > 1) + db.tag_templates = true; + switch (*first) + { + case 'G': + case 'T': + first = parse_special_name(first, last, db); + break; + default: + { + bool ends_with_template_args = false; + const char* t = parse_name(first, last, db, + &ends_with_template_args); + unsigned cv = db.cv; + unsigned ref = db.ref; + if (t != first) + { + if (t != last && *t != 'E' && *t != '.') + { + save_value<bool> sb2(db.tag_templates); + db.tag_templates = false; + const char* t2; + typename C::String ret2; + if (db.names.empty()) + return first; + const typename C::String& nm = db.names.back().first; + if (nm.empty()) + return first; + if (!db.parsed_ctor_dtor_cv && ends_with_template_args) + { + t2 = parse_type(t, last, db); + if (t2 == t) + return first; + if (db.names.size() < 2) + return first; + auto ret1 = std::move(db.names.back().first); + ret2 = std::move(db.names.back().second); + if (ret2.empty()) + ret1 += ' '; + db.names.pop_back(); + db.names.back().first.insert(0, ret1); + t = t2; + } + db.names.back().first += '('; + if (t != last && *t == 'v') + { + ++t; + } + else + { + bool first_arg = true; + while (true) + { + size_t k0 = db.names.size(); + t2 = parse_type(t, last, db); + size_t k1 = db.names.size(); + if (t2 == t) + break; + if (k1 > k0) + { + typename C::String tmp; + for (size_t k = k0; k < k1; ++k) + { + if (!tmp.empty()) + tmp += ", "; + tmp += db.names[k].move_full(); + } + for (size_t k = k0; k < k1; ++k) + db.names.pop_back(); + if (!tmp.empty()) + { + if (db.names.empty()) + return first; + if (!first_arg) + db.names.back().first += ", "; + else + first_arg = false; + db.names.back().first += tmp; + } + } + t = t2; + } + } + if (db.names.empty()) + return first; + db.names.back().first += ')'; + if (cv & 1) + db.names.back().first.append(" const"); + if (cv & 2) + db.names.back().first.append(" volatile"); + if (cv & 4) + db.names.back().first.append(" restrict"); + if (ref == 1) + db.names.back().first.append(" &"); + else if (ref == 2) + db.names.back().first.append(" &&"); + db.names.back().first += ret2; + first = t; + } + else + first = t; + } + break; + } + } + } + return first; +} + +// _block_invoke +// _block_invoke<decimal-digit>+ +// _block_invoke_<decimal-digit>+ + +template <class C> +const char* +parse_block_invoke(const char* first, const char* last, C& db) +{ + if (last - first >= 13) + { + const char test[] = "_block_invoke"; + const char* t = first; + for (int i = 0; i < 13; ++i, ++t) + { + if (*t != test[i]) + return first; + } + if (t != last) + { + if (*t == '_') + { + // must have at least 1 decimal digit + if (++t == last || !std::isdigit(*t)) + return first; + ++t; + } + // parse zero or more digits + while (t != last && isdigit(*t)) + ++t; + } + if (db.names.empty()) + return first; + db.names.back().first.insert(0, "invocation function for block in "); + first = t; + } + return first; +} + +// extension +// <dot-suffix> := .<anything and everything> + +template <class C> +const char* +parse_dot_suffix(const char* first, const char* last, C& db) +{ + if (first != last && *first == '.') + { + if (db.names.empty()) + return first; + db.names.back().first += " (" + typename C::String(first, last) + ")"; + first = last; + } + return first; +} + +// <block-involcaton-function> ___Z<encoding>_block_invoke +// <block-involcaton-function> ___Z<encoding>_block_invoke<decimal-digit>+ +// <block-involcaton-function> ___Z<encoding>_block_invoke_<decimal-digit>+ +// <mangled-name> ::= _Z<encoding> +// ::= <type> + +template <class C> +void +demangle(const char* first, const char* last, C& db, int& status) +{ + if (first >= last) + { + status = invalid_mangled_name; + return; + } + if (*first == '_') + { + if (last - first >= 4) + { + if (first[1] == 'Z') + { + const char* t = parse_encoding(first+2, last, db); + if (t != first+2 && t != last && *t == '.') + t = parse_dot_suffix(t, last, db); + if (t != last) + status = invalid_mangled_name; + } + else if (first[1] == '_' && first[2] == '_' && first[3] == 'Z') + { + const char* t = parse_encoding(first+4, last, db); + if (t != first+4 && t != last) + { + const char* t1 = parse_block_invoke(t, last, db); + if (t1 != last) + status = invalid_mangled_name; + } + else + status = invalid_mangled_name; + } + else + status = invalid_mangled_name; + } + else + status = invalid_mangled_name; + } + else + { + const char* t = parse_type(first, last, db); + if (t != last) + status = invalid_mangled_name; + } + if (status == success && db.names.empty()) + status = invalid_mangled_name; +} + +template <std::size_t N> +class arena +{ + static const std::size_t alignment = 16; + alignas(alignment) char buf_[N]; + char* ptr_; + +#if UPSTREAM_CODE + std::size_t + align_up(std::size_t n) noexcept + {return n + (alignment-1) & ~(alignment-1);} +#else + std::size_t + align_up(std::size_t n) noexcept + {return (n + (alignment-1)) & ~(alignment-1);} +#endif + + bool + pointer_in_buffer(char* p) noexcept + {return buf_ <= p && p <= buf_ + N;} + +public: + arena() noexcept : ptr_(buf_) {} + ~arena() {ptr_ = nullptr;} + arena(const arena&) = delete; + arena& operator=(const arena&) = delete; + + char* allocate(std::size_t n); + void deallocate(char* p, std::size_t n) noexcept; + + static constexpr std::size_t size() {return N;} + std::size_t used() const {return static_cast<std::size_t>(ptr_ - buf_);} + void reset() {ptr_ = buf_;} +}; + +template <std::size_t N> +char* +arena<N>::allocate(std::size_t n) +{ + n = align_up(n); + if (static_cast<std::size_t>(buf_ + N - ptr_) >= n) + { + char* r = ptr_; + ptr_ += n; + return r; + } + return static_cast<char*>(std::malloc(n)); +} + +template <std::size_t N> +void +arena<N>::deallocate(char* p, std::size_t n) noexcept +{ + if (pointer_in_buffer(p)) + { + n = align_up(n); + if (p + n == ptr_) + ptr_ = p; + } + else + std::free(p); +} + +template <class T, std::size_t N> +class short_alloc +{ + arena<N>& a_; +public: + typedef T value_type; + +public: + template <class _Up> struct rebind {typedef short_alloc<_Up, N> other;}; + + short_alloc(arena<N>& a) noexcept : a_(a) {} + template <class U> + short_alloc(const short_alloc<U, N>& a) noexcept + : a_(a.a_) {} + short_alloc(const short_alloc&) = default; + short_alloc& operator=(const short_alloc&) = delete; + + T* allocate(std::size_t n) + { + return reinterpret_cast<T*>(a_.allocate(n*sizeof(T))); + } + void deallocate(T* p, std::size_t n) noexcept + { + a_.deallocate(reinterpret_cast<char*>(p), n*sizeof(T)); + } + + template <class T1, std::size_t N1, class U, std::size_t M> + friend + bool + operator==(const short_alloc<T1, N1>& x, const short_alloc<U, M>& y) noexcept; + + template <class U, std::size_t M> friend class short_alloc; +}; + +template <class T, std::size_t N, class U, std::size_t M> +inline +bool +operator==(const short_alloc<T, N>& x, const short_alloc<U, M>& y) noexcept +{ + return N == M && &x.a_ == &y.a_; +} + +template <class T, std::size_t N, class U, std::size_t M> +inline +bool +operator!=(const short_alloc<T, N>& x, const short_alloc<U, M>& y) noexcept +{ + return !(x == y); +} + +template <class T> +class malloc_alloc +{ +public: + typedef T value_type; + + malloc_alloc() = default; + template <class U> malloc_alloc(const malloc_alloc<U>&) noexcept {} + + T* allocate(std::size_t n) + { + return static_cast<T*>(std::malloc(n*sizeof(T))); + } + void deallocate(T* p, std::size_t) noexcept + { + std::free(p); + } +}; + +template <class T, class U> +inline +bool +operator==(const malloc_alloc<T>&, const malloc_alloc<U>&) noexcept +{ + return true; +} + +template <class T, class U> +inline +bool +operator!=(const malloc_alloc<T>& x, const malloc_alloc<U>& y) noexcept +{ + return !(x == y); +} + +const size_t bs = 4 * 1024; +template <class T> using Alloc = short_alloc<T, bs>; +template <class T> using Vector = std::vector<T, Alloc<T>>; + +template <class StrT> +struct string_pair +{ + StrT first; + StrT second; + + string_pair() = default; + string_pair(StrT f) : first(std::move(f)) {} + string_pair(StrT f, StrT s) + : first(std::move(f)), second(std::move(s)) {} + template <size_t N> + string_pair(const char (&s)[N]) : first(s, N-1) {} + + size_t size() const {return first.size() + second.size();} + StrT full() const {return first + second;} + StrT move_full() {return std::move(first) + std::move(second);} +}; + +struct Db +{ +#if UPSTREAM_CODE + typedef std::basic_string<char, std::char_traits<char>, + malloc_alloc<char>> String; +#else + typedef std::basic_string<char, std::char_traits<char> > String; +#endif + typedef Vector<string_pair<String>> sub_type; + typedef Vector<sub_type> template_param_type; + sub_type names; + template_param_type subs; + Vector<template_param_type> template_param; + unsigned cv; + unsigned ref; + unsigned encoding_depth; + bool parsed_ctor_dtor_cv; + bool tag_templates; + bool fix_forward_references; + bool try_to_parse_template_args; + + template <size_t N> + Db(arena<N>& ar) : + names(ar), + subs(0, names, ar), + template_param(0, subs, ar) + {} +}; + +#if UPSTREAM_CODE +extern "C" +__attribute__ ((__visibility__("default"))) +char* +__cxa_demangle(const char* mangled_name, char* buf, size_t* n, int* status) +{ + if (mangled_name == nullptr || (buf != nullptr && n == nullptr)) + { + if (status) + *status = invalid_args; + return nullptr; + } + size_t internal_size = buf != nullptr ? *n : 0; + arena<bs> a; + Db db(a); + db.cv = 0; + db.ref = 0; + db.encoding_depth = 0; + db.parsed_ctor_dtor_cv = false; + db.tag_templates = true; + db.template_param.emplace_back(a); + db.fix_forward_references = false; + db.try_to_parse_template_args = true; + int internal_status = success; + size_t len = std::strlen(mangled_name); + demangle(mangled_name, mangled_name + len, db, + internal_status); + if (internal_status == success && db.fix_forward_references && + !db.template_param.empty() && !db.template_param.front().empty()) + { + db.fix_forward_references = false; + db.tag_templates = false; + db.names.clear(); + db.subs.clear(); + demangle(mangled_name, mangled_name + len, db, internal_status); + if (db.fix_forward_references) + internal_status = invalid_mangled_name; + } + if (internal_status == success) + { + size_t sz = db.names.back().size() + 1; + if (sz > internal_size) + { + char* newbuf = static_cast<char*>(std::realloc(buf, sz)); + if (newbuf == nullptr) + { + internal_status = memory_alloc_failure; + buf = nullptr; + } + else + { + buf = newbuf; + if (n != nullptr) + *n = sz; + } + } + if (buf != nullptr) + { + db.names.back().first += db.names.back().second; + std::memcpy(buf, db.names.back().first.data(), sz-1); + buf[sz-1] = char(0); + } + } + else + buf = nullptr; + if (status) + *status = internal_status; + return buf; +} +#endif + +} // namespace mcld diff --git a/include/mcld/Support/Demangle.h b/include/mcld/Support/Demangle.h new file mode 100644 index 0000000..b6f7412 --- /dev/null +++ b/include/mcld/Support/Demangle.h @@ -0,0 +1,22 @@ +//===- Demangle.h ---------------------------------------------------------===// +// +// The MCLinker Project +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifndef MCLD_SUPPORT_DEMANGLE_H +#define MCLD_SUPPORT_DEMANGLE_H + +#include <string> + +namespace mcld { + +std::string demangleName(const std::string& mangled_name); + +bool isCtorOrDtor(const char* pName, size_t pLength); + +} // namespace mcld + +#endif diff --git a/include/mcld/Support/FileOutputBuffer.h b/include/mcld/Support/FileOutputBuffer.h index 94cf640..6b48e14 100644 --- a/include/mcld/Support/FileOutputBuffer.h +++ b/include/mcld/Support/FileOutputBuffer.h @@ -10,11 +10,10 @@ #define MCLD_SUPPORT_FILEOUTPUTBUFFER_H #include <mcld/Support/MemoryRegion.h> -#include <llvm/ADT/OwningPtr.h> #include <llvm/ADT/StringRef.h> #include <llvm/Support/DataTypes.h> #include <llvm/Support/FileSystem.h> -#include <llvm/Support/system_error.h> +#include <system_error> namespace mcld { @@ -27,9 +26,9 @@ public: /// Factory method to create an OutputBuffer object which manages a read/write /// buffer of the specified size. When committed, the buffer will be written /// to the file at the specified path. - static llvm::error_code create(FileHandle& pFileHandle, - size_t pSize, - llvm::OwningPtr<FileOutputBuffer>& pResult); + static std::error_code create(FileHandle& pFileHandle, + size_t pSize, + std::unique_ptr<FileOutputBuffer>& pResult); /// Returns a pointer to the start of the buffer. uint8_t* getBufferStart() { @@ -60,7 +59,7 @@ private: FileOutputBuffer(llvm::sys::fs::mapped_file_region* pRegion, FileHandle& pFileHandle); - llvm::OwningPtr<llvm::sys::fs::mapped_file_region> m_pRegion; + std::unique_ptr<llvm::sys::fs::mapped_file_region> m_pRegion; FileHandle& m_FileHandle; }; diff --git a/include/mcld/Support/MemoryArea.h b/include/mcld/Support/MemoryArea.h index 2fef506..570e293 100644 --- a/include/mcld/Support/MemoryArea.h +++ b/include/mcld/Support/MemoryArea.h @@ -11,7 +11,6 @@ #include <mcld/ADT/Uncopyable.h> -#include <llvm/ADT/OwningPtr.h> #include <llvm/ADT/StringRef.h> #include <llvm/Support/MemoryBuffer.h> diff --git a/include/mcld/Target/GNULDBackend.h b/include/mcld/Target/GNULDBackend.h index 15337f6..351111d 100644 --- a/include/mcld/Target/GNULDBackend.h +++ b/include/mcld/Target/GNULDBackend.h @@ -19,6 +19,8 @@ #include <llvm/Support/ELF.h> +#include <cstdint> + namespace mcld { class Module; @@ -258,8 +260,8 @@ public: bool pSymHasPLT, bool isAbsReloc) const; - /// isSymbolPreemtible - whether the symbol can be preemted by other - /// link unit + /// isSymbolPreemptible - whether the symbol can be preemted by other link + /// units /// @ref Google gold linker, symtab.h:551 bool isSymbolPreemptible(const ResolveInfo& pSym) const; @@ -309,9 +311,13 @@ public: /// getStubFactory StubFactory* getStubFactory() { return m_pStubFactory; } - /// maxBranchOffset - return the max (forward) branch offset of the backend. + /// maxFwdBranchOffset - return the max forward branch offset of the backend. + /// Target can override this function if needed. + virtual int64_t maxFwdBranchOffset() { return INT64_MAX; } + + /// maxBwdBranchOffset - return the max backward branch offset of the backend. /// Target can override this function if needed. - virtual uint64_t maxBranchOffset() { return (uint64_t)-1; } + virtual int64_t maxBwdBranchOffset() { return 0; } /// checkAndSetHasTextRel - check pSection flag to set HasTextRel void checkAndSetHasTextRel(const LDSection& pSection); @@ -330,6 +336,10 @@ public: /// attribute - the attribute section data. const ELFAttribute& attribute() const { return *m_pAttribute; } + /// mayHaveUnsafeFunctionPointerAccess - check if the section may have unsafe + /// function pointer access + bool mayHaveUnsafeFunctionPointerAccess(const LDSection& pSection) const; + protected: /// getRelEntrySize - the size in BYTE of rel type relocation virtual size_t getRelEntrySize() = 0; diff --git a/include/mcld/Target/TargetLDBackend.h b/include/mcld/Target/TargetLDBackend.h index 29fb395..a1a5f1c 100644 --- a/include/mcld/Target/TargetLDBackend.h +++ b/include/mcld/Target/TargetLDBackend.h @@ -35,6 +35,7 @@ class ObjectBuilder; class ObjectReader; class ObjectWriter; class Relocator; +class ResolveInfo; class SectionData; class StubFactory; @@ -62,6 +63,7 @@ public: virtual bool initRelocator() = 0; virtual Relocator* getRelocator() = 0; + virtual const Relocator* getRelocator() const = 0; // ----- format dependent ----- // virtual ArchiveReader* createArchiveReader(Module&) = 0; @@ -167,6 +169,15 @@ public: /// entry in the middle virtual void createAndSizeEhFrameHdr(Module& pModule) = 0; + /// isSymbolPreemptible - whether the symbol can be preemted by other link + /// units + virtual bool isSymbolPreemptible(const ResolveInfo& pSym) const = 0; + + /// mayHaveUnsafeFunctionPointerAccess - check if the section may have unsafe + /// function pointer access + virtual bool mayHaveUnsafeFunctionPointerAccess(const LDSection& pSection) + const = 0; + protected: const LinkerConfig& config() const { return m_Config; } diff --git a/lib/CodeGen/MCLDTargetMachine.cpp b/lib/CodeGen/MCLDTargetMachine.cpp index e1d9a0e..7749748 100644 --- a/lib/CodeGen/MCLDTargetMachine.cpp +++ b/lib/CodeGen/MCLDTargetMachine.cpp @@ -15,7 +15,6 @@ #include <mcld/Support/ToolOutputFile.h> #include <mcld/Target/TargetLDBackend.h> -#include <llvm/ADT/OwningPtr.h> #include <llvm/Analysis/Passes.h> #include <llvm/CodeGen/AsmPrinter.h> #include <llvm/CodeGen/MachineFunctionAnalysis.h> @@ -127,7 +126,7 @@ static void addPassesToHandleExceptions(llvm::TargetMachine *TM, // FALLTHROUGH case llvm::ExceptionHandling::DwarfCFI: case llvm::ExceptionHandling::ARM: - case llvm::ExceptionHandling::Win64: + case llvm::ExceptionHandling::WinEH: PM.add(createDwarfEHPass(TM)); break; case llvm::ExceptionHandling::None: @@ -300,7 +299,7 @@ mcld::MCLDTargetMachine::addCompilerPasses(llvm::legacy::PassManagerBase &pPM, // now, we have MCCodeEmitter and MCAsmBackend, we can create AsmStreamer. - OwningPtr<MCStreamer> AsmStreamer( + std::unique_ptr<MCStreamer> AsmStreamer( m_pLLVMTarget->createAsmStreamer(*Context, pOutput, getVerboseAsm(getTM()), getTM().Options.MCOptions.MCUseDwarfDirectory, @@ -314,7 +313,7 @@ mcld::MCLDTargetMachine::addCompilerPasses(llvm::legacy::PassManagerBase &pPM, if (funcPass == 0) return true; // If successful, createAsmPrinter took ownership of AsmStreamer - AsmStreamer.take(); + AsmStreamer.release(); pPM.add(funcPass); return false; } @@ -338,7 +337,7 @@ mcld::MCLDTargetMachine::addAssemblerPasses(llvm::legacy::PassManagerBase &pPM, return true; // now, we have MCCodeEmitter and MCAsmBackend, we can create AsmStreamer. - OwningPtr<MCStreamer> AsmStreamer(m_pLLVMTarget->createMCObjectStreamer( + std::unique_ptr<MCStreamer> AsmStreamer(m_pLLVMTarget->createMCObjectStreamer( m_Triple, *Context, *MAB, pOutput, MCE, STI, getTM().Options.MCOptions.MCRelaxAll, getTM().Options.MCOptions.MCNoExecStack)); @@ -348,7 +347,7 @@ mcld::MCLDTargetMachine::addAssemblerPasses(llvm::legacy::PassManagerBase &pPM, if (funcPass == 0) return true; // If successful, createAsmPrinter took ownership of AsmStreamer - AsmStreamer.take(); + AsmStreamer.release(); pPM.add(funcPass); return false; } diff --git a/lib/Core/GeneralOptions.cpp b/lib/Core/GeneralOptions.cpp index 7f9b81c..f6e86b9 100644 --- a/lib/Core/GeneralOptions.cpp +++ b/lib/Core/GeneralOptions.cpp @@ -9,6 +9,7 @@ #include <mcld/GeneralOptions.h> #include <mcld/MC/Input.h> #include <mcld/MC/ZOption.h> +#include <cassert> using namespace mcld; @@ -54,7 +55,11 @@ GeneralOptions::GeneralOptions() m_bNoStdlib(false), m_bWarnMismatch(true), m_bGCSections(false), + m_bPrintGCSections(false), m_bGenUnwindInfo(true), + m_bPrintICFSections(false), + m_ICF(ICF_None), + m_ICFIterations(0) , m_GPSize(8), m_StripSymbols(KeepAllSymbols), m_HashStyle(SystemV) { @@ -145,3 +150,26 @@ void GeneralOptions::addZOption(const ZOption& pOption) break; } } + +bool GeneralOptions::isInExcludeLIBS(const Input& pInput) const +{ + assert(pInput.type() == Input::Archive); + + if (m_ExcludeLIBS.empty()) { + return false; + } + + // Specifying "--exclude-libs ALL" excludes symbols in all archive libraries + // from automatic export. + if (m_ExcludeLIBS.count("ALL") != 0) { + return true; + } + + std::string name(pInput.name()); + name.append(".a"); + if (m_ExcludeLIBS.count(name) != 0) { + return true; + } + + return false; +} diff --git a/lib/Core/IRBuilder.cpp b/lib/Core/IRBuilder.cpp index 080a122..bc44990 100644 --- a/lib/Core/IRBuilder.cpp +++ b/lib/Core/IRBuilder.cpp @@ -62,8 +62,12 @@ LDFileFormat::Kind GetELFSectionKind(uint32_t pType, const char* pName, case llvm::ELF::SHT_INIT_ARRAY: case llvm::ELF::SHT_FINI_ARRAY: case llvm::ELF::SHT_PREINIT_ARRAY: - case llvm::ELF::SHT_PROGBITS: - return LDFileFormat::Regular; + case llvm::ELF::SHT_PROGBITS: { + if ((pFlag & llvm::ELF::SHF_EXECINSTR) != 0) + return LDFileFormat::TEXT; + else + return LDFileFormat::DATA; + } case llvm::ELF::SHT_SYMTAB: case llvm::ELF::SHT_DYNSYM: case llvm::ELF::SHT_STRTAB: @@ -423,6 +427,13 @@ LDSymbol* IRBuilder::AddSymbol(Input& pInput, name = renameSym.getEntry()->value(); } + // Fix up the visibility if object has no export set. + if (pInput.noExport() && (pDesc != ResolveInfo::Undefined)) { + if ((pVis == ResolveInfo::Default) || (pVis == ResolveInfo::Protected)) { + pVis = ResolveInfo::Hidden; + } + } + switch (pInput.type()) { case Input::Object: { diff --git a/lib/Core/Linker.cpp b/lib/Core/Linker.cpp index 464f039..c8638a7 100644 --- a/lib/Core/Linker.cpp +++ b/lib/Core/Linker.cpp @@ -28,8 +28,6 @@ #include <mcld/Fragment/Relocation.h> #include <mcld/Fragment/FragmentRef.h> -#include <llvm/ADT/OwningPtr.h> - #include <cassert> using namespace mcld; @@ -96,7 +94,12 @@ bool Linker::normalize(Module& pModule, IRBuilder& pBuilder) if (!Diagnose()) return false; - // 4. - normalize the input tree + // 4.a - add undefined symbols + // before reading the inputs, we should add undefined symbols set by -u to + // ensure that correspoding objects (e.g. in an archive) will be included + m_pObjLinker->addUndefinedSymbols(); + + // 4.b - normalize the input tree // read out sections and symbol/string tables (from the files) and // set them in Module. When reading out the symbol, resolve their symbols // immediately and set their ResolveInfo (i.e., Symbol Resolution). @@ -204,7 +207,6 @@ bool Linker::layout() assert(NULL != m_pConfig && NULL != m_pObjLinker); // 10. - add standard symbols, target-dependent symbols and script symbols - // m_pObjLinker->addUndefSymbols(); if (!m_pObjLinker->addStandardSymbols() || !m_pObjLinker->addTargetSymbols() || !m_pObjLinker->addScriptSymbols()) @@ -280,7 +282,7 @@ bool Linker::emit(const Module& pModule, const std::string& pPath) return false; } - llvm::OwningPtr<FileOutputBuffer> output; + std::unique_ptr<FileOutputBuffer> output; FileOutputBuffer::create(file, m_pObjLinker->getWriter()->getOutputSize(pModule), output); @@ -295,7 +297,7 @@ bool Linker::emit(const Module& pModule, int pFileDescriptor) FileHandle file; file.delegate(pFileDescriptor); - llvm::OwningPtr<FileOutputBuffer> output; + std::unique_ptr<FileOutputBuffer> output; FileOutputBuffer::create(file, m_pObjLinker->getWriter()->getOutputSize(pModule), output); diff --git a/lib/LD/BranchIslandFactory.cpp b/lib/LD/BranchIslandFactory.cpp index 116f075..fcaa2eb 100644 --- a/lib/LD/BranchIslandFactory.cpp +++ b/lib/LD/BranchIslandFactory.cpp @@ -19,14 +19,16 @@ using namespace mcld; //===----------------------------------------------------------------------===// /// ctor -/// @param pMaxBranchRange - the max branch range of the target backend -/// @param pMaxIslandSize - a predifned value (1KB here) to decide the max -/// size of the island -BranchIslandFactory::BranchIslandFactory(uint64_t pMaxBranchRange, - uint64_t pMaxIslandSize) - : GCFactory<BranchIsland, 0>(1u), // magic number - m_MaxBranchRange(pMaxBranchRange - pMaxIslandSize), - m_MaxIslandSize(pMaxIslandSize) +/// @param pMaxFwdBranchRange - the max forward branch range of the target +/// @param pMaxBwdBranchRange - the max backward branch range of the target +/// @param pMaxIslandSize - the predefined value for the max size of a island +BranchIslandFactory::BranchIslandFactory(int64_t pMaxFwdBranchRange, + int64_t pMaxBwdBranchRange, + size_t pMaxIslandSize) + : GCFactory<BranchIsland, 0>(1u), // magic number + m_MaxFwdBranchRange(pMaxFwdBranchRange - pMaxIslandSize), + m_MaxBwdBranchRange(pMaxBwdBranchRange + pMaxIslandSize), + m_MaxIslandSize(pMaxIslandSize) { } @@ -38,11 +40,11 @@ BranchIslandFactory::~BranchIslandFactory() /// @param pSectionData - the SectionData holds fragments need to be grouped void BranchIslandFactory::group(Module& pModule) { - /* Currently only support relaxing .text section! */ + /* FIXME: Currently only support relaxing .text section! */ LDSection* text = pModule.getSection(".text"); if (text != NULL && text->hasSectionData()) { SectionData& sd = *text->getSectionData(); - uint64_t group_end = m_MaxBranchRange - m_MaxIslandSize; + uint64_t group_end = m_MaxFwdBranchRange; for (SectionData::iterator it = sd.begin(), ie = sd.end(); it != ie; ++it) { if ((*it).getOffset() + (*it).size() > group_end) { Fragment* frag = (*it).getPrevNode(); @@ -51,11 +53,11 @@ void BranchIslandFactory::group(Module& pModule) } if (frag != NULL) { produce(*frag); - group_end = (*it).getOffset() + m_MaxBranchRange - m_MaxIslandSize; + group_end = (*it).getOffset() + m_MaxFwdBranchRange; } } } - if (find(sd.back()) == NULL) + if (getIslands(sd.back()).first == NULL) produce(sd.back()); } } @@ -71,17 +73,29 @@ BranchIsland* BranchIslandFactory::produce(Fragment& pFragment) return island; } -/// find - find a island for the given fragment -/// @param pFragment - the fragment needs a branch isladn -BranchIsland* BranchIslandFactory::find(const Fragment& pFragment) +/// getIsland - find fwd and bwd islands for the fragment +/// @param pFragment - the fragment needs a branch island +std::pair<BranchIsland*, BranchIsland*> +BranchIslandFactory::getIslands(const Fragment& pFragment) { - // Currently we always find the island in a forward direction. - // TODO: If we can search backward, then we may reduce the number of stubs. - for (iterator it = begin(), ie = end(); it != ie; ++it) { + BranchIsland* fwd = NULL; + BranchIsland* bwd = NULL; + for (iterator it = begin(), ie = end(), prev = ie; it != ie; + prev = it, ++it) { if ((pFragment.getOffset() < (*it).offset()) && - ((pFragment.getOffset() + m_MaxBranchRange) >= (*it).offset())) - return &(*it); + ((pFragment.getOffset() + m_MaxFwdBranchRange) >= (*it).offset())) { + + fwd = &*it; + + if (prev != ie) { + int64_t bwd_off = (int64_t)pFragment.getOffset() + m_MaxBwdBranchRange; + if ((pFragment.getOffset() > (*prev).offset()) && + (bwd_off <= (*prev).offset())) { + bwd = &*prev; + } + } + break; + } } - return NULL; + return std::make_pair(fwd, bwd); } - diff --git a/lib/LD/DiagnosticInfos.cpp b/lib/LD/DiagnosticInfos.cpp index 943b046..aed389a 100644 --- a/lib/LD/DiagnosticInfos.cpp +++ b/lib/LD/DiagnosticInfos.cpp @@ -141,18 +141,17 @@ bool DiagnosticInfos::process(DiagnosticEngine& pEngine) const else severity = DiagnosticEngine::Ignore; break; - case LinkerConfig::Exec: - if (m_Config.options().isNoUndefined()) - severity = DiagnosticEngine::Error; - else - severity = DiagnosticEngine::Ignore; - break; default: severity = DiagnosticEngine::Error; break; } break; } + case diag::debug_print_gc_sections: { + if (!m_Config.options().getPrintGCSections()) + severity = DiagnosticEngine::Ignore; + break; + } default: break; } // end of switch diff --git a/lib/LD/ELFBinaryReader.cpp b/lib/LD/ELFBinaryReader.cpp index ee537c2..c72fa58 100644 --- a/lib/LD/ELFBinaryReader.cpp +++ b/lib/LD/ELFBinaryReader.cpp @@ -52,7 +52,7 @@ bool ELFBinaryReader::readBinary(Input& pInput) LDSection* data_sect = m_Builder.CreateELFHeader(pInput, ".data", - LDFileFormat::Regular, + LDFileFormat::DATA, llvm::ELF::SHF_WRITE | llvm::ELF::SHF_ALLOC, 0x1); diff --git a/lib/LD/ELFDynObjReader.cpp b/lib/LD/ELFDynObjReader.cpp index ebeba63..94eef58 100644 --- a/lib/LD/ELFDynObjReader.cpp +++ b/lib/LD/ELFDynObjReader.cpp @@ -17,7 +17,6 @@ #include <llvm/ADT/StringRef.h> #include <llvm/ADT/Twine.h> -#include <llvm/ADT/OwningPtr.h> #include <llvm/Support/ErrorHandling.h> #include <string> diff --git a/lib/LD/ELFFileFormat.cpp b/lib/LD/ELFFileFormat.cpp index 551a60d..318e595 100644 --- a/lib/LD/ELFFileFormat.cpp +++ b/lib/LD/ELFFileFormat.cpp @@ -67,7 +67,7 @@ ELFFileFormat::ELFFileFormat() void ELFFileFormat::initStdSections(ObjectBuilder& pBuilder, unsigned int pBitClass) { f_pTextSection = pBuilder.CreateSection(".text", - LDFileFormat::Regular, + LDFileFormat::TEXT, llvm::ELF::SHT_PROGBITS, llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_EXECINSTR, 0x1); @@ -76,7 +76,7 @@ void ELFFileFormat::initStdSections(ObjectBuilder& pBuilder, unsigned int pBitCl llvm::ELF::SHT_NULL, 0x0); f_pReadOnlySection = pBuilder.CreateSection(".rodata", - LDFileFormat::Regular, + LDFileFormat::TEXT, llvm::ELF::SHT_PROGBITS, llvm::ELF::SHF_ALLOC, 0x1); @@ -92,12 +92,12 @@ void ELFFileFormat::initStdSections(ObjectBuilder& pBuilder, unsigned int pBitCl 0x0, 0x1); f_pDataSection = pBuilder.CreateSection(".data", - LDFileFormat::Regular, + LDFileFormat::DATA, llvm::ELF::SHT_PROGBITS, llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_WRITE, 0x1); f_pData1 = pBuilder.CreateSection(".data1", - LDFileFormat::Regular, + LDFileFormat::DATA, llvm::ELF::SHT_PROGBITS, llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_WRITE, 0x1); @@ -107,22 +107,22 @@ void ELFFileFormat::initStdSections(ObjectBuilder& pBuilder, unsigned int pBitCl 0x0, 0x1); f_pInit = pBuilder.CreateSection(".init", - LDFileFormat::Regular, + LDFileFormat::TEXT, llvm::ELF::SHT_PROGBITS, llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_EXECINSTR, 0x1); f_pInitArray = pBuilder.CreateSection(".init_array", - LDFileFormat::Regular, + LDFileFormat::DATA, llvm::ELF::SHT_INIT_ARRAY, llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_WRITE, 0x1); f_pFini = pBuilder.CreateSection(".fini", - LDFileFormat::Regular, + LDFileFormat::TEXT, llvm::ELF::SHT_PROGBITS, llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_EXECINSTR, 0x1); f_pFiniArray = pBuilder.CreateSection(".fini_array", - LDFileFormat::Regular, + LDFileFormat::DATA, llvm::ELF::SHT_FINI_ARRAY, llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_WRITE, 0x1); @@ -132,14 +132,14 @@ void ELFFileFormat::initStdSections(ObjectBuilder& pBuilder, unsigned int pBitCl 0x0, 0x1); f_pPreInitArray = pBuilder.CreateSection(".preinit_array", - LDFileFormat::Regular, + LDFileFormat::DATA, llvm::ELF::SHT_PREINIT_ARRAY, llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_WRITE, 0x1); // the definition of SHF_XXX attributes of rodata in Linux Standard Base // conflicts with System V standard. We follow System V standard. f_pROData1 = pBuilder.CreateSection(".rodata1", - LDFileFormat::Regular, + LDFileFormat::TEXT, llvm::ELF::SHT_PROGBITS, llvm::ELF::SHF_ALLOC, 0x1); @@ -170,7 +170,7 @@ void ELFFileFormat::initStdSections(ObjectBuilder& pBuilder, unsigned int pBitCl llvm::ELF::SHF_TLS, 0x1); f_pTData = pBuilder.CreateSection(".tdata", - LDFileFormat::Regular, + LDFileFormat::DATA, llvm::ELF::SHT_PROGBITS, llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_WRITE | @@ -179,17 +179,17 @@ void ELFFileFormat::initStdSections(ObjectBuilder& pBuilder, unsigned int pBitCl /// @ref 10.3.1.2, ISO/IEC 23360, Part 1:2010(E), p. 24. f_pCtors = pBuilder.CreateSection(".ctors", - LDFileFormat::Regular, + LDFileFormat::DATA, llvm::ELF::SHT_PROGBITS, llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_WRITE, 0x1); f_pDataRelRo = pBuilder.CreateSection(".data.rel.ro", - LDFileFormat::Regular, + LDFileFormat::DATA, llvm::ELF::SHT_PROGBITS, llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_WRITE, 0x1); f_pDtors = pBuilder.CreateSection(".dtors", - LDFileFormat::Regular, + LDFileFormat::DATA, llvm::ELF::SHT_PROGBITS, llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_WRITE, 0x1); @@ -219,7 +219,7 @@ void ELFFileFormat::initStdSections(ObjectBuilder& pBuilder, unsigned int pBitCl llvm::ELF::SHF_ALLOC, 0x1); f_pJCR = pBuilder.CreateSection(".jcr", - LDFileFormat::Regular, + LDFileFormat::DATA, llvm::ELF::SHT_PROGBITS, llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_WRITE, 0x1); @@ -241,7 +241,7 @@ void ELFFileFormat::initStdSections(ObjectBuilder& pBuilder, unsigned int pBitCl /// @ref GCC convention, see http://www.airs.com/blog/archives/189 f_pDataRelRoLocal = pBuilder.CreateSection(".data.rel.ro.local", - LDFileFormat::Regular, + LDFileFormat::DATA, llvm::ELF::SHT_PROGBITS, llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_WRITE, 0x1); diff --git a/lib/LD/ELFObjectReader.cpp b/lib/LD/ELFObjectReader.cpp index a34739a..a0ece0f 100644 --- a/lib/LD/ELFObjectReader.cpp +++ b/lib/LD/ELFObjectReader.cpp @@ -170,7 +170,10 @@ bool ELFObjectReader::readSections(Input& pInput) fatal(diag::err_cannot_read_section) << (*section)->name(); } } else { - (*section)->setKind(LDFileFormat::Regular); + if (((*section)->flag() & llvm::ELF::SHF_EXECINSTR) != 0) + (*section)->setKind(LDFileFormat::TEXT); + else + (*section)->setKind(LDFileFormat::DATA); SectionData* sd = IRBuilder::CreateSectionData(**section); if (!m_pELFReader->readRegularSection(pInput, *sd)) fatal(diag::err_cannot_read_section) << (*section)->name(); @@ -199,7 +202,8 @@ bool ELFObjectReader::readSections(Input& pInput) // FIXME: support GCCExceptTable Kind case LDFileFormat::GCCExceptTable: /** Fall through **/ - case LDFileFormat::Regular: + case LDFileFormat::TEXT: + case LDFileFormat::DATA: case LDFileFormat::Note: case LDFileFormat::MetaData: { SectionData* sd = IRBuilder::CreateSectionData(**section); diff --git a/lib/LD/ELFObjectWriter.cpp b/lib/LD/ELFObjectWriter.cpp index dca6936..c90efdf 100644 --- a/lib/LD/ELFObjectWriter.cpp +++ b/lib/LD/ELFObjectWriter.cpp @@ -29,8 +29,8 @@ #include <mcld/LD/ELFFileFormat.h> #include <mcld/Target/GNUInfo.h> +#include <llvm/Support/Errc.h> #include <llvm/Support/ErrorHandling.h> -#include <llvm/Support/system_error.h> #include <llvm/Support/ELF.h> #include <llvm/Support/Casting.h> @@ -61,7 +61,8 @@ void ELFObjectWriter::writeSection(Module& pModule, if (section->getSectionData() == NULL) return; // Fall through - case LDFileFormat::Regular: + case LDFileFormat::TEXT: + case LDFileFormat::DATA: case LDFileFormat::Relocation: case LDFileFormat::Target: case LDFileFormat::Debug: @@ -94,7 +95,8 @@ void ELFObjectWriter::writeSection(Module& pModule, // Write out sections with data switch(section->kind()) { case LDFileFormat::GCCExceptTable: - case LDFileFormat::Regular: + case LDFileFormat::TEXT: + case LDFileFormat::DATA: case LDFileFormat::Debug: case LDFileFormat::Note: emitSectionData(*section, region); @@ -116,8 +118,8 @@ void ELFObjectWriter::writeSection(Module& pModule, } } -llvm::error_code ELFObjectWriter::writeObject(Module& pModule, - FileOutputBuffer& pOutput) +std::error_code ELFObjectWriter::writeObject(Module& pModule, + FileOutputBuffer& pOutput) { bool is_dynobj = m_Config.codeGenType() == LinkerConfig::DynObj; bool is_exec = m_Config.codeGenType() == LinkerConfig::Exec; @@ -180,10 +182,10 @@ llvm::error_code ELFObjectWriter::writeObject(Module& pModule, emitSectionHeader<64>(pModule, m_Config, pOutput); } else - return make_error_code(errc::not_supported); + return llvm::make_error_code(llvm::errc::function_not_supported); } - return llvm::make_error_code(llvm::errc::success); + return std::error_code(); } // getOutputSize - count the final output size diff --git a/lib/LD/GNUArchiveReader.cpp b/lib/LD/GNUArchiveReader.cpp index 3dc95a5..4553bfb 100644 --- a/lib/LD/GNUArchiveReader.cpp +++ b/lib/LD/GNUArchiveReader.cpp @@ -401,6 +401,10 @@ size_t GNUArchiveReader::includeMember(const LinkerConfig& pConfig, if (m_ELFObjectReader.isMyFormat(*member, doContinue)) { member->setType(Input::Object); + // Set this object as no export if the archive is in the exclude libs. + if (pArchive.getARFile().noExport()) { + member->setNoExport(); + } pArchive.addObjectMember(pFileOffset, parent->lastPos); m_ELFObjectReader.readHeader(*member); m_ELFObjectReader.readSections(*member); @@ -468,4 +472,3 @@ bool GNUArchiveReader::includeAllMembers(const LinkerConfig& pConfig, } return true; } - diff --git a/lib/LD/GarbageCollection.cpp b/lib/LD/GarbageCollection.cpp index 4ea40ac..44c9b5a 100644 --- a/lib/LD/GarbageCollection.cpp +++ b/lib/LD/GarbageCollection.cpp @@ -18,6 +18,7 @@ #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> @@ -63,7 +64,8 @@ static bool shouldKeep(const std::string& pName) /// shouldProcessGC - check if the section kind is handled in GC static bool mayProcessGC(const LDSection& pSection) { - if (pSection.kind() == LDFileFormat::Regular || + if (pSection.kind() == LDFileFormat::TEXT || + pSection.kind() == LDFileFormat::DATA || pSection.kind() == LDFileFormat::BSS || pSection.kind() == LDFileFormat::GCCExceptTable) return true; @@ -270,6 +272,24 @@ void GarbageCollection::getEntrySections(SectionVecTy& pEntry) 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) @@ -323,8 +343,11 @@ void GarbageCollection::stripSections() if (!mayProcessGC(*section)) continue; - if (m_ReferencedSections.find(section) == m_ReferencedSections.end()) + if (m_ReferencedSections.find(section) == m_ReferencedSections.end()) { section->setKind(LDFileFormat::Ignore); + debug(diag::debug_print_gc_sections) << section->name() + << (*obj)->name(); + } } } diff --git a/lib/LD/Relocator.cpp b/lib/LD/Relocator.cpp index bf76573..fa3a94f 100644 --- a/lib/LD/Relocator.cpp +++ b/lib/LD/Relocator.cpp @@ -6,7 +6,6 @@ // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// -#include <mcld/Config/Config.h> #include <mcld/Fragment/Fragment.h> #include <mcld/LD/LDContext.h> #include <mcld/LD/LDSection.h> @@ -14,40 +13,14 @@ #include <mcld/LD/Relocator.h> #include <mcld/LD/ResolveInfo.h> #include <mcld/LD/SectionData.h> +#include <mcld/Support/Demangle.h> #include <mcld/Support/MsgHandling.h> #include <mcld/Module.h> -#ifdef HAVE_CXXABI_H -#include <cxxabi.h> -#endif #include <sstream> using namespace mcld; //===----------------------------------------------------------------------===// -// Helper functions -//===----------------------------------------------------------------------===// -std::string demangleSymbol(const std::string& mangled_name) { -#ifdef HAVE_CXXABI_H - // __cxa_demangle needs manually handle the memory release, so we wrap - // it into this helper function. - size_t output_leng; - int status; - char* buffer = abi::__cxa_demangle(mangled_name.c_str(), /*buffer=*/0, - &output_leng, &status); - if (status != 0) { // Failed - return mangled_name; - } - std::string demangled_name(buffer); - free(buffer); - - return demangled_name; -#else - return mangled_name; -#endif -} - - -//===----------------------------------------------------------------------===// // Relocator //===----------------------------------------------------------------------===// Relocator::~Relocator() @@ -87,8 +60,7 @@ void Relocator::issueUndefRef(Relocation& pReloc, sect_name = sect_name.substr(sect_name.find('.', /*pos=*/1)); // Drop .rel(a) prefix std::string reloc_sym(pReloc.symInfo()->name()); - if (reloc_sym.substr(0, 2) == "_Z") - reloc_sym = demangleSymbol(reloc_sym); + reloc_sym = demangleName(reloc_sym); std::stringstream ss; ss << "0x" << std::hex << undef_sym_pos; @@ -119,8 +91,7 @@ void Relocator::issueUndefRef(Relocation& pReloc, } } - if (caller_func_name.substr(0, 2) == "_Z") - caller_func_name = demangleSymbol(caller_func_name); + caller_func_name = demangleName(caller_func_name); fatal(diag::undefined_reference_text) << reloc_sym << pInput.path() diff --git a/lib/LD/StaticResolver.cpp b/lib/LD/StaticResolver.cpp index d7daf1d..3dad005 100644 --- a/lib/LD/StaticResolver.cpp +++ b/lib/LD/StaticResolver.cpp @@ -8,10 +8,9 @@ //===----------------------------------------------------------------------===// #include <mcld/LD/StaticResolver.h> #include <mcld/LD/LDSymbol.h> +#include <mcld/Support/Demangle.h> #include <mcld/Support/MsgHandling.h> -#include <cxxabi.h> - using namespace mcld; //========================== @@ -171,9 +170,6 @@ bool StaticResolver::resolve(ResolveInfo& __restrict__ pOld, } /* Fall through */ case MDEF: { /* multiple definition error. */ - int status; - char* demangled_name = abi::__cxa_demangle(pNew.name(), NULL, NULL, - &status); if (pOld.isDefine() && pNew.isDefine() && pOld.isAbsolute() && pNew.isAbsolute() && (pOld.desc() == pNew.desc() || pOld.desc() == ResolveInfo::NoType || @@ -183,21 +179,15 @@ bool StaticResolver::resolve(ResolveInfo& __restrict__ pOld, old->override(pNew); break; } else { - if (demangled_name != NULL) { - error(diag::multiple_absolute_definitions) << demangled_name - << pOld.outSymbol()->value() << pValue; - } else { - error(diag::multiple_absolute_definitions) << pNew.name() - << pOld.outSymbol()->value() << pValue; - } + error(diag::multiple_absolute_definitions) + << demangleName(pNew.name()) + << pOld.outSymbol()->value() + << pValue; break; } } - if (demangled_name != NULL) { - error(diag::multiple_definitions) << demangled_name; - } else { - error(diag::multiple_definitions) << pNew.name(); - } + + error(diag::multiple_definitions) << demangleName(pNew.name()); break; } case REFC: { /* Mark indirect symbol referenced and then CYCLE. */ diff --git a/lib/LD/StubFactory.cpp b/lib/LD/StubFactory.cpp index 3d7c56a..52c372c 100644 --- a/lib/LD/StubFactory.cpp +++ b/lib/LD/StubFactory.cpp @@ -14,7 +14,6 @@ #include <mcld/LD/ResolveInfo.h> #include <mcld/Fragment/Stub.h> #include <mcld/Fragment/Relocation.h> -#include <mcld/Fragment/FragmentRef.h> #include <string> @@ -43,67 +42,78 @@ Stub* StubFactory::create(Relocation& pReloc, BranchIslandFactory& pBRIslandFactory) { // find if there is a prototype stub for the input relocation - Stub* prototype = findPrototype(pReloc, - pReloc.place(), - pTargetSymValue); - if (NULL != prototype) { - // find the island for the input relocation - BranchIsland* island = pBRIslandFactory.find(*(pReloc.targetRef().frag())); - if (NULL == island) { + Stub* stub = NULL; + Stub* prototype = findPrototype(pReloc, pReloc.place(), pTargetSymValue); + if (prototype != NULL) { + const Fragment* frag = pReloc.targetRef().frag(); + // find the islands for the input relocation + std::pair<BranchIsland*, BranchIsland*> islands = + pBRIslandFactory.getIslands(*frag); + if (islands.first == NULL) { + // early exit if we can not find the forward island. return NULL; } - // find if there is such a stub in the island already - Stub* stub = island->findStub(prototype, pReloc); - if (NULL != stub) { + // find if there is such a stub in the backward island first. + if (islands.second != NULL) { + stub = islands.second->findStub(prototype, pReloc); + } + + if (stub != NULL) { // reset the branch target to the stub instead! pReloc.setSymInfo(stub->symInfo()); - } - else { - // create a stub from the prototype - stub = prototype->clone(); + } else { + // find if there is such a stub in the forward island. + stub = islands.first->findStub(prototype, pReloc); + if (stub != NULL) { + // reset the branch target to the stub instead! + pReloc.setSymInfo(stub->symInfo()); + } else { + // create a stub from the prototype + stub = prototype->clone(); - // build a name for stub symbol - std::string name("__"); - name.append(pReloc.symInfo()->name()); - name.append("_"); - name.append(stub->name()); - name.append("@"); - name.append(island->name()); + // build a name for stub symbol + std::string name("__"); + name.append(pReloc.symInfo()->name()) + .append("_") + .append(stub->name()) + .append("@") + .append(islands.first->name()); - // create LDSymbol for the stub - LDSymbol* symbol = - pBuilder.AddSymbol<IRBuilder::Force, IRBuilder::Resolve>( - name, - ResolveInfo::Function, - ResolveInfo::Define, - ResolveInfo::Local, - stub->size(), // size - stub->initSymValue(), // value - FragmentRef::Create(*stub, stub->initSymValue()), - ResolveInfo::Default); - stub->setSymInfo(symbol->resolveInfo()); + // create LDSymbol for the stub + LDSymbol* symbol = + pBuilder.AddSymbol<IRBuilder::Force, IRBuilder::Unresolve>( + name, + ResolveInfo::Function, + ResolveInfo::Define, + ResolveInfo::Local, + stub->size(), // size + stub->initSymValue(), // value + FragmentRef::Create(*stub, stub->initSymValue()), + ResolveInfo::Default); + stub->setSymInfo(symbol->resolveInfo()); - // add relocations of this stub (i.e., set the branch target of the stub) - for (Stub::fixup_iterator it = stub->fixup_begin(), + // add relocations of this stub (i.e., set the branch target of the stub) + for (Stub::fixup_iterator it = stub->fixup_begin(), ie = stub->fixup_end(); it != ie; ++it) { - Relocation* reloc = Relocation::Create((*it)->type(), + Relocation* reloc = + Relocation::Create((*it)->type(), *(FragmentRef::Create(*stub, (*it)->offset())), (*it)->addend()); - reloc->setSymInfo(pReloc.symInfo()); - island->addRelocation(*reloc); - } + reloc->setSymInfo(pReloc.symInfo()); + islands.first->addRelocation(*reloc); + } - // add stub to the branch island - island->addStub(prototype, pReloc, *stub); + // add stub to the forward branch island + islands.first->addStub(prototype, pReloc, *stub); - // reset the branch target of the input reloc to this stub instead! - pReloc.setSymInfo(stub->symInfo()); - return stub; + // reset the branch target of the input reloc to this stub instead! + pReloc.setSymInfo(stub->symInfo()); + } } } - return NULL; + return stub; } /// findPrototype - find if there is a registered stub prototype for the given @@ -119,4 +129,3 @@ Stub* StubFactory::findPrototype(const Relocation& pReloc, } return NULL; } - diff --git a/lib/MC/Input.cpp b/lib/MC/Input.cpp index 2d948c2..26234fc 100644 --- a/lib/MC/Input.cpp +++ b/lib/MC/Input.cpp @@ -21,6 +21,7 @@ Input::Input(llvm::StringRef pName) m_Path(), m_pAttr(NULL), m_bNeeded(false), + m_bNoExport(false), m_fileOffset(0), m_pMemArea(NULL), m_pContext(NULL) { @@ -32,6 +33,7 @@ Input::Input(llvm::StringRef pName, const AttributeProxy& pProxy) m_Path(), m_pAttr(const_cast<Attribute*>(pProxy.attr())), m_bNeeded(false), + m_bNoExport(false), m_fileOffset(0), m_pMemArea(NULL), m_pContext(NULL) { @@ -46,6 +48,7 @@ Input::Input(llvm::StringRef pName, m_Path(pPath), m_pAttr(NULL), m_bNeeded(false), + m_bNoExport(false), m_fileOffset(pFileOffset), m_pMemArea(NULL), m_pContext(NULL) { @@ -61,6 +64,7 @@ Input::Input(llvm::StringRef pName, m_Path(pPath), m_pAttr(const_cast<Attribute*>(pProxy.attr())), m_bNeeded(false), + m_bNoExport(false), m_fileOffset(pFileOffset), m_pMemArea(NULL), m_pContext(NULL) { diff --git a/lib/Object/ObjectLinker.cpp b/lib/Object/ObjectLinker.cpp index d4df036..dfc7f37 100644 --- a/lib/Object/ObjectLinker.cpp +++ b/lib/Object/ObjectLinker.cpp @@ -22,6 +22,7 @@ #include <mcld/LD/GroupReader.h> #include <mcld/LD/BinaryReader.h> #include <mcld/LD/GarbageCollection.h> +#include <mcld/LD/IdenticalCodeFolding.h> #include <mcld/LD/ObjectWriter.h> #include <mcld/LD/ResolveInfo.h> #include <mcld/LD/RelocData.h> @@ -42,6 +43,7 @@ #include <llvm/Support/Casting.h> #include <llvm/Support/Host.h> +#include <system_error> using namespace llvm; using namespace mcld; @@ -111,6 +113,37 @@ bool ObjectLinker::initStdSections() return true; } +void ObjectLinker::addUndefinedSymbols() +{ + // Add the symbol set by -u as an undefind global symbol into symbol pool + 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) { + Resolver::Result result; + m_pModule->getNamePool().insertSymbol(*usym, // name + false, // isDyn + ResolveInfo::NoType, + ResolveInfo::Undefined, + ResolveInfo::Global, + 0x0, // size + 0x0, // value + ResolveInfo::Default, + NULL, + result); + + LDSymbol* output_sym = result.info->outSymbol(); + bool has_output_sym = (NULL != output_sym); + + // create the output symbol if it dose not have one + if (!result.existent || !has_output_sym) { + output_sym = LDSymbol::Create(*result.info); + result.info->setSymPtr(output_sym); + output_sym->setFragmentRef(FragmentRef::Null()); + } + } +} + void ObjectLinker::normalize() { // ----- set up inputs ----- // @@ -165,6 +198,9 @@ void ObjectLinker::normalize() // is an archive else if (doContinue && getArchiveReader()->isMyFormat(**input, doContinue)) { (*input)->setType(Input::Archive); + if (m_Config.options().isInExcludeLIBS(**input)) { + (*input)->setNoExport(); + } Archive archive(**input, m_pBuilder->getInputBuilder()); getArchiveReader()->readArchive(m_Config, archive); if(archive.numOfObjectMember() > 0) { @@ -236,6 +272,12 @@ void ObjectLinker::dataStrippingOpt() GarbageCollection GC(m_Config, m_LDBackend, *m_pModule); GC.run(); } + + // Identical code folding + if (m_Config.options().getICFMode() != GeneralOptions::ICF_None) { + IdenticalCodeFolding icf(m_Config, m_LDBackend, *m_pModule); + icf.foldIdenticalCode(); + } return; } @@ -267,6 +309,7 @@ bool ObjectLinker::mergeSections() for (sect = (*obj)->context()->sectBegin(); sect != sectEnd; ++sect) { switch ((*sect)->kind()) { // Some *INPUT sections should not be merged. + case LDFileFormat::Folded: case LDFileFormat::Ignore: case LDFileFormat::Null: case LDFileFormat::NamePool: @@ -278,7 +321,8 @@ bool ObjectLinker::mergeSections() if (!(*sect)->hasRelocData()) continue; // skip - if ((*sect)->getLink()->kind() == LDFileFormat::Ignore) + if ((*sect)->getLink()->kind() == LDFileFormat::Ignore || + (*sect)->getLink()->kind() == LDFileFormat::Folded) (*sect)->setKind(LDFileFormat::Ignore); break; } @@ -741,7 +785,7 @@ bool ObjectLinker::relocation() /// emitOutput - emit the output file. bool ObjectLinker::emitOutput(FileOutputBuffer& pOutput) { - return llvm::errc::success == getWriter()->writeObject(*m_pModule, pOutput); + return std::error_code() == getWriter()->writeObject(*m_pModule, pOutput); } diff --git a/lib/Object/SectionMap.cpp b/lib/Object/SectionMap.cpp index 64c4a87..4453d42 100644 --- a/lib/Object/SectionMap.cpp +++ b/lib/Object/SectionMap.cpp @@ -46,7 +46,7 @@ SectionMap::Input::Input(const std::string& pName, WildcardPattern::create(pName, WildcardPattern::SORT_NONE)); m_Spec.m_pWildcardSections = sections; - m_pSection = LDSection::Create(pName, LDFileFormat::Regular, 0, 0); + m_pSection = LDSection::Create(pName, LDFileFormat::TEXT, 0, 0); SectionData* sd = SectionData::Create(*m_pSection); m_pSection->setSectionData(sd); new NullFragment(sd); @@ -59,7 +59,7 @@ SectionMap::Input::Input(const InputSectDesc& pInputDesc) m_Spec.m_pWildcardFile = pInputDesc.spec().m_pWildcardFile; m_Spec.m_pExcludeFiles = pInputDesc.spec().m_pExcludeFiles; m_Spec.m_pWildcardSections = pInputDesc.spec().m_pWildcardSections; - m_pSection = LDSection::Create("", LDFileFormat::Regular, 0, 0); + m_pSection = LDSection::Create("", LDFileFormat::TEXT, 0, 0); SectionData* sd = SectionData::Create(*m_pSection); m_pSection->setSectionData(sd); new NullFragment(sd); @@ -85,7 +85,7 @@ SectionMap::Output::Output(const std::string& pName) m_Epilog.m_pPhdrs = NULL; m_Epilog.m_pFillExp = NULL; - m_pSection = LDSection::Create(pName, LDFileFormat::Regular, 0, 0); + m_pSection = LDSection::Create(pName, LDFileFormat::TEXT, 0, 0); SectionData* sd = SectionData::Create(*m_pSection); m_pSection->setSectionData(sd); @@ -98,7 +98,7 @@ SectionMap::Output::Output(const OutputSectDesc& pOutputDesc) m_Epilog(pOutputDesc.epilog()), m_Order(UINT_MAX) { - m_pSection = LDSection::Create(m_Name, LDFileFormat::Regular, 0, 0); + m_pSection = LDSection::Create(m_Name, LDFileFormat::TEXT, 0, 0); SectionData* sd = SectionData::Create(*m_pSection); m_pSection->setSectionData(sd); diff --git a/lib/Support/Demangle.cpp b/lib/Support/Demangle.cpp new file mode 100644 index 0000000..6db5c34 --- /dev/null +++ b/lib/Support/Demangle.cpp @@ -0,0 +1,76 @@ +//===- Demangle.cpp -------------------------------------------------------===// +// +// The MCLinker Project +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include <mcld/Config/Config.h> +#include <mcld/Support/CXADemangle.tcc> +#include <mcld/Support/Demangle.h> + +#ifdef HAVE_CXXABI_H +#include <cxxabi.h> +#endif + +namespace mcld { + +std::string demangleName(const std::string& pName) { +#ifdef HAVE_CXXABI_H + // Spoil names of symbols with C linkage, so use an heuristic approach to + // check if the name should be demangled. + if (pName.substr(0, 2) != "_Z") + return pName; + // __cxa_demangle needs manually handle the memory release, so we wrap + // it into this helper function. + size_t output_leng; + int status; + char* buffer = abi::__cxa_demangle(pName.c_str(), /*buffer=*/0, + &output_leng, &status); + if (status != 0) { // Failed + return pName; + } + std::string result(buffer); + free(buffer); + + return result; +#else + return pName; +#endif +} + +bool isCtorOrDtor(const char* pName, size_t pLength) +{ + arena<bs> a; + Db db(a); + db.cv = 0; + db.ref = 0; + db.encoding_depth = 0; + db.parsed_ctor_dtor_cv = false; + db.tag_templates = true; + db.template_param.emplace_back(a); + db.fix_forward_references = false; + db.try_to_parse_template_args = true; + int internal_status = success; + demangle(pName, pName + pLength, db, internal_status); + if (internal_status == success && + db.fix_forward_references && + !db.template_param.empty() && + !db.template_param.front().empty()) { + db.fix_forward_references = false; + db.tag_templates = false; + db.names.clear(); + db.subs.clear(); + demangle(pName, pName + pLength, db, internal_status); + if (db.fix_forward_references) + internal_status = invalid_mangled_name; + } + + if (internal_status != success) { + db.parsed_ctor_dtor_cv = false; + } + return db.parsed_ctor_dtor_cv; +} + +} // namespace mcld diff --git a/lib/Support/FileOutputBuffer.cpp b/lib/Support/FileOutputBuffer.cpp index ed8c898..4887e6b 100644 --- a/lib/Support/FileOutputBuffer.cpp +++ b/lib/Support/FileOutputBuffer.cpp @@ -25,25 +25,25 @@ FileOutputBuffer::~FileOutputBuffer() m_pRegion.reset(0); } -llvm::error_code FileOutputBuffer::create(FileHandle& pFileHandle, - size_t pSize, llvm::OwningPtr<FileOutputBuffer>& pResult) +std::error_code FileOutputBuffer::create(FileHandle& pFileHandle, + size_t pSize, std::unique_ptr<FileOutputBuffer>& pResult) { - llvm::error_code EC; - llvm::OwningPtr<mapped_file_region> mapped_file(new mapped_file_region( + std::error_code ec; + std::unique_ptr<mapped_file_region> mapped_file(new mapped_file_region( pFileHandle.handler(), false, mapped_file_region::readwrite, pSize, 0, - EC)); + ec)); - if (EC) - return EC; + if (ec) + return ec; pResult.reset(new FileOutputBuffer(mapped_file.get(), pFileHandle)); if (pResult) - mapped_file.take(); - return llvm::error_code::success(); + mapped_file.release(); + return std::error_code(); } MemoryRegion FileOutputBuffer::request(size_t pOffset, size_t pLength) diff --git a/lib/Support/MemoryArea.cpp b/lib/Support/MemoryArea.cpp index 0c7f3c1..548ac72 100644 --- a/lib/Support/MemoryArea.cpp +++ b/lib/Support/MemoryArea.cpp @@ -8,9 +8,11 @@ //===----------------------------------------------------------------------===// #include <mcld/Support/MemoryArea.h> #include <mcld/Support/MsgHandling.h> -#include <llvm/Support/system_error.h> + +#include <llvm/Support/ErrorOr.h> #include <cassert> +#include <system_error> using namespace mcld; @@ -19,12 +21,13 @@ using namespace mcld; //===--------------------------------------------------------------------===// MemoryArea::MemoryArea(llvm::StringRef pFilename) { - llvm::error_code ec = - llvm::MemoryBuffer::getFile(pFilename, m_pMemoryBuffer, /*FileSize*/ -1, + llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> buffer_or_error = + llvm::MemoryBuffer::getFile(pFilename, /*FileSize*/ -1, /*RequiresNullTerminator*/ false); - if (ec != llvm::errc::success) { + if (std::error_code ec = buffer_or_error.getError()) { fatal(diag::fatal_cannot_read_input) << pFilename.str(); } + m_pMemoryBuffer = std::move(buffer_or_error.get()); } MemoryArea::MemoryArea(const char* pMemBuffer, size_t pSize) diff --git a/lib/Target/AArch64/AArch64LDBackend.cpp b/lib/Target/AArch64/AArch64LDBackend.cpp index 09f6766..cf07428 100644 --- a/lib/Target/AArch64/AArch64LDBackend.cpp +++ b/lib/Target/AArch64/AArch64LDBackend.cpp @@ -152,6 +152,12 @@ bool AArch64GNULDBackend::initRelocator() return true; } +const Relocator* AArch64GNULDBackend::getRelocator() const +{ + assert(NULL != m_pRelocator); + return m_pRelocator; +} + Relocator* AArch64GNULDBackend::getRelocator() { assert(NULL != m_pRelocator); diff --git a/lib/Target/AArch64/AArch64LDBackend.h b/lib/Target/AArch64/AArch64LDBackend.h index c79bfa6..00e14fa 100644 --- a/lib/Target/AArch64/AArch64LDBackend.h +++ b/lib/Target/AArch64/AArch64LDBackend.h @@ -27,6 +27,10 @@ class GNUInfo; class AArch64GNULDBackend : public GNULDBackend { public: + static const int64_t AARCH64_MAX_FWD_BRANCH_OFFSET = (((1 << 25) - 1) << 2); + static const int64_t AARCH64_MAX_BWD_BRANCH_OFFSET = (-((1 << 25) << 2)); + +public: AArch64GNULDBackend(const LinkerConfig& pConfig, GNUInfo* pInfo); ~AArch64GNULDBackend(); @@ -41,9 +45,9 @@ public: bool initRelocator(); /// getRelocator - return relocator. + const Relocator* getRelocator() const; Relocator* getRelocator(); - /// doPreLayout - Backend can do any needed modification before layout void doPreLayout(IRBuilder& pBuilder); @@ -110,9 +114,8 @@ public: private: void defineGOTSymbol(IRBuilder& pBuilder); - /// maxBranchOffset - /// FIXME: - uint64_t maxBranchOffset() { return 0x0; } + int64_t maxFwdBranchOffset() { return AARCH64_MAX_FWD_BRANCH_OFFSET; } + int64_t maxBwdBranchOffset() { return AARCH64_MAX_BWD_BRANCH_OFFSET; } /// mayRelax - Backends should override this function if they need relaxation bool mayRelax() { return true; } diff --git a/lib/Target/ARM/ARMELFAttributeData.cpp b/lib/Target/ARM/ARMELFAttributeData.cpp index afab867..8656b7e 100644 --- a/lib/Target/ARM/ARMELFAttributeData.cpp +++ b/lib/Target/ARM/ARMELFAttributeData.cpp @@ -1073,3 +1073,21 @@ size_t ARMELFAttributeData::emit(char *pBuf) const { return (buffer - pBuf); } + +bool ARMELFAttributeData::usingThumb() const +{ + int arch = m_Attrs[Tag_CPU_arch].getIntValue(); + if ((arch == CPU_Arch_ARM_V6_M) || (arch == CPU_Arch_ARM_V6S_M)) + return true; + if ((arch != CPU_Arch_ARM_V7) && (arch != CPU_Arch_ARM_V7E_M)) + return false; + + arch = m_Attrs[Tag_CPU_arch_profile].getIntValue(); + return arch == Arch_Profile_Microcontroller; +} + +bool ARMELFAttributeData::usingThumb2() const +{ + int arch = m_Attrs[Tag_CPU_arch].getIntValue(); + return (arch == CPU_Arch_ARM_V6T2) || (arch == CPU_Arch_ARM_V7); +} diff --git a/lib/Target/ARM/ARMELFAttributeData.h b/lib/Target/ARM/ARMELFAttributeData.h index c4128fa..1183b3d 100644 --- a/lib/Target/ARM/ARMELFAttributeData.h +++ b/lib/Target/ARM/ARMELFAttributeData.h @@ -180,6 +180,10 @@ public: virtual size_t emit(char *pBuf) const; + virtual bool usingThumb() const; + + virtual bool usingThumb2() const; + private: /// GetAttributeValueType - obtain the value type of the indicated tag. static unsigned int GetAttributeValueType(TagType pTag); diff --git a/lib/Target/ARM/ARMLDBackend.cpp b/lib/Target/ARM/ARMLDBackend.cpp index e0ca782..2b30df8 100644 --- a/lib/Target/ARM/ARMLDBackend.cpp +++ b/lib/Target/ARM/ARMLDBackend.cpp @@ -17,13 +17,6 @@ #include "THMToTHMStub.h" #include "THMToARMStub.h" -#include <cstring> - -#include <llvm/ADT/Triple.h> -#include <llvm/ADT/Twine.h> -#include <llvm/Support/ELF.h> -#include <llvm/Support/Casting.h> - #include <mcld/IRBuilder.h> #include <mcld/LinkerConfig.h> #include <mcld/Fragment/FillFragment.h> @@ -45,6 +38,14 @@ #include <mcld/Target/GNUInfo.h> #include <mcld/Object/ObjectBuilder.h> +#include <llvm/ADT/StringRef.h> +#include <llvm/ADT/Triple.h> +#include <llvm/ADT/Twine.h> +#include <llvm/Support/ELF.h> +#include <llvm/Support/Casting.h> + +#include <cstring> + using namespace mcld; //===----------------------------------------------------------------------===// @@ -205,6 +206,12 @@ bool ARMGNULDBackend::initRelocator() return true; } +const Relocator* ARMGNULDBackend::getRelocator() const +{ + assert(NULL != m_pRelocator); + return m_pRelocator; +} + Relocator* ARMGNULDBackend::getRelocator() { assert(NULL != m_pRelocator); @@ -471,7 +478,8 @@ void ARMGNULDBackend::setUpReachedSectionsForGC(const Module& pModule, // the reference const LDSection* target_sect = &sym->outSymbol()->fragRef()->frag()->getParent()->getSection(); - if (target_sect->kind() != LDFileFormat::Regular && + if (target_sect->kind() != LDFileFormat::TEXT && + target_sect->kind() != LDFileFormat::DATA && target_sect->kind() != LDFileFormat::BSS) continue; @@ -716,13 +724,35 @@ bool ARMGNULDBackend::initTargetStubs() if (NULL != getStubFactory()) { getStubFactory()->addPrototype(new ARMToARMStub(config().isCodeIndep())); getStubFactory()->addPrototype(new ARMToTHMStub(config().isCodeIndep())); - getStubFactory()->addPrototype(new THMToTHMStub(config().isCodeIndep())); - getStubFactory()->addPrototype(new THMToARMStub(config().isCodeIndep())); + getStubFactory()->addPrototype( + new THMToTHMStub(config().isCodeIndep(), m_pAttrData->usingThumb2())); + getStubFactory()->addPrototype( + new THMToARMStub(config().isCodeIndep(), m_pAttrData->usingThumb2())); return true; } return false; } +/// maxFwdBranchOffset +int64_t ARMGNULDBackend::maxFwdBranchOffset() +{ + if (m_pAttrData->usingThumb2()) { + return THM2_MAX_FWD_BRANCH_OFFSET; + } else { + return THM_MAX_FWD_BRANCH_OFFSET; + } +} + +/// maxBwdBranchOffset +int64_t ARMGNULDBackend::maxBwdBranchOffset() +{ + if (m_pAttrData->usingThumb2()) { + return THM2_MAX_BWD_BRANCH_OFFSET; + } else { + return THM_MAX_BWD_BRANCH_OFFSET; + } +} + /// doCreateProgramHdrs - backend can implement this function to create the /// target-dependent segments void ARMGNULDBackend::doCreateProgramHdrs(Module& pModule) @@ -735,6 +765,19 @@ void ARMGNULDBackend::doCreateProgramHdrs(Module& pModule) } } +/// mayHaveUnsafeFunctionPointerAccess - check if the section may have unsafe +/// function pointer access +bool +ARMGNULDBackend::mayHaveUnsafeFunctionPointerAccess(const LDSection& pSection) + const +{ + llvm::StringRef name(pSection.name()); + return !name.startswith(".ARM.exidx") && + !name.startswith(".ARM.extab") && + GNULDBackend::mayHaveUnsafeFunctionPointerAccess(pSection); +} + + namespace mcld { //===----------------------------------------------------------------------===// diff --git a/lib/Target/ARM/ARMLDBackend.h b/lib/Target/ARM/ARMLDBackend.h index cae589d..14d3fde 100644 --- a/lib/Target/ARM/ARMLDBackend.h +++ b/lib/Target/ARM/ARMLDBackend.h @@ -55,9 +55,9 @@ public: bool initRelocator(); /// getRelocator - return relocator. + const Relocator* getRelocator() const; Relocator* getRelocator(); - /// doPreLayout - Backend can do any needed modification before layout void doPreLayout(IRBuilder& pBuilder); @@ -126,12 +126,17 @@ public: /// readSection - read target dependent sections bool readSection(Input& pInput, SectionData& pSD); + /// mayHaveUnsafeFunctionPointerAccess - check if the section may have unsafe + /// function pointer access + bool mayHaveUnsafeFunctionPointerAccess(const LDSection& pSection) const; + private: void defineGOTSymbol(IRBuilder& pBuilder); - /// maxBranchOffset - /// FIXME: if we can handle arm attributes, we may refine this! - uint64_t maxBranchOffset() { return THM_MAX_FWD_BRANCH_OFFSET; } + /// maxFwdBranchOffset + int64_t maxFwdBranchOffset(); + /// maxBwdBranchOffset + int64_t maxBwdBranchOffset(); /// mayRelax - Backends should override this function if they need relaxation bool mayRelax() { return true; } diff --git a/lib/Target/ARM/ARMRelocationFunctions.h b/lib/Target/ARM/ARMRelocationFunctions.h index 790ed1f..58f00ce 100644 --- a/lib/Target/ARM/ARMRelocationFunctions.h +++ b/lib/Target/ARM/ARMRelocationFunctions.h @@ -32,6 +32,7 @@ DECL_ARM_APPLY_RELOC_FUNC(thm_movt_prel) \ DECL_ARM_APPLY_RELOC_FUNC(prel31) \ DECL_ARM_APPLY_RELOC_FUNC(got_prel) \ DECL_ARM_APPLY_RELOC_FUNC(tls) \ +DECL_ARM_APPLY_RELOC_FUNC(thm_jump8) \ DECL_ARM_APPLY_RELOC_FUNC(thm_jump11) \ DECL_ARM_APPLY_RELOC_FUNC(thm_jump19) \ DECL_ARM_APPLY_RELOC_FUNC(unsupport) @@ -141,7 +142,7 @@ DECL_ARM_APPLY_RELOC_FUNC(unsupport) { &unsupport, 100, "R_ARM_GNU_VTENTRY" }, \ { &unsupport, 101, "R_ARM_GNU_VTINERIT" }, \ { &thm_jump11, 102, "R_ARM_THM_JUMP11" }, \ - { &unsupport, 103, "R_ARM_THM_JUMP8" }, \ + { &thm_jump8, 103, "R_ARM_THM_JUMP8" }, \ { &tls, 104, "R_ARM_TLS_GD32" }, \ { &unsupport, 105, "R_ARM_TLS_LDM32" }, \ { &unsupport, 106, "R_ARM_TLS_LDO32" }, \ diff --git a/lib/Target/ARM/ARMRelocator.cpp b/lib/Target/ARM/ARMRelocator.cpp index 088b219..248c528 100644 --- a/lib/Target/ARM/ARMRelocator.cpp +++ b/lib/Target/ARM/ARMRelocator.cpp @@ -460,6 +460,29 @@ void ARMRelocator::checkValidReloc(Relocation& pReloc) const } } +bool ARMRelocator::mayHaveFunctionPointerAccess(const Relocation& pReloc) const +{ + switch (pReloc.type()) { + case llvm::ELF::R_ARM_PC24: + case llvm::ELF::R_ARM_THM_CALL: + case llvm::ELF::R_ARM_PLT32: + case llvm::ELF::R_ARM_CALL: + case llvm::ELF::R_ARM_JUMP24: + case llvm::ELF::R_ARM_THM_JUMP24: + case llvm::ELF::R_ARM_SBREL31: + case llvm::ELF::R_ARM_PREL31: + case llvm::ELF::R_ARM_THM_JUMP19: + case llvm::ELF::R_ARM_THM_JUMP6: + case llvm::ELF::R_ARM_THM_JUMP11: + case llvm::ELF::R_ARM_THM_JUMP8: { + return false; + } + default: { + return true; + } + } +} + void ARMRelocator::scanLocalReloc(Relocation& pReloc, const LDSection& pSection) { @@ -936,6 +959,25 @@ ARMRelocator::Result got_prel(Relocation& pReloc, ARMRelocator& pParent) return Relocator::OK; } +// R_ARM_THM_JUMP8: S + A - P +ARMRelocator::Result thm_jump8(Relocation& pReloc, ARMRelocator& pParent) +{ + Relocator::DWord P = pReloc.place(); + Relocator::DWord A = helper_sign_extend((pReloc.target() & 0x00ff) << 1, 8) + + pReloc.addend(); + // S depends on PLT exists or not + Relocator::Address S = pReloc.symValue(); + if (pReloc.symInfo()->reserved() & ARMRelocator::ReservePLT) + S = helper_get_PLT_address(*pReloc.symInfo(), pParent); + + Relocator::DWord X = S + A - P; + if (helper_check_signed_overflow(X, 9)) + return Relocator::Overflow; + // Make sure the Imm is 0. Result Mask. + pReloc.target() = (pReloc.target() & 0xFFFFFF00u) | ((X & 0x01FEu) >> 1); + return Relocator::OK; +} + // R_ARM_THM_JUMP11: S + A - P ARMRelocator::Result thm_jump11(Relocation& pReloc, ARMRelocator& pParent) { @@ -948,7 +990,7 @@ ARMRelocator::Result thm_jump11(Relocation& pReloc, ARMRelocator& pParent) S = helper_get_PLT_address(*pReloc.symInfo(), pParent); Relocator::DWord X = S + A - P; - if (helper_check_signed_overflow(X, 11)) + if (helper_check_signed_overflow(X, 12)) return Relocator::Overflow; // Make sure the Imm is 0. Result Mask. pReloc.target() = (pReloc.target() & 0xFFFFF800u) | ((X & 0x0FFEu) >> 1); diff --git a/lib/Target/ARM/ARMRelocator.h b/lib/Target/ARM/ARMRelocator.h index da4e96b..9c3156b 100644 --- a/lib/Target/ARM/ARMRelocator.h +++ b/lib/Target/ARM/ARMRelocator.h @@ -98,6 +98,11 @@ public: LDSection& pSection, Input& pInput); + + /// mayHaveFunctionPointerAccess - check if the given reloc would possibly + /// access a function pointer. + virtual bool mayHaveFunctionPointerAccess(const Relocation& pReloc) const; + private: void scanLocalReloc(Relocation& pReloc, const LDSection& pSection); diff --git a/lib/Target/ARM/ARMToARMStub.cpp b/lib/Target/ARM/ARMToARMStub.cpp index 3f5e6f6..ba3acf4 100644 --- a/lib/Target/ARM/ARMToARMStub.cpp +++ b/lib/Target/ARM/ARMToARMStub.cpp @@ -32,7 +32,7 @@ const uint32_t ARMToARMStub::TEMPLATE[] = { }; ARMToARMStub::ARMToARMStub(bool pIsOutputPIC) - : Stub(), m_Name("A2A_prototype"), m_pData(NULL), m_Size(0x0) + : m_pData(NULL), m_Name("A2A_prototype"), m_Size(0x0) { if (pIsOutputPIC) { m_pData = PIC_TEMPLATE; @@ -51,7 +51,7 @@ ARMToARMStub::ARMToARMStub(const uint32_t* pData, size_t pSize, const_fixup_iterator pBegin, const_fixup_iterator pEnd) - : Stub(), m_Name("A2A_veneer"), m_pData(pData), m_Size(pSize) + : m_pData(pData), m_Name("A2A_veneer"), m_Size(pSize) { for (const_fixup_iterator it = pBegin, ie = pEnd; it != ie; ++it) addFixup(**it); @@ -113,4 +113,3 @@ Stub* ARMToARMStub::doClone() { return new ARMToARMStub(m_pData, m_Size, fixup_begin(), fixup_end()); } - diff --git a/lib/Target/ARM/ARMToARMStub.h b/lib/Target/ARM/ARMToARMStub.h index a38dd90..afb26a8 100644 --- a/lib/Target/ARM/ARMToARMStub.h +++ b/lib/Target/ARM/ARMToARMStub.h @@ -61,10 +61,10 @@ private: Stub* doClone(); private: - std::string m_Name; static const uint32_t PIC_TEMPLATE[]; static const uint32_t TEMPLATE[]; const uint32_t* m_pData; + std::string m_Name; size_t m_Size; }; diff --git a/lib/Target/ARM/ARMToTHMStub.cpp b/lib/Target/ARM/ARMToTHMStub.cpp index 702df38..a46a0c8 100644 --- a/lib/Target/ARM/ARMToTHMStub.cpp +++ b/lib/Target/ARM/ARMToTHMStub.cpp @@ -34,7 +34,7 @@ const uint32_t ARMToTHMStub::TEMPLATE[] = { }; ARMToTHMStub::ARMToTHMStub(bool pIsOutputPIC) - : Stub(), m_Name("A2T_prototype"), m_pData(NULL), m_Size(0x0) + : m_pData(NULL), m_Name("A2T_prototype"), m_Size(0x0) { if (pIsOutputPIC) { m_pData = PIC_TEMPLATE; @@ -53,7 +53,7 @@ ARMToTHMStub::ARMToTHMStub(const uint32_t* pData, size_t pSize, const_fixup_iterator pBegin, const_fixup_iterator pEnd) - : Stub(), m_Name("A2T_veneer"), m_pData(pData), m_Size(pSize) + : m_pData(pData), m_Name("A2T_veneer"), m_Size(pSize) { for (const_fixup_iterator it = pBegin, ie = pEnd; it != ie; ++it) addFixup(**it); @@ -121,4 +121,3 @@ Stub* ARMToTHMStub::doClone() { return new ARMToTHMStub(m_pData, m_Size, fixup_begin(), fixup_end()); } - diff --git a/lib/Target/ARM/ARMToTHMStub.h b/lib/Target/ARM/ARMToTHMStub.h index f39db84..e7a44bf 100644 --- a/lib/Target/ARM/ARMToTHMStub.h +++ b/lib/Target/ARM/ARMToTHMStub.h @@ -61,10 +61,10 @@ private: Stub* doClone(); private: - std::string m_Name; static const uint32_t PIC_TEMPLATE[]; static const uint32_t TEMPLATE[]; const uint32_t* m_pData; + std::string m_Name; size_t m_Size; }; diff --git a/lib/Target/ARM/THMToARMStub.cpp b/lib/Target/ARM/THMToARMStub.cpp index e32222e..34e0cbf 100644 --- a/lib/Target/ARM/THMToARMStub.cpp +++ b/lib/Target/ARM/THMToARMStub.cpp @@ -33,15 +33,17 @@ const uint32_t THMToARMStub::TEMPLATE[] = { 0x0 // dcd R_ARM_ABS32(X) }; -THMToARMStub::THMToARMStub(bool pIsOutputPIC) - : Stub(), m_Name("T2A_prototype"), m_pData(NULL), m_Size(0x0) +THMToARMStub::THMToARMStub(bool pIsOutputPIC, bool pUsingThumb2) + : m_pData(NULL), + m_Name("T2A_prototype"), + m_Size(0x0), + m_bUsingThumb2(pUsingThumb2) { if (pIsOutputPIC) { m_pData = PIC_TEMPLATE; m_Size = sizeof(PIC_TEMPLATE); addFixup(12u, -4, llvm::ELF::R_ARM_REL32); - } - else { + } else { m_pData = TEMPLATE; m_Size = sizeof(TEMPLATE); addFixup(8u, 0x0, llvm::ELF::R_ARM_ABS32); @@ -52,8 +54,12 @@ THMToARMStub::THMToARMStub(bool pIsOutputPIC) THMToARMStub::THMToARMStub(const uint32_t* pData, size_t pSize, const_fixup_iterator pBegin, - const_fixup_iterator pEnd) - : Stub(), m_Name("T2A_veneer"), m_pData(pData), m_Size(pSize) + const_fixup_iterator pEnd, + bool pUsingThumb2) + : m_pData(pData), + m_Name("T2A_veneer"), + m_Size(pSize), + m_bUsingThumb2(pUsingThumb2) { for (const_fixup_iterator it = pBegin, ie = pEnd; it != ie; ++it) addFixup(**it); @@ -76,9 +82,19 @@ bool THMToARMStub::isMyDuty(const class Relocation& pReloc, // then, we do not need a stub unless the branch target is too far. uint64_t dest = pTargetSymValue + pReloc.addend() + 4u; int64_t branch_offset = static_cast<int64_t>(dest) - pSource; - if ((branch_offset > ARMGNULDBackend::THM_MAX_FWD_BRANCH_OFFSET) || - (branch_offset < ARMGNULDBackend::THM_MAX_BWD_BRANCH_OFFSET)) - result = true; + if (m_bUsingThumb2) { + if ((branch_offset > ARMGNULDBackend::THM2_MAX_FWD_BRANCH_OFFSET) || + (branch_offset < ARMGNULDBackend::THM2_MAX_BWD_BRANCH_OFFSET)) { + result = true; + break; + } + } else { + if ((branch_offset > ARMGNULDBackend::THM_MAX_FWD_BRANCH_OFFSET) || + (branch_offset < ARMGNULDBackend::THM_MAX_BWD_BRANCH_OFFSET)) { + result = true; + break; + } + } break; } case llvm::ELF::R_ARM_THM_JUMP24: { @@ -121,6 +137,9 @@ uint64_t THMToARMStub::initSymValue() const Stub* THMToARMStub::doClone() { - return new THMToARMStub(m_pData, m_Size, fixup_begin(), fixup_end()); + return new THMToARMStub(m_pData, + m_Size, + fixup_begin(), + fixup_end(), + m_bUsingThumb2); } - diff --git a/lib/Target/ARM/THMToARMStub.h b/lib/Target/ARM/THMToARMStub.h index a7b6cad..af5a926 100644 --- a/lib/Target/ARM/THMToARMStub.h +++ b/lib/Target/ARM/THMToARMStub.h @@ -13,7 +13,6 @@ #include <llvm/Support/DataTypes.h> #include <mcld/Fragment/Stub.h> #include <string> -#include <vector> namespace mcld { @@ -28,7 +27,7 @@ class ResolveInfo; class THMToARMStub : public Stub { public: - THMToARMStub(bool pIsOutputPIC); + THMToARMStub(bool pIsOutputPIC, bool pUsingThumb2); ~THMToARMStub(); @@ -58,17 +57,19 @@ private: THMToARMStub(const uint32_t* pData, size_t pSize, const_fixup_iterator pBegin, - const_fixup_iterator pEnd); + const_fixup_iterator pEnd, + bool pUsingThumb2); /// doClone Stub* doClone(); private: - std::string m_Name; static const uint32_t PIC_TEMPLATE[]; static const uint32_t TEMPLATE[]; const uint32_t* m_pData; + std::string m_Name; size_t m_Size; + bool m_bUsingThumb2; }; } // namespace of mcld diff --git a/lib/Target/ARM/THMToTHMStub.cpp b/lib/Target/ARM/THMToTHMStub.cpp index b58de77..f167f28 100644 --- a/lib/Target/ARM/THMToTHMStub.cpp +++ b/lib/Target/ARM/THMToTHMStub.cpp @@ -35,15 +35,17 @@ const uint32_t THMToTHMStub::TEMPLATE[] = { 0x0 // dcd R_ARM_ABS32(X) }; -THMToTHMStub::THMToTHMStub(bool pIsOutputPIC) - : Stub(), m_Name("T2T_prototype"), m_pData(NULL), m_Size(0x0) +THMToTHMStub::THMToTHMStub(bool pIsOutputPIC, bool pUsingThumb2) + : m_pData(NULL), + m_Name("T2T_prototype"), + m_Size(0x0), + m_bUsingThumb2(pUsingThumb2) { if (pIsOutputPIC) { m_pData = PIC_TEMPLATE; m_Size = sizeof(PIC_TEMPLATE); addFixup(16u, 0x0, llvm::ELF::R_ARM_REL32); - } - else { + } else { m_pData = TEMPLATE; m_Size = sizeof(TEMPLATE); addFixup(12u, 0x0, llvm::ELF::R_ARM_ABS32); @@ -54,8 +56,12 @@ THMToTHMStub::THMToTHMStub(bool pIsOutputPIC) THMToTHMStub::THMToTHMStub(const uint32_t* pData, size_t pSize, const_fixup_iterator pBegin, - const_fixup_iterator pEnd) - : Stub(), m_Name("T2T_veneer"), m_pData(pData), m_Size(pSize) + const_fixup_iterator pEnd, + bool pUsingThumb2) + : m_pData(pData), + m_Name("T2T_veneer"), + m_Size(pSize), + m_bUsingThumb2(pUsingThumb2) { for (const_fixup_iterator it = pBegin, ie = pEnd; it != ie; ++it) addFixup(**it); @@ -78,9 +84,19 @@ bool THMToTHMStub::isMyDuty(const class Relocation& pReloc, // Check if the branch target is too far uint64_t dest = pTargetSymValue + pReloc.addend() + 4u; int64_t branch_offset = static_cast<int64_t>(dest) - pSource; - if ((branch_offset > ARMGNULDBackend::THM_MAX_FWD_BRANCH_OFFSET) || - (branch_offset < ARMGNULDBackend::THM_MAX_BWD_BRANCH_OFFSET)) - result = true; + if (m_bUsingThumb2) { + if ((branch_offset > ARMGNULDBackend::THM2_MAX_FWD_BRANCH_OFFSET) || + (branch_offset < ARMGNULDBackend::THM2_MAX_BWD_BRANCH_OFFSET)) { + result = true; + break; + } + } else { + if ((branch_offset > ARMGNULDBackend::THM_MAX_FWD_BRANCH_OFFSET) || + (branch_offset < ARMGNULDBackend::THM_MAX_BWD_BRANCH_OFFSET)) { + result = true; + break; + } + } break; } default: @@ -117,6 +133,9 @@ uint64_t THMToTHMStub::initSymValue() const Stub* THMToTHMStub::doClone() { - return new THMToTHMStub(m_pData, m_Size, fixup_begin(), fixup_end()); + return new THMToTHMStub(m_pData, + m_Size, + fixup_begin(), + fixup_end(), + m_bUsingThumb2); } - diff --git a/lib/Target/ARM/THMToTHMStub.h b/lib/Target/ARM/THMToTHMStub.h index f6d155e..4f3f363 100644 --- a/lib/Target/ARM/THMToTHMStub.h +++ b/lib/Target/ARM/THMToTHMStub.h @@ -13,7 +13,6 @@ #include <llvm/Support/DataTypes.h> #include <mcld/Fragment/Stub.h> #include <string> -#include <vector> namespace mcld { @@ -28,7 +27,7 @@ class ResolveInfo; class THMToTHMStub : public Stub { public: - THMToTHMStub(bool pIsOutputPIC); + THMToTHMStub(bool pIsOutputPIC, bool pUsingThumb2); ~THMToTHMStub(); @@ -58,17 +57,19 @@ private: THMToTHMStub(const uint32_t* pData, size_t pSize, const_fixup_iterator pBegin, - const_fixup_iterator pEnd); + const_fixup_iterator pEnd, + bool pUsingThumb2); /// doClone Stub* doClone(); private: - std::string m_Name; static const uint32_t PIC_TEMPLATE[]; static const uint32_t TEMPLATE[]; const uint32_t* m_pData; + std::string m_Name; size_t m_Size; + bool m_bUsingThumb2; }; } // namespace of mcld diff --git a/lib/Target/GNULDBackend.cpp b/lib/Target/GNULDBackend.cpp index e888bc9..7021c5f 100644 --- a/lib/Target/GNULDBackend.cpp +++ b/lib/Target/GNULDBackend.cpp @@ -8,13 +8,6 @@ //===----------------------------------------------------------------------===// #include <mcld/Target/GNULDBackend.h> -#include <string> -#include <cstring> -#include <cassert> -#include <vector> -#include <algorithm> -#include <map> - #include <mcld/Module.h> #include <mcld/LinkerConfig.h> #include <mcld/LinkerScript.h> @@ -49,8 +42,16 @@ #include <mcld/Fragment/FillFragment.h> #include <mcld/MC/Attribute.h> +#include <llvm/ADT/StringRef.h> #include <llvm/Support/Host.h> +#include <algorithm> +#include <cstring> +#include <cassert> +#include <map> +#include <string> +#include <vector> + namespace { //===--------------------------------------------------------------------===// @@ -1380,7 +1381,8 @@ unsigned int GNULDBackend::getSectionOrder(const LDSection& pSectHdr) const bool is_exec = (pSectHdr.flag() & llvm::ELF::SHF_EXECINSTR) != 0; // TODO: need to take care other possible output sections switch (pSectHdr.kind()) { - case LDFileFormat::Regular: + case LDFileFormat::TEXT: + case LDFileFormat::DATA: if (is_exec) { if (&pSectHdr == &file_format->getInit()) return SHO_INIT; @@ -2415,7 +2417,8 @@ void GNULDBackend::placeOutputSections(Module& pModule) config().codeGenType() == LinkerConfig::Object)) wanted = true; break; - case LDFileFormat::Regular: + case LDFileFormat::TEXT: + case LDFileFormat::DATA: case LDFileFormat::Target: case LDFileFormat::MetaData: case LDFileFormat::BSS: @@ -2580,6 +2583,19 @@ void GNULDBackend::createAndSizeEhFrameHdr(Module& pModule) } } +/// mayHaveUnsafeFunctionPointerAccess - check if the section may have unsafe +/// function pointer access +bool GNULDBackend::mayHaveUnsafeFunctionPointerAccess(const LDSection& pSection) + const +{ + llvm::StringRef name(pSection.name()); + return !name.startswith(".rodata._ZTV") && + !name.startswith(".data.rel.ro._ZTV") && + !name.startswith(".rodata._ZTC") && + !name.startswith(".data.rel.ro._ZTC") && + !name.startswith(".eh_frame"); +} + /// preLayout - Backend can do any needed modification before layout void GNULDBackend::preLayout(Module& pModule, IRBuilder& pBuilder) { @@ -3014,7 +3030,8 @@ void GNULDBackend::sortRelocation(LDSection& pSection) bool GNULDBackend::initBRIslandFactory() { if (NULL == m_pBRIslandFactory) { - m_pBRIslandFactory = new BranchIslandFactory(maxBranchOffset()); + m_pBRIslandFactory = new BranchIslandFactory(maxFwdBranchOffset(), + maxBwdBranchOffset()); } return true; } diff --git a/lib/Target/Hexagon/HexagonLDBackend.cpp b/lib/Target/Hexagon/HexagonLDBackend.cpp index 9d16804..eb3458b 100644 --- a/lib/Target/Hexagon/HexagonLDBackend.cpp +++ b/lib/Target/Hexagon/HexagonLDBackend.cpp @@ -72,6 +72,12 @@ bool HexagonLDBackend::initRelocator() return true; } +const Relocator* HexagonLDBackend::getRelocator() const +{ + assert(NULL != m_pRelocator); + return m_pRelocator; +} + Relocator* HexagonLDBackend::getRelocator() { assert(NULL != m_pRelocator); @@ -553,7 +559,9 @@ bool HexagonLDBackend::initTargetStubs() bool HexagonLDBackend::initBRIslandFactory() { if (NULL == m_pBRIslandFactory) { - m_pBRIslandFactory = new BranchIslandFactory(maxBranchOffset(), 0); + m_pBRIslandFactory = new BranchIslandFactory(maxFwdBranchOffset(), + maxBwdBranchOffset(), + 0); } return true; } diff --git a/lib/Target/Hexagon/HexagonLDBackend.h b/lib/Target/Hexagon/HexagonLDBackend.h index 21d2e38..0fc909c 100644 --- a/lib/Target/Hexagon/HexagonLDBackend.h +++ b/lib/Target/Hexagon/HexagonLDBackend.h @@ -83,6 +83,7 @@ public: bool initRelocator(); /// getRelocator - return relocator. + const Relocator* getRelocator() const; Relocator* getRelocator(); ResolveInfo::Desc getSymDesc(uint16_t shndx) const @@ -162,7 +163,8 @@ private: /// target-dependent segments void doCreateProgramHdrs(Module& pModule); - uint64_t maxBranchOffset() { return ~(~0 << 6); } + /// maxFwdBranchOffset + int64_t maxFwdBranchOffset() { return ~(~0 << 6); } virtual void setGOTSectionSize(IRBuilder& pBuilder); diff --git a/lib/Target/Mips/MipsLDBackend.cpp b/lib/Target/Mips/MipsLDBackend.cpp index 46c580d..9714e91 100644 --- a/lib/Target/Mips/MipsLDBackend.cpp +++ b/lib/Target/Mips/MipsLDBackend.cpp @@ -143,6 +143,21 @@ void MipsGNULDBackend::initTargetSymbols(IRBuilder& pBuilder, Module& pModule) 0x0, // value FragmentRef::Null(), // FragRef ResolveInfo::Default); + pBuilder.AddSymbol<IRBuilder::AsReferred, IRBuilder::Unresolve>( + "_gp", + ResolveInfo::NoType, + ResolveInfo::Define, + ResolveInfo::Absolute, + 0x0, // size + 0x0, // value + FragmentRef::Null(), // FragRef + ResolveInfo::Default); +} + +const Relocator* MipsGNULDBackend::getRelocator() const +{ + assert(NULL != m_pRelocator); + return m_pRelocator; } Relocator* MipsGNULDBackend::getRelocator() diff --git a/lib/Target/Mips/MipsLDBackend.h b/lib/Target/Mips/MipsLDBackend.h index 78c5c58..991b3c4 100644 --- a/lib/Target/Mips/MipsLDBackend.h +++ b/lib/Target/Mips/MipsLDBackend.h @@ -47,6 +47,7 @@ public: void initTargetSymbols(IRBuilder& pBuilder, Module& pModule); /// getRelocator - return relocator. + const Relocator* getRelocator() const; Relocator* getRelocator(); /// preLayout - Backend can do any needed modification before layout diff --git a/lib/Target/X86/X86LDBackend.cpp b/lib/Target/X86/X86LDBackend.cpp index f105dd3..eb4c6c6 100644 --- a/lib/Target/X86/X86LDBackend.cpp +++ b/lib/Target/X86/X86LDBackend.cpp @@ -72,6 +72,12 @@ X86GNULDBackend::~X86GNULDBackend() delete m_pDynamic; } +const Relocator* X86GNULDBackend::getRelocator() const +{ + assert(NULL != m_pRelocator); + return m_pRelocator; +} + Relocator* X86GNULDBackend::getRelocator() { assert(NULL != m_pRelocator); diff --git a/lib/Target/X86/X86LDBackend.h b/lib/Target/X86/X86LDBackend.h index 779c2d1..503fca9 100644 --- a/lib/Target/X86/X86LDBackend.h +++ b/lib/Target/X86/X86LDBackend.h @@ -76,6 +76,7 @@ public: virtual bool initRelocator() = 0; /// getRelocator - return relocator. + const Relocator* getRelocator() const; Relocator* getRelocator(); virtual void initTargetSections(Module& pModule, ObjectBuilder& pBuilder) = 0; diff --git a/lib/Target/X86/X86Relocator.cpp b/lib/Target/X86/X86Relocator.cpp index 8cc916e..794296b 100644 --- a/lib/Target/X86/X86Relocator.cpp +++ b/lib/Target/X86/X86Relocator.cpp @@ -323,6 +323,23 @@ Relocator::Size X86_32Relocator::getSize(Relocation::Type pType) const return X86_32ApplyFunctions[pType].size;; } +bool +X86_32Relocator::mayHaveFunctionPointerAccess(const Relocation& pReloc) const +{ + switch (pReloc.type()) { + case llvm::ELF::R_386_32: + case llvm::ELF::R_386_16: + case llvm::ELF::R_386_8: + case llvm::ELF::R_386_GOTOFF: + case llvm::ELF::R_386_GOT32: { + return true; + } + default: { + return false; + } + } +} + void X86_32Relocator::scanLocalReloc(Relocation& pReloc, IRBuilder& pBuilder, Module& pModule, @@ -1297,6 +1314,40 @@ Relocator::Size X86_64Relocator::getSize(Relocation::Type pType) const return X86_64ApplyFunctions[pType].size; } +bool +X86_64Relocator::mayHaveFunctionPointerAccess(const Relocation& pReloc) const +{ + bool possible_funcptr_reloc = false; + switch (pReloc.type()) { + case llvm::ELF::R_X86_64_64: + case llvm::ELF::R_X86_64_32: + case llvm::ELF::R_X86_64_32S: + case llvm::ELF::R_X86_64_16: + case llvm::ELF::R_X86_64_8: + case llvm::ELF::R_X86_64_GOT64: + case llvm::ELF::R_X86_64_GOT32: + case llvm::ELF::R_X86_64_GOTPCREL64: + case llvm::ELF::R_X86_64_GOTPCREL: + case llvm::ELF::R_X86_64_GOTPLT64: { + possible_funcptr_reloc = true; + break; + } + default: { + possible_funcptr_reloc = false; + break; + } + } + + if (pReloc.symInfo()->isGlobal()) { + return (config().codeGenType() == LinkerConfig::DynObj) && + ((pReloc.symInfo()->visibility() != ResolveInfo::Default) || + possible_funcptr_reloc); + } else { + return (config().codeGenType() == LinkerConfig::DynObj) || + possible_funcptr_reloc; + } +} + void X86_64Relocator::scanLocalReloc(Relocation& pReloc, IRBuilder& pBuilder, Module& pModule, diff --git a/lib/Target/X86/X86Relocator.h b/lib/Target/X86/X86Relocator.h index caf259f..84170fe 100644 --- a/lib/Target/X86/X86Relocator.h +++ b/lib/Target/X86/X86Relocator.h @@ -151,6 +151,10 @@ public: X86_32GOTEntry& getTLSModuleID(); + /// mayHaveFunctionPointerAccess - check if the given reloc would possibly + /// access a function pointer. + virtual bool mayHaveFunctionPointerAccess(const Relocation& pReloc) const; + private: void scanLocalReloc(Relocation& pReloc, IRBuilder& pBuilder, @@ -207,6 +211,10 @@ public: const RelRelMap& getRelRelMap() const { return m_RelRelMap; } RelRelMap& getRelRelMap() { return m_RelRelMap; } + /// mayHaveFunctionPointerAccess - check if the given reloc would possibly + /// access a function pointer. + virtual bool mayHaveFunctionPointerAccess(const Relocation& pReloc) const; + private: void scanLocalReloc(Relocation& pReloc, IRBuilder& pBuilder, diff --git a/tools/llvm-mcld/llvm-mcld.cpp b/tools/llvm-mcld/llvm-mcld.cpp deleted file mode 100644 index e984501..0000000 --- a/tools/llvm-mcld/llvm-mcld.cpp +++ /dev/null @@ -1,1552 +0,0 @@ -//===- llvm-mcld.cpp ------------------------------------------------------===// -// -// The MCLinker Project -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -#include <mcld/Module.h> -#include <mcld/LinkerConfig.h> -#include <mcld/LinkerScript.h> -#include <mcld/Target/TargetMachine.h> -#include <mcld/Support/TargetSelect.h> -#include <mcld/Support/TargetRegistry.h> -#include <mcld/Support/CommandLine.h> -#include <mcld/Support/Path.h> -#include <mcld/Support/RealPath.h> -#include <mcld/Support/MsgHandling.h> -#include <mcld/Support/FileHandle.h> -#include <mcld/Support/FileSystem.h> -#include <mcld/Support/raw_ostream.h> -#include <mcld/Support/SystemUtils.h> -#include <mcld/Support/ToolOutputFile.h> -#include <mcld/LD/DiagnosticLineInfo.h> -#include <mcld/LD/TextDiagnosticPrinter.h> - -#include <llvm/PassManager.h> -#include <llvm/Pass.h> -#include <llvm/IR/Module.h> -#include <llvm/IR/DataLayout.h> -#include <llvm/IR/LLVMContext.h> -#include <llvm/ADT/Triple.h> -#include <llvm/ADT/StringSwitch.h> -#include <llvm/MC/SubtargetFeature.h> -#include <llvm/Support/CommandLine.h> -#include <llvm/Support/Debug.h> -#include <llvm/Support/FormattedStream.h> -#include <llvm/Support/Host.h> -#include <llvm/Support/IRReader.h> -#include <llvm/Support/ManagedStatic.h> -#include <llvm/Support/Signals.h> -#include <llvm/Support/TargetRegistry.h> -#include <llvm/Support/TargetSelect.h> -#include <llvm/Support/Process.h> -#include <llvm/Target/TargetMachine.h> - -#if defined(HAVE_UNISTD_H) -# include <unistd.h> -#endif - -#if defined(_MSC_VER) || defined(__MINGW32__) -#include <io.h> -#ifndef STDIN_FILENO -# define STDIN_FILENO 0 -#endif -#ifndef STDOUT_FILENO -# define STDOUT_FILENO 1 -#endif -#ifndef STDERR_FILENO -# define STDERR_FILENO 2 -#endif -#endif - -using namespace llvm; - -#ifdef ENABLE_UNITTEST -#include <gtest.h> - -static cl::opt<bool> -UnitTest("unittest", cl::desc("do unit test") ); - -int unit_test( int argc, char* argv[] ) -{ - testing::InitGoogleTest( &argc, argv ); - return RUN_ALL_TESTS(); -} - -#endif - -// General options for llc. Other pass-specific options are specified -// within the corresponding llc passes, and target-specific options -// and back-end code generation options are specified with the target machine. -// -// Determine optimization level. -static cl::opt<char> -OptLevel("O", - cl::desc("Optimization level. [-O0, -O1, -O2, or -O3] " - "(default = '-O2')"), - cl::Prefix, - cl::ZeroOrMore, - cl::init(' ')); - -static cl::opt<std::string> -TargetTriple("mtriple", cl::desc("Override target triple for module")); - -static cl::opt<std::string> -MArch("march", cl::desc("Architecture to generate code for (see --version)")); - -static cl::opt<std::string> -MCPU("mcpu", - cl::desc("Target a specific cpu type (-mcpu=help for details)"), - cl::value_desc("cpu-name"), - cl::init("")); - -static cl::list<std::string> -MAttrs("mattr", - cl::CommaSeparated, - cl::desc("Target specific attributes (-mattr=help for details)"), - cl::value_desc("a1,+a2,-a3,...")); - -static cl::opt<llvm::CodeModel::Model> -CMModel("code-model", - cl::desc("Choose code model"), - cl::init(CodeModel::Default), - cl::values(clEnumValN(CodeModel::Default, "default", - "Target default code model"), - clEnumValN(CodeModel::Small, "small", - "Small code model"), - clEnumValN(CodeModel::Kernel, "kernel", - "Kernel code model"), - clEnumValN(CodeModel::Medium, "medium", - "Medium code model"), - clEnumValN(CodeModel::Large, "large", - "Large code model"), - clEnumValEnd)); - -cl::opt<bool> NoVerify("disable-verify", cl::Hidden, - cl::desc("Do not verify input module")); - -static cl::opt<bool> -EnableFPMAD("enable-fp-mad", - cl::desc("Enable less precise MAD instructions to be generated"), - cl::init(false)); - -static cl::opt<bool> -DisableFPElim("disable-fp-elim", - cl::desc("Disable frame pointer elimination optimization"), - cl::init(false)); - -static cl::opt<bool> -DisableFPElimNonLeaf("disable-non-leaf-fp-elim", - cl::desc("Disable frame pointer elimination optimization for non-leaf funcs"), - cl::init(false)); - -static cl::opt<llvm::FPOpFusion::FPOpFusionMode> -FuseFPOps("fuse-fp-ops", - cl::desc("Enable aggresive formation of fused FP ops"), - cl::init(FPOpFusion::Standard), - cl::values( - clEnumValN(FPOpFusion::Fast, "fast", - "Fuse FP ops whenever profitable"), - clEnumValN(FPOpFusion::Standard, "standard", - "Only fuse 'blessed' FP ops."), - clEnumValN(FPOpFusion::Strict, "strict", - "Only fuse FP ops when the result won't be effected."), - clEnumValEnd)); - -static cl::opt<bool> -EnableUnsafeFPMath("enable-unsafe-fp-math", - cl::desc("Enable optimizations that may decrease FP precision"), - cl::init(false)); - -static cl::opt<bool> -EnableNoInfsFPMath("enable-no-infs-fp-math", - cl::desc("Enable FP math optimizations that assume no +-Infs"), - cl::init(false)); - -static cl::opt<bool> -EnableNoNaNsFPMath("enable-no-nans-fp-math", - cl::desc("Enable FP math optimizations that assume no NaNs"), - cl::init(false)); - -static cl::opt<bool> -EnableHonorSignDependentRoundingFPMath("enable-sign-dependent-rounding-fp-math", - cl::Hidden, - cl::desc("Force codegen to assume rounding mode can change dynamically"), - cl::init(false)); - -static cl::opt<bool> -GenerateSoftFloatCalls("soft-float", - cl::desc("Generate software floating point library calls"), - cl::init(false)); - -static cl::opt<llvm::FloatABI::ABIType> -FloatABIForCalls("float-abi", - cl::desc("Choose float ABI type"), - cl::init(FloatABI::Default), - cl::values( - clEnumValN(FloatABI::Default, "default", - "Target default float ABI type"), - clEnumValN(FloatABI::Soft, "soft", - "Soft float ABI (implied by -soft-float)"), - clEnumValN(FloatABI::Hard, "hard", - "Hard float ABI (uses FP registers)"), - clEnumValEnd)); - -static cl::opt<bool> -DontPlaceZerosInBSS("nozero-initialized-in-bss", - cl::desc("Don't place zero-initialized symbols into bss section"), - cl::init(false)); - -static cl::opt<bool> -EnableJITExceptionHandling("jit-enable-eh", - cl::desc("Emit exception handling information"), - cl::init(false)); - -// In debug builds, make this default to true. -#ifdef NDEBUG -#define EMIT_DEBUG false -#else -#define EMIT_DEBUG true -#endif -static cl::opt<bool> -EmitJitDebugInfo("jit-emit-debug", - cl::desc("Emit debug information to debugger"), - cl::init(EMIT_DEBUG)); -#undef EMIT_DEBUG - -static cl::opt<bool> -EmitJitDebugInfoToDisk("jit-emit-debug-to-disk", - cl::Hidden, - cl::desc("Emit debug info objfiles to disk"), - cl::init(false)); - -static cl::opt<bool> -EnableGuaranteedTailCallOpt("tailcallopt", - cl::desc("Turn fastcc calls into tail calls by (potentially) changing ABI."), - cl::init(false)); - -static cl::opt<unsigned> -OverrideStackAlignment("stack-alignment", - cl::desc("Override default stack alignment"), - cl::init(0)); - -static cl::opt<bool> -EnableRealignStack("realign-stack", - cl::desc("Realign stack if needed"), - cl::init(true)); - -static cl::opt<std::string> -TrapFuncName("trap-func", cl::Hidden, - cl::desc("Emit a call to trap function rather than a trap instruction"), - cl::init("")); - -static cl::opt<bool> -SegmentedStacks("segmented-stacks", - cl::desc("Use segmented stacks if possible."), - cl::init(false)); - -//===----------------------------------------------------------------------===// -// Command Line Options -// There are four kinds of command line options: -// 1. Bitcode option. Used to represent a bitcode. -// 2. Attribute options. Attributes describes the input file after them. For -// example, --as-needed affects the input file after this option. Attribute -// options are not attributes. Attribute options are the options that is -// used to define a legal attribute. -// 3. Scripting options, Used to represent a subset of link scripting -// language, such as --defsym. -// 4. General options. (the rest of options) -//===----------------------------------------------------------------------===// -// Bitcode Options -//===----------------------------------------------------------------------===// -static cl::opt<mcld::sys::fs::Path, false, llvm::cl::parser<mcld::sys::fs::Path> > -ArgBitcodeFilename("dB", - cl::desc("set default bitcode"), - cl::value_desc("bitcode")); - -//===----------------------------------------------------------------------===// -// General Options -//===----------------------------------------------------------------------===// -static cl::opt<mcld::sys::fs::Path, false, llvm::cl::parser<mcld::sys::fs::Path> > -ArgOutputFilename("o", - cl::desc("Output filename"), - cl::value_desc("filename")); - -static cl::alias -AliasOutputFilename("output", - cl::desc("alias for -o"), - cl::aliasopt(ArgOutputFilename)); - -static cl::opt<mcld::sys::fs::Path, false, llvm::cl::parser<mcld::sys::fs::Path> > -ArgSysRoot("sysroot", - cl::desc("Use directory as the location of the sysroot, overriding the configure-time default."), - cl::value_desc("directory"), - cl::ValueRequired); - -static cl::list<std::string, bool, llvm::cl::SearchDirParser> -ArgSearchDirList("L", - cl::ZeroOrMore, - cl::desc("Add path searchdir to the list of paths that ld will search for archive libraries and ld control scripts."), - cl::value_desc("searchdir"), - cl::Prefix); - -static cl::alias -ArgSearchDirListAlias("library-path", - cl::desc("alias for -L"), - cl::aliasopt(ArgSearchDirList)); - -static cl::opt<bool> -ArgTrace("t", - cl::desc("Print the names of the input files as ld processes them.")); - -static cl::alias -ArgTraceAlias("trace", - cl::desc("alias for -t"), - cl::aliasopt(ArgTrace)); - -static cl::opt<int> -ArgVerbose("verbose", - cl::init(-1), - cl::desc("Display the version number for ld and list the linker emulations supported.")); - -static cl::opt<bool> -ArgVersion("V", - cl::init(false), - cl::desc("Display the version number for MCLinker.")); - -static cl::opt<int> -ArgMaxErrorNum("error-limit", - cl::init(-1), - cl::desc("limits the maximum number of erros.")); - -static cl::opt<int> -ArgMaxWarnNum("warning-limit", - cl::init(-1), - cl::desc("limits the maximum number of warnings.")); - -static cl::opt<std::string> -ArgEntry("e", - cl::desc("Use entry as the explicit symbol for beginning execution of your program."), - cl::value_desc("entry"), - cl::ValueRequired); - -static cl::alias -ArgEntryAlias("entry", - cl::desc("alias for -e"), - cl::aliasopt(ArgEntry)); - -static cl::opt<bool> -ArgBsymbolic("Bsymbolic", - cl::desc("Bind references within the shared library."), - cl::init(false)); - -static cl::opt<bool> -ArgBgroup("Bgroup", - cl::desc("Info the dynamic linker to perform lookups only inside the group."), - cl::init(false)); - -static cl::opt<std::string> -ArgSOName("soname", - cl::desc("Set internal name of shared library"), - cl::value_desc("name")); - -static cl::opt<bool> -ArgNoUndefined("no-undefined", - cl::desc("Do not allow unresolved references"), - cl::init(false)); - -static cl::opt<bool> -ArgAllowMulDefs("allow-multiple-definition", - cl::desc("Allow multiple definition"), - cl::init(false)); - -static cl::opt<bool> -ArgEhFrameHdr("eh-frame-hdr", - cl::desc("Request creation of \".eh_frame_hdr\" section and ELF \"PT_GNU_EH_FRAME\" segment header."), - cl::init(false)); - -static cl::list<mcld::ZOption, bool, llvm::cl::parser<mcld::ZOption> > -ArgZOptionList("z", - cl::ZeroOrMore, - cl::desc("The -z options for GNU ld compatibility."), - cl::value_desc("keyword"), - cl::Prefix); - -cl::opt<mcld::CodeGenFileType> -ArgFileType("filetype", cl::init(mcld::CGFT_EXEFile), - cl::desc("Choose a file type (not all types are supported by all targets):"), - cl::values( - clEnumValN(mcld::CGFT_ASMFile, "asm", - "Emit an assembly ('.s') file"), - clEnumValN(mcld::CGFT_OBJFile, "obj", - "Emit a relocatable object ('.o') file"), - clEnumValN(mcld::CGFT_DSOFile, "dso", - "Emit an dynamic shared object ('.so') file"), - clEnumValN(mcld::CGFT_EXEFile, "exe", - "Emit a executable ('.exe') file"), - clEnumValN(mcld::CGFT_NULLFile, "null", - "Emit nothing, for performance testing"), - clEnumValEnd)); - -static cl::opt<bool> -ArgShared("shared", - cl::desc("Create a shared library."), - cl::init(false)); - -static cl::alias -ArgSharedAlias("Bshareable", - cl::desc("alias for -shared"), - cl::aliasopt(ArgShared)); - -static cl::opt<bool> -ArgPIE("pie", - cl::desc("Emit a position-independent executable file"), - cl::init(false)); - -static cl::opt<bool> -ArgRelocatable("relocatable", - cl::desc("Generate relocatable output"), - cl::init(false)); - -static cl::alias -ArgRelocatableAlias("r", - cl::desc("alias for --relocatable"), - cl::aliasopt(ArgRelocatable)); - -static cl::opt<Reloc::Model> -ArgRelocModel("relocation-model", - cl::desc("Choose relocation model"), - cl::init(Reloc::Default), - cl::values( - clEnumValN(Reloc::Default, "default", - "Target default relocation model"), - clEnumValN(Reloc::Static, "static", - "Non-relocatable code"), - clEnumValN(Reloc::PIC_, "pic", - "Fully relocatable, position independent code"), - clEnumValN(Reloc::DynamicNoPIC, "dynamic-no-pic", - "Relocatable external references, non-relocatable code"), - clEnumValEnd)); - -static cl::opt<bool> -ArgFPIC("fPIC", - cl::desc("Set relocation model to pic. The same as -relocation-model=pic."), - cl::init(false)); - -static cl::opt<std::string> -ArgDyld("dynamic-linker", - cl::ZeroOrMore, - cl::desc("Set the name of the dynamic linker."), - cl::value_desc("Program")); - -namespace color { -enum Color { - Never, - Always, - Auto -}; -} // namespace of color - -static cl::opt<color::Color> -ArgColor("color", - cl::value_desc("WHEN"), - cl::desc("Surround the result strings with the marker"), - cl::init(color::Auto), - cl::values( - clEnumValN(color::Never, "never", - "do not surround result strings"), - clEnumValN(color::Always, "always", - "always surround result strings, even the output is a plain file"), - clEnumValN(color::Auto, "auto", - "surround result strings only if the output is a tty"), - clEnumValEnd)); - -static cl::opt<bool> -ArgDiscardLocals("discard-locals", - cl::desc("Delete all temporary local symbols."), - cl::init(false)); - -static cl::alias -ArgDiscardLocalsAlias("X", - cl::desc("alias for --discard-locals"), - cl::aliasopt(ArgDiscardLocals)); - -static cl::opt<bool> -ArgDiscardAll("discard-all", - cl::desc("Delete all local symbols."), - cl::init(false)); - -static cl::alias -ArgDiscardAllAlias("x", - cl::desc("alias for --discard-all"), - cl::aliasopt(ArgDiscardAll)); - -static cl::opt<bool> -ArgStripDebug("strip-debug", - cl::desc("Omit debugger symbol information from the output file."), - cl::init(false)); - -static cl::alias -ArgStripDebugAlias("S", - cl::desc("alias for --strip-debug"), - cl::aliasopt(ArgStripDebug)); - -static cl::opt<bool> -ArgStripAll("strip-all", - cl::desc("Omit all symbol information from the output file."), - cl::init(false)); - -static cl::alias -ArgStripAllAlias("s", - cl::desc("alias for --strip-all"), - cl::aliasopt(ArgStripAll)); - -static cl::opt<bool> -ArgNMagic("nmagic", - cl::desc("Do not page align data"), - cl::init(false)); - -static cl::alias -ArgNMagicAlias("n", - cl::desc("alias for --nmagic"), - cl::aliasopt(ArgNMagic)); - -static cl::opt<bool> -ArgOMagic("omagic", - cl::desc("Do not page align data, do not make text readonly"), - cl::init(false)); - -static cl::alias -ArgOMagicAlias("N", - cl::desc("alias for --omagic"), - cl::aliasopt(ArgOMagic)); - - -static cl::opt<int> -ArgGPSize("G", - cl::desc("Set the maximum size of objects to be optimized using GP"), - cl::init(8)); - -/// @{ -/// @name FIXME: begin of unsupported options -/// @} -static cl::opt<bool> -ArgGCSections("gc-sections", - cl::desc("Enable garbage collection of unused input sections."), - cl::init(false)); - -static cl::opt<bool> -ArgNoGCSections("no-gc-sections", - cl::desc("disable garbage collection of unused input sections."), - cl::init(false)); - -namespace icf { -enum Mode { - None, - All, - Safe -}; -} // namespace of icf - -static cl::opt<icf::Mode> -ArgICF("icf", - cl::ZeroOrMore, - cl::desc("Identical Code Folding"), - cl::init(icf::None), - cl::values( - clEnumValN(icf::None, "none", - "do not perform cold folding"), - clEnumValN(icf::All, "all", - "always preform cold folding"), - clEnumValN(icf::Safe, "safe", - "Folds ctors, dtors and functions whose pointers are definitely not taken."), - clEnumValEnd)); - -// FIXME: add this to target options? -static cl::opt<bool> -ArgFIXCA8("fix-cortex-a8", - cl::desc("Enable Cortex-A8 Thumb-2 branch erratum fix"), - cl::init(false)); - -static cl::opt<bool> -ArgExportDynamic("export-dynamic", - cl::desc("Export all dynamic symbols"), - cl::init(false)); - -static cl::alias -ArgExportDynamicAlias("E", - cl::desc("alias for --export-dynamic"), - cl::aliasopt(ArgExportDynamic)); - -static cl::opt<std::string> -ArgEmulation("m", - cl::ZeroOrMore, - cl::desc("Set GNU linker emulation"), - cl::value_desc("emulation")); - -static cl::list<std::string, bool, llvm::cl::SearchDirParser> -ArgRuntimePathLink("rpath-link", - cl::ZeroOrMore, - cl::desc("Add a directory to the link time library search path"), - cl::value_desc("dir")); - -static cl::list<std::string> -ArgExcludeLIBS("exclude-libs", - cl::CommaSeparated, - cl::desc("Exclude libraries from automatic export"), - cl::value_desc("lib1,lib2,...")); - -static cl::opt<std::string> -ArgBuildID("build-id", - cl::desc("Request creation of \".note.gnu.build-id\" ELF note section."), - cl::value_desc("style"), - cl::ValueOptional); - -static cl::opt<std::string> -ArgForceUndefined("u", - cl::desc("Force symbol to be undefined in the output file"), - cl::value_desc("symbol")); - -static cl::alias -ArgForceUndefinedAlias("undefined", - cl::desc("alias for -u"), - cl::aliasopt(ArgForceUndefined)); - -static cl::opt<std::string> -ArgVersionScript("version-script", - cl::desc("Version script."), - cl::value_desc("Version script")); - -static cl::opt<bool> -ArgWarnCommon("warn-common", - cl::desc("warn common symbol"), - cl::init(false)); - -static cl::opt<mcld::GeneralOptions::HashStyle> -ArgHashStyle("hash-style", cl::init(mcld::GeneralOptions::SystemV), - cl::desc("Set the type of linker's hash table(s)."), - cl::values( - clEnumValN(mcld::GeneralOptions::SystemV, "sysv", - "classic ELF .hash section"), - clEnumValN(mcld::GeneralOptions::GNU, "gnu", - "new style GNU .gnu.hash section"), - clEnumValN(mcld::GeneralOptions::Both, "both", - "both the classic ELF and new style GNU hash tables"), - clEnumValEnd)); - -static cl::opt<std::string> -ArgFilter("F", - cl::desc("Filter for shared object symbol table"), - cl::value_desc("name")); - -static cl::alias -ArgFilterAlias("filter", - cl::desc("alias for -F"), - cl::aliasopt(ArgFilterAlias)); - -static cl::list<std::string> -ArgAuxiliary("f", - cl::ZeroOrMore, - cl::desc("Auxiliary filter for shared object symbol table"), - cl::value_desc("name")); - -static cl::alias -ArgAuxiliaryAlias("auxiliary", - cl::desc("alias for -f"), - cl::aliasopt(ArgAuxiliary)); - -static cl::opt<bool> -ArgUseGold("use-gold", - cl::desc("GCC/collect2 compatibility: uses ld.gold. Ignored"), - cl::init(false)); - -static cl::opt<bool> -ArgUseMCLD("use-mcld", - cl::desc("GCC/collect2 compatibility: uses ld.mcld. Ignored"), - cl::init(false)); - -static cl::opt<bool> -ArgUseLD("use-ld", - cl::desc("GCC/collect2 compatibility: uses ld.bfd. Ignored"), - cl::init(false)); - -static cl::opt<bool> -ArgEB("EB", - cl::desc("Link big-endian objects. This affects the default output format."), - cl::init(false)); - -static cl::opt<bool> -ArgEL("EL", - cl::desc("Link little-endian objects. This affects the default output format."), - cl::init(false)); - -static cl::list<std::string> -ArgPlugin("plugin", - cl::desc("Load a plugin library."), - cl::value_desc("plugin")); - -static cl::list<std::string> -ArgPluginOpt("plugin-opt", - cl::desc(" Pass an option to the plugin."), - cl::value_desc("option")); - -static cl::opt<bool> -ArgSVR4Compatibility("Qy", - cl::desc("This option is ignored for SVR4 compatibility"), - cl::init(false)); - -static cl::list<std::string> -ArgY("Y", - cl::desc("Add path to the default library search path"), - cl::value_desc("default-search-path")); - -/// @{ -/// @name FIXME: end of unsupported options -/// @} - -static cl::opt<bool> -ArgNoStdlib("nostdlib", - cl::desc("Only search lib dirs explicitly specified on cmdline"), - cl::init(false)); - -static cl::list<std::string, bool, llvm::cl::SearchDirParser> -ArgRuntimePath("rpath", - cl::ZeroOrMore, - cl::desc("Add a directory to the runtime library search path"), - cl::value_desc("dir")); - -static cl::alias -ArgRuntimePathAlias("R", - cl::desc("alias for --rpath"), - cl::aliasopt(ArgRuntimePath), cl::Prefix); - -static cl::opt<bool> -ArgEnableNewDTags("enable-new-dtags", - cl::desc("Enable use of DT_RUNPATH and DT_FLAGS"), - cl::init(false)); - -static cl::opt<bool> -ArgPrintMap("M", - cl::desc("Print a link map to the standard output."), - cl::init(false)); - -static cl::alias -ArgPrintMapAlias("print-map", - cl::desc("alias for -M"), - cl::aliasopt(ArgPrintMap)); - -static bool ArgFatalWarnings; - -static cl::opt<bool, true, cl::FalseParser> -ArgNoFatalWarnings("no-fatal-warnings", - cl::location(ArgFatalWarnings), - cl::desc("do not turn warnings into errors"), - cl::init(false), - cl::ValueDisallowed); - -static cl::opt<bool, true> -ArgFatalWarningsFlag("fatal-warnings", - cl::location(ArgFatalWarnings), - cl::desc("turn all warnings into errors"), - cl::init(false), - cl::ValueDisallowed); - -static cl::opt<bool> -ArgWarnSharedTextrel("warn-shared-textrel", - cl::desc("Warn if adding DT_TEXTREL in a shared object."), - cl::init(false)); - -namespace format { -enum Format { - Binary, - Unknown // decided by triple -}; -} // namespace of format - -static cl::opt<format::Format> -ArgFormat("b", - cl::value_desc("Format"), - cl::desc("set input format"), - cl::init(format::Unknown), - cl::values( - clEnumValN(format::Binary, "binary", - "read in binary machine code."), - clEnumValEnd)); - -static cl::alias -ArgFormatAlias("format", - cl::desc("alias for -b"), - cl::aliasopt(ArgFormat)); - -static cl::opt<format::Format> -ArgOFormat("oformat", - cl::value_desc("Format"), - cl::desc("set output format"), - cl::init(format::Unknown), - cl::values( - clEnumValN(format::Binary, "binary", - "generate binary machine code."), - clEnumValEnd)); - -static cl::opt<bool> -ArgDefineCommon("d", - cl::ZeroOrMore, - cl::desc("Define common symbol"), - cl::init(false)); - -static cl::alias -ArgDefineCommonAlias1("dc", - cl::ZeroOrMore, - cl::desc("alias for -d"), - cl::aliasopt(ArgDefineCommon)); - -static cl::alias -ArgDefineCommonAlias2("dp", - cl::ZeroOrMore, - cl::desc("alias for -d"), - cl::aliasopt(ArgDefineCommon)); - -//===----------------------------------------------------------------------===// -// Scripting Options -//===----------------------------------------------------------------------===// -static cl::list<std::string> -ArgWrapList("wrap", - cl::ZeroOrMore, - cl::desc("Use a wrap function fo symbol."), - cl::value_desc("symbol")); - -static cl::list<std::string> -ArgPortList("portable", - cl::ZeroOrMore, - cl::desc("Use a portable function fo symbol."), - cl::value_desc("symbol")); - -static cl::list<std::string> -ArgAddressMapList("section-start", - cl::ZeroOrMore, - cl::desc("Locate a output section at the given absolute address"), - cl::value_desc("Set address of section"), - cl::Prefix); - -static cl::list<std::string> -ArgDefSymList("defsym", - cl::ZeroOrMore, - cl::desc("Define a symbol"), - cl::value_desc("symbol=expression")); - -static cl::opt<unsigned long long> -ArgBssSegAddr("Tbss", - cl::desc("Set the address of the bss segment"), - cl::init(-1U)); - -static cl::opt<unsigned long long> -ArgDataSegAddr("Tdata", - cl::desc("Set the address of the data segment"), - cl::init(-1U)); - -static cl::opt<unsigned long long> -ArgTextSegAddr("Ttext", - cl::desc("Set the address of the text segment"), - cl::init(-1U)); - -//===----------------------------------------------------------------------===// -// non-member functions -//===----------------------------------------------------------------------===// -/// GetOutputStream - get the output stream. -static mcld::ToolOutputFile *GetOutputStream(const char* pTargetName, - Triple::OSType pOSType, - mcld::CodeGenFileType pFileType, - const mcld::sys::fs::Path& pInputFilename, - mcld::sys::fs::Path& pOutputFilename) -{ - if (pOutputFilename.empty()) { - if (0 == pInputFilename.native().compare("-")) - pOutputFilename.assign("-"); - else { - switch(pFileType) { - case mcld::CGFT_ASMFile: { - if (0 == pInputFilename.native().compare("-")) - pOutputFilename.assign("_out"); - else - pOutputFilename.assign(pInputFilename.stem().native()); - - if (0 == strcmp(pTargetName, "c")) - pOutputFilename.native() += ".cbe.c"; - else if (0 == strcmp(pTargetName, "cpp")) - pOutputFilename.native() += ".cpp"; - else - pOutputFilename.native() += ".s"; - } - break; - - case mcld::CGFT_OBJFile: { - if (0 == pInputFilename.native().compare("-")) - pOutputFilename.assign("_out"); - else - pOutputFilename.assign(pInputFilename.stem().native()); - - if (pOSType == Triple::Win32) - pOutputFilename.native() += ".obj"; - else - pOutputFilename.native() += ".o"; - } - break; - - case mcld::CGFT_PARTIAL: { - if (Triple::Win32 == pOSType) { - if (0 == pInputFilename.native().compare("-")) - pOutputFilename.assign("_out"); - else - pOutputFilename.assign(pInputFilename.stem().native()); - pOutputFilename.native() += ".obj"; - } - else - pOutputFilename.assign("a.out"); - } - break; - - case mcld::CGFT_DSOFile: { - if (Triple::Win32 == pOSType) { - if (0 == pInputFilename.native().compare("-")) - pOutputFilename.assign("_out"); - else - pOutputFilename.assign(pInputFilename.stem().native()); - pOutputFilename.native() += ".dll"; - } - else - pOutputFilename.assign("a.out"); - } - break; - - case mcld::CGFT_EXEFile: { - if (Triple::Win32 == pOSType) { - if (0 == pInputFilename.native().compare("-")) - pOutputFilename.assign("_out"); - else - pOutputFilename.assign(pInputFilename.stem().native()); - pOutputFilename.native() += ".exe"; - } - else - pOutputFilename.assign("a.out"); - } - break; - - case mcld::CGFT_NULLFile: - break; - default: - llvm::report_fatal_error("Unknown output file type.\n"); - } // end of switch - } // end of ! pInputFilename == "-" - } // end of if empty pOutputFilename - - mcld::FileHandle::Permission permission; - switch (pFileType) { - default: assert(0 && "Unknown file type"); - case mcld::CGFT_ASMFile: - case mcld::CGFT_OBJFile: - case mcld::CGFT_PARTIAL: - permission = mcld::FileHandle::Permission(0x644); - break; - case mcld::CGFT_DSOFile: - case mcld::CGFT_EXEFile: - case mcld::CGFT_BINARY: - case mcld::CGFT_NULLFile: - permission = mcld::FileHandle::Permission(0x755); - break; - } - - // Open the file. - mcld::ToolOutputFile* result_output = - new mcld::ToolOutputFile(pOutputFilename, - mcld::FileHandle::ReadWrite | - mcld::FileHandle::Create | - mcld::FileHandle::Truncate, - permission); - - return result_output; -} - -/// ParseProgName - Parse program name -/// This function simplifies cross-compiling by reading triple from the program -/// name. For example, if the program name is `arm-linux-eabi-ld.mcld', we can -/// get the triple is arm-linux-eabi by the program name. -static std::string ParseProgName(const char *progname) -{ - static const char *suffixes[] = { - "ld", - "ld.mcld", - }; - - std::string ProgName(mcld::sys::fs::Path(progname).stem().native()); - - for (size_t i = 0; i < sizeof(suffixes) / sizeof(suffixes[0]); ++i) { - if (ProgName == suffixes[i]) - return std::string(); - } - - StringRef ProgNameRef(ProgName); - StringRef Prefix; - - for (size_t i = 0; i < sizeof(suffixes) / sizeof(suffixes[0]); ++i) { - if (!ProgNameRef.endswith(suffixes[i])) - continue; - - StringRef::size_type LastComponent = ProgNameRef.rfind('-', - ProgNameRef.size() - strlen(suffixes[i])); - if (LastComponent == StringRef::npos) - continue; - StringRef Prefix = ProgNameRef.slice(0, LastComponent); - std::string IgnoredError; - if (!llvm::TargetRegistry::lookupTarget(Prefix, IgnoredError)) - continue; - return Prefix.str(); - } - return std::string(); -} - -static Triple ParseEmulation(const std::string& pEmulation) -{ - Triple result = StringSwitch<Triple>(pEmulation) - .Case("armelf_linux_eabi", Triple("arm", "", "linux", "gnueabi")) - .Case("elf_i386", Triple("i386", "", "", "gnu")) - .Case("elf_x86_64", Triple("x86_64", "", "", "gnu")) - .Case("elf32_x86_64", Triple("x86_64", "", "", "gnux32")) - .Case("elf_i386_fbsd", Triple("i386", "", "freebsd", "gnu")) - .Case("elf_x86_64_fbsd", Triple("x86_64", "", "freebsd", "gnu")) - .Case("elf32ltsmip", Triple("mipsel", "", "", "gnu")) - .Default(Triple()); - - if (result.getArch() == Triple::UnknownArch && - result.getOS() == Triple::UnknownOS && - result.getEnvironment() == Triple::UnknownEnvironment) - mcld::error(mcld::diag::err_invalid_emulation) << pEmulation << "\n"; - - return result; -} - -static bool ShouldColorize() -{ - const char* term = getenv("TERM"); - return term && (0 != strcmp(term, "dumb")); -} - -static bool ProcessLinkerOptionsFromCommand(mcld::LinkerScript& pScript, - mcld::LinkerConfig& pConfig) -{ - // ----- Set up General Options ----- // - // set up colorize - switch (ArgColor) { - case color::Never: - pConfig.options().setColor(false); - break; - case color::Always: - pConfig.options().setColor(true); - break; - case color::Auto: - bool color_option = ShouldColorize() && - llvm::sys::Process::FileDescriptorIsDisplayed(STDOUT_FILENO); - pConfig.options().setColor(color_option); - break; - } - - mcld::outs().setColor(pConfig.options().color()); - mcld::errs().setColor(pConfig.options().color()); - - // set up soname - pConfig.options().setSOName(ArgSOName); - - // add all rpath entries - cl::list<std::string>::iterator rp; - cl::list<std::string>::iterator rpEnd = ArgRuntimePath.end(); - for (rp = ArgRuntimePath.begin(); rp != rpEnd; ++rp) { - pConfig.options().getRpathList().push_back(*rp); - } - - // --fatal-warnings - // pConfig.options().setFatalWarnings(ArgFatalWarnings); - - // -shared or -pie - if (true == ArgShared || true == ArgPIE) { - ArgFileType = mcld::CGFT_DSOFile; - } - else if (true == ArgRelocatable) { - ArgFileType = mcld::CGFT_PARTIAL; - } - else if (format::Binary == ArgOFormat) { - ArgFileType = mcld::CGFT_BINARY; - } - - // -b [input-format], --format=[input-format] - if (format::Binary == ArgFormat) - pConfig.options().setBinaryInput(); - - // -V - if (ArgVersion) { - mcld::outs() << "MCLinker - " - << mcld::LinkerConfig::version() - << "\n"; - } - - // set up sysroot - if (!ArgSysRoot.empty()) { - if (exists(ArgSysRoot) && is_directory(ArgSysRoot)) - pScript.setSysroot(ArgSysRoot); - } - - // add all search directories - cl::list<std::string>::iterator sd; - cl::list<std::string>::iterator sdEnd = ArgSearchDirList.end(); - for (sd=ArgSearchDirList.begin(); sd!=sdEnd; ++sd) { - if (!pScript.directories().insert(*sd)) { - // FIXME: need a warning function - errs() << "WARNING: can not open search directory `-L" - << *sd - << "'.\n"; - } - } - - pConfig.options().setPIE(ArgPIE); - pConfig.options().setTrace(ArgTrace); - pConfig.options().setVerbose(ArgVerbose); - pConfig.options().setMaxErrorNum(ArgMaxErrorNum); - pConfig.options().setMaxWarnNum(ArgMaxWarnNum); - pConfig.options().setEntry(ArgEntry); - pConfig.options().setBsymbolic(ArgBsymbolic); - pConfig.options().setBgroup(ArgBgroup); - pConfig.options().setDyld(ArgDyld); - pConfig.options().setNoUndefined(ArgNoUndefined); - pConfig.options().setMulDefs(ArgAllowMulDefs); - pConfig.options().setEhFrameHdr(ArgEhFrameHdr); - pConfig.options().setNMagic(ArgNMagic); - pConfig.options().setOMagic(ArgOMagic); - pConfig.options().setStripDebug(ArgStripDebug || ArgStripAll); - pConfig.options().setExportDynamic(ArgExportDynamic); - pConfig.options().setWarnSharedTextrel(ArgWarnSharedTextrel); - pConfig.options().setDefineCommon(ArgDefineCommon); - pConfig.options().setNewDTags(ArgEnableNewDTags); - pConfig.options().setHashStyle(ArgHashStyle); - pConfig.options().setNoStdlib(ArgNoStdlib); - pConfig.options().setPrintMap(ArgPrintMap); - pConfig.options().setGPSize(ArgGPSize); - - if (ArgStripAll) - pConfig.options().setStripSymbols(mcld::GeneralOptions::StripAllSymbols); - else if (ArgDiscardAll) - pConfig.options().setStripSymbols(mcld::GeneralOptions::StripLocals); - else if (ArgDiscardLocals) - pConfig.options().setStripSymbols(mcld::GeneralOptions::StripTemporaries); - else - pConfig.options().setStripSymbols(mcld::GeneralOptions::KeepAllSymbols); - - // set up rename map, for --wrap - cl::list<std::string>::iterator wname; - cl::list<std::string>::iterator wnameEnd = ArgWrapList.end(); - for (wname = ArgWrapList.begin(); wname != wnameEnd; ++wname) { - bool exist = false; - - // add wname -> __wrap_wname - mcld::StringEntry<llvm::StringRef>* to_wrap = - pScript.renameMap().insert(*wname, exist); - - std::string to_wrap_str = "__wrap_" + *wname; - to_wrap->setValue(to_wrap_str); - - if (exist) - mcld::warning(mcld::diag::rewrap) << *wname << to_wrap_str; - - // add __real_wname -> wname - std::string from_real_str = "__real_" + *wname; - mcld::StringEntry<llvm::StringRef>* from_real = - pScript.renameMap().insert(from_real_str, exist); - from_real->setValue(*wname); - if (exist) - mcld::warning(mcld::diag::rewrap) << *wname << from_real_str; - } // end of for - - // set up rename map, for --portable - cl::list<std::string>::iterator pname; - cl::list<std::string>::iterator pnameEnd = ArgPortList.end(); - for (pname = ArgPortList.begin(); pname != pnameEnd; ++pname) { - bool exist = false; - - // add pname -> pname_portable - mcld::StringEntry<llvm::StringRef>* to_port = - pScript.renameMap().insert(*pname, exist); - - std::string to_port_str = *pname + "_portable"; - to_port->setValue(to_port_str); - - if (exist) - mcld::warning(mcld::diag::rewrap) << *pname << to_port_str; - - // add __real_pname -> pname - std::string from_real_str = "__real_" + *pname; - mcld::StringEntry<llvm::StringRef>* from_real = - pScript.renameMap().insert(from_real_str, exist); - - from_real->setValue(*pname); - if (exist) - mcld::warning(mcld::diag::rewrap) << *pname << from_real_str; - } // end of for - - // add -z options - cl::list<mcld::ZOption>::iterator zOpt; - cl::list<mcld::ZOption>::iterator zOptEnd = ArgZOptionList.end(); - for (zOpt = ArgZOptionList.begin(); zOpt != zOptEnd; ++zOpt) { - pConfig.options().addZOption(*zOpt); - } - - if (ArgGCSections) { - mcld::warning(mcld::diag::warn_unsupported_option) << ArgGCSections.ArgStr; - } - - // set up icf mode - switch (ArgICF) { - case icf::None: - break; - case icf::All: - case icf::Safe: - default: - mcld::warning(mcld::diag::warn_unsupported_option) << ArgICF.ArgStr; - break; - } - - if (ArgFIXCA8) { - mcld::warning(mcld::diag::warn_unsupported_option) << ArgFIXCA8.ArgStr; - } - - // add address mappings - // -Ttext - if (-1U != ArgTextSegAddr) { - bool exist = false; - mcld::StringEntry<uint64_t>* text_mapping = - pScript.addressMap().insert(".text", exist); - text_mapping->setValue(ArgTextSegAddr); - } - // -Tdata - if (-1U != ArgDataSegAddr) { - bool exist = false; - mcld::StringEntry<uint64_t>* data_mapping = - pScript.addressMap().insert(".data", exist); - data_mapping->setValue(ArgDataSegAddr); - } - // -Tbss - if (-1U != ArgBssSegAddr) { - bool exist = false; - mcld::StringEntry<uint64_t>* bss_mapping = - pScript.addressMap().insert(".bss", exist); - bss_mapping->setValue(ArgBssSegAddr); - } - // --section-start SECTION=ADDRESS - for (cl::list<std::string>::iterator - it = ArgAddressMapList.begin(), ie = ArgAddressMapList.end(); - it != ie; ++it) { - // FIXME: Add a cl::parser - size_t pos = (*it).find_last_of('='); - llvm::StringRef script(*it); - uint64_t address = 0x0; - script.substr(pos + 1).getAsInteger(0, address); - bool exist = false; - mcld::StringEntry<uint64_t>* addr_mapping = - pScript.addressMap().insert(script.substr(0, pos), exist); - addr_mapping->setValue(address); - } - - // --defsym symbols - for (cl::list<std::string>::iterator - it = ArgDefSymList.begin(), ie = ArgDefSymList.end(); - it != ie ; ++it) { - llvm::StringRef expression(*it); - size_t pos = expression.find_last_of('='); - if (pos == expression.size() - 1) { - errs() << "defsym option: expression must not end with '='\n"; - return false; - } - if (llvm::StringRef::npos == pos) { - errs() << "syntax : --defsym symbol=expression\n"; - return false; - } - bool exist = false; - // FIXME: This will not work with multiple destinations such as - // --defsym abc=pqr=expression - - mcld::StringEntry<llvm::StringRef> *defsyms = - pScript.defSymMap().insert(expression.substr(0,pos),exist); - defsyms->setValue(expression.substr(pos + 1)); - } - - // set up filter/aux filter for shared object - pConfig.options().setFilter(ArgFilter); - - cl::list<std::string>::iterator aux; - cl::list<std::string>::iterator auxEnd = ArgAuxiliary.end(); - for (aux = ArgAuxiliary.begin(); aux != auxEnd; ++aux) - pConfig.options().getAuxiliaryList().push_back(*aux); - - return true; -} - -int main(int argc, char* argv[]) -{ - sys::PrintStackTraceOnErrorSignal(); - - LLVMContext &Context = getGlobalContext(); - llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. - - // Initialize targets first, so that --version shows registered targets. - InitializeAllTargets(); - InitializeAllAsmPrinters(); - InitializeAllAsmParsers(); - InitializeAllTargetMCs(); - mcld::InitializeAllTargets(); - mcld::InitializeAllLinkers(); - mcld::InitializeAllEmulations(); - mcld::InitializeAllDiagnostics(); - - cl::ParseCommandLineOptions(argc, argv, "MCLinker\n"); - -#ifdef ENABLE_UNITTEST - if (UnitTest) { - return unit_test( argc, argv ); - } -#endif - - // Load the module to be compiled... - std::auto_ptr<llvm::Module> M; - - // Load the module to be linked... - mcld::LinkerScript LDScript; - mcld::Module LDIRModule(LDScript); - mcld::LinkerConfig LDConfig; - - // Process the linker input from the command line - if (!ProcessLinkerOptionsFromCommand(LDScript, LDConfig)) { - errs() << argv[0] << ": failed to process linker options from command line!\n"; - return 1; - } - - if (ArgBitcodeFilename.empty() && - (mcld::CGFT_DSOFile != ArgFileType && - mcld::CGFT_EXEFile != ArgFileType && - mcld::CGFT_PARTIAL != ArgFileType && - mcld::CGFT_BINARY != ArgFileType)) { - // If the file is not given, forcefully read from stdin - if (ArgVerbose >= 0) { - errs() << "** The bitcode/llvm asm file is not given. Read from stdin.\n" - << "** Specify input bitcode/llvm asm file by\n\n" - << " llvm-mcld -dB [the bitcode/llvm asm]\n\n"; - } - - ArgBitcodeFilename.assign("-"); - } - - if (!ArgBitcodeFilename.empty()) { - SMDiagnostic Err; - M.reset(ParseIRFile(ArgBitcodeFilename.native(), Err, Context)); - - if (M.get() == 0) { - Err.print(argv[0], errs()); - errs() << "** Failed to to the given bitcode/llvm asm file '" - << ArgBitcodeFilename.native() << "'. **\n"; - return 1; - } - } - else { - // If here, output must be dynamic shared object (mcld::CGFT_DSOFile) and - // executable file (mcld::CGFT_EXEFile). - M.reset(new Module("Empty Module", Context)); - } - Module &mod = *M.get(); - - // If we are supposed to override the target triple, do so now. - Triple TheTriple; - if (!TargetTriple.empty()) { - // 1. Use the triple from command. - TheTriple.setTriple(TargetTriple); - mod.setTargetTriple(TargetTriple); - } else if (!mod.getTargetTriple().empty()) { - // 2. Use the triple in the input Module. - TheTriple.setTriple(mod.getTargetTriple()); - } else { - std::string ProgNameTriple = ParseProgName(argv[0]); - if (!ProgNameTriple.empty()) { - // 3. Use the triple from the program name prefix. - TheTriple.setTriple(ProgNameTriple); - mod.setTargetTriple(ProgNameTriple); - } else { - // 4. Use the default target triple. - TheTriple.setTriple(mcld::sys::getDefaultTargetTriple()); - if (!ArgEmulation.empty()) { - // Process target emulation. - Triple EmulationTriple = ParseEmulation(ArgEmulation); - if (EmulationTriple.getArch() != Triple::UnknownArch) - TheTriple.setArch(EmulationTriple.getArch()); - if (EmulationTriple.getOS() != Triple::UnknownOS) - TheTriple.setOS(EmulationTriple.getOS()); - if (EmulationTriple.getEnvironment() != Triple::UnknownEnvironment) - TheTriple.setEnvironment(EmulationTriple.getEnvironment()); - } - } - } - - // Allocate target machine. First, check whether the user has explicitly - // specified an architecture to compile for. If so we have to look it up by - // name, because it might be a backend that has no mapping to a target triple. - const mcld::Target *TheTarget = 0; - if (!MArch.empty()) { - for (mcld::TargetRegistry::iterator it = mcld::TargetRegistry::begin(), - ie = mcld::TargetRegistry::end(); it != ie; ++it) { - if (MArch == (*it)->get()->getName()) { - TheTarget = *it; - break; - } - } - - if (!TheTarget) { - errs() << argv[0] << ": error: invalid target '" << MArch << "'.\n"; - return 1; - } - - // Adjust the triple to match (if known), otherwise stick with the - // module/host triple. - Triple::ArchType Type = Triple::getArchTypeForLLVMName(MArch); - if (Type != Triple::UnknownArch) - TheTriple.setArch(Type); - } - else { - std::string Err; - TheTarget = mcld::TargetRegistry::lookupTarget(TheTriple.getTriple(), Err); - if (TheTarget == 0) { - errs() << "error: auto-selecting target `" << TheTriple.getTriple() - << "'\n" - << "Please use the -march option to explicitly select a target.\n" - << "Example:\n" - << " $ " << argv[0] << " -march=arm\n"; - return 1; - } - } - // Set up mcld::LinkerConfig - LDConfig.targets().setTriple(TheTriple); - - // Package up features to be passed to target/subtarget - std::string FeaturesStr; - if (MAttrs.size()) { - SubtargetFeatures Features; - for (unsigned i = 0; i != MAttrs.size(); ++i) - Features.AddFeature(MAttrs[i]); - FeaturesStr = Features.getString(); - } - - CodeGenOpt::Level OLvl = CodeGenOpt::Default; - switch (OptLevel) { - default: - errs() << argv[0] << ": invalid optimization level.\n"; - return 1; - case ' ': break; - case '0': OLvl = CodeGenOpt::None; break; - case '1': OLvl = CodeGenOpt::Less; break; - case '2': OLvl = CodeGenOpt::Default; break; - case '3': OLvl = CodeGenOpt::Aggressive; break; - } - - // set -fPIC - if (ArgFPIC) - ArgRelocModel = Reloc::PIC_; - - TargetOptions Options; - Options.LessPreciseFPMADOption = EnableFPMAD; - Options.NoFramePointerElim = DisableFPElim; - Options.NoFramePointerElimNonLeaf = DisableFPElimNonLeaf; - Options.AllowFPOpFusion = FuseFPOps; - Options.UnsafeFPMath = EnableUnsafeFPMath; - Options.NoInfsFPMath = EnableNoInfsFPMath; - Options.NoNaNsFPMath = EnableNoNaNsFPMath; - Options.HonorSignDependentRoundingFPMathOption = - EnableHonorSignDependentRoundingFPMath; - Options.UseSoftFloat = GenerateSoftFloatCalls; - if (FloatABIForCalls != FloatABI::Default) - Options.FloatABIType = FloatABIForCalls; - Options.NoZerosInBSS = DontPlaceZerosInBSS; - Options.JITExceptionHandling = EnableJITExceptionHandling; - Options.JITEmitDebugInfo = EmitJitDebugInfo; - Options.JITEmitDebugInfoToDisk = EmitJitDebugInfoToDisk; - Options.GuaranteedTailCallOpt = EnableGuaranteedTailCallOpt; - Options.StackAlignmentOverride = OverrideStackAlignment; - Options.RealignStack = EnableRealignStack; - Options.TrapFuncName = TrapFuncName; - Options.EnableSegmentedStacks = SegmentedStacks; - - std::auto_ptr<mcld::MCLDTargetMachine> target_machine( - TheTarget->createTargetMachine(TheTriple.getTriple(), - MCPU, FeaturesStr, Options, - ArgRelocModel, CMModel, OLvl)); - assert(target_machine.get() && "Could not allocate target machine!"); - mcld::MCLDTargetMachine &TheTargetMachine = *target_machine.get(); - - LDConfig.targets().setTargetCPU(MCPU); - LDConfig.targets().setTargetFeatureString(FeaturesStr); - - TheTargetMachine.getTM().setMCUseLoc(false); - TheTargetMachine.getTM().setMCUseCFI(false); - - // FIXME: Move the initialization of LineInfo to mcld::Linker when we - // finish LineInfo's implementation. - OwningPtr<mcld::DiagnosticLineInfo> - diag_line_info(TheTarget->createDiagnosticLineInfo(*TheTarget, - TheTriple.getTriple())); - - mcld::getDiagnosticEngine().setLineInfo(*diag_line_info.take()); - - // Figure out where we are going to send the output... - OwningPtr<mcld::ToolOutputFile> - Out(GetOutputStream(TheTarget->get()->getName(), - TheTriple.getOS(), - ArgFileType, - ArgBitcodeFilename, - ArgOutputFilename)); - if (!Out) { - // FIXME: show some error message pls. - return 1; - } - - // Build up all of the passes that we want to do to the module. - PassManager PM; - - // Add the data layout from the target machine, if it exists, or the module. - if (const DataLayout *DL = TheTargetMachine.getTM().getDataLayout()) - PM.add(new DataLayout(*DL)); - else - PM.add(new DataLayout(&mod)); - - // Override default to generate verbose assembly. - TheTargetMachine.getTM().setAsmVerbosityDefault(true); - - { - // Ask the target to add backend passes as necessary. - if( TheTargetMachine.addPassesToEmitFile(PM, - *Out, - ArgFileType, - OLvl, - LDIRModule, - LDConfig, - NoVerify)) { - errs() << argv[0] << ": target does not support generation of this" - << " file type!\n"; - return 1; - } - - // Before executing passes, print the final values of the LLVM options. - cl::PrintOptionValues(); - - PM.run(mod); - } - - if (mcld::getDiagnosticEngine().getPrinter()->getNumErrors()) - return 1; - - // Declare success. - Out->keep(); - return 0; -} diff --git a/tools/mcld/include/mcld/OptimizationOptions.h b/tools/mcld/include/mcld/OptimizationOptions.h index 485e732..5385f91 100644 --- a/tools/mcld/include/mcld/OptimizationOptions.h +++ b/tools/mcld/include/mcld/OptimizationOptions.h @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #ifndef MCLD_LDLITE_OPTIMIZATION_OPTIONS_H #define MCLD_LDLITE_OPTIMIZATION_OPTIONS_H +#include <mcld/GeneralOptions.h> #include <llvm/Support/CommandLine.h> #include <string> @@ -18,21 +19,17 @@ class LinkerConfig; class OptimizationOptions { public: - enum ICF { - ICF_None, - ICF_All, - ICF_Safe - }; - -public: OptimizationOptions(); bool parse(LinkerConfig& pConfig); private: bool& m_GCSections; + bool& m_PrintGCSections; bool& m_GenUnwindInfo; - llvm::cl::opt<ICF>& m_ICF; + llvm::cl::opt<mcld::GeneralOptions::ICF>& m_ICF; + llvm::cl::opt<unsigned>& m_ICFIterations; + llvm::cl::opt<bool>& m_PrintICFSections; llvm::cl::opt<char>& m_OptLevel; llvm::cl::list<std::string>& m_Plugin; llvm::cl::list<std::string>& m_PluginOpt; diff --git a/tools/mcld/include/mcld/SymbolOptions.h b/tools/mcld/include/mcld/SymbolOptions.h index b7c16f7..60f5e06 100644 --- a/tools/mcld/include/mcld/SymbolOptions.h +++ b/tools/mcld/include/mcld/SymbolOptions.h @@ -24,7 +24,7 @@ public: private: // not supported yet - llvm::cl::opt<std::string>& m_ForceUndefined; + llvm::cl::list<std::string>& m_ForceUndefined; llvm::cl::opt<std::string>& m_VersionScript; llvm::cl::opt<bool>& m_WarnCommon; llvm::cl::opt<bool>& m_DefineCommon; diff --git a/tools/mcld/lib/DynamicSectionOptions.cpp b/tools/mcld/lib/DynamicSectionOptions.cpp index 415bdc8..5ceeaef 100644 --- a/tools/mcld/lib/DynamicSectionOptions.cpp +++ b/tools/mcld/lib/DynamicSectionOptions.cpp @@ -75,7 +75,7 @@ llvm::cl::opt<std::string> ArgFilter("F", llvm::cl::alias ArgFilterAlias("filter", llvm::cl::desc("alias for -F"), - llvm::cl::aliasopt(ArgFilterAlias)); + llvm::cl::aliasopt(ArgFilter)); // } Not supported yet diff --git a/tools/mcld/lib/OptimizationOptions.cpp b/tools/mcld/lib/OptimizationOptions.cpp index 7eca773..9de70f4 100644 --- a/tools/mcld/lib/OptimizationOptions.cpp +++ b/tools/mcld/lib/OptimizationOptions.cpp @@ -13,7 +13,6 @@ namespace { -// Not supported yet bool ArgGCSections; llvm::cl::opt<bool, true> ArgGCSectionsFlag("gc-sections", @@ -28,6 +27,20 @@ llvm::cl::opt<bool, true, llvm::cl::FalseParser> ArgNoGCSectionsFlag("no-gc-sect llvm::cl::desc("disable garbage collection of unused input sections."), llvm::cl::init(false)); +bool ArgPrintGCSections; + +llvm::cl::opt<bool, true> ArgPrintGCSectionsFlag("print-gc-sections", + llvm::cl::ZeroOrMore, + llvm::cl::location(ArgPrintGCSections), + llvm::cl::desc("List all sections removed by garbage collection."), + llvm::cl::init(false)); + +llvm::cl::opt<bool, true, llvm::cl::FalseParser> ArgNoPrintGCSectionsFlag("no-print-gc-sections", + llvm::cl::ZeroOrMore, + llvm::cl::location(ArgPrintGCSections), + llvm::cl::desc("disable --print-gc-sections"), + llvm::cl::init(false)); + bool ArgGenUnwindInfo; llvm::cl::opt<bool, true, llvm::cl::FalseParser> @@ -47,19 +60,27 @@ ArgGenUnwindInfoFlag("ld-generated-unwind-info", llvm::cl::init(true), llvm::cl::ValueDisallowed); -llvm::cl::opt<mcld::OptimizationOptions::ICF> ArgICF("icf", +llvm::cl::opt<mcld::GeneralOptions::ICF> ArgICF("icf", llvm::cl::ZeroOrMore, llvm::cl::desc("Identical Code Folding"), - llvm::cl::init(mcld::OptimizationOptions::ICF_None), + llvm::cl::init(mcld::GeneralOptions::ICF_None), llvm::cl::values( - clEnumValN(mcld::OptimizationOptions::ICF_None, "none", + clEnumValN(mcld::GeneralOptions::ICF_None, "none", "do not perform cold folding"), - clEnumValN(mcld::OptimizationOptions::ICF_All, "all", + clEnumValN(mcld::GeneralOptions::ICF_All, "all", "always preform cold folding"), - clEnumValN(mcld::OptimizationOptions::ICF_Safe, "safe", + clEnumValN(mcld::GeneralOptions::ICF_Safe, "safe", "Folds those whose pointers are definitely not taken."), clEnumValEnd)); +llvm::cl::opt<unsigned> ArgICFIterations("icf-iterations", + llvm::cl::desc("Number of iterations to do ICF."), + llvm::cl::init(2)); + +llvm::cl::opt<bool> ArgPrintICFSections("print-icf-sections", + llvm::cl::desc("Print the folded identical sections."), + llvm::cl::init(false)); + llvm::cl::opt<char> ArgOptLevel("O", llvm::cl::desc("Optimization level. [-O0, -O1, -O2, or -O3] " "(default = '-O2')"), @@ -84,8 +105,11 @@ using namespace mcld; //===----------------------------------------------------------------------===// OptimizationOptions::OptimizationOptions() : m_GCSections(ArgGCSections), + m_PrintGCSections(ArgPrintGCSections), m_GenUnwindInfo(ArgGenUnwindInfo), m_ICF(ArgICF), + m_ICFIterations(ArgICFIterations), + m_PrintICFSections(ArgPrintICFSections), m_OptLevel(ArgOptLevel), m_Plugin(ArgPlugin), m_PluginOpt(ArgPluginOpt) { @@ -97,19 +121,17 @@ bool OptimizationOptions::parse(LinkerConfig& pConfig) if (m_GCSections) pConfig.options().setGCSections(); + // set --print-gc-sections + if (m_PrintGCSections) + pConfig.options().setPrintGCSections(); + // set --ld-generated-unwind-info (or not) pConfig.options().setGenUnwindInfo(m_GenUnwindInfo); // set --icf [mode] - switch (m_ICF) { - case ICF_None: - break; - case ICF_All: - case ICF_Safe: - default: - warning(mcld::diag::warn_unsupported_option) << m_ICF.ArgStr; - break; - } + pConfig.options().setICFMode(m_ICF); + pConfig.options().setICFIterations(m_ICFIterations); + pConfig.options().setPrintICFSections(m_PrintICFSections); return true; } diff --git a/tools/mcld/lib/OutputFormatOptions.cpp b/tools/mcld/lib/OutputFormatOptions.cpp index 5a148ee..87a3638 100644 --- a/tools/mcld/lib/OutputFormatOptions.cpp +++ b/tools/mcld/lib/OutputFormatOptions.cpp @@ -228,6 +228,14 @@ bool OutputFormatOptions::parse(mcld::Module& pModule, LinkerConfig& pConfig) pConfig.options().setOMagic(m_OMagic); pConfig.options().setHashStyle(m_HashStyle); pConfig.options().setExportDynamic(m_ExportDynamic); + + // --exclude-libs + llvm::cl::list<std::string>::iterator exclude, + excludeEnd = m_ExcludeLIBS.end(); + for (exclude = m_ExcludeLIBS.begin(); exclude != excludeEnd; ++exclude) { + pConfig.options().excludeLIBS().insert(*exclude); + } + if (m_NoWarnMismatch) pConfig.options().setWarnMismatch(false); else @@ -299,5 +307,3 @@ bool OutputFormatOptions::parseOutput(Module& pModule, LinkerConfig& pConfig) pModule.setName(output_filename); return true; } - - diff --git a/tools/mcld/lib/PreferenceOptions.cpp b/tools/mcld/lib/PreferenceOptions.cpp index 4fc6445..e5fa3df 100644 --- a/tools/mcld/lib/PreferenceOptions.cpp +++ b/tools/mcld/lib/PreferenceOptions.cpp @@ -91,17 +91,13 @@ llvm::cl::opt<bool, true> ArgFatalWarningsFlag("fatal-warnings", llvm::cl::init(false), llvm::cl::ValueDisallowed); -llvm::cl::opt<bool> ArgUseGold("use-gold", - llvm::cl::desc("GCC/collect2 compatibility: uses ld.gold. Ignored"), - llvm::cl::init(false)); - -llvm::cl::opt<bool> ArgUseMCLD("use-mcld", - llvm::cl::desc("GCC/collect2 compatibility: uses ld.mcld. Ignored"), - llvm::cl::init(false)); +llvm::cl::opt<std::string> ArgUseLD("fuse-ld", + llvm::cl::desc("Ignored for GCC/collect2 linker compatibility."), + llvm::cl::init("mcld")); -llvm::cl::opt<bool> ArgUseLD("use-ld", - llvm::cl::desc("GCC/collect2 compatibility: uses ld.bfd. Ignored"), - llvm::cl::init(false)); +llvm::cl::opt<std::string> ArgUseMCLD("use-mcld", + llvm::cl::desc("Ignored for GCC/collect2 linker compatibility."), + llvm::cl::init("mcld")); //===----------------------------------------------------------------------===// // Non-member functions diff --git a/tools/mcld/lib/SymbolOptions.cpp b/tools/mcld/lib/SymbolOptions.cpp index 66b52f6..4de764a 100644 --- a/tools/mcld/lib/SymbolOptions.cpp +++ b/tools/mcld/lib/SymbolOptions.cpp @@ -12,11 +12,13 @@ namespace { // Not supprted yet { -llvm::cl::opt<std::string> ArgForceUndefined("u", +llvm::cl::list<std::string> ArgForceUndefined("u", + llvm::cl::ZeroOrMore, llvm::cl::desc("Force symbol to be undefined in the output file"), llvm::cl::value_desc("symbol")); llvm::cl::alias ArgForceUndefinedAlias("undefined", + llvm::cl::ZeroOrMore, llvm::cl::desc("alias for -u"), llvm::cl::aliasopt(ArgForceUndefined)); @@ -64,6 +66,11 @@ bool SymbolOptions::parse(LinkerConfig& pConfig) // set -d pConfig.options().setDefineCommon(m_DefineCommon); + // set -u/--undefined symbols + llvm::cl::list<std::string>::iterator usym, usymEnd = m_ForceUndefined.end(); + for (usym = m_ForceUndefined.begin(); usym != usymEnd; ++usym) + pConfig.options().getUndefSymList().push_back(*usym); + return true; } |