summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2015-12-30 11:46:15 +0000
committerDimitry Andric <dim@FreeBSD.org>2015-12-30 11:46:15 +0000
commitdd58ef019b700900793a1eb48b52123db01b654e (patch)
treefcfbb4df56a744f4ddc6122c50521dd3f1c5e196 /tools
parent2fe5752e3a7c345cdb59e869278d36af33c13fa4 (diff)
Notes
Diffstat (limited to 'tools')
-rw-r--r--tools/CMakeLists.txt93
-rw-r--r--tools/LLVMBuild.txt3
-rw-r--r--tools/Makefile5
-rw-r--r--tools/bugpoint-passes/TestPasses.cpp53
-rw-r--r--tools/bugpoint/BugDriver.cpp6
-rw-r--r--tools/bugpoint/BugDriver.h19
-rw-r--r--tools/bugpoint/CMakeLists.txt1
-rw-r--r--tools/bugpoint/CrashDebugger.cpp265
-rw-r--r--tools/bugpoint/ExecutionDriver.cpp41
-rw-r--r--tools/bugpoint/ExtractFunction.cpp71
-rw-r--r--tools/bugpoint/ListReducer.h12
-rw-r--r--tools/bugpoint/Miscompilation.cpp277
-rw-r--r--tools/bugpoint/ToolRunner.cpp217
-rw-r--r--tools/bugpoint/ToolRunner.h50
-rw-r--r--tools/bugpoint/bugpoint.cpp10
-rw-r--r--tools/dsymutil/BinaryHolder.cpp172
-rw-r--r--tools/dsymutil/BinaryHolder.h98
-rw-r--r--tools/dsymutil/CMakeLists.txt1
-rw-r--r--tools/dsymutil/DebugMap.cpp38
-rw-r--r--tools/dsymutil/DebugMap.h19
-rw-r--r--tools/dsymutil/DwarfLinker.cpp1678
-rw-r--r--tools/dsymutil/MachODebugMapParser.cpp323
-rw-r--r--tools/dsymutil/MachOUtils.cpp521
-rw-r--r--tools/dsymutil/MachOUtils.h39
-rw-r--r--tools/dsymutil/NonRelocatableStringpool.h68
-rw-r--r--tools/dsymutil/dsymutil.cpp293
-rw-r--r--tools/dsymutil/dsymutil.h25
-rw-r--r--tools/gold/gold-plugin.cpp481
-rw-r--r--tools/llc/CMakeLists.txt1
-rw-r--r--tools/llc/LLVMBuild.txt2
-rw-r--r--tools/llc/Makefile2
-rw-r--r--tools/llc/llc.cpp55
-rw-r--r--tools/lli/OrcLazyJIT.cpp62
-rw-r--r--tools/lli/OrcLazyJIT.h54
-rw-r--r--tools/lli/RemoteTarget.cpp4
-rw-r--r--tools/lli/lli.cpp5
-rw-r--r--tools/llvm-ar/CMakeLists.txt29
-rw-r--r--tools/llvm-ar/install_symlink.cmake31
-rw-r--r--tools/llvm-ar/llvm-ar.cpp104
-rw-r--r--tools/llvm-as-fuzzer/CMakeLists.txt13
-rw-r--r--tools/llvm-as-fuzzer/llvm-as-fuzzer.cpp76
-rw-r--r--tools/llvm-as/llvm-as.cpp7
-rw-r--r--tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp67
-rw-r--r--tools/llvm-c-test/llvm-c-test.h4
-rw-r--r--tools/llvm-c-test/main.c18
-rw-r--r--tools/llvm-c-test/module.c38
-rw-r--r--tools/llvm-c-test/object.c1
-rw-r--r--tools/llvm-config/BuildVariables.inc.in5
-rw-r--r--tools/llvm-config/CMakeLists.txt8
-rw-r--r--tools/llvm-config/Makefile18
-rw-r--r--tools/llvm-config/llvm-config.cpp285
-rw-r--r--tools/llvm-cov/CoverageReport.cpp31
-rw-r--r--tools/llvm-cov/CoverageViewOptions.h1
-rw-r--r--tools/llvm-cov/gcov.cpp6
-rw-r--r--tools/llvm-cxxdump/llvm-cxxdump.cpp104
-rw-r--r--tools/llvm-diff/DiffLog.cpp3
-rw-r--r--tools/llvm-diff/DiffLog.h10
-rw-r--r--tools/llvm-diff/DifferenceEngine.cpp4
-rw-r--r--tools/llvm-dis/llvm-dis.cpp2
-rw-r--r--tools/llvm-dwarfdump/llvm-dwarfdump.cpp107
-rw-r--r--tools/llvm-dwp/CMakeLists.txt13
-rw-r--r--tools/llvm-dwp/LLVMBuild.txt23
-rw-r--r--tools/llvm-dwp/Makefile18
-rw-r--r--tools/llvm-dwp/llvm-dwp.cpp420
-rw-r--r--tools/llvm-extract/llvm-extract.cpp53
-rw-r--r--tools/llvm-go/llvm-go.go91
-rw-r--r--tools/llvm-link/CMakeLists.txt1
-rw-r--r--tools/llvm-link/LLVMBuild.txt2
-rw-r--r--tools/llvm-link/Makefile2
-rw-r--r--tools/llvm-link/llvm-link.cpp194
-rw-r--r--tools/llvm-lto/CMakeLists.txt3
-rw-r--r--tools/llvm-lto/LLVMBuild.txt2
-rw-r--r--tools/llvm-lto/Makefile2
-rw-r--r--tools/llvm-lto/llvm-lto.cpp269
-rw-r--r--tools/llvm-mc-fuzzer/CMakeLists.txt18
-rw-r--r--tools/llvm-mc-fuzzer/llvm-mc-fuzzer.cpp138
-rw-r--r--tools/llvm-mc/llvm-mc.cpp14
-rw-r--r--tools/llvm-nm/CMakeLists.txt4
-rw-r--r--tools/llvm-nm/llvm-nm.cpp258
-rw-r--r--tools/llvm-objdump/CMakeLists.txt5
-rw-r--r--tools/llvm-objdump/COFFDump.cpp85
-rw-r--r--tools/llvm-objdump/ELFDump.cpp32
-rw-r--r--tools/llvm-objdump/MachODump.cpp802
-rw-r--r--tools/llvm-objdump/llvm-objdump.cpp560
-rw-r--r--tools/llvm-objdump/llvm-objdump.h8
-rw-r--r--tools/llvm-pdbdump/BuiltinDumper.cpp66
-rw-r--r--tools/llvm-pdbdump/BuiltinDumper.h2
-rw-r--r--tools/llvm-pdbdump/LinePrinter.cpp104
-rw-r--r--tools/llvm-pdbdump/LinePrinter.h13
-rw-r--r--tools/llvm-pdbdump/llvm-pdbdump.cpp299
-rw-r--r--tools/llvm-pdbdump/llvm-pdbdump.h3
-rw-r--r--tools/llvm-profdata/llvm-profdata.cpp269
-rw-r--r--tools/llvm-readobj/ARMAttributeParser.cpp72
-rw-r--r--tools/llvm-readobj/ARMEHABIPrinter.h60
-rw-r--r--tools/llvm-readobj/ARMWinEHPrinter.cpp5
-rw-r--r--tools/llvm-readobj/CMakeLists.txt1
-rw-r--r--tools/llvm-readobj/COFFDumper.cpp120
-rw-r--r--tools/llvm-readobj/COFFImportDumper.cpp52
-rw-r--r--tools/llvm-readobj/ELFDumper.cpp903
-rw-r--r--tools/llvm-readobj/MachODumper.cpp240
-rw-r--r--tools/llvm-readobj/ObjDumper.h19
-rw-r--r--tools/llvm-readobj/StreamWriter.h32
-rw-r--r--tools/llvm-readobj/Win64EHDumper.cpp21
-rw-r--r--tools/llvm-readobj/llvm-readobj.cpp145
-rw-r--r--tools/llvm-readobj/llvm-readobj.h4
-rw-r--r--tools/llvm-rtdyld/llvm-rtdyld.cpp235
-rw-r--r--tools/llvm-shlib/CMakeLists.txt144
-rw-r--r--tools/llvm-shlib/Makefile2
-rw-r--r--tools/llvm-size/llvm-size.cpp44
-rw-r--r--tools/llvm-split/CMakeLists.txt11
-rw-r--r--tools/llvm-split/LLVMBuild.txt (renamed from tools/macho-dump/LLVMBuild.txt)6
-rw-r--r--tools/llvm-split/Makefile (renamed from tools/macho-dump/Makefile)10
-rw-r--r--tools/llvm-split/llvm-split.cpp67
-rw-r--r--tools/llvm-stress/CMakeLists.txt2
-rw-r--r--tools/llvm-stress/llvm-stress.cpp5
-rw-r--r--tools/llvm-symbolizer/CMakeLists.txt2
-rw-r--r--tools/llvm-symbolizer/LLVMSymbolize.cpp532
-rw-r--r--tools/llvm-symbolizer/LLVMSymbolize.h144
-rw-r--r--tools/llvm-symbolizer/Makefile2
-rw-r--r--tools/llvm-symbolizer/llvm-symbolizer.cpp50
-rw-r--r--tools/lto/CMakeLists.txt10
-rw-r--r--tools/lto/lto.cpp133
-rw-r--r--tools/macho-dump/CMakeLists.txt8
-rw-r--r--tools/macho-dump/macho-dump.cpp434
-rw-r--r--tools/obj2yaml/elf2yaml.cpp79
-rw-r--r--tools/obj2yaml/obj2yaml.cpp3
-rw-r--r--tools/opt/CMakeLists.txt1
-rw-r--r--tools/opt/opt.cpp56
-rw-r--r--tools/sancov/CMakeLists.txt17
-rw-r--r--tools/sancov/Makefile18
-rw-r--r--tools/sancov/sancov.cc533
-rw-r--r--tools/verify-uselistorder/verify-uselistorder.cpp1
-rw-r--r--tools/xcode-toolchain/CMakeLists.txt72
-rw-r--r--tools/yaml2obj/yaml2elf.cpp16
134 files changed, 9680 insertions, 4440 deletions
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
index c9c5a1fdfa08..9b89d87fc571 100644
--- a/tools/CMakeLists.txt
+++ b/tools/CMakeLists.txt
@@ -1,78 +1,45 @@
-add_llvm_tool_subdirectory(llvm-config)
+# This file will recurse into all subdirectories that contain CMakeLists.txt
+# Setting variables that match the pattern LLVM_TOOL_{NAME}_BUILD to Off will
+# prevent traversing into a directory.
+#
+# The only tools that need to be explicitly added are ones that have explicit
+# ordering requirements.
+
+# Iterates all the subdirectories to create CMake options to enable/disable
+# traversing each directory.
+create_llvm_tool_options()
# Build polly before the tools: the tools link against polly when
# LINK_POLLY_INTO_TOOLS is set.
if(WITH_POLLY)
add_llvm_external_project(polly)
-else(WITH_POLLY)
- list(APPEND LLVM_IMPLICIT_PROJECT_IGNORE "${LLVM_MAIN_SRC_DIR}/tools/polly")
-endif(WITH_POLLY)
-
-if( LLVM_BUILD_LLVM_DYLIB )
- add_llvm_tool_subdirectory(llvm-shlib)
else()
- ignore_llvm_tool_subdirectory(llvm-shlib)
+ set(LLVM_TOOL_POLLY_BUILD Off)
endif()
-add_llvm_tool_subdirectory(opt)
-add_llvm_tool_subdirectory(llvm-as)
-add_llvm_tool_subdirectory(llvm-dis)
-add_llvm_tool_subdirectory(llvm-mc)
-
-add_llvm_tool_subdirectory(llc)
-add_llvm_tool_subdirectory(llvm-ar)
-add_llvm_tool_subdirectory(llvm-nm)
-add_llvm_tool_subdirectory(llvm-size)
-
-add_llvm_tool_subdirectory(llvm-cov)
-add_llvm_tool_subdirectory(llvm-profdata)
-add_llvm_tool_subdirectory(llvm-link)
-add_llvm_tool_subdirectory(lli)
-
-add_llvm_tool_subdirectory(llvm-extract)
-add_llvm_tool_subdirectory(llvm-diff)
-add_llvm_tool_subdirectory(macho-dump)
-add_llvm_tool_subdirectory(llvm-objdump)
-add_llvm_tool_subdirectory(llvm-readobj)
-add_llvm_tool_subdirectory(llvm-rtdyld)
-add_llvm_tool_subdirectory(llvm-dwarfdump)
-add_llvm_tool_subdirectory(dsymutil)
-add_llvm_tool_subdirectory(llvm-cxxdump)
-if( LLVM_USE_INTEL_JITEVENTS )
- add_llvm_tool_subdirectory(llvm-jitlistener)
-else()
- ignore_llvm_tool_subdirectory(llvm-jitlistener)
-endif( LLVM_USE_INTEL_JITEVENTS )
-
-add_llvm_tool_subdirectory(bugpoint)
-add_llvm_tool_subdirectory(bugpoint-passes)
-add_llvm_tool_subdirectory(llvm-bcanalyzer)
-add_llvm_tool_subdirectory(llvm-stress)
-add_llvm_tool_subdirectory(llvm-mcmarkup)
-
-add_llvm_tool_subdirectory(verify-uselistorder)
-
-add_llvm_tool_subdirectory(llvm-symbolizer)
-
-add_llvm_tool_subdirectory(llvm-c-test)
-
-add_llvm_tool_subdirectory(obj2yaml)
-add_llvm_tool_subdirectory(yaml2obj)
-
-add_llvm_tool_subdirectory(llvm-go)
+if(NOT LLVM_BUILD_LLVM_DYLIB )
+ set(LLVM_TOOL_LLVM_SHLIB_BUILD Off)
+endif()
-add_llvm_tool_subdirectory(llvm-pdbdump)
+if(NOT LLVM_USE_INTEL_JITEVENTS )
+ set(LLVM_TOOL_LLVM_JITLISTENER_BUILD Off)
+endif()
-if(NOT CYGWIN AND LLVM_ENABLE_PIC)
- add_llvm_tool_subdirectory(lto)
- add_llvm_tool_subdirectory(llvm-lto)
-else()
- ignore_llvm_tool_subdirectory(lto)
- ignore_llvm_tool_subdirectory(llvm-lto)
+if(CYGWIN)
+ set(LLVM_TOOL_LTO_BUILD Off)
+ set(LLVM_TOOL_LLVM_LTO_BUILD Off)
endif()
-add_llvm_tool_subdirectory(gold)
+# Add LTO, llvm-ar, llvm-config, and llvm-profdata before clang, ExternalProject
+# requires targets specified in DEPENDS to exist before the call to
+# ExternalProject_Add.
+add_llvm_tool_subdirectory(lto)
+add_llvm_tool_subdirectory(llvm-ar)
+add_llvm_tool_subdirectory(llvm-config)
+add_llvm_tool_subdirectory(llvm-profdata)
+# Projects supported via LLVM_EXTERNAL_*_SOURCE_DIR need to be explicitly
+# specified.
add_llvm_external_project(clang)
add_llvm_external_project(llgo)
add_llvm_external_project(lld)
@@ -80,6 +47,6 @@ add_llvm_external_project(lldb)
# Automatically add remaining sub-directories containing a 'CMakeLists.txt'
# file as external projects.
-add_llvm_implicit_external_projects()
+add_llvm_implicit_projects()
set(LLVM_COMMON_DEPENDS ${LLVM_COMMON_DEPENDS} PARENT_SCOPE)
diff --git a/tools/LLVMBuild.txt b/tools/LLVMBuild.txt
index acf61b0268c1..d4b014771859 100644
--- a/tools/LLVMBuild.txt
+++ b/tools/LLVMBuild.txt
@@ -28,6 +28,7 @@ subdirectories =
llvm-diff
llvm-dis
llvm-dwarfdump
+ llvm-dwp
llvm-extract
llvm-jitlistener
llvm-link
@@ -40,7 +41,7 @@ subdirectories =
llvm-profdata
llvm-rtdyld
llvm-size
- macho-dump
+ llvm-split
opt
verify-uselistorder
diff --git a/tools/Makefile b/tools/Makefile
index a47710f111a4..92d495451879 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -29,10 +29,11 @@ OPTIONAL_DIRS := lldb
DIRS := llvm-config
PARALLEL_DIRS := opt llvm-as llvm-dis llc llvm-ar llvm-nm llvm-link \
lli llvm-extract llvm-mc bugpoint llvm-bcanalyzer llvm-diff \
- macho-dump llvm-objdump llvm-readobj llvm-rtdyld \
+ llvm-objdump llvm-readobj llvm-rtdyld \
llvm-dwarfdump llvm-cov llvm-size llvm-stress llvm-mcmarkup \
llvm-profdata llvm-symbolizer obj2yaml yaml2obj llvm-c-test \
- llvm-cxxdump verify-uselistorder dsymutil llvm-pdbdump
+ llvm-cxxdump verify-uselistorder dsymutil llvm-pdbdump \
+ llvm-split sancov llvm-dwp
# If Intel JIT Events support is configured, build an extra tool to test it.
ifeq ($(USE_INTEL_JITEVENTS), 1)
diff --git a/tools/bugpoint-passes/TestPasses.cpp b/tools/bugpoint-passes/TestPasses.cpp
index 6979d0345ee6..c1eb0fbb67c1 100644
--- a/tools/bugpoint-passes/TestPasses.cpp
+++ b/tools/bugpoint-passes/TestPasses.cpp
@@ -76,20 +76,21 @@ static RegisterPass<DeleteCalls>
namespace {
/// CrashOnDeclFunc - This pass is used to test bugpoint. It intentionally
- /// crash if the module has an undefined function (ie a function that is
- /// defined in an external module).
- class CrashOnDeclFunc : public ModulePass {
- public:
- static char ID; // Pass ID, replacement for typeid
- CrashOnDeclFunc() : ModulePass(ID) {}
- private:
- bool runOnModule(Module &M) override {
- for (auto &F : M.functions()) {
- if (F.isDeclaration())
- abort();
- }
- return false;
+/// crashes if the module has an undefined function (ie a function that is
+/// defined in an external module).
+class CrashOnDeclFunc : public ModulePass {
+public:
+ static char ID; // Pass ID, replacement for typeid
+ CrashOnDeclFunc() : ModulePass(ID) {}
+
+private:
+ bool runOnModule(Module &M) override {
+ for (auto &F : M.functions()) {
+ if (F.isDeclaration())
+ abort();
}
+ return false;
+ }
};
}
@@ -97,3 +98,29 @@ char CrashOnDeclFunc::ID = 0;
static RegisterPass<CrashOnDeclFunc>
Z("bugpoint-crash-decl-funcs",
"BugPoint Test Pass - Intentionally crash on declared functions");
+
+#include <iostream>
+namespace {
+/// CrashOnOneCU - This pass is used to test bugpoint. It intentionally
+/// crashes if the Module has two or more compile units
+class CrashOnTooManyCUs : public ModulePass {
+public:
+ static char ID;
+ CrashOnTooManyCUs() : ModulePass(ID) {}
+
+private:
+ bool runOnModule(Module &M) override {
+ NamedMDNode *CU_Nodes = M.getNamedMetadata("llvm.dbg.cu");
+ if (!CU_Nodes)
+ return false;
+ if (CU_Nodes->getNumOperands() >= 2)
+ abort();
+ return false;
+ }
+};
+}
+
+char CrashOnTooManyCUs::ID = 0;
+static RegisterPass<CrashOnTooManyCUs>
+ A("bugpoint-crash-too-many-cus",
+ "BugPoint Test Pass - Intentionally crash on too many CUs");
diff --git a/tools/bugpoint/BugDriver.cpp b/tools/bugpoint/BugDriver.cpp
index 43f4c2963fc3..030749fa9ea0 100644
--- a/tools/bugpoint/BugDriver.cpp
+++ b/tools/bugpoint/BugDriver.cpp
@@ -72,7 +72,7 @@ BugDriver::BugDriver(const char *toolname, bool find_bugs,
LLVMContext& ctxt)
: Context(ctxt), ToolName(toolname), ReferenceOutputFile(OutputFile),
Program(nullptr), Interpreter(nullptr), SafeInterpreter(nullptr),
- gcc(nullptr), run_find_bugs(find_bugs), Timeout(timeout),
+ cc(nullptr), run_find_bugs(find_bugs), Timeout(timeout),
MemoryLimit(memlimit), UseValgrind(use_valgrind) {}
BugDriver::~BugDriver() {
@@ -80,7 +80,7 @@ BugDriver::~BugDriver() {
if (Interpreter != SafeInterpreter)
delete Interpreter;
delete SafeInterpreter;
- delete gcc;
+ delete cc;
}
std::unique_ptr<Module> llvm::parseInputFile(StringRef Filename,
@@ -132,7 +132,7 @@ bool BugDriver::addSources(const std::vector<std::string> &Filenames) {
if (!M.get()) return true;
outs() << "Linking in input file: '" << Filenames[i] << "'\n";
- if (Linker::LinkModules(Program, M.get()))
+ if (Linker::linkModules(*Program, std::move(M)))
return true;
}
diff --git a/tools/bugpoint/BugDriver.h b/tools/bugpoint/BugDriver.h
index 579781246c54..20efff3fda5f 100644
--- a/tools/bugpoint/BugDriver.h
+++ b/tools/bugpoint/BugDriver.h
@@ -36,7 +36,7 @@ class LLVMContext;
class DebugCrashes;
-class GCC;
+class CC;
extern bool DisableSimplifyCFG;
@@ -52,7 +52,7 @@ class BugDriver {
std::vector<std::string> PassesToRun;
AbstractInterpreter *Interpreter; // How to run the program
AbstractInterpreter *SafeInterpreter; // To generate reference output, etc.
- GCC *gcc;
+ CC *cc;
bool run_find_bugs;
unsigned Timeout;
unsigned MemoryLimit;
@@ -321,16 +321,21 @@ void PrintFunctionList(const std::vector<Function*> &Funcs);
///
void PrintGlobalVariableList(const std::vector<GlobalVariable*> &GVs);
+// DeleteGlobalInitializer - "Remove" the global variable by deleting its
+// initializer, making it external.
+//
+void DeleteGlobalInitializer(GlobalVariable *GV);
+
// DeleteFunctionBody - "Remove" the function by deleting all of it's basic
// blocks, making it external.
//
void DeleteFunctionBody(Function *F);
-/// SplitFunctionsOutOfModule - Given a module and a list of functions in the
-/// module, split the functions OUT of the specified module, and place them in
-/// the new module.
-Module *SplitFunctionsOutOfModule(Module *M, const std::vector<Function*> &F,
- ValueToValueMapTy &VMap);
+/// Given a module and a list of functions in the module, split the functions
+/// OUT of the specified module, and place them in the new module.
+std::unique_ptr<Module>
+SplitFunctionsOutOfModule(Module *M, const std::vector<Function *> &F,
+ ValueToValueMapTy &VMap);
} // End llvm namespace
diff --git a/tools/bugpoint/CMakeLists.txt b/tools/bugpoint/CMakeLists.txt
index daf502e16ccd..287a26ecd40c 100644
--- a/tools/bugpoint/CMakeLists.txt
+++ b/tools/bugpoint/CMakeLists.txt
@@ -3,7 +3,6 @@ set(LLVM_LINK_COMPONENTS
BitWriter
CodeGen
Core
- IPA
IPO
IRReader
InstCombine
diff --git a/tools/bugpoint/CrashDebugger.cpp b/tools/bugpoint/CrashDebugger.cpp
index e2aaf6b01fc5..6cdc43ab8699 100644
--- a/tools/bugpoint/CrashDebugger.cpp
+++ b/tools/bugpoint/CrashDebugger.cpp
@@ -15,6 +15,7 @@
#include "ListReducer.h"
#include "ToolRunner.h"
#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/StringSet.h"
#include "llvm/IR/CFG.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DerivedTypes.h"
@@ -49,6 +50,10 @@ namespace {
DontReducePassList("disable-pass-list-reduction",
cl::desc("Skip pass list reduction steps"),
cl::init(false));
+
+ cl::opt<bool> NoNamedMDRM("disable-namedmd-remove",
+ cl::desc("Do not remove global named metadata"),
+ cl::init(false));
}
namespace llvm {
@@ -138,7 +143,7 @@ ReduceCrashingGlobalVariables::TestGlobalVariables(
std::vector<GlobalVariable*> &GVs) {
// Clone the program to try hacking it apart...
ValueToValueMapTy VMap;
- Module *M = CloneModule(BD.getProgram(), VMap);
+ Module *M = CloneModule(BD.getProgram(), VMap).release();
// Convert list to set for fast lookup...
std::set<GlobalVariable*> GVSet;
@@ -155,11 +160,10 @@ ReduceCrashingGlobalVariables::TestGlobalVariables(
// Loop over and delete any global variables which we aren't supposed to be
// playing with...
- for (Module::global_iterator I = M->global_begin(), E = M->global_end();
- I != E; ++I)
- if (I->hasInitializer() && !GVSet.count(I)) {
- I->setInitializer(nullptr);
- I->setLinkage(GlobalValue::ExternalLinkage);
+ for (GlobalVariable &I : M->globals())
+ if (I.hasInitializer() && !GVSet.count(&I)) {
+ DeleteGlobalInitializer(&I);
+ I.setLinkage(GlobalValue::ExternalLinkage);
}
// Try running the hacked up program...
@@ -235,7 +239,7 @@ bool ReduceCrashingFunctions::TestFuncs(std::vector<Function*> &Funcs) {
// Clone the program to try hacking it apart...
ValueToValueMapTy VMap;
- Module *M = CloneModule(BD.getProgram(), VMap);
+ Module *M = CloneModule(BD.getProgram(), VMap).release();
// Convert list to set for fast lookup...
std::set<Function*> Functions;
@@ -253,9 +257,9 @@ bool ReduceCrashingFunctions::TestFuncs(std::vector<Function*> &Funcs) {
if (!ReplaceFuncsWithNull) {
// Loop over and delete any functions which we aren't supposed to be playing
// with...
- for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I)
- if (!I->isDeclaration() && !Functions.count(I))
- DeleteFunctionBody(I);
+ for (Function &I : *M)
+ if (!I.isDeclaration() && !Functions.count(&I))
+ DeleteFunctionBody(&I);
} else {
std::vector<GlobalValue*> ToRemove;
// First, remove aliases to functions we're about to purge.
@@ -280,12 +284,12 @@ bool ReduceCrashingFunctions::TestFuncs(std::vector<Function*> &Funcs) {
ToRemove.push_back(&Alias);
}
- for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I) {
- if (!I->isDeclaration() && !Functions.count(I)) {
- PointerType *Ty = cast<PointerType>(I->getType());
+ for (Function &I : *M) {
+ if (!I.isDeclaration() && !Functions.count(&I)) {
+ PointerType *Ty = cast<PointerType>(I.getType());
Constant *Replacement = ConstantPointerNull::get(Ty);
- I->replaceAllUsesWith(Replacement);
- ToRemove.push_back(I);
+ I.replaceAllUsesWith(Replacement);
+ ToRemove.push_back(&I);
}
}
@@ -342,7 +346,7 @@ namespace {
bool ReduceCrashingBlocks::TestBlocks(std::vector<const BasicBlock*> &BBs) {
// Clone the program to try hacking it apart...
ValueToValueMapTy VMap;
- Module *M = CloneModule(BD.getProgram(), VMap);
+ Module *M = CloneModule(BD.getProgram(), VMap).release();
// Convert list to set for fast lookup...
SmallPtrSet<BasicBlock*, 8> Blocks;
@@ -361,20 +365,22 @@ bool ReduceCrashingBlocks::TestBlocks(std::vector<const BasicBlock*> &BBs) {
// Loop over and delete any hack up any blocks that are not listed...
for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I)
for (Function::iterator BB = I->begin(), E = I->end(); BB != E; ++BB)
- if (!Blocks.count(BB) && BB->getTerminator()->getNumSuccessors()) {
+ if (!Blocks.count(&*BB) && BB->getTerminator()->getNumSuccessors()) {
// Loop over all of the successors of this block, deleting any PHI nodes
// that might include it.
- for (succ_iterator SI = succ_begin(BB), E = succ_end(BB); SI != E; ++SI)
- (*SI)->removePredecessor(BB);
+ for (succ_iterator SI = succ_begin(&*BB), E = succ_end(&*BB); SI != E;
+ ++SI)
+ (*SI)->removePredecessor(&*BB);
TerminatorInst *BBTerm = BB->getTerminator();
-
- if (!BB->getTerminator()->getType()->isVoidTy())
+ if (BBTerm->isEHPad())
+ continue;
+ if (!BBTerm->getType()->isVoidTy() && !BBTerm->getType()->isTokenTy())
BBTerm->replaceAllUsesWith(Constant::getNullValue(BBTerm->getType()));
// Replace the old terminator instruction.
BB->getInstList().pop_back();
- new UnreachableInst(BB->getContext(), BB);
+ new UnreachableInst(BB->getContext(), &*BB);
}
// The CFG Simplifier pass may delete one of the basic blocks we are
@@ -450,7 +456,7 @@ bool ReduceCrashingInstructions::TestInsts(std::vector<const Instruction*>
&Insts) {
// Clone the program to try hacking it apart...
ValueToValueMapTy VMap;
- Module *M = CloneModule(BD.getProgram(), VMap);
+ Module *M = CloneModule(BD.getProgram(), VMap).release();
// Convert list to set for fast lookup...
SmallPtrSet<Instruction*, 64> Instructions;
@@ -468,10 +474,10 @@ bool ReduceCrashingInstructions::TestInsts(std::vector<const Instruction*>
for (Module::iterator MI = M->begin(), ME = M->end(); MI != ME; ++MI)
for (Function::iterator FI = MI->begin(), FE = MI->end(); FI != FE; ++FI)
for (BasicBlock::iterator I = FI->begin(), E = FI->end(); I != E;) {
- Instruction *Inst = I++;
+ Instruction *Inst = &*I++;
if (!Instructions.count(Inst) && !isa<TerminatorInst>(Inst) &&
- !isa<LandingPadInst>(Inst)) {
- if (!Inst->getType()->isVoidTy())
+ !Inst->isEHPad()) {
+ if (!Inst->getType()->isVoidTy() && !Inst->getType()->isTokenTy())
Inst->replaceAllUsesWith(UndefValue::get(Inst->getType()));
Inst->eraseFromParent();
}
@@ -497,6 +503,149 @@ bool ReduceCrashingInstructions::TestInsts(std::vector<const Instruction*>
return false;
}
+namespace {
+// Reduce the list of Named Metadata nodes. We keep this as a list of
+// names to avoid having to convert back and forth every time.
+class ReduceCrashingNamedMD : public ListReducer<std::string> {
+ BugDriver &BD;
+ bool (*TestFn)(const BugDriver &, Module *);
+
+public:
+ ReduceCrashingNamedMD(BugDriver &bd,
+ bool (*testFn)(const BugDriver &, Module *))
+ : BD(bd), TestFn(testFn) {}
+
+ TestResult doTest(std::vector<std::string> &Prefix,
+ std::vector<std::string> &Kept,
+ std::string &Error) override {
+ if (!Kept.empty() && TestNamedMDs(Kept))
+ return KeepSuffix;
+ if (!Prefix.empty() && TestNamedMDs(Prefix))
+ return KeepPrefix;
+ return NoFailure;
+ }
+
+ bool TestNamedMDs(std::vector<std::string> &NamedMDs);
+};
+}
+
+bool ReduceCrashingNamedMD::TestNamedMDs(std::vector<std::string> &NamedMDs) {
+
+ ValueToValueMapTy VMap;
+ Module *M = CloneModule(BD.getProgram(), VMap).release();
+
+ outs() << "Checking for crash with only these named metadata nodes:";
+ unsigned NumPrint = std::min<size_t>(NamedMDs.size(), 10);
+ for (unsigned i = 0, e = NumPrint; i != e; ++i)
+ outs() << " " << NamedMDs[i];
+ if (NumPrint < NamedMDs.size())
+ outs() << "... <" << NamedMDs.size() << " total>";
+ outs() << ": ";
+
+ // Make a StringMap for faster lookup
+ StringSet<> Names;
+ for (const std::string &Name : NamedMDs)
+ Names.insert(Name);
+
+ // First collect all the metadata to delete in a vector, then
+ // delete them all at once to avoid invalidating the iterator
+ std::vector<NamedMDNode *> ToDelete;
+ ToDelete.reserve(M->named_metadata_size() - Names.size());
+ for (auto &NamedMD : M->named_metadata())
+ if (!Names.count(NamedMD.getName()))
+ ToDelete.push_back(&NamedMD);
+
+ for (auto *NamedMD : ToDelete)
+ NamedMD->eraseFromParent();
+
+ // Verify that this is still valid.
+ legacy::PassManager Passes;
+ Passes.add(createVerifierPass());
+ Passes.run(*M);
+
+ // Try running on the hacked up program...
+ if (TestFn(BD, M)) {
+ BD.setNewProgram(M); // It crashed, keep the trimmed version...
+ return true;
+ }
+ delete M; // It didn't crash, try something else.
+ return false;
+}
+
+namespace {
+// Reduce the list of operands to named metadata nodes
+class ReduceCrashingNamedMDOps : public ListReducer<const MDNode *> {
+ BugDriver &BD;
+ bool (*TestFn)(const BugDriver &, Module *);
+
+public:
+ ReduceCrashingNamedMDOps(BugDriver &bd,
+ bool (*testFn)(const BugDriver &, Module *))
+ : BD(bd), TestFn(testFn) {}
+
+ TestResult doTest(std::vector<const MDNode *> &Prefix,
+ std::vector<const MDNode *> &Kept,
+ std::string &Error) override {
+ if (!Kept.empty() && TestNamedMDOps(Kept))
+ return KeepSuffix;
+ if (!Prefix.empty() && TestNamedMDOps(Prefix))
+ return KeepPrefix;
+ return NoFailure;
+ }
+
+ bool TestNamedMDOps(std::vector<const MDNode *> &NamedMDOps);
+};
+}
+
+bool ReduceCrashingNamedMDOps::TestNamedMDOps(
+ std::vector<const MDNode *> &NamedMDOps) {
+ // Convert list to set for fast lookup...
+ SmallPtrSet<const MDNode *, 64> OldMDNodeOps;
+ for (unsigned i = 0, e = NamedMDOps.size(); i != e; ++i) {
+ OldMDNodeOps.insert(NamedMDOps[i]);
+ }
+
+ outs() << "Checking for crash with only " << OldMDNodeOps.size();
+ if (OldMDNodeOps.size() == 1)
+ outs() << " named metadata operand: ";
+ else
+ outs() << " named metadata operands: ";
+
+ ValueToValueMapTy VMap;
+ Module *M = CloneModule(BD.getProgram(), VMap).release();
+
+ // This is a little wasteful. In the future it might be good if we could have
+ // these dropped during cloning.
+ for (auto &NamedMD : BD.getProgram()->named_metadata()) {
+ // Drop the old one and create a new one
+ M->eraseNamedMetadata(M->getNamedMetadata(NamedMD.getName()));
+ NamedMDNode *NewNamedMDNode =
+ M->getOrInsertNamedMetadata(NamedMD.getName());
+ for (MDNode *op : NamedMD.operands())
+ if (OldMDNodeOps.count(op))
+ NewNamedMDNode->addOperand(cast<MDNode>(MapMetadata(op, VMap)));
+ }
+
+ // Verify that this is still valid.
+ legacy::PassManager Passes;
+ Passes.add(createVerifierPass());
+ Passes.run(*M);
+
+ // Try running on the hacked up program...
+ if (TestFn(BD, M)) {
+ // Make sure to use instruction pointers that point into the now-current
+ // module, and that they don't include any deleted blocks.
+ NamedMDOps.clear();
+ for (const MDNode *Node : OldMDNodeOps)
+ NamedMDOps.push_back(cast<MDNode>(VMap.MD()[Node].get()));
+
+ BD.setNewProgram(M); // It crashed, keep the trimmed version...
+ return true;
+ }
+ delete M; // It didn't crash, try something else.
+ return false;
+}
+
/// DebugACrash - Given a predicate that determines whether a component crashes
/// on a program, try to destructively reduce the program while still keeping
/// the predicate true.
@@ -509,13 +658,13 @@ static bool DebugACrash(BugDriver &BD,
BD.getProgram()->global_begin() != BD.getProgram()->global_end()) {
// Now try to reduce the number of global variable initializers in the
// module to something small.
- Module *M = CloneModule(BD.getProgram());
+ Module *M = CloneModule(BD.getProgram()).release();
bool DeletedInit = false;
for (Module::global_iterator I = M->global_begin(), E = M->global_end();
I != E; ++I)
if (I->hasInitializer()) {
- I->setInitializer(nullptr);
+ DeleteGlobalInitializer(&*I);
I->setLinkage(GlobalValue::ExternalLinkage);
DeletedInit = true;
}
@@ -538,7 +687,7 @@ static bool DebugACrash(BugDriver &BD,
for (Module::global_iterator I = BD.getProgram()->global_begin(),
E = BD.getProgram()->global_end(); I != E; ++I)
if (I->hasInitializer())
- GVs.push_back(I);
+ GVs.push_back(&*I);
if (GVs.size() > 1 && !BugpointIsInterrupted) {
outs() << "\n*** Attempting to reduce the number of global "
@@ -558,10 +707,9 @@ static bool DebugACrash(BugDriver &BD,
// Now try to reduce the number of functions in the module to something small.
std::vector<Function*> Functions;
- for (Module::iterator I = BD.getProgram()->begin(),
- E = BD.getProgram()->end(); I != E; ++I)
- if (!I->isDeclaration())
- Functions.push_back(I);
+ for (Function &F : *BD.getProgram())
+ if (!F.isDeclaration())
+ Functions.push_back(&F);
if (Functions.size() > 1 && !BugpointIsInterrupted) {
outs() << "\n*** Attempting to reduce the number of functions "
@@ -581,10 +729,9 @@ static bool DebugACrash(BugDriver &BD,
//
if (!DisableSimplifyCFG && !BugpointIsInterrupted) {
std::vector<const BasicBlock*> Blocks;
- for (Module::const_iterator I = BD.getProgram()->begin(),
- E = BD.getProgram()->end(); I != E; ++I)
- for (Function::const_iterator FI = I->begin(), E = I->end(); FI !=E; ++FI)
- Blocks.push_back(FI);
+ for (Function &F : *BD.getProgram())
+ for (BasicBlock &BB : F)
+ Blocks.push_back(&BB);
unsigned OldSize = Blocks.size();
ReduceCrashingBlocks(BD, TestFn).reduceList(Blocks, Error);
if (Blocks.size() < OldSize)
@@ -595,14 +742,11 @@ static bool DebugACrash(BugDriver &BD,
// cases with large basic blocks where the problem is at one end.
if (!BugpointIsInterrupted) {
std::vector<const Instruction*> Insts;
- for (Module::const_iterator MI = BD.getProgram()->begin(),
- ME = BD.getProgram()->end(); MI != ME; ++MI)
- for (Function::const_iterator FI = MI->begin(), FE = MI->end(); FI != FE;
- ++FI)
- for (BasicBlock::const_iterator I = FI->begin(), E = FI->end();
- I != E; ++I)
- if (!isa<TerminatorInst>(I))
- Insts.push_back(I);
+ for (const Function &F : *BD.getProgram())
+ for (const BasicBlock &BB : F)
+ for (const Instruction &I : BB)
+ if (!isa<TerminatorInst>(&I))
+ Insts.push_back(&I);
ReduceCrashingInstructions(BD, TestFn).reduceList(Insts, Error);
}
@@ -642,12 +786,12 @@ static bool DebugACrash(BugDriver &BD,
} else {
if (BugpointIsInterrupted) goto ExitLoops;
- if (isa<LandingPadInst>(I))
+ if (I->isEHPad() || I->getType()->isTokenTy())
continue;
outs() << "Checking instruction: " << *I;
std::unique_ptr<Module> M =
- BD.deleteInstructionFromProgram(I, Simplification);
+ BD.deleteInstructionFromProgram(&*I, Simplification);
// Find out if the pass still crashes on this pass...
if (TestFn(BD, M.get())) {
@@ -666,12 +810,37 @@ static bool DebugACrash(BugDriver &BD,
}
} while (Simplification);
+
+ if (!NoNamedMDRM) {
+ BD.EmitProgressBitcode(BD.getProgram(), "reduced-instructions");
+
+ if (!BugpointIsInterrupted) {
+ // Try to reduce the amount of global metadata (particularly debug info),
+ // by dropping global named metadata that anchors them
+ outs() << "\n*** Attempting to remove named metadata: ";
+ std::vector<std::string> NamedMDNames;
+ for (auto &NamedMD : BD.getProgram()->named_metadata())
+ NamedMDNames.push_back(NamedMD.getName().str());
+ ReduceCrashingNamedMD(BD, TestFn).reduceList(NamedMDNames, Error);
+ }
+
+ if (!BugpointIsInterrupted) {
+ // Now that we quickly dropped all the named metadata that doesn't
+ // contribute to the crash, bisect the operands of the remaining ones
+ std::vector<const MDNode *> NamedMDOps;
+ for (auto &NamedMD : BD.getProgram()->named_metadata())
+ for (auto op : NamedMD.operands())
+ NamedMDOps.push_back(op);
+ ReduceCrashingNamedMDOps(BD, TestFn).reduceList(NamedMDOps, Error);
+ }
+ }
+
ExitLoops:
// Try to clean up the testcase by running funcresolve and globaldce...
if (!BugpointIsInterrupted) {
outs() << "\n*** Attempting to perform final cleanups: ";
- Module *M = CloneModule(BD.getProgram());
+ Module *M = CloneModule(BD.getProgram()).release();
M = BD.performFinalCleanups(M, true).release();
// Find out if the pass still crashes on the cleaned up program...
diff --git a/tools/bugpoint/ExecutionDriver.cpp b/tools/bugpoint/ExecutionDriver.cpp
index 25813b34e62d..41b8ccc18e87 100644
--- a/tools/bugpoint/ExecutionDriver.cpp
+++ b/tools/bugpoint/ExecutionDriver.cpp
@@ -17,6 +17,7 @@
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/FileUtilities.h"
+#include "llvm/Support/Program.h"
#include "llvm/Support/SystemUtils.h"
#include "llvm/Support/raw_ostream.h"
#include <fstream>
@@ -124,11 +125,10 @@ namespace {
cl::ZeroOrMore, cl::PositionalEatsArgs);
cl::opt<std::string>
- GCCBinary("gcc", cl::init("gcc"),
- cl::desc("The gcc binary to use. (default 'gcc')"));
+ CCBinary("gcc", cl::init(""), cl::desc("The gcc binary to use."));
cl::list<std::string>
- GCCToolArgv("gcc-tool-args", cl::Positional,
+ CCToolArgv("gcc-tool-args", cl::Positional,
cl::desc("<gcc-tool arguments>..."),
cl::ZeroOrMore, cl::PositionalEatsArgs);
}
@@ -148,6 +148,13 @@ bool BugDriver::initializeExecutionEnvironment() {
SafeInterpreter = nullptr;
std::string Message;
+ if (CCBinary.empty()) {
+ if (sys::findProgramByName("clang"))
+ CCBinary = "clang";
+ else
+ CCBinary = "gcc";
+ }
+
switch (InterpreterSel) {
case AutoPick:
if (!Interpreter) {
@@ -158,8 +165,8 @@ bool BugDriver::initializeExecutionEnvironment() {
if (!Interpreter) {
InterpreterSel = RunLLC;
Interpreter = AbstractInterpreter::createLLC(getToolName(), Message,
- GCCBinary, &ToolArgv,
- &GCCToolArgv);
+ CCBinary, &ToolArgv,
+ &CCToolArgv);
}
if (!Interpreter) {
InterpreterSel = RunLLI;
@@ -179,8 +186,8 @@ bool BugDriver::initializeExecutionEnvironment() {
case RunLLCIA:
case LLC_Safe:
Interpreter = AbstractInterpreter::createLLC(getToolName(), Message,
- GCCBinary, &ToolArgv,
- &GCCToolArgv,
+ CCBinary, &ToolArgv,
+ &CCToolArgv,
InterpreterSel == RunLLCIA);
break;
case RunJIT:
@@ -213,9 +220,9 @@ bool BugDriver::initializeExecutionEnvironment() {
SafeInterpreterSel = RunLLC;
SafeToolArgs.push_back("--relocation-model=pic");
SafeInterpreter = AbstractInterpreter::createLLC(Path.c_str(), Message,
- GCCBinary,
+ CCBinary,
&SafeToolArgs,
- &GCCToolArgv);
+ &CCToolArgv);
}
if (!SafeInterpreter &&
@@ -224,9 +231,9 @@ bool BugDriver::initializeExecutionEnvironment() {
SafeInterpreterSel = RunLLC;
SafeToolArgs.push_back("--relocation-model=pic");
SafeInterpreter = AbstractInterpreter::createLLC(Path.c_str(), Message,
- GCCBinary,
+ CCBinary,
&SafeToolArgs,
- &GCCToolArgv);
+ &CCToolArgv);
}
if (!SafeInterpreter) {
SafeInterpreterSel = AutoPick;
@@ -237,8 +244,8 @@ bool BugDriver::initializeExecutionEnvironment() {
case RunLLCIA:
SafeToolArgs.push_back("--relocation-model=pic");
SafeInterpreter = AbstractInterpreter::createLLC(Path.c_str(), Message,
- GCCBinary, &SafeToolArgs,
- &GCCToolArgv,
+ CCBinary, &SafeToolArgs,
+ &CCToolArgv,
SafeInterpreterSel == RunLLCIA);
break;
case Custom:
@@ -252,8 +259,8 @@ bool BugDriver::initializeExecutionEnvironment() {
}
if (!SafeInterpreter) { outs() << Message << "\nExiting.\n"; exit(1); }
- gcc = GCC::create(Message, GCCBinary, &GCCToolArgv);
- if (!gcc) { outs() << Message << "\nExiting.\n"; exit(1); }
+ cc = CC::create(Message, CCBinary, &CCToolArgv);
+ if (!cc) { outs() << Message << "\nExiting.\n"; exit(1); }
// If there was an error creating the selected interpreter, quit with error.
return Interpreter == nullptr;
@@ -388,13 +395,13 @@ std::string BugDriver::compileSharedObject(const std::string &BitcodeFile,
std::string OutputFile;
// Using the known-good backend.
- GCC::FileType FT = SafeInterpreter->OutputCode(BitcodeFile, OutputFile,
+ CC::FileType FT = SafeInterpreter->OutputCode(BitcodeFile, OutputFile,
Error);
if (!Error.empty())
return "";
std::string SharedObjectFile;
- bool Failure = gcc->MakeSharedObject(OutputFile, FT, SharedObjectFile,
+ bool Failure = cc->MakeSharedObject(OutputFile, FT, SharedObjectFile,
AdditionalLinkerArgs, Error);
if (!Error.empty())
return "";
diff --git a/tools/bugpoint/ExtractFunction.cpp b/tools/bugpoint/ExtractFunction.cpp
index 238cbbc70a03..fe0ab69dc162 100644
--- a/tools/bugpoint/ExtractFunction.cpp
+++ b/tools/bugpoint/ExtractFunction.cpp
@@ -86,7 +86,7 @@ std::unique_ptr<Module>
BugDriver::deleteInstructionFromProgram(const Instruction *I,
unsigned Simplification) {
// FIXME, use vmap?
- Module *Clone = CloneModule(Program);
+ Module *Clone = CloneModule(Program).release();
const BasicBlock *PBB = I->getParent();
const Function *PF = PBB->getParent();
@@ -100,7 +100,7 @@ BugDriver::deleteInstructionFromProgram(const Instruction *I,
BasicBlock::iterator RI = RBI->begin(); // Get iterator to corresponding inst
std::advance(RI, std::distance(PBB->begin(), BasicBlock::const_iterator(I)));
- Instruction *TheInst = RI; // Got the corresponding instruction!
+ Instruction *TheInst = &*RI; // Got the corresponding instruction!
// If this instruction produces a value, replace any users with null values
if (!TheInst->getType()->isVoidTy())
@@ -179,11 +179,43 @@ std::unique_ptr<Module> BugDriver::extractLoop(Module *M) {
return NewM;
}
+static void eliminateAliases(GlobalValue *GV) {
+ // First, check whether a GlobalAlias references this definition.
+ // GlobalAlias MAY NOT reference declarations.
+ for (;;) {
+ // 1. Find aliases
+ SmallVector<GlobalAlias*,1> aliases;
+ Module *M = GV->getParent();
+ for (Module::alias_iterator I=M->alias_begin(), E=M->alias_end(); I!=E; ++I)
+ if (I->getAliasee()->stripPointerCasts() == GV)
+ aliases.push_back(&*I);
+ if (aliases.empty())
+ break;
+ // 2. Resolve aliases
+ for (unsigned i=0, e=aliases.size(); i<e; ++i) {
+ aliases[i]->replaceAllUsesWith(aliases[i]->getAliasee());
+ aliases[i]->eraseFromParent();
+ }
+ // 3. Repeat until no more aliases found; there might
+ // be an alias to an alias...
+ }
+}
+
+//
+// DeleteGlobalInitializer - "Remove" the global variable by deleting its initializer,
+// making it external.
+//
+void llvm::DeleteGlobalInitializer(GlobalVariable *GV) {
+ eliminateAliases(GV);
+ GV->setInitializer(nullptr);
+}
// DeleteFunctionBody - "Remove" the function by deleting all of its basic
// blocks, making it external.
//
void llvm::DeleteFunctionBody(Function *F) {
+ eliminateAliases(F);
+
// delete the body of the function...
F->deleteBody();
assert(F->isDeclaration() && "This didn't make the function external!");
@@ -271,13 +303,8 @@ static void SplitStaticCtorDtor(const char *GlobalName, Module *M1, Module *M2,
}
}
-
-/// SplitFunctionsOutOfModule - Given a module and a list of functions in the
-/// module, split the functions OUT of the specified module, and place them in
-/// the new module.
-Module *
-llvm::SplitFunctionsOutOfModule(Module *M,
- const std::vector<Function*> &F,
+std::unique_ptr<Module>
+llvm::SplitFunctionsOutOfModule(Module *M, const std::vector<Function *> &F,
ValueToValueMapTy &VMap) {
// Make sure functions & globals are all external so that linkage
// between the two modules will work.
@@ -291,7 +318,7 @@ llvm::SplitFunctionsOutOfModule(Module *M,
}
ValueToValueMapTy NewVMap;
- Module *New = CloneModule(M, NewVMap);
+ std::unique_ptr<Module> New = CloneModule(M, NewVMap);
// Remove the Test functions from the Safe module
std::set<Function *> TestFunctions;
@@ -306,16 +333,14 @@ llvm::SplitFunctionsOutOfModule(Module *M,
// Remove the Safe functions from the Test module
- for (Module::iterator I = New->begin(), E = New->end(); I != E; ++I)
- if (!TestFunctions.count(I))
- DeleteFunctionBody(I);
-
+ for (Function &I : *New)
+ if (!TestFunctions.count(&I))
+ DeleteFunctionBody(&I);
// Try to split the global initializers evenly
- for (Module::global_iterator I = M->global_begin(), E = M->global_end();
- I != E; ++I) {
- GlobalVariable *GV = cast<GlobalVariable>(NewVMap[I]);
- if (Function *TestFn = globalInitUsesExternalBA(I)) {
+ for (GlobalVariable &I : M->globals()) {
+ GlobalVariable *GV = cast<GlobalVariable>(NewVMap[&I]);
+ if (Function *TestFn = globalInitUsesExternalBA(&I)) {
if (Function *SafeFn = globalInitUsesExternalBA(GV)) {
errs() << "*** Error: when reducing functions, encountered "
"the global '";
@@ -325,18 +350,18 @@ llvm::SplitFunctionsOutOfModule(Module *M,
<< "' and from test function '" << TestFn->getName() << "'.\n";
exit(1);
}
- I->setInitializer(nullptr); // Delete the initializer to make it external
+ DeleteGlobalInitializer(&I); // Delete the initializer to make it external
} else {
// If we keep it in the safe module, then delete it in the test module
- GV->setInitializer(nullptr);
+ DeleteGlobalInitializer(GV);
}
}
// Make sure that there is a global ctor/dtor array in both halves of the
// module if they both have static ctor/dtor functions.
- SplitStaticCtorDtor("llvm.global_ctors", M, New, NewVMap);
- SplitStaticCtorDtor("llvm.global_dtors", M, New, NewVMap);
-
+ SplitStaticCtorDtor("llvm.global_ctors", M, New.get(), NewVMap);
+ SplitStaticCtorDtor("llvm.global_dtors", M, New.get(), NewVMap);
+
return New;
}
diff --git a/tools/bugpoint/ListReducer.h b/tools/bugpoint/ListReducer.h
index a0bb570a5cb2..f08bc97a9988 100644
--- a/tools/bugpoint/ListReducer.h
+++ b/tools/bugpoint/ListReducer.h
@@ -75,6 +75,11 @@ struct ListReducer {
// Maximal number of allowed splitting iterations,
// before the elements are randomly shuffled.
const unsigned MaxIterationsWithoutProgress = 3;
+
+ // Maximal number of allowed single-element trim iterations. We add a
+ // threshhold here as single-element reductions may otherwise take a
+ // very long time to complete.
+ const unsigned MaxTrimIterationsWithoutBackJump = 3;
bool ShufflingEnabled = true;
Backjump:
@@ -157,6 +162,7 @@ Backjump:
if (TheList.size() > 2) {
bool Changed = true;
std::vector<ElTy> EmptyList;
+ unsigned TrimIterations = 0;
while (Changed) { // Trimming loop.
Changed = false;
@@ -186,9 +192,9 @@ Backjump:
if (!Error.empty())
return true;
}
- // This can take a long time if left uncontrolled. For now, don't
- // iterate.
- break;
+ if (TrimIterations >= MaxTrimIterationsWithoutBackJump)
+ break;
+ TrimIterations++;
}
}
diff --git a/tools/bugpoint/Miscompilation.cpp b/tools/bugpoint/Miscompilation.cpp
index fad16368698d..16919f5a4492 100644
--- a/tools/bugpoint/Miscompilation.cpp
+++ b/tools/bugpoint/Miscompilation.cpp
@@ -176,12 +176,15 @@ ReduceMiscompilingPasses::doTest(std::vector<std::string> &Prefix,
namespace {
class ReduceMiscompilingFunctions : public ListReducer<Function*> {
BugDriver &BD;
- bool (*TestFn)(BugDriver &, Module *, Module *, std::string &);
+ bool (*TestFn)(BugDriver &, std::unique_ptr<Module>,
+ std::unique_ptr<Module>, std::string &);
+
public:
ReduceMiscompilingFunctions(BugDriver &bd,
- bool (*F)(BugDriver &, Module *, Module *,
+ bool (*F)(BugDriver &, std::unique_ptr<Module>,
+ std::unique_ptr<Module>,
std::string &))
- : BD(bd), TestFn(F) {}
+ : BD(bd), TestFn(F) {}
TestResult doTest(std::vector<Function*> &Prefix,
std::vector<Function*> &Suffix,
@@ -207,32 +210,24 @@ namespace {
};
}
-/// TestMergedProgram - Given two modules, link them together and run the
-/// program, checking to see if the program matches the diff. If there is
-/// an error, return NULL. If not, return the merged module. The Broken argument
-/// will be set to true if the output is different. If the DeleteInputs
-/// argument is set to true then this function deletes both input
-/// modules before it returns.
+/// Given two modules, link them together and run the program, checking to see
+/// if the program matches the diff. If there is an error, return NULL. If not,
+/// return the merged module. The Broken argument will be set to true if the
+/// output is different. If the DeleteInputs argument is set to true then this
+/// function deletes both input modules before it returns.
///
-static Module *TestMergedProgram(const BugDriver &BD, Module *M1, Module *M2,
- bool DeleteInputs, std::string &Error,
- bool &Broken) {
- // Link the two portions of the program back to together.
- if (!DeleteInputs) {
- M1 = CloneModule(M1);
- M2 = CloneModule(M2);
- }
- if (Linker::LinkModules(M1, M2))
+static std::unique_ptr<Module> testMergedProgram(const BugDriver &BD,
+ std::unique_ptr<Module> M1,
+ std::unique_ptr<Module> M2,
+ std::string &Error,
+ bool &Broken) {
+ if (Linker::linkModules(*M1, std::move(M2)))
exit(1);
- delete M2; // We are done with this module.
// Execute the program.
- Broken = BD.diffProgram(M1, "", "", false, &Error);
- if (!Error.empty()) {
- // Delete the linked module
- delete M1;
+ Broken = BD.diffProgram(M1.get(), "", "", false, &Error);
+ if (!Error.empty())
return nullptr;
- }
return M1;
}
@@ -259,7 +254,7 @@ bool ReduceMiscompilingFunctions::TestFuncs(const std::vector<Function*> &Funcs,
// we can conclude that a function triggers the bug when in fact one
// needs a larger set of original functions to do so.
ValueToValueMapTy VMap;
- Module *Clone = CloneModule(BD.getProgram(), VMap);
+ Module *Clone = CloneModule(BD.getProgram(), VMap).release();
Module *Orig = BD.swapProgramIn(Clone);
std::vector<Function*> FuncsOnClone;
@@ -270,12 +265,12 @@ bool ReduceMiscompilingFunctions::TestFuncs(const std::vector<Function*> &Funcs,
// Split the module into the two halves of the program we want.
VMap.clear();
- Module *ToNotOptimize = CloneModule(BD.getProgram(), VMap);
- Module *ToOptimize = SplitFunctionsOutOfModule(ToNotOptimize, FuncsOnClone,
- VMap);
+ std::unique_ptr<Module> ToNotOptimize = CloneModule(BD.getProgram(), VMap);
+ std::unique_ptr<Module> ToOptimize =
+ SplitFunctionsOutOfModule(ToNotOptimize.get(), FuncsOnClone, VMap);
- // Run the predicate, note that the predicate will delete both input modules.
- bool Broken = TestFn(BD, ToOptimize, ToNotOptimize, Error);
+ bool Broken =
+ TestFn(BD, std::move(ToOptimize), std::move(ToNotOptimize), Error);
delete BD.swapProgramIn(Orig);
@@ -294,29 +289,29 @@ static void DisambiguateGlobalSymbols(Module *M) {
I->setName("anon_fn");
}
-/// ExtractLoops - Given a reduced list of functions that still exposed the bug,
-/// check to see if we can extract the loops in the region without obscuring the
-/// bug. If so, it reduces the amount of code identified.
+/// Given a reduced list of functions that still exposed the bug, check to see
+/// if we can extract the loops in the region without obscuring the bug. If so,
+/// it reduces the amount of code identified.
///
static bool ExtractLoops(BugDriver &BD,
- bool (*TestFn)(BugDriver &, Module *, Module *,
- std::string &),
- std::vector<Function*> &MiscompiledFunctions,
+ bool (*TestFn)(BugDriver &, std::unique_ptr<Module>,
+ std::unique_ptr<Module>, std::string &),
+ std::vector<Function *> &MiscompiledFunctions,
std::string &Error) {
bool MadeChange = false;
while (1) {
if (BugpointIsInterrupted) return MadeChange;
ValueToValueMapTy VMap;
- Module *ToNotOptimize = CloneModule(BD.getProgram(), VMap);
- Module *ToOptimize = SplitFunctionsOutOfModule(ToNotOptimize,
- MiscompiledFunctions,
- VMap);
- Module *ToOptimizeLoopExtracted = BD.extractLoop(ToOptimize).release();
+ std::unique_ptr<Module> ToNotOptimize = CloneModule(BD.getProgram(), VMap);
+ Module *ToOptimize = SplitFunctionsOutOfModule(ToNotOptimize.get(),
+ MiscompiledFunctions, VMap)
+ .release();
+ std::unique_ptr<Module> ToOptimizeLoopExtracted =
+ BD.extractLoop(ToOptimize);
if (!ToOptimizeLoopExtracted) {
// If the loop extractor crashed or if there were no extractible loops,
// then this chapter of our odyssey is over with.
- delete ToNotOptimize;
delete ToOptimize;
return MadeChange;
}
@@ -330,13 +325,14 @@ static bool ExtractLoops(BugDriver &BD,
// extraction.
AbstractInterpreter *AI = BD.switchToSafeInterpreter();
bool Failure;
- Module *New = TestMergedProgram(BD, ToOptimizeLoopExtracted,
- ToNotOptimize, false, Error, Failure);
+ std::unique_ptr<Module> New =
+ testMergedProgram(BD, std::move(ToOptimizeLoopExtracted),
+ std::move(ToNotOptimize), Error, Failure);
if (!New)
return false;
// Delete the original and set the new program.
- Module *Old = BD.swapProgramIn(New);
+ Module *Old = BD.swapProgramIn(New.release());
for (unsigned i = 0, e = MiscompiledFunctions.size(); i != e; ++i)
MiscompiledFunctions[i] = cast<Function>(VMap[MiscompiledFunctions[i]]);
delete Old;
@@ -350,16 +346,15 @@ static bool ExtractLoops(BugDriver &BD,
errs() << " Continuing on with un-loop-extracted version.\n";
BD.writeProgramToFile(OutputPrefix + "-loop-extract-fail-tno.bc",
- ToNotOptimize);
+ ToNotOptimize.get());
BD.writeProgramToFile(OutputPrefix + "-loop-extract-fail-to.bc",
ToOptimize);
BD.writeProgramToFile(OutputPrefix + "-loop-extract-fail-to-le.bc",
- ToOptimizeLoopExtracted);
+ ToOptimizeLoopExtracted.get());
errs() << "Please submit the "
<< OutputPrefix << "-loop-extract-fail-*.bc files.\n";
delete ToOptimize;
- delete ToNotOptimize;
return MadeChange;
}
delete ToOptimize;
@@ -367,18 +362,20 @@ static bool ExtractLoops(BugDriver &BD,
outs() << " Testing after loop extraction:\n";
// Clone modules, the tester function will free them.
- Module *TOLEBackup = CloneModule(ToOptimizeLoopExtracted, VMap);
- Module *TNOBackup = CloneModule(ToNotOptimize, VMap);
+ std::unique_ptr<Module> TOLEBackup =
+ CloneModule(ToOptimizeLoopExtracted.get(), VMap);
+ std::unique_ptr<Module> TNOBackup = CloneModule(ToNotOptimize.get(), VMap);
for (unsigned i = 0, e = MiscompiledFunctions.size(); i != e; ++i)
MiscompiledFunctions[i] = cast<Function>(VMap[MiscompiledFunctions[i]]);
- Failure = TestFn(BD, ToOptimizeLoopExtracted, ToNotOptimize, Error);
+ Failure = TestFn(BD, std::move(ToOptimizeLoopExtracted),
+ std::move(ToNotOptimize), Error);
if (!Error.empty())
return false;
- ToOptimizeLoopExtracted = TOLEBackup;
- ToNotOptimize = TNOBackup;
+ ToOptimizeLoopExtracted = std::move(TOLEBackup);
+ ToNotOptimize = std::move(TNOBackup);
if (!Failure) {
outs() << "*** Loop extraction masked the problem. Undoing.\n";
@@ -390,7 +387,8 @@ static bool ExtractLoops(BugDriver &BD,
MisCompFunctions.emplace_back(F->getName(), F->getFunctionType());
}
- if (Linker::LinkModules(ToNotOptimize, ToOptimizeLoopExtracted))
+ if (Linker::linkModules(*ToNotOptimize,
+ std::move(ToOptimizeLoopExtracted)))
exit(1);
MiscompiledFunctions.clear();
@@ -401,8 +399,7 @@ static bool ExtractLoops(BugDriver &BD,
MiscompiledFunctions.push_back(NewF);
}
- delete ToOptimizeLoopExtracted;
- BD.setNewProgram(ToNotOptimize);
+ BD.setNewProgram(ToNotOptimize.release());
return MadeChange;
}
@@ -418,11 +415,9 @@ static bool ExtractLoops(BugDriver &BD,
// extraction both didn't break the program, and didn't mask the problem.
// Replace the current program with the loop extracted version, and try to
// extract another loop.
- if (Linker::LinkModules(ToNotOptimize, ToOptimizeLoopExtracted))
+ if (Linker::linkModules(*ToNotOptimize, std::move(ToOptimizeLoopExtracted)))
exit(1);
- delete ToOptimizeLoopExtracted;
-
// All of the Function*'s in the MiscompiledFunctions list are in the old
// module. Update this list to include all of the functions in the
// optimized and loop extracted module.
@@ -434,7 +429,7 @@ static bool ExtractLoops(BugDriver &BD,
MiscompiledFunctions.push_back(NewF);
}
- BD.setNewProgram(ToNotOptimize);
+ BD.setNewProgram(ToNotOptimize.release());
MadeChange = true;
}
}
@@ -442,14 +437,15 @@ static bool ExtractLoops(BugDriver &BD,
namespace {
class ReduceMiscompiledBlocks : public ListReducer<BasicBlock*> {
BugDriver &BD;
- bool (*TestFn)(BugDriver &, Module *, Module *, std::string &);
+ bool (*TestFn)(BugDriver &, std::unique_ptr<Module>,
+ std::unique_ptr<Module>, std::string &);
std::vector<Function*> FunctionsBeingTested;
public:
ReduceMiscompiledBlocks(BugDriver &bd,
- bool (*F)(BugDriver &, Module *, Module *,
- std::string &),
- const std::vector<Function*> &Fns)
- : BD(bd), TestFn(F), FunctionsBeingTested(Fns) {}
+ bool (*F)(BugDriver &, std::unique_ptr<Module>,
+ std::unique_ptr<Module>, std::string &),
+ const std::vector<Function *> &Fns)
+ : BD(bd), TestFn(F), FunctionsBeingTested(Fns) {}
TestResult doTest(std::vector<BasicBlock*> &Prefix,
std::vector<BasicBlock*> &Suffix,
@@ -495,7 +491,7 @@ bool ReduceMiscompiledBlocks::TestFuncs(const std::vector<BasicBlock*> &BBs,
// Split the module into the two halves of the program we want.
ValueToValueMapTy VMap;
- Module *Clone = CloneModule(BD.getProgram(), VMap);
+ Module *Clone = CloneModule(BD.getProgram(), VMap).release();
Module *Orig = BD.swapProgramIn(Clone);
std::vector<Function*> FuncsOnClone;
std::vector<BasicBlock*> BBsOnClone;
@@ -509,45 +505,37 @@ bool ReduceMiscompiledBlocks::TestFuncs(const std::vector<BasicBlock*> &BBs,
}
VMap.clear();
- Module *ToNotOptimize = CloneModule(BD.getProgram(), VMap);
- Module *ToOptimize = SplitFunctionsOutOfModule(ToNotOptimize,
- FuncsOnClone,
- VMap);
+ std::unique_ptr<Module> ToNotOptimize = CloneModule(BD.getProgram(), VMap);
+ std::unique_ptr<Module> ToOptimize =
+ SplitFunctionsOutOfModule(ToNotOptimize.get(), FuncsOnClone, VMap);
// Try the extraction. If it doesn't work, then the block extractor crashed
// or something, in which case bugpoint can't chase down this possibility.
if (std::unique_ptr<Module> New =
- BD.extractMappedBlocksFromModule(BBsOnClone, ToOptimize)) {
- delete ToOptimize;
- // Run the predicate,
- // note that the predicate will delete both input modules.
- bool Ret = TestFn(BD, New.get(), ToNotOptimize, Error);
+ BD.extractMappedBlocksFromModule(BBsOnClone, ToOptimize.get())) {
+ bool Ret = TestFn(BD, std::move(New), std::move(ToNotOptimize), Error);
delete BD.swapProgramIn(Orig);
return Ret;
}
delete BD.swapProgramIn(Orig);
- delete ToOptimize;
- delete ToNotOptimize;
return false;
}
-
-/// ExtractBlocks - Given a reduced list of functions that still expose the bug,
-/// extract as many basic blocks from the region as possible without obscuring
-/// the bug.
+/// Given a reduced list of functions that still expose the bug, extract as many
+/// basic blocks from the region as possible without obscuring the bug.
///
static bool ExtractBlocks(BugDriver &BD,
- bool (*TestFn)(BugDriver &, Module *, Module *,
+ bool (*TestFn)(BugDriver &, std::unique_ptr<Module>,
+ std::unique_ptr<Module>,
std::string &),
- std::vector<Function*> &MiscompiledFunctions,
+ std::vector<Function *> &MiscompiledFunctions,
std::string &Error) {
if (BugpointIsInterrupted) return false;
std::vector<BasicBlock*> Blocks;
for (unsigned i = 0, e = MiscompiledFunctions.size(); i != e; ++i)
- for (Function::iterator I = MiscompiledFunctions[i]->begin(),
- E = MiscompiledFunctions[i]->end(); I != E; ++I)
- Blocks.push_back(I);
+ for (BasicBlock &BB : *MiscompiledFunctions[i])
+ Blocks.push_back(&BB);
// Use the list reducer to identify blocks that can be extracted without
// obscuring the bug. The Blocks list will end up containing blocks that must
@@ -571,10 +559,10 @@ static bool ExtractBlocks(BugDriver &BD,
}
ValueToValueMapTy VMap;
- Module *ProgClone = CloneModule(BD.getProgram(), VMap);
- Module *ToExtract = SplitFunctionsOutOfModule(ProgClone,
- MiscompiledFunctions,
- VMap);
+ Module *ProgClone = CloneModule(BD.getProgram(), VMap).release();
+ Module *ToExtract =
+ SplitFunctionsOutOfModule(ProgClone, MiscompiledFunctions, VMap)
+ .release();
std::unique_ptr<Module> Extracted =
BD.extractMappedBlocksFromModule(Blocks, ToExtract);
if (!Extracted) {
@@ -595,7 +583,7 @@ static bool ExtractBlocks(BugDriver &BD,
if (!I->isDeclaration())
MisCompFunctions.emplace_back(I->getName(), I->getFunctionType());
- if (Linker::LinkModules(ProgClone, Extracted.get()))
+ if (Linker::linkModules(*ProgClone, std::move(Extracted)))
exit(1);
// Set the new program and delete the old one.
@@ -613,14 +601,13 @@ static bool ExtractBlocks(BugDriver &BD,
return true;
}
-
-/// DebugAMiscompilation - This is a generic driver to narrow down
-/// miscompilations, either in an optimization or a code generator.
+/// This is a generic driver to narrow down miscompilations, either in an
+/// optimization or a code generator.
///
-static std::vector<Function*>
+static std::vector<Function *>
DebugAMiscompilation(BugDriver &BD,
- bool (*TestFn)(BugDriver &, Module *, Module *,
- std::string &),
+ bool (*TestFn)(BugDriver &, std::unique_ptr<Module>,
+ std::unique_ptr<Module>, std::string &),
std::string &Error) {
// Okay, now that we have reduced the list of passes which are causing the
// failure, see if we can pin down which functions are being
@@ -628,9 +615,9 @@ DebugAMiscompilation(BugDriver &BD,
// the program.
std::vector<Function*> MiscompiledFunctions;
Module *Prog = BD.getProgram();
- for (Module::iterator I = Prog->begin(), E = Prog->end(); I != E; ++I)
- if (!I->isDeclaration())
- MiscompiledFunctions.push_back(I);
+ for (Function &F : *Prog)
+ if (!F.isDeclaration())
+ MiscompiledFunctions.push_back(&F);
// Do the reduction...
if (!BugpointIsInterrupted)
@@ -699,28 +686,28 @@ DebugAMiscompilation(BugDriver &BD,
return MiscompiledFunctions;
}
-/// TestOptimizer - This is the predicate function used to check to see if the
-/// "Test" portion of the program is misoptimized. If so, return true. In any
-/// case, both module arguments are deleted.
+/// This is the predicate function used to check to see if the "Test" portion of
+/// the program is misoptimized. If so, return true. In any case, both module
+/// arguments are deleted.
///
-static bool TestOptimizer(BugDriver &BD, Module *Test, Module *Safe,
- std::string &Error) {
+static bool TestOptimizer(BugDriver &BD, std::unique_ptr<Module> Test,
+ std::unique_ptr<Module> Safe, std::string &Error) {
// Run the optimization passes on ToOptimize, producing a transformed version
// of the functions being tested.
outs() << " Optimizing functions being tested: ";
- std::unique_ptr<Module> Optimized = BD.runPassesOn(Test, BD.getPassesToRun(),
- /*AutoDebugCrashes*/ true);
+ std::unique_ptr<Module> Optimized =
+ BD.runPassesOn(Test.get(), BD.getPassesToRun(),
+ /*AutoDebugCrashes*/ true);
outs() << "done.\n";
- delete Test;
outs() << " Checking to see if the merged program executes correctly: ";
bool Broken;
- Module *New =
- TestMergedProgram(BD, Optimized.get(), Safe, true, Error, Broken);
+ std::unique_ptr<Module> New = testMergedProgram(
+ BD, std::move(Optimized), std::move(Safe), Error, Broken);
if (New) {
outs() << (Broken ? " nope.\n" : " yup.\n");
// Delete the original and set the new program.
- delete BD.swapProgramIn(New);
+ delete BD.swapProgramIn(New.release());
}
return Broken;
}
@@ -753,10 +740,10 @@ void BugDriver::debugMiscompilation(std::string *Error) {
// Output a bunch of bitcode files for the user...
outs() << "Outputting reduced bitcode files which expose the problem:\n";
ValueToValueMapTy VMap;
- Module *ToNotOptimize = CloneModule(getProgram(), VMap);
- Module *ToOptimize = SplitFunctionsOutOfModule(ToNotOptimize,
- MiscompiledFunctions,
- VMap);
+ Module *ToNotOptimize = CloneModule(getProgram(), VMap).release();
+ Module *ToOptimize =
+ SplitFunctionsOutOfModule(ToNotOptimize, MiscompiledFunctions, VMap)
+ .release();
outs() << " Non-optimized portion: ";
EmitProgressBitcode(ToNotOptimize, "tonotoptimize", true);
@@ -769,13 +756,13 @@ void BugDriver::debugMiscompilation(std::string *Error) {
return;
}
-/// CleanupAndPrepareModules - Get the specified modules ready for code
-/// generator testing.
+/// Get the specified modules ready for code generator testing.
///
-static void CleanupAndPrepareModules(BugDriver &BD, Module *&Test,
+static void CleanupAndPrepareModules(BugDriver &BD,
+ std::unique_ptr<Module> &Test,
Module *Safe) {
// Clean up the modules, removing extra cruft that we don't need anymore...
- Test = BD.performFinalCleanups(Test).release();
+ Test = BD.performFinalCleanups(Test.get());
// If we are executing the JIT, we have several nasty issues to take care of.
if (!BD.isExecutingJIT()) return;
@@ -788,21 +775,21 @@ static void CleanupAndPrepareModules(BugDriver &BD, Module *&Test,
// Rename it
oldMain->setName("llvm_bugpoint_old_main");
// Create a NEW `main' function with same type in the test module.
- Function *newMain = Function::Create(oldMain->getFunctionType(),
- GlobalValue::ExternalLinkage,
- "main", Test);
+ Function *newMain =
+ Function::Create(oldMain->getFunctionType(),
+ GlobalValue::ExternalLinkage, "main", Test.get());
// Create an `oldmain' prototype in the test module, which will
// corresponds to the real main function in the same module.
Function *oldMainProto = Function::Create(oldMain->getFunctionType(),
GlobalValue::ExternalLinkage,
- oldMain->getName(), Test);
+ oldMain->getName(), Test.get());
// Set up and remember the argument list for the main function.
std::vector<Value*> args;
for (Function::arg_iterator
I = newMain->arg_begin(), E = newMain->arg_end(),
OI = oldMain->arg_begin(); I != E; ++I, ++OI) {
I->setName(OI->getName()); // Copy argument names from oldMain
- args.push_back(I);
+ args.push_back(&*I);
}
// Call the old main function and return its result
@@ -905,9 +892,8 @@ static void CleanupAndPrepareModules(BugDriver &BD, Module *&Test,
// Save the argument list.
std::vector<Value*> Args;
- for (Function::arg_iterator i = FuncWrapper->arg_begin(),
- e = FuncWrapper->arg_end(); i != e; ++i)
- Args.push_back(i);
+ for (Argument &A : FuncWrapper->args())
+ Args.push_back(&A);
// Pass on the arguments to the real function, return its result
if (F->getReturnType()->isVoidTy()) {
@@ -932,15 +918,14 @@ static void CleanupAndPrepareModules(BugDriver &BD, Module *&Test,
}
}
-
-
-/// TestCodeGenerator - This is the predicate function used to check to see if
-/// the "Test" portion of the program is miscompiled by the code generator under
-/// test. If so, return true. In any case, both module arguments are deleted.
+/// This is the predicate function used to check to see if the "Test" portion of
+/// the program is miscompiled by the code generator under test. If so, return
+/// true. In any case, both module arguments are deleted.
///
-static bool TestCodeGenerator(BugDriver &BD, Module *Test, Module *Safe,
+static bool TestCodeGenerator(BugDriver &BD, std::unique_ptr<Module> Test,
+ std::unique_ptr<Module> Safe,
std::string &Error) {
- CleanupAndPrepareModules(BD, Test, Safe);
+ CleanupAndPrepareModules(BD, Test, Safe.get());
SmallString<128> TestModuleBC;
int TestModuleFD;
@@ -951,12 +936,11 @@ static bool TestCodeGenerator(BugDriver &BD, Module *Test, Module *Safe,
<< EC.message() << "\n";
exit(1);
}
- if (BD.writeProgramToFile(TestModuleBC.str(), TestModuleFD, Test)) {
+ if (BD.writeProgramToFile(TestModuleBC.str(), TestModuleFD, Test.get())) {
errs() << "Error writing bitcode to `" << TestModuleBC.str()
<< "'\nExiting.";
exit(1);
}
- delete Test;
FileRemover TestModuleBCRemover(TestModuleBC.str(), !SaveTemps);
@@ -971,7 +955,7 @@ static bool TestCodeGenerator(BugDriver &BD, Module *Test, Module *Safe,
exit(1);
}
- if (BD.writeProgramToFile(SafeModuleBC.str(), SafeModuleFD, Safe)) {
+ if (BD.writeProgramToFile(SafeModuleBC.str(), SafeModuleFD, Safe.get())) {
errs() << "Error writing bitcode to `" << SafeModuleBC
<< "'\nExiting.";
exit(1);
@@ -982,7 +966,6 @@ static bool TestCodeGenerator(BugDriver &BD, Module *Test, Module *Safe,
std::string SharedObject = BD.compileSharedObject(SafeModuleBC.str(), Error);
if (!Error.empty())
return false;
- delete Safe;
FileRemover SharedObjectRemover(SharedObject, !SaveTemps);
@@ -1030,11 +1013,12 @@ bool BugDriver::debugCodeGenerator(std::string *Error) {
// Split the module into the two halves of the program we want.
ValueToValueMapTy VMap;
- Module *ToNotCodeGen = CloneModule(getProgram(), VMap);
- Module *ToCodeGen = SplitFunctionsOutOfModule(ToNotCodeGen, Funcs, VMap);
+ std::unique_ptr<Module> ToNotCodeGen = CloneModule(getProgram(), VMap);
+ std::unique_ptr<Module> ToCodeGen =
+ SplitFunctionsOutOfModule(ToNotCodeGen.get(), Funcs, VMap);
// Condition the modules
- CleanupAndPrepareModules(*this, ToCodeGen, ToNotCodeGen);
+ CleanupAndPrepareModules(*this, ToCodeGen, ToNotCodeGen.get());
SmallString<128> TestModuleBC;
int TestModuleFD;
@@ -1046,12 +1030,11 @@ bool BugDriver::debugCodeGenerator(std::string *Error) {
exit(1);
}
- if (writeProgramToFile(TestModuleBC.str(), TestModuleFD, ToCodeGen)) {
+ if (writeProgramToFile(TestModuleBC.str(), TestModuleFD, ToCodeGen.get())) {
errs() << "Error writing bitcode to `" << TestModuleBC
<< "'\nExiting.";
exit(1);
}
- delete ToCodeGen;
// Make the shared library
SmallString<128> SafeModuleBC;
@@ -1064,7 +1047,8 @@ bool BugDriver::debugCodeGenerator(std::string *Error) {
exit(1);
}
- if (writeProgramToFile(SafeModuleBC.str(), SafeModuleFD, ToNotCodeGen)) {
+ if (writeProgramToFile(SafeModuleBC.str(), SafeModuleFD,
+ ToNotCodeGen.get())) {
errs() << "Error writing bitcode to `" << SafeModuleBC
<< "'\nExiting.";
exit(1);
@@ -1072,7 +1056,6 @@ bool BugDriver::debugCodeGenerator(std::string *Error) {
std::string SharedObject = compileSharedObject(SafeModuleBC.str(), *Error);
if (!Error->empty())
return true;
- delete ToNotCodeGen;
outs() << "You can reproduce the problem with the command line: \n";
if (isExecutingJIT()) {
@@ -1080,7 +1063,7 @@ bool BugDriver::debugCodeGenerator(std::string *Error) {
} else {
outs() << " llc " << TestModuleBC << " -o " << TestModuleBC
<< ".s\n";
- outs() << " gcc " << SharedObject << " " << TestModuleBC.str()
+ outs() << " cc " << SharedObject << " " << TestModuleBC.str()
<< ".s -o " << TestModuleBC << ".exe";
#if defined (HAVE_LINK_R)
outs() << " -Wl,-R.";
@@ -1093,7 +1076,7 @@ bool BugDriver::debugCodeGenerator(std::string *Error) {
outs() << '\n';
outs() << "The shared object was created with:\n llc -march=c "
<< SafeModuleBC.str() << " -o temporary.c\n"
- << " gcc -xc temporary.c -O2 -o " << SharedObject;
+ << " cc -xc temporary.c -O2 -o " << SharedObject;
if (TargetTriple.getArch() == Triple::sparc)
outs() << " -G"; // Compile a shared library, `-G' for Sparc
else
diff --git a/tools/bugpoint/ToolRunner.cpp b/tools/bugpoint/ToolRunner.cpp
index 51091e297d7d..2ccd64905128 100644
--- a/tools/bugpoint/ToolRunner.cpp
+++ b/tools/bugpoint/ToolRunner.cpp
@@ -64,16 +64,6 @@ static int RunProgramWithTimeout(StringRef ProgramPath,
unsigned MemoryLimit = 0,
std::string *ErrMsg = nullptr) {
const StringRef *Redirects[3] = { &StdInFile, &StdOutFile, &StdErrFile };
-
-#if 0 // For debug purposes
- {
- errs() << "RUN:";
- for (unsigned i = 0; Args[i]; ++i)
- errs() << " " << Args[i];
- errs() << "\n";
- }
-#endif
-
return sys::ExecuteAndWait(ProgramPath, Args, nullptr, Redirects,
NumSeconds, MemoryLimit, ErrMsg);
}
@@ -93,15 +83,6 @@ static int RunProgramRemotelyWithTimeout(StringRef RemoteClientPath,
unsigned MemoryLimit = 0) {
const StringRef *Redirects[3] = { &StdInFile, &StdOutFile, &StdErrFile };
-#if 0 // For debug purposes
- {
- errs() << "RUN:";
- for (unsigned i = 0; Args[i]; ++i)
- errs() << " " << Args[i];
- errs() << "\n";
- }
-#endif
-
// Run the program remotely with the remote client
int ReturnCode = sys::ExecuteAndWait(RemoteClientPath, Args, nullptr,
Redirects, NumSeconds, MemoryLimit);
@@ -152,7 +133,7 @@ static std::string ProcessFailure(StringRef ProgPath, const char** Args,
ErrorFilename.str(), Timeout, MemoryLimit);
// FIXME: check return code ?
- // Print out the error messages generated by GCC if possible...
+ // Print out the error messages generated by CC if possible...
std::ifstream ErrorFile(ErrorFilename.c_str());
if (ErrorFile) {
std::copy(std::istreambuf_iterator<char>(ErrorFile),
@@ -184,7 +165,7 @@ namespace {
const std::string &InputFile,
const std::string &OutputFile,
std::string *Error,
- const std::vector<std::string> &GCCArgs,
+ const std::vector<std::string> &CCArgs,
const std::vector<std::string> &SharedLibs =
std::vector<std::string>(),
unsigned Timeout = 0,
@@ -197,7 +178,7 @@ int LLI::ExecuteProgram(const std::string &Bitcode,
const std::string &InputFile,
const std::string &OutputFile,
std::string *Error,
- const std::vector<std::string> &GCCArgs,
+ const std::vector<std::string> &CCArgs,
const std::vector<std::string> &SharedLibs,
unsigned Timeout,
unsigned MemoryLimit) {
@@ -305,7 +286,7 @@ namespace {
const std::string &InputFile,
const std::string &OutputFile,
std::string *Error,
- const std::vector<std::string> &GCCArgs =
+ const std::vector<std::string> &CCArgs =
std::vector<std::string>(),
const std::vector<std::string> &SharedLibs =
std::vector<std::string>(),
@@ -361,7 +342,7 @@ namespace {
const std::string &InputFile,
const std::string &OutputFile,
std::string *Error,
- const std::vector<std::string> &GCCArgs,
+ const std::vector<std::string> &CCArgs,
const std::vector<std::string> &SharedLibs =
std::vector<std::string>(),
unsigned Timeout = 0,
@@ -374,7 +355,7 @@ int CustomExecutor::ExecuteProgram(const std::string &Bitcode,
const std::string &InputFile,
const std::string &OutputFile,
std::string *Error,
- const std::vector<std::string> &GCCArgs,
+ const std::vector<std::string> &CCArgs,
const std::vector<std::string> &SharedLibs,
unsigned Timeout,
unsigned MemoryLimit) {
@@ -473,7 +454,7 @@ AbstractInterpreter *AbstractInterpreter::createCustomExecutor(
//===----------------------------------------------------------------------===//
// LLC Implementation of AbstractIntepreter interface
//
-GCC::FileType LLC::OutputCode(const std::string &Bitcode,
+CC::FileType LLC::OutputCode(const std::string &Bitcode,
std::string &OutputAsmFile, std::string &Error,
unsigned Timeout, unsigned MemoryLimit) {
const char *Suffix = (UseIntegratedAssembler ? ".llc.o" : ".llc.s");
@@ -514,7 +495,7 @@ GCC::FileType LLC::OutputCode(const std::string &Bitcode,
Timeout, MemoryLimit))
Error = ProcessFailure(LLCPath, &LLCArgs[0],
Timeout, MemoryLimit);
- return UseIntegratedAssembler ? GCC::ObjectFile : GCC::AsmFile;
+ return UseIntegratedAssembler ? CC::ObjectFile : CC::AsmFile;
}
void LLC::compileProgram(const std::string &Bitcode, std::string *Error,
@@ -529,22 +510,22 @@ int LLC::ExecuteProgram(const std::string &Bitcode,
const std::string &InputFile,
const std::string &OutputFile,
std::string *Error,
- const std::vector<std::string> &ArgsForGCC,
+ const std::vector<std::string> &ArgsForCC,
const std::vector<std::string> &SharedLibs,
unsigned Timeout,
unsigned MemoryLimit) {
std::string OutputAsmFile;
- GCC::FileType FileKind = OutputCode(Bitcode, OutputAsmFile, *Error, Timeout,
+ CC::FileType FileKind = OutputCode(Bitcode, OutputAsmFile, *Error, Timeout,
MemoryLimit);
FileRemover OutFileRemover(OutputAsmFile, !SaveTemps);
- std::vector<std::string> GCCArgs(ArgsForGCC);
- GCCArgs.insert(GCCArgs.end(), SharedLibs.begin(), SharedLibs.end());
+ std::vector<std::string> CCArgs(ArgsForCC);
+ CCArgs.insert(CCArgs.end(), SharedLibs.begin(), SharedLibs.end());
- // Assuming LLC worked, compile the result with GCC and run it.
- return gcc->ExecuteProgram(OutputAsmFile, Args, FileKind,
- InputFile, OutputFile, Error, GCCArgs,
+ // Assuming LLC worked, compile the result with CC and run it.
+ return cc->ExecuteProgram(OutputAsmFile, Args, FileKind,
+ InputFile, OutputFile, Error, CCArgs,
Timeout, MemoryLimit);
}
@@ -552,9 +533,9 @@ int LLC::ExecuteProgram(const std::string &Bitcode,
///
LLC *AbstractInterpreter::createLLC(const char *Argv0,
std::string &Message,
- const std::string &GCCBinary,
+ const std::string &CCBinary,
const std::vector<std::string> *Args,
- const std::vector<std::string> *GCCArgs,
+ const std::vector<std::string> *CCArgs,
bool UseIntegratedAssembler) {
std::string LLCPath =
PrependMainExecutablePath("llc", Argv0, (void *)(intptr_t) & createLLC);
@@ -563,13 +544,13 @@ LLC *AbstractInterpreter::createLLC(const char *Argv0,
return nullptr;
}
- GCC *gcc = GCC::create(Message, GCCBinary, GCCArgs);
- if (!gcc) {
+ CC *cc = CC::create(Message, CCBinary, CCArgs);
+ if (!cc) {
errs() << Message << "\n";
exit(1);
}
Message = "Found llc: " + LLCPath + "\n";
- return new LLC(LLCPath, gcc, Args, UseIntegratedAssembler);
+ return new LLC(LLCPath, cc, Args, UseIntegratedAssembler);
}
//===---------------------------------------------------------------------===//
@@ -591,7 +572,7 @@ namespace {
const std::string &InputFile,
const std::string &OutputFile,
std::string *Error,
- const std::vector<std::string> &GCCArgs =
+ const std::vector<std::string> &CCArgs =
std::vector<std::string>(),
const std::vector<std::string> &SharedLibs =
std::vector<std::string>(),
@@ -605,7 +586,7 @@ int JIT::ExecuteProgram(const std::string &Bitcode,
const std::string &InputFile,
const std::string &OutputFile,
std::string *Error,
- const std::vector<std::string> &GCCArgs,
+ const std::vector<std::string> &CCArgs,
const std::vector<std::string> &SharedLibs,
unsigned Timeout,
unsigned MemoryLimit) {
@@ -656,7 +637,7 @@ AbstractInterpreter *AbstractInterpreter::createJIT(const char *Argv0,
}
//===---------------------------------------------------------------------===//
-// GCC abstraction
+// CC abstraction
//
static bool IsARMArchitecture(std::vector<const char*> Args) {
@@ -672,82 +653,82 @@ static bool IsARMArchitecture(std::vector<const char*> Args) {
return false;
}
-int GCC::ExecuteProgram(const std::string &ProgramFile,
+int CC::ExecuteProgram(const std::string &ProgramFile,
const std::vector<std::string> &Args,
FileType fileType,
const std::string &InputFile,
const std::string &OutputFile,
std::string *Error,
- const std::vector<std::string> &ArgsForGCC,
+ const std::vector<std::string> &ArgsForCC,
unsigned Timeout,
unsigned MemoryLimit) {
- std::vector<const char*> GCCArgs;
+ std::vector<const char*> CCArgs;
- GCCArgs.push_back(GCCPath.c_str());
+ CCArgs.push_back(CCPath.c_str());
if (TargetTriple.getArch() == Triple::x86)
- GCCArgs.push_back("-m32");
+ CCArgs.push_back("-m32");
for (std::vector<std::string>::const_iterator
- I = gccArgs.begin(), E = gccArgs.end(); I != E; ++I)
- GCCArgs.push_back(I->c_str());
+ I = ccArgs.begin(), E = ccArgs.end(); I != E; ++I)
+ CCArgs.push_back(I->c_str());
// Specify -x explicitly in case the extension is wonky
if (fileType != ObjectFile) {
- GCCArgs.push_back("-x");
+ CCArgs.push_back("-x");
if (fileType == CFile) {
- GCCArgs.push_back("c");
- GCCArgs.push_back("-fno-strict-aliasing");
+ CCArgs.push_back("c");
+ CCArgs.push_back("-fno-strict-aliasing");
} else {
- GCCArgs.push_back("assembler");
+ CCArgs.push_back("assembler");
// For ARM architectures we don't want this flag. bugpoint isn't
// explicitly told what architecture it is working on, so we get
- // it from gcc flags
- if (TargetTriple.isOSDarwin() && !IsARMArchitecture(GCCArgs))
- GCCArgs.push_back("-force_cpusubtype_ALL");
+ // it from cc flags
+ if (TargetTriple.isOSDarwin() && !IsARMArchitecture(CCArgs))
+ CCArgs.push_back("-force_cpusubtype_ALL");
}
}
- GCCArgs.push_back(ProgramFile.c_str()); // Specify the input filename.
+ CCArgs.push_back(ProgramFile.c_str()); // Specify the input filename.
- GCCArgs.push_back("-x");
- GCCArgs.push_back("none");
- GCCArgs.push_back("-o");
+ CCArgs.push_back("-x");
+ CCArgs.push_back("none");
+ CCArgs.push_back("-o");
SmallString<128> OutputBinary;
std::error_code EC =
- sys::fs::createUniqueFile(ProgramFile + "-%%%%%%%.gcc.exe", OutputBinary);
+ sys::fs::createUniqueFile(ProgramFile + "-%%%%%%%.cc.exe", OutputBinary);
if (EC) {
errs() << "Error making unique filename: " << EC.message() << "\n";
exit(1);
}
- GCCArgs.push_back(OutputBinary.c_str()); // Output to the right file...
+ CCArgs.push_back(OutputBinary.c_str()); // Output to the right file...
- // Add any arguments intended for GCC. We locate them here because this is
+ // Add any arguments intended for CC. We locate them here because this is
// most likely -L and -l options that need to come before other libraries but
// after the source. Other options won't be sensitive to placement on the
// command line, so this should be safe.
- for (unsigned i = 0, e = ArgsForGCC.size(); i != e; ++i)
- GCCArgs.push_back(ArgsForGCC[i].c_str());
+ for (unsigned i = 0, e = ArgsForCC.size(); i != e; ++i)
+ CCArgs.push_back(ArgsForCC[i].c_str());
- GCCArgs.push_back("-lm"); // Hard-code the math library...
- GCCArgs.push_back("-O2"); // Optimize the program a bit...
+ CCArgs.push_back("-lm"); // Hard-code the math library...
+ CCArgs.push_back("-O2"); // Optimize the program a bit...
#if defined (HAVE_LINK_R)
- GCCArgs.push_back("-Wl,-R."); // Search this dir for .so files
+ CCArgs.push_back("-Wl,-R."); // Search this dir for .so files
#endif
if (TargetTriple.getArch() == Triple::sparc)
- GCCArgs.push_back("-mcpu=v9");
- GCCArgs.push_back(nullptr); // NULL terminator
+ CCArgs.push_back("-mcpu=v9");
+ CCArgs.push_back(nullptr); // NULL terminator
- outs() << "<gcc>"; outs().flush();
+ outs() << "<CC>"; outs().flush();
DEBUG(errs() << "\nAbout to run:\t";
- for (unsigned i = 0, e = GCCArgs.size()-1; i != e; ++i)
- errs() << " " << GCCArgs[i];
+ for (unsigned i = 0, e = CCArgs.size()-1; i != e; ++i)
+ errs() << " " << CCArgs[i];
errs() << "\n";
);
- if (RunProgramWithTimeout(GCCPath, &GCCArgs[0], "", "", "")) {
- *Error = ProcessFailure(GCCPath, &GCCArgs[0]);
+ if (RunProgramWithTimeout(CCPath, &CCArgs[0], "", "", "")) {
+ *Error = ProcessFailure(CCPath, &CCArgs[0]);
return -1;
}
@@ -821,9 +802,9 @@ int GCC::ExecuteProgram(const std::string &ProgramFile,
}
}
-int GCC::MakeSharedObject(const std::string &InputFile, FileType fileType,
+int CC::MakeSharedObject(const std::string &InputFile, FileType fileType,
std::string &OutputFile,
- const std::vector<std::string> &ArgsForGCC,
+ const std::vector<std::string> &ArgsForCC,
std::string &Error) {
SmallString<128> UniqueFilename;
std::error_code EC = sys::fs::createUniqueFile(
@@ -834,84 +815,84 @@ int GCC::MakeSharedObject(const std::string &InputFile, FileType fileType,
}
OutputFile = UniqueFilename.str();
- std::vector<const char*> GCCArgs;
+ std::vector<const char*> CCArgs;
- GCCArgs.push_back(GCCPath.c_str());
+ CCArgs.push_back(CCPath.c_str());
if (TargetTriple.getArch() == Triple::x86)
- GCCArgs.push_back("-m32");
+ CCArgs.push_back("-m32");
for (std::vector<std::string>::const_iterator
- I = gccArgs.begin(), E = gccArgs.end(); I != E; ++I)
- GCCArgs.push_back(I->c_str());
+ I = ccArgs.begin(), E = ccArgs.end(); I != E; ++I)
+ CCArgs.push_back(I->c_str());
// Compile the C/asm file into a shared object
if (fileType != ObjectFile) {
- GCCArgs.push_back("-x");
- GCCArgs.push_back(fileType == AsmFile ? "assembler" : "c");
+ CCArgs.push_back("-x");
+ CCArgs.push_back(fileType == AsmFile ? "assembler" : "c");
}
- GCCArgs.push_back("-fno-strict-aliasing");
- GCCArgs.push_back(InputFile.c_str()); // Specify the input filename.
- GCCArgs.push_back("-x");
- GCCArgs.push_back("none");
+ CCArgs.push_back("-fno-strict-aliasing");
+ CCArgs.push_back(InputFile.c_str()); // Specify the input filename.
+ CCArgs.push_back("-x");
+ CCArgs.push_back("none");
if (TargetTriple.getArch() == Triple::sparc)
- GCCArgs.push_back("-G"); // Compile a shared library, `-G' for Sparc
+ CCArgs.push_back("-G"); // Compile a shared library, `-G' for Sparc
else if (TargetTriple.isOSDarwin()) {
// link all source files into a single module in data segment, rather than
// generating blocks. dynamic_lookup requires that you set
// MACOSX_DEPLOYMENT_TARGET=10.3 in your env. FIXME: it would be better for
- // bugpoint to just pass that in the environment of GCC.
- GCCArgs.push_back("-single_module");
- GCCArgs.push_back("-dynamiclib"); // `-dynamiclib' for MacOS X/PowerPC
- GCCArgs.push_back("-undefined");
- GCCArgs.push_back("dynamic_lookup");
+ // bugpoint to just pass that in the environment of CC.
+ CCArgs.push_back("-single_module");
+ CCArgs.push_back("-dynamiclib"); // `-dynamiclib' for MacOS X/PowerPC
+ CCArgs.push_back("-undefined");
+ CCArgs.push_back("dynamic_lookup");
} else
- GCCArgs.push_back("-shared"); // `-shared' for Linux/X86, maybe others
+ CCArgs.push_back("-shared"); // `-shared' for Linux/X86, maybe others
if (TargetTriple.getArch() == Triple::x86_64)
- GCCArgs.push_back("-fPIC"); // Requires shared objs to contain PIC
+ CCArgs.push_back("-fPIC"); // Requires shared objs to contain PIC
if (TargetTriple.getArch() == Triple::sparc)
- GCCArgs.push_back("-mcpu=v9");
+ CCArgs.push_back("-mcpu=v9");
- GCCArgs.push_back("-o");
- GCCArgs.push_back(OutputFile.c_str()); // Output to the right filename.
- GCCArgs.push_back("-O2"); // Optimize the program a bit.
+ CCArgs.push_back("-o");
+ CCArgs.push_back(OutputFile.c_str()); // Output to the right filename.
+ CCArgs.push_back("-O2"); // Optimize the program a bit.
- // Add any arguments intended for GCC. We locate them here because this is
+ // Add any arguments intended for CC. We locate them here because this is
// most likely -L and -l options that need to come before other libraries but
// after the source. Other options won't be sensitive to placement on the
// command line, so this should be safe.
- for (unsigned i = 0, e = ArgsForGCC.size(); i != e; ++i)
- GCCArgs.push_back(ArgsForGCC[i].c_str());
- GCCArgs.push_back(nullptr); // NULL terminator
+ for (unsigned i = 0, e = ArgsForCC.size(); i != e; ++i)
+ CCArgs.push_back(ArgsForCC[i].c_str());
+ CCArgs.push_back(nullptr); // NULL terminator
- outs() << "<gcc>"; outs().flush();
+ outs() << "<CC>"; outs().flush();
DEBUG(errs() << "\nAbout to run:\t";
- for (unsigned i = 0, e = GCCArgs.size()-1; i != e; ++i)
- errs() << " " << GCCArgs[i];
+ for (unsigned i = 0, e = CCArgs.size()-1; i != e; ++i)
+ errs() << " " << CCArgs[i];
errs() << "\n";
);
- if (RunProgramWithTimeout(GCCPath, &GCCArgs[0], "", "", "")) {
- Error = ProcessFailure(GCCPath, &GCCArgs[0]);
+ if (RunProgramWithTimeout(CCPath, &CCArgs[0], "", "", "")) {
+ Error = ProcessFailure(CCPath, &CCArgs[0]);
return 1;
}
return 0;
}
-/// create - Try to find the `gcc' executable
+/// create - Try to find the CC executable
///
-GCC *GCC::create(std::string &Message,
- const std::string &GCCBinary,
+CC *CC::create(std::string &Message,
+ const std::string &CCBinary,
const std::vector<std::string> *Args) {
- auto GCCPath = sys::findProgramByName(GCCBinary);
- if (!GCCPath) {
- Message = "Cannot find `" + GCCBinary + "' in PATH: " +
- GCCPath.getError().message() + "\n";
+ auto CCPath = sys::findProgramByName(CCBinary);
+ if (!CCPath) {
+ Message = "Cannot find `" + CCBinary + "' in PATH: " +
+ CCPath.getError().message() + "\n";
return nullptr;
}
@@ -926,6 +907,6 @@ GCC *GCC::create(std::string &Message,
RemoteClientPath = *Path;
}
- Message = "Found gcc: " + *GCCPath + "\n";
- return new GCC(*GCCPath, RemoteClientPath, Args);
+ Message = "Found CC: " + *CCPath + "\n";
+ return new CC(*CCPath, RemoteClientPath, Args);
}
diff --git a/tools/bugpoint/ToolRunner.h b/tools/bugpoint/ToolRunner.h
index 5d67a9426870..3accd70ed4ce 100644
--- a/tools/bugpoint/ToolRunner.h
+++ b/tools/bugpoint/ToolRunner.h
@@ -33,22 +33,22 @@ extern Triple TargetTriple;
class LLC;
//===---------------------------------------------------------------------===//
-// GCC abstraction
+// CC abstraction
//
-class GCC {
- std::string GCCPath; // The path to the gcc executable.
+class CC {
+ std::string CCPath; // The path to the cc executable.
std::string RemoteClientPath; // The path to the rsh / ssh executable.
- std::vector<std::string> gccArgs; // GCC-specific arguments.
- GCC(StringRef gccPath, StringRef RemotePath,
- const std::vector<std::string> *GCCArgs)
- : GCCPath(gccPath), RemoteClientPath(RemotePath) {
- if (GCCArgs) gccArgs = *GCCArgs;
+ std::vector<std::string> ccArgs; // CC-specific arguments.
+ CC(StringRef ccPath, StringRef RemotePath,
+ const std::vector<std::string> *CCArgs)
+ : CCPath(ccPath), RemoteClientPath(RemotePath) {
+ if (CCArgs) ccArgs = *CCArgs;
}
public:
enum FileType { AsmFile, ObjectFile, CFile };
- static GCC *create(std::string &Message,
- const std::string &GCCBinary,
+ static CC *create(std::string &Message,
+ const std::string &CCBinary,
const std::vector<std::string> *Args);
/// ExecuteProgram - Execute the program specified by "ProgramFile" (which is
@@ -64,7 +64,7 @@ public:
const std::string &InputFile,
const std::string &OutputFile,
std::string *Error = nullptr,
- const std::vector<std::string> &GCCArgs =
+ const std::vector<std::string> &CCArgs =
std::vector<std::string>(),
unsigned Timeout = 0,
unsigned MemoryLimit = 0);
@@ -74,7 +74,7 @@ public:
///
int MakeSharedObject(const std::string &InputFile, FileType fileType,
std::string &OutputFile,
- const std::vector<std::string> &ArgsForGCC,
+ const std::vector<std::string> &ArgsForCC,
std::string &Error);
};
@@ -88,9 +88,9 @@ class AbstractInterpreter {
virtual void anchor();
public:
static LLC *createLLC(const char *Argv0, std::string &Message,
- const std::string &GCCBinary,
+ const std::string &CCBinary,
const std::vector<std::string> *Args = nullptr,
- const std::vector<std::string> *GCCArgs = nullptr,
+ const std::vector<std::string> *CCArgs = nullptr,
bool UseIntegratedAssembler = false);
static AbstractInterpreter*
@@ -119,15 +119,15 @@ public:
unsigned Timeout = 0, unsigned MemoryLimit = 0) {}
/// OutputCode - Compile the specified program from bitcode to code
- /// understood by the GCC driver (either C or asm). If the code generator
+ /// understood by the CC driver (either C or asm). If the code generator
/// fails, it sets Error, otherwise, this function returns the type of code
/// emitted.
- virtual GCC::FileType OutputCode(const std::string &Bitcode,
+ virtual CC::FileType OutputCode(const std::string &Bitcode,
std::string &OutFile, std::string &Error,
unsigned Timeout = 0,
unsigned MemoryLimit = 0) {
Error = "OutputCode not supported by this AbstractInterpreter!";
- return GCC::AsmFile;
+ return CC::AsmFile;
}
/// ExecuteProgram - Run the specified bitcode file, emitting output to the
@@ -140,7 +140,7 @@ public:
const std::string &InputFile,
const std::string &OutputFile,
std::string *Error,
- const std::vector<std::string> &GCCArgs =
+ const std::vector<std::string> &CCArgs =
std::vector<std::string>(),
const std::vector<std::string> &SharedLibs =
std::vector<std::string>(),
@@ -154,18 +154,18 @@ public:
class LLC : public AbstractInterpreter {
std::string LLCPath; // The path to the LLC executable.
std::vector<std::string> ToolArgs; // Extra args to pass to LLC.
- GCC *gcc;
+ CC *cc;
bool UseIntegratedAssembler;
public:
- LLC(const std::string &llcPath, GCC *Gcc,
+ LLC(const std::string &llcPath, CC *cc,
const std::vector<std::string> *Args,
bool useIntegratedAssembler)
- : LLCPath(llcPath), gcc(Gcc),
+ : LLCPath(llcPath), cc(cc),
UseIntegratedAssembler(useIntegratedAssembler) {
ToolArgs.clear();
if (Args) ToolArgs = *Args;
}
- ~LLC() override { delete gcc; }
+ ~LLC() override { delete cc; }
/// compileProgram - Compile the specified program from bitcode to executable
/// code. This does not produce any output, it is only used when debugging
@@ -178,7 +178,7 @@ public:
const std::string &InputFile,
const std::string &OutputFile,
std::string *Error,
- const std::vector<std::string> &GCCArgs =
+ const std::vector<std::string> &CCArgs =
std::vector<std::string>(),
const std::vector<std::string> &SharedLibs =
std::vector<std::string>(),
@@ -186,10 +186,10 @@ public:
unsigned MemoryLimit = 0) override;
/// OutputCode - Compile the specified program from bitcode to code
- /// understood by the GCC driver (either C or asm). If the code generator
+ /// understood by the CC driver (either C or asm). If the code generator
/// fails, it sets Error, otherwise, this function returns the type of code
/// emitted.
- GCC::FileType OutputCode(const std::string &Bitcode,
+ CC::FileType OutputCode(const std::string &Bitcode,
std::string &OutFile, std::string &Error,
unsigned Timeout = 0,
unsigned MemoryLimit = 0) override;
diff --git a/tools/bugpoint/bugpoint.cpp b/tools/bugpoint/bugpoint.cpp
index af6d9fccf291..48f30e6709f8 100644
--- a/tools/bugpoint/bugpoint.cpp
+++ b/tools/bugpoint/bugpoint.cpp
@@ -126,7 +126,6 @@ int main(int argc, char **argv) {
initializeVectorization(Registry);
initializeIPO(Registry);
initializeAnalysis(Registry);
- initializeIPA(Registry);
initializeTransformUtils(Registry);
initializeInstCombine(Registry);
initializeInstrumentation(Registry);
@@ -181,19 +180,12 @@ int main(int argc, char **argv) {
Builder.Inliner = createFunctionInliningPass(225);
else
Builder.Inliner = createFunctionInliningPass(275);
-
- // Note that although clang/llvm-gcc use two separate passmanagers
- // here, it shouldn't normally make a difference.
Builder.populateFunctionPassManager(PM);
Builder.populateModulePassManager(PM);
}
- for (std::vector<const PassInfo*>::iterator I = PassList.begin(),
- E = PassList.end();
- I != E; ++I) {
- const PassInfo* PI = *I;
+ for (const PassInfo *PI : PassList)
D.addPass(PI->getPassArgument());
- }
// Bugpoint has the ability of generating a plethora of core files, so to
// avoid filling up the disk, we prevent it
diff --git a/tools/dsymutil/BinaryHolder.cpp b/tools/dsymutil/BinaryHolder.cpp
index ad66105bc249..4c2c1d195c3c 100644
--- a/tools/dsymutil/BinaryHolder.cpp
+++ b/tools/dsymutil/BinaryHolder.cpp
@@ -13,25 +13,59 @@
//===----------------------------------------------------------------------===//
#include "BinaryHolder.h"
+#include "llvm/Object/MachO.h"
#include "llvm/Support/raw_ostream.h"
namespace llvm {
namespace dsymutil {
-ErrorOr<MemoryBufferRef>
-BinaryHolder::GetMemoryBufferForFile(StringRef Filename) {
+Triple BinaryHolder::getTriple(const object::MachOObjectFile &Obj) {
+ // If a ThumbTriple is returned, use it instead of the standard
+ // one. This is because the thumb triple always allows to create a
+ // target, whereas the non-thumb one might not.
+ Triple ThumbTriple;
+ Triple T = Obj.getArch(nullptr, &ThumbTriple);
+ return ThumbTriple.getArch() ? ThumbTriple : T;
+}
+
+static std::vector<MemoryBufferRef>
+getMachOFatMemoryBuffers(StringRef Filename, MemoryBuffer &Mem,
+ object::MachOUniversalBinary &Fat) {
+ std::vector<MemoryBufferRef> Buffers;
+ StringRef FatData = Fat.getData();
+ for (auto It = Fat.begin_objects(), End = Fat.end_objects(); It != End;
+ ++It) {
+ StringRef ObjData = FatData.substr(It->getOffset(), It->getSize());
+ Buffers.emplace_back(ObjData, Filename);
+ }
+ return Buffers;
+}
+
+void BinaryHolder::changeBackingMemoryBuffer(
+ std::unique_ptr<MemoryBuffer> &&Buf) {
+ CurrentArchives.clear();
+ CurrentObjectFiles.clear();
+ CurrentFatBinary.reset();
+
+ CurrentMemoryBuffer = std::move(Buf);
+}
+
+ErrorOr<std::vector<MemoryBufferRef>>
+BinaryHolder::GetMemoryBuffersForFile(StringRef Filename,
+ sys::TimeValue Timestamp) {
if (Verbose)
outs() << "trying to open '" << Filename << "'\n";
// Try that first as it doesn't involve any filesystem access.
- if (auto ErrOrArchiveMember = GetArchiveMemberBuffer(Filename))
- return *ErrOrArchiveMember;
+ if (auto ErrOrArchiveMembers = GetArchiveMemberBuffers(Filename, Timestamp))
+ return *ErrOrArchiveMembers;
// If the name ends with a closing paren, there is a huge chance
// it is an archive member specification.
if (Filename.endswith(")"))
- if (auto ErrOrArchiveMember = MapArchiveAndGetMemberBuffer(Filename))
- return *ErrOrArchiveMember;
+ if (auto ErrOrArchiveMembers =
+ MapArchiveAndGetMemberBuffers(Filename, Timestamp))
+ return *ErrOrArchiveMembers;
// Otherwise, just try opening a standard file. If this is an
// archive member specifiaction and any of the above didn't handle it
@@ -42,39 +76,70 @@ BinaryHolder::GetMemoryBufferForFile(StringRef Filename) {
if (auto Err = ErrOrFile.getError())
return Err;
+ changeBackingMemoryBuffer(std::move(*ErrOrFile));
if (Verbose)
outs() << "\tloaded file.\n";
- CurrentArchive.reset();
- CurrentMemoryBuffer = std::move(ErrOrFile.get());
- return CurrentMemoryBuffer->getMemBufferRef();
+
+ auto ErrOrFat = object::MachOUniversalBinary::create(
+ CurrentMemoryBuffer->getMemBufferRef());
+ if (ErrOrFat.getError()) {
+ // Not a fat binary must be a standard one. Return a one element vector.
+ return std::vector<MemoryBufferRef>{CurrentMemoryBuffer->getMemBufferRef()};
+ }
+
+ CurrentFatBinary = std::move(*ErrOrFat);
+ return getMachOFatMemoryBuffers(Filename, *CurrentMemoryBuffer,
+ *CurrentFatBinary);
}
-ErrorOr<MemoryBufferRef>
-BinaryHolder::GetArchiveMemberBuffer(StringRef Filename) {
- if (!CurrentArchive)
+ErrorOr<std::vector<MemoryBufferRef>>
+BinaryHolder::GetArchiveMemberBuffers(StringRef Filename,
+ sys::TimeValue Timestamp) {
+ if (CurrentArchives.empty())
return make_error_code(errc::no_such_file_or_directory);
- StringRef CurArchiveName = CurrentArchive->getFileName();
+ StringRef CurArchiveName = CurrentArchives.front()->getFileName();
if (!Filename.startswith(Twine(CurArchiveName, "(").str()))
return make_error_code(errc::no_such_file_or_directory);
// Remove the archive name and the parens around the archive member name.
Filename = Filename.substr(CurArchiveName.size() + 1).drop_back();
- for (const auto &Child : CurrentArchive->children()) {
- if (auto NameOrErr = Child.getName())
- if (*NameOrErr == Filename) {
- if (Verbose)
- outs() << "\tfound member in current archive.\n";
- return Child.getMemoryBufferRef();
+ std::vector<MemoryBufferRef> Buffers;
+ Buffers.reserve(CurrentArchives.size());
+
+ for (const auto &CurrentArchive : CurrentArchives) {
+ for (auto ChildOrErr : CurrentArchive->children()) {
+ if (std::error_code Err = ChildOrErr.getError())
+ return Err;
+ const auto &Child = *ChildOrErr;
+ if (auto NameOrErr = Child.getName()) {
+ if (*NameOrErr == Filename) {
+ if (Timestamp != sys::TimeValue::PosixZeroTime() &&
+ Timestamp != Child.getLastModified()) {
+ if (Verbose)
+ outs() << "\tmember had timestamp mismatch.\n";
+ continue;
+ }
+ if (Verbose)
+ outs() << "\tfound member in current archive.\n";
+ auto ErrOrMem = Child.getMemoryBufferRef();
+ if (auto Err = ErrOrMem.getError())
+ return Err;
+ Buffers.push_back(*ErrOrMem);
+ }
}
+ }
}
- return make_error_code(errc::no_such_file_or_directory);
+ if (Buffers.empty())
+ return make_error_code(errc::no_such_file_or_directory);
+ return Buffers;
}
-ErrorOr<MemoryBufferRef>
-BinaryHolder::MapArchiveAndGetMemberBuffer(StringRef Filename) {
+ErrorOr<std::vector<MemoryBufferRef>>
+BinaryHolder::MapArchiveAndGetMemberBuffers(StringRef Filename,
+ sys::TimeValue Timestamp) {
StringRef ArchiveFilename = Filename.substr(0, Filename.find('('));
auto ErrOrBuff = MemoryBuffer::getFileOrSTDIN(ArchiveFilename);
@@ -83,29 +148,62 @@ BinaryHolder::MapArchiveAndGetMemberBuffer(StringRef Filename) {
if (Verbose)
outs() << "\topened new archive '" << ArchiveFilename << "'\n";
- auto ErrOrArchive = object::Archive::create((*ErrOrBuff)->getMemBufferRef());
- if (auto Err = ErrOrArchive.getError())
- return Err;
- CurrentArchive = std::move(*ErrOrArchive);
- CurrentMemoryBuffer = std::move(*ErrOrBuff);
+ changeBackingMemoryBuffer(std::move(*ErrOrBuff));
+ std::vector<MemoryBufferRef> ArchiveBuffers;
+ auto ErrOrFat = object::MachOUniversalBinary::create(
+ CurrentMemoryBuffer->getMemBufferRef());
+ if (ErrOrFat.getError()) {
+ // Not a fat binary must be a standard one.
+ ArchiveBuffers.push_back(CurrentMemoryBuffer->getMemBufferRef());
+ } else {
+ CurrentFatBinary = std::move(*ErrOrFat);
+ ArchiveBuffers = getMachOFatMemoryBuffers(
+ ArchiveFilename, *CurrentMemoryBuffer, *CurrentFatBinary);
+ }
- return GetArchiveMemberBuffer(Filename);
+ for (auto MemRef : ArchiveBuffers) {
+ auto ErrOrArchive = object::Archive::create(MemRef);
+ if (auto Err = ErrOrArchive.getError())
+ return Err;
+ CurrentArchives.push_back(std::move(*ErrOrArchive));
+ }
+ return GetArchiveMemberBuffers(Filename, Timestamp);
}
ErrorOr<const object::ObjectFile &>
-BinaryHolder::GetObjectFile(StringRef Filename) {
- auto ErrOrMemBufferRef = GetMemoryBufferForFile(Filename);
- if (auto Err = ErrOrMemBufferRef.getError())
- return Err;
+BinaryHolder::getObjfileForArch(const Triple &T) {
+ for (const auto &Obj : CurrentObjectFiles) {
+ if (const auto *MachO = dyn_cast<object::MachOObjectFile>(Obj.get())) {
+ if (getTriple(*MachO).str() == T.str())
+ return *MachO;
+ } else if (Obj->getArch() == T.getArch())
+ return *Obj;
+ }
- auto ErrOrObjectFile =
- object::ObjectFile::createObjectFile(*ErrOrMemBufferRef);
- if (auto Err = ErrOrObjectFile.getError())
+ return make_error_code(object::object_error::arch_not_found);
+}
+
+ErrorOr<std::vector<const object::ObjectFile *>>
+BinaryHolder::GetObjectFiles(StringRef Filename, sys::TimeValue Timestamp) {
+ auto ErrOrMemBufferRefs = GetMemoryBuffersForFile(Filename, Timestamp);
+ if (auto Err = ErrOrMemBufferRefs.getError())
return Err;
- CurrentObjectFile = std::move(*ErrOrObjectFile);
- return *CurrentObjectFile;
+ std::vector<const object::ObjectFile *> Objects;
+ Objects.reserve(ErrOrMemBufferRefs->size());
+
+ CurrentObjectFiles.clear();
+ for (auto MemBuf : *ErrOrMemBufferRefs) {
+ auto ErrOrObjectFile = object::ObjectFile::createObjectFile(MemBuf);
+ if (auto Err = ErrOrObjectFile.getError())
+ return Err;
+
+ Objects.push_back(ErrOrObjectFile->get());
+ CurrentObjectFiles.push_back(std::move(*ErrOrObjectFile));
+ }
+
+ return std::move(Objects);
}
}
}
diff --git a/tools/dsymutil/BinaryHolder.h b/tools/dsymutil/BinaryHolder.h
index 04871b5d5855..9d7b4bd8787f 100644
--- a/tools/dsymutil/BinaryHolder.h
+++ b/tools/dsymutil/BinaryHolder.h
@@ -14,11 +14,14 @@
#ifndef LLVM_TOOLS_DSYMUTIL_BINARYHOLDER_H
#define LLVM_TOOLS_DSYMUTIL_BINARYHOLDER_H
+#include "llvm/ADT/Triple.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/Error.h"
+#include "llvm/Object/MachOUniversal.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/ErrorOr.h"
+#include "llvm/Support/TimeValue.h"
namespace llvm {
namespace dsymutil {
@@ -35,69 +38,98 @@ namespace dsymutil {
/// meaning that a mapping request will invalidate the previous memory
/// mapping.
class BinaryHolder {
- std::unique_ptr<object::Archive> CurrentArchive;
+ std::vector<std::unique_ptr<object::Archive>> CurrentArchives;
std::unique_ptr<MemoryBuffer> CurrentMemoryBuffer;
- std::unique_ptr<object::ObjectFile> CurrentObjectFile;
+ std::vector<std::unique_ptr<object::ObjectFile>> CurrentObjectFiles;
+ std::unique_ptr<object::MachOUniversalBinary> CurrentFatBinary;
bool Verbose;
- /// \brief Get the MemoryBufferRef for the file specification in \p
- /// Filename from the current archive.
+ /// Get the MemoryBufferRefs for the file specification in \p
+ /// Filename from the current archive. Multiple buffers are returned
+ /// when there are multiple architectures available for the
+ /// requested file.
///
/// This function performs no system calls, it just looks up a
/// potential match for the given \p Filename in the currently
/// mapped archive if there is one.
- ErrorOr<MemoryBufferRef> GetArchiveMemberBuffer(StringRef Filename);
+ ErrorOr<std::vector<MemoryBufferRef>>
+ GetArchiveMemberBuffers(StringRef Filename, sys::TimeValue Timestamp);
- /// \brief Interpret Filename as an archive member specification,
- /// map the corresponding archive to memory and return the
- /// MemoryBufferRef corresponding to the described member.
- ErrorOr<MemoryBufferRef> MapArchiveAndGetMemberBuffer(StringRef Filename);
+ /// Interpret Filename as an archive member specification map the
+ /// corresponding archive to memory and return the MemoryBufferRefs
+ /// corresponding to the described member. Multiple buffers are
+ /// returned when there are multiple architectures available for the
+ /// requested file.
+ ErrorOr<std::vector<MemoryBufferRef>>
+ MapArchiveAndGetMemberBuffers(StringRef Filename, sys::TimeValue Timestamp);
- /// \brief Return the MemoryBufferRef that holds the memory
- /// mapping for the given \p Filename. This function will try to
- /// parse archive member specifications of the form
- /// /path/to/archive.a(member.o).
+ /// Return the MemoryBufferRef that holds the memory mapping for the
+ /// given \p Filename. This function will try to parse archive
+ /// member specifications of the form /path/to/archive.a(member.o).
///
- /// The returned MemoryBufferRef points to a buffer owned by this
+ /// The returned MemoryBufferRefs points to a buffer owned by this
/// object. The buffer is valid until the next call to
/// GetMemoryBufferForFile() on this object.
- ErrorOr<MemoryBufferRef> GetMemoryBufferForFile(StringRef Filename);
+ /// Multiple buffers are returned when there are multiple
+ /// architectures available for the requested file.
+ ErrorOr<std::vector<MemoryBufferRef>>
+ GetMemoryBuffersForFile(StringRef Filename, sys::TimeValue Timestamp);
+
+ void changeBackingMemoryBuffer(std::unique_ptr<MemoryBuffer> &&MemBuf);
+ ErrorOr<const object::ObjectFile &> getObjfileForArch(const Triple &T);
public:
BinaryHolder(bool Verbose) : Verbose(Verbose) {}
- /// \brief Get the ObjectFile designated by the \p Filename. This
+ /// Get the ObjectFiles designated by the \p Filename. This
/// might be an archive member specification of the form
/// /path/to/archive.a(member.o).
///
/// Calling this function invalidates the previous mapping owned by
- /// the BinaryHolder.
- ErrorOr<const object::ObjectFile &> GetObjectFile(StringRef Filename);
+ /// the BinaryHolder. Multiple buffers are returned when there are
+ /// multiple architectures available for the requested file.
+ ErrorOr<std::vector<const object::ObjectFile *>>
+ GetObjectFiles(StringRef Filename,
+ sys::TimeValue Timestamp = sys::TimeValue::PosixZeroTime());
- /// \brief Wraps GetObjectFile() to return a derived ObjectFile type.
+ /// Wraps GetObjectFiles() to return a derived ObjectFile type.
template <typename ObjectFileType>
- ErrorOr<const ObjectFileType &> GetFileAs(StringRef Filename) {
- auto ErrOrObjFile = GetObjectFile(Filename);
+ ErrorOr<std::vector<const ObjectFileType *>>
+ GetFilesAs(StringRef Filename,
+ sys::TimeValue Timestamp = sys::TimeValue::PosixZeroTime()) {
+ auto ErrOrObjFile = GetObjectFiles(Filename, Timestamp);
if (auto Err = ErrOrObjFile.getError())
return Err;
- if (const auto *Derived = dyn_cast<ObjectFileType>(CurrentObjectFile.get()))
- return *Derived;
- return make_error_code(object::object_error::invalid_file_type);
+
+ std::vector<const ObjectFileType *> Objects;
+ Objects.reserve((*ErrOrObjFile).size());
+ for (const auto &Obj : *ErrOrObjFile) {
+ const auto *Derived = dyn_cast<ObjectFileType>(Obj);
+ if (!Derived)
+ return make_error_code(object::object_error::invalid_file_type);
+ Objects.push_back(Derived);
+ }
+ return std::move(Objects);
}
- /// \brief Access the currently owned ObjectFile. As successfull
- /// call to GetObjectFile() or GetFileAs() must have been performed
- /// before calling this.
- const object::ObjectFile &Get() {
- assert(CurrentObjectFile);
- return *CurrentObjectFile;
+ /// Access the currently owned ObjectFile with architecture \p T. As
+ /// successfull call to GetObjectFiles() or GetFilesAs() must have
+ /// been performed before calling this.
+ ErrorOr<const object::ObjectFile &> Get(const Triple &T) {
+ return getObjfileForArch(T);
}
- /// \brief Access to a derived version of the currently owned
+ /// Access to a derived version of the currently owned
/// ObjectFile. The conversion must be known to be valid.
- template <typename ObjectFileType> const ObjectFileType &GetAs() {
- return cast<ObjectFileType>(*CurrentObjectFile);
+ template <typename ObjectFileType>
+ ErrorOr<const ObjectFileType &> GetAs(const Triple &T) {
+ auto ErrOrObj = Get(T);
+ if (auto Err = ErrOrObj.getError())
+ return Err;
+ return cast<ObjectFileType>(*ErrOrObj);
}
+
+ static Triple getTriple(const object::MachOObjectFile &Obj);
};
}
}
diff --git a/tools/dsymutil/CMakeLists.txt b/tools/dsymutil/CMakeLists.txt
index 88f9f1f083db..0f45d20de22c 100644
--- a/tools/dsymutil/CMakeLists.txt
+++ b/tools/dsymutil/CMakeLists.txt
@@ -14,5 +14,6 @@ add_llvm_tool(llvm-dsymutil
DebugMap.cpp
DwarfLinker.cpp
MachODebugMapParser.cpp
+ MachOUtils.cpp
)
diff --git a/tools/dsymutil/DebugMap.cpp b/tools/dsymutil/DebugMap.cpp
index e5cc87b3f318..4717085f4322 100644
--- a/tools/dsymutil/DebugMap.cpp
+++ b/tools/dsymutil/DebugMap.cpp
@@ -20,8 +20,9 @@ namespace dsymutil {
using namespace llvm::object;
-DebugMapObject::DebugMapObject(StringRef ObjectFilename)
- : Filename(ObjectFilename) {}
+DebugMapObject::DebugMapObject(StringRef ObjectFilename,
+ sys::TimeValue Timestamp)
+ : Filename(ObjectFilename), Timestamp(Timestamp) {}
bool DebugMapObject::addSymbol(StringRef Name, uint64_t ObjectAddress,
uint64_t LinkedAddress, uint32_t Size) {
@@ -58,8 +59,9 @@ void DebugMapObject::print(raw_ostream &OS) const {
void DebugMapObject::dump() const { print(errs()); }
#endif
-DebugMapObject &DebugMap::addDebugMapObject(StringRef ObjectFilePath) {
- Objects.emplace_back(new DebugMapObject(ObjectFilePath));
+DebugMapObject &DebugMap::addDebugMapObject(StringRef ObjectFilePath,
+ sys::TimeValue Timestamp) {
+ Objects.emplace_back(new DebugMapObject(ObjectFilePath, Timestamp));
return *Objects.back();
}
@@ -95,7 +97,7 @@ struct YAMLContext {
};
}
-ErrorOr<std::unique_ptr<DebugMap>>
+ErrorOr<std::vector<std::unique_ptr<DebugMap>>>
DebugMap::parseYAMLDebugMap(StringRef InputFile, StringRef PrependPath,
bool Verbose) {
auto ErrOrFile = MemoryBuffer::getFileOrSTDIN(InputFile);
@@ -112,8 +114,9 @@ DebugMap::parseYAMLDebugMap(StringRef InputFile, StringRef PrependPath,
if (auto EC = yin.error())
return EC;
-
- return std::move(Res);
+ std::vector<std::unique_ptr<DebugMap>> Result;
+ Result.push_back(std::move(Res));
+ return std::move(Result);
}
}
@@ -121,11 +124,12 @@ namespace yaml {
// Normalize/Denormalize between YAML and a DebugMapObject.
struct MappingTraits<dsymutil::DebugMapObject>::YamlDMO {
- YamlDMO(IO &io) {}
+ YamlDMO(IO &io) { Timestamp = 0; }
YamlDMO(IO &io, dsymutil::DebugMapObject &Obj);
dsymutil::DebugMapObject denormalize(IO &IO);
std::string Filename;
+ sys::TimeValue::SecondsType Timestamp;
std::vector<dsymutil::DebugMapObject::YAMLSymbolMapping> Entries;
};
@@ -141,6 +145,7 @@ void MappingTraits<dsymutil::DebugMapObject>::mapping(
IO &io, dsymutil::DebugMapObject &DMO) {
MappingNormalization<YamlDMO, dsymutil::DebugMapObject> Norm(io, DMO);
io.mapRequired("filename", Norm->Filename);
+ io.mapOptional("timestamp", Norm->Timestamp);
io.mapRequired("symbols", Norm->Entries);
}
@@ -174,9 +179,10 @@ SequenceTraits<std::vector<std::unique_ptr<dsymutil::DebugMapObject>>>::element(
void MappingTraits<dsymutil::DebugMap>::mapping(IO &io,
dsymutil::DebugMap &DM) {
io.mapRequired("triple", DM.BinaryTriple);
- io.mapOptional("objects", DM.Objects);
+ io.mapOptional("binary-path", DM.BinaryPath);
if (void *Ctxt = io.getContext())
reinterpret_cast<YAMLContext *>(Ctxt)->BinaryTriple = DM.BinaryTriple;
+ io.mapOptional("objects", DM.Objects);
}
void MappingTraits<std::unique_ptr<dsymutil::DebugMap>>::mapping(
@@ -184,14 +190,16 @@ void MappingTraits<std::unique_ptr<dsymutil::DebugMap>>::mapping(
if (!DM)
DM.reset(new DebugMap());
io.mapRequired("triple", DM->BinaryTriple);
- io.mapOptional("objects", DM->Objects);
+ io.mapOptional("binary-path", DM->BinaryPath);
if (void *Ctxt = io.getContext())
reinterpret_cast<YAMLContext *>(Ctxt)->BinaryTriple = DM->BinaryTriple;
+ io.mapOptional("objects", DM->Objects);
}
MappingTraits<dsymutil::DebugMapObject>::YamlDMO::YamlDMO(
IO &io, dsymutil::DebugMapObject &Obj) {
Filename = Obj.Filename;
+ Timestamp = Obj.getTimestamp().toEpochTime();
Entries.reserve(Obj.Symbols.size());
for (auto &Entry : Obj.Symbols)
Entries.push_back(std::make_pair(Entry.getKey(), Entry.getValue()));
@@ -205,11 +213,11 @@ MappingTraits<dsymutil::DebugMapObject>::YamlDMO::denormalize(IO &IO) {
StringMap<uint64_t> SymbolAddresses;
sys::path::append(Path, Filename);
- auto ErrOrObjectFile = BinHolder.GetObjectFile(Path);
- if (auto EC = ErrOrObjectFile.getError()) {
+ auto ErrOrObjectFiles = BinHolder.GetObjectFiles(Path);
+ if (auto EC = ErrOrObjectFiles.getError()) {
llvm::errs() << "warning: Unable to open " << Path << " " << EC.message()
<< '\n';
- } else {
+ } else if (auto ErrOrObjectFile = BinHolder.Get(Ctxt.BinaryTriple)) {
// Rewrite the object file symbol addresses in the debug map. The
// YAML input is mainly used to test llvm-dsymutil without
// requiring binaries checked-in. If we generate the object files
@@ -224,7 +232,9 @@ MappingTraits<dsymutil::DebugMapObject>::YamlDMO::denormalize(IO &IO) {
}
}
- dsymutil::DebugMapObject Res(Path);
+ sys::TimeValue TV;
+ TV.fromEpochTime(Timestamp);
+ dsymutil::DebugMapObject Res(Path, TV);
for (auto &Entry : Entries) {
auto &Mapping = Entry.second;
uint64_t ObjAddress = Mapping.ObjectAddress;
diff --git a/tools/dsymutil/DebugMap.h b/tools/dsymutil/DebugMap.h
index d0edbabb404b..06ac5a503dcd 100644
--- a/tools/dsymutil/DebugMap.h
+++ b/tools/dsymutil/DebugMap.h
@@ -29,6 +29,7 @@
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/Path.h"
+#include "llvm/Support/TimeValue.h"
#include "llvm/Support/YAMLTraits.h"
#include <vector>
@@ -65,6 +66,7 @@ class DebugMapObject;
/// }
class DebugMap {
Triple BinaryTriple;
+ std::string BinaryPath;
typedef std::vector<std::unique_ptr<DebugMapObject>> ObjectContainer;
ObjectContainer Objects;
@@ -75,7 +77,8 @@ class DebugMap {
DebugMap() = default;
///@}
public:
- DebugMap(const Triple &BinaryTriple) : BinaryTriple(BinaryTriple) {}
+ DebugMap(const Triple &BinaryTriple, StringRef BinaryPath)
+ : BinaryTriple(BinaryTriple), BinaryPath(BinaryPath) {}
typedef ObjectContainer::const_iterator const_iterator;
@@ -89,10 +92,13 @@ public:
/// This function adds an DebugMapObject to the list owned by this
/// debug map.
- DebugMapObject &addDebugMapObject(StringRef ObjectFilePath);
+ DebugMapObject &addDebugMapObject(StringRef ObjectFilePath,
+ sys::TimeValue Timestamp);
const Triple &getTriple() const { return BinaryTriple; }
+ StringRef getBinaryPath() const { return BinaryPath; }
+
void print(raw_ostream &OS) const;
#ifndef NDEBUG
@@ -100,7 +106,7 @@ public:
#endif
/// Read a debug map for \a InputFile.
- static ErrorOr<std::unique_ptr<DebugMap>>
+ static ErrorOr<std::vector<std::unique_ptr<DebugMap>>>
parseYAMLDebugMap(StringRef InputFile, StringRef PrependPath, bool Verbose);
};
@@ -139,6 +145,8 @@ public:
llvm::StringRef getObjectFilename() const { return Filename; }
+ sys::TimeValue getTimestamp() const { return Timestamp; }
+
iterator_range<StringMap<SymbolMapping>::const_iterator> symbols() const {
return make_range(Symbols.begin(), Symbols.end());
}
@@ -150,9 +158,10 @@ public:
private:
friend class DebugMap;
/// DebugMapObjects can only be constructed by the owning DebugMap.
- DebugMapObject(StringRef ObjectFilename);
+ DebugMapObject(StringRef ObjectFilename, sys::TimeValue Timestamp);
std::string Filename;
+ sys::TimeValue Timestamp;
StringMap<SymbolMapping> Symbols;
DenseMap<uint64_t, DebugMapEntry *> AddressToMapping;
@@ -167,12 +176,14 @@ private:
public:
DebugMapObject &operator=(DebugMapObject RHS) {
std::swap(Filename, RHS.Filename);
+ std::swap(Timestamp, RHS.Timestamp);
std::swap(Symbols, RHS.Symbols);
std::swap(AddressToMapping, RHS.AddressToMapping);
return *this;
}
DebugMapObject(DebugMapObject &&RHS) {
Filename = std::move(RHS.Filename);
+ Timestamp = std::move(RHS.Timestamp);
Symbols = std::move(RHS.Symbols);
AddressToMapping = std::move(RHS.AddressToMapping);
}
diff --git a/tools/dsymutil/DwarfLinker.cpp b/tools/dsymutil/DwarfLinker.cpp
index 6e9087c70eca..8c5f7946a425 100644
--- a/tools/dsymutil/DwarfLinker.cpp
+++ b/tools/dsymutil/DwarfLinker.cpp
@@ -10,11 +10,14 @@
#include "BinaryHolder.h"
#include "DebugMap.h"
#include "dsymutil.h"
+#include "MachOUtils.h"
+#include "NonRelocatableStringpool.h"
#include "llvm/ADT/IntervalMap.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/CodeGen/AsmPrinter.h"
#include "llvm/CodeGen/DIE.h"
+#include "llvm/Config/config.h"
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/DebugInfo/DWARF/DWARFDebugInfoEntry.h"
#include "llvm/DebugInfo/DWARF/DWARFFormValue.h"
@@ -28,6 +31,7 @@
#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/MC/MCTargetOptionsCommandFlags.h"
#include "llvm/Object/MachO.h"
#include "llvm/Support/Dwarf.h"
#include "llvm/Support/LEB128.h"
@@ -42,17 +46,6 @@ namespace dsymutil {
namespace {
-void warn(const Twine &Warning, const Twine &Context) {
- errs() << Twine("while processing ") + Context + ":\n";
- errs() << Twine("warning: ") + Warning + "\n";
-}
-
-bool error(const Twine &Error, const Twine &Context) {
- errs() << Twine("while processing ") + Context + ":\n";
- errs() << Twine("error: ") + Error + "\n";
- return false;
-}
-
template <typename KeyT, typename ValT>
using HalfOpenIntervalMap =
IntervalMap<KeyT, ValT, IntervalMapImpl::NodeSizer<KeyT, ValT>::LeafSize,
@@ -80,6 +73,112 @@ struct PatchLocation {
}
};
+class CompileUnit;
+struct DeclMapInfo;
+
+/// A DeclContext is a named program scope that is used for ODR
+/// uniquing of types.
+/// The set of DeclContext for the ODR-subject parts of a Dwarf link
+/// is expanded (and uniqued) with each new object file processed. We
+/// need to determine the context of each DIE in an linked object file
+/// to see if the corresponding type has already been emitted.
+///
+/// The contexts are conceptually organised as a tree (eg. a function
+/// scope is contained in a namespace scope that contains other
+/// scopes), but storing/accessing them in an actual tree is too
+/// inefficient: we need to be able to very quickly query a context
+/// for a given child context by name. Storing a StringMap in each
+/// DeclContext would be too space inefficient.
+/// The solution here is to give each DeclContext a link to its parent
+/// (this allows to walk up the tree), but to query the existance of a
+/// specific DeclContext using a separate DenseMap keyed on the hash
+/// of the fully qualified name of the context.
+class DeclContext {
+ unsigned QualifiedNameHash;
+ uint32_t Line;
+ uint32_t ByteSize;
+ uint16_t Tag;
+ StringRef Name;
+ StringRef File;
+ const DeclContext &Parent;
+ const DWARFDebugInfoEntryMinimal *LastSeenDIE;
+ uint32_t LastSeenCompileUnitID;
+ uint32_t CanonicalDIEOffset;
+
+ friend DeclMapInfo;
+
+public:
+ typedef DenseSet<DeclContext *, DeclMapInfo> Map;
+
+ DeclContext()
+ : QualifiedNameHash(0), Line(0), ByteSize(0),
+ Tag(dwarf::DW_TAG_compile_unit), Name(), File(), Parent(*this),
+ LastSeenDIE(nullptr), LastSeenCompileUnitID(0), CanonicalDIEOffset(0) {}
+
+ DeclContext(unsigned Hash, uint32_t Line, uint32_t ByteSize, uint16_t Tag,
+ StringRef Name, StringRef File, const DeclContext &Parent,
+ const DWARFDebugInfoEntryMinimal *LastSeenDIE = nullptr,
+ unsigned CUId = 0)
+ : QualifiedNameHash(Hash), Line(Line), ByteSize(ByteSize), Tag(Tag),
+ Name(Name), File(File), Parent(Parent), LastSeenDIE(LastSeenDIE),
+ LastSeenCompileUnitID(CUId), CanonicalDIEOffset(0) {}
+
+ uint32_t getQualifiedNameHash() const { return QualifiedNameHash; }
+
+ bool setLastSeenDIE(CompileUnit &U, const DWARFDebugInfoEntryMinimal *Die);
+
+ uint32_t getCanonicalDIEOffset() const { return CanonicalDIEOffset; }
+ void setCanonicalDIEOffset(uint32_t Offset) { CanonicalDIEOffset = Offset; }
+
+ uint16_t getTag() const { return Tag; }
+ StringRef getName() const { return Name; }
+};
+
+/// Info type for the DenseMap storing the DeclContext pointers.
+struct DeclMapInfo : private DenseMapInfo<DeclContext *> {
+ using DenseMapInfo<DeclContext *>::getEmptyKey;
+ using DenseMapInfo<DeclContext *>::getTombstoneKey;
+
+ static unsigned getHashValue(const DeclContext *Ctxt) {
+ return Ctxt->QualifiedNameHash;
+ }
+
+ static bool isEqual(const DeclContext *LHS, const DeclContext *RHS) {
+ if (RHS == getEmptyKey() || RHS == getTombstoneKey())
+ return RHS == LHS;
+ return LHS->QualifiedNameHash == RHS->QualifiedNameHash &&
+ LHS->Line == RHS->Line && LHS->ByteSize == RHS->ByteSize &&
+ LHS->Name.data() == RHS->Name.data() &&
+ LHS->File.data() == RHS->File.data() &&
+ LHS->Parent.QualifiedNameHash == RHS->Parent.QualifiedNameHash;
+ }
+};
+
+/// This class gives a tree-like API to the DenseMap that stores the
+/// DeclContext objects. It also holds the BumpPtrAllocator where
+/// these objects will be allocated.
+class DeclContextTree {
+ BumpPtrAllocator Allocator;
+ DeclContext Root;
+ DeclContext::Map Contexts;
+
+public:
+ /// Get the child of \a Context described by \a DIE in \a Unit. The
+ /// required strings will be interned in \a StringPool.
+ /// \returns The child DeclContext along with one bit that is set if
+ /// this context is invalid.
+ /// An invalid context means it shouldn't be considered for uniquing, but its
+ /// not returning null, because some children of that context might be
+ /// uniquing candidates. FIXME: The invalid bit along the return value is to
+ /// emulate some dsymutil-classic functionality.
+ PointerIntPair<DeclContext *, 1>
+ getChildDeclContext(DeclContext &Context,
+ const DWARFDebugInfoEntryMinimal *DIE, CompileUnit &Unit,
+ NonRelocatableStringpool &StringPool, bool InClangModule);
+
+ DeclContext &getRoot() { return Root; }
+};
+
/// \brief Stores all information relating to a compile unit, be it in
/// its original instance in the object file to its brand new cloned
/// and linked DIE tree.
@@ -88,16 +187,28 @@ public:
/// \brief Information gathered about a DIE in the object file.
struct DIEInfo {
int64_t AddrAdjust; ///< Address offset to apply to the described entity.
+ DeclContext *Ctxt; ///< ODR Declaration context.
DIE *Clone; ///< Cloned version of that DIE.
uint32_t ParentIdx; ///< The index of this DIE's parent.
- bool Keep; ///< Is the DIE part of the linked output?
- bool InDebugMap; ///< Was this DIE's entity found in the map?
+ bool Keep : 1; ///< Is the DIE part of the linked output?
+ bool InDebugMap : 1;///< Was this DIE's entity found in the map?
+ bool Prune : 1; ///< Is this a pure forward declaration we can strip?
};
- CompileUnit(DWARFUnit &OrigUnit, unsigned ID)
+ CompileUnit(DWARFUnit &OrigUnit, unsigned ID, bool CanUseODR,
+ StringRef ClangModuleName)
: OrigUnit(OrigUnit), ID(ID), LowPc(UINT64_MAX), HighPc(0), RangeAlloc(),
- Ranges(RangeAlloc) {
+ Ranges(RangeAlloc), ClangModuleName(ClangModuleName) {
Info.resize(OrigUnit.getNumDIEs());
+
+ const auto *CUDie = OrigUnit.getUnitDIE(false);
+ unsigned Lang = CUDie->getAttributeValueAsUnsignedConstant(
+ &OrigUnit, dwarf::DW_AT_language, 0);
+ HasODR = CanUseODR && (Lang == dwarf::DW_LANG_C_plus_plus ||
+ Lang == dwarf::DW_LANG_C_plus_plus_03 ||
+ Lang == dwarf::DW_LANG_C_plus_plus_11 ||
+ Lang == dwarf::DW_LANG_C_plus_plus_14 ||
+ Lang == dwarf::DW_LANG_ObjC_plus_plus);
}
CompileUnit(CompileUnit &&RHS)
@@ -116,6 +227,10 @@ public:
DIE *getOutputUnitDIE() const { return CUDie; }
void setOutputUnitDIE(DIE *Die) { CUDie = Die; }
+ bool hasODR() const { return HasODR; }
+ bool isClangModule() const { return !ClangModuleName.empty(); }
+ const std::string &getClangModuleName() const { return ClangModuleName; }
+
DIEInfo &getInfo(unsigned Idx) { return Info[Idx]; }
const DIEInfo &getInfo(unsigned Idx) const { return Info[Idx]; }
@@ -139,6 +254,14 @@ public:
return LocationAttributes;
}
+ void setHasInterestingContent() { HasInterestingContent = true; }
+ bool hasInterestingContent() { return HasInterestingContent; }
+
+ /// Mark every DIE in this unit as kept. This function also
+ /// marks variables as InDebugMap so that they appear in the
+ /// reconstructed accelerator tables.
+ void markEverythingAsKept();
+
/// \brief Compute the end offset for this unit. Must be
/// called after the CU's DIEs have been cloned.
/// \returns the next unit offset (which is also the current
@@ -147,9 +270,10 @@ public:
/// \brief Keep track of a forward reference to DIE \p Die in \p
/// RefUnit by \p Attr. The attribute should be fixed up later to
- /// point to the absolute offset of \p Die in the debug_info section.
+ /// point to the absolute offset of \p Die in the debug_info section
+ /// or to the canonical offset of \p Ctxt if it is non-null.
void noteForwardReference(DIE *Die, const CompileUnit *RefUnit,
- PatchLocation Attr);
+ DeclContext *Ctxt, PatchLocation Attr);
/// \brief Apply all fixups recored by noteForwardReference().
void fixupForwardReferences();
@@ -190,11 +314,27 @@ public:
const std::vector<AccelInfo> &getPubnames() const { return Pubnames; }
const std::vector<AccelInfo> &getPubtypes() const { return Pubtypes; }
+ /// Get the full path for file \a FileNum in the line table
+ const char *getResolvedPath(unsigned FileNum) {
+ if (FileNum >= ResolvedPaths.size())
+ return nullptr;
+ return ResolvedPaths[FileNum].size() ? ResolvedPaths[FileNum].c_str()
+ : nullptr;
+ }
+
+ /// Set the fully resolved path for the line-table's file \a FileNum
+ /// to \a Path.
+ void setResolvedPath(unsigned FileNum, const std::string &Path) {
+ if (ResolvedPaths.size() <= FileNum)
+ ResolvedPaths.resize(FileNum + 1);
+ ResolvedPaths[FileNum] = Path;
+ }
+
private:
DWARFUnit &OrigUnit;
unsigned ID;
- std::vector<DIEInfo> Info; ///< DIE info indexed by DIE index.
- DIE *CUDie; ///< Root of the linked DIE tree.
+ std::vector<DIEInfo> Info; ///< DIE info indexed by DIE index.
+ DIE *CUDie; ///< Root of the linked DIE tree.
uint64_t StartOffset;
uint64_t NextUnitOffset;
@@ -208,8 +348,8 @@ private:
/// The offsets for the attributes in this array couldn't be set while
/// cloning because for cross-cu forward refences the target DIE's
/// offset isn't known you emit the reference attribute.
- std::vector<std::tuple<DIE *, const CompileUnit *, PatchLocation>>
- ForwardDIEReferences;
+ std::vector<std::tuple<DIE *, const CompileUnit *, DeclContext *,
+ PatchLocation>> ForwardDIEReferences;
FunctionIntervals::Allocator RangeAlloc;
/// \brief The ranges in that interval map are the PC ranges for
@@ -236,8 +376,24 @@ private:
std::vector<AccelInfo> Pubnames;
std::vector<AccelInfo> Pubtypes;
/// @}
+
+ /// Cached resolved paths from the line table.
+ std::vector<std::string> ResolvedPaths;
+
+ /// Is this unit subject to the ODR rule?
+ bool HasODR;
+ /// Did a DIE actually contain a valid reloc?
+ bool HasInterestingContent;
+ /// If this is a Clang module, this holds the module's name.
+ std::string ClangModuleName;
};
+void CompileUnit::markEverythingAsKept() {
+ for (auto &I : Info)
+ // Mark everything that wasn't explicity marked for pruning.
+ I.Keep = !I.Prune;
+}
+
uint64_t CompileUnit::computeNextUnitOffset() {
NextUnitOffset = StartOffset + 11 /* Header size */;
// The root DIE might be null, meaning that the Unit had nothing to
@@ -251,8 +407,8 @@ uint64_t CompileUnit::computeNextUnitOffset() {
/// \brief Keep track of a forward cross-cu reference from this unit
/// to \p Die that lives in \p RefUnit.
void CompileUnit::noteForwardReference(DIE *Die, const CompileUnit *RefUnit,
- PatchLocation Attr) {
- ForwardDIEReferences.emplace_back(Die, RefUnit, Attr);
+ DeclContext *Ctxt, PatchLocation Attr) {
+ ForwardDIEReferences.emplace_back(Die, RefUnit, Ctxt, Attr);
}
/// \brief Apply all fixups recorded by noteForwardReference().
@@ -261,8 +417,12 @@ void CompileUnit::fixupForwardReferences() {
DIE *RefDie;
const CompileUnit *RefUnit;
PatchLocation Attr;
- std::tie(RefDie, RefUnit, Attr) = Ref;
- Attr.set(RefDie->getOffset() + RefUnit->getStartOffset());
+ DeclContext *Ctxt;
+ std::tie(RefDie, RefUnit, Ctxt, Attr) = Ref;
+ if (Ctxt && Ctxt->getCanonicalDIEOffset())
+ Attr.set(Ctxt->getCanonicalDIEOffset());
+ else
+ Attr.set(RefDie->getOffset() + RefUnit->getStartOffset());
}
}
@@ -298,91 +458,6 @@ void CompileUnit::addTypeAccelerator(const DIE *Die, const char *Name,
Pubtypes.emplace_back(Name, Die, Offset, false);
}
-/// \brief A string table that doesn't need relocations.
-///
-/// We are doing a final link, no need for a string table that
-/// has relocation entries for every reference to it. This class
-/// provides this ablitity by just associating offsets with
-/// strings.
-class NonRelocatableStringpool {
-public:
- /// \brief Entries are stored into the StringMap and simply linked
- /// together through the second element of this pair in order to
- /// keep track of insertion order.
- typedef StringMap<std::pair<uint32_t, StringMapEntryBase *>, BumpPtrAllocator>
- MapTy;
-
- NonRelocatableStringpool()
- : CurrentEndOffset(0), Sentinel(0), Last(&Sentinel) {
- // Legacy dsymutil puts an empty string at the start of the line
- // table.
- getStringOffset("");
- }
-
- /// \brief Get the offset of string \p S in the string table. This
- /// can insert a new element or return the offset of a preexisitng
- /// one.
- uint32_t getStringOffset(StringRef S);
-
- /// \brief Get permanent storage for \p S (but do not necessarily
- /// emit \p S in the output section).
- /// \returns The StringRef that points to permanent storage to use
- /// in place of \p S.
- StringRef internString(StringRef S);
-
- // \brief Return the first entry of the string table.
- const MapTy::MapEntryTy *getFirstEntry() const {
- return getNextEntry(&Sentinel);
- }
-
- // \brief Get the entry following \p E in the string table or null
- // if \p E was the last entry.
- const MapTy::MapEntryTy *getNextEntry(const MapTy::MapEntryTy *E) const {
- return static_cast<const MapTy::MapEntryTy *>(E->getValue().second);
- }
-
- uint64_t getSize() { return CurrentEndOffset; }
-
-private:
- MapTy Strings;
- uint32_t CurrentEndOffset;
- MapTy::MapEntryTy Sentinel, *Last;
-};
-
-/// \brief Get the offset of string \p S in the string table. This
-/// can insert a new element or return the offset of a preexisitng
-/// one.
-uint32_t NonRelocatableStringpool::getStringOffset(StringRef S) {
- if (S.empty() && !Strings.empty())
- return 0;
-
- std::pair<uint32_t, StringMapEntryBase *> Entry(0, nullptr);
- MapTy::iterator It;
- bool Inserted;
-
- // A non-empty string can't be at offset 0, so if we have an entry
- // with a 0 offset, it must be a previously interned string.
- std::tie(It, Inserted) = Strings.insert(std::make_pair(S, Entry));
- if (Inserted || It->getValue().first == 0) {
- // Set offset and chain at the end of the entries list.
- It->getValue().first = CurrentEndOffset;
- CurrentEndOffset += S.size() + 1; // +1 for the '\0'.
- Last->getValue().second = &*It;
- Last = &*It;
- }
- return It->getValue().first;
-}
-
-/// \brief Put \p S into the StringMap so that it gets permanent
-/// storage, but do not actually link it in the chain of elements
-/// that go into the output section. A latter call to
-/// getStringOffset() with the same string will chain it though.
-StringRef NonRelocatableStringpool::internString(StringRef S) {
- std::pair<uint32_t, StringMapEntryBase *> Entry(0, nullptr);
- auto InsertResult = Strings.insert(std::make_pair(S, Entry));
- return InsertResult.first->getKey();
-}
-
/// \brief The Dwarf streaming logic
///
/// All interactions with the MC layer that is used to build the debug
@@ -425,7 +500,7 @@ public:
bool init(Triple TheTriple, StringRef OutputFilename);
/// \brief Dump the file to the disk.
- bool finish();
+ bool finish(const DebugMap &);
AsmPrinter &getAsmPrinter() const { return *Asm; }
@@ -445,7 +520,7 @@ public:
/// \brief Emit the abbreviation table \p Abbrevs to the
/// debug_abbrev section.
- void emitAbbrevs(const std::vector<DIEAbbrev *> &Abbrevs);
+ void emitAbbrevs(const std::vector<std::unique_ptr<DIEAbbrev>> &Abbrevs);
/// \brief Emit the string table described by \p Pool.
void emitStrings(const NonRelocatableStringpool &Pool);
@@ -472,7 +547,8 @@ public:
/// \brief Emit the line table described in \p Rows into the
/// debug_line section.
- void emitLineTableForUnit(StringRef PrologueBytes, unsigned MinInstLength,
+ void emitLineTableForUnit(MCDwarfLineTableParams Params,
+ StringRef PrologueBytes, unsigned MinInstLength,
std::vector<DWARFDebugLine::Row> &Rows,
unsigned AdddressSize);
@@ -543,9 +619,11 @@ bool DwarfStreamer::init(Triple TheTriple, StringRef OutputFilename) {
if (EC)
return error(Twine(OutputFilename) + ": " + EC.message(), Context);
- MS = TheTarget->createMCObjectStreamer(TheTriple, *MC, *MAB, *OutFile, MCE,
- *MSTI, false,
- /*DWARFMustBeAtTheEnd*/ false);
+ MCTargetOptions MCOptions = InitMCTargetOptionsFromFlags();
+ MS = TheTarget->createMCObjectStreamer(
+ TheTriple, *MC, *MAB, *OutFile, MCE, *MSTI, MCOptions.MCRelaxAll,
+ MCOptions.MCIncrementalLinkerCompatible,
+ /*DWARFMustBeAtTheEnd*/ false);
if (!MS)
return error("no object streamer for target " + TripleName, Context);
@@ -566,7 +644,10 @@ bool DwarfStreamer::init(Triple TheTriple, StringRef OutputFilename) {
return true;
}
-bool DwarfStreamer::finish() {
+bool DwarfStreamer::finish(const DebugMap &DM) {
+ if (DM.getTriple().isOSDarwin() && !DM.getBinaryPath().empty())
+ return MachOUtils::generateDsymCompanion(DM, *MS, *OutFile);
+
MS->Finish();
return true;
}
@@ -605,7 +686,8 @@ void DwarfStreamer::emitCompileUnitHeader(CompileUnit &Unit) {
/// \brief Emit the \p Abbrevs array as the shared abbreviation table
/// for the linked Dwarf file.
-void DwarfStreamer::emitAbbrevs(const std::vector<DIEAbbrev *> &Abbrevs) {
+void DwarfStreamer::emitAbbrevs(
+ const std::vector<std::unique_ptr<DIEAbbrev>> &Abbrevs) {
MS->SwitchSection(MOFI->getDwarfAbbrevSection());
Asm->emitDwarfAbbrevs(Abbrevs);
}
@@ -637,7 +719,7 @@ void DwarfStreamer::emitRangesEntries(
MS->SwitchSection(MC->getObjectFileInfo()->getDwarfRangesSection());
// Offset each range by the right amount.
- int64_t PcOffset = FuncRange.value() + UnitPcOffset;
+ int64_t PcOffset = Entries.empty() ? 0 : FuncRange.value() + UnitPcOffset;
for (const auto &Range : Entries) {
if (Range.isBaseAddressSelectionEntry(AddressSize)) {
warn("unsupported base address selection operation",
@@ -796,7 +878,8 @@ void DwarfStreamer::emitLocationsForUnit(const CompileUnit &Unit,
}
}
-void DwarfStreamer::emitLineTableForUnit(StringRef PrologueBytes,
+void DwarfStreamer::emitLineTableForUnit(MCDwarfLineTableParams Params,
+ StringRef PrologueBytes,
unsigned MinInstLength,
std::vector<DWARFDebugLine::Row> &Rows,
unsigned PointerSize) {
@@ -819,7 +902,7 @@ void DwarfStreamer::emitLineTableForUnit(StringRef PrologueBytes,
if (Rows.empty()) {
// We only have the dummy entry, dsymutil emits an entry with a 0
// address in that case.
- MCDwarfLineAddr::Encode(*MC, INT64_MAX, 0, EncodingOS);
+ MCDwarfLineAddr::Encode(*MC, Params, INT64_MAX, 0, EncodingOS);
MS->EmitBytes(EncodingOS.str());
LineSectionSize += EncodingBuffer.size();
MS->EmitLabel(LineEndSym);
@@ -901,11 +984,10 @@ void DwarfStreamer::emitLineTableForUnit(StringRef PrologueBytes,
int64_t LineDelta = int64_t(Row.Line) - LastLine;
if (!Row.EndSequence) {
- MCDwarfLineAddr::Encode(*MC, LineDelta, AddressDelta, EncodingOS);
+ MCDwarfLineAddr::Encode(*MC, Params, LineDelta, AddressDelta, EncodingOS);
MS->EmitBytes(EncodingOS.str());
LineSectionSize += EncodingBuffer.size();
EncodingBuffer.resize(0);
- EncodingOS.resync();
Address = Row.Address;
LastLine = Row.Line;
RowsSinceLastSequence++;
@@ -920,11 +1002,10 @@ void DwarfStreamer::emitLineTableForUnit(StringRef PrologueBytes,
MS->EmitULEB128IntValue(AddressDelta);
LineSectionSize += 1 + getULEB128Size(AddressDelta);
}
- MCDwarfLineAddr::Encode(*MC, INT64_MAX, 0, EncodingOS);
+ MCDwarfLineAddr::Encode(*MC, Params, INT64_MAX, 0, EncodingOS);
MS->EmitBytes(EncodingOS.str());
LineSectionSize += EncodingBuffer.size();
EncodingBuffer.resize(0);
- EncodingOS.resync();
Address = -1ULL;
LastLine = FileNum = IsStatement = 1;
RowsSinceLastSequence = Column = Isa = 0;
@@ -932,11 +1013,10 @@ void DwarfStreamer::emitLineTableForUnit(StringRef PrologueBytes,
}
if (RowsSinceLastSequence) {
- MCDwarfLineAddr::Encode(*MC, INT64_MAX, 0, EncodingOS);
+ MCDwarfLineAddr::Encode(*MC, Params, INT64_MAX, 0, EncodingOS);
MS->EmitBytes(EncodingOS.str());
LineSectionSize += EncodingBuffer.size();
EncodingBuffer.resize(0);
- EncodingOS.resync();
}
MS->EmitLabel(LineEndSym);
@@ -1035,14 +1115,12 @@ public:
: OutputFilename(OutputFilename), Options(Options),
BinHolder(Options.Verbose), LastCIEOffset(0) {}
- ~DwarfLinker() {
- for (auto *Abbrev : Abbreviations)
- delete Abbrev;
- }
-
/// \brief Link the contents of the DebugMap.
bool link(const DebugMap &);
+ void reportWarning(const Twine &Warning, const DWARFUnit *Unit = nullptr,
+ const DWARFDebugInfoEntryMinimal *DIE = nullptr) const;
+
private:
/// \brief Called at the start of a debug object link.
void startDebugObject(DWARFContext &, DebugMapObject &);
@@ -1050,79 +1128,124 @@ private:
/// \brief Called at the end of a debug object link.
void endDebugObject();
- /// \defgroup FindValidRelocations Translate debug map into a list
- /// of relevant relocations
- ///
- /// @{
- struct ValidReloc {
- uint32_t Offset;
- uint32_t Size;
- uint64_t Addend;
- const DebugMapObject::DebugMapEntry *Mapping;
+ /// Keeps track of relocations.
+ class RelocationManager {
+ struct ValidReloc {
+ uint32_t Offset;
+ uint32_t Size;
+ uint64_t Addend;
+ const DebugMapObject::DebugMapEntry *Mapping;
- ValidReloc(uint32_t Offset, uint32_t Size, uint64_t Addend,
- const DebugMapObject::DebugMapEntry *Mapping)
- : Offset(Offset), Size(Size), Addend(Addend), Mapping(Mapping) {}
+ ValidReloc(uint32_t Offset, uint32_t Size, uint64_t Addend,
+ const DebugMapObject::DebugMapEntry *Mapping)
+ : Offset(Offset), Size(Size), Addend(Addend), Mapping(Mapping) {}
- bool operator<(const ValidReloc &RHS) const { return Offset < RHS.Offset; }
- };
+ bool operator<(const ValidReloc &RHS) const {
+ return Offset < RHS.Offset;
+ }
+ };
- /// \brief The valid relocations for the current DebugMapObject.
- /// This vector is sorted by relocation offset.
- std::vector<ValidReloc> ValidRelocs;
+ DwarfLinker &Linker;
- /// \brief Index into ValidRelocs of the next relocation to
- /// consider. As we walk the DIEs in acsending file offset and as
- /// ValidRelocs is sorted by file offset, keeping this index
- /// uptodate is all we have to do to have a cheap lookup during the
- /// root DIE selection and during DIE cloning.
- unsigned NextValidReloc;
+ /// \brief The valid relocations for the current DebugMapObject.
+ /// This vector is sorted by relocation offset.
+ std::vector<ValidReloc> ValidRelocs;
- bool findValidRelocsInDebugInfo(const object::ObjectFile &Obj,
- const DebugMapObject &DMO);
+ /// \brief Index into ValidRelocs of the next relocation to
+ /// consider. As we walk the DIEs in acsending file offset and as
+ /// ValidRelocs is sorted by file offset, keeping this index
+ /// uptodate is all we have to do to have a cheap lookup during the
+ /// root DIE selection and during DIE cloning.
+ unsigned NextValidReloc;
- bool findValidRelocs(const object::SectionRef &Section,
- const object::ObjectFile &Obj,
- const DebugMapObject &DMO);
+ public:
+ RelocationManager(DwarfLinker &Linker)
+ : Linker(Linker), NextValidReloc(0) {}
- void findValidRelocsMachO(const object::SectionRef &Section,
- const object::MachOObjectFile &Obj,
- const DebugMapObject &DMO);
- /// @}
+ bool hasValidRelocs() const { return !ValidRelocs.empty(); }
+ /// \brief Reset the NextValidReloc counter.
+ void resetValidRelocs() { NextValidReloc = 0; }
+
+ /// \defgroup FindValidRelocations Translate debug map into a list
+ /// of relevant relocations
+ ///
+ /// @{
+ bool findValidRelocsInDebugInfo(const object::ObjectFile &Obj,
+ const DebugMapObject &DMO);
+
+ bool findValidRelocs(const object::SectionRef &Section,
+ const object::ObjectFile &Obj,
+ const DebugMapObject &DMO);
+
+ void findValidRelocsMachO(const object::SectionRef &Section,
+ const object::MachOObjectFile &Obj,
+ const DebugMapObject &DMO);
+ /// @}
+
+ bool hasValidRelocation(uint32_t StartOffset, uint32_t EndOffset,
+ CompileUnit::DIEInfo &Info);
+
+ bool applyValidRelocs(MutableArrayRef<char> Data, uint32_t BaseOffset,
+ bool isLittleEndian);
+ };
/// \defgroup FindRootDIEs Find DIEs corresponding to debug map entries.
///
/// @{
/// \brief Recursively walk the \p DIE tree and look for DIEs to
/// keep. Store that information in \p CU's DIEInfo.
- void lookForDIEsToKeep(const DWARFDebugInfoEntryMinimal &DIE,
+ void lookForDIEsToKeep(RelocationManager &RelocMgr,
+ const DWARFDebugInfoEntryMinimal &DIE,
const DebugMapObject &DMO, CompileUnit &CU,
unsigned Flags);
+ /// If this compile unit is really a skeleton CU that points to a
+ /// clang module, register it in ClangModules and return true.
+ ///
+ /// A skeleton CU is a CU without children, a DW_AT_gnu_dwo_name
+ /// pointing to the module, and a DW_AT_gnu_dwo_id with the module
+ /// hash.
+ bool registerModuleReference(const DWARFDebugInfoEntryMinimal &CUDie,
+ const DWARFUnit &Unit, DebugMap &ModuleMap,
+ unsigned Indent = 0);
+
+ /// Recursively add the debug info in this clang module .pcm
+ /// file (and all the modules imported by it in a bottom-up fashion)
+ /// to Units.
+ void loadClangModule(StringRef Filename, StringRef ModulePath,
+ StringRef ModuleName, uint64_t DwoId,
+ DebugMap &ModuleMap, unsigned Indent = 0);
+
/// \brief Flags passed to DwarfLinker::lookForDIEsToKeep
enum TravesalFlags {
TF_Keep = 1 << 0, ///< Mark the traversed DIEs as kept.
TF_InFunctionScope = 1 << 1, ///< Current scope is a fucntion scope.
TF_DependencyWalk = 1 << 2, ///< Walking the dependencies of a kept DIE.
TF_ParentWalk = 1 << 3, ///< Walking up the parents of a kept DIE.
+ TF_ODR = 1 << 4, ///< Use the ODR whhile keeping dependants.
+ TF_SkipPC = 1 << 5, ///< Skip all location attributes.
};
/// \brief Mark the passed DIE as well as all the ones it depends on
/// as kept.
- void keepDIEAndDenpendencies(const DWARFDebugInfoEntryMinimal &DIE,
+ void keepDIEAndDependencies(RelocationManager &RelocMgr,
+ const DWARFDebugInfoEntryMinimal &DIE,
CompileUnit::DIEInfo &MyInfo,
const DebugMapObject &DMO, CompileUnit &CU,
- unsigned Flags);
+ bool UseODR);
- unsigned shouldKeepDIE(const DWARFDebugInfoEntryMinimal &DIE,
+ unsigned shouldKeepDIE(RelocationManager &RelocMgr,
+ const DWARFDebugInfoEntryMinimal &DIE,
CompileUnit &Unit, CompileUnit::DIEInfo &MyInfo,
unsigned Flags);
- unsigned shouldKeepVariableDIE(const DWARFDebugInfoEntryMinimal &DIE,
+ unsigned shouldKeepVariableDIE(RelocationManager &RelocMgr,
+ const DWARFDebugInfoEntryMinimal &DIE,
CompileUnit &Unit,
CompileUnit::DIEInfo &MyInfo, unsigned Flags);
- unsigned shouldKeepSubprogramDIE(const DWARFDebugInfoEntryMinimal &DIE,
+ unsigned shouldKeepSubprogramDIE(RelocationManager &RelocMgr,
+ const DWARFDebugInfoEntryMinimal &DIE,
CompileUnit &Unit,
CompileUnit::DIEInfo &MyInfo,
unsigned Flags);
@@ -1134,76 +1257,118 @@ private:
/// \defgroup Linking Methods used to link the debug information
///
/// @{
- /// \brief Recursively clone \p InputDIE into an tree of DIE objects
- /// where useless (as decided by lookForDIEsToKeep()) bits have been
- /// stripped out and addresses have been rewritten according to the
- /// debug map.
- ///
- /// \param OutOffset is the offset the cloned DIE in the output
- /// compile unit.
- /// \param PCOffset (while cloning a function scope) is the offset
- /// applied to the entry point of the function to get the linked address.
- ///
- /// \returns the root of the cloned tree.
- DIE *cloneDIE(const DWARFDebugInfoEntryMinimal &InputDIE, CompileUnit &U,
- int64_t PCOffset, uint32_t OutOffset);
- typedef DWARFAbbreviationDeclaration::AttributeSpec AttributeSpec;
+ class DIECloner {
+ DwarfLinker &Linker;
+ RelocationManager &RelocMgr;
+ /// Allocator used for all the DIEValue objects.
+ BumpPtrAllocator &DIEAlloc;
+ MutableArrayRef<CompileUnit> CompileUnits;
+ LinkOptions Options;
- /// \brief Information gathered and exchanged between the various
- /// clone*Attributes helpers about the attributes of a particular DIE.
- struct AttributesInfo {
- const char *Name, *MangledName; ///< Names.
- uint32_t NameOffset, MangledNameOffset; ///< Offsets in the string pool.
+ public:
+ DIECloner(DwarfLinker &Linker, RelocationManager &RelocMgr,
+ BumpPtrAllocator &DIEAlloc,
+ MutableArrayRef<CompileUnit> CompileUnits, LinkOptions &Options)
+ : Linker(Linker), RelocMgr(RelocMgr), DIEAlloc(DIEAlloc),
+ CompileUnits(CompileUnits), Options(Options) {}
- uint64_t OrigHighPc; ///< Value of AT_high_pc in the input DIE
- int64_t PCOffset; ///< Offset to apply to PC addresses inside a function.
+ /// Recursively clone \p InputDIE into an tree of DIE objects
+ /// where useless (as decided by lookForDIEsToKeep()) bits have been
+ /// stripped out and addresses have been rewritten according to the
+ /// debug map.
+ ///
+ /// \param OutOffset is the offset the cloned DIE in the output
+ /// compile unit.
+ /// \param PCOffset (while cloning a function scope) is the offset
+ /// applied to the entry point of the function to get the linked address.
+ ///
+ /// \returns the root of the cloned tree or null if nothing was selected.
+ DIE *cloneDIE(const DWARFDebugInfoEntryMinimal &InputDIE, CompileUnit &U,
+ int64_t PCOffset, uint32_t OutOffset, unsigned Flags);
- bool HasLowPc; ///< Does the DIE have a low_pc attribute?
- bool IsDeclaration; ///< Is this DIE only a declaration?
+ /// Construct the output DIE tree by cloning the DIEs we
+ /// chose to keep above. If there are no valid relocs, then there's
+ /// nothing to clone/emit.
+ void cloneAllCompileUnits(DWARFContextInMemory &DwarfContext);
- AttributesInfo()
- : Name(nullptr), MangledName(nullptr), NameOffset(0),
- MangledNameOffset(0), OrigHighPc(0), PCOffset(0), HasLowPc(false),
- IsDeclaration(false) {}
- };
+ private:
+ typedef DWARFAbbreviationDeclaration::AttributeSpec AttributeSpec;
+
+ /// Information gathered and exchanged between the various
+ /// clone*Attributes helpers about the attributes of a particular DIE.
+ struct AttributesInfo {
+ const char *Name, *MangledName; ///< Names.
+ uint32_t NameOffset, MangledNameOffset; ///< Offsets in the string pool.
+
+ uint64_t OrigLowPc; ///< Value of AT_low_pc in the input DIE
+ uint64_t OrigHighPc; ///< Value of AT_high_pc in the input DIE
+ int64_t PCOffset; ///< Offset to apply to PC addresses inside a function.
+
+ bool HasLowPc; ///< Does the DIE have a low_pc attribute?
+ bool IsDeclaration; ///< Is this DIE only a declaration?
+
+ AttributesInfo()
+ : Name(nullptr), MangledName(nullptr), NameOffset(0),
+ MangledNameOffset(0), OrigLowPc(UINT64_MAX), OrigHighPc(0),
+ PCOffset(0), HasLowPc(false), IsDeclaration(false) {}
+ };
- /// \brief Helper for cloneDIE.
- unsigned cloneAttribute(DIE &Die, const DWARFDebugInfoEntryMinimal &InputDIE,
- CompileUnit &U, const DWARFFormValue &Val,
- const AttributeSpec AttrSpec, unsigned AttrSize,
- AttributesInfo &AttrInfo);
+ /// Helper for cloneDIE.
+ unsigned cloneAttribute(DIE &Die,
+ const DWARFDebugInfoEntryMinimal &InputDIE,
+ CompileUnit &U, const DWARFFormValue &Val,
+ const AttributeSpec AttrSpec, unsigned AttrSize,
+ AttributesInfo &AttrInfo);
- /// \brief Helper for cloneDIE.
- unsigned cloneStringAttribute(DIE &Die, AttributeSpec AttrSpec,
- const DWARFFormValue &Val, const DWARFUnit &U);
+ /// Clone a string attribute described by \p AttrSpec and add
+ /// it to \p Die.
+ /// \returns the size of the new attribute.
+ unsigned cloneStringAttribute(DIE &Die, AttributeSpec AttrSpec,
+ const DWARFFormValue &Val,
+ const DWARFUnit &U);
- /// \brief Helper for cloneDIE.
- unsigned
- cloneDieReferenceAttribute(DIE &Die,
- const DWARFDebugInfoEntryMinimal &InputDIE,
- AttributeSpec AttrSpec, unsigned AttrSize,
- const DWARFFormValue &Val, CompileUnit &Unit);
+ /// Clone an attribute referencing another DIE and add
+ /// it to \p Die.
+ /// \returns the size of the new attribute.
+ unsigned
+ cloneDieReferenceAttribute(DIE &Die,
+ const DWARFDebugInfoEntryMinimal &InputDIE,
+ AttributeSpec AttrSpec, unsigned AttrSize,
+ const DWARFFormValue &Val, CompileUnit &Unit);
- /// \brief Helper for cloneDIE.
- unsigned cloneBlockAttribute(DIE &Die, AttributeSpec AttrSpec,
- const DWARFFormValue &Val, unsigned AttrSize);
+ /// Clone an attribute referencing another DIE and add
+ /// it to \p Die.
+ /// \returns the size of the new attribute.
+ unsigned cloneBlockAttribute(DIE &Die, AttributeSpec AttrSpec,
+ const DWARFFormValue &Val, unsigned AttrSize);
- /// \brief Helper for cloneDIE.
- unsigned cloneAddressAttribute(DIE &Die, AttributeSpec AttrSpec,
- const DWARFFormValue &Val,
- const CompileUnit &Unit, AttributesInfo &Info);
+ /// Clone an attribute referencing another DIE and add
+ /// it to \p Die.
+ /// \returns the size of the new attribute.
+ unsigned cloneAddressAttribute(DIE &Die, AttributeSpec AttrSpec,
+ const DWARFFormValue &Val,
+ const CompileUnit &Unit,
+ AttributesInfo &Info);
- /// \brief Helper for cloneDIE.
- unsigned cloneScalarAttribute(DIE &Die,
- const DWARFDebugInfoEntryMinimal &InputDIE,
- CompileUnit &U, AttributeSpec AttrSpec,
- const DWARFFormValue &Val, unsigned AttrSize,
- AttributesInfo &Info);
+ /// Clone a scalar attribute and add it to \p Die.
+ /// \returns the size of the new attribute.
+ unsigned cloneScalarAttribute(DIE &Die,
+ const DWARFDebugInfoEntryMinimal &InputDIE,
+ CompileUnit &U, AttributeSpec AttrSpec,
+ const DWARFFormValue &Val, unsigned AttrSize,
+ AttributesInfo &Info);
- /// \brief Helper for cloneDIE.
- bool applyValidRelocs(MutableArrayRef<char> Data, uint32_t BaseOffset,
- bool isLittleEndian);
+ /// Get the potential name and mangled name for the entity
+ /// described by \p Die and store them in \Info if they are not
+ /// already there.
+ /// \returns is a name was found.
+ bool getDIENames(const DWARFDebugInfoEntryMinimal &Die, DWARFUnit &U,
+ AttributesInfo &Info);
+
+ /// Create a copy of abbreviation Abbrev.
+ void copyAbbrev(const DWARFAbbreviationDeclaration &Abbrev, bool hasODR);
+ };
/// \brief Assign an abbreviation number to \p Abbrev
void AssignAbbrev(DIEAbbrev &Abbrev);
@@ -1213,7 +1378,7 @@ private:
/// \brief Storage for the unique Abbreviations.
/// This is passed to AsmPrinter::emitDwarfAbbrevs(), thus it cannot
/// be changed to a vecot of unique_ptrs.
- std::vector<DIEAbbrev *> Abbreviations;
+ std::vector<std::unique_ptr<DIEAbbrev>> Abbreviations;
/// \brief Compute and emit debug_ranges section for \p Unit, and
/// patch the attributes referencing it.
@@ -1243,35 +1408,31 @@ private:
BumpPtrAllocator DIEAlloc;
/// @}
+ /// ODR Contexts for that link.
+ DeclContextTree ODRContexts;
+
/// \defgroup Helpers Various helper methods.
///
/// @{
- const DWARFDebugInfoEntryMinimal *
- resolveDIEReference(DWARFFormValue &RefValue, const DWARFUnit &Unit,
- const DWARFDebugInfoEntryMinimal &DIE,
- CompileUnit *&ReferencedCU);
-
- CompileUnit *getUnitForOffset(unsigned Offset);
-
- bool getDIENames(const DWARFDebugInfoEntryMinimal &Die, DWARFUnit &U,
- AttributesInfo &Info);
-
- void reportWarning(const Twine &Warning, const DWARFUnit *Unit = nullptr,
- const DWARFDebugInfoEntryMinimal *DIE = nullptr) const;
-
bool createStreamer(Triple TheTriple, StringRef OutputFilename);
+
+ /// \brief Attempt to load a debug object from disk.
+ ErrorOr<const object::ObjectFile &> loadObject(BinaryHolder &BinaryHolder,
+ DebugMapObject &Obj,
+ const DebugMap &Map);
/// @}
-private:
std::string OutputFilename;
LinkOptions Options;
BinaryHolder BinHolder;
std::unique_ptr<DwarfStreamer> Streamer;
+ uint64_t OutputDebugInfoSize;
+ unsigned UnitID; ///< A unique ID that identifies each compile unit.
/// The units of the current debug map object.
std::vector<CompileUnit> Units;
- /// The debug map object curently under consideration.
+ /// The debug map object currently under consideration.
DebugMapObject *CurrentDebugObject;
/// \brief The Dwarf string pool
@@ -1294,11 +1455,15 @@ private:
/// Offset of the last CIE that has been emitted in the output
/// debug_frame section.
uint32_t LastCIEOffset;
+
+ /// Mapping the PCM filename to the DwoId.
+ StringMap<uint64_t> ClangModules;
};
-/// \brief Similar to DWARFUnitSection::getUnitForOffset(), but
-/// returning our CompileUnit object instead.
-CompileUnit *DwarfLinker::getUnitForOffset(unsigned Offset) {
+/// Similar to DWARFUnitSection::getUnitForOffset(), but returning our
+/// CompileUnit object instead.
+static CompileUnit *getUnitForOffset(MutableArrayRef<CompileUnit> Units,
+ unsigned Offset) {
auto CU =
std::upper_bound(Units.begin(), Units.end(), Offset,
[](uint32_t LHS, const CompileUnit &RHS) {
@@ -1307,38 +1472,251 @@ CompileUnit *DwarfLinker::getUnitForOffset(unsigned Offset) {
return CU != Units.end() ? &*CU : nullptr;
}
-/// \brief Resolve the DIE attribute reference that has been
+/// Resolve the DIE attribute reference that has been
/// extracted in \p RefValue. The resulting DIE migh be in another
/// CompileUnit which is stored into \p ReferencedCU.
/// \returns null if resolving fails for any reason.
-const DWARFDebugInfoEntryMinimal *DwarfLinker::resolveDIEReference(
- DWARFFormValue &RefValue, const DWARFUnit &Unit,
+static const DWARFDebugInfoEntryMinimal *resolveDIEReference(
+ const DwarfLinker &Linker, MutableArrayRef<CompileUnit> Units,
+ const DWARFFormValue &RefValue, const DWARFUnit &Unit,
const DWARFDebugInfoEntryMinimal &DIE, CompileUnit *&RefCU) {
assert(RefValue.isFormClass(DWARFFormValue::FC_Reference));
uint64_t RefOffset = *RefValue.getAsReference(&Unit);
- if ((RefCU = getUnitForOffset(RefOffset)))
+ if ((RefCU = getUnitForOffset(Units, RefOffset)))
if (const auto *RefDie = RefCU->getOrigUnit().getDIEForOffset(RefOffset))
return RefDie;
- reportWarning("could not find referenced DIE", &Unit, &DIE);
+ Linker.reportWarning("could not find referenced DIE", &Unit, &DIE);
return nullptr;
}
-/// \brief Get the potential name and mangled name for the entity
-/// described by \p Die and store them in \Info if they are not
-/// already there.
-/// \returns is a name was found.
-bool DwarfLinker::getDIENames(const DWARFDebugInfoEntryMinimal &Die,
- DWARFUnit &U, AttributesInfo &Info) {
- // FIXME: a bit wastefull as the first getName might return the
+/// \returns whether the passed \a Attr type might contain a DIE
+/// reference suitable for ODR uniquing.
+static bool isODRAttribute(uint16_t Attr) {
+ switch (Attr) {
+ default:
+ return false;
+ case dwarf::DW_AT_type:
+ case dwarf::DW_AT_containing_type:
+ case dwarf::DW_AT_specification:
+ case dwarf::DW_AT_abstract_origin:
+ case dwarf::DW_AT_import:
+ return true;
+ }
+ llvm_unreachable("Improper attribute.");
+}
+
+/// Set the last DIE/CU a context was seen in and, possibly invalidate
+/// the context if it is ambiguous.
+///
+/// In the current implementation, we don't handle overloaded
+/// functions well, because the argument types are not taken into
+/// account when computing the DeclContext tree.
+///
+/// Some of this is mitigated byt using mangled names that do contain
+/// the arguments types, but sometimes (eg. with function templates)
+/// we don't have that. In that case, just do not unique anything that
+/// refers to the contexts we are not able to distinguish.
+///
+/// If a context that is not a namespace appears twice in the same CU,
+/// we know it is ambiguous. Make it invalid.
+bool DeclContext::setLastSeenDIE(CompileUnit &U,
+ const DWARFDebugInfoEntryMinimal *Die) {
+ if (LastSeenCompileUnitID == U.getUniqueID()) {
+ DWARFUnit &OrigUnit = U.getOrigUnit();
+ uint32_t FirstIdx = OrigUnit.getDIEIndex(LastSeenDIE);
+ U.getInfo(FirstIdx).Ctxt = nullptr;
+ return false;
+ }
+
+ LastSeenCompileUnitID = U.getUniqueID();
+ LastSeenDIE = Die;
+ return true;
+}
+
+PointerIntPair<DeclContext *, 1> DeclContextTree::getChildDeclContext(
+ DeclContext &Context, const DWARFDebugInfoEntryMinimal *DIE, CompileUnit &U,
+ NonRelocatableStringpool &StringPool, bool InClangModule) {
+ unsigned Tag = DIE->getTag();
+
+ // FIXME: dsymutil-classic compat: We should bail out here if we
+ // have a specification or an abstract_origin. We will get the
+ // parent context wrong here.
+
+ switch (Tag) {
+ default:
+ // By default stop gathering child contexts.
+ return PointerIntPair<DeclContext *, 1>(nullptr);
+ case dwarf::DW_TAG_module:
+ break;
+ case dwarf::DW_TAG_compile_unit:
+ return PointerIntPair<DeclContext *, 1>(&Context);
+ case dwarf::DW_TAG_subprogram:
+ // Do not unique anything inside CU local functions.
+ if ((Context.getTag() == dwarf::DW_TAG_namespace ||
+ Context.getTag() == dwarf::DW_TAG_compile_unit) &&
+ !DIE->getAttributeValueAsUnsignedConstant(&U.getOrigUnit(),
+ dwarf::DW_AT_external, 0))
+ return PointerIntPair<DeclContext *, 1>(nullptr);
+ // Fallthrough
+ case dwarf::DW_TAG_member:
+ case dwarf::DW_TAG_namespace:
+ case dwarf::DW_TAG_structure_type:
+ case dwarf::DW_TAG_class_type:
+ case dwarf::DW_TAG_union_type:
+ case dwarf::DW_TAG_enumeration_type:
+ case dwarf::DW_TAG_typedef:
+ // Artificial things might be ambiguous, because they might be
+ // created on demand. For example implicitely defined constructors
+ // are ambiguous because of the way we identify contexts, and they
+ // won't be generated everytime everywhere.
+ if (DIE->getAttributeValueAsUnsignedConstant(&U.getOrigUnit(),
+ dwarf::DW_AT_artificial, 0))
+ return PointerIntPair<DeclContext *, 1>(nullptr);
+ break;
+ }
+
+ const char *Name = DIE->getName(&U.getOrigUnit(), DINameKind::LinkageName);
+ const char *ShortName = DIE->getName(&U.getOrigUnit(), DINameKind::ShortName);
+ StringRef NameRef;
+ StringRef ShortNameRef;
+ StringRef FileRef;
+
+ if (Name)
+ NameRef = StringPool.internString(Name);
+ else if (Tag == dwarf::DW_TAG_namespace)
+ // FIXME: For dsymutil-classic compatibility. I think uniquing
+ // within anonymous namespaces is wrong. There is no ODR guarantee
+ // there.
+ NameRef = StringPool.internString("(anonymous namespace)");
+
+ if (ShortName && ShortName != Name)
+ ShortNameRef = StringPool.internString(ShortName);
+ else
+ ShortNameRef = NameRef;
+
+ if (Tag != dwarf::DW_TAG_class_type && Tag != dwarf::DW_TAG_structure_type &&
+ Tag != dwarf::DW_TAG_union_type &&
+ Tag != dwarf::DW_TAG_enumeration_type && NameRef.empty())
+ return PointerIntPair<DeclContext *, 1>(nullptr);
+
+ std::string File;
+ unsigned Line = 0;
+ unsigned ByteSize = UINT32_MAX;
+
+ if (!InClangModule) {
+ // Gather some discriminating data about the DeclContext we will be
+ // creating: File, line number and byte size. This shouldn't be
+ // necessary, because the ODR is just about names, but given that we
+ // do some approximations with overloaded functions and anonymous
+ // namespaces, use these additional data points to make the process
+ // safer. This is disabled for clang modules, because forward
+ // declarations of module-defined types do not have a file and line.
+ ByteSize = DIE->getAttributeValueAsUnsignedConstant(
+ &U.getOrigUnit(), dwarf::DW_AT_byte_size, UINT64_MAX);
+ if (Tag != dwarf::DW_TAG_namespace || !Name) {
+ if (unsigned FileNum = DIE->getAttributeValueAsUnsignedConstant(
+ &U.getOrigUnit(), dwarf::DW_AT_decl_file, 0)) {
+ if (const auto *LT = U.getOrigUnit().getContext().getLineTableForUnit(
+ &U.getOrigUnit())) {
+ // FIXME: dsymutil-classic compatibility. I'd rather not
+ // unique anything in anonymous namespaces, but if we do, then
+ // verify that the file and line correspond.
+ if (!Name && Tag == dwarf::DW_TAG_namespace)
+ FileNum = 1;
+
+ // FIXME: Passing U.getOrigUnit().getCompilationDir()
+ // instead of "" would allow more uniquing, but for now, do
+ // it this way to match dsymutil-classic.
+ if (LT->getFileNameByIndex(
+ FileNum, "",
+ DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath,
+ File)) {
+ Line = DIE->getAttributeValueAsUnsignedConstant(
+ &U.getOrigUnit(), dwarf::DW_AT_decl_line, 0);
+#ifdef HAVE_REALPATH
+ // Cache the resolved paths, because calling realpath is expansive.
+ if (const char *ResolvedPath = U.getResolvedPath(FileNum)) {
+ File = ResolvedPath;
+ } else {
+ char RealPath[PATH_MAX + 1];
+ RealPath[PATH_MAX] = 0;
+ if (::realpath(File.c_str(), RealPath))
+ File = RealPath;
+ U.setResolvedPath(FileNum, File);
+ }
+#endif
+ FileRef = StringPool.internString(File);
+ }
+ }
+ }
+ }
+ }
+
+ if (!Line && NameRef.empty())
+ return PointerIntPair<DeclContext *, 1>(nullptr);
+
+ // We hash NameRef, which is the mangled name, in order to get most
+ // overloaded functions resolve correctly.
+ //
+ // Strictly speaking, hashing the Tag is only necessary for a
+ // DW_TAG_module, to prevent uniquing of a module and a namespace
+ // with the same name.
+ //
+ // FIXME: dsymutil-classic won't unique the same type presented
+ // once as a struct and once as a class. Using the Tag in the fully
+ // qualified name hash to get the same effect.
+ unsigned Hash = hash_combine(Context.getQualifiedNameHash(), Tag, NameRef);
+
+ // FIXME: dsymutil-classic compatibility: when we don't have a name,
+ // use the filename.
+ if (Tag == dwarf::DW_TAG_namespace && NameRef == "(anonymous namespace)")
+ Hash = hash_combine(Hash, FileRef);
+
+ // Now look if this context already exists.
+ DeclContext Key(Hash, Line, ByteSize, Tag, NameRef, FileRef, Context);
+ auto ContextIter = Contexts.find(&Key);
+
+ if (ContextIter == Contexts.end()) {
+ // The context wasn't found.
+ bool Inserted;
+ DeclContext *NewContext =
+ new (Allocator) DeclContext(Hash, Line, ByteSize, Tag, NameRef, FileRef,
+ Context, DIE, U.getUniqueID());
+ std::tie(ContextIter, Inserted) = Contexts.insert(NewContext);
+ assert(Inserted && "Failed to insert DeclContext");
+ (void)Inserted;
+ } else if (Tag != dwarf::DW_TAG_namespace &&
+ !(*ContextIter)->setLastSeenDIE(U, DIE)) {
+ // The context was found, but it is ambiguous with another context
+ // in the same file. Mark it invalid.
+ return PointerIntPair<DeclContext *, 1>(*ContextIter, /* Invalid= */ 1);
+ }
+
+ assert(ContextIter != Contexts.end());
+ // FIXME: dsymutil-classic compatibility. Union types aren't
+ // uniques, but their children might be.
+ if ((Tag == dwarf::DW_TAG_subprogram &&
+ Context.getTag() != dwarf::DW_TAG_structure_type &&
+ Context.getTag() != dwarf::DW_TAG_class_type) ||
+ (Tag == dwarf::DW_TAG_union_type))
+ return PointerIntPair<DeclContext *, 1>(*ContextIter, /* Invalid= */ 1);
+
+ return PointerIntPair<DeclContext *, 1>(*ContextIter);
+}
+
+bool DwarfLinker::DIECloner::getDIENames(const DWARFDebugInfoEntryMinimal &Die,
+ DWARFUnit &U, AttributesInfo &Info) {
+ // FIXME: a bit wasteful as the first getName might return the
// short name.
if (!Info.MangledName &&
(Info.MangledName = Die.getName(&U, DINameKind::LinkageName)))
- Info.MangledNameOffset = StringPool.getStringOffset(Info.MangledName);
+ Info.MangledNameOffset =
+ Linker.StringPool.getStringOffset(Info.MangledName);
if (!Info.Name && (Info.Name = Die.getName(&U, DINameKind::ShortName)))
- Info.NameOffset = StringPool.getStringOffset(Info.Name);
+ Info.NameOffset = Linker.StringPool.getStringOffset(Info.Name);
return Info.Name || Info.MangledName;
}
@@ -1368,17 +1746,70 @@ bool DwarfLinker::createStreamer(Triple TheTriple, StringRef OutputFilename) {
return Streamer->init(TheTriple, OutputFilename);
}
-/// \brief Recursive helper to gather the child->parent relationships in the
-/// original compile unit.
-static void gatherDIEParents(const DWARFDebugInfoEntryMinimal *DIE,
- unsigned ParentIdx, CompileUnit &CU) {
+/// Recursive helper to build the global DeclContext information and
+/// gather the child->parent relationships in the original compile unit.
+///
+/// \return true when this DIE and all of its children are only
+/// forward declarations to types defined in external clang modules
+/// (i.e., forward declarations that are children of a DW_TAG_module).
+static bool analyzeContextInfo(const DWARFDebugInfoEntryMinimal *DIE,
+ unsigned ParentIdx, CompileUnit &CU,
+ DeclContext *CurrentDeclContext,
+ NonRelocatableStringpool &StringPool,
+ DeclContextTree &Contexts,
+ bool InImportedModule = false) {
unsigned MyIdx = CU.getOrigUnit().getDIEIndex(DIE);
- CU.getInfo(MyIdx).ParentIdx = ParentIdx;
+ CompileUnit::DIEInfo &Info = CU.getInfo(MyIdx);
+
+ // Clang imposes an ODR on modules(!) regardless of the language:
+ // "The module-id should consist of only a single identifier,
+ // which provides the name of the module being defined. Each
+ // module shall have a single definition."
+ //
+ // This does not extend to the types inside the modules:
+ // "[I]n C, this implies that if two structs are defined in
+ // different submodules with the same name, those two types are
+ // distinct types (but may be compatible types if their
+ // definitions match)."
+ //
+ // We treat non-C++ modules like namespaces for this reason.
+ if (DIE->getTag() == dwarf::DW_TAG_module && ParentIdx == 0 &&
+ DIE->getAttributeValueAsString(&CU.getOrigUnit(), dwarf::DW_AT_name,
+ "") != CU.getClangModuleName()) {
+ InImportedModule = true;
+ }
+ Info.ParentIdx = ParentIdx;
+ bool InClangModule = CU.isClangModule() || InImportedModule;
+ if (CU.hasODR() || InClangModule) {
+ if (CurrentDeclContext) {
+ auto PtrInvalidPair = Contexts.getChildDeclContext(
+ *CurrentDeclContext, DIE, CU, StringPool, InClangModule);
+ CurrentDeclContext = PtrInvalidPair.getPointer();
+ Info.Ctxt =
+ PtrInvalidPair.getInt() ? nullptr : PtrInvalidPair.getPointer();
+ } else
+ Info.Ctxt = CurrentDeclContext = nullptr;
+ }
+
+ Info.Prune = InImportedModule;
if (DIE->hasChildren())
for (auto *Child = DIE->getFirstChild(); Child && !Child->isNULL();
Child = Child->getSibling())
- gatherDIEParents(Child, MyIdx, CU);
+ Info.Prune &= analyzeContextInfo(Child, MyIdx, CU, CurrentDeclContext,
+ StringPool, Contexts, InImportedModule);
+
+ // Prune this DIE if it is either a forward declaration inside a
+ // DW_TAG_module or a DW_TAG_module that contains nothing but
+ // forward declarations.
+ Info.Prune &= (DIE->getTag() == dwarf::DW_TAG_module) ||
+ DIE->getAttributeValueAsUnsignedConstant(
+ &CU.getOrigUnit(), dwarf::DW_AT_declaration, 0);
+
+ // Don't prune it if there is no definition for the DIE.
+ Info.Prune &= Info.Ctxt && Info.Ctxt->getCanonicalDIEOffset();
+
+ return Info.Prune;
}
static bool dieNeedsChildrenToBeMeaningful(uint32_t Tag) {
@@ -1396,9 +1827,14 @@ static bool dieNeedsChildrenToBeMeaningful(uint32_t Tag) {
llvm_unreachable("Invalid Tag");
}
+static unsigned getRefAddrSize(const DWARFUnit &U) {
+ if (U.getVersion() == 2)
+ return U.getAddressByteSize();
+ return 4;
+}
+
void DwarfLinker::startDebugObject(DWARFContext &Dwarf, DebugMapObject &Obj) {
Units.reserve(Dwarf.getNumCompileUnits());
- NextValidReloc = 0;
// Iterate over the debug map entries and put all the ones that are
// functions (because they have a size) into the Ranges map. This
// map is very similar to the FunctionRanges that are stored in each
@@ -1424,7 +1860,6 @@ void DwarfLinker::startDebugObject(DWARFContext &Dwarf, DebugMapObject &Obj) {
void DwarfLinker::endDebugObject() {
Units.clear();
- ValidRelocs.clear();
Ranges.clear();
for (auto I = DIEBlocks.begin(), E = DIEBlocks.end(); I != E; ++I)
@@ -1440,9 +1875,10 @@ void DwarfLinker::endDebugObject() {
/// \brief Iterate over the relocations of the given \p Section and
/// store the ones that correspond to debug map entries into the
/// ValidRelocs array.
-void DwarfLinker::findValidRelocsMachO(const object::SectionRef &Section,
- const object::MachOObjectFile &Obj,
- const DebugMapObject &DMO) {
+void DwarfLinker::RelocationManager::
+findValidRelocsMachO(const object::SectionRef &Section,
+ const object::MachOObjectFile &Obj,
+ const DebugMapObject &DMO) {
StringRef Contents;
Section.getContents(Contents);
DataExtractor Data(Contents, Obj.isLittleEndian(), 0);
@@ -1453,7 +1889,7 @@ void DwarfLinker::findValidRelocsMachO(const object::SectionRef &Section,
unsigned RelocSize = 1 << Obj.getAnyRelocationLength(MachOReloc);
uint64_t Offset64 = Reloc.getOffset();
if ((RelocSize != 4 && RelocSize != 8)) {
- reportWarning(" unsupported relocation in debug_info section.");
+ Linker.reportWarning(" unsupported relocation in debug_info section.");
continue;
}
uint32_t Offset = Offset64;
@@ -1464,7 +1900,7 @@ void DwarfLinker::findValidRelocsMachO(const object::SectionRef &Section,
if (Sym != Obj.symbol_end()) {
ErrorOr<StringRef> SymbolName = Sym->getName();
if (!SymbolName) {
- reportWarning("error getting relocation symbol name.");
+ Linker.reportWarning("error getting relocation symbol name.");
continue;
}
if (const auto *Mapping = DMO.lookupSymbol(*SymbolName))
@@ -1480,14 +1916,15 @@ void DwarfLinker::findValidRelocsMachO(const object::SectionRef &Section,
/// \brief Dispatch the valid relocation finding logic to the
/// appropriate handler depending on the object file format.
-bool DwarfLinker::findValidRelocs(const object::SectionRef &Section,
- const object::ObjectFile &Obj,
- const DebugMapObject &DMO) {
+bool DwarfLinker::RelocationManager::findValidRelocs(
+ const object::SectionRef &Section, const object::ObjectFile &Obj,
+ const DebugMapObject &DMO) {
// Dispatch to the right handler depending on the file type.
if (auto *MachOObj = dyn_cast<object::MachOObjectFile>(&Obj))
findValidRelocsMachO(Section, *MachOObj, DMO);
else
- reportWarning(Twine("unsupported object file type: ") + Obj.getFileName());
+ Linker.reportWarning(Twine("unsupported object file type: ") +
+ Obj.getFileName());
if (ValidRelocs.empty())
return false;
@@ -1505,8 +1942,9 @@ bool DwarfLinker::findValidRelocs(const object::SectionRef &Section,
/// link by indicating which DIEs refer to symbols present in the
/// linked binary.
/// \returns wether there are any valid relocations in the debug info.
-bool DwarfLinker::findValidRelocsInDebugInfo(const object::ObjectFile &Obj,
- const DebugMapObject &DMO) {
+bool DwarfLinker::RelocationManager::
+findValidRelocsInDebugInfo(const object::ObjectFile &Obj,
+ const DebugMapObject &DMO) {
// Find the debug_info section.
for (const object::SectionRef &Section : Obj.sections()) {
StringRef SectionName;
@@ -1525,8 +1963,9 @@ bool DwarfLinker::findValidRelocsInDebugInfo(const object::ObjectFile &Obj,
/// This function must be called with offsets in strictly ascending
/// order because it never looks back at relocations it already 'went past'.
/// \returns true and sets Info.InDebugMap if it is the case.
-bool DwarfLinker::hasValidRelocation(uint32_t StartOffset, uint32_t EndOffset,
- CompileUnit::DIEInfo &Info) {
+bool DwarfLinker::RelocationManager::
+hasValidRelocation(uint32_t StartOffset, uint32_t EndOffset,
+ CompileUnit::DIEInfo &Info) {
assert(NextValidReloc == 0 ||
StartOffset > ValidRelocs[NextValidReloc - 1].Offset);
if (NextValidReloc >= ValidRelocs.size())
@@ -1546,7 +1985,7 @@ bool DwarfLinker::hasValidRelocation(uint32_t StartOffset, uint32_t EndOffset,
const auto &ValidReloc = ValidRelocs[NextValidReloc++];
const auto &Mapping = ValidReloc.Mapping->getValue();
- if (Options.Verbose)
+ if (Linker.Options.Verbose)
outs() << "Found valid debug map entry: " << ValidReloc.Mapping->getKey()
<< " " << format("\t%016" PRIx64 " => %016" PRIx64,
uint64_t(Mapping.ObjectAddress),
@@ -1579,9 +2018,11 @@ getAttributeOffsets(const DWARFAbbreviationDeclaration *Abbrev, unsigned Idx,
/// \brief Check if a variable describing DIE should be kept.
/// \returns updated TraversalFlags.
-unsigned DwarfLinker::shouldKeepVariableDIE(
- const DWARFDebugInfoEntryMinimal &DIE, CompileUnit &Unit,
- CompileUnit::DIEInfo &MyInfo, unsigned Flags) {
+unsigned DwarfLinker::shouldKeepVariableDIE(RelocationManager &RelocMgr,
+ const DWARFDebugInfoEntryMinimal &DIE,
+ CompileUnit &Unit,
+ CompileUnit::DIEInfo &MyInfo,
+ unsigned Flags) {
const auto *Abbrev = DIE.getAbbreviationDeclarationPtr();
// Global variables with constant value can always be kept.
@@ -1606,7 +2047,7 @@ unsigned DwarfLinker::shouldKeepVariableDIE(
// always check in the variable has a valid relocation, so that the
// DIEInfo is filled. However, we don't want a static variable in a
// function to force us to keep the enclosing function.
- if (!hasValidRelocation(LocationOffset, LocationEndOffset, MyInfo) ||
+ if (!RelocMgr.hasValidRelocation(LocationOffset, LocationEndOffset, MyInfo) ||
(Flags & TF_InFunctionScope))
return Flags;
@@ -1619,6 +2060,7 @@ unsigned DwarfLinker::shouldKeepVariableDIE(
/// \brief Check if a function describing DIE should be kept.
/// \returns updated TraversalFlags.
unsigned DwarfLinker::shouldKeepSubprogramDIE(
+ RelocationManager &RelocMgr,
const DWARFDebugInfoEntryMinimal &DIE, CompileUnit &Unit,
CompileUnit::DIEInfo &MyInfo, unsigned Flags) {
const auto *Abbrev = DIE.getAbbreviationDeclarationPtr();
@@ -1639,7 +2081,7 @@ unsigned DwarfLinker::shouldKeepSubprogramDIE(
DIE.getAttributeValueAsAddress(&OrigUnit, dwarf::DW_AT_low_pc, -1ULL);
assert(LowPc != -1ULL && "low_pc attribute is not an address.");
if (LowPc == -1ULL ||
- !hasValidRelocation(LowPcOffset, LowPcEndOffset, MyInfo))
+ !RelocMgr.hasValidRelocation(LowPcOffset, LowPcEndOffset, MyInfo))
return Flags;
if (Options.Verbose)
@@ -1670,16 +2112,17 @@ unsigned DwarfLinker::shouldKeepSubprogramDIE(
/// \brief Check if a DIE should be kept.
/// \returns updated TraversalFlags.
-unsigned DwarfLinker::shouldKeepDIE(const DWARFDebugInfoEntryMinimal &DIE,
+unsigned DwarfLinker::shouldKeepDIE(RelocationManager &RelocMgr,
+ const DWARFDebugInfoEntryMinimal &DIE,
CompileUnit &Unit,
CompileUnit::DIEInfo &MyInfo,
unsigned Flags) {
switch (DIE.getTag()) {
case dwarf::DW_TAG_constant:
case dwarf::DW_TAG_variable:
- return shouldKeepVariableDIE(DIE, Unit, MyInfo, Flags);
+ return shouldKeepVariableDIE(RelocMgr, DIE, Unit, MyInfo, Flags);
case dwarf::DW_TAG_subprogram:
- return shouldKeepSubprogramDIE(DIE, Unit, MyInfo, Flags);
+ return shouldKeepSubprogramDIE(RelocMgr, DIE, Unit, MyInfo, Flags);
case dwarf::DW_TAG_module:
case dwarf::DW_TAG_imported_module:
case dwarf::DW_TAG_imported_declaration:
@@ -1699,26 +2142,28 @@ unsigned DwarfLinker::shouldKeepDIE(const DWARFDebugInfoEntryMinimal &DIE,
/// back to lookForDIEsToKeep while adding TF_DependencyWalk to the
/// TraversalFlags to inform it that it's not doing the primary DIE
/// tree walk.
-void DwarfLinker::keepDIEAndDenpendencies(const DWARFDebugInfoEntryMinimal &DIE,
+void DwarfLinker::keepDIEAndDependencies(RelocationManager &RelocMgr,
+ const DWARFDebugInfoEntryMinimal &Die,
CompileUnit::DIEInfo &MyInfo,
const DebugMapObject &DMO,
- CompileUnit &CU, unsigned Flags) {
+ CompileUnit &CU, bool UseODR) {
const DWARFUnit &Unit = CU.getOrigUnit();
MyInfo.Keep = true;
// First mark all the parent chain as kept.
unsigned AncestorIdx = MyInfo.ParentIdx;
while (!CU.getInfo(AncestorIdx).Keep) {
- lookForDIEsToKeep(*Unit.getDIEAtIndex(AncestorIdx), DMO, CU,
- TF_ParentWalk | TF_Keep | TF_DependencyWalk);
+ unsigned ODRFlag = UseODR ? TF_ODR : 0;
+ lookForDIEsToKeep(RelocMgr, *Unit.getDIEAtIndex(AncestorIdx), DMO, CU,
+ TF_ParentWalk | TF_Keep | TF_DependencyWalk | ODRFlag);
AncestorIdx = CU.getInfo(AncestorIdx).ParentIdx;
}
// Then we need to mark all the DIEs referenced by this DIE's
// attributes as kept.
DataExtractor Data = Unit.getDebugInfoExtractor();
- const auto *Abbrev = DIE.getAbbreviationDeclarationPtr();
- uint32_t Offset = DIE.getOffset() + getULEB128Size(Abbrev->getCode());
+ const auto *Abbrev = Die.getAbbreviationDeclarationPtr();
+ uint32_t Offset = Die.getOffset() + getULEB128Size(Abbrev->getCode());
// Mark all DIEs referenced through atttributes as kept.
for (const auto &AttrSpec : Abbrev->attributes()) {
@@ -1731,9 +2176,33 @@ void DwarfLinker::keepDIEAndDenpendencies(const DWARFDebugInfoEntryMinimal &DIE,
Val.extractValue(Data, &Offset, &Unit);
CompileUnit *ReferencedCU;
- if (const auto *RefDIE = resolveDIEReference(Val, Unit, DIE, ReferencedCU))
- lookForDIEsToKeep(*RefDIE, DMO, *ReferencedCU,
- TF_Keep | TF_DependencyWalk);
+ if (const auto *RefDIE =
+ resolveDIEReference(*this, MutableArrayRef<CompileUnit>(Units), Val,
+ Unit, Die, ReferencedCU)) {
+ uint32_t RefIdx = ReferencedCU->getOrigUnit().getDIEIndex(RefDIE);
+ CompileUnit::DIEInfo &Info = ReferencedCU->getInfo(RefIdx);
+ // If the referenced DIE has a DeclContext that has already been
+ // emitted, then do not keep the one in this CU. We'll link to
+ // the canonical DIE in cloneDieReferenceAttribute.
+ // FIXME: compatibility with dsymutil-classic. UseODR shouldn't
+ // be necessary and could be advantageously replaced by
+ // ReferencedCU->hasODR() && CU.hasODR().
+ // FIXME: compatibility with dsymutil-classic. There is no
+ // reason not to unique ref_addr references.
+ if (AttrSpec.Form != dwarf::DW_FORM_ref_addr && UseODR && Info.Ctxt &&
+ Info.Ctxt != ReferencedCU->getInfo(Info.ParentIdx).Ctxt &&
+ Info.Ctxt->getCanonicalDIEOffset() && isODRAttribute(AttrSpec.Attr))
+ continue;
+
+ // Keep a module forward declaration if there is no definition.
+ if (!(isODRAttribute(AttrSpec.Attr) && Info.Ctxt &&
+ Info.Ctxt->getCanonicalDIEOffset()))
+ Info.Prune = false;
+
+ unsigned ODRFlag = UseODR ? TF_ODR : 0;
+ lookForDIEsToKeep(RelocMgr, *RefDIE, DMO, *ReferencedCU,
+ TF_Keep | TF_DependencyWalk | ODRFlag);
+ }
}
}
@@ -1749,12 +2218,15 @@ void DwarfLinker::keepDIEAndDenpendencies(const DWARFDebugInfoEntryMinimal &DIE,
/// also called, but during these dependency walks the file order is
/// not respected. The TF_DependencyWalk flag tells us which kind of
/// traversal we are currently doing.
-void DwarfLinker::lookForDIEsToKeep(const DWARFDebugInfoEntryMinimal &DIE,
+void DwarfLinker::lookForDIEsToKeep(RelocationManager &RelocMgr,
+ const DWARFDebugInfoEntryMinimal &Die,
const DebugMapObject &DMO, CompileUnit &CU,
unsigned Flags) {
- unsigned Idx = CU.getOrigUnit().getDIEIndex(&DIE);
+ unsigned Idx = CU.getOrigUnit().getDIEIndex(&Die);
CompileUnit::DIEInfo &MyInfo = CU.getInfo(Idx);
bool AlreadyKept = MyInfo.Keep;
+ if (MyInfo.Prune)
+ return;
// If the Keep flag is set, we are marking a required DIE's
// dependencies. If our target is already marked as kept, we're all
@@ -1762,30 +2234,31 @@ void DwarfLinker::lookForDIEsToKeep(const DWARFDebugInfoEntryMinimal &DIE,
if ((Flags & TF_DependencyWalk) && AlreadyKept)
return;
- // We must not call shouldKeepDIE while called from keepDIEAndDenpendencies,
+ // We must not call shouldKeepDIE while called from keepDIEAndDependencies,
// because it would screw up the relocation finding logic.
if (!(Flags & TF_DependencyWalk))
- Flags = shouldKeepDIE(DIE, CU, MyInfo, Flags);
+ Flags = shouldKeepDIE(RelocMgr, Die, CU, MyInfo, Flags);
// If it is a newly kept DIE mark it as well as all its dependencies as kept.
- if (!AlreadyKept && (Flags & TF_Keep))
- keepDIEAndDenpendencies(DIE, MyInfo, DMO, CU, Flags);
-
+ if (!AlreadyKept && (Flags & TF_Keep)) {
+ bool UseOdr = (Flags & TF_DependencyWalk) ? (Flags & TF_ODR) : CU.hasODR();
+ keepDIEAndDependencies(RelocMgr, Die, MyInfo, DMO, CU, UseOdr);
+ }
// The TF_ParentWalk flag tells us that we are currently walking up
// the parent chain of a required DIE, and we don't want to mark all
// the children of the parents as kept (consider for example a
// DW_TAG_namespace node in the parent chain). There are however a
// set of DIE types for which we want to ignore that directive and still
// walk their children.
- if (dieNeedsChildrenToBeMeaningful(DIE.getTag()))
+ if (dieNeedsChildrenToBeMeaningful(Die.getTag()))
Flags &= ~TF_ParentWalk;
- if (!DIE.hasChildren() || (Flags & TF_ParentWalk))
+ if (!Die.hasChildren() || (Flags & TF_ParentWalk))
return;
- for (auto *Child = DIE.getFirstChild(); Child && !Child->isNULL();
+ for (auto *Child = Die.getFirstChild(); Child && !Child->isNULL();
Child = Child->getSibling())
- lookForDIEsToKeep(*Child, DMO, CU, Flags);
+ lookForDIEsToKeep(RelocMgr, *Child, DMO, CU, Flags);
}
/// \brief Assign an abbreviation numer to \p Abbrev.
@@ -1808,55 +2281,60 @@ void DwarfLinker::AssignAbbrev(DIEAbbrev &Abbrev) {
} else {
// Add to abbreviation list.
Abbreviations.push_back(
- new DIEAbbrev(Abbrev.getTag(), Abbrev.hasChildren()));
+ llvm::make_unique<DIEAbbrev>(Abbrev.getTag(), Abbrev.hasChildren()));
for (const auto &Attr : Abbrev.getData())
Abbreviations.back()->AddAttribute(Attr.getAttribute(), Attr.getForm());
- AbbreviationsSet.InsertNode(Abbreviations.back(), InsertToken);
+ AbbreviationsSet.InsertNode(Abbreviations.back().get(), InsertToken);
// Assign the unique abbreviation number.
Abbrev.setNumber(Abbreviations.size());
Abbreviations.back()->setNumber(Abbreviations.size());
}
}
-/// \brief Clone a string attribute described by \p AttrSpec and add
-/// it to \p Die.
-/// \returns the size of the new attribute.
-unsigned DwarfLinker::cloneStringAttribute(DIE &Die, AttributeSpec AttrSpec,
- const DWARFFormValue &Val,
- const DWARFUnit &U) {
+unsigned DwarfLinker::DIECloner::cloneStringAttribute(DIE &Die,
+ AttributeSpec AttrSpec,
+ const DWARFFormValue &Val,
+ const DWARFUnit &U) {
// Switch everything to out of line strings.
const char *String = *Val.getAsCString(&U);
- unsigned Offset = StringPool.getStringOffset(String);
+ unsigned Offset = Linker.StringPool.getStringOffset(String);
Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr), dwarf::DW_FORM_strp,
DIEInteger(Offset));
return 4;
}
-/// \brief Clone an attribute referencing another DIE and add
-/// it to \p Die.
-/// \returns the size of the new attribute.
-unsigned DwarfLinker::cloneDieReferenceAttribute(
+unsigned DwarfLinker::DIECloner::cloneDieReferenceAttribute(
DIE &Die, const DWARFDebugInfoEntryMinimal &InputDIE,
AttributeSpec AttrSpec, unsigned AttrSize, const DWARFFormValue &Val,
CompileUnit &Unit) {
- uint32_t Ref = *Val.getAsReference(&Unit.getOrigUnit());
+ const DWARFUnit &U = Unit.getOrigUnit();
+ uint32_t Ref = *Val.getAsReference(&U);
DIE *NewRefDie = nullptr;
CompileUnit *RefUnit = nullptr;
- const DWARFDebugInfoEntryMinimal *RefDie = nullptr;
+ DeclContext *Ctxt = nullptr;
- if (!(RefUnit = getUnitForOffset(Ref)) ||
- !(RefDie = RefUnit->getOrigUnit().getDIEForOffset(Ref))) {
- const char *AttributeString = dwarf::AttributeString(AttrSpec.Attr);
- if (!AttributeString)
- AttributeString = "DW_AT_???";
- reportWarning(Twine("Missing DIE for ref in attribute ") + AttributeString +
- ". Dropping.",
- &Unit.getOrigUnit(), &InputDIE);
+ const DWARFDebugInfoEntryMinimal *RefDie =
+ resolveDIEReference(Linker, CompileUnits, Val, U, InputDIE, RefUnit);
+
+ // If the referenced DIE is not found, drop the attribute.
+ if (!RefDie)
return 0;
- }
unsigned Idx = RefUnit->getOrigUnit().getDIEIndex(RefDie);
CompileUnit::DIEInfo &RefInfo = RefUnit->getInfo(Idx);
+
+ // If we already have emitted an equivalent DeclContext, just point
+ // at it.
+ if (isODRAttribute(AttrSpec.Attr)) {
+ Ctxt = RefInfo.Ctxt;
+ if (Ctxt && Ctxt->getCanonicalDIEOffset()) {
+ DIEInteger Attr(Ctxt->getCanonicalDIEOffset());
+ Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr),
+ dwarf::DW_FORM_ref_addr, Attr);
+ return getRefAddrSize(U);
+ }
+ }
+
if (!RefInfo.Clone) {
assert(Ref > InputDIE.getOffset());
// We haven't cloned this DIE yet. Just create an empty one and
@@ -1865,7 +2343,8 @@ unsigned DwarfLinker::cloneDieReferenceAttribute(
}
NewRefDie = RefInfo.Clone;
- if (AttrSpec.Form == dwarf::DW_FORM_ref_addr) {
+ if (AttrSpec.Form == dwarf::DW_FORM_ref_addr ||
+ (Unit.hasODR() && isODRAttribute(AttrSpec.Attr))) {
// We cannot currently rely on a DIEEntry to emit ref_addr
// references, because the implementation calls back to DwarfDebug
// to find the unit offset. (We don't have a DwarfDebug)
@@ -1883,11 +2362,11 @@ unsigned DwarfLinker::cloneDieReferenceAttribute(
// A forward reference. Note and fixup later.
Attr = 0xBADDEF;
Unit.noteForwardReference(
- NewRefDie, RefUnit,
+ NewRefDie, RefUnit, Ctxt,
Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr),
dwarf::DW_FORM_ref_addr, DIEInteger(Attr)));
}
- return AttrSize;
+ return getRefAddrSize(U);
}
Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr),
@@ -1895,25 +2374,24 @@ unsigned DwarfLinker::cloneDieReferenceAttribute(
return AttrSize;
}
-/// \brief Clone an attribute of block form (locations, constants) and add
-/// it to \p Die.
-/// \returns the size of the new attribute.
-unsigned DwarfLinker::cloneBlockAttribute(DIE &Die, AttributeSpec AttrSpec,
- const DWARFFormValue &Val,
- unsigned AttrSize) {
- DIE *Attr;
+unsigned DwarfLinker::DIECloner::cloneBlockAttribute(DIE &Die,
+ AttributeSpec AttrSpec,
+ const DWARFFormValue &Val,
+ unsigned AttrSize) {
+ DIEValueList *Attr;
DIEValue Value;
DIELoc *Loc = nullptr;
DIEBlock *Block = nullptr;
// Just copy the block data over.
if (AttrSpec.Form == dwarf::DW_FORM_exprloc) {
Loc = new (DIEAlloc) DIELoc;
- DIELocs.push_back(Loc);
+ Linker.DIELocs.push_back(Loc);
} else {
Block = new (DIEAlloc) DIEBlock;
- DIEBlocks.push_back(Block);
+ Linker.DIEBlocks.push_back(Block);
}
- Attr = Loc ? static_cast<DIE *>(Loc) : static_cast<DIE *>(Block);
+ Attr = Loc ? static_cast<DIEValueList *>(Loc)
+ : static_cast<DIEValueList *>(Block);
if (Loc)
Value = DIEValue(dwarf::Attribute(AttrSpec.Attr),
@@ -1928,27 +2406,30 @@ unsigned DwarfLinker::cloneBlockAttribute(DIE &Die, AttributeSpec AttrSpec,
// FIXME: If DIEBlock and DIELoc just reuses the Size field of
// the DIE class, this if could be replaced by
// Attr->setSize(Bytes.size()).
- if (Streamer) {
+ if (Linker.Streamer) {
+ auto *AsmPrinter = &Linker.Streamer->getAsmPrinter();
if (Loc)
- Loc->ComputeSize(&Streamer->getAsmPrinter());
+ Loc->ComputeSize(AsmPrinter);
else
- Block->ComputeSize(&Streamer->getAsmPrinter());
+ Block->ComputeSize(AsmPrinter);
}
Die.addValue(DIEAlloc, Value);
return AttrSize;
}
-/// \brief Clone an address attribute and add it to \p Die.
-/// \returns the size of the new attribute.
-unsigned DwarfLinker::cloneAddressAttribute(DIE &Die, AttributeSpec AttrSpec,
- const DWARFFormValue &Val,
- const CompileUnit &Unit,
- AttributesInfo &Info) {
+unsigned DwarfLinker::DIECloner::cloneAddressAttribute(
+ DIE &Die, AttributeSpec AttrSpec, const DWARFFormValue &Val,
+ const CompileUnit &Unit, AttributesInfo &Info) {
uint64_t Addr = *Val.getAsAddress(&Unit.getOrigUnit());
if (AttrSpec.Attr == dwarf::DW_AT_low_pc) {
if (Die.getTag() == dwarf::DW_TAG_inlined_subroutine ||
Die.getTag() == dwarf::DW_TAG_lexical_block)
- Addr += Info.PCOffset;
+ // The low_pc of a block or inline subroutine might get
+ // relocated because it happens to match the low_pc of the
+ // enclosing subprogram. To prevent issues with that, always use
+ // the low_pc from the input DIE if relocations have been applied.
+ Addr = (Info.OrigLowPc != UINT64_MAX ? Info.OrigLowPc : Addr) +
+ Info.PCOffset;
else if (Die.getTag() == dwarf::DW_TAG_compile_unit) {
Addr = Unit.getLowPc();
if (Addr == UINT64_MAX)
@@ -1973,9 +2454,7 @@ unsigned DwarfLinker::cloneAddressAttribute(DIE &Die, AttributeSpec AttrSpec,
return Unit.getOrigUnit().getAddressByteSize();
}
-/// \brief Clone a scalar attribute and add it to \p Die.
-/// \returns the size of the new attribute.
-unsigned DwarfLinker::cloneScalarAttribute(
+unsigned DwarfLinker::DIECloner::cloneScalarAttribute(
DIE &Die, const DWARFDebugInfoEntryMinimal &InputDIE, CompileUnit &Unit,
AttributeSpec AttrSpec, const DWARFFormValue &Val, unsigned AttrSize,
AttributesInfo &Info) {
@@ -1993,8 +2472,9 @@ unsigned DwarfLinker::cloneScalarAttribute(
else if (auto OptionalValue = Val.getAsUnsignedConstant())
Value = *OptionalValue;
else {
- reportWarning("Unsupported scalar attribute form. Dropping attribute.",
- &Unit.getOrigUnit(), &InputDIE);
+ Linker.reportWarning(
+ "Unsupported scalar attribute form. Dropping attribute.",
+ &Unit.getOrigUnit(), &InputDIE);
return 0;
}
PatchLocation Patch =
@@ -2002,6 +2482,7 @@ unsigned DwarfLinker::cloneScalarAttribute(
dwarf::Form(AttrSpec.Form), DIEInteger(Value));
if (AttrSpec.Attr == dwarf::DW_AT_ranges)
Unit.noteRangeAttribute(Die, Patch);
+
// A more generic way to check for location attributes would be
// nice, but it's very unlikely that any other attribute needs a
// location list.
@@ -2017,12 +2498,10 @@ unsigned DwarfLinker::cloneScalarAttribute(
/// \brief Clone \p InputDIE's attribute described by \p AttrSpec with
/// value \p Val, and add it to \p Die.
/// \returns the size of the cloned attribute.
-unsigned DwarfLinker::cloneAttribute(DIE &Die,
- const DWARFDebugInfoEntryMinimal &InputDIE,
- CompileUnit &Unit,
- const DWARFFormValue &Val,
- const AttributeSpec AttrSpec,
- unsigned AttrSize, AttributesInfo &Info) {
+unsigned DwarfLinker::DIECloner::cloneAttribute(
+ DIE &Die, const DWARFDebugInfoEntryMinimal &InputDIE, CompileUnit &Unit,
+ const DWARFFormValue &Val, const AttributeSpec AttrSpec, unsigned AttrSize,
+ AttributesInfo &Info) {
const DWARFUnit &U = Unit.getOrigUnit();
switch (AttrSpec.Form) {
@@ -2056,8 +2535,9 @@ unsigned DwarfLinker::cloneAttribute(DIE &Die,
return cloneScalarAttribute(Die, InputDIE, Unit, AttrSpec, Val, AttrSize,
Info);
default:
- reportWarning("Unsupported attribute form in cloneAttribute. Dropping.", &U,
- &InputDIE);
+ Linker.reportWarning(
+ "Unsupported attribute form in cloneAttribute. Dropping.", &U,
+ &InputDIE);
}
return 0;
@@ -2071,8 +2551,9 @@ unsigned DwarfLinker::cloneAttribute(DIE &Die,
/// monotonic \p BaseOffset values.
///
/// \returns wether any reloc has been applied.
-bool DwarfLinker::applyValidRelocs(MutableArrayRef<char> Data,
- uint32_t BaseOffset, bool isLittleEndian) {
+bool DwarfLinker::RelocationManager::
+applyValidRelocs(MutableArrayRef<char> Data, uint32_t BaseOffset,
+ bool isLittleEndian) {
assert((NextValidReloc == 0 ||
BaseOffset > ValidRelocs[NextValidReloc - 1].Offset) &&
"BaseOffset should only be increasing.");
@@ -2140,16 +2621,33 @@ static bool isTypeTag(uint16_t Tag) {
return false;
}
-/// \brief Recursively clone \p InputDIE's subtrees that have been
-/// selected to appear in the linked output.
-///
-/// \param OutOffset is the Offset where the newly created DIE will
-/// lie in the linked compile unit.
-///
-/// \returns the cloned DIE object or null if nothing was selected.
-DIE *DwarfLinker::cloneDIE(const DWARFDebugInfoEntryMinimal &InputDIE,
- CompileUnit &Unit, int64_t PCOffset,
- uint32_t OutOffset) {
+static bool
+shouldSkipAttribute(DWARFAbbreviationDeclaration::AttributeSpec AttrSpec,
+ uint16_t Tag, bool InDebugMap, bool SkipPC,
+ bool InFunctionScope) {
+ switch (AttrSpec.Attr) {
+ default:
+ return false;
+ case dwarf::DW_AT_low_pc:
+ case dwarf::DW_AT_high_pc:
+ case dwarf::DW_AT_ranges:
+ return SkipPC;
+ case dwarf::DW_AT_location:
+ case dwarf::DW_AT_frame_base:
+ // FIXME: for some reason dsymutil-classic keeps the location
+ // attributes when they are of block type (ie. not location
+ // lists). This is totally wrong for globals where we will keep a
+ // wrong address. It is mostly harmless for locals, but there is
+ // no point in keeping these anyway when the function wasn't linked.
+ return (SkipPC || (!InFunctionScope && Tag == dwarf::DW_TAG_variable &&
+ !InDebugMap)) &&
+ !DWARFFormValue(AttrSpec.Form).isFormClass(DWARFFormValue::FC_Block);
+ }
+}
+
+DIE *DwarfLinker::DIECloner::cloneDIE(
+ const DWARFDebugInfoEntryMinimal &InputDIE, CompileUnit &Unit,
+ int64_t PCOffset, uint32_t OutOffset, unsigned Flags) {
DWARFUnit &U = Unit.getOrigUnit();
unsigned Idx = U.getDIEIndex(&InputDIE);
CompileUnit::DIEInfo &Info = Unit.getInfo(Idx);
@@ -2166,10 +2664,25 @@ DIE *DwarfLinker::cloneDIE(const DWARFDebugInfoEntryMinimal &InputDIE,
Die = Info.Clone = DIE::get(DIEAlloc, dwarf::Tag(InputDIE.getTag()));
assert(Die->getTag() == InputDIE.getTag());
Die->setOffset(OutOffset);
+ if ((Unit.hasODR() || Unit.isClangModule()) &&
+ Die->getTag() != dwarf::DW_TAG_namespace && Info.Ctxt &&
+ Info.Ctxt != Unit.getInfo(Info.ParentIdx).Ctxt &&
+ !Info.Ctxt->getCanonicalDIEOffset()) {
+ // We are about to emit a DIE that is the root of its own valid
+ // DeclContext tree. Make the current offset the canonical offset
+ // for this context.
+ Info.Ctxt->setCanonicalDIEOffset(OutOffset + Unit.getStartOffset());
+ }
// Extract and clone every attribute.
DataExtractor Data = U.getDebugInfoExtractor();
- uint32_t NextOffset = U.getDIEAtIndex(Idx + 1)->getOffset();
+ // Point to the next DIE (generally there is always at least a NULL
+ // entry after the current one). If this is a lone
+ // DW_TAG_compile_unit without any children, point to the next unit.
+ uint32_t NextOffset =
+ (Idx + 1 < U.getNumDIEs())
+ ? U.getDIEAtIndex(Idx + 1)->getOffset()
+ : U.getNextUnitOffset();
AttributesInfo AttrInfo;
// We could copy the data only if we need to aply a relocation to
@@ -2178,7 +2691,7 @@ DIE *DwarfLinker::cloneDIE(const DWARFDebugInfoEntryMinimal &InputDIE,
SmallString<40> DIECopy(Data.getData().substr(Offset, NextOffset - Offset));
Data = DataExtractor(DIECopy, Data.isLittleEndian(), Data.getAddressSize());
// Modify the copy with relocated addresses.
- if (applyValidRelocs(DIECopy, Offset, Data.isLittleEndian())) {
+ if (RelocMgr.applyValidRelocs(DIECopy, Offset, Data.isLittleEndian())) {
// If we applied relocations, we store the value of high_pc that was
// potentially stored in the input DIE. If high_pc is an address
// (Dwarf version == 2), then it might have been relocated to a
@@ -2188,6 +2701,11 @@ DIE *DwarfLinker::cloneDIE(const DWARFDebugInfoEntryMinimal &InputDIE,
// high_pc value is done in cloneAddressAttribute().
AttrInfo.OrigHighPc =
InputDIE.getAttributeValueAsAddress(&U, dwarf::DW_AT_high_pc, 0);
+ // Also store the low_pc. It might get relocated in an
+ // inline_subprogram that happens at the beginning of its
+ // inlining function.
+ AttrInfo.OrigLowPc =
+ InputDIE.getAttributeValueAsAddress(&U, dwarf::DW_AT_low_pc, UINT64_MAX);
}
// Reset the Offset to 0 as we will be working on the local copy of
@@ -2202,7 +2720,27 @@ DIE *DwarfLinker::cloneDIE(const DWARFDebugInfoEntryMinimal &InputDIE,
PCOffset = Info.AddrAdjust;
AttrInfo.PCOffset = PCOffset;
+ if (Abbrev->getTag() == dwarf::DW_TAG_subprogram) {
+ Flags |= TF_InFunctionScope;
+ if (!Info.InDebugMap)
+ Flags |= TF_SkipPC;
+ }
+
+ bool Copied = false;
for (const auto &AttrSpec : Abbrev->attributes()) {
+ if (shouldSkipAttribute(AttrSpec, Die->getTag(), Info.InDebugMap,
+ Flags & TF_SkipPC, Flags & TF_InFunctionScope)) {
+ DWARFFormValue::skipValue(AttrSpec.Form, Data, &Offset, &U);
+ // FIXME: dsymutil-classic keeps the old abbreviation around
+ // even if it's not used. We can remove this (and the copyAbbrev
+ // helper) as soon as bit-for-bit compatibility is not a goal anymore.
+ if (!Copied) {
+ copyAbbrev(*InputDIE.getAbbreviationDeclarationPtr(), Unit.hasODR());
+ Copied = true;
+ }
+ continue;
+ }
+
DWARFFormValue Val(AttrSpec.Form);
uint32_t AttrSize = Offset;
Val.extractValue(Data, &Offset, &U);
@@ -2232,20 +2770,28 @@ DIE *DwarfLinker::cloneDIE(const DWARFDebugInfoEntryMinimal &InputDIE,
Unit.addTypeAccelerator(Die, AttrInfo.Name, AttrInfo.NameOffset);
}
+ // Determine whether there are any children that we want to keep.
+ bool HasChildren = false;
+ for (auto *Child = InputDIE.getFirstChild(); Child && !Child->isNULL();
+ Child = Child->getSibling()) {
+ unsigned Idx = U.getDIEIndex(Child);
+ if (Unit.getInfo(Idx).Keep) {
+ HasChildren = true;
+ break;
+ }
+ }
+
DIEAbbrev NewAbbrev = Die->generateAbbrev();
- // If a scope DIE is kept, we must have kept at least one child. If
- // it's not the case, we'll just be emitting one wasteful end of
- // children marker, but things won't break.
- if (InputDIE.hasChildren())
+ if (HasChildren)
NewAbbrev.setChildrenFlag(dwarf::DW_CHILDREN_yes);
// Assign a permanent abbrev number
- AssignAbbrev(NewAbbrev);
+ Linker.AssignAbbrev(NewAbbrev);
Die->setAbbrevNumber(NewAbbrev.getNumber());
// Add the size of the abbreviation number to the output offset.
OutOffset += getULEB128Size(Die->getAbbrevNumber());
- if (!Abbrev->hasChildren()) {
+ if (!HasChildren) {
// Update our size.
Die->setSize(OutOffset - Die->getOffset());
return Die;
@@ -2254,7 +2800,7 @@ DIE *DwarfLinker::cloneDIE(const DWARFDebugInfoEntryMinimal &InputDIE,
// Recursively clone children.
for (auto *Child = InputDIE.getFirstChild(); Child && !Child->isNULL();
Child = Child->getSibling()) {
- if (DIE *Clone = cloneDIE(*Child, Unit, PCOffset, OutOffset)) {
+ if (DIE *Clone = cloneDIE(*Child, Unit, PCOffset, OutOffset, Flags)) {
Die->addChild(Clone);
OutOffset = Clone->getOffset() + Clone->getSize();
}
@@ -2283,7 +2829,7 @@ void DwarfLinker::patchRangesForUnit(const CompileUnit &Unit,
uint64_t OrigLowPc = OrigUnitDie->getAttributeValueAsAddress(
&OrigUnit, dwarf::DW_AT_low_pc, -1ULL);
// Ranges addresses are based on the unit's low_pc. Compute the
- // offset we need to apply to adapt to the the new unit's low_pc.
+ // offset we need to apply to adapt to the new unit's low_pc.
int64_t UnitPcOffset = 0;
if (OrigLowPc != -1ULL)
UnitPcOffset = int64_t(OrigLowPc) - Unit.getLowPc();
@@ -2293,15 +2839,18 @@ void DwarfLinker::patchRangesForUnit(const CompileUnit &Unit,
RangeAttribute.set(Streamer->getRangesSectionSize());
RangeList.extract(RangeExtractor, &Offset);
const auto &Entries = RangeList.getEntries();
- const DWARFDebugRangeList::RangeListEntry &First = Entries.front();
+ if (!Entries.empty()) {
+ const DWARFDebugRangeList::RangeListEntry &First = Entries.front();
- if (CurrRange == InvalidRange || First.StartAddress < CurrRange.start() ||
- First.StartAddress >= CurrRange.stop()) {
- CurrRange = FunctionRanges.find(First.StartAddress + OrigLowPc);
if (CurrRange == InvalidRange ||
- CurrRange.start() > First.StartAddress + OrigLowPc) {
- reportWarning("no mapping for range.");
- continue;
+ First.StartAddress + OrigLowPc < CurrRange.start() ||
+ First.StartAddress + OrigLowPc >= CurrRange.stop()) {
+ CurrRange = FunctionRanges.find(First.StartAddress + OrigLowPc);
+ if (CurrRange == InvalidRange ||
+ CurrRange.start() > First.StartAddress + OrigLowPc) {
+ reportWarning("no mapping for range.");
+ continue;
+ }
}
}
@@ -2451,12 +3000,13 @@ void DwarfLinker::patchLineTableForUnit(CompileUnit &Unit,
if (StopAddress != -1ULL && !Seq.empty()) {
// Insert end sequence row with the computed end address, but
// the same line as the previous one.
- Seq.emplace_back(Seq.back());
- Seq.back().Address = StopAddress;
- Seq.back().EndSequence = 1;
- Seq.back().PrologueEnd = 0;
- Seq.back().BasicBlock = 0;
- Seq.back().EpilogueBegin = 0;
+ auto NextLine = Seq.back();
+ NextLine.Address = StopAddress;
+ NextLine.EndSequence = 1;
+ NextLine.PrologueEnd = 0;
+ NextLine.BasicBlock = 0;
+ NextLine.EpilogueBegin = 0;
+ Seq.push_back(NextLine);
insertLineSequence(Seq, NewRows);
}
@@ -2484,13 +3034,18 @@ void DwarfLinker::patchLineTableForUnit(CompileUnit &Unit,
// table emitter.
if (LineTable.Prologue.Version != 2 ||
LineTable.Prologue.DefaultIsStmt != DWARF2_LINE_DEFAULT_IS_STMT ||
- LineTable.Prologue.LineBase != -5 || LineTable.Prologue.LineRange != 14 ||
- LineTable.Prologue.OpcodeBase != 13)
+ LineTable.Prologue.OpcodeBase > 13)
reportWarning("line table paramters mismatch. Cannot emit.");
- else
- Streamer->emitLineTableForUnit(LineData.slice(StmtList + 4, PrologueEnd),
+ else {
+ MCDwarfLineTableParams Params;
+ Params.DWARF2LineOpcodeBase = LineTable.Prologue.OpcodeBase;
+ Params.DWARF2LineBase = LineTable.Prologue.LineBase;
+ Params.DWARF2LineRange = LineTable.Prologue.LineRange;
+ Streamer->emitLineTableForUnit(Params,
+ LineData.slice(StmtList + 4, PrologueEnd),
LineTable.Prologue.MinInstLength, NewRows,
Unit.getOrigUnit().getAddressByteSize());
+ }
}
void DwarfLinker::emitAcceleratorEntriesForUnit(CompileUnit &Unit) {
@@ -2583,33 +3138,211 @@ void DwarfLinker::patchFrameInfoForObject(const DebugMapObject &DMO,
}
}
-bool DwarfLinker::link(const DebugMap &Map) {
+void DwarfLinker::DIECloner::copyAbbrev(
+ const DWARFAbbreviationDeclaration &Abbrev, bool hasODR) {
+ DIEAbbrev Copy(dwarf::Tag(Abbrev.getTag()),
+ dwarf::Form(Abbrev.hasChildren()));
+
+ for (const auto &Attr : Abbrev.attributes()) {
+ uint16_t Form = Attr.Form;
+ if (hasODR && isODRAttribute(Attr.Attr))
+ Form = dwarf::DW_FORM_ref_addr;
+ Copy.AddAttribute(dwarf::Attribute(Attr.Attr), dwarf::Form(Form));
+ }
- if (Map.begin() == Map.end()) {
- errs() << "Empty debug map.\n";
+ Linker.AssignAbbrev(Copy);
+}
+
+static uint64_t getDwoId(const DWARFDebugInfoEntryMinimal &CUDie,
+ const DWARFUnit &Unit) {
+ uint64_t DwoId =
+ CUDie.getAttributeValueAsUnsignedConstant(&Unit, dwarf::DW_AT_dwo_id, 0);
+ if (!DwoId)
+ DwoId = CUDie.getAttributeValueAsUnsignedConstant(&Unit,
+ dwarf::DW_AT_GNU_dwo_id, 0);
+ return DwoId;
+}
+
+bool DwarfLinker::registerModuleReference(
+ const DWARFDebugInfoEntryMinimal &CUDie, const DWARFUnit &Unit,
+ DebugMap &ModuleMap, unsigned Indent) {
+ std::string PCMfile =
+ CUDie.getAttributeValueAsString(&Unit, dwarf::DW_AT_dwo_name, "");
+ if (PCMfile.empty())
+ PCMfile =
+ CUDie.getAttributeValueAsString(&Unit, dwarf::DW_AT_GNU_dwo_name, "");
+ if (PCMfile.empty())
return false;
+
+ // Clang module DWARF skeleton CUs abuse this for the path to the module.
+ std::string PCMpath =
+ CUDie.getAttributeValueAsString(&Unit, dwarf::DW_AT_comp_dir, "");
+ uint64_t DwoId = getDwoId(CUDie, Unit);
+
+ std::string Name =
+ CUDie.getAttributeValueAsString(&Unit, dwarf::DW_AT_name, "");
+ if (Name.empty()) {
+ reportWarning("Anonymous module skeleton CU for " + PCMfile);
+ return true;
+ }
+
+ if (Options.Verbose) {
+ outs().indent(Indent);
+ outs() << "Found clang module reference " << PCMfile;
+ }
+
+ auto Cached = ClangModules.find(PCMfile);
+ if (Cached != ClangModules.end()) {
+ if (Cached->second != DwoId)
+ reportWarning(Twine("hash mismatch: this object file was built against a "
+ "different version of the module ") + PCMfile);
+ if (Options.Verbose)
+ outs() << " [cached].\n";
+ return true;
+ }
+ if (Options.Verbose)
+ outs() << " ...\n";
+
+ // Cyclic dependencies are disallowed by Clang, but we still
+ // shouldn't run into an infinite loop, so mark it as processed now.
+ ClangModules.insert({PCMfile, DwoId});
+ loadClangModule(PCMfile, PCMpath, Name, DwoId, ModuleMap, Indent + 2);
+ return true;
+}
+
+ErrorOr<const object::ObjectFile &>
+DwarfLinker::loadObject(BinaryHolder &BinaryHolder, DebugMapObject &Obj,
+ const DebugMap &Map) {
+ auto ErrOrObjs =
+ BinaryHolder.GetObjectFiles(Obj.getObjectFilename(), Obj.getTimestamp());
+ if (std::error_code EC = ErrOrObjs.getError()) {
+ reportWarning(Twine(Obj.getObjectFilename()) + ": " + EC.message());
+ return EC;
+ }
+ auto ErrOrObj = BinaryHolder.Get(Map.getTriple());
+ if (std::error_code EC = ErrOrObj.getError())
+ reportWarning(Twine(Obj.getObjectFilename()) + ": " + EC.message());
+ return ErrOrObj;
+}
+
+void DwarfLinker::loadClangModule(StringRef Filename, StringRef ModulePath,
+ StringRef ModuleName, uint64_t DwoId,
+ DebugMap &ModuleMap, unsigned Indent) {
+ SmallString<80> Path(Options.PrependPath);
+ if (sys::path::is_relative(Filename))
+ sys::path::append(Path, ModulePath, Filename);
+ else
+ sys::path::append(Path, Filename);
+ BinaryHolder ObjHolder(Options.Verbose);
+ auto &Obj =
+ ModuleMap.addDebugMapObject(Path, sys::TimeValue::PosixZeroTime());
+ auto ErrOrObj = loadObject(ObjHolder, Obj, ModuleMap);
+ if (!ErrOrObj) {
+ ClangModules.erase(ClangModules.find(Filename));
+ return;
+ }
+
+ std::unique_ptr<CompileUnit> Unit;
+
+ // Setup access to the debug info.
+ DWARFContextInMemory DwarfContext(*ErrOrObj);
+ RelocationManager RelocMgr(*this);
+ for (const auto &CU : DwarfContext.compile_units()) {
+ auto *CUDie = CU->getUnitDIE(false);
+ // Recursively get all modules imported by this one.
+ if (!registerModuleReference(*CUDie, *CU, ModuleMap, Indent)) {
+ if (Unit) {
+ errs() << Filename << ": Clang modules are expected to have exactly"
+ << " 1 compile unit.\n";
+ exitDsymutil(1);
+ }
+ if (getDwoId(*CUDie, *CU) != DwoId)
+ reportWarning(
+ Twine("hash mismatch: this object file was built against a "
+ "different version of the module ") + Filename);
+
+ // Add this module.
+ Unit = llvm::make_unique<CompileUnit>(*CU, UnitID++, !Options.NoODR,
+ ModuleName);
+ Unit->setHasInterestingContent();
+ analyzeContextInfo(CUDie, 0, *Unit, &ODRContexts.getRoot(), StringPool,
+ ODRContexts);
+ // Keep everything.
+ Unit->markEverythingAsKept();
+ }
+ }
+ if (Options.Verbose) {
+ outs().indent(Indent);
+ outs() << "cloning .debug_info from " << Filename << "\n";
+ }
+
+ DIECloner(*this, RelocMgr, DIEAlloc, MutableArrayRef<CompileUnit>(*Unit),
+ Options)
+ .cloneAllCompileUnits(DwarfContext);
+}
+
+void DwarfLinker::DIECloner::cloneAllCompileUnits(
+ DWARFContextInMemory &DwarfContext) {
+ if (!Linker.Streamer)
+ return;
+
+ for (auto &CurrentUnit : CompileUnits) {
+ const auto *InputDIE = CurrentUnit.getOrigUnit().getUnitDIE();
+ CurrentUnit.setStartOffset(Linker.OutputDebugInfoSize);
+ DIE *OutputDIE = cloneDIE(*InputDIE, CurrentUnit, 0 /* PC offset */,
+ 11 /* Unit Header size */, 0);
+ CurrentUnit.setOutputUnitDIE(OutputDIE);
+ Linker.OutputDebugInfoSize = CurrentUnit.computeNextUnitOffset();
+ if (Linker.Options.NoOutput)
+ continue;
+ // FIXME: for compatibility with the classic dsymutil, we emit
+ // an empty line table for the unit, even if the unit doesn't
+ // actually exist in the DIE tree.
+ Linker.patchLineTableForUnit(CurrentUnit, DwarfContext);
+ if (!OutputDIE)
+ continue;
+ Linker.patchRangesForUnit(CurrentUnit, DwarfContext);
+ Linker.Streamer->emitLocationsForUnit(CurrentUnit, DwarfContext);
+ Linker.emitAcceleratorEntriesForUnit(CurrentUnit);
}
+ if (Linker.Options.NoOutput)
+ return;
+
+ // Emit all the compile unit's debug information.
+ for (auto &CurrentUnit : CompileUnits) {
+ Linker.generateUnitRanges(CurrentUnit);
+ CurrentUnit.fixupForwardReferences();
+ Linker.Streamer->emitCompileUnitHeader(CurrentUnit);
+ if (!CurrentUnit.getOutputUnitDIE())
+ continue;
+ Linker.Streamer->emitDIE(*CurrentUnit.getOutputUnitDIE());
+ }
+}
+
+bool DwarfLinker::link(const DebugMap &Map) {
+
if (!createStreamer(Map.getTriple(), OutputFilename))
return false;
// Size of the DIEs (and headers) generated for the linked output.
- uint64_t OutputDebugInfoSize = 0;
+ OutputDebugInfoSize = 0;
// A unique ID that identifies each compile unit.
- unsigned UnitID = 0;
+ UnitID = 0;
+ DebugMap ModuleMap(Map.getTriple(), Map.getBinaryPath());
+
for (const auto &Obj : Map.objects()) {
CurrentDebugObject = Obj.get();
if (Options.Verbose)
outs() << "DEBUG MAP OBJECT: " << Obj->getObjectFilename() << "\n";
- auto ErrOrObj = BinHolder.GetObjectFile(Obj->getObjectFilename());
- if (std::error_code EC = ErrOrObj.getError()) {
- reportWarning(Twine(Obj->getObjectFilename()) + ": " + EC.message());
+ auto ErrOrObj = loadObject(BinHolder, *Obj, Map);
+ if (!ErrOrObj)
continue;
- }
// Look for relocations that correspond to debug map entries.
- if (!findValidRelocsInDebugInfo(*ErrOrObj, *Obj)) {
+ RelocationManager RelocMgr(*this);
+ if (!RelocMgr.findValidRelocsInDebugInfo(*ErrOrObj, *Obj)) {
if (Options.Verbose)
outs() << "No valid relocations found. Skipping.\n";
continue;
@@ -2619,68 +3352,40 @@ bool DwarfLinker::link(const DebugMap &Map) {
DWARFContextInMemory DwarfContext(*ErrOrObj);
startDebugObject(DwarfContext, *Obj);
- // In a first phase, just read in the debug info and store the DIE
- // parent links that we will use during the next phase.
+ // In a first phase, just read in the debug info and load all clang modules.
for (const auto &CU : DwarfContext.compile_units()) {
auto *CUDie = CU->getUnitDIE(false);
if (Options.Verbose) {
outs() << "Input compilation unit:";
CUDie->dump(outs(), CU.get(), 0);
}
- Units.emplace_back(*CU, UnitID++);
- gatherDIEParents(CUDie, 0, Units.back());
+
+ if (!registerModuleReference(*CUDie, *CU, ModuleMap))
+ Units.emplace_back(*CU, UnitID++, !Options.NoODR, "");
}
+ // Now build the DIE parent links that we will use during the next phase.
+ for (auto &CurrentUnit : Units)
+ analyzeContextInfo(CurrentUnit.getOrigUnit().getUnitDIE(), 0, CurrentUnit,
+ &ODRContexts.getRoot(), StringPool, ODRContexts);
+
// Then mark all the DIEs that need to be present in the linked
// output and collect some information about them. Note that this
// loop can not be merged with the previous one becaue cross-cu
// references require the ParentIdx to be setup for every CU in
// the object file before calling this.
for (auto &CurrentUnit : Units)
- lookForDIEsToKeep(*CurrentUnit.getOrigUnit().getUnitDIE(), *Obj,
+ lookForDIEsToKeep(RelocMgr, *CurrentUnit.getOrigUnit().getUnitDIE(), *Obj,
CurrentUnit, 0);
// The calls to applyValidRelocs inside cloneDIE will walk the
// reloc array again (in the same way findValidRelocsInDebugInfo()
// did). We need to reset the NextValidReloc index to the beginning.
- NextValidReloc = 0;
-
- // Construct the output DIE tree by cloning the DIEs we chose to
- // keep above. If there are no valid relocs, then there's nothing
- // to clone/emit.
- if (!ValidRelocs.empty())
- for (auto &CurrentUnit : Units) {
- const auto *InputDIE = CurrentUnit.getOrigUnit().getUnitDIE();
- CurrentUnit.setStartOffset(OutputDebugInfoSize);
- DIE *OutputDIE = cloneDIE(*InputDIE, CurrentUnit, 0 /* PCOffset */,
- 11 /* Unit Header size */);
- CurrentUnit.setOutputUnitDIE(OutputDIE);
- OutputDebugInfoSize = CurrentUnit.computeNextUnitOffset();
- if (Options.NoOutput)
- continue;
- // FIXME: for compatibility with the classic dsymutil, we emit
- // an empty line table for the unit, even if the unit doesn't
- // actually exist in the DIE tree.
- patchLineTableForUnit(CurrentUnit, DwarfContext);
- if (!OutputDIE)
- continue;
- patchRangesForUnit(CurrentUnit, DwarfContext);
- Streamer->emitLocationsForUnit(CurrentUnit, DwarfContext);
- emitAcceleratorEntriesForUnit(CurrentUnit);
- }
-
- // Emit all the compile unit's debug information.
- if (!ValidRelocs.empty() && !Options.NoOutput)
- for (auto &CurrentUnit : Units) {
- generateUnitRanges(CurrentUnit);
- CurrentUnit.fixupForwardReferences();
- Streamer->emitCompileUnitHeader(CurrentUnit);
- if (!CurrentUnit.getOutputUnitDIE())
- continue;
- Streamer->emitDIE(*CurrentUnit.getOutputUnitDIE());
- }
-
- if (!ValidRelocs.empty() && !Options.NoOutput && !Units.empty())
+ RelocMgr.resetValidRelocs();
+ if (RelocMgr.hasValidRelocs())
+ DIECloner(*this, RelocMgr, DIEAlloc, Units, Options)
+ .cloneAllCompileUnits(DwarfContext);
+ if (!Options.NoOutput && !Units.empty())
patchFrameInfoForObject(*Obj, DwarfContext,
Units[0].getOrigUnit().getAddressByteSize());
@@ -2694,8 +3399,53 @@ bool DwarfLinker::link(const DebugMap &Map) {
Streamer->emitStrings(StringPool);
}
- return Options.NoOutput ? true : Streamer->finish();
+ return Options.NoOutput ? true : Streamer->finish(Map);
+}
}
+
+/// \brief Get the offset of string \p S in the string table. This
+/// can insert a new element or return the offset of a preexisitng
+/// one.
+uint32_t NonRelocatableStringpool::getStringOffset(StringRef S) {
+ if (S.empty() && !Strings.empty())
+ return 0;
+
+ std::pair<uint32_t, StringMapEntryBase *> Entry(0, nullptr);
+ MapTy::iterator It;
+ bool Inserted;
+
+ // A non-empty string can't be at offset 0, so if we have an entry
+ // with a 0 offset, it must be a previously interned string.
+ std::tie(It, Inserted) = Strings.insert(std::make_pair(S, Entry));
+ if (Inserted || It->getValue().first == 0) {
+ // Set offset and chain at the end of the entries list.
+ It->getValue().first = CurrentEndOffset;
+ CurrentEndOffset += S.size() + 1; // +1 for the '\0'.
+ Last->getValue().second = &*It;
+ Last = &*It;
+ }
+ return It->getValue().first;
+}
+
+/// \brief Put \p S into the StringMap so that it gets permanent
+/// storage, but do not actually link it in the chain of elements
+/// that go into the output section. A latter call to
+/// getStringOffset() with the same string will chain it though.
+StringRef NonRelocatableStringpool::internString(StringRef S) {
+ std::pair<uint32_t, StringMapEntryBase *> Entry(0, nullptr);
+ auto InsertResult = Strings.insert(std::make_pair(S, Entry));
+ return InsertResult.first->getKey();
+}
+
+void warn(const Twine &Warning, const Twine &Context) {
+ errs() << Twine("while processing ") + Context + ":\n";
+ errs() << Twine("warning: ") + Warning + "\n";
+}
+
+bool error(const Twine &Error, const Twine &Context) {
+ errs() << Twine("while processing ") + Context + ":\n";
+ errs() << Twine("error: ") + Error + "\n";
+ return false;
}
bool linkDwarf(StringRef OutputFilename, const DebugMap &DM,
diff --git a/tools/dsymutil/MachODebugMapParser.cpp b/tools/dsymutil/MachODebugMapParser.cpp
index 6c9fa9b51325..4412db25426c 100644
--- a/tools/dsymutil/MachODebugMapParser.cpp
+++ b/tools/dsymutil/MachODebugMapParser.cpp
@@ -10,6 +10,7 @@
#include "BinaryHolder.h"
#include "DebugMap.h"
#include "dsymutil.h"
+#include "llvm/ADT/Optional.h"
#include "llvm/Object/MachO.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
@@ -21,19 +22,25 @@ using namespace llvm::object;
class MachODebugMapParser {
public:
- MachODebugMapParser(StringRef BinaryPath, StringRef PathPrefix = "",
- bool Verbose = false)
- : BinaryPath(BinaryPath), PathPrefix(PathPrefix),
- MainBinaryHolder(Verbose), CurrentObjectHolder(Verbose),
- CurrentDebugMapObject(nullptr) {}
+ MachODebugMapParser(StringRef BinaryPath, ArrayRef<std::string> Archs,
+ StringRef PathPrefix = "", bool Verbose = false)
+ : BinaryPath(BinaryPath), Archs(Archs.begin(), Archs.end()),
+ PathPrefix(PathPrefix), MainBinaryHolder(Verbose),
+ CurrentObjectHolder(Verbose), CurrentDebugMapObject(nullptr) {}
- /// \brief Parses and returns the DebugMap of the input binary.
+ /// \brief Parses and returns the DebugMaps of the input binary.
+ /// The binary contains multiple maps in case it is a universal
+ /// binary.
/// \returns an error in case the provided BinaryPath doesn't exist
/// or isn't of a supported type.
- ErrorOr<std::unique_ptr<DebugMap>> parse();
+ ErrorOr<std::vector<std::unique_ptr<DebugMap>>> parse();
+
+ /// Walk the symbol table and dump it.
+ bool dumpStab();
private:
std::string BinaryPath;
+ SmallVector<StringRef, 1> Archs;
std::string PathPrefix;
/// Owns the MemoryBuffer for the main binary.
@@ -47,7 +54,7 @@ private:
/// Owns the MemoryBuffer for the currently handled object file.
BinaryHolder CurrentObjectHolder;
/// Map of the currently processed object file symbol addresses.
- StringMap<uint64_t> CurrentObjectAddresses;
+ StringMap<Optional<uint64_t>> CurrentObjectAddresses;
/// Element of the debug map corresponfing to the current object file.
DebugMapObject *CurrentDebugMapObject;
@@ -55,11 +62,14 @@ private:
const char *CurrentFunctionName;
uint64_t CurrentFunctionAddress;
- void switchToNewDebugMapObject(StringRef Filename);
+ std::unique_ptr<DebugMap> parseOneBinary(const MachOObjectFile &MainBinary,
+ StringRef BinaryPath);
+
+ void switchToNewDebugMapObject(StringRef Filename, sys::TimeValue Timestamp);
void resetParserState();
uint64_t getMainBinarySymbolAddress(StringRef Name);
- void loadMainBinarySymbols();
- void loadCurrentObjectFileSymbols();
+ void loadMainBinarySymbols(const MachOObjectFile &MainBinary);
+ void loadCurrentObjectFileSymbols(const object::MachOObjectFile &Obj);
void handleStabSymbolTableEntry(uint32_t StringIndex, uint8_t Type,
uint8_t SectionIndex, uint16_t Flags,
uint64_t Value);
@@ -68,10 +78,26 @@ private:
handleStabSymbolTableEntry(STE.n_strx, STE.n_type, STE.n_sect, STE.n_desc,
STE.n_value);
}
+
+ /// Dump the symbol table output header.
+ void dumpSymTabHeader(raw_ostream &OS, StringRef Arch);
+
+ /// Dump the contents of nlist entries.
+ void dumpSymTabEntry(raw_ostream &OS, uint64_t Index, uint32_t StringIndex,
+ uint8_t Type, uint8_t SectionIndex, uint16_t Flags,
+ uint64_t Value);
+
+ template <typename STEType>
+ void dumpSymTabEntry(raw_ostream &OS, uint64_t Index, const STEType &STE) {
+ dumpSymTabEntry(OS, Index, STE.n_strx, STE.n_type, STE.n_sect, STE.n_desc,
+ STE.n_value);
+ }
+ void dumpOneBinaryStab(const MachOObjectFile &MainBinary,
+ StringRef BinaryPath);
};
static void Warning(const Twine &Msg) { errs() << "warning: " + Msg + "\n"; }
-}
+} // anonymous namespace
/// Reset the parser state coresponding to the current object
/// file. This is to be called after an object file is finished
@@ -84,41 +110,44 @@ void MachODebugMapParser::resetParserState() {
/// Create a new DebugMapObject. This function resets the state of the
/// parser that was referring to the last object file and sets
/// everything up to add symbols to the new one.
-void MachODebugMapParser::switchToNewDebugMapObject(StringRef Filename) {
+void MachODebugMapParser::switchToNewDebugMapObject(StringRef Filename,
+ sys::TimeValue Timestamp) {
resetParserState();
SmallString<80> Path(PathPrefix);
sys::path::append(Path, Filename);
- auto MachOOrError = CurrentObjectHolder.GetFileAs<MachOObjectFile>(Path);
+ auto MachOOrError =
+ CurrentObjectHolder.GetFilesAs<MachOObjectFile>(Path, Timestamp);
if (auto Error = MachOOrError.getError()) {
Warning(Twine("cannot open debug object \"") + Path.str() + "\": " +
Error.message() + "\n");
return;
}
- loadCurrentObjectFileSymbols();
- CurrentDebugMapObject = &Result->addDebugMapObject(Path);
-}
+ auto ErrOrAchObj =
+ CurrentObjectHolder.GetAs<MachOObjectFile>(Result->getTriple());
+ if (auto Err = ErrOrAchObj.getError()) {
+ return Warning(Twine("cannot open debug object \"") + Path.str() + "\": " +
+ Err.message() + "\n");
+ }
-static Triple getTriple(const object::MachOObjectFile &Obj) {
- Triple TheTriple("unknown-unknown-unknown");
- TheTriple.setArch(Triple::ArchType(Obj.getArch()));
- TheTriple.setObjectFormat(Triple::MachO);
- return TheTriple;
+ CurrentDebugMapObject = &Result->addDebugMapObject(Path, Timestamp);
+ loadCurrentObjectFileSymbols(*ErrOrAchObj);
}
-/// This main parsing routine tries to open the main binary and if
-/// successful iterates over the STAB entries. The real parsing is
-/// done in handleStabSymbolTableEntry.
-ErrorOr<std::unique_ptr<DebugMap>> MachODebugMapParser::parse() {
- auto MainBinOrError = MainBinaryHolder.GetFileAs<MachOObjectFile>(BinaryPath);
- if (auto Error = MainBinOrError.getError())
- return Error;
+static std::string getArchName(const object::MachOObjectFile &Obj) {
+ Triple ThumbTriple;
+ Triple T = Obj.getArch(nullptr, &ThumbTriple);
+ return T.getArchName();
+}
- const MachOObjectFile &MainBinary = *MainBinOrError;
- loadMainBinarySymbols();
- Result = make_unique<DebugMap>(getTriple(MainBinary));
+std::unique_ptr<DebugMap>
+MachODebugMapParser::parseOneBinary(const MachOObjectFile &MainBinary,
+ StringRef BinaryPath) {
+ loadMainBinarySymbols(MainBinary);
+ Result =
+ make_unique<DebugMap>(BinaryHolder::getTriple(MainBinary), BinaryPath);
MainBinaryStrings = MainBinary.getStringTableData();
for (const SymbolRef &Symbol : MainBinary.symbols()) {
const DataRefImpl &DRI = Symbol.getRawDataRefImpl();
@@ -132,6 +161,179 @@ ErrorOr<std::unique_ptr<DebugMap>> MachODebugMapParser::parse() {
return std::move(Result);
}
+// Table that maps Darwin's Mach-O stab constants to strings to allow printing.
+// llvm-nm has very similar code, the strings used here are however slightly
+// different and part of the interface of dsymutil (some project's build-systems
+// parse the ouptut of dsymutil -s), thus they shouldn't be changed.
+struct DarwinStabName {
+ uint8_t NType;
+ const char *Name;
+};
+
+static const struct DarwinStabName DarwinStabNames[] = {
+ {MachO::N_GSYM, "N_GSYM"}, {MachO::N_FNAME, "N_FNAME"},
+ {MachO::N_FUN, "N_FUN"}, {MachO::N_STSYM, "N_STSYM"},
+ {MachO::N_LCSYM, "N_LCSYM"}, {MachO::N_BNSYM, "N_BNSYM"},
+ {MachO::N_PC, "N_PC"}, {MachO::N_AST, "N_AST"},
+ {MachO::N_OPT, "N_OPT"}, {MachO::N_RSYM, "N_RSYM"},
+ {MachO::N_SLINE, "N_SLINE"}, {MachO::N_ENSYM, "N_ENSYM"},
+ {MachO::N_SSYM, "N_SSYM"}, {MachO::N_SO, "N_SO"},
+ {MachO::N_OSO, "N_OSO"}, {MachO::N_LSYM, "N_LSYM"},
+ {MachO::N_BINCL, "N_BINCL"}, {MachO::N_SOL, "N_SOL"},
+ {MachO::N_PARAMS, "N_PARAM"}, {MachO::N_VERSION, "N_VERS"},
+ {MachO::N_OLEVEL, "N_OLEV"}, {MachO::N_PSYM, "N_PSYM"},
+ {MachO::N_EINCL, "N_EINCL"}, {MachO::N_ENTRY, "N_ENTRY"},
+ {MachO::N_LBRAC, "N_LBRAC"}, {MachO::N_EXCL, "N_EXCL"},
+ {MachO::N_RBRAC, "N_RBRAC"}, {MachO::N_BCOMM, "N_BCOMM"},
+ {MachO::N_ECOMM, "N_ECOMM"}, {MachO::N_ECOML, "N_ECOML"},
+ {MachO::N_LENG, "N_LENG"}, {0, nullptr}};
+
+static const char *getDarwinStabString(uint8_t NType) {
+ for (unsigned i = 0; DarwinStabNames[i].Name; i++) {
+ if (DarwinStabNames[i].NType == NType)
+ return DarwinStabNames[i].Name;
+ }
+ return nullptr;
+}
+
+void MachODebugMapParser::dumpSymTabHeader(raw_ostream &OS, StringRef Arch) {
+ OS << "-----------------------------------"
+ "-----------------------------------\n";
+ OS << "Symbol table for: '" << BinaryPath << "' (" << Arch.data() << ")\n";
+ OS << "-----------------------------------"
+ "-----------------------------------\n";
+ OS << "Index n_strx n_type n_sect n_desc n_value\n";
+ OS << "======== -------- ------------------ ------ ------ ----------------\n";
+}
+
+void MachODebugMapParser::dumpSymTabEntry(raw_ostream &OS, uint64_t Index,
+ uint32_t StringIndex, uint8_t Type,
+ uint8_t SectionIndex, uint16_t Flags,
+ uint64_t Value) {
+ // Index
+ OS << '[' << format_decimal(Index, 6) << "] "
+ // n_strx
+ << format_hex_no_prefix(StringIndex, 8) << ' '
+ // n_type...
+ << format_hex_no_prefix(Type, 2) << " (";
+
+ if (Type & MachO::N_STAB)
+ OS << left_justify(getDarwinStabString(Type), 13);
+ else {
+ if (Type & MachO::N_PEXT)
+ OS << "PEXT ";
+ else
+ OS << " ";
+ switch (Type & MachO::N_TYPE) {
+ case MachO::N_UNDF: // 0x0 undefined, n_sect == NO_SECT
+ OS << "UNDF";
+ break;
+ case MachO::N_ABS: // 0x2 absolute, n_sect == NO_SECT
+ OS << "ABS ";
+ break;
+ case MachO::N_SECT: // 0xe defined in section number n_sect
+ OS << "SECT";
+ break;
+ case MachO::N_PBUD: // 0xc prebound undefined (defined in a dylib)
+ OS << "PBUD";
+ break;
+ case MachO::N_INDR: // 0xa indirect
+ OS << "INDR";
+ break;
+ default:
+ OS << format_hex_no_prefix(Type, 2) << " ";
+ break;
+ }
+ if (Type & MachO::N_EXT)
+ OS << " EXT";
+ else
+ OS << " ";
+ }
+
+ OS << ") "
+ // n_sect
+ << format_hex_no_prefix(SectionIndex, 2) << " "
+ // n_desc
+ << format_hex_no_prefix(Flags, 4) << " "
+ // n_value
+ << format_hex_no_prefix(Value, 16);
+
+ const char *Name = &MainBinaryStrings.data()[StringIndex];
+ if (Name && Name[0])
+ OS << " '" << Name << "'";
+
+ OS << "\n";
+}
+
+void MachODebugMapParser::dumpOneBinaryStab(const MachOObjectFile &MainBinary,
+ StringRef BinaryPath) {
+ loadMainBinarySymbols(MainBinary);
+ MainBinaryStrings = MainBinary.getStringTableData();
+ raw_ostream &OS(llvm::outs());
+
+ dumpSymTabHeader(OS, getArchName(MainBinary));
+ uint64_t Idx = 0;
+ for (const SymbolRef &Symbol : MainBinary.symbols()) {
+ const DataRefImpl &DRI = Symbol.getRawDataRefImpl();
+ if (MainBinary.is64Bit())
+ dumpSymTabEntry(OS, Idx, MainBinary.getSymbol64TableEntry(DRI));
+ else
+ dumpSymTabEntry(OS, Idx, MainBinary.getSymbolTableEntry(DRI));
+ Idx++;
+ }
+
+ OS << "\n\n";
+ resetParserState();
+}
+
+static bool shouldLinkArch(SmallVectorImpl<StringRef> &Archs, StringRef Arch) {
+ if (Archs.empty() ||
+ std::find(Archs.begin(), Archs.end(), "all") != Archs.end() ||
+ std::find(Archs.begin(), Archs.end(), "*") != Archs.end())
+ return true;
+
+ if (Arch.startswith("arm") && Arch != "arm64" &&
+ std::find(Archs.begin(), Archs.end(), "arm") != Archs.end())
+ return true;
+
+ return std::find(Archs.begin(), Archs.end(), Arch) != Archs.end();
+}
+
+bool MachODebugMapParser::dumpStab() {
+ auto MainBinOrError =
+ MainBinaryHolder.GetFilesAs<MachOObjectFile>(BinaryPath);
+ if (auto Error = MainBinOrError.getError()) {
+ llvm::errs() << "Cannot get '" << BinaryPath
+ << "' as MachO file: " << Error.message() << "\n";
+ return false;
+ }
+
+ Triple T;
+ for (const auto *Binary : *MainBinOrError)
+ if (shouldLinkArch(Archs, Binary->getArch(nullptr, &T).getArchName()))
+ dumpOneBinaryStab(*Binary, BinaryPath);
+
+ return true;
+}
+
+/// This main parsing routine tries to open the main binary and if
+/// successful iterates over the STAB entries. The real parsing is
+/// done in handleStabSymbolTableEntry.
+ErrorOr<std::vector<std::unique_ptr<DebugMap>>> MachODebugMapParser::parse() {
+ auto MainBinOrError =
+ MainBinaryHolder.GetFilesAs<MachOObjectFile>(BinaryPath);
+ if (auto Error = MainBinOrError.getError())
+ return Error;
+
+ std::vector<std::unique_ptr<DebugMap>> Results;
+ Triple T;
+ for (const auto *Binary : *MainBinOrError)
+ if (shouldLinkArch(Archs, Binary->getArch(nullptr, &T).getArchName()))
+ Results.push_back(parseOneBinary(*Binary, BinaryPath));
+
+ return std::move(Results);
+}
+
/// Interpret the STAB entries to fill the DebugMap.
void MachODebugMapParser::handleStabSymbolTableEntry(uint32_t StringIndex,
uint8_t Type,
@@ -144,8 +346,11 @@ void MachODebugMapParser::handleStabSymbolTableEntry(uint32_t StringIndex,
const char *Name = &MainBinaryStrings.data()[StringIndex];
// An N_OSO entry represents the start of a new object file description.
- if (Type == MachO::N_OSO)
- return switchToNewDebugMapObject(Name);
+ if (Type == MachO::N_OSO) {
+ sys::TimeValue Timestamp;
+ Timestamp.fromEpochTime(Value);
+ return switchToNewDebugMapObject(Name, Timestamp);
+ }
// If the last N_OSO object file wasn't found,
// CurrentDebugMapObject will be null. Do not update anything
@@ -184,22 +389,33 @@ void MachODebugMapParser::handleStabSymbolTableEntry(uint32_t StringIndex,
if (ObjectSymIt == CurrentObjectAddresses.end())
return Warning("could not find object file symbol for symbol " +
Twine(Name));
- if (!CurrentDebugMapObject->addSymbol(Name, ObjectSymIt->getValue(), Value,
+ if (!ObjectSymIt->getValue())
+ return;
+ if (!CurrentDebugMapObject->addSymbol(Name, *ObjectSymIt->getValue(), Value,
Size))
return Warning(Twine("failed to insert symbol '") + Name +
"' in the debug map.");
}
/// Load the current object file symbols into CurrentObjectAddresses.
-void MachODebugMapParser::loadCurrentObjectFileSymbols() {
+void MachODebugMapParser::loadCurrentObjectFileSymbols(
+ const object::MachOObjectFile &Obj) {
CurrentObjectAddresses.clear();
- for (auto Sym : CurrentObjectHolder.Get().symbols()) {
+ for (auto Sym : Obj.symbols()) {
uint64_t Addr = Sym.getValue();
ErrorOr<StringRef> Name = Sym.getName();
if (!Name)
continue;
- CurrentObjectAddresses[*Name] = Addr;
+ // Objective-C on i386 uses artificial absolute symbols to
+ // perform some link time checks. Those symbols have a fixed 0
+ // address that might conflict with real symbols in the object
+ // file. As I cannot see a way for absolute symbols to find
+ // their way into the debug information, let's just ignore those.
+ if (Sym.getFlags() & SymbolRef::SF_Absolute)
+ CurrentObjectAddresses[*Name] = None;
+ else
+ CurrentObjectAddresses[*Name] = Addr;
}
}
@@ -215,9 +431,10 @@ uint64_t MachODebugMapParser::getMainBinarySymbolAddress(StringRef Name) {
/// Load the interesting main binary symbols' addresses into
/// MainBinarySymbolAddresses.
-void MachODebugMapParser::loadMainBinarySymbols() {
- const MachOObjectFile &MainBinary = MainBinaryHolder.GetAs<MachOObjectFile>();
+void MachODebugMapParser::loadMainBinarySymbols(
+ const MachOObjectFile &MainBinary) {
section_iterator Section = MainBinary.section_end();
+ MainBinarySymbolAddresses.clear();
for (const auto &Sym : MainBinary.symbols()) {
SymbolRef::Type Type = Sym.getType();
// Skip undefined and STAB entries.
@@ -227,8 +444,13 @@ void MachODebugMapParser::loadMainBinarySymbols() {
// are the only ones that need to be queried because the address
// of common data won't be described in the debug map. All other
// addresses should be fetched for the debug map.
- if (!(Sym.getFlags() & SymbolRef::SF_Global) || Sym.getSection(Section) ||
- Section == MainBinary.section_end() || Section->isText())
+ if (!(Sym.getFlags() & SymbolRef::SF_Global))
+ continue;
+ ErrorOr<section_iterator> SectionOrErr = Sym.getSection();
+ if (!SectionOrErr)
+ continue;
+ Section = *SectionOrErr;
+ if (Section == MainBinary.section_end() || Section->isText())
continue;
uint64_t Addr = Sym.getValue();
ErrorOr<StringRef> NameOrErr = Sym.getName();
@@ -243,16 +465,21 @@ void MachODebugMapParser::loadMainBinarySymbols() {
namespace llvm {
namespace dsymutil {
-llvm::ErrorOr<std::unique_ptr<DebugMap>> parseDebugMap(StringRef InputFile,
- StringRef PrependPath,
- bool Verbose,
- bool InputIsYAML) {
+llvm::ErrorOr<std::vector<std::unique_ptr<DebugMap>>>
+parseDebugMap(StringRef InputFile, ArrayRef<std::string> Archs,
+ StringRef PrependPath, bool Verbose, bool InputIsYAML) {
if (!InputIsYAML) {
- MachODebugMapParser Parser(InputFile, PrependPath, Verbose);
+ MachODebugMapParser Parser(InputFile, Archs, PrependPath, Verbose);
return Parser.parse();
} else {
return DebugMap::parseYAMLDebugMap(InputFile, PrependPath, Verbose);
}
}
+
+bool dumpStab(StringRef InputFile, ArrayRef<std::string> Archs,
+ StringRef PrependPath) {
+ MachODebugMapParser Parser(InputFile, Archs, PrependPath, false);
+ return Parser.dumpStab();
}
-}
+} // namespace dsymutil
+} // namespace llvm
diff --git a/tools/dsymutil/MachOUtils.cpp b/tools/dsymutil/MachOUtils.cpp
new file mode 100644
index 000000000000..44cc528ed8f2
--- /dev/null
+++ b/tools/dsymutil/MachOUtils.cpp
@@ -0,0 +1,521 @@
+//===-- MachOUtils.h - Mach-o specific helpers for dsymutil --------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MachOUtils.h"
+#include "BinaryHolder.h"
+#include "DebugMap.h"
+#include "dsymutil.h"
+#include "NonRelocatableStringpool.h"
+#include "llvm/MC/MCSectionMachO.h"
+#include "llvm/MC/MCAsmLayout.h"
+#include "llvm/MC/MCSectionMachO.h"
+#include "llvm/MC/MCObjectStreamer.h"
+#include "llvm/MC/MCStreamer.h"
+#include "llvm/Object/MachO.h"
+#include "llvm/Support/FileUtilities.h"
+#include "llvm/Support/Program.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace llvm {
+namespace dsymutil {
+namespace MachOUtils {
+
+std::string getArchName(StringRef Arch) {
+ if (Arch.startswith("thumb"))
+ return (llvm::Twine("arm") + Arch.drop_front(5)).str();
+ return Arch;
+}
+
+static bool runLipo(StringRef SDKPath, SmallVectorImpl<const char *> &Args) {
+ auto Path = sys::findProgramByName("lipo", makeArrayRef(SDKPath));
+ if (!Path)
+ Path = sys::findProgramByName("lipo");
+
+ if (!Path) {
+ errs() << "error: lipo: " << Path.getError().message() << "\n";
+ return false;
+ }
+
+ std::string ErrMsg;
+ int result =
+ sys::ExecuteAndWait(*Path, Args.data(), nullptr, nullptr, 0, 0, &ErrMsg);
+ if (result) {
+ errs() << "error: lipo: " << ErrMsg << "\n";
+ return false;
+ }
+
+ return true;
+}
+
+bool generateUniversalBinary(SmallVectorImpl<ArchAndFilename> &ArchFiles,
+ StringRef OutputFileName,
+ const LinkOptions &Options, StringRef SDKPath) {
+ // No need to merge one file into a universal fat binary. First, try
+ // to move it (rename) to the final location. If that fails because
+ // of cross-device link issues then copy and delete.
+ if (ArchFiles.size() == 1) {
+ StringRef From(ArchFiles.front().Path);
+ if (sys::fs::rename(From, OutputFileName)) {
+ if (std::error_code EC = sys::fs::copy_file(From, OutputFileName)) {
+ errs() << "error: while copying " << From << " to " << OutputFileName
+ << ": " << EC.message() << "\n";
+ return false;
+ }
+ sys::fs::remove(From);
+ }
+ return true;
+ }
+
+ SmallVector<const char *, 8> Args;
+ Args.push_back("lipo");
+ Args.push_back("-create");
+
+ for (auto &Thin : ArchFiles)
+ Args.push_back(Thin.Path.c_str());
+
+ // Align segments to match dsymutil-classic alignment
+ for (auto &Thin : ArchFiles) {
+ Thin.Arch = getArchName(Thin.Arch);
+ Args.push_back("-segalign");
+ Args.push_back(Thin.Arch.c_str());
+ Args.push_back("20");
+ }
+
+ Args.push_back("-output");
+ Args.push_back(OutputFileName.data());
+ Args.push_back(nullptr);
+
+ if (Options.Verbose) {
+ outs() << "Running lipo\n";
+ for (auto Arg : Args)
+ outs() << ' ' << ((Arg == nullptr) ? "\n" : Arg);
+ }
+
+ return Options.NoOutput ? true : runLipo(SDKPath, Args);
+}
+
+// Return a MachO::segment_command_64 that holds the same values as
+// the passed MachO::segment_command. We do that to avoid having to
+// duplicat the logic for 32bits and 64bits segments.
+struct MachO::segment_command_64 adaptFrom32bits(MachO::segment_command Seg) {
+ MachO::segment_command_64 Seg64;
+ Seg64.cmd = Seg.cmd;
+ Seg64.cmdsize = Seg.cmdsize;
+ memcpy(Seg64.segname, Seg.segname, sizeof(Seg.segname));
+ Seg64.vmaddr = Seg.vmaddr;
+ Seg64.vmsize = Seg.vmsize;
+ Seg64.fileoff = Seg.fileoff;
+ Seg64.filesize = Seg.filesize;
+ Seg64.maxprot = Seg.maxprot;
+ Seg64.initprot = Seg.initprot;
+ Seg64.nsects = Seg.nsects;
+ Seg64.flags = Seg.flags;
+ return Seg64;
+}
+
+// Iterate on all \a Obj segments, and apply \a Handler to them.
+template <typename FunctionTy>
+static void iterateOnSegments(const object::MachOObjectFile &Obj,
+ FunctionTy Handler) {
+ for (const auto &LCI : Obj.load_commands()) {
+ MachO::segment_command_64 Segment;
+ if (LCI.C.cmd == MachO::LC_SEGMENT)
+ Segment = adaptFrom32bits(Obj.getSegmentLoadCommand(LCI));
+ else if (LCI.C.cmd == MachO::LC_SEGMENT_64)
+ Segment = Obj.getSegment64LoadCommand(LCI);
+ else
+ continue;
+
+ Handler(Segment);
+ }
+}
+
+// Transfer the symbols described by \a NList to \a NewSymtab which is
+// just the raw contents of the symbol table for the dSYM companion file.
+// \returns whether the symbol was tranfered or not.
+template <typename NListTy>
+static bool transferSymbol(NListTy NList, bool IsLittleEndian,
+ StringRef Strings, SmallVectorImpl<char> &NewSymtab,
+ NonRelocatableStringpool &NewStrings,
+ bool &InDebugNote) {
+ // Do not transfer undefined symbols, we want real addresses.
+ if ((NList.n_type & MachO::N_TYPE) == MachO::N_UNDF)
+ return false;
+
+ StringRef Name = StringRef(Strings.begin() + NList.n_strx);
+ if (InDebugNote) {
+ InDebugNote =
+ (NList.n_type != MachO::N_SO) || (!Name.empty() && Name[0] != '\0');
+ return false;
+ } else if (NList.n_type == MachO::N_SO) {
+ InDebugNote = true;
+ return false;
+ }
+
+ // FIXME: The + 1 is here to mimic dsymutil-classic that has 2 empty
+ // strings at the start of the generated string table (There is
+ // corresponding code in the string table emission).
+ NList.n_strx = NewStrings.getStringOffset(Name) + 1;
+ if (IsLittleEndian != sys::IsLittleEndianHost)
+ MachO::swapStruct(NList);
+
+ NewSymtab.append(reinterpret_cast<char *>(&NList),
+ reinterpret_cast<char *>(&NList + 1));
+ return true;
+}
+
+// Wrapper around transferSymbol to transfer all of \a Obj symbols
+// to \a NewSymtab. This function does not write in the output file.
+// \returns the number of symbols in \a NewSymtab.
+static unsigned transferSymbols(const object::MachOObjectFile &Obj,
+ SmallVectorImpl<char> &NewSymtab,
+ NonRelocatableStringpool &NewStrings) {
+ unsigned Syms = 0;
+ StringRef Strings = Obj.getStringTableData();
+ bool IsLittleEndian = Obj.isLittleEndian();
+ bool InDebugNote = false;
+
+ if (Obj.is64Bit()) {
+ for (const object::SymbolRef &Symbol : Obj.symbols()) {
+ object::DataRefImpl DRI = Symbol.getRawDataRefImpl();
+ if (transferSymbol(Obj.getSymbol64TableEntry(DRI), IsLittleEndian,
+ Strings, NewSymtab, NewStrings, InDebugNote))
+ ++Syms;
+ }
+ } else {
+ for (const object::SymbolRef &Symbol : Obj.symbols()) {
+ object::DataRefImpl DRI = Symbol.getRawDataRefImpl();
+ if (transferSymbol(Obj.getSymbolTableEntry(DRI), IsLittleEndian, Strings,
+ NewSymtab, NewStrings, InDebugNote))
+ ++Syms;
+ }
+ }
+ return Syms;
+}
+
+static MachO::section
+getSection(const object::MachOObjectFile &Obj,
+ const MachO::segment_command &Seg,
+ const object::MachOObjectFile::LoadCommandInfo &LCI, unsigned Idx) {
+ return Obj.getSection(LCI, Idx);
+}
+
+static MachO::section_64
+getSection(const object::MachOObjectFile &Obj,
+ const MachO::segment_command_64 &Seg,
+ const object::MachOObjectFile::LoadCommandInfo &LCI, unsigned Idx) {
+ return Obj.getSection64(LCI, Idx);
+}
+
+// Transfer \a Segment from \a Obj to the output file. This calls into \a Writer
+// to write these load commands directly in the output file at the current
+// position.
+// The function also tries to find a hole in the address map to fit the __DWARF
+// segment of \a DwarfSegmentSize size. \a EndAddress is updated to point at the
+// highest segment address.
+// When the __LINKEDIT segment is transfered, its offset and size are set resp.
+// to \a LinkeditOffset and \a LinkeditSize.
+template <typename SegmentTy>
+static void transferSegmentAndSections(
+ const object::MachOObjectFile::LoadCommandInfo &LCI, SegmentTy Segment,
+ const object::MachOObjectFile &Obj, MCObjectWriter &Writer,
+ uint64_t LinkeditOffset, uint64_t LinkeditSize, uint64_t DwarfSegmentSize,
+ uint64_t &GapForDwarf, uint64_t &EndAddress) {
+ if (StringRef("__DWARF") == Segment.segname)
+ return;
+
+ Segment.fileoff = Segment.filesize = 0;
+
+ if (StringRef("__LINKEDIT") == Segment.segname) {
+ Segment.fileoff = LinkeditOffset;
+ Segment.filesize = LinkeditSize;
+ }
+
+ // Check if the end address of the last segment and our current
+ // start address leave a sufficient gap to store the __DWARF
+ // segment.
+ uint64_t PrevEndAddress = EndAddress;
+ EndAddress = RoundUpToAlignment(EndAddress, 0x1000);
+ if (GapForDwarf == UINT64_MAX && Segment.vmaddr > EndAddress &&
+ Segment.vmaddr - EndAddress >= DwarfSegmentSize)
+ GapForDwarf = EndAddress;
+
+ // The segments are not necessarily sorted by their vmaddr.
+ EndAddress =
+ std::max<uint64_t>(PrevEndAddress, Segment.vmaddr + Segment.vmsize);
+ unsigned nsects = Segment.nsects;
+ if (Obj.isLittleEndian() != sys::IsLittleEndianHost)
+ MachO::swapStruct(Segment);
+ Writer.writeBytes(
+ StringRef(reinterpret_cast<char *>(&Segment), sizeof(Segment)));
+ for (unsigned i = 0; i < nsects; ++i) {
+ auto Sect = getSection(Obj, Segment, LCI, i);
+ Sect.offset = Sect.reloff = Sect.nreloc = 0;
+ if (Obj.isLittleEndian() != sys::IsLittleEndianHost)
+ MachO::swapStruct(Sect);
+ Writer.writeBytes(StringRef(reinterpret_cast<char *>(&Sect), sizeof(Sect)));
+ }
+}
+
+// Write the __DWARF segment load command to the output file.
+static void createDwarfSegment(uint64_t VMAddr, uint64_t FileOffset,
+ uint64_t FileSize, unsigned NumSections,
+ MCAsmLayout &Layout, MachObjectWriter &Writer) {
+ Writer.writeSegmentLoadCommand("__DWARF", NumSections, VMAddr,
+ RoundUpToAlignment(FileSize, 0x1000),
+ FileOffset, FileSize, /* MaxProt */ 7,
+ /* InitProt =*/3);
+
+ for (unsigned int i = 0, n = Layout.getSectionOrder().size(); i != n; ++i) {
+ MCSection *Sec = Layout.getSectionOrder()[i];
+ if (Sec->begin() == Sec->end() || !Layout.getSectionFileSize(Sec))
+ continue;
+
+ unsigned Align = Sec->getAlignment();
+ if (Align > 1) {
+ VMAddr = RoundUpToAlignment(VMAddr, Align);
+ FileOffset = RoundUpToAlignment(FileOffset, Align);
+ }
+ Writer.writeSection(Layout, *Sec, VMAddr, FileOffset, 0, 0, 0);
+
+ FileOffset += Layout.getSectionAddressSize(Sec);
+ VMAddr += Layout.getSectionAddressSize(Sec);
+ }
+}
+
+static bool isExecutable(const object::MachOObjectFile &Obj) {
+ if (Obj.is64Bit())
+ return Obj.getHeader64().filetype != MachO::MH_OBJECT;
+ else
+ return Obj.getHeader().filetype != MachO::MH_OBJECT;
+}
+
+static bool hasLinkEditSegment(const object::MachOObjectFile &Obj) {
+ bool HasLinkEditSegment = false;
+ iterateOnSegments(Obj, [&](const MachO::segment_command_64 &Segment) {
+ if (StringRef("__LINKEDIT") == Segment.segname)
+ HasLinkEditSegment = true;
+ });
+ return HasLinkEditSegment;
+}
+
+static unsigned segmentLoadCommandSize(bool Is64Bit, unsigned NumSections) {
+ if (Is64Bit)
+ return sizeof(MachO::segment_command_64) +
+ NumSections * sizeof(MachO::section_64);
+
+ return sizeof(MachO::segment_command) + NumSections * sizeof(MachO::section);
+}
+
+// Stream a dSYM companion binary file corresponding to the binary referenced
+// by \a DM to \a OutFile. The passed \a MS MCStreamer is setup to write to
+// \a OutFile and it must be using a MachObjectWriter object to do so.
+bool generateDsymCompanion(const DebugMap &DM, MCStreamer &MS,
+ raw_fd_ostream &OutFile) {
+ auto &ObjectStreamer = static_cast<MCObjectStreamer &>(MS);
+ MCAssembler &MCAsm = ObjectStreamer.getAssembler();
+ auto &Writer = static_cast<MachObjectWriter &>(MCAsm.getWriter());
+ MCAsmLayout Layout(MCAsm);
+
+ MCAsm.layout(Layout);
+
+ BinaryHolder InputBinaryHolder(false);
+ auto ErrOrObjs = InputBinaryHolder.GetObjectFiles(DM.getBinaryPath());
+ if (auto Error = ErrOrObjs.getError())
+ return error(Twine("opening ") + DM.getBinaryPath() + ": " +
+ Error.message(),
+ "output file streaming");
+
+ auto ErrOrInputBinary =
+ InputBinaryHolder.GetAs<object::MachOObjectFile>(DM.getTriple());
+ if (auto Error = ErrOrInputBinary.getError())
+ return error(Twine("opening ") + DM.getBinaryPath() + ": " +
+ Error.message(),
+ "output file streaming");
+ auto &InputBinary = *ErrOrInputBinary;
+
+ bool Is64Bit = Writer.is64Bit();
+ MachO::symtab_command SymtabCmd = InputBinary.getSymtabLoadCommand();
+
+ // Get UUID.
+ MachO::uuid_command UUIDCmd;
+ memset(&UUIDCmd, 0, sizeof(UUIDCmd));
+ UUIDCmd.cmd = MachO::LC_UUID;
+ UUIDCmd.cmdsize = sizeof(MachO::uuid_command);
+ for (auto &LCI : InputBinary.load_commands()) {
+ if (LCI.C.cmd == MachO::LC_UUID) {
+ UUIDCmd = InputBinary.getUuidCommand(LCI);
+ break;
+ }
+ }
+
+ // Compute the number of load commands we will need.
+ unsigned LoadCommandSize = 0;
+ unsigned NumLoadCommands = 0;
+ // We will copy the UUID if there is one.
+ if (UUIDCmd.cmd != 0) {
+ ++NumLoadCommands;
+ LoadCommandSize += sizeof(MachO::uuid_command);
+ }
+
+ // If we have a valid symtab to copy, do it.
+ bool ShouldEmitSymtab =
+ isExecutable(InputBinary) && hasLinkEditSegment(InputBinary);
+ if (ShouldEmitSymtab) {
+ LoadCommandSize += sizeof(MachO::symtab_command);
+ ++NumLoadCommands;
+ }
+
+ unsigned HeaderSize =
+ Is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header);
+ // We will copy every segment that isn't __DWARF.
+ iterateOnSegments(InputBinary, [&](const MachO::segment_command_64 &Segment) {
+ if (StringRef("__DWARF") == Segment.segname)
+ return;
+
+ ++NumLoadCommands;
+ LoadCommandSize += segmentLoadCommandSize(Is64Bit, Segment.nsects);
+ });
+
+ // We will add our own brand new __DWARF segment if we have debug
+ // info.
+ unsigned NumDwarfSections = 0;
+ uint64_t DwarfSegmentSize = 0;
+
+ for (unsigned int i = 0, n = Layout.getSectionOrder().size(); i != n; ++i) {
+ MCSection *Sec = Layout.getSectionOrder()[i];
+ if (Sec->begin() == Sec->end())
+ continue;
+
+ if (uint64_t Size = Layout.getSectionFileSize(Sec)) {
+ DwarfSegmentSize =
+ RoundUpToAlignment(DwarfSegmentSize, Sec->getAlignment());
+ DwarfSegmentSize += Size;
+ ++NumDwarfSections;
+ }
+ }
+
+ if (NumDwarfSections) {
+ ++NumLoadCommands;
+ LoadCommandSize += segmentLoadCommandSize(Is64Bit, NumDwarfSections);
+ }
+
+ SmallString<0> NewSymtab;
+ NonRelocatableStringpool NewStrings;
+ unsigned NListSize = Is64Bit ? sizeof(MachO::nlist_64) : sizeof(MachO::nlist);
+ unsigned NumSyms = 0;
+ uint64_t NewStringsSize = 0;
+ if (ShouldEmitSymtab) {
+ NewSymtab.reserve(SymtabCmd.nsyms * NListSize / 2);
+ NumSyms = transferSymbols(InputBinary, NewSymtab, NewStrings);
+ NewStringsSize = NewStrings.getSize() + 1;
+ }
+
+ uint64_t SymtabStart = LoadCommandSize;
+ SymtabStart += HeaderSize;
+ SymtabStart = RoundUpToAlignment(SymtabStart, 0x1000);
+
+ // We gathered all the information we need, start emitting the output file.
+ Writer.writeHeader(MachO::MH_DSYM, NumLoadCommands, LoadCommandSize, false);
+
+ // Write the load commands.
+ assert(OutFile.tell() == HeaderSize);
+ if (UUIDCmd.cmd != 0) {
+ Writer.write32(UUIDCmd.cmd);
+ Writer.write32(UUIDCmd.cmdsize);
+ Writer.writeBytes(
+ StringRef(reinterpret_cast<const char *>(UUIDCmd.uuid), 16));
+ assert(OutFile.tell() == HeaderSize + sizeof(UUIDCmd));
+ }
+
+ assert(SymtabCmd.cmd && "No symbol table.");
+ uint64_t StringStart = SymtabStart + NumSyms * NListSize;
+ if (ShouldEmitSymtab)
+ Writer.writeSymtabLoadCommand(SymtabStart, NumSyms, StringStart,
+ NewStringsSize);
+
+ uint64_t DwarfSegmentStart = StringStart + NewStringsSize;
+ DwarfSegmentStart = RoundUpToAlignment(DwarfSegmentStart, 0x1000);
+
+ // Write the load commands for the segments and sections we 'import' from
+ // the original binary.
+ uint64_t EndAddress = 0;
+ uint64_t GapForDwarf = UINT64_MAX;
+ for (auto &LCI : InputBinary.load_commands()) {
+ if (LCI.C.cmd == MachO::LC_SEGMENT)
+ transferSegmentAndSections(LCI, InputBinary.getSegmentLoadCommand(LCI),
+ InputBinary, Writer, SymtabStart,
+ StringStart + NewStringsSize - SymtabStart,
+ DwarfSegmentSize, GapForDwarf, EndAddress);
+ else if (LCI.C.cmd == MachO::LC_SEGMENT_64)
+ transferSegmentAndSections(LCI, InputBinary.getSegment64LoadCommand(LCI),
+ InputBinary, Writer, SymtabStart,
+ StringStart + NewStringsSize - SymtabStart,
+ DwarfSegmentSize, GapForDwarf, EndAddress);
+ }
+
+ uint64_t DwarfVMAddr = RoundUpToAlignment(EndAddress, 0x1000);
+ uint64_t DwarfVMMax = Is64Bit ? UINT64_MAX : UINT32_MAX;
+ if (DwarfVMAddr + DwarfSegmentSize > DwarfVMMax ||
+ DwarfVMAddr + DwarfSegmentSize < DwarfVMAddr /* Overflow */) {
+ // There is no room for the __DWARF segment at the end of the
+ // address space. Look trhough segments to find a gap.
+ DwarfVMAddr = GapForDwarf;
+ if (DwarfVMAddr == UINT64_MAX)
+ warn("not enough VM space for the __DWARF segment.",
+ "output file streaming");
+ }
+
+ // Write the load command for the __DWARF segment.
+ createDwarfSegment(DwarfVMAddr, DwarfSegmentStart, DwarfSegmentSize,
+ NumDwarfSections, Layout, Writer);
+
+ assert(OutFile.tell() == LoadCommandSize + HeaderSize);
+ Writer.WriteZeros(SymtabStart - (LoadCommandSize + HeaderSize));
+ assert(OutFile.tell() == SymtabStart);
+
+ // Transfer symbols.
+ if (ShouldEmitSymtab) {
+ Writer.writeBytes(NewSymtab.str());
+ assert(OutFile.tell() == StringStart);
+
+ // Transfer string table.
+ // FIXME: The NonRelocatableStringpool starts with an empty string, but
+ // dsymutil-classic starts the reconstructed string table with 2 of these.
+ // Reproduce that behavior for now (there is corresponding code in
+ // transferSymbol).
+ Writer.WriteZeros(1);
+ typedef NonRelocatableStringpool::MapTy MapTy;
+ for (auto *Entry = NewStrings.getFirstEntry(); Entry;
+ Entry = static_cast<MapTy::MapEntryTy *>(Entry->getValue().second))
+ Writer.writeBytes(
+ StringRef(Entry->getKey().data(), Entry->getKey().size() + 1));
+ }
+
+ assert(OutFile.tell() == StringStart + NewStringsSize);
+
+ // Pad till the Dwarf segment start.
+ Writer.WriteZeros(DwarfSegmentStart - (StringStart + NewStringsSize));
+ assert(OutFile.tell() == DwarfSegmentStart);
+
+ // Emit the Dwarf sections contents.
+ for (const MCSection &Sec : MCAsm) {
+ if (Sec.begin() == Sec.end())
+ continue;
+
+ uint64_t Pos = OutFile.tell();
+ Writer.WriteZeros(RoundUpToAlignment(Pos, Sec.getAlignment()) - Pos);
+ MCAsm.writeSectionData(&Sec, Layout);
+ }
+
+ return true;
+}
+}
+}
+}
diff --git a/tools/dsymutil/MachOUtils.h b/tools/dsymutil/MachOUtils.h
new file mode 100644
index 000000000000..61dfadc70270
--- /dev/null
+++ b/tools/dsymutil/MachOUtils.h
@@ -0,0 +1,39 @@
+//===-- MachOUtils.h - Mach-o specific helpers for dsymutil --------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_TOOLS_DSYMUTIL_MACHOUTILS_H
+#define LLVM_TOOLS_DSYMUTIL_MACHOUTILS_H
+
+#include <string>
+#include "llvm/ADT/StringRef.h"
+
+namespace llvm {
+class MCStreamer;
+class raw_fd_ostream;
+namespace dsymutil {
+class DebugMap;
+struct LinkOptions;
+namespace MachOUtils {
+
+struct ArchAndFilename {
+ std::string Arch, Path;
+ ArchAndFilename(StringRef Arch, StringRef Path) : Arch(Arch), Path(Path) {}
+};
+
+bool generateUniversalBinary(SmallVectorImpl<ArchAndFilename> &ArchFiles,
+ StringRef OutputFileName, const LinkOptions &,
+ StringRef SDKPath);
+
+bool generateDsymCompanion(const DebugMap &DM, MCStreamer &MS,
+ raw_fd_ostream &OutFile);
+
+std::string getArchName(StringRef Arch);
+}
+}
+}
+#endif // LLVM_TOOLS_DSYMUTIL_MACHOUTILS_H
diff --git a/tools/dsymutil/NonRelocatableStringpool.h b/tools/dsymutil/NonRelocatableStringpool.h
new file mode 100644
index 000000000000..9db1f3342bd3
--- /dev/null
+++ b/tools/dsymutil/NonRelocatableStringpool.h
@@ -0,0 +1,68 @@
+//===-- NonRelocatableStringpool.h - A simple stringpool -----------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_TOOLS_DSYMUTIL_NONRELOCATABLESTRINGPOOL_H
+#define LLVM_TOOLS_DSYMUTIL_NONRELOCATABLESTRINGPOOL_H
+
+namespace llvm {
+namespace dsymutil {
+
+/// \brief A string table that doesn't need relocations.
+///
+/// We are doing a final link, no need for a string table that
+/// has relocation entries for every reference to it. This class
+/// provides this ablitity by just associating offsets with
+/// strings.
+class NonRelocatableStringpool {
+public:
+ /// \brief Entries are stored into the StringMap and simply linked
+ /// together through the second element of this pair in order to
+ /// keep track of insertion order.
+ typedef StringMap<std::pair<uint32_t, StringMapEntryBase *>, BumpPtrAllocator>
+ MapTy;
+
+ NonRelocatableStringpool()
+ : CurrentEndOffset(0), Sentinel(0), Last(&Sentinel) {
+ // Legacy dsymutil puts an empty string at the start of the line
+ // table.
+ getStringOffset("");
+ }
+
+ /// \brief Get the offset of string \p S in the string table. This
+ /// can insert a new element or return the offset of a preexisitng
+ /// one.
+ uint32_t getStringOffset(StringRef S);
+
+ /// \brief Get permanent storage for \p S (but do not necessarily
+ /// emit \p S in the output section).
+ /// \returns The StringRef that points to permanent storage to use
+ /// in place of \p S.
+ StringRef internString(StringRef S);
+
+ // \brief Return the first entry of the string table.
+ const MapTy::MapEntryTy *getFirstEntry() const {
+ return getNextEntry(&Sentinel);
+ }
+
+ // \brief Get the entry following \p E in the string table or null
+ // if \p E was the last entry.
+ const MapTy::MapEntryTy *getNextEntry(const MapTy::MapEntryTy *E) const {
+ return static_cast<const MapTy::MapEntryTy *>(E->getValue().second);
+ }
+
+ uint64_t getSize() { return CurrentEndOffset; }
+
+private:
+ MapTy Strings;
+ uint32_t CurrentEndOffset;
+ MapTy::MapEntryTy Sentinel, *Last;
+};
+}
+}
+
+#endif
diff --git a/tools/dsymutil/dsymutil.cpp b/tools/dsymutil/dsymutil.cpp
index 50091935a44e..e9ee57f3dee6 100644
--- a/tools/dsymutil/dsymutil.cpp
+++ b/tools/dsymutil/dsymutil.cpp
@@ -13,13 +13,18 @@
//===----------------------------------------------------------------------===//
#include "DebugMap.h"
+#include "MachOUtils.h"
#include "dsymutil.h"
+#include "llvm/Object/MachO.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/FileUtilities.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/Options.h"
#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/TargetSelect.h"
+#include <cstdint>
#include <string>
using namespace llvm::dsymutil;
@@ -27,35 +32,205 @@ using namespace llvm::dsymutil;
namespace {
using namespace llvm::cl;
-static opt<std::string> InputFile(Positional, desc("<input file>"),
- init("a.out"));
+OptionCategory DsymCategory("Specific Options");
+static opt<bool> Help("h", desc("Alias for -help"), Hidden);
+static opt<bool> Version("v", desc("Alias for -version"), Hidden);
+
+static list<std::string> InputFiles(Positional, OneOrMore,
+ desc("<input files>"), cat(DsymCategory));
static opt<std::string>
OutputFileOpt("o",
desc("Specify the output file. default: <input file>.dwarf"),
- value_desc("filename"));
+ value_desc("filename"), cat(DsymCategory));
static opt<std::string> OsoPrependPath(
"oso-prepend-path",
desc("Specify a directory to prepend to the paths of object files."),
- value_desc("path"));
+ value_desc("path"), cat(DsymCategory));
+
+static opt<bool> DumpStab(
+ "symtab",
+ desc("Dumps the symbol table found in executable or object file(s) and\n"
+ "exits."),
+ init(false), cat(DsymCategory));
+static alias DumpStabA("s", desc("Alias for --symtab"), aliasopt(DumpStab));
+
+static opt<bool> FlatOut("flat",
+ desc("Produce a flat dSYM file (not a bundle)."),
+ init(false), cat(DsymCategory));
+static alias FlatOutA("f", desc("Alias for --flat"), aliasopt(FlatOut));
-static opt<bool> Verbose("v", desc("Verbosity level"), init(false));
+static opt<bool> Verbose("verbose", desc("Verbosity level"), init(false),
+ cat(DsymCategory));
static opt<bool>
NoOutput("no-output",
desc("Do the link in memory, but do not emit the result file."),
- init(false));
+ init(false), cat(DsymCategory));
+
+static list<std::string> ArchFlags(
+ "arch",
+ desc("Link DWARF debug information only for specified CPU architecture\n"
+ "types. This option can be specified multiple times, once for each\n"
+ "desired architecture. All cpu architectures will be linked by\n"
+ "default."),
+ ZeroOrMore, cat(DsymCategory));
+
+static opt<bool>
+ NoODR("no-odr",
+ desc("Do not use ODR (One Definition Rule) for type uniquing."),
+ init(false), cat(DsymCategory));
static opt<bool> DumpDebugMap(
"dump-debug-map",
desc("Parse and dump the debug map to standard output. Not DWARF link "
"will take place."),
- init(false));
+ init(false), cat(DsymCategory));
static opt<bool> InputIsYAMLDebugMap(
"y", desc("Treat the input file is a YAML debug map rather than a binary."),
- init(false));
+ init(false), cat(DsymCategory));
+}
+
+static bool createPlistFile(llvm::StringRef BundleRoot) {
+ if (NoOutput)
+ return true;
+
+ // Create plist file to write to.
+ llvm::SmallString<128> InfoPlist(BundleRoot);
+ llvm::sys::path::append(InfoPlist, "Contents/Info.plist");
+ std::error_code EC;
+ llvm::raw_fd_ostream PL(InfoPlist, EC, llvm::sys::fs::F_Text);
+ if (EC) {
+ llvm::errs() << "error: cannot create plist file " << InfoPlist << ": "
+ << EC.message() << '\n';
+ return false;
+ }
+
+ // FIXME: Use CoreFoundation to get executable bundle info. Use
+ // dummy values for now.
+ std::string bundleVersionStr = "1", bundleShortVersionStr = "1.0",
+ bundleIDStr;
+
+ llvm::StringRef BundleID = *llvm::sys::path::rbegin(BundleRoot);
+ if (llvm::sys::path::extension(BundleRoot) == ".dSYM")
+ bundleIDStr = llvm::sys::path::stem(BundleID);
+ else
+ bundleIDStr = BundleID;
+
+ // Print out information to the plist file.
+ PL << "<?xml version=\"1.0\" encoding=\"UTF-8\"\?>\n"
+ << "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
+ << "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
+ << "<plist version=\"1.0\">\n"
+ << "\t<dict>\n"
+ << "\t\t<key>CFBundleDevelopmentRegion</key>\n"
+ << "\t\t<string>English</string>\n"
+ << "\t\t<key>CFBundleIdentifier</key>\n"
+ << "\t\t<string>com.apple.xcode.dsym." << bundleIDStr << "</string>\n"
+ << "\t\t<key>CFBundleInfoDictionaryVersion</key>\n"
+ << "\t\t<string>6.0</string>\n"
+ << "\t\t<key>CFBundlePackageType</key>\n"
+ << "\t\t<string>dSYM</string>\n"
+ << "\t\t<key>CFBundleSignature</key>\n"
+ << "\t\t<string>\?\?\?\?</string>\n"
+ << "\t\t<key>CFBundleShortVersionString</key>\n"
+ << "\t\t<string>" << bundleShortVersionStr << "</string>\n"
+ << "\t\t<key>CFBundleVersion</key>\n"
+ << "\t\t<string>" << bundleVersionStr << "</string>\n"
+ << "\t</dict>\n"
+ << "</plist>\n";
+
+ PL.close();
+ return true;
+}
+
+static bool createBundleDir(llvm::StringRef BundleBase) {
+ if (NoOutput)
+ return true;
+
+ llvm::SmallString<128> Bundle(BundleBase);
+ llvm::sys::path::append(Bundle, "Contents", "Resources", "DWARF");
+ if (std::error_code EC = create_directories(Bundle.str(), true,
+ llvm::sys::fs::perms::all_all)) {
+ llvm::errs() << "error: cannot create directory " << Bundle << ": "
+ << EC.message() << "\n";
+ return false;
+ }
+ return true;
+}
+
+static std::error_code getUniqueFile(const llvm::Twine &Model, int &ResultFD,
+ llvm::SmallVectorImpl<char> &ResultPath) {
+ // If in NoOutput mode, use the createUniqueFile variant that
+ // doesn't open the file but still generates a somewhat unique
+ // name. In the real usage scenario, we'll want to ensure that the
+ // file is trully unique, and creating it is the only way to achieve
+ // that.
+ if (NoOutput)
+ return llvm::sys::fs::createUniqueFile(Model, ResultPath);
+ return llvm::sys::fs::createUniqueFile(Model, ResultFD, ResultPath);
+}
+
+static std::string getOutputFileName(llvm::StringRef InputFile,
+ bool TempFile = false) {
+ if (TempFile) {
+ llvm::StringRef Basename =
+ OutputFileOpt.empty() ? InputFile : llvm::StringRef(OutputFileOpt);
+ llvm::Twine OutputFile = Basename + ".tmp%%%%%%.dwarf";
+ int FD;
+ llvm::SmallString<128> UniqueFile;
+ if (auto EC = getUniqueFile(OutputFile, FD, UniqueFile)) {
+ llvm::errs() << "error: failed to create temporary outfile '"
+ << OutputFile << "': " << EC.message() << '\n';
+ return "";
+ }
+ llvm::sys::RemoveFileOnSignal(UniqueFile);
+ if (!NoOutput) {
+ // Close the file immediately. We know it is unique. It will be
+ // reopened and written to later.
+ llvm::raw_fd_ostream CloseImmediately(FD, true /* shouldClose */, true);
+ }
+ return UniqueFile.str();
+ }
+
+ if (FlatOut) {
+ // If a flat dSYM has been requested, things are pretty simple.
+ if (OutputFileOpt.empty()) {
+ if (InputFile == "-")
+ return "a.out.dwarf";
+ return (InputFile + ".dwarf").str();
+ }
+
+ return OutputFileOpt;
+ }
+
+ // We need to create/update a dSYM bundle.
+ // A bundle hierarchy looks like this:
+ // <bundle name>.dSYM/
+ // Contents/
+ // Info.plist
+ // Resources/
+ // DWARF/
+ // <DWARF file(s)>
+ std::string DwarfFile =
+ InputFile == "-" ? llvm::StringRef("a.out") : InputFile;
+ llvm::SmallString<128> BundleDir(OutputFileOpt);
+ if (BundleDir.empty())
+ BundleDir = DwarfFile + ".dSYM";
+ if (!createBundleDir(BundleDir) || !createPlistFile(BundleDir))
+ return "";
+
+ llvm::sys::path::append(BundleDir, "Contents", "Resources", "DWARF",
+ llvm::sys::path::filename(DwarfFile));
+ return BundleDir.str();
+}
+
+void llvm::dsymutil::exitDsymutil(int ExitStatus) {
+ // Cleanup temporary files.
+ llvm::sys::RunInterruptHandlers();
+ exit(ExitStatus);
}
int main(int argc, char **argv) {
@@ -63,41 +238,105 @@ int main(int argc, char **argv) {
llvm::PrettyStackTraceProgram StackPrinter(argc, argv);
llvm::llvm_shutdown_obj Shutdown;
LinkOptions Options;
+ void *MainAddr = (void *)(intptr_t)&exitDsymutil;
+ std::string SDKPath = llvm::sys::fs::getMainExecutable(argv[0], MainAddr);
+ SDKPath = llvm::sys::path::parent_path(SDKPath);
- llvm::cl::ParseCommandLineOptions(argc, argv, "llvm dsymutil\n");
+ HideUnrelatedOptions(DsymCategory);
+ llvm::cl::ParseCommandLineOptions(
+ argc, argv,
+ "manipulate archived DWARF debug symbol files.\n\n"
+ "dsymutil links the DWARF debug information found in the object files\n"
+ "for the executable <input file> by using debug symbols information\n"
+ "contained in its symbol table.\n");
- auto DebugMapPtrOrErr =
- parseDebugMap(InputFile, OsoPrependPath, Verbose, InputIsYAMLDebugMap);
+ if (Help)
+ PrintHelpMessage();
+
+ if (Version) {
+ llvm::cl::PrintVersionMessage();
+ return 0;
+ }
Options.Verbose = Verbose;
Options.NoOutput = NoOutput;
+ Options.NoODR = NoODR;
+ Options.PrependPath = OsoPrependPath;
llvm::InitializeAllTargetInfos();
llvm::InitializeAllTargetMCs();
llvm::InitializeAllTargets();
llvm::InitializeAllAsmPrinters();
- if (auto EC = DebugMapPtrOrErr.getError()) {
- llvm::errs() << "error: cannot parse the debug map for \"" << InputFile
- << "\": " << EC.message() << '\n';
+ if (!FlatOut && OutputFileOpt == "-") {
+ llvm::errs() << "error: cannot emit to standard output without --flat\n";
+ return 1;
+ }
+
+ if (InputFiles.size() > 1 && FlatOut && !OutputFileOpt.empty()) {
+ llvm::errs() << "error: cannot use -o with multiple inputs in flat mode\n";
return 1;
}
- if (Verbose || DumpDebugMap)
- (*DebugMapPtrOrErr)->print(llvm::outs());
+ for (const auto &Arch : ArchFlags)
+ if (Arch != "*" && Arch != "all" &&
+ !llvm::object::MachOObjectFile::isValidArch(Arch)) {
+ llvm::errs() << "error: Unsupported cpu architecture: '" << Arch << "'\n";
+ exitDsymutil(1);
+ }
- if (DumpDebugMap)
- return 0;
+ for (auto &InputFile : InputFiles) {
+ // Dump the symbol table for each input file and requested arch
+ if (DumpStab) {
+ if (!dumpStab(InputFile, ArchFlags, OsoPrependPath))
+ exitDsymutil(1);
+ continue;
+ }
+
+ auto DebugMapPtrsOrErr = parseDebugMap(InputFile, ArchFlags, OsoPrependPath,
+ Verbose, InputIsYAMLDebugMap);
+
+ if (auto EC = DebugMapPtrsOrErr.getError()) {
+ llvm::errs() << "error: cannot parse the debug map for \"" << InputFile
+ << "\": " << EC.message() << '\n';
+ exitDsymutil(1);
+ }
+
+ if (DebugMapPtrsOrErr->empty()) {
+ llvm::errs() << "error: no architecture to link\n";
+ exitDsymutil(1);
+ }
+
+ // If there is more than one link to execute, we need to generate
+ // temporary files.
+ bool NeedsTempFiles = !DumpDebugMap && (*DebugMapPtrsOrErr).size() != 1;
+ llvm::SmallVector<MachOUtils::ArchAndFilename, 4> TempFiles;
+ for (auto &Map : *DebugMapPtrsOrErr) {
+ if (Verbose || DumpDebugMap)
+ Map->print(llvm::outs());
+
+ if (DumpDebugMap)
+ continue;
+
+ if (Map->begin() == Map->end())
+ llvm::errs() << "warning: no debug symbols in executable (-arch "
+ << MachOUtils::getArchName(Map->getTriple().getArchName())
+ << ")\n";
+
+ std::string OutputFile = getOutputFileName(InputFile, NeedsTempFiles);
+ if (OutputFile.empty() || !linkDwarf(OutputFile, *Map, Options))
+ exitDsymutil(1);
+
+ if (NeedsTempFiles)
+ TempFiles.emplace_back(Map->getTriple().getArchName().str(),
+ OutputFile);
+ }
- std::string OutputFile;
- if (OutputFileOpt.empty()) {
- if (InputFile == "-")
- OutputFile = "a.out.dwarf";
- else
- OutputFile = InputFile + ".dwarf";
- } else {
- OutputFile = OutputFileOpt;
+ if (NeedsTempFiles &&
+ !MachOUtils::generateUniversalBinary(
+ TempFiles, getOutputFileName(InputFile), Options, SDKPath))
+ exitDsymutil(1);
}
- return !linkDwarf(OutputFile, **DebugMapPtrOrErr, Options);
+ exitDsymutil(0);
}
diff --git a/tools/dsymutil/dsymutil.h b/tools/dsymutil/dsymutil.h
index 408918779833..91cb32766129 100644
--- a/tools/dsymutil/dsymutil.h
+++ b/tools/dsymutil/dsymutil.h
@@ -27,22 +27,35 @@ namespace dsymutil {
struct LinkOptions {
bool Verbose; ///< Verbosity
bool NoOutput; ///< Skip emitting output
+ bool NoODR; ///< Do not unique types according to ODR
+ std::string PrependPath; ///< -oso-prepend-path
LinkOptions() : Verbose(false), NoOutput(false) {}
};
-/// \brief Extract the DebugMap from the given file.
-/// The file has to be a MachO object file.
-llvm::ErrorOr<std::unique_ptr<DebugMap>> parseDebugMap(StringRef InputFile,
- StringRef PrependPath,
- bool Verbose,
- bool InputIsYAML);
+/// \brief Extract the DebugMaps from the given file.
+/// The file has to be a MachO object file. Multiple debug maps can be
+/// returned when the file is universal (aka fat) binary.
+llvm::ErrorOr<std::vector<std::unique_ptr<DebugMap>>>
+parseDebugMap(StringRef InputFile, ArrayRef<std::string> Archs,
+ StringRef PrependPath, bool Verbose, bool InputIsYAML);
+
+/// \brief Dump the symbol table
+bool dumpStab(StringRef InputFile, ArrayRef<std::string> Archs,
+ StringRef PrependPath = "");
/// \brief Link the Dwarf debuginfo as directed by the passed DebugMap
/// \p DM into a DwarfFile named \p OutputFilename.
/// \returns false if the link failed.
bool linkDwarf(StringRef OutputFilename, const DebugMap &DM,
const LinkOptions &Options);
+
+/// \brief Exit the dsymutil process, cleaning up every temporary
+/// files that we created.
+LLVM_ATTRIBUTE_NORETURN void exitDsymutil(int ExitStatus);
+
+void warn(const Twine &Warning, const Twine &Context);
+bool error(const Twine &Error, const Twine &Context);
}
}
#endif // LLVM_TOOLS_DSYMUTIL_DSYMUTIL_H
diff --git a/tools/gold/gold-plugin.cpp b/tools/gold/gold-plugin.cpp
index 68c9d1a6f6e4..31d7f156a700 100644
--- a/tools/gold/gold-plugin.cpp
+++ b/tools/gold/gold-plugin.cpp
@@ -20,6 +20,7 @@
#include "llvm/Bitcode/ReaderWriter.h"
#include "llvm/CodeGen/Analysis.h"
#include "llvm/CodeGen/CommandFlags.h"
+#include "llvm/CodeGen/ParallelCG.h"
#include "llvm/IR/AutoUpgrade.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DiagnosticInfo.h"
@@ -28,15 +29,16 @@
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Verifier.h"
-#include "llvm/Linker/Linker.h"
+#include "llvm/Linker/IRMover.h"
#include "llvm/MC/SubtargetFeature.h"
+#include "llvm/Object/FunctionIndexObjectFile.h"
#include "llvm/Object/IRObjectFile.h"
-#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/raw_ostream.h"
#include "llvm/Transforms/IPO.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
#include "llvm/Transforms/Utils/GlobalStatus.h"
@@ -55,30 +57,59 @@
using namespace llvm;
+static ld_plugin_status discard_message(int level, const char *format, ...) {
+ // Die loudly. Recent versions of Gold pass ld_plugin_message as the first
+ // callback in the transfer vector. This should never be called.
+ abort();
+}
+
+static ld_plugin_release_input_file release_input_file = nullptr;
+static ld_plugin_get_input_file get_input_file = nullptr;
+static ld_plugin_message message = discard_message;
+
namespace {
struct claimed_file {
void *handle;
std::vector<ld_plugin_symbol> syms;
};
-}
-static ld_plugin_status discard_message(int level, const char *format, ...) {
- // Die loudly. Recent versions of Gold pass ld_plugin_message as the first
- // callback in the transfer vector. This should never be called.
- abort();
+/// RAII wrapper to manage opening and releasing of a ld_plugin_input_file.
+struct PluginInputFile {
+ void *Handle;
+ ld_plugin_input_file File;
+
+ PluginInputFile(void *Handle) : Handle(Handle) {
+ if (get_input_file(Handle, &File) != LDPS_OK)
+ message(LDPL_FATAL, "Failed to get file information");
+ }
+ ~PluginInputFile() {
+ if (release_input_file(Handle) != LDPS_OK)
+ message(LDPL_FATAL, "Failed to release file information");
+ }
+ ld_plugin_input_file &file() { return File; }
+};
+
+struct ResolutionInfo {
+ bool IsLinkonceOdr = true;
+ bool UnnamedAddr = true;
+ GlobalValue::VisibilityTypes Visibility = GlobalValue::DefaultVisibility;
+ bool CommonInternal = false;
+ bool UseCommon = false;
+ unsigned CommonSize = 0;
+ unsigned CommonAlign = 0;
+ claimed_file *CommonFile = nullptr;
+};
}
-static ld_plugin_get_input_file get_input_file = nullptr;
-static ld_plugin_release_input_file release_input_file = nullptr;
static ld_plugin_add_symbols add_symbols = nullptr;
static ld_plugin_get_symbols get_symbols = nullptr;
static ld_plugin_add_input_file add_input_file = nullptr;
static ld_plugin_set_extra_library_path set_extra_library_path = nullptr;
static ld_plugin_get_view get_view = nullptr;
-static ld_plugin_message message = discard_message;
static Reloc::Model RelocationModel = Reloc::Default;
static std::string output_name = "";
static std::list<claimed_file> Modules;
+static StringMap<ResolutionInfo> ResInfo;
static std::vector<std::string> Cleanup;
static llvm::TargetOptions TargetOpts;
@@ -92,10 +123,20 @@ namespace options {
static bool generate_api_file = false;
static OutputType TheOutputType = OT_NORMAL;
static unsigned OptLevel = 2;
+ static unsigned Parallelism = 1;
+#ifdef NDEBUG
+ static bool DisableVerify = true;
+#else
+ static bool DisableVerify = false;
+#endif
static std::string obj_path;
static std::string extra_library_path;
static std::string triple;
static std::string mcpu;
+ // When the thinlto plugin option is specified, only read the function
+ // the information from intermediate files and write a combined
+ // global index for the ThinLTO backends.
+ static bool thinlto = false;
// Additional options to pass into the code generator.
// Note: This array will contain all plugin options which are not claimed
// as plugin exclusive to pass to the code generator.
@@ -103,7 +144,7 @@ namespace options {
// use only and will not be passed.
static std::vector<const char *> extra;
- static void process_plugin_option(const char* opt_)
+ static void process_plugin_option(const char *opt_)
{
if (opt_ == nullptr)
return;
@@ -125,10 +166,17 @@ namespace options {
TheOutputType = OT_SAVE_TEMPS;
} else if (opt == "disable-output") {
TheOutputType = OT_DISABLE;
+ } else if (opt == "thinlto") {
+ thinlto = true;
} else if (opt.size() == 2 && opt[0] == 'O') {
if (opt[1] < '0' || opt[1] > '3')
- report_fatal_error("Optimization level must be between 0 and 3");
+ message(LDPL_FATAL, "Optimization level must be between 0 and 3");
OptLevel = opt[1] - '0';
+ } else if (opt.startswith("jobs=")) {
+ if (StringRef(opt_ + 5).getAsInteger(10, Parallelism))
+ message(LDPL_FATAL, "Invalid parallelism level: %s", opt_ + 5);
+ } else if (opt == "disable-verify") {
+ DisableVerify = true;
} else {
// Save this option to pass to the code generator.
// ParseCommandLineOptions() expects argv[0] to be program name. Lazily
@@ -278,7 +326,7 @@ static bool shouldSkip(uint32_t Symflags) {
return false;
}
-static void diagnosticHandler(const DiagnosticInfo &DI, void *Context) {
+static void diagnosticHandler(const DiagnosticInfo &DI) {
if (const auto *BDI = dyn_cast<BitcodeDiagnosticInfo>(&DI)) {
std::error_code EC = BDI->getError();
if (EC == BitcodeError::InvalidBitcodeSignature)
@@ -308,6 +356,23 @@ static void diagnosticHandler(const DiagnosticInfo &DI, void *Context) {
message(Level, "LLVM gold plugin: %s", ErrStorage.c_str());
}
+static void diagnosticHandlerForContext(const DiagnosticInfo &DI,
+ void *Context) {
+ diagnosticHandler(DI);
+}
+
+static GlobalValue::VisibilityTypes
+getMinVisibility(GlobalValue::VisibilityTypes A,
+ GlobalValue::VisibilityTypes B) {
+ if (A == GlobalValue::HiddenVisibility)
+ return A;
+ if (B == GlobalValue::HiddenVisibility)
+ return B;
+ if (A == GlobalValue::ProtectedVisibility)
+ return A;
+ return B;
+}
+
/// Called by gold to see whether this file is one that our plugin can handle.
/// We'll try to open it and register all the symbols with add_symbol if
/// possible.
@@ -322,7 +387,8 @@ static ld_plugin_status claim_file_hook(const ld_plugin_input_file *file,
message(LDPL_ERROR, "Failed to get a view of %s", file->name);
return LDPS_ERR;
}
- BufferRef = MemoryBufferRef(StringRef((const char *)view, file->filesize), "");
+ BufferRef =
+ MemoryBufferRef(StringRef((const char *)view, file->filesize), "");
} else {
int64_t offset = 0;
// Gold has found what might be IR part-way inside of a file, such as
@@ -341,7 +407,7 @@ static ld_plugin_status claim_file_hook(const ld_plugin_input_file *file,
BufferRef = Buffer->getMemBufferRef();
}
- Context.setDiagnosticHandler(diagnosticHandler);
+ Context.setDiagnosticHandler(diagnosticHandlerForContext);
ErrorOr<std::unique_ptr<object::IRObjectFile>> ObjOrErr =
object::IRObjectFile::create(BufferRef, Context);
std::error_code EC = ObjOrErr.getError();
@@ -363,6 +429,11 @@ static ld_plugin_status claim_file_hook(const ld_plugin_input_file *file,
cf.handle = file->handle;
+ // If we are doing ThinLTO compilation, don't need to process the symbols.
+ // Later we simply build a combined index file after all files are claimed.
+ if (options::thinlto)
+ return LDPS_OK;
+
for (auto &Sym : Obj->symbols()) {
uint32_t Symflags = Sym.getFlags();
if (shouldSkip(Symflags))
@@ -381,8 +452,22 @@ static ld_plugin_status claim_file_hook(const ld_plugin_input_file *file,
const GlobalValue *GV = Obj->getSymbolGV(Sym.getRawDataRefImpl());
+ ResolutionInfo &Res = ResInfo[sym.name];
+
sym.visibility = LDPV_DEFAULT;
if (GV) {
+ Res.UnnamedAddr &= GV->hasUnnamedAddr();
+ Res.IsLinkonceOdr &= GV->hasLinkOnceLinkage();
+ if (GV->hasCommonLinkage()) {
+ Res.CommonAlign = std::max(Res.CommonAlign, GV->getAlignment());
+ const DataLayout &DL = GV->getParent()->getDataLayout();
+ uint64_t Size = DL.getTypeAllocSize(GV->getType()->getElementType());
+ if (Size >= Res.CommonSize) {
+ Res.CommonSize = Size;
+ Res.CommonFile = &cf;
+ }
+ }
+ Res.Visibility = getMinVisibility(Res.Visibility, GV->getVisibility());
switch (GV->getVisibility()) {
case GlobalValue::DefaultVisibility:
sym.visibility = LDPV_DEFAULT;
@@ -421,15 +506,13 @@ static ld_plugin_status claim_file_hook(const ld_plugin_input_file *file,
const Comdat *C = Base->getComdat();
if (C)
sym.comdat_key = strdup(C->getName().str().c_str());
- else if (Base->hasWeakLinkage() || Base->hasLinkOnceLinkage())
- sym.comdat_key = strdup(sym.name);
}
sym.resolution = LDPR_UNKNOWN;
}
if (!cf.syms.empty()) {
- if (add_symbols(cf.handle, cf.syms.size(), &cf.syms[0]) != LDPS_OK) {
+ if (add_symbols(cf.handle, cf.syms.size(), cf.syms.data()) != LDPS_OK) {
message(LDPL_ERROR, "Unable to add symbols!");
return LDPS_ERR;
}
@@ -438,27 +521,6 @@ static ld_plugin_status claim_file_hook(const ld_plugin_input_file *file,
return LDPS_OK;
}
-static void keepGlobalValue(GlobalValue &GV,
- std::vector<GlobalAlias *> &KeptAliases) {
- assert(!GV.hasLocalLinkage());
-
- if (auto *GA = dyn_cast<GlobalAlias>(&GV))
- KeptAliases.push_back(GA);
-
- switch (GV.getLinkage()) {
- default:
- break;
- case GlobalValue::LinkOnceAnyLinkage:
- GV.setLinkage(GlobalValue::WeakAnyLinkage);
- break;
- case GlobalValue::LinkOnceODRLinkage:
- GV.setLinkage(GlobalValue::WeakODRLinkage);
- break;
- }
-
- assert(!GV.isDiscardableIfUnused());
-}
-
static void internalize(GlobalValue &GV) {
if (GV.isDeclarationForLinker())
return; // We get here if there is a matching asm definition.
@@ -466,33 +528,6 @@ static void internalize(GlobalValue &GV) {
GV.setLinkage(GlobalValue::InternalLinkage);
}
-static void drop(GlobalValue &GV) {
- if (auto *F = dyn_cast<Function>(&GV)) {
- F->deleteBody();
- F->setComdat(nullptr); // Should deleteBody do this?
- return;
- }
-
- if (auto *Var = dyn_cast<GlobalVariable>(&GV)) {
- Var->setInitializer(nullptr);
- Var->setLinkage(
- GlobalValue::ExternalLinkage); // Should setInitializer do this?
- Var->setComdat(nullptr); // and this?
- return;
- }
-
- auto &Alias = cast<GlobalAlias>(GV);
- Module &M = *Alias.getParent();
- PointerType &Ty = *cast<PointerType>(Alias.getType());
- GlobalValue::LinkageTypes L = Alias.getLinkage();
- auto *Var =
- new GlobalVariable(M, Ty.getElementType(), /*isConstant*/ false, L,
- /*Initializer*/ nullptr);
- Var->takeName(&Alias);
- Alias.replaceAllUsesWith(Var);
- Alias.eraseFromParent();
-}
-
static const char *getResolutionName(ld_plugin_symbol_resolution R) {
switch (R) {
case LDPR_UNKNOWN:
@@ -519,71 +554,51 @@ static const char *getResolutionName(ld_plugin_symbol_resolution R) {
llvm_unreachable("Unknown resolution");
}
-namespace {
-class LocalValueMaterializer : public ValueMaterializer {
- DenseSet<GlobalValue *> &Dropped;
- DenseMap<GlobalObject *, GlobalObject *> LocalVersions;
-
-public:
- LocalValueMaterializer(DenseSet<GlobalValue *> &Dropped) : Dropped(Dropped) {}
- Value *materializeValueFor(Value *V) override;
-};
+static void freeSymName(ld_plugin_symbol &Sym) {
+ free(Sym.name);
+ free(Sym.comdat_key);
+ Sym.name = nullptr;
+ Sym.comdat_key = nullptr;
}
-Value *LocalValueMaterializer::materializeValueFor(Value *V) {
- auto *GO = dyn_cast<GlobalObject>(V);
- if (!GO)
- return nullptr;
+static std::unique_ptr<FunctionInfoIndex>
+getFunctionIndexForFile(claimed_file &F, ld_plugin_input_file &Info) {
- auto I = LocalVersions.find(GO);
- if (I != LocalVersions.end())
- return I->second;
+ if (get_symbols(F.handle, F.syms.size(), &F.syms[0]) != LDPS_OK)
+ message(LDPL_FATAL, "Failed to get symbol information");
- if (!Dropped.count(GO))
- return nullptr;
+ const void *View;
+ if (get_view(F.handle, &View) != LDPS_OK)
+ message(LDPL_FATAL, "Failed to get a view of file");
- Module &M = *GO->getParent();
- GlobalValue::LinkageTypes L = GO->getLinkage();
- GlobalObject *Declaration;
- if (auto *F = dyn_cast<Function>(GO)) {
- Declaration = Function::Create(F->getFunctionType(), L, "", &M);
- } else {
- auto *Var = cast<GlobalVariable>(GO);
- Declaration = new GlobalVariable(M, Var->getType()->getElementType(),
- Var->isConstant(), L,
- /*Initializer*/ nullptr);
- }
- Declaration->takeName(GO);
- Declaration->copyAttributesFrom(GO);
+ MemoryBufferRef BufferRef(StringRef((const char *)View, Info.filesize),
+ Info.name);
- GO->setLinkage(GlobalValue::InternalLinkage);
- GO->setName(Declaration->getName());
- Dropped.erase(GO);
- GO->replaceAllUsesWith(Declaration);
+ // Don't bother trying to build an index if there is no summary information
+ // in this bitcode file.
+ if (!object::FunctionIndexObjectFile::hasFunctionSummaryInMemBuffer(
+ BufferRef, diagnosticHandler))
+ return std::unique_ptr<FunctionInfoIndex>(nullptr);
- LocalVersions[Declaration] = GO;
+ ErrorOr<std::unique_ptr<object::FunctionIndexObjectFile>> ObjOrErr =
+ object::FunctionIndexObjectFile::create(BufferRef, diagnosticHandler);
- return GO;
-}
+ if (std::error_code EC = ObjOrErr.getError())
+ message(LDPL_FATAL, "Could not read function index bitcode from file : %s",
+ EC.message().c_str());
-static Constant *mapConstantToLocalCopy(Constant *C, ValueToValueMapTy &VM,
- LocalValueMaterializer *Materializer) {
- return MapValue(C, VM, RF_IgnoreMissingEntries, nullptr, Materializer);
-}
+ object::FunctionIndexObjectFile &Obj = **ObjOrErr;
-static void freeSymName(ld_plugin_symbol &Sym) {
- free(Sym.name);
- free(Sym.comdat_key);
- Sym.name = nullptr;
- Sym.comdat_key = nullptr;
+ return Obj.takeIndex();
}
static std::unique_ptr<Module>
getModuleForFile(LLVMContext &Context, claimed_file &F,
ld_plugin_input_file &Info, raw_fd_ostream *ApiFile,
- StringSet<> &Internalize, StringSet<> &Maybe) {
+ StringSet<> &Internalize, StringSet<> &Maybe,
+ std::vector<GlobalValue *> &Keep) {
- if (get_symbols(F.handle, F.syms.size(), &F.syms[0]) != LDPS_OK)
+ if (get_symbols(F.handle, F.syms.size(), F.syms.data()) != LDPS_OK)
message(LDPL_FATAL, "Failed to get symbol information");
const void *View;
@@ -609,11 +624,12 @@ getModuleForFile(LLVMContext &Context, claimed_file &F,
SmallPtrSet<GlobalValue *, 8> Used;
collectUsedGlobalVariables(M, Used, /*CompilerUsed*/ false);
- DenseSet<GlobalValue *> Drop;
- std::vector<GlobalAlias *> KeptAliases;
-
unsigned SymNum = 0;
for (auto &ObjSym : Obj.symbols()) {
+ GlobalValue *GV = Obj.getSymbolGV(ObjSym.getRawDataRefImpl());
+ if (GV && GV->hasAppendingLinkage())
+ Keep.push_back(GV);
+
if (shouldSkip(ObjSym.getFlags()))
continue;
ld_plugin_symbol &Sym = F.syms[SymNum];
@@ -625,20 +641,37 @@ getModuleForFile(LLVMContext &Context, claimed_file &F,
if (options::generate_api_file)
*ApiFile << Sym.name << ' ' << getResolutionName(Resolution) << '\n';
- GlobalValue *GV = Obj.getSymbolGV(ObjSym.getRawDataRefImpl());
if (!GV) {
freeSymName(Sym);
continue; // Asm symbol.
}
- if (Resolution != LDPR_PREVAILING_DEF_IRONLY && GV->hasCommonLinkage()) {
- // Common linkage is special. There is no single symbol that wins the
- // resolution. Instead we have to collect the maximum alignment and size.
- // The IR linker does that for us if we just pass it every common GV.
- // We still have to keep track of LDPR_PREVAILING_DEF_IRONLY so we
- // internalize once the IR linker has done its job.
- freeSymName(Sym);
- continue;
+ ResolutionInfo &Res = ResInfo[Sym.name];
+ if (Resolution == LDPR_PREVAILING_DEF_IRONLY_EXP && !Res.IsLinkonceOdr)
+ Resolution = LDPR_PREVAILING_DEF;
+
+ GV->setUnnamedAddr(Res.UnnamedAddr);
+ GV->setVisibility(Res.Visibility);
+
+ // Override gold's resolution for common symbols. We want the largest
+ // one to win.
+ if (GV->hasCommonLinkage()) {
+ cast<GlobalVariable>(GV)->setAlignment(Res.CommonAlign);
+ if (Resolution == LDPR_PREVAILING_DEF_IRONLY)
+ Res.CommonInternal = true;
+
+ if (Resolution == LDPR_PREVAILING_DEF_IRONLY ||
+ Resolution == LDPR_PREVAILING_DEF)
+ Res.UseCommon = true;
+
+ if (Res.CommonFile == &F && Res.UseCommon) {
+ if (Res.CommonInternal)
+ Resolution = LDPR_PREVAILING_DEF_IRONLY;
+ else
+ Resolution = LDPR_PREVAILING_DEF;
+ } else {
+ Resolution = LDPR_PREEMPTED_IR;
+ }
}
switch (Resolution) {
@@ -648,40 +681,37 @@ getModuleForFile(LLVMContext &Context, claimed_file &F,
case LDPR_RESOLVED_IR:
case LDPR_RESOLVED_EXEC:
case LDPR_RESOLVED_DYN:
- assert(GV->isDeclarationForLinker());
+ case LDPR_PREEMPTED_IR:
+ case LDPR_PREEMPTED_REG:
break;
case LDPR_UNDEF:
- if (!GV->isDeclarationForLinker()) {
+ if (!GV->isDeclarationForLinker())
assert(GV->hasComdat());
- Drop.insert(GV);
- }
break;
case LDPR_PREVAILING_DEF_IRONLY: {
- keepGlobalValue(*GV, KeptAliases);
- if (!Used.count(GV)) {
- // Since we use the regular lib/Linker, we cannot just internalize GV
- // now or it will not be copied to the merged module. Instead we force
- // it to be copied and then internalize it.
+ Keep.push_back(GV);
+ // The IR linker has to be able to map this value to a declaration,
+ // so we can only internalize after linking.
+ if (!Used.count(GV))
Internalize.insert(GV->getName());
- }
break;
}
case LDPR_PREVAILING_DEF:
- keepGlobalValue(*GV, KeptAliases);
- break;
-
- case LDPR_PREEMPTED_IR:
- // Gold might have selected a linkonce_odr and preempted a weak_odr.
- // In that case we have to make sure we don't end up internalizing it.
- if (!GV->isDiscardableIfUnused())
- Maybe.erase(GV->getName());
-
- // fall-through
- case LDPR_PREEMPTED_REG:
- Drop.insert(GV);
+ Keep.push_back(GV);
+ // There is a non IR use, so we have to force optimizations to keep this.
+ switch (GV->getLinkage()) {
+ default:
+ break;
+ case GlobalValue::LinkOnceAnyLinkage:
+ GV->setLinkage(GlobalValue::WeakAnyLinkage);
+ break;
+ case GlobalValue::LinkOnceODRLinkage:
+ GV->setLinkage(GlobalValue::WeakODRLinkage);
+ break;
+ }
break;
case LDPR_PREVAILING_DEF_IRONLY_EXP: {
@@ -689,9 +719,8 @@ getModuleForFile(LLVMContext &Context, claimed_file &F,
// reason is that this GV might have a copy in another module
// and in that module the address might be significant, but that
// copy will be LDPR_PREEMPTED_IR.
- if (GV->hasLinkOnceODRLinkage())
- Maybe.insert(GV->getName());
- keepGlobalValue(*GV, KeptAliases);
+ Maybe.insert(GV->getName());
+ Keep.push_back(GV);
break;
}
}
@@ -699,25 +728,11 @@ getModuleForFile(LLVMContext &Context, claimed_file &F,
freeSymName(Sym);
}
- ValueToValueMapTy VM;
- LocalValueMaterializer Materializer(Drop);
- for (GlobalAlias *GA : KeptAliases) {
- // Gold told us to keep GA. It is possible that a GV usied in the aliasee
- // expression is being dropped. If that is the case, that GV must be copied.
- Constant *Aliasee = GA->getAliasee();
- Constant *Replacement = mapConstantToLocalCopy(Aliasee, VM, &Materializer);
- GA->setAliasee(Replacement);
- }
-
- for (auto *GV : Drop)
- drop(*GV);
-
return Obj.takeModule();
}
static void runLTOPasses(Module &M, TargetMachine &TM) {
- if (const DataLayout *DL = TM.getDataLayout())
- M.setDataLayout(*DL);
+ M.setDataLayout(TM.createDataLayout());
legacy::PassManager passes;
passes.add(createTargetTransformInfoWrapperPass(TM.getTargetIRAnalysis()));
@@ -725,8 +740,10 @@ static void runLTOPasses(Module &M, TargetMachine &TM) {
PassManagerBuilder PMB;
PMB.LibraryInfo = new TargetLibraryInfoImpl(Triple(TM.getTargetTriple()));
PMB.Inliner = createFunctionInliningPass();
+ // Unconditionally verify input since it is not verified before this
+ // point and has unknown origin.
PMB.VerifyInput = true;
- PMB.VerifyOutput = true;
+ PMB.VerifyOutput = !options::DisableVerify;
PMB.LoopVectorize = true;
PMB.SLPVectorize = true;
PMB.OptLevel = options::OptLevel;
@@ -739,11 +756,11 @@ static void saveBCFile(StringRef Path, Module &M) {
raw_fd_ostream OS(Path, EC, sys::fs::OpenFlags::F_None);
if (EC)
message(LDPL_FATAL, "Failed to write the output file.");
- WriteBitcodeToFile(&M, OS, /* ShouldPreserveUseListOrder */ true);
+ WriteBitcodeToFile(&M, OS, /* ShouldPreserveUseListOrder */ false);
}
-static void codegen(Module &M) {
- const std::string &TripleStr = M.getTargetTriple();
+static void codegen(std::unique_ptr<Module> M) {
+ const std::string &TripleStr = M->getTargetTriple();
Triple TheTriple(TripleStr);
std::string ErrMsg;
@@ -779,12 +796,10 @@ static void codegen(Module &M) {
TripleStr, options::mcpu, Features.getString(), Options, RelocationModel,
CodeModel::Default, CGOptLevel));
- runLTOPasses(M, *TM);
+ runLTOPasses(*M, *TM);
if (options::TheOutputType == options::OT_SAVE_TEMPS)
- saveBCFile(output_name + ".opt.bc", M);
-
- legacy::PassManager CodeGenPasses;
+ saveBCFile(output_name + ".opt.bc", *M);
SmallString<128> Filename;
if (!options::obj_path.empty())
@@ -792,37 +807,47 @@ static void codegen(Module &M) {
else if (options::TheOutputType == options::OT_SAVE_TEMPS)
Filename = output_name + ".o";
- int FD;
+ std::vector<SmallString<128>> Filenames(options::Parallelism);
bool TempOutFile = Filename.empty();
- if (TempOutFile) {
- std::error_code EC =
- sys::fs::createTemporaryFile("lto-llvm", "o", FD, Filename);
- if (EC)
- message(LDPL_FATAL, "Could not create temporary file: %s",
- EC.message().c_str());
- } else {
- std::error_code EC =
- sys::fs::openFileForWrite(Filename.c_str(), FD, sys::fs::F_None);
- if (EC)
- message(LDPL_FATAL, "Could not open file: %s", EC.message().c_str());
- }
-
{
- raw_fd_ostream OS(FD, true);
+ // Open a file descriptor for each backend thread. This is done in a block
+ // so that the output file descriptors are closed before gold opens them.
+ std::list<llvm::raw_fd_ostream> OSs;
+ std::vector<llvm::raw_pwrite_stream *> OSPtrs(options::Parallelism);
+ for (unsigned I = 0; I != options::Parallelism; ++I) {
+ int FD;
+ if (TempOutFile) {
+ std::error_code EC =
+ sys::fs::createTemporaryFile("lto-llvm", "o", FD, Filenames[I]);
+ if (EC)
+ message(LDPL_FATAL, "Could not create temporary file: %s",
+ EC.message().c_str());
+ } else {
+ Filenames[I] = Filename;
+ if (options::Parallelism != 1)
+ Filenames[I] += utostr(I);
+ std::error_code EC =
+ sys::fs::openFileForWrite(Filenames[I], FD, sys::fs::F_None);
+ if (EC)
+ message(LDPL_FATAL, "Could not open file: %s", EC.message().c_str());
+ }
+ OSs.emplace_back(FD, true);
+ OSPtrs[I] = &OSs.back();
+ }
- if (TM->addPassesToEmitFile(CodeGenPasses, OS,
- TargetMachine::CGFT_ObjectFile))
- message(LDPL_FATAL, "Failed to setup codegen");
- CodeGenPasses.run(M);
+ // Run backend threads.
+ splitCodeGen(std::move(M), OSPtrs, options::mcpu, Features.getString(),
+ Options, RelocationModel, CodeModel::Default, CGOptLevel);
}
- if (add_input_file(Filename.c_str()) != LDPS_OK)
- message(LDPL_FATAL,
- "Unable to add .o file to the link. File left behind in: %s",
- Filename.c_str());
-
- if (TempOutFile)
- Cleanup.push_back(Filename.c_str());
+ for (auto &Filename : Filenames) {
+ if (add_input_file(Filename.c_str()) != LDPS_OK)
+ message(LDPL_FATAL,
+ "Unable to add .o file to the link. File left behind in: %s",
+ Filename.c_str());
+ if (TempOutFile)
+ Cleanup.push_back(Filename.c_str());
+ }
}
/// gold informs us that all symbols have been read. At this point, we use
@@ -832,32 +857,58 @@ static ld_plugin_status allSymbolsReadHook(raw_fd_ostream *ApiFile) {
if (Modules.empty())
return LDPS_OK;
+ // If we are doing ThinLTO compilation, simply build the combined
+ // function index/summary and emit it. We don't need to parse the modules
+ // and link them in this case.
+ if (options::thinlto) {
+ FunctionInfoIndex CombinedIndex;
+ uint64_t NextModuleId = 0;
+ for (claimed_file &F : Modules) {
+ PluginInputFile InputFile(F.handle);
+
+ std::unique_ptr<FunctionInfoIndex> Index =
+ getFunctionIndexForFile(F, InputFile.file());
+
+ // Skip files without a function summary.
+ if (Index)
+ CombinedIndex.mergeFrom(std::move(Index), ++NextModuleId);
+ }
+
+ std::error_code EC;
+ raw_fd_ostream OS(output_name + ".thinlto.bc", EC,
+ sys::fs::OpenFlags::F_None);
+ if (EC)
+ message(LDPL_FATAL, "Unable to open %s.thinlto.bc for writing: %s",
+ output_name.data(), EC.message().c_str());
+ WriteFunctionSummaryToFile(CombinedIndex, OS);
+ OS.close();
+
+ cleanup_hook();
+ exit(0);
+ }
+
LLVMContext Context;
- Context.setDiagnosticHandler(diagnosticHandler, nullptr, true);
+ Context.setDiagnosticHandler(diagnosticHandlerForContext, nullptr, true);
std::unique_ptr<Module> Combined(new Module("ld-temp.o", Context));
- Linker L(Combined.get());
+ IRMover L(*Combined);
std::string DefaultTriple = sys::getDefaultTargetTriple();
StringSet<> Internalize;
StringSet<> Maybe;
for (claimed_file &F : Modules) {
- ld_plugin_input_file File;
- if (get_input_file(F.handle, &File) != LDPS_OK)
- message(LDPL_FATAL, "Failed to get file information");
- std::unique_ptr<Module> M =
- getModuleForFile(Context, F, File, ApiFile, Internalize, Maybe);
+ PluginInputFile InputFile(F.handle);
+ std::vector<GlobalValue *> Keep;
+ std::unique_ptr<Module> M = getModuleForFile(
+ Context, F, InputFile.file(), ApiFile, Internalize, Maybe, Keep);
if (!options::triple.empty())
M->setTargetTriple(options::triple.c_str());
- else if (M->getTargetTriple().empty()) {
+ else if (M->getTargetTriple().empty())
M->setTargetTriple(DefaultTriple);
- }
- if (L.linkInModule(M.get()))
+ if (L.move(*M, Keep, [](GlobalValue &, IRMover::ValueAdder) {}))
message(LDPL_FATAL, "Failed to link module");
- if (release_input_file(F.handle) != LDPS_OK)
- message(LDPL_FATAL, "Failed to release file information");
}
for (const auto &Name : Internalize) {
@@ -884,12 +935,12 @@ static ld_plugin_status allSymbolsReadHook(raw_fd_ostream *ApiFile) {
path = output_name;
else
path = output_name + ".bc";
- saveBCFile(path, *L.getModule());
+ saveBCFile(path, *Combined);
if (options::TheOutputType == options::OT_BC_ONLY)
return LDPS_OK;
}
- codegen(*L.getModule());
+ codegen(std::move(Combined));
if (!options::extra_library_path.empty() &&
set_extra_library_path(options::extra_library_path.c_str()) != LDPS_OK)
diff --git a/tools/llc/CMakeLists.txt b/tools/llc/CMakeLists.txt
index ff5a89e1da44..136cf4be1c12 100644
--- a/tools/llc/CMakeLists.txt
+++ b/tools/llc/CMakeLists.txt
@@ -11,6 +11,7 @@ set(LLVM_LINK_COMPONENTS
SelectionDAG
Support
Target
+ TransformUtils
)
# Support plugins.
diff --git a/tools/llc/LLVMBuild.txt b/tools/llc/LLVMBuild.txt
index 38660cf27a46..c1f5cebea859 100644
--- a/tools/llc/LLVMBuild.txt
+++ b/tools/llc/LLVMBuild.txt
@@ -19,4 +19,4 @@
type = Tool
name = llc
parent = Tools
-required_libraries = AsmParser BitReader IRReader MIRParser all-targets
+required_libraries = AsmParser BitReader IRReader MIRParser TransformUtils all-targets
diff --git a/tools/llc/Makefile b/tools/llc/Makefile
index ae64c9a5b57c..cd34c80d840a 100644
--- a/tools/llc/Makefile
+++ b/tools/llc/Makefile
@@ -9,7 +9,7 @@
LEVEL := ../..
TOOLNAME := llc
-LINK_COMPONENTS := all-targets bitreader asmparser irreader mirparser
+LINK_COMPONENTS := all-targets bitreader asmparser irreader mirparser transformutils
# Support plugins.
NO_DEAD_STRIP := 1
diff --git a/tools/llc/llc.cpp b/tools/llc/llc.cpp
index e33cd795d3ae..bffa39fd9e5c 100644
--- a/tools/llc/llc.cpp
+++ b/tools/llc/llc.cpp
@@ -45,6 +45,7 @@
#include "llvm/Support/ToolOutputFile.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetSubtargetInfo.h"
+#include "llvm/Transforms/Utils/Cloning.h"
#include <memory>
using namespace llvm;
@@ -96,6 +97,12 @@ static cl::opt<bool> AsmVerbose("asm-verbose",
cl::desc("Add comments to directives."),
cl::init(true));
+static cl::opt<bool>
+ CompileTwice("compile-twice", cl::Hidden,
+ cl::desc("Run everything twice, re-using the same pass "
+ "manager and verify the result is the same."),
+ cl::init(false));
+
static int compileModule(char **, LLVMContext &);
static std::unique_ptr<tool_output_file>
@@ -312,8 +319,7 @@ static int compileModule(char **argv, LLVMContext &Context) {
PM.add(new TargetLibraryInfoWrapperPass(TLII));
// Add the target data from the target machine, if it exists, or the module.
- if (const DataLayout *DL = Target->getDataLayout())
- M->setDataLayout(*DL);
+ M->setDataLayout(Target->createDataLayout());
// Override function attributes based on CPUStr, FeaturesStr, and command line
// flags.
@@ -326,10 +332,15 @@ static int compileModule(char **argv, LLVMContext &Context) {
{
raw_pwrite_stream *OS = &Out->os();
- std::unique_ptr<buffer_ostream> BOS;
- if (FileType != TargetMachine::CGFT_AssemblyFile &&
- !Out->os().supportsSeeking()) {
- BOS = make_unique<buffer_ostream>(*OS);
+
+ // Manually do the buffering rather than using buffer_ostream,
+ // so we can memcmp the contents in CompileTwice mode
+ SmallVector<char, 0> Buffer;
+ std::unique_ptr<raw_svector_ostream> BOS;
+ if ((FileType != TargetMachine::CGFT_AssemblyFile &&
+ !Out->os().supportsSeeking()) ||
+ CompileTwice) {
+ BOS = make_unique<raw_svector_ostream>(Buffer);
OS = BOS.get();
}
@@ -379,7 +390,39 @@ static int compileModule(char **argv, LLVMContext &Context) {
// Before executing passes, print the final values of the LLVM options.
cl::PrintOptionValues();
+ // If requested, run the pass manager over the same module again,
+ // to catch any bugs due to persistent state in the passes. Note that
+ // opt has the same functionality, so it may be worth abstracting this out
+ // in the future.
+ SmallVector<char, 0> CompileTwiceBuffer;
+ if (CompileTwice) {
+ std::unique_ptr<Module> M2(llvm::CloneModule(M.get()));
+ PM.run(*M2);
+ CompileTwiceBuffer = Buffer;
+ Buffer.clear();
+ }
+
PM.run(*M);
+
+ // Compare the two outputs and make sure they're the same
+ if (CompileTwice) {
+ if (Buffer.size() != CompileTwiceBuffer.size() ||
+ (memcmp(Buffer.data(), CompileTwiceBuffer.data(), Buffer.size()) !=
+ 0)) {
+ errs()
+ << "Running the pass manager twice changed the output.\n"
+ "Writing the result of the second run to the specified output\n"
+ "To generate the one-run comparison binary, just run without\n"
+ "the compile-twice option\n";
+ Out->os() << Buffer;
+ Out->keep();
+ return 1;
+ }
+ }
+
+ if (BOS) {
+ Out->os() << Buffer;
+ }
}
// Declare success.
diff --git a/tools/lli/OrcLazyJIT.cpp b/tools/lli/OrcLazyJIT.cpp
index ae276e6cda8f..4235145ee7a5 100644
--- a/tools/lli/OrcLazyJIT.cpp
+++ b/tools/lli/OrcLazyJIT.cpp
@@ -38,26 +38,39 @@ namespace {
"Dump modules to the current "
"working directory. (WARNING: "
"will overwrite existing files)."),
- clEnumValEnd));
+ clEnumValEnd),
+ cl::Hidden);
+
+ cl::opt<bool> OrcInlineStubs("orc-lazy-inline-stubs",
+ cl::desc("Try to inline stubs"),
+ cl::init(true), cl::Hidden);
}
-OrcLazyJIT::CallbackManagerBuilder
-OrcLazyJIT::createCallbackManagerBuilder(Triple T) {
+std::unique_ptr<OrcLazyJIT::CompileCallbackMgr>
+OrcLazyJIT::createCompileCallbackMgr(Triple T) {
switch (T.getArch()) {
default: return nullptr;
case Triple::x86_64: {
- typedef orc::JITCompileCallbackManager<IRDumpLayerT,
- orc::OrcX86_64> CCMgrT;
- return [](IRDumpLayerT &IRDumpLayer, RuntimeDyld::MemoryManager &MemMgr,
- LLVMContext &Context) {
- return llvm::make_unique<CCMgrT>(IRDumpLayer, MemMgr, Context, 0,
- 64);
- };
+ typedef orc::LocalJITCompileCallbackManager<orc::OrcX86_64> CCMgrT;
+ return llvm::make_unique<CCMgrT>(0);
}
}
}
+OrcLazyJIT::IndirectStubsManagerBuilder
+OrcLazyJIT::createIndirectStubsMgrBuilder(Triple T) {
+ switch (T.getArch()) {
+ default: return nullptr;
+
+ case Triple::x86_64:
+ return [](){
+ return llvm::make_unique<
+ orc::LocalIndirectStubsManager<orc::OrcX86_64>>();
+ };
+ }
+}
+
OrcLazyJIT::TransformFtor OrcLazyJIT::createDebugDumper() {
switch (OrcDumpKind) {
@@ -111,6 +124,12 @@ OrcLazyJIT::TransformFtor OrcLazyJIT::createDebugDumper() {
// Defined in lli.cpp.
CodeGenOpt::Level getOptLevel();
+
+template <typename PtrTy>
+static PtrTy fromTargetAddress(orc::TargetAddress Addr) {
+ return reinterpret_cast<PtrTy>(static_cast<uintptr_t>(Addr));
+}
+
int llvm::runOrcLazyJIT(std::unique_ptr<Module> M, int ArgC, char* ArgV[]) {
// Add the program's symbols into the JIT's search space.
if (sys::DynamicLibrary::LoadLibraryPermanently(nullptr)) {
@@ -123,20 +142,31 @@ int llvm::runOrcLazyJIT(std::unique_ptr<Module> M, int ArgC, char* ArgV[]) {
EngineBuilder EB;
EB.setOptLevel(getOptLevel());
auto TM = std::unique_ptr<TargetMachine>(EB.selectTarget());
- auto &Context = getGlobalContext();
- auto CallbackMgrBuilder =
- OrcLazyJIT::createCallbackManagerBuilder(Triple(TM->getTargetTriple()));
+ auto CompileCallbackMgr =
+ OrcLazyJIT::createCompileCallbackMgr(Triple(TM->getTargetTriple()));
// If we couldn't build the factory function then there must not be a callback
// manager for this target. Bail out.
- if (!CallbackMgrBuilder) {
+ if (!CompileCallbackMgr) {
errs() << "No callback manager available for target '"
<< TM->getTargetTriple().str() << "'.\n";
return 1;
}
+ auto IndirectStubsMgrBuilder =
+ OrcLazyJIT::createIndirectStubsMgrBuilder(Triple(TM->getTargetTriple()));
+
+ // If we couldn't build a stubs-manager-builder for this target then bail out.
+ if (!IndirectStubsMgrBuilder) {
+ errs() << "No indirect stubs manager available for target '"
+ << TM->getTargetTriple().str() << "'.\n";
+ return 1;
+ }
+
// Everything looks good. Build the JIT.
- OrcLazyJIT J(std::move(TM), Context, CallbackMgrBuilder);
+ OrcLazyJIT J(std::move(TM), std::move(CompileCallbackMgr),
+ std::move(IndirectStubsMgrBuilder),
+ OrcInlineStubs);
// Add the module, look up main and run it.
auto MainHandle = J.addModule(std::move(M));
@@ -148,6 +178,6 @@ int llvm::runOrcLazyJIT(std::unique_ptr<Module> M, int ArgC, char* ArgV[]) {
}
typedef int (*MainFnPtr)(int, char*[]);
- auto Main = OrcLazyJIT::fromTargetAddress<MainFnPtr>(MainSym.getAddress());
+ auto Main = fromTargetAddress<MainFnPtr>(MainSym.getAddress());
return Main(ArgC, ArgV);
}
diff --git a/tools/lli/OrcLazyJIT.h b/tools/lli/OrcLazyJIT.h
index fe86adbf9516..bb4da33ea9b6 100644
--- a/tools/lli/OrcLazyJIT.h
+++ b/tools/lli/OrcLazyJIT.h
@@ -23,39 +23,36 @@
#include "llvm/ExecutionEngine/Orc/IRTransformLayer.h"
#include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h"
#include "llvm/ExecutionEngine/RTDyldMemoryManager.h"
-#include "llvm/IR/LLVMContext.h"
namespace llvm {
class OrcLazyJIT {
public:
- typedef orc::JITCompileCallbackManagerBase CompileCallbackMgr;
+ typedef orc::JITCompileCallbackManager CompileCallbackMgr;
typedef orc::ObjectLinkingLayer<> ObjLayerT;
typedef orc::IRCompileLayer<ObjLayerT> CompileLayerT;
typedef std::function<std::unique_ptr<Module>(std::unique_ptr<Module>)>
TransformFtor;
typedef orc::IRTransformLayer<CompileLayerT, TransformFtor> IRDumpLayerT;
typedef orc::CompileOnDemandLayer<IRDumpLayerT, CompileCallbackMgr> CODLayerT;
+ typedef CODLayerT::IndirectStubsManagerBuilderT
+ IndirectStubsManagerBuilder;
typedef CODLayerT::ModuleSetHandleT ModuleHandleT;
- typedef std::function<
- std::unique_ptr<CompileCallbackMgr>(IRDumpLayerT&,
- RuntimeDyld::MemoryManager&,
- LLVMContext&)>
- CallbackManagerBuilder;
-
- static CallbackManagerBuilder createCallbackManagerBuilder(Triple T);
-
- OrcLazyJIT(std::unique_ptr<TargetMachine> TM, LLVMContext &Context,
- CallbackManagerBuilder &BuildCallbackMgr)
- : TM(std::move(TM)),
- ObjectLayer(),
- CompileLayer(ObjectLayer, orc::SimpleCompiler(*this->TM)),
- IRDumpLayer(CompileLayer, createDebugDumper()),
- CCMgr(BuildCallbackMgr(IRDumpLayer, CCMgrMemMgr, Context)),
- CODLayer(IRDumpLayer, *CCMgr, false),
- CXXRuntimeOverrides([this](const std::string &S) { return mangle(S); }) {}
+ OrcLazyJIT(std::unique_ptr<TargetMachine> TM,
+ std::unique_ptr<CompileCallbackMgr> CCMgr,
+ IndirectStubsManagerBuilder IndirectStubsMgrBuilder,
+ bool InlineStubs)
+ : TM(std::move(TM)), DL(this->TM->createDataLayout()),
+ CCMgr(std::move(CCMgr)),
+ ObjectLayer(),
+ CompileLayer(ObjectLayer, orc::SimpleCompiler(*this->TM)),
+ IRDumpLayer(CompileLayer, createDebugDumper()),
+ CODLayer(IRDumpLayer, extractSingleFunction, *this->CCMgr,
+ std::move(IndirectStubsMgrBuilder), InlineStubs),
+ CXXRuntimeOverrides(
+ [this](const std::string &S) { return mangle(S); }) {}
~OrcLazyJIT() {
// Run any destructors registered with __cxa_atexit.
@@ -65,15 +62,13 @@ public:
DtorRunner.runViaLayer(CODLayer);
}
- template <typename PtrTy>
- static PtrTy fromTargetAddress(orc::TargetAddress Addr) {
- return reinterpret_cast<PtrTy>(static_cast<uintptr_t>(Addr));
- }
+ static std::unique_ptr<CompileCallbackMgr> createCompileCallbackMgr(Triple T);
+ static IndirectStubsManagerBuilder createIndirectStubsMgrBuilder(Triple T);
ModuleHandleT addModule(std::unique_ptr<Module> M) {
// Attach a data-layout if one isn't already present.
if (M->getDataLayout().isDefault())
- M->setDataLayout(*TM->getDataLayout());
+ M->setDataLayout(DL);
// Record the static constructors and destructors. We have to do this before
// we hand over ownership of the module to the JIT.
@@ -136,20 +131,27 @@ private:
std::string MangledName;
{
raw_string_ostream MangledNameStream(MangledName);
- Mangler::getNameWithPrefix(MangledNameStream, Name, *TM->getDataLayout());
+ Mangler::getNameWithPrefix(MangledNameStream, Name, DL);
}
return MangledName;
}
+ static std::set<Function*> extractSingleFunction(Function &F) {
+ std::set<Function*> Partition;
+ Partition.insert(&F);
+ return Partition;
+ }
+
static TransformFtor createDebugDumper();
std::unique_ptr<TargetMachine> TM;
+ DataLayout DL;
SectionMemoryManager CCMgrMemMgr;
+ std::unique_ptr<CompileCallbackMgr> CCMgr;
ObjLayerT ObjectLayer;
CompileLayerT CompileLayer;
IRDumpLayerT IRDumpLayer;
- std::unique_ptr<CompileCallbackMgr> CCMgr;
CODLayerT CODLayer;
orc::LocalCXXRuntimeOverrides CXXRuntimeOverrides;
diff --git a/tools/lli/RemoteTarget.cpp b/tools/lli/RemoteTarget.cpp
index 850fdc506999..95e1511eaaf5 100644
--- a/tools/lli/RemoteTarget.cpp
+++ b/tools/lli/RemoteTarget.cpp
@@ -1,4 +1,4 @@
-//===- RemoteTarget.cpp - LLVM Remote process JIT execution --------------===//
+//===- RemoteTarget.cpp - LLVM Remote process JIT execution -----*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
@@ -56,7 +56,7 @@ bool RemoteTarget::loadCode(uint64_t Address, const void *Data, size_t Size) {
}
bool RemoteTarget::executeCode(uint64_t Address, int &RetVal) {
- int (*fn)(void) = (int(*)(void))Address;
+ int (*fn)() = (int(*)())Address;
RetVal = fn();
return true;
}
diff --git a/tools/lli/lli.cpp b/tools/lli/lli.cpp
index 057841f0ad60..9f714060c17a 100644
--- a/tools/lli/lli.cpp
+++ b/tools/lli/lli.cpp
@@ -262,8 +262,7 @@ public:
if (!getCacheFilename(ModuleID, CacheName))
return;
if (!CacheDir.empty()) { // Create user-defined cache dir.
- SmallString<128> dir(CacheName);
- sys::path::remove_filename(dir);
+ SmallString<128> dir(sys::path::parent_path(CacheName));
sys::fs::create_directories(Twine(dir));
}
std::error_code EC;
@@ -422,7 +421,7 @@ int main(int argc, char **argv, char * const *envp) {
// If not jitting lazily, load the whole bitcode file eagerly too.
if (NoLazyCompilation) {
- if (std::error_code EC = Mod->materializeAllPermanently()) {
+ if (std::error_code EC = Mod->materializeAll()) {
errs() << argv[0] << ": bitcode didn't read correctly.\n";
errs() << "Reason: " << EC.message() << "\n";
exit(1);
diff --git a/tools/llvm-ar/CMakeLists.txt b/tools/llvm-ar/CMakeLists.txt
index 05ffe36fa7fe..86233dfce9a5 100644
--- a/tools/llvm-ar/CMakeLists.txt
+++ b/tools/llvm-ar/CMakeLists.txt
@@ -10,30 +10,5 @@ add_llvm_tool(llvm-ar
llvm-ar.cpp
)
-if(UNIX)
- set(LLVM_LINK_OR_COPY create_symlink)
- set(llvm_ar_binary "llvm-ar${CMAKE_EXECUTABLE_SUFFIX}")
-else()
- set(LLVM_LINK_OR_COPY copy)
- set(llvm_ar_binary "${LLVM_RUNTIME_OUTPUT_INTDIR}/llvm-ar${CMAKE_EXECUTABLE_SUFFIX}")
-endif()
-
-set(llvm_ranlib "${LLVM_RUNTIME_OUTPUT_INTDIR}/llvm-ranlib${CMAKE_EXECUTABLE_SUFFIX}")
-
-add_custom_command(OUTPUT ${llvm_ranlib}
- COMMAND ${CMAKE_COMMAND} -E ${LLVM_LINK_OR_COPY} "${llvm_ar_binary}" "${llvm_ranlib}"
- DEPENDS llvm-ar)
-
-add_custom_target(llvm-ranlib ALL DEPENDS ${llvm_ranlib})
-set_target_properties(llvm-ranlib PROPERTIES FOLDER Tools)
-
-set(llvm_lib "${LLVM_RUNTIME_OUTPUT_INTDIR}/llvm-lib${CMAKE_EXECUTABLE_SUFFIX}")
-
-add_custom_command(OUTPUT ${llvm_lib}
- COMMAND ${CMAKE_COMMAND} -E ${LLVM_LINK_OR_COPY} "${llvm_ar_binary}" "${llvm_lib}"
- DEPENDS llvm-ar)
-
-add_custom_target(llvm-lib ALL DEPENDS ${llvm_lib})
-set_target_properties(llvm-lib PROPERTIES FOLDER Tools)
-
-install(SCRIPT install_symlink.cmake -DCMAKE_INSTALL_PREFIX=\"${CMAKE_INSTALL_PREFIX}\")
+add_llvm_tool_symlink(llvm-ranlib llvm-ar)
+add_llvm_tool_symlink(llvm-lib llvm-ar)
diff --git a/tools/llvm-ar/install_symlink.cmake b/tools/llvm-ar/install_symlink.cmake
deleted file mode 100644
index d48431b128de..000000000000
--- a/tools/llvm-ar/install_symlink.cmake
+++ /dev/null
@@ -1,31 +0,0 @@
-# We need to execute this script at installation time because the
-# DESTDIR environment variable may be unset at configuration time.
-# See PR8397.
-
-if(UNIX)
- set(LINK_OR_COPY create_symlink)
- set(DESTDIR $ENV{DESTDIR})
-else()
- set(LINK_OR_COPY copy)
-endif()
-
-# CMAKE_EXECUTABLE_SUFFIX is undefined on cmake scripts. See PR9286.
-if( WIN32 )
- set(EXECUTABLE_SUFFIX ".exe")
-else()
- set(EXECUTABLE_SUFFIX "")
-endif()
-
-set(bindir "${DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/")
-
-message("Creating llvm-ranlib")
-
-execute_process(
- COMMAND "${CMAKE_COMMAND}" -E ${LINK_OR_COPY} "llvm-ar${EXECUTABLE_SUFFIX}" "llvm-ranlib${EXECUTABLE_SUFFIX}"
- WORKING_DIRECTORY "${bindir}")
-
-message("Creating llvm-lib")
-
-execute_process(
- COMMAND "${CMAKE_COMMAND}" -E ${LINK_OR_COPY} "llvm-ar${EXECUTABLE_SUFFIX}" "llvm-lib${EXECUTABLE_SUFFIX}"
- WORKING_DIRECTORY "${bindir}")
diff --git a/tools/llvm-ar/llvm-ar.cpp b/tools/llvm-ar/llvm-ar.cpp
index 2c9668c63b8c..ef5fab68b947 100644
--- a/tools/llvm-ar/llvm-ar.cpp
+++ b/tools/llvm-ar/llvm-ar.cpp
@@ -81,7 +81,7 @@ static cl::opt<Format>
clEnumValN(GNU, "gnu", "gnu"),
clEnumValN(BSD, "bsd", "bsd"), clEnumValEnd));
-std::string Options;
+static std::string Options;
// Provide additional help output explaining the operations and modifiers of
// llvm-ar. This object instructs the CommandLine library to print the text of
@@ -130,6 +130,7 @@ static bool OnlyUpdate = false; ///< 'u' modifier
static bool Verbose = false; ///< 'v' modifier
static bool Symtab = true; ///< 's' modifier
static bool Deterministic = true; ///< 'D' and 'U' modifiers
+static bool Thin = false; ///< 'T' modifier
// Relative Positional Argument (for insert/move). This variable holds
// the name of the archive member to which the 'a', 'b' or 'i' modifier
@@ -252,6 +253,9 @@ static ArchiveOperation parseCommandLine() {
case 'U':
Deterministic = false;
break;
+ case 'T':
+ Thin = true;
+ break;
default:
cl::PrintHelpMessage();
}
@@ -308,18 +312,9 @@ static void doPrint(StringRef Name, const object::Archive::Child &C) {
// Utility function for printing out the file mode when the 't' operation is in
// verbose mode.
static void printMode(unsigned mode) {
- if (mode & 004)
- outs() << "r";
- else
- outs() << "-";
- if (mode & 002)
- outs() << "w";
- else
- outs() << "-";
- if (mode & 001)
- outs() << "x";
- else
- outs() << "-";
+ outs() << ((mode & 004) ? "r" : "-");
+ outs() << ((mode & 002) ? "w" : "-");
+ outs() << ((mode & 001) ? "x" : "-");
}
// Implement the 't' operation. This function prints out just
@@ -334,7 +329,9 @@ static void doDisplayTable(StringRef Name, const object::Archive::Child &C) {
printMode(Mode & 007);
outs() << ' ' << C.getUID();
outs() << '/' << C.getGID();
- outs() << ' ' << format("%6llu", C.getSize());
+ ErrorOr<uint64_t> Size = C.getSize();
+ failIfError(Size.getError());
+ outs() << ' ' << format("%6llu", Size.get());
outs() << ' ' << C.getLastModified().str();
outs() << ' ';
}
@@ -393,13 +390,14 @@ static bool shouldCreateArchive(ArchiveOperation Op) {
static void performReadOperation(ArchiveOperation Operation,
object::Archive *OldArchive) {
- if (Operation == Extract && OldArchive->isThin()) {
- errs() << "extracting from a thin archive is not supported\n";
- std::exit(1);
- }
+ if (Operation == Extract && OldArchive->isThin())
+ fail("extracting from a thin archive is not supported");
bool Filter = !Members.empty();
- for (const object::Archive::Child &C : OldArchive->children()) {
+ for (auto &ChildOrErr : OldArchive->children()) {
+ failIfError(ChildOrErr.getError());
+ const object::Archive::Child &C = *ChildOrErr;
+
ErrorOr<StringRef> NameOrErr = C.getName();
failIfError(NameOrErr.getError());
StringRef Name = NameOrErr.get();
@@ -432,10 +430,21 @@ static void performReadOperation(ArchiveOperation Operation,
std::exit(1);
}
-template <typename T>
-void addMember(std::vector<NewArchiveIterator> &Members, T I, StringRef Name,
- int Pos = -1) {
- NewArchiveIterator NI(I, Name);
+static void addMember(std::vector<NewArchiveIterator> &Members,
+ StringRef FileName, int Pos = -1) {
+ NewArchiveIterator NI(FileName);
+ if (Pos == -1)
+ Members.push_back(NI);
+ else
+ Members[Pos] = NI;
+}
+
+static void addMember(std::vector<NewArchiveIterator> &Members,
+ const object::Archive::Child &M, StringRef Name,
+ int Pos = -1) {
+ if (Thin && !M.getParent()->isThin())
+ fail("Cannot convert a regular archive to a thin one");
+ NewArchiveIterator NI(M, Name);
if (Pos == -1)
Members.push_back(NI);
else
@@ -451,7 +460,7 @@ enum InsertAction {
};
static InsertAction computeInsertAction(ArchiveOperation Operation,
- object::Archive::child_iterator I,
+ const object::Archive::Child &Member,
StringRef Name,
std::vector<StringRef>::iterator &Pos) {
if (Operation == QuickAppend || Members.empty())
@@ -485,7 +494,7 @@ static InsertAction computeInsertAction(ArchiveOperation Operation,
// operation.
sys::fs::file_status Status;
failIfError(sys::fs::status(*MI, Status), *MI);
- if (Status.getLastModificationTime() < I->getLastModified()) {
+ if (Status.getLastModificationTime() < Member.getLastModified()) {
if (PosName.empty())
return IA_AddOldMember;
return IA_MoveOldMember;
@@ -508,7 +517,9 @@ computeNewArchiveMembers(ArchiveOperation Operation,
int InsertPos = -1;
StringRef PosName = sys::path::filename(RelPos);
if (OldArchive) {
- for (auto &Child : OldArchive->children()) {
+ for (auto &ChildOrErr : OldArchive->children()) {
+ failIfError(ChildOrErr.getError());
+ auto &Child = ChildOrErr.get();
int Pos = Ret.size();
ErrorOr<StringRef> NameOrErr = Child.getName();
failIfError(NameOrErr.getError());
@@ -529,7 +540,7 @@ computeNewArchiveMembers(ArchiveOperation Operation,
addMember(Ret, Child, Name);
break;
case IA_AddNewMeber:
- addMember(Ret, *MemberI, Name);
+ addMember(Ret, *MemberI);
break;
case IA_Delete:
break;
@@ -537,7 +548,7 @@ computeNewArchiveMembers(ArchiveOperation Operation,
addMember(Moved, Child, Name);
break;
case IA_MoveNewMember:
- addMember(Moved, *MemberI, Name);
+ addMember(Moved, *MemberI);
break;
}
if (MemberI != Members.end())
@@ -557,12 +568,10 @@ computeNewArchiveMembers(ArchiveOperation Operation,
assert(unsigned(InsertPos) <= Ret.size());
Ret.insert(Ret.begin() + InsertPos, Moved.begin(), Moved.end());
- Ret.insert(Ret.begin() + InsertPos, Members.size(),
- NewArchiveIterator("", ""));
+ Ret.insert(Ret.begin() + InsertPos, Members.size(), NewArchiveIterator(""));
int Pos = InsertPos;
for (auto &Member : Members) {
- StringRef Name = sys::path::filename(Member);
- addMember(Ret, Member, Name, Pos);
+ addMember(Ret, Member, Pos);
++Pos;
}
@@ -590,15 +599,15 @@ performWriteOperation(ArchiveOperation Operation, object::Archive *OldArchive,
break;
}
if (NewMembersP) {
- std::pair<StringRef, std::error_code> Result =
- writeArchive(ArchiveName, *NewMembersP, Symtab, Kind, Deterministic);
+ std::pair<StringRef, std::error_code> Result = writeArchive(
+ ArchiveName, *NewMembersP, Symtab, Kind, Deterministic, Thin);
failIfError(Result.second, Result.first);
return;
}
std::vector<NewArchiveIterator> NewMembers =
computeNewArchiveMembers(Operation, OldArchive);
auto Result =
- writeArchive(ArchiveName, NewMembers, Symtab, Kind, Deterministic);
+ writeArchive(ArchiveName, NewMembers, Symtab, Kind, Deterministic, Thin);
failIfError(Result.second, Result.first);
}
@@ -644,20 +653,13 @@ static int performOperation(ArchiveOperation Operation,
ErrorOr<std::unique_ptr<MemoryBuffer>> Buf =
MemoryBuffer::getFile(ArchiveName, -1, false);
std::error_code EC = Buf.getError();
- if (EC && EC != errc::no_such_file_or_directory) {
- errs() << ToolName << ": error opening '" << ArchiveName
- << "': " << EC.message() << "!\n";
- return 1;
- }
+ if (EC && EC != errc::no_such_file_or_directory)
+ fail("error opening '" + ArchiveName + "': " + EC.message() + "!");
if (!EC) {
object::Archive Archive(Buf.get()->getMemBufferRef(), EC);
-
- if (EC) {
- errs() << ToolName << ": error loading '" << ArchiveName
- << "': " << EC.message() << "!\n";
- return 1;
- }
+ failIfError(EC,
+ "error loading '" + ArchiveName + "': " + EC.message() + "!");
performOperation(Operation, &Archive, NewMembers);
return 0;
}
@@ -713,7 +715,9 @@ static void runMRIScript() {
failIfError(LibOrErr.getError(), "Could not parse library");
Archives.push_back(std::move(*LibOrErr));
object::Archive &Lib = *Archives.back();
- for (auto &Member : Lib.children()) {
+ for (auto &MemberOrErr : Lib.children()) {
+ failIfError(MemberOrErr.getError());
+ auto &Member = MemberOrErr.get();
ErrorOr<StringRef> NameOrErr = Member.getName();
failIfError(NameOrErr.getError());
addMember(NewMembers, Member, *NameOrErr);
@@ -721,7 +725,7 @@ static void runMRIScript() {
break;
}
case MRICommand::AddMod:
- addMember(NewMembers, Rest, sys::path::filename(Rest));
+ addMember(NewMembers, Rest);
break;
case MRICommand::Create:
Create = true;
@@ -784,9 +788,9 @@ int main(int argc, char **argv) {
" This program archives bitcode files into single libraries\n"
);
- if (Stem.find("ar") != StringRef::npos)
- return ar_main();
if (Stem.find("ranlib") != StringRef::npos)
return ranlib_main();
+ if (Stem.find("ar") != StringRef::npos)
+ return ar_main();
fail("Not ranlib, ar or lib!");
}
diff --git a/tools/llvm-as-fuzzer/CMakeLists.txt b/tools/llvm-as-fuzzer/CMakeLists.txt
new file mode 100644
index 000000000000..ff9bdaaf4c43
--- /dev/null
+++ b/tools/llvm-as-fuzzer/CMakeLists.txt
@@ -0,0 +1,13 @@
+if( LLVM_USE_SANITIZE_COVERAGE )
+ set(LLVM_LINK_COMPONENTS
+ AsmParser
+ BitWriter
+ Core
+ Support
+ )
+ add_llvm_tool(llvm-as-fuzzer
+ llvm-as-fuzzer.cpp)
+ target_link_libraries(llvm-as-fuzzer
+ LLVMFuzzer
+ )
+endif()
diff --git a/tools/llvm-as-fuzzer/llvm-as-fuzzer.cpp b/tools/llvm-as-fuzzer/llvm-as-fuzzer.cpp
new file mode 100644
index 000000000000..b4024bcaa99a
--- /dev/null
+++ b/tools/llvm-as-fuzzer/llvm-as-fuzzer.cpp
@@ -0,0 +1,76 @@
+//===--- fuzz-llvm-as.cpp - Fuzzer for llvm-as using lib/Fuzzer -----------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Build tool to fuzz the LLVM assembler (llvm-as) using
+// lib/Fuzzer. The main reason for using this tool is that it is much
+// faster than using afl-fuzz, since it is run in-process.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/AsmParser/Parser.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/Verifier.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/SourceMgr.h"
+
+#include <csetjmp>
+
+using namespace llvm;
+
+static jmp_buf JmpBuf;
+
+namespace {
+
+void MyFatalErrorHandler(void *user_data, const std::string& reason,
+ bool gen_crash_diag) {
+ // Don't bother printing reason, just return to the test function,
+ // since a fatal error represents a successful parse (i.e. it correctly
+ // terminated with an error message to the user).
+ longjmp(JmpBuf, 1);
+}
+
+static bool InstalledHandler = false;
+
+} // end of anonymous namespace
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+
+ // Allocate space for locals before setjmp so that memory can be collected
+ // if parse exits prematurely (via longjmp).
+ StringRef Input((const char *)Data, Size);
+ // Note: We need to create a buffer to add a null terminator to the
+ // end of the input string. The parser assumes that the string
+ // parsed is always null terminated.
+ std::unique_ptr<MemoryBuffer> MemBuf = MemoryBuffer::getMemBufferCopy(Input);
+ SMDiagnostic Err;
+ LLVMContext &Context = getGlobalContext();
+ std::unique_ptr<Module> M;
+
+ if (setjmp(JmpBuf))
+ // If reached, we have returned with non-zero status, so exit.
+ return 0;
+
+ // TODO(kschimpf) Write a main to do this initialization.
+ if (!InstalledHandler) {
+ llvm::install_fatal_error_handler(::MyFatalErrorHandler, nullptr);
+ InstalledHandler = true;
+ }
+
+ M = parseAssembly(MemBuf->getMemBufferRef(), Err, Context);
+
+ if (!M.get())
+ return 0;
+
+ verifyModule(*M.get());
+ return 0;
+}
diff --git a/tools/llvm-as/llvm-as.cpp b/tools/llvm-as/llvm-as.cpp
index 4455d24fb60d..d4e4d8d71070 100644
--- a/tools/llvm-as/llvm-as.cpp
+++ b/tools/llvm-as/llvm-as.cpp
@@ -45,6 +45,10 @@ static cl::opt<bool>
DisableOutput("disable-output", cl::desc("Disable output"), cl::init(false));
static cl::opt<bool>
+EmitFunctionSummary("function-summary", cl::desc("Emit function summary index"),
+ cl::init(false));
+
+static cl::opt<bool>
DumpAsm("d", cl::desc("Print assembly as parsed"), cl::Hidden);
static cl::opt<bool>
@@ -77,7 +81,8 @@ static void WriteOutputFile(const Module *M) {
}
if (Force || !CheckBitcodeOutputToConsole(Out->os(), true))
- WriteBitcodeToFile(M, Out->os(), PreserveBitcodeUseListOrder);
+ WriteBitcodeToFile(M, Out->os(), PreserveBitcodeUseListOrder,
+ EmitFunctionSummary);
// Declare success.
Out->keep();
diff --git a/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp b/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp
index 7672951f9c9b..fe68689def60 100644
--- a/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp
+++ b/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp
@@ -110,10 +110,16 @@ static const char *GetBlockName(unsigned BlockID,
case bitc::TYPE_BLOCK_ID_NEW: return "TYPE_BLOCK_ID";
case bitc::CONSTANTS_BLOCK_ID: return "CONSTANTS_BLOCK";
case bitc::FUNCTION_BLOCK_ID: return "FUNCTION_BLOCK";
+ case bitc::IDENTIFICATION_BLOCK_ID:
+ return "IDENTIFICATION_BLOCK_ID";
case bitc::VALUE_SYMTAB_BLOCK_ID: return "VALUE_SYMTAB";
case bitc::METADATA_BLOCK_ID: return "METADATA_BLOCK";
+ case bitc::METADATA_KIND_BLOCK_ID: return "METADATA_KIND_BLOCK";
case bitc::METADATA_ATTACHMENT_ID: return "METADATA_ATTACHMENT_BLOCK";
case bitc::USELIST_BLOCK_ID: return "USELIST_BLOCK_ID";
+ case bitc::FUNCTION_SUMMARY_BLOCK_ID:
+ return "FUNCTION_SUMMARY_BLOCK";
+ case bitc::MODULE_STRTAB_BLOCK_ID: return "MODULE_STRTAB_BLOCK";
}
}
@@ -165,6 +171,15 @@ static const char *GetCodeName(unsigned CodeID, unsigned BlockID,
STRINGIFY_CODE(MODULE_CODE, ALIAS)
STRINGIFY_CODE(MODULE_CODE, PURGEVALS)
STRINGIFY_CODE(MODULE_CODE, GCNAME)
+ STRINGIFY_CODE(MODULE_CODE, VSTOFFSET)
+ STRINGIFY_CODE(MODULE_CODE, METADATA_VALUES)
+ }
+ case bitc::IDENTIFICATION_BLOCK_ID:
+ switch (CodeID) {
+ default:
+ return nullptr;
+ STRINGIFY_CODE(IDENTIFICATION_CODE, STRING)
+ STRINGIFY_CODE(IDENTIFICATION_CODE, EPOCH)
}
case bitc::PARAMATTR_BLOCK_ID:
switch (CodeID) {
@@ -241,6 +256,9 @@ static const char *GetCodeName(unsigned CodeID, unsigned BlockID,
STRINGIFY_CODE(FUNC_CODE, INST_SWITCH)
STRINGIFY_CODE(FUNC_CODE, INST_INVOKE)
STRINGIFY_CODE(FUNC_CODE, INST_UNREACHABLE)
+ STRINGIFY_CODE(FUNC_CODE, INST_CLEANUPRET)
+ STRINGIFY_CODE(FUNC_CODE, INST_CATCHRET)
+ STRINGIFY_CODE(FUNC_CODE, INST_CATCHPAD)
STRINGIFY_CODE(FUNC_CODE, INST_PHI)
STRINGIFY_CODE(FUNC_CODE, INST_ALLOCA)
STRINGIFY_CODE(FUNC_CODE, INST_LOAD)
@@ -260,6 +278,21 @@ static const char *GetCodeName(unsigned CodeID, unsigned BlockID,
default: return nullptr;
STRINGIFY_CODE(VST_CODE, ENTRY)
STRINGIFY_CODE(VST_CODE, BBENTRY)
+ STRINGIFY_CODE(VST_CODE, FNENTRY)
+ STRINGIFY_CODE(VST_CODE, COMBINED_FNENTRY)
+ }
+ case bitc::MODULE_STRTAB_BLOCK_ID:
+ switch (CodeID) {
+ default:
+ return nullptr;
+ STRINGIFY_CODE(MST_CODE, ENTRY)
+ }
+ case bitc::FUNCTION_SUMMARY_BLOCK_ID:
+ switch (CodeID) {
+ default:
+ return nullptr;
+ STRINGIFY_CODE(FS_CODE, PERMODULE_ENTRY)
+ STRINGIFY_CODE(FS_CODE, COMBINED_ENTRY)
}
case bitc::METADATA_ATTACHMENT_ID:
switch(CodeID) {
@@ -271,7 +304,7 @@ static const char *GetCodeName(unsigned CodeID, unsigned BlockID,
default:return nullptr;
STRINGIFY_CODE(METADATA, STRING)
STRINGIFY_CODE(METADATA, NAME)
- STRINGIFY_CODE(METADATA, KIND)
+ STRINGIFY_CODE(METADATA, KIND) // Older bitcode has it in a MODULE_BLOCK
STRINGIFY_CODE(METADATA, NODE)
STRINGIFY_CODE(METADATA, VALUE)
STRINGIFY_CODE(METADATA, OLD_NODE)
@@ -301,6 +334,12 @@ static const char *GetCodeName(unsigned CodeID, unsigned BlockID,
STRINGIFY_CODE(METADATA, IMPORTED_ENTITY)
STRINGIFY_CODE(METADATA, MODULE)
}
+ case bitc::METADATA_KIND_BLOCK_ID:
+ switch (CodeID) {
+ default:
+ return nullptr;
+ STRINGIFY_CODE(METADATA, KIND)
+ }
case bitc::USELIST_BLOCK_ID:
switch(CodeID) {
default:return nullptr;
@@ -476,14 +515,38 @@ static bool ParseBlock(BitstreamCursor &Stream, unsigned BlockID,
GetCodeName(Code, BlockID, *Stream.getBitStreamReader(),
CurStreamType))
outs() << " codeid=" << Code;
- if (Entry.ID != bitc::UNABBREV_RECORD)
+ const BitCodeAbbrev *Abbv = nullptr;
+ if (Entry.ID != bitc::UNABBREV_RECORD) {
+ Abbv = Stream.getAbbrev(Entry.ID);
outs() << " abbrevid=" << Entry.ID;
+ }
for (unsigned i = 0, e = Record.size(); i != e; ++i)
outs() << " op" << i << "=" << (int64_t)Record[i];
outs() << "/>";
+ if (Abbv) {
+ for (unsigned i = 1, e = Abbv->getNumOperandInfos(); i != e; ++i) {
+ const BitCodeAbbrevOp &Op = Abbv->getOperandInfo(i);
+ if (!Op.isEncoding() || Op.getEncoding() != BitCodeAbbrevOp::Array)
+ continue;
+ assert(i + 2 == e && "Array op not second to last");
+ std::string Str;
+ bool ArrayIsPrintable = true;
+ for (unsigned j = i - 1, je = Record.size(); j != je; ++j) {
+ if (!isprint(static_cast<unsigned char>(Record[j]))) {
+ ArrayIsPrintable = false;
+ break;
+ }
+ Str += (char)Record[j];
+ }
+ if (ArrayIsPrintable)
+ outs() << " record string = '" << Str << "'";
+ break;
+ }
+ }
+
if (Blob.data()) {
outs() << " blob data = ";
if (ShowBinaryBlobs) {
diff --git a/tools/llvm-c-test/llvm-c-test.h b/tools/llvm-c-test/llvm-c-test.h
index 1b4976ae1423..7929fc4d19be 100644
--- a/tools/llvm-c-test/llvm-c-test.h
+++ b/tools/llvm-c-test/llvm-c-test.h
@@ -13,11 +13,13 @@
#ifndef LLVM_C_TEST_H
#define LLVM_C_TEST_H
+#include <stdbool.h>
+
// helpers.c
void tokenize_stdin(void (*cb)(char **tokens, int ntokens));
// module.c
-int module_dump(void);
+int module_dump(bool Lazy, bool New);
int module_list_functions(void);
int module_list_globals(void);
diff --git a/tools/llvm-c-test/main.c b/tools/llvm-c-test/main.c
index 59cc749fb155..e6b6e17098b3 100644
--- a/tools/llvm-c-test/main.c
+++ b/tools/llvm-c-test/main.c
@@ -23,6 +23,14 @@ static void print_usage(void) {
fprintf(stderr, " Commands:\n");
fprintf(stderr, " * --module-dump\n");
fprintf(stderr, " Read bytecode from stdin - print disassembly\n\n");
+ fprintf(stderr, " * --lazy-module-dump\n");
+ fprintf(stderr,
+ " Lazily read bytecode from stdin - print disassembly\n\n");
+ fprintf(stderr, " * --new-module-dump\n");
+ fprintf(stderr, " Read bytecode from stdin - print disassembly\n\n");
+ fprintf(stderr, " * --lazy-new-module-dump\n");
+ fprintf(stderr,
+ " Lazily read bytecode from stdin - print disassembly\n\n");
fprintf(stderr, " * --module-list-functions\n");
fprintf(stderr,
" Read bytecode from stdin - list summary of functions\n\n");
@@ -49,8 +57,14 @@ int main(int argc, char **argv) {
LLVMInitializeCore(pr);
- if (argc == 2 && !strcmp(argv[1], "--module-dump")) {
- return module_dump();
+ if (argc == 2 && !strcmp(argv[1], "--lazy-new-module-dump")) {
+ return module_dump(true, true);
+ } else if (argc == 2 && !strcmp(argv[1], "--new-module-dump")) {
+ return module_dump(false, true);
+ } else if (argc == 2 && !strcmp(argv[1], "--lazy-module-dump")) {
+ return module_dump(true, false);
+ } else if (argc == 2 && !strcmp(argv[1], "--module-dump")) {
+ return module_dump(false, false);
} else if (argc == 2 && !strcmp(argv[1], "--module-list-functions")) {
return module_list_functions();
} else if (argc == 2 && !strcmp(argv[1], "--module-list-globals")) {
diff --git a/tools/llvm-c-test/module.c b/tools/llvm-c-test/module.c
index 2661fc81d881..a6c47bf5fa16 100644
--- a/tools/llvm-c-test/module.c
+++ b/tools/llvm-c-test/module.c
@@ -19,7 +19,14 @@
#include <stdlib.h>
#include <string.h>
-static LLVMModuleRef load_module(void) {
+static void diagnosticHandler(LLVMDiagnosticInfoRef DI, void *C) {
+ char *CErr = LLVMGetDiagInfoDescription(DI);
+ fprintf(stderr, "Error with new bitcode parser: %s\n", CErr);
+ LLVMDisposeMessage(CErr);
+ exit(1);
+}
+
+static LLVMModuleRef load_module(bool Lazy, bool New) {
LLVMMemoryBufferRef MB;
LLVMModuleRef M;
char *msg = NULL;
@@ -29,18 +36,35 @@ static LLVMModuleRef load_module(void) {
exit(1);
}
- if (LLVMParseBitcode(MB, &M, &msg)) {
+ LLVMBool Ret;
+ if (New) {
+ LLVMContextRef C = LLVMGetGlobalContext();
+ LLVMContextSetDiagnosticHandler(C, diagnosticHandler, NULL);
+ if (Lazy)
+ Ret = LLVMGetBitcodeModule2(MB, &M);
+ else
+ Ret = LLVMParseBitcode2(MB, &M);
+ } else {
+ if (Lazy)
+ Ret = LLVMGetBitcodeModule(MB, &M, &msg);
+ else
+ Ret = LLVMParseBitcode(MB, &M, &msg);
+ }
+
+ if (Ret) {
fprintf(stderr, "Error parsing bitcode: %s\n", msg);
LLVMDisposeMemoryBuffer(MB);
exit(1);
}
- LLVMDisposeMemoryBuffer(MB);
+ if (!Lazy)
+ LLVMDisposeMemoryBuffer(MB);
+
return M;
}
-int module_dump(void) {
- LLVMModuleRef M = load_module();
+int module_dump(bool Lazy, bool New) {
+ LLVMModuleRef M = load_module(Lazy, New);
char *irstr = LLVMPrintModuleToString(M);
puts(irstr);
@@ -52,7 +76,7 @@ int module_dump(void) {
}
int module_list_functions(void) {
- LLVMModuleRef M = load_module();
+ LLVMModuleRef M = load_module(false, false);
LLVMValueRef f;
f = LLVMGetFirstFunction(M);
@@ -93,7 +117,7 @@ int module_list_functions(void) {
}
int module_list_globals(void) {
- LLVMModuleRef M = load_module();
+ LLVMModuleRef M = load_module(false, false);
LLVMValueRef g;
g = LLVMGetFirstGlobal(M);
diff --git a/tools/llvm-c-test/object.c b/tools/llvm-c-test/object.c
index a5421d9066d5..43521787b609 100644
--- a/tools/llvm-c-test/object.c
+++ b/tools/llvm-c-test/object.c
@@ -13,6 +13,7 @@
\*===----------------------------------------------------------------------===*/
#include "llvm-c-test.h"
+#include "llvm-c/Core.h"
#include "llvm-c/Object.h"
#include <stdio.h>
#include <stdlib.h>
diff --git a/tools/llvm-config/BuildVariables.inc.in b/tools/llvm-config/BuildVariables.inc.in
index 3f51f491a7a4..345f47e91b4a 100644
--- a/tools/llvm-config/BuildVariables.inc.in
+++ b/tools/llvm-config/BuildVariables.inc.in
@@ -26,3 +26,8 @@
#define LLVM_LIBDIR_SUFFIX "@LLVM_LIBDIR_SUFFIX@"
#define LLVM_TARGETS_BUILT "@LLVM_TARGETS_BUILT@"
#define LLVM_SYSTEM_LIBS "@LLVM_SYSTEM_LIBS@"
+#define LLVM_BUILD_SYSTEM "@LLVM_BUILD_SYSTEM@"
+#define LLVM_HAS_RTTI "@LLVM_HAS_RTTI@"
+#define LLVM_ENABLE_DYLIB "@LLVM_BUILD_LLVM_DYLIB@"
+#define LLVM_ENABLE_SHARED "@LLVM_ENABLE_SHARED@"
+#define LLVM_DYLIB_COMPONENTS "@LLVM_DYLIB_COMPONENTS@"
diff --git a/tools/llvm-config/CMakeLists.txt b/tools/llvm-config/CMakeLists.txt
index edbd8c950d7b..83794bb3fddd 100644
--- a/tools/llvm-config/CMakeLists.txt
+++ b/tools/llvm-config/CMakeLists.txt
@@ -24,10 +24,18 @@ set(LLVM_OBJ_ROOT ${LLVM_BINARY_DIR})
set(LLVM_CPPFLAGS "${CMAKE_CPP_FLAGS} ${CMAKE_CPP_FLAGS_${uppercase_CMAKE_BUILD_TYPE}} ${LLVM_DEFINITIONS}")
set(LLVM_CFLAGS "${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_${uppercase_CMAKE_BUILD_TYPE}} ${LLVM_DEFINITIONS}")
set(LLVM_CXXFLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_${uppercase_CMAKE_BUILD_TYPE}} ${COMPILE_FLAGS} ${LLVM_DEFINITIONS}")
+set(LLVM_BUILD_SYSTEM cmake)
+set(LLVM_HAS_RTTI ${LLVM_CONFIG_HAS_RTTI})
+
# Use the C++ link flags, since they should be a superset of C link flags.
set(LLVM_LDFLAGS "${CMAKE_CXX_LINK_FLAGS}")
set(LLVM_BUILDMODE ${CMAKE_BUILD_TYPE})
set(LLVM_SYSTEM_LIBS ${SYSTEM_LIBS})
+if(BUILD_SHARED_LIBS)
+ set(LLVM_ENABLE_SHARED ON)
+else()
+ set(LLVM_ENABLE_SHARED OFF)
+endif()
string(REPLACE ";" " " LLVM_TARGETS_BUILT "${LLVM_TARGETS_TO_BUILD}")
configure_file(${BUILDVARIABLES_SRCPATH} ${BUILDVARIABLES_OBJPATH} @ONLY)
diff --git a/tools/llvm-config/Makefile b/tools/llvm-config/Makefile
index 1ff8b6f04063..d2fe2cfa34aa 100644
--- a/tools/llvm-config/Makefile
+++ b/tools/llvm-config/Makefile
@@ -38,6 +38,12 @@ else
LLVM_SYSTEM_LIBS := $(LIBS)
endif
+ifneq ($(REQUIRES_RTTI), 1)
+ LLVM_HAS_RTTI := NO
+else
+ LLVM_HAS_RTTI := YES
+endif
+
# This is blank for now. We need to be careful about adding stuff here:
# LDFLAGS tend not to be portable, and we don't currently require the
# user to use libtool when linking against LLVM.
@@ -65,6 +71,18 @@ $(ObjDir)/BuildVariables.inc: $(BUILDVARIABLES_SRCPATH) Makefile $(ObjDir)/.dir
>> temp.sed
$(Verb) $(ECHO) 's/@LLVM_TARGETS_BUILT@/$(subst /,\/,$(TARGETS_TO_BUILD))/' \
>> temp.sed
+ $(if $(filter-out $(ENABLE_SHARED),0),\
+ $(Verb) $(ECHO) 's/@LLVM_BUILD_LLVM_DYLIB@/ON/',\
+ $(Verb) $(ECHO) 's/@LLVM_BUILD_LLVM_DYLIB@/OFF/') \
+ >> temp.sed
+ $(Verb) $(ECHO) 's/@LLVM_ENABLE_SHARED@/OFF/' \
+ >> temp.sed
+ $(Verb) $(ECHO) 's/@LLVM_DYLIB_COMPONENTS@/all/' \
+ >> temp.sed
+ $(Verb) $(ECHO) 's/@LLVM_BUILD_SYSTEM@/autoconf/' \
+ >> temp.sed
+ $(Verb) $(ECHO) 's/@LLVM_HAS_RTTI@/$(LLVM_HAS_RTTI)/' \
+ >> temp.sed
$(Verb) $(SED) -f temp.sed < $< > $@
$(Verb) $(RM) temp.sed
diff --git a/tools/llvm-config/llvm-config.cpp b/tools/llvm-config/llvm-config.cpp
index 879b9ab0945b..80f627936d0c 100644
--- a/tools/llvm-config/llvm-config.cpp
+++ b/tools/llvm-config/llvm-config.cpp
@@ -30,6 +30,7 @@
#include <cstdlib>
#include <set>
#include <vector>
+#include <unordered_set>
using namespace llvm;
@@ -51,12 +52,16 @@ using namespace llvm;
/// \param Name - The component to traverse.
/// \param ComponentMap - A prebuilt map of component names to descriptors.
/// \param VisitedComponents [in] [out] - The set of already visited components.
-/// \param RequiredLibs [out] - The ordered list of required libraries.
-static void VisitComponent(StringRef Name,
+/// \param RequiredLibs [out] - The ordered list of required
+/// libraries.
+/// \param GetComponentNames - Get the component names instead of the
+/// library name.
+static void VisitComponent(const std::string& Name,
const StringMap<AvailableComponent*> &ComponentMap,
std::set<AvailableComponent*> &VisitedComponents,
- std::vector<StringRef> &RequiredLibs,
- bool IncludeNonInstalled) {
+ std::vector<std::string> &RequiredLibs,
+ bool IncludeNonInstalled, bool GetComponentNames,
+ const std::string *ActiveLibDir, bool *HasMissing) {
// Lookup the component.
AvailableComponent *AC = ComponentMap.lookup(Name);
assert(AC && "Invalid component name!");
@@ -74,12 +79,22 @@ static void VisitComponent(StringRef Name,
// Otherwise, visit all the dependencies.
for (unsigned i = 0; AC->RequiredLibraries[i]; ++i) {
VisitComponent(AC->RequiredLibraries[i], ComponentMap, VisitedComponents,
- RequiredLibs, IncludeNonInstalled);
+ RequiredLibs, IncludeNonInstalled, GetComponentNames,
+ ActiveLibDir, HasMissing);
+ }
+
+ if (GetComponentNames) {
+ RequiredLibs.push_back(Name);
+ return;
}
// Add to the required library list.
- if (AC->Library)
+ if (AC->Library) {
+ if (!IncludeNonInstalled && HasMissing && !*HasMissing && ActiveLibDir) {
+ *HasMissing = !sys::fs::exists(*ActiveLibDir + "/" + AC->Library);
+ }
RequiredLibs.push_back(AC->Library);
+ }
}
/// \brief Compute the list of required libraries for a given list of
@@ -87,14 +102,15 @@ static void VisitComponent(StringRef Name,
/// appear prior to their dependencies).
///
/// \param Components - The names of the components to find libraries for.
-/// \param RequiredLibs [out] - On return, the ordered list of libraries that
-/// are required to link the given components.
/// \param IncludeNonInstalled - Whether non-installed components should be
/// reported.
-static void ComputeLibsForComponents(const std::vector<StringRef> &Components,
- std::vector<StringRef> &RequiredLibs,
- bool IncludeNonInstalled) {
- std::set<AvailableComponent*> VisitedComponents;
+/// \param GetComponentNames - True if one would prefer the component names.
+static std::vector<std::string>
+ComputeLibsForComponents(const std::vector<StringRef> &Components,
+ bool IncludeNonInstalled, bool GetComponentNames,
+ const std::string *ActiveLibDir, bool *HasMissing) {
+ std::vector<std::string> RequiredLibs;
+ std::set<AvailableComponent *> VisitedComponents;
// Build a map of component names to information.
StringMap<AvailableComponent*> ComponentMap;
@@ -116,12 +132,15 @@ static void ComputeLibsForComponents(const std::vector<StringRef> &Components,
}
VisitComponent(ComponentLower, ComponentMap, VisitedComponents,
- RequiredLibs, IncludeNonInstalled);
+ RequiredLibs, IncludeNonInstalled, GetComponentNames,
+ ActiveLibDir, HasMissing);
}
// The list is now ordered with leafs first, we want the libraries to printed
// in the reverse order of dependency.
std::reverse(RequiredLibs.begin(), RequiredLibs.end());
+
+ return RequiredLibs;
}
/* *** */
@@ -157,6 +176,9 @@ Options:\n\
--host-target Target triple used to configure LLVM.\n\
--build-mode Print build mode of LLVM tree (e.g. Debug or Release).\n\
--assertion-mode Print assertion mode of LLVM tree (ON or OFF).\n\
+ --build-system Print the build system used to build LLVM (autoconf or cmake).\n\
+ --has-rtti Print whether or not LLVM was built with rtti (YES or NO).\n\
+ --shared-mode Print how the provided components can be collectively linked (`shared` or `static`).\n\
Typical components:\n\
all All LLVM libraries (default).\n\
engine Either a native JIT or a bitcode interpreter.\n";
@@ -171,10 +193,34 @@ std::string GetExecutablePath(const char *Argv0) {
return llvm::sys::fs::getMainExecutable(Argv0, P);
}
+/// \brief Expand the semi-colon delimited LLVM_DYLIB_COMPONENTS into
+/// the full list of components.
+std::vector<std::string> GetAllDyLibComponents(const bool IsInDevelopmentTree,
+ const bool GetComponentNames) {
+ std::vector<StringRef> DyLibComponents;
+
+ StringRef DyLibComponentsStr(LLVM_DYLIB_COMPONENTS);
+ size_t Offset = 0;
+ while (true) {
+ const size_t NextOffset = DyLibComponentsStr.find(';', Offset);
+ DyLibComponents.push_back(DyLibComponentsStr.substr(Offset, NextOffset));
+ if (NextOffset == std::string::npos) {
+ break;
+ }
+ Offset = NextOffset + 1;
+ }
+
+ assert(!DyLibComponents.empty());
+
+ return ComputeLibsForComponents(DyLibComponents,
+ /*IncludeNonInstalled=*/IsInDevelopmentTree,
+ GetComponentNames, nullptr, nullptr);
+}
+
int main(int argc, char **argv) {
std::vector<StringRef> Components;
bool PrintLibs = false, PrintLibNames = false, PrintLibFiles = false;
- bool PrintSystemLibs = false;
+ bool PrintSystemLibs = false, PrintSharedMode = false;
bool HasAnyOption = false;
// llvm-config is designed to support being run both from a development tree
@@ -269,6 +315,108 @@ int main(int argc, char **argv) {
ActiveIncludeOption = "-I" + ActiveIncludeDir;
}
+ /// We only use `shared library` mode in cases where the static library form
+ /// of the components provided are not available; note however that this is
+ /// skipped if we're run from within the build dir. However, once installed,
+ /// we still need to provide correct output when the static archives are
+ /// removed or, as in the case of CMake's `BUILD_SHARED_LIBS`, never present
+ /// in the first place. This can't be done at configure/build time.
+
+ StringRef SharedExt, SharedVersionedExt, SharedDir, SharedPrefix, StaticExt,
+ StaticPrefix, StaticDir = "lib";
+ const Triple HostTriple(Triple::normalize(LLVM_DEFAULT_TARGET_TRIPLE));
+ if (HostTriple.isOSWindows()) {
+ SharedExt = "dll";
+ SharedVersionedExt = PACKAGE_VERSION ".dll";
+ StaticExt = "a";
+ SharedDir = ActiveBinDir;
+ StaticDir = ActiveLibDir;
+ StaticPrefix = SharedPrefix = "lib";
+ } else if (HostTriple.isOSDarwin()) {
+ SharedExt = "dylib";
+ SharedVersionedExt = PACKAGE_VERSION ".dylib";
+ StaticExt = "a";
+ StaticDir = SharedDir = ActiveLibDir;
+ StaticPrefix = SharedPrefix = "lib";
+ } else {
+ // default to the unix values:
+ SharedExt = "so";
+ SharedVersionedExt = PACKAGE_VERSION ".so";
+ StaticExt = "a";
+ StaticDir = SharedDir = ActiveLibDir;
+ StaticPrefix = SharedPrefix = "lib";
+ }
+
+ const bool BuiltDyLib = (std::strcmp(LLVM_ENABLE_DYLIB, "ON") == 0);
+
+ enum { CMake, AutoConf } ConfigTool;
+ if (std::strcmp(LLVM_BUILD_SYSTEM, "cmake") == 0) {
+ ConfigTool = CMake;
+ } else {
+ ConfigTool = AutoConf;
+ }
+
+ /// CMake style shared libs, ie each component is in a shared library.
+ const bool BuiltSharedLibs =
+ (ConfigTool == CMake && std::strcmp(LLVM_ENABLE_SHARED, "ON") == 0);
+
+ bool DyLibExists = false;
+ const std::string DyLibName =
+ (SharedPrefix + "LLVM-" + SharedVersionedExt).str();
+
+ if (BuiltDyLib) {
+ DyLibExists = sys::fs::exists(SharedDir + "/" + DyLibName);
+ }
+
+ /// Get the component's library name without the lib prefix and the
+ /// extension. Returns true if Lib is in a recognized format.
+ auto GetComponentLibraryNameSlice = [&](const StringRef &Lib,
+ StringRef &Out) {
+ if (Lib.startswith("lib")) {
+ unsigned FromEnd;
+ if (Lib.endswith(StaticExt)) {
+ FromEnd = StaticExt.size() + 1;
+ } else if (Lib.endswith(SharedExt)) {
+ FromEnd = SharedExt.size() + 1;
+ } else {
+ FromEnd = 0;
+ }
+
+ if (FromEnd != 0) {
+ Out = Lib.slice(3, Lib.size() - FromEnd);
+ return true;
+ }
+ }
+
+ return false;
+ };
+ /// Maps Unixizms to the host platform.
+ auto GetComponentLibraryFileName = [&](const StringRef &Lib,
+ const bool ForceShared) {
+ std::string LibFileName = Lib;
+ StringRef LibName;
+ if (GetComponentLibraryNameSlice(Lib, LibName)) {
+ if (BuiltSharedLibs || ForceShared) {
+ LibFileName = (SharedPrefix + LibName + "." + SharedExt).str();
+ } else {
+ // default to static
+ LibFileName = (StaticPrefix + LibName + "." + StaticExt).str();
+ }
+ }
+
+ return LibFileName;
+ };
+ /// Get the full path for a possibly shared component library.
+ auto GetComponentLibraryPath = [&](const StringRef &Name,
+ const bool ForceShared) {
+ auto LibFileName = GetComponentLibraryFileName(Name, ForceShared);
+ if (BuiltSharedLibs || ForceShared) {
+ return (SharedDir + "/" + LibFileName).str();
+ } else {
+ return (StaticDir + "/" + LibFileName).str();
+ }
+ };
+
raw_ostream &OS = outs();
for (int i = 1; i != argc; ++i) {
StringRef Arg = argv[i];
@@ -302,13 +450,33 @@ int main(int argc, char **argv) {
} else if (Arg == "--libfiles") {
PrintLibFiles = true;
} else if (Arg == "--components") {
+ /// If there are missing static archives and a dylib was
+ /// built, print LLVM_DYLIB_COMPONENTS instead of everything
+ /// in the manifest.
+ std::vector<std::string> Components;
for (unsigned j = 0; j != array_lengthof(AvailableComponents); ++j) {
// Only include non-installed components when in a development tree.
if (!AvailableComponents[j].IsInstalled && !IsInDevelopmentTree)
continue;
- OS << ' ';
- OS << AvailableComponents[j].Name;
+ Components.push_back(AvailableComponents[j].Name);
+ if (AvailableComponents[j].Library && !IsInDevelopmentTree) {
+ if (DyLibExists &&
+ !sys::fs::exists(GetComponentLibraryPath(
+ AvailableComponents[j].Library, false))) {
+ Components = GetAllDyLibComponents(IsInDevelopmentTree, true);
+ std::sort(Components.begin(), Components.end());
+ break;
+ }
+ }
+ }
+
+ for (unsigned I = 0; I < Components.size(); ++I) {
+ if (I) {
+ OS << ' ';
+ }
+
+ OS << Components[I];
}
OS << '\n';
} else if (Arg == "--targets-built") {
@@ -323,6 +491,12 @@ int main(int argc, char **argv) {
#else
OS << "ON\n";
#endif
+ } else if (Arg == "--build-system") {
+ OS << LLVM_BUILD_SYSTEM << '\n';
+ } else if (Arg == "--has-rtti") {
+ OS << LLVM_HAS_RTTI << '\n';
+ } else if (Arg == "--shared-mode") {
+ PrintSharedMode = true;
} else if (Arg == "--obj-root") {
OS << ActivePrefix << '\n';
} else if (Arg == "--src-root") {
@@ -338,35 +512,84 @@ int main(int argc, char **argv) {
if (!HasAnyOption)
usage();
- if (PrintLibs || PrintLibNames || PrintLibFiles || PrintSystemLibs) {
+ if (PrintLibs || PrintLibNames || PrintLibFiles || PrintSystemLibs ||
+ PrintSharedMode) {
+
+ if (PrintSharedMode && BuiltSharedLibs) {
+ OS << "shared\n";
+ return 0;
+ }
+
// If no components were specified, default to "all".
if (Components.empty())
Components.push_back("all");
// Construct the list of all the required libraries.
- std::vector<StringRef> RequiredLibs;
- ComputeLibsForComponents(Components, RequiredLibs,
- /*IncludeNonInstalled=*/IsInDevelopmentTree);
+ bool HasMissing = false;
+ std::vector<std::string> RequiredLibs =
+ ComputeLibsForComponents(Components,
+ /*IncludeNonInstalled=*/IsInDevelopmentTree,
+ false, &ActiveLibDir, &HasMissing);
+
+ if (PrintSharedMode) {
+ std::unordered_set<std::string> FullDyLibComponents;
+ std::vector<std::string> DyLibComponents =
+ GetAllDyLibComponents(IsInDevelopmentTree, false);
+
+ for (auto &Component : DyLibComponents) {
+ FullDyLibComponents.insert(Component);
+ }
+ DyLibComponents.clear();
+
+ for (auto &Lib : RequiredLibs) {
+ if (!FullDyLibComponents.count(Lib)) {
+ OS << "static\n";
+ return 0;
+ }
+ }
+ FullDyLibComponents.clear();
+
+ if (HasMissing && DyLibExists) {
+ OS << "shared\n";
+ return 0;
+ } else {
+ OS << "static\n";
+ return 0;
+ }
+ }
if (PrintLibs || PrintLibNames || PrintLibFiles) {
- for (unsigned i = 0, e = RequiredLibs.size(); i != e; ++i) {
- StringRef Lib = RequiredLibs[i];
- if (i)
- OS << ' ';
+ auto PrintForLib = [&](const StringRef &Lib, const bool ForceShared) {
if (PrintLibNames) {
- OS << Lib;
+ OS << GetComponentLibraryFileName(Lib, ForceShared);
} else if (PrintLibFiles) {
- OS << ActiveLibDir << '/' << Lib;
+ OS << GetComponentLibraryPath(Lib, ForceShared);
} else if (PrintLibs) {
// If this is a typical library name, include it using -l.
- if (Lib.startswith("lib") && Lib.endswith(".a")) {
- OS << "-l" << Lib.slice(3, Lib.size()-2);
- continue;
+ StringRef LibName;
+ if (Lib.startswith("lib")) {
+ if (GetComponentLibraryNameSlice(Lib, LibName)) {
+ OS << "-l" << LibName;
+ } else {
+ OS << "-l:" << GetComponentLibraryFileName(Lib, ForceShared);
+ }
+ } else {
+ // Otherwise, print the full path.
+ OS << GetComponentLibraryPath(Lib, ForceShared);
}
+ }
+ };
+
+ if (HasMissing && DyLibExists) {
+ PrintForLib(DyLibName, true);
+ } else {
+ for (unsigned i = 0, e = RequiredLibs.size(); i != e; ++i) {
+ auto Lib = RequiredLibs[i];
+ if (i)
+ OS << ' ';
- // Otherwise, print the full path.
- OS << ActiveLibDir << '/' << Lib;
+ PrintForLib(Lib, false);
}
}
OS << '\n';
diff --git a/tools/llvm-cov/CoverageReport.cpp b/tools/llvm-cov/CoverageReport.cpp
index 497c2f88f268..ed01a2e154f1 100644
--- a/tools/llvm-cov/CoverageReport.cpp
+++ b/tools/llvm-cov/CoverageReport.cpp
@@ -20,7 +20,7 @@ using namespace llvm;
namespace {
/// \brief Helper struct which prints trimmed and aligned columns.
struct Column {
- enum TrimKind { NoTrim, LeftTrim, RightTrim };
+ enum TrimKind { NoTrim, WidthTrim, LeftTrim, RightTrim };
enum AlignmentKind { LeftAlignment, RightAlignment };
@@ -30,7 +30,7 @@ struct Column {
AlignmentKind Alignment;
Column(StringRef Str, unsigned Width)
- : Str(Str), Width(Width), Trim(NoTrim), Alignment(LeftAlignment) {}
+ : Str(Str), Width(Width), Trim(WidthTrim), Alignment(LeftAlignment) {}
Column &set(TrimKind Value) {
Trim = Value;
@@ -44,6 +44,7 @@ struct Column {
void render(raw_ostream &OS) const;
};
+
raw_ostream &operator<<(raw_ostream &OS, const Column &Value) {
Value.render(OS);
return OS;
@@ -64,6 +65,9 @@ void Column::render(raw_ostream &OS) const {
switch (Trim) {
case NoTrim:
+ OS << Str;
+ break;
+ case WidthTrim:
OS << Str.substr(0, Width);
break;
case LeftTrim:
@@ -84,8 +88,19 @@ static Column column(StringRef Str, unsigned Width, const T &Value) {
return Column(Str, Width).set(Value);
}
-static const unsigned FileReportColumns[] = {25, 10, 8, 8, 10, 10};
-static const unsigned FunctionReportColumns[] = {25, 10, 8, 8, 10, 8, 8};
+static size_t FileReportColumns[] = {25, 10, 8, 8, 10, 10};
+static size_t FunctionReportColumns[] = {25, 10, 8, 8, 10, 8, 8};
+
+/// \brief Adjust column widths to fit long file paths and function names.
+static void adjustColumnWidths(coverage::CoverageMapping *CM) {
+ for (StringRef Filename : CM->getUniqueSourceFiles()) {
+ FileReportColumns[0] = std::max(FileReportColumns[0], Filename.size());
+ for (const auto &F : CM->getCoveredFunctions(Filename)) {
+ FunctionReportColumns[0] =
+ std::max(FunctionReportColumns[0], F.Name.size());
+ }
+ }
+}
/// \brief Prints a horizontal divider which spans across the given columns.
template <typename T, size_t N>
@@ -108,8 +123,9 @@ static raw_ostream::Colors determineCoveragePercentageColor(const T &Info) {
}
void CoverageReport::render(const FileCoverageSummary &File, raw_ostream &OS) {
- OS << column(File.Name, FileReportColumns[0], Column::LeftTrim)
- << format("%*u", FileReportColumns[1], (unsigned)File.RegionCoverage.NumRegions);
+ OS << column(File.Name, FileReportColumns[0], Column::NoTrim)
+ << format("%*u", FileReportColumns[1],
+ (unsigned)File.RegionCoverage.NumRegions);
Options.colored_ostream(OS, File.RegionCoverage.isFullyCovered()
? raw_ostream::GREEN
: raw_ostream::RED)
@@ -157,6 +173,7 @@ void CoverageReport::render(const FunctionCoverageSummary &Function,
void CoverageReport::renderFunctionReports(ArrayRef<std::string> Files,
raw_ostream &OS) {
+ adjustColumnWidths(Coverage.get());
bool isFirst = true;
for (StringRef Filename : Files) {
if (isFirst)
@@ -191,6 +208,7 @@ void CoverageReport::renderFunctionReports(ArrayRef<std::string> Files,
}
void CoverageReport::renderFileReports(raw_ostream &OS) {
+ adjustColumnWidths(Coverage.get());
OS << column("Filename", FileReportColumns[0])
<< column("Regions", FileReportColumns[1], Column::RightAlignment)
<< column("Miss", FileReportColumns[2], Column::RightAlignment)
@@ -200,6 +218,7 @@ void CoverageReport::renderFileReports(raw_ostream &OS) {
<< "\n";
renderDivider(FileReportColumns, OS);
OS << "\n";
+
FileCoverageSummary Totals("TOTAL");
for (StringRef Filename : Coverage->getUniqueSourceFiles()) {
FileCoverageSummary Summary(Filename);
diff --git a/tools/llvm-cov/CoverageViewOptions.h b/tools/llvm-cov/CoverageViewOptions.h
index 94b55fe793fc..1208fad79176 100644
--- a/tools/llvm-cov/CoverageViewOptions.h
+++ b/tools/llvm-cov/CoverageViewOptions.h
@@ -24,6 +24,7 @@ struct CoverageViewOptions {
bool ShowLineStatsOrRegionMarkers;
bool ShowExpandedRegions;
bool ShowFunctionInstantiations;
+ bool ShowFullFilenames;
/// \brief Change the output's stream color if the colors are enabled.
ColoredRawOstream colored_ostream(raw_ostream &OS,
diff --git a/tools/llvm-cov/gcov.cpp b/tools/llvm-cov/gcov.cpp
index 4377a50b5566..a5343fa29afc 100644
--- a/tools/llvm-cov/gcov.cpp
+++ b/tools/llvm-cov/gcov.cpp
@@ -26,7 +26,7 @@ using namespace llvm;
static void reportCoverage(StringRef SourceFile, StringRef ObjectDir,
const std::string &InputGCNO,
const std::string &InputGCDA, bool DumpGCOV,
- const GCOVOptions &Options) {
+ const GCOV::Options &Options) {
SmallString<128> CoverageFileStem(ObjectDir);
if (CoverageFileStem.empty()) {
// If no directory was specified with -o, look next to the source file.
@@ -143,8 +143,8 @@ int gcovMain(int argc, const char *argv[]) {
cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n");
- GCOVOptions Options(AllBlocks, BranchProb, BranchCount, FuncSummary,
- PreservePaths, UncondBranch, LongNames, NoOutput);
+ GCOV::Options Options(AllBlocks, BranchProb, BranchCount, FuncSummary,
+ PreservePaths, UncondBranch, LongNames, NoOutput);
for (const auto &SourceFile : SourceFiles)
reportCoverage(SourceFile, ObjectDir, InputGCNO, InputGCDA, DumpGCOV,
diff --git a/tools/llvm-cxxdump/llvm-cxxdump.cpp b/tools/llvm-cxxdump/llvm-cxxdump.cpp
index 4e06be9e78b9..3dda69266a2d 100644
--- a/tools/llvm-cxxdump/llvm-cxxdump.cpp
+++ b/tools/llvm-cxxdump/llvm-cxxdump.cpp
@@ -40,18 +40,14 @@ cl::list<std::string> InputFilenames(cl::Positional,
cl::ZeroOrMore);
} // namespace opts
-static int ReturnValue = EXIT_SUCCESS;
-
namespace llvm {
-static bool error(std::error_code EC) {
+static void error(std::error_code EC) {
if (!EC)
- return false;
-
- ReturnValue = EXIT_FAILURE;
+ return;
outs() << "\nError reading file: " << EC.message() << ".\n";
outs().flush();
- return true;
+ exit(1);
}
} // namespace llvm
@@ -59,38 +55,24 @@ static bool error(std::error_code EC) {
static void reportError(StringRef Input, StringRef Message) {
if (Input == "-")
Input = "<stdin>";
-
errs() << Input << ": " << Message << "\n";
errs().flush();
- ReturnValue = EXIT_FAILURE;
+ exit(1);
}
static void reportError(StringRef Input, std::error_code EC) {
reportError(Input, EC.message());
}
-static SmallVectorImpl<SectionRef> &getRelocSections(const ObjectFile *Obj,
- const SectionRef &Sec) {
- static bool MappingDone = false;
- static std::map<SectionRef, SmallVector<SectionRef, 1>> SectionRelocMap;
- if (!MappingDone) {
- for (const SectionRef &Section : Obj->sections()) {
- section_iterator Sec2 = Section.getRelocatedSection();
- if (Sec2 != Obj->section_end())
- SectionRelocMap[*Sec2].push_back(Section);
- }
- MappingDone = true;
- }
- return SectionRelocMap[Sec];
-}
+static std::map<SectionRef, SmallVector<SectionRef, 1>> SectionRelocMap;
-static bool collectRelocatedSymbols(const ObjectFile *Obj,
+static void collectRelocatedSymbols(const ObjectFile *Obj,
const SectionRef &Sec, uint64_t SecAddress,
uint64_t SymAddress, uint64_t SymSize,
StringRef *I, StringRef *E) {
uint64_t SymOffset = SymAddress - SecAddress;
uint64_t SymEnd = SymOffset + SymSize;
- for (const SectionRef &SR : getRelocSections(Obj, Sec)) {
+ for (const SectionRef &SR : SectionRelocMap[Sec]) {
for (const object::RelocationRef &Reloc : SR.relocations()) {
if (I == E)
break;
@@ -98,8 +80,7 @@ static bool collectRelocatedSymbols(const ObjectFile *Obj,
if (RelocSymI == Obj->symbol_end())
continue;
ErrorOr<StringRef> RelocSymName = RelocSymI->getName();
- if (error(RelocSymName.getError()))
- return true;
+ error(RelocSymName.getError());
uint64_t Offset = Reloc.getOffset();
if (Offset >= SymOffset && Offset < SymEnd) {
*I = *RelocSymName;
@@ -107,29 +88,26 @@ static bool collectRelocatedSymbols(const ObjectFile *Obj,
}
}
}
- return false;
}
-static bool collectRelocationOffsets(
+static void collectRelocationOffsets(
const ObjectFile *Obj, const SectionRef &Sec, uint64_t SecAddress,
uint64_t SymAddress, uint64_t SymSize, StringRef SymName,
std::map<std::pair<StringRef, uint64_t>, StringRef> &Collection) {
uint64_t SymOffset = SymAddress - SecAddress;
uint64_t SymEnd = SymOffset + SymSize;
- for (const SectionRef &SR : getRelocSections(Obj, Sec)) {
+ for (const SectionRef &SR : SectionRelocMap[Sec]) {
for (const object::RelocationRef &Reloc : SR.relocations()) {
const object::symbol_iterator RelocSymI = Reloc.getSymbol();
if (RelocSymI == Obj->symbol_end())
continue;
ErrorOr<StringRef> RelocSymName = RelocSymI->getName();
- if (error(RelocSymName.getError()))
- return true;
+ error(RelocSymName.getError());
uint64_t Offset = Reloc.getOffset();
if (Offset >= SymOffset && Offset < SymEnd)
Collection[std::make_pair(SymName, Offset - SymOffset)] = *RelocSymName;
}
}
- return false;
}
static void dumpCXXData(const ObjectFile *Obj) {
@@ -182,6 +160,13 @@ static void dumpCXXData(const ObjectFile *Obj) {
std::map<std::pair<StringRef, uint64_t>, StringRef> VTTEntries;
std::map<StringRef, StringRef> TINames;
+ SectionRelocMap.clear();
+ for (const SectionRef &Section : Obj->sections()) {
+ section_iterator Sec2 = Section.getRelocatedSection();
+ if (Sec2 != Obj->section_end())
+ SectionRelocMap[*Sec2].push_back(Section);
+ }
+
uint8_t BytesInAddress = Obj->getBytesInAddress();
std::vector<std::pair<SymbolRef, uint64_t>> SymAddr =
@@ -191,12 +176,11 @@ static void dumpCXXData(const ObjectFile *Obj) {
object::SymbolRef Sym = P.first;
uint64_t SymSize = P.second;
ErrorOr<StringRef> SymNameOrErr = Sym.getName();
- if (error(SymNameOrErr.getError()))
- return;
+ error(SymNameOrErr.getError());
StringRef SymName = *SymNameOrErr;
- object::section_iterator SecI(Obj->section_begin());
- if (error(Sym.getSection(SecI)))
- return;
+ ErrorOr<object::section_iterator> SecIOrErr = Sym.getSection();
+ error(SecIOrErr.getError());
+ object::section_iterator SecI = *SecIOrErr;
// Skip external symbols.
if (SecI == Obj->section_end())
continue;
@@ -205,11 +189,9 @@ static void dumpCXXData(const ObjectFile *Obj) {
if (Sec.isBSS() || Sec.isVirtual())
continue;
StringRef SecContents;
- if (error(Sec.getContents(SecContents)))
- return;
+ error(Sec.getContents(SecContents));
ErrorOr<uint64_t> SymAddressOrErr = Sym.getAddress();
- if (error(SymAddressOrErr.getError()))
- return;
+ error(SymAddressOrErr.getError());
uint64_t SymAddress = *SymAddressOrErr;
uint64_t SecAddress = Sec.getAddress();
uint64_t SecSize = Sec.getSize();
@@ -236,23 +218,19 @@ static void dumpCXXData(const ObjectFile *Obj) {
// Complete object locators in the MS-ABI start with '??_R4'
else if (SymName.startswith("??_R4")) {
CompleteObjectLocator COL;
- COL.Data = ArrayRef<little32_t>(
+ COL.Data = makeArrayRef(
reinterpret_cast<const little32_t *>(SymContents.data()), 3);
StringRef *I = std::begin(COL.Symbols), *E = std::end(COL.Symbols);
- if (collectRelocatedSymbols(Obj, Sec, SecAddress, SymAddress, SymSize, I,
- E))
- return;
+ collectRelocatedSymbols(Obj, Sec, SecAddress, SymAddress, SymSize, I, E);
COLs[SymName] = COL;
}
// Class hierarchy descriptors in the MS-ABI start with '??_R3'
else if (SymName.startswith("??_R3")) {
ClassHierarchyDescriptor CHD;
- CHD.Data = ArrayRef<little32_t>(
+ CHD.Data = makeArrayRef(
reinterpret_cast<const little32_t *>(SymContents.data()), 3);
StringRef *I = std::begin(CHD.Symbols), *E = std::end(CHD.Symbols);
- if (collectRelocatedSymbols(Obj, Sec, SecAddress, SymAddress, SymSize, I,
- E))
- return;
+ collectRelocatedSymbols(Obj, Sec, SecAddress, SymAddress, SymSize, I, E);
CHDs[SymName] = CHD;
}
// Class hierarchy descriptors in the MS-ABI start with '??_R2'
@@ -265,12 +243,10 @@ static void dumpCXXData(const ObjectFile *Obj) {
// Base class descriptors in the MS-ABI start with '??_R1'
else if (SymName.startswith("??_R1")) {
BaseClassDescriptor BCD;
- BCD.Data = ArrayRef<little32_t>(
+ BCD.Data = makeArrayRef(
reinterpret_cast<const little32_t *>(SymContents.data()) + 1, 5);
StringRef *I = std::begin(BCD.Symbols), *E = std::end(BCD.Symbols);
- if (collectRelocatedSymbols(Obj, Sec, SecAddress, SymAddress, SymSize, I,
- E))
- return;
+ collectRelocatedSymbols(Obj, Sec, SecAddress, SymAddress, SymSize, I, E);
BCDs[SymName] = BCD;
}
// Type descriptors in the MS-ABI start with '??_R0'
@@ -283,9 +259,7 @@ static void dumpCXXData(const ObjectFile *Obj) {
TD.AlwaysZero = *reinterpret_cast<const little32_t *>(DataPtr);
TD.MangledName = SymContents.drop_front(BytesInAddress * 2);
StringRef *I = std::begin(TD.Symbols), *E = std::end(TD.Symbols);
- if (collectRelocatedSymbols(Obj, Sec, SecAddress, SymAddress, SymSize, I,
- E))
- return;
+ collectRelocatedSymbols(Obj, Sec, SecAddress, SymAddress, SymSize, I, E);
TDs[SymName] = TD;
}
// Throw descriptors in the MS-ABI start with '_TI'
@@ -316,9 +290,7 @@ static void dumpCXXData(const ObjectFile *Obj) {
CT.VirtualBaseAdjustmentOffset = DataPtr[4];
CT.Size = DataPtr[5];
StringRef *I = std::begin(CT.Symbols), *E = std::end(CT.Symbols);
- if (collectRelocatedSymbols(Obj, Sec, SecAddress, SymAddress, SymSize, I,
- E))
- return;
+ collectRelocatedSymbols(Obj, Sec, SecAddress, SymAddress, SymSize, I, E);
CTs[SymName] = CT;
}
// Construction vtables in the Itanium ABI start with '_ZTT' or '__ZTT'.
@@ -510,7 +482,9 @@ static void dumpCXXData(const ObjectFile *Obj) {
}
static void dumpArchive(const Archive *Arc) {
- for (const Archive::Child &ArcC : Arc->children()) {
+ for (auto &ErrorOrChild : Arc->children()) {
+ error(ErrorOrChild.getError());
+ const Archive::Child &ArcC = *ErrorOrChild;
ErrorOr<std::unique_ptr<Binary>> ChildOrErr = ArcC.getAsBinary();
if (std::error_code EC = ChildOrErr.getError()) {
// Ignore non-object files.
@@ -527,12 +501,6 @@ static void dumpArchive(const Archive *Arc) {
}
static void dumpInput(StringRef File) {
- // If file isn't stdin, check that it exists.
- if (File != "-" && !sys::fs::exists(File)) {
- reportError(File, cxxdump_error::file_not_found);
- return;
- }
-
// Attempt to open the binary.
ErrorOr<OwningBinary<Binary>> BinaryOrErr = createBinary(File);
if (std::error_code EC = BinaryOrErr.getError()) {
@@ -569,5 +537,5 @@ int main(int argc, const char *argv[]) {
std::for_each(opts::InputFilenames.begin(), opts::InputFilenames.end(),
dumpInput);
- return ReturnValue;
+ return EXIT_SUCCESS;
}
diff --git a/tools/llvm-diff/DiffLog.cpp b/tools/llvm-diff/DiffLog.cpp
index 24a1b08d545d..ed86058f1af1 100644
--- a/tools/llvm-diff/DiffLog.cpp
+++ b/tools/llvm-diff/DiffLog.cpp
@@ -20,7 +20,8 @@
using namespace llvm;
LogBuilder::~LogBuilder() {
- consumer.logf(*this);
+ if (consumer)
+ consumer->logf(*this);
}
StringRef LogBuilder::getFormat() const { return Format; }
diff --git a/tools/llvm-diff/DiffLog.h b/tools/llvm-diff/DiffLog.h
index 8eb53ffffccf..8f28461afdde 100644
--- a/tools/llvm-diff/DiffLog.h
+++ b/tools/llvm-diff/DiffLog.h
@@ -27,7 +27,7 @@ namespace llvm {
/// A temporary-object class for building up log messages.
class LogBuilder {
- Consumer &consumer;
+ Consumer *consumer;
/// The use of a stored StringRef here is okay because
/// LogBuilder should be used only as a temporary, and as a
@@ -38,8 +38,12 @@ namespace llvm {
SmallVector<Value*, 4> Arguments;
public:
- LogBuilder(Consumer &c, StringRef Format)
- : consumer(c), Format(Format) {}
+ LogBuilder(Consumer &c, StringRef Format) : consumer(&c), Format(Format) {}
+ LogBuilder(LogBuilder &&L)
+ : consumer(L.consumer), Format(L.Format),
+ Arguments(std::move(L.Arguments)) {
+ L.consumer = nullptr;
+ }
LogBuilder &operator<<(Value *V) {
Arguments.push_back(V);
diff --git a/tools/llvm-diff/DifferenceEngine.cpp b/tools/llvm-diff/DifferenceEngine.cpp
index 7d379ef5dc41..456560b093ab 100644
--- a/tools/llvm-diff/DifferenceEngine.cpp
+++ b/tools/llvm-diff/DifferenceEngine.cpp
@@ -599,7 +599,7 @@ void FunctionDifferenceEngine::runBlockDiff(BasicBlock::iterator LStart,
TerminatorInst *RTerm = RStart->getParent()->getTerminator();
if (isa<BranchInst>(LTerm) && isa<InvokeInst>(RTerm)) {
if (cast<BranchInst>(LTerm)->isConditional()) return;
- BasicBlock::iterator I = LTerm;
+ BasicBlock::iterator I = LTerm->getIterator();
if (I == LStart->getParent()->begin()) return;
--I;
if (!isa<CallInst>(*I)) return;
@@ -612,7 +612,7 @@ void FunctionDifferenceEngine::runBlockDiff(BasicBlock::iterator LStart,
tryUnify(LTerm->getSuccessor(0), RInvoke->getNormalDest());
} else if (isa<InvokeInst>(LTerm) && isa<BranchInst>(RTerm)) {
if (cast<BranchInst>(RTerm)->isConditional()) return;
- BasicBlock::iterator I = RTerm;
+ BasicBlock::iterator I = RTerm->getIterator();
if (I == RStart->getParent()->begin()) return;
--I;
if (!isa<CallInst>(*I)) return;
diff --git a/tools/llvm-dis/llvm-dis.cpp b/tools/llvm-dis/llvm-dis.cpp
index 4b7d94d5b261..9fdfcd4256c9 100644
--- a/tools/llvm-dis/llvm-dis.cpp
+++ b/tools/llvm-dis/llvm-dis.cpp
@@ -159,7 +159,7 @@ int main(int argc, char **argv) {
ErrorOr<std::unique_ptr<Module>> MOrErr =
getStreamedBitcodeModule(DisplayFilename, std::move(Streamer), Context);
M = std::move(*MOrErr);
- M->materializeAllPermanently();
+ M->materializeAll();
} else {
errs() << argv[0] << ": " << ErrorMessage << '\n';
return 1;
diff --git a/tools/llvm-dwarfdump/llvm-dwarfdump.cpp b/tools/llvm-dwarfdump/llvm-dwarfdump.cpp
index db3fcf6ee7bd..eaacc7c5f21b 100644
--- a/tools/llvm-dwarfdump/llvm-dwarfdump.cpp
+++ b/tools/llvm-dwarfdump/llvm-dwarfdump.cpp
@@ -15,6 +15,7 @@
#include "llvm/ADT/Triple.h"
#include "llvm/DebugInfo/DIContext.h"
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
+#include "llvm/Object/MachOUniversal.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Object/RelocVisitor.h"
#include "llvm/Support/CommandLine.h"
@@ -22,6 +23,7 @@
#include "llvm/Support/Format.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/raw_ostream.h"
@@ -35,19 +37,19 @@ using namespace llvm;
using namespace object;
static cl::list<std::string>
-InputFilenames(cl::Positional, cl::desc("<input object files>"),
+InputFilenames(cl::Positional, cl::desc("<input object files or .dSYM bundles>"),
cl::ZeroOrMore);
-static cl::opt<DIDumpType>
-DumpType("debug-dump", cl::init(DIDT_All),
- cl::desc("Dump of debug sections:"),
- cl::values(
+static cl::opt<DIDumpType> DumpType(
+ "debug-dump", cl::init(DIDT_All), cl::desc("Dump of debug sections:"),
+ cl::values(
clEnumValN(DIDT_All, "all", "Dump all debug sections"),
clEnumValN(DIDT_Abbrev, "abbrev", ".debug_abbrev"),
clEnumValN(DIDT_AbbrevDwo, "abbrev.dwo", ".debug_abbrev.dwo"),
clEnumValN(DIDT_AppleNames, "apple_names", ".apple_names"),
clEnumValN(DIDT_AppleTypes, "apple_types", ".apple_types"),
- clEnumValN(DIDT_AppleNamespaces, "apple_namespaces", ".apple_namespaces"),
+ clEnumValN(DIDT_AppleNamespaces, "apple_namespaces",
+ ".apple_namespaces"),
clEnumValN(DIDT_AppleObjC, "apple_objc", ".apple_objc"),
clEnumValN(DIDT_Aranges, "aranges", ".debug_aranges"),
clEnumValN(DIDT_Info, "info", ".debug_info"),
@@ -59,6 +61,7 @@ DumpType("debug-dump", cl::init(DIDT_All),
clEnumValN(DIDT_Loc, "loc", ".debug_loc"),
clEnumValN(DIDT_LocDwo, "loc.dwo", ".debug_loc.dwo"),
clEnumValN(DIDT_Frames, "frames", ".debug_frame"),
+ clEnumValN(DIDT_Macro, "macro", ".debug_macinfo"),
clEnumValN(DIDT_Ranges, "ranges", ".debug_ranges"),
clEnumValN(DIDT_Pubnames, "pubnames", ".debug_pubnames"),
clEnumValN(DIDT_Pubtypes, "pubtypes", ".debug_pubtypes"),
@@ -66,38 +69,79 @@ DumpType("debug-dump", cl::init(DIDT_All),
clEnumValN(DIDT_GnuPubtypes, "gnu_pubtypes", ".debug_gnu_pubtypes"),
clEnumValN(DIDT_Str, "str", ".debug_str"),
clEnumValN(DIDT_StrDwo, "str.dwo", ".debug_str.dwo"),
- clEnumValN(DIDT_StrOffsetsDwo, "str_offsets.dwo", ".debug_str_offsets.dwo"),
- clEnumValEnd));
+ clEnumValN(DIDT_StrOffsetsDwo, "str_offsets.dwo",
+ ".debug_str_offsets.dwo"),
+ clEnumValN(DIDT_CUIndex, "cu_index", ".debug_cu_index"),
+ clEnumValN(DIDT_TUIndex, "tu_index", ".debug_tu_index"), clEnumValEnd));
-static int ReturnValue = EXIT_SUCCESS;
-
-static bool error(StringRef Filename, std::error_code EC) {
+static void error(StringRef Filename, std::error_code EC) {
if (!EC)
- return false;
+ return;
errs() << Filename << ": " << EC.message() << "\n";
- ReturnValue = EXIT_FAILURE;
- return true;
+ exit(1);
+}
+
+static void DumpObjectFile(ObjectFile &Obj, Twine Filename) {
+ std::unique_ptr<DIContext> DICtx(new DWARFContextInMemory(Obj));
+
+ outs() << Filename.str() << ":\tfile format " << Obj.getFileFormatName()
+ << "\n\n";
+ // Dump the complete DWARF structure.
+ DICtx->dump(outs(), DumpType);
}
static void DumpInput(StringRef Filename) {
ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr =
MemoryBuffer::getFileOrSTDIN(Filename);
- if (error(Filename, BuffOrErr.getError()))
- return;
+ error(Filename, BuffOrErr.getError());
std::unique_ptr<MemoryBuffer> Buff = std::move(BuffOrErr.get());
- ErrorOr<std::unique_ptr<ObjectFile>> ObjOrErr =
- ObjectFile::createObjectFile(Buff->getMemBufferRef());
- if (error(Filename, ObjOrErr.getError()))
- return;
- ObjectFile &Obj = *ObjOrErr.get();
+ ErrorOr<std::unique_ptr<Binary>> BinOrErr =
+ object::createBinary(Buff->getMemBufferRef());
+ error(Filename, BinOrErr.getError());
- std::unique_ptr<DIContext> DICtx(new DWARFContextInMemory(Obj));
+ if (auto *Obj = dyn_cast<ObjectFile>(BinOrErr->get()))
+ DumpObjectFile(*Obj, Filename);
+ else if (auto *Fat = dyn_cast<MachOUniversalBinary>(BinOrErr->get()))
+ for (auto &ObjForArch : Fat->objects()) {
+ auto MachOOrErr = ObjForArch.getAsObjectFile();
+ error(Filename, MachOOrErr.getError());
+ DumpObjectFile(**MachOOrErr,
+ Filename + " (" + ObjForArch.getArchTypeName() + ")");
+ }
+}
- outs() << Filename
- << ":\tfile format " << Obj.getFileFormatName() << "\n\n";
- // Dump the complete DWARF structure.
- DICtx->dump(outs(), DumpType);
+/// If the input path is a .dSYM bundle (as created by the dsymutil tool),
+/// replace it with individual entries for each of the object files inside the
+/// bundle otherwise return the input path.
+static std::vector<std::string> expandBundle(std::string InputPath) {
+ std::vector<std::string> BundlePaths;
+ SmallString<256> BundlePath(InputPath);
+ // Manually open up the bundle to avoid introducing additional dependencies.
+ if (sys::fs::is_directory(BundlePath) &&
+ sys::path::extension(BundlePath) == ".dSYM") {
+ std::error_code EC;
+ sys::path::append(BundlePath, "Contents", "Resources", "DWARF");
+ for (sys::fs::directory_iterator Dir(BundlePath, EC), DirEnd;
+ Dir != DirEnd && !EC; Dir.increment(EC)) {
+ const std::string &Path = Dir->path();
+ sys::fs::file_status Status;
+ EC = sys::fs::status(Path, Status);
+ error(Path, EC);
+ switch (Status.type()) {
+ case sys::fs::file_type::regular_file:
+ case sys::fs::file_type::symlink_file:
+ case sys::fs::file_type::type_unknown:
+ BundlePaths.push_back(Path);
+ break;
+ default: /*ignore*/;
+ }
+ }
+ error(BundlePath, EC);
+ }
+ if (!BundlePaths.size())
+ BundlePaths.push_back(InputPath);
+ return BundlePaths;
}
int main(int argc, char **argv) {
@@ -112,7 +156,14 @@ int main(int argc, char **argv) {
if (InputFilenames.size() == 0)
InputFilenames.push_back("a.out");
- std::for_each(InputFilenames.begin(), InputFilenames.end(), DumpInput);
+ // Expand any .dSYM bundles to the individual object files contained therein.
+ std::vector<std::string> Objects;
+ for (auto F : InputFilenames) {
+ auto Objs = expandBundle(F);
+ Objects.insert(Objects.end(), Objs.begin(), Objs.end());
+ }
+
+ std::for_each(Objects.begin(), Objects.end(), DumpInput);
- return ReturnValue;
+ return EXIT_SUCCESS;
}
diff --git a/tools/llvm-dwp/CMakeLists.txt b/tools/llvm-dwp/CMakeLists.txt
new file mode 100644
index 000000000000..b29c00d49c3d
--- /dev/null
+++ b/tools/llvm-dwp/CMakeLists.txt
@@ -0,0 +1,13 @@
+set(LLVM_LINK_COMPONENTS
+ ${LLVM_TARGETS_TO_BUILD}
+ AsmPrinter
+ DebugInfoDWARF
+ MC
+ Object
+ Support
+ Target
+ )
+
+add_llvm_tool(llvm-dwp
+ llvm-dwp.cpp
+ )
diff --git a/tools/llvm-dwp/LLVMBuild.txt b/tools/llvm-dwp/LLVMBuild.txt
new file mode 100644
index 000000000000..345a73757255
--- /dev/null
+++ b/tools/llvm-dwp/LLVMBuild.txt
@@ -0,0 +1,23 @@
+;===- ./tools/llvm-dwp/LLVMBuild.txt ---------------------------*- Conf -*--===;
+;
+; The LLVM Compiler Infrastructure
+;
+; This file is distributed under the University of Illinois Open Source
+; License. See LICENSE.TXT for details.
+;
+;===------------------------------------------------------------------------===;
+;
+; This is an LLVMBuild description file for the components in this subdirectory.
+;
+; For more information on the LLVMBuild system, please see:
+;
+; http://llvm.org/docs/LLVMBuild.html
+;
+;===------------------------------------------------------------------------===;
+
+[component_0]
+type = Tool
+name = llvm-dwp
+parent = Tools
+required_libraries = AsmPrinter DebugInfoDWARF MC Object Support all-targets
+
diff --git a/tools/llvm-dwp/Makefile b/tools/llvm-dwp/Makefile
new file mode 100644
index 000000000000..826371ecf915
--- /dev/null
+++ b/tools/llvm-dwp/Makefile
@@ -0,0 +1,18 @@
+##===- tools/llvm-dwp/Makefile -----------------------------*- Makefile -*-===##
+#
+# The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+##===----------------------------------------------------------------------===##
+
+LEVEL := ../..
+TOOLNAME := llvm-dwp
+LINK_COMPONENTS := all-targets AsmPrinter DebugInfoDWARF MC Object Support
+
+# This tool has no plugins, optimize startup time.
+TOOL_NO_EXPORTS := 1
+
+include $(LEVEL)/Makefile.common
+
diff --git a/tools/llvm-dwp/llvm-dwp.cpp b/tools/llvm-dwp/llvm-dwp.cpp
new file mode 100644
index 000000000000..570854b849c1
--- /dev/null
+++ b/tools/llvm-dwp/llvm-dwp.cpp
@@ -0,0 +1,420 @@
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/CodeGen/AsmPrinter.h"
+#include "llvm/DebugInfo/DWARF/DWARFFormValue.h"
+#include "llvm/DebugInfo/DWARF/DWARFUnitIndex.h"
+#include "llvm/MC/MCAsmInfo.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCInstrInfo.h"
+#include "llvm/MC/MCObjectFileInfo.h"
+#include "llvm/MC/MCRegisterInfo.h"
+#include "llvm/MC/MCSectionELF.h"
+#include "llvm/MC/MCStreamer.h"
+#include "llvm/MC/MCTargetOptionsCommandFlags.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/DataExtractor.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MathExtras.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Options.h"
+#include "llvm/Support/TargetRegistry.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Target/TargetMachine.h"
+#include <list>
+#include <memory>
+#include <unordered_set>
+
+using namespace llvm;
+using namespace llvm::object;
+using namespace cl;
+
+OptionCategory DwpCategory("Specific Options");
+static list<std::string> InputFiles(Positional, OneOrMore,
+ desc("<input files>"), cat(DwpCategory));
+
+static opt<std::string> OutputFilename(Required, "o",
+ desc("Specify the output file."),
+ value_desc("filename"),
+ cat(DwpCategory));
+
+static int error(const Twine &Error, const Twine &Context) {
+ errs() << Twine("while processing ") + Context + ":\n";
+ errs() << Twine("error: ") + Error + "\n";
+ return 1;
+}
+
+static std::error_code
+writeStringsAndOffsets(MCStreamer &Out, StringMap<uint32_t> &Strings,
+ uint32_t &StringOffset, MCSection *StrSection,
+ MCSection *StrOffsetSection, StringRef CurStrSection,
+ StringRef CurStrOffsetSection) {
+ // Could possibly produce an error or warning if one of these was non-null but
+ // the other was null.
+ if (CurStrSection.empty() || CurStrOffsetSection.empty())
+ return std::error_code();
+
+ DenseMap<uint32_t, uint32_t> OffsetRemapping;
+
+ DataExtractor Data(CurStrSection, true, 0);
+ uint32_t LocalOffset = 0;
+ uint32_t PrevOffset = 0;
+ while (const char *s = Data.getCStr(&LocalOffset)) {
+ StringRef Str(s, LocalOffset - PrevOffset - 1);
+ auto Pair = Strings.insert(std::make_pair(Str, StringOffset));
+ if (Pair.second) {
+ Out.SwitchSection(StrSection);
+ Out.EmitBytes(
+ StringRef(Pair.first->getKeyData(), Pair.first->getKeyLength() + 1));
+ StringOffset += Str.size() + 1;
+ }
+ OffsetRemapping[PrevOffset] = Pair.first->second;
+ PrevOffset = LocalOffset;
+ }
+
+ Data = DataExtractor(CurStrOffsetSection, true, 0);
+
+ Out.SwitchSection(StrOffsetSection);
+
+ uint32_t Offset = 0;
+ uint64_t Size = CurStrOffsetSection.size();
+ while (Offset < Size) {
+ auto OldOffset = Data.getU32(&Offset);
+ auto NewOffset = OffsetRemapping[OldOffset];
+ Out.EmitIntValue(NewOffset, 4);
+ }
+
+ return std::error_code();
+}
+
+static uint32_t getCUAbbrev(StringRef Abbrev, uint64_t AbbrCode) {
+ uint64_t CurCode;
+ uint32_t Offset = 0;
+ DataExtractor AbbrevData(Abbrev, true, 0);
+ while ((CurCode = AbbrevData.getULEB128(&Offset)) != AbbrCode) {
+ // Tag
+ AbbrevData.getULEB128(&Offset);
+ // DW_CHILDREN
+ AbbrevData.getU8(&Offset);
+ // Attributes
+ while (AbbrevData.getULEB128(&Offset) | AbbrevData.getULEB128(&Offset))
+ ;
+ }
+ return Offset;
+}
+
+static uint64_t getCUSignature(StringRef Abbrev, StringRef Info) {
+ uint32_t Offset = 0;
+ DataExtractor InfoData(Info, true, 0);
+ InfoData.getU32(&Offset); // Length
+ uint16_t Version = InfoData.getU16(&Offset);
+ InfoData.getU32(&Offset); // Abbrev offset (should be zero)
+ uint8_t AddrSize = InfoData.getU8(&Offset);
+
+ uint32_t AbbrCode = InfoData.getULEB128(&Offset);
+
+ DataExtractor AbbrevData(Abbrev, true, 0);
+ uint32_t AbbrevOffset = getCUAbbrev(Abbrev, AbbrCode);
+ uint64_t Tag = AbbrevData.getULEB128(&AbbrevOffset);
+ (void)Tag;
+ // FIXME: Real error handling
+ assert(Tag == dwarf::DW_TAG_compile_unit);
+ // DW_CHILDREN
+ AbbrevData.getU8(&AbbrevOffset);
+ uint32_t Name;
+ uint32_t Form;
+ while ((Name = AbbrevData.getULEB128(&AbbrevOffset)) |
+ (Form = AbbrevData.getULEB128(&AbbrevOffset)) &&
+ Name != dwarf::DW_AT_GNU_dwo_id) {
+ DWARFFormValue::skipValue(Form, InfoData, &Offset, Version, AddrSize);
+ }
+ // FIXME: Real error handling
+ assert(Name == dwarf::DW_AT_GNU_dwo_id);
+ return InfoData.getU64(&Offset);
+}
+
+struct UnitIndexEntry {
+ uint64_t Signature;
+ DWARFUnitIndex::Entry::SectionContribution Contributions[8];
+};
+
+static void addAllTypes(MCStreamer &Out,
+ std::vector<UnitIndexEntry> &TypeIndexEntries,
+ MCSection *OutputTypes, StringRef Types,
+ const UnitIndexEntry &CUEntry, uint32_t &TypesOffset) {
+ if (Types.empty())
+ return;
+
+ Out.SwitchSection(OutputTypes);
+ uint32_t Offset = 0;
+ DataExtractor Data(Types, true, 0);
+ while (Data.isValidOffset(Offset)) {
+ UnitIndexEntry Entry = CUEntry;
+ // Zero out the debug_info contribution
+ Entry.Contributions[0] = {};
+ auto &C = Entry.Contributions[DW_SECT_TYPES - DW_SECT_INFO];
+ C.Offset = TypesOffset;
+ auto PrevOffset = Offset;
+ // Length of the unit, including the 4 byte length field.
+ C.Length = Data.getU32(&Offset) + 4;
+
+ Data.getU16(&Offset); // Version
+ Data.getU32(&Offset); // Abbrev offset
+ Data.getU8(&Offset); // Address size
+ Entry.Signature = Data.getU64(&Offset);
+ Offset = PrevOffset + C.Length;
+
+ if (any_of(TypeIndexEntries, [&](const UnitIndexEntry &E) {
+ return E.Signature == Entry.Signature;
+ }))
+ continue;
+
+ Out.EmitBytes(Types.substr(PrevOffset, C.Length));
+ TypesOffset += C.Length;
+
+ TypeIndexEntries.push_back(Entry);
+ }
+}
+
+static void
+writeIndexTable(MCStreamer &Out, ArrayRef<unsigned> ContributionOffsets,
+ ArrayRef<UnitIndexEntry> IndexEntries,
+ uint32_t DWARFUnitIndex::Entry::SectionContribution::*Field) {
+ for (const auto &E : IndexEntries)
+ for (size_t i = 0; i != array_lengthof(E.Contributions); ++i)
+ if (ContributionOffsets[i])
+ Out.EmitIntValue(E.Contributions[i].*Field, 4);
+}
+
+static void writeIndex(MCStreamer &Out, MCSection *Section,
+ ArrayRef<unsigned> ContributionOffsets,
+ ArrayRef<UnitIndexEntry> IndexEntries) {
+ unsigned Columns = 0;
+ for (auto &C : ContributionOffsets)
+ if (C)
+ ++Columns;
+
+ std::vector<unsigned> Buckets(NextPowerOf2(3 * IndexEntries.size() / 2));
+ uint64_t Mask = Buckets.size() - 1;
+ for (size_t i = 0; i != IndexEntries.size(); ++i) {
+ auto S = IndexEntries[i].Signature;
+ auto H = S & Mask;
+ while (Buckets[H]) {
+ assert(S != IndexEntries[Buckets[H] - 1].Signature &&
+ "Duplicate type unit");
+ H += ((S >> 32) & Mask) | 1;
+ }
+ Buckets[H] = i + 1;
+ }
+
+ Out.SwitchSection(Section);
+ Out.EmitIntValue(2, 4); // Version
+ Out.EmitIntValue(Columns, 4); // Columns
+ Out.EmitIntValue(IndexEntries.size(), 4); // Num Units
+ Out.EmitIntValue(Buckets.size(), 4); // Num Buckets
+
+ // Write the signatures.
+ for (const auto &I : Buckets)
+ Out.EmitIntValue(I ? IndexEntries[I - 1].Signature : 0, 8);
+
+ // Write the indexes.
+ for (const auto &I : Buckets)
+ Out.EmitIntValue(I, 4);
+
+ // Write the column headers (which sections will appear in the table)
+ for (size_t i = 0; i != ContributionOffsets.size(); ++i)
+ if (ContributionOffsets[i])
+ Out.EmitIntValue(i + DW_SECT_INFO, 4);
+
+ // Write the offsets.
+ writeIndexTable(Out, ContributionOffsets, IndexEntries,
+ &DWARFUnitIndex::Entry::SectionContribution::Offset);
+
+ // Write the lengths.
+ writeIndexTable(Out, ContributionOffsets, IndexEntries,
+ &DWARFUnitIndex::Entry::SectionContribution::Length);
+}
+static std::error_code write(MCStreamer &Out, ArrayRef<std::string> Inputs) {
+ const auto &MCOFI = *Out.getContext().getObjectFileInfo();
+ MCSection *const StrSection = MCOFI.getDwarfStrDWOSection();
+ MCSection *const StrOffsetSection = MCOFI.getDwarfStrOffDWOSection();
+ MCSection *const TypesSection = MCOFI.getDwarfTypesDWOSection();
+ const StringMap<std::pair<MCSection *, DWARFSectionKind>> KnownSections = {
+ {"debug_info.dwo", {MCOFI.getDwarfInfoDWOSection(), DW_SECT_INFO}},
+ {"debug_types.dwo", {MCOFI.getDwarfTypesDWOSection(), DW_SECT_TYPES}},
+ {"debug_str_offsets.dwo", {StrOffsetSection, DW_SECT_STR_OFFSETS}},
+ {"debug_str.dwo", {StrSection, static_cast<DWARFSectionKind>(0)}},
+ {"debug_loc.dwo", {MCOFI.getDwarfLocDWOSection(), DW_SECT_LOC}},
+ {"debug_line.dwo", {MCOFI.getDwarfLineDWOSection(), DW_SECT_LINE}},
+ {"debug_abbrev.dwo", {MCOFI.getDwarfAbbrevDWOSection(), DW_SECT_ABBREV}}};
+
+ std::vector<UnitIndexEntry> IndexEntries;
+ std::vector<UnitIndexEntry> TypeIndexEntries;
+
+ StringMap<uint32_t> Strings;
+ uint32_t StringOffset = 0;
+
+ uint32_t ContributionOffsets[8] = {};
+
+ for (const auto &Input : Inputs) {
+ auto ErrOrObj = object::ObjectFile::createObjectFile(Input);
+ if (!ErrOrObj)
+ return ErrOrObj.getError();
+
+ IndexEntries.emplace_back();
+ UnitIndexEntry &CurEntry = IndexEntries.back();
+
+ StringRef CurStrSection;
+ StringRef CurStrOffsetSection;
+ StringRef CurTypesSection;
+ StringRef InfoSection;
+ StringRef AbbrevSection;
+
+ for (const auto &Section : ErrOrObj->getBinary()->sections()) {
+ StringRef Name;
+ if (std::error_code Err = Section.getName(Name))
+ return Err;
+
+ auto SectionPair =
+ KnownSections.find(Name.substr(Name.find_first_not_of("._")));
+ if (SectionPair == KnownSections.end())
+ continue;
+
+ StringRef Contents;
+ if (auto Err = Section.getContents(Contents))
+ return Err;
+
+ if (DWARFSectionKind Kind = SectionPair->second.second) {
+ auto Index = Kind - DW_SECT_INFO;
+ if (Kind != DW_SECT_TYPES) {
+ CurEntry.Contributions[Index].Offset = ContributionOffsets[Index];
+ ContributionOffsets[Index] +=
+ (CurEntry.Contributions[Index].Length = Contents.size());
+ }
+
+ switch (Kind) {
+ case DW_SECT_INFO:
+ InfoSection = Contents;
+ break;
+ case DW_SECT_ABBREV:
+ AbbrevSection = Contents;
+ break;
+ default:
+ break;
+ }
+ }
+
+ MCSection *OutSection = SectionPair->second.first;
+ if (OutSection == StrOffsetSection)
+ CurStrOffsetSection = Contents;
+ else if (OutSection == StrSection)
+ CurStrSection = Contents;
+ else if (OutSection == TypesSection)
+ CurTypesSection = Contents;
+ else {
+ Out.SwitchSection(OutSection);
+ Out.EmitBytes(Contents);
+ }
+ }
+
+ assert(!AbbrevSection.empty());
+ assert(!InfoSection.empty());
+ CurEntry.Signature = getCUSignature(AbbrevSection, InfoSection);
+ addAllTypes(Out, TypeIndexEntries, TypesSection, CurTypesSection, CurEntry,
+ ContributionOffsets[DW_SECT_TYPES - DW_SECT_INFO]);
+
+ if (auto Err = writeStringsAndOffsets(Out, Strings, StringOffset,
+ StrSection, StrOffsetSection,
+ CurStrSection, CurStrOffsetSection))
+ return Err;
+ }
+
+ if (!TypeIndexEntries.empty()) {
+ // Lie about there being no info contributions so the TU index only includes
+ // the type unit contribution
+ ContributionOffsets[0] = 0;
+ writeIndex(Out, MCOFI.getDwarfTUIndexSection(), ContributionOffsets,
+ TypeIndexEntries);
+ }
+
+ // Lie about the type contribution
+ ContributionOffsets[DW_SECT_TYPES - DW_SECT_INFO] = 0;
+ // Unlie about the info contribution
+ ContributionOffsets[0] = 1;
+
+ writeIndex(Out, MCOFI.getDwarfCUIndexSection(), ContributionOffsets,
+ IndexEntries);
+
+ return std::error_code();
+}
+
+int main(int argc, char **argv) {
+
+ ParseCommandLineOptions(argc, argv, "merge split dwarf (.dwo) files");
+
+ llvm::InitializeAllTargetInfos();
+ llvm::InitializeAllTargetMCs();
+ llvm::InitializeAllTargets();
+ llvm::InitializeAllAsmPrinters();
+
+ std::string ErrorStr;
+ StringRef Context = "dwarf streamer init";
+
+ Triple TheTriple("x86_64-linux-gnu");
+
+ // Get the target.
+ const Target *TheTarget =
+ TargetRegistry::lookupTarget("", TheTriple, ErrorStr);
+ if (!TheTarget)
+ return error(ErrorStr, Context);
+ std::string TripleName = TheTriple.getTriple();
+
+ // Create all the MC Objects.
+ std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TripleName));
+ if (!MRI)
+ return error(Twine("no register info for target ") + TripleName, Context);
+
+ std::unique_ptr<MCAsmInfo> MAI(TheTarget->createMCAsmInfo(*MRI, TripleName));
+ if (!MAI)
+ return error("no asm info for target " + TripleName, Context);
+
+ MCObjectFileInfo MOFI;
+ MCContext MC(MAI.get(), MRI.get(), &MOFI);
+ MOFI.InitMCObjectFileInfo(TheTriple, Reloc::Default, CodeModel::Default, MC);
+
+ auto MAB = TheTarget->createMCAsmBackend(*MRI, TripleName, "");
+ if (!MAB)
+ return error("no asm backend for target " + TripleName, Context);
+
+ std::unique_ptr<MCInstrInfo> MII(TheTarget->createMCInstrInfo());
+ if (!MII)
+ return error("no instr info info for target " + TripleName, Context);
+
+ std::unique_ptr<MCSubtargetInfo> MSTI(
+ TheTarget->createMCSubtargetInfo(TripleName, "", ""));
+ if (!MSTI)
+ return error("no subtarget info for target " + TripleName, Context);
+
+ MCCodeEmitter *MCE = TheTarget->createMCCodeEmitter(*MII, *MRI, MC);
+ if (!MCE)
+ return error("no code emitter for target " + TripleName, Context);
+
+ // Create the output file.
+ std::error_code EC;
+ raw_fd_ostream OutFile(OutputFilename, EC, sys::fs::F_None);
+ if (EC)
+ return error(Twine(OutputFilename) + ": " + EC.message(), Context);
+
+ MCTargetOptions MCOptions = InitMCTargetOptionsFromFlags();
+ std::unique_ptr<MCStreamer> MS(TheTarget->createMCObjectStreamer(
+ TheTriple, MC, *MAB, OutFile, MCE, *MSTI, MCOptions.MCRelaxAll,
+ MCOptions.MCIncrementalLinkerCompatible,
+ /*DWARFMustBeAtTheEnd*/ false));
+ if (!MS)
+ return error("no object streamer for target " + TripleName, Context);
+
+ if (auto Err = write(*MS, InputFiles))
+ return error(Err.message(), "Writing DWP file");
+
+ MS->Finish();
+}
diff --git a/tools/llvm-extract/llvm-extract.cpp b/tools/llvm-extract/llvm-extract.cpp
index 936496cd7fe6..1da456d33f52 100644
--- a/tools/llvm-extract/llvm-extract.cpp
+++ b/tools/llvm-extract/llvm-extract.cpp
@@ -222,45 +222,42 @@ int main(int argc, char **argv) {
}
}
- // Materialize requisite global values.
- if (!DeleteFn)
- for (size_t i = 0, e = GVs.size(); i != e; ++i) {
- GlobalValue *GV = GVs[i];
- if (std::error_code EC = GV->materialize()) {
- errs() << argv[0] << ": error reading input: " << EC.message() << "\n";
- return 1;
- }
+ auto Materialize = [&](GlobalValue &GV) {
+ if (std::error_code EC = GV.materialize()) {
+ errs() << argv[0] << ": error reading input: " << EC.message() << "\n";
+ exit(1);
}
- else {
+ };
+
+ // Materialize requisite global values.
+ if (!DeleteFn) {
+ for (size_t i = 0, e = GVs.size(); i != e; ++i)
+ Materialize(*GVs[i]);
+ } else {
// Deleting. Materialize every GV that's *not* in GVs.
SmallPtrSet<GlobalValue *, 8> GVSet(GVs.begin(), GVs.end());
- for (auto &G : M->globals()) {
- if (!GVSet.count(&G)) {
- if (std::error_code EC = G.materialize()) {
- errs() << argv[0] << ": error reading input: " << EC.message()
- << "\n";
- return 1;
- }
- }
- }
for (auto &F : *M) {
- if (!GVSet.count(&F)) {
- if (std::error_code EC = F.materialize()) {
- errs() << argv[0] << ": error reading input: " << EC.message()
- << "\n";
- return 1;
- }
- }
+ if (!GVSet.count(&F))
+ Materialize(F);
}
}
+ {
+ std::vector<GlobalValue *> Gvs(GVs.begin(), GVs.end());
+ legacy::PassManager Extract;
+ Extract.add(createGVExtractionPass(Gvs, DeleteFn));
+ Extract.run(*M);
+
+ // Now that we have all the GVs we want, mark the module as fully
+ // materialized.
+ // FIXME: should the GVExtractionPass handle this?
+ M->materializeAll();
+ }
+
// In addition to deleting all other functions, we also want to spiff it
// up a little bit. Do this now.
legacy::PassManager Passes;
- std::vector<GlobalValue*> Gvs(GVs.begin(), GVs.end());
-
- Passes.add(createGVExtractionPass(Gvs, DeleteFn));
if (!DeleteFn)
Passes.add(createGlobalDCEPass()); // Delete unreachable globals
Passes.add(createStripDeadDebugInfoPass()); // Remove dead debug info
diff --git a/tools/llvm-go/llvm-go.go b/tools/llvm-go/llvm-go.go
index c5c3fd244cad..ed79ca67b89d 100644
--- a/tools/llvm-go/llvm-go.go
+++ b/tools/llvm-go/llvm-go.go
@@ -24,6 +24,11 @@ import (
"strings"
)
+const (
+ linkmodeComponentLibs = "component-libs"
+ linkmodeDylib = "dylib"
+)
+
type pkg struct {
llvmpath, pkgpath string
}
@@ -66,11 +71,12 @@ var components = []string{
func llvmConfig(args ...string) string {
configpath := os.Getenv("LLVM_CONFIG")
if configpath == "" {
- // strip llvm-go, add llvm-config
- configpath = os.Args[0][:len(os.Args[0])-7] + "llvm-config"
+ bin, _ := filepath.Split(os.Args[0])
+ configpath = filepath.Join(bin, "llvm-config")
}
cmd := exec.Command(configpath, args...)
+ cmd.Stderr = os.Stderr
out, err := cmd.Output()
if err != nil {
panic(err.Error())
@@ -78,11 +84,21 @@ func llvmConfig(args ...string) string {
outstr := string(out)
outstr = strings.TrimSuffix(outstr, "\n")
- return strings.Replace(outstr, "\n", " ", -1)
+ outstr = strings.Replace(outstr, "\n", " ", -1)
+ return outstr
}
-func llvmFlags() compilerFlags {
- ldflags := llvmConfig(append([]string{"--ldflags", "--libs", "--system-libs"}, components...)...)
+func llvmFlags(linkmode string) compilerFlags {
+ ldflags := llvmConfig("--ldflags")
+ switch linkmode {
+ case linkmodeComponentLibs:
+ ldflags += " " + llvmConfig(append([]string{"--libs"}, components...)...)
+ case linkmodeDylib:
+ ldflags += " -lLLVM"
+ default:
+ panic("invalid linkmode: " + linkmode)
+ }
+ ldflags += " " + llvmConfig("--system-libs")
if runtime.GOOS != "darwin" {
// OS X doesn't like -rpath with cgo. See:
// https://code.google.com/p/go/issues/detail?id=7293
@@ -117,8 +133,8 @@ func printComponents() {
fmt.Println(strings.Join(components, " "))
}
-func printConfig() {
- flags := llvmFlags()
+func printConfig(linkmode string) {
+ flags := llvmFlags(linkmode)
fmt.Printf(`// +build !byollvm
@@ -137,7 +153,7 @@ type (run_build_sh int)
`, flags.cpp, flags.cxx, flags.ld)
}
-func runGoWithLLVMEnv(args []string, cc, cxx, gocmd, llgo, cppflags, cxxflags, ldflags string) {
+func runGoWithLLVMEnv(args []string, cc, cxx, gocmd, llgo, cppflags, cxxflags, ldflags, linkmode string) {
args = addTag(args, "byollvm")
srcdir := llvmConfig("--src-root")
@@ -166,7 +182,7 @@ func runGoWithLLVMEnv(args []string, cc, cxx, gocmd, llgo, cppflags, cxxflags, l
newgopathlist = append(newgopathlist, filepath.SplitList(os.Getenv("GOPATH"))...)
newgopath := strings.Join(newgopathlist, string(filepath.ListSeparator))
- flags := llvmFlags()
+ flags := llvmFlags(linkmode)
newenv := []string{
"CC=" + cc,
@@ -178,7 +194,7 @@ func runGoWithLLVMEnv(args []string, cc, cxx, gocmd, llgo, cppflags, cxxflags, l
"PATH=" + newpath,
}
if llgo != "" {
- newenv = append(newenv, "GCCGO=" + llgo)
+ newenv = append(newenv, "GCCGO="+llgo)
}
for _, v := range os.Environ() {
@@ -234,45 +250,44 @@ func main() {
ldflags := os.Getenv("CGO_LDFLAGS")
gocmd := "go"
llgo := ""
+ linkmode := linkmodeComponentLibs
+
+ flags := []struct {
+ name string
+ dest *string
+ }{
+ {"cc", &cc},
+ {"cxx", &cxx},
+ {"go", &gocmd},
+ {"llgo", &llgo},
+ {"cppflags", &cppflags},
+ {"ldflags", &ldflags},
+ {"linkmode", &linkmode},
+ }
args := os.Args[1:]
- DONE: for {
- switch {
- case len(args) == 0:
+LOOP:
+ for {
+ if len(args) == 0 {
usage()
- case strings.HasPrefix(args[0], "cc="):
- cc = args[0][3:]
- args = args[1:]
- case strings.HasPrefix(args[0], "cxx="):
- cxx = args[0][4:]
- args = args[1:]
- case strings.HasPrefix(args[0], "go="):
- gocmd = args[0][3:]
- args = args[1:]
- case strings.HasPrefix(args[0], "llgo="):
- llgo = args[0][5:]
- args = args[1:]
- case strings.HasPrefix(args[0], "cppflags="):
- cppflags = args[0][9:]
- args = args[1:]
- case strings.HasPrefix(args[0], "cxxflags="):
- cxxflags = args[0][9:]
- args = args[1:]
- case strings.HasPrefix(args[0], "ldflags="):
- ldflags = args[0][8:]
- args = args[1:]
- default:
- break DONE
}
+ for _, flag := range flags {
+ if strings.HasPrefix(args[0], flag.name+"=") {
+ *flag.dest = args[0][len(flag.name)+1:]
+ args = args[1:]
+ continue LOOP
+ }
+ }
+ break
}
switch args[0] {
case "build", "get", "install", "run", "test":
- runGoWithLLVMEnv(args, cc, cxx, gocmd, llgo, cppflags, cxxflags, ldflags)
+ runGoWithLLVMEnv(args, cc, cxx, gocmd, llgo, cppflags, cxxflags, ldflags, linkmode)
case "print-components":
printComponents()
case "print-config":
- printConfig()
+ printConfig(linkmode)
default:
usage()
}
diff --git a/tools/llvm-link/CMakeLists.txt b/tools/llvm-link/CMakeLists.txt
index d4f5be749046..e4ced186336c 100644
--- a/tools/llvm-link/CMakeLists.txt
+++ b/tools/llvm-link/CMakeLists.txt
@@ -3,6 +3,7 @@ set(LLVM_LINK_COMPONENTS
Core
IRReader
Linker
+ Object
Support
)
diff --git a/tools/llvm-link/LLVMBuild.txt b/tools/llvm-link/LLVMBuild.txt
index 2e386f3c2316..807823820ed6 100644
--- a/tools/llvm-link/LLVMBuild.txt
+++ b/tools/llvm-link/LLVMBuild.txt
@@ -19,4 +19,4 @@
type = Tool
name = llvm-link
parent = Tools
-required_libraries = AsmParser BitReader BitWriter IRReader Linker
+required_libraries = AsmParser BitReader BitWriter IRReader Linker Object
diff --git a/tools/llvm-link/Makefile b/tools/llvm-link/Makefile
index ed30d2d256b8..a08d2844ceca 100644
--- a/tools/llvm-link/Makefile
+++ b/tools/llvm-link/Makefile
@@ -9,7 +9,7 @@
LEVEL := ../..
TOOLNAME := llvm-link
-LINK_COMPONENTS := linker bitreader bitwriter asmparser irreader
+LINK_COMPONENTS := linker bitreader bitwriter asmparser irreader object
# This tool has no plugins, optimize startup time.
TOOL_NO_EXPORTS := 1
diff --git a/tools/llvm-link/llvm-link.cpp b/tools/llvm-link/llvm-link.cpp
index 369f3477fe5c..a32383028ae2 100644
--- a/tools/llvm-link/llvm-link.cpp
+++ b/tools/llvm-link/llvm-link.cpp
@@ -18,10 +18,12 @@
#include "llvm/IR/AutoUpgrade.h"
#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/IR/DiagnosticPrinter.h"
+#include "llvm/IR/FunctionInfo.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Verifier.h"
#include "llvm/IRReader/IRReader.h"
+#include "llvm/Object/FunctionIndexObjectFile.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/ManagedStatic.h"
@@ -43,11 +45,34 @@ static cl::list<std::string> OverridingInputs(
cl::desc(
"input bitcode file which can override previously defined symbol(s)"));
+// Option to simulate function importing for testing. This enables using
+// llvm-link to simulate ThinLTO backend processes.
+static cl::list<std::string> Imports(
+ "import", cl::ZeroOrMore, cl::value_desc("function:filename"),
+ cl::desc("Pair of function name and filename, where function should be "
+ "imported from bitcode in filename"));
+
+// Option to support testing of function importing. The function index
+// must be specified in the case were we request imports via the -import
+// option, as well as when compiling any module with functions that may be
+// exported (imported by a different llvm-link -import invocation), to ensure
+// consistent promotion and renaming of locals.
+static cl::opt<std::string> FunctionIndex("functionindex",
+ cl::desc("Function index filename"),
+ cl::init(""),
+ cl::value_desc("filename"));
+
static cl::opt<std::string>
OutputFilename("o", cl::desc("Override output filename"), cl::init("-"),
cl::value_desc("filename"));
static cl::opt<bool>
+Internalize("internalize", cl::desc("Internalize linked symbols"));
+
+static cl::opt<bool>
+OnlyNeeded("only-needed", cl::desc("Link only needed symbols"));
+
+static cl::opt<bool>
Force("f", cl::desc("Enable binary output on terminals"));
static cl::opt<bool>
@@ -64,6 +89,10 @@ static cl::opt<bool>
SuppressWarnings("suppress-warnings", cl::desc("Suppress all linking warnings"),
cl::init(false));
+static cl::opt<bool>
+ PreserveModules("preserve-modules",
+ cl::desc("Preserve linked modules for testing"));
+
static cl::opt<bool> PreserveBitcodeUseListOrder(
"preserve-bc-uselistorder",
cl::desc("Preserve use-list order when writing LLVM bitcode."),
@@ -77,16 +106,21 @@ static cl::opt<bool> PreserveAssemblyUseListOrder(
// Read the specified bitcode file in and return it. This routine searches the
// link path for the specified file to try to find it...
//
-static std::unique_ptr<Module>
-loadFile(const char *argv0, const std::string &FN, LLVMContext &Context) {
+static std::unique_ptr<Module> loadFile(const char *argv0,
+ const std::string &FN,
+ LLVMContext &Context,
+ bool MaterializeMetadata = true) {
SMDiagnostic Err;
if (Verbose) errs() << "Loading '" << FN << "'\n";
- std::unique_ptr<Module> Result = getLazyIRFileModule(FN, Err, Context);
+ std::unique_ptr<Module> Result =
+ getLazyIRFileModule(FN, Err, Context, !MaterializeMetadata);
if (!Result)
Err.print(argv0, errs());
- Result->materializeMetadata();
- UpgradeDebugInfo(*Result);
+ if (MaterializeMetadata) {
+ Result->materializeMetadata();
+ UpgradeDebugInfo(*Result);
+ }
return Result;
}
@@ -112,9 +146,111 @@ static void diagnosticHandler(const DiagnosticInfo &DI) {
errs() << '\n';
}
+static void diagnosticHandlerWithContext(const DiagnosticInfo &DI, void *C) {
+ diagnosticHandler(DI);
+}
+
+/// Import any functions requested via the -import option.
+static bool importFunctions(const char *argv0, LLVMContext &Context,
+ Linker &L) {
+ StringMap<std::unique_ptr<DenseMap<unsigned, MDNode *>>>
+ ModuleToTempMDValsMap;
+ for (const auto &Import : Imports) {
+ // Identify the requested function and its bitcode source file.
+ size_t Idx = Import.find(':');
+ if (Idx == std::string::npos) {
+ errs() << "Import parameter bad format: " << Import << "\n";
+ return false;
+ }
+ std::string FunctionName = Import.substr(0, Idx);
+ std::string FileName = Import.substr(Idx + 1, std::string::npos);
+
+ // Load the specified source module.
+ std::unique_ptr<Module> M = loadFile(argv0, FileName, Context, false);
+ if (!M.get()) {
+ errs() << argv0 << ": error loading file '" << FileName << "'\n";
+ return false;
+ }
+
+ if (verifyModule(*M, &errs())) {
+ errs() << argv0 << ": " << FileName
+ << ": error: input module is broken!\n";
+ return false;
+ }
+
+ Function *F = M->getFunction(FunctionName);
+ if (!F) {
+ errs() << "Ignoring import request for non-existent function "
+ << FunctionName << " from " << FileName << "\n";
+ continue;
+ }
+ // We cannot import weak_any functions without possibly affecting the
+ // order they are seen and selected by the linker, changing program
+ // semantics.
+ if (F->hasWeakAnyLinkage()) {
+ errs() << "Ignoring import request for weak-any function " << FunctionName
+ << " from " << FileName << "\n";
+ continue;
+ }
+
+ if (Verbose)
+ errs() << "Importing " << FunctionName << " from " << FileName << "\n";
+
+ std::unique_ptr<FunctionInfoIndex> Index;
+ if (!FunctionIndex.empty()) {
+ ErrorOr<std::unique_ptr<FunctionInfoIndex>> IndexOrErr =
+ llvm::getFunctionIndexForFile(FunctionIndex, diagnosticHandler);
+ std::error_code EC = IndexOrErr.getError();
+ if (EC) {
+ errs() << EC.message() << '\n';
+ return false;
+ }
+ Index = std::move(IndexOrErr.get());
+ }
+
+ // Save the mapping of value ids to temporary metadata created when
+ // importing this function. If we have already imported from this module,
+ // add new temporary metadata to the existing mapping.
+ auto &TempMDVals = ModuleToTempMDValsMap[FileName];
+ if (!TempMDVals)
+ TempMDVals = llvm::make_unique<DenseMap<unsigned, MDNode *>>();
+
+ // Link in the specified function.
+ DenseSet<const GlobalValue *> FunctionsToImport;
+ FunctionsToImport.insert(F);
+ if (L.linkInModule(std::move(M), Linker::Flags::None, Index.get(),
+ &FunctionsToImport, TempMDVals.get()))
+ return false;
+ }
+
+ // Now link in metadata for all modules from which we imported functions.
+ for (StringMapEntry<std::unique_ptr<DenseMap<unsigned, MDNode *>>> &SME :
+ ModuleToTempMDValsMap) {
+ // Load the specified source module.
+ std::unique_ptr<Module> M = loadFile(argv0, SME.getKey(), Context, true);
+ if (!M.get()) {
+ errs() << argv0 << ": error loading file '" << SME.getKey() << "'\n";
+ return false;
+ }
+
+ if (verifyModule(*M, &errs())) {
+ errs() << argv0 << ": " << SME.getKey()
+ << ": error: input module is broken!\n";
+ return false;
+ }
+
+ // Link in all necessary metadata from this module.
+ if (L.linkInMetadata(*M, SME.getValue().get()))
+ return false;
+ }
+ return true;
+}
+
static bool linkFiles(const char *argv0, LLVMContext &Context, Linker &L,
const cl::list<std::string> &Files,
- bool OverrideDuplicateSymbols) {
+ unsigned Flags) {
+ // Filter out flags that don't apply to the first file we load.
+ unsigned ApplicableFlags = Flags & Linker::Flags::OverrideFromSrc;
for (const auto &File : Files) {
std::unique_ptr<Module> M = loadFile(argv0, File, Context);
if (!M.get()) {
@@ -127,11 +263,36 @@ static bool linkFiles(const char *argv0, LLVMContext &Context, Linker &L,
return false;
}
+ // If a function index is supplied, load it so linkInModule can treat
+ // local functions/variables as exported and promote if necessary.
+ std::unique_ptr<FunctionInfoIndex> Index;
+ if (!FunctionIndex.empty()) {
+ ErrorOr<std::unique_ptr<FunctionInfoIndex>> IndexOrErr =
+ llvm::getFunctionIndexForFile(FunctionIndex, diagnosticHandler);
+ std::error_code EC = IndexOrErr.getError();
+ if (EC) {
+ errs() << EC.message() << '\n';
+ return false;
+ }
+ Index = std::move(IndexOrErr.get());
+ }
+
if (Verbose)
errs() << "Linking in '" << File << "'\n";
- if (L.linkInModule(M.get(), OverrideDuplicateSymbols))
+ if (L.linkInModule(std::move(M), ApplicableFlags, Index.get()))
return false;
+ // All linker flags apply to linking of subsequent files.
+ ApplicableFlags = Flags;
+
+ // If requested for testing, preserve modules by releasing them from
+ // the unique_ptr before the are freed. This can help catch any
+ // cross-module references from e.g. unneeded metadata references
+ // that aren't properly set to null but instead mapped to the source
+ // module version. The bitcode writer will assert if it finds any such
+ // cross-module references.
+ if (PreserveModules)
+ M.release();
}
return true;
@@ -143,18 +304,31 @@ int main(int argc, char **argv) {
PrettyStackTraceProgram X(argc, argv);
LLVMContext &Context = getGlobalContext();
+ Context.setDiagnosticHandler(diagnosticHandlerWithContext, nullptr, true);
+
llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
cl::ParseCommandLineOptions(argc, argv, "llvm linker\n");
auto Composite = make_unique<Module>("llvm-link", Context);
- Linker L(Composite.get(), diagnosticHandler);
+ Linker L(*Composite);
+
+ unsigned Flags = Linker::Flags::None;
+ if (Internalize)
+ Flags |= Linker::Flags::InternalizeLinkedSymbols;
+ if (OnlyNeeded)
+ Flags |= Linker::Flags::LinkOnlyNeeded;
// First add all the regular input files
- if (!linkFiles(argv[0], Context, L, InputFilenames, false))
+ if (!linkFiles(argv[0], Context, L, InputFilenames, Flags))
return 1;
// Next the -override ones.
- if (!linkFiles(argv[0], Context, L, OverridingInputs, true))
+ if (!linkFiles(argv[0], Context, L, OverridingInputs,
+ Flags | Linker::Flags::OverrideFromSrc))
+ return 1;
+
+ // Import any functions requested via -import
+ if (!importFunctions(argv[0], Context, L))
return 1;
if (DumpAsm) errs() << "Here's the assembly:\n" << *Composite;
diff --git a/tools/llvm-lto/CMakeLists.txt b/tools/llvm-lto/CMakeLists.txt
index 3ea1aeea9a41..29b3339e6cea 100644
--- a/tools/llvm-lto/CMakeLists.txt
+++ b/tools/llvm-lto/CMakeLists.txt
@@ -1,7 +1,10 @@
set(LLVM_LINK_COMPONENTS
${LLVM_TARGETS_TO_BUILD}
+ BitWriter
+ Core
LTO
MC
+ Object
Support
Target
)
diff --git a/tools/llvm-lto/LLVMBuild.txt b/tools/llvm-lto/LLVMBuild.txt
index c1613a34c0e4..b36f4a981532 100644
--- a/tools/llvm-lto/LLVMBuild.txt
+++ b/tools/llvm-lto/LLVMBuild.txt
@@ -19,4 +19,4 @@
type = Tool
name = llvm-lto
parent = Tools
-required_libraries = LTO Support all-targets
+required_libraries = BitWriter Core LTO Object Support all-targets
diff --git a/tools/llvm-lto/Makefile b/tools/llvm-lto/Makefile
index f1801b4b20cc..f8ca7e1cac5d 100644
--- a/tools/llvm-lto/Makefile
+++ b/tools/llvm-lto/Makefile
@@ -14,6 +14,4 @@ LINK_COMPONENTS := lto ipo scalaropts linker bitreader bitwriter mcdisassembler
# This tool has no plugins, optimize startup time.
TOOL_NO_EXPORTS := 1
-NO_INSTALL := 1
-
include $(LEVEL)/Makefile.common
diff --git a/tools/llvm-lto/llvm-lto.cpp b/tools/llvm-lto/llvm-lto.cpp
index 08218986f45b..4bc692279b9e 100644
--- a/tools/llvm-lto/llvm-lto.cpp
+++ b/tools/llvm-lto/llvm-lto.cpp
@@ -13,16 +13,22 @@
//===----------------------------------------------------------------------===//
#include "llvm/ADT/StringSet.h"
+#include "llvm/Bitcode/ReaderWriter.h"
#include "llvm/CodeGen/CommandFlags.h"
+#include "llvm/IR/DiagnosticPrinter.h"
+#include "llvm/IR/LLVMContext.h"
#include "llvm/LTO/LTOCodeGenerator.h"
#include "llvm/LTO/LTOModule.h"
+#include "llvm/Object/FunctionIndexObjectFile.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/ToolOutputFile.h"
#include "llvm/Support/raw_ostream.h"
+#include <list>
using namespace llvm;
@@ -34,6 +40,10 @@ OptLevel("O",
cl::ZeroOrMore,
cl::init('2'));
+static cl::opt<bool> DisableVerify(
+ "disable-verify", cl::init(false),
+ cl::desc("Do not run the verifier during the optimization pipeline"));
+
static cl::opt<bool>
DisableInline("disable-inlining", cl::init(false),
cl::desc("Do not run the inliner pass"));
@@ -50,6 +60,14 @@ static cl::opt<bool>
UseDiagnosticHandler("use-diagnostic-handler", cl::init(false),
cl::desc("Use a diagnostic handler to test the handler interface"));
+static cl::opt<bool>
+ ThinLTO("thinlto", cl::init(false),
+ cl::desc("Only write combined global index for ThinLTO backends"));
+
+static cl::opt<bool>
+SaveModuleFile("save-merged-module", cl::init(false),
+ cl::desc("Write merged LTO module to file before CodeGen"));
+
static cl::list<std::string>
InputFilenames(cl::Positional, cl::OneOrMore,
cl::desc("<input bitcode files>"));
@@ -77,6 +95,9 @@ static cl::opt<bool> SetMergedModule(
"set-merged-module", cl::init(false),
cl::desc("Use the first input module as the merged module"));
+static cl::opt<unsigned> Parallelism("j", cl::Prefix, cl::init(1),
+ cl::desc("Number of backend threads"));
+
namespace {
struct ModuleInfo {
std::vector<bool> CanBeHidden;
@@ -85,6 +106,7 @@ struct ModuleInfo {
static void handleDiagnostics(lto_codegen_diagnostic_severity_t Severity,
const char *Msg, void *) {
+ errs() << "llvm-lto: ";
switch (Severity) {
case LTO_DS_NOTE:
errs() << "note: ";
@@ -102,18 +124,68 @@ static void handleDiagnostics(lto_codegen_diagnostic_severity_t Severity,
errs() << Msg << "\n";
}
+static std::string CurrentActivity;
+static void diagnosticHandler(const DiagnosticInfo &DI) {
+ raw_ostream &OS = errs();
+ OS << "llvm-lto: ";
+ switch (DI.getSeverity()) {
+ case DS_Error:
+ OS << "error";
+ break;
+ case DS_Warning:
+ OS << "warning";
+ break;
+ case DS_Remark:
+ OS << "remark";
+ break;
+ case DS_Note:
+ OS << "note";
+ break;
+ }
+ if (!CurrentActivity.empty())
+ OS << ' ' << CurrentActivity;
+ OS << ": ";
+
+ DiagnosticPrinterRawOStream DP(OS);
+ DI.print(DP);
+ OS << '\n';
+
+ if (DI.getSeverity() == DS_Error)
+ exit(1);
+}
+
+static void diagnosticHandlerWithContenxt(const DiagnosticInfo &DI,
+ void *Context) {
+ diagnosticHandler(DI);
+}
+
+static void error(const Twine &Msg) {
+ errs() << "llvm-lto: " << Msg << '\n';
+ exit(1);
+}
+
+static void error(std::error_code EC, const Twine &Prefix) {
+ if (EC)
+ error(Prefix + ": " + EC.message());
+}
+
+template <typename T>
+static void error(const ErrorOr<T> &V, const Twine &Prefix) {
+ error(V.getError(), Prefix);
+}
+
static std::unique_ptr<LTOModule>
getLocalLTOModule(StringRef Path, std::unique_ptr<MemoryBuffer> &Buffer,
- const TargetOptions &Options, std::string &Error) {
+ const TargetOptions &Options) {
ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
MemoryBuffer::getFile(Path);
- if (std::error_code EC = BufferOrErr.getError()) {
- Error = EC.message();
- return nullptr;
- }
+ error(BufferOrErr, "error loading file '" + Path + "'");
Buffer = std::move(BufferOrErr.get());
- return std::unique_ptr<LTOModule>(LTOModule::createInLocalContext(
- Buffer->getBufferStart(), Buffer->getBufferSize(), Options, Error, Path));
+ CurrentActivity = ("loading file '" + Path + "'").str();
+ ErrorOr<std::unique_ptr<LTOModule>> Ret = LTOModule::createInLocalContext(
+ Buffer->getBufferStart(), Buffer->getBufferSize(), Options, Path);
+ CurrentActivity = "";
+ return std::move(*Ret);
}
/// \brief List symbols in each IR file.
@@ -122,24 +194,44 @@ getLocalLTOModule(StringRef Path, std::unique_ptr<MemoryBuffer> &Buffer,
/// functionality that's exposed by the C API to list symbols. Moreover, this
/// provides testing coverage for modules that have been created in their own
/// contexts.
-static int listSymbols(StringRef Command, const TargetOptions &Options) {
+static void listSymbols(const TargetOptions &Options) {
for (auto &Filename : InputFilenames) {
- std::string Error;
std::unique_ptr<MemoryBuffer> Buffer;
std::unique_ptr<LTOModule> Module =
- getLocalLTOModule(Filename, Buffer, Options, Error);
- if (!Module) {
- errs() << Command << ": error loading file '" << Filename
- << "': " << Error << "\n";
- return 1;
- }
+ getLocalLTOModule(Filename, Buffer, Options);
// List the symbols.
outs() << Filename << ":\n";
for (int I = 0, E = Module->getSymbolCount(); I != E; ++I)
outs() << Module->getSymbolName(I) << "\n";
}
- return 0;
+}
+
+/// Create a combined index file from the input IR files and write it.
+///
+/// This is meant to enable testing of ThinLTO combined index generation,
+/// currently available via the gold plugin via -thinlto.
+static void createCombinedFunctionIndex() {
+ FunctionInfoIndex CombinedIndex;
+ uint64_t NextModuleId = 0;
+ for (auto &Filename : InputFilenames) {
+ CurrentActivity = "loading file '" + Filename + "'";
+ ErrorOr<std::unique_ptr<FunctionInfoIndex>> IndexOrErr =
+ llvm::getFunctionIndexForFile(Filename, diagnosticHandler);
+ std::unique_ptr<FunctionInfoIndex> Index = std::move(IndexOrErr.get());
+ CurrentActivity = "";
+ // Skip files without a function summary.
+ if (!Index)
+ continue;
+ CombinedIndex.mergeFrom(std::move(Index), ++NextModuleId);
+ }
+ std::error_code EC;
+ assert(!OutputFilename.empty());
+ raw_fd_ostream OS(OutputFilename + ".thinlto.bc", EC,
+ sys::fs::OpenFlags::F_None);
+ error(EC, "error opening the file '" + OutputFilename + ".thinlto.bc'");
+ WriteFunctionSummaryToFile(CombinedIndex, OS);
+ OS.close();
}
int main(int argc, char **argv) {
@@ -150,10 +242,8 @@ int main(int argc, char **argv) {
llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
cl::ParseCommandLineOptions(argc, argv, "llvm LTO linker\n");
- if (OptLevel < '0' || OptLevel > '3') {
- errs() << argv[0] << ": optimization level must be between 0 and 3\n";
- return 1;
- }
+ if (OptLevel < '0' || OptLevel > '3')
+ error("optimization level must be between 0 and 3");
// Initialize the configured targets.
InitializeAllTargets();
@@ -164,29 +254,27 @@ int main(int argc, char **argv) {
// set up the TargetOptions for the machine
TargetOptions Options = InitTargetOptionsFromCodeGenFlags();
- if (ListSymbolsOnly)
- return listSymbols(argv[0], Options);
+ if (ListSymbolsOnly) {
+ listSymbols(Options);
+ return 0;
+ }
+
+ if (ThinLTO) {
+ createCombinedFunctionIndex();
+ return 0;
+ }
unsigned BaseArg = 0;
- LTOCodeGenerator CodeGen;
+ LLVMContext Context;
+ Context.setDiagnosticHandler(diagnosticHandlerWithContenxt, nullptr, true);
+
+ LTOCodeGenerator CodeGen(Context);
if (UseDiagnosticHandler)
CodeGen.setDiagnosticHandler(handleDiagnostics, nullptr);
- switch (RelocModel) {
- case Reloc::Static:
- CodeGen.setCodePICModel(LTO_CODEGEN_PIC_MODEL_STATIC);
- break;
- case Reloc::PIC_:
- CodeGen.setCodePICModel(LTO_CODEGEN_PIC_MODEL_DYNAMIC);
- break;
- case Reloc::DynamicNoPIC:
- CodeGen.setCodePICModel(LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC);
- break;
- default:
- CodeGen.setCodePICModel(LTO_CODEGEN_PIC_MODEL_DEFAULT);
- }
+ CodeGen.setCodePICModel(RelocModel);
CodeGen.setDebugInfo(LTO_DEBUG_MODEL_DWARF);
CodeGen.setTargetOptions(Options);
@@ -198,38 +286,33 @@ int main(int argc, char **argv) {
std::vector<std::string> KeptDSOSyms;
for (unsigned i = BaseArg; i < InputFilenames.size(); ++i) {
- std::string error;
- std::unique_ptr<LTOModule> Module(
- LTOModule::createFromFile(InputFilenames[i].c_str(), Options, error));
- if (!error.empty()) {
- errs() << argv[0] << ": error loading file '" << InputFilenames[i]
- << "': " << error << "\n";
- return 1;
- }
+ CurrentActivity = "loading file '" + InputFilenames[i] + "'";
+ ErrorOr<std::unique_ptr<LTOModule>> ModuleOrErr =
+ LTOModule::createFromFile(Context, InputFilenames[i].c_str(), Options);
+ std::unique_ptr<LTOModule> &Module = *ModuleOrErr;
+ CurrentActivity = "";
- LTOModule *LTOMod = Module.get();
+ unsigned NumSyms = Module->getSymbolCount();
+ for (unsigned I = 0; I < NumSyms; ++I) {
+ StringRef Name = Module->getSymbolName(I);
+ if (!DSOSymbolsSet.count(Name))
+ continue;
+ lto_symbol_attributes Attrs = Module->getSymbolAttributes(I);
+ unsigned Scope = Attrs & LTO_SYMBOL_SCOPE_MASK;
+ if (Scope != LTO_SYMBOL_SCOPE_DEFAULT_CAN_BE_HIDDEN)
+ KeptDSOSyms.push_back(Name);
+ }
// We use the first input module as the destination module when
// SetMergedModule is true.
if (SetMergedModule && i == BaseArg) {
// Transfer ownership to the code generator.
- CodeGen.setModule(Module.release());
+ CodeGen.setModule(std::move(Module));
} else if (!CodeGen.addModule(Module.get())) {
// Print a message here so that we know addModule() did not abort.
errs() << argv[0] << ": error adding file '" << InputFilenames[i] << "'\n";
return 1;
}
-
- unsigned NumSyms = LTOMod->getSymbolCount();
- for (unsigned I = 0; I < NumSyms; ++I) {
- StringRef Name = LTOMod->getSymbolName(I);
- if (!DSOSymbolsSet.count(Name))
- continue;
- lto_symbol_attributes Attrs = LTOMod->getSymbolAttributes(I);
- unsigned Scope = Attrs & LTO_SYMBOL_SCOPE_MASK;
- if (Scope != LTO_SYMBOL_SCOPE_DEFAULT_CAN_BE_HIDDEN)
- KeptDSOSyms.push_back(Name);
- }
}
// Add all the exported symbols to the table of symbols to preserve.
@@ -255,34 +338,68 @@ int main(int argc, char **argv) {
if (!attrs.empty())
CodeGen.setAttr(attrs.c_str());
+ if (FileType.getNumOccurrences())
+ CodeGen.setFileType(FileType);
+
if (!OutputFilename.empty()) {
- std::string ErrorInfo;
- std::unique_ptr<MemoryBuffer> Code = CodeGen.compile(
- DisableInline, DisableGVNLoadPRE, DisableLTOVectorization, ErrorInfo);
- if (!Code) {
- errs() << argv[0]
- << ": error compiling the code: " << ErrorInfo << "\n";
+ if (!CodeGen.optimize(DisableVerify, DisableInline, DisableGVNLoadPRE,
+ DisableLTOVectorization)) {
+ // Diagnostic messages should have been printed by the handler.
+ errs() << argv[0] << ": error optimizing the code\n";
return 1;
}
- std::error_code EC;
- raw_fd_ostream FileStream(OutputFilename, EC, sys::fs::F_None);
- if (EC) {
- errs() << argv[0] << ": error opening the file '" << OutputFilename
- << "': " << EC.message() << "\n";
+ if (SaveModuleFile) {
+ std::string ModuleFilename = OutputFilename;
+ ModuleFilename += ".merged.bc";
+ std::string ErrMsg;
+
+ if (!CodeGen.writeMergedModules(ModuleFilename.c_str())) {
+ errs() << argv[0] << ": writing merged module failed.\n";
+ return 1;
+ }
+ }
+
+ std::list<tool_output_file> OSs;
+ std::vector<raw_pwrite_stream *> OSPtrs;
+ for (unsigned I = 0; I != Parallelism; ++I) {
+ std::string PartFilename = OutputFilename;
+ if (Parallelism != 1)
+ PartFilename += "." + utostr(I);
+ std::error_code EC;
+ OSs.emplace_back(PartFilename, EC, sys::fs::F_None);
+ if (EC) {
+ errs() << argv[0] << ": error opening the file '" << PartFilename
+ << "': " << EC.message() << "\n";
+ return 1;
+ }
+ OSPtrs.push_back(&OSs.back().os());
+ }
+
+ if (!CodeGen.compileOptimized(OSPtrs)) {
+ // Diagnostic messages should have been printed by the handler.
+ errs() << argv[0] << ": error compiling the code\n";
return 1;
}
- FileStream.write(Code->getBufferStart(), Code->getBufferSize());
+ for (tool_output_file &OS : OSs)
+ OS.keep();
} else {
- std::string ErrorInfo;
+ if (Parallelism != 1) {
+ errs() << argv[0] << ": -j must be specified together with -o\n";
+ return 1;
+ }
+
+ if (SaveModuleFile) {
+ errs() << argv[0] << ": -save-merged-module must be specified with -o\n";
+ return 1;
+ }
+
const char *OutputName = nullptr;
- if (!CodeGen.compile_to_file(&OutputName, DisableInline,
- DisableGVNLoadPRE, DisableLTOVectorization,
- ErrorInfo)) {
- errs() << argv[0]
- << ": error compiling the code: " << ErrorInfo
- << "\n";
+ if (!CodeGen.compile_to_file(&OutputName, DisableVerify, DisableInline,
+ DisableGVNLoadPRE, DisableLTOVectorization)) {
+ // Diagnostic messages should have been printed by the handler.
+ errs() << argv[0] << ": error compiling the code\n";
return 1;
}
diff --git a/tools/llvm-mc-fuzzer/CMakeLists.txt b/tools/llvm-mc-fuzzer/CMakeLists.txt
new file mode 100644
index 000000000000..0d6432b23dfa
--- /dev/null
+++ b/tools/llvm-mc-fuzzer/CMakeLists.txt
@@ -0,0 +1,18 @@
+if( LLVM_USE_SANITIZE_COVERAGE )
+ include_directories(BEFORE
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../lib/Fuzzer)
+
+ set(LLVM_LINK_COMPONENTS
+ AllTargetsDescs
+ AllTargetsDisassemblers
+ AllTargetsInfos
+ MC
+ MCDisassembler
+ Support
+ )
+ add_llvm_tool(llvm-mc-fuzzer
+ llvm-mc-fuzzer.cpp)
+ target_link_libraries(llvm-mc-fuzzer
+ LLVMFuzzerNoMain
+ )
+endif()
diff --git a/tools/llvm-mc-fuzzer/llvm-mc-fuzzer.cpp b/tools/llvm-mc-fuzzer/llvm-mc-fuzzer.cpp
new file mode 100644
index 000000000000..3f80e4582ee1
--- /dev/null
+++ b/tools/llvm-mc-fuzzer/llvm-mc-fuzzer.cpp
@@ -0,0 +1,138 @@
+//===--- llvm-mc-fuzzer.cpp - Fuzzer for the MC layer ---------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm-c/Disassembler.h"
+#include "llvm-c/Target.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/MC/SubtargetFeature.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/raw_ostream.h"
+#include "FuzzerInterface.h"
+
+using namespace llvm;
+
+const unsigned AssemblyTextBufSize = 80;
+
+enum ActionType {
+ AC_Assemble,
+ AC_Disassemble
+};
+
+static cl::opt<ActionType>
+Action(cl::desc("Action to perform:"),
+ cl::init(AC_Assemble),
+ cl::values(clEnumValN(AC_Assemble, "assemble",
+ "Assemble a .s file (default)"),
+ clEnumValN(AC_Disassemble, "disassemble",
+ "Disassemble strings of hex bytes"),
+ clEnumValEnd));
+
+static cl::opt<std::string>
+ TripleName("triple", cl::desc("Target triple to assemble for, "
+ "see -version for available targets"));
+
+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(""));
+
+// This is useful for variable-length instruction sets.
+static cl::opt<unsigned> InsnLimit(
+ "insn-limit",
+ cl::desc("Limit the number of instructions to process (0 for no limit)"),
+ cl::value_desc("count"), cl::init(0));
+
+static cl::list<std::string>
+ MAttrs("mattr", cl::CommaSeparated,
+ cl::desc("Target specific attributes (-mattr=help for details)"),
+ cl::value_desc("a1,+a2,-a3,..."));
+// The feature string derived from -mattr's values.
+std::string FeaturesStr;
+
+static cl::list<std::string>
+ FuzzerArgv("fuzzer-args", cl::Positional,
+ cl::desc("Options to pass to the fuzzer"), cl::ZeroOrMore,
+ cl::PositionalEatsArgs);
+
+int DisassembleOneInput(const uint8_t *Data, size_t Size) {
+ char AssemblyText[AssemblyTextBufSize];
+
+ std::vector<uint8_t> DataCopy(Data, Data + Size);
+
+ LLVMDisasmContextRef Ctx = LLVMCreateDisasmCPUFeatures(
+ TripleName.c_str(), MCPU.c_str(), FeaturesStr.c_str(), nullptr, 0,
+ nullptr, nullptr);
+ assert(Ctx);
+ uint8_t *p = DataCopy.data();
+ unsigned Consumed;
+ unsigned InstructionsProcessed = 0;
+ do {
+ Consumed = LLVMDisasmInstruction(Ctx, p, Size, 0, AssemblyText,
+ AssemblyTextBufSize);
+ Size -= Consumed;
+ p += Consumed;
+
+ InstructionsProcessed ++;
+ if (InsnLimit != 0 && InstructionsProcessed < InsnLimit)
+ break;
+ } while (Consumed != 0);
+ LLVMDisasmDispose(Ctx);
+ return 0;
+}
+
+int main(int argc, char **argv) {
+ // The command line is unusual compared to other fuzzers due to the need to
+ // specify the target. Options like -triple, -mcpu, and -mattr work like
+ // their counterparts in llvm-mc, while -fuzzer-args collects options for the
+ // fuzzer itself.
+ //
+ // Examples:
+ //
+ // Fuzz the big-endian MIPS32R6 disassembler using 100,000 inputs of up to
+ // 4-bytes each and use the contents of ./corpus as the test corpus:
+ // llvm-mc-fuzzer -triple mips-linux-gnu -mcpu=mips32r6 -disassemble \
+ // -fuzzer-args -max_len=4 -runs=100000 ./corpus
+ //
+ // Infinitely fuzz the little-endian MIPS64R2 disassembler with the MSA
+ // feature enabled using up to 64-byte inputs:
+ // llvm-mc-fuzzer -triple mipsel-linux-gnu -mcpu=mips64r2 -mattr=msa \
+ // -disassemble -fuzzer-args ./corpus
+ //
+ // If your aim is to find instructions that are not tested, then it is
+ // advisable to constrain the maximum input size to a single instruction
+ // using -max_len as in the first example. This results in a test corpus of
+ // individual instructions that test unique paths. Without this constraint,
+ // there will be considerable redundancy in the corpus.
+
+ LLVMInitializeAllTargetInfos();
+ LLVMInitializeAllTargetMCs();
+ LLVMInitializeAllDisassemblers();
+
+ cl::ParseCommandLineOptions(argc, argv);
+
+ // Package up features to be passed to target/subtarget
+ // We have to pass it via a global since the callback doesn't
+ // permit any user data.
+ if (MAttrs.size()) {
+ SubtargetFeatures Features;
+ for (unsigned i = 0; i != MAttrs.size(); ++i)
+ Features.AddFeature(MAttrs[i]);
+ FeaturesStr = Features.getString();
+ }
+
+ if (Action == AC_Assemble)
+ errs() << "error: -assemble is not implemented\n";
+ else if (Action == AC_Disassemble)
+ return fuzzer::FuzzerDriver(argc, argv, DisassembleOneInput);
+
+ llvm_unreachable("Unknown action");
+ return 1;
+}
diff --git a/tools/llvm-mc/llvm-mc.cpp b/tools/llvm-mc/llvm-mc.cpp
index 6ecdb2eaa6d5..96e3f7c21a51 100644
--- a/tools/llvm-mc/llvm-mc.cpp
+++ b/tools/llvm-mc/llvm-mc.cpp
@@ -1,4 +1,4 @@
-//===-- llvm-mc.cpp - Machine Code Hacking Driver -------------------------===//
+//===-- llvm-mc.cpp - Machine Code Hacking Driver ---------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
@@ -39,6 +39,7 @@
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/ToolOutputFile.h"
+
using namespace llvm;
static cl::opt<std::string>
@@ -234,7 +235,7 @@ static void setDwarfDebugFlags(int argc, char **argv) {
}
static std::string DwarfDebugProducer;
-static void setDwarfDebugProducer(void) {
+static void setDwarfDebugProducer() {
if(!getenv("DEBUG_PRODUCER"))
return;
DwarfDebugProducer += getenv("DEBUG_PRODUCER");
@@ -398,7 +399,7 @@ int main(int argc, char **argv) {
ErrorOr<std::unique_ptr<MemoryBuffer>> BufferPtr =
MemoryBuffer::getFileOrSTDIN(InputFilename);
if (std::error_code EC = BufferPtr.getError()) {
- errs() << ProgName << ": " << EC.message() << '\n';
+ errs() << InputFilename << ": " << EC.message() << '\n';
return 1;
}
MemoryBuffer *Buffer = BufferPtr->get();
@@ -510,9 +511,10 @@ int main(int argc, char **argv) {
MCCodeEmitter *CE = TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx);
MCAsmBackend *MAB = TheTarget->createMCAsmBackend(*MRI, TripleName, MCPU);
- Str.reset(TheTarget->createMCObjectStreamer(TheTriple, Ctx, *MAB, *OS, CE,
- *STI, RelaxAll,
- /*DWARFMustBeAtTheEnd*/ false));
+ Str.reset(TheTarget->createMCObjectStreamer(
+ TheTriple, Ctx, *MAB, *OS, CE, *STI, MCOptions.MCRelaxAll,
+ MCOptions.MCIncrementalLinkerCompatible,
+ /*DWARFMustBeAtTheEnd*/ false));
if (NoExecStack)
Str->InitSections(true);
}
diff --git a/tools/llvm-nm/CMakeLists.txt b/tools/llvm-nm/CMakeLists.txt
index 20293bb88f05..22b7bd3e35e9 100644
--- a/tools/llvm-nm/CMakeLists.txt
+++ b/tools/llvm-nm/CMakeLists.txt
@@ -1,5 +1,7 @@
set(LLVM_LINK_COMPONENTS
- ${LLVM_TARGETS_TO_BUILD}
+ AllTargetsAsmParsers
+ AllTargetsDescs
+ AllTargetsInfos
Core
Object
Support
diff --git a/tools/llvm-nm/llvm-nm.cpp b/tools/llvm-nm/llvm-nm.cpp
index e7ee3124ed73..b812233034ee 100644
--- a/tools/llvm-nm/llvm-nm.cpp
+++ b/tools/llvm-nm/llvm-nm.cpp
@@ -20,6 +20,7 @@
#include "llvm/IR/GlobalAlias.h"
#include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Module.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/COFF.h"
#include "llvm/Object/ELFObjectFile.h"
@@ -44,6 +45,7 @@
#include <cstring>
#include <system_error>
#include <vector>
+
using namespace llvm;
using namespace object;
@@ -64,27 +66,30 @@ cl::list<std::string> InputFilenames(cl::Positional, cl::desc("<input files>"),
cl::opt<bool> UndefinedOnly("undefined-only",
cl::desc("Show only undefined symbols"));
cl::alias UndefinedOnly2("u", cl::desc("Alias for --undefined-only"),
- cl::aliasopt(UndefinedOnly));
+ cl::aliasopt(UndefinedOnly), cl::Grouping);
cl::opt<bool> DynamicSyms("dynamic",
cl::desc("Display the dynamic symbols instead "
"of normal symbols."));
cl::alias DynamicSyms2("D", cl::desc("Alias for --dynamic"),
- cl::aliasopt(DynamicSyms));
+ cl::aliasopt(DynamicSyms), cl::Grouping);
cl::opt<bool> DefinedOnly("defined-only",
cl::desc("Show only defined symbols"));
cl::alias DefinedOnly2("U", cl::desc("Alias for --defined-only"),
- cl::aliasopt(DefinedOnly));
+ cl::aliasopt(DefinedOnly), cl::Grouping);
cl::opt<bool> ExternalOnly("extern-only",
cl::desc("Show only external symbols"));
cl::alias ExternalOnly2("g", cl::desc("Alias for --extern-only"),
- cl::aliasopt(ExternalOnly));
+ cl::aliasopt(ExternalOnly), cl::Grouping);
-cl::opt<bool> BSDFormat("B", cl::desc("Alias for --format=bsd"));
-cl::opt<bool> POSIXFormat("P", cl::desc("Alias for --format=posix"));
-cl::opt<bool> DarwinFormat("m", cl::desc("Alias for --format=darwin"));
+cl::opt<bool> BSDFormat("B", cl::desc("Alias for --format=bsd"),
+ cl::Grouping);
+cl::opt<bool> POSIXFormat("P", cl::desc("Alias for --format=posix"),
+ cl::Grouping);
+cl::opt<bool> DarwinFormat("m", cl::desc("Alias for --format=darwin"),
+ cl::Grouping);
static cl::list<std::string>
ArchFlags("arch", cl::desc("architecture(s) from a Mach-O file to dump"),
@@ -96,32 +101,33 @@ cl::opt<bool> PrintFileName(
cl::desc("Precede each symbol with the object file it came from"));
cl::alias PrintFileNameA("A", cl::desc("Alias for --print-file-name"),
- cl::aliasopt(PrintFileName));
+ cl::aliasopt(PrintFileName), cl::Grouping);
cl::alias PrintFileNameo("o", cl::desc("Alias for --print-file-name"),
- cl::aliasopt(PrintFileName));
+ cl::aliasopt(PrintFileName), cl::Grouping);
cl::opt<bool> DebugSyms("debug-syms",
cl::desc("Show all symbols, even debugger only"));
cl::alias DebugSymsa("a", cl::desc("Alias for --debug-syms"),
- cl::aliasopt(DebugSyms));
+ cl::aliasopt(DebugSyms), cl::Grouping);
cl::opt<bool> NumericSort("numeric-sort", cl::desc("Sort symbols by address"));
cl::alias NumericSortn("n", cl::desc("Alias for --numeric-sort"),
- cl::aliasopt(NumericSort));
+ cl::aliasopt(NumericSort), cl::Grouping);
cl::alias NumericSortv("v", cl::desc("Alias for --numeric-sort"),
- cl::aliasopt(NumericSort));
+ cl::aliasopt(NumericSort), cl::Grouping);
cl::opt<bool> NoSort("no-sort", cl::desc("Show symbols in order encountered"));
-cl::alias NoSortp("p", cl::desc("Alias for --no-sort"), cl::aliasopt(NoSort));
+cl::alias NoSortp("p", cl::desc("Alias for --no-sort"), cl::aliasopt(NoSort),
+ cl::Grouping);
cl::opt<bool> ReverseSort("reverse-sort", cl::desc("Sort in reverse order"));
cl::alias ReverseSortr("r", cl::desc("Alias for --reverse-sort"),
- cl::aliasopt(ReverseSort));
+ cl::aliasopt(ReverseSort), cl::Grouping);
cl::opt<bool> PrintSize("print-size",
cl::desc("Show symbol size instead of address"));
cl::alias PrintSizeS("S", cl::desc("Alias for --print-size"),
- cl::aliasopt(PrintSize));
+ cl::aliasopt(PrintSize), cl::Grouping);
cl::opt<bool> SizeSort("size-sort", cl::desc("Sort symbols by size"));
@@ -130,12 +136,12 @@ cl::opt<bool> WithoutAliases("without-aliases", cl::Hidden,
cl::opt<bool> ArchiveMap("print-armap", cl::desc("Print the archive map"));
cl::alias ArchiveMaps("M", cl::desc("Alias for --print-armap"),
- cl::aliasopt(ArchiveMap));
+ cl::aliasopt(ArchiveMap), cl::Grouping);
cl::opt<bool> JustSymbolName("just-symbol-name",
cl::desc("Print just the symbol's name"));
cl::alias JustSymbolNames("j", cl::desc("Alias for --just-symbol-name"),
- cl::aliasopt(JustSymbolName));
+ cl::aliasopt(JustSymbolName), cl::Grouping);
// FIXME: This option takes exactly two strings and should be allowed anywhere
// on the command line. Such that "llvm-nm -s __TEXT __text foo.o" would work.
@@ -147,7 +153,7 @@ cl::list<std::string> SegSect("s", cl::Positional, cl::ZeroOrMore,
"and section name, Mach-O only"));
cl::opt<bool> FormatMachOasHex("x", cl::desc("Print symbol entry in hex, "
- "Mach-O only"));
+ "Mach-O only"), cl::Grouping);
cl::opt<bool> NoLLVMBitcode("no-llvm-bc",
cl::desc("Disable LLVM bitcode reader"));
@@ -159,7 +165,7 @@ bool MultipleFiles = false;
bool HadError = false;
std::string ToolName;
-}
+} // anonymous namespace
static void error(Twine Message, Twine Path = Twine()) {
HadError = true;
@@ -182,7 +188,7 @@ struct NMSymbol {
StringRef Name;
BasicSymbolRef Sym;
};
-}
+} // anonymous namespace
static bool compareSymbolAddress(const NMSymbol &A, const NMSymbol &B) {
bool ADefined = !(A.Sym.getFlags() & SymbolRef::SF_Undefined);
@@ -202,8 +208,14 @@ static bool compareSymbolName(const NMSymbol &A, const NMSymbol &B) {
}
static char isSymbolList64Bit(SymbolicFile &Obj) {
- if (isa<IRObjectFile>(Obj))
- return false;
+ if (isa<IRObjectFile>(Obj)) {
+ IRObjectFile *IRobj = dyn_cast<IRObjectFile>(&Obj);
+ Module &M = IRobj->getModule();
+ if (M.getTargetTriple().empty())
+ return false;
+ Triple T(M.getTargetTriple());
+ return T.isArch64Bit();
+ }
if (isa<COFFObjectFile>(Obj))
return false;
if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(&Obj))
@@ -215,54 +227,80 @@ static StringRef CurrentFilename;
typedef std::vector<NMSymbol> SymbolListT;
static SymbolListT SymbolList;
+static char getSymbolNMTypeChar(IRObjectFile &Obj, basic_symbol_iterator I);
+
// darwinPrintSymbol() is used to print a symbol from a Mach-O file when the
// the OutputFormat is darwin or we are printing Mach-O symbols in hex. For
// the darwin format it produces the same output as darwin's nm(1) -m output
// and when printing Mach-O symbols in hex it produces the same output as
// darwin's nm(1) -x format.
-static void darwinPrintSymbol(MachOObjectFile *MachO, SymbolListT::iterator I,
- char *SymbolAddrStr, const char *printBlanks) {
+static void darwinPrintSymbol(SymbolicFile &Obj, SymbolListT::iterator I,
+ char *SymbolAddrStr, const char *printBlanks,
+ const char *printDashes, const char *printFormat) {
MachO::mach_header H;
MachO::mach_header_64 H_64;
- uint32_t Filetype, Flags;
- MachO::nlist_64 STE_64;
- MachO::nlist STE;
- uint8_t NType;
- uint8_t NSect;
- uint16_t NDesc;
- uint32_t NStrx;
- uint64_t NValue;
- DataRefImpl SymDRI = I->Sym.getRawDataRefImpl();
- if (MachO->is64Bit()) {
- H_64 = MachO->MachOObjectFile::getHeader64();
- Filetype = H_64.filetype;
- Flags = H_64.flags;
- STE_64 = MachO->getSymbol64TableEntry(SymDRI);
- NType = STE_64.n_type;
- NSect = STE_64.n_sect;
- NDesc = STE_64.n_desc;
- NStrx = STE_64.n_strx;
- NValue = STE_64.n_value;
+ uint32_t Filetype = MachO::MH_OBJECT;
+ uint32_t Flags = 0;
+ uint8_t NType = 0;
+ uint8_t NSect = 0;
+ uint16_t NDesc = 0;
+ uint32_t NStrx = 0;
+ uint64_t NValue = 0;
+ MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(&Obj);
+ if (Obj.isIR()) {
+ uint32_t SymFlags = I->Sym.getFlags();
+ if (SymFlags & SymbolRef::SF_Global)
+ NType |= MachO::N_EXT;
+ if (SymFlags & SymbolRef::SF_Hidden)
+ NType |= MachO::N_PEXT;
+ if (SymFlags & SymbolRef::SF_Undefined)
+ NType |= MachO::N_EXT | MachO::N_UNDF;
+ else {
+ // Here we have a symbol definition. So to fake out a section name we
+ // use 1, 2 and 3 for section numbers. See below where they are used to
+ // print out fake section names.
+ NType |= MachO::N_SECT;
+ if(SymFlags & SymbolRef::SF_Const)
+ NSect = 3;
+ else {
+ IRObjectFile *IRobj = dyn_cast<IRObjectFile>(&Obj);
+ char c = getSymbolNMTypeChar(*IRobj, I->Sym);
+ if (c == 't')
+ NSect = 1;
+ else
+ NSect = 2;
+ }
+ }
+ if (SymFlags & SymbolRef::SF_Weak)
+ NDesc |= MachO::N_WEAK_DEF;
} else {
- H = MachO->MachOObjectFile::getHeader();
- Filetype = H.filetype;
- Flags = H.flags;
- STE = MachO->getSymbolTableEntry(SymDRI);
- NType = STE.n_type;
- NSect = STE.n_sect;
- NDesc = STE.n_desc;
- NStrx = STE.n_strx;
- NValue = STE.n_value;
+ DataRefImpl SymDRI = I->Sym.getRawDataRefImpl();
+ if (MachO->is64Bit()) {
+ H_64 = MachO->MachOObjectFile::getHeader64();
+ Filetype = H_64.filetype;
+ Flags = H_64.flags;
+ MachO::nlist_64 STE_64 = MachO->getSymbol64TableEntry(SymDRI);
+ NType = STE_64.n_type;
+ NSect = STE_64.n_sect;
+ NDesc = STE_64.n_desc;
+ NStrx = STE_64.n_strx;
+ NValue = STE_64.n_value;
+ } else {
+ H = MachO->MachOObjectFile::getHeader();
+ Filetype = H.filetype;
+ Flags = H.flags;
+ MachO::nlist STE = MachO->getSymbolTableEntry(SymDRI);
+ NType = STE.n_type;
+ NSect = STE.n_sect;
+ NDesc = STE.n_desc;
+ NStrx = STE.n_strx;
+ NValue = STE.n_value;
+ }
}
// If we are printing Mach-O symbols in hex do that and return.
if (FormatMachOasHex) {
char Str[18] = "";
- const char *printFormat;
- if (MachO->is64Bit())
- printFormat = "%016" PRIx64;
- else
- printFormat = "%08" PRIx64;
format(printFormat, NValue).print(Str, sizeof(Str));
outs() << Str << ' ';
format("%02x", NType).print(Str, sizeof(Str));
@@ -280,6 +318,8 @@ static void darwinPrintSymbol(MachOObjectFile *MachO, SymbolListT::iterator I,
if (PrintAddress) {
if ((NType & MachO::N_TYPE) == MachO::N_INDR)
strcpy(SymbolAddrStr, printBlanks);
+ if (Obj.isIR() && (NType & MachO::N_TYPE) == MachO::N_TYPE)
+ strcpy(SymbolAddrStr, printDashes);
outs() << SymbolAddrStr << ' ';
}
@@ -314,8 +354,20 @@ static void darwinPrintSymbol(MachOObjectFile *MachO, SymbolListT::iterator I,
outs() << "(indirect) ";
break;
case MachO::N_SECT: {
- section_iterator Sec = MachO->section_end();
- MachO->getSymbolSection(I->Sym.getRawDataRefImpl(), Sec);
+ if (Obj.isIR()) {
+ // For llvm bitcode files print out a fake section name using the values
+ // use 1, 2 and 3 for section numbers as set above.
+ if (NSect == 1)
+ outs() << "(LTO,CODE) ";
+ else if (NSect == 2)
+ outs() << "(LTO,DATA) ";
+ else if (NSect == 3)
+ outs() << "(LTO,RODATA) ";
+ else
+ outs() << "(?,?) ";
+ break;
+ }
+ section_iterator Sec = *MachO->getSymbolSection(I->Sym.getRawDataRefImpl());
DataRefImpl Ref = Sec->getRawDataRefImpl();
StringRef SectionName;
MachO->getSectionName(Ref, SectionName);
@@ -374,7 +426,8 @@ static void darwinPrintSymbol(MachOObjectFile *MachO, SymbolListT::iterator I,
if ((NType & MachO::N_TYPE) == MachO::N_INDR) {
outs() << I->Name << " (for ";
StringRef IndirectName;
- if (MachO->getIndirectName(I->Sym.getRawDataRefImpl(), IndirectName))
+ if (!MachO ||
+ MachO->getIndirectName(I->Sym.getRawDataRefImpl(), IndirectName))
outs() << "?)";
else
outs() << IndirectName << ")";
@@ -392,7 +445,8 @@ static void darwinPrintSymbol(MachOObjectFile *MachO, SymbolListT::iterator I,
outs() << " (dynamically looked up)";
else {
StringRef LibraryName;
- if (MachO->getLibraryShortNameByIndex(LibraryOrdinal - 1, LibraryName))
+ if (!MachO ||
+ MachO->getLibraryShortNameByIndex(LibraryOrdinal - 1, LibraryName))
outs() << " (from bad library ordinal " << LibraryOrdinal << ")";
else
outs() << " (from " << LibraryName << ")";
@@ -440,13 +494,14 @@ static const struct DarwinStabName DarwinStabNames[] = {
{MachO::N_ECOMM, "ECOMM"},
{MachO::N_ECOML, "ECOML"},
{MachO::N_LENG, "LENG"},
- {0, 0}};
+ {0, nullptr}};
+
static const char *getDarwinStabString(uint8_t NType) {
for (unsigned i = 0; DarwinStabNames[i].Name; i++) {
if (DarwinStabNames[i].NType == NType)
return DarwinStabNames[i].Name;
}
- return 0;
+ return nullptr;
}
// darwinPrintStab() prints the n_sect, n_desc along with a symbolic name of
@@ -511,12 +566,14 @@ static void sortAndPrintSymbolList(SymbolicFile &Obj, bool printName,
}
}
- const char *printBlanks, *printFormat;
+ const char *printBlanks, *printDashes, *printFormat;
if (isSymbolList64Bit(Obj)) {
printBlanks = " ";
+ printDashes = "----------------";
printFormat = "%016" PRIx64;
} else {
printBlanks = " ";
+ printDashes = "--------";
printFormat = "%08" PRIx64;
}
@@ -528,6 +585,9 @@ static void sortAndPrintSymbolList(SymbolicFile &Obj, bool printName,
continue;
if (Undefined && DefinedOnly)
continue;
+ bool Global = SymFlags & SymbolRef::SF_Global;
+ if (!Global && ExternalOnly)
+ continue;
if (SizeSort && !PrintAddress)
continue;
if (PrintFileName) {
@@ -537,7 +597,8 @@ static void sortAndPrintSymbolList(SymbolicFile &Obj, bool printName,
outs() << ArchiveName << ":";
outs() << CurrentFilename << ": ";
}
- if (JustSymbolName || (UndefinedOnly && isa<MachOObjectFile>(Obj))) {
+ if ((JustSymbolName || (UndefinedOnly && isa<MachOObjectFile>(Obj) &&
+ OutputFormat != darwin)) && OutputFormat != posix) {
outs() << I->Name << "\n";
continue;
}
@@ -550,9 +611,13 @@ static void sortAndPrintSymbolList(SymbolicFile &Obj, bool printName,
if (OutputFormat == sysv)
strcpy(SymbolSizeStr, printBlanks);
- if (I->TypeChar != 'U')
- format(printFormat, I->Address)
+ if (I->TypeChar != 'U') {
+ if (Obj.isIR())
+ strcpy(SymbolAddrStr, printDashes);
+ else
+ format(printFormat, I->Address)
.print(SymbolAddrStr, sizeof(SymbolAddrStr));
+ }
format(printFormat, I->Size).print(SymbolSizeStr, sizeof(SymbolSizeStr));
// If OutputFormat is darwin or we are printing Mach-O symbols in hex and
@@ -561,11 +626,15 @@ static void sortAndPrintSymbolList(SymbolicFile &Obj, bool printName,
// printing Mach-O symbols in hex and not a Mach-O object fall back to
// OutputFormat bsd (see below).
MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(&Obj);
- if ((OutputFormat == darwin || FormatMachOasHex) && MachO) {
- darwinPrintSymbol(MachO, I, SymbolAddrStr, printBlanks);
+ if ((OutputFormat == darwin || FormatMachOasHex) && (MachO || Obj.isIR())) {
+ darwinPrintSymbol(Obj, I, SymbolAddrStr, printBlanks, printDashes,
+ printFormat);
} else if (OutputFormat == posix) {
- outs() << I->Name << " " << I->TypeChar << " " << SymbolAddrStr
- << SymbolSizeStr << "\n";
+ outs() << I->Name << " " << I->TypeChar << " ";
+ if (MachO)
+ outs() << I->Address << " " << "0" /* SymbolSizeStr */ << "\n";
+ else
+ outs() << SymbolAddrStr << SymbolSizeStr << "\n";
} else if (OutputFormat == bsd || (OutputFormat == darwin && !MachO)) {
if (PrintAddress)
outs() << SymbolAddrStr << ' ';
@@ -594,10 +663,11 @@ static char getSymbolNMTypeChar(ELFObjectFileBase &Obj,
// OK, this is ELF
elf_symbol_iterator SymI(I);
- elf_section_iterator SecI = Obj.section_end();
- if (error(SymI->getSection(SecI)))
+ ErrorOr<elf_section_iterator> SecIOrErr = SymI->getSection();
+ if (error(SecIOrErr.getError()))
return '?';
+ elf_section_iterator SecI = *SecIOrErr;
if (SecI != Obj.section_end()) {
switch (SecI->getType()) {
case ELF::SHT_PROGBITS:
@@ -651,9 +721,10 @@ static char getSymbolNMTypeChar(COFFObjectFile &Obj, symbol_iterator I) {
uint32_t Characteristics = 0;
if (!COFF::isReservedSectionNumber(Symb.getSectionNumber())) {
- section_iterator SecI = Obj.section_end();
- if (error(SymI->getSection(SecI)))
+ ErrorOr<section_iterator> SecIOrErr = SymI->getSection();
+ if (error(SecIOrErr.getError()))
return '?';
+ section_iterator SecI = *SecIOrErr;
const coff_section *Section = Obj.getCOFFSection(*SecI);
Characteristics = Section->Characteristics;
}
@@ -701,8 +772,7 @@ static char getSymbolNMTypeChar(MachOObjectFile &Obj, basic_symbol_iterator I) {
case MachO::N_INDR:
return 'i';
case MachO::N_SECT: {
- section_iterator Sec = Obj.section_end();
- Obj.getSymbolSection(Symb, Sec);
+ section_iterator Sec = *Obj.getSymbolSection(Symb);
DataRefImpl Ref = Sec->getRawDataRefImpl();
StringRef SectionName;
Obj.getSectionName(Ref, SectionName);
@@ -762,8 +832,14 @@ static char getNMTypeChar(SymbolicFile &Obj, basic_symbol_iterator I) {
char Ret = '?';
if (Symflags & object::SymbolRef::SF_Absolute)
Ret = 'a';
- else if (IRObjectFile *IR = dyn_cast<IRObjectFile>(&Obj))
+ else if (IRObjectFile *IR = dyn_cast<IRObjectFile>(&Obj)) {
Ret = getSymbolNMTypeChar(*IR, I);
+ Triple Host(sys::getDefaultTargetTriple());
+ if (Ret == 'd' && Host.isOSDarwin()) {
+ if(Symflags & SymbolRef::SF_Const)
+ Ret = 's';
+ }
+ }
else if (COFFObjectFile *COFF = dyn_cast<COFFObjectFile>(&Obj))
Ret = getSymbolNMTypeChar(*COFF, I);
else if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(&Obj))
@@ -943,10 +1019,10 @@ static void dumpSymbolNamesFromFile(std::string &Filename) {
if (I != E) {
outs() << "Archive map\n";
for (; I != E; ++I) {
- ErrorOr<Archive::child_iterator> C = I->getMember();
+ ErrorOr<Archive::Child> C = I->getMember();
if (error(C.getError()))
return;
- ErrorOr<StringRef> FileNameOrErr = C.get()->getName();
+ ErrorOr<StringRef> FileNameOrErr = C->getName();
if (error(FileNameOrErr.getError()))
return;
StringRef SymName = I->getName();
@@ -958,7 +1034,10 @@ static void dumpSymbolNamesFromFile(std::string &Filename) {
for (Archive::child_iterator I = A->child_begin(), E = A->child_end();
I != E; ++I) {
- ErrorOr<std::unique_ptr<Binary>> ChildOrErr = I->getAsBinary(&Context);
+ if (error(I->getError()))
+ return;
+ auto &C = I->get();
+ ErrorOr<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary(&Context);
if (ChildOrErr.getError())
continue;
if (SymbolicFile *O = dyn_cast<SymbolicFile>(&*ChildOrErr.get())) {
@@ -1013,8 +1092,11 @@ static void dumpSymbolNamesFromFile(std::string &Filename) {
for (Archive::child_iterator AI = A->child_begin(),
AE = A->child_end();
AI != AE; ++AI) {
+ if (error(AI->getError()))
+ return;
+ auto &C = AI->get();
ErrorOr<std::unique_ptr<Binary>> ChildOrErr =
- AI->getAsBinary(&Context);
+ C.getAsBinary(&Context);
if (ChildOrErr.getError())
continue;
if (SymbolicFile *O =
@@ -1067,8 +1149,11 @@ static void dumpSymbolNamesFromFile(std::string &Filename) {
for (Archive::child_iterator AI = A->child_begin(),
AE = A->child_end();
AI != AE; ++AI) {
+ if (error(AI->getError()))
+ return;
+ auto &C = AI->get();
ErrorOr<std::unique_ptr<Binary>> ChildOrErr =
- AI->getAsBinary(&Context);
+ C.getAsBinary(&Context);
if (ChildOrErr.getError())
continue;
if (SymbolicFile *O =
@@ -1116,8 +1201,10 @@ static void dumpSymbolNamesFromFile(std::string &Filename) {
std::unique_ptr<Archive> &A = *AOrErr;
for (Archive::child_iterator AI = A->child_begin(), AE = A->child_end();
AI != AE; ++AI) {
- ErrorOr<std::unique_ptr<Binary>> ChildOrErr =
- AI->getAsBinary(&Context);
+ if (error(AI->getError()))
+ return;
+ auto &C = AI->get();
+ ErrorOr<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary(&Context);
if (ChildOrErr.getError())
continue;
if (SymbolicFile *O = dyn_cast<SymbolicFile>(&*ChildOrErr.get())) {
@@ -1150,7 +1237,6 @@ static void dumpSymbolNamesFromFile(std::string &Filename) {
return;
}
error("unrecognizable file type", Filename);
- return;
}
int main(int argc, char **argv) {
diff --git a/tools/llvm-objdump/CMakeLists.txt b/tools/llvm-objdump/CMakeLists.txt
index 1f2721ab5452..d0dd4ac0182c 100644
--- a/tools/llvm-objdump/CMakeLists.txt
+++ b/tools/llvm-objdump/CMakeLists.txt
@@ -1,5 +1,8 @@
set(LLVM_LINK_COMPONENTS
- ${LLVM_TARGETS_TO_BUILD}
+ AllTargetsAsmPrinters
+ AllTargetsDescs
+ AllTargetsDisassemblers
+ AllTargetsInfos
CodeGen
DebugInfoDWARF
MC
diff --git a/tools/llvm-objdump/COFFDump.cpp b/tools/llvm-objdump/COFFDump.cpp
index 8b94a50ea28b..f286351614ac 100644
--- a/tools/llvm-objdump/COFFDump.cpp
+++ b/tools/llvm-objdump/COFFDump.cpp
@@ -151,7 +151,7 @@ static void printAllUnwindCodes(ArrayRef<UnwindCode> UCs) {
<< " remaining in buffer";
return ;
}
- printUnwindCode(ArrayRef<UnwindCode>(I, E));
+ printUnwindCode(makeArrayRef(I, E));
I += UsedSlots;
}
}
@@ -165,10 +165,10 @@ resolveSectionAndAddress(const COFFObjectFile *Obj, const SymbolRef &Sym,
if (std::error_code EC = ResolvedAddrOrErr.getError())
return EC;
ResolvedAddr = *ResolvedAddrOrErr;
- section_iterator iter(Obj->section_begin());
- if (std::error_code EC = Sym.getSection(iter))
+ ErrorOr<section_iterator> Iter = Sym.getSection();
+ if (std::error_code EC = Iter.getError())
return EC;
- ResolvedSection = Obj->getCOFFSection(*iter);
+ ResolvedSection = Obj->getCOFFSection(**Iter);
return std::error_code();
}
@@ -241,12 +241,10 @@ printSEHTable(const COFFObjectFile *Obj, uint32_t TableVA, int Count) {
return;
const pe32_header *PE32Header;
- if (error(Obj->getPE32Header(PE32Header)))
- return;
+ error(Obj->getPE32Header(PE32Header));
uint32_t ImageBase = PE32Header->ImageBase;
uintptr_t IntPtr = 0;
- if (error(Obj->getVaPtr(TableVA, IntPtr)))
- return;
+ error(Obj->getVaPtr(TableVA, IntPtr));
const support::ulittle32_t *P = (const support::ulittle32_t *)IntPtr;
outs() << "SEH Table:";
for (int I = 0; I < Count; ++I)
@@ -257,8 +255,7 @@ printSEHTable(const COFFObjectFile *Obj, uint32_t TableVA, int Count) {
static void printLoadConfiguration(const COFFObjectFile *Obj) {
// Skip if it's not executable.
const pe32_header *PE32Header;
- if (error(Obj->getPE32Header(PE32Header)))
- return;
+ error(Obj->getPE32Header(PE32Header));
if (!PE32Header)
return;
@@ -267,13 +264,11 @@ static void printLoadConfiguration(const COFFObjectFile *Obj) {
return;
const data_directory *DataDir;
- if (error(Obj->getDataDirectory(COFF::LOAD_CONFIG_TABLE, DataDir)))
- return;
+ error(Obj->getDataDirectory(COFF::LOAD_CONFIG_TABLE, DataDir));
uintptr_t IntPtr = 0;
if (DataDir->RelativeVirtualAddress == 0)
return;
- if (error(Obj->getRvaPtr(DataDir->RelativeVirtualAddress, IntPtr)))
- return;
+ error(Obj->getRvaPtr(DataDir->RelativeVirtualAddress, IntPtr));
auto *LoadConf = reinterpret_cast<const coff_load_configuration32 *>(IntPtr);
outs() << "Load configuration:"
@@ -381,8 +376,7 @@ static bool getPDataSection(const COFFObjectFile *Obj,
const RuntimeFunction *&RFStart, int &NumRFs) {
for (const SectionRef &Section : Obj->sections()) {
StringRef Name;
- if (error(Section.getName(Name)))
- continue;
+ error(Section.getName(Name));
if (Name != ".pdata")
continue;
@@ -394,8 +388,7 @@ static bool getPDataSection(const COFFObjectFile *Obj,
std::sort(Rels.begin(), Rels.end(), RelocAddressLess);
ArrayRef<uint8_t> Contents;
- if (error(Obj->getSectionContents(Pdata, Contents)))
- continue;
+ error(Obj->getSectionContents(Pdata, Contents));
if (Contents.empty())
continue;
@@ -440,7 +433,7 @@ static void printWin64EHUnwindInfo(const Win64EH::UnwindInfo *UI) {
if (UI->NumCodes)
outs() << " Unwind Codes:\n";
- printAllUnwindCodes(ArrayRef<UnwindCode>(&UI->UnwindCodes[0], UI->NumCodes));
+ printAllUnwindCodes(makeArrayRef(&UI->UnwindCodes[0], UI->NumCodes));
outs() << "\n";
outs().flush();
@@ -499,11 +492,10 @@ static void printRuntimeFunctionRels(const COFFObjectFile *Obj,
ArrayRef<uint8_t> XContents;
uint64_t UnwindInfoOffset = 0;
- if (error(getSectionContents(
+ error(getSectionContents(
Obj, Rels, SectionOffset +
/*offsetof(RuntimeFunction, UnwindInfoOffset)*/ 8,
- XContents, UnwindInfoOffset)))
- return;
+ XContents, UnwindInfoOffset));
if (XContents.empty())
return;
@@ -550,3 +542,52 @@ void llvm::printCOFFFileHeader(const object::ObjectFile *Obj) {
printImportTables(file);
printExportTable(file);
}
+
+void llvm::printCOFFSymbolTable(const COFFObjectFile *coff) {
+ for (unsigned SI = 0, SE = coff->getNumberOfSymbols(); SI != SE; ++SI) {
+ ErrorOr<COFFSymbolRef> Symbol = coff->getSymbol(SI);
+ StringRef Name;
+ error(Symbol.getError());
+ error(coff->getSymbolName(*Symbol, Name));
+
+ outs() << "[" << format("%2d", SI) << "]"
+ << "(sec " << format("%2d", int(Symbol->getSectionNumber())) << ")"
+ << "(fl 0x00)" // Flag bits, which COFF doesn't have.
+ << "(ty " << format("%3x", unsigned(Symbol->getType())) << ")"
+ << "(scl " << format("%3x", unsigned(Symbol->getStorageClass())) << ") "
+ << "(nx " << unsigned(Symbol->getNumberOfAuxSymbols()) << ") "
+ << "0x" << format("%08x", unsigned(Symbol->getValue())) << " "
+ << Name << "\n";
+
+ for (unsigned AI = 0, AE = Symbol->getNumberOfAuxSymbols(); AI < AE; ++AI, ++SI) {
+ if (Symbol->isSectionDefinition()) {
+ const coff_aux_section_definition *asd;
+ error(coff->getAuxSymbol<coff_aux_section_definition>(SI + 1, asd));
+
+ int32_t AuxNumber = asd->getNumber(Symbol->isBigObj());
+
+ outs() << "AUX "
+ << format("scnlen 0x%x nreloc %d nlnno %d checksum 0x%x "
+ , unsigned(asd->Length)
+ , unsigned(asd->NumberOfRelocations)
+ , unsigned(asd->NumberOfLinenumbers)
+ , unsigned(asd->CheckSum))
+ << format("assoc %d comdat %d\n"
+ , unsigned(AuxNumber)
+ , unsigned(asd->Selection));
+ } else if (Symbol->isFileRecord()) {
+ const char *FileName;
+ error(coff->getAuxSymbol<char>(SI + 1, FileName));
+
+ StringRef Name(FileName, Symbol->getNumberOfAuxSymbols() *
+ coff->getSymbolTableEntrySize());
+ outs() << "AUX " << Name.rtrim(StringRef("\0", 1)) << '\n';
+
+ SI = SI + Symbol->getNumberOfAuxSymbols();
+ break;
+ } else {
+ outs() << "AUX Unknown\n";
+ }
+ }
+ }
+}
diff --git a/tools/llvm-objdump/ELFDump.cpp b/tools/llvm-objdump/ELFDump.cpp
index 2d0d7d7db0f3..7b44e3916c48 100644
--- a/tools/llvm-objdump/ELFDump.cpp
+++ b/tools/llvm-objdump/ELFDump.cpp
@@ -24,10 +24,8 @@ using namespace llvm::object;
template <class ELFT> void printProgramHeaders(const ELFFile<ELFT> *o) {
typedef ELFFile<ELFT> ELFO;
outs() << "Program Header:\n";
- for (typename ELFO::Elf_Phdr_Iter pi = o->program_header_begin(),
- pe = o->program_header_end();
- pi != pe; ++pi) {
- switch (pi->p_type) {
+ for (const typename ELFO::Elf_Phdr &Phdr : o->program_headers()) {
+ switch (Phdr.p_type) {
case ELF::PT_LOAD:
outs() << " LOAD ";
break;
@@ -55,22 +53,16 @@ template <class ELFT> void printProgramHeaders(const ELFFile<ELFT> *o) {
const char *Fmt = ELFT::Is64Bits ? "0x%016" PRIx64 " " : "0x%08" PRIx64 " ";
- outs() << "off "
- << format(Fmt, (uint64_t)pi->p_offset)
- << "vaddr "
- << format(Fmt, (uint64_t)pi->p_vaddr)
- << "paddr "
- << format(Fmt, (uint64_t)pi->p_paddr)
- << format("align 2**%u\n", countTrailingZeros<uint64_t>(pi->p_align))
- << " filesz "
- << format(Fmt, (uint64_t)pi->p_filesz)
- << "memsz "
- << format(Fmt, (uint64_t)pi->p_memsz)
- << "flags "
- << ((pi->p_flags & ELF::PF_R) ? "r" : "-")
- << ((pi->p_flags & ELF::PF_W) ? "w" : "-")
- << ((pi->p_flags & ELF::PF_X) ? "x" : "-")
- << "\n";
+ outs() << "off " << format(Fmt, (uint64_t)Phdr.p_offset) << "vaddr "
+ << format(Fmt, (uint64_t)Phdr.p_vaddr) << "paddr "
+ << format(Fmt, (uint64_t)Phdr.p_paddr)
+ << format("align 2**%u\n",
+ countTrailingZeros<uint64_t>(Phdr.p_align))
+ << " filesz " << format(Fmt, (uint64_t)Phdr.p_filesz)
+ << "memsz " << format(Fmt, (uint64_t)Phdr.p_memsz) << "flags "
+ << ((Phdr.p_flags & ELF::PF_R) ? "r" : "-")
+ << ((Phdr.p_flags & ELF::PF_W) ? "w" : "-")
+ << ((Phdr.p_flags & ELF::PF_X) ? "x" : "-") << "\n";
}
outs() << "\n";
}
diff --git a/tools/llvm-objdump/MachODump.cpp b/tools/llvm-objdump/MachODump.cpp
index 04c72f4856c8..a2f3bc8e8a7c 100644
--- a/tools/llvm-objdump/MachODump.cpp
+++ b/tools/llvm-objdump/MachODump.cpp
@@ -13,6 +13,7 @@
#include "llvm-objdump.h"
#include "llvm-c/Disassembler.h"
+#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/Triple.h"
@@ -97,11 +98,6 @@ cl::opt<bool>
cl::desc("Print the linker optimization hints for "
"Mach-O objects (requires -macho)"));
-cl::list<std::string>
- llvm::DumpSections("section",
- cl::desc("Prints the specified segment,section for "
- "Mach-O objects (requires -macho)"));
-
cl::opt<bool>
llvm::InfoPlist("info-plist",
cl::desc("Print the info plist section as strings for "
@@ -138,6 +134,7 @@ static cl::opt<bool> NoSymbolicOperands(
static cl::list<std::string>
ArchFlags("arch", cl::desc("architecture(s) from a Mach-O file to dump"),
cl::ZeroOrMore);
+
bool ArchAll = false;
static std::string ThumbTripleName;
@@ -210,19 +207,19 @@ static uint64_t DumpDataInCode(const uint8_t *bytes, uint64_t Length,
case MachO::DICE_KIND_DATA:
if (Length >= 4) {
if (!NoShowRawInsn)
- dumpBytes(ArrayRef<uint8_t>(bytes, 4), outs());
+ dumpBytes(makeArrayRef(bytes, 4), outs());
Value = bytes[3] << 24 | bytes[2] << 16 | bytes[1] << 8 | bytes[0];
outs() << "\t.long " << Value;
Size = 4;
} else if (Length >= 2) {
if (!NoShowRawInsn)
- dumpBytes(ArrayRef<uint8_t>(bytes, 2), outs());
+ dumpBytes(makeArrayRef(bytes, 2), outs());
Value = bytes[1] << 8 | bytes[0];
outs() << "\t.short " << Value;
Size = 2;
} else {
if (!NoShowRawInsn)
- dumpBytes(ArrayRef<uint8_t>(bytes, 2), outs());
+ dumpBytes(makeArrayRef(bytes, 2), outs());
Value = bytes[0];
outs() << "\t.byte " << Value;
Size = 1;
@@ -234,14 +231,14 @@ static uint64_t DumpDataInCode(const uint8_t *bytes, uint64_t Length,
break;
case MachO::DICE_KIND_JUMP_TABLE8:
if (!NoShowRawInsn)
- dumpBytes(ArrayRef<uint8_t>(bytes, 1), outs());
+ dumpBytes(makeArrayRef(bytes, 1), outs());
Value = bytes[0];
outs() << "\t.byte " << format("%3u", Value) << "\t@ KIND_JUMP_TABLE8\n";
Size = 1;
break;
case MachO::DICE_KIND_JUMP_TABLE16:
if (!NoShowRawInsn)
- dumpBytes(ArrayRef<uint8_t>(bytes, 2), outs());
+ dumpBytes(makeArrayRef(bytes, 2), outs());
Value = bytes[1] << 8 | bytes[0];
outs() << "\t.short " << format("%5u", Value & 0xffff)
<< "\t@ KIND_JUMP_TABLE16\n";
@@ -250,7 +247,7 @@ static uint64_t DumpDataInCode(const uint8_t *bytes, uint64_t Length,
case MachO::DICE_KIND_JUMP_TABLE32:
case MachO::DICE_KIND_ABS_JUMP_TABLE32:
if (!NoShowRawInsn)
- dumpBytes(ArrayRef<uint8_t>(bytes, 4), outs());
+ dumpBytes(makeArrayRef(bytes, 4), outs());
Value = bytes[3] << 24 | bytes[2] << 16 | bytes[1] << 8 | bytes[0];
outs() << "\t.long " << Value;
if (Kind == MachO::DICE_KIND_JUMP_TABLE32)
@@ -670,13 +667,9 @@ static void DumpLiteral8(MachOObjectFile *O, uint32_t l0, uint32_t l1,
double d) {
outs() << format("0x%08" PRIx32, l0) << " " << format("0x%08" PRIx32, l1);
uint32_t Hi, Lo;
- if (O->isLittleEndian()) {
- Hi = l1;
- Lo = l0;
- } else {
- Hi = l0;
- Lo = l1;
- }
+ Hi = (O->isLittleEndian()) ? l1 : l0;
+ Lo = (O->isLittleEndian()) ? l0 : l1;
+
// Hi is the high word, so this is equivalent to if(isfinite(d))
if ((Hi & 0x7ff00000) != 0x7ff00000)
outs() << format(" (%.16e)\n", d);
@@ -921,10 +914,7 @@ static void DumpInitTermPointerSection(MachOObjectFile *O, const char *sect,
SymbolAddressMap *AddrMap,
bool verbose) {
uint32_t stride;
- if (O->is64Bit())
- stride = sizeof(uint64_t);
- else
- stride = sizeof(uint32_t);
+ stride = (O->is64Bit()) ? sizeof(uint64_t) : sizeof(uint32_t);
for (uint32_t i = 0; i < sect_size; i += stride) {
const char *SymbolName = nullptr;
if (O->is64Bit()) {
@@ -1006,8 +996,8 @@ static void DumpSectionContents(StringRef Filename, MachOObjectFile *O,
if (verbose)
CreateSymbolAddressMap(O, &AddrMap);
- for (unsigned i = 0; i < DumpSections.size(); ++i) {
- StringRef DumpSection = DumpSections[i];
+ for (unsigned i = 0; i < FilterSections.size(); ++i) {
+ StringRef DumpSection = FilterSections[i];
std::pair<StringRef, StringRef> DumpSegSectName;
DumpSegSectName = DumpSection.split(',');
StringRef DumpSegName, DumpSectName;
@@ -1171,7 +1161,7 @@ static void ProcessMachO(StringRef Filename, MachOObjectFile *MachOOF,
// UniversalHeaders or ArchiveHeaders.
if (Disassemble || PrivateHeaders || ExportsTrie || Rebase || Bind ||
LazyBind || WeakBind || IndirectSymbols || DataInCode || LinkOptHints ||
- DylibsUsed || DylibId || ObjcMetaData || (DumpSections.size() != 0)) {
+ DylibsUsed || DylibId || ObjcMetaData || (FilterSections.size() != 0)) {
outs() << Filename;
if (!ArchiveMemberName.empty())
outs() << '(' << ArchiveMemberName << ')';
@@ -1194,7 +1184,7 @@ static void ProcessMachO(StringRef Filename, MachOObjectFile *MachOOF,
PrintSectionHeaders(MachOOF);
if (SectionContents)
PrintSectionContents(MachOOF);
- if (DumpSections.size() != 0)
+ if (FilterSections.size() != 0)
DumpSectionContents(Filename, MachOOF, !NonVerbose);
if (InfoPlist)
DumpInfoPlistSectionContents(Filename, MachOOF);
@@ -1395,7 +1385,7 @@ static void printMachOUniversalHeaders(const object::MachOUniversalBinary *UB,
}
}
-static void printArchiveChild(Archive::Child &C, bool verbose,
+static void printArchiveChild(const Archive::Child &C, bool verbose,
bool print_offset) {
if (print_offset)
outs() << C.getChildOffset() << "\t";
@@ -1404,42 +1394,15 @@ static void printArchiveChild(Archive::Child &C, bool verbose,
// FIXME: this first dash, "-", is for (Mode & S_IFMT) == S_IFREG.
// But there is nothing in sys::fs::perms for S_IFMT or S_IFREG.
outs() << "-";
- if (Mode & sys::fs::owner_read)
- outs() << "r";
- else
- outs() << "-";
- if (Mode & sys::fs::owner_write)
- outs() << "w";
- else
- outs() << "-";
- if (Mode & sys::fs::owner_exe)
- outs() << "x";
- else
- outs() << "-";
- if (Mode & sys::fs::group_read)
- outs() << "r";
- else
- outs() << "-";
- if (Mode & sys::fs::group_write)
- outs() << "w";
- else
- outs() << "-";
- if (Mode & sys::fs::group_exe)
- outs() << "x";
- else
- outs() << "-";
- if (Mode & sys::fs::others_read)
- outs() << "r";
- else
- outs() << "-";
- if (Mode & sys::fs::others_write)
- outs() << "w";
- else
- outs() << "-";
- if (Mode & sys::fs::others_exe)
- outs() << "x";
- else
- outs() << "-";
+ outs() << ((Mode & sys::fs::owner_read) ? "r" : "-");
+ outs() << ((Mode & sys::fs::owner_write) ? "w" : "-");
+ outs() << ((Mode & sys::fs::owner_exe) ? "x" : "-");
+ outs() << ((Mode & sys::fs::group_read) ? "r" : "-");
+ outs() << ((Mode & sys::fs::group_write) ? "w" : "-");
+ outs() << ((Mode & sys::fs::group_exe) ? "x" : "-");
+ outs() << ((Mode & sys::fs::others_read) ? "r" : "-");
+ outs() << ((Mode & sys::fs::others_write) ? "w" : "-");
+ outs() << ((Mode & sys::fs::others_exe) ? "x" : "-");
} else {
outs() << format("0%o ", Mode);
}
@@ -1448,8 +1411,10 @@ static void printArchiveChild(Archive::Child &C, bool verbose,
outs() << format("%3d/", UID);
unsigned GID = C.getGID();
outs() << format("%-3d ", GID);
- uint64_t Size = C.getRawSize();
- outs() << format("%5" PRId64, Size) << " ";
+ ErrorOr<uint64_t> Size = C.getRawSize();
+ if (std::error_code EC = Size.getError())
+ report_fatal_error(EC.message());
+ outs() << format("%5" PRId64, Size.get()) << " ";
StringRef RawLastModified = C.getRawLastModified();
if (verbose) {
@@ -1483,14 +1448,11 @@ static void printArchiveChild(Archive::Child &C, bool verbose,
}
static void printArchiveHeaders(Archive *A, bool verbose, bool print_offset) {
- if (A->hasSymbolTable()) {
- Archive::child_iterator S = A->getSymbolTableChild();
- Archive::Child C = *S;
- printArchiveChild(C, verbose, print_offset);
- }
- for (Archive::child_iterator I = A->child_begin(), E = A->child_end(); I != E;
- ++I) {
- Archive::Child C = *I;
+ for (Archive::child_iterator I = A->child_begin(false), E = A->child_end();
+ I != E; ++I) {
+ if (std::error_code EC = I->getError())
+ report_fatal_error(EC.message());
+ const Archive::Child &C = **I;
printArchiveChild(C, verbose, print_offset);
}
}
@@ -1527,7 +1489,10 @@ void llvm::ParseInputMachO(StringRef Filename) {
printArchiveHeaders(A, !NonVerbose, ArchiveMemberOffsets);
for (Archive::child_iterator I = A->child_begin(), E = A->child_end();
I != E; ++I) {
- ErrorOr<std::unique_ptr<Binary>> ChildOrErr = I->getAsBinary();
+ if (std::error_code EC = I->getError())
+ report_error(Filename, EC);
+ auto &C = I->get();
+ ErrorOr<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
if (ChildOrErr.getError())
continue;
if (MachOObjectFile *O = dyn_cast<MachOObjectFile>(&*ChildOrErr.get())) {
@@ -1575,7 +1540,10 @@ void llvm::ParseInputMachO(StringRef Filename) {
for (Archive::child_iterator AI = A->child_begin(),
AE = A->child_end();
AI != AE; ++AI) {
- ErrorOr<std::unique_ptr<Binary>> ChildOrErr = AI->getAsBinary();
+ if (std::error_code EC = AI->getError())
+ report_error(Filename, EC);
+ auto &C = AI->get();
+ ErrorOr<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
if (ChildOrErr.getError())
continue;
if (MachOObjectFile *O =
@@ -1617,7 +1585,10 @@ void llvm::ParseInputMachO(StringRef Filename) {
for (Archive::child_iterator AI = A->child_begin(),
AE = A->child_end();
AI != AE; ++AI) {
- ErrorOr<std::unique_ptr<Binary>> ChildOrErr = AI->getAsBinary();
+ if (std::error_code EC = AI->getError())
+ report_error(Filename, EC);
+ auto &C = AI->get();
+ ErrorOr<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
if (ChildOrErr.getError())
continue;
if (MachOObjectFile *O =
@@ -1653,7 +1624,10 @@ void llvm::ParseInputMachO(StringRef Filename) {
printArchiveHeaders(A.get(), !NonVerbose, ArchiveMemberOffsets);
for (Archive::child_iterator AI = A->child_begin(), AE = A->child_end();
AI != AE; ++AI) {
- ErrorOr<std::unique_ptr<Binary>> ChildOrErr = AI->getAsBinary();
+ if (std::error_code EC = AI->getError())
+ report_error(Filename, EC);
+ auto &C = AI->get();
+ ErrorOr<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
if (ChildOrErr.getError())
continue;
if (MachOObjectFile *O =
@@ -1676,8 +1650,7 @@ void llvm::ParseInputMachO(StringRef Filename) {
errs() << "llvm-objdump: '" << Filename << "': "
<< "Object is not a Mach-O file type.\n";
} else
- errs() << "llvm-objdump: '" << Filename << "': "
- << "Unrecognized file type.\n";
+ report_error(Filename, object_error::invalid_file_type);
}
typedef std::pair<uint64_t, const char *> BindInfoEntry;
@@ -1698,6 +1671,7 @@ struct DisassembleInfo {
uint64_t adrp_addr;
uint32_t adrp_inst;
BindTable *bindtable;
+ uint32_t depth;
};
// SymbolizerGetOpInfo() is the operand information call back function.
@@ -1735,8 +1709,15 @@ static int SymbolizerGetOpInfo(void *DisInfo, uint64_t Pc, uint64_t Offset,
if (Arch == Triple::x86) {
if (Size != 1 && Size != 2 && Size != 4 && Size != 0)
return 0;
- // First search the section's relocation entries (if any) for an entry
- // for this section offset.
+ if (info->O->getHeader().filetype != MachO::MH_OBJECT) {
+ // TODO:
+ // Search the external relocation entries of a fully linked image
+ // (if any) for an entry that matches this segment offset.
+ // uint32_t seg_offset = (Pc + Offset);
+ return 0;
+ }
+ // In MH_OBJECT filetypes search the section's relocation entries (if any)
+ // for an entry for this section offset.
uint32_t sect_addr = info->S.getAddress();
uint32_t sect_offset = (Pc + Offset) - sect_addr;
bool reloc_found = false;
@@ -1806,17 +1787,20 @@ static int SymbolizerGetOpInfo(void *DisInfo, uint64_t Pc, uint64_t Offset,
op_info->Value = offset;
return 1;
}
- // TODO:
- // Second search the external relocation entries of a fully linked image
- // (if any) for an entry that matches this segment offset.
- // uint32_t seg_offset = (Pc + Offset);
return 0;
}
if (Arch == Triple::x86_64) {
if (Size != 1 && Size != 2 && Size != 4 && Size != 0)
return 0;
- // First search the section's relocation entries (if any) for an entry
- // for this section offset.
+ if (info->O->getHeader().filetype != MachO::MH_OBJECT) {
+ // TODO:
+ // Search the external relocation entries of a fully linked image
+ // (if any) for an entry that matches this segment offset.
+ // uint64_t seg_offset = (Pc + Offset);
+ return 0;
+ }
+ // In MH_OBJECT filetypes search the section's relocation entries (if any)
+ // for an entry for this section offset.
uint64_t sect_addr = info->S.getAddress();
uint64_t sect_offset = (Pc + Offset) - sect_addr;
bool reloc_found = false;
@@ -1874,17 +1858,20 @@ static int SymbolizerGetOpInfo(void *DisInfo, uint64_t Pc, uint64_t Offset,
op_info->AddSymbol.Name = name;
return 1;
}
- // TODO:
- // Second search the external relocation entries of a fully linked image
- // (if any) for an entry that matches this segment offset.
- // uint64_t seg_offset = (Pc + Offset);
return 0;
}
if (Arch == Triple::arm) {
if (Offset != 0 || (Size != 4 && Size != 2))
return 0;
- // First search the section's relocation entries (if any) for an entry
- // for this section offset.
+ if (info->O->getHeader().filetype != MachO::MH_OBJECT) {
+ // TODO:
+ // Search the external relocation entries of a fully linked image
+ // (if any) for an entry that matches this segment offset.
+ // uint32_t seg_offset = (Pc + Offset);
+ return 0;
+ }
+ // In MH_OBJECT filetypes search the section's relocation entries (if any)
+ // for an entry for this section offset.
uint32_t sect_addr = info->S.getAddress();
uint32_t sect_offset = (Pc + Offset) - sect_addr;
DataRefImpl Rel;
@@ -2016,8 +2003,15 @@ static int SymbolizerGetOpInfo(void *DisInfo, uint64_t Pc, uint64_t Offset,
if (Arch == Triple::aarch64) {
if (Offset != 0 || Size != 4)
return 0;
- // First search the section's relocation entries (if any) for an entry
- // for this section offset.
+ if (info->O->getHeader().filetype != MachO::MH_OBJECT) {
+ // TODO:
+ // Search the external relocation entries of a fully linked image
+ // (if any) for an entry that matches this segment offset.
+ // uint64_t seg_offset = (Pc + Offset);
+ return 0;
+ }
+ // In MH_OBJECT filetypes search the section's relocation entries (if any)
+ // for an entry for this section offset.
uint64_t sect_addr = info->S.getAddress();
uint64_t sect_offset = (Pc + Offset) - sect_addr;
auto Reloc =
@@ -2370,6 +2364,8 @@ static const char *get_pointer_64(uint64_t Address, uint32_t &offset,
for (unsigned SectIdx = 0; SectIdx != info->Sections->size(); SectIdx++) {
uint64_t SectAddress = ((*(info->Sections))[SectIdx]).getAddress();
uint64_t SectSize = ((*(info->Sections))[SectIdx]).getSize();
+ if (SectSize == 0)
+ continue;
if (objc_only) {
StringRef SectName;
((*(info->Sections))[SectIdx]).getName(SectName);
@@ -3267,6 +3263,8 @@ walk_pointer_list_32(const char *listname, const SectionRef S,
}
static void print_layout_map(const char *layout_map, uint32_t left) {
+ if (layout_map == nullptr)
+ return;
outs() << " layout map: ";
do {
outs() << format("0x%02" PRIx32, (*layout_map) & 0xff) << " ";
@@ -3330,8 +3328,8 @@ static void print_method_list64_t(uint64_t p, struct DisassembleInfo *info,
return;
memset(&m, '\0', sizeof(struct method64_t));
if (left < sizeof(struct method64_t)) {
- memcpy(&ml, r, left);
- outs() << indent << " (method_t entends past the end of the section)\n";
+ memcpy(&m, r, left);
+ outs() << indent << " (method_t extends past the end of the section)\n";
} else
memcpy(&m, r, sizeof(struct method64_t));
if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
@@ -4222,7 +4220,7 @@ static void print_objc_property_list32(uint32_t p,
}
}
-static void print_class_ro64_t(uint64_t p, struct DisassembleInfo *info,
+static bool print_class_ro64_t(uint64_t p, struct DisassembleInfo *info,
bool &is_meta_class) {
struct class_ro64_t cro;
const char *r;
@@ -4233,7 +4231,7 @@ static void print_class_ro64_t(uint64_t p, struct DisassembleInfo *info,
r = get_pointer_64(p, offset, left, S, info);
if (r == nullptr || left < sizeof(struct class_ro64_t))
- return;
+ return false;
memset(&cro, '\0', sizeof(struct class_ro64_t));
if (left < sizeof(struct class_ro64_t)) {
memcpy(&cro, r, left);
@@ -4357,10 +4355,11 @@ static void print_class_ro64_t(uint64_t p, struct DisassembleInfo *info,
if (cro.baseProperties + n_value != 0)
print_objc_property_list64(cro.baseProperties + n_value, info);
- is_meta_class = (cro.flags & RO_META) ? true : false;
+ is_meta_class = (cro.flags & RO_META) != 0;
+ return true;
}
-static void print_class_ro32_t(uint32_t p, struct DisassembleInfo *info,
+static bool print_class_ro32_t(uint32_t p, struct DisassembleInfo *info,
bool &is_meta_class) {
struct class_ro32_t cro;
const char *r;
@@ -4370,7 +4369,7 @@ static void print_class_ro32_t(uint32_t p, struct DisassembleInfo *info,
r = get_pointer_32(p, offset, left, S, info);
if (r == nullptr)
- return;
+ return false;
memset(&cro, '\0', sizeof(struct class_ro32_t));
if (left < sizeof(struct class_ro32_t)) {
memcpy(&cro, r, left);
@@ -4420,7 +4419,8 @@ static void print_class_ro32_t(uint32_t p, struct DisassembleInfo *info,
<< format("0x%" PRIx32, cro.baseProperties) << "\n";
if (cro.baseProperties != 0)
print_objc_property_list32(cro.baseProperties, info);
- is_meta_class = (cro.flags & RO_META) ? true : false;
+ is_meta_class = (cro.flags & RO_META) != 0;
+ return true;
}
static void print_class64_t(uint64_t p, struct DisassembleInfo *info) {
@@ -4490,11 +4490,16 @@ static void print_class64_t(uint64_t p, struct DisassembleInfo *info) {
outs() << " Swift class";
outs() << "\n";
bool is_meta_class;
- print_class_ro64_t((c.data + n_value) & ~0x7, info, is_meta_class);
+ if (!print_class_ro64_t((c.data + n_value) & ~0x7, info, is_meta_class))
+ return;
- if (is_meta_class == false) {
- outs() << "Meta Class\n";
- print_class64_t(c.isa + isa_n_value, info);
+ if (!is_meta_class &&
+ c.isa + isa_n_value != p &&
+ c.isa + isa_n_value != 0 &&
+ info->depth < 100) {
+ info->depth++;
+ outs() << "Meta Class\n";
+ print_class64_t(c.isa + isa_n_value, info);
}
}
@@ -4555,9 +4560,10 @@ static void print_class32_t(uint32_t p, struct DisassembleInfo *info) {
outs() << " Swift class";
outs() << "\n";
bool is_meta_class;
- print_class_ro32_t(c.data & ~0x3, info, is_meta_class);
+ if (!print_class_ro32_t(c.data & ~0x3, info, is_meta_class))
+ return;
- if (is_meta_class == false) {
+ if (!is_meta_class) {
outs() << "Meta Class\n";
print_class32_t(c.isa, info);
}
@@ -4865,7 +4871,7 @@ static void print_category32_t(uint32_t p, struct DisassembleInfo *info) {
outs() << " name " << format("0x%" PRIx32, c.name);
name = get_symbol_32(offset + offsetof(struct category32_t, name), S, info,
c.name);
- if (name != NULL)
+ if (name)
outs() << " " << name;
outs() << "\n";
@@ -5006,6 +5012,9 @@ static void print_image_info64(SectionRef S, struct DisassembleInfo *info) {
struct objc_image_info64 o;
const char *r;
+ if (S == SectionRef())
+ return;
+
StringRef SectName;
S.getName(SectName);
DataRefImpl Ref = S.getRawDataRefImpl();
@@ -5142,75 +5151,48 @@ static void printObjc2_64bit_MetaData(MachOObjectFile *O, bool verbose) {
info.adrp_addr = 0;
info.adrp_inst = 0;
- const SectionRef CL = get_section(O, "__OBJC2", "__class_list");
- if (CL != SectionRef()) {
- info.S = CL;
- walk_pointer_list_64("class", CL, O, &info, print_class64_t);
- } else {
- const SectionRef CL = get_section(O, "__DATA", "__objc_classlist");
- info.S = CL;
- walk_pointer_list_64("class", CL, O, &info, print_class64_t);
- }
+ info.depth = 0;
+ SectionRef CL = get_section(O, "__OBJC2", "__class_list");
+ if (CL == SectionRef())
+ CL = get_section(O, "__DATA", "__objc_classlist");
+ info.S = CL;
+ walk_pointer_list_64("class", CL, O, &info, print_class64_t);
- const SectionRef CR = get_section(O, "__OBJC2", "__class_refs");
- if (CR != SectionRef()) {
- info.S = CR;
- walk_pointer_list_64("class refs", CR, O, &info, nullptr);
- } else {
- const SectionRef CR = get_section(O, "__DATA", "__objc_classrefs");
- info.S = CR;
- walk_pointer_list_64("class refs", CR, O, &info, nullptr);
- }
+ SectionRef CR = get_section(O, "__OBJC2", "__class_refs");
+ if (CR == SectionRef())
+ CR = get_section(O, "__DATA", "__objc_classrefs");
+ info.S = CR;
+ walk_pointer_list_64("class refs", CR, O, &info, nullptr);
- const SectionRef SR = get_section(O, "__OBJC2", "__super_refs");
- if (SR != SectionRef()) {
- info.S = SR;
- walk_pointer_list_64("super refs", SR, O, &info, nullptr);
- } else {
- const SectionRef SR = get_section(O, "__DATA", "__objc_superrefs");
- info.S = SR;
- walk_pointer_list_64("super refs", SR, O, &info, nullptr);
- }
+ SectionRef SR = get_section(O, "__OBJC2", "__super_refs");
+ if (SR == SectionRef())
+ SR = get_section(O, "__DATA", "__objc_superrefs");
+ info.S = SR;
+ walk_pointer_list_64("super refs", SR, O, &info, nullptr);
- const SectionRef CA = get_section(O, "__OBJC2", "__category_list");
- if (CA != SectionRef()) {
- info.S = CA;
- walk_pointer_list_64("category", CA, O, &info, print_category64_t);
- } else {
- const SectionRef CA = get_section(O, "__DATA", "__objc_catlist");
- info.S = CA;
- walk_pointer_list_64("category", CA, O, &info, print_category64_t);
- }
+ SectionRef CA = get_section(O, "__OBJC2", "__category_list");
+ if (CA == SectionRef())
+ CA = get_section(O, "__DATA", "__objc_catlist");
+ info.S = CA;
+ walk_pointer_list_64("category", CA, O, &info, print_category64_t);
- const SectionRef PL = get_section(O, "__OBJC2", "__protocol_list");
- if (PL != SectionRef()) {
- info.S = PL;
- walk_pointer_list_64("protocol", PL, O, &info, nullptr);
- } else {
- const SectionRef PL = get_section(O, "__DATA", "__objc_protolist");
- info.S = PL;
- walk_pointer_list_64("protocol", PL, O, &info, nullptr);
- }
+ SectionRef PL = get_section(O, "__OBJC2", "__protocol_list");
+ if (PL == SectionRef())
+ PL = get_section(O, "__DATA", "__objc_protolist");
+ info.S = PL;
+ walk_pointer_list_64("protocol", PL, O, &info, nullptr);
- const SectionRef MR = get_section(O, "__OBJC2", "__message_refs");
- if (MR != SectionRef()) {
- info.S = MR;
- print_message_refs64(MR, &info);
- } else {
- const SectionRef MR = get_section(O, "__DATA", "__objc_msgrefs");
- info.S = MR;
- print_message_refs64(MR, &info);
- }
+ SectionRef MR = get_section(O, "__OBJC2", "__message_refs");
+ if (MR == SectionRef())
+ MR = get_section(O, "__DATA", "__objc_msgrefs");
+ info.S = MR;
+ print_message_refs64(MR, &info);
- const SectionRef II = get_section(O, "__OBJC2", "__image_info");
- if (II != SectionRef()) {
- info.S = II;
- print_image_info64(II, &info);
- } else {
- const SectionRef II = get_section(O, "__DATA", "__objc_imageinfo");
- info.S = II;
- print_image_info64(II, &info);
- }
+ SectionRef II = get_section(O, "__OBJC2", "__image_info");
+ if (II == SectionRef())
+ II = get_section(O, "__DATA", "__objc_imageinfo");
+ info.S = II;
+ print_image_info64(II, &info);
if (info.bindtable != nullptr)
delete info.bindtable;
@@ -5559,7 +5541,7 @@ static void printObjcMetaData(MachOObjectFile *O, bool verbose) {
// binary for the iOS simulator which is the second Objective-C
// ABI. In that case printObjc1_32bit_MetaData() will determine that
// and return false.
- if (printObjc1_32bit_MetaData(O, verbose) == false)
+ if (!printObjc1_32bit_MetaData(O, verbose))
printObjc2_32bit_MetaData(O, verbose);
}
}
@@ -5588,36 +5570,38 @@ static const char *GuessLiteralPointer(uint64_t ReferenceValue,
uint64_t *ReferenceType,
struct DisassembleInfo *info) {
// First see if there is an external relocation entry at the ReferencePC.
- uint64_t sect_addr = info->S.getAddress();
- uint64_t sect_offset = ReferencePC - sect_addr;
- bool reloc_found = false;
- DataRefImpl Rel;
- MachO::any_relocation_info RE;
- bool isExtern = false;
- SymbolRef Symbol;
- for (const RelocationRef &Reloc : info->S.relocations()) {
- uint64_t RelocOffset = Reloc.getOffset();
- if (RelocOffset == sect_offset) {
- Rel = Reloc.getRawDataRefImpl();
- RE = info->O->getRelocation(Rel);
- if (info->O->isRelocationScattered(RE))
- continue;
- isExtern = info->O->getPlainRelocationExternal(RE);
- if (isExtern) {
- symbol_iterator RelocSym = Reloc.getSymbol();
- Symbol = *RelocSym;
+ if (info->O->getHeader().filetype == MachO::MH_OBJECT) {
+ uint64_t sect_addr = info->S.getAddress();
+ uint64_t sect_offset = ReferencePC - sect_addr;
+ bool reloc_found = false;
+ DataRefImpl Rel;
+ MachO::any_relocation_info RE;
+ bool isExtern = false;
+ SymbolRef Symbol;
+ for (const RelocationRef &Reloc : info->S.relocations()) {
+ uint64_t RelocOffset = Reloc.getOffset();
+ if (RelocOffset == sect_offset) {
+ Rel = Reloc.getRawDataRefImpl();
+ RE = info->O->getRelocation(Rel);
+ if (info->O->isRelocationScattered(RE))
+ continue;
+ isExtern = info->O->getPlainRelocationExternal(RE);
+ if (isExtern) {
+ symbol_iterator RelocSym = Reloc.getSymbol();
+ Symbol = *RelocSym;
+ }
+ reloc_found = true;
+ break;
}
- reloc_found = true;
- break;
}
- }
- // If there is an external relocation entry for a symbol in a section
- // then used that symbol's value for the value of the reference.
- if (reloc_found && isExtern) {
- if (info->O->getAnyRelocationPCRel(RE)) {
- unsigned Type = info->O->getAnyRelocationType(RE);
- if (Type == MachO::X86_64_RELOC_SIGNED) {
- ReferenceValue = Symbol.getValue();
+ // If there is an external relocation entry for a symbol in a section
+ // then used that symbol's value for the value of the reference.
+ if (reloc_found && isExtern) {
+ if (info->O->getAnyRelocationPCRel(RE)) {
+ unsigned Type = info->O->getAnyRelocationType(RE);
+ if (Type == MachO::X86_64_RELOC_SIGNED) {
+ ReferenceValue = Symbol.getValue();
+ }
}
}
}
@@ -5872,7 +5856,6 @@ static void emitComments(raw_svector_ostream &CommentStream,
formatted_raw_ostream &FormattedOS,
const MCAsmInfo &MAI) {
// Flush the stream before taking its content.
- CommentStream.flush();
StringRef Comments = CommentsToEmit.str();
// Get the default information for printing a comment.
const char *CommentBegin = MAI.getCommentString();
@@ -5893,7 +5876,6 @@ static void emitComments(raw_svector_ostream &CommentStream,
// Tell the comment stream that the vector changed underneath it.
CommentsToEmit.clear();
- CommentStream.resync();
}
static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
@@ -6065,7 +6047,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
diContext.reset(new DWARFContextInMemory(*DbgObj));
}
- if (DumpSections.size() == 0)
+ if (FilterSections.size() == 0)
outs() << "(" << DisSegName << "," << DisSectName << ") section\n";
for (unsigned SectIdx = 0; SectIdx != Sections.size(); SectIdx++) {
@@ -6087,19 +6069,6 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
bool symbolTableWorked = false;
- // Parse relocations.
- std::vector<std::pair<uint64_t, SymbolRef>> Relocs;
- for (const RelocationRef &Reloc : Sections[SectIdx].relocations()) {
- uint64_t RelocOffset = Reloc.getOffset();
- uint64_t SectionAddress = Sections[SectIdx].getAddress();
- RelocOffset -= SectionAddress;
-
- symbol_iterator RelocSym = Reloc.getSymbol();
-
- Relocs.push_back(std::make_pair(RelocOffset, *RelocSym));
- }
- array_pod_sort(Relocs.begin(), Relocs.end());
-
// Create a map of symbol addresses to symbol names for use by
// the SymbolizerSymbolLookUp() routine.
SymbolAddressMap AddrMap;
@@ -6157,7 +6126,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
StringRef SymName = *SymNameOrErr;
SymbolRef::Type ST = Symbols[SymIdx].getType();
- if (ST != SymbolRef::ST_Function)
+ if (ST != SymbolRef::ST_Function && ST != SymbolRef::ST_Data)
continue;
// Make sure the symbol is defined in this section.
@@ -6251,10 +6220,9 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
DebugOut, Annotations);
if (gotInst) {
if (!NoShowRawInsn) {
- dumpBytes(ArrayRef<uint8_t>(Bytes.data() + Index, Size), outs());
+ dumpBytes(makeArrayRef(Bytes.data() + Index, Size), outs());
}
formatted_raw_ostream FormattedOS(outs());
- Annotations.flush();
StringRef AnnotationsStr = Annotations.str();
if (isThumb)
ThumbIP->printInst(&Inst, FormattedOS, AnnotationsStr, *ThumbSTI);
@@ -6316,7 +6284,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
}
if (!NoShowRawInsn) {
outs() << "\t";
- dumpBytes(ArrayRef<uint8_t>(Bytes.data() + Index, InstSize), outs());
+ dumpBytes(makeArrayRef(Bytes.data() + Index, InstSize), outs());
}
IP->printInst(&Inst, outs(), "", *STI);
outs() << "\n";
@@ -6441,8 +6409,7 @@ static void findUnwindRelocNameAddend(const MachOObjectFile *Obj,
// Go back one so that SymbolAddress <= Addr.
--Sym;
- section_iterator SymSection = Obj->section_end();
- Sym->second.getSection(SymSection);
+ section_iterator SymSection = *Sym->second.getSection();
if (RelocSection == *SymSection) {
// There's a valid symbol in the same section before this reference.
ErrorOr<StringRef> NameOrErr = Sym->second.getName();
@@ -6780,13 +6747,268 @@ static void printMachOUnwindInfoSection(const MachOObjectFile *Obj,
}
}
+static unsigned getSizeForEncoding(bool is64Bit,
+ unsigned symbolEncoding) {
+ unsigned format = symbolEncoding & 0x0f;
+ switch (format) {
+ default: llvm_unreachable("Unknown Encoding");
+ case dwarf::DW_EH_PE_absptr:
+ case dwarf::DW_EH_PE_signed:
+ return is64Bit ? 8 : 4;
+ case dwarf::DW_EH_PE_udata2:
+ case dwarf::DW_EH_PE_sdata2:
+ return 2;
+ case dwarf::DW_EH_PE_udata4:
+ case dwarf::DW_EH_PE_sdata4:
+ return 4;
+ case dwarf::DW_EH_PE_udata8:
+ case dwarf::DW_EH_PE_sdata8:
+ return 8;
+ }
+}
+
+static uint64_t readPointer(const char *&Pos, bool is64Bit, unsigned Encoding) {
+ switch (getSizeForEncoding(is64Bit, Encoding)) {
+ case 2:
+ return readNext<uint16_t>(Pos);
+ break;
+ case 4:
+ return readNext<uint32_t>(Pos);
+ break;
+ case 8:
+ return readNext<uint64_t>(Pos);
+ break;
+ default:
+ llvm_unreachable("Illegal data size");
+ }
+}
+
+static void printMachOEHFrameSection(const MachOObjectFile *Obj,
+ std::map<uint64_t, SymbolRef> &Symbols,
+ const SectionRef &EHFrame) {
+ if (!Obj->isLittleEndian()) {
+ outs() << "warning: cannot handle big endian __eh_frame section\n";
+ return;
+ }
+
+ bool is64Bit = Obj->is64Bit();
+
+ outs() << "Contents of __eh_frame section:\n";
+
+ StringRef Contents;
+ EHFrame.getContents(Contents);
+
+ /// A few fields of the CIE are used when decoding the FDE's. This struct
+ /// will cache those fields we need so that we don't have to decode it
+ /// repeatedly for each FDE that references it.
+ struct DecodedCIE {
+ Optional<uint32_t> FDEPointerEncoding;
+ Optional<uint32_t> LSDAPointerEncoding;
+ bool hasAugmentationLength;
+ };
+
+ // Map from the start offset of the CIE to the cached data for that CIE.
+ DenseMap<uint64_t, DecodedCIE> CachedCIEs;
+
+ for (const char *Pos = Contents.data(), *End = Contents.end(); Pos != End; ) {
+
+ const char *EntryStartPos = Pos;
+
+ uint64_t Length = readNext<uint32_t>(Pos);
+ if (Length == 0xffffffff)
+ Length = readNext<uint64_t>(Pos);
+
+ // Save the Pos so that we can check the length we encoded against what we
+ // end up decoding.
+ const char *PosAfterLength = Pos;
+ const char *EntryEndPos = PosAfterLength + Length;
+
+ assert(EntryEndPos <= End &&
+ "__eh_frame entry length exceeds section size");
+
+ uint32_t ID = readNext<uint32_t>(Pos);
+ if (ID == 0) {
+ // This is a CIE.
+
+ uint32_t Version = readNext<uint8_t>(Pos);
+
+ // Parse a null terminated augmentation string
+ SmallString<8> AugmentationString;
+ for (uint8_t Char = readNext<uint8_t>(Pos); Char;
+ Char = readNext<uint8_t>(Pos))
+ AugmentationString.push_back(Char);
+
+ // Optionally parse the EH data if the augmentation string says it's there.
+ Optional<uint64_t> EHData;
+ if (StringRef(AugmentationString).count("eh"))
+ EHData = is64Bit ? readNext<uint64_t>(Pos) : readNext<uint32_t>(Pos);
+
+ unsigned ULEBByteCount;
+ uint64_t CodeAlignmentFactor = decodeULEB128((const uint8_t *)Pos,
+ &ULEBByteCount);
+ Pos += ULEBByteCount;
+
+ int64_t DataAlignmentFactor = decodeSLEB128((const uint8_t *)Pos,
+ &ULEBByteCount);
+ Pos += ULEBByteCount;
+
+ uint32_t ReturnAddressRegister = readNext<uint8_t>(Pos);
+
+ Optional<uint64_t> AugmentationLength;
+ Optional<uint32_t> LSDAPointerEncoding;
+ Optional<uint32_t> PersonalityEncoding;
+ Optional<uint64_t> Personality;
+ Optional<uint32_t> FDEPointerEncoding;
+ if (!AugmentationString.empty() && AugmentationString.front() == 'z') {
+ AugmentationLength = decodeULEB128((const uint8_t *)Pos,
+ &ULEBByteCount);
+ Pos += ULEBByteCount;
+
+ // Walk the augmentation string to get all the augmentation data.
+ for (unsigned i = 1, e = AugmentationString.size(); i != e; ++i) {
+ char Char = AugmentationString[i];
+ switch (Char) {
+ case 'e':
+ assert((i + 1) != e && AugmentationString[i + 1] == 'h' &&
+ "Expected 'eh' in augmentation string");
+ break;
+ case 'L':
+ assert(!LSDAPointerEncoding && "Duplicate LSDA encoding");
+ LSDAPointerEncoding = readNext<uint8_t>(Pos);
+ break;
+ case 'P': {
+ assert(!Personality && "Duplicate personality");
+ PersonalityEncoding = readNext<uint8_t>(Pos);
+ Personality = readPointer(Pos, is64Bit, *PersonalityEncoding);
+ break;
+ }
+ case 'R':
+ assert(!FDEPointerEncoding && "Duplicate FDE encoding");
+ FDEPointerEncoding = readNext<uint8_t>(Pos);
+ break;
+ case 'z':
+ llvm_unreachable("'z' must be first in the augmentation string");
+ }
+ }
+ }
+
+ outs() << "CIE:\n";
+ outs() << " Length: " << Length << "\n";
+ outs() << " CIE ID: " << ID << "\n";
+ outs() << " Version: " << Version << "\n";
+ outs() << " Augmentation String: " << AugmentationString << "\n";
+ if (EHData)
+ outs() << " EHData: " << *EHData << "\n";
+ outs() << " Code Alignment Factor: " << CodeAlignmentFactor << "\n";
+ outs() << " Data Alignment Factor: " << DataAlignmentFactor << "\n";
+ outs() << " Return Address Register: " << ReturnAddressRegister << "\n";
+ if (AugmentationLength) {
+ outs() << " Augmentation Data Length: " << *AugmentationLength << "\n";
+ if (LSDAPointerEncoding) {
+ outs() << " FDE LSDA Pointer Encoding: "
+ << *LSDAPointerEncoding << "\n";
+ }
+ if (Personality) {
+ outs() << " Personality Encoding: " << *PersonalityEncoding << "\n";
+ outs() << " Personality: " << *Personality << "\n";
+ }
+ if (FDEPointerEncoding) {
+ outs() << " FDE Address Pointer Encoding: "
+ << *FDEPointerEncoding << "\n";
+ }
+ }
+ // FIXME: Handle instructions.
+ // For now just emit some bytes
+ outs() << " Instructions:\n ";
+ dumpBytes(makeArrayRef((const uint8_t*)Pos, (const uint8_t*)EntryEndPos),
+ outs());
+ outs() << "\n";
+ Pos = EntryEndPos;
+
+ // Cache this entry.
+ uint64_t Offset = EntryStartPos - Contents.data();
+ CachedCIEs[Offset] = { FDEPointerEncoding, LSDAPointerEncoding,
+ AugmentationLength.hasValue() };
+ continue;
+ }
+
+ // This is an FDE.
+ // The CIE pointer for an FDE is the same location as the ID which we
+ // already read.
+ uint32_t CIEPointer = ID;
+
+ const char *CIEStart = PosAfterLength - CIEPointer;
+ assert(CIEStart >= Contents.data() &&
+ "FDE points to CIE before the __eh_frame start");
+
+ uint64_t CIEOffset = CIEStart - Contents.data();
+ auto CIEIt = CachedCIEs.find(CIEOffset);
+ if (CIEIt == CachedCIEs.end())
+ llvm_unreachable("Couldn't find CIE at offset in to __eh_frame section");
+
+ const DecodedCIE &CIE = CIEIt->getSecond();
+ assert(CIE.FDEPointerEncoding &&
+ "FDE references CIE which did not set pointer encoding");
+
+ uint64_t PCPointerSize = getSizeForEncoding(is64Bit,
+ *CIE.FDEPointerEncoding);
+
+ uint64_t PCBegin = readPointer(Pos, is64Bit, *CIE.FDEPointerEncoding);
+ uint64_t PCRange = readPointer(Pos, is64Bit, *CIE.FDEPointerEncoding);
+
+ Optional<uint64_t> AugmentationLength;
+ uint32_t LSDAPointerSize;
+ Optional<uint64_t> LSDAPointer;
+ if (CIE.hasAugmentationLength) {
+ unsigned ULEBByteCount;
+ AugmentationLength = decodeULEB128((const uint8_t *)Pos,
+ &ULEBByteCount);
+ Pos += ULEBByteCount;
+
+ // Decode the LSDA if the CIE augmentation string said we should.
+ if (CIE.LSDAPointerEncoding) {
+ LSDAPointerSize = getSizeForEncoding(is64Bit, *CIE.LSDAPointerEncoding);
+ LSDAPointer = readPointer(Pos, is64Bit, *CIE.LSDAPointerEncoding);
+ }
+ }
+
+ outs() << "FDE:\n";
+ outs() << " Length: " << Length << "\n";
+ outs() << " CIE Offset: " << CIEOffset << "\n";
+
+ if (PCPointerSize == 8) {
+ outs() << format(" PC Begin: %016" PRIx64, PCBegin) << "\n";
+ outs() << format(" PC Range: %016" PRIx64, PCRange) << "\n";
+ } else {
+ outs() << format(" PC Begin: %08" PRIx64, PCBegin) << "\n";
+ outs() << format(" PC Range: %08" PRIx64, PCRange) << "\n";
+ }
+ if (AugmentationLength) {
+ outs() << " Augmentation Data Length: " << *AugmentationLength << "\n";
+ if (LSDAPointer) {
+ if (LSDAPointerSize == 8)
+ outs() << format(" LSDA Pointer: %016\n" PRIx64, *LSDAPointer);
+ else
+ outs() << format(" LSDA Pointer: %08\n" PRIx64, *LSDAPointer);
+ }
+ }
+
+ // FIXME: Handle instructions.
+ // For now just emit some bytes
+ outs() << " Instructions:\n ";
+ dumpBytes(makeArrayRef((const uint8_t*)Pos, (const uint8_t*)EntryEndPos),
+ outs());
+ outs() << "\n";
+ Pos = EntryEndPos;
+ }
+}
+
void llvm::printMachOUnwindInfo(const MachOObjectFile *Obj) {
std::map<uint64_t, SymbolRef> Symbols;
for (const SymbolRef &SymRef : Obj->symbols()) {
// Discard any undefined or absolute symbols. They're not going to take part
// in the convenience lookup for unwind info and just take up resources.
- section_iterator Section = Obj->section_end();
- SymRef.getSection(Section);
+ section_iterator Section = *SymRef.getSection();
if (Section == Obj->section_end())
continue;
@@ -6802,7 +7024,7 @@ void llvm::printMachOUnwindInfo(const MachOObjectFile *Obj) {
else if (SectName == "__unwind_info")
printMachOUnwindInfoSection(Obj, Symbols, Section);
else if (SectName == "__eh_frame")
- outs() << "llvm-objdump: warning: unhandled __eh_frame section\n";
+ printMachOEHFrameSection(Obj, Symbols, Section);
}
}
@@ -7128,36 +7350,20 @@ static void PrintSegmentCommand(uint32_t cmd, uint32_t cmdsize,
MachO::VM_PROT_EXECUTE)) != 0)
outs() << " maxprot ?" << format("0x%08" PRIx32, maxprot) << "\n";
else {
- if (maxprot & MachO::VM_PROT_READ)
- outs() << " maxprot r";
- else
- outs() << " maxprot -";
- if (maxprot & MachO::VM_PROT_WRITE)
- outs() << "w";
- else
- outs() << "-";
- if (maxprot & MachO::VM_PROT_EXECUTE)
- outs() << "x\n";
- else
- outs() << "-\n";
+ outs() << " maxprot ";
+ outs() << ((maxprot & MachO::VM_PROT_READ) ? "r" : "-");
+ outs() << ((maxprot & MachO::VM_PROT_WRITE) ? "w" : "-");
+ outs() << ((maxprot & MachO::VM_PROT_EXECUTE) ? "x\n" : "-\n");
}
if ((initprot &
~(MachO::VM_PROT_READ | MachO::VM_PROT_WRITE |
MachO::VM_PROT_EXECUTE)) != 0)
outs() << " initprot ?" << format("0x%08" PRIx32, initprot) << "\n";
else {
- if (initprot & MachO::VM_PROT_READ)
- outs() << " initprot r";
- else
- outs() << " initprot -";
- if (initprot & MachO::VM_PROT_WRITE)
- outs() << "w";
- else
- outs() << "-";
- if (initprot & MachO::VM_PROT_EXECUTE)
- outs() << "x\n";
- else
- outs() << "-\n";
+ outs() << " initprot ";
+ outs() << ((initprot & MachO::VM_PROT_READ) ? "r" : "-");
+ outs() << ((initprot & MachO::VM_PROT_WRITE) ? "w" : "-");
+ outs() << ((initprot & MachO::VM_PROT_EXECUTE) ? "x\n" : "-\n");
}
} else {
outs() << " maxprot " << format("0x%08" PRIx32, maxprot) << "\n";
@@ -7611,26 +7817,11 @@ static void PrintUuidLoadCommand(MachO::uuid_command uuid) {
else
outs() << "\n";
outs() << " uuid ";
- outs() << format("%02" PRIX32, uuid.uuid[0]);
- outs() << format("%02" PRIX32, uuid.uuid[1]);
- outs() << format("%02" PRIX32, uuid.uuid[2]);
- outs() << format("%02" PRIX32, uuid.uuid[3]);
- outs() << "-";
- outs() << format("%02" PRIX32, uuid.uuid[4]);
- outs() << format("%02" PRIX32, uuid.uuid[5]);
- outs() << "-";
- outs() << format("%02" PRIX32, uuid.uuid[6]);
- outs() << format("%02" PRIX32, uuid.uuid[7]);
- outs() << "-";
- outs() << format("%02" PRIX32, uuid.uuid[8]);
- outs() << format("%02" PRIX32, uuid.uuid[9]);
- outs() << "-";
- outs() << format("%02" PRIX32, uuid.uuid[10]);
- outs() << format("%02" PRIX32, uuid.uuid[11]);
- outs() << format("%02" PRIX32, uuid.uuid[12]);
- outs() << format("%02" PRIX32, uuid.uuid[13]);
- outs() << format("%02" PRIX32, uuid.uuid[14]);
- outs() << format("%02" PRIX32, uuid.uuid[15]);
+ for (int i = 0; i < 16; ++i) {
+ outs() << format("%02" PRIX32, uuid.uuid[i]);
+ if (i == 3 || i == 5 || i == 7 || i == 9)
+ outs() << "-";
+ }
outs() << "\n";
}
@@ -7650,30 +7841,47 @@ static void PrintRpathLoadCommand(MachO::rpath_command rpath, const char *Ptr) {
}
static void PrintVersionMinLoadCommand(MachO::version_min_command vd) {
- if (vd.cmd == MachO::LC_VERSION_MIN_MACOSX)
- outs() << " cmd LC_VERSION_MIN_MACOSX\n";
- else if (vd.cmd == MachO::LC_VERSION_MIN_IPHONEOS)
- outs() << " cmd LC_VERSION_MIN_IPHONEOS\n";
- else
- outs() << " cmd " << vd.cmd << " (?)\n";
+ StringRef LoadCmdName;
+ switch (vd.cmd) {
+ case MachO::LC_VERSION_MIN_MACOSX:
+ LoadCmdName = "LC_VERSION_MIN_MACOSX";
+ break;
+ case MachO::LC_VERSION_MIN_IPHONEOS:
+ LoadCmdName = "LC_VERSION_MIN_IPHONEOS";
+ break;
+ case MachO::LC_VERSION_MIN_TVOS:
+ LoadCmdName = "LC_VERSION_MIN_TVOS";
+ break;
+ case MachO::LC_VERSION_MIN_WATCHOS:
+ LoadCmdName = "LC_VERSION_MIN_WATCHOS";
+ break;
+ default:
+ llvm_unreachable("Unknown version min load command");
+ }
+
+ outs() << " cmd " << LoadCmdName << '\n';
outs() << " cmdsize " << vd.cmdsize;
if (vd.cmdsize != sizeof(struct MachO::version_min_command))
outs() << " Incorrect size\n";
else
outs() << "\n";
- outs() << " version " << ((vd.version >> 16) & 0xffff) << "."
- << ((vd.version >> 8) & 0xff);
- if ((vd.version & 0xff) != 0)
- outs() << "." << (vd.version & 0xff);
+ outs() << " version "
+ << MachOObjectFile::getVersionMinMajor(vd, false) << "."
+ << MachOObjectFile::getVersionMinMinor(vd, false);
+ uint32_t Update = MachOObjectFile::getVersionMinUpdate(vd, false);
+ if (Update != 0)
+ outs() << "." << Update;
outs() << "\n";
if (vd.sdk == 0)
outs() << " sdk n/a";
else {
- outs() << " sdk " << ((vd.sdk >> 16) & 0xffff) << "."
- << ((vd.sdk >> 8) & 0xff);
+ outs() << " sdk "
+ << MachOObjectFile::getVersionMinMajor(vd, true) << "."
+ << MachOObjectFile::getVersionMinMinor(vd, true);
}
- if ((vd.sdk & 0xff) != 0)
- outs() << "." << (vd.sdk & 0xff);
+ Update = MachOObjectFile::getVersionMinUpdate(vd, true);
+ if (Update != 0)
+ outs() << "." << Update;
outs() << "\n";
}
@@ -8366,7 +8574,9 @@ static void PrintLoadCommands(const MachOObjectFile *Obj, uint32_t filetype,
MachO::rpath_command Rpath = Obj->getRpathCommand(Command);
PrintRpathLoadCommand(Rpath, Command.Ptr);
} else if (Command.C.cmd == MachO::LC_VERSION_MIN_MACOSX ||
- Command.C.cmd == MachO::LC_VERSION_MIN_IPHONEOS) {
+ Command.C.cmd == MachO::LC_VERSION_MIN_IPHONEOS ||
+ Command.C.cmd == MachO::LC_VERSION_MIN_TVOS ||
+ Command.C.cmd == MachO::LC_VERSION_MIN_WATCHOS) {
MachO::version_min_command Vd = Obj->getVersionMinLoadCommand(Command);
PrintVersionMinLoadCommand(Vd);
} else if (Command.C.cmd == MachO::LC_SOURCE_VERSION) {
@@ -8536,6 +8746,7 @@ public:
StringRef segmentName(uint32_t SegIndex);
StringRef sectionName(uint32_t SegIndex, uint64_t SegOffset);
uint64_t address(uint32_t SegIndex, uint64_t SegOffset);
+ bool isValidSegIndexAndOffset(uint32_t SegIndex, uint64_t SegOffset);
private:
struct SectionInfo {
@@ -8559,8 +8770,7 @@ SegInfo::SegInfo(const object::MachOObjectFile *Obj) {
uint64_t CurSegAddress;
for (const SectionRef &Section : Obj->sections()) {
SectionInfo Info;
- if (error(Section.getName(Info.SectionName)))
- return;
+ error(Section.getName(Info.SectionName));
Info.Address = Section.getAddress();
Info.Size = Section.getSize();
Info.SegmentName =
@@ -8585,6 +8795,20 @@ StringRef SegInfo::segmentName(uint32_t SegIndex) {
llvm_unreachable("invalid segIndex");
}
+bool SegInfo::isValidSegIndexAndOffset(uint32_t SegIndex,
+ uint64_t OffsetInSeg) {
+ for (const SectionInfo &SI : Sections) {
+ if (SI.SegmentIndex != SegIndex)
+ continue;
+ if (SI.OffsetInSegment > OffsetInSeg)
+ continue;
+ if (OffsetInSeg >= (SI.OffsetInSegment + SI.Size))
+ continue;
+ return true;
+ }
+ return false;
+}
+
const SegInfo::SectionInfo &SegInfo::findSection(uint32_t SegIndex,
uint64_t OffsetInSeg) {
for (const SectionInfo &SI : Sections) {
@@ -8753,6 +8977,8 @@ static const char *get_dyld_bind_info_symbolname(uint64_t ReferenceValue,
for (const llvm::object::MachOBindEntry &Entry : info->O->bindTable()) {
uint32_t SegIndex = Entry.segmentIndex();
uint64_t OffsetInSeg = Entry.segmentOffset();
+ if (!sectionTable.isValidSegIndexAndOffset(SegIndex, OffsetInSeg))
+ continue;
uint64_t Address = sectionTable.address(SegIndex, OffsetInSeg);
const char *SymbolName = nullptr;
StringRef name = Entry.symbolName();
diff --git a/tools/llvm-objdump/llvm-objdump.cpp b/tools/llvm-objdump/llvm-objdump.cpp
index 275eb9c6a454..22167c788901 100644
--- a/tools/llvm-objdump/llvm-objdump.cpp
+++ b/tools/llvm-objdump/llvm-objdump.cpp
@@ -73,6 +73,13 @@ Disassembled("d", cl::desc("Alias for --disassemble"),
cl::aliasopt(Disassemble));
cl::opt<bool>
+llvm::DisassembleAll("disassemble-all",
+ cl::desc("Display assembler mnemonics for the machine instructions"));
+static cl::alias
+DisassembleAlld("D", cl::desc("Alias for --disassemble-all"),
+ cl::aliasopt(DisassembleAll));
+
+cl::opt<bool>
llvm::Relocations("r", cl::desc("Display the relocation entries in the file"));
cl::opt<bool>
@@ -130,6 +137,13 @@ SectionHeadersShorter("h", cl::desc("Alias for --section-headers"),
cl::aliasopt(SectionHeaders));
cl::list<std::string>
+llvm::FilterSections("section", cl::desc("Operate on the specified sections only. "
+ "With -macho dump segment,section"));
+cl::alias
+static FilterSectionsj("j", cl::desc("Alias for --section"),
+ cl::aliasopt(llvm::FilterSections));
+
+cl::list<std::string>
llvm::MAttrs("mattr",
cl::CommaSeparated,
cl::desc("Target specific attributes"),
@@ -163,22 +177,86 @@ cl::opt<bool> PrintFaultMaps("fault-map-section",
cl::desc("Display contents of faultmap section"));
static StringRef ToolName;
-static int ReturnValue = EXIT_SUCCESS;
-bool llvm::error(std::error_code EC) {
+namespace {
+typedef std::function<bool(llvm::object::SectionRef const &)> FilterPredicate;
+
+class SectionFilterIterator {
+public:
+ SectionFilterIterator(FilterPredicate P,
+ llvm::object::section_iterator const &I,
+ llvm::object::section_iterator const &E)
+ : Predicate(P), Iterator(I), End(E) {
+ ScanPredicate();
+ }
+ const llvm::object::SectionRef &operator*() const { return *Iterator; }
+ SectionFilterIterator &operator++() {
+ ++Iterator;
+ ScanPredicate();
+ return *this;
+ }
+ bool operator!=(SectionFilterIterator const &Other) const {
+ return Iterator != Other.Iterator;
+ }
+
+private:
+ void ScanPredicate() {
+ while (Iterator != End && !Predicate(*Iterator)) {
+ ++Iterator;
+ }
+ }
+ FilterPredicate Predicate;
+ llvm::object::section_iterator Iterator;
+ llvm::object::section_iterator End;
+};
+
+class SectionFilter {
+public:
+ SectionFilter(FilterPredicate P, llvm::object::ObjectFile const &O)
+ : Predicate(P), Object(O) {}
+ SectionFilterIterator begin() {
+ return SectionFilterIterator(Predicate, Object.section_begin(),
+ Object.section_end());
+ }
+ SectionFilterIterator end() {
+ return SectionFilterIterator(Predicate, Object.section_end(),
+ Object.section_end());
+ }
+
+private:
+ FilterPredicate Predicate;
+ llvm::object::ObjectFile const &Object;
+};
+SectionFilter ToolSectionFilter(llvm::object::ObjectFile const &O) {
+ return SectionFilter([](llvm::object::SectionRef const &S) {
+ if(FilterSections.empty())
+ return true;
+ llvm::StringRef String;
+ std::error_code error = S.getName(String);
+ if (error)
+ return false;
+ return std::find(FilterSections.begin(),
+ FilterSections.end(),
+ String) != FilterSections.end();
+ },
+ O);
+}
+}
+
+void llvm::error(std::error_code EC) {
if (!EC)
- return false;
+ return;
- outs() << ToolName << ": error reading file: " << EC.message() << ".\n";
- outs().flush();
- ReturnValue = EXIT_FAILURE;
- return true;
+ errs() << ToolName << ": error reading file: " << EC.message() << ".\n";
+ errs().flush();
+ exit(1);
}
-static void report_error(StringRef File, std::error_code EC) {
+LLVM_ATTRIBUTE_NORETURN void llvm::report_error(StringRef File,
+ std::error_code EC) {
assert(EC);
errs() << ToolName << ": '" << File << "': " << EC.message() << ".\n";
- ReturnValue = EXIT_FAILURE;
+ exit(1);
}
static const Target *getTarget(const ObjectFile *Obj = nullptr) {
@@ -205,10 +283,8 @@ static const Target *getTarget(const ObjectFile *Obj = nullptr) {
std::string Error;
const Target *TheTarget = TargetRegistry::lookupTarget(ArchName, TheTriple,
Error);
- if (!TheTarget) {
- errs() << ToolName << ": " << Error;
- return nullptr;
- }
+ if (!TheTarget)
+ report_fatal_error("can't find target: " + Error);
// Update the triple name and return the found target.
TripleName = TheTriple.getTriple();
@@ -301,11 +377,12 @@ PrettyPrinter &selectPrettyPrinter(Triple const &Triple) {
template <class ELFT>
static std::error_code getRelocationValueString(const ELFObjectFile<ELFT> *Obj,
- DataRefImpl Rel,
+ const RelocationRef &RelRef,
SmallVectorImpl<char> &Result) {
+ DataRefImpl Rel = RelRef.getRawDataRefImpl();
+
typedef typename ELFObjectFile<ELFT>::Elf_Sym Elf_Sym;
typedef typename ELFObjectFile<ELFT>::Elf_Shdr Elf_Shdr;
- typedef typename ELFObjectFile<ELFT>::Elf_Rel Elf_Rel;
typedef typename ELFObjectFile<ELFT>::Elf_Rela Elf_Rela;
const ELFFile<ELFT> &EF = *Obj->getELFFile();
@@ -327,36 +404,31 @@ static std::error_code getRelocationValueString(const ELFObjectFile<ELFT> *Obj,
if (std::error_code EC = StrTabOrErr.getError())
return EC;
StringRef StrTab = *StrTabOrErr;
- uint8_t type;
+ uint8_t type = RelRef.getType();
StringRef res;
int64_t addend = 0;
- uint16_t symbol_index = 0;
switch (Sec->sh_type) {
default:
return object_error::parse_failed;
case ELF::SHT_REL: {
- const Elf_Rel *ERel = Obj->getRel(Rel);
- type = ERel->getType(EF.isMips64EL());
- symbol_index = ERel->getSymbol(EF.isMips64EL());
// TODO: Read implicit addend from section data.
break;
}
case ELF::SHT_RELA: {
const Elf_Rela *ERela = Obj->getRela(Rel);
- type = ERela->getType(EF.isMips64EL());
- symbol_index = ERela->getSymbol(EF.isMips64EL());
addend = ERela->r_addend;
break;
}
}
- const Elf_Sym *symb =
- EF.template getEntry<Elf_Sym>(Sec->sh_link, symbol_index);
+ symbol_iterator SI = RelRef.getSymbol();
+ const Elf_Sym *symb = Obj->getSymbol(SI->getRawDataRefImpl());
StringRef Target;
- ErrorOr<const Elf_Shdr *> SymSec = EF.getSection(symb);
- if (std::error_code EC = SymSec.getError())
- return EC;
if (symb->getType() == ELF::STT_SECTION) {
- ErrorOr<StringRef> SecName = EF.getSectionName(*SymSec);
+ ErrorOr<section_iterator> SymSI = SI->getSection();
+ if (std::error_code EC = SymSI.getError())
+ return EC;
+ const Elf_Shdr *SymSec = Obj->getSection((*SymSI)->getRawDataRefImpl());
+ ErrorOr<StringRef> SecName = EF.getSectionName(SymSec);
if (std::error_code EC = SecName.getError())
return EC;
Target = *SecName;
@@ -404,6 +476,7 @@ static std::error_code getRelocationValueString(const ELFObjectFile<ELFT> *Obj,
break;
}
case ELF::EM_386:
+ case ELF::EM_IAMCU:
case ELF::EM_ARM:
case ELF::EM_HEXAGON:
case ELF::EM_MIPS:
@@ -418,9 +491,8 @@ static std::error_code getRelocationValueString(const ELFObjectFile<ELFT> *Obj,
}
static std::error_code getRelocationValueString(const ELFObjectFileBase *Obj,
- const RelocationRef &RelRef,
+ const RelocationRef &Rel,
SmallVectorImpl<char> &Result) {
- DataRefImpl Rel = RelRef.getRawDataRefImpl();
if (auto *ELF32LE = dyn_cast<ELF32LEObjectFile>(Obj))
return getRelocationValueString(ELF32LE, Rel, Result);
if (auto *ELF64LE = dyn_cast<ELF64LEObjectFile>(Obj))
@@ -471,7 +543,7 @@ static void printRelocationTargetName(const MachOObjectFile *O,
// If we couldn't find a symbol that this relocation refers to, try
// to find a section beginning instead.
- for (const SectionRef &Section : O->sections()) {
+ for (const SectionRef &Section : ToolSectionFilter(*O)) {
std::error_code ec;
StringRef Name;
@@ -496,8 +568,8 @@ static void printRelocationTargetName(const MachOObjectFile *O,
symbol_iterator SI = O->symbol_begin();
advance(SI, Val);
ErrorOr<StringRef> SOrErr = SI->getName();
- if (!error(SOrErr.getError()))
- S = *SOrErr;
+ error(SOrErr.getError());
+ S = *SOrErr;
} else {
section_iterator SI = O->section_begin();
// Adjust for the fact that sections are 1-indexed.
@@ -732,10 +804,6 @@ static bool getHidden(RelocationRef RelRef) {
static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) {
const Target *TheTarget = getTarget(Obj);
- // getTarget() will have already issued a diagnostic if necessary, so
- // just bail here if it failed.
- if (!TheTarget)
- return;
// Package up features to be passed to target/subtarget
std::string FeaturesStr;
@@ -748,42 +816,28 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) {
std::unique_ptr<const MCRegisterInfo> MRI(
TheTarget->createMCRegInfo(TripleName));
- if (!MRI) {
- errs() << "error: no register info for target " << TripleName << "\n";
- return;
- }
+ if (!MRI)
+ report_fatal_error("error: no register info for target " + TripleName);
// Set up disassembler.
std::unique_ptr<const MCAsmInfo> AsmInfo(
TheTarget->createMCAsmInfo(*MRI, TripleName));
- if (!AsmInfo) {
- errs() << "error: no assembly info for target " << TripleName << "\n";
- return;
- }
-
+ if (!AsmInfo)
+ report_fatal_error("error: no assembly info for target " + TripleName);
std::unique_ptr<const MCSubtargetInfo> STI(
TheTarget->createMCSubtargetInfo(TripleName, MCPU, FeaturesStr));
- if (!STI) {
- errs() << "error: no subtarget info for target " << TripleName << "\n";
- return;
- }
-
+ if (!STI)
+ report_fatal_error("error: no subtarget info for target " + TripleName);
std::unique_ptr<const MCInstrInfo> MII(TheTarget->createMCInstrInfo());
- if (!MII) {
- errs() << "error: no instruction info for target " << TripleName << "\n";
- return;
- }
-
+ if (!MII)
+ report_fatal_error("error: no instruction info for target " + TripleName);
std::unique_ptr<const MCObjectFileInfo> MOFI(new MCObjectFileInfo);
MCContext Ctx(AsmInfo.get(), MRI.get(), MOFI.get());
std::unique_ptr<MCDisassembler> DisAsm(
TheTarget->createMCDisassembler(*STI, Ctx));
-
- if (!DisAsm) {
- errs() << "error: no disassembler for target " << TripleName << "\n";
- return;
- }
+ if (!DisAsm)
+ report_fatal_error("error: no disassembler for target " + TripleName);
std::unique_ptr<const MCInstrAnalysis> MIA(
TheTarget->createMCInstrAnalysis(MII.get()));
@@ -791,11 +845,9 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) {
int AsmPrinterVariant = AsmInfo->getAssemblerDialect();
std::unique_ptr<MCInstPrinter> IP(TheTarget->createMCInstPrinter(
Triple(TripleName), AsmPrinterVariant, *AsmInfo, *MII, *MRI));
- if (!IP) {
- errs() << "error: no instruction printer for target " << TripleName
- << '\n';
- return;
- }
+ if (!IP)
+ report_fatal_error("error: no instruction printer for target " +
+ TripleName);
IP->setPrintImmHex(PrintImmHex);
PrettyPrinter &PIP = selectPrettyPrinter(Triple(TripleName));
@@ -806,38 +858,75 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) {
// in RelocSecs contain the relocations for section S.
std::error_code EC;
std::map<SectionRef, SmallVector<SectionRef, 1>> SectionRelocMap;
- for (const SectionRef &Section : Obj->sections()) {
+ for (const SectionRef &Section : ToolSectionFilter(*Obj)) {
section_iterator Sec2 = Section.getRelocatedSection();
if (Sec2 != Obj->section_end())
SectionRelocMap[*Sec2].push_back(Section);
}
// Create a mapping from virtual address to symbol name. This is used to
- // pretty print the target of a call.
- std::vector<std::pair<uint64_t, StringRef>> AllSymbols;
- if (MIA) {
- for (const SymbolRef &Symbol : Obj->symbols()) {
- if (Symbol.getType() != SymbolRef::ST_Function)
- continue;
+ // pretty print the symbols while disassembling.
+ typedef std::vector<std::pair<uint64_t, StringRef>> SectionSymbolsTy;
+ std::map<SectionRef, SectionSymbolsTy> AllSymbols;
+ for (const SymbolRef &Symbol : Obj->symbols()) {
+ ErrorOr<uint64_t> AddressOrErr = Symbol.getAddress();
+ error(AddressOrErr.getError());
+ uint64_t Address = *AddressOrErr;
- ErrorOr<uint64_t> AddressOrErr = Symbol.getAddress();
- if (error(AddressOrErr.getError()))
- break;
- uint64_t Address = *AddressOrErr;
+ ErrorOr<StringRef> Name = Symbol.getName();
+ error(Name.getError());
+ if (Name->empty())
+ continue;
- ErrorOr<StringRef> Name = Symbol.getName();
- if (error(Name.getError()))
- break;
- if (Name->empty())
+ ErrorOr<section_iterator> SectionOrErr = Symbol.getSection();
+ error(SectionOrErr.getError());
+ section_iterator SecI = *SectionOrErr;
+ if (SecI == Obj->section_end())
+ continue;
+
+ AllSymbols[*SecI].emplace_back(Address, *Name);
+ }
+
+ // Create a mapping from virtual address to section.
+ std::vector<std::pair<uint64_t, SectionRef>> SectionAddresses;
+ for (SectionRef Sec : Obj->sections())
+ SectionAddresses.emplace_back(Sec.getAddress(), Sec);
+ array_pod_sort(SectionAddresses.begin(), SectionAddresses.end());
+
+ // Linked executables (.exe and .dll files) typically don't include a real
+ // symbol table but they might contain an export table.
+ if (const auto *COFFObj = dyn_cast<COFFObjectFile>(Obj)) {
+ for (const auto &ExportEntry : COFFObj->export_directories()) {
+ StringRef Name;
+ error(ExportEntry.getSymbolName(Name));
+ if (Name.empty())
continue;
- AllSymbols.push_back(std::make_pair(Address, *Name));
- }
+ uint32_t RVA;
+ error(ExportEntry.getExportRVA(RVA));
+
+ uint64_t VA = COFFObj->getImageBase() + RVA;
+ auto Sec = std::upper_bound(
+ SectionAddresses.begin(), SectionAddresses.end(), VA,
+ [](uint64_t LHS, const std::pair<uint64_t, SectionRef> &RHS) {
+ return LHS < RHS.first;
+ });
+ if (Sec != SectionAddresses.begin())
+ --Sec;
+ else
+ Sec = SectionAddresses.end();
- array_pod_sort(AllSymbols.begin(), AllSymbols.end());
+ if (Sec != SectionAddresses.end())
+ AllSymbols[Sec->second].emplace_back(VA, Name);
+ }
}
- for (const SectionRef &Section : Obj->sections()) {
- if (!Section.isText() || Section.isVirtual())
+ // Sort all the symbols, this allows us to use a simple binary search to find
+ // a symbol near an address.
+ for (std::pair<const SectionRef, SectionSymbolsTy> &SecSyms : AllSymbols)
+ array_pod_sort(SecSyms.second.begin(), SecSyms.second.end());
+
+ for (const SectionRef &Section : ToolSectionFilter(*Obj)) {
+ if (!DisassembleAll && (!Section.isText() || Section.isVirtual()))
continue;
uint64_t SectionAddr = Section.getAddress();
@@ -845,27 +934,23 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) {
if (!SectSize)
continue;
- // Make a list of all the symbols in this section.
- std::vector<std::pair<uint64_t, StringRef>> Symbols;
- for (const SymbolRef &Symbol : Obj->symbols()) {
- if (Section.containsSymbol(Symbol)) {
- ErrorOr<uint64_t> AddressOrErr = Symbol.getAddress();
- if (error(AddressOrErr.getError()))
- break;
- uint64_t Address = *AddressOrErr;
- Address -= SectionAddr;
- if (Address >= SectSize)
- continue;
-
- ErrorOr<StringRef> Name = Symbol.getName();
- if (error(Name.getError()))
- break;
- Symbols.push_back(std::make_pair(Address, *Name));
+ // Get the list of all the symbols in this section.
+ SectionSymbolsTy &Symbols = AllSymbols[Section];
+ std::vector<uint64_t> DataMappingSymsAddr;
+ std::vector<uint64_t> TextMappingSymsAddr;
+ if (Obj->isELF() && Obj->getArch() == Triple::aarch64) {
+ for (const auto &Symb : Symbols) {
+ uint64_t Address = Symb.first;
+ StringRef Name = Symb.second;
+ if (Name.startswith("$d"))
+ DataMappingSymsAddr.push_back(Address - SectionAddr);
+ if (Name.startswith("$x"))
+ TextMappingSymsAddr.push_back(Address - SectionAddr);
}
}
- // Sort the symbols by address, just in case they didn't come in that way.
- array_pod_sort(Symbols.begin(), Symbols.end());
+ std::sort(DataMappingSymsAddr.begin(), DataMappingSymsAddr.end());
+ std::sort(TextMappingSymsAddr.begin(), TextMappingSymsAddr.end());
// Make a list of all the relocations for this section.
std::vector<RelocationRef> Rels;
@@ -886,8 +971,7 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) {
SegmentName = MachO->getSectionFinalSegmentName(DR);
}
StringRef name;
- if (error(Section.getName(name)))
- break;
+ error(Section.getName(name));
outs() << "Disassembly of section ";
if (!SegmentName.empty())
outs() << SegmentName << ",";
@@ -895,14 +979,13 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) {
// If the section has no symbol at the start, just insert a dummy one.
if (Symbols.empty() || Symbols[0].first != 0)
- Symbols.insert(Symbols.begin(), std::make_pair(0, name));
+ Symbols.insert(Symbols.begin(), std::make_pair(SectionAddr, name));
SmallString<40> Comments;
raw_svector_ostream CommentStream(Comments);
StringRef BytesStr;
- if (error(Section.getContents(BytesStr)))
- break;
+ error(Section.getContents(BytesStr));
ArrayRef<uint8_t> Bytes(reinterpret_cast<const uint8_t *>(BytesStr.data()),
BytesStr.size());
@@ -914,11 +997,16 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) {
// Disassemble symbol by symbol.
for (unsigned si = 0, se = Symbols.size(); si != se; ++si) {
- uint64_t Start = Symbols[si].first;
- // The end is either the section end or the beginning of the next symbol.
- uint64_t End = (si == se - 1) ? SectSize : Symbols[si + 1].first;
+ uint64_t Start = Symbols[si].first - SectionAddr;
+ // The end is either the section end or the beginning of the next
+ // symbol.
+ uint64_t End =
+ (si == se - 1) ? SectSize : Symbols[si + 1].first - SectionAddr;
+ // Don't try to disassemble beyond the end of section contents.
+ if (End > SectSize)
+ End = SectSize;
// If this symbol has the same address as the next symbol, then skip it.
- if (Start == End)
+ if (Start >= End)
continue;
outs() << '\n' << Symbols[si].second << ":\n";
@@ -932,6 +1020,45 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) {
for (Index = Start; Index < End; Index += Size) {
MCInst Inst;
+ // AArch64 ELF binaries can interleave data and text in the
+ // same section. We rely on the markers introduced to
+ // understand what we need to dump.
+ if (Obj->isELF() && Obj->getArch() == Triple::aarch64) {
+ uint64_t Stride = 0;
+
+ auto DAI = std::lower_bound(DataMappingSymsAddr.begin(),
+ DataMappingSymsAddr.end(), Index);
+ if (DAI != DataMappingSymsAddr.end() && *DAI == Index) {
+ // Switch to data.
+ while (Index < End) {
+ outs() << format("%8" PRIx64 ":", SectionAddr + Index);
+ outs() << "\t";
+ if (Index + 4 <= End) {
+ Stride = 4;
+ dumpBytes(Bytes.slice(Index, 4), outs());
+ outs() << "\t.word";
+ } else if (Index + 2 <= End) {
+ Stride = 2;
+ dumpBytes(Bytes.slice(Index, 2), outs());
+ outs() << "\t.short";
+ } else {
+ Stride = 1;
+ dumpBytes(Bytes.slice(Index, 1), outs());
+ outs() << "\t.byte";
+ }
+ Index += Stride;
+ outs() << "\n";
+ auto TAI = std::lower_bound(TextMappingSymsAddr.begin(),
+ TextMappingSymsAddr.end(), Index);
+ if (TAI != TextMappingSymsAddr.end() && *TAI == Index)
+ break;
+ }
+ }
+ }
+
+ if (Index >= End)
+ break;
+
if (DisAsm->getInstruction(Inst, Size, Bytes.slice(Index),
SectionAddr + Index, DebugOut,
CommentStream)) {
@@ -940,26 +1067,55 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) {
SectionAddr + Index, outs(), "", *STI);
outs() << CommentStream.str();
Comments.clear();
+
+ // Try to resolve the target of a call, tail call, etc. to a specific
+ // symbol.
if (MIA && (MIA->isCall(Inst) || MIA->isUnconditionalBranch(Inst) ||
MIA->isConditionalBranch(Inst))) {
uint64_t Target;
if (MIA->evaluateBranch(Inst, SectionAddr + Index, Size, Target)) {
- auto TargetSym = std::upper_bound(
- AllSymbols.begin(), AllSymbols.end(), Target,
- [](uint64_t LHS, const std::pair<uint64_t, StringRef> &RHS) {
- return LHS < RHS.first;
- });
- if (TargetSym != AllSymbols.begin())
- --TargetSym;
- else
- TargetSym = AllSymbols.end();
+ // In a relocatable object, the target's section must reside in
+ // the same section as the call instruction or it is accessed
+ // through a relocation.
+ //
+ // In a non-relocatable object, the target may be in any section.
+ //
+ // N.B. We don't walk the relocations in the relocatable case yet.
+ auto *TargetSectionSymbols = &Symbols;
+ if (!Obj->isRelocatableObject()) {
+ auto SectionAddress = std::upper_bound(
+ SectionAddresses.begin(), SectionAddresses.end(), Target,
+ [](uint64_t LHS,
+ const std::pair<uint64_t, SectionRef> &RHS) {
+ return LHS < RHS.first;
+ });
+ if (SectionAddress != SectionAddresses.begin()) {
+ --SectionAddress;
+ TargetSectionSymbols = &AllSymbols[SectionAddress->second];
+ } else {
+ TargetSectionSymbols = nullptr;
+ }
+ }
- if (TargetSym != AllSymbols.end()) {
- outs() << " <" << TargetSym->second;
- uint64_t Disp = Target - TargetSym->first;
- if (Disp)
- outs() << '+' << utohexstr(Disp);
- outs() << '>';
+ // Find the first symbol in the section whose offset is less than
+ // or equal to the target.
+ if (TargetSectionSymbols) {
+ auto TargetSym = std::upper_bound(
+ TargetSectionSymbols->begin(), TargetSectionSymbols->end(),
+ Target, [](uint64_t LHS,
+ const std::pair<uint64_t, StringRef> &RHS) {
+ return LHS < RHS.first;
+ });
+ if (TargetSym != TargetSectionSymbols->begin()) {
+ --TargetSym;
+ uint64_t TargetAddress = std::get<0>(*TargetSym);
+ StringRef TargetName = std::get<1>(*TargetSym);
+ outs() << " <" << TargetName;
+ uint64_t Disp = Target - TargetAddress;
+ if (Disp)
+ outs() << '+' << utohexstr(Disp);
+ outs() << '>';
+ }
}
}
}
@@ -983,8 +1139,7 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) {
// Stop when rel_cur's address is past the current instruction.
if (addr >= Index + Size) break;
rel_cur->getTypeName(name);
- if (error(getRelocationValueString(*rel_cur, val)))
- goto skip_print_rel;
+ error(getRelocationValueString(*rel_cur, val));
outs() << format(Fmt.data(), SectionAddr + addr) << name
<< "\t" << val << "\n";
@@ -1004,12 +1159,11 @@ void llvm::PrintRelocations(const ObjectFile *Obj) {
if (!Obj->isRelocatableObject())
return;
- for (const SectionRef &Section : Obj->sections()) {
+ for (const SectionRef &Section : ToolSectionFilter(*Obj)) {
if (Section.relocation_begin() == Section.relocation_end())
continue;
StringRef secname;
- if (error(Section.getName(secname)))
- continue;
+ error(Section.getName(secname));
outs() << "RELOCATION RECORDS FOR [" << secname << "]:\n";
for (const RelocationRef &Reloc : Section.relocations()) {
bool hidden = getHidden(Reloc);
@@ -1019,8 +1173,7 @@ void llvm::PrintRelocations(const ObjectFile *Obj) {
if (hidden)
continue;
Reloc.getTypeName(relocname);
- if (error(getRelocationValueString(Reloc, valuestr)))
- continue;
+ error(getRelocationValueString(Reloc, valuestr));
outs() << format(Fmt.data(), address) << " " << relocname << " "
<< valuestr << "\n";
}
@@ -1032,10 +1185,9 @@ void llvm::PrintSectionHeaders(const ObjectFile *Obj) {
outs() << "Sections:\n"
"Idx Name Size Address Type\n";
unsigned i = 0;
- for (const SectionRef &Section : Obj->sections()) {
+ for (const SectionRef &Section : ToolSectionFilter(*Obj)) {
StringRef Name;
- if (error(Section.getName(Name)))
- return;
+ error(Section.getName(Name));
uint64_t Address = Section.getAddress();
uint64_t Size = Section.getSize();
bool Text = Section.isText();
@@ -1051,11 +1203,10 @@ void llvm::PrintSectionHeaders(const ObjectFile *Obj) {
void llvm::PrintSectionContents(const ObjectFile *Obj) {
std::error_code EC;
- for (const SectionRef &Section : Obj->sections()) {
+ for (const SectionRef &Section : ToolSectionFilter(*Obj)) {
StringRef Name;
StringRef Contents;
- if (error(Section.getName(Name)))
- continue;
+ error(Section.getName(Name));
uint64_t BaseAddr = Section.getAddress();
uint64_t Size = Section.getSize();
if (!Size)
@@ -1069,8 +1220,7 @@ void llvm::PrintSectionContents(const ObjectFile *Obj) {
continue;
}
- if (error(Section.getContents(Contents)))
- continue;
+ error(Section.getContents(Contents));
// Dump out the content as hex and printable ascii characters.
for (std::size_t addr = 0, end = Contents.size(); addr < end; addr += 16) {
@@ -1098,84 +1248,28 @@ void llvm::PrintSectionContents(const ObjectFile *Obj) {
}
}
-static void PrintCOFFSymbolTable(const COFFObjectFile *coff) {
- for (unsigned SI = 0, SE = coff->getNumberOfSymbols(); SI != SE; ++SI) {
- ErrorOr<COFFSymbolRef> Symbol = coff->getSymbol(SI);
- StringRef Name;
- if (error(Symbol.getError()))
- return;
-
- if (error(coff->getSymbolName(*Symbol, Name)))
- return;
-
- outs() << "[" << format("%2d", SI) << "]"
- << "(sec " << format("%2d", int(Symbol->getSectionNumber())) << ")"
- << "(fl 0x00)" // Flag bits, which COFF doesn't have.
- << "(ty " << format("%3x", unsigned(Symbol->getType())) << ")"
- << "(scl " << format("%3x", unsigned(Symbol->getStorageClass())) << ") "
- << "(nx " << unsigned(Symbol->getNumberOfAuxSymbols()) << ") "
- << "0x" << format("%08x", unsigned(Symbol->getValue())) << " "
- << Name << "\n";
-
- for (unsigned AI = 0, AE = Symbol->getNumberOfAuxSymbols(); AI < AE; ++AI, ++SI) {
- if (Symbol->isSectionDefinition()) {
- const coff_aux_section_definition *asd;
- if (error(coff->getAuxSymbol<coff_aux_section_definition>(SI + 1, asd)))
- return;
-
- int32_t AuxNumber = asd->getNumber(Symbol->isBigObj());
-
- outs() << "AUX "
- << format("scnlen 0x%x nreloc %d nlnno %d checksum 0x%x "
- , unsigned(asd->Length)
- , unsigned(asd->NumberOfRelocations)
- , unsigned(asd->NumberOfLinenumbers)
- , unsigned(asd->CheckSum))
- << format("assoc %d comdat %d\n"
- , unsigned(AuxNumber)
- , unsigned(asd->Selection));
- } else if (Symbol->isFileRecord()) {
- const char *FileName;
- if (error(coff->getAuxSymbol<char>(SI + 1, FileName)))
- return;
-
- StringRef Name(FileName, Symbol->getNumberOfAuxSymbols() *
- coff->getSymbolTableEntrySize());
- outs() << "AUX " << Name.rtrim(StringRef("\0", 1)) << '\n';
-
- SI = SI + Symbol->getNumberOfAuxSymbols();
- break;
- } else {
- outs() << "AUX Unknown\n";
- }
- }
- }
-}
-
void llvm::PrintSymbolTable(const ObjectFile *o) {
outs() << "SYMBOL TABLE:\n";
if (const COFFObjectFile *coff = dyn_cast<const COFFObjectFile>(o)) {
- PrintCOFFSymbolTable(coff);
+ printCOFFSymbolTable(coff);
return;
}
for (const SymbolRef &Symbol : o->symbols()) {
ErrorOr<uint64_t> AddressOrError = Symbol.getAddress();
- if (error(AddressOrError.getError()))
- continue;
+ error(AddressOrError.getError());
uint64_t Address = *AddressOrError;
SymbolRef::Type Type = Symbol.getType();
uint32_t Flags = Symbol.getFlags();
- section_iterator Section = o->section_end();
- if (error(Symbol.getSection(Section)))
- continue;
+ ErrorOr<section_iterator> SectionOrErr = Symbol.getSection();
+ error(SectionOrErr.getError());
+ section_iterator Section = *SectionOrErr;
StringRef Name;
if (Type == SymbolRef::ST_Debug && Section != o->section_end()) {
Section->getName(Name);
} else {
ErrorOr<StringRef> NameOrErr = Symbol.getName();
- if (error(NameOrErr.getError()))
- continue;
+ error(NameOrErr.getError());
Name = *NameOrErr;
}
@@ -1222,8 +1316,7 @@ void llvm::PrintSymbolTable(const ObjectFile *o) {
outs() << SegmentName << ",";
}
StringRef SectionName;
- if (error(Section->getName(SectionName)))
- SectionName = "";
+ error(Section->getName(SectionName));
outs() << SectionName;
}
@@ -1329,7 +1422,7 @@ void llvm::printRawClangAST(const ObjectFile *Obj) {
}
Optional<object::SectionRef> ClangASTSection;
- for (auto Sec : Obj->sections()) {
+ for (auto Sec : ToolSectionFilter(*Obj)) {
StringRef Name;
Sec.getName(Name);
if (Name == ClangASTSectionName) {
@@ -1341,11 +1434,7 @@ void llvm::printRawClangAST(const ObjectFile *Obj) {
return;
StringRef ClangASTContents;
- if (error(ClangASTSection.getValue().getContents(ClangASTContents))) {
- errs() << "Could not read the " << ClangASTSectionName << " section!\n";
- return;
- }
-
+ error(ClangASTSection.getValue().getContents(ClangASTContents));
outs().write(ClangASTContents.data(), ClangASTContents.size());
}
@@ -1364,7 +1453,7 @@ static void printFaultMaps(const ObjectFile *Obj) {
Optional<object::SectionRef> FaultMapSection;
- for (auto Sec : Obj->sections()) {
+ for (auto Sec : ToolSectionFilter(*Obj)) {
StringRef Name;
Sec.getName(Name);
if (Name == FaultMapSectionName) {
@@ -1381,10 +1470,7 @@ static void printFaultMaps(const ObjectFile *Obj) {
}
StringRef FaultMapContents;
- if (error(FaultMapSection.getValue().getContents(FaultMapContents))) {
- errs() << "Could not read the " << FaultMapContents << " section!\n";
- return;
- }
+ error(FaultMapSection.getValue().getContents(FaultMapContents));
FaultMapParser FMP(FaultMapContents.bytes_begin(),
FaultMapContents.bytes_end());
@@ -1393,13 +1479,14 @@ static void printFaultMaps(const ObjectFile *Obj) {
}
static void printPrivateFileHeader(const ObjectFile *o) {
- if (o->isELF()) {
+ if (o->isELF())
printELFFileHeader(o);
- } else if (o->isCOFF()) {
+ else if (o->isCOFF())
printCOFFFileHeader(o);
- } else if (o->isMachO()) {
+ else if (o->isMachO())
printMachOFileHeader(o);
- }
+ else
+ report_fatal_error("Invalid/Unsupported object file format");
}
static void DumpObject(const ObjectFile *o) {
@@ -1442,15 +1529,14 @@ static void DumpObject(const ObjectFile *o) {
/// @brief Dump each object file in \a a;
static void DumpArchive(const Archive *a) {
- for (Archive::child_iterator i = a->child_begin(), e = a->child_end(); i != e;
- ++i) {
- ErrorOr<std::unique_ptr<Binary>> ChildOrErr = i->getAsBinary();
- if (std::error_code EC = ChildOrErr.getError()) {
- // Ignore non-object files.
+ for (auto &ErrorOrChild : a->children()) {
+ if (std::error_code EC = ErrorOrChild.getError())
+ report_error(a->getFileName(), EC);
+ const Archive::Child &C = *ErrorOrChild;
+ ErrorOr<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
+ if (std::error_code EC = ChildOrErr.getError())
if (EC != object_error::invalid_file_type)
report_error(a->getFileName(), EC);
- continue;
- }
if (ObjectFile *o = dyn_cast<ObjectFile>(&*ChildOrErr.get()))
DumpObject(o);
else
@@ -1460,11 +1546,6 @@ static void DumpArchive(const Archive *a) {
/// @brief Open file and figure out how to dump it.
static void DumpInput(StringRef file) {
- // If file isn't stdin, check that it exists.
- if (file != "-" && !sys::fs::exists(file)) {
- report_error(file, errc::no_such_file_or_directory);
- return;
- }
// If we are using the Mach-O specific object file parser, then let it parse
// the file and process the command line options. So the -arch flags can
@@ -1476,10 +1557,8 @@ static void DumpInput(StringRef file) {
// Attempt to open the binary.
ErrorOr<OwningBinary<Binary>> BinaryOrErr = createBinary(file);
- if (std::error_code EC = BinaryOrErr.getError()) {
+ if (std::error_code EC = BinaryOrErr.getError())
report_error(file, EC);
- return;
- }
Binary &Binary = *BinaryOrErr.get().getBinary();
if (Archive *a = dyn_cast<Archive>(&Binary))
@@ -1499,7 +1578,6 @@ int main(int argc, char **argv) {
// Initialize targets and assembly printers/parsers.
llvm::InitializeAllTargetInfos();
llvm::InitializeAllTargetMCs();
- llvm::InitializeAllAsmParsers();
llvm::InitializeAllDisassemblers();
// Register the target printer for --version.
@@ -1514,6 +1592,8 @@ int main(int argc, char **argv) {
if (InputFilenames.size() == 0)
InputFilenames.push_back("a.out");
+ if (DisassembleAll)
+ Disassemble = true;
if (!Disassemble
&& !Relocations
&& !SectionHeaders
@@ -1536,7 +1616,7 @@ int main(int argc, char **argv) {
&& !(DylibsUsed && MachOOpt)
&& !(DylibId && MachOOpt)
&& !(ObjcMetaData && MachOOpt)
- && !(DumpSections.size() != 0 && MachOOpt)
+ && !(FilterSections.size() != 0 && MachOOpt)
&& !PrintFaultMaps) {
cl::PrintHelpMessage();
return 2;
@@ -1545,5 +1625,5 @@ int main(int argc, char **argv) {
std::for_each(InputFilenames.begin(), InputFilenames.end(),
DumpInput);
- return ReturnValue;
+ return EXIT_SUCCESS;
}
diff --git a/tools/llvm-objdump/llvm-objdump.h b/tools/llvm-objdump/llvm-objdump.h
index eb10d8344f71..6e8ad6bda156 100644
--- a/tools/llvm-objdump/llvm-objdump.h
+++ b/tools/llvm-objdump/llvm-objdump.h
@@ -11,6 +11,7 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Compiler.h"
#include "llvm/Support/DataTypes.h"
namespace llvm {
@@ -25,8 +26,9 @@ extern cl::opt<std::string> TripleName;
extern cl::opt<std::string> ArchName;
extern cl::opt<std::string> MCPU;
extern cl::list<std::string> MAttrs;
-extern cl::list<std::string> DumpSections;
+extern cl::list<std::string> FilterSections;
extern cl::opt<bool> Disassemble;
+extern cl::opt<bool> DisassembleAll;
extern cl::opt<bool> NoShowRawInsn;
extern cl::opt<bool> PrivateHeaders;
extern cl::opt<bool> ExportsTrie;
@@ -54,7 +56,7 @@ extern cl::opt<bool> UnwindInfo;
extern cl::opt<bool> PrintImmHex;
// Various helper functions.
-bool error(std::error_code ec);
+void error(std::error_code ec);
bool RelocAddressLess(object::RelocationRef a, object::RelocationRef b);
void ParseInputMachO(StringRef Filename);
void printCOFFUnwindInfo(const object::COFFObjectFile* o);
@@ -66,6 +68,7 @@ void printMachOLazyBindTable(const object::MachOObjectFile* o);
void printMachOWeakBindTable(const object::MachOObjectFile* o);
void printELFFileHeader(const object::ObjectFile *o);
void printCOFFFileHeader(const object::ObjectFile *o);
+void printCOFFSymbolTable(const object::COFFObjectFile *o);
void printMachOFileHeader(const object::ObjectFile *o);
void printExportsTrie(const object::ObjectFile *o);
void printRebaseTable(const object::ObjectFile *o);
@@ -77,6 +80,7 @@ void PrintRelocations(const object::ObjectFile *o);
void PrintSectionHeaders(const object::ObjectFile *o);
void PrintSectionContents(const object::ObjectFile *o);
void PrintSymbolTable(const object::ObjectFile *o);
+LLVM_ATTRIBUTE_NORETURN void report_error(StringRef File, std::error_code EC);
} // end namespace llvm
diff --git a/tools/llvm-pdbdump/BuiltinDumper.cpp b/tools/llvm-pdbdump/BuiltinDumper.cpp
index d80829841f3a..43270540f453 100644
--- a/tools/llvm-pdbdump/BuiltinDumper.cpp
+++ b/tools/llvm-pdbdump/BuiltinDumper.cpp
@@ -19,69 +19,53 @@ BuiltinDumper::BuiltinDumper(LinePrinter &P)
: PDBSymDumper(false), Printer(P) {}
void BuiltinDumper::start(const PDBSymbolTypeBuiltin &Symbol) {
+ WithColor(Printer, PDB_ColorItem::Type).get() << getTypeName(Symbol);
+}
+
+StringRef BuiltinDumper::getTypeName(const PDBSymbolTypeBuiltin &Symbol) {
PDB_BuiltinType Type = Symbol.getBuiltinType();
switch (Type) {
case PDB_BuiltinType::Float:
if (Symbol.getLength() == 4)
- WithColor(Printer, PDB_ColorItem::Type).get() << "float";
- else
- WithColor(Printer, PDB_ColorItem::Type).get() << "double";
- break;
+ return "float";
+ return "double";
case PDB_BuiltinType::UInt:
- WithColor(Printer, PDB_ColorItem::Type).get() << "unsigned";
if (Symbol.getLength() == 8)
- WithColor(Printer, PDB_ColorItem::Type).get() << " __int64";
- break;
+ return "unsigned __int64";
+ return "unsigned";
case PDB_BuiltinType::Int:
if (Symbol.getLength() == 4)
- WithColor(Printer, PDB_ColorItem::Type).get() << "int";
- else
- WithColor(Printer, PDB_ColorItem::Type).get() << "__int64";
- break;
+ return "int";
+ return "__int64";
case PDB_BuiltinType::Char:
- WithColor(Printer, PDB_ColorItem::Type).get() << "char";
- break;
+ return "char";
case PDB_BuiltinType::WCharT:
- WithColor(Printer, PDB_ColorItem::Type).get() << "wchar_t";
- break;
+ return "wchar_t";
case PDB_BuiltinType::Void:
- WithColor(Printer, PDB_ColorItem::Type).get() << "void";
- break;
+ return "void";
case PDB_BuiltinType::Long:
- WithColor(Printer, PDB_ColorItem::Type).get() << "long";
- break;
+ return "long";
case PDB_BuiltinType::ULong:
- WithColor(Printer, PDB_ColorItem::Type).get() << "unsigned long";
- break;
+ return "unsigned long";
case PDB_BuiltinType::Bool:
- WithColor(Printer, PDB_ColorItem::Type).get() << "bool";
- break;
+ return "bool";
case PDB_BuiltinType::Currency:
- WithColor(Printer, PDB_ColorItem::Type).get() << "CURRENCY";
- break;
+ return "CURRENCY";
case PDB_BuiltinType::Date:
- WithColor(Printer, PDB_ColorItem::Type).get() << "DATE";
- break;
+ return "DATE";
case PDB_BuiltinType::Variant:
- WithColor(Printer, PDB_ColorItem::Type).get() << "VARIANT";
- break;
+ return "VARIANT";
case PDB_BuiltinType::Complex:
- WithColor(Printer, PDB_ColorItem::Type).get() << "complex";
- break;
+ return "complex";
case PDB_BuiltinType::Bitfield:
- WithColor(Printer, PDB_ColorItem::Type).get() << "bitfield";
- break;
+ return "bitfield";
case PDB_BuiltinType::BSTR:
- WithColor(Printer, PDB_ColorItem::Type).get() << "BSTR";
- break;
+ return "BSTR";
case PDB_BuiltinType::HResult:
- WithColor(Printer, PDB_ColorItem::Type).get() << "HRESULT";
- break;
+ return "HRESULT";
case PDB_BuiltinType::BCD:
- WithColor(Printer, PDB_ColorItem::Type).get() << "HRESULT";
- break;
+ return "HRESULT";
default:
- WithColor(Printer, PDB_ColorItem::Type).get() << "void";
- break;
+ return "void";
}
}
diff --git a/tools/llvm-pdbdump/BuiltinDumper.h b/tools/llvm-pdbdump/BuiltinDumper.h
index 8cf984a0ca61..ac666dbd059b 100644
--- a/tools/llvm-pdbdump/BuiltinDumper.h
+++ b/tools/llvm-pdbdump/BuiltinDumper.h
@@ -23,6 +23,8 @@ public:
void start(const PDBSymbolTypeBuiltin &Symbol);
private:
+ StringRef getTypeName(const PDBSymbolTypeBuiltin &Symbol);
+
LinePrinter &Printer;
};
}
diff --git a/tools/llvm-pdbdump/LinePrinter.cpp b/tools/llvm-pdbdump/LinePrinter.cpp
index 6bbc403f5caf..a43727f02b5e 100644
--- a/tools/llvm-pdbdump/LinePrinter.cpp
+++ b/tools/llvm-pdbdump/LinePrinter.cpp
@@ -11,19 +11,49 @@
#include "llvm-pdbdump.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Regex.h"
#include <algorithm>
+namespace {
+bool IsItemExcluded(llvm::StringRef Item,
+ std::list<llvm::Regex> &IncludeFilters,
+ std::list<llvm::Regex> &ExcludeFilters) {
+ if (Item.empty())
+ return false;
+
+ auto match_pred = [Item](llvm::Regex &R) { return R.match(Item); };
+
+ // Include takes priority over exclude. If the user specified include
+ // filters, and none of them include this item, them item is gone.
+ if (!IncludeFilters.empty() && !any_of(IncludeFilters, match_pred))
+ return true;
+
+ if (any_of(ExcludeFilters, match_pred))
+ return true;
+
+ return false;
+}
+}
+
using namespace llvm;
LinePrinter::LinePrinter(int Indent, llvm::raw_ostream &Stream)
: OS(Stream), IndentSpaces(Indent), CurrentIndent(0) {
- SetFilters(TypeFilters, opts::ExcludeTypes.begin(), opts::ExcludeTypes.end());
- SetFilters(SymbolFilters, opts::ExcludeSymbols.begin(),
+ SetFilters(ExcludeTypeFilters, opts::ExcludeTypes.begin(),
+ opts::ExcludeTypes.end());
+ SetFilters(ExcludeSymbolFilters, opts::ExcludeSymbols.begin(),
opts::ExcludeSymbols.end());
- SetFilters(CompilandFilters, opts::ExcludeCompilands.begin(),
+ SetFilters(ExcludeCompilandFilters, opts::ExcludeCompilands.begin(),
opts::ExcludeCompilands.end());
+
+ SetFilters(IncludeTypeFilters, opts::IncludeTypes.begin(),
+ opts::IncludeTypes.end());
+ SetFilters(IncludeSymbolFilters, opts::IncludeSymbols.begin(),
+ opts::IncludeSymbols.end());
+ SetFilters(IncludeCompilandFilters, opts::IncludeCompilands.begin(),
+ opts::IncludeCompilands.end());
}
void LinePrinter::Indent() { CurrentIndent += IndentSpaces; }
@@ -38,87 +68,53 @@ void LinePrinter::NewLine() {
}
bool LinePrinter::IsTypeExcluded(llvm::StringRef TypeName) {
- if (TypeName.empty())
- return false;
-
- for (auto &Expr : TypeFilters) {
- if (Expr.match(TypeName))
- return true;
- }
- return false;
+ return IsItemExcluded(TypeName, IncludeTypeFilters, ExcludeTypeFilters);
}
bool LinePrinter::IsSymbolExcluded(llvm::StringRef SymbolName) {
- if (SymbolName.empty())
- return false;
-
- for (auto &Expr : SymbolFilters) {
- if (Expr.match(SymbolName))
- return true;
- }
- return false;
+ return IsItemExcluded(SymbolName, IncludeSymbolFilters, ExcludeSymbolFilters);
}
bool LinePrinter::IsCompilandExcluded(llvm::StringRef CompilandName) {
- if (CompilandName.empty())
- return false;
-
- for (auto &Expr : CompilandFilters) {
- if (Expr.match(CompilandName))
- return true;
- }
- return false;
+ return IsItemExcluded(CompilandName, IncludeCompilandFilters,
+ ExcludeCompilandFilters);
}
WithColor::WithColor(LinePrinter &P, PDB_ColorItem C) : OS(P.OS) {
- if (C == PDB_ColorItem::None)
- OS.resetColor();
- else {
- raw_ostream::Colors Color;
- bool Bold;
- translateColor(C, Color, Bold);
- OS.changeColor(Color, Bold);
- }
+ applyColor(C);
}
WithColor::~WithColor() { OS.resetColor(); }
-void WithColor::translateColor(PDB_ColorItem C, raw_ostream::Colors &Color,
- bool &Bold) const {
+void WithColor::applyColor(PDB_ColorItem C) {
switch (C) {
+ case PDB_ColorItem::None:
+ OS.resetColor();
+ return;
case PDB_ColorItem::Address:
- Color = raw_ostream::YELLOW;
- Bold = true;
+ OS.changeColor(raw_ostream::YELLOW, /*bold=*/true);
return;
case PDB_ColorItem::Keyword:
- Color = raw_ostream::MAGENTA;
- Bold = true;
+ OS.changeColor(raw_ostream::MAGENTA, true);
return;
case PDB_ColorItem::Register:
case PDB_ColorItem::Offset:
- Color = raw_ostream::YELLOW;
- Bold = false;
+ OS.changeColor(raw_ostream::YELLOW, false);
return;
case PDB_ColorItem::Type:
- Color = raw_ostream::CYAN;
- Bold = true;
+ OS.changeColor(raw_ostream::CYAN, true);
return;
case PDB_ColorItem::Identifier:
- Color = raw_ostream::CYAN;
- Bold = false;
+ OS.changeColor(raw_ostream::CYAN, false);
return;
case PDB_ColorItem::Path:
- Color = raw_ostream::CYAN;
- Bold = false;
+ OS.changeColor(raw_ostream::CYAN, false);
return;
case PDB_ColorItem::SectionHeader:
- Color = raw_ostream::RED;
- Bold = true;
+ OS.changeColor(raw_ostream::RED, true);
return;
case PDB_ColorItem::LiteralValue:
- Color = raw_ostream::GREEN;
- Bold = true;
- default:
+ OS.changeColor(raw_ostream::GREEN, true);
return;
}
}
diff --git a/tools/llvm-pdbdump/LinePrinter.h b/tools/llvm-pdbdump/LinePrinter.h
index b985e9341909..b0a9d2cfc4e8 100644
--- a/tools/llvm-pdbdump/LinePrinter.h
+++ b/tools/llvm-pdbdump/LinePrinter.h
@@ -48,9 +48,13 @@ private:
int IndentSpaces;
int CurrentIndent;
- std::list<Regex> CompilandFilters;
- std::list<Regex> TypeFilters;
- std::list<Regex> SymbolFilters;
+ std::list<Regex> ExcludeCompilandFilters;
+ std::list<Regex> ExcludeTypeFilters;
+ std::list<Regex> ExcludeSymbolFilters;
+
+ std::list<Regex> IncludeCompilandFilters;
+ std::list<Regex> IncludeTypeFilters;
+ std::list<Regex> IncludeSymbolFilters;
};
template <class T>
@@ -80,8 +84,7 @@ public:
raw_ostream &get() { return OS; }
private:
- void translateColor(PDB_ColorItem C, raw_ostream::Colors &Color,
- bool &Bold) const;
+ void applyColor(PDB_ColorItem C);
raw_ostream &OS;
};
}
diff --git a/tools/llvm-pdbdump/llvm-pdbdump.cpp b/tools/llvm-pdbdump/llvm-pdbdump.cpp
index 4a4c64b80cc1..0e3f0b281fe0 100644
--- a/tools/llvm-pdbdump/llvm-pdbdump.cpp
+++ b/tools/llvm-pdbdump/llvm-pdbdump.cpp
@@ -22,6 +22,8 @@
#include "VariableDumper.h"
#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/BitVector.h"
+#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Config/config.h"
#include "llvm/DebugInfo/PDB/IPDBEnumChildren.h"
@@ -38,12 +40,16 @@
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/Signals.h"
#if defined(HAVE_DIA_SDK)
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif
#include <Windows.h>
#endif
@@ -79,6 +85,17 @@ cl::opt<uint64_t> LoadAddress(
cl::desc("Assume the module is loaded at the specified address"),
cl::cat(OtherOptions));
+cl::opt<bool> DumpHeaders("dump-headers", cl::desc("dump PDB headers"),
+ cl::cat(OtherOptions));
+cl::opt<bool> DumpStreamSizes("dump-stream-sizes",
+ cl::desc("dump PDB stream sizes"),
+ cl::cat(OtherOptions));
+cl::opt<bool> DumpStreamBlocks("dump-stream-blocks",
+ cl::desc("dump PDB stream blocks"),
+ cl::cat(OtherOptions));
+cl::opt<std::string> DumpStreamData("dump-stream", cl::desc("dump stream data"),
+ cl::cat(OtherOptions));
+
cl::list<std::string>
ExcludeTypes("exclude-types",
cl::desc("Exclude types by regular expression"),
@@ -91,6 +108,20 @@ cl::list<std::string>
ExcludeCompilands("exclude-compilands",
cl::desc("Exclude compilands by regular expression"),
cl::ZeroOrMore, cl::cat(FilterCategory));
+
+cl::list<std::string> IncludeTypes(
+ "include-types",
+ cl::desc("Include only types which match a regular expression"),
+ cl::ZeroOrMore, cl::cat(FilterCategory));
+cl::list<std::string> IncludeSymbols(
+ "include-symbols",
+ cl::desc("Include only symbols which match a regular expression"),
+ cl::ZeroOrMore, cl::cat(FilterCategory));
+cl::list<std::string> IncludeCompilands(
+ "include-compilands",
+ cl::desc("Include only compilands those which match a regular expression"),
+ cl::ZeroOrMore, cl::cat(FilterCategory));
+
cl::opt<bool> ExcludeCompilerGenerated(
"no-compiler-generated",
cl::desc("Don't show compiler generated types and symbols"),
@@ -107,10 +138,264 @@ cl::opt<bool> NoEnumDefs("no-enum-definitions",
cl::cat(FilterCategory));
}
+
+static void reportError(StringRef Input, StringRef Message) {
+ if (Input == "-")
+ Input = "<stdin>";
+ errs() << Input << ": " << Message << "\n";
+ errs().flush();
+ exit(1);
+}
+
+static void reportError(StringRef Input, std::error_code EC) {
+ reportError(Input, EC.message());
+}
+
+static std::error_code checkOffset(MemoryBufferRef M, uintptr_t Addr,
+ const uint64_t Size) {
+ if (Addr + Size < Addr || Addr + Size < Size ||
+ Addr + Size > uintptr_t(M.getBufferEnd()) ||
+ Addr < uintptr_t(M.getBufferStart())) {
+ return std::make_error_code(std::errc::bad_address);
+ }
+ return std::error_code();
+}
+
+template <typename T>
+static std::error_code checkOffset(MemoryBufferRef M, ArrayRef<T> AR) {
+ return checkOffset(M, uintptr_t(AR.data()), (uint64_t)AR.size() * sizeof(T));
+}
+
+static std::error_code checkOffset(MemoryBufferRef M, StringRef SR) {
+ return checkOffset(M, uintptr_t(SR.data()), SR.size());
+}
+
+// Sets Obj unless any bytes in [addr, addr + size) fall outsize of m.
+// Returns unexpected_eof if error.
+template <typename T>
+static std::error_code getObject(const T *&Obj, MemoryBufferRef M,
+ const void *Ptr,
+ const uint64_t Size = sizeof(T)) {
+ uintptr_t Addr = uintptr_t(Ptr);
+ if (std::error_code EC = checkOffset(M, Addr, Size))
+ return EC;
+ Obj = reinterpret_cast<const T *>(Addr);
+ return std::error_code();
+}
+
+static uint64_t bytesToBlocks(uint64_t NumBytes, uint64_t BlockSize) {
+ return RoundUpToAlignment(NumBytes, BlockSize) / BlockSize;
+}
+
+static uint64_t blockToOffset(uint64_t BlockNumber, uint64_t BlockSize) {
+ return BlockNumber * BlockSize;
+}
+
+static void dumpStructure(MemoryBufferRef M) {
+ const PDB::SuperBlock *SB;
+ if (auto EC = getObject(SB, M, M.getBufferStart()))
+ reportError(M.getBufferIdentifier(), EC);
+
+ if (opts::DumpHeaders) {
+ outs() << "BlockSize: " << SB->BlockSize << '\n';
+ outs() << "Unknown0: " << SB->Unknown0 << '\n';
+ outs() << "NumBlocks: " << SB->NumBlocks << '\n';
+ outs() << "NumDirectoryBytes: " << SB->NumDirectoryBytes << '\n';
+ outs() << "Unknown1: " << SB->Unknown1 << '\n';
+ outs() << "BlockMapAddr: " << SB->BlockMapAddr << '\n';
+ }
+
+ // We don't support blocksizes which aren't a multiple of four bytes.
+ if (SB->BlockSize % sizeof(support::ulittle32_t) != 0)
+ reportError(M.getBufferIdentifier(),
+ std::make_error_code(std::errc::illegal_byte_sequence));
+
+ // We don't support directories whose sizes aren't a multiple of four bytes.
+ if (SB->NumDirectoryBytes % sizeof(support::ulittle32_t) != 0)
+ reportError(M.getBufferIdentifier(),
+ std::make_error_code(std::errc::illegal_byte_sequence));
+
+ // The number of blocks which comprise the directory is a simple function of
+ // the number of bytes it contains.
+ uint64_t NumDirectoryBlocks =
+ bytesToBlocks(SB->NumDirectoryBytes, SB->BlockSize);
+ if (opts::DumpHeaders)
+ outs() << "NumDirectoryBlocks: " << NumDirectoryBlocks << '\n';
+
+ // The block map, as we understand it, is a block which consists of a list of
+ // block numbers.
+ // It is unclear what would happen if the number of blocks couldn't fit on a
+ // single block.
+ if (NumDirectoryBlocks > SB->BlockSize / sizeof(support::ulittle32_t))
+ reportError(M.getBufferIdentifier(),
+ std::make_error_code(std::errc::illegal_byte_sequence));
+
+
+ uint64_t BlockMapOffset = (uint64_t)SB->BlockMapAddr * SB->BlockSize;
+ if (opts::DumpHeaders)
+ outs() << "BlockMapOffset: " << BlockMapOffset << '\n';
+
+ // The directory is not contiguous. Instead, the block map contains a
+ // contiguous list of block numbers whose contents, when concatenated in
+ // order, make up the directory.
+ auto DirectoryBlocks =
+ makeArrayRef(reinterpret_cast<const support::ulittle32_t *>(
+ M.getBufferStart() + BlockMapOffset),
+ NumDirectoryBlocks);
+ if (auto EC = checkOffset(M, DirectoryBlocks))
+ reportError(M.getBufferIdentifier(), EC);
+
+ if (opts::DumpHeaders) {
+ outs() << "DirectoryBlocks: [";
+ for (const support::ulittle32_t &DirectoryBlockAddr : DirectoryBlocks) {
+ if (&DirectoryBlockAddr != &DirectoryBlocks.front())
+ outs() << ", ";
+ outs() << DirectoryBlockAddr;
+ }
+ outs() << "]\n";
+ }
+
+ bool SeenNumStreams = false;
+ uint32_t NumStreams = 0;
+ std::vector<uint32_t> StreamSizes;
+ DenseMap<uint32_t, std::vector<uint32_t>> StreamMap;
+ uint32_t StreamIdx = 0;
+ uint64_t DirectoryBytesRead = 0;
+ // The structure of the directory is as follows:
+ // struct PDBDirectory {
+ // uint32_t NumStreams;
+ // uint32_t StreamSizes[NumStreams];
+ // uint32_t StreamMap[NumStreams][];
+ // };
+ //
+ // Empty streams don't consume entries in the StreamMap.
+ for (uint32_t DirectoryBlockAddr : DirectoryBlocks) {
+ uint64_t DirectoryBlockOffset =
+ blockToOffset(DirectoryBlockAddr, SB->BlockSize);
+ auto DirectoryBlock =
+ makeArrayRef(reinterpret_cast<const support::ulittle32_t *>(
+ M.getBufferStart() + DirectoryBlockOffset),
+ SB->BlockSize / sizeof(support::ulittle32_t));
+ if (auto EC = checkOffset(M, DirectoryBlock))
+ reportError(M.getBufferIdentifier(), EC);
+
+ // We read data out of the directory four bytes at a time. Depending on
+ // where we are in the directory, the contents may be: the number of streams
+ // in the directory, a stream's size, or a block in the stream map.
+ for (uint32_t Data : DirectoryBlock) {
+ // Don't read beyond the end of the directory.
+ if (DirectoryBytesRead == SB->NumDirectoryBytes)
+ break;
+
+ DirectoryBytesRead += sizeof(Data);
+
+ // This data must be the number of streams if we haven't seen it yet.
+ if (!SeenNumStreams) {
+ NumStreams = Data;
+ SeenNumStreams = true;
+ continue;
+ }
+ // This data must be a stream size if we have not seen them all yet.
+ if (StreamSizes.size() < NumStreams) {
+ // It seems like some streams have their set to -1 when their contents
+ // are not present. Treat them like empty streams for now.
+ if (Data == UINT32_MAX)
+ StreamSizes.push_back(0);
+ else
+ StreamSizes.push_back(Data);
+ continue;
+ }
+
+ // This data must be a stream block number if we have seen all of the
+ // stream sizes.
+ std::vector<uint32_t> *StreamBlocks = nullptr;
+ // Figure out which stream this block number belongs to.
+ while (StreamIdx < NumStreams) {
+ uint64_t NumExpectedStreamBlocks =
+ bytesToBlocks(StreamSizes[StreamIdx], SB->BlockSize);
+ StreamBlocks = &StreamMap[StreamIdx];
+ if (NumExpectedStreamBlocks > StreamBlocks->size())
+ break;
+ ++StreamIdx;
+ }
+ // It seems this block doesn't belong to any stream? The stream is either
+ // corrupt or something more mysterious is going on.
+ if (StreamIdx == NumStreams)
+ reportError(M.getBufferIdentifier(),
+ std::make_error_code(std::errc::illegal_byte_sequence));
+
+ StreamBlocks->push_back(Data);
+ }
+ }
+
+ // We should have read exactly SB->NumDirectoryBytes bytes.
+ assert(DirectoryBytesRead == SB->NumDirectoryBytes);
+
+ if (opts::DumpHeaders)
+ outs() << "NumStreams: " << NumStreams << '\n';
+ if (opts::DumpStreamSizes)
+ for (uint32_t StreamIdx = 0; StreamIdx < NumStreams; ++StreamIdx)
+ outs() << "StreamSizes[" << StreamIdx << "]: " << StreamSizes[StreamIdx]
+ << '\n';
+
+ if (opts::DumpStreamBlocks) {
+ for (uint32_t StreamIdx = 0; StreamIdx < NumStreams; ++StreamIdx) {
+ outs() << "StreamBlocks[" << StreamIdx << "]: [";
+ std::vector<uint32_t> &StreamBlocks = StreamMap[StreamIdx];
+ for (uint32_t &StreamBlock : StreamBlocks) {
+ if (&StreamBlock != &StreamBlocks.front())
+ outs() << ", ";
+ outs() << StreamBlock;
+ }
+ outs() << "]\n";
+ }
+ }
+
+ StringRef DumpStreamStr = opts::DumpStreamData;
+ uint32_t DumpStreamNum;
+ if (!DumpStreamStr.getAsInteger(/*Radix=*/0U, DumpStreamNum) &&
+ DumpStreamNum < NumStreams) {
+ uint32_t StreamBytesRead = 0;
+ uint32_t StreamSize = StreamSizes[DumpStreamNum];
+ std::vector<uint32_t> &StreamBlocks = StreamMap[DumpStreamNum];
+ for (uint32_t &StreamBlockAddr : StreamBlocks) {
+ uint64_t StreamBlockOffset = blockToOffset(StreamBlockAddr, SB->BlockSize);
+ uint32_t BytesLeftToReadInStream = StreamSize - StreamBytesRead;
+ if (BytesLeftToReadInStream == 0)
+ break;
+
+ uint32_t BytesToReadInBlock = std::min(
+ BytesLeftToReadInStream, static_cast<uint32_t>(SB->BlockSize));
+ auto StreamBlockData =
+ StringRef(M.getBufferStart() + StreamBlockOffset, BytesToReadInBlock);
+ if (auto EC = checkOffset(M, StreamBlockData))
+ reportError(M.getBufferIdentifier(), EC);
+
+ outs() << StreamBlockData;
+ StreamBytesRead += StreamBlockData.size();
+ }
+ }
+}
+
static void dumpInput(StringRef Path) {
+ if (opts::DumpHeaders || !opts::DumpStreamData.empty()) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> ErrorOrBuffer =
+ MemoryBuffer::getFileOrSTDIN(Path, /*FileSize=*/-1,
+ /*RequiresNullTerminator=*/false);
+
+ if (std::error_code EC = ErrorOrBuffer.getError())
+ reportError(Path, EC);
+
+ std::unique_ptr<MemoryBuffer> &Buffer = ErrorOrBuffer.get();
+
+ dumpStructure(Buffer->getMemBufferRef());
+
+ outs().flush();
+ return;
+ }
+
std::unique_ptr<IPDBSession> Session;
- PDB_ErrorCode Error =
- llvm::loadDataForPDB(PDB_ReaderType::DIA, Path, Session);
+ PDB_ErrorCode Error = loadDataForPDB(PDB_ReaderType::DIA, Path, Session);
switch (Error) {
case PDB_ErrorCode::Success:
break;
@@ -145,7 +430,7 @@ static void dumpInput(StringRef Path) {
Printer.NewLine();
WithColor(Printer, PDB_ColorItem::Identifier).get() << "Size";
- if (!llvm::sys::fs::file_size(FileName, FileSize)) {
+ if (!sys::fs::file_size(FileName, FileSize)) {
Printer << ": " << FileSize << " bytes";
} else {
Printer << ": (Unable to obtain file size)";
@@ -242,11 +527,11 @@ int main(int argc_, const char *argv_[]) {
PrettyStackTraceProgram X(argc_, argv_);
SmallVector<const char *, 256> argv;
- llvm::SpecificBumpPtrAllocator<char> ArgAllocator;
- std::error_code EC = llvm::sys::Process::GetArgumentVector(
- argv, llvm::makeArrayRef(argv_, argc_), ArgAllocator);
+ SpecificBumpPtrAllocator<char> ArgAllocator;
+ std::error_code EC = sys::Process::GetArgumentVector(
+ argv, makeArrayRef(argv_, argc_), ArgAllocator);
if (EC) {
- llvm::errs() << "error: couldn't get arguments: " << EC.message() << '\n';
+ errs() << "error: couldn't get arguments: " << EC.message() << '\n';
return 1;
}
diff --git a/tools/llvm-pdbdump/llvm-pdbdump.h b/tools/llvm-pdbdump/llvm-pdbdump.h
index 586a9ea374ed..cb5bec64dec8 100644
--- a/tools/llvm-pdbdump/llvm-pdbdump.h
+++ b/tools/llvm-pdbdump/llvm-pdbdump.h
@@ -27,6 +27,9 @@ extern llvm::cl::opt<bool> NoEnumDefs;
extern llvm::cl::list<std::string> ExcludeTypes;
extern llvm::cl::list<std::string> ExcludeSymbols;
extern llvm::cl::list<std::string> ExcludeCompilands;
+extern llvm::cl::list<std::string> IncludeTypes;
+extern llvm::cl::list<std::string> IncludeSymbols;
+extern llvm::cl::list<std::string> IncludeCompilands;
}
#endif \ No newline at end of file
diff --git a/tools/llvm-profdata/llvm-profdata.cpp b/tools/llvm-profdata/llvm-profdata.cpp
index 6fb48d8fad58..dc6cd0a5784d 100644
--- a/tools/llvm-profdata/llvm-profdata.cpp
+++ b/tools/llvm-profdata/llvm-profdata.cpp
@@ -11,6 +11,8 @@
//
//===----------------------------------------------------------------------===//
+#include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/ProfileData/InstrProfReader.h"
@@ -18,6 +20,7 @@
#include "llvm/ProfileData/SampleProfReader.h"
#include "llvm/ProfileData/SampleProfWriter.h"
#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Errc.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/ManagedStatic.h"
@@ -26,67 +29,150 @@
#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+#include <tuple>
using namespace llvm;
-static void exitWithError(const Twine &Message, StringRef Whence = "") {
+enum ProfileFormat { PF_None = 0, PF_Text, PF_Binary, PF_GCC };
+
+static void exitWithError(const Twine &Message, StringRef Whence = "",
+ StringRef Hint = "") {
errs() << "error: ";
if (!Whence.empty())
errs() << Whence << ": ";
errs() << Message << "\n";
+ if (!Hint.empty())
+ errs() << Hint << "\n";
::exit(1);
}
+static void exitWithErrorCode(const std::error_code &Error,
+ StringRef Whence = "") {
+ if (Error.category() == instrprof_category()) {
+ instrprof_error instrError = static_cast<instrprof_error>(Error.value());
+ if (instrError == instrprof_error::unrecognized_format) {
+ // Hint for common error of forgetting -sample for sample profiles.
+ exitWithError(Error.message(), Whence,
+ "Perhaps you forgot to use the -sample option?");
+ }
+ }
+ exitWithError(Error.message(), Whence);
+}
+
namespace {
enum ProfileKinds { instr, sample };
}
-static void mergeInstrProfile(const cl::list<std::string> &Inputs,
- StringRef OutputFilename) {
+static void handleMergeWriterError(std::error_code &Error,
+ StringRef WhenceFile = "",
+ StringRef WhenceFunction = "",
+ bool ShowHint = true) {
+ if (!WhenceFile.empty())
+ errs() << WhenceFile << ": ";
+ if (!WhenceFunction.empty())
+ errs() << WhenceFunction << ": ";
+ errs() << Error.message() << "\n";
+
+ if (ShowHint) {
+ StringRef Hint = "";
+ if (Error.category() == instrprof_category()) {
+ instrprof_error instrError = static_cast<instrprof_error>(Error.value());
+ switch (instrError) {
+ case instrprof_error::hash_mismatch:
+ case instrprof_error::count_mismatch:
+ case instrprof_error::value_site_count_mismatch:
+ Hint = "Make sure that all profile data to be merged is generated "
+ "from the same binary.";
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (!Hint.empty())
+ errs() << Hint << "\n";
+ }
+}
+
+struct WeightedFile {
+ StringRef Filename;
+ uint64_t Weight;
+
+ WeightedFile() {}
+
+ WeightedFile(StringRef F, uint64_t W) : Filename{F}, Weight{W} {}
+};
+typedef SmallVector<WeightedFile, 5> WeightedFileVector;
+
+static void mergeInstrProfile(const WeightedFileVector &Inputs,
+ StringRef OutputFilename,
+ ProfileFormat OutputFormat) {
if (OutputFilename.compare("-") == 0)
exitWithError("Cannot write indexed profdata format to stdout.");
+ if (OutputFormat != PF_Binary && OutputFormat != PF_Text)
+ exitWithError("Unknown format is specified.");
+
std::error_code EC;
raw_fd_ostream Output(OutputFilename.data(), EC, sys::fs::F_None);
if (EC)
- exitWithError(EC.message(), OutputFilename);
+ exitWithErrorCode(EC, OutputFilename);
InstrProfWriter Writer;
- for (const auto &Filename : Inputs) {
- auto ReaderOrErr = InstrProfReader::create(Filename);
+ SmallSet<std::error_code, 4> WriterErrorCodes;
+ for (const auto &Input : Inputs) {
+ auto ReaderOrErr = InstrProfReader::create(Input.Filename);
if (std::error_code ec = ReaderOrErr.getError())
- exitWithError(ec.message(), Filename);
+ exitWithErrorCode(ec, Input.Filename);
auto Reader = std::move(ReaderOrErr.get());
- for (const auto &I : *Reader)
- if (std::error_code EC =
- Writer.addFunctionCounts(I.Name, I.Hash, I.Counts))
- errs() << Filename << ": " << I.Name << ": " << EC.message() << "\n";
+ for (auto &I : *Reader) {
+ if (std::error_code EC = Writer.addRecord(std::move(I), Input.Weight)) {
+ // Only show hint the first time an error occurs.
+ bool firstTime = WriterErrorCodes.insert(EC).second;
+ handleMergeWriterError(EC, Input.Filename, I.Name, firstTime);
+ }
+ }
if (Reader->hasError())
- exitWithError(Reader->getError().message(), Filename);
+ exitWithErrorCode(Reader->getError(), Input.Filename);
}
- Writer.write(Output);
+ if (OutputFormat == PF_Text)
+ Writer.writeText(Output);
+ else
+ Writer.write(Output);
}
-static void mergeSampleProfile(const cl::list<std::string> &Inputs,
+static sampleprof::SampleProfileFormat FormatMap[] = {
+ sampleprof::SPF_None, sampleprof::SPF_Text, sampleprof::SPF_Binary,
+ sampleprof::SPF_GCC};
+
+static void mergeSampleProfile(const WeightedFileVector &Inputs,
StringRef OutputFilename,
- sampleprof::SampleProfileFormat OutputFormat) {
+ ProfileFormat OutputFormat) {
using namespace sampleprof;
- auto WriterOrErr = SampleProfileWriter::create(OutputFilename, OutputFormat);
+ auto WriterOrErr =
+ SampleProfileWriter::create(OutputFilename, FormatMap[OutputFormat]);
if (std::error_code EC = WriterOrErr.getError())
- exitWithError(EC.message(), OutputFilename);
+ exitWithErrorCode(EC, OutputFilename);
auto Writer = std::move(WriterOrErr.get());
StringMap<FunctionSamples> ProfileMap;
- for (const auto &Filename : Inputs) {
+ SmallVector<std::unique_ptr<sampleprof::SampleProfileReader>, 5> Readers;
+ for (const auto &Input : Inputs) {
auto ReaderOrErr =
- SampleProfileReader::create(Filename, getGlobalContext());
+ SampleProfileReader::create(Input.Filename, getGlobalContext());
if (std::error_code EC = ReaderOrErr.getError())
- exitWithError(EC.message(), Filename);
+ exitWithErrorCode(EC, Input.Filename);
- auto Reader = std::move(ReaderOrErr.get());
+ // We need to keep the readers around until after all the files are
+ // read so that we do not lose the function names stored in each
+ // reader's memory. The function names are needed to write out the
+ // merged profile map.
+ Readers.push_back(std::move(ReaderOrErr.get()));
+ const auto Reader = Readers.back().get();
if (std::error_code EC = Reader->read())
- exitWithError(EC.message(), Filename);
+ exitWithErrorCode(EC, Input.Filename);
StringMap<FunctionSamples> &Profiles = Reader->getProfiles();
for (StringMap<FunctionSamples>::iterator I = Profiles.begin(),
@@ -94,16 +180,36 @@ static void mergeSampleProfile(const cl::list<std::string> &Inputs,
I != E; ++I) {
StringRef FName = I->first();
FunctionSamples &Samples = I->second;
- ProfileMap[FName].merge(Samples);
+ sampleprof_error Result = ProfileMap[FName].merge(Samples, Input.Weight);
+ if (Result != sampleprof_error::success) {
+ std::error_code EC = make_error_code(Result);
+ handleMergeWriterError(EC, Input.Filename, FName);
+ }
}
}
Writer->write(ProfileMap);
}
-static int merge_main(int argc, const char *argv[]) {
- cl::list<std::string> Inputs(cl::Positional, cl::Required, cl::OneOrMore,
- cl::desc("<filenames...>"));
+static WeightedFile parseWeightedFile(const StringRef &WeightedFilename) {
+ StringRef WeightStr, FileName;
+ std::tie(WeightStr, FileName) = WeightedFilename.split(',');
+
+ uint64_t Weight;
+ if (WeightStr.getAsInteger(10, Weight) || Weight < 1)
+ exitWithError("Input weight must be a positive integer.");
+
+ if (!sys::fs::exists(FileName))
+ exitWithErrorCode(make_error_code(errc::no_such_file_or_directory),
+ FileName);
+
+ return WeightedFile(FileName, Weight);
+}
+static int merge_main(int argc, const char *argv[]) {
+ cl::list<std::string> InputFilenames(cl::Positional,
+ cl::desc("<filename...>"));
+ cl::list<std::string> WeightedInputFilenames("weighted-input",
+ cl::desc("<weight>,<filename>"));
cl::opt<std::string> OutputFilename("output", cl::value_desc("output"),
cl::init("-"), cl::Required,
cl::desc("Output file"));
@@ -114,31 +220,41 @@ static int merge_main(int argc, const char *argv[]) {
cl::values(clEnumVal(instr, "Instrumentation profile (default)"),
clEnumVal(sample, "Sample profile"), clEnumValEnd));
- cl::opt<sampleprof::SampleProfileFormat> OutputFormat(
- cl::desc("Format of output profile (only meaningful with --sample)"),
- cl::init(sampleprof::SPF_Binary),
- cl::values(clEnumValN(sampleprof::SPF_Binary, "binary",
- "Binary encoding (default)"),
- clEnumValN(sampleprof::SPF_Text, "text", "Text encoding"),
- clEnumValN(sampleprof::SPF_GCC, "gcc", "GCC encoding"),
+ cl::opt<ProfileFormat> OutputFormat(
+ cl::desc("Format of output profile"), cl::init(PF_Binary),
+ cl::values(clEnumValN(PF_Binary, "binary", "Binary encoding (default)"),
+ clEnumValN(PF_Text, "text", "Text encoding"),
+ clEnumValN(PF_GCC, "gcc",
+ "GCC encoding (only meaningful for -sample)"),
clEnumValEnd));
cl::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n");
+ if (InputFilenames.empty() && WeightedInputFilenames.empty())
+ exitWithError("No input files specified. See " +
+ sys::path::filename(argv[0]) + " -help");
+
+ WeightedFileVector WeightedInputs;
+ for (StringRef Filename : InputFilenames)
+ WeightedInputs.push_back(WeightedFile(Filename, 1));
+ for (StringRef WeightedFilename : WeightedInputFilenames)
+ WeightedInputs.push_back(parseWeightedFile(WeightedFilename));
+
if (ProfileKind == instr)
- mergeInstrProfile(Inputs, OutputFilename);
+ mergeInstrProfile(WeightedInputs, OutputFilename, OutputFormat);
else
- mergeSampleProfile(Inputs, OutputFilename, OutputFormat);
+ mergeSampleProfile(WeightedInputs, OutputFilename, OutputFormat);
return 0;
}
static int showInstrProfile(std::string Filename, bool ShowCounts,
- bool ShowAllFunctions, std::string ShowFunction,
+ bool ShowIndirectCallTargets, bool ShowAllFunctions,
+ std::string ShowFunction, bool TextFormat,
raw_fd_ostream &OS) {
auto ReaderOrErr = InstrProfReader::create(Filename);
if (std::error_code EC = ReaderOrErr.getError())
- exitWithError(EC.message(), Filename);
+ exitWithErrorCode(EC, Filename);
auto Reader = std::move(ReaderOrErr.get());
uint64_t MaxFunctionCount = 0, MaxBlockCount = 0;
@@ -148,35 +264,71 @@ static int showInstrProfile(std::string Filename, bool ShowCounts,
ShowAllFunctions || (!ShowFunction.empty() &&
Func.Name.find(ShowFunction) != Func.Name.npos);
+ bool doTextFormatDump = (Show && ShowCounts && TextFormat);
+
+ if (doTextFormatDump) {
+ InstrProfSymtab &Symtab = Reader->getSymtab();
+ InstrProfWriter::writeRecordInText(Func, Symtab, OS);
+ continue;
+ }
+
++TotalFunctions;
assert(Func.Counts.size() > 0 && "function missing entry counter");
if (Func.Counts[0] > MaxFunctionCount)
MaxFunctionCount = Func.Counts[0];
+ for (size_t I = 1, E = Func.Counts.size(); I < E; ++I) {
+ if (Func.Counts[I] > MaxBlockCount)
+ MaxBlockCount = Func.Counts[I];
+ }
+
if (Show) {
+
if (!ShownFunctions)
OS << "Counters:\n";
+
++ShownFunctions;
OS << " " << Func.Name << ":\n"
<< " Hash: " << format("0x%016" PRIx64, Func.Hash) << "\n"
<< " Counters: " << Func.Counts.size() << "\n"
<< " Function count: " << Func.Counts[0] << "\n";
- }
- if (Show && ShowCounts)
- OS << " Block counts: [";
- for (size_t I = 1, E = Func.Counts.size(); I < E; ++I) {
- if (Func.Counts[I] > MaxBlockCount)
- MaxBlockCount = Func.Counts[I];
- if (Show && ShowCounts)
- OS << (I == 1 ? "" : ", ") << Func.Counts[I];
+ if (ShowIndirectCallTargets)
+ OS << " Indirect Call Site Count: "
+ << Func.getNumValueSites(IPVK_IndirectCallTarget) << "\n";
+
+ if (ShowCounts) {
+ OS << " Block counts: [";
+ for (size_t I = 1, E = Func.Counts.size(); I < E; ++I) {
+ OS << (I == 1 ? "" : ", ") << Func.Counts[I];
+ }
+ OS << "]\n";
+ }
+
+ if (ShowIndirectCallTargets) {
+ InstrProfSymtab &Symtab = Reader->getSymtab();
+ uint32_t NS = Func.getNumValueSites(IPVK_IndirectCallTarget);
+ OS << " Indirect Target Results: \n";
+ for (size_t I = 0; I < NS; ++I) {
+ uint32_t NV = Func.getNumValueDataForSite(IPVK_IndirectCallTarget, I);
+ std::unique_ptr<InstrProfValueData[]> VD =
+ Func.getValueForSite(IPVK_IndirectCallTarget, I);
+ for (uint32_t V = 0; V < NV; V++) {
+ OS << "\t[ " << I << ", ";
+ OS << Symtab.getFuncName(VD[V].Value) << ", " << VD[V].Count
+ << " ]\n";
+ }
+ }
+ }
}
- if (Show && ShowCounts)
- OS << "]\n";
}
+
if (Reader->hasError())
- exitWithError(Reader->getError().message(), Filename);
+ exitWithErrorCode(Reader->getError(), Filename);
+
+ if (ShowCounts && TextFormat)
+ return 0;
if (ShowAllFunctions || !ShowFunction.empty())
OS << "Functions shown: " << ShownFunctions << "\n";
@@ -192,10 +344,12 @@ static int showSampleProfile(std::string Filename, bool ShowCounts,
using namespace sampleprof;
auto ReaderOrErr = SampleProfileReader::create(Filename, getGlobalContext());
if (std::error_code EC = ReaderOrErr.getError())
- exitWithError(EC.message(), Filename);
+ exitWithErrorCode(EC, Filename);
auto Reader = std::move(ReaderOrErr.get());
- Reader->read();
+ if (std::error_code EC = Reader->read())
+ exitWithErrorCode(EC, Filename);
+
if (ShowAllFunctions || ShowFunction.empty())
Reader->dump(OS);
else
@@ -210,6 +364,12 @@ static int show_main(int argc, const char *argv[]) {
cl::opt<bool> ShowCounts("counts", cl::init(false),
cl::desc("Show counter values for shown functions"));
+ cl::opt<bool> TextFormat(
+ "text", cl::init(false),
+ cl::desc("Show instr profile data in text dump format"));
+ cl::opt<bool> ShowIndirectCallTargets(
+ "ic-targets", cl::init(false),
+ cl::desc("Show indirect call site target values for shown functions"));
cl::opt<bool> ShowAllFunctions("all-functions", cl::init(false),
cl::desc("Details for every function"));
cl::opt<std::string> ShowFunction("function",
@@ -232,14 +392,14 @@ static int show_main(int argc, const char *argv[]) {
std::error_code EC;
raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::F_Text);
if (EC)
- exitWithError(EC.message(), OutputFilename);
+ exitWithErrorCode(EC, OutputFilename);
if (ShowAllFunctions && !ShowFunction.empty())
errs() << "warning: -function argument ignored: showing all functions\n";
if (ProfileKind == instr)
- return showInstrProfile(Filename, ShowCounts, ShowAllFunctions,
- ShowFunction, OS);
+ return showInstrProfile(Filename, ShowCounts, ShowIndirectCallTargets,
+ ShowAllFunctions, ShowFunction, TextFormat, OS);
else
return showSampleProfile(Filename, ShowCounts, ShowAllFunctions,
ShowFunction, OS);
@@ -266,8 +426,7 @@ int main(int argc, const char *argv[]) {
return func(argc - 1, argv + 1);
}
- if (strcmp(argv[1], "-h") == 0 ||
- strcmp(argv[1], "-help") == 0 ||
+ if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "-help") == 0 ||
strcmp(argv[1], "--help") == 0) {
errs() << "OVERVIEW: LLVM profile data tools\n\n"
diff --git a/tools/llvm-readobj/ARMAttributeParser.cpp b/tools/llvm-readobj/ARMAttributeParser.cpp
index e2d71912a21e..688d349d7ec0 100644
--- a/tools/llvm-readobj/ARMAttributeParser.cpp
+++ b/tools/llvm-readobj/ARMAttributeParser.cpp
@@ -118,7 +118,7 @@ void ARMAttributeParser::PrintAttribute(unsigned Tag, unsigned Value,
void ARMAttributeParser::CPU_arch(AttrType Tag, const uint8_t *Data,
uint32_t &Offset) {
- static const char *Strings[] = {
+ static const char *const Strings[] = {
"Pre-v4", "ARM v4", "ARM v4T", "ARM v5T", "ARM v5TE", "ARM v5TEJ", "ARM v6",
"ARM v6KZ", "ARM v6T2", "ARM v6K", "ARM v7", "ARM v6-M", "ARM v6S-M",
"ARM v7E-M", "ARM v8"
@@ -149,7 +149,7 @@ void ARMAttributeParser::CPU_arch_profile(AttrType Tag, const uint8_t *Data,
void ARMAttributeParser::ARM_ISA_use(AttrType Tag, const uint8_t *Data,
uint32_t &Offset) {
- static const char *Strings[] = { "Not Permitted", "Permitted" };
+ static const char *const Strings[] = { "Not Permitted", "Permitted" };
uint64_t Value = ParseInteger(Data, Offset);
StringRef ValueDesc =
@@ -159,7 +159,7 @@ void ARMAttributeParser::ARM_ISA_use(AttrType Tag, const uint8_t *Data,
void ARMAttributeParser::THUMB_ISA_use(AttrType Tag, const uint8_t *Data,
uint32_t &Offset) {
- static const char *Strings[] = { "Not Permitted", "Thumb-1", "Thumb-2" };
+ static const char *const Strings[] = { "Not Permitted", "Thumb-1", "Thumb-2" };
uint64_t Value = ParseInteger(Data, Offset);
StringRef ValueDesc =
@@ -169,7 +169,7 @@ void ARMAttributeParser::THUMB_ISA_use(AttrType Tag, const uint8_t *Data,
void ARMAttributeParser::FP_arch(AttrType Tag, const uint8_t *Data,
uint32_t &Offset) {
- static const char *Strings[] = {
+ static const char *const Strings[] = {
"Not Permitted", "VFPv1", "VFPv2", "VFPv3", "VFPv3-D16", "VFPv4",
"VFPv4-D16", "ARMv8-a FP", "ARMv8-a FP-D16"
};
@@ -182,7 +182,7 @@ void ARMAttributeParser::FP_arch(AttrType Tag, const uint8_t *Data,
void ARMAttributeParser::WMMX_arch(AttrType Tag, const uint8_t *Data,
uint32_t &Offset) {
- static const char *Strings[] = { "Not Permitted", "WMMXv1", "WMMXv2" };
+ static const char *const Strings[] = { "Not Permitted", "WMMXv1", "WMMXv2" };
uint64_t Value = ParseInteger(Data, Offset);
StringRef ValueDesc =
@@ -192,8 +192,8 @@ void ARMAttributeParser::WMMX_arch(AttrType Tag, const uint8_t *Data,
void ARMAttributeParser::Advanced_SIMD_arch(AttrType Tag, const uint8_t *Data,
uint32_t &Offset) {
- static const char *Strings[] = {
- "Not Permitted", "NEONv1", "NEONv2+FMA", "ARMv8-a NEON"
+ static const char *const Strings[] = {
+ "Not Permitted", "NEONv1", "NEONv2+FMA", "ARMv8-a NEON", "ARMv8.1-a NEON"
};
uint64_t Value = ParseInteger(Data, Offset);
@@ -204,7 +204,7 @@ void ARMAttributeParser::Advanced_SIMD_arch(AttrType Tag, const uint8_t *Data,
void ARMAttributeParser::PCS_config(AttrType Tag, const uint8_t *Data,
uint32_t &Offset) {
- static const char *Strings[] = {
+ static const char *const Strings[] = {
"None", "Bare Platform", "Linux Application", "Linux DSO", "Palm OS 2004",
"Reserved (Palm OS)", "Symbian OS 2004", "Reserved (Symbian OS)"
};
@@ -217,7 +217,7 @@ void ARMAttributeParser::PCS_config(AttrType Tag, const uint8_t *Data,
void ARMAttributeParser::ABI_PCS_R9_use(AttrType Tag, const uint8_t *Data,
uint32_t &Offset) {
- static const char *Strings[] = { "v6", "Static Base", "TLS", "Unused" };
+ static const char *const Strings[] = { "v6", "Static Base", "TLS", "Unused" };
uint64_t Value = ParseInteger(Data, Offset);
StringRef ValueDesc =
@@ -227,7 +227,7 @@ void ARMAttributeParser::ABI_PCS_R9_use(AttrType Tag, const uint8_t *Data,
void ARMAttributeParser::ABI_PCS_RW_data(AttrType Tag, const uint8_t *Data,
uint32_t &Offset) {
- static const char *Strings[] = {
+ static const char *const Strings[] = {
"Absolute", "PC-relative", "SB-relative", "Not Permitted"
};
@@ -239,7 +239,9 @@ void ARMAttributeParser::ABI_PCS_RW_data(AttrType Tag, const uint8_t *Data,
void ARMAttributeParser::ABI_PCS_RO_data(AttrType Tag, const uint8_t *Data,
uint32_t &Offset) {
- static const char *Strings[] = { "Absolute", "PC-relative", "Not Permitted" };
+ static const char *const Strings[] = {
+ "Absolute", "PC-relative", "Not Permitted"
+ };
uint64_t Value = ParseInteger(Data, Offset);
StringRef ValueDesc =
@@ -249,7 +251,9 @@ void ARMAttributeParser::ABI_PCS_RO_data(AttrType Tag, const uint8_t *Data,
void ARMAttributeParser::ABI_PCS_GOT_use(AttrType Tag, const uint8_t *Data,
uint32_t &Offset) {
- static const char *Strings[] = { "Not Permitted", "Direct", "GOT-Indirect" };
+ static const char *const Strings[] = {
+ "Not Permitted", "Direct", "GOT-Indirect"
+ };
uint64_t Value = ParseInteger(Data, Offset);
StringRef ValueDesc =
@@ -259,7 +263,7 @@ void ARMAttributeParser::ABI_PCS_GOT_use(AttrType Tag, const uint8_t *Data,
void ARMAttributeParser::ABI_PCS_wchar_t(AttrType Tag, const uint8_t *Data,
uint32_t &Offset) {
- static const char *Strings[] = {
+ static const char *const Strings[] = {
"Not Permitted", "Unknown", "2-byte", "Unknown", "4-byte"
};
@@ -271,7 +275,7 @@ void ARMAttributeParser::ABI_PCS_wchar_t(AttrType Tag, const uint8_t *Data,
void ARMAttributeParser::ABI_FP_rounding(AttrType Tag, const uint8_t *Data,
uint32_t &Offset) {
- static const char *Strings[] = { "IEEE-754", "Runtime" };
+ static const char *const Strings[] = { "IEEE-754", "Runtime" };
uint64_t Value = ParseInteger(Data, Offset);
StringRef ValueDesc =
@@ -281,7 +285,9 @@ void ARMAttributeParser::ABI_FP_rounding(AttrType Tag, const uint8_t *Data,
void ARMAttributeParser::ABI_FP_denormal(AttrType Tag, const uint8_t *Data,
uint32_t &Offset) {
- static const char *Strings[] = { "Unsupported", "IEEE-754", "Sign Only" };
+ static const char *const Strings[] = {
+ "Unsupported", "IEEE-754", "Sign Only"
+ };
uint64_t Value = ParseInteger(Data, Offset);
StringRef ValueDesc =
@@ -291,7 +297,7 @@ void ARMAttributeParser::ABI_FP_denormal(AttrType Tag, const uint8_t *Data,
void ARMAttributeParser::ABI_FP_exceptions(AttrType Tag, const uint8_t *Data,
uint32_t &Offset) {
- static const char *Strings[] = { "Not Permitted", "IEEE-754" };
+ static const char *const Strings[] = { "Not Permitted", "IEEE-754" };
uint64_t Value = ParseInteger(Data, Offset);
StringRef ValueDesc =
@@ -302,7 +308,7 @@ void ARMAttributeParser::ABI_FP_exceptions(AttrType Tag, const uint8_t *Data,
void ARMAttributeParser::ABI_FP_user_exceptions(AttrType Tag,
const uint8_t *Data,
uint32_t &Offset) {
- static const char *Strings[] = { "Not Permitted", "IEEE-754" };
+ static const char *const Strings[] = { "Not Permitted", "IEEE-754" };
uint64_t Value = ParseInteger(Data, Offset);
StringRef ValueDesc =
@@ -312,7 +318,7 @@ void ARMAttributeParser::ABI_FP_user_exceptions(AttrType Tag,
void ARMAttributeParser::ABI_FP_number_model(AttrType Tag, const uint8_t *Data,
uint32_t &Offset) {
- static const char *Strings[] = {
+ static const char *const Strings[] = {
"Not Permitted", "Finite Only", "RTABI", "IEEE-754"
};
@@ -324,7 +330,7 @@ void ARMAttributeParser::ABI_FP_number_model(AttrType Tag, const uint8_t *Data,
void ARMAttributeParser::ABI_align_needed(AttrType Tag, const uint8_t *Data,
uint32_t &Offset) {
- static const char *Strings[] = {
+ static const char *const Strings[] = {
"Not Permitted", "8-byte alignment", "4-byte alignment", "Reserved"
};
@@ -344,7 +350,7 @@ void ARMAttributeParser::ABI_align_needed(AttrType Tag, const uint8_t *Data,
void ARMAttributeParser::ABI_align_preserved(AttrType Tag, const uint8_t *Data,
uint32_t &Offset) {
- static const char *Strings[] = {
+ static const char *const Strings[] = {
"Not Required", "8-byte data alignment", "8-byte data and code alignment",
"Reserved"
};
@@ -365,7 +371,7 @@ void ARMAttributeParser::ABI_align_preserved(AttrType Tag, const uint8_t *Data,
void ARMAttributeParser::ABI_enum_size(AttrType Tag, const uint8_t *Data,
uint32_t &Offset) {
- static const char *Strings[] = {
+ static const char *const Strings[] = {
"Not Permitted", "Packed", "Int32", "External Int32"
};
@@ -377,7 +383,7 @@ void ARMAttributeParser::ABI_enum_size(AttrType Tag, const uint8_t *Data,
void ARMAttributeParser::ABI_HardFP_use(AttrType Tag, const uint8_t *Data,
uint32_t &Offset) {
- static const char *Strings[] = {
+ static const char *const Strings[] = {
"Tag_FP_arch", "Single-Precision", "Reserved", "Tag_FP_arch (deprecated)"
};
@@ -389,7 +395,7 @@ void ARMAttributeParser::ABI_HardFP_use(AttrType Tag, const uint8_t *Data,
void ARMAttributeParser::ABI_VFP_args(AttrType Tag, const uint8_t *Data,
uint32_t &Offset) {
- static const char *Strings[] = {
+ static const char *const Strings[] = {
"AAPCS", "AAPCS VFP", "Custom", "Not Permitted"
};
@@ -401,7 +407,7 @@ void ARMAttributeParser::ABI_VFP_args(AttrType Tag, const uint8_t *Data,
void ARMAttributeParser::ABI_WMMX_args(AttrType Tag, const uint8_t *Data,
uint32_t &Offset) {
- static const char *Strings[] = { "AAPCS", "iWMMX", "Custom" };
+ static const char *const Strings[] = { "AAPCS", "iWMMX", "Custom" };
uint64_t Value = ParseInteger(Data, Offset);
StringRef ValueDesc =
@@ -412,7 +418,7 @@ void ARMAttributeParser::ABI_WMMX_args(AttrType Tag, const uint8_t *Data,
void ARMAttributeParser::ABI_optimization_goals(AttrType Tag,
const uint8_t *Data,
uint32_t &Offset) {
- static const char *Strings[] = {
+ static const char *const Strings[] = {
"None", "Speed", "Aggressive Speed", "Size", "Aggressive Size", "Debugging",
"Best Debugging"
};
@@ -426,7 +432,7 @@ void ARMAttributeParser::ABI_optimization_goals(AttrType Tag,
void ARMAttributeParser::ABI_FP_optimization_goals(AttrType Tag,
const uint8_t *Data,
uint32_t &Offset) {
- static const char *Strings[] = {
+ static const char *const Strings[] = {
"None", "Speed", "Aggressive Speed", "Size", "Aggressive Size", "Accuracy",
"Best Accuracy"
};
@@ -461,7 +467,7 @@ void ARMAttributeParser::compatibility(AttrType Tag, const uint8_t *Data,
void ARMAttributeParser::CPU_unaligned_access(AttrType Tag, const uint8_t *Data,
uint32_t &Offset) {
- static const char *Strings[] = { "Not Permitted", "v6-style" };
+ static const char *const Strings[] = { "Not Permitted", "v6-style" };
uint64_t Value = ParseInteger(Data, Offset);
StringRef ValueDesc =
@@ -471,7 +477,7 @@ void ARMAttributeParser::CPU_unaligned_access(AttrType Tag, const uint8_t *Data,
void ARMAttributeParser::FP_HP_extension(AttrType Tag, const uint8_t *Data,
uint32_t &Offset) {
- static const char *Strings[] = { "If Available", "Permitted" };
+ static const char *const Strings[] = { "If Available", "Permitted" };
uint64_t Value = ParseInteger(Data, Offset);
StringRef ValueDesc =
@@ -481,7 +487,7 @@ void ARMAttributeParser::FP_HP_extension(AttrType Tag, const uint8_t *Data,
void ARMAttributeParser::ABI_FP_16bit_format(AttrType Tag, const uint8_t *Data,
uint32_t &Offset) {
- static const char *Strings[] = { "Not Permitted", "IEEE-754", "VFPv3" };
+ static const char *const Strings[] = { "Not Permitted", "IEEE-754", "VFPv3" };
uint64_t Value = ParseInteger(Data, Offset);
StringRef ValueDesc =
@@ -491,7 +497,7 @@ void ARMAttributeParser::ABI_FP_16bit_format(AttrType Tag, const uint8_t *Data,
void ARMAttributeParser::MPextension_use(AttrType Tag, const uint8_t *Data,
uint32_t &Offset) {
- static const char *Strings[] = { "Not Permitted", "Permitted" };
+ static const char *const Strings[] = { "Not Permitted", "Permitted" };
uint64_t Value = ParseInteger(Data, Offset);
StringRef ValueDesc =
@@ -501,7 +507,7 @@ void ARMAttributeParser::MPextension_use(AttrType Tag, const uint8_t *Data,
void ARMAttributeParser::DIV_use(AttrType Tag, const uint8_t *Data,
uint32_t &Offset) {
- static const char *Strings[] = {
+ static const char *const Strings[] = {
"If Available", "Not Permitted", "Permitted"
};
@@ -513,7 +519,7 @@ void ARMAttributeParser::DIV_use(AttrType Tag, const uint8_t *Data,
void ARMAttributeParser::T2EE_use(AttrType Tag, const uint8_t *Data,
uint32_t &Offset) {
- static const char *Strings[] = { "Not Permitted", "Permitted" };
+ static const char *const Strings[] = { "Not Permitted", "Permitted" };
uint64_t Value = ParseInteger(Data, Offset);
StringRef ValueDesc =
@@ -523,7 +529,7 @@ void ARMAttributeParser::T2EE_use(AttrType Tag, const uint8_t *Data,
void ARMAttributeParser::Virtualization_use(AttrType Tag, const uint8_t *Data,
uint32_t &Offset) {
- static const char *Strings[] = {
+ static const char *const Strings[] = {
"Not Permitted", "TrustZone", "Virtualization Extensions",
"TrustZone + Virtualization Extensions"
};
diff --git a/tools/llvm-readobj/ARMEHABIPrinter.h b/tools/llvm-readobj/ARMEHABIPrinter.h
index dd2490d503db..beb5fd4ea042 100644
--- a/tools/llvm-readobj/ARMEHABIPrinter.h
+++ b/tools/llvm-readobj/ARMEHABIPrinter.h
@@ -305,13 +305,15 @@ void OpcodeDecoder::Decode(const uint8_t *Opcodes, off_t Offset, size_t Length)
template <typename ET>
class PrinterContext {
- StreamWriter &SW;
- const object::ELFFile<ET> *ELF;
-
typedef typename object::ELFFile<ET>::Elf_Sym Elf_Sym;
typedef typename object::ELFFile<ET>::Elf_Shdr Elf_Shdr;
+ typedef typename object::ELFFile<ET>::Elf_Rel Elf_Rel;
+ typedef typename object::ELFFile<ET>::Elf_Word Elf_Word;
- typedef typename object::ELFFile<ET>::Elf_Rel_Iter Elf_Rel_iterator;
+ StreamWriter &SW;
+ const object::ELFFile<ET> *ELF;
+ const Elf_Shdr *Symtab;
+ ArrayRef<Elf_Word> ShndxTable;
static const size_t IndexTableEntrySize;
@@ -332,8 +334,9 @@ class PrinterContext {
void PrintOpcodes(const uint8_t *Entry, size_t Length, off_t Offset) const;
public:
- PrinterContext(StreamWriter &Writer, const object::ELFFile<ET> *File)
- : SW(Writer), ELF(File) {}
+ PrinterContext(StreamWriter &SW, const object::ELFFile<ET> *ELF,
+ const Elf_Shdr *Symtab)
+ : SW(SW), ELF(ELF), Symtab(Symtab) {}
void PrintUnwindInformation() const;
};
@@ -345,10 +348,14 @@ template <typename ET>
ErrorOr<StringRef>
PrinterContext<ET>::FunctionAtAddress(unsigned Section,
uint64_t Address) const {
- for (const Elf_Sym &Sym : ELF->symbols())
+ ErrorOr<StringRef> StrTableOrErr = ELF->getStringTableForSymtab(*Symtab);
+ error(StrTableOrErr.getError());
+ StringRef StrTable = *StrTableOrErr;
+
+ for (const Elf_Sym &Sym : ELF->symbols(Symtab))
if (Sym.st_shndx == Section && Sym.st_value == Address &&
Sym.getType() == ELF::STT_FUNC)
- return ELF->getSymbolName(&Sym, false);
+ return Sym.getName(StrTable);
return readobj_error::unknown_symbol;
}
@@ -365,24 +372,29 @@ PrinterContext<ET>::FindExceptionTable(unsigned IndexSectionIndex,
/// table.
for (const Elf_Shdr &Sec : ELF->sections()) {
- if (Sec.sh_type == ELF::SHT_REL && Sec.sh_info == IndexSectionIndex) {
- for (Elf_Rel_iterator RI = ELF->rel_begin(&Sec), RE = ELF->rel_end(&Sec);
- RI != RE; ++RI) {
- if (RI->r_offset == static_cast<unsigned>(IndexTableOffset)) {
- typename object::ELFFile<ET>::Elf_Rela RelA;
- RelA.r_offset = RI->r_offset;
- RelA.r_info = RI->r_info;
- RelA.r_addend = 0;
+ if (Sec.sh_type != ELF::SHT_REL || Sec.sh_info != IndexSectionIndex)
+ continue;
- std::pair<const Elf_Shdr *, const Elf_Sym *> Symbol =
- ELF->getRelocationSymbol(&Sec, &RelA);
+ ErrorOr<const Elf_Shdr *> SymTabOrErr = ELF->getSection(Sec.sh_link);
+ error(SymTabOrErr.getError());
+ const Elf_Shdr *SymTab = *SymTabOrErr;
- ErrorOr<const Elf_Shdr *> Ret = ELF->getSection(Symbol.second);
- if (std::error_code EC = Ret.getError())
- report_fatal_error(EC.message());
- return *Ret;
- }
- }
+ for (const Elf_Rel &R : ELF->rels(&Sec)) {
+ if (R.r_offset != static_cast<unsigned>(IndexTableOffset))
+ continue;
+
+ typename object::ELFFile<ET>::Elf_Rela RelA;
+ RelA.r_offset = R.r_offset;
+ RelA.r_info = R.r_info;
+ RelA.r_addend = 0;
+
+ const Elf_Sym *Symbol = ELF->getRelocationSymbol(&RelA, SymTab);
+
+ ErrorOr<const Elf_Shdr *> Ret =
+ ELF->getSection(Symbol, SymTab, ShndxTable);
+ if (std::error_code EC = Ret.getError())
+ report_fatal_error(EC.message());
+ return *Ret;
}
}
return nullptr;
diff --git a/tools/llvm-readobj/ARMWinEHPrinter.cpp b/tools/llvm-readobj/ARMWinEHPrinter.cpp
index bf5ff8e1d031..650955d1d75c 100644
--- a/tools/llvm-readobj/ARMWinEHPrinter.cpp
+++ b/tools/llvm-readobj/ARMWinEHPrinter.cpp
@@ -630,9 +630,10 @@ bool Decoder::dumpUnpackedEntry(const COFFObjectFile &COFF,
SW.printString("ExceptionRecord", formatSymbol(*Name, Address));
- section_iterator SI = COFF.section_end();
- if (XDataRecord->getSection(SI))
+ ErrorOr<section_iterator> SIOrErr = XDataRecord->getSection();
+ if (!SIOrErr)
return false;
+ section_iterator SI = *SIOrErr;
return dumpXDataRecord(COFF, *SI, FunctionAddress, Address);
} else {
diff --git a/tools/llvm-readobj/CMakeLists.txt b/tools/llvm-readobj/CMakeLists.txt
index 87407a258021..a06b678780cb 100644
--- a/tools/llvm-readobj/CMakeLists.txt
+++ b/tools/llvm-readobj/CMakeLists.txt
@@ -7,6 +7,7 @@ add_llvm_tool(llvm-readobj
ARMAttributeParser.cpp
ARMWinEHPrinter.cpp
COFFDumper.cpp
+ COFFImportDumper.cpp
ELFDumper.cpp
Error.cpp
llvm-readobj.cpp
diff --git a/tools/llvm-readobj/COFFDumper.cpp b/tools/llvm-readobj/COFFDumper.cpp
index cf897d7cb484..516d1cf8057b 100644
--- a/tools/llvm-readobj/COFFDumper.cpp
+++ b/tools/llvm-readobj/COFFDumper.cpp
@@ -60,6 +60,7 @@ public:
void printCOFFExports() override;
void printCOFFDirectives() override;
void printCOFFBaseReloc() override;
+ void printCodeViewDebugInfo() override;
void printStackMap() const override;
private:
void printSymbol(const SymbolRef &Sym);
@@ -71,7 +72,7 @@ private:
void printBaseOfDataField(const pe32_header *Hdr);
void printBaseOfDataField(const pe32plus_header *Hdr);
- void printCodeViewDebugInfo(const SectionRef &Section);
+ void printCodeViewSection(const SectionRef &Section);
void printCodeViewSymbolsSubsection(StringRef Subsection,
const SectionRef &Section,
@@ -219,6 +220,7 @@ static const EnumEntry<COFF::DLLCharacteristics> PEDLLCharacteristics[] = {
static const EnumEntry<COFF::SectionCharacteristics>
ImageSectionCharacteristics[] = {
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_TYPE_NOLOAD ),
LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_TYPE_NO_PAD ),
LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_CNT_CODE ),
LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_CNT_INITIALIZED_DATA ),
@@ -385,14 +387,12 @@ void COFFDumper::printFileHeaders() {
// Print PE header. This header does not exist if this is an object file and
// not an executable.
const pe32_header *PEHeader = nullptr;
- if (error(Obj->getPE32Header(PEHeader)))
- return;
+ error(Obj->getPE32Header(PEHeader));
if (PEHeader)
printPEHeader<pe32_header>(PEHeader);
const pe32plus_header *PEPlusHeader = nullptr;
- if (error(Obj->getPE32PlusHeader(PEPlusHeader)))
- return;
+ error(Obj->getPE32PlusHeader(PEPlusHeader));
if (PEPlusHeader)
printPEHeader<pe32plus_header>(PEPlusHeader);
@@ -475,10 +475,18 @@ void COFFDumper::printBaseOfDataField(const pe32_header *Hdr) {
void COFFDumper::printBaseOfDataField(const pe32plus_header *) {}
-void COFFDumper::printCodeViewDebugInfo(const SectionRef &Section) {
+void COFFDumper::printCodeViewDebugInfo() {
+ for (const SectionRef &S : Obj->sections()) {
+ StringRef SecName;
+ error(S.getName(SecName));
+ if (SecName == ".debug$S")
+ printCodeViewSection(S);
+ }
+}
+
+void COFFDumper::printCodeViewSection(const SectionRef &Section) {
StringRef Data;
- if (error(Section.getContents(Data)))
- return;
+ error(Section.getContents(Data));
SmallVector<StringRef, 10> FunctionNames;
StringMap<StringRef> FunctionLineTables;
@@ -518,8 +526,7 @@ void COFFDumper::printCodeViewDebugInfo(const SectionRef &Section) {
switch (SubSectionType) {
case COFF::DEBUG_SYMBOL_SUBSECTION:
- if (opts::SectionSymbols)
- printCodeViewSymbolsSubsection(Contents, Section, Offset);
+ printCodeViewSymbolsSubsection(Contents, Section, Offset);
break;
case COFF::DEBUG_LINE_TABLE_SUBSECTION: {
// Holds a PC to file:line table. Some data to parse this subsection is
@@ -533,19 +540,18 @@ void COFFDumper::printCodeViewDebugInfo(const SectionRef &Section) {
return;
}
- StringRef FunctionName;
- if (error(resolveSymbolName(Obj->getCOFFSection(Section), Offset,
- FunctionName)))
- return;
- W.printString("FunctionName", FunctionName);
- if (FunctionLineTables.count(FunctionName) != 0) {
+ StringRef LinkageName;
+ error(resolveSymbolName(Obj->getCOFFSection(Section), Offset,
+ LinkageName));
+ W.printString("LinkageName", LinkageName);
+ if (FunctionLineTables.count(LinkageName) != 0) {
// Saw debug info for this function already?
error(object_error::parse_failed);
return;
}
- FunctionLineTables[FunctionName] = Contents;
- FunctionNames.push_back(FunctionName);
+ FunctionLineTables[LinkageName] = Contents;
+ FunctionNames.push_back(LinkageName);
break;
}
case COFF::DEBUG_STRING_TABLE_SUBSECTION:
@@ -582,7 +588,7 @@ void COFFDumper::printCodeViewDebugInfo(const SectionRef &Section) {
for (unsigned I = 0, E = FunctionNames.size(); I != E; ++I) {
StringRef Name = FunctionNames[I];
ListScope S(W, "FunctionLineTable");
- W.printString("FunctionName", Name);
+ W.printString("LinkageName", Name);
DataExtractor DE(FunctionLineTables[Name], true, 4);
uint32_t Offset = 6; // Skip relocations.
@@ -695,9 +701,8 @@ void COFFDumper::printCodeViewSymbolsSubsection(StringRef Subsection,
uint32_t CodeSize = DE.getU32(&Offset);
DE.getU8(&Offset, Unused, 12);
StringRef SectionName;
- if (error(resolveSymbolName(Obj->getCOFFSection(Section),
- OffsetInSection + Offset, SectionName)))
- return;
+ error(resolveSymbolName(Obj->getCOFFSection(Section),
+ OffsetInSection + Offset, SectionName));
Offset += 4;
DE.getU8(&Offset, Unused, 3);
StringRef DisplayName = DE.getCStr(&Offset);
@@ -748,8 +753,7 @@ void COFFDumper::printSections() {
const coff_section *Section = Obj->getCOFFSection(Sec);
StringRef Name;
- if (error(Sec.getName(Name)))
- Name = "";
+ error(Sec.getName(Name));
DictScope D(W, "Section");
W.printNumber("Number", SectionNumber);
@@ -782,14 +786,10 @@ void COFFDumper::printSections() {
}
}
- if (Name == ".debug$S" && opts::CodeView)
- printCodeViewDebugInfo(Sec);
-
if (opts::SectionData &&
!(Section->Characteristics & COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA)) {
StringRef Data;
- if (error(Sec.getContents(Data)))
- break;
+ error(Sec.getContents(Data));
W.printBinaryBlock("SectionData", Data);
}
@@ -803,8 +803,7 @@ void COFFDumper::printRelocations() {
for (const SectionRef &Section : Obj->sections()) {
++SectionNumber;
StringRef Name;
- if (error(Section.getName(Name)))
- continue;
+ error(Section.getName(Name));
bool PrintedGroup = false;
for (const RelocationRef &Reloc : Section.relocations()) {
@@ -834,8 +833,7 @@ void COFFDumper::printRelocation(const SectionRef &Section,
symbol_iterator Symbol = Reloc.getSymbol();
if (Symbol != Obj->symbol_end()) {
ErrorOr<StringRef> SymbolNameOrErr = Symbol->getName();
- if (error(SymbolNameOrErr.getError()))
- return;
+ error(SymbolNameOrErr.getError());
SymbolName = *SymbolNameOrErr;
}
@@ -914,8 +912,7 @@ void COFFDumper::printSymbol(const SymbolRef &Sym) {
for (uint8_t I = 0; I < Symbol.getNumberOfAuxSymbols(); ++I) {
if (Symbol.isFunctionDefinition()) {
const coff_aux_function_definition *Aux;
- if (error(getSymbolAuxData(Obj, Symbol, I, Aux)))
- break;
+ error(getSymbolAuxData(Obj, Symbol, I, Aux));
DictScope AS(W, "AuxFunctionDef");
W.printNumber("TagIndex", Aux->TagIndex);
@@ -925,8 +922,7 @@ void COFFDumper::printSymbol(const SymbolRef &Sym) {
} else if (Symbol.isAnyUndefined()) {
const coff_aux_weak_external *Aux;
- if (error(getSymbolAuxData(Obj, Symbol, I, Aux)))
- break;
+ error(getSymbolAuxData(Obj, Symbol, I, Aux));
ErrorOr<COFFSymbolRef> Linked = Obj->getSymbol(Aux->TagIndex);
StringRef LinkedName;
@@ -943,8 +939,7 @@ void COFFDumper::printSymbol(const SymbolRef &Sym) {
} else if (Symbol.isFileRecord()) {
const char *FileName;
- if (error(getSymbolAuxData(Obj, Symbol, I, FileName)))
- break;
+ error(getSymbolAuxData(Obj, Symbol, I, FileName));
DictScope AS(W, "AuxFileRecord");
@@ -954,8 +949,7 @@ void COFFDumper::printSymbol(const SymbolRef &Sym) {
break;
} else if (Symbol.isSectionDefinition()) {
const coff_aux_section_definition *Aux;
- if (error(getSymbolAuxData(Obj, Symbol, I, Aux)))
- break;
+ error(getSymbolAuxData(Obj, Symbol, I, Aux));
int32_t AuxNumber = Aux->getNumber(Symbol.isBigObj());
@@ -986,8 +980,7 @@ void COFFDumper::printSymbol(const SymbolRef &Sym) {
}
} else if (Symbol.isCLRToken()) {
const coff_aux_clr_token *Aux;
- if (error(getSymbolAuxData(Obj, Symbol, I, Aux)))
- break;
+ error(getSymbolAuxData(Obj, Symbol, I, Aux));
ErrorOr<COFFSymbolRef> ReferredSym =
Obj->getSymbol(Aux->SymbolTableIndex);
@@ -1040,9 +1033,9 @@ void COFFDumper::printImportedSymbols(
iterator_range<imported_symbol_iterator> Range) {
for (const ImportedSymbolRef &I : Range) {
StringRef Sym;
- if (error(I.getSymbolName(Sym))) return;
+ error(I.getSymbolName(Sym));
uint16_t Ordinal;
- if (error(I.getOrdinal(Ordinal))) return;
+ error(I.getOrdinal(Ordinal));
W.printNumber("Symbol", Sym, Ordinal);
}
}
@@ -1054,12 +1047,12 @@ void COFFDumper::printDelayImportedSymbols(
for (const ImportedSymbolRef &S : Range) {
DictScope Import(W, "Import");
StringRef Sym;
- if (error(S.getSymbolName(Sym))) return;
+ error(S.getSymbolName(Sym));
uint16_t Ordinal;
- if (error(S.getOrdinal(Ordinal))) return;
+ error(S.getOrdinal(Ordinal));
W.printNumber("Symbol", Sym, Ordinal);
uint64_t Addr;
- if (error(I.getImportAddress(Index++, Addr))) return;
+ error(I.getImportAddress(Index++, Addr));
W.printHex("Address", Addr);
}
}
@@ -1069,12 +1062,12 @@ void COFFDumper::printCOFFImports() {
for (const ImportDirectoryEntryRef &I : Obj->import_directories()) {
DictScope Import(W, "Import");
StringRef Name;
- if (error(I.getName(Name))) return;
+ error(I.getName(Name));
W.printString("Name", Name);
uint32_t Addr;
- if (error(I.getImportLookupTableRVA(Addr))) return;
+ error(I.getImportLookupTableRVA(Addr));
W.printHex("ImportLookupTableRVA", Addr);
- if (error(I.getImportAddressTableRVA(Addr))) return;
+ error(I.getImportAddressTableRVA(Addr));
W.printHex("ImportAddressTableRVA", Addr);
printImportedSymbols(I.imported_symbols());
}
@@ -1083,10 +1076,10 @@ void COFFDumper::printCOFFImports() {
for (const DelayImportDirectoryEntryRef &I : Obj->delay_import_directories()) {
DictScope Import(W, "DelayImport");
StringRef Name;
- if (error(I.getName(Name))) return;
+ error(I.getName(Name));
W.printString("Name", Name);
const delay_import_directory_table_entry *Table;
- if (error(I.getDelayImportTable(Table))) return;
+ error(I.getDelayImportTable(Table));
W.printHex("Attributes", Table->Attributes);
W.printHex("ModuleHandle", Table->ModuleHandle);
W.printHex("ImportAddressTable", Table->DelayImportAddressTable);
@@ -1104,12 +1097,9 @@ void COFFDumper::printCOFFExports() {
StringRef Name;
uint32_t Ordinal, RVA;
- if (error(E.getSymbolName(Name)))
- continue;
- if (error(E.getOrdinal(Ordinal)))
- continue;
- if (error(E.getExportRVA(RVA)))
- continue;
+ error(E.getSymbolName(Name));
+ error(E.getOrdinal(Ordinal));
+ error(E.getExportRVA(RVA));
W.printNumber("Ordinal", Ordinal);
W.printString("Name", Name);
@@ -1122,13 +1112,11 @@ void COFFDumper::printCOFFDirectives() {
StringRef Contents;
StringRef Name;
- if (error(Section.getName(Name)))
- continue;
+ error(Section.getName(Name));
if (Name != ".drectve")
continue;
- if (error(Section.getContents(Contents)))
- return;
+ error(Section.getContents(Contents));
W.printString("Directive(s)", Contents);
}
@@ -1152,10 +1140,8 @@ void COFFDumper::printCOFFBaseReloc() {
for (const BaseRelocRef &I : Obj->base_relocs()) {
uint8_t Type;
uint32_t RVA;
- if (error(I.getRVA(RVA)))
- continue;
- if (error(I.getType(Type)))
- continue;
+ error(I.getRVA(RVA));
+ error(I.getType(Type));
DictScope Import(W, "Entry");
W.printString("Type", getBaseRelocTypeName(Type));
W.printHex("Address", RVA);
diff --git a/tools/llvm-readobj/COFFImportDumper.cpp b/tools/llvm-readobj/COFFImportDumper.cpp
new file mode 100644
index 000000000000..83715e60f057
--- /dev/null
+++ b/tools/llvm-readobj/COFFImportDumper.cpp
@@ -0,0 +1,52 @@
+//===-- COFFImportDumper.cpp - COFF import library dumper -------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief This file implements the COFF import library dumper for llvm-readobj.
+///
+//===----------------------------------------------------------------------===//
+
+#include "Error.h"
+#include "ObjDumper.h"
+#include "llvm-readobj.h"
+#include "llvm/Object/COFF.h"
+#include "llvm/Object/COFFImportFile.h"
+#include "llvm/Support/COFF.h"
+
+using namespace llvm::object;
+
+namespace llvm {
+
+void dumpCOFFImportFile(const COFFImportFile *File) {
+ outs() << '\n';
+ outs() << "File: " << File->getFileName() << "\n";
+ outs() << "Format: COFF-import-file\n";
+
+ const coff_import_header *H = File->getCOFFImportHeader();
+ switch (H->getType()) {
+ case COFF::IMPORT_CODE: outs() << "Type: code\n"; break;
+ case COFF::IMPORT_DATA: outs() << "Type: data\n"; break;
+ case COFF::IMPORT_CONST: outs() << "Type: const\n"; break;
+ }
+
+ switch (H->getNameType()) {
+ case COFF::IMPORT_ORDINAL: outs() << "Name type: ordinal\n"; break;
+ case COFF::IMPORT_NAME: outs() << "Name type: name\n"; break;
+ case COFF::IMPORT_NAME_NOPREFIX: outs() << "Name type: noprefix\n"; break;
+ case COFF::IMPORT_NAME_UNDECORATE: outs() << "Name type: undecorate\n"; break;
+ }
+
+ for (const object::BasicSymbolRef &Sym : File->symbols()) {
+ outs() << "Symbol: ";
+ Sym.printName(outs());
+ outs() << "\n";
+ }
+}
+
+} // namespace llvm
diff --git a/tools/llvm-readobj/ELFDumper.cpp b/tools/llvm-readobj/ELFDumper.cpp
index 1cdf5529c080..02397f382848 100644
--- a/tools/llvm-readobj/ELFDumper.cpp
+++ b/tools/llvm-readobj/ELFDumper.cpp
@@ -42,8 +42,7 @@ namespace {
template<typename ELFT>
class ELFDumper : public ObjDumper {
public:
- ELFDumper(const ELFFile<ELFT> *Obj, StreamWriter &Writer)
- : ObjDumper(Writer), Obj(Obj) {}
+ ELFDumper(const ELFFile<ELFT> *Obj, StreamWriter &Writer);
void printFileHeaders() override;
void printSections() override;
@@ -57,6 +56,9 @@ public:
void printNeededLibraries() override;
void printProgramHeaders() override;
void printHashTable() override;
+ void printGnuHashTable() override;
+ void printLoadName() override;
+ void printVersionInfo() override;
void printAttributes() override;
void printMipsPLTGOT() override;
@@ -69,13 +71,114 @@ private:
typedef ELFFile<ELFT> ELFO;
typedef typename ELFO::Elf_Shdr Elf_Shdr;
typedef typename ELFO::Elf_Sym Elf_Sym;
+ typedef typename ELFO::Elf_Dyn Elf_Dyn;
+ typedef typename ELFO::Elf_Dyn_Range Elf_Dyn_Range;
+ typedef typename ELFO::Elf_Rel Elf_Rel;
+ typedef typename ELFO::Elf_Rela Elf_Rela;
+ typedef typename ELFO::Elf_Rela_Range Elf_Rela_Range;
+ typedef typename ELFO::Elf_Phdr Elf_Phdr;
+ typedef typename ELFO::Elf_Half Elf_Half;
+ typedef typename ELFO::Elf_Hash Elf_Hash;
+ typedef typename ELFO::Elf_GnuHash Elf_GnuHash;
+ typedef typename ELFO::Elf_Ehdr Elf_Ehdr;
+ typedef typename ELFO::Elf_Word Elf_Word;
+ typedef typename ELFO::uintX_t uintX_t;
+ typedef typename ELFO::Elf_Versym Elf_Versym;
+ typedef typename ELFO::Elf_Verneed Elf_Verneed;
+ typedef typename ELFO::Elf_Vernaux Elf_Vernaux;
+ typedef typename ELFO::Elf_Verdef Elf_Verdef;
+ typedef typename ELFO::Elf_Verdaux Elf_Verdaux;
- void printSymbol(const Elf_Sym *Symbol, bool IsDynamic);
+ /// \brief Represents a region described by entries in the .dynamic table.
+ struct DynRegionInfo {
+ DynRegionInfo() : Addr(nullptr), Size(0), EntSize(0) {}
+ /// \brief Address in current address space.
+ const void *Addr;
+ /// \brief Size in bytes of the region.
+ uintX_t Size;
+ /// \brief Size of each entity in the region.
+ uintX_t EntSize;
+ };
+
+ void printSymbolsHelper(bool IsDynamic);
+ void printSymbol(const Elf_Sym *Symbol, const Elf_Shdr *SymTab,
+ StringRef StrTable, bool IsDynamic);
void printRelocations(const Elf_Shdr *Sec);
- void printRelocation(const Elf_Shdr *Sec, typename ELFO::Elf_Rela Rel);
+ void printRelocation(Elf_Rela Rel, const Elf_Shdr *SymTab);
+ void printValue(uint64_t Type, uint64_t Value);
+
+ const Elf_Rela *dyn_rela_begin() const;
+ const Elf_Rela *dyn_rela_end() const;
+ Elf_Rela_Range dyn_relas() const;
+ StringRef getDynamicString(uint64_t Offset) const;
+ const Elf_Dyn *dynamic_table_begin() const {
+ ErrorOr<const Elf_Dyn *> Ret = Obj->dynamic_table_begin(DynamicProgHeader);
+ error(Ret.getError());
+ return *Ret;
+ }
+ const Elf_Dyn *dynamic_table_end() const {
+ ErrorOr<const Elf_Dyn *> Ret = Obj->dynamic_table_end(DynamicProgHeader);
+ error(Ret.getError());
+ return *Ret;
+ }
+ StringRef getSymbolVersion(StringRef StrTab, const Elf_Sym *symb,
+ bool &IsDefault);
+ void LoadVersionMap();
+ void LoadVersionNeeds(const Elf_Shdr *ec) const;
+ void LoadVersionDefs(const Elf_Shdr *sec) const;
const ELFO *Obj;
+ DynRegionInfo DynRelaRegion;
+ const Elf_Phdr *DynamicProgHeader = nullptr;
+ StringRef DynamicStringTable;
+ const Elf_Sym *DynSymStart = nullptr;
+ StringRef SOName;
+ const Elf_Hash *HashTable = nullptr;
+ const Elf_GnuHash *GnuHashTable = nullptr;
+ const Elf_Shdr *DotDynSymSec = nullptr;
+ const Elf_Shdr *DotSymtabSec = nullptr;
+ ArrayRef<Elf_Word> ShndxTable;
+
+ const Elf_Shdr *dot_gnu_version_sec = nullptr; // .gnu.version
+ const Elf_Shdr *dot_gnu_version_r_sec = nullptr; // .gnu.version_r
+ const Elf_Shdr *dot_gnu_version_d_sec = nullptr; // .gnu.version_d
+
+ // Records for each version index the corresponding Verdef or Vernaux entry.
+ // This is filled the first time LoadVersionMap() is called.
+ class VersionMapEntry : public PointerIntPair<const void *, 1> {
+ public:
+ // If the integer is 0, this is an Elf_Verdef*.
+ // If the integer is 1, this is an Elf_Vernaux*.
+ VersionMapEntry() : PointerIntPair<const void *, 1>(nullptr, 0) {}
+ VersionMapEntry(const Elf_Verdef *verdef)
+ : PointerIntPair<const void *, 1>(verdef, 0) {}
+ VersionMapEntry(const Elf_Vernaux *vernaux)
+ : PointerIntPair<const void *, 1>(vernaux, 1) {}
+ bool isNull() const { return getPointer() == nullptr; }
+ bool isVerdef() const { return !isNull() && getInt() == 0; }
+ bool isVernaux() const { return !isNull() && getInt() == 1; }
+ const Elf_Verdef *getVerdef() const {
+ return isVerdef() ? (const Elf_Verdef *)getPointer() : nullptr;
+ }
+ const Elf_Vernaux *getVernaux() const {
+ return isVernaux() ? (const Elf_Vernaux *)getPointer() : nullptr;
+ }
+ };
+ mutable SmallVector<VersionMapEntry, 16> VersionMap;
+
+public:
+ Elf_Dyn_Range dynamic_table() const {
+ ErrorOr<Elf_Dyn_Range> Ret = Obj->dynamic_table(DynamicProgHeader);
+ error(Ret.getError());
+ return *Ret;
+ }
+
+ std::string getFullSymbolName(const Elf_Sym *Symbol, StringRef StrTable,
+ bool IsDynamic);
+ const Elf_Shdr *getDotDynSymSec() const { return DotDynSymSec; }
+ const Elf_Shdr *getDotSymtabSec() const { return DotSymtabSec; }
+ ArrayRef<Elf_Word> getShndxTable() { return ShndxTable; }
};
template <class T> T errorOrDefault(ErrorOr<T> Val, T Default = T()) {
@@ -122,30 +225,246 @@ std::error_code createELFDumper(const object::ObjectFile *Obj,
} // namespace llvm
-template <typename ELFO>
-static std::string getFullSymbolName(const ELFO &Obj,
- const typename ELFO::Elf_Sym *Symbol,
- bool IsDynamic) {
- StringRef SymbolName = errorOrDefault(Obj.getSymbolName(Symbol, IsDynamic));
+// Iterate through the versions needed section, and place each Elf_Vernaux
+// in the VersionMap according to its index.
+template <class ELFT>
+void ELFDumper<ELFT>::LoadVersionNeeds(const Elf_Shdr *sec) const {
+ unsigned vn_size = sec->sh_size; // Size of section in bytes
+ unsigned vn_count = sec->sh_info; // Number of Verneed entries
+ const char *sec_start = (const char *)Obj->base() + sec->sh_offset;
+ const char *sec_end = sec_start + vn_size;
+ // The first Verneed entry is at the start of the section.
+ const char *p = sec_start;
+ for (unsigned i = 0; i < vn_count; i++) {
+ if (p + sizeof(Elf_Verneed) > sec_end)
+ report_fatal_error("Section ended unexpectedly while scanning "
+ "version needed records.");
+ const Elf_Verneed *vn = reinterpret_cast<const Elf_Verneed *>(p);
+ if (vn->vn_version != ELF::VER_NEED_CURRENT)
+ report_fatal_error("Unexpected verneed version");
+ // Iterate through the Vernaux entries
+ const char *paux = p + vn->vn_aux;
+ for (unsigned j = 0; j < vn->vn_cnt; j++) {
+ if (paux + sizeof(Elf_Vernaux) > sec_end)
+ report_fatal_error("Section ended unexpected while scanning auxiliary "
+ "version needed records.");
+ const Elf_Vernaux *vna = reinterpret_cast<const Elf_Vernaux *>(paux);
+ size_t index = vna->vna_other & ELF::VERSYM_VERSION;
+ if (index >= VersionMap.size())
+ VersionMap.resize(index + 1);
+ VersionMap[index] = VersionMapEntry(vna);
+ paux += vna->vna_next;
+ }
+ p += vn->vn_next;
+ }
+}
+
+// Iterate through the version definitions, and place each Elf_Verdef
+// in the VersionMap according to its index.
+template <class ELFT>
+void ELFDumper<ELFT>::LoadVersionDefs(const Elf_Shdr *sec) const {
+ unsigned vd_size = sec->sh_size; // Size of section in bytes
+ unsigned vd_count = sec->sh_info; // Number of Verdef entries
+ const char *sec_start = (const char *)Obj->base() + sec->sh_offset;
+ const char *sec_end = sec_start + vd_size;
+ // The first Verdef entry is at the start of the section.
+ const char *p = sec_start;
+ for (unsigned i = 0; i < vd_count; i++) {
+ if (p + sizeof(Elf_Verdef) > sec_end)
+ report_fatal_error("Section ended unexpectedly while scanning "
+ "version definitions.");
+ const Elf_Verdef *vd = reinterpret_cast<const Elf_Verdef *>(p);
+ if (vd->vd_version != ELF::VER_DEF_CURRENT)
+ report_fatal_error("Unexpected verdef version");
+ size_t index = vd->vd_ndx & ELF::VERSYM_VERSION;
+ if (index >= VersionMap.size())
+ VersionMap.resize(index + 1);
+ VersionMap[index] = VersionMapEntry(vd);
+ p += vd->vd_next;
+ }
+}
+
+template <class ELFT> void ELFDumper<ELFT>::LoadVersionMap() {
+ // If there is no dynamic symtab or version table, there is nothing to do.
+ if (!DynSymStart || !dot_gnu_version_sec)
+ return;
+
+ // Has the VersionMap already been loaded?
+ if (VersionMap.size() > 0)
+ return;
+
+ // The first two version indexes are reserved.
+ // Index 0 is LOCAL, index 1 is GLOBAL.
+ VersionMap.push_back(VersionMapEntry());
+ VersionMap.push_back(VersionMapEntry());
+
+ if (dot_gnu_version_d_sec)
+ LoadVersionDefs(dot_gnu_version_d_sec);
+
+ if (dot_gnu_version_r_sec)
+ LoadVersionNeeds(dot_gnu_version_r_sec);
+}
+
+
+template <typename ELFO, class ELFT>
+static void printVersionSymbolSection(ELFDumper<ELFT> *Dumper,
+ const ELFO *Obj,
+ const typename ELFO::Elf_Shdr *Sec,
+ StreamWriter &W) {
+ DictScope SS(W, "Version symbols");
+ if (!Sec)
+ return;
+ StringRef Name = errorOrDefault(Obj->getSectionName(Sec));
+ W.printNumber("Section Name", Name, Sec->sh_name);
+ W.printHex("Address", Sec->sh_addr);
+ W.printHex("Offset", Sec->sh_offset);
+ W.printNumber("Link", Sec->sh_link);
+
+ const typename ELFO::Elf_Shdr *DynSymSec = Dumper->getDotDynSymSec();
+ const uint8_t *P = (const uint8_t *)Obj->base() + Sec->sh_offset;
+ ErrorOr<StringRef> StrTableOrErr =
+ Obj->getStringTableForSymtab(*DynSymSec);
+ error(StrTableOrErr.getError());
+
+ // Same number of entries in the dynamic symbol table (DT_SYMTAB).
+ ListScope Syms(W, "Symbols");
+ for (const typename ELFO::Elf_Sym &Sym : Obj->symbols(DynSymSec)) {
+ DictScope S(W, "Symbol");
+ std::string FullSymbolName =
+ Dumper->getFullSymbolName(&Sym, *StrTableOrErr, true /* IsDynamic */);
+ W.printNumber("Version", *P);
+ W.printString("Name", FullSymbolName);
+ P += sizeof(typename ELFO::Elf_Half);
+ }
+}
+
+template <typename ELFO, class ELFT>
+static void printVersionDefinitionSection(ELFDumper<ELFT> *Dumper,
+ const ELFO *Obj,
+ const typename ELFO::Elf_Shdr *Sec,
+ StreamWriter &W) {
+ DictScope SD(W, "Version definition");
+ if (!Sec)
+ return;
+ StringRef Name = errorOrDefault(Obj->getSectionName(Sec));
+ W.printNumber("Section Name", Name, Sec->sh_name);
+ W.printHex("Address", Sec->sh_addr);
+ W.printHex("Offset", Sec->sh_offset);
+ W.printNumber("Link", Sec->sh_link);
+
+ unsigned verdef_entries = 0;
+ // The number of entries in the section SHT_GNU_verdef
+ // is determined by DT_VERDEFNUM tag.
+ for (const typename ELFO::Elf_Dyn &Dyn : Dumper->dynamic_table()) {
+ if (Dyn.d_tag == DT_VERDEFNUM)
+ verdef_entries = Dyn.d_un.d_val;
+ }
+ const uint8_t *SecStartAddress =
+ (const uint8_t *)Obj->base() + Sec->sh_offset;
+ const uint8_t *SecEndAddress = SecStartAddress + Sec->sh_size;
+ const uint8_t *P = SecStartAddress;
+ ErrorOr<const typename ELFO::Elf_Shdr *> StrTabOrErr =
+ Obj->getSection(Sec->sh_link);
+ error(StrTabOrErr.getError());
+
+ ListScope Entries(W, "Entries");
+ for (unsigned i = 0; i < verdef_entries; ++i) {
+ if (P + sizeof(typename ELFO::Elf_Verdef) > SecEndAddress)
+ report_fatal_error("invalid offset in the section");
+ auto *VD = reinterpret_cast<const typename ELFO::Elf_Verdef *>(P);
+ DictScope Entry(W, "Entry");
+ W.printHex("Offset", (uintptr_t)P - (uintptr_t)SecStartAddress);
+ W.printNumber("Rev", VD->vd_version);
+ // FIXME: print something more readable.
+ W.printNumber("Flags", VD->vd_flags);
+ W.printNumber("Index", VD->vd_ndx);
+ W.printNumber("Cnt", VD->vd_cnt);
+ W.printString("Name", StringRef((const char *)(Obj->base() +
+ (*StrTabOrErr)->sh_offset +
+ VD->getAux()->vda_name)));
+ P += VD->vd_next;
+ }
+}
+
+template <typename ELFT> void ELFDumper<ELFT>::printVersionInfo() {
+ // Dump version symbol section.
+ printVersionSymbolSection(this, Obj, dot_gnu_version_sec, W);
+
+ // Dump version definition section.
+ printVersionDefinitionSection(this, Obj, dot_gnu_version_d_sec, W);
+}
+
+template <typename ELFT>
+StringRef ELFDumper<ELFT>::getSymbolVersion(StringRef StrTab,
+ const Elf_Sym *symb,
+ bool &IsDefault) {
+ // This is a dynamic symbol. Look in the GNU symbol version table.
+ if (!dot_gnu_version_sec) {
+ // No version table.
+ IsDefault = false;
+ return StringRef("");
+ }
+
+ // Determine the position in the symbol table of this entry.
+ size_t entry_index = (reinterpret_cast<uintptr_t>(symb) -
+ reinterpret_cast<uintptr_t>(DynSymStart)) /
+ sizeof(Elf_Sym);
+
+ // Get the corresponding version index entry
+ const Elf_Versym *vs =
+ Obj->template getEntry<Elf_Versym>(dot_gnu_version_sec, entry_index);
+ size_t version_index = vs->vs_index & ELF::VERSYM_VERSION;
+
+ // Special markers for unversioned symbols.
+ if (version_index == ELF::VER_NDX_LOCAL ||
+ version_index == ELF::VER_NDX_GLOBAL) {
+ IsDefault = false;
+ return StringRef("");
+ }
+
+ // Lookup this symbol in the version table
+ LoadVersionMap();
+ if (version_index >= VersionMap.size() || VersionMap[version_index].isNull())
+ reportError("Invalid version entry");
+ const VersionMapEntry &entry = VersionMap[version_index];
+
+ // Get the version name string
+ size_t name_offset;
+ if (entry.isVerdef()) {
+ // The first Verdaux entry holds the name.
+ name_offset = entry.getVerdef()->getAux()->vda_name;
+ IsDefault = !(vs->vs_index & ELF::VERSYM_HIDDEN);
+ } else {
+ name_offset = entry.getVernaux()->vna_name;
+ IsDefault = false;
+ }
+ if (name_offset >= StrTab.size())
+ reportError("Invalid string offset");
+ return StringRef(StrTab.data() + name_offset);
+}
+
+template <typename ELFT>
+std::string ELFDumper<ELFT>::getFullSymbolName(const Elf_Sym *Symbol,
+ StringRef StrTable,
+ bool IsDynamic) {
+ StringRef SymbolName = errorOrDefault(Symbol->getName(StrTable));
if (!IsDynamic)
return SymbolName;
std::string FullSymbolName(SymbolName);
bool IsDefault;
- ErrorOr<StringRef> Version =
- Obj.getSymbolVersion(nullptr, &*Symbol, IsDefault);
- if (Version) {
- FullSymbolName += (IsDefault ? "@@" : "@");
- FullSymbolName += *Version;
- } else
- error(Version.getError());
+ StringRef Version = getSymbolVersion(StrTable, &*Symbol, IsDefault);
+ FullSymbolName += (IsDefault ? "@@" : "@");
+ FullSymbolName += Version;
return FullSymbolName;
}
template <typename ELFO>
static void
getSectionNameIndex(const ELFO &Obj, const typename ELFO::Elf_Sym *Symbol,
+ const typename ELFO::Elf_Shdr *SymTab,
+ ArrayRef<typename ELFO::Elf_Word> ShndxTable,
StringRef &SectionName, unsigned &SectionIndex) {
SectionIndex = Symbol->st_shndx;
if (Symbol->isUndefined())
@@ -162,25 +481,26 @@ getSectionNameIndex(const ELFO &Obj, const typename ELFO::Elf_Sym *Symbol,
SectionName = "Reserved";
else {
if (SectionIndex == SHN_XINDEX)
- SectionIndex = Obj.getExtendedSymbolTableIndex(&*Symbol);
+ SectionIndex =
+ Obj.getExtendedSymbolTableIndex(Symbol, SymTab, ShndxTable);
ErrorOr<const typename ELFO::Elf_Shdr *> Sec = Obj.getSection(SectionIndex);
- if (!error(Sec.getError()))
- SectionName = errorOrDefault(Obj.getSectionName(*Sec));
+ error(Sec.getError());
+ SectionName = errorOrDefault(Obj.getSectionName(*Sec));
}
}
-template <class ELFT>
-static const typename ELFFile<ELFT>::Elf_Shdr *
-findSectionByAddress(const ELFFile<ELFT> *Obj, uint64_t Addr) {
+template <class ELFO>
+static const typename ELFO::Elf_Shdr *findSectionByAddress(const ELFO *Obj,
+ uint64_t Addr) {
for (const auto &Shdr : Obj->sections())
if (Shdr.sh_addr == Addr)
return &Shdr;
return nullptr;
}
-template <class ELFT>
-static const typename ELFFile<ELFT>::Elf_Shdr *
-findSectionByName(const ELFFile<ELFT> &Obj, StringRef Name) {
+template <class ELFO>
+static const typename ELFO::Elf_Shdr *findSectionByName(const ELFO &Obj,
+ StringRef Name) {
for (const auto &Shdr : Obj.sections()) {
if (Name == errorOrDefault(Obj.getSectionName(&Shdr)))
return &Shdr;
@@ -409,6 +729,12 @@ static const EnumEntry<unsigned> ElfSymbolTypes[] = {
{ "GNU_IFunc", ELF::STT_GNU_IFUNC }
};
+static const EnumEntry<unsigned> AMDGPUSymbolTypes[] = {
+ { "AMDGPU_HSA_KERNEL", ELF::STT_AMDGPU_HSA_KERNEL },
+ { "AMDGPU_HSA_INDIRECT_FUNCTION", ELF::STT_AMDGPU_HSA_INDIRECT_FUNCTION },
+ { "AMDGPU_HSA_METADATA", ELF::STT_AMDGPU_HSA_METADATA }
+};
+
static const char *getElfSectionType(unsigned Arch, unsigned Type) {
switch (Arch) {
case ELF::EM_ARM:
@@ -473,13 +799,24 @@ static const EnumEntry<unsigned> ElfSectionFlags[] = {
LLVM_READOBJ_ENUM_ENT(ELF, SHF_TLS ),
LLVM_READOBJ_ENUM_ENT(ELF, XCORE_SHF_CP_SECTION),
LLVM_READOBJ_ENUM_ENT(ELF, XCORE_SHF_DP_SECTION),
- LLVM_READOBJ_ENUM_ENT(ELF, SHF_MIPS_NOSTRIP )
+ LLVM_READOBJ_ENUM_ENT(ELF, SHF_MIPS_NOSTRIP ),
+ LLVM_READOBJ_ENUM_ENT(ELF, SHF_AMDGPU_HSA_GLOBAL),
+ LLVM_READOBJ_ENUM_ENT(ELF, SHF_AMDGPU_HSA_READONLY),
+ LLVM_READOBJ_ENUM_ENT(ELF, SHF_AMDGPU_HSA_CODE),
+ LLVM_READOBJ_ENUM_ENT(ELF, SHF_AMDGPU_HSA_AGENT)
};
static const char *getElfSegmentType(unsigned Arch, unsigned Type) {
// Check potentially overlapped processor-specific
// program header type.
switch (Arch) {
+ case ELF::EM_AMDGPU:
+ switch (Type) {
+ LLVM_READOBJ_ENUM_CASE(ELF, PT_AMDGPU_HSA_LOAD_GLOBAL_PROGRAM);
+ LLVM_READOBJ_ENUM_CASE(ELF, PT_AMDGPU_HSA_LOAD_GLOBAL_AGENT);
+ LLVM_READOBJ_ENUM_CASE(ELF, PT_AMDGPU_HSA_LOAD_READONLY_AGENT);
+ LLVM_READOBJ_ENUM_CASE(ELF, PT_AMDGPU_HSA_LOAD_CODE_AGENT);
+ }
case ELF::EM_ARM:
switch (Type) {
LLVM_READOBJ_ENUM_CASE(ELF, PT_ARM_EXIDX);
@@ -565,9 +902,138 @@ static const EnumEntry<unsigned> ElfHeaderMipsFlags[] = {
LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ARCH_64R6)
};
+template <typename ELFT>
+ELFDumper<ELFT>::ELFDumper(const ELFFile<ELFT> *Obj, StreamWriter &Writer)
+ : ObjDumper(Writer), Obj(Obj) {
+
+ SmallVector<const Elf_Phdr *, 4> LoadSegments;
+ for (const Elf_Phdr &Phdr : Obj->program_headers()) {
+ if (Phdr.p_type == ELF::PT_DYNAMIC) {
+ DynamicProgHeader = &Phdr;
+ continue;
+ }
+ if (Phdr.p_type != ELF::PT_LOAD || Phdr.p_filesz == 0)
+ continue;
+ LoadSegments.push_back(&Phdr);
+ }
+
+ auto toMappedAddr = [&](uint64_t VAddr) -> const uint8_t * {
+ const Elf_Phdr **I = std::upper_bound(
+ LoadSegments.begin(), LoadSegments.end(), VAddr, compareAddr<ELFT>);
+ if (I == LoadSegments.begin())
+ report_fatal_error("Virtual address is not in any segment");
+ --I;
+ const Elf_Phdr &Phdr = **I;
+ uint64_t Delta = VAddr - Phdr.p_vaddr;
+ if (Delta >= Phdr.p_filesz)
+ report_fatal_error("Virtual address is not in any segment");
+ return Obj->base() + Phdr.p_offset + Delta;
+ };
+
+ uint64_t SONameOffset = 0;
+ const char *StringTableBegin = nullptr;
+ uint64_t StringTableSize = 0;
+ for (const Elf_Dyn &Dyn : dynamic_table()) {
+ switch (Dyn.d_tag) {
+ case ELF::DT_HASH:
+ HashTable =
+ reinterpret_cast<const Elf_Hash *>(toMappedAddr(Dyn.getPtr()));
+ break;
+ case ELF::DT_GNU_HASH:
+ GnuHashTable =
+ reinterpret_cast<const Elf_GnuHash *>(toMappedAddr(Dyn.getPtr()));
+ break;
+ case ELF::DT_RELA:
+ DynRelaRegion.Addr = toMappedAddr(Dyn.getPtr());
+ break;
+ case ELF::DT_RELASZ:
+ DynRelaRegion.Size = Dyn.getVal();
+ break;
+ case ELF::DT_RELAENT:
+ DynRelaRegion.EntSize = Dyn.getVal();
+ break;
+ case ELF::DT_SONAME:
+ SONameOffset = Dyn.getVal();
+ break;
+ case ELF::DT_STRTAB:
+ StringTableBegin = (const char *)toMappedAddr(Dyn.getPtr());
+ break;
+ case ELF::DT_STRSZ:
+ StringTableSize = Dyn.getVal();
+ break;
+ case ELF::DT_SYMTAB:
+ DynSymStart =
+ reinterpret_cast<const Elf_Sym *>(toMappedAddr(Dyn.getPtr()));
+ break;
+ }
+ }
+ if (StringTableBegin)
+ DynamicStringTable = StringRef(StringTableBegin, StringTableSize);
+ if (SONameOffset)
+ SOName = getDynamicString(SONameOffset);
+
+ for (const Elf_Shdr &Sec : Obj->sections()) {
+ switch (Sec.sh_type) {
+ case ELF::SHT_GNU_versym:
+ if (dot_gnu_version_sec != nullptr)
+ reportError("Multiple SHT_GNU_versym");
+ dot_gnu_version_sec = &Sec;
+ break;
+ case ELF::SHT_GNU_verdef:
+ if (dot_gnu_version_d_sec != nullptr)
+ reportError("Multiple SHT_GNU_verdef");
+ dot_gnu_version_d_sec = &Sec;
+ break;
+ case ELF::SHT_GNU_verneed:
+ if (dot_gnu_version_r_sec != nullptr)
+ reportError("Multilpe SHT_GNU_verneed");
+ dot_gnu_version_r_sec = &Sec;
+ break;
+ case ELF::SHT_DYNSYM:
+ if (DotDynSymSec != nullptr)
+ reportError("Multilpe SHT_DYNSYM");
+ DotDynSymSec = &Sec;
+ break;
+ case ELF::SHT_SYMTAB:
+ if (DotSymtabSec != nullptr)
+ reportError("Multilpe SHT_SYMTAB");
+ DotSymtabSec = &Sec;
+ break;
+ case ELF::SHT_SYMTAB_SHNDX: {
+ ErrorOr<ArrayRef<Elf_Word>> TableOrErr = Obj->getSHNDXTable(Sec);
+ error(TableOrErr.getError());
+ ShndxTable = *TableOrErr;
+ break;
+ }
+ }
+ }
+}
+
+template <typename ELFT>
+const typename ELFDumper<ELFT>::Elf_Rela *
+ELFDumper<ELFT>::dyn_rela_begin() const {
+ if (DynRelaRegion.Size && DynRelaRegion.EntSize != sizeof(Elf_Rela))
+ report_fatal_error("Invalid relocation entry size");
+ return reinterpret_cast<const Elf_Rela *>(DynRelaRegion.Addr);
+}
+
+template <typename ELFT>
+const typename ELFDumper<ELFT>::Elf_Rela *
+ELFDumper<ELFT>::dyn_rela_end() const {
+ uint64_t Size = DynRelaRegion.Size;
+ if (Size % sizeof(Elf_Rela))
+ report_fatal_error("Invalid relocation table size");
+ return dyn_rela_begin() + Size / sizeof(Elf_Rela);
+}
+
+template <typename ELFT>
+typename ELFDumper<ELFT>::Elf_Rela_Range ELFDumper<ELFT>::dyn_relas() const {
+ return make_range(dyn_rela_begin(), dyn_rela_end());
+}
+
template<class ELFT>
void ELFDumper<ELFT>::printFileHeaders() {
- const typename ELFO::Elf_Ehdr *Header = Obj->getHeader();
+ const Elf_Ehdr *Header = Obj->getHeader();
{
DictScope D(W, "ElfHeader");
@@ -618,7 +1084,7 @@ void ELFDumper<ELFT>::printSections() {
ListScope SectionsD(W, "Sections");
int SectionIndex = -1;
- for (const typename ELFO::Elf_Shdr &Sec : Obj->sections()) {
+ for (const Elf_Shdr &Sec : Obj->sections()) {
++SectionIndex;
StringRef Name = errorOrDefault(Obj->getSectionName(&Sec));
@@ -645,12 +1111,18 @@ void ELFDumper<ELFT>::printSections() {
if (opts::SectionSymbols) {
ListScope D(W, "Symbols");
- for (const typename ELFO::Elf_Sym &Sym : Obj->symbols()) {
- ErrorOr<const Elf_Shdr *> SymSec = Obj->getSection(&Sym);
+ const Elf_Shdr *Symtab = DotSymtabSec;
+ ErrorOr<StringRef> StrTableOrErr = Obj->getStringTableForSymtab(*Symtab);
+ error(StrTableOrErr.getError());
+ StringRef StrTable = *StrTableOrErr;
+
+ for (const Elf_Sym &Sym : Obj->symbols(Symtab)) {
+ ErrorOr<const Elf_Shdr *> SymSec =
+ Obj->getSection(&Sym, Symtab, ShndxTable);
if (!SymSec)
continue;
if (*SymSec == &Sec)
- printSymbol(&Sym, false);
+ printSymbol(&Sym, Symtab, StrTable, false);
}
}
@@ -667,7 +1139,7 @@ void ELFDumper<ELFT>::printRelocations() {
ListScope D(W, "Relocations");
int SectionNumber = -1;
- for (const typename ELFO::Elf_Shdr &Sec : Obj->sections()) {
+ for (const Elf_Shdr &Sec : Obj->sections()) {
++SectionNumber;
if (Sec.sh_type != ELF::SHT_REL && Sec.sh_type != ELF::SHT_RELA)
@@ -689,29 +1161,25 @@ template<class ELFT>
void ELFDumper<ELFT>::printDynamicRelocations() {
W.startLine() << "Dynamic Relocations {\n";
W.indent();
- for (typename ELFO::Elf_Rela_Iter RelI = Obj->dyn_rela_begin(),
- RelE = Obj->dyn_rela_end();
- RelI != RelE; ++RelI) {
+ for (const Elf_Rela &Rel : dyn_relas()) {
SmallString<32> RelocName;
- Obj->getRelocationTypeName(RelI->getType(Obj->isMips64EL()), RelocName);
+ Obj->getRelocationTypeName(Rel.getType(Obj->isMips64EL()), RelocName);
StringRef SymbolName;
- uint32_t SymIndex = RelI->getSymbol(Obj->isMips64EL());
- const typename ELFO::Elf_Sym *Sym = Obj->dynamic_symbol_begin() + SymIndex;
- SymbolName = errorOrDefault(Obj->getSymbolName(Sym, true));
+ uint32_t SymIndex = Rel.getSymbol(Obj->isMips64EL());
+ const Elf_Sym *Sym = DynSymStart + SymIndex;
+ SymbolName = errorOrDefault(Sym->getName(DynamicStringTable));
if (opts::ExpandRelocs) {
DictScope Group(W, "Relocation");
- W.printHex("Offset", RelI->r_offset);
- W.printNumber("Type", RelocName, (int)RelI->getType(Obj->isMips64EL()));
+ W.printHex("Offset", Rel.r_offset);
+ W.printNumber("Type", RelocName, (int)Rel.getType(Obj->isMips64EL()));
W.printString("Symbol", SymbolName.size() > 0 ? SymbolName : "-");
- W.printHex("Addend", RelI->r_addend);
+ W.printHex("Addend", Rel.r_addend);
}
else {
raw_ostream& OS = W.startLine();
- OS << W.hex(RelI->r_offset)
- << " " << RelocName
- << " " << (SymbolName.size() > 0 ? SymbolName : "-")
- << " " << W.hex(RelI->r_addend)
- << "\n";
+ OS << W.hex(Rel.r_offset) << " " << RelocName << " "
+ << (SymbolName.size() > 0 ? SymbolName : "-") << " "
+ << W.hex(Rel.r_addend) << "\n";
}
}
W.unindent();
@@ -720,51 +1188,43 @@ void ELFDumper<ELFT>::printDynamicRelocations() {
template <class ELFT>
void ELFDumper<ELFT>::printRelocations(const Elf_Shdr *Sec) {
+ ErrorOr<const Elf_Shdr *> SymTabOrErr = Obj->getSection(Sec->sh_link);
+ error(SymTabOrErr.getError());
+ const Elf_Shdr *SymTab = *SymTabOrErr;
+
switch (Sec->sh_type) {
case ELF::SHT_REL:
- for (typename ELFO::Elf_Rel_Iter RI = Obj->rel_begin(Sec),
- RE = Obj->rel_end(Sec);
- RI != RE; ++RI) {
- typename ELFO::Elf_Rela Rela;
- Rela.r_offset = RI->r_offset;
- Rela.r_info = RI->r_info;
+ for (const Elf_Rel &R : Obj->rels(Sec)) {
+ Elf_Rela Rela;
+ Rela.r_offset = R.r_offset;
+ Rela.r_info = R.r_info;
Rela.r_addend = 0;
- printRelocation(Sec, Rela);
+ printRelocation(Rela, SymTab);
}
break;
case ELF::SHT_RELA:
- for (typename ELFO::Elf_Rela_Iter RI = Obj->rela_begin(Sec),
- RE = Obj->rela_end(Sec);
- RI != RE; ++RI) {
- printRelocation(Sec, *RI);
- }
+ for (const Elf_Rela &R : Obj->relas(Sec))
+ printRelocation(R, SymTab);
break;
}
}
template <class ELFT>
-void ELFDumper<ELFT>::printRelocation(const Elf_Shdr *Sec,
- typename ELFO::Elf_Rela Rel) {
+void ELFDumper<ELFT>::printRelocation(Elf_Rela Rel, const Elf_Shdr *SymTab) {
SmallString<32> RelocName;
Obj->getRelocationTypeName(Rel.getType(Obj->isMips64EL()), RelocName);
StringRef TargetName;
- std::pair<const Elf_Shdr *, const Elf_Sym *> Sym =
- Obj->getRelocationSymbol(Sec, &Rel);
- if (Sym.second && Sym.second->getType() == ELF::STT_SECTION) {
- ErrorOr<const Elf_Shdr *> Sec = Obj->getSection(Sym.second);
- if (!error(Sec.getError())) {
- ErrorOr<StringRef> SecName = Obj->getSectionName(*Sec);
- if (SecName)
- TargetName = SecName.get();
- }
- } else if (Sym.first) {
- const Elf_Shdr *SymTable = Sym.first;
- ErrorOr<const Elf_Shdr *> StrTableSec = Obj->getSection(SymTable->sh_link);
- if (!error(StrTableSec.getError())) {
- ErrorOr<StringRef> StrTableOrErr = Obj->getStringTable(*StrTableSec);
- if (!error(StrTableOrErr.getError()))
- TargetName = errorOrDefault(Sym.second->getName(*StrTableOrErr));
- }
+ const Elf_Sym *Sym = Obj->getRelocationSymbol(&Rel, SymTab);
+ if (Sym && Sym->getType() == ELF::STT_SECTION) {
+ ErrorOr<const Elf_Shdr *> Sec = Obj->getSection(Sym, SymTab, ShndxTable);
+ error(Sec.getError());
+ ErrorOr<StringRef> SecName = Obj->getSectionName(*Sec);
+ if (SecName)
+ TargetName = SecName.get();
+ } else if (Sym) {
+ ErrorOr<StringRef> StrTableOrErr = Obj->getStringTableForSymtab(*SymTab);
+ error(StrTableOrErr.getError());
+ TargetName = errorOrDefault(Sym->getName(*StrTableOrErr));
}
if (opts::ExpandRelocs) {
@@ -783,27 +1243,38 @@ void ELFDumper<ELFT>::printRelocation(const Elf_Shdr *Sec,
}
template<class ELFT>
+void ELFDumper<ELFT>::printSymbolsHelper(bool IsDynamic) {
+ const Elf_Shdr *Symtab = (IsDynamic) ? DotDynSymSec : DotSymtabSec;
+ if (!Symtab)
+ return;
+ ErrorOr<StringRef> StrTableOrErr = Obj->getStringTableForSymtab(*Symtab);
+ error(StrTableOrErr.getError());
+ StringRef StrTable = *StrTableOrErr;
+ for (const Elf_Sym &Sym : Obj->symbols(Symtab))
+ printSymbol(&Sym, Symtab, StrTable, IsDynamic);
+}
+
+template<class ELFT>
void ELFDumper<ELFT>::printSymbols() {
ListScope Group(W, "Symbols");
- for (const typename ELFO::Elf_Sym &Sym : Obj->symbols())
- printSymbol(&Sym, false);
+ printSymbolsHelper(false);
}
template<class ELFT>
void ELFDumper<ELFT>::printDynamicSymbols() {
ListScope Group(W, "DynamicSymbols");
-
- for (const typename ELFO::Elf_Sym &Sym : Obj->dynamic_symbols())
- printSymbol(&Sym, true);
+ printSymbolsHelper(true);
}
template <class ELFT>
-void ELFDumper<ELFT>::printSymbol(const typename ELFO::Elf_Sym *Symbol,
- bool IsDynamic) {
+void ELFDumper<ELFT>::printSymbol(const Elf_Sym *Symbol, const Elf_Shdr *SymTab,
+ StringRef StrTable, bool IsDynamic) {
unsigned SectionIndex = 0;
StringRef SectionName;
- getSectionNameIndex(*Obj, Symbol, SectionName, SectionIndex);
- std::string FullSymbolName = getFullSymbolName(*Obj, Symbol, IsDynamic);
+ getSectionNameIndex(*Obj, Symbol, SymTab, ShndxTable, SectionName,
+ SectionIndex);
+ std::string FullSymbolName = getFullSymbolName(Symbol, StrTable, IsDynamic);
+ unsigned char SymbolType = Symbol->getType();
DictScope D(W, "Symbol");
W.printNumber("Name", FullSymbolName, Symbol->st_name);
@@ -811,7 +1282,11 @@ void ELFDumper<ELFT>::printSymbol(const typename ELFO::Elf_Sym *Symbol,
W.printNumber("Size", Symbol->st_size);
W.printEnum ("Binding", Symbol->getBinding(),
makeArrayRef(ElfSymbolBindings));
- W.printEnum ("Type", Symbol->getType(), makeArrayRef(ElfSymbolTypes));
+ if (Obj->getHeader()->e_machine == ELF::EM_AMDGPU &&
+ SymbolType >= ELF::STT_LOOS && SymbolType < ELF::STT_HIOS)
+ W.printEnum ("Type", SymbolType, makeArrayRef(AMDGPUSymbolTypes));
+ else
+ W.printEnum ("Type", SymbolType, makeArrayRef(ElfSymbolTypes));
W.printNumber("Other", Symbol->st_other);
W.printHex("Section", SectionName, SectionIndex);
}
@@ -855,12 +1330,15 @@ static const char *getTypeString(uint64_t Type) {
LLVM_READOBJ_TYPE_CASE(SYMENT);
LLVM_READOBJ_TYPE_CASE(SYMTAB);
LLVM_READOBJ_TYPE_CASE(TEXTREL);
+ LLVM_READOBJ_TYPE_CASE(VERDEF);
+ LLVM_READOBJ_TYPE_CASE(VERDEFNUM);
LLVM_READOBJ_TYPE_CASE(VERNEED);
LLVM_READOBJ_TYPE_CASE(VERNEEDNUM);
LLVM_READOBJ_TYPE_CASE(VERSYM);
LLVM_READOBJ_TYPE_CASE(RELCOUNT);
LLVM_READOBJ_TYPE_CASE(GNU_HASH);
LLVM_READOBJ_TYPE_CASE(MIPS_RLD_VERSION);
+ LLVM_READOBJ_TYPE_CASE(MIPS_RLD_MAP_REL);
LLVM_READOBJ_TYPE_CASE(MIPS_FLAGS);
LLVM_READOBJ_TYPE_CASE(MIPS_BASE_ADDRESS);
LLVM_READOBJ_TYPE_CASE(MIPS_LOCAL_GOTNO);
@@ -956,8 +1434,15 @@ void printFlags(T Value, ArrayRef<EnumEntry<TFlag>> Flags, raw_ostream &OS) {
}
template <class ELFT>
-static void printValue(const ELFFile<ELFT> *O, uint64_t Type, uint64_t Value,
- bool Is64, raw_ostream &OS) {
+StringRef ELFDumper<ELFT>::getDynamicString(uint64_t Value) const {
+ if (Value >= DynamicStringTable.size())
+ reportError("Invalid dynamic string table reference");
+ return StringRef(DynamicStringTable.data() + Value);
+}
+
+template <class ELFT>
+void ELFDumper<ELFT>::printValue(uint64_t Type, uint64_t Value) {
+ raw_ostream &OS = W.getOStream();
switch (Type) {
case DT_PLTREL:
if (Value == DT_REL) {
@@ -981,6 +1466,7 @@ static void printValue(const ELFFile<ELFT> *O, uint64_t Type, uint64_t Value,
case DT_FINI_ARRAY:
case DT_PREINIT_ARRAY:
case DT_DEBUG:
+ case DT_VERDEF:
case DT_VERNEED:
case DT_VERSYM:
case DT_GNU_HASH:
@@ -988,11 +1474,13 @@ static void printValue(const ELFFile<ELFT> *O, uint64_t Type, uint64_t Value,
case DT_MIPS_BASE_ADDRESS:
case DT_MIPS_GOTSYM:
case DT_MIPS_RLD_MAP:
+ case DT_MIPS_RLD_MAP_REL:
case DT_MIPS_PLTGOT:
case DT_MIPS_OPTIONS:
OS << format("0x%" PRIX64, Value);
break;
case DT_RELCOUNT:
+ case DT_VERDEFNUM:
case DT_VERNEEDNUM:
case DT_MIPS_RLD_VERSION:
case DT_MIPS_LOCAL_GOTNO:
@@ -1013,14 +1501,14 @@ static void printValue(const ELFFile<ELFT> *O, uint64_t Type, uint64_t Value,
OS << Value << " (bytes)";
break;
case DT_NEEDED:
- OS << "SharedLibrary (" << O->getDynamicString(Value) << ")";
+ OS << "SharedLibrary (" << getDynamicString(Value) << ")";
break;
case DT_SONAME:
- OS << "LibrarySoname (" << O->getDynamicString(Value) << ")";
+ OS << "LibrarySoname (" << getDynamicString(Value) << ")";
break;
case DT_RPATH:
case DT_RUNPATH:
- OS << O->getDynamicString(Value);
+ OS << getDynamicString(Value);
break;
case DT_MIPS_FLAGS:
printFlags(Value, makeArrayRef(ElfDynamicDTMipsFlags), OS);
@@ -1046,7 +1534,8 @@ namespace {
template <> void ELFDumper<ELFType<support::little, false>>::printUnwindInfo() {
const unsigned Machine = Obj->getHeader()->e_machine;
if (Machine == EM_ARM) {
- ARM::EHABI::PrinterContext<ELFType<support::little, false>> Ctx(W, Obj);
+ ARM::EHABI::PrinterContext<ELFType<support::little, false>> Ctx(
+ W, Obj, DotSymtabSec);
return Ctx.PrintUnwindInformation();
}
W.startLine() << "UnwindInfo not implemented.\n";
@@ -1055,9 +1544,20 @@ template <> void ELFDumper<ELFType<support::little, false>>::printUnwindInfo() {
template<class ELFT>
void ELFDumper<ELFT>::printDynamicTable() {
- auto DynTable = Obj->dynamic_table(true);
+ auto I = dynamic_table_begin();
+ auto E = dynamic_table_end();
+
+ if (I == E)
+ return;
- ptrdiff_t Total = std::distance(DynTable.begin(), DynTable.end());
+ --E;
+ while (I != E && E->getTag() == ELF::DT_NULL)
+ --E;
+ if (E->getTag() != ELF::DT_NULL)
+ ++E;
+ ++E;
+
+ ptrdiff_t Total = std::distance(I, E);
if (Total == 0)
return;
@@ -1069,12 +1569,13 @@ void ELFDumper<ELFT>::printDynamicTable() {
W.startLine()
<< " Tag" << (Is64 ? " " : " ") << "Type"
<< " " << "Name/Value\n";
- for (const auto &Entry : DynTable) {
- W.startLine()
- << " "
- << format(Is64 ? "0x%016" PRIX64 : "0x%08" PRIX64, Entry.getTag())
- << " " << format("%-21s", getTypeString(Entry.getTag()));
- printValue(Obj, Entry.getTag(), Entry.getVal(), Is64, OS);
+ while (I != E) {
+ const Elf_Dyn &Entry = *I;
+ uintX_t Tag = Entry.getTag();
+ ++I;
+ W.startLine() << " " << format_hex(Tag, Is64 ? 18 : 10, true) << " "
+ << format("%-21s", getTypeString(Tag));
+ printValue(Tag, Entry.getVal());
OS << "\n";
}
@@ -1088,14 +1589,14 @@ void ELFDumper<ELFT>::printNeededLibraries() {
typedef std::vector<StringRef> LibsTy;
LibsTy Libs;
- for (const auto &Entry : Obj->dynamic_table())
+ for (const auto &Entry : dynamic_table())
if (Entry.d_tag == ELF::DT_NEEDED)
- Libs.push_back(Obj->getDynamicString(Entry.d_un.d_val));
+ Libs.push_back(getDynamicString(Entry.d_un.d_val));
std::stable_sort(Libs.begin(), Libs.end());
- for (LibsTy::const_iterator I = Libs.begin(), E = Libs.end(); I != E; ++I) {
- outs() << " " << *I << "\n";
+ for (const auto &L : Libs) {
+ outs() << " " << L << "\n";
}
}
@@ -1103,33 +1604,51 @@ template<class ELFT>
void ELFDumper<ELFT>::printProgramHeaders() {
ListScope L(W, "ProgramHeaders");
- for (typename ELFO::Elf_Phdr_Iter PI = Obj->program_header_begin(),
- PE = Obj->program_header_end();
- PI != PE; ++PI) {
+ for (const Elf_Phdr &Phdr : Obj->program_headers()) {
DictScope P(W, "ProgramHeader");
- W.printHex ("Type",
- getElfSegmentType(Obj->getHeader()->e_machine, PI->p_type),
- PI->p_type);
- W.printHex ("Offset", PI->p_offset);
- W.printHex ("VirtualAddress", PI->p_vaddr);
- W.printHex ("PhysicalAddress", PI->p_paddr);
- W.printNumber("FileSize", PI->p_filesz);
- W.printNumber("MemSize", PI->p_memsz);
- W.printFlags ("Flags", PI->p_flags, makeArrayRef(ElfSegmentFlags));
- W.printNumber("Alignment", PI->p_align);
+ W.printHex("Type",
+ getElfSegmentType(Obj->getHeader()->e_machine, Phdr.p_type),
+ Phdr.p_type);
+ W.printHex("Offset", Phdr.p_offset);
+ W.printHex("VirtualAddress", Phdr.p_vaddr);
+ W.printHex("PhysicalAddress", Phdr.p_paddr);
+ W.printNumber("FileSize", Phdr.p_filesz);
+ W.printNumber("MemSize", Phdr.p_memsz);
+ W.printFlags("Flags", Phdr.p_flags, makeArrayRef(ElfSegmentFlags));
+ W.printNumber("Alignment", Phdr.p_align);
}
}
template <typename ELFT>
void ELFDumper<ELFT>::printHashTable() {
DictScope D(W, "HashTable");
- auto HT = Obj->getHashTable();
- if (!HT)
+ if (!HashTable)
return;
- W.printNumber("Num Buckets", HT->nbucket);
- W.printNumber("Num Chains", HT->nchain);
- W.printList("Buckets", HT->buckets());
- W.printList("Chains", HT->chains());
+ W.printNumber("Num Buckets", HashTable->nbucket);
+ W.printNumber("Num Chains", HashTable->nchain);
+ W.printList("Buckets", HashTable->buckets());
+ W.printList("Chains", HashTable->chains());
+}
+
+template <typename ELFT>
+void ELFDumper<ELFT>::printGnuHashTable() {
+ DictScope D(W, "GnuHashTable");
+ if (!GnuHashTable)
+ return;
+ W.printNumber("Num Buckets", GnuHashTable->nbuckets);
+ W.printNumber("First Hashed Symbol Index", GnuHashTable->symndx);
+ W.printNumber("Num Mask Words", GnuHashTable->maskwords);
+ W.printNumber("Shift Count", GnuHashTable->shift2);
+ W.printHexList("Bloom Filter", GnuHashTable->filter());
+ W.printList("Buckets", GnuHashTable->buckets());
+ if (!DotDynSymSec)
+ reportError("No dynamic symbol section");
+ W.printHexList("Values",
+ GnuHashTable->values(DotDynSymSec->getEntityCount()));
+}
+
+template <typename ELFT> void ELFDumper<ELFT>::printLoadName() {
+ outs() << "LoadName: " << SOName << '\n';
}
template <class ELFT>
@@ -1171,21 +1690,23 @@ template <> void ELFDumper<ELFType<support::little, false>>::printAttributes() {
namespace {
template <class ELFT> class MipsGOTParser {
public:
- typedef object::ELFFile<ELFT> ObjectFile;
- typedef typename ObjectFile::Elf_Shdr Elf_Shdr;
- typedef typename ObjectFile::Elf_Sym Elf_Sym;
+ typedef object::ELFFile<ELFT> ELFO;
+ typedef typename ELFO::Elf_Shdr Elf_Shdr;
+ typedef typename ELFO::Elf_Sym Elf_Sym;
+ typedef typename ELFO::Elf_Dyn_Range Elf_Dyn_Range;
+ typedef typename ELFO::Elf_Addr GOTEntry;
+ typedef typename ELFO::Elf_Rel Elf_Rel;
+ typedef typename ELFO::Elf_Rela Elf_Rela;
- MipsGOTParser(const ObjectFile *Obj, StreamWriter &W);
+ MipsGOTParser(ELFDumper<ELFT> *Dumper, const ELFO *Obj,
+ Elf_Dyn_Range DynTable, StreamWriter &W);
void parseGOT();
void parsePLT();
private:
- typedef typename ObjectFile::Elf_Addr GOTEntry;
- typedef typename ObjectFile::template ELFEntityIterator<const GOTEntry>
- GOTIter;
-
- const ObjectFile *Obj;
+ ELFDumper<ELFT> *Dumper;
+ const ELFO *Obj;
StreamWriter &W;
llvm::Optional<uint64_t> DtPltGot;
llvm::Optional<uint64_t> DtLocalGotNum;
@@ -1194,22 +1715,26 @@ private:
llvm::Optional<uint64_t> DtJmpRel;
std::size_t getGOTTotal(ArrayRef<uint8_t> GOT) const;
- GOTIter makeGOTIter(ArrayRef<uint8_t> GOT, std::size_t EntryNum);
+ const GOTEntry *makeGOTIter(ArrayRef<uint8_t> GOT, std::size_t EntryNum);
- void printGotEntry(uint64_t GotAddr, GOTIter BeginIt, GOTIter It);
- void printGlobalGotEntry(uint64_t GotAddr, GOTIter BeginIt, GOTIter It,
- const Elf_Sym *Sym, bool IsDynamic);
- void printPLTEntry(uint64_t PLTAddr, GOTIter BeginIt, GOTIter It,
- StringRef Purpose);
- void printPLTEntry(uint64_t PLTAddr, GOTIter BeginIt, GOTIter It,
+ void printGotEntry(uint64_t GotAddr, const GOTEntry *BeginIt,
+ const GOTEntry *It);
+ void printGlobalGotEntry(uint64_t GotAddr, const GOTEntry *BeginIt,
+ const GOTEntry *It, const Elf_Sym *Sym,
+ StringRef StrTable, bool IsDynamic);
+ void printPLTEntry(uint64_t PLTAddr, const GOTEntry *BeginIt,
+ const GOTEntry *It, StringRef Purpose);
+ void printPLTEntry(uint64_t PLTAddr, const GOTEntry *BeginIt,
+ const GOTEntry *It, StringRef StrTable,
const Elf_Sym *Sym);
};
}
template <class ELFT>
-MipsGOTParser<ELFT>::MipsGOTParser(const ObjectFile *Obj, StreamWriter &W)
- : Obj(Obj), W(W) {
- for (const auto &Entry : Obj->dynamic_table()) {
+MipsGOTParser<ELFT>::MipsGOTParser(ELFDumper<ELFT> *Dumper, const ELFO *Obj,
+ Elf_Dyn_Range DynTable, StreamWriter &W)
+ : Dumper(Dumper), Obj(Obj), W(W) {
+ for (const auto &Entry : DynTable) {
switch (Entry.getTag()) {
case ELF::DT_PLTGOT:
DtPltGot = Entry.getVal();
@@ -1264,8 +1789,11 @@ template <class ELFT> void MipsGOTParser<ELFT>::parseGOT() {
return;
}
- const Elf_Sym *DynSymBegin = Obj->dynamic_symbol_begin();
- const Elf_Sym *DynSymEnd = Obj->dynamic_symbol_end();
+ const Elf_Shdr *DynSymSec = Dumper->getDotDynSymSec();
+ ErrorOr<StringRef> StrTable = Obj->getStringTableForSymtab(*DynSymSec);
+ error(StrTable.getError());
+ const Elf_Sym *DynSymBegin = Obj->symbol_begin(DynSymSec);
+ const Elf_Sym *DynSymEnd = Obj->symbol_end(DynSymSec);
std::size_t DynSymTotal = std::size_t(std::distance(DynSymBegin, DynSymEnd));
if (*DtGotSym > DynSymTotal) {
@@ -1280,9 +1808,9 @@ template <class ELFT> void MipsGOTParser<ELFT>::parseGOT() {
return;
}
- GOTIter GotBegin = makeGOTIter(*GOT, 0);
- GOTIter GotLocalEnd = makeGOTIter(*GOT, *DtLocalGotNum);
- GOTIter It = GotBegin;
+ const GOTEntry *GotBegin = makeGOTIter(*GOT, 0);
+ const GOTEntry *GotLocalEnd = makeGOTIter(*GOT, *DtLocalGotNum);
+ const GOTEntry *It = GotBegin;
DictScope GS(W, "Primary GOT");
@@ -1312,11 +1840,13 @@ template <class ELFT> void MipsGOTParser<ELFT>::parseGOT() {
{
ListScope GS(W, "Global entries");
- GOTIter GotGlobalEnd = makeGOTIter(*GOT, *DtLocalGotNum + GlobalGotNum);
+ const GOTEntry *GotGlobalEnd =
+ makeGOTIter(*GOT, *DtLocalGotNum + GlobalGotNum);
const Elf_Sym *GotDynSym = DynSymBegin + *DtGotSym;
for (; It != GotGlobalEnd; ++It) {
DictScope D(W, "Entry");
- printGlobalGotEntry(GOTShdr->sh_addr, GotBegin, It, GotDynSym++, true);
+ printGlobalGotEntry(GOTShdr->sh_addr, GotBegin, It, GotDynSym++,
+ *StrTable, true);
}
}
@@ -1350,10 +1880,16 @@ template <class ELFT> void MipsGOTParser<ELFT>::parsePLT() {
W.startLine() << "There is no .rel.plt section in the file.\n";
return;
}
+ ErrorOr<const Elf_Shdr *> SymTableOrErr =
+ Obj->getSection(PLTRelShdr->sh_link);
+ error(SymTableOrErr.getError());
+ const Elf_Shdr *SymTable = *SymTableOrErr;
+ ErrorOr<StringRef> StrTable = Obj->getStringTableForSymtab(*SymTable);
+ error(StrTable.getError());
- GOTIter PLTBegin = makeGOTIter(*PLT, 0);
- GOTIter PLTEnd = makeGOTIter(*PLT, getGOTTotal(*PLT));
- GOTIter It = PLTBegin;
+ const GOTEntry *PLTBegin = makeGOTIter(*PLT, 0);
+ const GOTEntry *PLTEnd = makeGOTIter(*PLT, getGOTTotal(*PLT));
+ const GOTEntry *It = PLTBegin;
DictScope GS(W, "PLT GOT");
{
@@ -1367,21 +1903,19 @@ template <class ELFT> void MipsGOTParser<ELFT>::parsePLT() {
switch (PLTRelShdr->sh_type) {
case ELF::SHT_REL:
- for (typename ObjectFile::Elf_Rel_Iter RI = Obj->rel_begin(PLTRelShdr),
- RE = Obj->rel_end(PLTRelShdr);
+ for (const Elf_Rel *RI = Obj->rel_begin(PLTRelShdr),
+ *RE = Obj->rel_end(PLTRelShdr);
RI != RE && It != PLTEnd; ++RI, ++It) {
- const Elf_Sym *Sym =
- Obj->getRelocationSymbol(&*PLTRelShdr, &*RI).second;
- printPLTEntry(PLTShdr->sh_addr, PLTBegin, It, Sym);
+ const Elf_Sym *Sym = Obj->getRelocationSymbol(&*RI, SymTable);
+ printPLTEntry(PLTShdr->sh_addr, PLTBegin, It, *StrTable, Sym);
}
break;
case ELF::SHT_RELA:
- for (typename ObjectFile::Elf_Rela_Iter RI = Obj->rela_begin(PLTRelShdr),
- RE = Obj->rela_end(PLTRelShdr);
+ for (const Elf_Rela *RI = Obj->rela_begin(PLTRelShdr),
+ *RE = Obj->rela_end(PLTRelShdr);
RI != RE && It != PLTEnd; ++RI, ++It) {
- const Elf_Sym *Sym =
- Obj->getRelocationSymbol(&*PLTRelShdr, &*RI).second;
- printPLTEntry(PLTShdr->sh_addr, PLTBegin, It, Sym);
+ const Elf_Sym *Sym = Obj->getRelocationSymbol(&*RI, SymTable);
+ printPLTEntry(PLTShdr->sh_addr, PLTBegin, It, *StrTable, Sym);
}
break;
}
@@ -1394,15 +1928,16 @@ std::size_t MipsGOTParser<ELFT>::getGOTTotal(ArrayRef<uint8_t> GOT) const {
}
template <class ELFT>
-typename MipsGOTParser<ELFT>::GOTIter
+const typename MipsGOTParser<ELFT>::GOTEntry *
MipsGOTParser<ELFT>::makeGOTIter(ArrayRef<uint8_t> GOT, std::size_t EntryNum) {
const char *Data = reinterpret_cast<const char *>(GOT.data());
- return GOTIter(sizeof(GOTEntry), Data + EntryNum * sizeof(GOTEntry));
+ return reinterpret_cast<const GOTEntry *>(Data + EntryNum * sizeof(GOTEntry));
}
template <class ELFT>
-void MipsGOTParser<ELFT>::printGotEntry(uint64_t GotAddr, GOTIter BeginIt,
- GOTIter It) {
+void MipsGOTParser<ELFT>::printGotEntry(uint64_t GotAddr,
+ const GOTEntry *BeginIt,
+ const GOTEntry *It) {
int64_t Offset = std::distance(BeginIt, It) * sizeof(GOTEntry);
W.printHex("Address", GotAddr + Offset);
W.printNumber("Access", Offset - 0x7ff0);
@@ -1410,9 +1945,9 @@ void MipsGOTParser<ELFT>::printGotEntry(uint64_t GotAddr, GOTIter BeginIt,
}
template <class ELFT>
-void MipsGOTParser<ELFT>::printGlobalGotEntry(uint64_t GotAddr, GOTIter BeginIt,
- GOTIter It, const Elf_Sym *Sym,
- bool IsDynamic) {
+void MipsGOTParser<ELFT>::printGlobalGotEntry(
+ uint64_t GotAddr, const GOTEntry *BeginIt, const GOTEntry *It,
+ const Elf_Sym *Sym, StringRef StrTable, bool IsDynamic) {
printGotEntry(GotAddr, BeginIt, It);
W.printHex("Value", Sym->st_value);
@@ -1420,16 +1955,19 @@ void MipsGOTParser<ELFT>::printGlobalGotEntry(uint64_t GotAddr, GOTIter BeginIt,
unsigned SectionIndex = 0;
StringRef SectionName;
- getSectionNameIndex(*Obj, Sym, SectionName, SectionIndex);
+ getSectionNameIndex(*Obj, Sym, Dumper->getDotDynSymSec(),
+ Dumper->getShndxTable(), SectionName, SectionIndex);
W.printHex("Section", SectionName, SectionIndex);
- std::string FullSymbolName = getFullSymbolName(*Obj, Sym, IsDynamic);
+ std::string FullSymbolName =
+ Dumper->getFullSymbolName(Sym, StrTable, IsDynamic);
W.printNumber("Name", FullSymbolName, Sym->st_name);
}
template <class ELFT>
-void MipsGOTParser<ELFT>::printPLTEntry(uint64_t PLTAddr, GOTIter BeginIt,
- GOTIter It, StringRef Purpose) {
+void MipsGOTParser<ELFT>::printPLTEntry(uint64_t PLTAddr,
+ const GOTEntry *BeginIt,
+ const GOTEntry *It, StringRef Purpose) {
DictScope D(W, "Entry");
int64_t Offset = std::distance(BeginIt, It) * sizeof(GOTEntry);
W.printHex("Address", PLTAddr + Offset);
@@ -1438,8 +1976,10 @@ void MipsGOTParser<ELFT>::printPLTEntry(uint64_t PLTAddr, GOTIter BeginIt,
}
template <class ELFT>
-void MipsGOTParser<ELFT>::printPLTEntry(uint64_t PLTAddr, GOTIter BeginIt,
- GOTIter It, const Elf_Sym *Sym) {
+void MipsGOTParser<ELFT>::printPLTEntry(uint64_t PLTAddr,
+ const GOTEntry *BeginIt,
+ const GOTEntry *It, StringRef StrTable,
+ const Elf_Sym *Sym) {
DictScope D(W, "Entry");
int64_t Offset = std::distance(BeginIt, It) * sizeof(GOTEntry);
W.printHex("Address", PLTAddr + Offset);
@@ -1449,10 +1989,11 @@ void MipsGOTParser<ELFT>::printPLTEntry(uint64_t PLTAddr, GOTIter BeginIt,
unsigned SectionIndex = 0;
StringRef SectionName;
- getSectionNameIndex(*Obj, Sym, SectionName, SectionIndex);
+ getSectionNameIndex(*Obj, Sym, Dumper->getDotDynSymSec(),
+ Dumper->getShndxTable(), SectionName, SectionIndex);
W.printHex("Section", SectionName, SectionIndex);
- std::string FullSymbolName = getFullSymbolName(*Obj, Sym, true);
+ std::string FullSymbolName = Dumper->getFullSymbolName(Sym, StrTable, true);
W.printNumber("Name", FullSymbolName, Sym->st_name);
}
@@ -1462,7 +2003,7 @@ template <class ELFT> void ELFDumper<ELFT>::printMipsPLTGOT() {
return;
}
- MipsGOTParser<ELFT> GOTParser(Obj, W);
+ MipsGOTParser<ELFT> GOTParser(this, Obj, dynamic_table(), W);
GOTParser.parseGOT();
GOTParser.parsePLT();
}
@@ -1604,7 +2145,7 @@ template <class ELFT> void ELFDumper<ELFT>::printMipsReginfo() {
}
template <class ELFT> void ELFDumper<ELFT>::printStackMap() const {
- const typename ELFFile<ELFT>::Elf_Shdr *StackMapSection = nullptr;
+ const Elf_Shdr *StackMapSection = nullptr;
for (const auto &Sec : Obj->sections()) {
ErrorOr<StringRef> Name = Obj->getSectionName(&Sec);
if (*Name == ".llvm_stackmaps") {
diff --git a/tools/llvm-readobj/MachODumper.cpp b/tools/llvm-readobj/MachODumper.cpp
index adb99b0acd7f..58d2c9fca47d 100644
--- a/tools/llvm-readobj/MachODumper.cpp
+++ b/tools/llvm-readobj/MachODumper.cpp
@@ -40,6 +40,14 @@ public:
void printUnwindInfo() override;
void printStackMap() const override;
+ // MachO-specific.
+ void printMachODataInCode() override;
+ void printMachOVersionMin() override;
+ void printMachODysymtab() override;
+ void printMachOSegment() override;
+ void printMachOIndirectSymbols() override;
+ void printMachOLinkerOptions () override;
+
private:
template<class MachHeader>
void printFileHeaders(const MachHeader &Header);
@@ -255,6 +263,21 @@ namespace {
uint32_t Flags;
uint32_t Reserved1;
uint32_t Reserved2;
+ uint32_t Reserved3;
+ };
+
+ struct MachOSegment {
+ std::string CmdName;
+ std::string SegName;
+ uint64_t cmdsize;
+ uint64_t vmaddr;
+ uint64_t vmsize;
+ uint64_t fileoff;
+ uint64_t filesize;
+ uint32_t maxprot;
+ uint32_t initprot;
+ uint32_t nsects;
+ uint32_t flags;
};
struct MachOSymbol {
@@ -266,6 +289,18 @@ namespace {
};
}
+static std::string getMask(uint32_t prot)
+{
+ // TODO (davide): This always assumes prot is valid.
+ // Catch mistakes and report if needed.
+ std::string Prot;
+ Prot = "";
+ Prot += (prot & MachO::VM_PROT_READ) ? "r" : "-";
+ Prot += (prot & MachO::VM_PROT_WRITE) ? "w" : "-";
+ Prot += (prot & MachO::VM_PROT_EXECUTE) ? "x" : "-";
+ return Prot;
+}
+
static void getSection(const MachOObjectFile *Obj,
DataRefImpl Sec,
MachOSection &Section) {
@@ -292,8 +327,40 @@ static void getSection(const MachOObjectFile *Obj,
Section.Flags = Sect.flags;
Section.Reserved1 = Sect.reserved1;
Section.Reserved2 = Sect.reserved2;
+ Section.Reserved3 = Sect.reserved3;
}
+static void getSegment(const MachOObjectFile *Obj,
+ const MachOObjectFile::LoadCommandInfo &L,
+ MachOSegment &Segment) {
+ if (!Obj->is64Bit()) {
+ MachO::segment_command SC = Obj->getSegmentLoadCommand(L);
+ Segment.CmdName = "LC_SEGMENT";
+ Segment.SegName = SC.segname;
+ Segment.cmdsize = SC.cmdsize;
+ Segment.vmaddr = SC.vmaddr;
+ Segment.vmsize = SC.vmsize;
+ Segment.fileoff = SC.fileoff;
+ Segment.filesize = SC.filesize;
+ Segment.maxprot = SC.maxprot;
+ Segment.initprot = SC.initprot;
+ Segment.nsects = SC.nsects;
+ Segment.flags = SC.flags;
+ return;
+ }
+ MachO::segment_command_64 SC = Obj->getSegment64LoadCommand(L);
+ Segment.CmdName = "LC_SEGMENT_64";
+ Segment.SegName = SC.segname;
+ Segment.cmdsize = SC.cmdsize;
+ Segment.vmaddr = SC.vmaddr;
+ Segment.vmsize = SC.vmsize;
+ Segment.fileoff = SC.fileoff;
+ Segment.filesize = SC.filesize;
+ Segment.maxprot = SC.maxprot;
+ Segment.initprot = SC.initprot;
+ Segment.nsects = SC.nsects;
+ Segment.flags = SC.flags;
+}
static void getSymbol(const MachOObjectFile *Obj,
DataRefImpl DRI,
@@ -375,8 +442,7 @@ void MachODumper::printSections(const MachOObjectFile *Obj) {
DataRefImpl DR = Section.getRawDataRefImpl();
StringRef Name;
- if (error(Section.getName(Name)))
- Name = "";
+ error(Section.getName(Name));
ArrayRef<char> RawName = Obj->getSectionRawName(DR);
StringRef SegmentName = Obj->getSectionFinalSegmentName(DR);
@@ -398,6 +464,8 @@ void MachODumper::printSections(const MachOObjectFile *Obj) {
makeArrayRef(MachOSectionAttributes));
W.printHex("Reserved1", MOSection.Reserved1);
W.printHex("Reserved2", MOSection.Reserved2);
+ if (Obj->is64Bit())
+ W.printHex("Reserved3", MOSection.Reserved3);
if (opts::SectionRelocations) {
ListScope D(W, "Relocations");
@@ -419,8 +487,7 @@ void MachODumper::printSections(const MachOObjectFile *Obj) {
bool IsBSS = Section.isBSS();
if (!IsBSS) {
StringRef Data;
- if (error(Section.getContents(Data)))
- break;
+ error(Section.getContents(Data));
W.printBinaryBlock("SectionData", Data);
}
@@ -434,8 +501,7 @@ void MachODumper::printRelocations() {
std::error_code EC;
for (const SectionRef &Section : Obj->sections()) {
StringRef Name;
- if (error(Section.getName(Name)))
- continue;
+ error(Section.getName(Name));
bool PrintedGroup = false;
for (const RelocationRef &Reloc : Section.relocations()) {
@@ -475,15 +541,13 @@ void MachODumper::printRelocation(const MachOObjectFile *Obj,
symbol_iterator Symbol = Reloc.getSymbol();
if (Symbol != Obj->symbol_end()) {
ErrorOr<StringRef> TargetNameOrErr = Symbol->getName();
- if (error(TargetNameOrErr.getError()))
- return;
+ error(TargetNameOrErr.getError());
TargetName = *TargetNameOrErr;
}
} else if (!IsScattered) {
section_iterator SecI = Obj->getRelocationSection(DR);
if (SecI != Obj->section_end()) {
- if (error(SecI->getName(TargetName)))
- return;
+ error(SecI->getName(TargetName));
}
}
if (TargetName.empty())
@@ -547,8 +611,10 @@ void MachODumper::printSymbol(const SymbolRef &Symbol) {
getSymbol(Obj, Symbol.getRawDataRefImpl(), MOSymbol);
StringRef SectionName = "";
- section_iterator SecI(Obj->section_begin());
- if (!error(Symbol.getSection(SecI)) && SecI != Obj->section_end())
+ ErrorOr<section_iterator> SecIOrErr = Symbol.getSection();
+ error(SecIOrErr.getError());
+ section_iterator SecI = *SecIOrErr;
+ if (SecI != Obj->section_end())
error(SecI->getName(SectionName));
DictScope D(W, "Symbol");
@@ -603,3 +669,153 @@ void MachODumper::printStackMap() const {
prettyPrintStackMap(llvm::outs(),
StackMapV1Parser<support::big>(StackMapContentsArray));
}
+
+void MachODumper::printMachODataInCode() {
+ for (const auto &Load : Obj->load_commands()) {
+ if (Load.C.cmd == MachO::LC_DATA_IN_CODE) {
+ MachO::linkedit_data_command LLC = Obj->getLinkeditDataLoadCommand(Load);
+ DictScope Group(W, "DataInCode");
+ W.printNumber("Data offset", LLC.dataoff);
+ W.printNumber("Data size", LLC.datasize);
+ ListScope D(W, "Data entries");
+ unsigned NumRegions = LLC.datasize / sizeof(MachO::data_in_code_entry);
+ for (unsigned i = 0; i < NumRegions; ++i) {
+ MachO::data_in_code_entry DICE = Obj->getDataInCodeTableEntry(
+ LLC.dataoff, i);
+ DictScope Group(W, "Entry");
+ W.printNumber("Index", i);
+ W.printNumber("Offset", DICE.offset);
+ W.printNumber("Length", DICE.length);
+ W.printNumber("Kind", DICE.kind);
+ }
+ }
+ }
+}
+
+void MachODumper::printMachOVersionMin() {
+ for (const auto &Load : Obj->load_commands()) {
+ StringRef Cmd;
+ switch (Load.C.cmd) {
+ case MachO::LC_VERSION_MIN_MACOSX:
+ Cmd = "LC_VERSION_MIN_MACOSX";
+ break;
+ case MachO::LC_VERSION_MIN_IPHONEOS:
+ Cmd = "LC_VERSION_MIN_IPHONEOS";
+ break;
+ case MachO::LC_VERSION_MIN_TVOS:
+ Cmd = "LC_VERSION_MIN_TVOS";
+ break;
+ case MachO::LC_VERSION_MIN_WATCHOS:
+ Cmd = "LC_VERSION_MIN_WATCHOS";
+ break;
+ default:
+ continue;
+ }
+
+ MachO::version_min_command VMC = Obj->getVersionMinLoadCommand(Load);
+ DictScope Group(W, "MinVersion");
+ W.printString("Cmd", Cmd);
+ W.printNumber("Size", VMC.cmdsize);
+ SmallString<32> Version;
+ Version = utostr(MachOObjectFile::getVersionMinMajor(VMC, false)) + "." +
+ utostr(MachOObjectFile::getVersionMinMinor(VMC, false));
+ uint32_t Update = MachOObjectFile::getVersionMinUpdate(VMC, false);
+ if (Update != 0)
+ Version += "." + utostr(MachOObjectFile::getVersionMinUpdate(VMC, false));
+ W.printString("Version", Version);
+ SmallString<32> SDK;
+ if (VMC.sdk == 0)
+ SDK = "n/a";
+ else {
+ SDK = utostr(MachOObjectFile::getVersionMinMajor(VMC, true)) + "." +
+ utostr(MachOObjectFile::getVersionMinMinor(VMC, true));
+ uint32_t Update = MachOObjectFile::getVersionMinUpdate(VMC, true);
+ if (Update != 0)
+ SDK += "." + utostr(MachOObjectFile::getVersionMinUpdate(VMC, true));
+ }
+ W.printString("SDK", SDK);
+ }
+}
+
+void MachODumper::printMachODysymtab() {
+ for (const auto &Load : Obj->load_commands()) {
+ if (Load.C.cmd == MachO::LC_DYSYMTAB) {
+ MachO::dysymtab_command DLC = Obj->getDysymtabLoadCommand();
+ DictScope Group(W, "Dysymtab");
+ W.printNumber("ilocalsym", DLC.ilocalsym);
+ W.printNumber("nlocalsym", DLC.nlocalsym);
+ W.printNumber("iextdefsym", DLC.iextdefsym);
+ W.printNumber("nextdefsym", DLC.nextdefsym);
+ W.printNumber("iundefsym", DLC.iundefsym);
+ W.printNumber("nundefsym", DLC.nundefsym);
+ W.printNumber("tocoff", DLC.tocoff);
+ W.printNumber("ntoc", DLC.ntoc);
+ W.printNumber("modtaboff", DLC.modtaboff);
+ W.printNumber("nmodtab", DLC.nmodtab);
+ W.printNumber("extrefsymoff", DLC.extrefsymoff);
+ W.printNumber("nextrefsyms", DLC.nextrefsyms);
+ W.printNumber("indirectsymoff", DLC.indirectsymoff);
+ W.printNumber("nindirectsyms", DLC.nindirectsyms);
+ W.printNumber("extreloff", DLC.extreloff);
+ W.printNumber("nextrel", DLC.nextrel);
+ W.printNumber("locreloff", DLC.locreloff);
+ W.printNumber("nlocrel", DLC.nlocrel);
+ }
+ }
+}
+
+void MachODumper::printMachOSegment() {
+ for (const auto &Load : Obj->load_commands()) {
+ if (Load.C.cmd == MachO::LC_SEGMENT || Load.C.cmd == MachO::LC_SEGMENT_64) {
+ MachOSegment MOSegment;
+ getSegment(Obj, Load, MOSegment);
+ DictScope Group(W, "Segment");
+ W.printString("Cmd", MOSegment.CmdName);
+ W.printString("Name", MOSegment.SegName);
+ W.printNumber("Size", MOSegment.cmdsize);
+ W.printHex("vmaddr", MOSegment.vmaddr);
+ W.printHex("vmsize", MOSegment.vmsize);
+ W.printNumber("fileoff", MOSegment.fileoff);
+ W.printNumber("filesize", MOSegment.filesize);
+ W.printString("maxprot", getMask(MOSegment.maxprot));
+ W.printString("initprot", getMask(MOSegment.initprot));
+ W.printNumber("nsects", MOSegment.nsects);
+ W.printHex("flags", MOSegment.flags);
+ }
+ }
+}
+
+void MachODumper::printMachOIndirectSymbols() {
+ for (const auto &Load : Obj->load_commands()) {
+ if (Load.C.cmd == MachO::LC_DYSYMTAB) {
+ MachO::dysymtab_command DLC = Obj->getDysymtabLoadCommand();
+ DictScope Group(W, "Indirect Symbols");
+ W.printNumber("Number", DLC.nindirectsyms);
+ ListScope D(W, "Symbols");
+ for (unsigned i = 0; i < DLC.nindirectsyms; ++i) {
+ DictScope Group(W, "Entry");
+ W.printNumber("Entry Index", i);
+ W.printHex("Symbol Index", Obj->getIndirectSymbolTableEntry(DLC, i));
+ }
+ }
+ }
+}
+
+void MachODumper::printMachOLinkerOptions() {
+ for (const auto &Load : Obj->load_commands()) {
+ if (Load.C.cmd == MachO::LC_LINKER_OPTION) {
+ MachO::linker_option_command LOLC = Obj->getLinkerOptionLoadCommand(Load);
+ DictScope Group(W, "Linker Options");
+ W.printNumber("Size", LOLC.cmdsize);
+ ListScope D(W, "Strings");
+ uint64_t DataSize = LOLC.cmdsize - sizeof(MachO::linker_option_command);
+ const char *P = Load.Ptr + sizeof(MachO::linker_option_command);
+ StringRef Data(P, DataSize);
+ for (unsigned i = 0; i < LOLC.count; ++i) {
+ std::pair<StringRef,StringRef> Split = Data.split('\0');
+ W.printString("Value", Split.first);
+ Data = Split.second;
+ }
+ }
+ }
+}
diff --git a/tools/llvm-readobj/ObjDumper.h b/tools/llvm-readobj/ObjDumper.h
index 5ecf0ec3d6fa..db26d6983552 100644
--- a/tools/llvm-readobj/ObjDumper.h
+++ b/tools/llvm-readobj/ObjDumper.h
@@ -1,4 +1,4 @@
-//===-- ObjDumper.h -------------------------------------------------------===//
+//===-- ObjDumper.h ---------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
@@ -15,7 +15,8 @@
namespace llvm {
namespace object {
- class ObjectFile;
+class COFFImportFile;
+class ObjectFile;
}
class StreamWriter;
@@ -38,6 +39,9 @@ public:
virtual void printNeededLibraries() { }
virtual void printProgramHeaders() { }
virtual void printHashTable() { }
+ virtual void printGnuHashTable() { }
+ virtual void printLoadName() {}
+ virtual void printVersionInfo() {}
// Only implemented for ARM ELF at this time.
virtual void printAttributes() { }
@@ -52,6 +56,15 @@ public:
virtual void printCOFFExports() { }
virtual void printCOFFDirectives() { }
virtual void printCOFFBaseReloc() { }
+ virtual void printCodeViewDebugInfo() { }
+
+ // Only implemented for MachO.
+ virtual void printMachODataInCode() { }
+ virtual void printMachOVersionMin() { }
+ virtual void printMachODysymtab() { }
+ virtual void printMachOSegment() { }
+ virtual void printMachOIndirectSymbols() { }
+ virtual void printMachOLinkerOptions() { }
virtual void printStackMap() const = 0;
@@ -71,6 +84,8 @@ std::error_code createMachODumper(const object::ObjectFile *Obj,
StreamWriter &Writer,
std::unique_ptr<ObjDumper> &Result);
+void dumpCOFFImportFile(const object::COFFImportFile *File);
+
} // namespace llvm
#endif
diff --git a/tools/llvm-readobj/StreamWriter.h b/tools/llvm-readobj/StreamWriter.h
index f3cc57ef940e..d2dbb07af256 100644
--- a/tools/llvm-readobj/StreamWriter.h
+++ b/tools/llvm-readobj/StreamWriter.h
@@ -34,14 +34,17 @@ struct HexNumber {
// unsigned type. The overloads are here so that every type that is implicitly
// convertible to an integer (including enums and endian helpers) can be used
// without requiring type traits or call-site changes.
- HexNumber(int8_t Value) : Value(static_cast<uint8_t >(Value)) { }
- HexNumber(int16_t Value) : Value(static_cast<uint16_t>(Value)) { }
- HexNumber(int32_t Value) : Value(static_cast<uint32_t>(Value)) { }
- HexNumber(int64_t Value) : Value(static_cast<uint64_t>(Value)) { }
- HexNumber(uint8_t Value) : Value(Value) { }
- HexNumber(uint16_t Value) : Value(Value) { }
- HexNumber(uint32_t Value) : Value(Value) { }
- HexNumber(uint64_t Value) : Value(Value) { }
+ HexNumber(char Value) : Value(static_cast<unsigned char>(Value)) { }
+ HexNumber(signed char Value) : Value(static_cast<unsigned char>(Value)) { }
+ HexNumber(signed short Value) : Value(static_cast<unsigned short>(Value)) { }
+ HexNumber(signed int Value) : Value(static_cast<unsigned int>(Value)) { }
+ HexNumber(signed long Value) : Value(static_cast<unsigned long>(Value)) { }
+ HexNumber(signed long long Value) : Value(static_cast<unsigned long long>(Value)) { }
+ HexNumber(unsigned char Value) : Value(Value) { }
+ HexNumber(unsigned short Value) : Value(Value) { }
+ HexNumber(unsigned int Value) : Value(Value) { }
+ HexNumber(unsigned long Value) : Value(Value) { }
+ HexNumber(unsigned long long Value) : Value(Value) { }
uint64_t Value;
};
@@ -194,6 +197,19 @@ public:
OS << "]\n";
}
+ template <typename T>
+ void printHexList(StringRef Label, const T &List) {
+ startLine() << Label << ": [";
+ bool Comma = false;
+ for (const auto &Item : List) {
+ if (Comma)
+ OS << ", ";
+ OS << hex(Item);
+ Comma = true;
+ }
+ OS << "]\n";
+ }
+
template<typename T>
void printHex(StringRef Label, T Value) {
startLine() << Label << ": " << hex(Value) << "\n";
diff --git a/tools/llvm-readobj/Win64EHDumper.cpp b/tools/llvm-readobj/Win64EHDumper.cpp
index f57eea20e2d9..2da5ae3200fd 100644
--- a/tools/llvm-readobj/Win64EHDumper.cpp
+++ b/tools/llvm-readobj/Win64EHDumper.cpp
@@ -149,11 +149,8 @@ static std::error_code resolveRelocation(const Dumper::Context &Ctx,
return EC;
ResolvedAddress = *ResolvedAddressOrErr;
- section_iterator SI = Ctx.COFF.section_begin();
- if (std::error_code EC = Symbol.getSection(SI))
- return EC;
-
- ResolvedSection = Ctx.COFF.getCOFFSection(*SI);
+ ErrorOr<section_iterator> SI = Symbol.getSection();
+ ResolvedSection = Ctx.COFF.getCOFFSection(**SI);
return std::error_code();
}
@@ -257,7 +254,7 @@ void Dumper::printUnwindInfo(const Context &Ctx, const coff_section *Section,
return;
}
- printUnwindCode(UI, ArrayRef<UnwindCode>(UCI, UCE));
+ printUnwindCode(UI, makeArrayRef(UCI, UCE));
UCI = UCI + UsedSlots - 1;
}
}
@@ -284,11 +281,11 @@ void Dumper::printRuntimeFunction(const Context &Ctx,
const coff_section *XData;
uint64_t Offset;
- if (error(resolveRelocation(Ctx, Section, SectionOffset + 8, XData, Offset)))
- return;
+ resolveRelocation(Ctx, Section, SectionOffset + 8, XData, Offset);
ArrayRef<uint8_t> Contents;
- if (error(Ctx.COFF.getSectionContents(XData, Contents)) || Contents.empty())
+ error(Ctx.COFF.getSectionContents(XData, Contents));
+ if (Contents.empty())
return;
Offset = Offset + RF.UnwindInfoOffset;
@@ -302,15 +299,15 @@ void Dumper::printRuntimeFunction(const Context &Ctx,
void Dumper::printData(const Context &Ctx) {
for (const auto &Section : Ctx.COFF.sections()) {
StringRef Name;
- if (error(Section.getName(Name)))
- continue;
+ Section.getName(Name);
if (Name != ".pdata" && !Name.startswith(".pdata$"))
continue;
const coff_section *PData = Ctx.COFF.getCOFFSection(Section);
ArrayRef<uint8_t> Contents;
- if (error(Ctx.COFF.getSectionContents(PData, Contents)) || Contents.empty())
+ error(Ctx.COFF.getSectionContents(PData, Contents));
+ if (Contents.empty())
continue;
const RuntimeFunction *Entries =
diff --git a/tools/llvm-readobj/llvm-readobj.cpp b/tools/llvm-readobj/llvm-readobj.cpp
index 12afacb0a858..fa8fee2b03ab 100644
--- a/tools/llvm-readobj/llvm-readobj.cpp
+++ b/tools/llvm-readobj/llvm-readobj.cpp
@@ -24,6 +24,7 @@
#include "ObjDumper.h"
#include "StreamWriter.h"
#include "llvm/Object/Archive.h"
+#include "llvm/Object/COFFImportFile.h"
#include "llvm/Object/ELFObjectFile.h"
#include "llvm/Object/MachOUniversal.h"
#include "llvm/Object/ObjectFile.h"
@@ -131,6 +132,10 @@ namespace opts {
cl::opt<bool> HashTable("hash-table",
cl::desc("Display ELF hash table"));
+ // -gnu-hash-table
+ cl::opt<bool> GnuHashTable("gnu-hash-table",
+ cl::desc("Display ELF .gnu.hash section"));
+
// -expand-relocs
cl::opt<bool> ExpandRelocs("expand-relocs",
cl::desc("Expand each shown relocation to multiple lines"));
@@ -181,25 +186,62 @@ namespace opts {
COFFBaseRelocs("coff-basereloc",
cl::desc("Display the PE/COFF .reloc section"));
+ // -macho-data-in-code
+ cl::opt<bool>
+ MachODataInCode("macho-data-in-code",
+ cl::desc("Display MachO Data in Code command"));
+
+ // -macho-indirect-symbols
+ cl::opt<bool>
+ MachOIndirectSymbols("macho-indirect-symbols",
+ cl::desc("Display MachO indirect symbols"));
+
+ // -macho-linker-options
+ cl::opt<bool>
+ MachOLinkerOptions("macho-linker-options",
+ cl::desc("Display MachO linker options"));
+
+ // -macho-segment
+ cl::opt<bool>
+ MachOSegment("macho-segment",
+ cl::desc("Display MachO Segment command"));
+
+ // -macho-version-min
+ cl::opt<bool>
+ MachOVersionMin("macho-version-min",
+ cl::desc("Display MachO version min command"));
+
+ // -macho-dysymtab
+ cl::opt<bool>
+ MachODysymtab("macho-dysymtab",
+ cl::desc("Display MachO Dysymtab command"));
+
// -stackmap
cl::opt<bool>
PrintStackMap("stackmap",
cl::desc("Display contents of stackmap section"));
+ // -version-info
+ cl::opt<bool>
+ VersionInfo("version-info",
+ cl::desc("Display ELF version sections (if present)"));
+ cl::alias VersionInfoShort("V", cl::desc("Alias for -version-info"),
+ cl::aliasopt(VersionInfo));
} // namespace opts
-static int ReturnValue = EXIT_SUCCESS;
-
namespace llvm {
-bool error(std::error_code EC) {
+LLVM_ATTRIBUTE_NORETURN void reportError(Twine Msg) {
+ errs() << "\nError reading file: " << Msg << ".\n";
+ errs().flush();
+ exit(1);
+}
+
+void error(std::error_code EC) {
if (!EC)
- return false;
+ return;
- ReturnValue = EXIT_FAILURE;
- outs() << "\nError reading file: " << EC.message() << ".\n";
- outs().flush();
- return true;
+ reportError(EC.message());
}
bool relocAddressLess(RelocationRef a, RelocationRef b) {
@@ -212,17 +254,14 @@ static void reportError(StringRef Input, std::error_code EC) {
if (Input == "-")
Input = "<stdin>";
- errs() << Input << ": " << EC.message() << "\n";
- errs().flush();
- ReturnValue = EXIT_FAILURE;
+ reportError(Twine(Input) + ": " + EC.message());
}
static void reportError(StringRef Input, StringRef Message) {
if (Input == "-")
Input = "<stdin>";
- errs() << Input << ": " << Message << "\n";
- ReturnValue = EXIT_FAILURE;
+ reportError(Twine(Input) + ": " + Message);
}
static bool isMipsArch(unsigned Arch) {
@@ -253,26 +292,12 @@ static std::error_code createDumper(const ObjectFile *Obj, StreamWriter &Writer,
return readobj_error::unsupported_obj_file_format;
}
-static StringRef getLoadName(const ObjectFile *Obj) {
- if (auto *ELF = dyn_cast<ELF32LEObjectFile>(Obj))
- return ELF->getLoadName();
- if (auto *ELF = dyn_cast<ELF64LEObjectFile>(Obj))
- return ELF->getLoadName();
- if (auto *ELF = dyn_cast<ELF32BEObjectFile>(Obj))
- return ELF->getLoadName();
- if (auto *ELF = dyn_cast<ELF64BEObjectFile>(Obj))
- return ELF->getLoadName();
- llvm_unreachable("Not ELF");
-}
-
/// @brief Dumps the specified object file.
static void dumpObject(const ObjectFile *Obj) {
StreamWriter Writer(outs());
std::unique_ptr<ObjDumper> Dumper;
- if (std::error_code EC = createDumper(Obj, Writer, Dumper)) {
+ if (std::error_code EC = createDumper(Obj, Writer, Dumper))
reportError(Obj->getFileName(), EC);
- return;
- }
outs() << '\n';
outs() << "File: " << Obj->getFileName() << "\n";
@@ -281,8 +306,7 @@ static void dumpObject(const ObjectFile *Obj) {
<< Triple::getArchTypeName((llvm::Triple::ArchType)Obj->getArch())
<< "\n";
outs() << "AddressSize: " << (8*Obj->getBytesInAddress()) << "bit\n";
- if (Obj->isELF())
- outs() << "LoadName: " << getLoadName(Obj) << "\n";
+ Dumper->printLoadName();
if (opts::FileHeaders)
Dumper->printFileHeaders();
@@ -306,6 +330,10 @@ static void dumpObject(const ObjectFile *Obj) {
Dumper->printProgramHeaders();
if (opts::HashTable)
Dumper->printHashTable();
+ if (opts::GnuHashTable)
+ Dumper->printGnuHashTable();
+ if (opts::VersionInfo)
+ Dumper->printVersionInfo();
if (Obj->getArch() == llvm::Triple::arm && Obj->isELF())
if (opts::ARMAttributes)
Dumper->printAttributes();
@@ -317,25 +345,43 @@ static void dumpObject(const ObjectFile *Obj) {
if (opts::MipsReginfo)
Dumper->printMipsReginfo();
}
- if (opts::COFFImports)
- Dumper->printCOFFImports();
- if (opts::COFFExports)
- Dumper->printCOFFExports();
- if (opts::COFFDirectives)
- Dumper->printCOFFDirectives();
- if (opts::COFFBaseRelocs)
- Dumper->printCOFFBaseReloc();
-
+ if (Obj->isCOFF()) {
+ if (opts::COFFImports)
+ Dumper->printCOFFImports();
+ if (opts::COFFExports)
+ Dumper->printCOFFExports();
+ if (opts::COFFDirectives)
+ Dumper->printCOFFDirectives();
+ if (opts::COFFBaseRelocs)
+ Dumper->printCOFFBaseReloc();
+ if (opts::CodeView)
+ Dumper->printCodeViewDebugInfo();
+ }
+ if (Obj->isMachO()) {
+ if (opts::MachODataInCode)
+ Dumper->printMachODataInCode();
+ if (opts::MachOIndirectSymbols)
+ Dumper->printMachOIndirectSymbols();
+ if (opts::MachOLinkerOptions)
+ Dumper->printMachOLinkerOptions();
+ if (opts::MachOSegment)
+ Dumper->printMachOSegment();
+ if (opts::MachOVersionMin)
+ Dumper->printMachOVersionMin();
+ if (opts::MachODysymtab)
+ Dumper->printMachODysymtab();
+ }
if (opts::PrintStackMap)
Dumper->printStackMap();
}
/// @brief Dumps each object file in \a Arc;
static void dumpArchive(const Archive *Arc) {
- for (Archive::child_iterator ArcI = Arc->child_begin(),
- ArcE = Arc->child_end();
- ArcI != ArcE; ++ArcI) {
- ErrorOr<std::unique_ptr<Binary>> ChildOrErr = ArcI->getAsBinary();
+ for (auto &ErrorOrChild : Arc->children()) {
+ if (std::error_code EC = ErrorOrChild.getError())
+ reportError(Arc->getFileName(), EC.message());
+ const auto &Child = *ErrorOrChild;
+ ErrorOr<std::unique_ptr<Binary>> ChildOrErr = Child.getAsBinary();
if (std::error_code EC = ChildOrErr.getError()) {
// Ignore non-object files.
if (EC != object_error::invalid_file_type)
@@ -365,18 +411,11 @@ static void dumpMachOUniversalBinary(const MachOUniversalBinary *UBinary) {
/// @brief Opens \a File and dumps it.
static void dumpInput(StringRef File) {
- // If file isn't stdin, check that it exists.
- if (File != "-" && !sys::fs::exists(File)) {
- reportError(File, readobj_error::file_not_found);
- return;
- }
// Attempt to open the binary.
ErrorOr<OwningBinary<Binary>> BinaryOrErr = createBinary(File);
- if (std::error_code EC = BinaryOrErr.getError()) {
+ if (std::error_code EC = BinaryOrErr.getError())
reportError(File, EC);
- return;
- }
Binary &Binary = *BinaryOrErr.get().getBinary();
if (Archive *Arc = dyn_cast<Archive>(&Binary))
@@ -386,6 +425,8 @@ static void dumpInput(StringRef File) {
dumpMachOUniversalBinary(UBinary);
else if (ObjectFile *Obj = dyn_cast<ObjectFile>(&Binary))
dumpObject(Obj);
+ else if (COFFImportFile *Import = dyn_cast<COFFImportFile>(&Binary))
+ dumpCOFFImportFile(Import);
else
reportError(File, readobj_error::unrecognized_file_format);
}
@@ -407,5 +448,5 @@ int main(int argc, const char *argv[]) {
std::for_each(opts::InputFilenames.begin(), opts::InputFilenames.end(),
dumpInput);
- return ReturnValue;
+ return 0;
}
diff --git a/tools/llvm-readobj/llvm-readobj.h b/tools/llvm-readobj/llvm-readobj.h
index 74b9a60d34b2..5a103920c165 100644
--- a/tools/llvm-readobj/llvm-readobj.h
+++ b/tools/llvm-readobj/llvm-readobj.h
@@ -11,6 +11,7 @@
#define LLVM_TOOLS_LLVM_READOBJ_LLVM_READOBJ_H
#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Compiler.h"
#include <string>
namespace llvm {
@@ -19,7 +20,8 @@ namespace llvm {
}
// Various helper functions.
- bool error(std::error_code ec);
+ LLVM_ATTRIBUTE_NORETURN void reportError(Twine Msg);
+ void error(std::error_code ec);
bool relocAddressLess(object::RelocationRef A,
object::RelocationRef B);
} // namespace llvm
diff --git a/tools/llvm-rtdyld/llvm-rtdyld.cpp b/tools/llvm-rtdyld/llvm-rtdyld.cpp
index 86f66f89b159..6ee3a44b63bf 100644
--- a/tools/llvm-rtdyld/llvm-rtdyld.cpp
+++ b/tools/llvm-rtdyld/llvm-rtdyld.cpp
@@ -94,6 +94,11 @@ CheckFiles("check",
cl::ZeroOrMore);
static cl::opt<uint64_t>
+PreallocMemory("preallocate",
+ cl::desc("Allocate memory upfront rather than on-demand"),
+ cl::init(0));
+
+static cl::opt<uint64_t>
TargetAddrStart("target-addr-start",
cl::desc("For -verify only: start of phony target address "
"range."),
@@ -127,6 +132,12 @@ DummySymbolMappings("dummy-extern",
cl::ZeroOrMore,
cl::Hidden);
+static cl::opt<bool>
+PrintAllocationRequests("print-alloc-requests",
+ cl::desc("Print allocation requests made to the memory "
+ "manager by RuntimeDyld"),
+ cl::Hidden);
+
/* *** */
// A trivial memory manager that doesn't do anything fancy, just uses the
@@ -150,12 +161,6 @@ public:
bool finalizeMemory(std::string *ErrMsg) override { return false; }
- // Invalidate instruction cache for sections with execute permissions.
- // Some platforms with separate data cache and instruction cache require
- // explicit cache flush, otherwise JIT code manipulations (like resolved
- // relocations) will get to the data cache but not to the instruction cache.
- virtual void invalidateInstructionCache();
-
void addDummySymbol(const std::string &Name, uint64_t Addr) {
DummyExterns[Name] = Addr;
}
@@ -173,15 +178,56 @@ public:
size_t Size) override {}
void deregisterEHFrames(uint8_t *Addr, uint64_t LoadAddr,
size_t Size) override {}
+
+ void preallocateSlab(uint64_t Size) {
+ std::string Err;
+ sys::MemoryBlock MB = sys::Memory::AllocateRWX(Size, nullptr, &Err);
+ if (!MB.base())
+ report_fatal_error("Can't allocate enough memory: " + Err);
+
+ PreallocSlab = MB;
+ UsePreallocation = true;
+ SlabSize = Size;
+ }
+
+ uint8_t *allocateFromSlab(uintptr_t Size, unsigned Alignment, bool isCode) {
+ Size = RoundUpToAlignment(Size, Alignment);
+ if (CurrentSlabOffset + Size > SlabSize)
+ report_fatal_error("Can't allocate enough memory. Tune --preallocate");
+
+ uintptr_t OldSlabOffset = CurrentSlabOffset;
+ sys::MemoryBlock MB((void *)OldSlabOffset, Size);
+ if (isCode)
+ FunctionMemory.push_back(MB);
+ else
+ DataMemory.push_back(MB);
+ CurrentSlabOffset += Size;
+ return (uint8_t*)OldSlabOffset;
+ }
+
private:
std::map<std::string, uint64_t> DummyExterns;
+ sys::MemoryBlock PreallocSlab;
+ bool UsePreallocation = false;
+ uintptr_t SlabSize = 0;
+ uintptr_t CurrentSlabOffset = 0;
};
uint8_t *TrivialMemoryManager::allocateCodeSection(uintptr_t Size,
unsigned Alignment,
unsigned SectionID,
StringRef SectionName) {
- sys::MemoryBlock MB = sys::Memory::AllocateRWX(Size, nullptr, nullptr);
+ if (PrintAllocationRequests)
+ outs() << "allocateCodeSection(Size = " << Size << ", Alignment = "
+ << Alignment << ", SectionName = " << SectionName << ")\n";
+
+ if (UsePreallocation)
+ return allocateFromSlab(Size, Alignment, true /* isCode */);
+
+ std::string Err;
+ sys::MemoryBlock MB = sys::Memory::AllocateRWX(Size, nullptr, &Err);
+ if (!MB.base())
+ report_fatal_error("MemoryManager allocation failed: " + Err);
FunctionMemory.push_back(MB);
return (uint8_t*)MB.base();
}
@@ -191,41 +237,35 @@ uint8_t *TrivialMemoryManager::allocateDataSection(uintptr_t Size,
unsigned SectionID,
StringRef SectionName,
bool IsReadOnly) {
- sys::MemoryBlock MB = sys::Memory::AllocateRWX(Size, nullptr, nullptr);
- DataMemory.push_back(MB);
- return (uint8_t*)MB.base();
-}
+ if (PrintAllocationRequests)
+ outs() << "allocateDataSection(Size = " << Size << ", Alignment = "
+ << Alignment << ", SectionName = " << SectionName << ")\n";
-void TrivialMemoryManager::invalidateInstructionCache() {
- for (int i = 0, e = FunctionMemory.size(); i != e; ++i)
- sys::Memory::InvalidateInstructionCache(FunctionMemory[i].base(),
- FunctionMemory[i].size());
+ if (UsePreallocation)
+ return allocateFromSlab(Size, Alignment, false /* isCode */);
- for (int i = 0, e = DataMemory.size(); i != e; ++i)
- sys::Memory::InvalidateInstructionCache(DataMemory[i].base(),
- DataMemory[i].size());
+ std::string Err;
+ sys::MemoryBlock MB = sys::Memory::AllocateRWX(Size, nullptr, &Err);
+ if (!MB.base())
+ report_fatal_error("MemoryManager allocation failed: " + Err);
+ DataMemory.push_back(MB);
+ return (uint8_t*)MB.base();
}
static const char *ProgramName;
-static void Message(const char *Type, const Twine &Msg) {
- errs() << ProgramName << ": " << Type << ": " << Msg << "\n";
-}
-
static int Error(const Twine &Msg) {
- Message("error", Msg);
+ errs() << ProgramName << ": error: " << Msg << "\n";
return 1;
}
static void loadDylibs() {
for (const std::string &Dylib : Dylibs) {
- if (sys::fs::is_regular_file(Dylib)) {
- std::string ErrMsg;
- if (sys::DynamicLibrary::LoadLibraryPermanently(Dylib.c_str(), &ErrMsg))
- llvm::errs() << "Error loading '" << Dylib << "': "
- << ErrMsg << "\n";
- } else
- llvm::errs() << "Dylib not found: '" << Dylib << "'.\n";
+ if (!sys::fs::is_regular_file(Dylib))
+ report_fatal_error("Dylib not found: '" + Dylib + "'.");
+ std::string ErrMsg;
+ if (sys::DynamicLibrary::LoadLibraryPermanently(Dylib.c_str(), &ErrMsg))
+ report_fatal_error("Error loading '" + Dylib + "': " + ErrMsg);
}
}
@@ -240,7 +280,7 @@ static int printLineInfoForInput(bool LoadObjects, bool UseDebugObj) {
// If we don't have any input files, read from stdin.
if (!InputFileList.size())
InputFileList.push_back("-");
- for(unsigned i = 0, e = InputFileList.size(); i != e; ++i) {
+ for (auto &File : InputFileList) {
// Instantiate a dynamic linker.
TrivialMemoryManager MemMgr;
RuntimeDyld Dyld(MemMgr, MemMgr);
@@ -248,7 +288,7 @@ static int printLineInfoForInput(bool LoadObjects, bool UseDebugObj) {
// Load the input memory buffer.
ErrorOr<std::unique_ptr<MemoryBuffer>> InputBuffer =
- MemoryBuffer::getFileOrSTDIN(InputFileList[i]);
+ MemoryBuffer::getFileOrSTDIN(File);
if (std::error_code EC = InputBuffer.getError())
return Error("unable to read input: '" + EC.message() + "'");
@@ -277,6 +317,7 @@ static int printLineInfoForInput(bool LoadObjects, bool UseDebugObj) {
if (UseDebugObj) {
DebugObj = LoadedObjInfo->getObjectForDebug(Obj);
SymbolObj = DebugObj.getBinary();
+ LoadedObjInfo.reset();
}
}
@@ -303,12 +344,11 @@ static int printLineInfoForInput(bool LoadObjects, bool UseDebugObj) {
// symbol in memory (rather than that in the unrelocated object file)
// and use that to query the DWARFContext.
if (!UseDebugObj && LoadObjects) {
- object::section_iterator Sec(SymbolObj->section_end());
- Sym.getSection(Sec);
+ object::section_iterator Sec = *Sym.getSection();
StringRef SecName;
Sec->getName(SecName);
uint64_t SectionLoadAddress =
- LoadedObjInfo->getSectionLoadAddress(SecName);
+ LoadedObjInfo->getSectionLoadAddress(*Sec);
if (SectionLoadAddress != 0)
Addr += SectionLoadAddress - Sec->getAddress();
}
@@ -317,11 +357,9 @@ static int printLineInfoForInput(bool LoadObjects, bool UseDebugObj) {
<< ", Addr = " << Addr << "\n";
DILineInfoTable Lines = Context->getLineInfoForAddressRange(Addr, Size);
- DILineInfoTable::iterator Begin = Lines.begin();
- DILineInfoTable::iterator End = Lines.end();
- for (DILineInfoTable::iterator It = Begin; It != End; ++It) {
- outs() << " Line info @ " << It->first - Addr << ": "
- << It->second.FileName << ", line:" << It->second.Line << "\n";
+ for (auto &D : Lines) {
+ outs() << " Line info @ " << D.first - Addr << ": "
+ << D.second.FileName << ", line:" << D.second.Line << "\n";
}
}
}
@@ -330,26 +368,33 @@ static int printLineInfoForInput(bool LoadObjects, bool UseDebugObj) {
return 0;
}
+static void doPreallocation(TrivialMemoryManager &MemMgr) {
+ // Allocate a slab of memory upfront, if required. This is used if
+ // we want to test small code models.
+ if (static_cast<intptr_t>(PreallocMemory) < 0)
+ report_fatal_error("Pre-allocated bytes of memory must be a positive integer.");
+
+ // FIXME: Limit the amount of memory that can be preallocated?
+ if (PreallocMemory != 0)
+ MemMgr.preallocateSlab(PreallocMemory);
+}
+
static int executeInput() {
// Load any dylibs requested on the command line.
loadDylibs();
// Instantiate a dynamic linker.
TrivialMemoryManager MemMgr;
+ doPreallocation(MemMgr);
RuntimeDyld Dyld(MemMgr, MemMgr);
- // FIXME: Preserve buffers until resolveRelocations time to work around a bug
- // in RuntimeDyldELF.
- // This fixme should be fixed ASAP. This is a very brittle workaround.
- std::vector<std::unique_ptr<MemoryBuffer>> InputBuffers;
-
// If we don't have any input files, read from stdin.
if (!InputFileList.size())
InputFileList.push_back("-");
- for(unsigned i = 0, e = InputFileList.size(); i != e; ++i) {
+ for (auto &File : InputFileList) {
// Load the input memory buffer.
ErrorOr<std::unique_ptr<MemoryBuffer>> InputBuffer =
- MemoryBuffer::getFileOrSTDIN(InputFileList[i]);
+ MemoryBuffer::getFileOrSTDIN(File);
if (std::error_code EC = InputBuffer.getError())
return Error("unable to read input: '" + EC.message() + "'");
ErrorOr<std::unique_ptr<ObjectFile>> MaybeObj(
@@ -359,7 +404,6 @@ static int executeInput() {
return Error("unable to create object file: '" + EC.message() + "'");
ObjectFile &Obj = **MaybeObj;
- InputBuffers.push_back(std::move(*InputBuffer));
// Load the object file
Dyld.loadObject(Obj);
@@ -368,12 +412,9 @@ static int executeInput() {
}
}
- // Resolve all the relocations we can.
- Dyld.resolveRelocations();
- // Clear instruction cache before code will be executed.
- MemMgr.invalidateInstructionCache();
-
+ // Resove all the relocations we can.
// FIXME: Error out if there are unresolved relocations.
+ Dyld.resolveRelocations();
// Get the address of the entry point (_main by default).
void *MainAddress = Dyld.getSymbolLocalAddress(EntryPoint);
@@ -381,12 +422,12 @@ static int executeInput() {
return Error("no definition for '" + EntryPoint + "'");
// Invalidate the instruction cache for each loaded function.
- for (unsigned i = 0, e = MemMgr.FunctionMemory.size(); i != e; ++i) {
- sys::MemoryBlock &Data = MemMgr.FunctionMemory[i];
+ for (auto &FM : MemMgr.FunctionMemory) {
+
// Make sure the memory is executable.
+ // setExecutable will call InvalidateInstructionCache.
std::string ErrorStr;
- sys::Memory::InvalidateInstructionCache(Data.base(), Data.size());
- if (!sys::Memory::setExecutable(Data, &ErrorStr))
+ if (!sys::Memory::setExecutable(FM, &ErrorStr))
return Error("unable to mark function executable: '" + ErrorStr + "'");
}
@@ -428,11 +469,9 @@ applySpecificSectionMappings(RuntimeDyldChecker &Checker) {
std::string SectionIDStr = Mapping.substr(0, EqualsIdx);
size_t ComaIdx = Mapping.find_first_of(",");
- if (ComaIdx == StringRef::npos) {
- errs() << "Invalid section specification '" << Mapping
- << "'. Should be '<file name>,<section name>=<addr>'\n";
- exit(1);
- }
+ if (ComaIdx == StringRef::npos)
+ report_fatal_error("Invalid section specification '" + Mapping +
+ "'. Should be '<file name>,<section name>=<addr>'");
std::string FileName = SectionIDStr.substr(0, ComaIdx);
std::string SectionName = SectionIDStr.substr(ComaIdx + 1);
@@ -442,20 +481,17 @@ applySpecificSectionMappings(RuntimeDyldChecker &Checker) {
std::tie(OldAddrInt, ErrorMsg) =
Checker.getSectionAddr(FileName, SectionName, true);
- if (ErrorMsg != "") {
- errs() << ErrorMsg;
- exit(1);
- }
+ if (ErrorMsg != "")
+ report_fatal_error(ErrorMsg);
void* OldAddr = reinterpret_cast<void*>(static_cast<uintptr_t>(OldAddrInt));
std::string NewAddrStr = Mapping.substr(EqualsIdx + 1);
uint64_t NewAddr;
- if (StringRef(NewAddrStr).getAsInteger(0, NewAddr)) {
- errs() << "Invalid section address in mapping '" << Mapping << "'.\n";
- exit(1);
- }
+ if (StringRef(NewAddrStr).getAsInteger(0, NewAddr))
+ report_fatal_error("Invalid section address in mapping '" + Mapping +
+ "'.");
Checker.getRTDyld().mapSectionAddress(OldAddr, NewAddr);
SpecificMappings[OldAddr] = NewAddr;
@@ -544,20 +580,16 @@ static void remapSectionsAndSymbols(const llvm::Triple &TargetTriple,
for (const auto &Mapping : DummySymbolMappings) {
size_t EqualsIdx = Mapping.find_first_of("=");
- if (EqualsIdx == StringRef::npos) {
- errs() << "Invalid dummy symbol specification '" << Mapping
- << "'. Should be '<symbol name>=<addr>'\n";
- exit(1);
- }
+ if (EqualsIdx == StringRef::npos)
+ report_fatal_error("Invalid dummy symbol specification '" + Mapping +
+ "'. Should be '<symbol name>=<addr>'");
std::string Symbol = Mapping.substr(0, EqualsIdx);
std::string AddrStr = Mapping.substr(EqualsIdx + 1);
uint64_t Addr;
- if (StringRef(AddrStr).getAsInteger(0, Addr)) {
- errs() << "Invalid symbol mapping '" << Mapping << "'.\n";
- exit(1);
- }
+ if (StringRef(AddrStr).getAsInteger(0, Addr))
+ report_fatal_error("Invalid symbol mapping '" + Mapping + "'.");
MemMgr.addDummySymbol(Symbol, Addr);
}
@@ -569,38 +601,38 @@ static void remapSectionsAndSymbols(const llvm::Triple &TargetTriple,
static int linkAndVerify() {
// Check for missing triple.
- if (TripleName == "") {
- llvm::errs() << "Error: -triple required when running in -verify mode.\n";
- return 1;
- }
+ if (TripleName == "")
+ return Error("-triple required when running in -verify mode.");
// Look up the target and build the disassembler.
Triple TheTriple(Triple::normalize(TripleName));
std::string ErrorStr;
const Target *TheTarget =
TargetRegistry::lookupTarget("", TheTriple, ErrorStr);
- if (!TheTarget) {
- llvm::errs() << "Error accessing target '" << TripleName << "': "
- << ErrorStr << "\n";
- return 1;
- }
+ if (!TheTarget)
+ return Error("Error accessing target '" + TripleName + "': " + ErrorStr);
+
TripleName = TheTriple.getTriple();
std::unique_ptr<MCSubtargetInfo> STI(
TheTarget->createMCSubtargetInfo(TripleName, MCPU, ""));
- assert(STI && "Unable to create subtarget info!");
+ if (!STI)
+ return Error("Unable to create subtarget info!");
std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TripleName));
- assert(MRI && "Unable to create target register info!");
+ if (!MRI)
+ return Error("Unable to create target register info!");
std::unique_ptr<MCAsmInfo> MAI(TheTarget->createMCAsmInfo(*MRI, TripleName));
- assert(MAI && "Unable to create target asm info!");
+ if (!MAI)
+ return Error("Unable to create target asm info!");
MCContext Ctx(MAI.get(), MRI.get(), nullptr);
std::unique_ptr<MCDisassembler> Disassembler(
TheTarget->createMCDisassembler(*STI, Ctx));
- assert(Disassembler && "Unable to create disassembler!");
+ if (!Disassembler)
+ return Error("Unable to create disassembler!");
std::unique_ptr<MCInstrInfo> MII(TheTarget->createMCInstrInfo());
@@ -612,23 +644,19 @@ static int linkAndVerify() {
// Instantiate a dynamic linker.
TrivialMemoryManager MemMgr;
+ doPreallocation(MemMgr);
RuntimeDyld Dyld(MemMgr, MemMgr);
Dyld.setProcessAllSections(true);
RuntimeDyldChecker Checker(Dyld, Disassembler.get(), InstPrinter.get(),
llvm::dbgs());
- // FIXME: Preserve buffers until resolveRelocations time to work around a bug
- // in RuntimeDyldELF.
- // This fixme should be fixed ASAP. This is a very brittle workaround.
- std::vector<std::unique_ptr<MemoryBuffer>> InputBuffers;
-
// If we don't have any input files, read from stdin.
if (!InputFileList.size())
InputFileList.push_back("-");
- for(unsigned i = 0, e = InputFileList.size(); i != e; ++i) {
+ for (auto &Filename : InputFileList) {
// Load the input memory buffer.
ErrorOr<std::unique_ptr<MemoryBuffer>> InputBuffer =
- MemoryBuffer::getFileOrSTDIN(InputFileList[i]);
+ MemoryBuffer::getFileOrSTDIN(Filename);
if (std::error_code EC = InputBuffer.getError())
return Error("unable to read input: '" + EC.message() + "'");
@@ -640,7 +668,6 @@ static int linkAndVerify() {
return Error("unable to create object file: '" + EC.message() + "'");
ObjectFile &Obj = **MaybeObj;
- InputBuffers.push_back(std::move(*InputBuffer));
// Load the object file
Dyld.loadObject(Obj);
@@ -660,11 +687,9 @@ static int linkAndVerify() {
Dyld.registerEHFrames();
int ErrorCode = checkAllExpressions(Checker);
- if (Dyld.hasError()) {
- errs() << "RTDyld reported an error applying relocations:\n "
- << Dyld.getErrorString() << "\n";
- ErrorCode = 1;
- }
+ if (Dyld.hasError())
+ return Error("RTDyld reported an error applying relocations:\n " +
+ Dyld.getErrorString());
return ErrorCode;
}
diff --git a/tools/llvm-shlib/CMakeLists.txt b/tools/llvm-shlib/CMakeLists.txt
index 54d71d3f6320..2356103a9cd5 100644
--- a/tools/llvm-shlib/CMakeLists.txt
+++ b/tools/llvm-shlib/CMakeLists.txt
@@ -2,42 +2,6 @@
# library is enabled by setting LLVM_BUILD_LLVM_DYLIB=yes on the CMake
# commandline. By default the shared library only exports the LLVM C API.
-
-# You can configure which libraries from LLVM you want to include in the shared
-# library by setting LLVM_DYLIB_COMPONENTS to a semi-colon delimited list of
-# LLVM components. All compoenent names handled by llvm-config are valid.
-
-if(NOT DEFINED LLVM_DYLIB_COMPONENTS)
- set(LLVM_DYLIB_COMPONENTS
- ${LLVM_TARGETS_TO_BUILD}
- Analysis
- BitReader
- BitWriter
- CodeGen
- Core
- DebugInfoDWARF
- DebugInfoPDB
- ExecutionEngine
- IPA
- IPO
- IRReader
- InstCombine
- Instrumentation
- Interpreter
- Linker
- MCDisassembler
- MCJIT
- ObjCARCOpts
- Object
- ScalarOpts
- Support
- Target
- TransformUtils
- Vectorize
- native
- )
-endif()
-
add_definitions( -DLLVM_VERSION_INFO=\"${PACKAGE_VERSION}\" )
set(SOURCES
@@ -46,56 +10,35 @@ set(SOURCES
llvm_map_components_to_libnames(LIB_NAMES ${LLVM_DYLIB_COMPONENTS})
-if(NOT DEFINED LLVM_DYLIB_EXPORTED_SYMBOL_FILE)
-
- if( WIN32 AND NOT CYGWIN )
- message(FATAL_ERROR "Auto-generation not implemented for Win32 without GNU utils. Please specify LLVM_EXPORTED_SYMBOL_FILE.")
+if(LLVM_LINK_LLVM_DYLIB)
+ if(LLVM_DYLIB_EXPORTED_SYMBOL_FILE)
+ message(WARNING "Using LLVM_LINK_LLVM_DYLIB with LLVM_DYLIB_EXPORTED_SYMBOL_FILE may not work. Use at your own risk.")
endif()
- # To get the export list for a single llvm library:
- # nm ${LIB_PATH} | awk "/T _LLVM/ { print $3 }" | sort -u | sed -e "s/^_//g" > ${LIB_PATH}.exports
-
- set(LLVM_EXPORTED_SYMBOL_FILE ${CMAKE_BINARY_DIR}/libllvm.exports)
-
- if (NOT LLVM_DYLIB_EXPORT_ALL)
- foreach (lib ${LIB_NAMES})
- set(LIB_DIR ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib${LLVM_LIBDIR_SUFFIX})
- set(LIB_NAME ${LIB_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}${lib})
- set(LIB_PATH ${LIB_NAME}${CMAKE_STATIC_LIBRARY_SUFFIX})
- set(LIB_EXPORTS_PATH ${LIB_NAME}.exports)
- list(APPEND LLVM_DYLIB_REQUIRED_EXPORTS ${LIB_EXPORTS_PATH})
-
-
- add_custom_command(OUTPUT ${LIB_EXPORTS_PATH}
- COMMAND nm ${LIB_PATH} | awk "/T _LLVM/ || /T LLVM/ { print $3 }" | sort -u | sed -e "s/^_//g" > ${LIB_EXPORTS_PATH}
- WORKING_DIRECTORY ${LIB_DIR}
- DEPENDS ${lib}
- COMMENT "Generating Export list for ${lib}..."
- VERBATIM )
- endforeach ()
- endif()
-
- if (LLVM_DYLIB_EXPORT_ALL)
- add_custom_command(OUTPUT ${LLVM_EXPORTED_SYMBOL_FILE}
- COMMAND echo \"LLVM*\" > ${LLVM_EXPORTED_SYMBOL_FILE} && echo \"_Z*llvm*\" >> ${LLVM_EXPORTED_SYMBOL_FILE}
- WORKING_DIRECTORY ${LIB_DIR}
- DEPENDS ${LLVM_DYLIB_REQUIRED_EXPORTS}
- COMMENT "Generating combined export list...")
- else()
- add_custom_command(OUTPUT ${LLVM_EXPORTED_SYMBOL_FILE}
- COMMAND cat ${LLVM_DYLIB_REQUIRED_EXPORTS} > ${LLVM_EXPORTED_SYMBOL_FILE}
- WORKING_DIRECTORY ${LIB_DIR}
- DEPENDS ${LLVM_DYLIB_REQUIRED_EXPORTS}
- COMMENT "Generating combined export list...")
- endif()
+ # libLLVM.so should not have any dependencies on any other LLVM
+ # shared libraries. When using the "all" pseudo-component,
+ # LLVM_AVAILABLE_LIBS is added to the dependencies, which may
+ # contain shared libraries (e.g. libLTO).
+ #
+ # Also exclude libLLVMTableGen for the following reasons:
+ # - it is only used by internal *-tblgen utilities;
+ # - it pollutes the global options space.
+ foreach(lib ${LIB_NAMES})
+ get_target_property(t ${lib} TYPE)
+ if("${lib}" STREQUAL "LLVMTableGen")
+ elseif("x${t}" STREQUAL "xSTATIC_LIBRARY")
+ list(APPEND FILTERED_LIB_NAMES ${lib})
+ endif()
+ endforeach()
+ set(LIB_NAMES ${FILTERED_LIB_NAMES})
+endif()
- add_custom_target(libLLVMExports DEPENDS ${LLVM_EXPORTED_SYMBOL_FILE})
-else()
+if(LLVM_DYLIB_EXPORTED_SYMBOL_FILE)
set(LLVM_EXPORTED_SYMBOL_FILE ${LLVM_DYLIB_EXPORTED_SYMBOL_FILE})
add_custom_target(libLLVMExports DEPENDS ${LLVM_EXPORTED_SYMBOL_FILE})
endif()
-add_llvm_library(LLVM SHARED ${SOURCES})
+add_llvm_library(LLVM SHARED DISABLE_LLVM_LINK_LLVM_DYLIB SONAME ${SOURCES})
list(REMOVE_DUPLICATES LIB_NAMES)
if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") # FIXME: It should be "GNU ld for elf"
@@ -107,11 +50,48 @@ endif()
target_link_libraries(LLVM PRIVATE ${LIB_NAMES})
-add_dependencies(LLVM libLLVMExports)
-
if (APPLE)
set_property(TARGET LLVM APPEND_STRING PROPERTY
LINK_FLAGS
- " -compatibility_version ${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR} -current_version ${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}")
+ " -compatibility_version 1 -current_version ${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}.${LLVM_VERSION_PATCH}")
+endif()
+
+if(TARGET libLLVMExports)
+ add_dependencies(LLVM libLLVMExports)
+endif()
+
+if(LLVM_BUILD_LLVM_C_DYLIB)
+ # To get the export list for a single llvm library:
+ # nm ${LIB_PATH} | awk "/T _LLVM/ { print $3 }" | sort -u | sed -e "s/^_//g" > ${LIB_PATH}.exports
+
+ if(NOT APPLE)
+ message(FATAL_ERROR "Generating libLLVM-c is only supported on Darwin")
+ endif()
+
+ set(LLVM_EXPORTED_SYMBOL_FILE ${CMAKE_BINARY_DIR}/libllvm-c.exports)
+
+ set(LIB_DIR ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib${LLVM_LIBDIR_SUFFIX})
+ set(LIB_NAME ${LIB_DIR}/${CMAKE_SHARED_LIBRARY_PREFIX}LLVM)
+ set(LIB_PATH ${LIB_NAME}${CMAKE_SHARED_LIBRARY_SUFFIX})
+ set(LIB_EXPORTS_PATH ${LIB_NAME}.exports)
+ list(APPEND LLVM_DYLIB_REQUIRED_EXPORTS ${LIB_EXPORTS_PATH})
+
+ add_custom_command(OUTPUT ${LLVM_EXPORTED_SYMBOL_FILE}
+ COMMAND nm ${LIB_PATH} | awk "/T _LLVM/ || /T LLVM/ { print $3 }" | sort -u | sed -e "s/^_//g" > ${LLVM_EXPORTED_SYMBOL_FILE}
+ WORKING_DIRECTORY ${LIB_DIR}
+ DEPENDS LLVM
+ COMMENT "Generating Export list for LLVM..."
+ VERBATIM )
+
+ add_custom_target(libLLVMCExports DEPENDS ${LLVM_EXPORTED_SYMBOL_FILE})
+
+ add_llvm_library(LLVM-C SHARED ${SOURCES})
+
+ target_link_libraries(LLVM-C PUBLIC LLVM)
+ add_dependencies(LLVM-C libLLVMCExports)
+
+ set_property(TARGET LLVM-C APPEND_STRING PROPERTY
+ LINK_FLAGS
+ " -compatibility_version 1 -current_version ${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}.${LLVM_VERSION_PATCH} -Wl,-reexport_library ${LIB_PATH}")
endif()
diff --git a/tools/llvm-shlib/Makefile b/tools/llvm-shlib/Makefile
index 2bc81dac5f00..19077a3858a6 100644
--- a/tools/llvm-shlib/Makefile
+++ b/tools/llvm-shlib/Makefile
@@ -61,7 +61,7 @@ endif
ifeq ($(HOST_OS), $(filter $(HOST_OS), DragonFly Linux FreeBSD GNU/kFreeBSD GNU))
# Add soname to the library.
- LLVMLibsOptions += -Wl,--soname,lib$(LIBRARYNAME).1$(SHLIBEXT)
+ LLVMLibsOptions += -Wl,--soname,lib$(LIBRARYNAME)$(SHLIBEXT)
endif
ifeq ($(HOST_OS), $(filter $(HOST_OS), Linux GNU GNU/kFreeBSD))
diff --git a/tools/llvm-size/llvm-size.cpp b/tools/llvm-size/llvm-size.cpp
index 9a6e2c1ae4be..069cc621f615 100644
--- a/tools/llvm-size/llvm-size.cpp
+++ b/tools/llvm-size/llvm-size.cpp
@@ -1,4 +1,4 @@
-//===-- llvm-size.cpp - Print the size of each object section -------------===//
+//===-- llvm-size.cpp - Print the size of each object section ---*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
@@ -30,6 +30,7 @@
#include <algorithm>
#include <string>
#include <system_error>
+
using namespace llvm;
using namespace object;
@@ -98,7 +99,7 @@ static size_t getNumLengthAsString(uint64_t num) {
}
/// @brief Return the printing format for the Radix.
-static const char *getRadixFmt(void) {
+static const char *getRadixFmt() {
switch (Radix) {
case octal:
return PRIo64;
@@ -413,14 +414,6 @@ static bool checkMachOAndArchFlags(ObjectFile *o, StringRef file) {
/// @brief Print the section sizes for @p file. If @p file is an archive, print
/// the section sizes for each archive member.
static void PrintFileSectionSizes(StringRef file) {
- // If file is not stdin, check that it exists.
- if (file != "-") {
- if (!sys::fs::exists(file)) {
- errs() << ToolName << ": '" << file << "': "
- << "No such file\n";
- return;
- }
- }
// Attempt to open the binary.
ErrorOr<OwningBinary<Binary>> BinaryOrErr = createBinary(file);
@@ -435,7 +428,13 @@ static void PrintFileSectionSizes(StringRef file) {
for (object::Archive::child_iterator i = a->child_begin(),
e = a->child_end();
i != e; ++i) {
- ErrorOr<std::unique_ptr<Binary>> ChildOrErr = i->getAsBinary();
+ if (i->getError()) {
+ errs() << ToolName << ": " << file << ": " << i->getError().message()
+ << ".\n";
+ exit(1);
+ }
+ auto &c = i->get();
+ ErrorOr<std::unique_ptr<Binary>> ChildOrErr = c.getAsBinary();
if (std::error_code EC = ChildOrErr.getError()) {
errs() << ToolName << ": " << file << ": " << EC.message() << ".\n";
continue;
@@ -497,7 +496,13 @@ static void PrintFileSectionSizes(StringRef file) {
for (object::Archive::child_iterator i = UA->child_begin(),
e = UA->child_end();
i != e; ++i) {
- ErrorOr<std::unique_ptr<Binary>> ChildOrErr = i->getAsBinary();
+ if (std::error_code EC = i->getError()) {
+ errs() << ToolName << ": " << file << ": " << EC.message()
+ << ".\n";
+ exit(1);
+ }
+ auto &c = i->get();
+ ErrorOr<std::unique_ptr<Binary>> ChildOrErr = c.getAsBinary();
if (std::error_code EC = ChildOrErr.getError()) {
errs() << ToolName << ": " << file << ": " << EC.message()
<< ".\n";
@@ -574,7 +579,13 @@ static void PrintFileSectionSizes(StringRef file) {
for (object::Archive::child_iterator i = UA->child_begin(),
e = UA->child_end();
i != e; ++i) {
- ErrorOr<std::unique_ptr<Binary>> ChildOrErr = i->getAsBinary();
+ if (std::error_code EC = i->getError()) {
+ errs() << ToolName << ": " << file << ": " << EC.message()
+ << ".\n";
+ exit(1);
+ }
+ auto &c = i->get();
+ ErrorOr<std::unique_ptr<Binary>> ChildOrErr = c.getAsBinary();
if (std::error_code EC = ChildOrErr.getError()) {
errs() << ToolName << ": " << file << ": " << EC.message()
<< ".\n";
@@ -638,7 +649,12 @@ static void PrintFileSectionSizes(StringRef file) {
for (object::Archive::child_iterator i = UA->child_begin(),
e = UA->child_end();
i != e; ++i) {
- ErrorOr<std::unique_ptr<Binary>> ChildOrErr = i->getAsBinary();
+ if (std::error_code EC = i->getError()) {
+ errs() << ToolName << ": " << file << ": " << EC.message() << ".\n";
+ exit(1);
+ }
+ auto &c = i->get();
+ ErrorOr<std::unique_ptr<Binary>> ChildOrErr = c.getAsBinary();
if (std::error_code EC = ChildOrErr.getError()) {
errs() << ToolName << ": " << file << ": " << EC.message() << ".\n";
continue;
diff --git a/tools/llvm-split/CMakeLists.txt b/tools/llvm-split/CMakeLists.txt
new file mode 100644
index 000000000000..6e22a7236714
--- /dev/null
+++ b/tools/llvm-split/CMakeLists.txt
@@ -0,0 +1,11 @@
+set(LLVM_LINK_COMPONENTS
+ TransformUtils
+ BitWriter
+ Core
+ IRReader
+ Support
+ )
+
+add_llvm_tool(llvm-split
+ llvm-split.cpp
+ )
diff --git a/tools/macho-dump/LLVMBuild.txt b/tools/llvm-split/LLVMBuild.txt
index 1ad9b84261c9..780c76afa41c 100644
--- a/tools/macho-dump/LLVMBuild.txt
+++ b/tools/llvm-split/LLVMBuild.txt
@@ -1,4 +1,4 @@
-;===- ./tools/macho-dump/LLVMBuild.txt -------------------------*- Conf -*--===;
+;===- ./tools/llvm-split/LLVMBuild.txt -------------------------*- Conf -*--===;
;
; The LLVM Compiler Infrastructure
;
@@ -17,6 +17,6 @@
[component_0]
type = Tool
-name = macho-dump
+name = llvm-split
parent = Tools
-required_libraries = Object Support
+required_libraries = TransformUtils BitWriter Core IRReader Support
diff --git a/tools/macho-dump/Makefile b/tools/llvm-split/Makefile
index 0843e982e1af..ef1243dbf1ff 100644
--- a/tools/macho-dump/Makefile
+++ b/tools/llvm-split/Makefile
@@ -1,15 +1,15 @@
-##===- tools/macho-dump/Makefile ---------------------------*- Makefile -*-===##
-#
+##===- tools/llvm-split/Makefile ---------------------------*- Makefile -*-===##
+#
# The LLVM Compiler Infrastructure
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
-#
+#
##===----------------------------------------------------------------------===##
LEVEL := ../..
-TOOLNAME := macho-dump
-LINK_COMPONENTS := support object
+TOOLNAME := llvm-split
+LINK_COMPONENTS := transformutils bitwriter core irreader support
# This tool has no plugins, optimize startup time.
TOOL_NO_EXPORTS := 1
diff --git a/tools/llvm-split/llvm-split.cpp b/tools/llvm-split/llvm-split.cpp
new file mode 100644
index 000000000000..059770fbf4ac
--- /dev/null
+++ b/tools/llvm-split/llvm-split.cpp
@@ -0,0 +1,67 @@
+//===-- llvm-split: command line tool for testing module splitter ---------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This program can be used to test the llvm::SplitModule function.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Bitcode/ReaderWriter.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IRReader/IRReader.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Transforms/Utils/SplitModule.h"
+
+using namespace llvm;
+
+static cl::opt<std::string>
+InputFilename(cl::Positional, cl::desc("<input bitcode file>"),
+ cl::init("-"), cl::value_desc("filename"));
+
+static cl::opt<std::string>
+OutputFilename("o", cl::desc("Override output filename"),
+ cl::value_desc("filename"));
+
+static cl::opt<unsigned> NumOutputs("j", cl::Prefix, cl::init(2),
+ cl::desc("Number of output files"));
+
+int main(int argc, char **argv) {
+ LLVMContext &Context = getGlobalContext();
+ SMDiagnostic Err;
+ cl::ParseCommandLineOptions(argc, argv, "LLVM module splitter\n");
+
+ std::unique_ptr<Module> M = parseIRFile(InputFilename, Err, Context);
+
+ if (!M) {
+ Err.print(argv[0], errs());
+ return 1;
+ }
+
+ unsigned I = 0;
+ SplitModule(std::move(M), NumOutputs, [&](std::unique_ptr<Module> MPart) {
+ std::error_code EC;
+ std::unique_ptr<tool_output_file> Out(new tool_output_file(
+ OutputFilename + utostr(I++), EC, sys::fs::F_None));
+ if (EC) {
+ errs() << EC.message() << '\n';
+ exit(1);
+ }
+
+ WriteBitcodeToFile(MPart.get(), Out->os());
+
+ // Declare success.
+ Out->keep();
+ });
+
+ return 0;
+}
diff --git a/tools/llvm-stress/CMakeLists.txt b/tools/llvm-stress/CMakeLists.txt
index d5c10e13f5b3..8f2b30e4163b 100644
--- a/tools/llvm-stress/CMakeLists.txt
+++ b/tools/llvm-stress/CMakeLists.txt
@@ -1,6 +1,6 @@
set(LLVM_LINK_COMPONENTS
+ Analysis
Core
- IPA
Support
)
diff --git a/tools/llvm-stress/llvm-stress.cpp b/tools/llvm-stress/llvm-stress.cpp
index 6a1a248a0572..99d2afdcd301 100644
--- a/tools/llvm-stress/llvm-stress.cpp
+++ b/tools/llvm-stress/llvm-stress.cpp
@@ -612,7 +612,8 @@ struct CmpModifier: public Modifier {
}
Value *V = CmpInst::Create(fp ? Instruction::FCmp : Instruction::ICmp,
- op, Val0, Val1, "Cmp", BB->getTerminator());
+ (CmpInst::Predicate)op, Val0, Val1, "Cmp",
+ BB->getTerminator());
return PT->push_back(V);
}
};
@@ -666,7 +667,7 @@ static void IntroduceControlFlow(Function *F, Random &R) {
for (auto *Instr : BoolInst) {
BasicBlock *Curr = Instr->getParent();
- BasicBlock::iterator Loc = Instr;
+ BasicBlock::iterator Loc = Instr->getIterator();
BasicBlock *Next = Curr->splitBasicBlock(Loc, "CF");
Instr->moveBefore(Curr->getTerminator());
if (Curr != &F->getEntryBlock()) {
diff --git a/tools/llvm-symbolizer/CMakeLists.txt b/tools/llvm-symbolizer/CMakeLists.txt
index 5df3b17a065e..b04c45ff7442 100644
--- a/tools/llvm-symbolizer/CMakeLists.txt
+++ b/tools/llvm-symbolizer/CMakeLists.txt
@@ -8,9 +8,9 @@ set(LLVM_LINK_COMPONENTS
DebugInfoPDB
Object
Support
+ Symbolize
)
add_llvm_tool(llvm-symbolizer
- LLVMSymbolize.cpp
llvm-symbolizer.cpp
)
diff --git a/tools/llvm-symbolizer/LLVMSymbolize.cpp b/tools/llvm-symbolizer/LLVMSymbolize.cpp
deleted file mode 100644
index c57c219b11d2..000000000000
--- a/tools/llvm-symbolizer/LLVMSymbolize.cpp
+++ /dev/null
@@ -1,532 +0,0 @@
-//===-- LLVMSymbolize.cpp -------------------------------------------------===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// Implementation for LLVM symbolization library.
-//
-//===----------------------------------------------------------------------===//
-
-#include "LLVMSymbolize.h"
-#include "llvm/ADT/STLExtras.h"
-#include "llvm/Config/config.h"
-#include "llvm/DebugInfo/DWARF/DWARFContext.h"
-#include "llvm/DebugInfo/PDB/PDB.h"
-#include "llvm/DebugInfo/PDB/PDBContext.h"
-#include "llvm/Object/ELFObjectFile.h"
-#include "llvm/Object/MachO.h"
-#include "llvm/Object/SymbolSize.h"
-#include "llvm/Support/Casting.h"
-#include "llvm/Support/Compression.h"
-#include "llvm/Support/DataExtractor.h"
-#include "llvm/Support/Errc.h"
-#include "llvm/Support/FileSystem.h"
-#include "llvm/Support/MemoryBuffer.h"
-#include "llvm/Support/Path.h"
-#include <sstream>
-#include <stdlib.h>
-
-#if defined(_MSC_VER)
-#include <Windows.h>
-#include <DbgHelp.h>
-#pragma comment(lib, "dbghelp.lib")
-#endif
-
-namespace llvm {
-namespace symbolize {
-
-static bool error(std::error_code ec) {
- if (!ec)
- return false;
- errs() << "LLVMSymbolizer: error reading file: " << ec.message() << ".\n";
- return true;
-}
-
-static DILineInfoSpecifier
-getDILineInfoSpecifier(const LLVMSymbolizer::Options &Opts) {
- return DILineInfoSpecifier(
- DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath,
- Opts.PrintFunctions);
-}
-
-ModuleInfo::ModuleInfo(ObjectFile *Obj, DIContext *DICtx)
- : Module(Obj), DebugInfoContext(DICtx) {
- std::unique_ptr<DataExtractor> OpdExtractor;
- uint64_t OpdAddress = 0;
- // Find the .opd (function descriptor) section if any, for big-endian
- // PowerPC64 ELF.
- if (Module->getArch() == Triple::ppc64) {
- for (section_iterator Section : Module->sections()) {
- StringRef Name;
- if (!error(Section->getName(Name)) && Name == ".opd") {
- StringRef Data;
- if (!error(Section->getContents(Data))) {
- OpdExtractor.reset(new DataExtractor(Data, Module->isLittleEndian(),
- Module->getBytesInAddress()));
- OpdAddress = Section->getAddress();
- }
- break;
- }
- }
- }
- std::vector<std::pair<SymbolRef, uint64_t>> Symbols =
- computeSymbolSizes(*Module);
- for (auto &P : Symbols)
- addSymbol(P.first, P.second, OpdExtractor.get(), OpdAddress);
-}
-
-void ModuleInfo::addSymbol(const SymbolRef &Symbol, uint64_t SymbolSize,
- DataExtractor *OpdExtractor, uint64_t OpdAddress) {
- SymbolRef::Type SymbolType = Symbol.getType();
- if (SymbolType != SymbolRef::ST_Function && SymbolType != SymbolRef::ST_Data)
- return;
- ErrorOr<uint64_t> SymbolAddressOrErr = Symbol.getAddress();
- if (error(SymbolAddressOrErr.getError()))
- return;
- uint64_t SymbolAddress = *SymbolAddressOrErr;
- if (OpdExtractor) {
- // For big-endian PowerPC64 ELF, symbols in the .opd section refer to
- // function descriptors. The first word of the descriptor is a pointer to
- // the function's code.
- // For the purposes of symbolization, pretend the symbol's address is that
- // of the function's code, not the descriptor.
- uint64_t OpdOffset = SymbolAddress - OpdAddress;
- uint32_t OpdOffset32 = OpdOffset;
- if (OpdOffset == OpdOffset32 &&
- OpdExtractor->isValidOffsetForAddress(OpdOffset32))
- SymbolAddress = OpdExtractor->getAddress(&OpdOffset32);
- }
- ErrorOr<StringRef> SymbolNameOrErr = Symbol.getName();
- if (error(SymbolNameOrErr.getError()))
- return;
- StringRef SymbolName = *SymbolNameOrErr;
- // Mach-O symbol table names have leading underscore, skip it.
- if (Module->isMachO() && SymbolName.size() > 0 && SymbolName[0] == '_')
- SymbolName = SymbolName.drop_front();
- // FIXME: If a function has alias, there are two entries in symbol table
- // with same address size. Make sure we choose the correct one.
- auto &M = SymbolType == SymbolRef::ST_Function ? Functions : Objects;
- SymbolDesc SD = { SymbolAddress, SymbolSize };
- M.insert(std::make_pair(SD, SymbolName));
-}
-
-bool ModuleInfo::getNameFromSymbolTable(SymbolRef::Type Type, uint64_t Address,
- std::string &Name, uint64_t &Addr,
- uint64_t &Size) const {
- const auto &SymbolMap = Type == SymbolRef::ST_Function ? Functions : Objects;
- if (SymbolMap.empty())
- return false;
- SymbolDesc SD = { Address, Address };
- auto SymbolIterator = SymbolMap.upper_bound(SD);
- if (SymbolIterator == SymbolMap.begin())
- return false;
- --SymbolIterator;
- if (SymbolIterator->first.Size != 0 &&
- SymbolIterator->first.Addr + SymbolIterator->first.Size <= Address)
- return false;
- Name = SymbolIterator->second.str();
- Addr = SymbolIterator->first.Addr;
- Size = SymbolIterator->first.Size;
- return true;
-}
-
-DILineInfo ModuleInfo::symbolizeCode(
- uint64_t ModuleOffset, const LLVMSymbolizer::Options &Opts) const {
- DILineInfo LineInfo;
- if (DebugInfoContext) {
- LineInfo = DebugInfoContext->getLineInfoForAddress(
- ModuleOffset, getDILineInfoSpecifier(Opts));
- }
- // Override function name from symbol table if necessary.
- if (Opts.PrintFunctions != FunctionNameKind::None && Opts.UseSymbolTable) {
- std::string FunctionName;
- uint64_t Start, Size;
- if (getNameFromSymbolTable(SymbolRef::ST_Function, ModuleOffset,
- FunctionName, Start, Size)) {
- LineInfo.FunctionName = FunctionName;
- }
- }
- return LineInfo;
-}
-
-DIInliningInfo ModuleInfo::symbolizeInlinedCode(
- uint64_t ModuleOffset, const LLVMSymbolizer::Options &Opts) const {
- DIInliningInfo InlinedContext;
-
- if (DebugInfoContext) {
- InlinedContext = DebugInfoContext->getInliningInfoForAddress(
- ModuleOffset, getDILineInfoSpecifier(Opts));
- }
- // Make sure there is at least one frame in context.
- if (InlinedContext.getNumberOfFrames() == 0) {
- InlinedContext.addFrame(DILineInfo());
- }
- // Override the function name in lower frame with name from symbol table.
- if (Opts.PrintFunctions != FunctionNameKind::None && Opts.UseSymbolTable) {
- DIInliningInfo PatchedInlinedContext;
- for (uint32_t i = 0, n = InlinedContext.getNumberOfFrames(); i < n; i++) {
- DILineInfo LineInfo = InlinedContext.getFrame(i);
- if (i == n - 1) {
- std::string FunctionName;
- uint64_t Start, Size;
- if (getNameFromSymbolTable(SymbolRef::ST_Function, ModuleOffset,
- FunctionName, Start, Size)) {
- LineInfo.FunctionName = FunctionName;
- }
- }
- PatchedInlinedContext.addFrame(LineInfo);
- }
- InlinedContext = PatchedInlinedContext;
- }
- return InlinedContext;
-}
-
-bool ModuleInfo::symbolizeData(uint64_t ModuleOffset, std::string &Name,
- uint64_t &Start, uint64_t &Size) const {
- return getNameFromSymbolTable(SymbolRef::ST_Data, ModuleOffset, Name, Start,
- Size);
-}
-
-const char LLVMSymbolizer::kBadString[] = "??";
-
-std::string LLVMSymbolizer::symbolizeCode(const std::string &ModuleName,
- uint64_t ModuleOffset) {
- ModuleInfo *Info = getOrCreateModuleInfo(ModuleName);
- if (!Info)
- return printDILineInfo(DILineInfo());
- if (Opts.PrintInlining) {
- DIInliningInfo InlinedContext =
- Info->symbolizeInlinedCode(ModuleOffset, Opts);
- uint32_t FramesNum = InlinedContext.getNumberOfFrames();
- assert(FramesNum > 0);
- std::string Result;
- for (uint32_t i = 0; i < FramesNum; i++) {
- DILineInfo LineInfo = InlinedContext.getFrame(i);
- Result += printDILineInfo(LineInfo);
- }
- return Result;
- }
- DILineInfo LineInfo = Info->symbolizeCode(ModuleOffset, Opts);
- return printDILineInfo(LineInfo);
-}
-
-std::string LLVMSymbolizer::symbolizeData(const std::string &ModuleName,
- uint64_t ModuleOffset) {
- std::string Name = kBadString;
- uint64_t Start = 0;
- uint64_t Size = 0;
- if (Opts.UseSymbolTable) {
- if (ModuleInfo *Info = getOrCreateModuleInfo(ModuleName)) {
- if (Info->symbolizeData(ModuleOffset, Name, Start, Size) && Opts.Demangle)
- Name = DemangleName(Name);
- }
- }
- std::stringstream ss;
- ss << Name << "\n" << Start << " " << Size << "\n";
- return ss.str();
-}
-
-void LLVMSymbolizer::flush() {
- DeleteContainerSeconds(Modules);
- ObjectPairForPathArch.clear();
- ObjectFileForArch.clear();
-}
-
-// For Path="/path/to/foo" and Basename="foo" assume that debug info is in
-// /path/to/foo.dSYM/Contents/Resources/DWARF/foo.
-// For Path="/path/to/bar.dSYM" and Basename="foo" assume that debug info is in
-// /path/to/bar.dSYM/Contents/Resources/DWARF/foo.
-static
-std::string getDarwinDWARFResourceForPath(
- const std::string &Path, const std::string &Basename) {
- SmallString<16> ResourceName = StringRef(Path);
- if (sys::path::extension(Path) != ".dSYM") {
- ResourceName += ".dSYM";
- }
- sys::path::append(ResourceName, "Contents", "Resources", "DWARF");
- sys::path::append(ResourceName, Basename);
- return ResourceName.str();
-}
-
-static bool checkFileCRC(StringRef Path, uint32_t CRCHash) {
- ErrorOr<std::unique_ptr<MemoryBuffer>> MB =
- MemoryBuffer::getFileOrSTDIN(Path);
- if (!MB)
- return false;
- return !zlib::isAvailable() || CRCHash == zlib::crc32(MB.get()->getBuffer());
-}
-
-static bool findDebugBinary(const std::string &OrigPath,
- const std::string &DebuglinkName, uint32_t CRCHash,
- std::string &Result) {
- std::string OrigRealPath = OrigPath;
-#if defined(HAVE_REALPATH)
- if (char *RP = realpath(OrigPath.c_str(), nullptr)) {
- OrigRealPath = RP;
- free(RP);
- }
-#endif
- SmallString<16> OrigDir(OrigRealPath);
- llvm::sys::path::remove_filename(OrigDir);
- SmallString<16> DebugPath = OrigDir;
- // Try /path/to/original_binary/debuglink_name
- llvm::sys::path::append(DebugPath, DebuglinkName);
- if (checkFileCRC(DebugPath, CRCHash)) {
- Result = DebugPath.str();
- return true;
- }
- // Try /path/to/original_binary/.debug/debuglink_name
- DebugPath = OrigRealPath;
- llvm::sys::path::append(DebugPath, ".debug", DebuglinkName);
- if (checkFileCRC(DebugPath, CRCHash)) {
- Result = DebugPath.str();
- return true;
- }
- // Try /usr/lib/debug/path/to/original_binary/debuglink_name
- DebugPath = "/usr/lib/debug";
- llvm::sys::path::append(DebugPath, llvm::sys::path::relative_path(OrigDir),
- DebuglinkName);
- if (checkFileCRC(DebugPath, CRCHash)) {
- Result = DebugPath.str();
- return true;
- }
- return false;
-}
-
-static bool getGNUDebuglinkContents(const ObjectFile *Obj, std::string &DebugName,
- uint32_t &CRCHash) {
- if (!Obj)
- return false;
- for (const SectionRef &Section : Obj->sections()) {
- StringRef Name;
- Section.getName(Name);
- Name = Name.substr(Name.find_first_not_of("._"));
- if (Name == "gnu_debuglink") {
- StringRef Data;
- Section.getContents(Data);
- DataExtractor DE(Data, Obj->isLittleEndian(), 0);
- uint32_t Offset = 0;
- if (const char *DebugNameStr = DE.getCStr(&Offset)) {
- // 4-byte align the offset.
- Offset = (Offset + 3) & ~0x3;
- if (DE.isValidOffsetForDataOfSize(Offset, 4)) {
- DebugName = DebugNameStr;
- CRCHash = DE.getU32(&Offset);
- return true;
- }
- }
- break;
- }
- }
- return false;
-}
-
-static
-bool darwinDsymMatchesBinary(const MachOObjectFile *DbgObj,
- const MachOObjectFile *Obj) {
- ArrayRef<uint8_t> dbg_uuid = DbgObj->getUuid();
- ArrayRef<uint8_t> bin_uuid = Obj->getUuid();
- if (dbg_uuid.empty() || bin_uuid.empty())
- return false;
- return !memcmp(dbg_uuid.data(), bin_uuid.data(), dbg_uuid.size());
-}
-
-ObjectFile *LLVMSymbolizer::lookUpDsymFile(const std::string &ExePath,
- const MachOObjectFile *MachExeObj, const std::string &ArchName) {
- // On Darwin we may find DWARF in separate object file in
- // resource directory.
- std::vector<std::string> DsymPaths;
- StringRef Filename = sys::path::filename(ExePath);
- DsymPaths.push_back(getDarwinDWARFResourceForPath(ExePath, Filename));
- for (const auto &Path : Opts.DsymHints) {
- DsymPaths.push_back(getDarwinDWARFResourceForPath(Path, Filename));
- }
- for (const auto &path : DsymPaths) {
- ErrorOr<OwningBinary<Binary>> BinaryOrErr = createBinary(path);
- std::error_code EC = BinaryOrErr.getError();
- if (EC != errc::no_such_file_or_directory && !error(EC)) {
- OwningBinary<Binary> B = std::move(BinaryOrErr.get());
- ObjectFile *DbgObj =
- getObjectFileFromBinary(B.getBinary(), ArchName);
- const MachOObjectFile *MachDbgObj =
- dyn_cast<const MachOObjectFile>(DbgObj);
- if (!MachDbgObj) continue;
- if (darwinDsymMatchesBinary(MachDbgObj, MachExeObj)) {
- addOwningBinary(std::move(B));
- return DbgObj;
- }
- }
- }
- return nullptr;
-}
-
-LLVMSymbolizer::ObjectPair
-LLVMSymbolizer::getOrCreateObjects(const std::string &Path,
- const std::string &ArchName) {
- const auto &I = ObjectPairForPathArch.find(std::make_pair(Path, ArchName));
- if (I != ObjectPairForPathArch.end())
- return I->second;
- ObjectFile *Obj = nullptr;
- ObjectFile *DbgObj = nullptr;
- ErrorOr<OwningBinary<Binary>> BinaryOrErr = createBinary(Path);
- if (!error(BinaryOrErr.getError())) {
- OwningBinary<Binary> &B = BinaryOrErr.get();
- Obj = getObjectFileFromBinary(B.getBinary(), ArchName);
- if (!Obj) {
- ObjectPair Res = std::make_pair(nullptr, nullptr);
- ObjectPairForPathArch[std::make_pair(Path, ArchName)] = Res;
- return Res;
- }
- addOwningBinary(std::move(B));
- if (auto MachObj = dyn_cast<const MachOObjectFile>(Obj))
- DbgObj = lookUpDsymFile(Path, MachObj, ArchName);
- // Try to locate the debug binary using .gnu_debuglink section.
- if (!DbgObj) {
- std::string DebuglinkName;
- uint32_t CRCHash;
- std::string DebugBinaryPath;
- if (getGNUDebuglinkContents(Obj, DebuglinkName, CRCHash) &&
- findDebugBinary(Path, DebuglinkName, CRCHash, DebugBinaryPath)) {
- BinaryOrErr = createBinary(DebugBinaryPath);
- if (!error(BinaryOrErr.getError())) {
- OwningBinary<Binary> B = std::move(BinaryOrErr.get());
- DbgObj = getObjectFileFromBinary(B.getBinary(), ArchName);
- addOwningBinary(std::move(B));
- }
- }
- }
- }
- if (!DbgObj)
- DbgObj = Obj;
- ObjectPair Res = std::make_pair(Obj, DbgObj);
- ObjectPairForPathArch[std::make_pair(Path, ArchName)] = Res;
- return Res;
-}
-
-ObjectFile *
-LLVMSymbolizer::getObjectFileFromBinary(Binary *Bin,
- const std::string &ArchName) {
- if (!Bin)
- return nullptr;
- ObjectFile *Res = nullptr;
- if (MachOUniversalBinary *UB = dyn_cast<MachOUniversalBinary>(Bin)) {
- const auto &I = ObjectFileForArch.find(
- std::make_pair(UB, ArchName));
- if (I != ObjectFileForArch.end())
- return I->second;
- ErrorOr<std::unique_ptr<ObjectFile>> ParsedObj =
- UB->getObjectForArch(ArchName);
- if (ParsedObj) {
- Res = ParsedObj.get().get();
- ParsedBinariesAndObjects.push_back(std::move(ParsedObj.get()));
- }
- ObjectFileForArch[std::make_pair(UB, ArchName)] = Res;
- } else if (Bin->isObject()) {
- Res = cast<ObjectFile>(Bin);
- }
- return Res;
-}
-
-ModuleInfo *
-LLVMSymbolizer::getOrCreateModuleInfo(const std::string &ModuleName) {
- const auto &I = Modules.find(ModuleName);
- if (I != Modules.end())
- return I->second;
- std::string BinaryName = ModuleName;
- std::string ArchName = Opts.DefaultArch;
- size_t ColonPos = ModuleName.find_last_of(':');
- // Verify that substring after colon form a valid arch name.
- if (ColonPos != std::string::npos) {
- std::string ArchStr = ModuleName.substr(ColonPos + 1);
- if (Triple(ArchStr).getArch() != Triple::UnknownArch) {
- BinaryName = ModuleName.substr(0, ColonPos);
- ArchName = ArchStr;
- }
- }
- ObjectPair Objects = getOrCreateObjects(BinaryName, ArchName);
-
- if (!Objects.first) {
- // Failed to find valid object file.
- Modules.insert(make_pair(ModuleName, (ModuleInfo *)nullptr));
- return nullptr;
- }
- DIContext *Context = nullptr;
- if (auto CoffObject = dyn_cast<COFFObjectFile>(Objects.first)) {
- // If this is a COFF object, assume it contains PDB debug information. If
- // we don't find any we will fall back to the DWARF case.
- std::unique_ptr<IPDBSession> Session;
- PDB_ErrorCode Error = loadDataForEXE(PDB_ReaderType::DIA,
- Objects.first->getFileName(), Session);
- if (Error == PDB_ErrorCode::Success) {
- Context = new PDBContext(*CoffObject, std::move(Session),
- Opts.RelativeAddresses);
- }
- }
- if (!Context)
- Context = new DWARFContextInMemory(*Objects.second);
- assert(Context);
- ModuleInfo *Info = new ModuleInfo(Objects.first, Context);
- Modules.insert(make_pair(ModuleName, Info));
- return Info;
-}
-
-std::string LLVMSymbolizer::printDILineInfo(DILineInfo LineInfo) const {
- // By default, DILineInfo contains "<invalid>" for function/filename it
- // cannot fetch. We replace it to "??" to make our output closer to addr2line.
- static const std::string kDILineInfoBadString = "<invalid>";
- std::stringstream Result;
- if (Opts.PrintFunctions != FunctionNameKind::None) {
- std::string FunctionName = LineInfo.FunctionName;
- if (FunctionName == kDILineInfoBadString)
- FunctionName = kBadString;
- else if (Opts.Demangle)
- FunctionName = DemangleName(FunctionName);
- Result << FunctionName << "\n";
- }
- std::string Filename = LineInfo.FileName;
- if (Filename == kDILineInfoBadString)
- Filename = kBadString;
- Result << Filename << ":" << LineInfo.Line << ":" << LineInfo.Column << "\n";
- return Result.str();
-}
-
-#if !defined(_MSC_VER)
-// Assume that __cxa_demangle is provided by libcxxabi (except for Windows).
-extern "C" char *__cxa_demangle(const char *mangled_name, char *output_buffer,
- size_t *length, int *status);
-#endif
-
-std::string LLVMSymbolizer::DemangleName(const std::string &Name) {
-#if !defined(_MSC_VER)
- // We can spoil names of symbols with C linkage, so use an heuristic
- // approach to check if the name should be demangled.
- if (Name.substr(0, 2) != "_Z")
- return Name;
- int status = 0;
- char *DemangledName = __cxa_demangle(Name.c_str(), nullptr, nullptr, &status);
- if (status != 0)
- return Name;
- std::string Result = DemangledName;
- free(DemangledName);
- return Result;
-#else
- char DemangledName[1024] = {0};
- DWORD result = ::UnDecorateSymbolName(
- Name.c_str(), DemangledName, 1023,
- UNDNAME_NO_ACCESS_SPECIFIERS | // Strip public, private, protected
- UNDNAME_NO_ALLOCATION_LANGUAGE | // Strip __thiscall, __stdcall, etc
- UNDNAME_NO_THROW_SIGNATURES | // Strip throw() specifications
- UNDNAME_NO_MEMBER_TYPE | // Strip virtual, static, etc specifiers
- UNDNAME_NO_MS_KEYWORDS | // Strip all MS extension keywords
- UNDNAME_NO_FUNCTION_RETURNS); // Strip function return types
-
- return (result == 0) ? Name : std::string(DemangledName);
-#endif
-}
-
-} // namespace symbolize
-} // namespace llvm
diff --git a/tools/llvm-symbolizer/LLVMSymbolize.h b/tools/llvm-symbolizer/LLVMSymbolize.h
deleted file mode 100644
index be246c3f8712..000000000000
--- a/tools/llvm-symbolizer/LLVMSymbolize.h
+++ /dev/null
@@ -1,144 +0,0 @@
-//===-- LLVMSymbolize.h ----------------------------------------- C++ -----===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// Header for LLVM symbolization library.
-//
-//===----------------------------------------------------------------------===//
-#ifndef LLVM_TOOLS_LLVM_SYMBOLIZER_LLVMSYMBOLIZE_H
-#define LLVM_TOOLS_LLVM_SYMBOLIZER_LLVMSYMBOLIZE_H
-
-#include "llvm/ADT/SmallVector.h"
-#include "llvm/DebugInfo/DIContext.h"
-#include "llvm/Object/MachOUniversal.h"
-#include "llvm/Object/ObjectFile.h"
-#include "llvm/Support/DataExtractor.h"
-#include "llvm/Support/MemoryBuffer.h"
-#include <map>
-#include <memory>
-#include <string>
-
-namespace llvm {
-
-typedef DILineInfoSpecifier::FunctionNameKind FunctionNameKind;
-using namespace object;
-
-namespace symbolize {
-
-class ModuleInfo;
-
-class LLVMSymbolizer {
-public:
- struct Options {
- FunctionNameKind PrintFunctions;
- bool UseSymbolTable : 1;
- bool PrintInlining : 1;
- bool Demangle : 1;
- bool RelativeAddresses : 1;
- std::string DefaultArch;
- std::vector<std::string> DsymHints;
- Options(FunctionNameKind PrintFunctions = FunctionNameKind::LinkageName,
- bool UseSymbolTable = true, bool PrintInlining = true,
- bool Demangle = true, bool RelativeAddresses = false,
- std::string DefaultArch = "")
- : PrintFunctions(PrintFunctions), UseSymbolTable(UseSymbolTable),
- PrintInlining(PrintInlining), Demangle(Demangle),
- RelativeAddresses(RelativeAddresses), DefaultArch(DefaultArch) {}
- };
-
- LLVMSymbolizer(const Options &Opts = Options()) : Opts(Opts) {}
- ~LLVMSymbolizer() {
- flush();
- }
-
- // Returns the result of symbolization for module name/offset as
- // a string (possibly containing newlines).
- std::string
- symbolizeCode(const std::string &ModuleName, uint64_t ModuleOffset);
- std::string
- symbolizeData(const std::string &ModuleName, uint64_t ModuleOffset);
- void flush();
- static std::string DemangleName(const std::string &Name);
-private:
- typedef std::pair<ObjectFile*, ObjectFile*> ObjectPair;
-
- ModuleInfo *getOrCreateModuleInfo(const std::string &ModuleName);
- ObjectFile *lookUpDsymFile(const std::string &Path, const MachOObjectFile *ExeObj,
- const std::string &ArchName);
-
- /// \brief Returns pair of pointers to object and debug object.
- ObjectPair getOrCreateObjects(const std::string &Path,
- const std::string &ArchName);
- /// \brief Returns a parsed object file for a given architecture in a
- /// universal binary (or the binary itself if it is an object file).
- ObjectFile *getObjectFileFromBinary(Binary *Bin, const std::string &ArchName);
-
- std::string printDILineInfo(DILineInfo LineInfo) const;
-
- // Owns all the parsed binaries and object files.
- SmallVector<std::unique_ptr<Binary>, 4> ParsedBinariesAndObjects;
- SmallVector<std::unique_ptr<MemoryBuffer>, 4> MemoryBuffers;
- void addOwningBinary(OwningBinary<Binary> OwningBin) {
- std::unique_ptr<Binary> Bin;
- std::unique_ptr<MemoryBuffer> MemBuf;
- std::tie(Bin, MemBuf) = OwningBin.takeBinary();
- ParsedBinariesAndObjects.push_back(std::move(Bin));
- MemoryBuffers.push_back(std::move(MemBuf));
- }
-
- // Owns module info objects.
- std::map<std::string, ModuleInfo *> Modules;
- std::map<std::pair<MachOUniversalBinary *, std::string>, ObjectFile *>
- ObjectFileForArch;
- std::map<std::pair<std::string, std::string>, ObjectPair>
- ObjectPairForPathArch;
-
- Options Opts;
- static const char kBadString[];
-};
-
-class ModuleInfo {
-public:
- ModuleInfo(ObjectFile *Obj, DIContext *DICtx);
-
- DILineInfo symbolizeCode(uint64_t ModuleOffset,
- const LLVMSymbolizer::Options &Opts) const;
- DIInliningInfo symbolizeInlinedCode(
- uint64_t ModuleOffset, const LLVMSymbolizer::Options &Opts) const;
- bool symbolizeData(uint64_t ModuleOffset, std::string &Name, uint64_t &Start,
- uint64_t &Size) const;
-
-private:
- bool getNameFromSymbolTable(SymbolRef::Type Type, uint64_t Address,
- std::string &Name, uint64_t &Addr,
- uint64_t &Size) const;
- // For big-endian PowerPC64 ELF, OpdAddress is the address of the .opd
- // (function descriptor) section and OpdExtractor refers to its contents.
- void addSymbol(const SymbolRef &Symbol, uint64_t SymbolSize,
- DataExtractor *OpdExtractor = nullptr,
- uint64_t OpdAddress = 0);
- ObjectFile *Module;
- std::unique_ptr<DIContext> DebugInfoContext;
-
- struct SymbolDesc {
- uint64_t Addr;
- // If size is 0, assume that symbol occupies the whole memory range up to
- // the following symbol.
- uint64_t Size;
- friend bool operator<(const SymbolDesc &s1, const SymbolDesc &s2) {
- return s1.Addr < s2.Addr;
- }
- };
- std::map<SymbolDesc, StringRef> Functions;
- std::map<SymbolDesc, StringRef> Objects;
-};
-
-} // namespace symbolize
-} // namespace llvm
-
-#endif
diff --git a/tools/llvm-symbolizer/Makefile b/tools/llvm-symbolizer/Makefile
index de75befb1c97..8272d61280c7 100644
--- a/tools/llvm-symbolizer/Makefile
+++ b/tools/llvm-symbolizer/Makefile
@@ -9,7 +9,7 @@
LEVEL := ../..
TOOLNAME := llvm-symbolizer
-LINK_COMPONENTS := DebugInfoDWARF DebugInfoPDB Object
+LINK_COMPONENTS := DebugInfoDWARF DebugInfoPDB Object Support Symbolize
# This tool has no plugins, optimize startup time.
TOOL_NO_EXPORTS := 1
diff --git a/tools/llvm-symbolizer/llvm-symbolizer.cpp b/tools/llvm-symbolizer/llvm-symbolizer.cpp
index 9c9f3adbf60e..e45660c84c77 100644
--- a/tools/llvm-symbolizer/llvm-symbolizer.cpp
+++ b/tools/llvm-symbolizer/llvm-symbolizer.cpp
@@ -15,8 +15,9 @@
//
//===----------------------------------------------------------------------===//
-#include "LLVMSymbolize.h"
#include "llvm/ADT/StringRef.h"
+#include "llvm/DebugInfo/Symbolize/DIPrinter.h"
+#include "llvm/DebugInfo/Symbolize/Symbolize.h"
#include "llvm/Support/COM.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
@@ -73,6 +74,20 @@ static cl::list<std::string>
ClDsymHint("dsym-hint", cl::ZeroOrMore,
cl::desc("Path to .dSYM bundles to search for debug info for the "
"object files"));
+static cl::opt<bool>
+ ClPrintAddress("print-address", cl::init(false),
+ cl::desc("Show address before line information"));
+
+static cl::opt<bool>
+ ClPrettyPrint("pretty-print", cl::init(false),
+ cl::desc("Make the output more human friendly"));
+
+static bool error(std::error_code ec) {
+ if (!ec)
+ return false;
+ errs() << "LLVMSymbolizer: error reading file: " << ec.message() << ".\n";
+ return true;
+}
static bool parseCommand(bool &IsData, std::string &ModuleName,
uint64_t &ModuleOffset) {
@@ -118,9 +133,7 @@ static bool parseCommand(bool &IsData, std::string &ModuleName,
// Skip delimiters and parse module offset.
pos += strspn(pos, kDelimiters);
int offset_length = strcspn(pos, kDelimiters);
- if (StringRef(pos, offset_length).getAsInteger(0, ModuleOffset))
- return false;
- return true;
+ return !StringRef(pos, offset_length).getAsInteger(0, ModuleOffset);
}
int main(int argc, char **argv) {
@@ -132,9 +145,9 @@ int main(int argc, char **argv) {
llvm::sys::InitializeCOMRAII COM(llvm::sys::COMThreadingMode::MultiThreaded);
cl::ParseCommandLineOptions(argc, argv, "llvm-symbolizer\n");
- LLVMSymbolizer::Options Opts(ClPrintFunctions, ClUseSymbolTable,
- ClPrintInlining, ClDemangle,
+ LLVMSymbolizer::Options Opts(ClPrintFunctions, ClUseSymbolTable, ClDemangle,
ClUseRelativeAddress, ClDefaultArch);
+
for (const auto &hint : ClDsymHint) {
if (sys::path::extension(hint) == ".dSYM") {
Opts.DsymHints.push_back(hint);
@@ -148,11 +161,28 @@ int main(int argc, char **argv) {
bool IsData = false;
std::string ModuleName;
uint64_t ModuleOffset;
+ DIPrinter Printer(outs(), ClPrintFunctions != FunctionNameKind::None,
+ ClPrettyPrint);
+
while (parseCommand(IsData, ModuleName, ModuleOffset)) {
- std::string Result =
- IsData ? Symbolizer.symbolizeData(ModuleName, ModuleOffset)
- : Symbolizer.symbolizeCode(ModuleName, ModuleOffset);
- outs() << Result << "\n";
+ if (ClPrintAddress) {
+ outs() << "0x";
+ outs().write_hex(ModuleOffset);
+ StringRef Delimiter = (ClPrettyPrint == true) ? ": " : "\n";
+ outs() << Delimiter;
+ }
+ if (IsData) {
+ auto ResOrErr = Symbolizer.symbolizeData(ModuleName, ModuleOffset);
+ Printer << (error(ResOrErr.getError()) ? DIGlobal() : ResOrErr.get());
+ } else if (ClPrintInlining) {
+ auto ResOrErr = Symbolizer.symbolizeInlinedCode(ModuleName, ModuleOffset);
+ Printer << (error(ResOrErr.getError()) ? DIInliningInfo()
+ : ResOrErr.get());
+ } else {
+ auto ResOrErr = Symbolizer.symbolizeCode(ModuleName, ModuleOffset);
+ Printer << (error(ResOrErr.getError()) ? DILineInfo() : ResOrErr.get());
+ }
+ outs() << "\n";
outs().flush();
}
diff --git a/tools/lto/CMakeLists.txt b/tools/lto/CMakeLists.txt
index c479fa954cb0..62447eec389b 100644
--- a/tools/lto/CMakeLists.txt
+++ b/tools/lto/CMakeLists.txt
@@ -19,3 +19,13 @@ add_llvm_library(LTO SHARED ${SOURCES})
install(FILES ${LLVM_MAIN_INCLUDE_DIR}/llvm-c/lto.h
DESTINATION include/llvm-c)
+
+if (APPLE)
+ set(LTO_VERSION ${LLVM_VERSION_MAJOR})
+ if(LLVM_LTO_VERSION_OFFSET)
+ math(EXPR LTO_VERSION "${LLVM_VERSION_MAJOR} + ${LLVM_LTO_VERSION_OFFSET}")
+ endif()
+ set_property(TARGET LTO APPEND_STRING PROPERTY
+ LINK_FLAGS
+ " -compatibility_version 1 -current_version ${LTO_VERSION}.${LLVM_VERSION_MINOR}.${LLVM_VERSION_PATCH}")
+endif()
diff --git a/tools/lto/lto.cpp b/tools/lto/lto.cpp
index 5c712f18c9ee..d8f99c050a34 100644
--- a/tools/lto/lto.cpp
+++ b/tools/lto/lto.cpp
@@ -15,6 +15,8 @@
#include "llvm-c/lto.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/CodeGen/CommandFlags.h"
+#include "llvm/IR/DiagnosticInfo.h"
+#include "llvm/IR/DiagnosticPrinter.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/LTO/LTOCodeGenerator.h"
#include "llvm/LTO/LTOModule.h"
@@ -43,6 +45,16 @@ static cl::opt<bool>
DisableLTOVectorization("disable-lto-vectorization", cl::init(false),
cl::desc("Do not run loop or slp vectorization during LTO"));
+#ifdef NDEBUG
+static bool VerifyByDefault = false;
+#else
+static bool VerifyByDefault = true;
+#endif
+
+static cl::opt<bool> DisableVerify(
+ "disable-llvm-verifier", cl::init(!VerifyByDefault),
+ cl::desc("Don't run the LLVM verifier during the optimization pipeline"));
+
// Holds most recent error string.
// *** Not thread safe ***
static std::string sLastErrorString;
@@ -54,6 +66,24 @@ static bool initialized = false;
// Holds the command-line option parsing state of the LTO module.
static bool parsedOptions = false;
+static LLVMContext *LTOContext = nullptr;
+
+static void diagnosticHandler(const DiagnosticInfo &DI, void *Context) {
+ if (DI.getSeverity() != DS_Error) {
+ DiagnosticPrinterRawOStream DP(errs());
+ DI.print(DP);
+ errs() << '\n';
+ return;
+ }
+ sLastErrorString = "";
+ {
+ raw_string_ostream Stream(sLastErrorString);
+ DiagnosticPrinterRawOStream DP(Stream);
+ DI.print(DP);
+ }
+ sLastErrorString += '\n';
+}
+
// Initialize the configured targets if they have not been initialized.
static void lto_initialize() {
if (!initialized) {
@@ -69,21 +99,37 @@ static void lto_initialize() {
InitializeAllAsmParsers();
InitializeAllAsmPrinters();
InitializeAllDisassemblers();
+
+ LTOContext = &getGlobalContext();
+ LTOContext->setDiagnosticHandler(diagnosticHandler, nullptr, true);
initialized = true;
}
}
namespace {
+static void handleLibLTODiagnostic(lto_codegen_diagnostic_severity_t Severity,
+ const char *Msg, void *) {
+ sLastErrorString = Msg;
+ sLastErrorString += "\n";
+}
+
// This derived class owns the native object file. This helps implement the
// libLTO API semantics, which require that the code generator owns the object
// file.
struct LibLTOCodeGenerator : LTOCodeGenerator {
- LibLTOCodeGenerator() {}
+ LibLTOCodeGenerator() : LTOCodeGenerator(*LTOContext) {
+ setDiagnosticHandler(handleLibLTODiagnostic, nullptr); }
LibLTOCodeGenerator(std::unique_ptr<LLVMContext> Context)
- : LTOCodeGenerator(std::move(Context)) {}
+ : LTOCodeGenerator(*Context), OwnedContext(std::move(Context)) {
+ setDiagnosticHandler(handleLibLTODiagnostic, nullptr); }
+
+ // Reset the module first in case MergedModule is created in OwnedContext.
+ // Module must be destructed before its context gets destructed.
+ ~LibLTOCodeGenerator() { resetMergedModule(); }
std::unique_ptr<MemoryBuffer> NativeObjectFile;
+ std::unique_ptr<LLVMContext> OwnedContext;
};
}
@@ -147,14 +193,21 @@ lto_module_is_object_file_in_memory_for_target(const void* mem,
lto_module_t lto_module_create(const char* path) {
lto_initialize();
llvm::TargetOptions Options = InitTargetOptionsFromCodeGenFlags();
- return wrap(LTOModule::createFromFile(path, Options, sLastErrorString));
+ ErrorOr<std::unique_ptr<LTOModule>> M =
+ LTOModule::createFromFile(*LTOContext, path, Options);
+ if (!M)
+ return nullptr;
+ return wrap(M->release());
}
lto_module_t lto_module_create_from_fd(int fd, const char *path, size_t size) {
lto_initialize();
llvm::TargetOptions Options = InitTargetOptionsFromCodeGenFlags();
- return wrap(
- LTOModule::createFromOpenFile(fd, path, size, Options, sLastErrorString));
+ ErrorOr<std::unique_ptr<LTOModule>> M =
+ LTOModule::createFromOpenFile(*LTOContext, fd, path, size, Options);
+ if (!M)
+ return nullptr;
+ return wrap(M->release());
}
lto_module_t lto_module_create_from_fd_at_offset(int fd, const char *path,
@@ -163,14 +216,21 @@ lto_module_t lto_module_create_from_fd_at_offset(int fd, const char *path,
off_t offset) {
lto_initialize();
llvm::TargetOptions Options = InitTargetOptionsFromCodeGenFlags();
- return wrap(LTOModule::createFromOpenFileSlice(fd, path, map_size, offset,
- Options, sLastErrorString));
+ ErrorOr<std::unique_ptr<LTOModule>> M = LTOModule::createFromOpenFileSlice(
+ *LTOContext, fd, path, map_size, offset, Options);
+ if (!M)
+ return nullptr;
+ return wrap(M->release());
}
lto_module_t lto_module_create_from_memory(const void* mem, size_t length) {
lto_initialize();
llvm::TargetOptions Options = InitTargetOptionsFromCodeGenFlags();
- return wrap(LTOModule::createFromBuffer(mem, length, Options, sLastErrorString));
+ ErrorOr<std::unique_ptr<LTOModule>> M =
+ LTOModule::createFromBuffer(*LTOContext, mem, length, Options);
+ if (!M)
+ return nullptr;
+ return wrap(M->release());
}
lto_module_t lto_module_create_from_memory_with_path(const void* mem,
@@ -178,16 +238,22 @@ lto_module_t lto_module_create_from_memory_with_path(const void* mem,
const char *path) {
lto_initialize();
llvm::TargetOptions Options = InitTargetOptionsFromCodeGenFlags();
- return wrap(
- LTOModule::createFromBuffer(mem, length, Options, sLastErrorString, path));
+ ErrorOr<std::unique_ptr<LTOModule>> M =
+ LTOModule::createFromBuffer(*LTOContext, mem, length, Options, path);
+ if (!M)
+ return nullptr;
+ return wrap(M->release());
}
lto_module_t lto_module_create_in_local_context(const void *mem, size_t length,
const char *path) {
lto_initialize();
llvm::TargetOptions Options = InitTargetOptionsFromCodeGenFlags();
- return wrap(LTOModule::createInLocalContext(mem, length, Options,
- sLastErrorString, path));
+ ErrorOr<std::unique_ptr<LTOModule>> M =
+ LTOModule::createInLocalContext(mem, length, Options, path);
+ if (!M)
+ return nullptr;
+ return wrap(M->release());
}
lto_module_t lto_module_create_in_codegen_context(const void *mem,
@@ -196,8 +262,9 @@ lto_module_t lto_module_create_in_codegen_context(const void *mem,
lto_code_gen_t cg) {
lto_initialize();
llvm::TargetOptions Options = InitTargetOptionsFromCodeGenFlags();
- return wrap(LTOModule::createInContext(mem, length, Options, sLastErrorString,
- path, &unwrap(cg)->getContext()));
+ ErrorOr<std::unique_ptr<LTOModule>> M = LTOModule::createInContext(
+ mem, length, Options, path, &unwrap(cg)->getContext());
+ return wrap(M->release());
}
void lto_module_dispose(lto_module_t mod) { delete unwrap(mod); }
@@ -260,7 +327,7 @@ bool lto_codegen_add_module(lto_code_gen_t cg, lto_module_t mod) {
}
void lto_codegen_set_module(lto_code_gen_t cg, lto_module_t mod) {
- unwrap(cg)->setModule(unwrap(mod));
+ unwrap(cg)->setModule(std::unique_ptr<LTOModule>(unwrap(mod)));
}
bool lto_codegen_set_debug_model(lto_code_gen_t cg, lto_debug_model debug) {
@@ -269,8 +336,22 @@ bool lto_codegen_set_debug_model(lto_code_gen_t cg, lto_debug_model debug) {
}
bool lto_codegen_set_pic_model(lto_code_gen_t cg, lto_codegen_model model) {
- unwrap(cg)->setCodePICModel(model);
- return false;
+ switch (model) {
+ case LTO_CODEGEN_PIC_MODEL_STATIC:
+ unwrap(cg)->setCodePICModel(Reloc::Static);
+ return false;
+ case LTO_CODEGEN_PIC_MODEL_DYNAMIC:
+ unwrap(cg)->setCodePICModel(Reloc::PIC_);
+ return false;
+ case LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC:
+ unwrap(cg)->setCodePICModel(Reloc::DynamicNoPIC);
+ return false;
+ case LTO_CODEGEN_PIC_MODEL_DEFAULT:
+ unwrap(cg)->setCodePICModel(Reloc::Default);
+ return false;
+ }
+ sLastErrorString = "Unknown PIC model";
+ return true;
}
void lto_codegen_set_cpu(lto_code_gen_t cg, const char *cpu) {
@@ -301,14 +382,15 @@ static void maybeParseOptions(lto_code_gen_t cg) {
bool lto_codegen_write_merged_modules(lto_code_gen_t cg, const char *path) {
maybeParseOptions(cg);
- return !unwrap(cg)->writeMergedModules(path, sLastErrorString);
+ return !unwrap(cg)->writeMergedModules(path);
}
const void *lto_codegen_compile(lto_code_gen_t cg, size_t *length) {
maybeParseOptions(cg);
LibLTOCodeGenerator *CG = unwrap(cg);
- CG->NativeObjectFile = CG->compile(DisableInline, DisableGVNLoadPRE,
- DisableLTOVectorization, sLastErrorString);
+ CG->NativeObjectFile =
+ CG->compile(DisableVerify, DisableInline, DisableGVNLoadPRE,
+ DisableLTOVectorization);
if (!CG->NativeObjectFile)
return nullptr;
*length = CG->NativeObjectFile->getBufferSize();
@@ -317,15 +399,14 @@ const void *lto_codegen_compile(lto_code_gen_t cg, size_t *length) {
bool lto_codegen_optimize(lto_code_gen_t cg) {
maybeParseOptions(cg);
- return !unwrap(cg)->optimize(DisableInline,
- DisableGVNLoadPRE, DisableLTOVectorization,
- sLastErrorString);
+ return !unwrap(cg)->optimize(DisableVerify, DisableInline, DisableGVNLoadPRE,
+ DisableLTOVectorization);
}
const void *lto_codegen_compile_optimized(lto_code_gen_t cg, size_t *length) {
maybeParseOptions(cg);
LibLTOCodeGenerator *CG = unwrap(cg);
- CG->NativeObjectFile = CG->compileOptimized(sLastErrorString);
+ CG->NativeObjectFile = CG->compileOptimized();
if (!CG->NativeObjectFile)
return nullptr;
*length = CG->NativeObjectFile->getBufferSize();
@@ -335,8 +416,8 @@ const void *lto_codegen_compile_optimized(lto_code_gen_t cg, size_t *length) {
bool lto_codegen_compile_to_file(lto_code_gen_t cg, const char **name) {
maybeParseOptions(cg);
return !unwrap(cg)->compile_to_file(
- name, DisableInline, DisableGVNLoadPRE,
- DisableLTOVectorization, sLastErrorString);
+ name, DisableVerify, DisableInline, DisableGVNLoadPRE,
+ DisableLTOVectorization);
}
void lto_codegen_debug_options(lto_code_gen_t cg, const char *opt) {
diff --git a/tools/macho-dump/CMakeLists.txt b/tools/macho-dump/CMakeLists.txt
deleted file mode 100644
index bc2dfbf427c3..000000000000
--- a/tools/macho-dump/CMakeLists.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-set(LLVM_LINK_COMPONENTS
- Object
- Support
- )
-
-add_llvm_tool(macho-dump
- macho-dump.cpp
- )
diff --git a/tools/macho-dump/macho-dump.cpp b/tools/macho-dump/macho-dump.cpp
deleted file mode 100644
index 39c2860df355..000000000000
--- a/tools/macho-dump/macho-dump.cpp
+++ /dev/null
@@ -1,434 +0,0 @@
-//===-- macho-dump.cpp - Mach Object Dumping Tool -------------------------===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This is a testing tool for use with the MC/Mach-O LLVM components.
-//
-//===----------------------------------------------------------------------===//
-
-#include "llvm/Object/MachO.h"
-#include "llvm/ADT/StringExtras.h"
-#include "llvm/ADT/Twine.h"
-#include "llvm/Support/Casting.h"
-#include "llvm/Support/CommandLine.h"
-#include "llvm/Support/Format.h"
-#include "llvm/Support/ManagedStatic.h"
-#include "llvm/Support/MemoryBuffer.h"
-#include "llvm/Support/raw_ostream.h"
-#include <system_error>
-using namespace llvm;
-using namespace llvm::object;
-
-static cl::opt<std::string>
-InputFile(cl::Positional, cl::desc("<input file>"), cl::init("-"));
-
-static cl::opt<bool>
-ShowSectionData("dump-section-data", cl::desc("Dump the contents of sections"),
- cl::init(false));
-
-///
-
-static const char *ProgramName;
-
-static void Message(const char *Type, const Twine &Msg) {
- errs() << ProgramName << ": " << Type << ": " << Msg << "\n";
-}
-
-static int Error(const Twine &Msg) {
- Message("error", Msg);
- return 1;
-}
-
-static void Warning(const Twine &Msg) {
- Message("warning", Msg);
-}
-
-///
-
-static void DumpSegmentCommandData(StringRef Name,
- uint64_t VMAddr, uint64_t VMSize,
- uint64_t FileOffset, uint64_t FileSize,
- uint32_t MaxProt, uint32_t InitProt,
- uint32_t NumSections, uint32_t Flags) {
- outs() << " ('segment_name', '";
- outs().write_escaped(Name, /*UseHexEscapes=*/true) << "')\n";
- outs() << " ('vm_addr', " << VMAddr << ")\n";
- outs() << " ('vm_size', " << VMSize << ")\n";
- outs() << " ('file_offset', " << FileOffset << ")\n";
- outs() << " ('file_size', " << FileSize << ")\n";
- outs() << " ('maxprot', " << MaxProt << ")\n";
- outs() << " ('initprot', " << InitProt << ")\n";
- outs() << " ('num_sections', " << NumSections << ")\n";
- outs() << " ('flags', " << Flags << ")\n";
-}
-
-static int DumpSectionData(const MachOObjectFile &Obj, unsigned Index,
- StringRef Name,
- StringRef SegmentName, uint64_t Address,
- uint64_t Size, uint32_t Offset,
- uint32_t Align, uint32_t RelocationTableOffset,
- uint32_t NumRelocationTableEntries,
- uint32_t Flags, uint32_t Reserved1,
- uint32_t Reserved2, uint64_t Reserved3 = ~0ULL) {
- outs() << " # Section " << Index << "\n";
- outs() << " (('section_name', '";
- outs().write_escaped(Name, /*UseHexEscapes=*/true) << "')\n";
- outs() << " ('segment_name', '";
- outs().write_escaped(SegmentName, /*UseHexEscapes=*/true) << "')\n";
- outs() << " ('address', " << Address << ")\n";
- outs() << " ('size', " << Size << ")\n";
- outs() << " ('offset', " << Offset << ")\n";
- outs() << " ('alignment', " << Align << ")\n";
- outs() << " ('reloc_offset', " << RelocationTableOffset << ")\n";
- outs() << " ('num_reloc', " << NumRelocationTableEntries << ")\n";
- outs() << " ('flags', " << format("0x%x", Flags) << ")\n";
- outs() << " ('reserved1', " << Reserved1 << ")\n";
- outs() << " ('reserved2', " << Reserved2 << ")\n";
- if (Reserved3 != ~0ULL)
- outs() << " ('reserved3', " << Reserved3 << ")\n";
- outs() << " ),\n";
-
- // Dump the relocation entries.
- outs() << " ('_relocations', [\n";
- unsigned RelNum = 0;
- for (relocation_iterator I = Obj.section_rel_begin(Index),
- E = Obj.section_rel_end(Index);
- I != E; ++I, ++RelNum) {
- MachO::any_relocation_info RE = Obj.getRelocation(I->getRawDataRefImpl());
- outs() << " # Relocation " << RelNum << "\n";
- outs() << " (('word-0', " << format("0x%x", RE.r_word0) << "),\n";
- outs() << " ('word-1', " << format("0x%x", RE.r_word1) << ")),\n";
- }
- outs() << " ])\n";
-
- // Dump the section data, if requested.
- if (ShowSectionData) {
- outs() << " ('_section_data', '";
- StringRef Data = Obj.getData().substr(Offset, Size);
- for (unsigned i = 0; i != Data.size(); ++i) {
- if (i && (i % 4) == 0)
- outs() << ' ';
- outs() << hexdigit((Data[i] >> 4) & 0xF, /*LowerCase=*/true);
- outs() << hexdigit((Data[i] >> 0) & 0xF, /*LowerCase=*/true);
- }
- outs() << "')\n";
- }
-
- return 0;
-}
-
-static int DumpSegmentCommand(const MachOObjectFile &Obj,
- const MachOObjectFile::LoadCommandInfo &LCI) {
- MachO::segment_command SLC = Obj.getSegmentLoadCommand(LCI);
-
- DumpSegmentCommandData(StringRef(SLC.segname, 16), SLC.vmaddr,
- SLC.vmsize, SLC.fileoff, SLC.filesize,
- SLC.maxprot, SLC.initprot, SLC.nsects, SLC.flags);
-
- // Dump the sections.
- outs() << " ('sections', [\n";
- for (unsigned i = 0; i != SLC.nsects; ++i) {
- MachO::section Sect = Obj.getSection(LCI, i);
- DumpSectionData(Obj, i, StringRef(Sect.sectname, 16),
- StringRef(Sect.segname, 16), Sect.addr,
- Sect.size, Sect.offset, Sect.align,
- Sect.reloff, Sect.nreloc, Sect.flags,
- Sect.reserved1, Sect.reserved2);
- }
- outs() << " ])\n";
-
- return 0;
-}
-
-static int DumpSegment64Command(const MachOObjectFile &Obj,
- const MachOObjectFile::LoadCommandInfo &LCI) {
- MachO::segment_command_64 SLC = Obj.getSegment64LoadCommand(LCI);
- DumpSegmentCommandData(StringRef(SLC.segname, 16), SLC.vmaddr,
- SLC.vmsize, SLC.fileoff, SLC.filesize,
- SLC.maxprot, SLC.initprot, SLC.nsects, SLC.flags);
-
- // Dump the sections.
- outs() << " ('sections', [\n";
- for (unsigned i = 0; i != SLC.nsects; ++i) {
- MachO::section_64 Sect = Obj.getSection64(LCI, i);
-
- DumpSectionData(Obj, i, StringRef(Sect.sectname, 16),
- StringRef(Sect.segname, 16), Sect.addr,
- Sect.size, Sect.offset, Sect.align,
- Sect.reloff, Sect.nreloc, Sect.flags,
- Sect.reserved1, Sect.reserved2,
- Sect.reserved3);
- }
- outs() << " ])\n";
-
- return 0;
-}
-
-static void DumpSymbolTableEntryData(const MachOObjectFile &Obj,
- unsigned Index, uint32_t StringIndex,
- uint8_t Type, uint8_t SectionIndex,
- uint16_t Flags, uint64_t Value,
- StringRef StringTable) {
- const char *Name = &StringTable.data()[StringIndex];
- outs() << " # Symbol " << Index << "\n";
- outs() << " (('n_strx', " << StringIndex << ")\n";
- outs() << " ('n_type', " << format("0x%x", Type) << ")\n";
- outs() << " ('n_sect', " << uint32_t(SectionIndex) << ")\n";
- outs() << " ('n_desc', " << Flags << ")\n";
- outs() << " ('n_value', " << Value << ")\n";
- outs() << " ('_string', '" << Name << "')\n";
- outs() << " ),\n";
-}
-
-static int DumpSymtabCommand(const MachOObjectFile &Obj) {
- MachO::symtab_command SLC = Obj.getSymtabLoadCommand();
-
- outs() << " ('symoff', " << SLC.symoff << ")\n";
- outs() << " ('nsyms', " << SLC.nsyms << ")\n";
- outs() << " ('stroff', " << SLC.stroff << ")\n";
- outs() << " ('strsize', " << SLC.strsize << ")\n";
-
- // Dump the string data.
- outs() << " ('_string_data', '";
- StringRef StringTable = Obj.getStringTableData();
- outs().write_escaped(StringTable,
- /*UseHexEscapes=*/true) << "')\n";
-
- // Dump the symbol table.
- outs() << " ('_symbols', [\n";
- unsigned SymNum = 0;
- for (const SymbolRef &Symbol : Obj.symbols()) {
- DataRefImpl DRI = Symbol.getRawDataRefImpl();
- if (Obj.is64Bit()) {
- MachO::nlist_64 STE = Obj.getSymbol64TableEntry(DRI);
- DumpSymbolTableEntryData(Obj, SymNum, STE.n_strx, STE.n_type,
- STE.n_sect, STE.n_desc, STE.n_value,
- StringTable);
- } else {
- MachO::nlist STE = Obj.getSymbolTableEntry(DRI);
- DumpSymbolTableEntryData(Obj, SymNum, STE.n_strx, STE.n_type,
- STE.n_sect, STE.n_desc, STE.n_value,
- StringTable);
- }
- SymNum++;
- }
- outs() << " ])\n";
-
- return 0;
-}
-
-static int DumpDysymtabCommand(const MachOObjectFile &Obj) {
- MachO::dysymtab_command DLC = Obj.getDysymtabLoadCommand();
-
- outs() << " ('ilocalsym', " << DLC.ilocalsym << ")\n";
- outs() << " ('nlocalsym', " << DLC.nlocalsym << ")\n";
- outs() << " ('iextdefsym', " << DLC.iextdefsym << ")\n";
- outs() << " ('nextdefsym', " << DLC.nextdefsym << ")\n";
- outs() << " ('iundefsym', " << DLC.iundefsym << ")\n";
- outs() << " ('nundefsym', " << DLC.nundefsym << ")\n";
- outs() << " ('tocoff', " << DLC.tocoff << ")\n";
- outs() << " ('ntoc', " << DLC.ntoc << ")\n";
- outs() << " ('modtaboff', " << DLC.modtaboff << ")\n";
- outs() << " ('nmodtab', " << DLC.nmodtab << ")\n";
- outs() << " ('extrefsymoff', " << DLC.extrefsymoff << ")\n";
- outs() << " ('nextrefsyms', " << DLC.nextrefsyms << ")\n";
- outs() << " ('indirectsymoff', " << DLC.indirectsymoff << ")\n";
- outs() << " ('nindirectsyms', " << DLC.nindirectsyms << ")\n";
- outs() << " ('extreloff', " << DLC.extreloff << ")\n";
- outs() << " ('nextrel', " << DLC.nextrel << ")\n";
- outs() << " ('locreloff', " << DLC.locreloff << ")\n";
- outs() << " ('nlocrel', " << DLC.nlocrel << ")\n";
-
- // Dump the indirect symbol table.
- outs() << " ('_indirect_symbols', [\n";
- for (unsigned i = 0; i != DLC.nindirectsyms; ++i) {
- uint32_t ISTE = Obj.getIndirectSymbolTableEntry(DLC, i);
- outs() << " # Indirect Symbol " << i << "\n";
- outs() << " (('symbol_index', " << format("0x%x", ISTE) << "),),\n";
- }
- outs() << " ])\n";
-
- return 0;
-}
-
-static int
-DumpLinkeditDataCommand(const MachOObjectFile &Obj,
- const MachOObjectFile::LoadCommandInfo &LCI) {
- MachO::linkedit_data_command LLC = Obj.getLinkeditDataLoadCommand(LCI);
- outs() << " ('dataoff', " << LLC.dataoff << ")\n"
- << " ('datasize', " << LLC.datasize << ")\n"
- << " ('_addresses', [\n";
-
- SmallVector<uint64_t, 8> Addresses;
- Obj.ReadULEB128s(LLC.dataoff, Addresses);
- for (unsigned i = 0, e = Addresses.size(); i != e; ++i)
- outs() << " # Address " << i << '\n'
- << " ('address', " << format("0x%x", Addresses[i]) << "),\n";
-
- outs() << " ])\n";
-
- return 0;
-}
-
-static int
-DumpDataInCodeDataCommand(const MachOObjectFile &Obj,
- const MachOObjectFile::LoadCommandInfo &LCI) {
- MachO::linkedit_data_command LLC = Obj.getLinkeditDataLoadCommand(LCI);
- outs() << " ('dataoff', " << LLC.dataoff << ")\n"
- << " ('datasize', " << LLC.datasize << ")\n"
- << " ('_data_regions', [\n";
-
- unsigned NumRegions = LLC.datasize / sizeof(MachO::data_in_code_entry);
- for (unsigned i = 0; i < NumRegions; ++i) {
- MachO::data_in_code_entry DICE= Obj.getDataInCodeTableEntry(LLC.dataoff, i);
- outs() << " # DICE " << i << "\n"
- << " ('offset', " << DICE.offset << ")\n"
- << " ('length', " << DICE.length << ")\n"
- << " ('kind', " << DICE.kind << ")\n";
- }
-
- outs() <<" ])\n";
-
- return 0;
-}
-
-static int
-DumpLinkerOptionsCommand(const MachOObjectFile &Obj,
- const MachOObjectFile::LoadCommandInfo &LCI) {
- MachO::linker_option_command LOLC = Obj.getLinkerOptionLoadCommand(LCI);
- outs() << " ('count', " << LOLC.count << ")\n"
- << " ('_strings', [\n";
-
- uint64_t DataSize = LOLC.cmdsize - sizeof(MachO::linker_option_command);
- const char *P = LCI.Ptr + sizeof(MachO::linker_option_command);
- StringRef Data(P, DataSize);
- for (unsigned i = 0; i != LOLC.count; ++i) {
- std::pair<StringRef,StringRef> Split = Data.split('\0');
- outs() << "\t\"";
- outs().write_escaped(Split.first);
- outs() << "\",\n";
- Data = Split.second;
- }
- outs() <<" ])\n";
-
- return 0;
-}
-
-static int
-DumpVersionMin(const MachOObjectFile &Obj,
- const MachOObjectFile::LoadCommandInfo &LCI) {
- MachO::version_min_command VMLC = Obj.getVersionMinLoadCommand(LCI);
- outs() << " ('version, " << VMLC.version << ")\n"
- << " ('sdk, " << VMLC.sdk << ")\n";
- return 0;
-}
-
-static int
-DumpDylibID(const MachOObjectFile &Obj,
- const MachOObjectFile::LoadCommandInfo &LCI) {
- MachO::dylib_command DLLC = Obj.getDylibIDLoadCommand(LCI);
- outs() << " ('install_name', '" << LCI.Ptr + DLLC.dylib.name << "')\n"
- << " ('timestamp, " << DLLC.dylib.timestamp << ")\n"
- << " ('cur_version, " << DLLC.dylib.current_version << ")\n"
- << " ('compat_version, " << DLLC.dylib.compatibility_version << ")\n";
- return 0;
-}
-
-static int DumpLoadCommand(const MachOObjectFile &Obj,
- const MachOObjectFile::LoadCommandInfo &LCI) {
- switch (LCI.C.cmd) {
- case MachO::LC_SEGMENT:
- return DumpSegmentCommand(Obj, LCI);
- case MachO::LC_SEGMENT_64:
- return DumpSegment64Command(Obj, LCI);
- case MachO::LC_SYMTAB:
- return DumpSymtabCommand(Obj);
- case MachO::LC_DYSYMTAB:
- return DumpDysymtabCommand(Obj);
- case MachO::LC_CODE_SIGNATURE:
- case MachO::LC_SEGMENT_SPLIT_INFO:
- case MachO::LC_FUNCTION_STARTS:
- return DumpLinkeditDataCommand(Obj, LCI);
- case MachO::LC_DATA_IN_CODE:
- return DumpDataInCodeDataCommand(Obj, LCI);
- case MachO::LC_LINKER_OPTION:
- return DumpLinkerOptionsCommand(Obj, LCI);
- case MachO::LC_VERSION_MIN_IPHONEOS:
- case MachO::LC_VERSION_MIN_MACOSX:
- return DumpVersionMin(Obj, LCI);
- case MachO::LC_ID_DYLIB:
- return DumpDylibID(Obj, LCI);
- default:
- Warning("unknown load command: " + Twine(LCI.C.cmd));
- return 0;
- }
-}
-
-static int DumpLoadCommand(const MachOObjectFile &Obj, unsigned Index,
- const MachOObjectFile::LoadCommandInfo &LCI) {
- outs() << " # Load Command " << Index << "\n"
- << " (('command', " << LCI.C.cmd << ")\n"
- << " ('size', " << LCI.C.cmdsize << ")\n";
- int Res = DumpLoadCommand(Obj, LCI);
- outs() << " ),\n";
- return Res;
-}
-
-static void printHeader(const MachOObjectFile *Obj,
- const MachO::mach_header &Header) {
- outs() << "('cputype', " << Header.cputype << ")\n";
- outs() << "('cpusubtype', " << Header.cpusubtype << ")\n";
- outs() << "('filetype', " << Header.filetype << ")\n";
- outs() << "('num_load_commands', " << Header.ncmds << ")\n";
- outs() << "('load_commands_size', " << Header.sizeofcmds << ")\n";
- outs() << "('flag', " << Header.flags << ")\n";
-
- // Print extended header if 64-bit.
- if (Obj->is64Bit()) {
- const MachO::mach_header_64 *Header64 =
- reinterpret_cast<const MachO::mach_header_64 *>(&Header);
- outs() << "('reserved', " << Header64->reserved << ")\n";
- }
-}
-
-int main(int argc, char **argv) {
- ProgramName = argv[0];
- llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
-
- cl::ParseCommandLineOptions(argc, argv, "llvm Mach-O dumping tool\n");
-
- ErrorOr<OwningBinary<Binary>> BinaryOrErr = createBinary(InputFile);
- if (std::error_code EC = BinaryOrErr.getError())
- return Error("unable to read input: '" + EC.message() + "'");
- Binary &Binary = *BinaryOrErr.get().getBinary();
-
- const MachOObjectFile *InputObject = dyn_cast<MachOObjectFile>(&Binary);
- if (!InputObject)
- return Error("Not a MachO object");
-
- // Print the header
- MachO::mach_header_64 Header64;
- MachO::mach_header *Header = reinterpret_cast<MachO::mach_header*>(&Header64);
- if (InputObject->is64Bit())
- Header64 = InputObject->getHeader64();
- else
- *Header = InputObject->getHeader();
- printHeader(InputObject, *Header);
-
- // Print the load commands.
- int Res = 0;
- unsigned Index = 0;
- outs() << "('load_commands', [\n";
- for (const auto &Load : InputObject->load_commands()) {
- if (DumpLoadCommand(*InputObject, Index++, Load))
- break;
- }
- outs() << "])\n";
-
- return Res;
-}
diff --git a/tools/obj2yaml/elf2yaml.cpp b/tools/obj2yaml/elf2yaml.cpp
index f117a10d3822..f2b013807345 100644
--- a/tools/obj2yaml/elf2yaml.cpp
+++ b/tools/obj2yaml/elf2yaml.cpp
@@ -26,14 +26,15 @@ class ELFDumper {
typedef typename object::ELFFile<ELFT>::Elf_Word Elf_Word;
const object::ELFFile<ELFT> &Obj;
+ ArrayRef<Elf_Word> ShndxTable;
- std::error_code dumpSymbol(const Elf_Sym *Sym, bool IsDynamic,
- ELFYAML::Symbol &S);
+ std::error_code dumpSymbol(const Elf_Sym *Sym, const Elf_Shdr *SymTab,
+ StringRef StrTable, ELFYAML::Symbol &S);
std::error_code dumpCommonSection(const Elf_Shdr *Shdr, ELFYAML::Section &S);
std::error_code dumpCommonRelocationSection(const Elf_Shdr *Shdr,
ELFYAML::RelocationSection &S);
template <class RelT>
- std::error_code dumpRelocation(const Elf_Shdr *Shdr, const RelT *Rel,
+ std::error_code dumpRelocation(const RelT *Rel, const Elf_Shdr *SymTab,
ELFYAML::Relocation &R);
ErrorOr<ELFYAML::RelocationSection *> dumpRelSection(const Elf_Shdr *Shdr);
@@ -68,15 +69,26 @@ ErrorOr<ELFYAML::Object *> ELFDumper<ELFT>::dump() {
Y->Header.Flags = Obj.getHeader()->e_flags;
Y->Header.Entry = Obj.getHeader()->e_entry;
+ const Elf_Shdr *Symtab = nullptr;
+
// Dump sections
for (const Elf_Shdr &Sec : Obj.sections()) {
switch (Sec.sh_type) {
case ELF::SHT_NULL:
- case ELF::SHT_SYMTAB:
case ELF::SHT_DYNSYM:
case ELF::SHT_STRTAB:
// Do not dump these sections.
break;
+ case ELF::SHT_SYMTAB:
+ Symtab = &Sec;
+ break;
+ case ELF::SHT_SYMTAB_SHNDX: {
+ ErrorOr<ArrayRef<Elf_Word>> TableOrErr = Obj.getSHNDXTable(Sec);
+ if (std::error_code EC = TableOrErr.getError())
+ return EC;
+ ShndxTable = *TableOrErr;
+ break;
+ }
case ELF::SHT_RELA: {
ErrorOr<ELFYAML::RelocationSection *> S = dumpRelaSection(&Sec);
if (std::error_code EC = S.getError())
@@ -122,18 +134,24 @@ ErrorOr<ELFYAML::Object *> ELFDumper<ELFT>::dump() {
}
// Dump symbols
+ ErrorOr<StringRef> StrTableOrErr = Obj.getStringTableForSymtab(*Symtab);
+ if (std::error_code EC = StrTableOrErr.getError())
+ return EC;
+ StringRef StrTable = *StrTableOrErr;
+
bool IsFirstSym = true;
- for (auto SI = Obj.symbol_begin(), SE = Obj.symbol_end(); SI != SE; ++SI) {
+ for (const Elf_Sym &Sym : Obj.symbols(Symtab)) {
if (IsFirstSym) {
IsFirstSym = false;
continue;
}
ELFYAML::Symbol S;
- if (std::error_code EC = ELFDumper<ELFT>::dumpSymbol(SI, false, S))
+ if (std::error_code EC =
+ ELFDumper<ELFT>::dumpSymbol(&Sym, Symtab, StrTable, S))
return EC;
- switch (SI->getBinding())
+ switch (Sym.getBinding())
{
case ELF::STB_LOCAL:
Y->Symbols.Local.push_back(S);
@@ -153,19 +171,20 @@ ErrorOr<ELFYAML::Object *> ELFDumper<ELFT>::dump() {
}
template <class ELFT>
-std::error_code ELFDumper<ELFT>::dumpSymbol(const Elf_Sym *Sym, bool IsDynamic,
- ELFYAML::Symbol &S) {
+std::error_code
+ELFDumper<ELFT>::dumpSymbol(const Elf_Sym *Sym, const Elf_Shdr *SymTab,
+ StringRef StrTable, ELFYAML::Symbol &S) {
S.Type = Sym->getType();
S.Value = Sym->st_value;
S.Size = Sym->st_size;
S.Other = Sym->st_other;
- ErrorOr<StringRef> NameOrErr = Obj.getSymbolName(Sym, IsDynamic);
+ ErrorOr<StringRef> NameOrErr = Sym->getName(StrTable);
if (std::error_code EC = NameOrErr.getError())
return EC;
S.Name = NameOrErr.get();
- ErrorOr<const Elf_Shdr *> ShdrOrErr = Obj.getSection(&*Sym);
+ ErrorOr<const Elf_Shdr *> ShdrOrErr = Obj.getSection(Sym, SymTab, ShndxTable);
if (std::error_code EC = ShdrOrErr.getError())
return EC;
const Elf_Shdr *Shdr = *ShdrOrErr;
@@ -182,18 +201,14 @@ std::error_code ELFDumper<ELFT>::dumpSymbol(const Elf_Sym *Sym, bool IsDynamic,
template <class ELFT>
template <class RelT>
-std::error_code ELFDumper<ELFT>::dumpRelocation(const Elf_Shdr *Shdr,
- const RelT *Rel,
+std::error_code ELFDumper<ELFT>::dumpRelocation(const RelT *Rel,
+ const Elf_Shdr *SymTab,
ELFYAML::Relocation &R) {
R.Type = Rel->getType(Obj.isMips64EL());
R.Offset = Rel->r_offset;
R.Addend = 0;
- auto NamePair = Obj.getRelocationSymbol(Shdr, Rel);
- if (!NamePair.first)
- return obj2yaml_error::success;
-
- const Elf_Shdr *SymTab = NamePair.first;
+ const Elf_Sym *Sym = Obj.getRelocationSymbol(Rel, SymTab);
ErrorOr<const Elf_Shdr *> StrTabSec = Obj.getSection(SymTab->sh_link);
if (std::error_code EC = StrTabSec.getError())
return EC;
@@ -202,7 +217,7 @@ std::error_code ELFDumper<ELFT>::dumpRelocation(const Elf_Shdr *Shdr,
return EC;
StringRef StrTab = *StrTabOrErr;
- ErrorOr<StringRef> NameOrErr = NamePair.second->getName(StrTab);
+ ErrorOr<StringRef> NameOrErr = Sym->getName(StrTab);
if (std::error_code EC = NameOrErr.getError())
return EC;
R.Symbol = NameOrErr.get();
@@ -264,9 +279,14 @@ ELFDumper<ELFT>::dumpRelSection(const Elf_Shdr *Shdr) {
if (std::error_code EC = dumpCommonRelocationSection(Shdr, *S))
return EC;
+ ErrorOr<const Elf_Shdr *> SymTabOrErr = Obj.getSection(Shdr->sh_link);
+ if (std::error_code EC = SymTabOrErr.getError())
+ return EC;
+ const Elf_Shdr *SymTab = *SymTabOrErr;
+
for (auto RI = Obj.rel_begin(Shdr), RE = Obj.rel_end(Shdr); RI != RE; ++RI) {
ELFYAML::Relocation R;
- if (std::error_code EC = dumpRelocation(Shdr, &*RI, R))
+ if (std::error_code EC = dumpRelocation(&*RI, SymTab, R))
return EC;
S->Relocations.push_back(R);
}
@@ -283,10 +303,15 @@ ELFDumper<ELFT>::dumpRelaSection(const Elf_Shdr *Shdr) {
if (std::error_code EC = dumpCommonRelocationSection(Shdr, *S))
return EC;
+ ErrorOr<const Elf_Shdr *> SymTabOrErr = Obj.getSection(Shdr->sh_link);
+ if (std::error_code EC = SymTabOrErr.getError())
+ return EC;
+ const Elf_Shdr *SymTab = *SymTabOrErr;
+
for (auto RI = Obj.rela_begin(Shdr), RE = Obj.rela_end(Shdr); RI != RE;
++RI) {
ELFYAML::Relocation R;
- if (std::error_code EC = dumpRelocation(Shdr, &*RI, R))
+ if (std::error_code EC = dumpRelocation(&*RI, SymTab, R))
return EC;
R.Addend = RI->r_addend;
S->Relocations.push_back(R);
@@ -331,14 +356,12 @@ ErrorOr<ELFYAML::Group *> ELFDumper<ELFT>::dumpGroup(const Elf_Shdr *Shdr) {
if (std::error_code EC = dumpCommonSection(Shdr, *S))
return EC;
// Get sh_info which is the signature.
- const Elf_Sym *symbol = Obj.getSymbol(Shdr->sh_info);
- ErrorOr<const Elf_Shdr *> Symtab = Obj.getSection(Shdr->sh_link);
- if (std::error_code EC = Symtab.getError())
- return EC;
- ErrorOr<const Elf_Shdr *> StrTabSec = Obj.getSection((*Symtab)->sh_link);
- if (std::error_code EC = StrTabSec.getError())
+ ErrorOr<const Elf_Shdr *> SymtabOrErr = Obj.getSection(Shdr->sh_link);
+ if (std::error_code EC = SymtabOrErr.getError())
return EC;
- ErrorOr<StringRef> StrTabOrErr = Obj.getStringTable(*StrTabSec);
+ const Elf_Shdr *Symtab = *SymtabOrErr;
+ const Elf_Sym *symbol = Obj.getSymbol(Symtab, Shdr->sh_info);
+ ErrorOr<StringRef> StrTabOrErr = Obj.getStringTableForSymtab(*Symtab);
if (std::error_code EC = StrTabOrErr.getError())
return EC;
StringRef StrTab = *StrTabOrErr;
diff --git a/tools/obj2yaml/obj2yaml.cpp b/tools/obj2yaml/obj2yaml.cpp
index b64096d75fa4..ee6284da6e41 100644
--- a/tools/obj2yaml/obj2yaml.cpp
+++ b/tools/obj2yaml/obj2yaml.cpp
@@ -29,9 +29,6 @@ static std::error_code dumpObject(const ObjectFile &Obj) {
}
static std::error_code dumpInput(StringRef File) {
- if (File != "-" && !sys::fs::exists(File))
- return obj2yaml_error::file_not_found;
-
ErrorOr<OwningBinary<Binary>> BinaryOrErr = createBinary(File);
if (std::error_code EC = BinaryOrErr.getError())
return EC;
diff --git a/tools/opt/CMakeLists.txt b/tools/opt/CMakeLists.txt
index 5f825220cc88..9194a6ba0985 100644
--- a/tools/opt/CMakeLists.txt
+++ b/tools/opt/CMakeLists.txt
@@ -4,7 +4,6 @@ set(LLVM_LINK_COMPONENTS
BitWriter
CodeGen
Core
- IPA
IPO
IRReader
InstCombine
diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp
index 0db60d144409..fe1605aa8436 100644
--- a/tools/opt/opt.cpp
+++ b/tools/opt/opt.cpp
@@ -28,6 +28,7 @@
#include "llvm/IR/DebugInfo.h"
#include "llvm/IR/IRPrintingPasses.h"
#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/LegacyPassNameParser.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Verifier.h"
@@ -36,7 +37,6 @@
#include "llvm/LinkAllIR.h"
#include "llvm/LinkAllPasses.h"
#include "llvm/MC/SubtargetFeature.h"
-#include "llvm/IR/LegacyPassManager.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Host.h"
@@ -51,6 +51,7 @@
#include "llvm/Support/ToolOutputFile.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
+#include "llvm/Transforms/Utils/Cloning.h"
#include <algorithm>
#include <memory>
using namespace llvm;
@@ -190,6 +191,11 @@ static cl::opt<bool> PreserveAssemblyUseListOrder(
cl::desc("Preserve use-list order when writing LLVM assembly."),
cl::init(false), cl::Hidden);
+static cl::opt<bool>
+ RunTwice("run-twice",
+ cl::desc("Run all passes twice, re-using the same pass manager."),
+ cl::init(false), cl::Hidden);
+
static inline void addPass(legacy::PassManagerBase &PM, Pass *P) {
// Add the pass to the pass manager...
PM.add(P);
@@ -312,7 +318,6 @@ int main(int argc, char **argv) {
initializeVectorization(Registry);
initializeIPO(Registry);
initializeAnalysis(Registry);
- initializeIPA(Registry);
initializeTransformUtils(Registry);
initializeInstCombine(Registry);
initializeInstrumentation(Registry);
@@ -583,22 +588,61 @@ int main(int argc, char **argv) {
if (!NoVerify && !VerifyEach)
Passes.add(createVerifierPass());
+ // In run twice mode, we want to make sure the output is bit-by-bit
+ // equivalent if we run the pass manager again, so setup two buffers and
+ // a stream to write to them. Note that llc does something similar and it
+ // may be worth to abstract this out in the future.
+ SmallVector<char, 0> Buffer;
+ SmallVector<char, 0> CompileTwiceBuffer;
+ std::unique_ptr<raw_svector_ostream> BOS;
+ raw_ostream *OS = nullptr;
+
// Write bitcode or assembly to the output as the last step...
if (!NoOutput && !AnalyzeOnly) {
+ assert(Out);
+ OS = &Out->os();
+ if (RunTwice) {
+ BOS = make_unique<raw_svector_ostream>(Buffer);
+ OS = BOS.get();
+ }
if (OutputAssembly)
- Passes.add(
- createPrintModulePass(Out->os(), "", PreserveAssemblyUseListOrder));
+ Passes.add(createPrintModulePass(*OS, "", PreserveAssemblyUseListOrder));
else
- Passes.add(
- createBitcodeWriterPass(Out->os(), PreserveBitcodeUseListOrder));
+ Passes.add(createBitcodeWriterPass(*OS, PreserveBitcodeUseListOrder));
}
// Before executing passes, print the final values of the LLVM options.
cl::PrintOptionValues();
+ // If requested, run all passes again with the same pass manager to catch
+ // bugs caused by persistent state in the passes
+ if (RunTwice) {
+ std::unique_ptr<Module> M2(CloneModule(M.get()));
+ Passes.run(*M2);
+ CompileTwiceBuffer = Buffer;
+ Buffer.clear();
+ }
+
// Now that we have all of the passes ready, run them.
Passes.run(*M);
+ // Compare the two outputs and make sure they're the same
+ if (RunTwice) {
+ assert(Out);
+ if (Buffer.size() != CompileTwiceBuffer.size() ||
+ (memcmp(Buffer.data(), CompileTwiceBuffer.data(), Buffer.size()) !=
+ 0)) {
+ errs() << "Running the pass manager twice changed the output.\n"
+ "Writing the result of the second run to the specified output.\n"
+ "To generate the one-run comparison binary, just run without\n"
+ "the compile-twice option\n";
+ Out->os() << BOS->str();
+ Out->keep();
+ return 1;
+ }
+ Out->os() << BOS->str();
+ }
+
// Declare success.
if (!NoOutput || PrintBreakpoints)
Out->keep();
diff --git a/tools/sancov/CMakeLists.txt b/tools/sancov/CMakeLists.txt
new file mode 100644
index 000000000000..e92b1fcbb862
--- /dev/null
+++ b/tools/sancov/CMakeLists.txt
@@ -0,0 +1,17 @@
+set(LLVM_LINK_COMPONENTS
+ AllTargetsAsmPrinters
+ AllTargetsDescs
+ AllTargetsDisassemblers
+ AllTargetsInfos
+ DebugInfoDWARF
+ DebugInfoPDB
+ MC
+ MCDisassembler
+ Object
+ Support
+ Symbolize
+ )
+
+add_llvm_tool(sancov
+ sancov.cc
+ )
diff --git a/tools/sancov/Makefile b/tools/sancov/Makefile
new file mode 100644
index 000000000000..7dba1a7a594a
--- /dev/null
+++ b/tools/sancov/Makefile
@@ -0,0 +1,18 @@
+##===- tools/sancov/Makefile ----------------------*- Makefile -*-===##
+#
+# The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+##===----------------------------------------------------------------------===##
+
+LEVEL := ../..
+TOOLNAME := sancov
+LINK_COMPONENTS := all-targets DebugInfoDWARF DebugInfoPDB MC MCParser \
+ MCDisassembler Object Support Symbolize
+
+# This tool has no plugins, optimize startup time.
+TOOL_NO_EXPORTS := 1
+
+include $(LEVEL)/Makefile.common
diff --git a/tools/sancov/sancov.cc b/tools/sancov/sancov.cc
new file mode 100644
index 000000000000..a07cdbe097a3
--- /dev/null
+++ b/tools/sancov/sancov.cc
@@ -0,0 +1,533 @@
+//===-- sancov.cc --------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a command-line tool for reading and analyzing sanitizer
+// coverage.
+//===----------------------------------------------------------------------===//
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/DebugInfo/Symbolize/Symbolize.h"
+#include "llvm/MC/MCAsmInfo.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCDisassembler.h"
+#include "llvm/MC/MCInst.h"
+#include "llvm/MC/MCInstPrinter.h"
+#include "llvm/MC/MCInstrAnalysis.h"
+#include "llvm/MC/MCInstrInfo.h"
+#include "llvm/MC/MCObjectFileInfo.h"
+#include "llvm/MC/MCRegisterInfo.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/Object/Archive.h"
+#include "llvm/Object/Binary.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/ErrorOr.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/LineIterator.h"
+#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/PrettyStackTrace.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/SpecialCaseList.h"
+#include "llvm/Support/TargetRegistry.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <set>
+#include <stdio.h>
+#include <string>
+#include <vector>
+
+using namespace llvm;
+
+namespace {
+
+// --------- COMMAND LINE FLAGS ---------
+
+enum ActionType {
+ PrintAction,
+ CoveredFunctionsAction,
+ NotCoveredFunctionsAction
+};
+
+cl::opt<ActionType> Action(
+ cl::desc("Action (required)"), cl::Required,
+ cl::values(clEnumValN(PrintAction, "print", "Print coverage addresses"),
+ clEnumValN(CoveredFunctionsAction, "covered-functions",
+ "Print all covered funcions."),
+ clEnumValN(NotCoveredFunctionsAction, "not-covered-functions",
+ "Print all not covered funcions."),
+ clEnumValEnd));
+
+static cl::list<std::string> ClInputFiles(cl::Positional, cl::OneOrMore,
+ cl::desc("<filenames...>"));
+
+static cl::opt<std::string>
+ ClBinaryName("obj", cl::Required,
+ cl::desc("Path to object file to be symbolized"));
+
+static cl::opt<bool>
+ ClDemangle("demangle", cl::init(true),
+ cl::desc("Print demangled function name."));
+
+static cl::opt<std::string> ClStripPathPrefix(
+ "strip_path_prefix", cl::init(""),
+ cl::desc("Strip this prefix from file paths in reports."));
+
+static cl::opt<std::string>
+ ClBlacklist("blacklist", cl::init(""),
+ cl::desc("Blacklist file (sanitizer blacklist format)."));
+
+static cl::opt<bool> ClUseDefaultBlacklist(
+ "use_default_blacklist", cl::init(true), cl::Hidden,
+ cl::desc("Controls if default blacklist should be used."));
+
+static const char *const DefaultBlacklist = "fun:__sanitizer_*";
+
+// --------- FORMAT SPECIFICATION ---------
+
+struct FileHeader {
+ uint32_t Bitness;
+ uint32_t Magic;
+};
+
+static const uint32_t BinCoverageMagic = 0xC0BFFFFF;
+static const uint32_t Bitness32 = 0xFFFFFF32;
+static const uint32_t Bitness64 = 0xFFFFFF64;
+
+// ---------
+
+static void FailIfError(std::error_code Error) {
+ if (!Error)
+ return;
+ errs() << "Error: " << Error.message() << "(" << Error.value() << ")\n";
+ exit(1);
+}
+
+template <typename T> static void FailIfError(const ErrorOr<T> &E) {
+ FailIfError(E.getError());
+}
+
+static void FailIfNotEmpty(const std::string &E) {
+ if (E.empty())
+ return;
+ errs() << "Error: " << E << "\n";
+ exit(1);
+}
+
+template <typename T>
+static void FailIfEmpty(const std::unique_ptr<T> &Ptr,
+ const std::string &Message) {
+ if (Ptr.get())
+ return;
+ errs() << "Error: " << Message << "\n";
+ exit(1);
+}
+
+template <typename T>
+static void readInts(const char *Start, const char *End,
+ std::set<uint64_t> *Ints) {
+ const T *S = reinterpret_cast<const T *>(Start);
+ const T *E = reinterpret_cast<const T *>(End);
+ std::copy(S, E, std::inserter(*Ints, Ints->end()));
+}
+
+struct FileLoc {
+ bool operator<(const FileLoc &RHS) const {
+ return std::tie(FileName, Line) < std::tie(RHS.FileName, RHS.Line);
+ }
+
+ std::string FileName;
+ uint32_t Line;
+};
+
+struct FunctionLoc {
+ bool operator<(const FunctionLoc &RHS) const {
+ return std::tie(Loc, FunctionName) < std::tie(RHS.Loc, RHS.FunctionName);
+ }
+
+ FileLoc Loc;
+ std::string FunctionName;
+};
+
+std::string stripPathPrefix(std::string Path) {
+ if (ClStripPathPrefix.empty())
+ return Path;
+ size_t Pos = Path.find(ClStripPathPrefix);
+ if (Pos == std::string::npos)
+ return Path;
+ return Path.substr(Pos + ClStripPathPrefix.size());
+}
+
+// Compute [FileLoc -> FunctionName] map for given addresses.
+static std::map<FileLoc, std::string>
+computeFunctionsMap(const std::set<uint64_t> &Addrs) {
+ std::map<FileLoc, std::string> Fns;
+
+ symbolize::LLVMSymbolizer::Options SymbolizerOptions;
+ SymbolizerOptions.Demangle = ClDemangle;
+ SymbolizerOptions.UseSymbolTable = true;
+ symbolize::LLVMSymbolizer Symbolizer(SymbolizerOptions);
+
+ // Fill in Fns map.
+ for (auto Addr : Addrs) {
+ auto InliningInfo = Symbolizer.symbolizeInlinedCode(ClBinaryName, Addr);
+ FailIfError(InliningInfo);
+ for (uint32_t I = 0; I < InliningInfo->getNumberOfFrames(); ++I) {
+ auto FrameInfo = InliningInfo->getFrame(I);
+ SmallString<256> FileName(FrameInfo.FileName);
+ sys::path::remove_dots(FileName, /* remove_dot_dot */ true);
+ FileLoc Loc = {FileName.str(), FrameInfo.Line};
+ Fns[Loc] = FrameInfo.FunctionName;
+ }
+ }
+
+ return Fns;
+}
+
+// Compute functions for given addresses. It keeps only the first
+// occurence of a function within a file.
+std::set<FunctionLoc> computeFunctionLocs(const std::set<uint64_t> &Addrs) {
+ std::map<FileLoc, std::string> Fns = computeFunctionsMap(Addrs);
+
+ std::set<FunctionLoc> Result;
+ std::string LastFileName;
+ std::set<std::string> ProcessedFunctions;
+
+ for (const auto &P : Fns) {
+ std::string FileName = P.first.FileName;
+ std::string FunctionName = P.second;
+
+ if (LastFileName != FileName)
+ ProcessedFunctions.clear();
+ LastFileName = FileName;
+
+ if (!ProcessedFunctions.insert(FunctionName).second)
+ continue;
+
+ Result.insert(FunctionLoc{P.first, P.second});
+ }
+
+ return Result;
+}
+
+// Locate __sanitizer_cov* function addresses that are used for coverage
+// reporting.
+static std::set<uint64_t>
+findSanitizerCovFunctions(const object::ObjectFile &O) {
+ std::set<uint64_t> Result;
+
+ for (const object::SymbolRef &Symbol : O.symbols()) {
+ ErrorOr<uint64_t> AddressOrErr = Symbol.getAddress();
+ FailIfError(AddressOrErr);
+
+ ErrorOr<StringRef> NameOrErr = Symbol.getName();
+ FailIfError(NameOrErr);
+ StringRef Name = NameOrErr.get();
+
+ if (Name == "__sanitizer_cov" || Name == "__sanitizer_cov_with_check" ||
+ Name == "__sanitizer_cov_trace_func_enter") {
+ Result.insert(AddressOrErr.get());
+ }
+ }
+
+ if (Result.empty())
+ FailIfNotEmpty("__sanitizer_cov* functions not found");
+
+ return Result;
+}
+
+// Locate addresses of all coverage points in a file. Coverage point
+// is defined as the 'address of instruction following __sanitizer_cov
+// call - 1'.
+static void getObjectCoveragePoints(const object::ObjectFile &O,
+ std::set<uint64_t> *Addrs) {
+ Triple TheTriple("unknown-unknown-unknown");
+ TheTriple.setArch(Triple::ArchType(O.getArch()));
+ auto TripleName = TheTriple.getTriple();
+
+ std::string Error;
+ const Target *TheTarget = TargetRegistry::lookupTarget(TripleName, Error);
+ FailIfNotEmpty(Error);
+
+ std::unique_ptr<const MCSubtargetInfo> STI(
+ TheTarget->createMCSubtargetInfo(TripleName, "", ""));
+ FailIfEmpty(STI, "no subtarget info for target " + TripleName);
+
+ std::unique_ptr<const MCRegisterInfo> MRI(
+ TheTarget->createMCRegInfo(TripleName));
+ FailIfEmpty(MRI, "no register info for target " + TripleName);
+
+ std::unique_ptr<const MCAsmInfo> AsmInfo(
+ TheTarget->createMCAsmInfo(*MRI, TripleName));
+ FailIfEmpty(AsmInfo, "no asm info for target " + TripleName);
+
+ std::unique_ptr<const MCObjectFileInfo> MOFI(new MCObjectFileInfo);
+ MCContext Ctx(AsmInfo.get(), MRI.get(), MOFI.get());
+ std::unique_ptr<MCDisassembler> DisAsm(
+ TheTarget->createMCDisassembler(*STI, Ctx));
+ FailIfEmpty(DisAsm, "no disassembler info for target " + TripleName);
+
+ std::unique_ptr<const MCInstrInfo> MII(TheTarget->createMCInstrInfo());
+ FailIfEmpty(MII, "no instruction info for target " + TripleName);
+
+ std::unique_ptr<const MCInstrAnalysis> MIA(
+ TheTarget->createMCInstrAnalysis(MII.get()));
+ FailIfEmpty(MIA, "no instruction analysis info for target " + TripleName);
+
+ auto SanCovAddrs = findSanitizerCovFunctions(O);
+
+ for (const auto Section : O.sections()) {
+ if (Section.isVirtual() || !Section.isText()) // llvm-objdump does the same.
+ continue;
+ uint64_t SectionAddr = Section.getAddress();
+ uint64_t SectSize = Section.getSize();
+ if (!SectSize)
+ continue;
+
+ StringRef SectionName;
+ FailIfError(Section.getName(SectionName));
+
+ StringRef BytesStr;
+ FailIfError(Section.getContents(BytesStr));
+ ArrayRef<uint8_t> Bytes(reinterpret_cast<const uint8_t *>(BytesStr.data()),
+ BytesStr.size());
+
+ for (uint64_t Index = 0, Size = 0; Index < Section.getSize();
+ Index += Size) {
+ MCInst Inst;
+ if (!DisAsm->getInstruction(Inst, Size, Bytes.slice(Index),
+ SectionAddr + Index, nulls(), nulls())) {
+ if (Size == 0)
+ Size = 1;
+ continue;
+ }
+ uint64_t Target;
+ if (MIA->isCall(Inst) &&
+ MIA->evaluateBranch(Inst, SectionAddr + Index, Size, Target)) {
+ if (SanCovAddrs.find(Target) != SanCovAddrs.end()) {
+ // Sanitizer coverage uses the address of the next instruction - 1.
+ Addrs->insert(Index + SectionAddr + Size - 1);
+ }
+ }
+ }
+ }
+}
+
+static void getArchiveCoveragePoints(const object::Archive &A,
+ std::set<uint64_t> *Addrs) {
+ for (auto &ErrorOrChild : A.children()) {
+ FailIfError(ErrorOrChild);
+ const object::Archive::Child &C = *ErrorOrChild;
+ ErrorOr<std::unique_ptr<object::Binary>> ChildOrErr = C.getAsBinary();
+ FailIfError(ChildOrErr);
+ if (object::ObjectFile *O =
+ dyn_cast<object::ObjectFile>(&*ChildOrErr.get()))
+ getObjectCoveragePoints(*O, Addrs);
+ else
+ FailIfError(object::object_error::invalid_file_type);
+ }
+}
+
+// Locate addresses of all coverage points in a file. Coverage point
+// is defined as the 'address of instruction following __sanitizer_cov
+// call - 1'.
+std::set<uint64_t> getCoveragePoints(std::string FileName) {
+ std::set<uint64_t> Result;
+
+ ErrorOr<object::OwningBinary<object::Binary>> BinaryOrErr =
+ object::createBinary(FileName);
+ FailIfError(BinaryOrErr);
+
+ object::Binary &Binary = *BinaryOrErr.get().getBinary();
+ if (object::Archive *A = dyn_cast<object::Archive>(&Binary))
+ getArchiveCoveragePoints(*A, &Result);
+ else if (object::ObjectFile *O = dyn_cast<object::ObjectFile>(&Binary))
+ getObjectCoveragePoints(*O, &Result);
+ else
+ FailIfError(object::object_error::invalid_file_type);
+
+ return Result;
+}
+
+static std::unique_ptr<SpecialCaseList> createDefaultBlacklist() {
+ if (!ClUseDefaultBlacklist)
+ return std::unique_ptr<SpecialCaseList>();
+ std::unique_ptr<MemoryBuffer> MB =
+ MemoryBuffer::getMemBuffer(DefaultBlacklist);
+ std::string Error;
+ auto Blacklist = SpecialCaseList::create(MB.get(), Error);
+ FailIfNotEmpty(Error);
+ return Blacklist;
+}
+
+static std::unique_ptr<SpecialCaseList> createUserBlacklist() {
+ if (ClBlacklist.empty())
+ return std::unique_ptr<SpecialCaseList>();
+
+ return SpecialCaseList::createOrDie({{ClBlacklist}});
+}
+
+static void printFunctionLocs(const std::set<FunctionLoc> &FnLocs,
+ raw_ostream &OS) {
+ std::unique_ptr<SpecialCaseList> DefaultBlacklist = createDefaultBlacklist();
+ std::unique_ptr<SpecialCaseList> UserBlacklist = createUserBlacklist();
+
+ for (const FunctionLoc &FnLoc : FnLocs) {
+ if (DefaultBlacklist &&
+ DefaultBlacklist->inSection("fun", FnLoc.FunctionName))
+ continue;
+ if (DefaultBlacklist &&
+ DefaultBlacklist->inSection("src", FnLoc.Loc.FileName))
+ continue;
+ if (UserBlacklist && UserBlacklist->inSection("fun", FnLoc.FunctionName))
+ continue;
+ if (UserBlacklist && UserBlacklist->inSection("src", FnLoc.Loc.FileName))
+ continue;
+
+ OS << stripPathPrefix(FnLoc.Loc.FileName) << ":" << FnLoc.Loc.Line << " "
+ << FnLoc.FunctionName << "\n";
+ }
+}
+
+class CoverageData {
+ public:
+ // Read single file coverage data.
+ static ErrorOr<std::unique_ptr<CoverageData>> read(std::string FileName) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
+ MemoryBuffer::getFile(FileName);
+ if (!BufOrErr)
+ return BufOrErr.getError();
+ std::unique_ptr<MemoryBuffer> Buf = std::move(BufOrErr.get());
+ if (Buf->getBufferSize() < 8) {
+ errs() << "File too small (<8): " << Buf->getBufferSize();
+ return make_error_code(errc::illegal_byte_sequence);
+ }
+ const FileHeader *Header =
+ reinterpret_cast<const FileHeader *>(Buf->getBufferStart());
+
+ if (Header->Magic != BinCoverageMagic) {
+ errs() << "Wrong magic: " << Header->Magic;
+ return make_error_code(errc::illegal_byte_sequence);
+ }
+
+ auto Addrs = llvm::make_unique<std::set<uint64_t>>();
+
+ switch (Header->Bitness) {
+ case Bitness64:
+ readInts<uint64_t>(Buf->getBufferStart() + 8, Buf->getBufferEnd(),
+ Addrs.get());
+ break;
+ case Bitness32:
+ readInts<uint32_t>(Buf->getBufferStart() + 8, Buf->getBufferEnd(),
+ Addrs.get());
+ break;
+ default:
+ errs() << "Unsupported bitness: " << Header->Bitness;
+ return make_error_code(errc::illegal_byte_sequence);
+ }
+
+ return std::unique_ptr<CoverageData>(new CoverageData(std::move(Addrs)));
+ }
+
+ // Merge multiple coverage data together.
+ static std::unique_ptr<CoverageData>
+ merge(const std::vector<std::unique_ptr<CoverageData>> &Covs) {
+ auto Addrs = llvm::make_unique<std::set<uint64_t>>();
+
+ for (const auto &Cov : Covs)
+ Addrs->insert(Cov->Addrs->begin(), Cov->Addrs->end());
+
+ return std::unique_ptr<CoverageData>(new CoverageData(std::move(Addrs)));
+ }
+
+ // Read list of files and merges their coverage info.
+ static ErrorOr<std::unique_ptr<CoverageData>>
+ readAndMerge(const std::vector<std::string> &FileNames) {
+ std::vector<std::unique_ptr<CoverageData>> Covs;
+ for (const auto &FileName : FileNames) {
+ auto Cov = read(FileName);
+ if (!Cov)
+ return Cov.getError();
+ Covs.push_back(std::move(Cov.get()));
+ }
+ return merge(Covs);
+ }
+
+ // Print coverage addresses.
+ void printAddrs(raw_ostream &OS) {
+ for (auto Addr : *Addrs) {
+ OS << "0x";
+ OS.write_hex(Addr);
+ OS << "\n";
+ }
+ }
+
+ // Print list of covered functions.
+ // Line format: <file_name>:<line> <function_name>
+ void printCoveredFunctions(raw_ostream &OS) {
+ printFunctionLocs(computeFunctionLocs(*Addrs), OS);
+ }
+
+ // Print list of not covered functions.
+ // Line format: <file_name>:<line> <function_name>
+ void printNotCoveredFunctions(raw_ostream &OS) {
+ std::set<FunctionLoc> AllFns =
+ computeFunctionLocs(getCoveragePoints(ClBinaryName));
+ std::set<FunctionLoc> CoveredFns = computeFunctionLocs(*Addrs);
+
+ std::set<FunctionLoc> NotCoveredFns;
+ std::set_difference(AllFns.begin(), AllFns.end(), CoveredFns.begin(),
+ CoveredFns.end(),
+ std::inserter(NotCoveredFns, NotCoveredFns.end()));
+ printFunctionLocs(NotCoveredFns, OS);
+ }
+
+private:
+ explicit CoverageData(std::unique_ptr<std::set<uint64_t>> Addrs)
+ : Addrs(std::move(Addrs)) {}
+
+ std::unique_ptr<std::set<uint64_t>> Addrs;
+};
+} // namespace
+
+int main(int argc, char **argv) {
+ // Print stack trace if we signal out.
+ sys::PrintStackTraceOnErrorSignal();
+ PrettyStackTraceProgram X(argc, argv);
+ llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
+
+ llvm::InitializeAllTargetInfos();
+ llvm::InitializeAllTargetMCs();
+ llvm::InitializeAllDisassemblers();
+
+ cl::ParseCommandLineOptions(argc, argv, "Sanitizer Coverage Processing Tool");
+
+ auto CovData = CoverageData::readAndMerge(ClInputFiles);
+ FailIfError(CovData);
+
+ switch (Action) {
+ case PrintAction: {
+ CovData.get()->printAddrs(outs());
+ return 0;
+ }
+ case CoveredFunctionsAction: {
+ CovData.get()->printCoveredFunctions(outs());
+ return 0;
+ }
+ case NotCoveredFunctionsAction: {
+ CovData.get()->printNotCoveredFunctions(outs());
+ return 0;
+ }
+ }
+
+ llvm_unreachable("unsupported action");
+}
diff --git a/tools/verify-uselistorder/verify-uselistorder.cpp b/tools/verify-uselistorder/verify-uselistorder.cpp
index efa4bcbe1aaa..c3fec1326acb 100644
--- a/tools/verify-uselistorder/verify-uselistorder.cpp
+++ b/tools/verify-uselistorder/verify-uselistorder.cpp
@@ -346,6 +346,7 @@ static void verifyAfterRoundTrip(const Module &M,
if (!matches(ValueMapping(M), ValueMapping(*OtherM)))
report_fatal_error("use-list order changed");
}
+
static void verifyBitcodeUseListOrder(const Module &M) {
TempFile F;
if (F.init("bc"))
diff --git a/tools/xcode-toolchain/CMakeLists.txt b/tools/xcode-toolchain/CMakeLists.txt
new file mode 100644
index 000000000000..4eacbd320efc
--- /dev/null
+++ b/tools/xcode-toolchain/CMakeLists.txt
@@ -0,0 +1,72 @@
+# OS X 10.11 El Capitan has just been released. One of the new features, System
+# Integrity Protection, prevents modifying the base OS install, even with sudo.
+# This prevents LLVM developers on OS X from being able to easily install new
+# system compilers. The feature can be disabled, but to make it easier for
+# developers to work without disabling SIP, this file can generate an Xcode
+# toolchain. Xcode toolchains are a mostly-undocumented feature that allows
+# multiple copies of low level tools to be installed to different locations, and
+# users can easily switch between them.
+
+# Setting an environment variable TOOLCHAINS to the toolchain's identifier will
+# result in /usr/bin/<tool> or xcrun <tool> to find the tool in the toolchain.
+
+# To make this work with Xcode 7.1 and later you can install the toolchain this
+# file generates anywhere on your system and set EXTERNAL_TOOLCHAINS_DIR to the
+# path specified by $CMAKE_INSTALL_PREFIX/Toolchains
+
+# This file generates a custom install-xcode-toolchain target which constructs
+# and installs a toolchain with the identifier in the pattern:
+# org.llvm.${PACKAGE_VERSION}. This toolchain can then be used to override the
+# system compiler by setting TOOLCHAINS=org.llvm.${PACKAGE_VERSION} in the
+# in the environment.
+
+# Example usage:
+# cmake -G Ninja -DLLVM_CREATE_XCODE_TOOLCHAIN=On
+# -DCMAKE_INSTALL_PREFIX=$PWD/install
+# ninja install-xcode-toolchain
+# export EXTERNAL_TOOLCHAINS_DIR=$PWD/install/Toolchains
+# export TOOLCHAINS=org.llvm.3.8.0svn
+
+# `xcrun -find clang` should return the installed clang, and `clang --version`
+# should show 3.8.0svn.
+
+if(NOT APPLE)
+ return()
+endif()
+
+option(LLVM_CREATE_XCODE_TOOLCHAIN "Create a target to install LLVM into an Xcode toolchain" Off)
+
+if(NOT LLVM_CREATE_XCODE_TOOLCHAIN)
+ return()
+endif()
+
+execute_process(
+ COMMAND xcrun -find otool
+ OUTPUT_VARIABLE clang_path
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ ERROR_FILE /dev/null
+)
+string(REGEX MATCH "(.*/Toolchains)/.*" toolchains_match ${clang_path})
+if(NOT toolchains_match)
+ message(FATAL_ERROR "Could not identify toolchain dir")
+endif()
+set(toolchains_dir ${CMAKE_MATCH_1})
+
+set(XcodeDefaultInfo "${toolchains_dir}/XcodeDefault.xctoolchain/ToolchainInfo.plist")
+set(LLVMToolchainDir "${CMAKE_INSTALL_PREFIX}/Toolchains/LLVM${PACKAGE_VERSION}.xctoolchain/")
+
+add_custom_command(OUTPUT ${LLVMToolchainDir}
+ COMMAND ${CMAKE_COMMAND} -E make_directory ${LLVMToolchainDir})
+
+add_custom_command(OUTPUT ${LLVMToolchainDir}/ToolchainInfo.plist
+ DEPENDS ${LLVMToolchainDir}
+ COMMAND ${CMAKE_COMMAND} -E copy "${XcodeDefaultInfo}" "${LLVMToolchainDir}/ToolchainInfo.plist"
+ COMMAND /usr/libexec/PlistBuddy -c "Set:Identifier org.llvm.${PACKAGE_VERSION}" "${LLVMToolchainDir}/ToolchainInfo.plist")
+
+add_custom_target(install-xcode-toolchain
+ DEPENDS ${LLVMToolchainDir}/ToolchainInfo.plist
+ COMMAND "${CMAKE_COMMAND}" --build ${CMAKE_BINARY_DIR} --target all
+ COMMAND "${CMAKE_COMMAND}"
+ -DCMAKE_INSTALL_PREFIX=${LLVMToolchainDir}/usr/
+ -P "${CMAKE_BINARY_DIR}/cmake_install.cmake"
+ ${cmake_3_2_USES_TERMINAL})
diff --git a/tools/yaml2obj/yaml2elf.cpp b/tools/yaml2obj/yaml2elf.cpp
index a247f48c053d..d03986e9c88d 100644
--- a/tools/yaml2obj/yaml2elf.cpp
+++ b/tools/yaml2obj/yaml2elf.cpp
@@ -105,10 +105,10 @@ class ELFState {
typedef typename object::ELFFile<ELFT>::Elf_Rela Elf_Rela;
/// \brief The future ".strtab" section.
- StringTableBuilder DotStrtab;
+ StringTableBuilder DotStrtab{StringTableBuilder::ELF};
/// \brief The future ".shstrtab" section.
- StringTableBuilder DotShStrtab;
+ StringTableBuilder DotShStrtab{StringTableBuilder::ELF};
NameToIdxMap SN2I;
NameToIdxMap SymN2I;
@@ -193,7 +193,7 @@ bool ELFState<ELFT>::initSectionHeaders(std::vector<Elf_Shdr> &SHeaders,
for (const auto &Sec : Doc.Sections)
DotShStrtab.add(Sec->Name);
- DotShStrtab.finalize(StringTableBuilder::ELF);
+ DotShStrtab.finalize();
for (const auto &Sec : Doc.Sections) {
zero(SHeader);
@@ -222,9 +222,11 @@ bool ELFState<ELFT>::initSectionHeaders(std::vector<Elf_Shdr> &SHeaders,
unsigned Index;
if (SN2I.lookup(S->Info, Index)) {
- errs() << "error: Unknown section referenced: '" << S->Info
- << "' at YAML section '" << S->Name << "'.\n";
- return false;
+ if (S->Info.getAsInteger(0, Index)) {
+ errs() << "error: Unknown section referenced: '" << S->Info
+ << "' at YAML section '" << S->Name << "'.\n";
+ return false;
+ }
}
SHeader.sh_info = Index;
@@ -284,7 +286,7 @@ void ELFState<ELFT>::initSymtabSectionHeader(Elf_Shdr &SHeader,
DotStrtab.add(Sym.Name);
for (const auto &Sym : Doc.Symbols.Weak)
DotStrtab.add(Sym.Name);
- DotStrtab.finalize(StringTableBuilder::ELF);
+ DotStrtab.finalize();
addSymbols(Doc.Symbols.Local, Syms, ELF::STB_LOCAL);
addSymbols(Doc.Symbols.Global, Syms, ELF::STB_GLOBAL);