summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/CMakeLists.txt7
-rw-r--r--tools/Makefile81
-rw-r--r--tools/bugpoint-passes/Makefile23
-rw-r--r--tools/bugpoint/BugDriver.h35
-rw-r--r--tools/bugpoint/CrashDebugger.cpp153
-rw-r--r--tools/bugpoint/ExecutionDriver.cpp2
-rw-r--r--tools/bugpoint/ExtractFunction.cpp4
-rw-r--r--tools/bugpoint/Makefile18
-rw-r--r--tools/bugpoint/Miscompilation.cpp23
-rw-r--r--tools/bugpoint/OptimizerDriver.cpp9
-rw-r--r--tools/bugpoint/ToolRunner.cpp13
-rw-r--r--tools/bugpoint/bugpoint.cpp4
-rw-r--r--tools/dsymutil/BinaryHolder.cpp39
-rw-r--r--tools/dsymutil/BinaryHolder.h3
-rw-r--r--tools/dsymutil/DebugMap.cpp28
-rw-r--r--tools/dsymutil/DebugMap.h15
-rw-r--r--tools/dsymutil/DwarfLinker.cpp174
-rw-r--r--tools/dsymutil/MachODebugMapParser.cpp66
-rw-r--r--tools/dsymutil/MachOUtils.cpp22
-rw-r--r--tools/dsymutil/Makefile17
-rw-r--r--tools/dsymutil/NonRelocatableStringpool.h2
-rw-r--r--tools/dsymutil/dsymutil.cpp11
-rw-r--r--tools/gold/CMakeLists.txt1
-rw-r--r--tools/gold/Makefile31
-rw-r--r--tools/gold/gold-plugin.cpp1096
-rw-r--r--tools/llc/Makefile18
-rw-r--r--tools/llc/llc.cpp134
-rw-r--r--tools/lli/CMakeLists.txt4
-rw-r--r--tools/lli/ChildTarget/CMakeLists.txt3
-rw-r--r--tools/lli/ChildTarget/ChildTarget.cpp35
-rw-r--r--tools/lli/ChildTarget/Makefile19
-rw-r--r--tools/lli/Makefile31
-rw-r--r--tools/lli/OrcLazyJIT.cpp35
-rw-r--r--tools/lli/OrcLazyJIT.h8
-rw-r--r--tools/lli/RemoteJITUtils.h45
-rw-r--r--tools/lli/lli.cpp119
-rw-r--r--tools/llvm-ar/Makefile21
-rw-r--r--tools/llvm-ar/llvm-ar.cpp224
-rw-r--r--tools/llvm-as-fuzzer/llvm-as-fuzzer.cpp2
-rw-r--r--tools/llvm-as/Makefile17
-rw-r--r--tools/llvm-as/llvm-as.cpp46
-rw-r--r--tools/llvm-bcanalyzer/Makefile17
-rw-r--r--tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp210
-rw-r--r--tools/llvm-c-test/CMakeLists.txt3
-rw-r--r--tools/llvm-c-test/Makefile29
-rw-r--r--tools/llvm-c-test/calc.c5
-rw-r--r--tools/llvm-c-test/diagnostic.c89
-rw-r--r--tools/llvm-c-test/disassemble.c4
-rw-r--r--tools/llvm-c-test/echo.cpp958
-rw-r--r--tools/llvm-c-test/helpers.c2
-rw-r--r--tools/llvm-c-test/llvm-c-test.h38
-rw-r--r--tools/llvm-c-test/main.c49
-rw-r--r--tools/llvm-c-test/metadata.c5
-rw-r--r--tools/llvm-c-test/module.c15
-rw-r--r--tools/llvm-c-test/object.c5
-rw-r--r--tools/llvm-c-test/targets.c2
-rw-r--r--tools/llvm-config/BuildVariables.inc.in3
-rw-r--r--tools/llvm-config/CMakeLists.txt14
-rw-r--r--tools/llvm-config/Makefile95
-rw-r--r--tools/llvm-config/llvm-config.cpp287
-rw-r--r--tools/llvm-cov/CMakeLists.txt4
-rw-r--r--tools/llvm-cov/CodeCoverage.cpp363
-rw-r--r--tools/llvm-cov/CoverageFilters.h2
-rw-r--r--tools/llvm-cov/CoverageReport.cpp2
-rw-r--r--tools/llvm-cov/CoverageReport.h2
-rw-r--r--tools/llvm-cov/CoverageSummaryInfo.h8
-rw-r--r--tools/llvm-cov/CoverageViewOptions.h15
-rw-r--r--tools/llvm-cov/LLVMBuild.txt2
-rw-r--r--tools/llvm-cov/Makefile17
-rw-r--r--tools/llvm-cov/RenderingSupport.h3
-rw-r--r--tools/llvm-cov/SourceCoverageView.cpp273
-rw-r--r--tools/llvm-cov/SourceCoverageView.h230
-rw-r--r--tools/llvm-cov/SourceCoverageViewHTML.cpp436
-rw-r--r--tools/llvm-cov/SourceCoverageViewHTML.h83
-rw-r--r--tools/llvm-cov/SourceCoverageViewText.cpp213
-rw-r--r--tools/llvm-cov/SourceCoverageViewText.h83
-rw-r--r--tools/llvm-cov/TestingSupport.cpp26
-rw-r--r--tools/llvm-cov/gcov.cpp8
-rw-r--r--tools/llvm-cov/llvm-cov.cpp8
-rw-r--r--tools/llvm-cxxdump/Error.cpp3
-rw-r--r--tools/llvm-cxxdump/Makefile18
-rw-r--r--tools/llvm-cxxdump/llvm-cxxdump.cpp55
-rw-r--r--tools/llvm-diff/DiffConsumer.h3
-rw-r--r--tools/llvm-diff/DiffLog.cpp1
-rw-r--r--tools/llvm-diff/DifferenceEngine.cpp11
-rw-r--r--tools/llvm-diff/DifferenceEngine.h1
-rw-r--r--tools/llvm-diff/Makefile17
-rw-r--r--tools/llvm-diff/llvm-diff.cpp2
-rw-r--r--tools/llvm-dis/Makefile17
-rw-r--r--tools/llvm-dis/llvm-dis.cpp67
-rw-r--r--tools/llvm-dwarfdump/Makefile17
-rw-r--r--tools/llvm-dwarfdump/fuzzer/llvm-dwarfdump-fuzzer.cpp6
-rw-r--r--tools/llvm-dwarfdump/llvm-dwarfdump.cpp14
-rw-r--r--tools/llvm-dwp/CMakeLists.txt1
-rw-r--r--tools/llvm-dwp/DWPError.cpp3
-rw-r--r--tools/llvm-dwp/DWPError.h23
-rw-r--r--tools/llvm-dwp/DWPStringPool.h56
-rw-r--r--tools/llvm-dwp/Makefile18
-rw-r--r--tools/llvm-dwp/llvm-dwp.cpp555
-rw-r--r--tools/llvm-extract/Makefile17
-rw-r--r--tools/llvm-extract/llvm-extract.cpp4
-rw-r--r--tools/llvm-go/Makefile16
-rw-r--r--tools/llvm-go/llvm-go.go28
-rw-r--r--tools/llvm-jitlistener/Makefile27
-rw-r--r--tools/llvm-jitlistener/llvm-jitlistener.cpp4
-rw-r--r--tools/llvm-link/CMakeLists.txt1
-rw-r--r--tools/llvm-link/LLVMBuild.txt2
-rw-r--r--tools/llvm-link/Makefile17
-rw-r--r--tools/llvm-link/llvm-link.cpp203
-rw-r--r--tools/llvm-lto/CMakeLists.txt2
-rw-r--r--tools/llvm-lto/LLVMBuild.txt2
-rw-r--r--tools/llvm-lto/Makefile17
-rw-r--r--tools/llvm-lto/llvm-lto.cpp627
-rw-r--r--tools/llvm-mc-fuzzer/CMakeLists.txt2
-rw-r--r--tools/llvm-mc-fuzzer/llvm-mc-fuzzer.cpp46
-rw-r--r--tools/llvm-mc/Disassembler.cpp2
-rw-r--r--tools/llvm-mc/Makefile17
-rw-r--r--tools/llvm-mc/llvm-mc.cpp60
-rw-r--r--tools/llvm-mcmarkup/Makefile17
-rw-r--r--tools/llvm-mcmarkup/llvm-mcmarkup.cpp2
-rw-r--r--tools/llvm-nm/Makefile17
-rw-r--r--tools/llvm-nm/llvm-nm.cpp415
-rw-r--r--tools/llvm-objdump/CMakeLists.txt4
-rw-r--r--tools/llvm-objdump/COFFDump.cpp102
-rw-r--r--tools/llvm-objdump/MachODump.cpp1156
-rw-r--r--tools/llvm-objdump/Makefile17
-rw-r--r--tools/llvm-objdump/llvm-objdump.cpp383
-rw-r--r--tools/llvm-objdump/llvm-objdump.h22
-rw-r--r--tools/llvm-pdbdump/BuiltinDumper.cpp27
-rw-r--r--tools/llvm-pdbdump/BuiltinDumper.h3
-rw-r--r--tools/llvm-pdbdump/CMakeLists.txt11
-rw-r--r--tools/llvm-pdbdump/ClassDefinitionDumper.cpp3
-rw-r--r--tools/llvm-pdbdump/ClassDefinitionDumper.h3
-rw-r--r--tools/llvm-pdbdump/CompilandDumper.cpp68
-rw-r--r--tools/llvm-pdbdump/CompilandDumper.h7
-rw-r--r--tools/llvm-pdbdump/EnumDumper.cpp3
-rw-r--r--tools/llvm-pdbdump/EnumDumper.h3
-rw-r--r--tools/llvm-pdbdump/ExternalSymbolDumper.cpp1
-rw-r--r--tools/llvm-pdbdump/ExternalSymbolDumper.h2
-rw-r--r--tools/llvm-pdbdump/FunctionDumper.cpp15
-rw-r--r--tools/llvm-pdbdump/FunctionDumper.h3
-rw-r--r--tools/llvm-pdbdump/LLVMOutputStyle.cpp775
-rw-r--r--tools/llvm-pdbdump/LLVMOutputStyle.h50
-rw-r--r--tools/llvm-pdbdump/LinePrinter.cpp29
-rw-r--r--tools/llvm-pdbdump/LinePrinter.h2
-rw-r--r--tools/llvm-pdbdump/Makefile17
-rw-r--r--tools/llvm-pdbdump/OutputStyle.h28
-rw-r--r--tools/llvm-pdbdump/PdbYaml.cpp162
-rw-r--r--tools/llvm-pdbdump/PdbYaml.h112
-rw-r--r--tools/llvm-pdbdump/TypeDumper.cpp5
-rw-r--r--tools/llvm-pdbdump/TypeDumper.h4
-rw-r--r--tools/llvm-pdbdump/TypedefDumper.cpp1
-rw-r--r--tools/llvm-pdbdump/TypedefDumper.h2
-rw-r--r--tools/llvm-pdbdump/VariableDumper.cpp3
-rw-r--r--tools/llvm-pdbdump/VariableDumper.h7
-rw-r--r--tools/llvm-pdbdump/YAMLOutputStyle.cpp142
-rw-r--r--tools/llvm-pdbdump/YAMLOutputStyle.h45
-rw-r--r--tools/llvm-pdbdump/fuzzer/CMakeLists.txt15
-rw-r--r--tools/llvm-pdbdump/fuzzer/llvm-pdbdump-fuzzer.cpp104
-rw-r--r--tools/llvm-pdbdump/llvm-pdbdump.cpp709
-rw-r--r--tools/llvm-pdbdump/llvm-pdbdump.h38
-rw-r--r--tools/llvm-profdata/Makefile17
-rw-r--r--tools/llvm-profdata/llvm-profdata.cpp245
-rw-r--r--tools/llvm-readobj/ARMAttributeParser.cpp19
-rw-r--r--tools/llvm-readobj/ARMAttributeParser.h8
-rw-r--r--tools/llvm-readobj/ARMEHABIPrinter.h22
-rw-r--r--tools/llvm-readobj/ARMWinEHPrinter.cpp115
-rw-r--r--tools/llvm-readobj/ARMWinEHPrinter.h6
-rw-r--r--tools/llvm-readobj/CMakeLists.txt3
-rw-r--r--tools/llvm-readobj/COFFDumper.cpp766
-rw-r--r--tools/llvm-readobj/CodeView.h54
-rw-r--r--tools/llvm-readobj/ELFDumper.cpp2842
-rw-r--r--tools/llvm-readobj/Error.cpp3
-rw-r--r--tools/llvm-readobj/MachODumper.cpp29
-rw-r--r--tools/llvm-readobj/Makefile18
-rw-r--r--tools/llvm-readobj/ObjDumper.cpp7
-rw-r--r--tools/llvm-readobj/ObjDumper.h24
-rw-r--r--tools/llvm-readobj/StreamWriter.cpp79
-rw-r--r--tools/llvm-readobj/StreamWriter.h320
-rw-r--r--tools/llvm-readobj/Win64EHDumper.cpp16
-rw-r--r--tools/llvm-readobj/Win64EHDumper.h6
-rw-r--r--tools/llvm-readobj/llvm-readobj.cpp149
-rw-r--r--tools/llvm-readobj/llvm-readobj.h21
-rw-r--r--tools/llvm-rtdyld/Makefile17
-rw-r--r--tools/llvm-rtdyld/llvm-rtdyld.cpp108
-rw-r--r--tools/llvm-shlib/CMakeLists.txt2
-rw-r--r--tools/llvm-shlib/Makefile116
-rw-r--r--tools/llvm-shlib/libllvm.cpp7
-rw-r--r--tools/llvm-size/Makefile17
-rw-r--r--tools/llvm-size/llvm-size.cpp343
-rw-r--r--tools/llvm-split/Makefile17
-rw-r--r--tools/llvm-split/llvm-split.cpp10
-rw-r--r--tools/llvm-stress/Makefile15
-rw-r--r--tools/llvm-stress/llvm-stress.cpp9
-rw-r--r--tools/llvm-symbolizer/Makefile17
-rw-r--r--tools/llvm-symbolizer/llvm-symbolizer.cpp16
-rw-r--r--tools/lto/CMakeLists.txt4
-rw-r--r--tools/lto/Makefile42
-rw-r--r--tools/lto/lto.cpp156
-rw-r--r--tools/lto/lto.exports20
-rw-r--r--tools/obj2yaml/CMakeLists.txt7
-rw-r--r--tools/obj2yaml/Error.cpp17
-rw-r--r--tools/obj2yaml/Error.h21
-rw-r--r--tools/obj2yaml/Makefile17
-rw-r--r--tools/obj2yaml/coff2yaml.cpp14
-rw-r--r--tools/obj2yaml/elf2yaml.cpp24
-rw-r--r--tools/obj2yaml/macho2yaml.cpp527
-rw-r--r--tools/obj2yaml/obj2yaml.cpp12
-rw-r--r--tools/obj2yaml/obj2yaml.h2
-rw-r--r--tools/opt/AnalysisWrappers.cpp20
-rw-r--r--tools/opt/BreakpointPrinter.cpp10
-rw-r--r--tools/opt/Makefile17
-rw-r--r--tools/opt/NewPMDriver.cpp35
-rw-r--r--tools/opt/NewPMDriver.h3
-rw-r--r--tools/opt/opt.cpp98
-rw-r--r--tools/sancov/Makefile18
-rw-r--r--tools/sancov/sancov.cc1025
-rw-r--r--tools/sanstats/CMakeLists.txt8
-rw-r--r--tools/sanstats/sanstats.cpp138
-rw-r--r--tools/verify-uselistorder/Makefile17
-rw-r--r--tools/verify-uselistorder/verify-uselistorder.cpp12
-rw-r--r--tools/xcode-toolchain/CMakeLists.txt22
-rw-r--r--tools/yaml2obj/CMakeLists.txt2
-rw-r--r--tools/yaml2obj/Makefile17
-rw-r--r--tools/yaml2obj/yaml2coff.cpp42
-rw-r--r--tools/yaml2obj/yaml2elf.cpp12
-rw-r--r--tools/yaml2obj/yaml2macho.cpp539
-rw-r--r--tools/yaml2obj/yaml2obj.cpp60
-rw-r--r--tools/yaml2obj/yaml2obj.h16
229 files changed, 15365 insertions, 5834 deletions
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
index f50382b08977e..b654b8c5cb8e0 100644
--- a/tools/CMakeLists.txt
+++ b/tools/CMakeLists.txt
@@ -34,8 +34,10 @@ endif()
# requires targets specified in DEPENDS to exist before the call to
# ExternalProject_Add.
add_llvm_tool_subdirectory(lto)
+add_llvm_tool_subdirectory(gold)
add_llvm_tool_subdirectory(llvm-ar)
add_llvm_tool_subdirectory(llvm-config)
+add_llvm_tool_subdirectory(llvm-lto)
add_llvm_tool_subdirectory(llvm-profdata)
# Projects supported via LLVM_EXTERNAL_*_SOURCE_DIR need to be explicitly
@@ -49,4 +51,9 @@ add_llvm_external_project(lldb)
# file as external projects.
add_llvm_implicit_projects()
+# Add subprojects specified using LLVM_EXTERNAL_PROJECTS
+foreach(p ${LLVM_EXTERNAL_PROJECTS})
+ add_llvm_external_project(${p})
+endforeach(p)
+
set(LLVM_COMMON_DEPENDS ${LLVM_COMMON_DEPENDS} PARENT_SCOPE)
diff --git a/tools/Makefile b/tools/Makefile
deleted file mode 100644
index 92d495451879b..0000000000000
--- a/tools/Makefile
+++ /dev/null
@@ -1,81 +0,0 @@
-##===- tools/Makefile --------------------------------------*- Makefile -*-===##
-#
-# The LLVM Compiler Infrastructure
-#
-# This file is distributed under the University of Illinois Open Source
-# License. See LICENSE.TXT for details.
-#
-##===----------------------------------------------------------------------===##
-
-LEVEL := ..
-
-include $(LEVEL)/Makefile.config
-
-# Build clang if present.
-
-ifneq ($(CLANG_SRC_ROOT),)
- OPTIONAL_PARALLEL_DIRS := $(CLANG_SRC_ROOT)
-else
- OPTIONAL_PARALLEL_DIRS := clang
-endif
-
-# Build LLDB if present. Note LLDB must be built last as it depends on
-# the wider LLVM infrastructure (including Clang).
-OPTIONAL_DIRS := lldb
-
-# NOTE: The tools are organized into five groups of four consisting of one
-# large and three small executables. This is done to minimize memory load
-# in parallel builds. Please retain this ordering.
-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 \
- 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-split sancov llvm-dwp
-
-# If Intel JIT Events support is configured, build an extra tool to test it.
-ifeq ($(USE_INTEL_JITEVENTS), 1)
- PARALLEL_DIRS += llvm-jitlistener
-endif
-
-# Let users override the set of tools to build from the command line.
-ifdef ONLY_TOOLS
- OPTIONAL_PARALLEL_DIRS :=
- OPTIONAL_DIRS := $(findstring lldb,$(ONLY_TOOLS))
- PARALLEL_DIRS := $(filter-out lldb,$(ONLY_TOOLS))
-endif
-
-# These libraries build as dynamic libraries (.dylib /.so), they can only be
-# built if ENABLE_PIC is set.
-ifndef ONLY_TOOLS
-ifeq ($(ENABLE_PIC),1)
- # gold only builds if binutils is around. It requires "lto" to build before
- # it so it is added to DIRS. llvm-lto also requires lto
- DIRS += lto llvm-lto
- ifdef BINUTILS_INCDIR
- DIRS += gold
- endif
-
- PARALLEL_DIRS += bugpoint-passes
-endif
-
-ifdef LLVM_HAS_POLLY
- PARALLEL_DIRS += polly
-endif
-endif
-
-# On Win32, loadable modules can be built with ENABLE_SHARED.
-ifneq ($(ENABLE_SHARED),1)
- ifneq (,$(filter $(HOST_OS), Cygwin MingW))
- PARALLEL_DIRS := $(filter-out bugpoint-passes, \
- $(PARALLEL_DIRS))
- endif
-endif
-
-ifneq (,$(filter go,$(BINDINGS_TO_BUILD)))
- PARALLEL_DIRS += llvm-go
-endif
-
-include $(LEVEL)/Makefile.common
diff --git a/tools/bugpoint-passes/Makefile b/tools/bugpoint-passes/Makefile
deleted file mode 100644
index 61f96bc338590..0000000000000
--- a/tools/bugpoint-passes/Makefile
+++ /dev/null
@@ -1,23 +0,0 @@
-##===- tools/bugpoint-passes/Makefile -- -------------------*- Makefile -*-===##
-#
-# The LLVM Compiler Infrastructure
-#
-# This file is distributed under the University of Illinois Open Source
-# License. See LICENSE.TXT for details.
-#
-##===----------------------------------------------------------------------===##
-
-LEVEL := ../..
-LIBRARYNAME := BugpointPasses
-LOADABLE_MODULE := 1
-USEDLIBS :=
-
-# If we don't need RTTI or EH, there's no reason to export anything
-# from this plugin.
-ifneq ($(REQUIRES_RTTI), 1)
-ifneq ($(REQUIRES_EH), 1)
-EXPORTED_SYMBOL_FILE = $(PROJ_SRC_DIR)/bugpoint.exports
-endif
-endif
-
-include $(LEVEL)/Makefile.common
diff --git a/tools/bugpoint/BugDriver.h b/tools/bugpoint/BugDriver.h
index 20efff3fda5f7..52ec2c0056496 100644
--- a/tools/bugpoint/BugDriver.h
+++ b/tools/bugpoint/BugDriver.h
@@ -76,7 +76,7 @@ public:
// command line arguments into instance variables of BugDriver.
//
bool addSources(const std::vector<std::string> &FileNames);
- void addPass(std::string p) { PassesToRun.push_back(p); }
+ void addPass(std::string p) { PassesToRun.push_back(std::move(p)); }
void setPassesToRun(const std::vector<std::string> &PTR) {
PassesToRun = PTR;
}
@@ -130,12 +130,6 @@ public:
///
bool isExecutingJIT();
- /// runPasses - Run all of the passes in the "PassesToRun" list, discard the
- /// output, and return true if any of the passes crashed.
- bool runPasses(Module *M) const {
- return runPasses(M, PassesToRun);
- }
-
Module *getProgram() const { return Program; }
/// swapProgramIn - Set the current module to the specified module, returning
@@ -183,7 +177,7 @@ public:
/// Error.
///
std::string executeProgramSafely(const Module *Program,
- std::string OutputFile,
+ const std::string &OutputFile,
std::string *Error) const;
/// createReferenceFile - calls compileProgram and then records the output
@@ -243,12 +237,8 @@ public:
/// Carefully run the specified set of pass on the specified/ module,
/// returning the transformed module on success, or a null pointer on failure.
- /// If AutoDebugCrashes is set to true, then bugpoint will automatically
- /// attempt to track down a crashing pass if one exists, and this method will
- /// never return null.
std::unique_ptr<Module> runPassesOn(Module *M,
const std::vector<std::string> &Passes,
- bool AutoDebugCrashes = false,
unsigned NumExtraArgs = 0,
const char *const *ExtraArgs = nullptr);
@@ -266,6 +256,16 @@ public:
std::string &OutputFilename, bool DeleteOutput = false,
bool Quiet = false, unsigned NumExtraArgs = 0,
const char * const *ExtraArgs = nullptr) const;
+
+ /// runPasses - Just like the method above, but this just returns true or
+ /// false indicating whether or not the optimizer crashed on the specified
+ /// input (true = crashed). Does not produce any output.
+ ///
+ bool runPasses(Module *M,
+ const std::vector<std::string> &PassesToRun) const {
+ std::string Filename;
+ return runPasses(M, PassesToRun, Filename, true);
+ }
/// runManyPasses - Take the specified pass list and create different
/// combinations of passes to compile the program with. Compile the program with
@@ -285,17 +285,6 @@ public:
const Module *M) const;
private:
- /// runPasses - Just like the method above, but this just returns true or
- /// false indicating whether or not the optimizer crashed on the specified
- /// input (true = crashed).
- ///
- bool runPasses(Module *M,
- const std::vector<std::string> &PassesToRun,
- bool DeleteOutput = true) const {
- std::string Filename;
- return runPasses(M, PassesToRun, Filename, DeleteOutput);
- }
-
/// initializeExecutionEnvironment - This method is used to set up the
/// environment for executing LLVM programs.
///
diff --git a/tools/bugpoint/CrashDebugger.cpp b/tools/bugpoint/CrashDebugger.cpp
index 6cdc43ab8699c..b25e6ecec19b6 100644
--- a/tools/bugpoint/CrashDebugger.cpp
+++ b/tools/bugpoint/CrashDebugger.cpp
@@ -54,6 +54,9 @@ namespace {
cl::opt<bool> NoNamedMDRM("disable-namedmd-remove",
cl::desc("Do not remove global named metadata"),
cl::init(false));
+ cl::opt<bool> VerboseErrors("verbose-errors",
+ cl::desc("Print the output of crashing program"),
+ cl::init(false));
}
namespace llvm {
@@ -164,6 +167,7 @@ ReduceCrashingGlobalVariables::TestGlobalVariables(
if (I.hasInitializer() && !GVSet.count(&I)) {
DeleteGlobalInitializer(&I);
I.setLinkage(GlobalValue::ExternalLinkage);
+ I.setComdat(nullptr);
}
// Try running the hacked up program...
@@ -264,8 +268,8 @@ bool ReduceCrashingFunctions::TestFuncs(std::vector<Function*> &Funcs) {
std::vector<GlobalValue*> ToRemove;
// First, remove aliases to functions we're about to purge.
for (GlobalAlias &Alias : M->aliases()) {
- Constant *Root = Alias.getAliasee()->stripPointerCasts();
- Function *F = dyn_cast<Function>(Root);
+ GlobalObject *Root = Alias.getBaseObject();
+ Function *F = dyn_cast_or_null<Function>(Root);
if (F) {
if (Functions.count(F))
// We're keeping this function.
@@ -373,9 +377,9 @@ bool ReduceCrashingBlocks::TestBlocks(std::vector<const BasicBlock*> &BBs) {
(*SI)->removePredecessor(&*BB);
TerminatorInst *BBTerm = BB->getTerminator();
- if (BBTerm->isEHPad())
+ if (BBTerm->isEHPad() || BBTerm->getType()->isTokenTy())
continue;
- if (!BBTerm->getType()->isVoidTy() && !BBTerm->getType()->isTokenTy())
+ if (!BBTerm->getType()->isVoidTy())
BBTerm->replaceAllUsesWith(Constant::getNullValue(BBTerm->getType()));
// Replace the old terminator instruction.
@@ -459,7 +463,7 @@ bool ReduceCrashingInstructions::TestInsts(std::vector<const Instruction*>
Module *M = CloneModule(BD.getProgram(), VMap).release();
// Convert list to set for fast lookup...
- SmallPtrSet<Instruction*, 64> Instructions;
+ SmallPtrSet<Instruction*, 32> Instructions;
for (unsigned i = 0, e = Insts.size(); i != e; ++i) {
assert(!isa<TerminatorInst>(Insts[i]));
Instructions.insert(cast<Instruction>(VMap[Insts[i]]));
@@ -476,8 +480,8 @@ bool ReduceCrashingInstructions::TestInsts(std::vector<const Instruction*>
for (BasicBlock::iterator I = FI->begin(), E = FI->end(); I != E;) {
Instruction *Inst = &*I++;
if (!Instructions.count(Inst) && !isa<TerminatorInst>(Inst) &&
- !Inst->isEHPad()) {
- if (!Inst->getType()->isVoidTy() && !Inst->getType()->isTokenTy())
+ !Inst->isEHPad() && !Inst->getType()->isTokenTy()) {
+ if (!Inst->getType()->isVoidTy())
Inst->replaceAllUsesWith(UndefValue::get(Inst->getType()));
Inst->eraseFromParent();
}
@@ -552,7 +556,9 @@ bool ReduceCrashingNamedMD::TestNamedMDs(std::vector<std::string> &NamedMDs) {
std::vector<NamedMDNode *> ToDelete;
ToDelete.reserve(M->named_metadata_size() - Names.size());
for (auto &NamedMD : M->named_metadata())
- if (!Names.count(NamedMD.getName()))
+ // Always keep a nonempty llvm.dbg.cu because the Verifier would complain.
+ if (!Names.count(NamedMD.getName()) &&
+ (!(NamedMD.getName() == "llvm.dbg.cu" && NamedMD.getNumOperands() > 0)))
ToDelete.push_back(&NamedMD);
for (auto *NamedMD : ToDelete)
@@ -600,7 +606,7 @@ public:
bool ReduceCrashingNamedMDOps::TestNamedMDOps(
std::vector<const MDNode *> &NamedMDOps) {
// Convert list to set for fast lookup...
- SmallPtrSet<const MDNode *, 64> OldMDNodeOps;
+ SmallPtrSet<const MDNode *, 32> OldMDNodeOps;
for (unsigned i = 0, e = NamedMDOps.size(); i != e; ++i) {
OldMDNodeOps.insert(NamedMDOps[i]);
}
@@ -637,7 +643,7 @@ bool ReduceCrashingNamedMDOps::TestNamedMDOps(
// 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()));
+ NamedMDOps.push_back(cast<MDNode>(*VMap.getMappedMD(Node)));
BD.setNewProgram(M); // It crashed, keep the trimmed version...
return true;
@@ -646,16 +652,10 @@ bool ReduceCrashingNamedMDOps::TestNamedMDOps(
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.
-static bool DebugACrash(BugDriver &BD,
- bool (*TestFn)(const BugDriver &, Module *),
- std::string &Error) {
- // See if we can get away with nuking some of the global variable initializers
- // in the program...
- if (!NoGlobalRM &&
- BD.getProgram()->global_begin() != BD.getProgram()->global_end()) {
+static void ReduceGlobalInitializers(BugDriver &BD,
+ bool (*TestFn)(const BugDriver &, Module *),
+ std::string &Error) {
+ if (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()).release();
@@ -666,6 +666,7 @@ static bool DebugACrash(BugDriver &BD,
if (I->hasInitializer()) {
DeleteGlobalInitializer(&*I);
I->setLinkage(GlobalValue::ExternalLinkage);
+ I->setComdat(nullptr);
DeletedInit = true;
}
@@ -695,8 +696,7 @@ static bool DebugACrash(BugDriver &BD,
unsigned OldSize = GVs.size();
ReduceCrashingGlobalVariables(BD, TestFn).reduceList(GVs, Error);
- if (!Error.empty())
- return true;
+ assert(!Error.empty());
if (GVs.size() < OldSize)
BD.EmitProgressBitcode(BD.getProgram(), "reduced-global-variables");
@@ -704,40 +704,11 @@ static bool DebugACrash(BugDriver &BD,
}
}
}
+}
- // Now try to reduce the number of functions in the module to something small.
- std::vector<Function*> Functions;
- 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 "
- "in the testcase\n";
-
- unsigned OldSize = Functions.size();
- ReduceCrashingFunctions(BD, TestFn).reduceList(Functions, Error);
-
- if (Functions.size() < OldSize)
- BD.EmitProgressBitcode(BD.getProgram(), "reduced-function");
- }
-
- // Attempt to delete entire basic blocks at a time to speed up
- // convergence... this actually works by setting the terminator of the blocks
- // to a return instruction then running simplifycfg, which can potentially
- // shrinks the code dramatically quickly
- //
- if (!DisableSimplifyCFG && !BugpointIsInterrupted) {
- std::vector<const BasicBlock*> Blocks;
- 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)
- BD.EmitProgressBitcode(BD.getProgram(), "reduced-blocks");
- }
-
+static void ReduceInsts(BugDriver &BD,
+ bool (*TestFn)(const BugDriver &, Module *),
+ std::string &Error) {
// Attempt to delete instructions using bisection. This should help out nasty
// cases with large basic blocks where the problem is at one end.
if (!BugpointIsInterrupted) {
@@ -751,11 +722,10 @@ static bool DebugACrash(BugDriver &BD,
ReduceCrashingInstructions(BD, TestFn).reduceList(Insts, Error);
}
- // FIXME: This should use the list reducer to converge faster by deleting
- // larger chunks of instructions at a time!
unsigned Simplification = 2;
do {
- if (BugpointIsInterrupted) break;
+ if (BugpointIsInterrupted)
+ return;
--Simplification;
outs() << "\n*** Attempting to reduce testcase by deleting instruc"
<< "tions: Simplification Level #" << Simplification << '\n';
@@ -784,7 +754,8 @@ static bool DebugACrash(BugDriver &BD,
if (InstructionsToSkipBeforeDeleting) {
--InstructionsToSkipBeforeDeleting;
} else {
- if (BugpointIsInterrupted) goto ExitLoops;
+ if (BugpointIsInterrupted)
+ return;
if (I->isEHPad() || I->getType()->isTokenTy())
continue;
@@ -810,10 +781,60 @@ static bool DebugACrash(BugDriver &BD,
}
} while (Simplification);
+ BD.EmitProgressBitcode(BD.getProgram(), "reduced-instructions");
+}
- if (!NoNamedMDRM) {
- BD.EmitProgressBitcode(BD.getProgram(), "reduced-instructions");
+/// 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.
+static bool DebugACrash(BugDriver &BD,
+ bool (*TestFn)(const BugDriver &, Module *),
+ std::string &Error) {
+ // See if we can get away with nuking some of the global variable initializers
+ // in the program...
+ if (!NoGlobalRM)
+ ReduceGlobalInitializers(BD, TestFn, Error);
+
+ // Now try to reduce the number of functions in the module to something small.
+ std::vector<Function*> Functions;
+ 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 "
+ "in the testcase\n";
+
+ unsigned OldSize = Functions.size();
+ ReduceCrashingFunctions(BD, TestFn).reduceList(Functions, Error);
+
+ if (Functions.size() < OldSize)
+ BD.EmitProgressBitcode(BD.getProgram(), "reduced-function");
+ }
+
+ // Attempt to delete entire basic blocks at a time to speed up
+ // convergence... this actually works by setting the terminator of the blocks
+ // to a return instruction then running simplifycfg, which can potentially
+ // shrinks the code dramatically quickly
+ //
+ if (!DisableSimplifyCFG && !BugpointIsInterrupted) {
+ std::vector<const BasicBlock*> Blocks;
+ 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)
+ BD.EmitProgressBitcode(BD.getProgram(), "reduced-blocks");
+ }
+
+ // Attempt to delete instructions using bisection. This should help out nasty
+ // cases with large basic blocks where the problem is at one end.
+ if (!BugpointIsInterrupted)
+ ReduceInsts(BD, TestFn, Error);
+
+ if (!NoNamedMDRM) {
if (!BugpointIsInterrupted) {
// Try to reduce the amount of global metadata (particularly debug info),
// by dropping global named metadata that anchors them
@@ -833,10 +854,9 @@ static bool DebugACrash(BugDriver &BD,
NamedMDOps.push_back(op);
ReduceCrashingNamedMDOps(BD, TestFn).reduceList(NamedMDOps, Error);
}
+ BD.EmitProgressBitcode(BD.getProgram(), "reduced-named-md");
}
-ExitLoops:
-
// Try to clean up the testcase by running funcresolve and globaldce...
if (!BugpointIsInterrupted) {
outs() << "\n*** Attempting to perform final cleanups: ";
@@ -857,7 +877,7 @@ ExitLoops:
}
static bool TestForOptimizerCrash(const BugDriver &BD, Module *M) {
- return BD.runPasses(M);
+ return BD.runPasses(M, BD.getPassesToRun());
}
/// debugOptimizerCrash - This method is called when some pass crashes on input.
@@ -888,7 +908,10 @@ static bool TestForCodeGenCrash(const BugDriver &BD, Module *M) {
std::string Error;
BD.compileProgram(M, &Error);
if (!Error.empty()) {
- errs() << "<crash>\n";
+ if (VerboseErrors)
+ errs() << Error << "\n";
+ else
+ errs() << "<crash>\n";
return true; // Tool is still crashing.
}
errs() << '\n';
diff --git a/tools/bugpoint/ExecutionDriver.cpp b/tools/bugpoint/ExecutionDriver.cpp
index 41b8ccc18e871..ab9c05fa924af 100644
--- a/tools/bugpoint/ExecutionDriver.cpp
+++ b/tools/bugpoint/ExecutionDriver.cpp
@@ -384,7 +384,7 @@ std::string BugDriver::executeProgram(const Module *Program,
/// backend, if reference output is not provided.
///
std::string BugDriver::executeProgramSafely(const Module *Program,
- std::string OutputFile,
+ const std::string &OutputFile,
std::string *Error) const {
return executeProgram(Program, OutputFile, "", "", SafeInterpreter, Error);
}
diff --git a/tools/bugpoint/ExtractFunction.cpp b/tools/bugpoint/ExtractFunction.cpp
index fe0ab69dc162d..de596a57d83cd 100644
--- a/tools/bugpoint/ExtractFunction.cpp
+++ b/tools/bugpoint/ExtractFunction.cpp
@@ -215,6 +215,8 @@ void llvm::DeleteGlobalInitializer(GlobalVariable *GV) {
//
void llvm::DeleteFunctionBody(Function *F) {
eliminateAliases(F);
+ // Function declarations can't have comdats.
+ F->setComdat(nullptr);
// delete the body of the function...
F->deleteBody();
@@ -409,7 +411,7 @@ BugDriver::extractMappedBlocksFromModule(const std::vector<BasicBlock *> &BBs,
std::vector<std::string> PI;
PI.push_back("extract-blocks");
- std::unique_ptr<Module> Ret = runPassesOn(M, PI, false, 1, &ExtraArg);
+ std::unique_ptr<Module> Ret = runPassesOn(M, PI, 1, &ExtraArg);
sys::fs::remove(Filename.c_str());
diff --git a/tools/bugpoint/Makefile b/tools/bugpoint/Makefile
deleted file mode 100644
index 174f8d25161ee..0000000000000
--- a/tools/bugpoint/Makefile
+++ /dev/null
@@ -1,18 +0,0 @@
-##===- tools/bugpoint/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 := bugpoint
-LINK_COMPONENTS := asmparser instrumentation scalaropts ipo linker bitreader \
- bitwriter irreader vectorize objcarcopts codegen
-
-# Support plugins.
-NO_DEAD_STRIP := 1
-
-include $(LEVEL)/Makefile.common
diff --git a/tools/bugpoint/Miscompilation.cpp b/tools/bugpoint/Miscompilation.cpp
index 16919f5a44921..40955ed3caa93 100644
--- a/tools/bugpoint/Miscompilation.cpp
+++ b/tools/bugpoint/Miscompilation.cpp
@@ -26,12 +26,13 @@
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileUtilities.h"
#include "llvm/Transforms/Utils/Cloning.h"
+
using namespace llvm;
namespace llvm {
extern cl::opt<std::string> OutputPrefix;
extern cl::list<std::string> InputArgv;
-}
+} // end namespace llvm
namespace {
static llvm::cl::opt<bool>
@@ -52,7 +53,7 @@ namespace {
std::vector<std::string> &Suffix,
std::string &Error) override;
};
-}
+} // end anonymous namespace
/// TestResult - After passes have been split into a test group and a control
/// group, see if they still break the program.
@@ -208,7 +209,7 @@ namespace {
bool TestFuncs(const std::vector<Function*> &Prefix, std::string &Error);
};
-}
+} // end anonymous namespace
/// 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,
@@ -469,7 +470,7 @@ namespace {
bool TestFuncs(const std::vector<BasicBlock*> &BBs, std::string &Error);
};
-}
+} // end anonymous namespace
/// TestFuncs - Extract all blocks for the miscompiled functions except for the
/// specified blocks. If the problem still exists, return true.
@@ -696,8 +697,14 @@ static bool TestOptimizer(BugDriver &BD, std::unique_ptr<Module> Test,
// of the functions being tested.
outs() << " Optimizing functions being tested: ";
std::unique_ptr<Module> Optimized =
- BD.runPassesOn(Test.get(), BD.getPassesToRun(),
- /*AutoDebugCrashes*/ true);
+ BD.runPassesOn(Test.get(), BD.getPassesToRun());
+ if (!Optimized) {
+ errs() << " Error running this sequence of passes"
+ << " on the input program!\n";
+ delete BD.swapProgramIn(Test.get());
+ BD.EmitProgressBitcode(Test.get(), "pass-error", false);
+ return BD.debugOptimizerCrash();
+ }
outs() << "done.\n";
outs() << " Checking to see if the merged program executes correctly: ";
@@ -712,7 +719,6 @@ static bool TestOptimizer(BugDriver &BD, std::unique_ptr<Module> Test,
return Broken;
}
-
/// debugMiscompilation - This method is used when the passes selected are not
/// crashing, but the generated output is semantically different from the
/// input.
@@ -752,8 +758,6 @@ void BugDriver::debugMiscompilation(std::string *Error) {
outs() << " Portion that is input to optimizer: ";
EmitProgressBitcode(ToOptimize, "tooptimize");
delete ToOptimize; // Delete hacked module.
-
- return;
}
/// Get the specified modules ready for code generator testing.
@@ -984,7 +988,6 @@ static bool TestCodeGenerator(BugDriver &BD, std::unique_ptr<Module> Test,
return Result;
}
-
/// debugCodeGenerator - debug errors in LLC, LLI, or CBE.
///
bool BugDriver::debugCodeGenerator(std::string *Error) {
diff --git a/tools/bugpoint/OptimizerDriver.cpp b/tools/bugpoint/OptimizerDriver.cpp
index 344e7b588fb86..2cc2f4471a581 100644
--- a/tools/bugpoint/OptimizerDriver.cpp
+++ b/tools/bugpoint/OptimizerDriver.cpp
@@ -267,18 +267,11 @@ bool BugDriver::runPasses(Module *Program,
std::unique_ptr<Module>
BugDriver::runPassesOn(Module *M, const std::vector<std::string> &Passes,
- bool AutoDebugCrashes, unsigned NumExtraArgs,
+ unsigned NumExtraArgs,
const char *const *ExtraArgs) {
std::string BitcodeResult;
if (runPasses(M, Passes, BitcodeResult, false/*delete*/, true/*quiet*/,
NumExtraArgs, ExtraArgs)) {
- if (AutoDebugCrashes) {
- errs() << " Error running this sequence of passes"
- << " on the input program!\n";
- delete swapProgramIn(M);
- EmitProgressBitcode(M, "pass-error", false);
- exit(debugOptimizerCrash());
- }
return nullptr;
}
diff --git a/tools/bugpoint/ToolRunner.cpp b/tools/bugpoint/ToolRunner.cpp
index 2ccd649051289..4af4b10c4d78a 100644
--- a/tools/bugpoint/ToolRunner.cpp
+++ b/tools/bugpoint/ToolRunner.cpp
@@ -21,6 +21,7 @@
#include "llvm/Support/raw_ostream.h"
#include <fstream>
#include <sstream>
+#include <utility>
using namespace llvm;
#define DEBUG_TYPE "toolrunner"
@@ -272,9 +273,9 @@ namespace {
std::string CompilerCommand;
std::vector<std::string> CompilerArgs;
public:
- CustomCompiler(
- const std::string &CompilerCmd, std::vector<std::string> CompArgs) :
- CompilerCommand(CompilerCmd), CompilerArgs(CompArgs) {}
+ CustomCompiler(const std::string &CompilerCmd,
+ std::vector<std::string> CompArgs)
+ : CompilerCommand(CompilerCmd), CompilerArgs(std::move(CompArgs)) {}
void compileProgram(const std::string &Bitcode,
std::string *Error,
@@ -333,9 +334,9 @@ namespace {
std::string ExecutionCommand;
std::vector<std::string> ExecutorArgs;
public:
- CustomExecutor(
- const std::string &ExecutionCmd, std::vector<std::string> ExecArgs) :
- ExecutionCommand(ExecutionCmd), ExecutorArgs(ExecArgs) {}
+ CustomExecutor(const std::string &ExecutionCmd,
+ std::vector<std::string> ExecArgs)
+ : ExecutionCommand(ExecutionCmd), ExecutorArgs(std::move(ExecArgs)) {}
int ExecuteProgram(const std::string &Bitcode,
const std::vector<std::string> &Args,
diff --git a/tools/bugpoint/bugpoint.cpp b/tools/bugpoint/bugpoint.cpp
index 48f30e6709f80..28565f1daac33 100644
--- a/tools/bugpoint/bugpoint.cpp
+++ b/tools/bugpoint/bugpoint.cpp
@@ -113,7 +113,7 @@ void initializePollyPasses(llvm::PassRegistry &Registry);
int main(int argc, char **argv) {
#ifndef DEBUG_BUGPOINT
- llvm::sys::PrintStackTraceOnErrorSignal();
+ llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
llvm::PrettyStackTraceProgram X(argc, argv);
llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
#endif
@@ -143,7 +143,7 @@ int main(int argc, char **argv) {
sys::SetInterruptFunction(BugpointInterruptFunction);
#endif
- LLVMContext& Context = getGlobalContext();
+ LLVMContext Context;
// If we have an override, set it and then track the triple we want Modules
// to use.
if (!OverrideTriple.empty()) {
diff --git a/tools/dsymutil/BinaryHolder.cpp b/tools/dsymutil/BinaryHolder.cpp
index 4c2c1d195c3ca..e45f7fefe0844 100644
--- a/tools/dsymutil/BinaryHolder.cpp
+++ b/tools/dsymutil/BinaryHolder.cpp
@@ -19,15 +19,6 @@
namespace llvm {
namespace dsymutil {
-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) {
@@ -82,13 +73,15 @@ BinaryHolder::GetMemoryBuffersForFile(StringRef Filename,
auto ErrOrFat = object::MachOUniversalBinary::create(
CurrentMemoryBuffer->getMemBufferRef());
- if (ErrOrFat.getError()) {
+ if (!ErrOrFat) {
+ consumeError(ErrOrFat.takeError());
// 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,
+ CurrentFatBinaryName = Filename;
+ return getMachOFatMemoryBuffers(CurrentFatBinaryName, *CurrentMemoryBuffer,
*CurrentFatBinary);
}
@@ -109,10 +102,8 @@ BinaryHolder::GetArchiveMemberBuffers(StringRef Filename,
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;
+ Error Err;
+ for (auto Child : CurrentArchive->children(Err)) {
if (auto NameOrErr = Child.getName()) {
if (*NameOrErr == Filename) {
if (Timestamp != sys::TimeValue::PosixZeroTime() &&
@@ -130,6 +121,8 @@ BinaryHolder::GetArchiveMemberBuffers(StringRef Filename,
}
}
}
+ if (Err)
+ return errorToErrorCode(std::move(Err));
}
if (Buffers.empty())
@@ -153,19 +146,21 @@ BinaryHolder::MapArchiveAndGetMemberBuffers(StringRef Filename,
std::vector<MemoryBufferRef> ArchiveBuffers;
auto ErrOrFat = object::MachOUniversalBinary::create(
CurrentMemoryBuffer->getMemBufferRef());
- if (ErrOrFat.getError()) {
+ if (!ErrOrFat) {
+ consumeError(ErrOrFat.takeError());
// Not a fat binary must be a standard one.
ArchiveBuffers.push_back(CurrentMemoryBuffer->getMemBufferRef());
} else {
CurrentFatBinary = std::move(*ErrOrFat);
+ CurrentFatBinaryName = ArchiveFilename;
ArchiveBuffers = getMachOFatMemoryBuffers(
- ArchiveFilename, *CurrentMemoryBuffer, *CurrentFatBinary);
+ CurrentFatBinaryName, *CurrentMemoryBuffer, *CurrentFatBinary);
}
for (auto MemRef : ArchiveBuffers) {
auto ErrOrArchive = object::Archive::create(MemRef);
- if (auto Err = ErrOrArchive.getError())
- return Err;
+ if (!ErrOrArchive)
+ return errorToErrorCode(ErrOrArchive.takeError());
CurrentArchives.push_back(std::move(*ErrOrArchive));
}
return GetArchiveMemberBuffers(Filename, Timestamp);
@@ -175,7 +170,7 @@ ErrorOr<const object::ObjectFile &>
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())
+ if (MachO->getArchTriple().str() == T.str())
return *MachO;
} else if (Obj->getArch() == T.getArch())
return *Obj;
@@ -196,8 +191,8 @@ BinaryHolder::GetObjectFiles(StringRef Filename, sys::TimeValue Timestamp) {
CurrentObjectFiles.clear();
for (auto MemBuf : *ErrOrMemBufferRefs) {
auto ErrOrObjectFile = object::ObjectFile::createObjectFile(MemBuf);
- if (auto Err = ErrOrObjectFile.getError())
- return Err;
+ if (!ErrOrObjectFile)
+ return errorToErrorCode(ErrOrObjectFile.takeError());
Objects.push_back(ErrOrObjectFile->get());
CurrentObjectFiles.push_back(std::move(*ErrOrObjectFile));
diff --git a/tools/dsymutil/BinaryHolder.h b/tools/dsymutil/BinaryHolder.h
index 9d7b4bd8787f3..97508b9fb09da 100644
--- a/tools/dsymutil/BinaryHolder.h
+++ b/tools/dsymutil/BinaryHolder.h
@@ -42,6 +42,7 @@ class BinaryHolder {
std::unique_ptr<MemoryBuffer> CurrentMemoryBuffer;
std::vector<std::unique_ptr<object::ObjectFile>> CurrentObjectFiles;
std::unique_ptr<object::MachOUniversalBinary> CurrentFatBinary;
+ std::string CurrentFatBinaryName;
bool Verbose;
/// Get the MemoryBufferRefs for the file specification in \p
@@ -128,8 +129,6 @@ public:
return Err;
return cast<ObjectFileType>(*ErrOrObj);
}
-
- static Triple getTriple(const object::MachOObjectFile &Obj);
};
}
}
diff --git a/tools/dsymutil/DebugMap.cpp b/tools/dsymutil/DebugMap.cpp
index 4717085f43227..5130cd62ed2fc 100644
--- a/tools/dsymutil/DebugMap.cpp
+++ b/tools/dsymutil/DebugMap.cpp
@@ -24,13 +24,13 @@ DebugMapObject::DebugMapObject(StringRef ObjectFilename,
sys::TimeValue Timestamp)
: Filename(ObjectFilename), Timestamp(Timestamp) {}
-bool DebugMapObject::addSymbol(StringRef Name, uint64_t ObjectAddress,
+bool DebugMapObject::addSymbol(StringRef Name, Optional<uint64_t> ObjectAddress,
uint64_t LinkedAddress, uint32_t Size) {
auto InsertResult = Symbols.insert(
std::make_pair(Name, SymbolMapping(ObjectAddress, LinkedAddress, Size)));
- if (InsertResult.second)
- AddressToMapping[ObjectAddress] = &*InsertResult.first;
+ if (ObjectAddress && InsertResult.second)
+ AddressToMapping[*ObjectAddress] = &*InsertResult.first;
return InsertResult.second;
}
@@ -47,8 +47,11 @@ void DebugMapObject::print(raw_ostream &OS) const {
Entries.begin(), Entries.end(),
[](const Entry &LHS, const Entry &RHS) { return LHS.first < RHS.first; });
for (const auto &Sym : Entries) {
- OS << format("\t%016" PRIx64 " => %016" PRIx64 "+0x%x\t%s\n",
- uint64_t(Sym.second.ObjectAddress),
+ if (Sym.second.ObjectAddress)
+ OS << format("\t%016" PRIx64, uint64_t(*Sym.second.ObjectAddress));
+ else
+ OS << "\t????????????????";
+ OS << format(" => %016" PRIx64 "+0x%x\t%s\n",
uint64_t(Sym.second.BinaryAddress), uint32_t(Sym.second.Size),
Sym.first.data());
}
@@ -136,7 +139,7 @@ struct MappingTraits<dsymutil::DebugMapObject>::YamlDMO {
void MappingTraits<std::pair<std::string, DebugMapObject::SymbolMapping>>::
mapping(IO &io, std::pair<std::string, DebugMapObject::SymbolMapping> &s) {
io.mapRequired("sym", s.first);
- io.mapRequired("objAddr", s.second.ObjectAddress);
+ io.mapOptional("objAddr", s.second.ObjectAddress);
io.mapRequired("binAddr", s.second.BinaryAddress);
io.mapOptional("size", s.second.Size);
}
@@ -225,9 +228,14 @@ MappingTraits<dsymutil::DebugMapObject>::YamlDMO::denormalize(IO &IO) {
// look them up here and rewrite them.
for (const auto &Sym : ErrOrObjectFile->symbols()) {
uint64_t Address = Sym.getValue();
- ErrorOr<StringRef> Name = Sym.getName();
- if (!Name)
+ Expected<StringRef> Name = Sym.getName();
+ if (!Name ||
+ (Sym.getFlags() & (SymbolRef::SF_Absolute | SymbolRef::SF_Common))) {
+ // TODO: Actually report errors helpfully.
+ if (!Name)
+ consumeError(Name.takeError());
continue;
+ }
SymbolAddresses[*Name] = Address;
}
}
@@ -237,7 +245,9 @@ MappingTraits<dsymutil::DebugMapObject>::YamlDMO::denormalize(IO &IO) {
dsymutil::DebugMapObject Res(Path, TV);
for (auto &Entry : Entries) {
auto &Mapping = Entry.second;
- uint64_t ObjAddress = Mapping.ObjectAddress;
+ Optional<uint64_t> ObjAddress;
+ if (Mapping.ObjectAddress)
+ ObjAddress = *Mapping.ObjectAddress;
auto AddressIt = SymbolAddresses.find(Entry.first);
if (AddressIt != SymbolAddresses.end())
ObjAddress = AddressIt->getValue();
diff --git a/tools/dsymutil/DebugMap.h b/tools/dsymutil/DebugMap.h
index 06ac5a503dcd1..1a3d62b67b7b4 100644
--- a/tools/dsymutil/DebugMap.h
+++ b/tools/dsymutil/DebugMap.h
@@ -1,4 +1,4 @@
-//===- tools/dsymutil/DebugMap.h - Generic debug map representation -------===//
+//=== tools/dsymutil/DebugMap.h - Generic debug map representation -*- C++ -*-//
//
// The LLVM Linker
//
@@ -117,12 +117,15 @@ public:
class DebugMapObject {
public:
struct SymbolMapping {
- yaml::Hex64 ObjectAddress;
+ Optional<yaml::Hex64> ObjectAddress;
yaml::Hex64 BinaryAddress;
yaml::Hex32 Size;
- SymbolMapping(uint64_t ObjectAddress, uint64_t BinaryAddress, uint32_t Size)
- : ObjectAddress(ObjectAddress), BinaryAddress(BinaryAddress),
- Size(Size) {}
+ SymbolMapping(Optional<uint64_t> ObjectAddr, uint64_t BinaryAddress,
+ uint32_t Size)
+ : BinaryAddress(BinaryAddress), Size(Size) {
+ if (ObjectAddr)
+ ObjectAddress = *ObjectAddr;
+ }
/// For YAML IO support
SymbolMapping() = default;
};
@@ -132,7 +135,7 @@ public:
/// \brief Adds a symbol mapping to this DebugMapObject.
/// \returns false if the symbol was already registered. The request
/// is discarded in this case.
- bool addSymbol(llvm::StringRef SymName, uint64_t ObjectAddress,
+ bool addSymbol(llvm::StringRef SymName, Optional<uint64_t> ObjectAddress,
uint64_t LinkedAddress, uint32_t Size);
/// \brief Lookup a symbol mapping.
diff --git a/tools/dsymutil/DwarfLinker.cpp b/tools/dsymutil/DwarfLinker.cpp
index 8c5f7946a4257..bea30ded591fc 100644
--- a/tools/dsymutil/DwarfLinker.cpp
+++ b/tools/dsymutil/DwarfLinker.cpp
@@ -315,16 +315,15 @@ public:
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) {
+ StringRef getResolvedPath(unsigned FileNum) {
if (FileNum >= ResolvedPaths.size())
- return nullptr;
- return ResolvedPaths[FileNum].size() ? ResolvedPaths[FileNum].c_str()
- : nullptr;
+ return StringRef();
+ return ResolvedPaths[FileNum];
}
/// Set the fully resolved path for the line-table's file \a FileNum
/// to \a Path.
- void setResolvedPath(unsigned FileNum, const std::string &Path) {
+ void setResolvedPath(unsigned FileNum, StringRef Path) {
if (ResolvedPaths.size() <= FileNum)
ResolvedPaths.resize(FileNum + 1);
ResolvedPaths[FileNum] = Path;
@@ -378,7 +377,10 @@ private:
/// @}
/// Cached resolved paths from the line table.
- std::vector<std::string> ResolvedPaths;
+ /// Note, the StringRefs here point in to the intern (uniquing) string pool.
+ /// This means that a StringRef returned here doesn't need to then be uniqued
+ /// for the purposes of getting a unique address for each string.
+ std::vector<StringRef> ResolvedPaths;
/// Is this unit subject to the ODR rule?
bool HasODR;
@@ -529,7 +531,7 @@ public:
/// original \p Entries.
void emitRangesEntries(
int64_t UnitPcOffset, uint64_t OrigLowPc,
- FunctionIntervals::const_iterator FuncRange,
+ const FunctionIntervals::const_iterator &FuncRange,
const std::vector<DWARFDebugRangeList::RangeListEntry> &Entries,
unsigned AddressSize);
@@ -593,8 +595,7 @@ bool DwarfStreamer::init(Triple TheTriple, StringRef OutputFilename) {
MOFI.reset(new MCObjectFileInfo);
MC.reset(new MCContext(MAI.get(), MRI.get(), MOFI.get()));
- MOFI->InitMCObjectFileInfo(TheTriple, Reloc::Default, CodeModel::Default,
- *MC);
+ MOFI->InitMCObjectFileInfo(TheTriple, /*PIC*/ false, CodeModel::Default, *MC);
MAB = TheTarget->createMCAsmBackend(*MRI, TripleName, "");
if (!MAB)
@@ -628,7 +629,8 @@ bool DwarfStreamer::init(Triple TheTriple, StringRef OutputFilename) {
return error("no object streamer for target " + TripleName, Context);
// Finally create the AsmPrinter we'll use to emit the DIEs.
- TM.reset(TheTarget->createTargetMachine(TripleName, "", "", TargetOptions()));
+ TM.reset(TheTarget->createTargetMachine(TripleName, "", "", TargetOptions(),
+ None));
if (!TM)
return error("no target machine for target " + TripleName, Context);
@@ -713,7 +715,7 @@ void DwarfStreamer::emitStrings(const NonRelocatableStringpool &Pool) {
/// sized addresses describing the ranges.
void DwarfStreamer::emitRangesEntries(
int64_t UnitPcOffset, uint64_t OrigLowPc,
- FunctionIntervals::const_iterator FuncRange,
+ const FunctionIntervals::const_iterator &FuncRange,
const std::vector<DWARFDebugRangeList::RangeListEntry> &Entries,
unsigned AddressSize) {
MS->SwitchSection(MC->getObjectFileInfo()->getDwarfRangesSection());
@@ -790,7 +792,7 @@ void DwarfStreamer::emitUnitRangesEntries(CompileUnit &Unit,
Asm->EmitInt8(AddressSize); // Address size
Asm->EmitInt8(0); // Segment size
- Asm->OutStreamer->EmitFill(Padding, 0x0);
+ Asm->OutStreamer->emitFill(Padding, 0x0);
for (auto Range = Ranges.begin(), End = Ranges.end(); Range != End;
++Range) {
@@ -1414,7 +1416,7 @@ private:
/// \defgroup Helpers Various helper methods.
///
/// @{
- bool createStreamer(Triple TheTriple, StringRef OutputFilename);
+ bool createStreamer(const Triple &TheTriple, StringRef OutputFilename);
/// \brief Attempt to load a debug object from disk.
ErrorOr<const object::ObjectFile &> loadObject(BinaryHolder &BinaryHolder,
@@ -1458,6 +1460,9 @@ private:
/// Mapping the PCM filename to the DwoId.
StringMap<uint64_t> ClangModules;
+
+ bool ModuleCacheHintDisplayed = false;
+ bool ArchiveHintDisplayed = false;
};
/// Similar to DWARFUnitSection::getUnitForOffset(), but returning our
@@ -1601,7 +1606,6 @@ PointerIntPair<DeclContext *, 1> DeclContextTree::getChildDeclContext(
Tag != dwarf::DW_TAG_enumeration_type && NameRef.empty())
return PointerIntPair<DeclContext *, 1>(nullptr);
- std::string File;
unsigned Line = 0;
unsigned ByteSize = UINT32_MAX;
@@ -1629,25 +1633,27 @@ PointerIntPair<DeclContext *, 1> DeclContextTree::getChildDeclContext(
// FIXME: Passing U.getOrigUnit().getCompilationDir()
// instead of "" would allow more uniquing, but for now, do
// it this way to match dsymutil-classic.
+ std::string File;
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;
+ StringRef ResolvedPath = U.getResolvedPath(FileNum);
+ if (!ResolvedPath.empty()) {
+ FileRef = ResolvedPath;
} else {
+#ifdef HAVE_REALPATH
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);
+ FileRef = StringPool.internString(File);
+ U.setResolvedPath(FileNum, FileRef);
+ }
}
}
}
@@ -1738,7 +1744,8 @@ void DwarfLinker::reportWarning(const Twine &Warning, const DWARFUnit *Unit,
6 /* Indent */);
}
-bool DwarfLinker::createStreamer(Triple TheTriple, StringRef OutputFilename) {
+bool DwarfLinker::createStreamer(const Triple &TheTriple,
+ StringRef OutputFilename) {
if (Options.NoOutput)
return true;
@@ -1851,10 +1858,10 @@ void DwarfLinker::startDebugObject(DWARFContext &Dwarf, DebugMapObject &Obj) {
// -gline-tables-only on Darwin.
for (const auto &Entry : Obj.symbols()) {
const auto &Mapping = Entry.getValue();
- if (Mapping.Size)
- Ranges[Mapping.ObjectAddress] = std::make_pair(
- Mapping.ObjectAddress + Mapping.Size,
- int64_t(Mapping.BinaryAddress) - Mapping.ObjectAddress);
+ if (Mapping.Size && Mapping.ObjectAddress)
+ Ranges[*Mapping.ObjectAddress] = std::make_pair(
+ *Mapping.ObjectAddress + Mapping.Size,
+ int64_t(Mapping.BinaryAddress) - *Mapping.ObjectAddress);
}
}
@@ -1872,6 +1879,26 @@ void DwarfLinker::endDebugObject() {
DIEAlloc.Reset();
}
+static bool isMachOPairedReloc(uint64_t RelocType, uint64_t Arch) {
+ switch (Arch) {
+ case Triple::x86:
+ return RelocType == MachO::GENERIC_RELOC_SECTDIFF ||
+ RelocType == MachO::GENERIC_RELOC_LOCAL_SECTDIFF;
+ case Triple::x86_64:
+ return RelocType == MachO::X86_64_RELOC_SUBTRACTOR;
+ case Triple::arm:
+ case Triple::thumb:
+ return RelocType == MachO::ARM_RELOC_SECTDIFF ||
+ RelocType == MachO::ARM_RELOC_LOCAL_SECTDIFF ||
+ RelocType == MachO::ARM_RELOC_HALF ||
+ RelocType == MachO::ARM_RELOC_HALF_SECTDIFF;
+ case Triple::aarch64:
+ return RelocType == MachO::ARM64_RELOC_SUBTRACTOR;
+ default:
+ return false;
+ }
+}
+
/// \brief Iterate over the relocations of the given \p Section and
/// store the ones that correspond to debug map entries into the
/// ValidRelocs array.
@@ -1882,10 +1909,24 @@ findValidRelocsMachO(const object::SectionRef &Section,
StringRef Contents;
Section.getContents(Contents);
DataExtractor Data(Contents, Obj.isLittleEndian(), 0);
+ bool SkipNext = false;
for (const object::RelocationRef &Reloc : Section.relocations()) {
+ if (SkipNext) {
+ SkipNext = false;
+ continue;
+ }
+
object::DataRefImpl RelocDataRef = Reloc.getRawDataRefImpl();
MachO::any_relocation_info MachOReloc = Obj.getRelocation(RelocDataRef);
+
+ if (isMachOPairedReloc(Obj.getAnyRelocationType(MachOReloc),
+ Obj.getArch())) {
+ SkipNext = true;
+ Linker.reportWarning(" unsupported relocation in debug_info section.");
+ continue;
+ }
+
unsigned RelocSize = 1 << Obj.getAnyRelocationLength(MachOReloc);
uint64_t Offset64 = Reloc.getOffset();
if ((RelocSize != 4 && RelocSize != 8)) {
@@ -1895,21 +1936,35 @@ findValidRelocsMachO(const object::SectionRef &Section,
uint32_t Offset = Offset64;
// Mach-o uses REL relocations, the addend is at the relocation offset.
uint64_t Addend = Data.getUnsigned(&Offset, RelocSize);
+ uint64_t SymAddress;
+ int64_t SymOffset;
+
+ if (Obj.isRelocationScattered(MachOReloc)) {
+ // The address of the base symbol for scattered relocations is
+ // stored in the reloc itself. The actual addend will store the
+ // base address plus the offset.
+ SymAddress = Obj.getScatteredRelocationValue(MachOReloc);
+ SymOffset = int64_t(Addend) - SymAddress;
+ } else {
+ SymAddress = Addend;
+ SymOffset = 0;
+ }
auto Sym = Reloc.getSymbol();
if (Sym != Obj.symbol_end()) {
- ErrorOr<StringRef> SymbolName = Sym->getName();
+ Expected<StringRef> SymbolName = Sym->getName();
if (!SymbolName) {
+ consumeError(SymbolName.takeError());
Linker.reportWarning("error getting relocation symbol name.");
continue;
}
if (const auto *Mapping = DMO.lookupSymbol(*SymbolName))
ValidRelocs.emplace_back(Offset64, RelocSize, Addend, Mapping);
- } else if (const auto *Mapping = DMO.lookupObjectAddress(Addend)) {
+ } else if (const auto *Mapping = DMO.lookupObjectAddress(SymAddress)) {
// Do not store the addend. The addend was the address of the
// symbol in the object file, the address in the binary that is
// stored in the debug map doesn't need to be offseted.
- ValidRelocs.emplace_back(Offset64, RelocSize, 0, Mapping);
+ ValidRelocs.emplace_back(Offset64, RelocSize, SymOffset, Mapping);
}
}
}
@@ -1985,14 +2040,16 @@ hasValidRelocation(uint32_t StartOffset, uint32_t EndOffset,
const auto &ValidReloc = ValidRelocs[NextValidReloc++];
const auto &Mapping = ValidReloc.Mapping->getValue();
+ uint64_t ObjectAddress =
+ Mapping.ObjectAddress ? uint64_t(*Mapping.ObjectAddress) : UINT64_MAX;
if (Linker.Options.Verbose)
outs() << "Found valid debug map entry: " << ValidReloc.Mapping->getKey()
- << " " << format("\t%016" PRIx64 " => %016" PRIx64,
- uint64_t(Mapping.ObjectAddress),
+ << " " << format("\t%016" PRIx64 " => %016" PRIx64, ObjectAddress,
uint64_t(Mapping.BinaryAddress));
- Info.AddrAdjust = int64_t(Mapping.BinaryAddress) + ValidReloc.Addend -
- Mapping.ObjectAddress;
+ Info.AddrAdjust = int64_t(Mapping.BinaryAddress) + ValidReloc.Addend;
+ if (Mapping.ObjectAddress)
+ Info.AddrAdjust -= ObjectAddress;
Info.InDebugMap = true;
return true;
}
@@ -3193,7 +3250,10 @@ bool DwarfLinker::registerModuleReference(
auto Cached = ClangModules.find(PCMfile);
if (Cached != ClangModules.end()) {
- if (Cached->second != DwoId)
+ // FIXME: Until PR27449 (https://llvm.org/bugs/show_bug.cgi?id=27449) is
+ // fixed in clang, only warn about DWO_id mismatches in verbose mode.
+ // ASTFileSignatures will change randomly when a module is rebuilt.
+ if (Options.Verbose && (Cached->second != DwoId))
reportWarning(Twine("hash mismatch: this object file was built against a "
"different version of the module ") + PCMfile);
if (Options.Verbose)
@@ -3238,7 +3298,37 @@ void DwarfLinker::loadClangModule(StringRef Filename, StringRef ModulePath,
ModuleMap.addDebugMapObject(Path, sys::TimeValue::PosixZeroTime());
auto ErrOrObj = loadObject(ObjHolder, Obj, ModuleMap);
if (!ErrOrObj) {
- ClangModules.erase(ClangModules.find(Filename));
+ // Try and emit more helpful warnings by applying some heuristics.
+ StringRef ObjFile = CurrentDebugObject->getObjectFilename();
+ bool isClangModule = sys::path::extension(Filename).equals(".pcm");
+ bool isArchive = ObjFile.endswith(")");
+ if (isClangModule) {
+ StringRef ModuleCacheDir = sys::path::parent_path(Path);
+ if (sys::fs::exists(ModuleCacheDir)) {
+ // If the module's parent directory exists, we assume that the module
+ // cache has expired and was pruned by clang. A more adventurous
+ // dsymutil would invoke clang to rebuild the module now.
+ if (!ModuleCacheHintDisplayed) {
+ errs() << "note: The clang module cache may have expired since this "
+ "object file was built. Rebuilding the object file will "
+ "rebuild the module cache.\n";
+ ModuleCacheHintDisplayed = true;
+ }
+ } else if (isArchive) {
+ // If the module cache directory doesn't exist at all and the object
+ // file is inside a static library, we assume that the static library
+ // was built on a different machine. We don't want to discourage module
+ // debugging for convenience libraries within a project though.
+ if (!ArchiveHintDisplayed) {
+ errs() << "note: Linking a static library that was built with "
+ "-gmodules, but the module cache was not found. "
+ "Redistributable static libraries should never be built "
+ "with module debugging enabled. The debug experience will "
+ "be degraded due to incomplete debug information.\n";
+ ArchiveHintDisplayed = true;
+ }
+ }
+ }
return;
}
@@ -3256,10 +3346,18 @@ void DwarfLinker::loadClangModule(StringRef Filename, StringRef ModulePath,
<< " 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);
+ // FIXME: Until PR27449 (https://llvm.org/bugs/show_bug.cgi?id=27449) is
+ // fixed in clang, only warn about DWO_id mismatches in verbose mode.
+ // ASTFileSignatures will change randomly when a module is rebuilt.
+ uint64_t PCMDwoId = getDwoId(*CUDie, *CU);
+ if (PCMDwoId != DwoId) {
+ if (Options.Verbose)
+ reportWarning(
+ Twine("hash mismatch: this object file was built against a "
+ "different version of the module ") + Filename);
+ // Update the cache entry with the DwoId of the module loaded from disk.
+ ClangModules[Filename] = PCMDwoId;
+ }
// Add this module.
Unit = llvm::make_unique<CompileUnit>(*CU, UnitID++, !Options.NoODR,
diff --git a/tools/dsymutil/MachODebugMapParser.cpp b/tools/dsymutil/MachODebugMapParser.cpp
index 4412db25426ca..22215200ed5f6 100644
--- a/tools/dsymutil/MachODebugMapParser.cpp
+++ b/tools/dsymutil/MachODebugMapParser.cpp
@@ -137,8 +137,7 @@ void MachODebugMapParser::switchToNewDebugMapObject(StringRef Filename,
}
static std::string getArchName(const object::MachOObjectFile &Obj) {
- Triple ThumbTriple;
- Triple T = Obj.getArch(nullptr, &ThumbTriple);
+ Triple T = Obj.getArchTriple();
return T.getArchName();
}
@@ -146,8 +145,7 @@ std::unique_ptr<DebugMap>
MachODebugMapParser::parseOneBinary(const MachOObjectFile &MainBinary,
StringRef BinaryPath) {
loadMainBinarySymbols(MainBinary);
- Result =
- make_unique<DebugMap>(BinaryHolder::getTriple(MainBinary), BinaryPath);
+ Result = make_unique<DebugMap>(MainBinary.getArchTriple(), BinaryPath);
MainBinaryStrings = MainBinary.getStringTableData();
for (const SymbolRef &Symbol : MainBinary.symbols()) {
const DataRefImpl &DRI = Symbol.getRawDataRefImpl();
@@ -296,7 +294,11 @@ static bool shouldLinkArch(SmallVectorImpl<StringRef> &Archs, StringRef Arch) {
std::find(Archs.begin(), Archs.end(), "arm") != Archs.end())
return true;
- return std::find(Archs.begin(), Archs.end(), Arch) != Archs.end();
+ SmallString<16> ArchName = Arch;
+ if (Arch.startswith("thumb"))
+ ArchName = ("arm" + Arch.substr(5)).str();
+
+ return std::find(Archs.begin(), Archs.end(), ArchName) != Archs.end();
}
bool MachODebugMapParser::dumpStab() {
@@ -308,9 +310,8 @@ bool MachODebugMapParser::dumpStab() {
return false;
}
- Triple T;
for (const auto *Binary : *MainBinOrError)
- if (shouldLinkArch(Archs, Binary->getArch(nullptr, &T).getArchName()))
+ if (shouldLinkArch(Archs, Binary->getArchTriple().getArchName()))
dumpOneBinaryStab(*Binary, BinaryPath);
return true;
@@ -326,9 +327,8 @@ ErrorOr<std::vector<std::unique_ptr<DebugMap>>> MachODebugMapParser::parse() {
return Error;
std::vector<std::unique_ptr<DebugMap>> Results;
- Triple T;
for (const auto *Binary : *MainBinOrError)
- if (shouldLinkArch(Archs, Binary->getArch(nullptr, &T).getArchName()))
+ if (shouldLinkArch(Archs, Binary->getArchTriple().getArchName()))
Results.push_back(parseOneBinary(*Binary, BinaryPath));
return std::move(Results);
@@ -389,9 +389,7 @@ void MachODebugMapParser::handleStabSymbolTableEntry(uint32_t StringIndex,
if (ObjectSymIt == CurrentObjectAddresses.end())
return Warning("could not find object file symbol for symbol " +
Twine(Name));
- if (!ObjectSymIt->getValue())
- return;
- if (!CurrentDebugMapObject->addSymbol(Name, *ObjectSymIt->getValue(), Value,
+ if (!CurrentDebugMapObject->addSymbol(Name, ObjectSymIt->getValue(), Value,
Size))
return Warning(Twine("failed to insert symbol '") + Name +
"' in the debug map.");
@@ -404,15 +402,21 @@ void MachODebugMapParser::loadCurrentObjectFileSymbols(
for (auto Sym : Obj.symbols()) {
uint64_t Addr = Sym.getValue();
- ErrorOr<StringRef> Name = Sym.getName();
- if (!Name)
+ Expected<StringRef> Name = Sym.getName();
+ if (!Name) {
+ // TODO: Actually report errors helpfully.
+ consumeError(Name.takeError());
continue;
- // 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)
+ }
+ // The value of some categories of symbols isn't meaningful. For
+ // example common symbols store their size in the value field, not
+ // their address. Absolute symbols have a fixed address that can
+ // conflict with standard symbols. These symbols (especially the
+ // common ones), might still be referenced by relocations. These
+ // relocations will use the symbol itself, and won't need an
+ // object file address. The object file address field is optional
+ // in the DebugMap, leave it unassigned for these symbols.
+ if (Sym.getFlags() & (SymbolRef::SF_Absolute | SymbolRef::SF_Common))
CurrentObjectAddresses[*Name] = None;
else
CurrentObjectAddresses[*Name] = Addr;
@@ -436,7 +440,13 @@ void MachODebugMapParser::loadMainBinarySymbols(
section_iterator Section = MainBinary.section_end();
MainBinarySymbolAddresses.clear();
for (const auto &Sym : MainBinary.symbols()) {
- SymbolRef::Type Type = Sym.getType();
+ Expected<SymbolRef::Type> TypeOrErr = Sym.getType();
+ if (!TypeOrErr) {
+ // TODO: Actually report errors helpfully.
+ consumeError(TypeOrErr.takeError());
+ continue;
+ }
+ SymbolRef::Type Type = *TypeOrErr;
// Skip undefined and STAB entries.
if ((Type & SymbolRef::ST_Debug) || (Type & SymbolRef::ST_Unknown))
continue;
@@ -446,16 +456,22 @@ void MachODebugMapParser::loadMainBinarySymbols(
// addresses should be fetched for the debug map.
if (!(Sym.getFlags() & SymbolRef::SF_Global))
continue;
- ErrorOr<section_iterator> SectionOrErr = Sym.getSection();
- if (!SectionOrErr)
+ Expected<section_iterator> SectionOrErr = Sym.getSection();
+ if (!SectionOrErr) {
+ // TODO: Actually report errors helpfully.
+ consumeError(SectionOrErr.takeError());
continue;
+ }
Section = *SectionOrErr;
if (Section == MainBinary.section_end() || Section->isText())
continue;
uint64_t Addr = Sym.getValue();
- ErrorOr<StringRef> NameOrErr = Sym.getName();
- if (!NameOrErr)
+ Expected<StringRef> NameOrErr = Sym.getName();
+ if (!NameOrErr) {
+ // TODO: Actually report errors helpfully.
+ consumeError(NameOrErr.takeError());
continue;
+ }
StringRef Name = *NameOrErr;
if (Name.size() == 0 || Name[0] == '\0')
continue;
diff --git a/tools/dsymutil/MachOUtils.cpp b/tools/dsymutil/MachOUtils.cpp
index 44cc528ed8f2b..8a730a1d0c8a2 100644
--- a/tools/dsymutil/MachOUtils.cpp
+++ b/tools/dsymutil/MachOUtils.cpp
@@ -16,6 +16,7 @@
#include "llvm/MC/MCAsmLayout.h"
#include "llvm/MC/MCSectionMachO.h"
#include "llvm/MC/MCObjectStreamer.h"
+#include "llvm/MC/MCMachObjectWriter.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/Object/MachO.h"
#include "llvm/Support/FileUtilities.h"
@@ -241,7 +242,7 @@ static void transferSegmentAndSections(
// start address leave a sufficient gap to store the __DWARF
// segment.
uint64_t PrevEndAddress = EndAddress;
- EndAddress = RoundUpToAlignment(EndAddress, 0x1000);
+ EndAddress = alignTo(EndAddress, 0x1000);
if (GapForDwarf == UINT64_MAX && Segment.vmaddr > EndAddress &&
Segment.vmaddr - EndAddress >= DwarfSegmentSize)
GapForDwarf = EndAddress;
@@ -268,8 +269,8 @@ 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,
+ alignTo(FileSize, 0x1000), FileOffset,
+ FileSize, /* MaxProt */ 7,
/* InitProt =*/3);
for (unsigned int i = 0, n = Layout.getSectionOrder().size(); i != n; ++i) {
@@ -279,8 +280,8 @@ static void createDwarfSegment(uint64_t VMAddr, uint64_t FileOffset,
unsigned Align = Sec->getAlignment();
if (Align > 1) {
- VMAddr = RoundUpToAlignment(VMAddr, Align);
- FileOffset = RoundUpToAlignment(FileOffset, Align);
+ VMAddr = alignTo(VMAddr, Align);
+ FileOffset = alignTo(FileOffset, Align);
}
Writer.writeSection(Layout, *Sec, VMAddr, FileOffset, 0, 0, 0);
@@ -394,8 +395,7 @@ bool generateDsymCompanion(const DebugMap &DM, MCStreamer &MS,
continue;
if (uint64_t Size = Layout.getSectionFileSize(Sec)) {
- DwarfSegmentSize =
- RoundUpToAlignment(DwarfSegmentSize, Sec->getAlignment());
+ DwarfSegmentSize = alignTo(DwarfSegmentSize, Sec->getAlignment());
DwarfSegmentSize += Size;
++NumDwarfSections;
}
@@ -419,7 +419,7 @@ bool generateDsymCompanion(const DebugMap &DM, MCStreamer &MS,
uint64_t SymtabStart = LoadCommandSize;
SymtabStart += HeaderSize;
- SymtabStart = RoundUpToAlignment(SymtabStart, 0x1000);
+ SymtabStart = alignTo(SymtabStart, 0x1000);
// We gathered all the information we need, start emitting the output file.
Writer.writeHeader(MachO::MH_DSYM, NumLoadCommands, LoadCommandSize, false);
@@ -441,7 +441,7 @@ bool generateDsymCompanion(const DebugMap &DM, MCStreamer &MS,
NewStringsSize);
uint64_t DwarfSegmentStart = StringStart + NewStringsSize;
- DwarfSegmentStart = RoundUpToAlignment(DwarfSegmentStart, 0x1000);
+ DwarfSegmentStart = alignTo(DwarfSegmentStart, 0x1000);
// Write the load commands for the segments and sections we 'import' from
// the original binary.
@@ -460,7 +460,7 @@ bool generateDsymCompanion(const DebugMap &DM, MCStreamer &MS,
DwarfSegmentSize, GapForDwarf, EndAddress);
}
- uint64_t DwarfVMAddr = RoundUpToAlignment(EndAddress, 0x1000);
+ uint64_t DwarfVMAddr = alignTo(EndAddress, 0x1000);
uint64_t DwarfVMMax = Is64Bit ? UINT64_MAX : UINT32_MAX;
if (DwarfVMAddr + DwarfSegmentSize > DwarfVMMax ||
DwarfVMAddr + DwarfSegmentSize < DwarfVMAddr /* Overflow */) {
@@ -510,7 +510,7 @@ bool generateDsymCompanion(const DebugMap &DM, MCStreamer &MS,
continue;
uint64_t Pos = OutFile.tell();
- Writer.WriteZeros(RoundUpToAlignment(Pos, Sec.getAlignment()) - Pos);
+ Writer.WriteZeros(alignTo(Pos, Sec.getAlignment()) - Pos);
MCAsm.writeSectionData(&Sec, Layout);
}
diff --git a/tools/dsymutil/Makefile b/tools/dsymutil/Makefile
deleted file mode 100644
index c4365e0196b3e..0000000000000
--- a/tools/dsymutil/Makefile
+++ /dev/null
@@ -1,17 +0,0 @@
-##===- tools/dsymutil/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-dsymutil
-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/dsymutil/NonRelocatableStringpool.h b/tools/dsymutil/NonRelocatableStringpool.h
index 9db1f3342bd3f..fffd02084e344 100644
--- a/tools/dsymutil/NonRelocatableStringpool.h
+++ b/tools/dsymutil/NonRelocatableStringpool.h
@@ -9,6 +9,8 @@
#ifndef LLVM_TOOLS_DSYMUTIL_NONRELOCATABLESTRINGPOOL_H
#define LLVM_TOOLS_DSYMUTIL_NONRELOCATABLESTRINGPOOL_H
+#include "llvm/ADT/StringMap.h"
+
namespace llvm {
namespace dsymutil {
diff --git a/tools/dsymutil/dsymutil.cpp b/tools/dsymutil/dsymutil.cpp
index e9ee57f3dee67..1ce0aefeec2af 100644
--- a/tools/dsymutil/dsymutil.cpp
+++ b/tools/dsymutil/dsymutil.cpp
@@ -176,14 +176,17 @@ static std::error_code getUniqueFile(const llvm::Twine &Model, int &ResultFD,
static std::string getOutputFileName(llvm::StringRef InputFile,
bool TempFile = false) {
if (TempFile) {
+ llvm::SmallString<128> TmpFile;
+ llvm::sys::path::system_temp_directory(true, TmpFile);
llvm::StringRef Basename =
OutputFileOpt.empty() ? InputFile : llvm::StringRef(OutputFileOpt);
- llvm::Twine OutputFile = Basename + ".tmp%%%%%%.dwarf";
+ llvm::sys::path::append(TmpFile, llvm::sys::path::filename(Basename));
+
int FD;
llvm::SmallString<128> UniqueFile;
- if (auto EC = getUniqueFile(OutputFile, FD, UniqueFile)) {
+ if (auto EC = getUniqueFile(TmpFile + ".tmp%%%%%.dwarf", FD, UniqueFile)) {
llvm::errs() << "error: failed to create temporary outfile '"
- << OutputFile << "': " << EC.message() << '\n';
+ << TmpFile << "': " << EC.message() << '\n';
return "";
}
llvm::sys::RemoveFileOnSignal(UniqueFile);
@@ -234,7 +237,7 @@ void llvm::dsymutil::exitDsymutil(int ExitStatus) {
}
int main(int argc, char **argv) {
- llvm::sys::PrintStackTraceOnErrorSignal();
+ llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
llvm::PrettyStackTraceProgram StackPrinter(argc, argv);
llvm::llvm_shutdown_obj Shutdown;
LinkOptions Options;
diff --git a/tools/gold/CMakeLists.txt b/tools/gold/CMakeLists.txt
index 1a6169d65c2ca..e9029e1e7c107 100644
--- a/tools/gold/CMakeLists.txt
+++ b/tools/gold/CMakeLists.txt
@@ -10,6 +10,7 @@ if( LLVM_ENABLE_PIC AND LLVM_BINUTILS_INCDIR )
set(LLVM_LINK_COMPONENTS
${LLVM_TARGETS_TO_BUILD}
Linker
+ LTO
BitWriter
IPO
)
diff --git a/tools/gold/Makefile b/tools/gold/Makefile
deleted file mode 100644
index aa006b0048fc7..0000000000000
--- a/tools/gold/Makefile
+++ /dev/null
@@ -1,31 +0,0 @@
-#===- tools/gold/Makefile ----------------------------------*- Makefile -*-===##
-#
-# The LLVM Compiler Infrastructure
-#
-# This file is distributed under the University of Illinois Open Source
-# License. See LICENSE.TXT for details.
-#
-##===----------------------------------------------------------------------===##
-
-LEVEL := ../..
-LIBRARYNAME := LLVMgold
-LINK_LIBS_IN_SHARED := 1
-SHARED_LIBRARY := 1
-LOADABLE_MODULE := 1
-
-EXPORTED_SYMBOL_FILE = $(PROJ_SRC_DIR)/gold.exports
-
-# Include this here so we can get the configuration of the targets
-# that have been configured for construction. We have to do this
-# early so we can set up LINK_COMPONENTS before including Makefile.rules
-include $(LEVEL)/Makefile.config
-
-LINK_COMPONENTS := $(TARGETS_TO_BUILD) Linker BitWriter IPO
-
-# Because off_t is used in the public API, the largefile parts are required for
-# ABI compatibility.
-CXXFLAGS += -I$(BINUTILS_INCDIR) -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
-LDFLAGS += -L$(SharedLibDir)/$(SharedPrefix)
-
-include $(LEVEL)/Makefile.common
-
diff --git a/tools/gold/gold-plugin.cpp b/tools/gold/gold-plugin.cpp
index 31d7f156a7004..e3351c29fcdb7 100644
--- a/tools/gold/gold-plugin.cpp
+++ b/tools/gold/gold-plugin.cpp
@@ -12,8 +12,6 @@
//
//===----------------------------------------------------------------------===//
-#include "llvm/Config/config.h" // plugin-api.h requires HAVE_STDINT_H
-#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/TargetTransformInfo.h"
@@ -21,6 +19,7 @@
#include "llvm/CodeGen/Analysis.h"
#include "llvm/CodeGen/CommandFlags.h"
#include "llvm/CodeGen/ParallelCG.h"
+#include "llvm/Config/config.h" // plugin-api.h requires HAVE_STDINT_H
#include "llvm/IR/AutoUpgrade.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DiagnosticInfo.h"
@@ -29,31 +28,37 @@
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Verifier.h"
+#include "llvm/LTO/LTO.h"
#include "llvm/Linker/IRMover.h"
#include "llvm/MC/SubtargetFeature.h"
-#include "llvm/Object/FunctionIndexObjectFile.h"
#include "llvm/Object/IRObjectFile.h"
+#include "llvm/Object/ModuleSummaryIndexObjectFile.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/ThreadPool.h"
#include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/thread.h"
#include "llvm/Transforms/IPO.h"
+#include "llvm/Transforms/IPO/FunctionImport.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
+#include "llvm/Transforms/Utils/FunctionImportUtils.h"
#include "llvm/Transforms/Utils/GlobalStatus.h"
-#include "llvm/Transforms/Utils/ModuleUtils.h"
#include "llvm/Transforms/Utils/ValueMapper.h"
#include <list>
#include <plugin-api.h>
#include <system_error>
+#include <utility>
#include <vector>
-#ifndef LDPO_PIE
// FIXME: remove this declaration when we stop maintaining Ubuntu Quantal and
// Precise and Debian Wheezy (binutils 2.23 is required)
-# define LDPO_PIE 3
-#endif
+#define LDPO_PIE 3
+
+#define LDPT_GET_SYMBOLS_V3 28
using namespace llvm;
@@ -70,34 +75,68 @@ static ld_plugin_message message = discard_message;
namespace {
struct claimed_file {
void *handle;
+ void *leader_handle;
std::vector<ld_plugin_symbol> syms;
+ off_t filesize;
+ std::string name;
};
/// RAII wrapper to manage opening and releasing of a ld_plugin_input_file.
struct PluginInputFile {
void *Handle;
- ld_plugin_input_file File;
+ std::unique_ptr<ld_plugin_input_file> File;
PluginInputFile(void *Handle) : Handle(Handle) {
- if (get_input_file(Handle, &File) != LDPS_OK)
+ File = llvm::make_unique<ld_plugin_input_file>();
+ if (get_input_file(Handle, File.get()) != 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");
+ // File would have been reset to nullptr if we moved this object
+ // to a new owner.
+ if (File)
+ if (release_input_file(Handle) != LDPS_OK)
+ message(LDPL_FATAL, "Failed to release file information");
}
- ld_plugin_input_file &file() { return File; }
+
+ ld_plugin_input_file &file() { return *File; }
+
+ PluginInputFile(PluginInputFile &&RHS) = default;
+ PluginInputFile &operator=(PluginInputFile &&RHS) = default;
};
struct ResolutionInfo {
+ uint64_t CommonSize = 0;
+ unsigned CommonAlign = 0;
bool IsLinkonceOdr = true;
- bool UnnamedAddr = true;
+ GlobalValue::UnnamedAddr UnnamedAddr = GlobalValue::UnnamedAddr::Global;
GlobalValue::VisibilityTypes Visibility = GlobalValue::DefaultVisibility;
bool CommonInternal = false;
bool UseCommon = false;
- unsigned CommonSize = 0;
- unsigned CommonAlign = 0;
- claimed_file *CommonFile = nullptr;
+};
+
+/// Class to own information used by a task or during its cleanup for a
+/// ThinLTO backend instantiation.
+class ThinLTOTaskInfo {
+ /// The output stream the task will codegen into.
+ std::unique_ptr<raw_fd_ostream> OS;
+
+ /// The file name corresponding to the output stream, used during cleanup.
+ std::string Filename;
+
+ /// Flag indicating whether the output file is a temp file that must be
+ /// added to the cleanup list during cleanup.
+ bool TempOutFile;
+
+public:
+ ThinLTOTaskInfo(std::unique_ptr<raw_fd_ostream> OS, std::string Filename,
+ bool TempOutFile)
+ : OS(std::move(OS)), Filename(std::move(Filename)),
+ TempOutFile(TempOutFile) {}
+
+ /// Performs task related cleanup activities that must be done
+ /// single-threaded (i.e. call backs to gold).
+ void cleanup();
};
}
@@ -106,12 +145,14 @@ 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 Reloc::Model RelocationModel = Reloc::Default;
+static Optional<Reloc::Model> RelocationModel;
static std::string output_name = "";
static std::list<claimed_file> Modules;
+static DenseMap<int, void *> FDToLeaderHandle;
static StringMap<ResolutionInfo> ResInfo;
static std::vector<std::string> Cleanup;
static llvm::TargetOptions TargetOpts;
+static std::string DefaultTriple = sys::getDefaultTargetTriple();
namespace options {
enum OutputType {
@@ -123,7 +164,11 @@ namespace options {
static bool generate_api_file = false;
static OutputType TheOutputType = OT_NORMAL;
static unsigned OptLevel = 2;
- static unsigned Parallelism = 1;
+ // Default parallelism of 0 used to indicate that user did not specify.
+ // Actual parallelism default value depends on implementation.
+ // Currently, code generation defaults to no parallelism, whereas
+ // ThinLTO uses the hardware_concurrency as the default.
+ static unsigned Parallelism = 0;
#ifdef NDEBUG
static bool DisableVerify = true;
#else
@@ -137,6 +182,24 @@ namespace options {
// the information from intermediate files and write a combined
// global index for the ThinLTO backends.
static bool thinlto = false;
+ // If false, all ThinLTO backend compilations through code gen are performed
+ // using multiple threads in the gold-plugin, before handing control back to
+ // gold. If true, write individual backend index files which reflect
+ // the import decisions, and exit afterwards. The assumption is
+ // that the build system will launch the backend processes.
+ static bool thinlto_index_only = false;
+ // If true, when generating individual index files for distributed backends,
+ // also generate a "${bitcodefile}.imports" file at the same location for each
+ // bitcode file, listing the files it imports from in plain text. This is to
+ // support distributed build file staging.
+ static bool thinlto_emit_imports_files = false;
+ // Option to control where files for a distributed backend (the individual
+ // index files and optional imports files) are created.
+ // If specified, expects a string of the form "oldprefix:newprefix", and
+ // instead of generating these files in the same directory path as the
+ // corresponding bitcode file, will use a path formed by replacing the
+ // bitcode file's path prefix matching oldprefix with newprefix.
+ static std::string thinlto_prefix_replace;
// 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.
@@ -168,6 +231,14 @@ namespace options {
TheOutputType = OT_DISABLE;
} else if (opt == "thinlto") {
thinlto = true;
+ } else if (opt == "thinlto-index-only") {
+ thinlto_index_only = true;
+ } else if (opt == "thinlto-emit-imports-files") {
+ thinlto_emit_imports_files = true;
+ } else if (opt.startswith("thinlto-prefix-replace=")) {
+ thinlto_prefix_replace = opt.substr(strlen("thinlto-prefix-replace="));
+ if (thinlto_prefix_replace.find(";") == std::string::npos)
+ message(LDPL_FATAL, "thinlto-prefix-replace expects 'old;new' format");
} else if (opt.size() == 2 && opt[0] == 'O') {
if (opt[1] < '0' || opt[1] > '3')
message(LDPL_FATAL, "Optimization level must be between 0 and 3");
@@ -212,79 +283,87 @@ ld_plugin_status onload(ld_plugin_tv *tv) {
bool RegisteredAllSymbolsRead = false;
for (; tv->tv_tag != LDPT_NULL; ++tv) {
- switch (tv->tv_tag) {
- case LDPT_OUTPUT_NAME:
- output_name = tv->tv_u.tv_string;
- break;
- case LDPT_LINKER_OUTPUT:
- switch (tv->tv_u.tv_val) {
- case LDPO_REL: // .o
- case LDPO_DYN: // .so
- case LDPO_PIE: // position independent executable
- RelocationModel = Reloc::PIC_;
- break;
- case LDPO_EXEC: // .exe
- RelocationModel = Reloc::Static;
- break;
- default:
- message(LDPL_ERROR, "Unknown output file type %d", tv->tv_u.tv_val);
- return LDPS_ERR;
- }
- break;
- case LDPT_OPTION:
- options::process_plugin_option(tv->tv_u.tv_string);
- break;
- case LDPT_REGISTER_CLAIM_FILE_HOOK: {
- ld_plugin_register_claim_file callback;
- callback = tv->tv_u.tv_register_claim_file;
-
- if (callback(claim_file_hook) != LDPS_OK)
- return LDPS_ERR;
-
- registeredClaimFile = true;
- } break;
- case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK: {
- ld_plugin_register_all_symbols_read callback;
- callback = tv->tv_u.tv_register_all_symbols_read;
-
- if (callback(all_symbols_read_hook) != LDPS_OK)
- return LDPS_ERR;
-
- RegisteredAllSymbolsRead = true;
- } break;
- case LDPT_REGISTER_CLEANUP_HOOK: {
- ld_plugin_register_cleanup callback;
- callback = tv->tv_u.tv_register_cleanup;
-
- if (callback(cleanup_hook) != LDPS_OK)
- return LDPS_ERR;
- } break;
- case LDPT_GET_INPUT_FILE:
- get_input_file = tv->tv_u.tv_get_input_file;
- break;
- case LDPT_RELEASE_INPUT_FILE:
- release_input_file = tv->tv_u.tv_release_input_file;
- break;
- case LDPT_ADD_SYMBOLS:
- add_symbols = tv->tv_u.tv_add_symbols;
- break;
- case LDPT_GET_SYMBOLS_V2:
- get_symbols = tv->tv_u.tv_get_symbols;
- break;
- case LDPT_ADD_INPUT_FILE:
- add_input_file = tv->tv_u.tv_add_input_file;
- break;
- case LDPT_SET_EXTRA_LIBRARY_PATH:
- set_extra_library_path = tv->tv_u.tv_set_extra_library_path;
- break;
- case LDPT_GET_VIEW:
- get_view = tv->tv_u.tv_get_view;
+ // Cast tv_tag to int to allow values not in "enum ld_plugin_tag", like, for
+ // example, LDPT_GET_SYMBOLS_V3 when building against an older plugin-api.h
+ // header.
+ switch (static_cast<int>(tv->tv_tag)) {
+ case LDPT_OUTPUT_NAME:
+ output_name = tv->tv_u.tv_string;
+ break;
+ case LDPT_LINKER_OUTPUT:
+ switch (tv->tv_u.tv_val) {
+ case LDPO_REL: // .o
+ case LDPO_DYN: // .so
+ case LDPO_PIE: // position independent executable
+ RelocationModel = Reloc::PIC_;
break;
- case LDPT_MESSAGE:
- message = tv->tv_u.tv_message;
+ case LDPO_EXEC: // .exe
+ RelocationModel = Reloc::Static;
break;
default:
- break;
+ message(LDPL_ERROR, "Unknown output file type %d", tv->tv_u.tv_val);
+ return LDPS_ERR;
+ }
+ break;
+ case LDPT_OPTION:
+ options::process_plugin_option(tv->tv_u.tv_string);
+ break;
+ case LDPT_REGISTER_CLAIM_FILE_HOOK: {
+ ld_plugin_register_claim_file callback;
+ callback = tv->tv_u.tv_register_claim_file;
+
+ if (callback(claim_file_hook) != LDPS_OK)
+ return LDPS_ERR;
+
+ registeredClaimFile = true;
+ } break;
+ case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK: {
+ ld_plugin_register_all_symbols_read callback;
+ callback = tv->tv_u.tv_register_all_symbols_read;
+
+ if (callback(all_symbols_read_hook) != LDPS_OK)
+ return LDPS_ERR;
+
+ RegisteredAllSymbolsRead = true;
+ } break;
+ case LDPT_REGISTER_CLEANUP_HOOK: {
+ ld_plugin_register_cleanup callback;
+ callback = tv->tv_u.tv_register_cleanup;
+
+ if (callback(cleanup_hook) != LDPS_OK)
+ return LDPS_ERR;
+ } break;
+ case LDPT_GET_INPUT_FILE:
+ get_input_file = tv->tv_u.tv_get_input_file;
+ break;
+ case LDPT_RELEASE_INPUT_FILE:
+ release_input_file = tv->tv_u.tv_release_input_file;
+ break;
+ case LDPT_ADD_SYMBOLS:
+ add_symbols = tv->tv_u.tv_add_symbols;
+ break;
+ case LDPT_GET_SYMBOLS_V2:
+ // Do not override get_symbols_v3 with get_symbols_v2.
+ if (!get_symbols)
+ get_symbols = tv->tv_u.tv_get_symbols;
+ break;
+ case LDPT_GET_SYMBOLS_V3:
+ get_symbols = tv->tv_u.tv_get_symbols;
+ break;
+ case LDPT_ADD_INPUT_FILE:
+ add_input_file = tv->tv_u.tv_add_input_file;
+ break;
+ case LDPT_SET_EXTRA_LIBRARY_PATH:
+ set_extra_library_path = tv->tv_u.tv_set_extra_library_path;
+ break;
+ case LDPT_GET_VIEW:
+ get_view = tv->tv_u.tv_get_view;
+ break;
+ case LDPT_MESSAGE:
+ message = tv->tv_u.tv_message;
+ break;
+ default:
+ break;
}
}
@@ -305,7 +384,7 @@ ld_plugin_status onload(ld_plugin_tv *tv) {
return LDPS_ERR;
}
if (!release_input_file) {
- message(LDPL_ERROR, "relesase_input_file not passed to LLVMgold.");
+ message(LDPL_ERROR, "release_input_file not passed to LLVMgold.");
return LDPS_ERR;
}
@@ -344,7 +423,6 @@ static void diagnosticHandler(const DiagnosticInfo &DI) {
case DS_Error:
message(LDPL_FATAL, "LLVM gold plugin has failed to create LTO module: %s",
ErrStorage.c_str());
- llvm_unreachable("Fatal doesn't return.");
case DS_Warning:
Level = LDPL_WARNING;
break;
@@ -428,11 +506,23 @@ static ld_plugin_status claim_file_hook(const ld_plugin_input_file *file,
claimed_file &cf = Modules.back();
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;
+ // Keep track of the first handle for each file descriptor, since there are
+ // multiple in the case of an archive. This is used later in the case of
+ // ThinLTO parallel backends to ensure that each file is only opened and
+ // released once.
+ auto LeaderHandle =
+ FDToLeaderHandle.insert(std::make_pair(file->fd, file->handle)).first;
+ cf.leader_handle = LeaderHandle->second;
+ // Save the filesize since for parallel ThinLTO backends we can only
+ // invoke get_input_file once per archive (only for the leader handle).
+ cf.filesize = file->filesize;
+ // In the case of an archive library, all but the first member must have a
+ // non-zero offset, which we can append to the file name to obtain a
+ // unique name.
+ cf.name = file->name;
+ if (file->offset)
+ cf.name += ".llvm." + std::to_string(file->offset) + "." +
+ sys::path::filename(Obj->getModule().getSourceFileName()).str();
for (auto &Sym : Obj->symbols()) {
uint32_t Symflags = Sym.getFlags();
@@ -456,21 +546,12 @@ static ld_plugin_status claim_file_hook(const ld_plugin_input_file *file,
sym.visibility = LDPV_DEFAULT;
if (GV) {
- Res.UnnamedAddr &= GV->hasUnnamedAddr();
+ Res.UnnamedAddr =
+ GlobalValue::getMinUnnamedAddr(Res.UnnamedAddr, GV->getUnnamedAddr());
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;
break;
case GlobalValue::HiddenVisibility:
sym.visibility = LDPV_HIDDEN;
@@ -561,52 +642,56 @@ static void freeSymName(ld_plugin_symbol &Sym) {
Sym.comdat_key = nullptr;
}
-static std::unique_ptr<FunctionInfoIndex>
-getFunctionIndexForFile(claimed_file &F, ld_plugin_input_file &Info) {
+/// Helper to get a file's symbols and a view into it via gold callbacks.
+static const void *getSymbolsAndView(claimed_file &F) {
+ ld_plugin_status status = get_symbols(F.handle, F.syms.size(), F.syms.data());
+ if (status == LDPS_NO_SYMS)
+ return nullptr;
- if (get_symbols(F.handle, F.syms.size(), &F.syms[0]) != LDPS_OK)
+ if (status != LDPS_OK)
message(LDPL_FATAL, "Failed to get symbol information");
const void *View;
if (get_view(F.handle, &View) != LDPS_OK)
message(LDPL_FATAL, "Failed to get a view of file");
- MemoryBufferRef BufferRef(StringRef((const char *)View, Info.filesize),
- Info.name);
+ return View;
+}
+
+static std::unique_ptr<ModuleSummaryIndex>
+getModuleSummaryIndexForFile(claimed_file &F) {
+ const void *View = getSymbolsAndView(F);
+ if (!View)
+ return nullptr;
+
+ MemoryBufferRef BufferRef(StringRef((const char *)View, F.filesize), F.name);
// Don't bother trying to build an index if there is no summary information
// in this bitcode file.
- if (!object::FunctionIndexObjectFile::hasFunctionSummaryInMemBuffer(
+ if (!object::ModuleSummaryIndexObjectFile::hasGlobalValueSummaryInMemBuffer(
BufferRef, diagnosticHandler))
- return std::unique_ptr<FunctionInfoIndex>(nullptr);
+ return std::unique_ptr<ModuleSummaryIndex>(nullptr);
- ErrorOr<std::unique_ptr<object::FunctionIndexObjectFile>> ObjOrErr =
- object::FunctionIndexObjectFile::create(BufferRef, diagnosticHandler);
+ ErrorOr<std::unique_ptr<object::ModuleSummaryIndexObjectFile>> ObjOrErr =
+ object::ModuleSummaryIndexObjectFile::create(BufferRef,
+ diagnosticHandler);
if (std::error_code EC = ObjOrErr.getError())
- message(LDPL_FATAL, "Could not read function index bitcode from file : %s",
+ message(LDPL_FATAL,
+ "Could not read module summary index bitcode from file : %s",
EC.message().c_str());
- object::FunctionIndexObjectFile &Obj = **ObjOrErr;
+ object::ModuleSummaryIndexObjectFile &Obj = **ObjOrErr;
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,
- std::vector<GlobalValue *> &Keep) {
-
- if (get_symbols(F.handle, F.syms.size(), F.syms.data()) != LDPS_OK)
- message(LDPL_FATAL, "Failed to get symbol information");
-
- const void *View;
- if (get_view(F.handle, &View) != LDPS_OK)
- message(LDPL_FATAL, "Failed to get a view of file");
-
- MemoryBufferRef BufferRef(StringRef((const char *)View, Info.filesize),
- Info.name);
+getModuleForFile(LLVMContext &Context, claimed_file &F, const void *View,
+ StringRef Name, raw_fd_ostream *ApiFile,
+ StringSet<> &Internalize, std::vector<GlobalValue *> &Keep,
+ StringMap<unsigned> &Realign) {
+ MemoryBufferRef BufferRef(StringRef((const char *)View, F.filesize), Name);
ErrorOr<std::unique_ptr<object::IRObjectFile>> ObjOrErr =
object::IRObjectFile::create(BufferRef, Context);
@@ -656,7 +741,6 @@ getModuleForFile(LLVMContext &Context, claimed_file &F,
// 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;
@@ -664,14 +748,29 @@ getModuleForFile(LLVMContext &Context, claimed_file &F,
Resolution == LDPR_PREVAILING_DEF)
Res.UseCommon = true;
- if (Res.CommonFile == &F && Res.UseCommon) {
+ const DataLayout &DL = GV->getParent()->getDataLayout();
+ uint64_t Size = DL.getTypeAllocSize(GV->getType()->getElementType());
+ unsigned Align = GV->getAlignment();
+
+ if (Res.UseCommon && Size >= Res.CommonSize) {
+ // Take GV.
if (Res.CommonInternal)
Resolution = LDPR_PREVAILING_DEF_IRONLY;
else
Resolution = LDPR_PREVAILING_DEF;
+ cast<GlobalVariable>(GV)->setAlignment(
+ std::max(Res.CommonAlign, Align));
} else {
+ // Do not take GV, it's smaller than what we already have in the
+ // combined module.
Resolution = LDPR_PREEMPTED_IR;
+ if (Align > Res.CommonAlign)
+ // Need to raise the alignment though.
+ Realign[Sym.name] = Align;
}
+
+ Res.CommonSize = std::max(Res.CommonSize, Size);
+ Res.CommonAlign = std::max(Res.CommonAlign, Align);
}
switch (Resolution) {
@@ -715,12 +814,9 @@ getModuleForFile(LLVMContext &Context, claimed_file &F,
break;
case LDPR_PREVAILING_DEF_IRONLY_EXP: {
- // We can only check for address uses after we merge the modules. The
- // 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.
- Maybe.insert(GV->getName());
Keep.push_back(GV);
+ if (canBeOmittedFromSymbolTable(GV))
+ Internalize.insert(GV->getName());
break;
}
}
@@ -731,26 +827,6 @@ getModuleForFile(LLVMContext &Context, claimed_file &F,
return Obj.takeModule();
}
-static void runLTOPasses(Module &M, TargetMachine &TM) {
- M.setDataLayout(TM.createDataLayout());
-
- legacy::PassManager passes;
- passes.add(createTargetTransformInfoWrapperPass(TM.getTargetIRAnalysis()));
-
- 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 = !options::DisableVerify;
- PMB.LoopVectorize = true;
- PMB.SLPVectorize = true;
- PMB.OptLevel = options::OptLevel;
- PMB.populateLTOPassManager(passes);
- passes.run(M);
-}
-
static void saveBCFile(StringRef Path, Module &M) {
std::error_code EC;
raw_fd_ostream OS(Path, EC, sys::fs::OpenFlags::F_None);
@@ -759,156 +835,660 @@ static void saveBCFile(StringRef Path, Module &M) {
WriteBitcodeToFile(&M, OS, /* ShouldPreserveUseListOrder */ false);
}
-static void codegen(std::unique_ptr<Module> M) {
- const std::string &TripleStr = M->getTargetTriple();
- Triple TheTriple(TripleStr);
+static void recordFile(std::string Filename, bool TempOutFile) {
+ 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());
+}
- std::string ErrMsg;
- const Target *TheTarget = TargetRegistry::lookupTarget(TripleStr, ErrMsg);
- if (!TheTarget)
- message(LDPL_FATAL, "Target not found: %s", ErrMsg.c_str());
+void ThinLTOTaskInfo::cleanup() {
+ // Close the output file descriptor before we pass it to gold.
+ OS->close();
- if (unsigned NumOpts = options::extra.size())
- cl::ParseCommandLineOptions(NumOpts, &options::extra[0]);
+ recordFile(Filename, TempOutFile);
+}
+
+namespace {
+/// Class to manage optimization and code generation for a module, possibly
+/// in a thread (ThinLTO).
+class CodeGen {
+ /// The module for which this will generate code.
+ std::unique_ptr<llvm::Module> M;
+
+ /// The output stream to generate code into.
+ raw_fd_ostream *OS;
+
+ /// The task ID when this was invoked in a thread (ThinLTO).
+ int TaskID;
+
+ /// The module summary index for ThinLTO tasks.
+ const ModuleSummaryIndex *CombinedIndex;
+
+ /// The target machine for generating code for this module.
+ std::unique_ptr<TargetMachine> TM;
+
+ /// Filename to use as base when save-temps is enabled, used to get
+ /// a unique and identifiable save-temps output file for each ThinLTO backend.
+ std::string SaveTempsFilename;
+
+ /// Map from a module name to the corresponding buffer holding a view of the
+ /// bitcode provided via the get_view gold callback.
+ StringMap<MemoryBufferRef> *ModuleMap;
+ // Functions to import into this module.
+ FunctionImporter::ImportMapTy *ImportList;
+
+ // Map of globals defined in this module to their summary.
+ std::map<GlobalValue::GUID, GlobalValueSummary *> *DefinedGlobals;
+
+public:
+ /// Constructor used by full LTO.
+ CodeGen(std::unique_ptr<llvm::Module> M)
+ : M(std::move(M)), OS(nullptr), TaskID(-1), CombinedIndex(nullptr),
+ ModuleMap(nullptr) {
+ initTargetMachine();
+ }
+ /// Constructor used by ThinLTO.
+ CodeGen(std::unique_ptr<llvm::Module> M, raw_fd_ostream *OS, int TaskID,
+ const ModuleSummaryIndex *CombinedIndex, std::string Filename,
+ StringMap<MemoryBufferRef> *ModuleMap,
+ FunctionImporter::ImportMapTy *ImportList,
+ std::map<GlobalValue::GUID, GlobalValueSummary *> *DefinedGlobals)
+ : M(std::move(M)), OS(OS), TaskID(TaskID), CombinedIndex(CombinedIndex),
+ SaveTempsFilename(std::move(Filename)), ModuleMap(ModuleMap),
+ ImportList(ImportList), DefinedGlobals(DefinedGlobals) {
+ assert(options::thinlto == !!CombinedIndex &&
+ "Expected module summary index iff performing ThinLTO");
+ initTargetMachine();
+ }
+
+ /// Invoke LTO passes and the code generator for the module.
+ void runAll();
+
+ /// Invoke the actual code generation to emit Module's object to file.
+ void runCodegenPasses();
+
+private:
+ const Target *TheTarget;
+ std::string TripleStr;
+ std::string FeaturesString;
+ TargetOptions Options;
+
+ /// Create a target machine for the module. Must be unique for each
+ /// module/task.
+ void initTargetMachine();
+
+ std::unique_ptr<TargetMachine> createTargetMachine();
+
+ /// Run all LTO passes on the module.
+ void runLTOPasses();
+
+ /// Sets up output files necessary to perform optional multi-threaded
+ /// split code generation, and invokes the code generation implementation.
+ /// If BCFileName is not empty, saves bitcode for module partitions into
+ /// {BCFileName}0 .. {BCFileName}N.
+ void runSplitCodeGen(const SmallString<128> &BCFilename);
+};
+}
+
+static SubtargetFeatures getFeatures(Triple &TheTriple) {
SubtargetFeatures Features;
Features.getDefaultSubtargetFeatures(TheTriple);
for (const std::string &A : MAttrs)
Features.AddFeature(A);
+ return Features;
+}
- TargetOptions Options = InitTargetOptionsFromCodeGenFlags();
- CodeGenOpt::Level CGOptLevel;
+static CodeGenOpt::Level getCGOptLevel() {
switch (options::OptLevel) {
case 0:
- CGOptLevel = CodeGenOpt::None;
- break;
+ return CodeGenOpt::None;
case 1:
- CGOptLevel = CodeGenOpt::Less;
- break;
+ return CodeGenOpt::Less;
case 2:
- CGOptLevel = CodeGenOpt::Default;
- break;
+ return CodeGenOpt::Default;
case 3:
- CGOptLevel = CodeGenOpt::Aggressive;
- break;
+ return CodeGenOpt::Aggressive;
}
- std::unique_ptr<TargetMachine> TM(TheTarget->createTargetMachine(
- TripleStr, options::mcpu, Features.getString(), Options, RelocationModel,
+ llvm_unreachable("Invalid optimization level");
+}
+
+void CodeGen::initTargetMachine() {
+ TripleStr = M->getTargetTriple();
+ Triple TheTriple(TripleStr);
+
+ std::string ErrMsg;
+ TheTarget = TargetRegistry::lookupTarget(TripleStr, ErrMsg);
+ if (!TheTarget)
+ message(LDPL_FATAL, "Target not found: %s", ErrMsg.c_str());
+
+ SubtargetFeatures Features = getFeatures(TheTriple);
+ FeaturesString = Features.getString();
+ Options = InitTargetOptionsFromCodeGenFlags();
+
+ // Disable the new X86 relax relocations since gold might not support them.
+ // FIXME: Check the gold version or add a new option to enable them.
+ Options.RelaxELFRelocations = false;
+
+ TM = createTargetMachine();
+}
+
+std::unique_ptr<TargetMachine> CodeGen::createTargetMachine() {
+ CodeGenOpt::Level CGOptLevel = getCGOptLevel();
+
+ return std::unique_ptr<TargetMachine>(TheTarget->createTargetMachine(
+ TripleStr, options::mcpu, FeaturesString, Options, RelocationModel,
CodeModel::Default, CGOptLevel));
+}
+
+void CodeGen::runLTOPasses() {
+ M->setDataLayout(TM->createDataLayout());
+
+ if (CombinedIndex) {
+ // Apply summary-based LinkOnce/Weak resolution decisions.
+ thinLTOResolveWeakForLinkerModule(*M, *DefinedGlobals);
+
+ // Apply summary-based internalization decisions. Skip if there are no
+ // defined globals from the summary since not only is it unnecessary, but
+ // if this module did not have a summary section the internalizer will
+ // assert if it finds any definitions in this module that aren't in the
+ // DefinedGlobals set.
+ if (!DefinedGlobals->empty())
+ thinLTOInternalizeModule(*M, *DefinedGlobals);
+
+ // Create a loader that will parse the bitcode from the buffers
+ // in the ModuleMap.
+ ModuleLoader Loader(M->getContext(), *ModuleMap);
- runLTOPasses(*M, *TM);
+ // Perform function importing.
+ FunctionImporter Importer(*CombinedIndex, Loader);
+ Importer.importFunctions(*M, *ImportList);
+ }
+
+ legacy::PassManager passes;
+ passes.add(createTargetTransformInfoWrapperPass(TM->getTargetIRAnalysis()));
+
+ 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 = !options::DisableVerify;
+ PMB.LoopVectorize = true;
+ PMB.SLPVectorize = true;
+ PMB.OptLevel = options::OptLevel;
+ if (options::thinlto)
+ PMB.populateThinLTOPassManager(passes);
+ else
+ PMB.populateLTOPassManager(passes);
+ passes.run(*M);
+}
+
+/// Open a file and return the new file descriptor given a base input
+/// file name, a flag indicating whether a temp file should be generated,
+/// and an optional task id. The new filename generated is
+/// returned in \p NewFilename.
+static int openOutputFile(SmallString<128> InFilename, bool TempOutFile,
+ SmallString<128> &NewFilename, int TaskID = -1) {
+ int FD;
+ if (TempOutFile) {
+ std::error_code EC =
+ sys::fs::createTemporaryFile("lto-llvm", "o", FD, NewFilename);
+ if (EC)
+ message(LDPL_FATAL, "Could not create temporary file: %s",
+ EC.message().c_str());
+ } else {
+ NewFilename = InFilename;
+ if (TaskID >= 0)
+ NewFilename += utostr(TaskID);
+ std::error_code EC =
+ sys::fs::openFileForWrite(NewFilename, FD, sys::fs::F_None);
+ if (EC)
+ message(LDPL_FATAL, "Could not open file: %s", EC.message().c_str());
+ }
+ return FD;
+}
- if (options::TheOutputType == options::OT_SAVE_TEMPS)
- saveBCFile(output_name + ".opt.bc", *M);
+void CodeGen::runCodegenPasses() {
+ assert(OS && "Output stream must be set before emitting to file");
+ legacy::PassManager CodeGenPasses;
+ if (TM->addPassesToEmitFile(CodeGenPasses, *OS,
+ TargetMachine::CGFT_ObjectFile))
+ report_fatal_error("Failed to setup codegen");
+ CodeGenPasses.run(*M);
+}
+void CodeGen::runSplitCodeGen(const SmallString<128> &BCFilename) {
SmallString<128> Filename;
+ // Note that openOutputFile will append a unique ID for each task
if (!options::obj_path.empty())
Filename = options::obj_path;
else if (options::TheOutputType == options::OT_SAVE_TEMPS)
Filename = output_name + ".o";
- std::vector<SmallString<128>> Filenames(options::Parallelism);
+ // Note that the default parallelism is 1 instead of the
+ // hardware_concurrency, as there are behavioral differences between
+ // parallelism levels (e.g. symbol ordering will be different, and some uses
+ // of inline asm currently have issues with parallelism >1).
+ unsigned int MaxThreads = options::Parallelism ? options::Parallelism : 1;
+
+ std::vector<SmallString<128>> Filenames(MaxThreads);
+ std::vector<SmallString<128>> BCFilenames(MaxThreads);
bool TempOutFile = Filename.empty();
{
- // Open a file descriptor for each backend thread. This is done in a block
+ // Open a file descriptor for each backend task. 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());
- }
+ std::vector<llvm::raw_pwrite_stream *> OSPtrs(MaxThreads);
+ for (unsigned I = 0; I != MaxThreads; ++I) {
+ int FD = openOutputFile(Filename, TempOutFile, Filenames[I],
+ // Only append ID if there are multiple tasks.
+ MaxThreads > 1 ? I : -1);
OSs.emplace_back(FD, true);
OSPtrs[I] = &OSs.back();
}
- // Run backend threads.
- splitCodeGen(std::move(M), OSPtrs, options::mcpu, Features.getString(),
- Options, RelocationModel, CodeModel::Default, CGOptLevel);
+ std::list<llvm::raw_fd_ostream> BCOSs;
+ std::vector<llvm::raw_pwrite_stream *> BCOSPtrs;
+ if (!BCFilename.empty() && MaxThreads > 1) {
+ for (unsigned I = 0; I != MaxThreads; ++I) {
+ int FD = openOutputFile(BCFilename, false, BCFilenames[I], I);
+ BCOSs.emplace_back(FD, true);
+ BCOSPtrs.push_back(&BCOSs.back());
+ }
+ }
+
+ // Run backend tasks.
+ splitCodeGen(std::move(M), OSPtrs, BCOSPtrs,
+ [&]() { return createTargetMachine(); });
}
- 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());
+ for (auto &Filename : Filenames)
+ recordFile(Filename.c_str(), TempOutFile);
+}
+
+void CodeGen::runAll() {
+ runLTOPasses();
+
+ SmallString<128> OptFilename;
+ if (options::TheOutputType == options::OT_SAVE_TEMPS) {
+ OptFilename = output_name;
+ // If the CodeGen client provided a filename, use it. Always expect
+ // a provided filename if we are in a task (i.e. ThinLTO backend).
+ assert(!SaveTempsFilename.empty() || TaskID == -1);
+ if (!SaveTempsFilename.empty())
+ OptFilename = SaveTempsFilename;
+ OptFilename += ".opt.bc";
+ saveBCFile(OptFilename, *M);
}
+
+ // If we are already in a thread (i.e. ThinLTO), just perform
+ // codegen passes directly.
+ if (TaskID >= 0)
+ runCodegenPasses();
+ // Otherwise attempt split code gen.
+ else
+ runSplitCodeGen(OptFilename);
}
-/// gold informs us that all symbols have been read. At this point, we use
-/// get_symbols to see if any of our definitions have been overridden by a
-/// native object file. Then, perform optimization and codegen.
-static ld_plugin_status allSymbolsReadHook(raw_fd_ostream *ApiFile) {
- if (Modules.empty())
- return LDPS_OK;
+/// Links the module in \p View from file \p F into the combined module
+/// saved in the IRMover \p L.
+static void linkInModule(LLVMContext &Context, IRMover &L, claimed_file &F,
+ const void *View, StringRef Name,
+ raw_fd_ostream *ApiFile, StringSet<> &Internalize,
+ bool SetName = false) {
+ std::vector<GlobalValue *> Keep;
+ StringMap<unsigned> Realign;
+ std::unique_ptr<Module> M = getModuleForFile(Context, F, View, Name, ApiFile,
+ Internalize, Keep, Realign);
+ if (!M.get())
+ return;
+ if (!options::triple.empty())
+ M->setTargetTriple(options::triple.c_str());
+ else if (M->getTargetTriple().empty()) {
+ M->setTargetTriple(DefaultTriple);
+ }
+
+ // For ThinLTO we want to propagate the source file name to ensure
+ // we can create the correct global identifiers matching those in the
+ // original module.
+ if (SetName)
+ L.getModule().setSourceFileName(M->getSourceFileName());
+
+ if (Error E = L.move(std::move(M), Keep,
+ [](GlobalValue &, IRMover::ValueAdder) {})) {
+ handleAllErrors(std::move(E), [&](const llvm::ErrorInfoBase &EIB) {
+ message(LDPL_FATAL, "Failed to link module %s: %s", Name.str().c_str(),
+ EIB.message().c_str());
+ });
+ }
+
+ for (const auto &I : Realign) {
+ GlobalValue *Dst = L.getModule().getNamedValue(I.first());
+ if (!Dst)
+ continue;
+ cast<GlobalVariable>(Dst)->setAlignment(I.second);
+ }
+}
+
+/// Perform the ThinLTO backend on a single module, invoking the LTO and codegen
+/// pipelines.
+static void thinLTOBackendTask(claimed_file &F, const void *View,
+ StringRef Name, raw_fd_ostream *ApiFile,
+ const ModuleSummaryIndex &CombinedIndex,
+ raw_fd_ostream *OS, unsigned TaskID,
+ StringMap<MemoryBufferRef> &ModuleMap,
+ FunctionImporter::ImportMapTy &ImportList,
+ std::map<GlobalValue::GUID, GlobalValueSummary *> &DefinedGlobals) {
+ // Need to use a separate context for each task
+ LLVMContext Context;
+ Context.setDiscardValueNames(options::TheOutputType !=
+ options::OT_SAVE_TEMPS);
+ Context.enableDebugTypeODRUniquing(); // Merge debug info types.
+ Context.setDiagnosticHandler(diagnosticHandlerForContext, nullptr, true);
+
+ std::unique_ptr<llvm::Module> NewModule(new llvm::Module(Name, Context));
+ IRMover L(*NewModule.get());
+
+ StringSet<> Dummy;
+ linkInModule(Context, L, F, View, Name, ApiFile, Dummy, true);
+ if (renameModuleForThinLTO(*NewModule, CombinedIndex))
+ message(LDPL_FATAL, "Failed to rename module for ThinLTO");
- // 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;
+ CodeGen codeGen(std::move(NewModule), OS, TaskID, &CombinedIndex, Name,
+ &ModuleMap, &ImportList, &DefinedGlobals);
+ codeGen.runAll();
+}
+
+/// Launch each module's backend pipeline in a separate task in a thread pool.
+static void
+thinLTOBackends(raw_fd_ostream *ApiFile,
+ const ModuleSummaryIndex &CombinedIndex,
+ StringMap<MemoryBufferRef> &ModuleMap,
+ StringMap<FunctionImporter::ImportMapTy> &ImportLists,
+ StringMap<std::map<GlobalValue::GUID, GlobalValueSummary *>>
+ &ModuleToDefinedGVSummaries) {
+ unsigned TaskCount = 0;
+ std::vector<ThinLTOTaskInfo> Tasks;
+ Tasks.reserve(Modules.size());
+ unsigned int MaxThreads = options::Parallelism
+ ? options::Parallelism
+ : thread::hardware_concurrency();
+
+ // Create ThreadPool in nested scope so that threads will be joined
+ // on destruction.
+ {
+ ThreadPool ThinLTOThreadPool(MaxThreads);
for (claimed_file &F : Modules) {
- PluginInputFile InputFile(F.handle);
+ // Do all the gold callbacks in the main thread, since gold is not thread
+ // safe by default.
+ const void *View = getSymbolsAndView(F);
+ if (!View)
+ continue;
+
+ SmallString<128> Filename;
+ if (!options::obj_path.empty())
+ // Note that openOutputFile will append a unique ID for each task
+ Filename = options::obj_path;
+ else if (options::TheOutputType == options::OT_SAVE_TEMPS) {
+ // Use the input file name so that we get a unique and identifiable
+ // output file for each ThinLTO backend task.
+ Filename = F.name;
+ Filename += ".thinlto.o";
+ }
+ bool TempOutFile = Filename.empty();
+
+ SmallString<128> NewFilename;
+ int FD = openOutputFile(Filename, TempOutFile, NewFilename,
+ // Only append the TaskID if we will use the
+ // non-unique obj_path.
+ !options::obj_path.empty() ? TaskCount : -1);
+ TaskCount++;
+ std::unique_ptr<raw_fd_ostream> OS =
+ llvm::make_unique<raw_fd_ostream>(FD, true);
+
+ // Enqueue the task
+ ThinLTOThreadPool.async(thinLTOBackendTask, std::ref(F), View, F.name,
+ ApiFile, std::ref(CombinedIndex), OS.get(),
+ TaskCount, std::ref(ModuleMap),
+ std::ref(ImportLists[F.name]),
+ std::ref(ModuleToDefinedGVSummaries[F.name]));
+
+ // Record the information needed by the task or during its cleanup
+ // to a ThinLTOTaskInfo instance. For information needed by the task
+ // the unique_ptr ownership is transferred to the ThinLTOTaskInfo.
+ Tasks.emplace_back(std::move(OS), NewFilename.c_str(), TempOutFile);
+ }
+ }
+
+ for (auto &Task : Tasks)
+ Task.cleanup();
+}
- std::unique_ptr<FunctionInfoIndex> Index =
- getFunctionIndexForFile(F, InputFile.file());
+/// Parse the thinlto_prefix_replace option into the \p OldPrefix and
+/// \p NewPrefix strings, if it was specified.
+static void getThinLTOOldAndNewPrefix(std::string &OldPrefix,
+ std::string &NewPrefix) {
+ StringRef PrefixReplace = options::thinlto_prefix_replace;
+ assert(PrefixReplace.empty() || PrefixReplace.find(";") != StringRef::npos);
+ std::pair<StringRef, StringRef> Split = PrefixReplace.split(";");
+ OldPrefix = Split.first.str();
+ NewPrefix = Split.second.str();
+}
+
+/// Given the original \p Path to an output file, replace any path
+/// prefix matching \p OldPrefix with \p NewPrefix. Also, create the
+/// resulting directory if it does not yet exist.
+static std::string getThinLTOOutputFile(const std::string &Path,
+ const std::string &OldPrefix,
+ const std::string &NewPrefix) {
+ if (OldPrefix.empty() && NewPrefix.empty())
+ return Path;
+ SmallString<128> NewPath(Path);
+ llvm::sys::path::replace_path_prefix(NewPath, OldPrefix, NewPrefix);
+ StringRef ParentPath = llvm::sys::path::parent_path(NewPath.str());
+ if (!ParentPath.empty()) {
+ // Make sure the new directory exists, creating it if necessary.
+ if (std::error_code EC = llvm::sys::fs::create_directories(ParentPath))
+ llvm::errs() << "warning: could not create directory '" << ParentPath
+ << "': " << EC.message() << '\n';
+ }
+ return NewPath.str();
+}
+
+/// Perform ThinLTO link, which creates the combined index file.
+/// Also, either launch backend threads or (under thinlto-index-only)
+/// emit individual index files for distributed backends and exit.
+static ld_plugin_status thinLTOLink(raw_fd_ostream *ApiFile) {
+ // Map from a module name to the corresponding buffer holding a view of the
+ // bitcode provided via the get_view gold callback.
+ StringMap<MemoryBufferRef> ModuleMap;
+ // Map to own RAII objects that manage the file opening and releasing
+ // interfaces with gold.
+ DenseMap<void *, std::unique_ptr<PluginInputFile>> HandleToInputFile;
+
+ // Keep track of symbols that must not be internalized because they
+ // are referenced outside of a single IR module.
+ DenseSet<GlobalValue::GUID> Preserve;
+
+ // Keep track of the prevailing copy for each GUID, for use in resolving
+ // weak linkages.
+ DenseMap<GlobalValue::GUID, const GlobalValueSummary *> PrevailingCopy;
+
+ ModuleSummaryIndex CombinedIndex;
+ uint64_t NextModuleId = 0;
+ for (claimed_file &F : Modules) {
+ if (!HandleToInputFile.count(F.leader_handle))
+ HandleToInputFile.insert(std::make_pair(
+ F.leader_handle, llvm::make_unique<PluginInputFile>(F.handle)));
+ // Pass this into getModuleSummaryIndexForFile
+ const void *View = getSymbolsAndView(F);
+ if (!View)
+ continue;
- // Skip files without a function summary.
- if (Index)
- CombinedIndex.mergeFrom(std::move(Index), ++NextModuleId);
+ MemoryBufferRef ModuleBuffer(StringRef((const char *)View, F.filesize),
+ F.name);
+ assert(ModuleMap.find(ModuleBuffer.getBufferIdentifier()) ==
+ ModuleMap.end() &&
+ "Expect unique Buffer Identifier");
+ ModuleMap[ModuleBuffer.getBufferIdentifier()] = ModuleBuffer;
+
+ std::unique_ptr<ModuleSummaryIndex> Index = getModuleSummaryIndexForFile(F);
+
+ // Use gold's symbol resolution information to identify symbols referenced
+ // by more than a single IR module (i.e. referenced by multiple IR modules
+ // or by a non-IR module). Cross references introduced by importing are
+ // checked separately via the export lists. Also track the prevailing copy
+ // for later symbol resolution.
+ for (auto &Sym : F.syms) {
+ ld_plugin_symbol_resolution Resolution =
+ (ld_plugin_symbol_resolution)Sym.resolution;
+ GlobalValue::GUID SymGUID = GlobalValue::getGUID(Sym.name);
+ if (Resolution != LDPR_PREVAILING_DEF_IRONLY)
+ Preserve.insert(SymGUID);
+
+ if (Index && (Resolution == LDPR_PREVAILING_DEF ||
+ Resolution == LDPR_PREVAILING_DEF_IRONLY ||
+ Resolution == LDPR_PREVAILING_DEF_IRONLY_EXP))
+ PrevailingCopy[SymGUID] = Index->getGlobalValueSummary(SymGUID);
}
+ // Skip files without a module summary.
+ if (Index)
+ CombinedIndex.mergeFrom(std::move(Index), ++NextModuleId);
+ }
+
+ // Collect for each module the list of function it defines (GUID ->
+ // Summary).
+ StringMap<std::map<GlobalValue::GUID, GlobalValueSummary *>>
+ ModuleToDefinedGVSummaries(NextModuleId);
+ CombinedIndex.collectDefinedGVSummariesPerModule(ModuleToDefinedGVSummaries);
+
+ StringMap<FunctionImporter::ImportMapTy> ImportLists(NextModuleId);
+ StringMap<FunctionImporter::ExportSetTy> ExportLists(NextModuleId);
+ ComputeCrossModuleImport(CombinedIndex, ModuleToDefinedGVSummaries,
+ ImportLists, ExportLists);
+
+ auto isPrevailing = [&](GlobalValue::GUID GUID, const GlobalValueSummary *S) {
+ const auto &Prevailing = PrevailingCopy.find(GUID);
+ assert(Prevailing != PrevailingCopy.end());
+ return Prevailing->second == S;
+ };
+
+ // Callback for internalization, to prevent internalization of symbols
+ // that were not candidates initially, and those that are being imported
+ // (which introduces new cross references).
+ auto isExported = [&](StringRef ModuleIdentifier, GlobalValue::GUID GUID) {
+ const auto &ExportList = ExportLists.find(ModuleIdentifier);
+ return (ExportList != ExportLists.end() &&
+ ExportList->second.count(GUID)) ||
+ Preserve.count(GUID);
+ };
+
+ thinLTOResolveWeakForLinkerInIndex(
+ CombinedIndex, isPrevailing,
+ [](StringRef ModuleIdentifier, GlobalValue::GUID GUID,
+ GlobalValue::LinkageTypes NewLinkage) {});
+
+ // Use global summary-based analysis to identify symbols that can be
+ // internalized (because they aren't exported or preserved as per callback).
+ // Changes are made in the index, consumed in the ThinLTO backends.
+ thinLTOInternalizeAndPromoteInIndex(CombinedIndex, isExported);
+
+ if (options::thinlto_emit_imports_files && !options::thinlto_index_only)
+ message(LDPL_WARNING,
+ "thinlto-emit-imports-files ignored unless thinlto-index-only");
+
+ if (options::thinlto_index_only) {
+ // If the thinlto-prefix-replace option was specified, parse it and
+ // extract the old and new prefixes.
+ std::string OldPrefix, NewPrefix;
+ getThinLTOOldAndNewPrefix(OldPrefix, NewPrefix);
+
+ // For each input bitcode file, generate an individual index that
+ // contains summaries only for its own global values, and for any that
+ // should be imported.
+ for (claimed_file &F : Modules) {
+ std::error_code EC;
+
+ std::string NewModulePath =
+ getThinLTOOutputFile(F.name, OldPrefix, NewPrefix);
+ raw_fd_ostream OS((Twine(NewModulePath) + ".thinlto.bc").str(), EC,
+ sys::fs::OpenFlags::F_None);
+ if (EC)
+ message(LDPL_FATAL, "Unable to open %s.thinlto.bc for writing: %s",
+ NewModulePath.c_str(), EC.message().c_str());
+ // Build a map of module to the GUIDs and summary objects that should
+ // be written to its index.
+ std::map<std::string, GVSummaryMapTy> ModuleToSummariesForIndex;
+ gatherImportedSummariesForModule(F.name, ModuleToDefinedGVSummaries,
+ ImportLists, ModuleToSummariesForIndex);
+ WriteIndexToFile(CombinedIndex, OS, &ModuleToSummariesForIndex);
+
+ if (options::thinlto_emit_imports_files) {
+ if ((EC = EmitImportsFiles(F.name,
+ (Twine(NewModulePath) + ".imports").str(),
+ ImportLists)))
+ message(LDPL_FATAL, "Unable to open %s.imports",
+ NewModulePath.c_str(), EC.message().c_str());
+ }
+ }
+
+ cleanup_hook();
+ exit(0);
+ }
+
+ // Create OS in nested scope so that it will be closed on destruction.
+ {
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);
+ WriteIndexToFile(CombinedIndex, OS);
}
+ thinLTOBackends(ApiFile, CombinedIndex, ModuleMap, ImportLists,
+ ModuleToDefinedGVSummaries);
+ return LDPS_OK;
+}
+
+/// gold informs us that all symbols have been read. At this point, we use
+/// get_symbols to see if any of our definitions have been overridden by a
+/// native object file. Then, perform optimization and codegen.
+static ld_plugin_status allSymbolsReadHook(raw_fd_ostream *ApiFile) {
+ if (Modules.empty())
+ return LDPS_OK;
+
+ if (unsigned NumOpts = options::extra.size())
+ cl::ParseCommandLineOptions(NumOpts, &options::extra[0]);
+
+ if (options::thinlto)
+ return thinLTOLink(ApiFile);
+
LLVMContext Context;
+ Context.setDiscardValueNames(options::TheOutputType !=
+ options::OT_SAVE_TEMPS);
+ Context.enableDebugTypeODRUniquing(); // Merge debug info types.
Context.setDiagnosticHandler(diagnosticHandlerForContext, nullptr, true);
std::unique_ptr<Module> Combined(new Module("ld-temp.o", Context));
IRMover L(*Combined);
- std::string DefaultTriple = sys::getDefaultTargetTriple();
-
StringSet<> Internalize;
- StringSet<> Maybe;
for (claimed_file &F : Modules) {
+ // RAII object to manage the file opening and releasing interfaces with
+ // gold.
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())
- M->setTargetTriple(DefaultTriple);
-
- if (L.move(*M, Keep, [](GlobalValue &, IRMover::ValueAdder) {}))
- message(LDPL_FATAL, "Failed to link module");
+ const void *View = getSymbolsAndView(F);
+ if (!View)
+ continue;
+ linkInModule(Context, L, F, View, F.name, ApiFile, Internalize);
}
for (const auto &Name : Internalize) {
@@ -917,15 +1497,6 @@ static ld_plugin_status allSymbolsReadHook(raw_fd_ostream *ApiFile) {
internalize(*GV);
}
- for (const auto &Name : Maybe) {
- GlobalValue *GV = Combined->getNamedValue(Name.first());
- if (!GV)
- continue;
- GV->setLinkage(GlobalValue::LinkOnceODRLinkage);
- if (canBeOmittedFromSymbolTable(GV))
- internalize(*GV);
- }
-
if (options::TheOutputType == options::OT_DISABLE)
return LDPS_OK;
@@ -940,7 +1511,8 @@ static ld_plugin_status allSymbolsReadHook(raw_fd_ostream *ApiFile) {
return LDPS_OK;
}
- codegen(std::move(Combined));
+ CodeGen codeGen(std::move(Combined));
+ codeGen.runAll();
if (!options::extra_library_path.empty() &&
set_extra_library_path(options::extra_library_path.c_str()) != LDPS_OK)
@@ -966,10 +1538,14 @@ static ld_plugin_status all_symbols_read_hook(void) {
if (options::TheOutputType == options::OT_BC_ONLY ||
options::TheOutputType == options::OT_DISABLE) {
- if (options::TheOutputType == options::OT_DISABLE)
+ if (options::TheOutputType == options::OT_DISABLE) {
// Remove the output file here since ld.bfd creates the output file
// early.
- sys::fs::remove(output_name);
+ std::error_code EC = sys::fs::remove(output_name);
+ if (EC)
+ message(LDPL_ERROR, "Failed to delete '%s': %s", output_name.c_str(),
+ EC.message().c_str());
+ }
exit(0);
}
diff --git a/tools/llc/Makefile b/tools/llc/Makefile
deleted file mode 100644
index cd34c80d840a8..0000000000000
--- a/tools/llc/Makefile
+++ /dev/null
@@ -1,18 +0,0 @@
-#===- tools/llc/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 := llc
-LINK_COMPONENTS := all-targets bitreader asmparser irreader mirparser transformutils
-
-# Support plugins.
-NO_DEAD_STRIP := 1
-
-include $(LEVEL)/Makefile.common
-
diff --git a/tools/llc/llc.cpp b/tools/llc/llc.cpp
index bffa39fd9e5c5..9b6086ad4ea83 100644
--- a/tools/llc/llc.cpp
+++ b/tools/llc/llc.cpp
@@ -21,7 +21,12 @@
#include "llvm/CodeGen/LinkAllAsmWriterComponents.h"
#include "llvm/CodeGen/LinkAllCodegenComponents.h"
#include "llvm/CodeGen/MIRParser/MIRParser.h"
+#include "llvm/CodeGen/MachineFunctionPass.h"
+#include "llvm/CodeGen/MachineModuleInfo.h"
+#include "llvm/CodeGen/TargetPassConfig.h"
#include "llvm/IR/DataLayout.h"
+#include "llvm/IR/DiagnosticInfo.h"
+#include "llvm/IR/DiagnosticPrinter.h"
#include "llvm/IR/IRPrintingPasses.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/LegacyPassManager.h"
@@ -68,6 +73,11 @@ static cl::opt<bool>
NoIntegratedAssembler("no-integrated-as", cl::Hidden,
cl::desc("Disable integrated assembler"));
+static cl::opt<bool>
+ PreserveComments("preserve-as-comments", cl::Hidden,
+ cl::desc("Preserve Comments in outputted assembly"),
+ cl::init(true));
+
// Determine optimization level.
static cl::opt<char>
OptLevel("O",
@@ -103,6 +113,33 @@ static cl::opt<bool>
"manager and verify the result is the same."),
cl::init(false));
+static cl::opt<bool> DiscardValueNames(
+ "discard-value-names",
+ cl::desc("Discard names from Value (other than GlobalValue)."),
+ cl::init(false), cl::Hidden);
+
+namespace {
+static ManagedStatic<std::vector<std::string>> RunPassNames;
+
+struct RunPassOption {
+ void operator=(const std::string &Val) const {
+ if (Val.empty())
+ return;
+ SmallVector<StringRef, 8> PassNames;
+ StringRef(Val).split(PassNames, ',', -1, false);
+ for (auto PassName : PassNames)
+ RunPassNames->push_back(PassName);
+ }
+};
+}
+
+static RunPassOption RunPassOpt;
+
+static cl::opt<RunPassOption, true, cl::parser<std::string>> RunPass(
+ "run-pass",
+ cl::desc("Run compiler only for specified passes (comma separated list)"),
+ cl::value_desc("pass-name"), cl::ZeroOrMore, cl::location(RunPassOpt));
+
static int compileModule(char **, LLVMContext &);
static std::unique_ptr<tool_output_file>
@@ -173,16 +210,27 @@ GetOutputStream(const char *TargetName, Triple::OSType OS,
return FDOut;
}
+static void DiagnosticHandler(const DiagnosticInfo &DI, void *Context) {
+ bool *HasError = static_cast<bool *>(Context);
+ if (DI.getSeverity() == DS_Error)
+ *HasError = true;
+
+ DiagnosticPrinterRawOStream DP(errs());
+ errs() << LLVMContext::getDiagnosticMessagePrefix(DI.getSeverity()) << ": ";
+ DI.print(DP);
+ errs() << "\n";
+}
+
// main - Entry point for the llc compiler.
//
int main(int argc, char **argv) {
- sys::PrintStackTraceOnErrorSignal();
+ sys::PrintStackTraceOnErrorSignal(argv[0]);
PrettyStackTraceProgram X(argc, argv);
// Enable debug stream buffering.
EnableDebugBuffering = true;
- LLVMContext &Context = getGlobalContext();
+ LLVMContext Context;
llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
// Initialize targets first, so that --version shows registered targets.
@@ -198,13 +246,19 @@ int main(int argc, char **argv) {
initializeCodeGen(*Registry);
initializeLoopStrengthReducePass(*Registry);
initializeLowerIntrinsicsPass(*Registry);
- initializeUnreachableBlockElimPass(*Registry);
+ initializeUnreachableBlockElimLegacyPassPass(*Registry);
// Register the target printer for --version.
cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion);
cl::ParseCommandLineOptions(argc, argv, "llvm system compiler\n");
+ Context.setDiscardValueNames(DiscardValueNames);
+
+ // Set a diagnostic handler that doesn't exit on the first error
+ bool HasError = false;
+ Context.setDiagnosticHandler(DiagnosticHandler, &HasError);
+
// Compile the module TimeCompilations times to give better compile time
// metrics.
for (unsigned I = TimeCompilations; I; --I)
@@ -213,6 +267,34 @@ int main(int argc, char **argv) {
return 0;
}
+static bool addPass(PassManagerBase &PM, const char *argv0,
+ StringRef PassName, TargetPassConfig &TPC) {
+ if (PassName == "none")
+ return false;
+
+ const PassRegistry *PR = PassRegistry::getPassRegistry();
+ const PassInfo *PI = PR->getPassInfo(PassName);
+ if (!PI) {
+ errs() << argv0 << ": run-pass " << PassName << " is not registered.\n";
+ return true;
+ }
+
+ Pass *P;
+ if (PI->getTargetMachineCtor())
+ P = PI->getTargetMachineCtor()(&TPC.getTM<TargetMachine>());
+ else if (PI->getNormalCtor())
+ P = PI->getNormalCtor()();
+ else {
+ errs() << argv0 << ": cannot create pass: " << PI->getPassName() << "\n";
+ return true;
+ }
+ std::string Banner = std::string("After ") + std::string(P->getPassName());
+ PM.add(P);
+ TPC.printAndVerify(Banner);
+
+ return false;
+}
+
static int compileModule(char **argv, LLVMContext &Context) {
// Load the module to be compiled...
SMDiagnostic Err;
@@ -227,10 +309,8 @@ static int compileModule(char **argv, LLVMContext &Context) {
if (!SkipModule) {
if (StringRef(InputFilename).endswith_lower(".mir")) {
MIR = createMIRParserFromFile(InputFilename, Err, Context);
- if (MIR) {
+ if (MIR)
M = MIR->parseLLVMModule();
- assert(M && "parseLLVMModule should exit on failure");
- }
} else
M = parseIRFile(InputFilename, Err, Context);
if (!M) {
@@ -285,10 +365,11 @@ static int compileModule(char **argv, LLVMContext &Context) {
Options.MCOptions.ShowMCEncoding = ShowMCEncoding;
Options.MCOptions.MCUseDwarfDirectory = EnableDwarfDirectory;
Options.MCOptions.AsmVerbose = AsmVerbose;
+ Options.MCOptions.PreserveAsmComments = PreserveComments;
std::unique_ptr<TargetMachine> Target(
TheTarget->createTargetMachine(TheTriple.getTriple(), CPUStr, FeaturesStr,
- Options, RelocModel, CMModel, OLvl));
+ Options, getRelocModel(), CMModel, OLvl));
assert(Target && "Could not allocate target machine!");
@@ -348,18 +429,28 @@ static int compileModule(char **argv, LLVMContext &Context) {
AnalysisID StartAfterID = nullptr;
AnalysisID StopAfterID = nullptr;
const PassRegistry *PR = PassRegistry::getPassRegistry();
- if (!RunPass.empty()) {
+ if (!RunPassNames->empty()) {
if (!StartAfter.empty() || !StopAfter.empty()) {
errs() << argv[0] << ": start-after and/or stop-after passes are "
"redundant when run-pass is specified.\n";
return 1;
}
- const PassInfo *PI = PR->getPassInfo(RunPass);
- if (!PI) {
- errs() << argv[0] << ": run-pass pass is not registered.\n";
+ if (!MIR) {
+ errs() << argv[0] << ": run-pass needs a .mir input.\n";
return 1;
}
- StopAfterID = StartBeforeID = PI->getTypeInfo();
+ LLVMTargetMachine &LLVMTM = static_cast<LLVMTargetMachine&>(*Target);
+ TargetPassConfig &TPC = *LLVMTM.createPassConfig(PM);
+ PM.add(&TPC);
+ LLVMTM.addMachineModuleInfo(PM);
+ LLVMTM.addMachineFunctionAnalysis(PM, MIR.get());
+ TPC.printAndVerify("");
+
+ for (const std::string &RunPassName : *RunPassNames) {
+ if (addPass(PM, argv[0], RunPassName, TPC))
+ return 1;
+ }
+ PM.add(createPrintMIRPass(*OS));
} else {
if (!StartAfter.empty()) {
const PassInfo *PI = PR->getPassInfo(StartAfter);
@@ -377,14 +468,15 @@ static int compileModule(char **argv, LLVMContext &Context) {
}
StopAfterID = PI->getTypeInfo();
}
- }
- // Ask the target to add backend passes as necessary.
- if (Target->addPassesToEmitFile(PM, *OS, FileType, NoVerify, StartBeforeID,
- StartAfterID, StopAfterID, MIR.get())) {
- errs() << argv[0] << ": target does not support generation of this"
- << " file type!\n";
- return 1;
+ // Ask the target to add backend passes as necessary.
+ if (Target->addPassesToEmitFile(PM, *OS, FileType, NoVerify,
+ StartBeforeID, StartAfterID, StopAfterID,
+ MIR.get())) {
+ errs() << argv[0] << ": target does not support generation of this"
+ << " file type!\n";
+ return 1;
+ }
}
// Before executing passes, print the final values of the LLVM options.
@@ -404,6 +496,10 @@ static int compileModule(char **argv, LLVMContext &Context) {
PM.run(*M);
+ auto HasError = *static_cast<bool *>(Context.getDiagnosticContext());
+ if (HasError)
+ return 1;
+
// Compare the two outputs and make sure they're the same
if (CompileTwice) {
if (Buffer.size() != CompileTwiceBuffer.size() ||
diff --git a/tools/lli/CMakeLists.txt b/tools/lli/CMakeLists.txt
index e317db6b713c9..2bdd066eb7a62 100644
--- a/tools/lli/CMakeLists.txt
+++ b/tools/lli/CMakeLists.txt
@@ -1,4 +1,6 @@
-add_subdirectory(ChildTarget)
+if ( LLVM_INCLUDE_UTILS )
+ add_subdirectory(ChildTarget)
+endif()
set(LLVM_LINK_COMPONENTS
CodeGen
diff --git a/tools/lli/ChildTarget/CMakeLists.txt b/tools/lli/ChildTarget/CMakeLists.txt
index 0f851d593444d..e4fe0c7197f24 100644
--- a/tools/lli/ChildTarget/CMakeLists.txt
+++ b/tools/lli/ChildTarget/CMakeLists.txt
@@ -4,8 +4,7 @@ set(LLVM_LINK_COMPONENTS
Support
)
-add_llvm_executable(lli-child-target
+add_llvm_utility(lli-child-target
ChildTarget.cpp
)
-set_target_properties(lli-child-target PROPERTIES FOLDER "Misc")
diff --git a/tools/lli/ChildTarget/ChildTarget.cpp b/tools/lli/ChildTarget/ChildTarget.cpp
index 0b75e20f83e9a..f6d2413655eac 100644
--- a/tools/lli/ChildTarget/ChildTarget.cpp
+++ b/tools/lli/ChildTarget/ChildTarget.cpp
@@ -1,4 +1,4 @@
-#include "llvm/ExecutionEngine/Orc/OrcArchitectureSupport.h"
+#include "llvm/ExecutionEngine/Orc/OrcABISupport.h"
#include "llvm/ExecutionEngine/Orc/OrcRemoteTargetServer.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/DynamicLibrary.h"
@@ -12,11 +12,13 @@ using namespace llvm::orc;
using namespace llvm::sys;
#ifdef __x86_64__
-typedef OrcX86_64 HostOrcArch;
+typedef OrcX86_64_SysV HostOrcArch;
#else
-typedef OrcGenericArchitecture HostOrcArch;
+typedef OrcGenericABI HostOrcArch;
#endif
+ExitOnError ExitOnErr;
+
int main(int argc, char *argv[]) {
if (argc != 3) {
@@ -24,6 +26,8 @@ int main(int argc, char *argv[]) {
return 1;
}
+ ExitOnErr.setBanner(std::string(argv[0]) + ":");
+
int InFD;
int OutFD;
{
@@ -41,24 +45,29 @@ int main(int argc, char *argv[]) {
return RTDyldMemoryManager::getSymbolAddressInProcess(Name);
};
+ auto RegisterEHFrames = [](uint8_t *Addr, uint32_t Size) {
+ RTDyldMemoryManager::registerEHFramesInProcess(Addr, Size);
+ };
+
+ auto DeregisterEHFrames = [](uint8_t *Addr, uint32_t Size) {
+ RTDyldMemoryManager::deregisterEHFramesInProcess(Addr, Size);
+ };
+
FDRPCChannel Channel(InFD, OutFD);
typedef remote::OrcRemoteTargetServer<FDRPCChannel, HostOrcArch> JITServer;
- JITServer Server(Channel, SymbolLookup);
+ JITServer Server(Channel, SymbolLookup, RegisterEHFrames, DeregisterEHFrames);
while (1) {
- JITServer::JITProcId Id = JITServer::InvalidId;
- if (auto EC = Server.getNextProcId(Id)) {
- errs() << "Error: " << EC.message() << "\n";
- return 1;
- }
+ uint32_t RawId;
+ ExitOnErr(Server.startReceivingFunction(Channel, RawId));
+ auto Id = static_cast<JITServer::JITFuncId>(RawId);
switch (Id) {
case JITServer::TerminateSessionId:
+ ExitOnErr(Server.handleTerminateSession());
return 0;
default:
- if (auto EC = Server.handleKnownProcedure(Id)) {
- errs() << "Error: " << EC.message() << "\n";
- return 1;
- }
+ ExitOnErr(Server.handleKnownFunction(Id));
+ break;
}
}
diff --git a/tools/lli/ChildTarget/Makefile b/tools/lli/ChildTarget/Makefile
deleted file mode 100644
index 0767ba1d724cf..0000000000000
--- a/tools/lli/ChildTarget/Makefile
+++ /dev/null
@@ -1,19 +0,0 @@
-##===- tools/lli/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 := lli-child-target
-
-include $(LEVEL)/Makefile.config
-
-LINK_COMPONENTS := support OrcJIT
-
-SOURCES := ChildTarget.cpp
-
-include $(LLVM_SRC_ROOT)/Makefile.rules
diff --git a/tools/lli/Makefile b/tools/lli/Makefile
deleted file mode 100644
index 70d8c80e5c0af..0000000000000
--- a/tools/lli/Makefile
+++ /dev/null
@@ -1,31 +0,0 @@
-##===- tools/lli/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 := lli
-
-PARALLEL_DIRS := ChildTarget
-
-include $(LEVEL)/Makefile.config
-
-LINK_COMPONENTS := mcjit orcjit instrumentation interpreter nativecodegen bitreader asmparser irreader selectiondag native
-
-# If Intel JIT Events support is confiured, link against the LLVM Intel JIT
-# Events interface library
-ifeq ($(USE_INTEL_JITEVENTS), 1)
- LINK_COMPONENTS += debuginfodwarf inteljitevents object
-endif
-
-# If oprofile support is confiured, link against the LLVM oprofile interface
-# library
-ifeq ($(USE_OPROFILE), 1)
- LINK_COMPONENTS += oprofilejit
-endif
-
-include $(LLVM_SRC_ROOT)/Makefile.rules
diff --git a/tools/lli/OrcLazyJIT.cpp b/tools/lli/OrcLazyJIT.cpp
index 7f483f742b80d..b13e7696627f0 100644
--- a/tools/lli/OrcLazyJIT.cpp
+++ b/tools/lli/OrcLazyJIT.cpp
@@ -8,7 +8,7 @@
//===----------------------------------------------------------------------===//
#include "OrcLazyJIT.h"
-#include "llvm/ExecutionEngine/Orc/OrcArchitectureSupport.h"
+#include "llvm/ExecutionEngine/Orc/OrcABISupport.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/DynamicLibrary.h"
#include <cstdio>
@@ -46,31 +46,6 @@ namespace {
cl::init(true), cl::Hidden);
}
-std::unique_ptr<OrcLazyJIT::CompileCallbackMgr>
-OrcLazyJIT::createCompileCallbackMgr(Triple T) {
- switch (T.getArch()) {
- default: return nullptr;
-
- case Triple::x86_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) {
@@ -142,8 +117,8 @@ 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 CompileCallbackMgr =
- OrcLazyJIT::createCompileCallbackMgr(Triple(TM->getTargetTriple()));
+ Triple T(TM->getTargetTriple());
+ auto CompileCallbackMgr = orc::createLocalCompileCallbackManager(T, 0);
// If we couldn't build the factory function then there must not be a callback
// manager for this target. Bail out.
@@ -153,8 +128,7 @@ int llvm::runOrcLazyJIT(std::unique_ptr<Module> M, int ArgC, char* ArgV[]) {
return 1;
}
- auto IndirectStubsMgrBuilder =
- OrcLazyJIT::createIndirectStubsMgrBuilder(Triple(TM->getTargetTriple()));
+ auto IndirectStubsMgrBuilder = orc::createLocalIndirectStubsManagerBuilder(T);
// If we couldn't build a stubs-manager-builder for this target then bail out.
if (!IndirectStubsMgrBuilder) {
@@ -181,3 +155,4 @@ int llvm::runOrcLazyJIT(std::unique_ptr<Module> M, int ArgC, char* ArgV[]) {
auto Main = fromTargetAddress<MainFnPtr>(MainSym.getAddress());
return Main(ArgC, ArgV);
}
+
diff --git a/tools/lli/OrcLazyJIT.h b/tools/lli/OrcLazyJIT.h
index 2a5b31d1bfb8e..733bdd8c04d5f 100644
--- a/tools/lli/OrcLazyJIT.h
+++ b/tools/lli/OrcLazyJIT.h
@@ -62,9 +62,6 @@ public:
DtorRunner.runViaLayer(CODLayer);
}
- 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())
@@ -82,12 +79,11 @@ public:
// 1) Search the JIT symbols.
// 2) Check for C++ runtime overrides.
// 3) Search the host process (LLI)'s symbol table.
- std::shared_ptr<RuntimeDyld::SymbolResolver> Resolver =
+ auto Resolver =
orc::createLambdaResolver(
[this](const std::string &Name) {
if (auto Sym = CODLayer.findSymbol(Name, true))
- return RuntimeDyld::SymbolInfo(Sym.getAddress(),
- Sym.getFlags());
+ return Sym.toRuntimeDyldSymbol();
if (auto Sym = CXXRuntimeOverrides.searchOverrides(Name))
return Sym;
diff --git a/tools/lli/RemoteJITUtils.h b/tools/lli/RemoteJITUtils.h
index a3f3fa0fd3b41..15068d2faf6fd 100644
--- a/tools/lli/RemoteJITUtils.h
+++ b/tools/lli/RemoteJITUtils.h
@@ -16,6 +16,7 @@
#include "llvm/ExecutionEngine/Orc/RPCChannel.h"
#include "llvm/ExecutionEngine/RTDyldMemoryManager.h"
+#include <mutex>
#if !defined(_MSC_VER) && !defined(__MINGW32__)
#include <unistd.h>
@@ -24,27 +25,47 @@
#endif
/// RPC channel that reads from and writes from file descriptors.
-class FDRPCChannel : public llvm::orc::remote::RPCChannel {
+class FDRPCChannel final : public llvm::orc::remote::RPCChannel {
public:
FDRPCChannel(int InFD, int OutFD) : InFD(InFD), OutFD(OutFD) {}
- std::error_code readBytes(char *Dst, unsigned Size) override {
+ llvm::Error readBytes(char *Dst, unsigned Size) override {
assert(Dst && "Attempt to read into null.");
- ssize_t ReadResult = ::read(InFD, Dst, Size);
- if (ReadResult != (ssize_t)Size)
- return std::error_code(errno, std::generic_category());
- return std::error_code();
+ ssize_t Completed = 0;
+ while (Completed < static_cast<ssize_t>(Size)) {
+ ssize_t Read = ::read(InFD, Dst + Completed, Size - Completed);
+ if (Read <= 0) {
+ auto ErrNo = errno;
+ if (ErrNo == EAGAIN || ErrNo == EINTR)
+ continue;
+ else
+ return llvm::errorCodeToError(
+ std::error_code(errno, std::generic_category()));
+ }
+ Completed += Read;
+ }
+ return llvm::Error::success();
}
- std::error_code appendBytes(const char *Src, unsigned Size) override {
+ llvm::Error appendBytes(const char *Src, unsigned Size) override {
assert(Src && "Attempt to append from null.");
- ssize_t WriteResult = ::write(OutFD, Src, Size);
- if (WriteResult != (ssize_t)Size)
- std::error_code(errno, std::generic_category());
- return std::error_code();
+ ssize_t Completed = 0;
+ while (Completed < static_cast<ssize_t>(Size)) {
+ ssize_t Written = ::write(OutFD, Src + Completed, Size - Completed);
+ if (Written < 0) {
+ auto ErrNo = errno;
+ if (ErrNo == EAGAIN || ErrNo == EINTR)
+ continue;
+ else
+ return llvm::errorCodeToError(
+ std::error_code(errno, std::generic_category()));
+ }
+ Completed += Written;
+ }
+ return llvm::Error::success();
}
- std::error_code send() override { return std::error_code(); }
+ llvm::Error send() override { return llvm::Error::success(); }
private:
int InFD, OutFD;
diff --git a/tools/lli/lli.cpp b/tools/lli/lli.cpp
index a76ec11fb1daf..92de5da472166 100644
--- a/tools/lli/lli.cpp
+++ b/tools/lli/lli.cpp
@@ -187,20 +187,15 @@ namespace {
cl::desc("Disable JIT lazy compilation"),
cl::init(false));
- cl::opt<Reloc::Model>
- RelocModel("relocation-model",
- cl::desc("Choose relocation model"),
- cl::init(Reloc::Default),
- cl::values(
- clEnumValN(Reloc::Default, "default",
- "Target default relocation model"),
- clEnumValN(Reloc::Static, "static",
- "Non-relocatable code"),
- clEnumValN(Reloc::PIC_, "pic",
- "Fully relocatable, position independent code"),
- clEnumValN(Reloc::DynamicNoPIC, "dynamic-no-pic",
- "Relocatable external references, non-relocatable code"),
- clEnumValEnd));
+ cl::opt<Reloc::Model> RelocModel(
+ "relocation-model", cl::desc("Choose relocation model"),
+ cl::values(
+ clEnumValN(Reloc::Static, "static", "Non-relocatable code"),
+ clEnumValN(Reloc::PIC_, "pic",
+ "Fully relocatable, position independent code"),
+ clEnumValN(Reloc::DynamicNoPIC, "dynamic-no-pic",
+ "Relocatable external references, non-relocatable code"),
+ clEnumValEnd));
cl::opt<llvm::CodeModel::Model>
CMModel("code-model",
@@ -235,6 +230,8 @@ namespace {
clEnumValN(FloatABI::Hard, "hard",
"Hard float ABI (uses FP registers)"),
clEnumValEnd));
+
+ ExitOnError ExitOnErr;
}
//===----------------------------------------------------------------------===//
@@ -257,7 +254,7 @@ public:
~LLIObjectCache() override {}
void notifyObjectCompiled(const Module *M, MemoryBufferRef Obj) override {
- const std::string ModuleID = M->getModuleIdentifier();
+ const std::string &ModuleID = M->getModuleIdentifier();
std::string CacheName;
if (!getCacheFilename(ModuleID, CacheName))
return;
@@ -272,7 +269,7 @@ public:
}
std::unique_ptr<MemoryBuffer> getObject(const Module* M) override {
- const std::string ModuleID = M->getModuleIdentifier();
+ const std::string &ModuleID = M->getModuleIdentifier();
std::string CacheName;
if (!getCacheFilename(ModuleID, CacheName))
return nullptr;
@@ -312,26 +309,12 @@ private:
}
};
-static ExecutionEngine *EE = nullptr;
-static LLIObjectCache *CacheManager = nullptr;
-
-static void do_shutdown() {
- // Cygwin-1.5 invokes DLL's dtors before atexit handler.
-#ifndef DO_NOTHING_ATEXIT
- delete EE;
- if (CacheManager)
- delete CacheManager;
- llvm_shutdown();
-#endif
-}
-
// On Mingw and Cygwin, an external symbol named '__main' is called from the
// generated 'main' function to allow static intialization. To avoid linking
// problems with remote targets (because lli's remote target support does not
// currently handle external linking) we add a secondary module which defines
// an empty '__main' function.
-static void addCygMingExtraModule(ExecutionEngine *EE,
- LLVMContext &Context,
+static void addCygMingExtraModule(ExecutionEngine &EE, LLVMContext &Context,
StringRef TargetTripleStr) {
IRBuilder<> Builder(Context);
Triple TargetTriple(TargetTripleStr);
@@ -361,7 +344,7 @@ static void addCygMingExtraModule(ExecutionEngine *EE,
Builder.CreateRet(ReturnVal);
// Add this new module to the ExecutionEngine.
- EE->addModule(std::move(M));
+ EE.addModule(std::move(M));
}
CodeGenOpt::Level getOptLevel() {
@@ -382,11 +365,13 @@ CodeGenOpt::Level getOptLevel() {
// main Driver function
//
int main(int argc, char **argv, char * const *envp) {
- sys::PrintStackTraceOnErrorSignal();
+ sys::PrintStackTraceOnErrorSignal(argv[0]);
PrettyStackTraceProgram X(argc, argv);
- LLVMContext &Context = getGlobalContext();
- atexit(do_shutdown); // Call llvm_shutdown() on exit.
+ atexit(llvm_shutdown); // Call llvm_shutdown() on exit.
+
+ if (argc > 1)
+ ExitOnErr.setBanner(std::string(argv[0]) + ": ");
// If we have a native target, initialize it to ensure it is linked in and
// usable by the JIT.
@@ -401,6 +386,8 @@ int main(int argc, char **argv, char * const *envp) {
if (DisableCoreFiles)
sys::Process::PreventCoreFiles();
+ LLVMContext Context;
+
// Load the bitcode...
SMDiagnostic Err;
std::unique_ptr<Module> Owner = parseIRFile(InputFile, Err, Context);
@@ -433,7 +420,8 @@ int main(int argc, char **argv, char * const *envp) {
builder.setMArch(MArch);
builder.setMCPU(MCPU);
builder.setMAttrs(MAttrs);
- builder.setRelocationModel(RelocModel);
+ if (RelocModel.getNumOccurrences())
+ builder.setRelocationModel(RelocModel);
builder.setCodeModel(CMModel);
builder.setErrorStr(&ErrorMsg);
builder.setEngineKind(ForceInterpreter
@@ -471,7 +459,7 @@ int main(int argc, char **argv, char * const *envp) {
builder.setTargetOptions(Options);
- EE = builder.create();
+ std::unique_ptr<ExecutionEngine> EE(builder.create());
if (!EE) {
if (!ErrorMsg.empty())
errs() << argv[0] << ": error creating EE: " << ErrorMsg << "\n";
@@ -480,9 +468,10 @@ int main(int argc, char **argv, char * const *envp) {
exit(1);
}
+ std::unique_ptr<LLIObjectCache> CacheManager;
if (EnableCacheManager) {
- CacheManager = new LLIObjectCache(ObjectCacheDir);
- EE->setObjectCache(CacheManager);
+ CacheManager.reset(new LLIObjectCache(ObjectCacheDir));
+ EE->setObjectCache(CacheManager.get());
}
// Load any additional modules specified on the command line.
@@ -501,9 +490,11 @@ int main(int argc, char **argv, char * const *envp) {
}
for (unsigned i = 0, e = ExtraObjects.size(); i != e; ++i) {
- ErrorOr<object::OwningBinary<object::ObjectFile>> Obj =
+ Expected<object::OwningBinary<object::ObjectFile>> Obj =
object::ObjectFile::createObjectFile(ExtraObjects[i]);
if (!Obj) {
+ // TODO: Actually report errors helpfully.
+ consumeError(Obj.takeError());
Err.print(argv[0], errs());
return 1;
}
@@ -520,10 +511,14 @@ int main(int argc, char **argv, char * const *envp) {
}
std::unique_ptr<MemoryBuffer> &ArBuf = ArBufOrErr.get();
- ErrorOr<std::unique_ptr<object::Archive>> ArOrErr =
+ Expected<std::unique_ptr<object::Archive>> ArOrErr =
object::Archive::create(ArBuf->getMemBufferRef());
- if (std::error_code EC = ArOrErr.getError()) {
- errs() << EC.message();
+ if (!ArOrErr) {
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(ArOrErr.takeError(), OS, "");
+ OS.flush();
+ errs() << Buf;
return 1;
}
std::unique_ptr<object::Archive> &Ar = ArOrErr.get();
@@ -536,7 +531,7 @@ int main(int argc, char **argv, char * const *envp) {
// If the target is Cygwin/MingW and we are generating remote code, we
// need an extra module to help out with linking.
if (RemoteMCJIT && Triple(Mod->getTargetTriple()).isOSCygMing()) {
- addCygMingExtraModule(EE, Context, Mod->getTargetTriple());
+ addCygMingExtraModule(*EE, Context, Mod->getTargetTriple());
}
// The following functions have no effect if their respective profiling
@@ -580,14 +575,14 @@ int main(int argc, char **argv, char * const *envp) {
// Reset errno to zero on entry to main.
errno = 0;
- int Result;
+ int Result = -1;
// Sanity check use of remote-jit: LLI currently only supports use of the
// remote JIT on Unix platforms.
if (RemoteMCJIT) {
#ifndef LLVM_ON_UNIX
errs() << "Warning: host does not support external remote targets.\n"
- << " Defaulting to local execution execution\n";
+ << " Defaulting to local execution\n";
return -1;
#else
if (ChildExecPath.empty()) {
@@ -658,18 +653,11 @@ int main(int argc, char **argv, char * const *envp) {
// Create a remote target client running over the channel.
typedef orc::remote::OrcRemoteTargetClient<orc::remote::RPCChannel> MyRemote;
- ErrorOr<MyRemote> R = MyRemote::Create(*C);
- if (!R) {
- errs() << "Could not create remote: " << R.getError().message() << "\n";
- exit(1);
- }
+ MyRemote R = ExitOnErr(MyRemote::Create(*C));
// Create a remote memory manager.
std::unique_ptr<MyRemote::RCMemoryManager> RemoteMM;
- if (auto EC = R->createRemoteMemoryManager(RemoteMM)) {
- errs() << "Could not create remote memory manager: " << EC.message() << "\n";
- exit(1);
- }
+ ExitOnErr(R.createRemoteMemoryManager(RemoteMM));
// Forward MCJIT's memory manager calls to the remote memory manager.
static_cast<ForwardingMemoryManager*>(RTDyldMM)->setMemMgr(
@@ -678,15 +666,12 @@ int main(int argc, char **argv, char * const *envp) {
// Forward MCJIT's symbol resolution calls to the remote.
static_cast<ForwardingMemoryManager*>(RTDyldMM)->setResolver(
orc::createLambdaResolver(
+ [](const std::string &Name) { return nullptr; },
[&](const std::string &Name) {
- orc::TargetAddress Addr = 0;
- if (auto EC = R->getSymbolAddress(Addr, Name)) {
- errs() << "Failure during symbol lookup: " << EC.message() << "\n";
- exit(1);
- }
- return RuntimeDyld::SymbolInfo(Addr, JITSymbolFlags::Exported);
- },
- [](const std::string &Name) { return nullptr; }
+ if (auto Addr = ExitOnErr(R.getSymbolAddress(Name)))
+ return RuntimeDyld::SymbolInfo(Addr, JITSymbolFlags::Exported);
+ return RuntimeDyld::SymbolInfo(nullptr);
+ }
));
// Grab the target address of the JIT'd main function on the remote and call
@@ -696,8 +681,7 @@ int main(int argc, char **argv, char * const *envp) {
EE->finalizeObject();
DEBUG(dbgs() << "Executing '" << EntryFn->getName() << "' at 0x"
<< format("%llx", Entry) << "\n");
- if (auto EC = R->callIntVoid(Result, Entry))
- errs() << "ERROR: " << EC.message() << "\n";
+ Result = ExitOnErr(R.callIntVoid(Entry));
// Like static constructors, the remote target MCJIT support doesn't handle
// this yet. It could. FIXME.
@@ -705,11 +689,10 @@ int main(int argc, char **argv, char * const *envp) {
// Delete the EE - we need to tear it down *before* we terminate the session
// with the remote, otherwise it'll crash when it tries to release resources
// on a remote that has already been disconnected.
- delete EE;
- EE = nullptr;
+ EE.reset();
// Signal the remote target that we're done JITing.
- R->terminateSession();
+ ExitOnErr(R.terminateSession());
}
return Result;
diff --git a/tools/llvm-ar/Makefile b/tools/llvm-ar/Makefile
deleted file mode 100644
index 824bb9473000d..0000000000000
--- a/tools/llvm-ar/Makefile
+++ /dev/null
@@ -1,21 +0,0 @@
-##===- tools/llvm-ar/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-ar
-TOOLALIAS = llvm-ranlib
-LINK_COMPONENTS := all-targets bitreader libdriver support object
-
-# This tool has no plugins, optimize startup time.
-TOOL_NO_EXPORTS := 1
-
-include $(LEVEL)/Makefile.common
-
-all-local::
- $(Verb) $(AliasTool) $(notdir $(ToolBuildPath)) $(ToolDir)/llvm-lib$(EXEEXT)
diff --git a/tools/llvm-ar/llvm-ar.cpp b/tools/llvm-ar/llvm-ar.cpp
index ef5fab68b9479..865152b6af831 100644
--- a/tools/llvm-ar/llvm-ar.cpp
+++ b/tools/llvm-ar/llvm-ar.cpp
@@ -19,6 +19,7 @@
#include "llvm/LibDriver/LibDriver.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/ArchiveWriter.h"
+#include "llvm/Object/MachO.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Errc.h"
@@ -64,12 +65,25 @@ static void failIfError(std::error_code EC, Twine Context = "") {
fail(Context + ": " + EC.message());
}
+static void failIfError(Error E, Twine Context = "") {
+ if (!E)
+ return;
+
+ handleAllErrors(std::move(E), [&](const llvm::ErrorInfoBase &EIB) {
+ std::string ContextStr = Context.str();
+ if (ContextStr == "")
+ fail(EIB.message());
+ fail(Context + ": " + EIB.message());
+ });
+}
+
// llvm-ar/llvm-ranlib remaining positional arguments.
static cl::list<std::string>
RestOfArgs(cl::Positional, cl::ZeroOrMore,
cl::desc("[relpos] [count] <archive-file> [members]..."));
static cl::opt<bool> MRI("M", cl::desc(""));
+static cl::opt<std::string> Plugin("plugin", cl::desc("plugin (ignored for compatibility"));
namespace {
enum Format { Default, GNU, BSD };
@@ -77,7 +91,7 @@ enum Format { Default, GNU, BSD };
static cl::opt<Format>
FormatOpt("format", cl::desc("Archive format to create"),
- cl::values(clEnumValN(Default, "defalut", "default"),
+ cl::values(clEnumValN(Default, "default", "default"),
clEnumValN(GNU, "gnu", "gnu"),
clEnumValN(BSD, "bsd", "bsd"), clEnumValEnd));
@@ -343,12 +357,9 @@ static void doDisplayTable(StringRef Name, const object::Archive::Child &C) {
static void doExtract(StringRef Name, const object::Archive::Child &C) {
// Retain the original mode.
sys::fs::perms Mode = C.getAccessMode();
- SmallString<128> Storage = Name;
int FD;
- failIfError(
- sys::fs::openFileForWrite(Storage.c_str(), FD, sys::fs::F_None, Mode),
- Storage.c_str());
+ failIfError(sys::fs::openFileForWrite(Name, FD, sys::fs::F_None, Mode), Name);
{
raw_fd_ostream file(FD, false);
@@ -394,35 +405,37 @@ static void performReadOperation(ArchiveOperation Operation,
fail("extracting from a thin archive is not supported");
bool Filter = !Members.empty();
- 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();
-
- if (Filter) {
- auto I = std::find(Members.begin(), Members.end(), Name);
- if (I == Members.end())
- continue;
- Members.erase(I);
- }
+ {
+ Error Err;
+ for (auto &C : OldArchive->children(Err)) {
+ ErrorOr<StringRef> NameOrErr = C.getName();
+ failIfError(NameOrErr.getError());
+ StringRef Name = NameOrErr.get();
- switch (Operation) {
- default:
- llvm_unreachable("Not a read operation");
- case Print:
- doPrint(Name, C);
- break;
- case DisplayTable:
- doDisplayTable(Name, C);
- break;
- case Extract:
- doExtract(Name, C);
- break;
+ if (Filter) {
+ auto I = std::find(Members.begin(), Members.end(), Name);
+ if (I == Members.end())
+ continue;
+ Members.erase(I);
+ }
+
+ switch (Operation) {
+ default:
+ llvm_unreachable("Not a read operation");
+ case Print:
+ doPrint(Name, C);
+ break;
+ case DisplayTable:
+ doDisplayTable(Name, C);
+ break;
+ case Extract:
+ doExtract(Name, C);
+ break;
+ }
}
+ failIfError(std::move(Err));
}
+
if (Members.empty())
return;
for (StringRef Name : Members)
@@ -430,25 +443,28 @@ static void performReadOperation(ArchiveOperation Operation,
std::exit(1);
}
-static void addMember(std::vector<NewArchiveIterator> &Members,
+static void addMember(std::vector<NewArchiveMember> &Members,
StringRef FileName, int Pos = -1) {
- NewArchiveIterator NI(FileName);
+ Expected<NewArchiveMember> NMOrErr =
+ NewArchiveMember::getFile(FileName, Deterministic);
+ failIfError(NMOrErr.takeError(), FileName);
if (Pos == -1)
- Members.push_back(NI);
+ Members.push_back(std::move(*NMOrErr));
else
- Members[Pos] = NI;
+ Members[Pos] = std::move(*NMOrErr);
}
-static void addMember(std::vector<NewArchiveIterator> &Members,
- const object::Archive::Child &M, StringRef Name,
- int Pos = -1) {
+static void addMember(std::vector<NewArchiveMember> &Members,
+ const object::Archive::Child &M, int Pos = -1) {
if (Thin && !M.getParent()->isThin())
fail("Cannot convert a regular archive to a thin one");
- NewArchiveIterator NI(M, Name);
+ Expected<NewArchiveMember> NMOrErr =
+ NewArchiveMember::getOldMember(M, Deterministic);
+ failIfError(NMOrErr.takeError());
if (Pos == -1)
- Members.push_back(NI);
+ Members.push_back(std::move(*NMOrErr));
else
- Members[Pos] = NI;
+ Members[Pos] = std::move(*NMOrErr);
}
enum InsertAction {
@@ -509,17 +525,16 @@ static InsertAction computeInsertAction(ArchiveOperation Operation,
// We have to walk this twice and computing it is not trivial, so creating an
// explicit std::vector is actually fairly efficient.
-static std::vector<NewArchiveIterator>
+static std::vector<NewArchiveMember>
computeNewArchiveMembers(ArchiveOperation Operation,
object::Archive *OldArchive) {
- std::vector<NewArchiveIterator> Ret;
- std::vector<NewArchiveIterator> Moved;
+ std::vector<NewArchiveMember> Ret;
+ std::vector<NewArchiveMember> Moved;
int InsertPos = -1;
StringRef PosName = sys::path::filename(RelPos);
if (OldArchive) {
- for (auto &ChildOrErr : OldArchive->children()) {
- failIfError(ChildOrErr.getError());
- auto &Child = ChildOrErr.get();
+ Error Err;
+ for (auto &Child : OldArchive->children(Err)) {
int Pos = Ret.size();
ErrorOr<StringRef> NameOrErr = Child.getName();
failIfError(NameOrErr.getError());
@@ -537,7 +552,7 @@ computeNewArchiveMembers(ArchiveOperation Operation,
computeInsertAction(Operation, Child, Name, MemberI);
switch (Action) {
case IA_AddOldMember:
- addMember(Ret, Child, Name);
+ addMember(Ret, Child);
break;
case IA_AddNewMeber:
addMember(Ret, *MemberI);
@@ -545,7 +560,7 @@ computeNewArchiveMembers(ArchiveOperation Operation,
case IA_Delete:
break;
case IA_MoveOldMember:
- addMember(Moved, Child, Name);
+ addMember(Moved, Child);
break;
case IA_MoveNewMember:
addMember(Moved, *MemberI);
@@ -554,6 +569,7 @@ computeNewArchiveMembers(ArchiveOperation Operation,
if (MemberI != Members.end())
Members.erase(MemberI);
}
+ failIfError(std::move(Err));
}
if (Operation == Delete)
@@ -566,10 +582,15 @@ computeNewArchiveMembers(ArchiveOperation Operation,
InsertPos = Ret.size();
assert(unsigned(InsertPos) <= Ret.size());
- Ret.insert(Ret.begin() + InsertPos, Moved.begin(), Moved.end());
-
- Ret.insert(Ret.begin() + InsertPos, Members.size(), NewArchiveIterator(""));
int Pos = InsertPos;
+ for (auto &M : Moved) {
+ Ret.insert(Ret.begin() + Pos, std::move(M));
+ ++Pos;
+ }
+
+ for (unsigned I = 0; I != Members.size(); ++I)
+ Ret.insert(Ret.begin() + InsertPos, NewArchiveMember());
+ Pos = InsertPos;
for (auto &Member : Members) {
addMember(Ret, Member, Pos);
++Pos;
@@ -578,36 +599,61 @@ computeNewArchiveMembers(ArchiveOperation Operation,
return Ret;
}
+static object::Archive::Kind getDefaultForHost() {
+ return Triple(sys::getProcessTriple()).isOSDarwin() ? object::Archive::K_BSD
+ : object::Archive::K_GNU;
+}
+
+static object::Archive::Kind getKindFromMember(const NewArchiveMember &Member) {
+ Expected<std::unique_ptr<object::ObjectFile>> OptionalObject =
+ object::ObjectFile::createObjectFile(Member.Buf->getMemBufferRef());
+
+ if (OptionalObject)
+ return isa<object::MachOObjectFile>(**OptionalObject)
+ ? object::Archive::K_BSD
+ : object::Archive::K_GNU;
+
+ // squelch the error in case we had a non-object file
+ consumeError(OptionalObject.takeError());
+ return getDefaultForHost();
+}
+
static void
-performWriteOperation(ArchiveOperation Operation, object::Archive *OldArchive,
- std::vector<NewArchiveIterator> *NewMembersP) {
+performWriteOperation(ArchiveOperation Operation,
+ object::Archive *OldArchive,
+ std::unique_ptr<MemoryBuffer> OldArchiveBuf,
+ std::vector<NewArchiveMember> *NewMembersP) {
+ std::vector<NewArchiveMember> NewMembers;
+ if (!NewMembersP)
+ NewMembers = computeNewArchiveMembers(Operation, OldArchive);
+
object::Archive::Kind Kind;
switch (FormatOpt) {
- case Default: {
- Triple T(sys::getProcessTriple());
- if (T.isOSDarwin())
- Kind = object::Archive::K_BSD;
- else
+ case Default:
+ if (Thin)
Kind = object::Archive::K_GNU;
+ else if (OldArchive)
+ Kind = OldArchive->kind();
+ else if (NewMembersP)
+ Kind = NewMembersP->size() ? getKindFromMember(NewMembersP->front())
+ : getDefaultForHost();
+ else
+ Kind = NewMembers.size() ? getKindFromMember(NewMembers.front())
+ : getDefaultForHost();
break;
- }
case GNU:
Kind = object::Archive::K_GNU;
break;
case BSD:
+ if (Thin)
+ fail("Only the gnu format has a thin mode");
Kind = object::Archive::K_BSD;
break;
}
- if (NewMembersP) {
- 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, Thin);
+
+ std::pair<StringRef, std::error_code> Result =
+ writeArchive(ArchiveName, NewMembersP ? *NewMembersP : NewMembers, Symtab,
+ Kind, Deterministic, Thin, std::move(OldArchiveBuf));
failIfError(Result.second, Result.first);
}
@@ -621,12 +667,13 @@ static void createSymbolTable(object::Archive *OldArchive) {
if (OldArchive->hasSymbolTable())
return;
- performWriteOperation(CreateSymTab, OldArchive, nullptr);
+ performWriteOperation(CreateSymTab, OldArchive, nullptr, nullptr);
}
static void performOperation(ArchiveOperation Operation,
object::Archive *OldArchive,
- std::vector<NewArchiveIterator> *NewMembers) {
+ std::unique_ptr<MemoryBuffer> OldArchiveBuf,
+ std::vector<NewArchiveMember> *NewMembers) {
switch (Operation) {
case Print:
case DisplayTable:
@@ -638,7 +685,8 @@ static void performOperation(ArchiveOperation Operation,
case Move:
case QuickAppend:
case ReplaceOrInsert:
- performWriteOperation(Operation, OldArchive, NewMembers);
+ performWriteOperation(Operation, OldArchive, std::move(OldArchiveBuf),
+ NewMembers);
return;
case CreateSymTab:
createSymbolTable(OldArchive);
@@ -648,7 +696,7 @@ static void performOperation(ArchiveOperation Operation,
}
static int performOperation(ArchiveOperation Operation,
- std::vector<NewArchiveIterator> *NewMembers) {
+ std::vector<NewArchiveMember> *NewMembers) {
// Create or open the archive object.
ErrorOr<std::unique_ptr<MemoryBuffer>> Buf =
MemoryBuffer::getFile(ArchiveName, -1, false);
@@ -657,10 +705,12 @@ static int performOperation(ArchiveOperation Operation,
fail("error opening '" + ArchiveName + "': " + EC.message() + "!");
if (!EC) {
- object::Archive Archive(Buf.get()->getMemBufferRef(), EC);
+ Error Err;
+ object::Archive Archive(Buf.get()->getMemBufferRef(), Err);
+ EC = errorToErrorCode(std::move(Err));
failIfError(EC,
"error loading '" + ArchiveName + "': " + EC.message() + "!");
- performOperation(Operation, &Archive, NewMembers);
+ performOperation(Operation, &Archive, std::move(Buf.get()), NewMembers);
return 0;
}
@@ -675,7 +725,7 @@ static int performOperation(ArchiveOperation Operation,
}
}
- performOperation(Operation, nullptr, NewMembers);
+ performOperation(Operation, nullptr, nullptr, NewMembers);
return 0;
}
@@ -686,7 +736,7 @@ static void runMRIScript() {
failIfError(Buf.getError());
const MemoryBuffer &Ref = *Buf.get();
bool Saved = false;
- std::vector<NewArchiveIterator> NewMembers;
+ std::vector<NewArchiveMember> NewMembers;
std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers;
std::vector<std::unique_ptr<object::Archive>> Archives;
@@ -712,15 +762,15 @@ static void runMRIScript() {
ArchiveBuffers.push_back(std::move(*BufOrErr));
auto LibOrErr =
object::Archive::create(ArchiveBuffers.back()->getMemBufferRef());
- failIfError(LibOrErr.getError(), "Could not parse library");
+ failIfError(errorToErrorCode(LibOrErr.takeError()),
+ "Could not parse library");
Archives.push_back(std::move(*LibOrErr));
object::Archive &Lib = *Archives.back();
- for (auto &MemberOrErr : Lib.children()) {
- failIfError(MemberOrErr.getError());
- auto &Member = MemberOrErr.get();
- ErrorOr<StringRef> NameOrErr = Member.getName();
- failIfError(NameOrErr.getError());
- addMember(NewMembers, Member, *NameOrErr);
+ {
+ Error Err;
+ for (auto &Member : Lib.children(Err))
+ addMember(NewMembers, Member);
+ failIfError(std::move(Err));
}
break;
}
@@ -760,7 +810,7 @@ static int ar_main() {
static int ranlib_main() {
if (RestOfArgs.size() != 1)
- fail(ToolName + "takes just one archive as argument");
+ fail(ToolName + " takes just one archive as an argument");
ArchiveName = RestOfArgs[0];
return performOperation(CreateSymTab, nullptr);
}
@@ -768,7 +818,7 @@ static int ranlib_main() {
int main(int argc, char **argv) {
ToolName = argv[0];
// Print a stack trace if we signal out.
- sys::PrintStackTraceOnErrorSignal();
+ sys::PrintStackTraceOnErrorSignal(argv[0]);
PrettyStackTraceProgram X(argc, argv);
llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
diff --git a/tools/llvm-as-fuzzer/llvm-as-fuzzer.cpp b/tools/llvm-as-fuzzer/llvm-as-fuzzer.cpp
index b4024bcaa99ae..52deca6e9cd77 100644
--- a/tools/llvm-as-fuzzer/llvm-as-fuzzer.cpp
+++ b/tools/llvm-as-fuzzer/llvm-as-fuzzer.cpp
@@ -53,7 +53,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
// parsed is always null terminated.
std::unique_ptr<MemoryBuffer> MemBuf = MemoryBuffer::getMemBufferCopy(Input);
SMDiagnostic Err;
- LLVMContext &Context = getGlobalContext();
+ LLVMContext Context;
std::unique_ptr<Module> M;
if (setjmp(JmpBuf))
diff --git a/tools/llvm-as/Makefile b/tools/llvm-as/Makefile
deleted file mode 100644
index dfd71b295a9a8..0000000000000
--- a/tools/llvm-as/Makefile
+++ /dev/null
@@ -1,17 +0,0 @@
-##===- tools/llvm-as/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-as
-LINK_COMPONENTS := asmparser bitwriter
-
-# This tool has no plugins, optimize startup time.
-TOOL_NO_EXPORTS := 1
-
-include $(LEVEL)/Makefile.common
diff --git a/tools/llvm-as/llvm-as.cpp b/tools/llvm-as/llvm-as.cpp
index d4e4d8d71070a..89a6ee5edfd89 100644
--- a/tools/llvm-as/llvm-as.cpp
+++ b/tools/llvm-as/llvm-as.cpp
@@ -15,9 +15,9 @@
//
//===----------------------------------------------------------------------===//
-#include "llvm/IR/LLVMContext.h"
#include "llvm/AsmParser/Parser.h"
#include "llvm/Bitcode/ReaderWriter.h"
+#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Verifier.h"
#include "llvm/Support/CommandLine.h"
@@ -31,29 +31,28 @@
#include <memory>
using namespace llvm;
-static cl::opt<std::string>
-InputFilename(cl::Positional, cl::desc("<input .llvm file>"), cl::init("-"));
+static cl::opt<std::string> InputFilename(cl::Positional,
+ cl::desc("<input .llvm file>"),
+ cl::init("-"));
-static cl::opt<std::string>
-OutputFilename("o", cl::desc("Override output filename"),
- cl::value_desc("filename"));
+static cl::opt<std::string> OutputFilename("o",
+ cl::desc("Override output filename"),
+ cl::value_desc("filename"));
-static cl::opt<bool>
-Force("f", cl::desc("Enable binary output on terminals"));
+static cl::opt<bool> Force("f", cl::desc("Enable binary output on terminals"));
-static cl::opt<bool>
-DisableOutput("disable-output", cl::desc("Disable output"), cl::init(false));
+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> EmitModuleHash("module-hash", cl::desc("Emit module hash"),
+ cl::init(false));
-static cl::opt<bool>
-DumpAsm("d", cl::desc("Print assembly as parsed"), cl::Hidden);
+static cl::opt<bool> DumpAsm("d", cl::desc("Print assembly as parsed"),
+ cl::Hidden);
static cl::opt<bool>
-DisableVerify("disable-verify", cl::Hidden,
- cl::desc("Do not run verifier on input LLVM (dangerous!)"));
+ DisableVerify("disable-verify", cl::Hidden,
+ cl::desc("Do not run verifier on input LLVM (dangerous!)"));
static cl::opt<bool> PreserveBitcodeUseListOrder(
"preserve-bc-uselistorder",
@@ -81,8 +80,8 @@ static void WriteOutputFile(const Module *M) {
}
if (Force || !CheckBitcodeOutputToConsole(Out->os(), true))
- WriteBitcodeToFile(M, Out->os(), PreserveBitcodeUseListOrder,
- EmitFunctionSummary);
+ WriteBitcodeToFile(M, Out->os(), PreserveBitcodeUseListOrder, nullptr,
+ EmitModuleHash);
// Declare success.
Out->keep();
@@ -90,10 +89,10 @@ static void WriteOutputFile(const Module *M) {
int main(int argc, char **argv) {
// Print a stack trace if we signal out.
- sys::PrintStackTraceOnErrorSignal();
+ sys::PrintStackTraceOnErrorSignal(argv[0]);
PrettyStackTraceProgram X(argc, argv);
- LLVMContext &Context = getGlobalContext();
- llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
+ LLVMContext Context;
+ llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
cl::ParseCommandLineOptions(argc, argv, "llvm .ll -> .bc assembler\n");
// Parse the file now...
@@ -115,7 +114,8 @@ int main(int argc, char **argv) {
}
}
- if (DumpAsm) errs() << "Here's the assembly:\n" << *M.get();
+ if (DumpAsm)
+ errs() << "Here's the assembly:\n" << *M.get();
if (!DisableOutput)
WriteOutputFile(M.get());
diff --git a/tools/llvm-bcanalyzer/Makefile b/tools/llvm-bcanalyzer/Makefile
deleted file mode 100644
index 2fc61dbd62a7d..0000000000000
--- a/tools/llvm-bcanalyzer/Makefile
+++ /dev/null
@@ -1,17 +0,0 @@
-##===- tools/llvm-bcanalyzer/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-bcanalyzer
-LINK_COMPONENTS := bitreader
-
-# This tool has no plugins, optimize startup time.
-TOOL_NO_EXPORTS := 1
-
-include $(LEVEL)/Makefile.common
diff --git a/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp b/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp
index fe68689def60a..319e34d882dcc 100644
--- a/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp
+++ b/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp
@@ -27,8 +27,8 @@
//
//===----------------------------------------------------------------------===//
+#include "llvm/ADT/StringExtras.h"
#include "llvm/Bitcode/BitstreamReader.h"
-#include "llvm/ADT/Optional.h"
#include "llvm/Bitcode/LLVMBitCodes.h"
#include "llvm/Bitcode/ReaderWriter.h"
#include "llvm/IR/Verifier.h"
@@ -37,6 +37,7 @@
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/PrettyStackTrace.h"
+#include "llvm/Support/SHA1.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
@@ -103,23 +104,24 @@ static const char *GetBlockName(unsigned BlockID,
if (CurStreamType != LLVMIRBitstream) return nullptr;
switch (BlockID) {
- default: return nullptr;
- case bitc::MODULE_BLOCK_ID: return "MODULE_BLOCK";
- case bitc::PARAMATTR_BLOCK_ID: return "PARAMATTR_BLOCK";
- case bitc::PARAMATTR_GROUP_BLOCK_ID: return "PARAMATTR_GROUP_BLOCK_ID";
- 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";
+ default: return nullptr;
+ case bitc::OPERAND_BUNDLE_TAGS_BLOCK_ID: return "OPERAND_BUNDLE_TAGS_BLOCK";
+ case bitc::MODULE_BLOCK_ID: return "MODULE_BLOCK";
+ case bitc::PARAMATTR_BLOCK_ID: return "PARAMATTR_BLOCK";
+ case bitc::PARAMATTR_GROUP_BLOCK_ID: return "PARAMATTR_GROUP_BLOCK_ID";
+ 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";
+ 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::GLOBALVAL_SUMMARY_BLOCK_ID:
+ return "GLOBALVAL_SUMMARY_BLOCK";
+ case bitc::MODULE_STRTAB_BLOCK_ID: return "MODULE_STRTAB_BLOCK";
}
}
@@ -172,7 +174,9 @@ static const char *GetCodeName(unsigned CodeID, unsigned BlockID,
STRINGIFY_CODE(MODULE_CODE, PURGEVALS)
STRINGIFY_CODE(MODULE_CODE, GCNAME)
STRINGIFY_CODE(MODULE_CODE, VSTOFFSET)
- STRINGIFY_CODE(MODULE_CODE, METADATA_VALUES)
+ STRINGIFY_CODE(MODULE_CODE, METADATA_VALUES_UNUSED)
+ STRINGIFY_CODE(MODULE_CODE, SOURCE_FILENAME)
+ STRINGIFY_CODE(MODULE_CODE, HASH)
}
case bitc::IDENTIFICATION_BLOCK_ID:
switch (CodeID) {
@@ -187,6 +191,10 @@ static const char *GetCodeName(unsigned CodeID, unsigned BlockID,
// FIXME: Should these be different?
case bitc::PARAMATTR_CODE_ENTRY_OLD: return "ENTRY";
case bitc::PARAMATTR_CODE_ENTRY: return "ENTRY";
+ }
+ case bitc::PARAMATTR_GROUP_BLOCK_ID:
+ switch (CodeID) {
+ default: return nullptr;
case bitc::PARAMATTR_GRP_CODE_ENTRY: return "ENTRY";
}
case bitc::TYPE_BLOCK_ID_NEW:
@@ -272,6 +280,7 @@ static const char *GetCodeName(unsigned CodeID, unsigned BlockID,
STRINGIFY_CODE(FUNC_CODE, INST_CALL)
STRINGIFY_CODE(FUNC_CODE, DEBUG_LOC)
STRINGIFY_CODE(FUNC_CODE, INST_GEP)
+ STRINGIFY_CODE(FUNC_CODE, OPERAND_BUNDLE)
}
case bitc::VALUE_SYMTAB_BLOCK_ID:
switch (CodeID) {
@@ -279,20 +288,29 @@ static const char *GetCodeName(unsigned CodeID, unsigned BlockID,
STRINGIFY_CODE(VST_CODE, ENTRY)
STRINGIFY_CODE(VST_CODE, BBENTRY)
STRINGIFY_CODE(VST_CODE, FNENTRY)
- STRINGIFY_CODE(VST_CODE, COMBINED_FNENTRY)
+ STRINGIFY_CODE(VST_CODE, COMBINED_ENTRY)
}
case bitc::MODULE_STRTAB_BLOCK_ID:
switch (CodeID) {
default:
return nullptr;
STRINGIFY_CODE(MST_CODE, ENTRY)
+ STRINGIFY_CODE(MST_CODE, HASH)
}
- case bitc::FUNCTION_SUMMARY_BLOCK_ID:
+ case bitc::GLOBALVAL_SUMMARY_BLOCK_ID:
switch (CodeID) {
default:
return nullptr;
- STRINGIFY_CODE(FS_CODE, PERMODULE_ENTRY)
- STRINGIFY_CODE(FS_CODE, COMBINED_ENTRY)
+ STRINGIFY_CODE(FS, PERMODULE)
+ STRINGIFY_CODE(FS, PERMODULE_PROFILE)
+ STRINGIFY_CODE(FS, PERMODULE_GLOBALVAR_INIT_REFS)
+ STRINGIFY_CODE(FS, COMBINED)
+ STRINGIFY_CODE(FS, COMBINED_PROFILE)
+ STRINGIFY_CODE(FS, COMBINED_GLOBALVAR_INIT_REFS)
+ STRINGIFY_CODE(FS, ALIAS)
+ STRINGIFY_CODE(FS, COMBINED_ALIAS)
+ STRINGIFY_CODE(FS, COMBINED_ORIGINAL_NAME)
+ STRINGIFY_CODE(FS, VERSION)
}
case bitc::METADATA_ATTACHMENT_ID:
switch(CodeID) {
@@ -302,7 +320,8 @@ static const char *GetCodeName(unsigned CodeID, unsigned BlockID,
case bitc::METADATA_BLOCK_ID:
switch(CodeID) {
default:return nullptr;
- STRINGIFY_CODE(METADATA, STRING)
+ STRINGIFY_CODE(METADATA, STRING_OLD)
+ STRINGIFY_CODE(METADATA, STRINGS)
STRINGIFY_CODE(METADATA, NAME)
STRINGIFY_CODE(METADATA, KIND) // Older bitcode has it in a MODULE_BLOCK
STRINGIFY_CODE(METADATA, NODE)
@@ -346,6 +365,12 @@ static const char *GetCodeName(unsigned CodeID, unsigned BlockID,
case bitc::USELIST_CODE_DEFAULT: return "USELIST_CODE_DEFAULT";
case bitc::USELIST_CODE_BB: return "USELIST_CODE_BB";
}
+
+ case bitc::OPERAND_BUNDLE_TAGS_BLOCK_ID:
+ switch(CodeID) {
+ default: return nullptr;
+ case bitc::OPERAND_BUNDLE_TAG: return "OPERAND_BUNDLE_TAG";
+ }
}
#undef STRINGIFY_CODE
}
@@ -394,6 +419,57 @@ static bool Error(const Twine &Err) {
return true;
}
+static bool decodeMetadataStringsBlob(BitstreamReader &Reader, StringRef Indent,
+ ArrayRef<uint64_t> Record,
+ StringRef Blob) {
+ if (Blob.empty())
+ return true;
+
+ if (Record.size() != 2)
+ return true;
+
+ unsigned NumStrings = Record[0];
+ unsigned StringsOffset = Record[1];
+ outs() << " num-strings = " << NumStrings << " {\n";
+
+ StringRef Lengths = Blob.slice(0, StringsOffset);
+ SimpleBitstreamCursor R(Reader);
+ R.jumpToPointer(Lengths.begin());
+
+ // Ensure that Blob doesn't get invalidated, even if this is reading from a
+ // StreamingMemoryObject with corrupt data.
+ R.setArtificialByteLimit(R.getCurrentByteNo() + StringsOffset);
+
+ StringRef Strings = Blob.drop_front(StringsOffset);
+ do {
+ if (R.AtEndOfStream())
+ return Error("bad length");
+
+ unsigned Size = R.ReadVBR(6);
+ if (Strings.size() < Size)
+ return Error("truncated chars");
+
+ outs() << Indent << " '";
+ outs().write_escaped(Strings.slice(0, Size), /*hex=*/true);
+ outs() << "'\n";
+ Strings = Strings.drop_front(Size);
+ } while (--NumStrings);
+
+ outs() << Indent << " }";
+ return false;
+}
+
+static bool decodeBlob(unsigned Code, unsigned BlockID, BitstreamReader &Reader,
+ StringRef Indent, ArrayRef<uint64_t> Record,
+ StringRef Blob) {
+ if (BlockID != bitc::METADATA_BLOCK_ID)
+ return true;
+ if (Code != bitc::METADATA_STRINGS)
+ return true;
+
+ return decodeMetadataStringsBlob(Reader, Indent, Record, Blob);
+}
+
/// ParseBlock - Read a block, updating statistics, etc.
static bool ParseBlock(BitstreamCursor &Stream, unsigned BlockID,
unsigned IndentLevel, CurStreamTypeType CurStreamType) {
@@ -406,21 +482,24 @@ static bool ParseBlock(BitstreamCursor &Stream, unsigned BlockID,
BlockStats.NumInstances++;
// BLOCKINFO is a special part of the stream.
+ bool DumpRecords = Dump;
if (BlockID == bitc::BLOCKINFO_BLOCK_ID) {
if (Dump) outs() << Indent << "<BLOCKINFO_BLOCK/>\n";
- if (Stream.ReadBlockInfoBlock())
+ if (BitstreamCursor(Stream).ReadBlockInfoBlock())
return Error("Malformed BlockInfoBlock");
- uint64_t BlockBitEnd = Stream.GetCurrentBitNo();
- BlockStats.NumBits += BlockBitEnd-BlockBitStart;
- return false;
+ // It's not really interesting to dump the contents of the blockinfo block.
+ DumpRecords = false;
}
unsigned NumWords = 0;
if (Stream.EnterSubBlock(BlockID, &NumWords))
return Error("Malformed block record");
+ // Keep it for later, when we see a MODULE_HASH record
+ uint64_t BlockEntryPos = Stream.getCurrentByteNo();
+
const char *BlockName = nullptr;
- if (Dump) {
+ if (DumpRecords) {
outs() << Indent << "<";
if ((BlockName = GetBlockName(BlockID, *Stream.getBitStreamReader(),
CurStreamType)))
@@ -453,7 +532,7 @@ static bool ParseBlock(BitstreamCursor &Stream, unsigned BlockID,
case BitstreamEntry::EndBlock: {
uint64_t BlockBitEnd = Stream.GetCurrentBitNo();
BlockStats.NumBits += BlockBitEnd-BlockBitStart;
- if (Dump) {
+ if (DumpRecords) {
outs() << Indent << "</";
if (BlockName)
outs() << BlockName << ">\n";
@@ -490,6 +569,7 @@ static bool ParseBlock(BitstreamCursor &Stream, unsigned BlockID,
++BlockStats.NumRecords;
StringRef Blob;
+ unsigned CurrentRecordPos = Stream.getCurrentByteNo();
unsigned Code = Stream.readRecord(Entry.ID, Record, &Blob);
// Increment the # occurrences of this code.
@@ -503,7 +583,7 @@ static bool ParseBlock(BitstreamCursor &Stream, unsigned BlockID,
++BlockStats.NumAbbreviatedRecords;
}
- if (Dump) {
+ if (DumpRecords) {
outs() << Indent << " <";
if (const char *CodeName =
GetCodeName(Code, BlockID, *Stream.getBitStreamReader(),
@@ -524,6 +604,37 @@ static bool ParseBlock(BitstreamCursor &Stream, unsigned BlockID,
for (unsigned i = 0, e = Record.size(); i != e; ++i)
outs() << " op" << i << "=" << (int64_t)Record[i];
+ // If we found a module hash, let's verify that it matches!
+ if (BlockID == bitc::MODULE_BLOCK_ID && Code == bitc::MODULE_CODE_HASH) {
+ if (Record.size() != 5)
+ outs() << " (invalid)";
+ else {
+ // Recompute the hash and compare it to the one in the bitcode
+ SHA1 Hasher;
+ StringRef Hash;
+ {
+ int BlockSize = CurrentRecordPos - BlockEntryPos;
+ auto Ptr = Stream.getPointerToByte(BlockEntryPos, BlockSize);
+ Hasher.update(ArrayRef<uint8_t>(Ptr, BlockSize));
+ Hash = Hasher.result();
+ }
+ SmallString<20> RecordedHash;
+ RecordedHash.resize(20);
+ int Pos = 0;
+ for (auto &Val : Record) {
+ assert(!(Val >> 32) && "Unexpected high bits set");
+ RecordedHash[Pos++] = (Val >> 24) & 0xFF;
+ RecordedHash[Pos++] = (Val >> 16) & 0xFF;
+ RecordedHash[Pos++] = (Val >> 8) & 0xFF;
+ RecordedHash[Pos++] = (Val >> 0) & 0xFF;
+ }
+ if (Hash == RecordedHash)
+ outs() << " (match)";
+ else
+ outs() << " (!mismatch!)";
+ }
+ }
+
outs() << "/>";
if (Abbv) {
@@ -547,7 +658,8 @@ static bool ParseBlock(BitstreamCursor &Stream, unsigned BlockID,
}
}
- if (Blob.data()) {
+ if (Blob.data() && decodeBlob(Code, BlockID, *Stream.getBitStreamReader(),
+ Indent, Record, Blob)) {
outs() << " blob data = ";
if (ShowBinaryBlobs) {
outs() << "'";
@@ -600,9 +712,28 @@ static bool openBitcodeFile(StringRef Path,
// If we have a wrapper header, parse it and ignore the non-bc file contents.
// The magic number is 0x0B17C0DE stored in little endian.
- if (isBitcodeWrapper(BufPtr, EndBufPtr))
+ if (isBitcodeWrapper(BufPtr, EndBufPtr)) {
+ if (MemBuf->getBufferSize() < BWH_HeaderSize)
+ return Error("Invalid bitcode wrapper header");
+
+ if (Dump) {
+ unsigned Magic = support::endian::read32le(&BufPtr[BWH_MagicField]);
+ unsigned Version = support::endian::read32le(&BufPtr[BWH_VersionField]);
+ unsigned Offset = support::endian::read32le(&BufPtr[BWH_OffsetField]);
+ unsigned Size = support::endian::read32le(&BufPtr[BWH_SizeField]);
+ unsigned CPUType = support::endian::read32le(&BufPtr[BWH_CPUTypeField]);
+
+ outs() << "<BITCODE_WRAPPER_HEADER"
+ << " Magic=" << format_hex(Magic, 10)
+ << " Version=" << format_hex(Version, 10)
+ << " Offset=" << format_hex(Offset, 10)
+ << " Size=" << format_hex(Size, 10)
+ << " CPUType=" << format_hex(CPUType, 10) << "/>\n";
+ }
+
if (SkipBitcodeWrapperHeader(BufPtr, EndBufPtr, true))
return Error("Invalid bitcode wrapper header");
+ }
StreamFile = BitstreamReader(BufPtr, EndBufPtr);
Stream = BitstreamCursor(StreamFile);
@@ -745,7 +876,7 @@ static int AnalyzeBitcode() {
std::reverse(FreqPairs.begin(), FreqPairs.end());
outs() << "\tRecord Histogram:\n";
- outs() << "\t\t Count # Bits %% Abv Record Kind\n";
+ outs() << "\t\t Count # Bits b/Rec % Abv Record Kind\n";
for (unsigned i = 0, e = FreqPairs.size(); i != e; ++i) {
const PerRecordStats &RecStats = Stats.CodeFreq[FreqPairs[i].second];
@@ -753,13 +884,20 @@ static int AnalyzeBitcode() {
RecStats.NumInstances,
(unsigned long)RecStats.TotalBits);
+ if (RecStats.NumInstances > 1)
+ outs() << format(" %9.1f",
+ (double)RecStats.TotalBits/RecStats.NumInstances);
+ else
+ outs() << " ";
+
if (RecStats.NumAbbrev)
outs() <<
- format("%7.2f ",
+ format(" %7.2f",
(double)RecStats.NumAbbrev/RecStats.NumInstances*100);
else
- outs() << " ";
+ outs() << " ";
+ outs() << " ";
if (const char *CodeName =
GetCodeName(FreqPairs[i].second, I->first, StreamFile,
CurStreamType))
@@ -777,7 +915,7 @@ static int AnalyzeBitcode() {
int main(int argc, char **argv) {
// Print a stack trace if we signal out.
- sys::PrintStackTraceOnErrorSignal();
+ sys::PrintStackTraceOnErrorSignal(argv[0]);
PrettyStackTraceProgram X(argc, argv);
llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
cl::ParseCommandLineOptions(argc, argv, "llvm-bcanalyzer file analyzer\n");
diff --git a/tools/llvm-c-test/CMakeLists.txt b/tools/llvm-c-test/CMakeLists.txt
index f22dffb30e8a4..858f2b07214a1 100644
--- a/tools/llvm-c-test/CMakeLists.txt
+++ b/tools/llvm-c-test/CMakeLists.txt
@@ -4,6 +4,7 @@ set(LLVM_LINK_COMPONENTS
Core
MCDisassembler
Object
+ Support
Target
)
@@ -36,7 +37,9 @@ endif ()
add_llvm_tool(llvm-c-test
calc.c
+ diagnostic.c
disassemble.c
+ echo.cpp
helpers.c
include-all.c
main.c
diff --git a/tools/llvm-c-test/Makefile b/tools/llvm-c-test/Makefile
deleted file mode 100644
index 08be7c3619fb2..0000000000000
--- a/tools/llvm-c-test/Makefile
+++ /dev/null
@@ -1,29 +0,0 @@
-##===- tools/llvm-c-test -----------------------------------*- 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-c-test
-
-TOOL_NO_EXPORTS = 1
-NO_INSTALL = 1
-
-
-# If there is no shared lib, link all components...
-ifneq ($(ENABLE_SHARED),1)
-LINK_COMPONENTS = all
-endif
-
-include $(LEVEL)/Makefile.common
-
-CFLAGS += -std=c99 -Wall -Wstrict-prototypes
-
-# ...but if it is built - use it
-ifeq ($(ENABLE_SHARED),1)
-LIBS = -lLLVM-$(LLVMVersion)
-endif
diff --git a/tools/llvm-c-test/calc.c b/tools/llvm-c-test/calc.c
index 3119ccf8e9aa7..4c273cbf70a26 100644
--- a/tools/llvm-c-test/calc.c
+++ b/tools/llvm-c-test/calc.c
@@ -14,7 +14,6 @@
\*===----------------------------------------------------------------------===*/
#include "llvm-c-test.h"
-#include "llvm-c/Core.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -140,9 +139,9 @@ static void handle_line(char **tokens, int ntokens) {
LLVMDisposeModule(M);
}
-int calc(void) {
+int llvm_calc(void) {
- tokenize_stdin(handle_line);
+ llvm_tokenize_stdin(handle_line);
return 0;
}
diff --git a/tools/llvm-c-test/diagnostic.c b/tools/llvm-c-test/diagnostic.c
new file mode 100644
index 0000000000000..16d5174732360
--- /dev/null
+++ b/tools/llvm-c-test/diagnostic.c
@@ -0,0 +1,89 @@
+//===-- diagnostic.cpp - tool for testing libLLVM and llvm-c API ----------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the --test-diagnostic-handler command in llvm-c-test.
+//
+// This command uses the C API to read a module with a custom diagnostic
+// handler set to test the diagnostic handler functionality.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm-c-test.h"
+#include "llvm-c/BitReader.h"
+#include "llvm-c/Core.h"
+
+#include <stdio.h>
+
+static void diagnosticHandler(LLVMDiagnosticInfoRef DI, void *C) {
+ fprintf(stderr, "Executing diagnostic handler\n");
+
+ fprintf(stderr, "Diagnostic severity is of type ");
+ switch (LLVMGetDiagInfoSeverity(DI)) {
+ case LLVMDSError:
+ fprintf(stderr, "error");
+ break;
+ case LLVMDSWarning:
+ fprintf(stderr, "warning");
+ break;
+ case LLVMDSRemark:
+ fprintf(stderr, "remark");
+ break;
+ case LLVMDSNote:
+ fprintf(stderr, "note");
+ break;
+ }
+ fprintf(stderr, "\n");
+
+ (*(int *)C) = 1;
+}
+
+static int handlerCalled = 0;
+
+int llvm_test_diagnostic_handler(void) {
+ LLVMContextRef C = LLVMGetGlobalContext();
+ LLVMContextSetDiagnosticHandler(C, diagnosticHandler, &handlerCalled);
+
+ if (LLVMContextGetDiagnosticHandler(C) != diagnosticHandler) {
+ fprintf(stderr, "LLVMContext{Set,Get}DiagnosticHandler failed\n");
+ return 1;
+ }
+
+ int *DC = (int *)LLVMContextGetDiagnosticContext(C);
+ if (DC != &handlerCalled || *DC) {
+ fprintf(stderr, "LLVMContextGetDiagnosticContext failed\n");
+ return 1;
+ }
+
+ LLVMMemoryBufferRef MB;
+ char *msg = NULL;
+ if (LLVMCreateMemoryBufferWithSTDIN(&MB, &msg)) {
+ fprintf(stderr, "Error reading file: %s\n", msg);
+ LLVMDisposeMessage(msg);
+ return 1;
+ }
+
+
+ LLVMModuleRef M;
+ int Ret = LLVMGetBitcodeModule2(MB, &M);
+ if (Ret) {
+ // We do not return if the bitcode was invalid, as we want to test whether
+ // the diagnostic handler was executed.
+ fprintf(stderr, "Error parsing bitcode: %s\n", msg);
+ }
+
+ LLVMDisposeMemoryBuffer(MB);
+
+ if (handlerCalled) {
+ fprintf(stderr, "Diagnostic handler was called while loading module\n");
+ } else {
+ fprintf(stderr, "Diagnostic handler was not called while loading module\n");
+ }
+
+ return 0;
+}
diff --git a/tools/llvm-c-test/disassemble.c b/tools/llvm-c-test/disassemble.c
index 05a9218d014a6..7d6f1783aed9e 100644
--- a/tools/llvm-c-test/disassemble.c
+++ b/tools/llvm-c-test/disassemble.c
@@ -83,12 +83,12 @@ static void handle_line(char **tokens, int ntokens) {
do_disassemble(triple, features, disbuf, disbuflen);
}
-int disassemble(void) {
+int llvm_disassemble(void) {
LLVMInitializeAllTargetInfos();
LLVMInitializeAllTargetMCs();
LLVMInitializeAllDisassemblers();
- tokenize_stdin(handle_line);
+ llvm_tokenize_stdin(handle_line);
return 0;
}
diff --git a/tools/llvm-c-test/echo.cpp b/tools/llvm-c-test/echo.cpp
new file mode 100644
index 0000000000000..72ff138c74e32
--- /dev/null
+++ b/tools/llvm-c-test/echo.cpp
@@ -0,0 +1,958 @@
+//===-- echo.cpp - tool for testing libLLVM and llvm-c API ----------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the --echo command in llvm-c-test.
+//
+// This command uses the C API to read a module and output an exact copy of it
+// as output. It is used to check that the resulting module matches the input
+// to validate that the C API can read and write modules properly.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm-c-test.h"
+#include "llvm-c/Target.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/Support/ErrorHandling.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+using namespace llvm;
+
+// Provide DenseMapInfo for C API opaque types.
+template<typename T>
+struct CAPIDenseMap {};
+
+// The default DenseMapInfo require to know about pointer alignement.
+// Because the C API uses opaques pointer types, their alignement is unknown.
+// As a result, we need to roll out our own implementation.
+template<typename T>
+struct CAPIDenseMap<T*> {
+ struct CAPIDenseMapInfo {
+ static inline T* getEmptyKey() {
+ uintptr_t Val = static_cast<uintptr_t>(-1);
+ return reinterpret_cast<T*>(Val);
+ }
+ static inline T* getTombstoneKey() {
+ uintptr_t Val = static_cast<uintptr_t>(-2);
+ return reinterpret_cast<T*>(Val);
+ }
+ static unsigned getHashValue(const T *PtrVal) {
+ return hash_value(PtrVal);
+ }
+ static bool isEqual(const T *LHS, const T *RHS) { return LHS == RHS; }
+ };
+
+ typedef DenseMap<T*, T*, CAPIDenseMapInfo> Map;
+};
+
+typedef CAPIDenseMap<LLVMValueRef>::Map ValueMap;
+typedef CAPIDenseMap<LLVMBasicBlockRef>::Map BasicBlockMap;
+
+struct TypeCloner {
+ LLVMModuleRef M;
+ LLVMContextRef Ctx;
+
+ TypeCloner(LLVMModuleRef M): M(M), Ctx(LLVMGetModuleContext(M)) {}
+
+ LLVMTypeRef Clone(LLVMValueRef Src) {
+ return Clone(LLVMTypeOf(Src));
+ }
+
+ LLVMTypeRef Clone(LLVMTypeRef Src) {
+ LLVMTypeKind Kind = LLVMGetTypeKind(Src);
+ switch (Kind) {
+ case LLVMVoidTypeKind:
+ return LLVMVoidTypeInContext(Ctx);
+ case LLVMHalfTypeKind:
+ return LLVMHalfTypeInContext(Ctx);
+ case LLVMFloatTypeKind:
+ return LLVMFloatTypeInContext(Ctx);
+ case LLVMDoubleTypeKind:
+ return LLVMDoubleTypeInContext(Ctx);
+ case LLVMX86_FP80TypeKind:
+ return LLVMX86FP80TypeInContext(Ctx);
+ case LLVMFP128TypeKind:
+ return LLVMFP128TypeInContext(Ctx);
+ case LLVMPPC_FP128TypeKind:
+ return LLVMPPCFP128TypeInContext(Ctx);
+ case LLVMLabelTypeKind:
+ return LLVMLabelTypeInContext(Ctx);
+ case LLVMIntegerTypeKind:
+ return LLVMIntTypeInContext(Ctx, LLVMGetIntTypeWidth(Src));
+ case LLVMFunctionTypeKind: {
+ unsigned ParamCount = LLVMCountParamTypes(Src);
+ LLVMTypeRef* Params = nullptr;
+ if (ParamCount > 0) {
+ Params = (LLVMTypeRef*) malloc(ParamCount * sizeof(LLVMTypeRef));
+ LLVMGetParamTypes(Src, Params);
+ for (unsigned i = 0; i < ParamCount; i++)
+ Params[i] = Clone(Params[i]);
+ }
+
+ LLVMTypeRef FunTy = LLVMFunctionType(Clone(LLVMGetReturnType(Src)),
+ Params, ParamCount,
+ LLVMIsFunctionVarArg(Src));
+ if (ParamCount > 0)
+ free(Params);
+ return FunTy;
+ }
+ case LLVMStructTypeKind: {
+ LLVMTypeRef S = nullptr;
+ const char *Name = LLVMGetStructName(Src);
+ if (Name) {
+ S = LLVMGetTypeByName(M, Name);
+ if (S)
+ return S;
+ S = LLVMStructCreateNamed(Ctx, Name);
+ if (LLVMIsOpaqueStruct(Src))
+ return S;
+ }
+
+ unsigned EltCount = LLVMCountStructElementTypes(Src);
+ SmallVector<LLVMTypeRef, 8> Elts;
+ for (unsigned i = 0; i < EltCount; i++)
+ Elts.push_back(Clone(LLVMStructGetTypeAtIndex(Src, i)));
+ if (Name)
+ LLVMStructSetBody(S, Elts.data(), EltCount, LLVMIsPackedStruct(Src));
+ else
+ S = LLVMStructTypeInContext(Ctx, Elts.data(), EltCount,
+ LLVMIsPackedStruct(Src));
+ return S;
+ }
+ case LLVMArrayTypeKind:
+ return LLVMArrayType(
+ Clone(LLVMGetElementType(Src)),
+ LLVMGetArrayLength(Src)
+ );
+ case LLVMPointerTypeKind:
+ return LLVMPointerType(
+ Clone(LLVMGetElementType(Src)),
+ LLVMGetPointerAddressSpace(Src)
+ );
+ case LLVMVectorTypeKind:
+ return LLVMVectorType(
+ Clone(LLVMGetElementType(Src)),
+ LLVMGetVectorSize(Src)
+ );
+ case LLVMMetadataTypeKind:
+ break;
+ case LLVMX86_MMXTypeKind:
+ return LLVMX86MMXTypeInContext(Ctx);
+ default:
+ break;
+ }
+
+ fprintf(stderr, "%d is not a supported typekind\n", Kind);
+ exit(-1);
+ }
+};
+
+static ValueMap clone_params(LLVMValueRef Src, LLVMValueRef Dst) {
+ unsigned Count = LLVMCountParams(Src);
+ if (Count != LLVMCountParams(Dst))
+ report_fatal_error("Parameter count mismatch");
+
+ ValueMap VMap;
+ if (Count == 0)
+ return VMap;
+
+ LLVMValueRef SrcFirst = LLVMGetFirstParam(Src);
+ LLVMValueRef DstFirst = LLVMGetFirstParam(Dst);
+ LLVMValueRef SrcLast = LLVMGetLastParam(Src);
+ LLVMValueRef DstLast = LLVMGetLastParam(Dst);
+
+ LLVMValueRef SrcCur = SrcFirst;
+ LLVMValueRef DstCur = DstFirst;
+ LLVMValueRef SrcNext = nullptr;
+ LLVMValueRef DstNext = nullptr;
+ while (true) {
+ const char *Name = LLVMGetValueName(SrcCur);
+ LLVMSetValueName(DstCur, Name);
+
+ VMap[SrcCur] = DstCur;
+
+ Count--;
+ SrcNext = LLVMGetNextParam(SrcCur);
+ DstNext = LLVMGetNextParam(DstCur);
+ if (SrcNext == nullptr && DstNext == nullptr) {
+ if (SrcCur != SrcLast)
+ report_fatal_error("SrcLast param does not match End");
+ if (DstCur != DstLast)
+ report_fatal_error("DstLast param does not match End");
+ break;
+ }
+
+ if (SrcNext == nullptr)
+ report_fatal_error("SrcNext was unexpectedly null");
+ if (DstNext == nullptr)
+ report_fatal_error("DstNext was unexpectedly null");
+
+ LLVMValueRef SrcPrev = LLVMGetPreviousParam(SrcNext);
+ if (SrcPrev != SrcCur)
+ report_fatal_error("SrcNext.Previous param is not Current");
+
+ LLVMValueRef DstPrev = LLVMGetPreviousParam(DstNext);
+ if (DstPrev != DstCur)
+ report_fatal_error("DstNext.Previous param is not Current");
+
+ SrcCur = SrcNext;
+ DstCur = DstNext;
+ }
+
+ if (Count != 0)
+ report_fatal_error("Parameter count does not match iteration");
+
+ return VMap;
+}
+
+static void check_value_kind(LLVMValueRef V, LLVMValueKind K) {
+ if (LLVMGetValueKind(V) != K)
+ report_fatal_error("LLVMGetValueKind returned incorrect type");
+}
+
+static LLVMValueRef clone_constant_impl(LLVMValueRef Cst, LLVMModuleRef M);
+
+static LLVMValueRef clone_constant(LLVMValueRef Cst, LLVMModuleRef M) {
+ LLVMValueRef Ret = clone_constant_impl(Cst, M);
+ check_value_kind(Ret, LLVMGetValueKind(Cst));
+ return Ret;
+}
+
+static LLVMValueRef clone_constant_impl(LLVMValueRef Cst, LLVMModuleRef M) {
+ if (!LLVMIsAConstant(Cst))
+ report_fatal_error("Expected a constant");
+
+ // Maybe it is a symbol
+ if (LLVMIsAGlobalValue(Cst)) {
+ const char *Name = LLVMGetValueName(Cst);
+
+ // Try function
+ if (LLVMIsAFunction(Cst)) {
+ check_value_kind(Cst, LLVMFunctionValueKind);
+ LLVMValueRef Dst = LLVMGetNamedFunction(M, Name);
+ if (Dst)
+ return Dst;
+ report_fatal_error("Could not find function");
+ }
+
+ // Try global variable
+ if (LLVMIsAGlobalVariable(Cst)) {
+ check_value_kind(Cst, LLVMGlobalVariableValueKind);
+ LLVMValueRef Dst = LLVMGetNamedGlobal(M, Name);
+ if (Dst)
+ return Dst;
+ report_fatal_error("Could not find function");
+ }
+
+ fprintf(stderr, "Could not find @%s\n", Name);
+ exit(-1);
+ }
+
+ // Try integer literal
+ if (LLVMIsAConstantInt(Cst)) {
+ check_value_kind(Cst, LLVMConstantIntValueKind);
+ return LLVMConstInt(TypeCloner(M).Clone(Cst),
+ LLVMConstIntGetZExtValue(Cst), false);
+ }
+
+ // Try zeroinitializer
+ if (LLVMIsAConstantAggregateZero(Cst)) {
+ check_value_kind(Cst, LLVMConstantAggregateZeroValueKind);
+ return LLVMConstNull(TypeCloner(M).Clone(Cst));
+ }
+
+ // Try constant array
+ if (LLVMIsAConstantArray(Cst)) {
+ check_value_kind(Cst, LLVMConstantArrayValueKind);
+ LLVMTypeRef Ty = TypeCloner(M).Clone(Cst);
+ unsigned EltCount = LLVMGetArrayLength(Ty);
+ SmallVector<LLVMValueRef, 8> Elts;
+ for (unsigned i = 0; i < EltCount; i++)
+ Elts.push_back(clone_constant(LLVMGetOperand(Cst, i), M));
+ return LLVMConstArray(LLVMGetElementType(Ty), Elts.data(), EltCount);
+ }
+
+ // Try contant data array
+ if (LLVMIsAConstantDataArray(Cst)) {
+ check_value_kind(Cst, LLVMConstantDataArrayValueKind);
+ LLVMTypeRef Ty = TypeCloner(M).Clone(Cst);
+ unsigned EltCount = LLVMGetArrayLength(Ty);
+ SmallVector<LLVMValueRef, 8> Elts;
+ for (unsigned i = 0; i < EltCount; i++)
+ Elts.push_back(clone_constant(LLVMGetElementAsConstant(Cst, i), M));
+ return LLVMConstArray(LLVMGetElementType(Ty), Elts.data(), EltCount);
+ }
+
+ // Try constant struct
+ if (LLVMIsAConstantStruct(Cst)) {
+ check_value_kind(Cst, LLVMConstantStructValueKind);
+ LLVMTypeRef Ty = TypeCloner(M).Clone(Cst);
+ unsigned EltCount = LLVMCountStructElementTypes(Ty);
+ SmallVector<LLVMValueRef, 8> Elts;
+ for (unsigned i = 0; i < EltCount; i++)
+ Elts.push_back(clone_constant(LLVMGetOperand(Cst, i), M));
+ if (LLVMGetStructName(Ty))
+ return LLVMConstNamedStruct(Ty, Elts.data(), EltCount);
+ return LLVMConstStructInContext(LLVMGetModuleContext(M), Elts.data(),
+ EltCount, LLVMIsPackedStruct(Ty));
+ }
+
+ // Try undef
+ if (LLVMIsUndef(Cst)) {
+ check_value_kind(Cst, LLVMUndefValueValueKind);
+ return LLVMGetUndef(TypeCloner(M).Clone(Cst));
+ }
+
+ // Try float literal
+ if (LLVMIsAConstantFP(Cst)) {
+ check_value_kind(Cst, LLVMConstantFPValueKind);
+ report_fatal_error("ConstantFP is not supported");
+ }
+
+ // This kind of constant is not supported
+ if (!LLVMIsAConstantExpr(Cst))
+ report_fatal_error("Expected a constant expression");
+
+ // At this point, it must be a constant expression
+ check_value_kind(Cst, LLVMConstantExprValueKind);
+
+ LLVMOpcode Op = LLVMGetConstOpcode(Cst);
+ switch(Op) {
+ case LLVMBitCast:
+ return LLVMConstBitCast(clone_constant(LLVMGetOperand(Cst, 0), M),
+ TypeCloner(M).Clone(Cst));
+ default:
+ fprintf(stderr, "%d is not a supported opcode\n", Op);
+ exit(-1);
+ }
+}
+
+struct FunCloner {
+ LLVMValueRef Fun;
+ LLVMModuleRef M;
+
+ ValueMap VMap;
+ BasicBlockMap BBMap;
+
+ FunCloner(LLVMValueRef Src, LLVMValueRef Dst): Fun(Dst),
+ M(LLVMGetGlobalParent(Fun)), VMap(clone_params(Src, Dst)) {}
+
+ LLVMTypeRef CloneType(LLVMTypeRef Src) {
+ return TypeCloner(M).Clone(Src);
+ }
+
+ LLVMTypeRef CloneType(LLVMValueRef Src) {
+ return TypeCloner(M).Clone(Src);
+ }
+
+ // Try to clone everything in the llvm::Value hierarchy.
+ LLVMValueRef CloneValue(LLVMValueRef Src) {
+ // First, the value may be constant.
+ if (LLVMIsAConstant(Src))
+ return clone_constant(Src, M);
+
+ // Function argument should always be in the map already.
+ auto i = VMap.find(Src);
+ if (i != VMap.end())
+ return i->second;
+
+ if (!LLVMIsAInstruction(Src))
+ report_fatal_error("Expected an instruction");
+
+ auto Ctx = LLVMGetModuleContext(M);
+ auto Builder = LLVMCreateBuilderInContext(Ctx);
+ auto BB = DeclareBB(LLVMGetInstructionParent(Src));
+ LLVMPositionBuilderAtEnd(Builder, BB);
+ auto Dst = CloneInstruction(Src, Builder);
+ LLVMDisposeBuilder(Builder);
+ return Dst;
+ }
+
+ void CloneAttrs(LLVMValueRef Src, LLVMValueRef Dst) {
+ auto Ctx = LLVMGetModuleContext(M);
+ int ArgCount = LLVMGetNumArgOperands(Src);
+ for (int i = LLVMAttributeReturnIndex; i <= ArgCount; i++) {
+ for (unsigned k = 0, e = LLVMGetLastEnumAttributeKind(); k < e; ++k) {
+ if (auto SrcA = LLVMGetCallSiteEnumAttribute(Src, i, k)) {
+ auto Val = LLVMGetEnumAttributeValue(SrcA);
+ auto A = LLVMCreateEnumAttribute(Ctx, k, Val);
+ LLVMAddCallSiteAttribute(Dst, i, A);
+ }
+ }
+ }
+ }
+
+ LLVMValueRef CloneInstruction(LLVMValueRef Src, LLVMBuilderRef Builder) {
+ check_value_kind(Src, LLVMInstructionValueKind);
+ if (!LLVMIsAInstruction(Src))
+ report_fatal_error("Expected an instruction");
+
+ const char *Name = LLVMGetValueName(Src);
+
+ // Check if this is something we already computed.
+ {
+ auto i = VMap.find(Src);
+ if (i != VMap.end()) {
+ // If we have a hit, it means we already generated the instruction
+ // as a dependancy to somethign else. We need to make sure
+ // it is ordered properly.
+ auto I = i->second;
+ LLVMInstructionRemoveFromParent(I);
+ LLVMInsertIntoBuilderWithName(Builder, I, Name);
+ return I;
+ }
+ }
+
+ // We tried everything, it must be an instruction
+ // that hasn't been generated already.
+ LLVMValueRef Dst = nullptr;
+
+ LLVMOpcode Op = LLVMGetInstructionOpcode(Src);
+ switch(Op) {
+ case LLVMRet: {
+ int OpCount = LLVMGetNumOperands(Src);
+ if (OpCount == 0)
+ Dst = LLVMBuildRetVoid(Builder);
+ else
+ Dst = LLVMBuildRet(Builder, CloneValue(LLVMGetOperand(Src, 0)));
+ break;
+ }
+ case LLVMBr: {
+ if (!LLVMIsConditional(Src)) {
+ LLVMValueRef SrcOp = LLVMGetOperand(Src, 0);
+ LLVMBasicBlockRef SrcBB = LLVMValueAsBasicBlock(SrcOp);
+ Dst = LLVMBuildBr(Builder, DeclareBB(SrcBB));
+ break;
+ }
+
+ LLVMValueRef Cond = LLVMGetCondition(Src);
+ LLVMValueRef Else = LLVMGetOperand(Src, 1);
+ LLVMBasicBlockRef ElseBB = DeclareBB(LLVMValueAsBasicBlock(Else));
+ LLVMValueRef Then = LLVMGetOperand(Src, 2);
+ LLVMBasicBlockRef ThenBB = DeclareBB(LLVMValueAsBasicBlock(Then));
+ Dst = LLVMBuildCondBr(Builder, Cond, ThenBB, ElseBB);
+ break;
+ }
+ case LLVMSwitch:
+ case LLVMIndirectBr:
+ break;
+ case LLVMInvoke: {
+ SmallVector<LLVMValueRef, 8> Args;
+ int ArgCount = LLVMGetNumArgOperands(Src);
+ for (int i = 0; i < ArgCount; i++)
+ Args.push_back(CloneValue(LLVMGetOperand(Src, i)));
+ LLVMValueRef Fn = CloneValue(LLVMGetCalledValue(Src));
+ LLVMBasicBlockRef Then = DeclareBB(LLVMGetNormalDest(Src));
+ LLVMBasicBlockRef Unwind = DeclareBB(LLVMGetUnwindDest(Src));
+ Dst = LLVMBuildInvoke(Builder, Fn, Args.data(), ArgCount,
+ Then, Unwind, Name);
+ CloneAttrs(Src, Dst);
+ break;
+ }
+ case LLVMUnreachable:
+ Dst = LLVMBuildUnreachable(Builder);
+ break;
+ case LLVMAdd: {
+ LLVMValueRef LHS = CloneValue(LLVMGetOperand(Src, 0));
+ LLVMValueRef RHS = CloneValue(LLVMGetOperand(Src, 1));
+ Dst = LLVMBuildAdd(Builder, LHS, RHS, Name);
+ break;
+ }
+ case LLVMSub: {
+ LLVMValueRef LHS = CloneValue(LLVMGetOperand(Src, 0));
+ LLVMValueRef RHS = CloneValue(LLVMGetOperand(Src, 1));
+ Dst = LLVMBuildSub(Builder, LHS, RHS, Name);
+ break;
+ }
+ case LLVMMul: {
+ LLVMValueRef LHS = CloneValue(LLVMGetOperand(Src, 0));
+ LLVMValueRef RHS = CloneValue(LLVMGetOperand(Src, 1));
+ Dst = LLVMBuildMul(Builder, LHS, RHS, Name);
+ break;
+ }
+ case LLVMUDiv: {
+ LLVMValueRef LHS = CloneValue(LLVMGetOperand(Src, 0));
+ LLVMValueRef RHS = CloneValue(LLVMGetOperand(Src, 1));
+ Dst = LLVMBuildUDiv(Builder, LHS, RHS, Name);
+ break;
+ }
+ case LLVMSDiv: {
+ LLVMValueRef LHS = CloneValue(LLVMGetOperand(Src, 0));
+ LLVMValueRef RHS = CloneValue(LLVMGetOperand(Src, 1));
+ Dst = LLVMBuildSDiv(Builder, LHS, RHS, Name);
+ break;
+ }
+ case LLVMURem: {
+ LLVMValueRef LHS = CloneValue(LLVMGetOperand(Src, 0));
+ LLVMValueRef RHS = CloneValue(LLVMGetOperand(Src, 1));
+ Dst = LLVMBuildURem(Builder, LHS, RHS, Name);
+ break;
+ }
+ case LLVMSRem: {
+ LLVMValueRef LHS = CloneValue(LLVMGetOperand(Src, 0));
+ LLVMValueRef RHS = CloneValue(LLVMGetOperand(Src, 1));
+ Dst = LLVMBuildSRem(Builder, LHS, RHS, Name);
+ break;
+ }
+ case LLVMShl: {
+ LLVMValueRef LHS = CloneValue(LLVMGetOperand(Src, 0));
+ LLVMValueRef RHS = CloneValue(LLVMGetOperand(Src, 1));
+ Dst = LLVMBuildShl(Builder, LHS, RHS, Name);
+ break;
+ }
+ case LLVMLShr: {
+ LLVMValueRef LHS = CloneValue(LLVMGetOperand(Src, 0));
+ LLVMValueRef RHS = CloneValue(LLVMGetOperand(Src, 1));
+ Dst = LLVMBuildLShr(Builder, LHS, RHS, Name);
+ break;
+ }
+ case LLVMAShr: {
+ LLVMValueRef LHS = CloneValue(LLVMGetOperand(Src, 0));
+ LLVMValueRef RHS = CloneValue(LLVMGetOperand(Src, 1));
+ Dst = LLVMBuildAShr(Builder, LHS, RHS, Name);
+ break;
+ }
+ case LLVMAnd: {
+ LLVMValueRef LHS = CloneValue(LLVMGetOperand(Src, 0));
+ LLVMValueRef RHS = CloneValue(LLVMGetOperand(Src, 1));
+ Dst = LLVMBuildAnd(Builder, LHS, RHS, Name);
+ break;
+ }
+ case LLVMOr: {
+ LLVMValueRef LHS = CloneValue(LLVMGetOperand(Src, 0));
+ LLVMValueRef RHS = CloneValue(LLVMGetOperand(Src, 1));
+ Dst = LLVMBuildOr(Builder, LHS, RHS, Name);
+ break;
+ }
+ case LLVMXor: {
+ LLVMValueRef LHS = CloneValue(LLVMGetOperand(Src, 0));
+ LLVMValueRef RHS = CloneValue(LLVMGetOperand(Src, 1));
+ Dst = LLVMBuildXor(Builder, LHS, RHS, Name);
+ break;
+ }
+ case LLVMAlloca: {
+ LLVMTypeRef Ty = CloneType(LLVMGetAllocatedType(Src));
+ Dst = LLVMBuildAlloca(Builder, Ty, Name);
+ break;
+ }
+ case LLVMLoad: {
+ LLVMValueRef Ptr = CloneValue(LLVMGetOperand(Src, 0));
+ Dst = LLVMBuildLoad(Builder, Ptr, Name);
+ LLVMSetAlignment(Dst, LLVMGetAlignment(Src));
+ break;
+ }
+ case LLVMStore: {
+ LLVMValueRef Val = CloneValue(LLVMGetOperand(Src, 0));
+ LLVMValueRef Ptr = CloneValue(LLVMGetOperand(Src, 1));
+ Dst = LLVMBuildStore(Builder, Val, Ptr);
+ LLVMSetAlignment(Dst, LLVMGetAlignment(Src));
+ break;
+ }
+ case LLVMGetElementPtr: {
+ LLVMValueRef Ptr = CloneValue(LLVMGetOperand(Src, 0));
+ SmallVector<LLVMValueRef, 8> Idx;
+ int NumIdx = LLVMGetNumIndices(Src);
+ for (int i = 1; i <= NumIdx; i++)
+ Idx.push_back(CloneValue(LLVMGetOperand(Src, i)));
+ if (LLVMIsInBounds(Src))
+ Dst = LLVMBuildInBoundsGEP(Builder, Ptr, Idx.data(), NumIdx, Name);
+ else
+ Dst = LLVMBuildGEP(Builder, Ptr, Idx.data(), NumIdx, Name);
+ break;
+ }
+ case LLVMAtomicCmpXchg: {
+ LLVMValueRef Ptr = CloneValue(LLVMGetOperand(Src, 0));
+ LLVMValueRef Cmp = CloneValue(LLVMGetOperand(Src, 1));
+ LLVMValueRef New = CloneValue(LLVMGetOperand(Src, 2));
+ LLVMAtomicOrdering Succ = LLVMGetCmpXchgSuccessOrdering(Src);
+ LLVMAtomicOrdering Fail = LLVMGetCmpXchgFailureOrdering(Src);
+ LLVMBool SingleThread = LLVMIsAtomicSingleThread(Src);
+
+ Dst = LLVMBuildAtomicCmpXchg(Builder, Ptr, Cmp, New, Succ, Fail,
+ SingleThread);
+ } break;
+ case LLVMBitCast: {
+ LLVMValueRef V = CloneValue(LLVMGetOperand(Src, 0));
+ Dst = LLVMBuildBitCast(Builder, V, CloneType(Src), Name);
+ break;
+ }
+ case LLVMICmp: {
+ LLVMIntPredicate Pred = LLVMGetICmpPredicate(Src);
+ LLVMValueRef LHS = CloneValue(LLVMGetOperand(Src, 0));
+ LLVMValueRef RHS = CloneValue(LLVMGetOperand(Src, 1));
+ Dst = LLVMBuildICmp(Builder, Pred, LHS, RHS, Name);
+ break;
+ }
+ case LLVMPHI: {
+ // We need to agressively set things here because of loops.
+ VMap[Src] = Dst = LLVMBuildPhi(Builder, CloneType(Src), Name);
+
+ SmallVector<LLVMValueRef, 8> Values;
+ SmallVector<LLVMBasicBlockRef, 8> Blocks;
+
+ unsigned IncomingCount = LLVMCountIncoming(Src);
+ for (unsigned i = 0; i < IncomingCount; ++i) {
+ Blocks.push_back(DeclareBB(LLVMGetIncomingBlock(Src, i)));
+ Values.push_back(CloneValue(LLVMGetIncomingValue(Src, i)));
+ }
+
+ LLVMAddIncoming(Dst, Values.data(), Blocks.data(), IncomingCount);
+ return Dst;
+ }
+ case LLVMCall: {
+ SmallVector<LLVMValueRef, 8> Args;
+ int ArgCount = LLVMGetNumArgOperands(Src);
+ for (int i = 0; i < ArgCount; i++)
+ Args.push_back(CloneValue(LLVMGetOperand(Src, i)));
+ LLVMValueRef Fn = CloneValue(LLVMGetCalledValue(Src));
+ Dst = LLVMBuildCall(Builder, Fn, Args.data(), ArgCount, Name);
+ LLVMSetTailCall(Dst, LLVMIsTailCall(Src));
+ CloneAttrs(Src, Dst);
+ break;
+ }
+ case LLVMResume: {
+ Dst = LLVMBuildResume(Builder, CloneValue(LLVMGetOperand(Src, 0)));
+ break;
+ }
+ case LLVMLandingPad: {
+ // The landing pad API is a bit screwed up for historical reasons.
+ Dst = LLVMBuildLandingPad(Builder, CloneType(Src), nullptr, 0, Name);
+ unsigned NumClauses = LLVMGetNumClauses(Src);
+ for (unsigned i = 0; i < NumClauses; ++i)
+ LLVMAddClause(Dst, CloneValue(LLVMGetClause(Src, i)));
+ LLVMSetCleanup(Dst, LLVMIsCleanup(Src));
+ break;
+ }
+ case LLVMExtractValue: {
+ LLVMValueRef Agg = CloneValue(LLVMGetOperand(Src, 0));
+ if (LLVMGetNumIndices(Src) != 1)
+ report_fatal_error("Expected only one indice");
+ auto I = LLVMGetIndices(Src)[0];
+ Dst = LLVMBuildExtractValue(Builder, Agg, I, Name);
+ break;
+ }
+ case LLVMInsertValue: {
+ LLVMValueRef Agg = CloneValue(LLVMGetOperand(Src, 0));
+ LLVMValueRef V = CloneValue(LLVMGetOperand(Src, 1));
+ if (LLVMGetNumIndices(Src) != 1)
+ report_fatal_error("Expected only one indice");
+ auto I = LLVMGetIndices(Src)[0];
+ Dst = LLVMBuildInsertValue(Builder, Agg, V, I, Name);
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (Dst == nullptr) {
+ fprintf(stderr, "%d is not a supported opcode\n", Op);
+ exit(-1);
+ }
+
+ check_value_kind(Dst, LLVMInstructionValueKind);
+ return VMap[Src] = Dst;
+ }
+
+ LLVMBasicBlockRef DeclareBB(LLVMBasicBlockRef Src) {
+ // Check if this is something we already computed.
+ {
+ auto i = BBMap.find(Src);
+ if (i != BBMap.end()) {
+ return i->second;
+ }
+ }
+
+ LLVMValueRef V = LLVMBasicBlockAsValue(Src);
+ if (!LLVMValueIsBasicBlock(V) || LLVMValueAsBasicBlock(V) != Src)
+ report_fatal_error("Basic block is not a basic block");
+
+ const char *Name = LLVMGetBasicBlockName(Src);
+ const char *VName = LLVMGetValueName(V);
+ if (Name != VName)
+ report_fatal_error("Basic block name mismatch");
+
+ LLVMBasicBlockRef BB = LLVMAppendBasicBlock(Fun, Name);
+ return BBMap[Src] = BB;
+ }
+
+ LLVMBasicBlockRef CloneBB(LLVMBasicBlockRef Src) {
+ LLVMBasicBlockRef BB = DeclareBB(Src);
+
+ // Make sure ordering is correct.
+ LLVMBasicBlockRef Prev = LLVMGetPreviousBasicBlock(Src);
+ if (Prev)
+ LLVMMoveBasicBlockAfter(BB, DeclareBB(Prev));
+
+ LLVMValueRef First = LLVMGetFirstInstruction(Src);
+ LLVMValueRef Last = LLVMGetLastInstruction(Src);
+
+ if (First == nullptr) {
+ if (Last != nullptr)
+ report_fatal_error("Has no first instruction, but last one");
+ return BB;
+ }
+
+ auto Ctx = LLVMGetModuleContext(M);
+ LLVMBuilderRef Builder = LLVMCreateBuilderInContext(Ctx);
+ LLVMPositionBuilderAtEnd(Builder, BB);
+
+ LLVMValueRef Cur = First;
+ LLVMValueRef Next = nullptr;
+ while(true) {
+ CloneInstruction(Cur, Builder);
+ Next = LLVMGetNextInstruction(Cur);
+ if (Next == nullptr) {
+ if (Cur != Last)
+ report_fatal_error("Final instruction does not match Last");
+ break;
+ }
+
+ LLVMValueRef Prev = LLVMGetPreviousInstruction(Next);
+ if (Prev != Cur)
+ report_fatal_error("Next.Previous instruction is not Current");
+
+ Cur = Next;
+ }
+
+ LLVMDisposeBuilder(Builder);
+ return BB;
+ }
+
+ void CloneBBs(LLVMValueRef Src) {
+ unsigned Count = LLVMCountBasicBlocks(Src);
+ if (Count == 0)
+ return;
+
+ LLVMBasicBlockRef First = LLVMGetFirstBasicBlock(Src);
+ LLVMBasicBlockRef Last = LLVMGetLastBasicBlock(Src);
+
+ LLVMBasicBlockRef Cur = First;
+ LLVMBasicBlockRef Next = nullptr;
+ while(true) {
+ CloneBB(Cur);
+ Count--;
+ Next = LLVMGetNextBasicBlock(Cur);
+ if (Next == nullptr) {
+ if (Cur != Last)
+ report_fatal_error("Final basic block does not match Last");
+ break;
+ }
+
+ LLVMBasicBlockRef Prev = LLVMGetPreviousBasicBlock(Next);
+ if (Prev != Cur)
+ report_fatal_error("Next.Previous basic bloc is not Current");
+
+ Cur = Next;
+ }
+
+ if (Count != 0)
+ report_fatal_error("Basic block count does not match iterration");
+ }
+};
+
+static void declare_symbols(LLVMModuleRef Src, LLVMModuleRef M) {
+ LLVMValueRef Begin = LLVMGetFirstGlobal(Src);
+ LLVMValueRef End = LLVMGetLastGlobal(Src);
+
+ LLVMValueRef Cur = Begin;
+ LLVMValueRef Next = nullptr;
+ if (!Begin) {
+ if (End != nullptr)
+ report_fatal_error("Range has an end but no begining");
+ goto FunDecl;
+ }
+
+ while (true) {
+ const char *Name = LLVMGetValueName(Cur);
+ if (LLVMGetNamedGlobal(M, Name))
+ report_fatal_error("GlobalVariable already cloned");
+ LLVMAddGlobal(M, LLVMGetElementType(TypeCloner(M).Clone(Cur)), Name);
+
+ Next = LLVMGetNextGlobal(Cur);
+ if (Next == nullptr) {
+ if (Cur != End)
+ report_fatal_error("");
+ break;
+ }
+
+ LLVMValueRef Prev = LLVMGetPreviousGlobal(Next);
+ if (Prev != Cur)
+ report_fatal_error("Next.Previous global is not Current");
+
+ Cur = Next;
+ }
+
+FunDecl:
+ Begin = LLVMGetFirstFunction(Src);
+ End = LLVMGetLastFunction(Src);
+ if (!Begin) {
+ if (End != nullptr)
+ report_fatal_error("Range has an end but no begining");
+ return;
+ }
+
+ auto Ctx = LLVMGetModuleContext(M);
+
+ Cur = Begin;
+ Next = nullptr;
+ while (true) {
+ const char *Name = LLVMGetValueName(Cur);
+ if (LLVMGetNamedFunction(M, Name))
+ report_fatal_error("Function already cloned");
+ auto Ty = LLVMGetElementType(TypeCloner(M).Clone(Cur));
+ auto F = LLVMAddFunction(M, Name, Ty);
+
+ // Copy attributes
+ for (int i = LLVMAttributeFunctionIndex, c = LLVMCountParams(F);
+ i <= c; ++i) {
+ for (unsigned k = 0, e = LLVMGetLastEnumAttributeKind(); k < e; ++k) {
+ if (auto SrcA = LLVMGetEnumAttributeAtIndex(Cur, i, k)) {
+ auto Val = LLVMGetEnumAttributeValue(SrcA);
+ auto DstA = LLVMCreateEnumAttribute(Ctx, k, Val);
+ LLVMAddAttributeAtIndex(F, i, DstA);
+ }
+ }
+ }
+
+ Next = LLVMGetNextFunction(Cur);
+ if (Next == nullptr) {
+ if (Cur != End)
+ report_fatal_error("Last function does not match End");
+ break;
+ }
+
+ LLVMValueRef Prev = LLVMGetPreviousFunction(Next);
+ if (Prev != Cur)
+ report_fatal_error("Next.Previous function is not Current");
+
+ Cur = Next;
+ }
+}
+
+static void clone_symbols(LLVMModuleRef Src, LLVMModuleRef M) {
+ LLVMValueRef Begin = LLVMGetFirstGlobal(Src);
+ LLVMValueRef End = LLVMGetLastGlobal(Src);
+
+ LLVMValueRef Cur = Begin;
+ LLVMValueRef Next = nullptr;
+ if (!Begin) {
+ if (End != nullptr)
+ report_fatal_error("Range has an end but no begining");
+ goto FunClone;
+ }
+
+ while (true) {
+ const char *Name = LLVMGetValueName(Cur);
+ LLVMValueRef G = LLVMGetNamedGlobal(M, Name);
+ if (!G)
+ report_fatal_error("GlobalVariable must have been declared already");
+
+ if (auto I = LLVMGetInitializer(Cur))
+ LLVMSetInitializer(G, clone_constant(I, M));
+
+ LLVMSetGlobalConstant(G, LLVMIsGlobalConstant(Cur));
+ LLVMSetThreadLocal(G, LLVMIsThreadLocal(Cur));
+ LLVMSetExternallyInitialized(G, LLVMIsExternallyInitialized(Cur));
+ LLVMSetLinkage(G, LLVMGetLinkage(Cur));
+ LLVMSetSection(G, LLVMGetSection(Cur));
+ LLVMSetVisibility(G, LLVMGetVisibility(Cur));
+ LLVMSetUnnamedAddr(G, LLVMHasUnnamedAddr(Cur));
+ LLVMSetAlignment(G, LLVMGetAlignment(Cur));
+
+ Next = LLVMGetNextGlobal(Cur);
+ if (Next == nullptr) {
+ if (Cur != End)
+ report_fatal_error("");
+ break;
+ }
+
+ LLVMValueRef Prev = LLVMGetPreviousGlobal(Next);
+ if (Prev != Cur)
+ report_fatal_error("Next.Previous global is not Current");
+
+ Cur = Next;
+ }
+
+FunClone:
+ Begin = LLVMGetFirstFunction(Src);
+ End = LLVMGetLastFunction(Src);
+ if (!Begin) {
+ if (End != nullptr)
+ report_fatal_error("Range has an end but no begining");
+ return;
+ }
+
+ Cur = Begin;
+ Next = nullptr;
+ while (true) {
+ const char *Name = LLVMGetValueName(Cur);
+ LLVMValueRef Fun = LLVMGetNamedFunction(M, Name);
+ if (!Fun)
+ report_fatal_error("Function must have been declared already");
+
+ if (LLVMHasPersonalityFn(Cur)) {
+ const char *FName = LLVMGetValueName(LLVMGetPersonalityFn(Cur));
+ LLVMValueRef P = LLVMGetNamedFunction(M, FName);
+ if (!P)
+ report_fatal_error("Could not find personality function");
+ LLVMSetPersonalityFn(Fun, P);
+ }
+
+ FunCloner FC(Cur, Fun);
+ FC.CloneBBs(Cur);
+
+ Next = LLVMGetNextFunction(Cur);
+ if (Next == nullptr) {
+ if (Cur != End)
+ report_fatal_error("Last function does not match End");
+ break;
+ }
+
+ LLVMValueRef Prev = LLVMGetPreviousFunction(Next);
+ if (Prev != Cur)
+ report_fatal_error("Next.Previous function is not Current");
+
+ Cur = Next;
+ }
+}
+
+int llvm_echo(void) {
+ LLVMEnablePrettyStackTrace();
+
+ LLVMModuleRef Src = llvm_load_module(false, true);
+ size_t Len;
+ const char *ModuleName = LLVMGetModuleIdentifier(Src, &Len);
+ LLVMContextRef Ctx = LLVMContextCreate();
+ LLVMModuleRef M = LLVMModuleCreateWithNameInContext(ModuleName, Ctx);
+
+ // This whole switcharound is done because the C API has no way to
+ // set the source_filename
+ LLVMSetModuleIdentifier(M, "", 0);
+ LLVMGetModuleIdentifier(M, &Len);
+ if (Len != 0)
+ report_fatal_error("LLVM{Set,Get}ModuleIdentifier failed");
+ LLVMSetModuleIdentifier(M, ModuleName, strlen(ModuleName));
+
+ LLVMSetTarget(M, LLVMGetTarget(Src));
+ LLVMSetModuleDataLayout(M, LLVMGetModuleDataLayout(Src));
+ if (strcmp(LLVMGetDataLayoutStr(M), LLVMGetDataLayoutStr(Src)))
+ report_fatal_error("Inconsistent DataLayout string representation");
+
+ declare_symbols(Src, M);
+ clone_symbols(Src, M);
+ char *Str = LLVMPrintModuleToString(M);
+ fputs(Str, stdout);
+
+ LLVMDisposeMessage(Str);
+ LLVMDisposeModule(M);
+ LLVMContextDispose(Ctx);
+
+ return 0;
+}
diff --git a/tools/llvm-c-test/helpers.c b/tools/llvm-c-test/helpers.c
index 1ea8a4f669952..97fbaab6d6c33 100644
--- a/tools/llvm-c-test/helpers.c
+++ b/tools/llvm-c-test/helpers.c
@@ -18,7 +18,7 @@
#define MAX_TOKENS 512
#define MAX_LINE_LEN 1024
-void tokenize_stdin(void (*cb)(char **tokens, int ntokens)) {
+void llvm_tokenize_stdin(void (*cb)(char **tokens, int ntokens)) {
char line[MAX_LINE_LEN];
char *tokbuf[MAX_TOKENS];
diff --git a/tools/llvm-c-test/llvm-c-test.h b/tools/llvm-c-test/llvm-c-test.h
index 7929fc4d19be7..0d1ade093bc7f 100644
--- a/tools/llvm-c-test/llvm-c-test.h
+++ b/tools/llvm-c-test/llvm-c-test.h
@@ -14,30 +14,46 @@
#define LLVM_C_TEST_H
#include <stdbool.h>
+#include "llvm-c/Core.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
// helpers.c
-void tokenize_stdin(void (*cb)(char **tokens, int ntokens));
+void llvm_tokenize_stdin(void (*cb)(char **tokens, int ntokens));
// module.c
-int module_dump(bool Lazy, bool New);
-int module_list_functions(void);
-int module_list_globals(void);
+LLVMModuleRef llvm_load_module(bool Lazy, bool New);
+int llvm_module_dump(bool Lazy, bool New);
+int llvm_module_list_functions(void);
+int llvm_module_list_globals(void);
// calc.c
-int calc(void);
+int llvm_calc(void);
// disassemble.c
-int disassemble(void);
+int llvm_disassemble(void);
// metadata.c
-int add_named_metadata_operand(void);
-int set_metadata(void);
+int llvm_add_named_metadata_operand(void);
+int llvm_set_metadata(void);
// object.c
-int object_list_sections(void);
-int object_list_symbols(void);
+int llvm_object_list_sections(void);
+int llvm_object_list_symbols(void);
// targets.c
-int targets_list(void);
+int llvm_targets_list(void);
+
+// echo.c
+int llvm_echo(void);
+
+// diagnostic.c
+int llvm_test_diagnostic_handler(void);
+
+#ifdef __cplusplus
+}
+#endif /* !defined(__cplusplus) */
#endif
diff --git a/tools/llvm-c-test/main.c b/tools/llvm-c-test/main.c
index e6b6e17098b36..90d3501778516 100644
--- a/tools/llvm-c-test/main.c
+++ b/tools/llvm-c-test/main.c
@@ -13,7 +13,6 @@
#include "llvm-c-test.h"
#include "llvm-c/BitReader.h"
-#include "llvm-c/Core.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -22,20 +21,20 @@ static void print_usage(void) {
fprintf(stderr, "llvm-c-test command\n\n");
fprintf(stderr, " Commands:\n");
fprintf(stderr, " * --module-dump\n");
- fprintf(stderr, " Read bytecode from stdin - print disassembly\n\n");
+ fprintf(stderr, " Read bitcode from stdin - print disassembly\n\n");
fprintf(stderr, " * --lazy-module-dump\n");
fprintf(stderr,
- " Lazily read bytecode from stdin - print disassembly\n\n");
+ " Lazily read bitcode from stdin - print disassembly\n\n");
fprintf(stderr, " * --new-module-dump\n");
- fprintf(stderr, " Read bytecode from stdin - print disassembly\n\n");
+ fprintf(stderr, " Read bitcode from stdin - print disassembly\n\n");
fprintf(stderr, " * --lazy-new-module-dump\n");
fprintf(stderr,
- " Lazily read bytecode from stdin - print disassembly\n\n");
+ " Lazily read bitcode from stdin - print disassembly\n\n");
fprintf(stderr, " * --module-list-functions\n");
fprintf(stderr,
- " Read bytecode from stdin - list summary of functions\n\n");
+ " Read bitcode from stdin - list summary of functions\n\n");
fprintf(stderr, " * --module-list-globals\n");
- fprintf(stderr, " Read bytecode from stdin - list summary of globals\n\n");
+ fprintf(stderr, " Read bitcode from stdin - list summary of globals\n\n");
fprintf(stderr, " * --targets-list\n");
fprintf(stderr, " List available targets\n\n");
fprintf(stderr, " * --object-list-sections\n");
@@ -50,6 +49,12 @@ static void print_usage(void) {
fprintf(
stderr,
" Read lines of name, rpn from stdin - print generated module\n\n");
+ fprintf(stderr, " * --echo\n");
+ fprintf(stderr,
+ " Read bitcode file form stdin - print it back out\n\n");
+ fprintf(stderr, " * --test-diagnostic-handler\n");
+ fprintf(stderr,
+ " Read bitcode file form stdin with a diagnostic handler set\n\n");
}
int main(int argc, char **argv) {
@@ -58,31 +63,35 @@ int main(int argc, char **argv) {
LLVMInitializeCore(pr);
if (argc == 2 && !strcmp(argv[1], "--lazy-new-module-dump")) {
- return module_dump(true, true);
+ return llvm_module_dump(true, true);
} else if (argc == 2 && !strcmp(argv[1], "--new-module-dump")) {
- return module_dump(false, true);
+ return llvm_module_dump(false, true);
} else if (argc == 2 && !strcmp(argv[1], "--lazy-module-dump")) {
- return module_dump(true, false);
+ return llvm_module_dump(true, false);
} else if (argc == 2 && !strcmp(argv[1], "--module-dump")) {
- return module_dump(false, false);
+ return llvm_module_dump(false, false);
} else if (argc == 2 && !strcmp(argv[1], "--module-list-functions")) {
- return module_list_functions();
+ return llvm_module_list_functions();
} else if (argc == 2 && !strcmp(argv[1], "--module-list-globals")) {
- return module_list_globals();
+ return llvm_module_list_globals();
} else if (argc == 2 && !strcmp(argv[1], "--targets-list")) {
- return targets_list();
+ return llvm_targets_list();
} else if (argc == 2 && !strcmp(argv[1], "--object-list-sections")) {
- return object_list_sections();
+ return llvm_object_list_sections();
} else if (argc == 2 && !strcmp(argv[1], "--object-list-symbols")) {
- return object_list_symbols();
+ return llvm_object_list_symbols();
} else if (argc == 2 && !strcmp(argv[1], "--disassemble")) {
- return disassemble();
+ return llvm_disassemble();
} else if (argc == 2 && !strcmp(argv[1], "--calc")) {
- return calc();
+ return llvm_calc();
} else if (argc == 2 && !strcmp(argv[1], "--add-named-metadata-operand")) {
- return add_named_metadata_operand();
+ return llvm_add_named_metadata_operand();
} else if (argc == 2 && !strcmp(argv[1], "--set-metadata")) {
- return set_metadata();
+ return llvm_set_metadata();
+ } else if (argc == 2 && !strcmp(argv[1], "--echo")) {
+ return llvm_echo();
+ } else if (argc == 2 && !strcmp(argv[1], "--test-diagnostic-handler")) {
+ return llvm_test_diagnostic_handler();
} else {
print_usage();
}
diff --git a/tools/llvm-c-test/metadata.c b/tools/llvm-c-test/metadata.c
index b64a696c52810..89215b8ebcbd8 100644
--- a/tools/llvm-c-test/metadata.c
+++ b/tools/llvm-c-test/metadata.c
@@ -13,9 +13,8 @@
\*===----------------------------------------------------------------------===*/
#include "llvm-c-test.h"
-#include "llvm-c/Core.h"
-int add_named_metadata_operand(void) {
+int llvm_add_named_metadata_operand(void) {
LLVMModuleRef m = LLVMModuleCreateWithName("Mod");
LLVMValueRef values[] = { LLVMConstInt(LLVMInt32Type(), 0, 0) };
@@ -27,7 +26,7 @@ int add_named_metadata_operand(void) {
return 0;
}
-int set_metadata(void) {
+int llvm_set_metadata(void) {
LLVMBuilderRef b = LLVMCreateBuilder();
LLVMValueRef values[] = { LLVMConstInt(LLVMInt32Type(), 0, 0) };
diff --git a/tools/llvm-c-test/module.c b/tools/llvm-c-test/module.c
index a6c47bf5fa168..c47b55d502949 100644
--- a/tools/llvm-c-test/module.c
+++ b/tools/llvm-c-test/module.c
@@ -14,7 +14,6 @@
#include "llvm-c-test.h"
#include "llvm-c/BitReader.h"
-#include "llvm-c/Core.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -26,7 +25,7 @@ static void diagnosticHandler(LLVMDiagnosticInfoRef DI, void *C) {
exit(1);
}
-static LLVMModuleRef load_module(bool Lazy, bool New) {
+LLVMModuleRef llvm_load_module(bool Lazy, bool New) {
LLVMMemoryBufferRef MB;
LLVMModuleRef M;
char *msg = NULL;
@@ -63,8 +62,8 @@ static LLVMModuleRef load_module(bool Lazy, bool New) {
return M;
}
-int module_dump(bool Lazy, bool New) {
- LLVMModuleRef M = load_module(Lazy, New);
+int llvm_module_dump(bool Lazy, bool New) {
+ LLVMModuleRef M = llvm_load_module(Lazy, New);
char *irstr = LLVMPrintModuleToString(M);
puts(irstr);
@@ -75,8 +74,8 @@ int module_dump(bool Lazy, bool New) {
return 0;
}
-int module_list_functions(void) {
- LLVMModuleRef M = load_module(false, false);
+int llvm_module_list_functions(void) {
+ LLVMModuleRef M = llvm_load_module(false, false);
LLVMValueRef f;
f = LLVMGetFirstFunction(M);
@@ -116,8 +115,8 @@ int module_list_functions(void) {
return 0;
}
-int module_list_globals(void) {
- LLVMModuleRef M = load_module(false, false);
+int llvm_module_list_globals(void) {
+ LLVMModuleRef M = llvm_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 43521787b6099..809ad54f8721c 100644
--- a/tools/llvm-c-test/object.c
+++ b/tools/llvm-c-test/object.c
@@ -13,12 +13,11 @@
\*===----------------------------------------------------------------------===*/
#include "llvm-c-test.h"
-#include "llvm-c/Core.h"
#include "llvm-c/Object.h"
#include <stdio.h>
#include <stdlib.h>
-int object_list_sections(void) {
+int llvm_object_list_sections(void) {
LLVMMemoryBufferRef MB;
LLVMObjectFileRef O;
LLVMSectionIteratorRef sect;
@@ -50,7 +49,7 @@ int object_list_sections(void) {
return 0;
}
-int object_list_symbols(void) {
+int llvm_object_list_symbols(void) {
LLVMMemoryBufferRef MB;
LLVMObjectFileRef O;
LLVMSectionIteratorRef sect;
diff --git a/tools/llvm-c-test/targets.c b/tools/llvm-c-test/targets.c
index 252c2e02a67ab..f2a9e924a74a3 100644
--- a/tools/llvm-c-test/targets.c
+++ b/tools/llvm-c-test/targets.c
@@ -14,7 +14,7 @@
#include "llvm-c/TargetMachine.h"
#include <stdio.h>
-int targets_list(void) {
+int llvm_targets_list(void) {
LLVMTargetRef t;
LLVMInitializeAllTargetInfos();
LLVMInitializeAllTargets();
diff --git a/tools/llvm-config/BuildVariables.inc.in b/tools/llvm-config/BuildVariables.inc.in
index 345f47e91b4a3..709ea35044c6a 100644
--- a/tools/llvm-config/BuildVariables.inc.in
+++ b/tools/llvm-config/BuildVariables.inc.in
@@ -29,5 +29,8 @@
#define LLVM_BUILD_SYSTEM "@LLVM_BUILD_SYSTEM@"
#define LLVM_HAS_RTTI "@LLVM_HAS_RTTI@"
#define LLVM_ENABLE_DYLIB "@LLVM_BUILD_LLVM_DYLIB@"
+#define LLVM_LINK_DYLIB "@LLVM_LINK_LLVM_DYLIB@"
#define LLVM_ENABLE_SHARED "@LLVM_ENABLE_SHARED@"
#define LLVM_DYLIB_COMPONENTS "@LLVM_DYLIB_COMPONENTS@"
+#define LLVM_DYLIB_VERSION "@LLVM_DYLIB_VERSION@"
+#define LLVM_HAS_GLOBAL_ISEL "@LLVM_HAS_GLOBAL_ISEL@"
diff --git a/tools/llvm-config/CMakeLists.txt b/tools/llvm-config/CMakeLists.txt
index 83794bb3fddd6..d45877135ba2a 100644
--- a/tools/llvm-config/CMakeLists.txt
+++ b/tools/llvm-config/CMakeLists.txt
@@ -11,7 +11,17 @@ add_llvm_tool(llvm-config
# Compute the substitution values for various items.
get_property(LLVM_SYSTEM_LIBS_LIST TARGET LLVMSupport PROPERTY LLVM_SYSTEM_LIBS)
foreach(l ${LLVM_SYSTEM_LIBS_LIST})
- set(SYSTEM_LIBS ${SYSTEM_LIBS} "-l${l}")
+ if(MSVC)
+ set(SYSTEM_LIBS ${SYSTEM_LIBS} "${l}.lib")
+ else()
+ if (l MATCHES "^-")
+ # If it's an option, pass it without changes.
+ set(SYSTEM_LIBS ${SYSTEM_LIBS} "${l}")
+ else()
+ # Otherwise assume it's a library name we need to link with.
+ set(SYSTEM_LIBS ${SYSTEM_LIBS} "-l${l}")
+ endif()
+ endif()
endforeach()
string(REPLACE ";" " " SYSTEM_LIBS "${SYSTEM_LIBS}")
@@ -26,6 +36,8 @@ set(LLVM_CFLAGS "${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_${uppercase_CMAKE_BUILD_TYPE}}
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})
+set(LLVM_DYLIB_VERSION "${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}${LLVM_VERSION_SUFFIX}")
+set(LLVM_HAS_GLOBAL_ISEL "${LLVM_BUILD_GLOBAL_ISEL}")
# Use the C++ link flags, since they should be a superset of C link flags.
set(LLVM_LDFLAGS "${CMAKE_CXX_LINK_FLAGS}")
diff --git a/tools/llvm-config/Makefile b/tools/llvm-config/Makefile
deleted file mode 100644
index d2fe2cfa34aad..0000000000000
--- a/tools/llvm-config/Makefile
+++ /dev/null
@@ -1,95 +0,0 @@
-##===- tools/llvm-config/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-config
-USEDLIBS := LLVMSupport.a
-
-# We generate sources in the build directory, make sure it is in the include
-# paths.
-INCLUDE_BUILD_DIR := 1
-
-# This tool has no plugins, optimize startup time.
-TOOL_NO_EXPORTS := 1
-
-# Note that we have to use lazy expansion here.
-BUILDVARIABLES_SRCPATH = $(PROJ_SRC_ROOT)/tools/$(TOOLNAME)/BuildVariables.inc.in
-BUILDVARIABLES_OBJPATH = $(ObjDir)/BuildVariables.inc
-BUILT_SOURCES = $(BUILDVARIABLES_OBJPATH)
-
-include $(LEVEL)/Makefile.common
-
-# Combine preprocessor flags (except for -I) and CXX flags.
-SUB_CPPFLAGS := ${CPP.BaseFlags}
-SUB_CFLAGS := ${CPP.BaseFlags} ${C.Flags}
-SUB_CXXFLAGS := ${CPP.BaseFlags} ${CXX.Flags}
-
-# Override LIBS with TARGET's LIBS for cross compilation.
-# FIXME: Host's llvm-config is not generated. It's for target's.
-ifneq ($(TARGET_LIBS), )
- LLVM_SYSTEM_LIBS := $(TARGET_LIBS)
-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.
-SUB_LDFLAGS :=
-
-$(ObjDir)/BuildVariables.inc: $(BUILDVARIABLES_SRCPATH) Makefile $(ObjDir)/.dir
- $(Echo) "Building llvm-config BuildVariables.inc file."
- $(Verb) $(ECHO) 's/@LLVM_SRC_ROOT@/$(subst /,\/,$(LLVM_SRC_ROOT))/' \
- > temp.sed
- $(Verb) $(ECHO) 's/@LLVM_OBJ_ROOT@/$(subst /,\/,$(LLVM_OBJ_ROOT))/' \
- >> temp.sed
- $(Verb) $(ECHO) 's/@LLVM_CPPFLAGS@/$(subst /,\/,$(SUB_CPPFLAGS))/' \
- >> temp.sed
- $(Verb) $(ECHO) 's/@LLVM_CFLAGS@/$(subst /,\/,$(SUB_CFLAGS))/' \
- >> temp.sed
- $(Verb) $(ECHO) 's/@LLVM_CXXFLAGS@/$(subst /,\/,$(SUB_CXXFLAGS))/' \
- >> temp.sed
- $(Verb) $(ECHO) 's/@LLVM_LDFLAGS@/$(subst /,\/,$(SUB_LDFLAGS))/' \
- >> temp.sed
- $(Verb) $(ECHO) 's/@LLVM_BUILDMODE@/$(subst /,\/,$(BuildMode))/' \
- >> temp.sed
- $(Verb) $(ECHO) 's/@LLVM_LIBDIR_SUFFIX@//' \
- >> temp.sed
- $(Verb) $(ECHO) 's/@LLVM_SYSTEM_LIBS@/$(subst /,\/,$(LLVM_SYSTEM_LIBS))/' \
- >> 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
-
-# When cross-compiling, install a version of llvm-config that runs on the host.
-ifeq ($(LLVM_CROSS_COMPILING),1)
-install:: $(DESTDIR)$(PROJ_bindir)
- $(Echo) Installing llvm-config-host
- $(Verb) $(ProgInstall) $(BuildLLVMToolDir)/llvm-config \
- $(DESTDIR)$(PROJ_bindir)/$(program_prefix)llvm-config-host
-endif
diff --git a/tools/llvm-config/llvm-config.cpp b/tools/llvm-config/llvm-config.cpp
index 80f627936d0cf..94d426be3613e 100644
--- a/tools/llvm-config/llvm-config.cpp
+++ b/tools/llvm-config/llvm-config.cpp
@@ -29,8 +29,8 @@
#include "llvm/Support/raw_ostream.h"
#include <cstdlib>
#include <set>
-#include <vector>
#include <unordered_set>
+#include <vector>
using namespace llvm;
@@ -46,6 +46,22 @@ using namespace llvm;
// create entries for pseudo groups like x86 or all-targets.
#include "LibraryDependencies.inc"
+// LinkMode determines what libraries and flags are returned by llvm-config.
+enum LinkMode {
+ // LinkModeAuto will link with the default link mode for the installation,
+ // which is dependent on the value of LLVM_LINK_LLVM_DYLIB, and fall back
+ // to the alternative if the required libraries are not available.
+ LinkModeAuto = 0,
+
+ // LinkModeShared will link with the dynamic component libraries if they
+ // exist, and return an error otherwise.
+ LinkModeShared = 1,
+
+ // LinkModeStatic will link with the static component libraries if they
+ // exist, and return an error otherwise.
+ LinkModeStatic = 2,
+};
+
/// \brief Traverse a single component adding to the topological ordering in
/// \arg RequiredLibs.
///
@@ -56,14 +72,25 @@ using namespace llvm;
/// 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,
+static void VisitComponent(const std::string &Name,
+ const StringMap<AvailableComponent *> &ComponentMap,
+ std::set<AvailableComponent *> &VisitedComponents,
std::vector<std::string> &RequiredLibs,
bool IncludeNonInstalled, bool GetComponentNames,
- const std::string *ActiveLibDir, bool *HasMissing) {
+ const std::function<std::string(const StringRef &)>
+ *GetComponentLibraryPath,
+ std::vector<std::string> *Missing,
+ const std::string &DirSep) {
// Lookup the component.
AvailableComponent *AC = ComponentMap.lookup(Name);
+ if (!AC) {
+ errs() << "Can't find component: '" << Name << "' in the map. Available components are: ";
+ for (const auto &Component : ComponentMap) {
+ errs() << "'" << Component.first() << "' ";
+ }
+ errs() << "\n";
+ report_fatal_error("abort");
+ }
assert(AC && "Invalid component name!");
// Add to the visited table.
@@ -80,7 +107,7 @@ static void VisitComponent(const std::string& Name,
for (unsigned i = 0; AC->RequiredLibraries[i]; ++i) {
VisitComponent(AC->RequiredLibraries[i], ComponentMap, VisitedComponents,
RequiredLibs, IncludeNonInstalled, GetComponentNames,
- ActiveLibDir, HasMissing);
+ GetComponentLibraryPath, Missing, DirSep);
}
if (GetComponentNames) {
@@ -90,8 +117,13 @@ static void VisitComponent(const std::string& Name,
// Add to the required library list.
if (AC->Library) {
- if (!IncludeNonInstalled && HasMissing && !*HasMissing && ActiveLibDir) {
- *HasMissing = !sys::fs::exists(*ActiveLibDir + "/" + AC->Library);
+ if (Missing && GetComponentLibraryPath) {
+ std::string path = (*GetComponentLibraryPath)(AC->Library);
+ if (DirSep == "\\") {
+ std::replace(path.begin(), path.end(), '/', '\\');
+ }
+ if (!sys::fs::exists(path))
+ Missing->push_back(path);
}
RequiredLibs.push_back(AC->Library);
}
@@ -105,15 +137,16 @@ static void VisitComponent(const std::string& Name,
/// \param IncludeNonInstalled - Whether non-installed components should be
/// reported.
/// \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) {
+static std::vector<std::string> ComputeLibsForComponents(
+ const std::vector<StringRef> &Components, bool IncludeNonInstalled,
+ bool GetComponentNames, const std::function<std::string(const StringRef &)>
+ *GetComponentLibraryPath,
+ std::vector<std::string> *Missing, const std::string &DirSep) {
std::vector<std::string> RequiredLibs;
std::set<AvailableComponent *> VisitedComponents;
// Build a map of component names to information.
- StringMap<AvailableComponent*> ComponentMap;
+ StringMap<AvailableComponent *> ComponentMap;
for (unsigned i = 0; i != array_lengthof(AvailableComponents); ++i) {
AvailableComponent *AC = &AvailableComponents[i];
ComponentMap[AC->Name] = AC;
@@ -133,7 +166,7 @@ ComputeLibsForComponents(const std::vector<StringRef> &Components,
VisitComponent(ComponentLower, ComponentMap, VisitedComponents,
RequiredLibs, IncludeNonInstalled, GetComponentNames,
- ActiveLibDir, HasMissing);
+ GetComponentLibraryPath, Missing, DirSep);
}
// The list is now ordered with leafs first, we want the libraries to printed
@@ -176,9 +209,12 @@ 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\
+ --build-system Print the build system used to build LLVM (always cmake).\n\
--has-rtti Print whether or not LLVM was built with rtti (YES or NO).\n\
+ --has-global-isel Print whether or not LLVM was built with global-isel support (YES or NO).\n\
--shared-mode Print how the provided components can be collectively linked (`shared` or `static`).\n\
+ --link-shared Link the components as shared libraries.\n\
+ --link-static Link the component libraries statically.\n\
Typical components:\n\
all All LLVM libraries (default).\n\
engine Either a native JIT or a bitcode interpreter.\n";
@@ -189,14 +225,15 @@ Typical components:\n\
std::string GetExecutablePath(const char *Argv0) {
// This just needs to be some symbol in the binary; C++ doesn't
// allow taking the address of ::main however.
- void *P = (void*) (intptr_t) GetExecutablePath;
+ void *P = (void *)(intptr_t)GetExecutablePath;
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) {
+ const bool GetComponentNames,
+ const std::string &DirSep) {
std::vector<StringRef> DyLibComponents;
StringRef DyLibComponentsStr(LLVM_DYLIB_COMPONENTS);
@@ -214,7 +251,7 @@ std::vector<std::string> GetAllDyLibComponents(const bool IsInDevelopmentTree,
return ComputeLibsForComponents(DyLibComponents,
/*IncludeNonInstalled=*/IsInDevelopmentTree,
- GetComponentNames, nullptr, nullptr);
+ GetComponentNames, nullptr, nullptr, DirSep);
}
int main(int argc, char **argv) {
@@ -228,7 +265,7 @@ int main(int argc, char **argv) {
// that we can report the correct information when run from a development
// tree.
bool IsInDevelopmentTree;
- enum { MakefileStyle, CMakeStyle, CMakeBuildModeStyle } DevelopmentTreeLayout;
+ enum { CMakeStyle, CMakeBuildModeStyle } DevelopmentTreeLayout;
llvm::SmallString<256> CurrentPath(GetExecutablePath(argv[0]));
std::string CurrentExecPrefix;
std::string ActiveObjRoot;
@@ -243,25 +280,12 @@ int main(int argc, char **argv) {
// Create an absolute path, and pop up one directory (we expect to be inside a
// bin dir).
sys::fs::make_absolute(CurrentPath);
- CurrentExecPrefix = sys::path::parent_path(
- sys::path::parent_path(CurrentPath)).str();
+ CurrentExecPrefix =
+ sys::path::parent_path(sys::path::parent_path(CurrentPath)).str();
// Check to see if we are inside a development tree by comparing to possible
// locations (prefix style or CMake style).
- if (sys::fs::equivalent(CurrentExecPrefix,
- Twine(LLVM_OBJ_ROOT) + "/" + build_mode)) {
- IsInDevelopmentTree = true;
- DevelopmentTreeLayout = MakefileStyle;
-
- // If we are in a development tree, then check if we are in a BuildTools
- // directory. This indicates we are built for the build triple, but we
- // always want to provide information for the host triple.
- if (sys::path::filename(LLVM_OBJ_ROOT) == "BuildTools") {
- ActiveObjRoot = sys::path::parent_path(LLVM_OBJ_ROOT);
- } else {
- ActiveObjRoot = LLVM_OBJ_ROOT;
- }
- } else if (sys::fs::equivalent(CurrentExecPrefix, LLVM_OBJ_ROOT)) {
+ if (sys::fs::equivalent(CurrentExecPrefix, LLVM_OBJ_ROOT)) {
IsInDevelopmentTree = true;
DevelopmentTreeLayout = CMakeStyle;
ActiveObjRoot = LLVM_OBJ_ROOT;
@@ -272,7 +296,7 @@ int main(int argc, char **argv) {
ActiveObjRoot = LLVM_OBJ_ROOT;
} else {
IsInDevelopmentTree = false;
- DevelopmentTreeLayout = MakefileStyle; // Initialized to avoid warnings.
+ DevelopmentTreeLayout = CMakeStyle; // Initialized to avoid warnings.
}
// Compute various directory locations based on the derived location
@@ -286,12 +310,6 @@ int main(int argc, char **argv) {
// CMake organizes the products differently than a normal prefix style
// layout.
switch (DevelopmentTreeLayout) {
- case MakefileStyle:
- ActivePrefix = ActiveObjRoot;
- ActiveBinDir = ActiveObjRoot + "/" + build_mode + "/bin";
- ActiveLibDir =
- ActiveObjRoot + "/" + build_mode + "/lib" + LLVM_LIBDIR_SUFFIX;
- break;
case CMakeStyle:
ActiveBinDir = ActiveObjRoot + "/bin";
ActiveLibDir = ActiveObjRoot + "/lib" + LLVM_LIBDIR_SUFFIX;
@@ -305,8 +323,8 @@ int main(int argc, char **argv) {
}
// We need to include files from both the source and object trees.
- ActiveIncludeOption = ("-I" + ActiveIncludeDir + " " +
- "-I" + ActiveObjRoot + "/include");
+ ActiveIncludeOption =
+ ("-I" + ActiveIncludeDir + " " + "-I" + ActiveObjRoot + "/include");
} else {
ActivePrefix = CurrentExecPrefix;
ActiveIncludeDir = ActivePrefix + "/include";
@@ -323,25 +341,36 @@ int main(int argc, char **argv) {
/// 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));
+ StaticPrefix, StaticDir = "lib", DirSep = "/";
+ const Triple HostTriple(Triple::normalize(LLVM_HOST_TRIPLE));
if (HostTriple.isOSWindows()) {
SharedExt = "dll";
- SharedVersionedExt = PACKAGE_VERSION ".dll";
- StaticExt = "a";
+ SharedVersionedExt = LLVM_DYLIB_VERSION ".dll";
+ if (HostTriple.isOSCygMing()) {
+ StaticExt = "a";
+ StaticPrefix = "lib";
+ } else {
+ StaticExt = "lib";
+ DirSep = "\\";
+ std::replace(ActiveObjRoot.begin(), ActiveObjRoot.end(), '/', '\\');
+ std::replace(ActivePrefix.begin(), ActivePrefix.end(), '/', '\\');
+ std::replace(ActiveBinDir.begin(), ActiveBinDir.end(), '/', '\\');
+ std::replace(ActiveLibDir.begin(), ActiveLibDir.end(), '/', '\\');
+ std::replace(ActiveIncludeOption.begin(), ActiveIncludeOption.end(), '/',
+ '\\');
+ }
SharedDir = ActiveBinDir;
StaticDir = ActiveLibDir;
- StaticPrefix = SharedPrefix = "lib";
} else if (HostTriple.isOSDarwin()) {
SharedExt = "dylib";
- SharedVersionedExt = PACKAGE_VERSION ".dylib";
+ SharedVersionedExt = LLVM_DYLIB_VERSION ".dylib";
StaticExt = "a";
StaticDir = SharedDir = ActiveLibDir;
StaticPrefix = SharedPrefix = "lib";
} else {
// default to the unix values:
SharedExt = "so";
- SharedVersionedExt = PACKAGE_VERSION ".so";
+ SharedVersionedExt = LLVM_DYLIB_VERSION ".so";
StaticExt = "a";
StaticDir = SharedDir = ActiveLibDir;
StaticPrefix = SharedPrefix = "lib";
@@ -349,24 +378,32 @@ int main(int argc, char **argv) {
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);
+ const bool BuiltSharedLibs = std::strcmp(LLVM_ENABLE_SHARED, "ON") == 0;
bool DyLibExists = false;
const std::string DyLibName =
- (SharedPrefix + "LLVM-" + SharedVersionedExt).str();
+ (SharedPrefix + "LLVM-" + SharedVersionedExt).str();
+
+ // If LLVM_LINK_DYLIB is ON, the single shared library will be returned
+ // for "--libs", etc, if they exist. This behaviour can be overridden with
+ // --link-static or --link-shared.
+ bool LinkDyLib = (std::strcmp(LLVM_LINK_DYLIB, "ON") == 0);
if (BuiltDyLib) {
- DyLibExists = sys::fs::exists(SharedDir + "/" + DyLibName);
+ std::string path((SharedDir + DirSep + DyLibName).str());
+ if (DirSep == "\\") {
+ std::replace(path.begin(), path.end(), '/', '\\');
+ }
+ DyLibExists = sys::fs::exists(path);
+ if (!DyLibExists) {
+ // The shared library does not exist: don't error unless the user
+ // explicitly passes --link-shared.
+ LinkDyLib = false;
+ }
}
+ LinkMode LinkMode =
+ (LinkDyLib || BuiltSharedLibs) ? LinkModeShared : LinkModeAuto;
/// Get the component's library name without the lib prefix and the
/// extension. Returns true if Lib is in a recognized format.
@@ -392,28 +429,24 @@ int main(int argc, char **argv) {
};
/// 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();
- }
+ const bool Shared) {
+ std::string LibFileName;
+ if (Shared) {
+ LibFileName = (SharedPrefix + Lib + "." + SharedExt).str();
+ } else {
+ // default to static
+ LibFileName = (StaticPrefix + Lib + "." + 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();
+ auto GetComponentLibraryPath = [&](const StringRef &Name, const bool Shared) {
+ auto LibFileName = GetComponentLibraryFileName(Name, Shared);
+ if (Shared) {
+ return (SharedDir + DirSep + LibFileName).str();
} else {
- return (StaticDir + "/" + LibFileName).str();
+ return (StaticDir + DirSep + LibFileName).str();
}
};
@@ -440,7 +473,8 @@ int main(int argc, char **argv) {
} else if (Arg == "--cxxflags") {
OS << ActiveIncludeOption << ' ' << LLVM_CXXFLAGS << '\n';
} else if (Arg == "--ldflags") {
- OS << "-L" << ActiveLibDir << ' ' << LLVM_LDFLAGS << '\n';
+ OS << ((HostTriple.isWindowsMSVCEnvironment()) ? "-LIBPATH:" : "-L")
+ << ActiveLibDir << ' ' << LLVM_LDFLAGS << '\n';
} else if (Arg == "--system-libs") {
PrintSystemLibs = true;
} else if (Arg == "--libs") {
@@ -461,10 +495,14 @@ int main(int argc, char **argv) {
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::string path(
+ GetComponentLibraryPath(AvailableComponents[j].Library, false));
+ if (DirSep == "\\") {
+ std::replace(path.begin(), path.end(), '/', '\\');
+ }
+ if (DyLibExists && !sys::fs::exists(path)) {
+ Components =
+ GetAllDyLibComponents(IsInDevelopmentTree, true, DirSep);
std::sort(Components.begin(), Components.end());
break;
}
@@ -495,12 +533,18 @@ int main(int argc, char **argv) {
OS << LLVM_BUILD_SYSTEM << '\n';
} else if (Arg == "--has-rtti") {
OS << LLVM_HAS_RTTI << '\n';
+ } else if (Arg == "--has-global-isel") {
+ OS << LLVM_HAS_GLOBAL_ISEL << '\n';
} else if (Arg == "--shared-mode") {
PrintSharedMode = true;
} else if (Arg == "--obj-root") {
OS << ActivePrefix << '\n';
} else if (Arg == "--src-root") {
OS << LLVM_SRC_ROOT << '\n';
+ } else if (Arg == "--link-shared") {
+ LinkMode = LinkModeShared;
+ } else if (Arg == "--link-static") {
+ LinkMode = LinkModeStatic;
} else {
usage();
}
@@ -512,6 +556,11 @@ int main(int argc, char **argv) {
if (!HasAnyOption)
usage();
+ if (LinkMode == LinkModeShared && !DyLibExists && !BuiltSharedLibs) {
+ errs() << "llvm-config: error: " << DyLibName << " is missing\n";
+ return 1;
+ }
+
if (PrintLibs || PrintLibNames || PrintLibFiles || PrintSystemLibs ||
PrintSharedMode) {
@@ -525,16 +574,45 @@ int main(int argc, char **argv) {
Components.push_back("all");
// Construct the list of all the required libraries.
- bool HasMissing = false;
- std::vector<std::string> RequiredLibs =
- ComputeLibsForComponents(Components,
- /*IncludeNonInstalled=*/IsInDevelopmentTree,
- false, &ActiveLibDir, &HasMissing);
+ std::function<std::string(const StringRef &)>
+ GetComponentLibraryPathFunction = [&](const StringRef &Name) {
+ return GetComponentLibraryPath(Name, LinkMode == LinkModeShared);
+ };
+ std::vector<std::string> MissingLibs;
+ std::vector<std::string> RequiredLibs = ComputeLibsForComponents(
+ Components,
+ /*IncludeNonInstalled=*/IsInDevelopmentTree, false,
+ &GetComponentLibraryPathFunction, &MissingLibs, DirSep);
+ if (!MissingLibs.empty()) {
+ switch (LinkMode) {
+ case LinkModeShared:
+ if (DyLibExists && !BuiltSharedLibs)
+ break;
+ // Using component shared libraries.
+ for (auto &Lib : MissingLibs)
+ errs() << "llvm-config: error: missing: " << Lib << "\n";
+ return 1;
+ case LinkModeAuto:
+ if (DyLibExists) {
+ LinkMode = LinkModeShared;
+ break;
+ }
+ errs()
+ << "llvm-config: error: component libraries and shared library\n\n";
+ // fall through
+ case LinkModeStatic:
+ for (auto &Lib : MissingLibs)
+ errs() << "llvm-config: error: missing: " << Lib << "\n";
+ return 1;
+ }
+ } else if (LinkMode == LinkModeAuto) {
+ LinkMode = LinkModeStatic;
+ }
if (PrintSharedMode) {
std::unordered_set<std::string> FullDyLibComponents;
std::vector<std::string> DyLibComponents =
- GetAllDyLibComponents(IsInDevelopmentTree, false);
+ GetAllDyLibComponents(IsInDevelopmentTree, false, DirSep);
for (auto &Component : DyLibComponents) {
FullDyLibComponents.insert(Component);
@@ -549,7 +627,7 @@ int main(int argc, char **argv) {
}
FullDyLibComponents.clear();
- if (HasMissing && DyLibExists) {
+ if (LinkMode == LinkModeShared) {
OS << "shared\n";
return 0;
} else {
@@ -560,36 +638,39 @@ int main(int argc, char **argv) {
if (PrintLibs || PrintLibNames || PrintLibFiles) {
- auto PrintForLib = [&](const StringRef &Lib, const bool ForceShared) {
+ auto PrintForLib = [&](const StringRef &Lib) {
+ const bool Shared = LinkMode == LinkModeShared;
if (PrintLibNames) {
- OS << GetComponentLibraryFileName(Lib, ForceShared);
+ OS << GetComponentLibraryFileName(Lib, Shared);
} else if (PrintLibFiles) {
- OS << GetComponentLibraryPath(Lib, ForceShared);
+ OS << GetComponentLibraryPath(Lib, Shared);
} else if (PrintLibs) {
- // If this is a typical library name, include it using -l.
- StringRef LibName;
- if (Lib.startswith("lib")) {
+ // On Windows, output full path to library without parameters.
+ // Elsewhere, if this is a typical library name, include it using -l.
+ if (HostTriple.isWindowsMSVCEnvironment()) {
+ OS << GetComponentLibraryPath(Lib, Shared);
+ } else {
+ StringRef LibName;
if (GetComponentLibraryNameSlice(Lib, LibName)) {
+ // Extract library name (remove prefix and suffix).
OS << "-l" << LibName;
} else {
- OS << "-l:" << GetComponentLibraryFileName(Lib, ForceShared);
+ // Lib is already a library name without prefix and suffix.
+ OS << "-l" << Lib;
}
- } else {
- // Otherwise, print the full path.
- OS << GetComponentLibraryPath(Lib, ForceShared);
}
}
};
- if (HasMissing && DyLibExists) {
- PrintForLib(DyLibName, true);
+ if (LinkMode == LinkModeShared && !BuiltSharedLibs) {
+ PrintForLib(DyLibName);
} else {
for (unsigned i = 0, e = RequiredLibs.size(); i != e; ++i) {
auto Lib = RequiredLibs[i];
if (i)
OS << ' ';
- PrintForLib(Lib, false);
+ PrintForLib(Lib);
}
}
OS << '\n';
diff --git a/tools/llvm-cov/CMakeLists.txt b/tools/llvm-cov/CMakeLists.txt
index 193218a6639f2..e22828e11effe 100644
--- a/tools/llvm-cov/CMakeLists.txt
+++ b/tools/llvm-cov/CMakeLists.txt
@@ -1,4 +1,4 @@
-set(LLVM_LINK_COMPONENTS core support object profiledata)
+set(LLVM_LINK_COMPONENTS core support object coverage profiledata)
add_llvm_tool(llvm-cov
llvm-cov.cpp
@@ -8,5 +8,7 @@ add_llvm_tool(llvm-cov
CoverageReport.cpp
CoverageSummaryInfo.cpp
SourceCoverageView.cpp
+ SourceCoverageViewHTML.cpp
+ SourceCoverageViewText.cpp
TestingSupport.cpp
)
diff --git a/tools/llvm-cov/CodeCoverage.cpp b/tools/llvm-cov/CodeCoverage.cpp
index 8dc4d665f23ca..0a4d1a67d6105 100644
--- a/tools/llvm-cov/CodeCoverage.cpp
+++ b/tools/llvm-cov/CodeCoverage.cpp
@@ -13,24 +13,25 @@
//
//===----------------------------------------------------------------------===//
-#include "RenderingSupport.h"
#include "CoverageFilters.h"
#include "CoverageReport.h"
#include "CoverageViewOptions.h"
+#include "RenderingSupport.h"
#include "SourceCoverageView.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Triple.h"
-#include "llvm/ProfileData/CoverageMapping.h"
+#include "llvm/ProfileData/Coverage/CoverageMapping.h"
#include "llvm/ProfileData/InstrProfReader.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
#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/Process.h"
-#include "llvm/Support/Signals.h"
+#include "llvm/Support/Program.h"
+#include "llvm/Support/ThreadPool.h"
+#include "llvm/Support/ToolOutputFile.h"
#include <functional>
#include <system_error>
@@ -51,28 +52,47 @@ public:
/// \brief Print the error message to the error output stream.
void error(const Twine &Message, StringRef Whence = "");
+ /// \brief Record (but do not print) an error message in a thread-safe way.
+ void deferError(const Twine &Message, StringRef Whence = "");
+
+ /// \brief Record (but do not print) a warning message in a thread-safe way.
+ void deferWarning(const Twine &Message, StringRef Whence = "");
+
+ /// \brief Print (and then clear) all deferred error and warning messages.
+ void consumeDeferredMessages();
+
+ /// \brief Append a reference to a private copy of \p Path into SourceFiles.
+ void addCollectedPath(const std::string &Path);
+
/// \brief Return a memory buffer for the given source file.
ErrorOr<const MemoryBuffer &> getSourceFile(StringRef SourceFile);
/// \brief Create source views for the expansions of the view.
void attachExpansionSubViews(SourceCoverageView &View,
ArrayRef<ExpansionRecord> Expansions,
- CoverageMapping &Coverage);
+ const CoverageMapping &Coverage);
/// \brief Create the source view of a particular function.
std::unique_ptr<SourceCoverageView>
- createFunctionView(const FunctionRecord &Function, CoverageMapping &Coverage);
+ createFunctionView(const FunctionRecord &Function,
+ const CoverageMapping &Coverage);
/// \brief Create the main source view of a particular source file.
std::unique_ptr<SourceCoverageView>
- createSourceFileView(StringRef SourceFile, CoverageMapping &Coverage);
+ createSourceFileView(StringRef SourceFile, const CoverageMapping &Coverage);
- /// \brief Load the coverage mapping data. Return true if an error occured.
+ /// \brief Load the coverage mapping data. Return nullptr if an error occured.
std::unique_ptr<CoverageMapping> load();
+ /// \brief If a demangler is available, demangle all symbol names.
+ void demangleSymbols(const CoverageMapping &Coverage);
+
+ /// \brief Demangle \p Sym if possible. Otherwise, just return \p Sym.
+ StringRef getSymbolForHumans(StringRef Sym) const;
+
int run(Command Cmd, int argc, const char **argv);
- typedef std::function<int(int, const char **)> CommandLineParserType;
+ typedef llvm::function_ref<int(int, const char **)> CommandLineParserType;
int show(int argc, const char **argv,
CommandLineParserType commandLineParser);
@@ -84,25 +104,69 @@ public:
CoverageViewOptions ViewOpts;
std::string PGOFilename;
CoverageFiltersMatchAll Filters;
- std::vector<std::string> SourceFiles;
- std::vector<std::pair<std::string, std::unique_ptr<MemoryBuffer>>>
- LoadedSourceFiles;
+ std::vector<StringRef> SourceFiles;
bool CompareFilenamesOnly;
StringMap<std::string> RemappedFilenames;
std::string CoverageArch;
+
+private:
+ /// A cache for demangled symbol names.
+ StringMap<std::string> DemangledNames;
+
+ /// File paths (absolute, or otherwise) to input source files.
+ std::vector<std::string> CollectedPaths;
+
+ /// Errors and warnings which have not been printed.
+ std::mutex DeferredMessagesLock;
+ std::vector<std::string> DeferredMessages;
+
+ /// A container for input source file buffers.
+ std::mutex LoadedSourceFilesLock;
+ std::vector<std::pair<std::string, std::unique_ptr<MemoryBuffer>>>
+ LoadedSourceFiles;
};
}
-void CodeCoverageTool::error(const Twine &Message, StringRef Whence) {
- errs() << "error: ";
+static std::string getErrorString(const Twine &Message, StringRef Whence,
+ bool Warning) {
+ std::string Str = (Warning ? "warning" : "error");
+ Str += ": ";
if (!Whence.empty())
- errs() << Whence << ": ";
- errs() << Message << "\n";
+ Str += Whence.str() + ": ";
+ Str += Message.str() + "\n";
+ return Str;
+}
+
+void CodeCoverageTool::error(const Twine &Message, StringRef Whence) {
+ errs() << getErrorString(Message, Whence, false);
+}
+
+void CodeCoverageTool::deferError(const Twine &Message, StringRef Whence) {
+ std::unique_lock<std::mutex> Guard{DeferredMessagesLock};
+ DeferredMessages.emplace_back(getErrorString(Message, Whence, false));
+}
+
+void CodeCoverageTool::deferWarning(const Twine &Message, StringRef Whence) {
+ std::unique_lock<std::mutex> Guard{DeferredMessagesLock};
+ DeferredMessages.emplace_back(getErrorString(Message, Whence, true));
+}
+
+void CodeCoverageTool::consumeDeferredMessages() {
+ std::unique_lock<std::mutex> Guard{DeferredMessagesLock};
+ for (const std::string &Message : DeferredMessages)
+ ViewOpts.colored_ostream(errs(), raw_ostream::RED) << Message;
+ DeferredMessages.clear();
+}
+
+void CodeCoverageTool::addCollectedPath(const std::string &Path) {
+ CollectedPaths.push_back(Path);
+ SourceFiles.emplace_back(CollectedPaths.back());
}
ErrorOr<const MemoryBuffer &>
CodeCoverageTool::getSourceFile(StringRef SourceFile) {
// If we've remapped filenames, look up the real location for this file.
+ std::unique_lock<std::mutex> Guard{LoadedSourceFilesLock};
if (!RemappedFilenames.empty()) {
auto Loc = RemappedFilenames.find(SourceFile);
if (Loc != RemappedFilenames.end())
@@ -113,17 +177,16 @@ CodeCoverageTool::getSourceFile(StringRef SourceFile) {
return *Files.second;
auto Buffer = MemoryBuffer::getFile(SourceFile);
if (auto EC = Buffer.getError()) {
- error(EC.message(), SourceFile);
+ deferError(EC.message(), SourceFile);
return EC;
}
LoadedSourceFiles.emplace_back(SourceFile, std::move(Buffer.get()));
return *LoadedSourceFiles.back().second;
}
-void
-CodeCoverageTool::attachExpansionSubViews(SourceCoverageView &View,
- ArrayRef<ExpansionRecord> Expansions,
- CoverageMapping &Coverage) {
+void CodeCoverageTool::attachExpansionSubViews(
+ SourceCoverageView &View, ArrayRef<ExpansionRecord> Expansions,
+ const CoverageMapping &Coverage) {
if (!ViewOpts.ShowExpandedRegions)
return;
for (const auto &Expansion : Expansions) {
@@ -135,8 +198,9 @@ CodeCoverageTool::attachExpansionSubViews(SourceCoverageView &View,
continue;
auto SubViewExpansions = ExpansionCoverage.getExpansions();
- auto SubView = llvm::make_unique<SourceCoverageView>(
- SourceBuffer.get(), ViewOpts, std::move(ExpansionCoverage));
+ auto SubView =
+ SourceCoverageView::create(Expansion.Function.Name, SourceBuffer.get(),
+ ViewOpts, std::move(ExpansionCoverage));
attachExpansionSubViews(*SubView, SubViewExpansions, Coverage);
View.addExpansion(Expansion.Region, std::move(SubView));
}
@@ -144,7 +208,7 @@ CodeCoverageTool::attachExpansionSubViews(SourceCoverageView &View,
std::unique_ptr<SourceCoverageView>
CodeCoverageTool::createFunctionView(const FunctionRecord &Function,
- CoverageMapping &Coverage) {
+ const CoverageMapping &Coverage) {
auto FunctionCoverage = Coverage.getCoverageForFunction(Function);
if (FunctionCoverage.empty())
return nullptr;
@@ -153,8 +217,9 @@ CodeCoverageTool::createFunctionView(const FunctionRecord &Function,
return nullptr;
auto Expansions = FunctionCoverage.getExpansions();
- auto View = llvm::make_unique<SourceCoverageView>(
- SourceBuffer.get(), ViewOpts, std::move(FunctionCoverage));
+ auto View = SourceCoverageView::create(getSymbolForHumans(Function.Name),
+ SourceBuffer.get(), ViewOpts,
+ std::move(FunctionCoverage));
attachExpansionSubViews(*View, Expansions, Coverage);
return View;
@@ -162,7 +227,7 @@ CodeCoverageTool::createFunctionView(const FunctionRecord &Function,
std::unique_ptr<SourceCoverageView>
CodeCoverageTool::createSourceFileView(StringRef SourceFile,
- CoverageMapping &Coverage) {
+ const CoverageMapping &Coverage) {
auto SourceBuffer = getSourceFile(SourceFile);
if (!SourceBuffer)
return nullptr;
@@ -171,15 +236,16 @@ CodeCoverageTool::createSourceFileView(StringRef SourceFile,
return nullptr;
auto Expansions = FileCoverage.getExpansions();
- auto View = llvm::make_unique<SourceCoverageView>(
- SourceBuffer.get(), ViewOpts, std::move(FileCoverage));
+ auto View = SourceCoverageView::create(SourceFile, SourceBuffer.get(),
+ ViewOpts, std::move(FileCoverage));
attachExpansionSubViews(*View, Expansions, Coverage);
- for (auto Function : Coverage.getInstantiations(SourceFile)) {
+ for (const auto *Function : Coverage.getInstantiations(SourceFile)) {
auto SubViewCoverage = Coverage.getCoverageForFunction(*Function);
auto SubViewExpansions = SubViewCoverage.getExpansions();
- auto SubView = llvm::make_unique<SourceCoverageView>(
- SourceBuffer.get(), ViewOpts, std::move(SubViewCoverage));
+ auto SubView = SourceCoverageView::create(
+ getSymbolForHumans(Function->Name), SourceBuffer.get(), ViewOpts,
+ std::move(SubViewCoverage));
attachExpansionSubViews(*SubView, SubViewExpansions, Coverage);
if (SubView) {
@@ -210,10 +276,9 @@ std::unique_ptr<CoverageMapping> CodeCoverageTool::load() {
errs() << "warning: profile data may be out of date - object is newer\n";
auto CoverageOrErr = CoverageMapping::load(ObjectFilename, PGOFilename,
CoverageArch);
- if (std::error_code EC = CoverageOrErr.getError()) {
+ if (Error E = CoverageOrErr.takeError()) {
colored_ostream(errs(), raw_ostream::RED)
- << "error: Failed to load coverage: " << EC.message();
- errs() << "\n";
+ << "error: Failed to load coverage: " << toString(std::move(E)) << "\n";
return nullptr;
}
auto Coverage = std::move(CoverageOrErr.get());
@@ -237,15 +302,95 @@ std::unique_ptr<CoverageMapping> CodeCoverageTool::load() {
}
}
+ demangleSymbols(*Coverage);
+
return Coverage;
}
-int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
- // Print a stack trace if we signal out.
- sys::PrintStackTraceOnErrorSignal();
- PrettyStackTraceProgram X(argc, argv);
- llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
+void CodeCoverageTool::demangleSymbols(const CoverageMapping &Coverage) {
+ if (!ViewOpts.hasDemangler())
+ return;
+
+ // Pass function names to the demangler in a temporary file.
+ int InputFD;
+ SmallString<256> InputPath;
+ std::error_code EC =
+ sys::fs::createTemporaryFile("demangle-in", "list", InputFD, InputPath);
+ if (EC) {
+ error(InputPath, EC.message());
+ return;
+ }
+ tool_output_file InputTOF{InputPath, InputFD};
+
+ unsigned NumSymbols = 0;
+ for (const auto &Function : Coverage.getCoveredFunctions()) {
+ InputTOF.os() << Function.Name << '\n';
+ ++NumSymbols;
+ }
+ InputTOF.os().close();
+
+ // Use another temporary file to store the demangler's output.
+ int OutputFD;
+ SmallString<256> OutputPath;
+ EC = sys::fs::createTemporaryFile("demangle-out", "list", OutputFD,
+ OutputPath);
+ if (EC) {
+ error(OutputPath, EC.message());
+ return;
+ }
+ tool_output_file OutputTOF{OutputPath, OutputFD};
+ OutputTOF.os().close();
+
+ // Invoke the demangler.
+ std::vector<const char *> ArgsV;
+ for (const std::string &Arg : ViewOpts.DemanglerOpts)
+ ArgsV.push_back(Arg.c_str());
+ ArgsV.push_back(nullptr);
+ StringRef InputPathRef = InputPath.str();
+ StringRef OutputPathRef = OutputPath.str();
+ StringRef StderrRef;
+ const StringRef *Redirects[] = {&InputPathRef, &OutputPathRef, &StderrRef};
+ std::string ErrMsg;
+ int RC = sys::ExecuteAndWait(ViewOpts.DemanglerOpts[0], ArgsV.data(),
+ /*env=*/nullptr, Redirects, /*secondsToWait=*/0,
+ /*memoryLimit=*/0, &ErrMsg);
+ if (RC) {
+ error(ErrMsg, ViewOpts.DemanglerOpts[0]);
+ return;
+ }
+ // Parse the demangler's output.
+ auto BufOrError = MemoryBuffer::getFile(OutputPath);
+ if (!BufOrError) {
+ error(OutputPath, BufOrError.getError().message());
+ return;
+ }
+
+ std::unique_ptr<MemoryBuffer> DemanglerBuf = std::move(*BufOrError);
+
+ SmallVector<StringRef, 8> Symbols;
+ StringRef DemanglerData = DemanglerBuf->getBuffer();
+ DemanglerData.split(Symbols, '\n', /*MaxSplit=*/NumSymbols,
+ /*KeepEmpty=*/false);
+ if (Symbols.size() != NumSymbols) {
+ error("Demangler did not provide expected number of symbols");
+ return;
+ }
+
+ // Cache the demangled names.
+ unsigned I = 0;
+ for (const auto &Function : Coverage.getCoveredFunctions())
+ DemangledNames[Function.Name] = Symbols[I++];
+}
+
+StringRef CodeCoverageTool::getSymbolForHumans(StringRef Sym) const {
+ const auto DemangledName = DemangledNames.find(Sym);
+ if (DemangledName == DemangledNames.end())
+ return Sym;
+ return DemangledName->getValue();
+}
+
+int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
cl::opt<std::string, true> ObjectFilename(
cl::Positional, cl::Required, cl::location(this->ObjectFilename),
cl::desc("Covered executable or object file."));
@@ -264,6 +409,15 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
cl::opt<bool> DebugDump("dump", cl::Optional,
cl::desc("Show internal debug dump"));
+ cl::opt<CoverageViewOptions::OutputFormat> Format(
+ "format", cl::desc("Output format for line-based coverage reports"),
+ cl::values(clEnumValN(CoverageViewOptions::OutputFormat::Text, "text",
+ "Text output"),
+ clEnumValN(CoverageViewOptions::OutputFormat::HTML, "html",
+ "HTML output"),
+ clEnumValEnd),
+ cl::init(CoverageViewOptions::OutputFormat::Text));
+
cl::opt<bool> FilenameEquivalence(
"filename-equivalence", cl::Optional,
cl::desc("Treat source files as equivalent to paths in the coverage data "
@@ -310,14 +464,39 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
"use-color", cl::desc("Emit colored output (default=autodetect)"),
cl::init(cl::BOU_UNSET));
+ cl::list<std::string> DemanglerOpts(
+ "Xdemangler", cl::desc("<demangler-path>|<demangler-option>"));
+
auto commandLineParser = [&, this](int argc, const char **argv) -> int {
cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n");
ViewOpts.Debug = DebugDump;
CompareFilenamesOnly = FilenameEquivalence;
- ViewOpts.Colors = UseColor == cl::BOU_UNSET
- ? sys::Process::StandardOutHasColors()
- : UseColor == cl::BOU_TRUE;
+ ViewOpts.Format = Format;
+ switch (ViewOpts.Format) {
+ case CoverageViewOptions::OutputFormat::Text:
+ ViewOpts.Colors = UseColor == cl::BOU_UNSET
+ ? sys::Process::StandardOutHasColors()
+ : UseColor == cl::BOU_TRUE;
+ break;
+ case CoverageViewOptions::OutputFormat::HTML:
+ if (UseColor == cl::BOU_FALSE)
+ error("Color output cannot be disabled when generating html.");
+ ViewOpts.Colors = true;
+ break;
+ }
+
+ // If a demangler is supplied, check if it exists and register it.
+ if (DemanglerOpts.size()) {
+ auto DemanglerPathOrErr = sys::findProgramByName(DemanglerOpts[0]);
+ if (!DemanglerPathOrErr) {
+ error("Could not find the demangler!",
+ DemanglerPathOrErr.getError().message());
+ return 1;
+ }
+ DemanglerOpts[0] = *DemanglerPathOrErr;
+ ViewOpts.DemanglerOpts.swap(DemanglerOpts);
+ }
// Create the function filters
if (!NameFilters.empty() || !NameRegexFilters.empty()) {
@@ -363,7 +542,7 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
errs() << "error: " << File << ": " << EC.message();
return 1;
}
- SourceFiles.push_back(Path.str());
+ addCollectedPath(Path.str());
}
return 0;
};
@@ -406,6 +585,12 @@ int CodeCoverageTool::show(int argc, const char **argv,
cl::desc("Show function instantiations"),
cl::cat(ViewCategory));
+ cl::opt<std::string> ShowOutputDirectory(
+ "output-dir", cl::init(""),
+ cl::desc("Directory in which coverage information is written out"));
+ cl::alias ShowOutputDirectoryA("o", cl::desc("Alias for --output-dir"),
+ cl::aliasopt(ShowOutputDirectory));
+
auto Err = commandLineParser(argc, argv);
if (Err)
return Err;
@@ -417,30 +602,46 @@ int CodeCoverageTool::show(int argc, const char **argv,
ViewOpts.ShowLineStatsOrRegionMarkers = ShowBestLineRegionsCounts;
ViewOpts.ShowExpandedRegions = ShowExpansions;
ViewOpts.ShowFunctionInstantiations = ShowInstantiations;
+ ViewOpts.ShowOutputDirectory = ShowOutputDirectory;
+
+ if (ViewOpts.hasOutputDirectory()) {
+ if (auto E = sys::fs::create_directories(ViewOpts.ShowOutputDirectory)) {
+ error("Could not create output directory!", E.message());
+ return 1;
+ }
+ }
auto Coverage = load();
if (!Coverage)
return 1;
+ auto Printer = CoveragePrinter::create(ViewOpts);
+
if (!Filters.empty()) {
- // Show functions
+ auto OSOrErr = Printer->createViewFile("functions", /*InToplevel=*/true);
+ if (Error E = OSOrErr.takeError()) {
+ error("Could not create view file!", toString(std::move(E)));
+ return 1;
+ }
+ auto OS = std::move(OSOrErr.get());
+
+ // Show functions.
for (const auto &Function : Coverage->getCoveredFunctions()) {
if (!Filters.matches(Function))
continue;
auto mainView = createFunctionView(Function, *Coverage);
if (!mainView) {
- ViewOpts.colored_ostream(outs(), raw_ostream::RED)
- << "warning: Could not read coverage for '" << Function.Name;
- outs() << "\n";
+ ViewOpts.colored_ostream(errs(), raw_ostream::RED)
+ << "warning: Could not read coverage for '" << Function.Name << "'."
+ << "\n";
continue;
}
- ViewOpts.colored_ostream(outs(), raw_ostream::CYAN) << Function.Name
- << ":";
- outs() << "\n";
- mainView->render(outs(), /*WholeFile=*/false);
- outs() << "\n";
+
+ mainView->print(*OS.get(), /*WholeFile=*/false, /*ShowSourceName=*/true);
}
+
+ Printer->closeViewFile(std::move(OS));
return 0;
}
@@ -448,28 +649,49 @@ int CodeCoverageTool::show(int argc, const char **argv,
bool ShowFilenames = SourceFiles.size() != 1;
if (SourceFiles.empty())
- // Get the source files from the function coverage mapping
+ // Get the source files from the function coverage mapping.
for (StringRef Filename : Coverage->getUniqueSourceFiles())
SourceFiles.push_back(Filename);
- for (const auto &SourceFile : SourceFiles) {
- auto mainView = createSourceFileView(SourceFile, *Coverage);
- if (!mainView) {
- ViewOpts.colored_ostream(outs(), raw_ostream::RED)
- << "warning: The file '" << SourceFile << "' isn't covered.";
- outs() << "\n";
- continue;
+ // Create an index out of the source files.
+ if (ViewOpts.hasOutputDirectory()) {
+ if (Error E = Printer->createIndexFile(SourceFiles)) {
+ error("Could not create index file!", toString(std::move(E)));
+ return 1;
}
+ }
- if (ShowFilenames) {
- ViewOpts.colored_ostream(outs(), raw_ostream::CYAN) << SourceFile << ":";
- outs() << "\n";
- }
- mainView->render(outs(), /*Wholefile=*/true);
- if (SourceFiles.size() > 1)
- outs() << "\n";
+ // In -output-dir mode, it's safe to use multiple threads to print files.
+ unsigned ThreadCount = 1;
+ if (ViewOpts.hasOutputDirectory())
+ ThreadCount = std::thread::hardware_concurrency();
+ ThreadPool Pool(ThreadCount);
+
+ for (StringRef SourceFile : SourceFiles) {
+ Pool.async([this, SourceFile, &Coverage, &Printer, ShowFilenames] {
+ auto View = createSourceFileView(SourceFile, *Coverage);
+ if (!View) {
+ deferWarning("The file '" + SourceFile.str() + "' isn't covered.");
+ return;
+ }
+
+ auto OSOrErr = Printer->createViewFile(SourceFile, /*InToplevel=*/false);
+ if (Error E = OSOrErr.takeError()) {
+ deferError("Could not create view file!", toString(std::move(E)));
+ return;
+ }
+ auto OS = std::move(OSOrErr.get());
+
+ View->print(*OS.get(), /*Wholefile=*/true,
+ /*ShowSourceName=*/ShowFilenames);
+ Printer->closeViewFile(std::move(OS));
+ });
}
+ Pool.wait();
+
+ consumeDeferredMessages();
+
return 0;
}
@@ -479,6 +701,9 @@ int CodeCoverageTool::report(int argc, const char **argv,
if (Err)
return Err;
+ if (ViewOpts.Format == CoverageViewOptions::OutputFormat::HTML)
+ error("HTML output for summary reports is not yet supported.");
+
auto Coverage = load();
if (!Coverage)
return 1;
diff --git a/tools/llvm-cov/CoverageFilters.h b/tools/llvm-cov/CoverageFilters.h
index dc5dc98807b1a..756c4b47872c1 100644
--- a/tools/llvm-cov/CoverageFilters.h
+++ b/tools/llvm-cov/CoverageFilters.h
@@ -14,7 +14,7 @@
#ifndef LLVM_COV_COVERAGEFILTERS_H
#define LLVM_COV_COVERAGEFILTERS_H
-#include "llvm/ProfileData/CoverageMapping.h"
+#include "llvm/ProfileData/Coverage/CoverageMapping.h"
#include <memory>
#include <vector>
diff --git a/tools/llvm-cov/CoverageReport.cpp b/tools/llvm-cov/CoverageReport.cpp
index ed01a2e154f1b..10e53b3f1f723 100644
--- a/tools/llvm-cov/CoverageReport.cpp
+++ b/tools/llvm-cov/CoverageReport.cpp
@@ -171,7 +171,7 @@ void CoverageReport::render(const FunctionCoverageSummary &Function,
OS << "\n";
}
-void CoverageReport::renderFunctionReports(ArrayRef<std::string> Files,
+void CoverageReport::renderFunctionReports(ArrayRef<StringRef> Files,
raw_ostream &OS) {
adjustColumnWidths(Coverage.get());
bool isFirst = true;
diff --git a/tools/llvm-cov/CoverageReport.h b/tools/llvm-cov/CoverageReport.h
index 7ec3df90b8f92..bb3d734b52a54 100644
--- a/tools/llvm-cov/CoverageReport.h
+++ b/tools/llvm-cov/CoverageReport.h
@@ -32,7 +32,7 @@ public:
std::unique_ptr<coverage::CoverageMapping> Coverage)
: Options(Options), Coverage(std::move(Coverage)) {}
- void renderFunctionReports(ArrayRef<std::string> Files, raw_ostream &OS);
+ void renderFunctionReports(ArrayRef<StringRef> Files, raw_ostream &OS);
void renderFileReports(raw_ostream &OS);
};
diff --git a/tools/llvm-cov/CoverageSummaryInfo.h b/tools/llvm-cov/CoverageSummaryInfo.h
index c393b00d32a43..822742b635e96 100644
--- a/tools/llvm-cov/CoverageSummaryInfo.h
+++ b/tools/llvm-cov/CoverageSummaryInfo.h
@@ -15,7 +15,7 @@
#ifndef LLVM_COV_COVERAGESUMMARYINFO_H
#define LLVM_COV_COVERAGESUMMARYINFO_H
-#include "llvm/ProfileData/CoverageMapping.h"
+#include "llvm/ProfileData/Coverage/CoverageMapping.h"
#include "llvm/Support/raw_ostream.h"
namespace llvm {
@@ -47,6 +47,8 @@ struct RegionCoverageInfo {
bool isFullyCovered() const { return Covered == NumRegions; }
double getPercentCovered() const {
+ if (NumRegions == 0)
+ return 0.0;
return double(Covered) / double(NumRegions) * 100.0;
}
};
@@ -83,6 +85,8 @@ struct LineCoverageInfo {
bool isFullyCovered() const { return Covered == (NumLines - NonCodeLines); }
double getPercentCovered() const {
+ if (NumLines - NonCodeLines == 0)
+ return 0.0;
return double(Covered) / double(NumLines - NonCodeLines) * 100.0;
}
};
@@ -109,6 +113,8 @@ struct FunctionCoverageInfo {
bool isFullyCovered() const { return Executed == NumFunctions; }
double getPercentCovered() const {
+ if (NumFunctions == 0)
+ return 0.0;
return double(Executed) / double(NumFunctions) * 100.0;
}
};
diff --git a/tools/llvm-cov/CoverageViewOptions.h b/tools/llvm-cov/CoverageViewOptions.h
index 1208fad791766..350c264a28764 100644
--- a/tools/llvm-cov/CoverageViewOptions.h
+++ b/tools/llvm-cov/CoverageViewOptions.h
@@ -11,11 +11,17 @@
#define LLVM_COV_COVERAGEVIEWOPTIONS_H
#include "RenderingSupport.h"
+#include <vector>
namespace llvm {
/// \brief The options for displaying the code coverage information.
struct CoverageViewOptions {
+ enum class OutputFormat {
+ Text,
+ HTML
+ };
+
bool Debug;
bool Colors;
bool ShowLineNumbers;
@@ -25,12 +31,21 @@ struct CoverageViewOptions {
bool ShowExpandedRegions;
bool ShowFunctionInstantiations;
bool ShowFullFilenames;
+ OutputFormat Format;
+ std::string ShowOutputDirectory;
+ std::vector<std::string> DemanglerOpts;
/// \brief Change the output's stream color if the colors are enabled.
ColoredRawOstream colored_ostream(raw_ostream &OS,
raw_ostream::Colors Color) const {
return llvm::colored_ostream(OS, Color, Colors);
}
+
+ /// \brief Check if an output directory has been specified.
+ bool hasOutputDirectory() const { return !ShowOutputDirectory.empty(); }
+
+ /// \brief Check if a demangler has been specified.
+ bool hasDemangler() const { return !DemanglerOpts.empty(); }
};
}
diff --git a/tools/llvm-cov/LLVMBuild.txt b/tools/llvm-cov/LLVMBuild.txt
index d6eb74de0d4bf..33f51fb2ed5a9 100644
--- a/tools/llvm-cov/LLVMBuild.txt
+++ b/tools/llvm-cov/LLVMBuild.txt
@@ -19,4 +19,4 @@
type = Tool
name = llvm-cov
parent = Tools
-required_libraries = ProfileData Support Instrumentation
+required_libraries = Coverage Support Instrumentation
diff --git a/tools/llvm-cov/Makefile b/tools/llvm-cov/Makefile
deleted file mode 100644
index 6e32b4d233d7e..0000000000000
--- a/tools/llvm-cov/Makefile
+++ /dev/null
@@ -1,17 +0,0 @@
-##===- tools/llvm-cov/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-cov
-LINK_COMPONENTS := core support profiledata object
-
-# This tool has no plugins, optimize startup time.
-TOOL_NO_EXPORTS := 1
-
-include $(LEVEL)/Makefile.common
diff --git a/tools/llvm-cov/RenderingSupport.h b/tools/llvm-cov/RenderingSupport.h
index 3ef155d3fd4bb..aa70fbc23e3c0 100644
--- a/tools/llvm-cov/RenderingSupport.h
+++ b/tools/llvm-cov/RenderingSupport.h
@@ -55,6 +55,7 @@ inline ColoredRawOstream colored_ostream(raw_ostream &OS,
OS.changeColor(Color, Bold, BG);
return ColoredRawOstream(OS, IsColorUsed);
}
-}
+
+} // namespace llvm
#endif // LLVM_COV_RENDERINGSUPPORT_H
diff --git a/tools/llvm-cov/SourceCoverageView.cpp b/tools/llvm-cov/SourceCoverageView.cpp
index 58c8a67952946..baf7c148bb800 100644
--- a/tools/llvm-cov/SourceCoverageView.cpp
+++ b/tools/llvm-cov/SourceCoverageView.cpp
@@ -6,80 +6,82 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-//
-// This class implements rendering for code coverage of source code.
-//
+///
+/// \file This class implements rendering for code coverage of source code.
+///
//===----------------------------------------------------------------------===//
#include "SourceCoverageView.h"
-#include "llvm/ADT/Optional.h"
+#include "SourceCoverageViewHTML.h"
+#include "SourceCoverageViewText.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/FileSystem.h"
#include "llvm/Support/LineIterator.h"
+#include "llvm/Support/Path.h"
using namespace llvm;
-void SourceCoverageView::renderLine(
- raw_ostream &OS, StringRef Line, int64_t LineNumber,
- const coverage::CoverageSegment *WrappedSegment,
- ArrayRef<const coverage::CoverageSegment *> Segments,
- unsigned ExpansionCol) {
- Optional<raw_ostream::Colors> Highlight;
- SmallVector<std::pair<unsigned, unsigned>, 2> HighlightedRanges;
-
- // The first segment overlaps from a previous line, so we treat it specially.
- if (WrappedSegment && WrappedSegment->HasCount && WrappedSegment->Count == 0)
- Highlight = raw_ostream::RED;
-
- // Output each segment of the line, possibly highlighted.
- unsigned Col = 1;
- for (const auto *S : Segments) {
- unsigned End = std::min(S->Col, static_cast<unsigned>(Line.size()) + 1);
- colored_ostream(OS, Highlight ? *Highlight : raw_ostream::SAVEDCOLOR,
- Options.Colors && Highlight, /*Bold=*/false, /*BG=*/true)
- << Line.substr(Col - 1, End - Col);
- if (Options.Debug && Highlight)
- HighlightedRanges.push_back(std::make_pair(Col, End));
- Col = End;
- if (Col == ExpansionCol)
- Highlight = raw_ostream::CYAN;
- else if (S->HasCount && S->Count == 0)
- Highlight = raw_ostream::RED;
- else
- Highlight = None;
- }
+void CoveragePrinter::StreamDestructor::operator()(raw_ostream *OS) const {
+ if (OS == &outs())
+ return;
+ delete OS;
+}
- // Show the rest of the line
- colored_ostream(OS, Highlight ? *Highlight : raw_ostream::SAVEDCOLOR,
- Options.Colors && Highlight, /*Bold=*/false, /*BG=*/true)
- << Line.substr(Col - 1, Line.size() - Col + 1);
- OS << "\n";
-
- if (Options.Debug) {
- for (const auto &Range : HighlightedRanges)
- errs() << "Highlighted line " << LineNumber << ", " << Range.first
- << " -> " << Range.second << "\n";
- if (Highlight)
- errs() << "Highlighted line " << LineNumber << ", " << Col << " -> ?\n";
- }
+std::string CoveragePrinter::getOutputPath(StringRef Path, StringRef Extension,
+ bool InToplevel, bool Relative) {
+ assert(Extension.size() && "The file extension may not be empty");
+
+ SmallString<256> FullPath;
+
+ if (!Relative)
+ FullPath.append(Opts.ShowOutputDirectory);
+
+ if (!InToplevel)
+ sys::path::append(FullPath, getCoverageDir());
+
+ SmallString<256> ParentPath = sys::path::parent_path(Path);
+ sys::path::remove_dots(ParentPath, /*remove_dot_dots=*/true);
+ sys::path::append(FullPath, sys::path::relative_path(ParentPath));
+
+ auto PathFilename = (sys::path::filename(Path) + "." + Extension).str();
+ sys::path::append(FullPath, PathFilename);
+
+ return FullPath.str();
}
-void SourceCoverageView::renderIndent(raw_ostream &OS, unsigned Level) {
- for (unsigned I = 0; I < Level; ++I)
- OS << " |";
+Expected<CoveragePrinter::OwnedStream>
+CoveragePrinter::createOutputStream(StringRef Path, StringRef Extension,
+ bool InToplevel) {
+ if (!Opts.hasOutputDirectory())
+ return OwnedStream(&outs());
+
+ std::string FullPath = getOutputPath(Path, Extension, InToplevel, false);
+
+ auto ParentDir = sys::path::parent_path(FullPath);
+ if (auto E = sys::fs::create_directories(ParentDir))
+ return errorCodeToError(E);
+
+ std::error_code E;
+ raw_ostream *RawStream = new raw_fd_ostream(FullPath, E, sys::fs::F_RW);
+ auto OS = CoveragePrinter::OwnedStream(RawStream);
+ if (E)
+ return errorCodeToError(E);
+ return std::move(OS);
}
-void SourceCoverageView::renderViewDivider(unsigned Level, unsigned Length,
- raw_ostream &OS) {
- assert(Level != 0 && "Cannot render divider at top level");
- renderIndent(OS, Level - 1);
- OS.indent(2);
- for (unsigned I = 0; I < Length; ++I)
- OS << "-";
+std::unique_ptr<CoveragePrinter>
+CoveragePrinter::create(const CoverageViewOptions &Opts) {
+ switch (Opts.Format) {
+ case CoverageViewOptions::OutputFormat::Text:
+ return llvm::make_unique<CoveragePrinterText>(Opts);
+ case CoverageViewOptions::OutputFormat::HTML:
+ return llvm::make_unique<CoveragePrinterHTML>(Opts);
+ }
+ llvm_unreachable("Unknown coverage output format!");
}
-/// Format a count using engineering notation with 3 significant digits.
-static std::string formatCount(uint64_t N) {
+std::string SourceCoverageView::formatCount(uint64_t N) {
std::string Number = utostr(N);
int Len = Number.size();
if (Len <= 3)
@@ -94,63 +96,49 @@ static std::string formatCount(uint64_t N) {
return Result;
}
-void
-SourceCoverageView::renderLineCoverageColumn(raw_ostream &OS,
- const LineCoverageInfo &Line) {
- if (!Line.isMapped()) {
- OS.indent(LineCoverageColumnWidth) << '|';
- return;
- }
- std::string C = formatCount(Line.ExecutionCount);
- OS.indent(LineCoverageColumnWidth - C.size());
- colored_ostream(OS, raw_ostream::MAGENTA,
- Line.hasMultipleRegions() && Options.Colors)
- << C;
- OS << '|';
+bool SourceCoverageView::shouldRenderRegionMarkers(
+ bool LineHasMultipleRegions) const {
+ return getOptions().ShowRegionMarkers &&
+ (!getOptions().ShowLineStatsOrRegionMarkers || LineHasMultipleRegions);
}
-void SourceCoverageView::renderLineNumberColumn(raw_ostream &OS,
- unsigned LineNo) {
- SmallString<32> Buffer;
- raw_svector_ostream BufferOS(Buffer);
- BufferOS << LineNo;
- auto Str = BufferOS.str();
- // Trim and align to the right
- Str = Str.substr(0, std::min(Str.size(), (size_t)LineNumberColumnWidth));
- OS.indent(LineNumberColumnWidth - Str.size()) << Str << '|';
+bool SourceCoverageView::hasSubViews() const {
+ return !ExpansionSubViews.empty() || !InstantiationSubViews.empty();
}
-void SourceCoverageView::renderRegionMarkers(
- raw_ostream &OS, ArrayRef<const coverage::CoverageSegment *> Segments) {
- unsigned PrevColumn = 1;
- for (const auto *S : Segments) {
- if (!S->IsRegionEntry)
- continue;
- // Skip to the new region
- if (S->Col > PrevColumn)
- OS.indent(S->Col - PrevColumn);
- PrevColumn = S->Col + 1;
- std::string C = formatCount(S->Count);
- PrevColumn += C.size();
- OS << '^' << C;
+std::unique_ptr<SourceCoverageView>
+SourceCoverageView::create(StringRef SourceName, const MemoryBuffer &File,
+ const CoverageViewOptions &Options,
+ coverage::CoverageData &&CoverageInfo) {
+ switch (Options.Format) {
+ case CoverageViewOptions::OutputFormat::Text:
+ return llvm::make_unique<SourceCoverageViewText>(SourceName, File, Options,
+ std::move(CoverageInfo));
+ case CoverageViewOptions::OutputFormat::HTML:
+ return llvm::make_unique<SourceCoverageViewHTML>(SourceName, File, Options,
+ std::move(CoverageInfo));
}
- OS << "\n";
+ llvm_unreachable("Unknown coverage output format!");
+}
+
+void SourceCoverageView::addExpansion(
+ const coverage::CounterMappingRegion &Region,
+ std::unique_ptr<SourceCoverageView> View) {
+ ExpansionSubViews.emplace_back(Region, std::move(View));
+}
- if (Options.Debug)
- for (const auto *S : Segments)
- errs() << "Marker at " << S->Line << ":" << S->Col << " = "
- << formatCount(S->Count) << (S->IsRegionEntry ? "\n" : " (pop)\n");
+void SourceCoverageView::addInstantiation(
+ StringRef FunctionName, unsigned Line,
+ std::unique_ptr<SourceCoverageView> View) {
+ InstantiationSubViews.emplace_back(FunctionName, Line, std::move(View));
}
-void SourceCoverageView::render(raw_ostream &OS, bool WholeFile,
- unsigned IndentLevel) {
- // The width of the leading columns
- unsigned CombinedColumnWidth =
- (Options.ShowLineStats ? LineCoverageColumnWidth + 1 : 0) +
- (Options.ShowLineNumbers ? LineNumberColumnWidth + 1 : 0);
- // The width of the line that is used to divide between the view and the
- // subviews.
- unsigned DividerWidth = CombinedColumnWidth + 4;
+void SourceCoverageView::print(raw_ostream &OS, bool WholeFile,
+ bool ShowSourceName, unsigned ViewDepth) {
+ if (ShowSourceName)
+ renderSourceName(OS);
+
+ renderViewHeader(OS);
// We need the expansions and instantiations sorted so we can go through them
// while we iterate lines.
@@ -186,79 +174,60 @@ void SourceCoverageView::render(raw_ostream &OS, bool WholeFile,
LineSegments.push_back(&*NextSegment++);
// Calculate a count to be for the line as a whole.
- LineCoverageInfo LineCount;
+ LineCoverageStats LineCount;
if (WrappedSegment && WrappedSegment->HasCount)
LineCount.addRegionCount(WrappedSegment->Count);
for (const auto *S : LineSegments)
if (S->HasCount && S->IsRegionEntry)
- LineCount.addRegionStartCount(S->Count);
+ LineCount.addRegionStartCount(S->Count);
- // Render the line prefix.
- renderIndent(OS, IndentLevel);
- if (Options.ShowLineStats)
+ renderLinePrefix(OS, ViewDepth);
+ if (getOptions().ShowLineStats)
renderLineCoverageColumn(OS, LineCount);
- if (Options.ShowLineNumbers)
+ if (getOptions().ShowLineNumbers)
renderLineNumberColumn(OS, LI.line_number());
// If there are expansion subviews, we want to highlight the first one.
unsigned ExpansionColumn = 0;
if (NextESV != EndESV && NextESV->getLine() == LI.line_number() &&
- Options.Colors)
+ getOptions().Colors)
ExpansionColumn = NextESV->getStartCol();
// Display the source code for the current line.
- renderLine(OS, *LI, LI.line_number(), WrappedSegment, LineSegments,
- ExpansionColumn);
+ renderLine(OS, {*LI, LI.line_number()}, WrappedSegment, LineSegments,
+ ExpansionColumn, ViewDepth);
// Show the region markers.
- if (Options.ShowRegionMarkers && (!Options.ShowLineStatsOrRegionMarkers ||
- LineCount.hasMultipleRegions()) &&
- !LineSegments.empty()) {
- renderIndent(OS, IndentLevel);
- OS.indent(CombinedColumnWidth);
- renderRegionMarkers(OS, LineSegments);
- }
+ if (shouldRenderRegionMarkers(LineCount.hasMultipleRegions()))
+ renderRegionMarkers(OS, LineSegments, ViewDepth);
// Show the expansions and instantiations for this line.
- unsigned NestedIndent = IndentLevel + 1;
bool RenderedSubView = false;
for (; NextESV != EndESV && NextESV->getLine() == LI.line_number();
++NextESV) {
- renderViewDivider(NestedIndent, DividerWidth, OS);
- OS << "\n";
+ renderViewDivider(OS, ViewDepth + 1);
+
+ // Re-render the current line and highlight the expansion range for
+ // this subview.
if (RenderedSubView) {
- // Re-render the current line and highlight the expansion range for
- // this subview.
ExpansionColumn = NextESV->getStartCol();
- renderIndent(OS, IndentLevel);
- OS.indent(CombinedColumnWidth + (IndentLevel == 0 ? 0 : 1));
- renderLine(OS, *LI, LI.line_number(), WrappedSegment, LineSegments,
- ExpansionColumn);
- renderViewDivider(NestedIndent, DividerWidth, OS);
- OS << "\n";
+ renderExpansionSite(OS, {*LI, LI.line_number()}, WrappedSegment,
+ LineSegments, ExpansionColumn, ViewDepth);
+ renderViewDivider(OS, ViewDepth + 1);
}
- // Render the child subview
- if (Options.Debug)
- errs() << "Expansion at line " << NextESV->getLine() << ", "
- << NextESV->getStartCol() << " -> " << NextESV->getEndCol()
- << "\n";
- NextESV->View->render(OS, false, NestedIndent);
+
+ renderExpansionView(OS, *NextESV, ViewDepth + 1);
RenderedSubView = true;
}
for (; NextISV != EndISV && NextISV->Line == LI.line_number(); ++NextISV) {
- renderViewDivider(NestedIndent, DividerWidth, OS);
- OS << "\n";
- renderIndent(OS, NestedIndent);
- OS << ' ';
- Options.colored_ostream(OS, raw_ostream::CYAN) << NextISV->FunctionName
- << ":";
- OS << "\n";
- NextISV->View->render(OS, false, NestedIndent);
+ renderViewDivider(OS, ViewDepth + 1);
+ renderInstantiationView(OS, *NextISV, ViewDepth + 1);
RenderedSubView = true;
}
- if (RenderedSubView) {
- renderViewDivider(NestedIndent, DividerWidth, OS);
- OS << "\n";
- }
+ if (RenderedSubView)
+ renderViewDivider(OS, ViewDepth + 1);
+ renderLineSuffix(OS, ViewDepth);
}
+
+ renderViewFooter(OS);
}
diff --git a/tools/llvm-cov/SourceCoverageView.h b/tools/llvm-cov/SourceCoverageView.h
index 9e6fe5f350012..feef959f86bf1 100644
--- a/tools/llvm-cov/SourceCoverageView.h
+++ b/tools/llvm-cov/SourceCoverageView.h
@@ -6,16 +6,16 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-//
-// This class implements rendering for code coverage of source code.
-//
+///
+/// \file This class implements rendering for code coverage of source code.
+///
//===----------------------------------------------------------------------===//
#ifndef LLVM_COV_SOURCECOVERAGEVIEW_H
#define LLVM_COV_SOURCECOVERAGEVIEW_H
#include "CoverageViewOptions.h"
-#include "llvm/ProfileData/CoverageMapping.h"
+#include "llvm/ProfileData/Coverage/CoverageMapping.h"
#include "llvm/Support/MemoryBuffer.h"
#include <vector>
@@ -23,7 +23,7 @@ namespace llvm {
class SourceCoverageView;
-/// \brief A view that represents a macro or include expansion
+/// \brief A view that represents a macro or include expansion.
struct ExpansionView {
coverage::CounterMappingRegion Region;
std::unique_ptr<SourceCoverageView> View;
@@ -48,7 +48,7 @@ struct ExpansionView {
}
};
-/// \brief A view that represents a function instantiation
+/// \brief A view that represents a function instantiation.
struct InstantiationView {
StringRef FunctionName;
unsigned Line;
@@ -73,87 +73,211 @@ struct InstantiationView {
}
};
-/// \brief A code coverage view of a specific source file.
-/// It can have embedded coverage views.
-class SourceCoverageView {
-private:
- /// \brief Coverage information for a single line.
- struct LineCoverageInfo {
- uint64_t ExecutionCount;
- unsigned RegionCount;
- bool Mapped;
+/// \brief Coverage statistics for a single line.
+struct LineCoverageStats {
+ uint64_t ExecutionCount;
+ unsigned RegionCount;
+ bool Mapped;
+
+ LineCoverageStats() : ExecutionCount(0), RegionCount(0), Mapped(false) {}
- LineCoverageInfo() : ExecutionCount(0), RegionCount(0), Mapped(false) {}
+ bool isMapped() const { return Mapped; }
- bool isMapped() const { return Mapped; }
+ bool hasMultipleRegions() const { return RegionCount > 1; }
- bool hasMultipleRegions() const { return RegionCount > 1; }
+ void addRegionStartCount(uint64_t Count) {
+ // The max of all region starts is the most interesting value.
+ addRegionCount(RegionCount ? std::max(ExecutionCount, Count) : Count);
+ ++RegionCount;
+ }
+
+ void addRegionCount(uint64_t Count) {
+ Mapped = true;
+ ExecutionCount = Count;
+ }
+};
- void addRegionStartCount(uint64_t Count) {
- // The max of all region starts is the most interesting value.
- addRegionCount(RegionCount ? std::max(ExecutionCount, Count) : Count);
- ++RegionCount;
- }
+/// \brief A file manager that handles format-aware file creation.
+class CoveragePrinter {
+ const CoverageViewOptions &Opts;
- void addRegionCount(uint64_t Count) {
- Mapped = true;
- ExecutionCount = Count;
- }
+public:
+ struct StreamDestructor {
+ void operator()(raw_ostream *OS) const;
};
+ using OwnedStream = std::unique_ptr<raw_ostream, StreamDestructor>;
+
+protected:
+ CoveragePrinter(const CoverageViewOptions &Opts) : Opts(Opts) {}
+
+ /// \brief Return `OutputDir/ToplevelDir/Path.Extension`. If \p InToplevel is
+ /// false, skip the ToplevelDir component. If \p Relative is false, skip the
+ /// OutputDir component.
+ std::string getOutputPath(StringRef Path, StringRef Extension,
+ bool InToplevel, bool Relative = true);
+
+ /// \brief If directory output is enabled, create a file in that directory
+ /// at the path given by getOutputPath(). Otherwise, return stdout.
+ Expected<OwnedStream> createOutputStream(StringRef Path, StringRef Extension,
+ bool InToplevel);
+
+ /// \brief Return the sub-directory name for file coverage reports.
+ static StringRef getCoverageDir() { return "coverage"; }
+
+public:
+ static std::unique_ptr<CoveragePrinter>
+ create(const CoverageViewOptions &Opts);
+
+ virtual ~CoveragePrinter() {}
+
+ /// @name File Creation Interface
+ /// @{
+
+ /// \brief Create a file to print a coverage view into.
+ virtual Expected<OwnedStream> createViewFile(StringRef Path,
+ bool InToplevel) = 0;
+
+ /// \brief Close a file which has been used to print a coverage view.
+ virtual void closeViewFile(OwnedStream OS) = 0;
+
+ /// \brief Create an index which lists reports for the given source files.
+ virtual Error createIndexFile(ArrayRef<StringRef> SourceFiles) = 0;
+
+ /// @}
+};
+
+/// \brief A code coverage view of a source file or function.
+///
+/// A source coverage view and its nested sub-views form a file-oriented
+/// representation of code coverage data. This view can be printed out by a
+/// renderer which implements the Rendering Interface.
+class SourceCoverageView {
+ /// A function or file name.
+ StringRef SourceName;
+
+ /// A memory buffer backing the source on display.
const MemoryBuffer &File;
+
+ /// Various options to guide the coverage renderer.
const CoverageViewOptions &Options;
+
+ /// Complete coverage information about the source on display.
coverage::CoverageData CoverageInfo;
+
+ /// A container for all expansions (e.g macros) in the source on display.
std::vector<ExpansionView> ExpansionSubViews;
+
+ /// A container for all instantiations (e.g template functions) in the source
+ /// on display.
std::vector<InstantiationView> InstantiationSubViews;
- /// \brief Render a source line with highlighting.
- void renderLine(raw_ostream &OS, StringRef Line, int64_t LineNumber,
- const coverage::CoverageSegment *WrappedSegment,
- ArrayRef<const coverage::CoverageSegment *> Segments,
- unsigned ExpansionCol);
+protected:
+ struct LineRef {
+ StringRef Line;
+ int64_t LineNo;
+
+ LineRef(StringRef Line, int64_t LineNo) : Line(Line), LineNo(LineNo) {}
+ };
+
+ using CoverageSegmentArray = ArrayRef<const coverage::CoverageSegment *>;
+
+ /// @name Rendering Interface
+ /// @{
- void renderIndent(raw_ostream &OS, unsigned Level);
+ /// \brief Render a header for the view.
+ virtual void renderViewHeader(raw_ostream &OS) = 0;
- void renderViewDivider(unsigned Offset, unsigned Length, raw_ostream &OS);
+ /// \brief Render a footer for the view.
+ virtual void renderViewFooter(raw_ostream &OS) = 0;
+
+ /// \brief Render the source name for the view.
+ virtual void renderSourceName(raw_ostream &OS) = 0;
+
+ /// \brief Render the line prefix at the given \p ViewDepth.
+ virtual void renderLinePrefix(raw_ostream &OS, unsigned ViewDepth) = 0;
+
+ /// \brief Render the line suffix at the given \p ViewDepth.
+ virtual void renderLineSuffix(raw_ostream &OS, unsigned ViewDepth) = 0;
+
+ /// \brief Render a view divider at the given \p ViewDepth.
+ virtual void renderViewDivider(raw_ostream &OS, unsigned ViewDepth) = 0;
+
+ /// \brief Render a source line with highlighting.
+ virtual void renderLine(raw_ostream &OS, LineRef L,
+ const coverage::CoverageSegment *WrappedSegment,
+ CoverageSegmentArray Segments, unsigned ExpansionCol,
+ unsigned ViewDepth) = 0;
/// \brief Render the line's execution count column.
- void renderLineCoverageColumn(raw_ostream &OS, const LineCoverageInfo &Line);
+ virtual void renderLineCoverageColumn(raw_ostream &OS,
+ const LineCoverageStats &Line) = 0;
/// \brief Render the line number column.
- void renderLineNumberColumn(raw_ostream &OS, unsigned LineNo);
+ virtual void renderLineNumberColumn(raw_ostream &OS, unsigned LineNo) = 0;
/// \brief Render all the region's execution counts on a line.
- void
- renderRegionMarkers(raw_ostream &OS,
- ArrayRef<const coverage::CoverageSegment *> Segments);
+ virtual void renderRegionMarkers(raw_ostream &OS,
+ CoverageSegmentArray Segments,
+ unsigned ViewDepth) = 0;
- static const unsigned LineCoverageColumnWidth = 7;
- static const unsigned LineNumberColumnWidth = 5;
+ /// \brief Render the site of an expansion.
+ virtual void
+ renderExpansionSite(raw_ostream &OS, LineRef L,
+ const coverage::CoverageSegment *WrappedSegment,
+ CoverageSegmentArray Segments, unsigned ExpansionCol,
+ unsigned ViewDepth) = 0;
-public:
- SourceCoverageView(const MemoryBuffer &File,
+ /// \brief Render an expansion view and any nested views.
+ virtual void renderExpansionView(raw_ostream &OS, ExpansionView &ESV,
+ unsigned ViewDepth) = 0;
+
+ /// \brief Render an instantiation view and any nested views.
+ virtual void renderInstantiationView(raw_ostream &OS, InstantiationView &ISV,
+ unsigned ViewDepth) = 0;
+
+ /// @}
+
+ /// \brief Format a count using engineering notation with 3 significant
+ /// digits.
+ static std::string formatCount(uint64_t N);
+
+ /// \brief Check if region marker output is expected for a line.
+ bool shouldRenderRegionMarkers(bool LineHasMultipleRegions) const;
+
+ /// \brief Check if there are any sub-views attached to this view.
+ bool hasSubViews() const;
+
+ SourceCoverageView(StringRef SourceName, const MemoryBuffer &File,
const CoverageViewOptions &Options,
coverage::CoverageData &&CoverageInfo)
- : File(File), Options(Options), CoverageInfo(std::move(CoverageInfo)) {}
+ : SourceName(SourceName), File(File), Options(Options),
+ CoverageInfo(std::move(CoverageInfo)) {}
+
+public:
+ static std::unique_ptr<SourceCoverageView>
+ create(StringRef SourceName, const MemoryBuffer &File,
+ const CoverageViewOptions &Options,
+ coverage::CoverageData &&CoverageInfo);
+
+ virtual ~SourceCoverageView() {}
+
+ StringRef getSourceName() const { return SourceName; }
const CoverageViewOptions &getOptions() const { return Options; }
/// \brief Add an expansion subview to this view.
void addExpansion(const coverage::CounterMappingRegion &Region,
- std::unique_ptr<SourceCoverageView> View) {
- ExpansionSubViews.emplace_back(Region, std::move(View));
- }
+ std::unique_ptr<SourceCoverageView> View);
/// \brief Add a function instantiation subview to this view.
void addInstantiation(StringRef FunctionName, unsigned Line,
- std::unique_ptr<SourceCoverageView> View) {
- InstantiationSubViews.emplace_back(FunctionName, Line, std::move(View));
- }
+ std::unique_ptr<SourceCoverageView> View);
- /// \brief Print the code coverage information for a specific
- /// portion of a source file to the output stream.
- void render(raw_ostream &OS, bool WholeFile, unsigned IndentLevel = 0);
+ /// \brief Print the code coverage information for a specific portion of a
+ /// source file to the output stream.
+ void print(raw_ostream &OS, bool WholeFile, bool ShowSourceName,
+ unsigned ViewDepth = 0);
};
} // namespace llvm
diff --git a/tools/llvm-cov/SourceCoverageViewHTML.cpp b/tools/llvm-cov/SourceCoverageViewHTML.cpp
new file mode 100644
index 0000000000000..81963e5c54474
--- /dev/null
+++ b/tools/llvm-cov/SourceCoverageViewHTML.cpp
@@ -0,0 +1,436 @@
+//===- SourceCoverageViewHTML.cpp - A html code coverage view -------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file This file implements the html coverage renderer.
+///
+//===----------------------------------------------------------------------===//
+
+#include "SourceCoverageViewHTML.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/Path.h"
+
+using namespace llvm;
+
+namespace {
+
+const char *BeginHeader =
+ "<head>"
+ "<meta name='viewport' content='width=device-width,initial-scale=1'>"
+ "<meta charset='UTF-8'>";
+
+const char *CSSForCoverage =
+ "<style>"
+R"(
+
+.red {
+ background-color: #FFD0D0;
+}
+.cyan {
+ background-color: cyan;
+}
+.black {
+ background-color: black;
+ color: white;
+}
+.green {
+ background-color: #98FFA6;
+ color: white;
+}
+.magenta {
+ background-color: #F998FF;
+ color: white;
+}
+body {
+ font-family: -apple-system, sans-serif;
+}
+pre {
+ margin-top: 0px !important;
+ margin-bottom: 0px !important;
+}
+.source-name-title {
+ padding: 5px 10px;
+ border-bottom: 1px solid #dbdbdb;
+ background-color: #eee;
+}
+.centered {
+ display: table;
+ margin-left: auto;
+ margin-right: auto;
+ border: 1px solid #dbdbdb;
+ border-radius: 3px;
+}
+.expansion-view {
+ background-color: rgba(0, 0, 0, 0);
+ margin-left: 0px;
+ margin-top: 5px;
+ margin-right: 5px;
+ margin-bottom: 5px;
+ border: 1px solid #dbdbdb;
+ border-radius: 3px;
+}
+table {
+ border-collapse: collapse;
+}
+.line-number {
+ text-align: right;
+ color: #aaa;
+}
+.covered-line {
+ text-align: right;
+ color: #0080ff;
+}
+.uncovered-line {
+ text-align: right;
+ color: #ff3300;
+}
+.tooltip {
+ position: relative;
+ display: inline;
+ background-color: #b3e6ff;
+ text-decoration: none;
+}
+.tooltip span.tooltip-content {
+ position: absolute;
+ width: 100px;
+ margin-left: -50px;
+ color: #FFFFFF;
+ background: #000000;
+ height: 30px;
+ line-height: 30px;
+ text-align: center;
+ visibility: hidden;
+ border-radius: 6px;
+}
+.tooltip span.tooltip-content:after {
+ content: '';
+ position: absolute;
+ top: 100%;
+ left: 50%;
+ margin-left: -8px;
+ width: 0; height: 0;
+ border-top: 8px solid #000000;
+ border-right: 8px solid transparent;
+ border-left: 8px solid transparent;
+}
+:hover.tooltip span.tooltip-content {
+ visibility: visible;
+ opacity: 0.8;
+ bottom: 30px;
+ left: 50%;
+ z-index: 999;
+}
+th, td {
+ vertical-align: top;
+ padding: 2px 5px;
+ border-collapse: collapse;
+ border-right: solid 1px #eee;
+ border-left: solid 1px #eee;
+}
+td:first-child {
+ border-left: none;
+}
+td:last-child {
+ border-right: none;
+}
+
+)"
+ "</style>";
+
+const char *EndHeader = "</head>";
+
+const char *BeginCenteredDiv = "<div class='centered'>";
+
+const char *EndCenteredDiv = "</div>";
+
+const char *BeginSourceNameDiv = "<div class='source-name-title'>";
+
+const char *EndSourceNameDiv = "</div>";
+
+const char *BeginCodeTD = "<td class='code'>";
+
+const char *EndCodeTD = "</td>";
+
+const char *BeginPre = "<pre>";
+
+const char *EndPre = "</pre>";
+
+const char *BeginExpansionDiv = "<div class='expansion-view'>";
+
+const char *EndExpansionDiv = "</div>";
+
+const char *BeginTable = "<table>";
+
+const char *EndTable = "</table>";
+
+void emitPrelude(raw_ostream &OS) {
+ OS << "<!doctype html>"
+ "<html>"
+ << BeginHeader << CSSForCoverage << EndHeader << "<body>"
+ << BeginCenteredDiv;
+}
+
+void emitEpilog(raw_ostream &OS) {
+ OS << EndCenteredDiv << "</body>"
+ "</html>";
+}
+
+// Return a string with the special characters in \p Str escaped.
+std::string escape(StringRef Str) {
+ std::string Result;
+ for (char C : Str) {
+ if (C == '&')
+ Result += "&amp;";
+ else if (C == '<')
+ Result += "&lt;";
+ else if (C == '>')
+ Result += "&gt;";
+ else if (C == '\"')
+ Result += "&quot;";
+ else
+ Result += C;
+ }
+ return Result;
+}
+
+// Create a \p Name tag around \p Str, and optionally set its \p ClassName.
+std::string tag(const std::string &Name, const std::string &Str,
+ const std::string &ClassName = "") {
+ std::string Tag = "<" + Name;
+ if (ClassName != "")
+ Tag += " class='" + ClassName + "'";
+ return Tag + ">" + Str + "</" + Name + ">";
+}
+
+// Create an anchor to \p Link with the label \p Str.
+std::string a(const std::string &Link, const std::string &Str) {
+ return "<a href='" + Link + "'>" + Str + "</a>";
+}
+
+} // anonymous namespace
+
+Expected<CoveragePrinter::OwnedStream>
+CoveragePrinterHTML::createViewFile(StringRef Path, bool InToplevel) {
+ auto OSOrErr = createOutputStream(Path, "html", InToplevel);
+ if (!OSOrErr)
+ return OSOrErr;
+
+ OwnedStream OS = std::move(OSOrErr.get());
+ emitPrelude(*OS.get());
+ return std::move(OS);
+}
+
+void CoveragePrinterHTML::closeViewFile(OwnedStream OS) {
+ emitEpilog(*OS.get());
+}
+
+Error CoveragePrinterHTML::createIndexFile(ArrayRef<StringRef> SourceFiles) {
+ auto OSOrErr = createOutputStream("index", "html", /*InToplevel=*/true);
+ if (Error E = OSOrErr.takeError())
+ return E;
+ auto OS = std::move(OSOrErr.get());
+ raw_ostream &OSRef = *OS.get();
+
+ // Emit a table containing links to reports for each file in the covmapping.
+ emitPrelude(OSRef);
+ OSRef << BeginSourceNameDiv << "Index" << EndSourceNameDiv;
+ OSRef << BeginTable;
+ for (StringRef SF : SourceFiles) {
+ std::string LinkText = escape(sys::path::relative_path(SF));
+ std::string LinkTarget =
+ escape(getOutputPath(SF, "html", /*InToplevel=*/false));
+ OSRef << tag("tr", tag("td", tag("pre", a(LinkTarget, LinkText), "code")));
+ }
+ OSRef << EndTable;
+ emitEpilog(OSRef);
+
+ return Error::success();
+}
+
+void SourceCoverageViewHTML::renderViewHeader(raw_ostream &OS) {
+ OS << BeginTable;
+}
+
+void SourceCoverageViewHTML::renderViewFooter(raw_ostream &OS) {
+ OS << EndTable;
+}
+
+void SourceCoverageViewHTML::renderSourceName(raw_ostream &OS) {
+ OS << BeginSourceNameDiv << tag("pre", escape(getSourceName()))
+ << EndSourceNameDiv;
+}
+
+void SourceCoverageViewHTML::renderLinePrefix(raw_ostream &OS, unsigned) {
+ OS << "<tr>";
+}
+
+void SourceCoverageViewHTML::renderLineSuffix(raw_ostream &OS, unsigned) {
+ // If this view has sub-views, renderLine() cannot close the view's cell.
+ // Take care of it here, after all sub-views have been rendered.
+ if (hasSubViews())
+ OS << EndCodeTD;
+ OS << "</tr>";
+}
+
+void SourceCoverageViewHTML::renderViewDivider(raw_ostream &, unsigned) {
+ // The table-based output makes view dividers unnecessary.
+}
+
+void SourceCoverageViewHTML::renderLine(
+ raw_ostream &OS, LineRef L, const coverage::CoverageSegment *WrappedSegment,
+ CoverageSegmentArray Segments, unsigned ExpansionCol, unsigned) {
+ StringRef Line = L.Line;
+
+ // Steps for handling text-escaping, highlighting, and tooltip creation:
+ //
+ // 1. Split the line into N+1 snippets, where N = |Segments|. The first
+ // snippet starts from Col=1 and ends at the start of the first segment.
+ // The last snippet starts at the last mapped column in the line and ends
+ // at the end of the line. Both are required but may be empty.
+
+ SmallVector<std::string, 8> Snippets;
+
+ unsigned LCol = 1;
+ auto Snip = [&](unsigned Start, unsigned Len) {
+ assert(Start + Len <= Line.size() && "Snippet extends past the EOL");
+ Snippets.push_back(Line.substr(Start, Len));
+ LCol += Len;
+ };
+
+ Snip(LCol - 1, Segments.empty() ? 0 : (Segments.front()->Col - 1));
+
+ for (unsigned I = 1, E = Segments.size(); I < E; ++I) {
+ assert(LCol == Segments[I - 1]->Col && "Snippet start position is wrong");
+ Snip(LCol - 1, Segments[I]->Col - LCol);
+ }
+
+ // |Line| + 1 is needed to avoid underflow when, e.g |Line| = 0 and LCol = 1.
+ Snip(LCol - 1, Line.size() + 1 - LCol);
+ assert(LCol == Line.size() + 1 && "Final snippet doesn't reach the EOL");
+
+ // 2. Escape all of the snippets.
+
+ for (unsigned I = 0, E = Snippets.size(); I < E; ++I)
+ Snippets[I] = escape(Snippets[I]);
+
+ // 3. Use \p WrappedSegment to set the highlight for snippets 0 and 1. Use
+ // segment 1 to set the highlight for snippet 2, segment 2 to set the
+ // highlight for snippet 3, and so on.
+
+ Optional<std::string> Color;
+ auto Highlight = [&](const std::string &Snippet) {
+ return tag("span", Snippet, Color.getValue());
+ };
+
+ auto CheckIfUncovered = [](const coverage::CoverageSegment *S) {
+ return S && S->HasCount && S->Count == 0;
+ };
+
+ if (CheckIfUncovered(WrappedSegment) ||
+ CheckIfUncovered(Segments.empty() ? nullptr : Segments.front())) {
+ Color = "red";
+ Snippets[0] = Highlight(Snippets[0]);
+ Snippets[1] = Highlight(Snippets[1]);
+ }
+
+ for (unsigned I = 1, E = Segments.size(); I < E; ++I) {
+ const auto *CurSeg = Segments[I];
+ if (CurSeg->Col == ExpansionCol)
+ Color = "cyan";
+ else if (CheckIfUncovered(CurSeg))
+ Color = "red";
+ else
+ Color = None;
+
+ if (Color.hasValue())
+ Snippets[I + 1] = Highlight(Snippets[I + 1]);
+ }
+
+ // 4. Snippets[1:N+1] correspond to \p Segments[0:N]: use these to generate
+ // sub-line region count tooltips if needed.
+
+ bool HasMultipleRegions = [&] {
+ unsigned RegionCount = 0;
+ for (const auto *S : Segments)
+ if (S->HasCount && S->IsRegionEntry)
+ if (++RegionCount > 1)
+ return true;
+ return false;
+ }();
+
+ if (shouldRenderRegionMarkers(HasMultipleRegions)) {
+ for (unsigned I = 0, E = Segments.size(); I < E; ++I) {
+ const auto *CurSeg = Segments[I];
+ if (!CurSeg->IsRegionEntry || !CurSeg->HasCount)
+ continue;
+
+ Snippets[I + 1] =
+ tag("div", Snippets[I + 1] + tag("span", formatCount(CurSeg->Count),
+ "tooltip-content"),
+ "tooltip");
+ }
+ }
+
+ OS << BeginCodeTD;
+ OS << BeginPre;
+ for (const auto &Snippet : Snippets)
+ OS << Snippet;
+ OS << EndPre;
+
+ // If there are no sub-views left to attach to this cell, end the cell.
+ // Otherwise, end it after the sub-views are rendered (renderLineSuffix()).
+ if (!hasSubViews())
+ OS << EndCodeTD;
+}
+
+void SourceCoverageViewHTML::renderLineCoverageColumn(
+ raw_ostream &OS, const LineCoverageStats &Line) {
+ std::string Count = "";
+ if (Line.isMapped())
+ Count = tag("pre", formatCount(Line.ExecutionCount));
+ std::string CoverageClass =
+ (Line.ExecutionCount > 0) ? "covered-line" : "uncovered-line";
+ OS << tag("td", Count, CoverageClass);
+}
+
+void SourceCoverageViewHTML::renderLineNumberColumn(raw_ostream &OS,
+ unsigned LineNo) {
+ OS << tag("td", tag("pre", utostr(uint64_t(LineNo))), "line-number");
+}
+
+void SourceCoverageViewHTML::renderRegionMarkers(raw_ostream &,
+ CoverageSegmentArray,
+ unsigned) {
+ // Region markers are rendered in-line using tooltips.
+}
+
+void SourceCoverageViewHTML::renderExpansionSite(
+ raw_ostream &OS, LineRef L, const coverage::CoverageSegment *WrappedSegment,
+ CoverageSegmentArray Segments, unsigned ExpansionCol, unsigned ViewDepth) {
+ // Render the line containing the expansion site. No extra formatting needed.
+ renderLine(OS, L, WrappedSegment, Segments, ExpansionCol, ViewDepth);
+}
+
+void SourceCoverageViewHTML::renderExpansionView(raw_ostream &OS,
+ ExpansionView &ESV,
+ unsigned ViewDepth) {
+ OS << BeginExpansionDiv;
+ ESV.View->print(OS, /*WholeFile=*/false, /*ShowSourceName=*/false,
+ ViewDepth + 1);
+ OS << EndExpansionDiv;
+}
+
+void SourceCoverageViewHTML::renderInstantiationView(raw_ostream &OS,
+ InstantiationView &ISV,
+ unsigned ViewDepth) {
+ OS << BeginExpansionDiv;
+ ISV.View->print(OS, /*WholeFile=*/false, /*ShowSourceName=*/true, ViewDepth);
+ OS << EndExpansionDiv;
+}
diff --git a/tools/llvm-cov/SourceCoverageViewHTML.h b/tools/llvm-cov/SourceCoverageViewHTML.h
new file mode 100644
index 0000000000000..50ecf2bf89970
--- /dev/null
+++ b/tools/llvm-cov/SourceCoverageViewHTML.h
@@ -0,0 +1,83 @@
+//===- SourceCoverageViewHTML.h - A html code coverage view ---------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file This file defines the interface to the html coverage renderer.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_COV_SOURCECOVERAGEVIEWHTML_H
+#define LLVM_COV_SOURCECOVERAGEVIEWHTML_H
+
+#include "SourceCoverageView.h"
+
+namespace llvm {
+
+/// \brief A coverage printer for html output.
+class CoveragePrinterHTML : public CoveragePrinter {
+public:
+ Expected<OwnedStream> createViewFile(StringRef Path,
+ bool InToplevel) override;
+
+ void closeViewFile(OwnedStream OS) override;
+
+ Error createIndexFile(ArrayRef<StringRef> SourceFiles) override;
+
+ CoveragePrinterHTML(const CoverageViewOptions &Opts)
+ : CoveragePrinter(Opts) {}
+};
+
+/// \brief A code coverage view which supports html-based rendering.
+class SourceCoverageViewHTML : public SourceCoverageView {
+ void renderViewHeader(raw_ostream &OS) override;
+
+ void renderViewFooter(raw_ostream &OS) override;
+
+ void renderSourceName(raw_ostream &OS) override;
+
+ void renderLinePrefix(raw_ostream &OS, unsigned ViewDepth) override;
+
+ void renderLineSuffix(raw_ostream &OS, unsigned ViewDepth) override;
+
+ void renderViewDivider(raw_ostream &OS, unsigned ViewDepth) override;
+
+ void renderLine(raw_ostream &OS, LineRef L,
+ const coverage::CoverageSegment *WrappedSegment,
+ CoverageSegmentArray Segments, unsigned ExpansionCol,
+ unsigned ViewDepth) override;
+
+ void renderExpansionSite(raw_ostream &OS, LineRef L,
+ const coverage::CoverageSegment *WrappedSegment,
+ CoverageSegmentArray Segments, unsigned ExpansionCol,
+ unsigned ViewDepth) override;
+
+ void renderExpansionView(raw_ostream &OS, ExpansionView &ESV,
+ unsigned ViewDepth) override;
+
+ void renderInstantiationView(raw_ostream &OS, InstantiationView &ISV,
+ unsigned ViewDepth) override;
+
+ void renderLineCoverageColumn(raw_ostream &OS,
+ const LineCoverageStats &Line) override;
+
+ void renderLineNumberColumn(raw_ostream &OS, unsigned LineNo) override;
+
+ void renderRegionMarkers(raw_ostream &OS, CoverageSegmentArray Segments,
+ unsigned ViewDepth) override;
+
+public:
+ SourceCoverageViewHTML(StringRef SourceName, const MemoryBuffer &File,
+ const CoverageViewOptions &Options,
+ coverage::CoverageData &&CoverageInfo)
+ : SourceCoverageView(SourceName, File, Options, std::move(CoverageInfo)) {
+ }
+};
+
+} // namespace llvm
+
+#endif // LLVM_COV_SOURCECOVERAGEVIEWHTML_H
diff --git a/tools/llvm-cov/SourceCoverageViewText.cpp b/tools/llvm-cov/SourceCoverageViewText.cpp
new file mode 100644
index 0000000000000..ae9d6daed08f7
--- /dev/null
+++ b/tools/llvm-cov/SourceCoverageViewText.cpp
@@ -0,0 +1,213 @@
+//===- SourceCoverageViewText.cpp - A text-based code coverage view -------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file This file implements the text-based coverage renderer.
+///
+//===----------------------------------------------------------------------===//
+
+#include "SourceCoverageViewText.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringExtras.h"
+
+using namespace llvm;
+
+Expected<CoveragePrinter::OwnedStream>
+CoveragePrinterText::createViewFile(StringRef Path, bool InToplevel) {
+ return createOutputStream(Path, "txt", InToplevel);
+}
+
+void CoveragePrinterText::closeViewFile(OwnedStream OS) {
+ OS->operator<<('\n');
+}
+
+Error CoveragePrinterText::createIndexFile(ArrayRef<StringRef> SourceFiles) {
+ auto OSOrErr = createOutputStream("index", "txt", /*InToplevel=*/true);
+ if (Error E = OSOrErr.takeError())
+ return E;
+ auto OS = std::move(OSOrErr.get());
+ raw_ostream &OSRef = *OS.get();
+
+ for (StringRef SF : SourceFiles)
+ OSRef << getOutputPath(SF, "txt", /*InToplevel=*/false) << '\n';
+
+ return Error::success();
+}
+
+namespace {
+
+static const unsigned LineCoverageColumnWidth = 7;
+static const unsigned LineNumberColumnWidth = 5;
+
+/// \brief Get the width of the leading columns.
+unsigned getCombinedColumnWidth(const CoverageViewOptions &Opts) {
+ return (Opts.ShowLineStats ? LineCoverageColumnWidth + 1 : 0) +
+ (Opts.ShowLineNumbers ? LineNumberColumnWidth + 1 : 0);
+}
+
+/// \brief The width of the line that is used to divide between the view and
+/// the subviews.
+unsigned getDividerWidth(const CoverageViewOptions &Opts) {
+ return getCombinedColumnWidth(Opts) + 4;
+}
+
+} // anonymous namespace
+
+void SourceCoverageViewText::renderViewHeader(raw_ostream &) {}
+
+void SourceCoverageViewText::renderViewFooter(raw_ostream &) {}
+
+void SourceCoverageViewText::renderSourceName(raw_ostream &OS) {
+ getOptions().colored_ostream(OS, raw_ostream::CYAN) << getSourceName()
+ << ":\n";
+}
+
+void SourceCoverageViewText::renderLinePrefix(raw_ostream &OS,
+ unsigned ViewDepth) {
+ for (unsigned I = 0; I < ViewDepth; ++I)
+ OS << " |";
+}
+
+void SourceCoverageViewText::renderLineSuffix(raw_ostream &, unsigned) {}
+
+void SourceCoverageViewText::renderViewDivider(raw_ostream &OS,
+ unsigned ViewDepth) {
+ assert(ViewDepth != 0 && "Cannot render divider at top level");
+ renderLinePrefix(OS, ViewDepth - 1);
+ OS.indent(2);
+ unsigned Length = getDividerWidth(getOptions());
+ for (unsigned I = 0; I < Length; ++I)
+ OS << '-';
+ OS << '\n';
+}
+
+void SourceCoverageViewText::renderLine(
+ raw_ostream &OS, LineRef L,
+ const coverage::CoverageSegment *WrappedSegment,
+ CoverageSegmentArray Segments, unsigned ExpansionCol, unsigned ViewDepth) {
+ StringRef Line = L.Line;
+ unsigned LineNumber = L.LineNo;
+
+ Optional<raw_ostream::Colors> Highlight;
+ SmallVector<std::pair<unsigned, unsigned>, 2> HighlightedRanges;
+
+ // The first segment overlaps from a previous line, so we treat it specially.
+ if (WrappedSegment && WrappedSegment->HasCount && WrappedSegment->Count == 0)
+ Highlight = raw_ostream::RED;
+
+ // Output each segment of the line, possibly highlighted.
+ unsigned Col = 1;
+ for (const auto *S : Segments) {
+ unsigned End = std::min(S->Col, static_cast<unsigned>(Line.size()) + 1);
+ colored_ostream(OS, Highlight ? *Highlight : raw_ostream::SAVEDCOLOR,
+ getOptions().Colors && Highlight, /*Bold=*/false,
+ /*BG=*/true)
+ << Line.substr(Col - 1, End - Col);
+ if (getOptions().Debug && Highlight)
+ HighlightedRanges.push_back(std::make_pair(Col, End));
+ Col = End;
+ if (Col == ExpansionCol)
+ Highlight = raw_ostream::CYAN;
+ else if (S->HasCount && S->Count == 0)
+ Highlight = raw_ostream::RED;
+ else
+ Highlight = None;
+ }
+
+ // Show the rest of the line.
+ colored_ostream(OS, Highlight ? *Highlight : raw_ostream::SAVEDCOLOR,
+ getOptions().Colors && Highlight, /*Bold=*/false, /*BG=*/true)
+ << Line.substr(Col - 1, Line.size() - Col + 1);
+ OS << '\n';
+
+ if (getOptions().Debug) {
+ for (const auto &Range : HighlightedRanges)
+ errs() << "Highlighted line " << LineNumber << ", " << Range.first
+ << " -> " << Range.second << '\n';
+ if (Highlight)
+ errs() << "Highlighted line " << LineNumber << ", " << Col << " -> ?\n";
+ }
+}
+
+void SourceCoverageViewText::renderLineCoverageColumn(
+ raw_ostream &OS, const LineCoverageStats &Line) {
+ if (!Line.isMapped()) {
+ OS.indent(LineCoverageColumnWidth) << '|';
+ return;
+ }
+ std::string C = formatCount(Line.ExecutionCount);
+ OS.indent(LineCoverageColumnWidth - C.size());
+ colored_ostream(OS, raw_ostream::MAGENTA,
+ Line.hasMultipleRegions() && getOptions().Colors)
+ << C;
+ OS << '|';
+}
+
+void SourceCoverageViewText::renderLineNumberColumn(raw_ostream &OS,
+ unsigned LineNo) {
+ SmallString<32> Buffer;
+ raw_svector_ostream BufferOS(Buffer);
+ BufferOS << LineNo;
+ auto Str = BufferOS.str();
+ // Trim and align to the right.
+ Str = Str.substr(0, std::min(Str.size(), (size_t)LineNumberColumnWidth));
+ OS.indent(LineNumberColumnWidth - Str.size()) << Str << '|';
+}
+
+void SourceCoverageViewText::renderRegionMarkers(
+ raw_ostream &OS, CoverageSegmentArray Segments, unsigned ViewDepth) {
+ renderLinePrefix(OS, ViewDepth);
+ OS.indent(getCombinedColumnWidth(getOptions()));
+
+ unsigned PrevColumn = 1;
+ for (const auto *S : Segments) {
+ if (!S->IsRegionEntry)
+ continue;
+ // Skip to the new region.
+ if (S->Col > PrevColumn)
+ OS.indent(S->Col - PrevColumn);
+ PrevColumn = S->Col + 1;
+ std::string C = formatCount(S->Count);
+ PrevColumn += C.size();
+ OS << '^' << C;
+ }
+ OS << '\n';
+
+ if (getOptions().Debug)
+ for (const auto *S : Segments)
+ errs() << "Marker at " << S->Line << ":" << S->Col << " = "
+ << formatCount(S->Count) << (S->IsRegionEntry ? "\n" : " (pop)\n");
+}
+
+void SourceCoverageViewText::renderExpansionSite(
+ raw_ostream &OS, LineRef L, const coverage::CoverageSegment *WrappedSegment,
+ CoverageSegmentArray Segments, unsigned ExpansionCol, unsigned ViewDepth) {
+ renderLinePrefix(OS, ViewDepth);
+ OS.indent(getCombinedColumnWidth(getOptions()) + (ViewDepth == 0 ? 0 : 1));
+ renderLine(OS, L, WrappedSegment, Segments, ExpansionCol, ViewDepth);
+}
+
+void SourceCoverageViewText::renderExpansionView(raw_ostream &OS,
+ ExpansionView &ESV,
+ unsigned ViewDepth) {
+ // Render the child subview.
+ if (getOptions().Debug)
+ errs() << "Expansion at line " << ESV.getLine() << ", " << ESV.getStartCol()
+ << " -> " << ESV.getEndCol() << '\n';
+ ESV.View->print(OS, /*WholeFile=*/false, /*ShowSourceName=*/false,
+ ViewDepth + 1);
+}
+
+void SourceCoverageViewText::renderInstantiationView(raw_ostream &OS,
+ InstantiationView &ISV,
+ unsigned ViewDepth) {
+ renderLinePrefix(OS, ViewDepth);
+ OS << ' ';
+ ISV.View->print(OS, /*WholeFile=*/false, /*ShowSourceName=*/true, ViewDepth);
+}
diff --git a/tools/llvm-cov/SourceCoverageViewText.h b/tools/llvm-cov/SourceCoverageViewText.h
new file mode 100644
index 0000000000000..b2331247b37b7
--- /dev/null
+++ b/tools/llvm-cov/SourceCoverageViewText.h
@@ -0,0 +1,83 @@
+//===- SourceCoverageViewText.h - A text-based code coverage view ---------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file This file defines the interface to the text-based coverage renderer.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_COV_SOURCECOVERAGEVIEWTEXT_H
+#define LLVM_COV_SOURCECOVERAGEVIEWTEXT_H
+
+#include "SourceCoverageView.h"
+
+namespace llvm {
+
+/// \brief A coverage printer for text output.
+class CoveragePrinterText : public CoveragePrinter {
+public:
+ Expected<OwnedStream> createViewFile(StringRef Path,
+ bool InToplevel) override;
+
+ void closeViewFile(OwnedStream OS) override;
+
+ Error createIndexFile(ArrayRef<StringRef> SourceFiles) override;
+
+ CoveragePrinterText(const CoverageViewOptions &Opts)
+ : CoveragePrinter(Opts) {}
+};
+
+/// \brief A code coverage view which supports text-based rendering.
+class SourceCoverageViewText : public SourceCoverageView {
+ void renderViewHeader(raw_ostream &OS) override;
+
+ void renderViewFooter(raw_ostream &OS) override;
+
+ void renderSourceName(raw_ostream &OS) override;
+
+ void renderLinePrefix(raw_ostream &OS, unsigned ViewDepth) override;
+
+ void renderLineSuffix(raw_ostream &OS, unsigned ViewDepth) override;
+
+ void renderViewDivider(raw_ostream &OS, unsigned ViewDepth) override;
+
+ void renderLine(raw_ostream &OS, LineRef L,
+ const coverage::CoverageSegment *WrappedSegment,
+ CoverageSegmentArray Segments, unsigned ExpansionCol,
+ unsigned ViewDepth) override;
+
+ void renderExpansionSite(raw_ostream &OS, LineRef L,
+ const coverage::CoverageSegment *WrappedSegment,
+ CoverageSegmentArray Segments, unsigned ExpansionCol,
+ unsigned ViewDepth) override;
+
+ void renderExpansionView(raw_ostream &OS, ExpansionView &ESV,
+ unsigned ViewDepth) override;
+
+ void renderInstantiationView(raw_ostream &OS, InstantiationView &ISV,
+ unsigned ViewDepth) override;
+
+ void renderLineCoverageColumn(raw_ostream &OS,
+ const LineCoverageStats &Line) override;
+
+ void renderLineNumberColumn(raw_ostream &OS, unsigned LineNo) override;
+
+ void renderRegionMarkers(raw_ostream &OS, CoverageSegmentArray Segments,
+ unsigned ViewDepth) override;
+
+public:
+ SourceCoverageViewText(StringRef SourceName, const MemoryBuffer &File,
+ const CoverageViewOptions &Options,
+ coverage::CoverageData &&CoverageInfo)
+ : SourceCoverageView(SourceName, File, Options, std::move(CoverageInfo)) {
+ }
+};
+
+} // namespace llvm
+
+#endif // LLVM_COV_SOURCECOVERAGEVIEWTEXT_H
diff --git a/tools/llvm-cov/TestingSupport.cpp b/tools/llvm-cov/TestingSupport.cpp
index 6959897482ca2..72768f4fd583f 100644
--- a/tools/llvm-cov/TestingSupport.cpp
+++ b/tools/llvm-cov/TestingSupport.cpp
@@ -8,11 +8,9 @@
//===----------------------------------------------------------------------===//
#include "llvm/Object/ObjectFile.h"
+#include "llvm/ProfileData/InstrProf.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/LEB128.h"
-#include "llvm/Support/ManagedStatic.h"
-#include "llvm/Support/PrettyStackTrace.h"
-#include "llvm/Support/Signals.h"
#include "llvm/Support/raw_ostream.h"
#include <functional>
#include <system_error>
@@ -21,10 +19,6 @@ using namespace llvm;
using namespace object;
int convertForTestingMain(int argc, const char *argv[]) {
- sys::PrintStackTraceOnErrorSignal();
- PrettyStackTraceProgram X(argc, argv);
- llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
-
cl::opt<std::string> InputSourceFile(cl::Positional, cl::Required,
cl::desc("<Source file>"));
@@ -36,8 +30,12 @@ int convertForTestingMain(int argc, const char *argv[]) {
cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n");
auto ObjErr = llvm::object::ObjectFile::createObjectFile(InputSourceFile);
- if (auto Err = ObjErr.getError()) {
- errs() << "error: " << Err.message() << "\n";
+ if (!ObjErr) {
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(ObjErr.takeError(), OS, "");
+ OS.flush();
+ errs() << "error: " << Buf;
return 1;
}
ObjectFile *OF = ObjErr.get().getBinary();
@@ -54,9 +52,9 @@ int convertForTestingMain(int argc, const char *argv[]) {
StringRef Name;
if (Section.getName(Name))
return 1;
- if (Name == "__llvm_prf_names") {
+ if (Name == llvm::getInstrProfNameSectionName(false)) {
ProfileNames = Section;
- } else if (Name == "__llvm_covmap") {
+ } else if (Name == llvm::getInstrProfCoverageSectionName(false)) {
CoverageMapping = Section;
} else
continue;
@@ -84,7 +82,11 @@ int convertForTestingMain(int argc, const char *argv[]) {
OS << "llvmcovmtestdata";
encodeULEB128(ProfileNamesData.size(), OS);
encodeULEB128(ProfileNamesAddress, OS);
- OS << ProfileNamesData << CoverageMappingData;
+ OS << ProfileNamesData;
+ // Coverage mapping data is expected to have an alignment of 8.
+ for (unsigned Pad = OffsetToAlignment(OS.tell(), 8); Pad; --Pad)
+ OS.write(uint8_t(0));
+ OS << CoverageMappingData;
return 0;
}
diff --git a/tools/llvm-cov/gcov.cpp b/tools/llvm-cov/gcov.cpp
index a5343fa29afc3..4652fed2a384e 100644
--- a/tools/llvm-cov/gcov.cpp
+++ b/tools/llvm-cov/gcov.cpp
@@ -16,10 +16,7 @@
#include "llvm/Support/Errc.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/GCOV.h"
-#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/Path.h"
-#include "llvm/Support/PrettyStackTrace.h"
-#include "llvm/Support/Signals.h"
#include <system_error>
using namespace llvm;
@@ -85,11 +82,6 @@ static void reportCoverage(StringRef SourceFile, StringRef ObjectDir,
}
int gcovMain(int argc, const char *argv[]) {
- // Print a stack trace if we signal out.
- sys::PrintStackTraceOnErrorSignal();
- PrettyStackTraceProgram X(argc, argv);
- llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
-
cl::list<std::string> SourceFiles(cl::Positional, cl::OneOrMore,
cl::desc("SOURCEFILE"));
diff --git a/tools/llvm-cov/llvm-cov.cpp b/tools/llvm-cov/llvm-cov.cpp
index 8c5acaef63b25..ba60cd91da906 100644
--- a/tools/llvm-cov/llvm-cov.cpp
+++ b/tools/llvm-cov/llvm-cov.cpp
@@ -14,8 +14,11 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/Path.h"
+#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/Process.h"
+#include "llvm/Support/Signals.h"
#include "llvm/Support/raw_ostream.h"
#include <string>
@@ -51,6 +54,11 @@ static int versionMain(int argc, const char *argv[]) {
}
int main(int argc, const char **argv) {
+ // Print a stack trace if we signal out.
+ sys::PrintStackTraceOnErrorSignal(argv[0]);
+ PrettyStackTraceProgram X(argc, argv);
+ llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
+
// If argv[0] is or ends with 'gcov', always be gcov compatible
if (sys::path::stem(argv[0]).endswith_lower("gcov"))
return gcovMain(argc, argv);
diff --git a/tools/llvm-cxxdump/Error.cpp b/tools/llvm-cxxdump/Error.cpp
index 16fed96e4834c..ff9f0f5790669 100644
--- a/tools/llvm-cxxdump/Error.cpp
+++ b/tools/llvm-cxxdump/Error.cpp
@@ -17,6 +17,9 @@
using namespace llvm;
namespace {
+// FIXME: This class is only here to support the transition to llvm::Error. It
+// will be removed once this transition is complete. Clients should prefer to
+// deal with the Error value directly, rather than converting to error_code.
class cxxdump_error_category : public std::error_category {
public:
const char *name() const LLVM_NOEXCEPT override { return "llvm.cxxdump"; }
diff --git a/tools/llvm-cxxdump/Makefile b/tools/llvm-cxxdump/Makefile
deleted file mode 100644
index 02e8e5f901080..0000000000000
--- a/tools/llvm-cxxdump/Makefile
+++ /dev/null
@@ -1,18 +0,0 @@
-##===- tools/llvm-cxxdump/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-cxxdump
-LINK_COMPONENTS := bitreader object all-targets
-
-# This tool has no plugins, optimize startup time.
-TOOL_NO_EXPORTS := 1
-
-include $(LEVEL)/Makefile.common
-
diff --git a/tools/llvm-cxxdump/llvm-cxxdump.cpp b/tools/llvm-cxxdump/llvm-cxxdump.cpp
index 3dda69266a2d8..c92d20d6ccf17 100644
--- a/tools/llvm-cxxdump/llvm-cxxdump.cpp
+++ b/tools/llvm-cxxdump/llvm-cxxdump.cpp
@@ -50,6 +50,14 @@ static void error(std::error_code EC) {
exit(1);
}
+static void error(Error Err) {
+ if (Err) {
+ logAllUnhandledErrors(std::move(Err), outs(), "Error reading file: ");
+ outs().flush();
+ exit(1);
+ }
+}
+
} // namespace llvm
static void reportError(StringRef Input, StringRef Message) {
@@ -79,8 +87,8 @@ static void collectRelocatedSymbols(const ObjectFile *Obj,
const object::symbol_iterator RelocSymI = Reloc.getSymbol();
if (RelocSymI == Obj->symbol_end())
continue;
- ErrorOr<StringRef> RelocSymName = RelocSymI->getName();
- error(RelocSymName.getError());
+ Expected<StringRef> RelocSymName = RelocSymI->getName();
+ error(errorToErrorCode(RelocSymName.takeError()));
uint64_t Offset = Reloc.getOffset();
if (Offset >= SymOffset && Offset < SymEnd) {
*I = *RelocSymName;
@@ -101,8 +109,8 @@ static void collectRelocationOffsets(
const object::symbol_iterator RelocSymI = Reloc.getSymbol();
if (RelocSymI == Obj->symbol_end())
continue;
- ErrorOr<StringRef> RelocSymName = RelocSymI->getName();
- error(RelocSymName.getError());
+ Expected<StringRef> RelocSymName = RelocSymI->getName();
+ error(errorToErrorCode(RelocSymName.takeError()));
uint64_t Offset = Reloc.getOffset();
if (Offset >= SymOffset && Offset < SymEnd)
Collection[std::make_pair(SymName, Offset - SymOffset)] = *RelocSymName;
@@ -175,11 +183,11 @@ static void dumpCXXData(const ObjectFile *Obj) {
for (auto &P : SymAddr) {
object::SymbolRef Sym = P.first;
uint64_t SymSize = P.second;
- ErrorOr<StringRef> SymNameOrErr = Sym.getName();
- error(SymNameOrErr.getError());
+ Expected<StringRef> SymNameOrErr = Sym.getName();
+ error(errorToErrorCode(SymNameOrErr.takeError()));
StringRef SymName = *SymNameOrErr;
- ErrorOr<object::section_iterator> SecIOrErr = Sym.getSection();
- error(SecIOrErr.getError());
+ Expected<object::section_iterator> SecIOrErr = Sym.getSection();
+ error(errorToErrorCode(SecIOrErr.takeError()));
object::section_iterator SecI = *SecIOrErr;
// Skip external symbols.
if (SecI == Obj->section_end())
@@ -190,8 +198,8 @@ static void dumpCXXData(const ObjectFile *Obj) {
continue;
StringRef SecContents;
error(Sec.getContents(SecContents));
- ErrorOr<uint64_t> SymAddressOrErr = Sym.getAddress();
- error(SymAddressOrErr.getError());
+ Expected<uint64_t> SymAddressOrErr = Sym.getAddress();
+ error(errorToErrorCode(SymAddressOrErr.takeError()));
uint64_t SymAddress = *SymAddressOrErr;
uint64_t SecAddress = Sec.getAddress();
uint64_t SecSize = Sec.getSize();
@@ -482,14 +490,19 @@ static void dumpCXXData(const ObjectFile *Obj) {
}
static void dumpArchive(const Archive *Arc) {
- 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()) {
+ Error Err;
+ for (auto &ArcC : Arc->children(Err)) {
+ Expected<std::unique_ptr<Binary>> ChildOrErr = ArcC.getAsBinary();
+ if (!ChildOrErr) {
// Ignore non-object files.
- if (EC != object_error::invalid_file_type)
- reportError(Arc->getFileName(), EC.message());
+ if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) {
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(std::move(E), OS, "");
+ OS.flush();
+ reportError(Arc->getFileName(), Buf);
+ }
+ ChildOrErr.takeError();
continue;
}
@@ -498,12 +511,14 @@ static void dumpArchive(const Archive *Arc) {
else
reportError(Arc->getFileName(), cxxdump_error::unrecognized_file_format);
}
+ error(std::move(Err));
}
static void dumpInput(StringRef File) {
// Attempt to open the binary.
- ErrorOr<OwningBinary<Binary>> BinaryOrErr = createBinary(File);
- if (std::error_code EC = BinaryOrErr.getError()) {
+ Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(File);
+ if (!BinaryOrErr) {
+ auto EC = errorToErrorCode(BinaryOrErr.takeError());
reportError(File, EC);
return;
}
@@ -518,7 +533,7 @@ static void dumpInput(StringRef File) {
}
int main(int argc, const char *argv[]) {
- sys::PrintStackTraceOnErrorSignal();
+ sys::PrintStackTraceOnErrorSignal(argv[0]);
PrettyStackTraceProgram X(argc, argv);
llvm_shutdown_obj Y;
diff --git a/tools/llvm-diff/DiffConsumer.h b/tools/llvm-diff/DiffConsumer.h
index 855f6884e65b4..82f5ce598b443 100644
--- a/tools/llvm-diff/DiffConsumer.h
+++ b/tools/llvm-diff/DiffConsumer.h
@@ -17,11 +17,12 @@
#include "DiffLog.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallVector.h"
-#include "llvm/ADT/StringRef.h"
+#include "llvm/IR/Value.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/raw_ostream.h"
namespace llvm {
+class StringRef;
class Module;
class Value;
class Function;
diff --git a/tools/llvm-diff/DiffLog.cpp b/tools/llvm-diff/DiffLog.cpp
index ed86058f1af10..898749e73bd20 100644
--- a/tools/llvm-diff/DiffLog.cpp
+++ b/tools/llvm-diff/DiffLog.cpp
@@ -13,7 +13,6 @@
#include "DiffLog.h"
#include "DiffConsumer.h"
-#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/IR/Instructions.h"
diff --git a/tools/llvm-diff/DifferenceEngine.cpp b/tools/llvm-diff/DifferenceEngine.cpp
index 456560b093aba..df208a26ab7df 100644
--- a/tools/llvm-diff/DifferenceEngine.cpp
+++ b/tools/llvm-diff/DifferenceEngine.cpp
@@ -16,7 +16,6 @@
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/SmallVector.h"
-#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/IR/CFG.h"
#include "llvm/IR/CallSite.h"
@@ -210,7 +209,8 @@ class FunctionDifferenceEngine {
if (!LeftI->use_empty())
TentativeValues.insert(std::make_pair(LeftI, RightI));
- ++LI, ++RI;
+ ++LI;
+ ++RI;
} while (LI != LE); // This is sufficient: we can't get equality of
// terminators if there are residual instructions.
@@ -555,7 +555,9 @@ void FunctionDifferenceEngine::runBlockDiff(BasicBlock::iterator LStart,
PI = Path.begin(), PE = Path.end();
while (PI != PE && *PI == DC_match) {
unify(&*LI, &*RI);
- ++PI, ++LI, ++RI;
+ ++PI;
+ ++LI;
+ ++RI;
}
for (; PI != PE; ++PI) {
@@ -589,7 +591,8 @@ void FunctionDifferenceEngine::runBlockDiff(BasicBlock::iterator LStart,
while (LI != LE) {
assert(RI != RE);
unify(&*LI, &*RI);
- ++LI, ++RI;
+ ++LI;
+ ++RI;
}
// If the terminators have different kinds, but one is an invoke and the
diff --git a/tools/llvm-diff/DifferenceEngine.h b/tools/llvm-diff/DifferenceEngine.h
index f0d831144a4cb..7f084a377f0c7 100644
--- a/tools/llvm-diff/DifferenceEngine.h
+++ b/tools/llvm-diff/DifferenceEngine.h
@@ -17,7 +17,6 @@
#include "DiffConsumer.h"
#include "DiffLog.h"
-#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include <utility>
diff --git a/tools/llvm-diff/Makefile b/tools/llvm-diff/Makefile
deleted file mode 100644
index bd97a6a9f5e92..0000000000000
--- a/tools/llvm-diff/Makefile
+++ /dev/null
@@ -1,17 +0,0 @@
-##===- tools/llvm-diff/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-diff
-LINK_COMPONENTS := asmparser bitreader irreader
-
-# This tool has no plugins, optimize startup time.
-TOOL_NO_EXPORTS := 1
-
-include $(LEVEL)/Makefile.common
diff --git a/tools/llvm-diff/llvm-diff.cpp b/tools/llvm-diff/llvm-diff.cpp
index ae58f5caa9138..e449d6994784e 100644
--- a/tools/llvm-diff/llvm-diff.cpp
+++ b/tools/llvm-diff/llvm-diff.cpp
@@ -13,8 +13,6 @@
#include "DiffLog.h"
#include "DifferenceEngine.h"
-#include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
diff --git a/tools/llvm-dis/Makefile b/tools/llvm-dis/Makefile
deleted file mode 100644
index aeeeed0d68c9f..0000000000000
--- a/tools/llvm-dis/Makefile
+++ /dev/null
@@ -1,17 +0,0 @@
-##===- tools/llvm-dis/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-dis
-LINK_COMPONENTS := bitreader analysis
-
-# This tool has no plugins, optimize startup time.
-TOOL_NO_EXPORTS := 1
-
-include $(LEVEL)/Makefile.common
diff --git a/tools/llvm-dis/llvm-dis.cpp b/tools/llvm-dis/llvm-dis.cpp
index 9fdfcd4256c9a..88333aeb68892 100644
--- a/tools/llvm-dis/llvm-dis.cpp
+++ b/tools/llvm-dis/llvm-dis.cpp
@@ -27,6 +27,7 @@
#include "llvm/IR/Type.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/DataStream.h"
+#include "llvm/Support/Error.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/FormattedStream.h"
#include "llvm/Support/ManagedStatic.h"
@@ -59,6 +60,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>
+ MaterializeMetadata("materialize-metadata",
+ cl::desc("Load module without materializing metadata, "
+ "then materialize only the metadata"));
+
namespace {
static void printDebugLoc(const DebugLoc &DL, formatted_raw_ostream &OS) {
@@ -132,38 +138,59 @@ static void diagnosticHandler(const DiagnosticInfo &DI, void *Context) {
exit(1);
}
+static Expected<std::unique_ptr<Module>> openInputFile(LLVMContext &Context) {
+ if (MaterializeMetadata) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr =
+ MemoryBuffer::getFileOrSTDIN(InputFilename);
+ if (!MBOrErr)
+ return errorCodeToError(MBOrErr.getError());
+ ErrorOr<std::unique_ptr<Module>> MOrErr =
+ getLazyBitcodeModule(std::move(*MBOrErr), Context,
+ /*ShouldLazyLoadMetadata=*/true);
+ if (!MOrErr)
+ return errorCodeToError(MOrErr.getError());
+ (*MOrErr)->materializeMetadata();
+ return std::move(*MOrErr);
+ } else {
+ std::string ErrorMessage;
+ std::unique_ptr<DataStreamer> Streamer =
+ getDataFileStreamer(InputFilename, &ErrorMessage);
+ if (!Streamer)
+ return make_error<StringError>(ErrorMessage, inconvertibleErrorCode());
+ std::string DisplayFilename;
+ if (InputFilename == "-")
+ DisplayFilename = "<stdin>";
+ else
+ DisplayFilename = InputFilename;
+ ErrorOr<std::unique_ptr<Module>> MOrErr =
+ getStreamedBitcodeModule(DisplayFilename, std::move(Streamer), Context);
+ (*MOrErr)->materializeAll();
+ return std::move(*MOrErr);
+ }
+}
+
int main(int argc, char **argv) {
// Print a stack trace if we signal out.
- sys::PrintStackTraceOnErrorSignal();
+ sys::PrintStackTraceOnErrorSignal(argv[0]);
PrettyStackTraceProgram X(argc, argv);
- LLVMContext &Context = getGlobalContext();
+ LLVMContext Context;
llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
Context.setDiagnosticHandler(diagnosticHandler, argv[0]);
cl::ParseCommandLineOptions(argc, argv, "llvm .bc -> .ll disassembler\n");
- std::string ErrorMessage;
- std::unique_ptr<Module> M;
-
- // Use the bitcode streaming interface
- std::unique_ptr<DataStreamer> Streamer =
- getDataFileStreamer(InputFilename, &ErrorMessage);
- if (Streamer) {
- std::string DisplayFilename;
- if (InputFilename == "-")
- DisplayFilename = "<stdin>";
- else
- DisplayFilename = InputFilename;
- ErrorOr<std::unique_ptr<Module>> MOrErr =
- getStreamedBitcodeModule(DisplayFilename, std::move(Streamer), Context);
- M = std::move(*MOrErr);
- M->materializeAll();
- } else {
- errs() << argv[0] << ": " << ErrorMessage << '\n';
+ Expected<std::unique_ptr<Module>> MOrErr = openInputFile(Context);
+ if (!MOrErr) {
+ handleAllErrors(MOrErr.takeError(), [&](ErrorInfoBase &EIB) {
+ errs() << argv[0] << ": ";
+ EIB.log(errs());
+ errs() << '\n';
+ });
return 1;
}
+ std::unique_ptr<Module> M = std::move(*MOrErr);
// Just use stdout. We won't actually print anything on it.
if (DontPrint)
diff --git a/tools/llvm-dwarfdump/Makefile b/tools/llvm-dwarfdump/Makefile
deleted file mode 100644
index 00b18c60bb2ad..0000000000000
--- a/tools/llvm-dwarfdump/Makefile
+++ /dev/null
@@ -1,17 +0,0 @@
-##===- tools/llvm-dwarfdump/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-dwarfdump
-LINK_COMPONENTS := DebugInfoDWARF Object
-
-# This tool has no plugins, optimize startup time.
-TOOL_NO_EXPORTS := 1
-
-include $(LEVEL)/Makefile.common
diff --git a/tools/llvm-dwarfdump/fuzzer/llvm-dwarfdump-fuzzer.cpp b/tools/llvm-dwarfdump/fuzzer/llvm-dwarfdump-fuzzer.cpp
index af0ac36522875..32e173f9d1fce 100644
--- a/tools/llvm-dwarfdump/fuzzer/llvm-dwarfdump-fuzzer.cpp
+++ b/tools/llvm-dwarfdump/fuzzer/llvm-dwarfdump-fuzzer.cpp
@@ -24,10 +24,12 @@ extern "C" void LLVMFuzzerTestOneInput(uint8_t *data, size_t size) {
std::unique_ptr<MemoryBuffer> Buff = MemoryBuffer::getMemBuffer(
StringRef((const char *)data, size), "", false);
- ErrorOr<std::unique_ptr<ObjectFile>> ObjOrErr =
+ Expected<std::unique_ptr<ObjectFile>> ObjOrErr =
ObjectFile::createObjectFile(Buff->getMemBufferRef());
- if (!ObjOrErr)
+ if (auto E = ObjOrErr.takeError()) {
+ consumeError(std::move(E));
return;
+ }
ObjectFile &Obj = *ObjOrErr.get();
std::unique_ptr<DIContext> DICtx(new DWARFContextInMemory(Obj));
DICtx->dump(nulls(), DIDT_All);
diff --git a/tools/llvm-dwarfdump/llvm-dwarfdump.cpp b/tools/llvm-dwarfdump/llvm-dwarfdump.cpp
index eaacc7c5f21b7..4b3a011f861d1 100644
--- a/tools/llvm-dwarfdump/llvm-dwarfdump.cpp
+++ b/tools/llvm-dwarfdump/llvm-dwarfdump.cpp
@@ -29,7 +29,6 @@
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cstring>
-#include <list>
#include <string>
#include <system_error>
@@ -96,16 +95,17 @@ static void DumpInput(StringRef Filename) {
error(Filename, BuffOrErr.getError());
std::unique_ptr<MemoryBuffer> Buff = std::move(BuffOrErr.get());
- ErrorOr<std::unique_ptr<Binary>> BinOrErr =
+ Expected<std::unique_ptr<Binary>> BinOrErr =
object::createBinary(Buff->getMemBufferRef());
- error(Filename, BinOrErr.getError());
+ if (!BinOrErr)
+ error(Filename, errorToErrorCode(BinOrErr.takeError()));
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());
+ error(Filename, errorToErrorCode(MachOOrErr.takeError()));
DumpObjectFile(**MachOOrErr,
Filename + " (" + ObjForArch.getArchTypeName() + ")");
}
@@ -114,7 +114,7 @@ static void DumpInput(StringRef Filename) {
/// 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) {
+static std::vector<std::string> expandBundle(const std::string &InputPath) {
std::vector<std::string> BundlePaths;
SmallString<256> BundlePath(InputPath);
// Manually open up the bundle to avoid introducing additional dependencies.
@@ -146,7 +146,7 @@ static std::vector<std::string> expandBundle(std::string InputPath) {
int main(int argc, char **argv) {
// Print a stack trace if we signal out.
- sys::PrintStackTraceOnErrorSignal();
+ sys::PrintStackTraceOnErrorSignal(argv[0]);
PrettyStackTraceProgram X(argc, argv);
llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
@@ -158,7 +158,7 @@ int main(int argc, char **argv) {
// Expand any .dSYM bundles to the individual object files contained therein.
std::vector<std::string> Objects;
- for (auto F : InputFilenames) {
+ for (const auto &F : InputFilenames) {
auto Objs = expandBundle(F);
Objects.insert(Objects.end(), Objs.begin(), Objs.end());
}
diff --git a/tools/llvm-dwp/CMakeLists.txt b/tools/llvm-dwp/CMakeLists.txt
index b29c00d49c3d3..740ab5ca9040e 100644
--- a/tools/llvm-dwp/CMakeLists.txt
+++ b/tools/llvm-dwp/CMakeLists.txt
@@ -10,4 +10,5 @@ set(LLVM_LINK_COMPONENTS
add_llvm_tool(llvm-dwp
llvm-dwp.cpp
+ DWPError.cpp
)
diff --git a/tools/llvm-dwp/DWPError.cpp b/tools/llvm-dwp/DWPError.cpp
new file mode 100644
index 0000000000000..21d53ed6d198c
--- /dev/null
+++ b/tools/llvm-dwp/DWPError.cpp
@@ -0,0 +1,3 @@
+#include "DWPError.h"
+using namespace llvm;
+char DWPError::ID;
diff --git a/tools/llvm-dwp/DWPError.h b/tools/llvm-dwp/DWPError.h
new file mode 100644
index 0000000000000..62025ed4caa55
--- /dev/null
+++ b/tools/llvm-dwp/DWPError.h
@@ -0,0 +1,23 @@
+#ifndef TOOLS_LLVM_DWP_DWPERROR
+#define TOOLS_LLVM_DWP_DWPERROR
+
+#include "llvm/Support/Error.h"
+#include "llvm/Support/ErrorHandling.h"
+#include <string>
+
+namespace llvm {
+class DWPError : public ErrorInfo<DWPError> {
+public:
+ DWPError(std::string Info) : Info(std::move(Info)) {}
+ void log(raw_ostream &OS) const override { OS << Info; }
+ std::error_code convertToErrorCode() const override {
+ llvm_unreachable("Not implemented");
+ }
+ static char ID;
+
+private:
+ std::string Info;
+};
+}
+
+#endif
diff --git a/tools/llvm-dwp/DWPStringPool.h b/tools/llvm-dwp/DWPStringPool.h
new file mode 100644
index 0000000000000..7d41176b56197
--- /dev/null
+++ b/tools/llvm-dwp/DWPStringPool.h
@@ -0,0 +1,56 @@
+#ifndef TOOLS_LLVM_DWP_DWPSTRINGPOOL
+#define TOOLS_LLVM_DWP_DWPSTRINGPOOL
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/MC/MCSection.h"
+#include "llvm/MC/MCStreamer.h"
+#include <cassert>
+
+namespace llvm {
+class DWPStringPool {
+
+ struct CStrDenseMapInfo {
+ static inline const char *getEmptyKey() {
+ return reinterpret_cast<const char *>(~static_cast<uintptr_t>(0));
+ }
+ static inline const char *getTombstoneKey() {
+ return reinterpret_cast<const char *>(~static_cast<uintptr_t>(1));
+ }
+ static unsigned getHashValue(const char *Val) {
+ assert(Val != getEmptyKey() && "Cannot hash the empty key!");
+ assert(Val != getTombstoneKey() && "Cannot hash the tombstone key!");
+ return (unsigned)hash_value(StringRef(Val));
+ }
+ static bool isEqual(const char *LHS, const char *RHS) {
+ if (RHS == getEmptyKey())
+ return LHS == getEmptyKey();
+ if (RHS == getTombstoneKey())
+ return LHS == getTombstoneKey();
+ return strcmp(LHS, RHS) == 0;
+ }
+ };
+
+ MCStreamer &Out;
+ MCSection *Sec;
+ DenseMap<const char *, uint32_t, CStrDenseMapInfo> Pool;
+ uint32_t Offset = 0;
+
+public:
+ DWPStringPool(MCStreamer &Out, MCSection *Sec) : Out(Out), Sec(Sec) {}
+
+ uint32_t getOffset(const char *Str, unsigned Length) {
+ assert(strlen(Str) + 1 == Length && "Ensure length hint is correct");
+
+ auto Pair = Pool.insert(std::make_pair(Str, Offset));
+ if (Pair.second) {
+ Out.SwitchSection(Sec);
+ Out.EmitBytes(StringRef(Str, Length));
+ Offset += Length;
+ }
+
+ return Pair.first->second;
+ }
+};
+}
+
+#endif
diff --git a/tools/llvm-dwp/Makefile b/tools/llvm-dwp/Makefile
deleted file mode 100644
index 826371ecf915f..0000000000000
--- a/tools/llvm-dwp/Makefile
+++ /dev/null
@@ -1,18 +0,0 @@
-##===- 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
index 570854b849c17..7bd3d3f3f4d08 100644
--- a/tools/llvm-dwp/llvm-dwp.cpp
+++ b/tools/llvm-dwp/llvm-dwp.cpp
@@ -1,3 +1,19 @@
+//===-- llvm-dwp.cpp - Split DWARF merging tool for llvm ------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// A utility for merging DWARF 5 Split DWARF .dwo files into .dwp (DWARF
+// package files).
+//
+//===----------------------------------------------------------------------===//
+#include "DWPError.h"
+#include "DWPStringPool.h"
+#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/CodeGen/AsmPrinter.h"
@@ -12,7 +28,9 @@
#include "llvm/MC/MCStreamer.h"
#include "llvm/MC/MCTargetOptionsCommandFlags.h"
#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/Compression.h"
#include "llvm/Support/DataExtractor.h"
+#include "llvm/Support/Error.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/MemoryBuffer.h"
@@ -21,9 +39,9 @@
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Target/TargetMachine.h"
-#include <list>
+#include <deque>
+#include <iostream>
#include <memory>
-#include <unordered_set>
using namespace llvm;
using namespace llvm::object;
@@ -38,21 +56,14 @@ static opt<std::string> OutputFilename(Required, "o",
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) {
+static void writeStringsAndOffsets(MCStreamer &Out, DWPStringPool &Strings,
+ 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();
+ return;
DenseMap<uint32_t, uint32_t> OffsetRemapping;
@@ -60,15 +71,8 @@ writeStringsAndOffsets(MCStreamer &Out, StringMap<uint32_t> &Strings,
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;
+ OffsetRemapping[PrevOffset] =
+ Strings.getOffset(s, LocalOffset - PrevOffset);
PrevOffset = LocalOffset;
}
@@ -83,8 +87,6 @@ writeStringsAndOffsets(MCStreamer &Out, StringMap<uint32_t> &Strings,
auto NewOffset = OffsetRemapping[OldOffset];
Out.EmitIntValue(NewOffset, 4);
}
-
- return std::error_code();
}
static uint32_t getCUAbbrev(StringRef Abbrev, uint64_t AbbrCode) {
@@ -103,7 +105,32 @@ static uint32_t getCUAbbrev(StringRef Abbrev, uint64_t AbbrCode) {
return Offset;
}
-static uint64_t getCUSignature(StringRef Abbrev, StringRef Info) {
+struct CompileUnitIdentifiers {
+ uint64_t Signature = 0;
+ const char *Name = "";
+ const char *DWOName = "";
+};
+
+static Expected<const char *>
+getIndexedString(uint32_t Form, DataExtractor InfoData, uint32_t &InfoOffset,
+ StringRef StrOffsets, StringRef Str) {
+ if (Form == dwarf::DW_FORM_string)
+ return InfoData.getCStr(&InfoOffset);
+ if (Form != dwarf::DW_FORM_GNU_str_index)
+ return make_error<DWPError>(
+ "string field encoded without DW_FORM_string or DW_FORM_GNU_str_index");
+ auto StrIndex = InfoData.getULEB128(&InfoOffset);
+ DataExtractor StrOffsetsData(StrOffsets, true, 0);
+ uint32_t StrOffsetsOffset = 4 * StrIndex;
+ uint32_t StrOffset = StrOffsetsData.getU32(&StrOffsetsOffset);
+ DataExtractor StrData(Str, true, 0);
+ return StrData.getCStr(&StrOffset);
+}
+
+static Expected<CompileUnitIdentifiers> getCUIdentifiers(StringRef Abbrev,
+ StringRef Info,
+ StringRef StrOffsets,
+ StringRef Str) {
uint32_t Offset = 0;
DataExtractor InfoData(Info, true, 0);
InfoData.getU32(&Offset); // Length
@@ -116,79 +143,141 @@ static uint64_t getCUSignature(StringRef Abbrev, StringRef Info) {
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);
+ if (Tag != dwarf::DW_TAG_compile_unit)
+ return make_error<DWPError>("top level DIE is not a compile unit");
// DW_CHILDREN
AbbrevData.getU8(&AbbrevOffset);
uint32_t Name;
uint32_t Form;
+ CompileUnitIdentifiers ID;
while ((Name = AbbrevData.getULEB128(&AbbrevOffset)) |
(Form = AbbrevData.getULEB128(&AbbrevOffset)) &&
- Name != dwarf::DW_AT_GNU_dwo_id) {
- DWARFFormValue::skipValue(Form, InfoData, &Offset, Version, AddrSize);
+ (Name != 0 || Form != 0)) {
+ switch (Name) {
+ case dwarf::DW_AT_name: {
+ Expected<const char *> EName =
+ getIndexedString(Form, InfoData, Offset, StrOffsets, Str);
+ if (!EName)
+ return EName.takeError();
+ ID.Name = *EName;
+ break;
+ }
+ case dwarf::DW_AT_GNU_dwo_name: {
+ Expected<const char *> EName =
+ getIndexedString(Form, InfoData, Offset, StrOffsets, Str);
+ if (!EName)
+ return EName.takeError();
+ ID.DWOName = *EName;
+ break;
+ }
+ case dwarf::DW_AT_GNU_dwo_id:
+ ID.Signature = InfoData.getU64(&Offset);
+ break;
+ default:
+ DWARFFormValue::skipValue(Form, InfoData, &Offset, Version, AddrSize);
+ }
}
- // FIXME: Real error handling
- assert(Name == dwarf::DW_AT_GNU_dwo_id);
- return InfoData.getU64(&Offset);
+ return ID;
}
struct UnitIndexEntry {
- uint64_t Signature;
DWARFUnitIndex::Entry::SectionContribution Contributions[8];
+ std::string Name;
+ std::string DWOName;
+ StringRef DWPName;
};
-static void addAllTypes(MCStreamer &Out,
- std::vector<UnitIndexEntry> &TypeIndexEntries,
- MCSection *OutputTypes, StringRef Types,
- const UnitIndexEntry &CUEntry, uint32_t &TypesOffset) {
- if (Types.empty())
- return;
+static StringRef getSubsection(StringRef Section,
+ const DWARFUnitIndex::Entry &Entry,
+ DWARFSectionKind Kind) {
+ const auto *Off = Entry.getOffset(Kind);
+ if (!Off)
+ return StringRef();
+ return Section.substr(Off->Offset, Off->Length);
+}
+static void addAllTypesFromDWP(
+ MCStreamer &Out, MapVector<uint64_t, UnitIndexEntry> &TypeIndexEntries,
+ const DWARFUnitIndex &TUIndex, MCSection *OutputTypes, StringRef Types,
+ const UnitIndexEntry &TUEntry, uint32_t &TypesOffset) {
Out.SwitchSection(OutputTypes);
- uint32_t Offset = 0;
- DataExtractor Data(Types, true, 0);
- while (Data.isValidOffset(Offset)) {
- UnitIndexEntry Entry = CUEntry;
+ for (const DWARFUnitIndex::Entry &E : TUIndex.getRows()) {
+ auto *I = E.getOffsets();
+ if (!I)
+ continue;
+ auto P = TypeIndexEntries.insert(std::make_pair(E.getSignature(), TUEntry));
+ if (!P.second)
+ continue;
+ auto &Entry = P.first->second;
// Zero out the debug_info contribution
Entry.Contributions[0] = {};
+ for (auto Kind : TUIndex.getColumnKinds()) {
+ auto &C = Entry.Contributions[Kind - DW_SECT_INFO];
+ C.Offset += I->Offset;
+ C.Length = I->Length;
+ ++I;
+ }
auto &C = Entry.Contributions[DW_SECT_TYPES - DW_SECT_INFO];
+ Out.EmitBytes(Types.substr(
+ C.Offset - TUEntry.Contributions[DW_SECT_TYPES - DW_SECT_INFO].Offset,
+ C.Length));
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 addAllTypes(MCStreamer &Out,
+ MapVector<uint64_t, UnitIndexEntry> &TypeIndexEntries,
+ MCSection *OutputTypes,
+ const std::vector<StringRef> &TypesSections,
+ const UnitIndexEntry &CUEntry, uint32_t &TypesOffset) {
+ for (StringRef Types : TypesSections) {
+ 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
+ auto Signature = Data.getU64(&Offset);
+ Offset = PrevOffset + C.Length;
+
+ auto P = TypeIndexEntries.insert(std::make_pair(Signature, Entry));
+ if (!P.second)
+ continue;
+
+ Out.EmitBytes(Types.substr(PrevOffset, C.Length));
+ TypesOffset += C.Length;
+ }
}
}
static void
writeIndexTable(MCStreamer &Out, ArrayRef<unsigned> ContributionOffsets,
- ArrayRef<UnitIndexEntry> IndexEntries,
+ const MapVector<uint64_t, UnitIndexEntry> &IndexEntries,
uint32_t DWARFUnitIndex::Entry::SectionContribution::*Field) {
for (const auto &E : IndexEntries)
- for (size_t i = 0; i != array_lengthof(E.Contributions); ++i)
+ for (size_t i = 0; i != array_lengthof(E.second.Contributions); ++i)
if (ContributionOffsets[i])
- Out.EmitIntValue(E.Contributions[i].*Field, 4);
+ Out.EmitIntValue(E.second.Contributions[i].*Field, 4);
}
-static void writeIndex(MCStreamer &Out, MCSection *Section,
- ArrayRef<unsigned> ContributionOffsets,
- ArrayRef<UnitIndexEntry> IndexEntries) {
+static void
+writeIndex(MCStreamer &Out, MCSection *Section,
+ ArrayRef<unsigned> ContributionOffsets,
+ const MapVector<uint64_t, UnitIndexEntry> &IndexEntries) {
+ if (IndexEntries.empty())
+ return;
+
unsigned Columns = 0;
for (auto &C : ContributionOffsets)
if (C)
@@ -196,15 +285,18 @@ static void writeIndex(MCStreamer &Out, MCSection *Section,
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;
+ size_t i = 0;
+ for (const auto &P : IndexEntries) {
+ auto S = P.first;
auto H = S & Mask;
+ auto HP = ((S >> 32) & Mask) | 1;
while (Buckets[H]) {
- assert(S != IndexEntries[Buckets[H] - 1].Signature &&
- "Duplicate type unit");
- H += ((S >> 32) & Mask) | 1;
+ assert(S != IndexEntries.begin()[Buckets[H] - 1].first &&
+ "Duplicate unit");
+ H = (H + HP) & Mask;
}
Buckets[H] = i + 1;
+ ++i;
}
Out.SwitchSection(Section);
@@ -215,7 +307,7 @@ static void writeIndex(MCStreamer &Out, MCSection *Section,
// Write the signatures.
for (const auto &I : Buckets)
- Out.EmitIntValue(I ? IndexEntries[I - 1].Signature : 0, 8);
+ Out.EmitIntValue(I ? IndexEntries.begin()[I - 1].first : 0, 8);
// Write the indexes.
for (const auto &I : Buckets)
@@ -234,11 +326,149 @@ static void writeIndex(MCStreamer &Out, MCSection *Section,
writeIndexTable(Out, ContributionOffsets, IndexEntries,
&DWARFUnitIndex::Entry::SectionContribution::Length);
}
-static std::error_code write(MCStreamer &Out, ArrayRef<std::string> Inputs) {
+static bool consumeCompressedDebugSectionHeader(StringRef &data,
+ uint64_t &OriginalSize) {
+ // Consume "ZLIB" prefix.
+ if (!data.startswith("ZLIB"))
+ return false;
+ data = data.substr(4);
+ // Consume uncompressed section size (big-endian 8 bytes).
+ DataExtractor extractor(data, false, 8);
+ uint32_t Offset = 0;
+ OriginalSize = extractor.getU64(&Offset);
+ if (Offset == 0)
+ return false;
+ data = data.substr(Offset);
+ return true;
+}
+
+std::string buildDWODescription(StringRef Name, StringRef DWPName, StringRef DWOName) {
+ std::string Text = "\'";
+ Text += Name;
+ Text += '\'';
+ if (!DWPName.empty()) {
+ Text += " (from ";
+ if (!DWOName.empty()) {
+ Text += '\'';
+ Text += DWOName;
+ Text += "' in ";
+ }
+ Text += '\'';
+ Text += DWPName;
+ Text += "')";
+ }
+ return Text;
+}
+
+static Error handleCompressedSection(
+ std::deque<SmallString<32>> &UncompressedSections, StringRef &Name,
+ StringRef &Contents) {
+ if (!Name.startswith("zdebug_"))
+ return Error();
+ UncompressedSections.emplace_back();
+ uint64_t OriginalSize;
+ if (!zlib::isAvailable())
+ return make_error<DWPError>("zlib not available");
+ if (!consumeCompressedDebugSectionHeader(Contents, OriginalSize) ||
+ zlib::uncompress(Contents, UncompressedSections.back(), OriginalSize) !=
+ zlib::StatusOK)
+ return make_error<DWPError>(
+ ("failure while decompressing compressed section: '" + Name + "\'")
+ .str());
+ Name = Name.substr(1);
+ Contents = UncompressedSections.back();
+ return Error();
+}
+
+static Error handleSection(
+ const StringMap<std::pair<MCSection *, DWARFSectionKind>> &KnownSections,
+ const MCSection *StrSection, const MCSection *StrOffsetSection,
+ const MCSection *TypesSection, const MCSection *CUIndexSection,
+ const MCSection *TUIndexSection, const SectionRef &Section, MCStreamer &Out,
+ std::deque<SmallString<32>> &UncompressedSections,
+ uint32_t (&ContributionOffsets)[8], UnitIndexEntry &CurEntry,
+ StringRef &CurStrSection, StringRef &CurStrOffsetSection,
+ std::vector<StringRef> &CurTypesSection, StringRef &InfoSection,
+ StringRef &AbbrevSection, StringRef &CurCUIndexSection,
+ StringRef &CurTUIndexSection) {
+ if (Section.isBSS())
+ return Error();
+
+ if (Section.isVirtual())
+ return Error();
+
+ StringRef Name;
+ if (std::error_code Err = Section.getName(Name))
+ return errorCodeToError(Err);
+
+ Name = Name.substr(Name.find_first_not_of("._"));
+
+ StringRef Contents;
+ if (auto Err = Section.getContents(Contents))
+ return errorCodeToError(Err);
+
+ if (auto Err = handleCompressedSection(UncompressedSections, Name, Contents))
+ return Err;
+
+ auto SectionPair = KnownSections.find(Name);
+ if (SectionPair == KnownSections.end())
+ return Error();
+
+ 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.push_back(Contents);
+ else if (OutSection == CUIndexSection)
+ CurCUIndexSection = Contents;
+ else if (OutSection == TUIndexSection)
+ CurTUIndexSection = Contents;
+ else {
+ Out.SwitchSection(OutSection);
+ Out.EmitBytes(Contents);
+ }
+ return Error();
+}
+
+static Error
+buildDuplicateError(const std::pair<uint64_t, UnitIndexEntry> &PrevE,
+ const CompileUnitIdentifiers &ID, StringRef DWPName) {
+ return make_error<DWPError>(
+ std::string("Duplicate DWO ID (") + utohexstr(PrevE.first) + ") in " +
+ buildDWODescription(PrevE.second.Name, PrevE.second.DWPName,
+ PrevE.second.DWOName) +
+ " and " + buildDWODescription(ID.Name, DWPName, ID.DWOName));
+}
+
+static Error 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();
+ MCSection *const CUIndexSection = MCOFI.getDwarfCUIndexSection();
+ MCSection *const TUIndexSection = MCOFI.getDwarfTUIndexSection();
const StringMap<std::pair<MCSection *, DWARFSectionKind>> KnownSections = {
{"debug_info.dwo", {MCOFI.getDwarfInfoDWOSection(), DW_SECT_INFO}},
{"debug_types.dwo", {MCOFI.getDwarfTypesDWOSection(), DW_SECT_TYPES}},
@@ -246,96 +476,121 @@ static std::error_code write(MCStreamer &Out, ArrayRef<std::string> Inputs) {
{"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;
+ {"debug_abbrev.dwo", {MCOFI.getDwarfAbbrevDWOSection(), DW_SECT_ABBREV}},
+ {"debug_cu_index", {CUIndexSection, static_cast<DWARFSectionKind>(0)}},
+ {"debug_tu_index", {TUIndexSection, static_cast<DWARFSectionKind>(0)}}};
- StringMap<uint32_t> Strings;
- uint32_t StringOffset = 0;
+ MapVector<uint64_t, UnitIndexEntry> IndexEntries;
+ MapVector<uint64_t, UnitIndexEntry> TypeIndexEntries;
uint32_t ContributionOffsets[8] = {};
+ DWPStringPool Strings(Out, StrSection);
+
+ SmallVector<OwningBinary<object::ObjectFile>, 128> Objects;
+ Objects.reserve(Inputs.size());
+
+ std::deque<SmallString<32>> UncompressedSections;
+
for (const auto &Input : Inputs) {
auto ErrOrObj = object::ObjectFile::createObjectFile(Input);
if (!ErrOrObj)
- return ErrOrObj.getError();
+ return ErrOrObj.takeError();
- IndexEntries.emplace_back();
- UnitIndexEntry &CurEntry = IndexEntries.back();
+ auto &Obj = *ErrOrObj->getBinary();
+ Objects.push_back(std::move(*ErrOrObj));
+
+ UnitIndexEntry CurEntry = {};
StringRef CurStrSection;
StringRef CurStrOffsetSection;
- StringRef CurTypesSection;
+ std::vector<StringRef> CurTypesSection;
StringRef InfoSection;
StringRef AbbrevSection;
-
- for (const auto &Section : ErrOrObj->getBinary()->sections()) {
- StringRef Name;
- if (std::error_code Err = Section.getName(Name))
+ StringRef CurCUIndexSection;
+ StringRef CurTUIndexSection;
+
+ for (const auto &Section : Obj.sections())
+ if (auto Err = handleSection(
+ KnownSections, StrSection, StrOffsetSection, TypesSection,
+ CUIndexSection, TUIndexSection, Section, Out,
+ UncompressedSections, ContributionOffsets, CurEntry,
+ CurStrSection, CurStrOffsetSection, CurTypesSection, InfoSection,
+ AbbrevSection, CurCUIndexSection, CurTUIndexSection))
return Err;
- auto SectionPair =
- KnownSections.find(Name.substr(Name.find_first_not_of("._")));
- if (SectionPair == KnownSections.end())
- continue;
+ if (InfoSection.empty())
+ continue;
- StringRef Contents;
- if (auto Err = Section.getContents(Contents))
- return Err;
+ writeStringsAndOffsets(Out, Strings, StrOffsetSection, CurStrSection,
+ CurStrOffsetSection);
+
+ if (CurCUIndexSection.empty()) {
+ Expected<CompileUnitIdentifiers> EID = getCUIdentifiers(
+ AbbrevSection, InfoSection, CurStrOffsetSection, CurStrSection);
+ if (!EID)
+ return EID.takeError();
+ const auto &ID = *EID;
+ auto P = IndexEntries.insert(std::make_pair(ID.Signature, CurEntry));
+ if (!P.second)
+ return buildDuplicateError(*P.first, ID, "");
+ P.first->second.Name = ID.Name;
+ P.first->second.DWOName = ID.DWOName;
+ addAllTypes(Out, TypeIndexEntries, TypesSection, CurTypesSection,
+ CurEntry, ContributionOffsets[DW_SECT_TYPES - DW_SECT_INFO]);
+ continue;
+ }
- 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;
- }
- }
+ DWARFUnitIndex CUIndex(DW_SECT_INFO);
+ DataExtractor CUIndexData(CurCUIndexSection, Obj.isLittleEndian(), 0);
+ if (!CUIndex.parse(CUIndexData))
+ return make_error<DWPError>("Failed to parse cu_index");
- 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);
+ for (const DWARFUnitIndex::Entry &E : CUIndex.getRows()) {
+ auto *I = E.getOffsets();
+ if (!I)
+ continue;
+ auto P = IndexEntries.insert(std::make_pair(E.getSignature(), CurEntry));
+ Expected<CompileUnitIdentifiers> EID = getCUIdentifiers(
+ getSubsection(AbbrevSection, E, DW_SECT_ABBREV),
+ getSubsection(InfoSection, E, DW_SECT_INFO),
+ getSubsection(CurStrOffsetSection, E, DW_SECT_STR_OFFSETS),
+ CurStrSection);
+ if (!EID)
+ return EID.takeError();
+ const auto &ID = *EID;
+ if (!P.second)
+ return buildDuplicateError(*P.first, ID, Input);
+ auto &NewEntry = P.first->second;
+ NewEntry.Name = ID.Name;
+ NewEntry.DWOName = ID.DWOName;
+ NewEntry.DWPName = Input;
+ for (auto Kind : CUIndex.getColumnKinds()) {
+ auto &C = NewEntry.Contributions[Kind - DW_SECT_INFO];
+ C.Offset += I->Offset;
+ C.Length = I->Length;
+ ++I;
}
}
- 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 (!CurTypesSection.empty()) {
+ if (CurTypesSection.size() != 1)
+ return make_error<DWPError>("multiple type unit sections in .dwp file");
+ DWARFUnitIndex TUIndex(DW_SECT_TYPES);
+ DataExtractor TUIndexData(CurTUIndexSection, Obj.isLittleEndian(), 0);
+ if (!TUIndex.parse(TUIndexData))
+ return make_error<DWPError>("Failed to parse tu_index");
+ addAllTypesFromDWP(Out, TypeIndexEntries, TUIndex, TypesSection,
+ CurTypesSection.front(), CurEntry,
+ ContributionOffsets[DW_SECT_TYPES - DW_SECT_INFO]);
+ }
}
- 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 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;
@@ -345,7 +600,13 @@ static std::error_code write(MCStreamer &Out, ArrayRef<std::string> Inputs) {
writeIndex(Out, MCOFI.getDwarfCUIndexSection(), ContributionOffsets,
IndexEntries);
- return std::error_code();
+ return Error();
+}
+
+static int error(const Twine &Error, const Twine &Context) {
+ errs() << Twine("while processing ") + Context + ":\n";
+ errs() << Twine("error: ") + Error + "\n";
+ return 1;
}
int main(int argc, char **argv) {
@@ -380,7 +641,7 @@ int main(int argc, char **argv) {
MCObjectFileInfo MOFI;
MCContext MC(MAI.get(), MRI.get(), &MOFI);
- MOFI.InitMCObjectFileInfo(TheTriple, Reloc::Default, CodeModel::Default, MC);
+ MOFI.InitMCObjectFileInfo(TheTriple, /*PIC*/ false, CodeModel::Default, MC);
auto MAB = TheTarget->createMCAsmBackend(*MRI, TripleName, "");
if (!MAB)
@@ -413,8 +674,10 @@ int main(int argc, char **argv) {
if (!MS)
return error("no object streamer for target " + TripleName, Context);
- if (auto Err = write(*MS, InputFiles))
- return error(Err.message(), "Writing DWP file");
+ if (auto Err = write(*MS, InputFiles)) {
+ logAllUnhandledErrors(std::move(Err), errs(), "error: ");
+ return 1;
+ }
MS->Finish();
}
diff --git a/tools/llvm-extract/Makefile b/tools/llvm-extract/Makefile
deleted file mode 100644
index d371c54759263..0000000000000
--- a/tools/llvm-extract/Makefile
+++ /dev/null
@@ -1,17 +0,0 @@
-##===- tools/llvm-extract/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-extract
-LINK_COMPONENTS := ipo bitreader bitwriter asmparser irreader
-
-# This tool has no plugins, optimize startup time.
-TOOL_NO_EXPORTS := 1
-
-include $(LEVEL)/Makefile.common
diff --git a/tools/llvm-extract/llvm-extract.cpp b/tools/llvm-extract/llvm-extract.cpp
index 1da456d33f52d..900c461ebd861 100644
--- a/tools/llvm-extract/llvm-extract.cpp
+++ b/tools/llvm-extract/llvm-extract.cpp
@@ -102,10 +102,10 @@ static cl::opt<bool> PreserveAssemblyUseListOrder(
int main(int argc, char **argv) {
// Print a stack trace if we signal out.
- sys::PrintStackTraceOnErrorSignal();
+ sys::PrintStackTraceOnErrorSignal(argv[0]);
PrettyStackTraceProgram X(argc, argv);
- LLVMContext &Context = getGlobalContext();
+ LLVMContext Context;
llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
cl::ParseCommandLineOptions(argc, argv, "llvm extractor\n");
diff --git a/tools/llvm-go/Makefile b/tools/llvm-go/Makefile
deleted file mode 100644
index 4465b2a4a9dbd..0000000000000
--- a/tools/llvm-go/Makefile
+++ /dev/null
@@ -1,16 +0,0 @@
-##===- tools/llvm-go/Makefile ------------------------------*- Makefile -*-===##
-#
-# The LLVM Compiler Infrastructure
-#
-# This file is distributed under the University of Illinois Open Source
-# License. See LICENSE.TXT for details.
-#
-##===----------------------------------------------------------------------===##
-
-LEVEL := ../..
-include $(LEVEL)/Makefile.common
-
-all:: $(ToolDir)/llvm-go$(EXEEXT)
-
-$(ToolDir)/llvm-go$(EXEEXT): $(PROJ_SRC_DIR)/llvm-go.go
- $(GO) build -o $@ $<
diff --git a/tools/llvm-go/llvm-go.go b/tools/llvm-go/llvm-go.go
index ed79ca67b89d3..12f0d73b4cc48 100644
--- a/tools/llvm-go/llvm-go.go
+++ b/tools/llvm-go/llvm-go.go
@@ -88,17 +88,9 @@ func llvmConfig(args ...string) string {
return outstr
}
-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")
+func llvmFlags() compilerFlags {
+ args := append([]string{"--ldflags", "--libs", "--system-libs"}, components...)
+ ldflags := llvmConfig(args...)
if runtime.GOOS != "darwin" {
// OS X doesn't like -rpath with cgo. See:
// https://code.google.com/p/go/issues/detail?id=7293
@@ -133,8 +125,8 @@ func printComponents() {
fmt.Println(strings.Join(components, " "))
}
-func printConfig(linkmode string) {
- flags := llvmFlags(linkmode)
+func printConfig() {
+ flags := llvmFlags()
fmt.Printf(`// +build !byollvm
@@ -153,7 +145,7 @@ type (run_build_sh int)
`, flags.cpp, flags.cxx, flags.ld)
}
-func runGoWithLLVMEnv(args []string, cc, cxx, gocmd, llgo, cppflags, cxxflags, ldflags, linkmode string) {
+func runGoWithLLVMEnv(args []string, cc, cxx, gocmd, llgo, cppflags, cxxflags, ldflags string) {
args = addTag(args, "byollvm")
srcdir := llvmConfig("--src-root")
@@ -182,7 +174,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(linkmode)
+ flags := llvmFlags()
newenv := []string{
"CC=" + cc,
@@ -250,7 +242,6 @@ func main() {
ldflags := os.Getenv("CGO_LDFLAGS")
gocmd := "go"
llgo := ""
- linkmode := linkmodeComponentLibs
flags := []struct {
name string
@@ -262,7 +253,6 @@ func main() {
{"llgo", &llgo},
{"cppflags", &cppflags},
{"ldflags", &ldflags},
- {"linkmode", &linkmode},
}
args := os.Args[1:]
@@ -283,11 +273,11 @@ LOOP:
switch args[0] {
case "build", "get", "install", "run", "test":
- runGoWithLLVMEnv(args, cc, cxx, gocmd, llgo, cppflags, cxxflags, ldflags, linkmode)
+ runGoWithLLVMEnv(args, cc, cxx, gocmd, llgo, cppflags, cxxflags, ldflags)
case "print-components":
printComponents()
case "print-config":
- printConfig(linkmode)
+ printConfig()
default:
usage()
}
diff --git a/tools/llvm-jitlistener/Makefile b/tools/llvm-jitlistener/Makefile
deleted file mode 100644
index 4acb6a5617157..0000000000000
--- a/tools/llvm-jitlistener/Makefile
+++ /dev/null
@@ -1,27 +0,0 @@
-##===- tools/llvm-jitlistener/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-jitlistener
-
-include $(LEVEL)/Makefile.config
-
-LINK_COMPONENTS := mcjit interpreter nativecodegen bitreader asmparser irreader selectiondag Object
-
-# If Intel JIT Events support is configured, link against the LLVM Intel JIT
-# Events interface library. If not, this tool will do nothing useful, but it
-# will build correctly.
-ifeq ($(USE_INTEL_JITEVENTS), 1)
- LINK_COMPONENTS += debuginfodwarf inteljitevents
-endif
-
-# This tool has no plugins, optimize startup time.
-TOOL_NO_EXPORTS := 1
-
-include $(LLVM_SRC_ROOT)/Makefile.rules
diff --git a/tools/llvm-jitlistener/llvm-jitlistener.cpp b/tools/llvm-jitlistener/llvm-jitlistener.cpp
index af1a59bdbd377..6b72c17b1fede 100644
--- a/tools/llvm-jitlistener/llvm-jitlistener.cpp
+++ b/tools/llvm-jitlistener/llvm-jitlistener.cpp
@@ -105,8 +105,6 @@ unsigned int GetNewMethodID(void) {
class JitEventListenerTest {
protected:
void InitEE(const std::string &IRFile) {
- LLVMContext &Context = getGlobalContext();
-
// If we have a native target, initialize it to ensure it is linked in and
// usable by the JIT.
InitializeNativeTarget();
@@ -181,7 +179,7 @@ InputFilename(cl::Positional, cl::desc("<input IR file>"),
int main(int argc, char **argv) {
// Print a stack trace if we signal out.
- sys::PrintStackTraceOnErrorSignal();
+ sys::PrintStackTraceOnErrorSignal(argv[0]);
PrettyStackTraceProgram X(argc, argv);
llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
diff --git a/tools/llvm-link/CMakeLists.txt b/tools/llvm-link/CMakeLists.txt
index e4ced186336c6..f69df95947d95 100644
--- a/tools/llvm-link/CMakeLists.txt
+++ b/tools/llvm-link/CMakeLists.txt
@@ -5,6 +5,7 @@ set(LLVM_LINK_COMPONENTS
Linker
Object
Support
+ TransformUtils
)
add_llvm_tool(llvm-link
diff --git a/tools/llvm-link/LLVMBuild.txt b/tools/llvm-link/LLVMBuild.txt
index 807823820ed6b..1dba5c0adb3f0 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 Object
+required_libraries = AsmParser BitReader BitWriter IRReader Linker Object TransformUtils
diff --git a/tools/llvm-link/Makefile b/tools/llvm-link/Makefile
deleted file mode 100644
index a08d2844ceca8..0000000000000
--- a/tools/llvm-link/Makefile
+++ /dev/null
@@ -1,17 +0,0 @@
-##===- tools/llvm-link/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-link
-LINK_COMPONENTS := linker bitreader bitwriter asmparser irreader object
-
-# This tool has no plugins, optimize startup time.
-TOOL_NO_EXPORTS := 1
-
-include $(LEVEL)/Makefile.common
diff --git a/tools/llvm-link/llvm-link.cpp b/tools/llvm-link/llvm-link.cpp
index a32383028ae24..185ae2a82a17b 100644
--- a/tools/llvm-link/llvm-link.cpp
+++ b/tools/llvm-link/llvm-link.cpp
@@ -12,18 +12,18 @@
//
//===----------------------------------------------------------------------===//
-#include "llvm/Linker/Linker.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Bitcode/ReaderWriter.h"
#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/ModuleSummaryIndex.h"
#include "llvm/IR/Verifier.h"
#include "llvm/IRReader/IRReader.h"
-#include "llvm/Object/FunctionIndexObjectFile.h"
+#include "llvm/Linker/Linker.h"
+#include "llvm/Object/ModuleSummaryIndexObjectFile.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/ManagedStatic.h"
@@ -33,7 +33,10 @@
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/SystemUtils.h"
#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Transforms/Utils/FunctionImportUtils.h"
+
#include <memory>
+#include <utility>
using namespace llvm;
static cl::list<std::string>
@@ -52,15 +55,14 @@ static cl::list<std::string> Imports(
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
+// Option to support testing of function importing. The module summary
// 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>
+ SummaryIndex("summary-index", cl::desc("Module summary index filename"),
+ cl::init(""), cl::value_desc("filename"));
static cl::opt<std::string>
OutputFilename("o", cl::desc("Override output filename"), cl::init("-"),
@@ -70,6 +72,10 @@ static cl::opt<bool>
Internalize("internalize", cl::desc("Internalize linked symbols"));
static cl::opt<bool>
+ DisableDITypeMap("disable-debug-info-type-map",
+ cl::desc("Don't use a uniquing type map for debug info"));
+
+static cl::opt<bool>
OnlyNeeded("only-needed", cl::desc("Link only needed symbols"));
static cl::opt<bool>
@@ -89,10 +95,6 @@ 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."),
@@ -114,8 +116,10 @@ static std::unique_ptr<Module> loadFile(const char *argv0,
if (Verbose) errs() << "Loading '" << FN << "'\n";
std::unique_ptr<Module> Result =
getLazyIRFileModule(FN, Err, Context, !MaterializeMetadata);
- if (!Result)
+ if (!Result) {
Err.print(argv0, errs());
+ return nullptr;
+ }
if (MaterializeMetadata) {
Result->materializeMetadata();
@@ -125,6 +129,48 @@ static std::unique_ptr<Module> loadFile(const char *argv0,
return Result;
}
+namespace {
+
+/// Helper to load on demand a Module from file and cache it for subsequent
+/// queries during function importing.
+class ModuleLazyLoaderCache {
+ /// Cache of lazily loaded module for import.
+ StringMap<std::unique_ptr<Module>> ModuleMap;
+
+ /// Retrieve a Module from the cache or lazily load it on demand.
+ std::function<std::unique_ptr<Module>(const char *argv0,
+ const std::string &FileName)>
+ createLazyModule;
+
+public:
+ /// Create the loader, Module will be initialized in \p Context.
+ ModuleLazyLoaderCache(std::function<std::unique_ptr<Module>(
+ const char *argv0, const std::string &FileName)>
+ createLazyModule)
+ : createLazyModule(std::move(createLazyModule)) {}
+
+ /// Retrieve a Module from the cache or lazily load it on demand.
+ Module &operator()(const char *argv0, const std::string &FileName);
+
+ std::unique_ptr<Module> takeModule(const std::string &FileName) {
+ auto I = ModuleMap.find(FileName);
+ assert(I != ModuleMap.end());
+ std::unique_ptr<Module> Ret = std::move(I->second);
+ ModuleMap.erase(I);
+ return Ret;
+ }
+};
+
+// Get a Module for \p FileName from the cache, or load it lazily.
+Module &ModuleLazyLoaderCache::operator()(const char *argv0,
+ const std::string &Identifier) {
+ auto &Module = ModuleMap[Identifier];
+ if (!Module)
+ Module = createLazyModule(argv0, Identifier);
+ return *Module;
+}
+} // anonymous namespace
+
static void diagnosticHandler(const DiagnosticInfo &DI) {
unsigned Severity = DI.getSeverity();
switch (Severity) {
@@ -153,8 +199,24 @@ static void diagnosticHandlerWithContext(const DiagnosticInfo &DI, void *C) {
/// 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;
+ if (SummaryIndex.empty())
+ return true;
+ ErrorOr<std::unique_ptr<ModuleSummaryIndex>> IndexOrErr =
+ llvm::getModuleSummaryIndexForFile(SummaryIndex, diagnosticHandler);
+ std::error_code EC = IndexOrErr.getError();
+ if (EC) {
+ errs() << EC.message() << '\n';
+ return false;
+ }
+ auto Index = std::move(IndexOrErr.get());
+
+ // Map of Module -> List of globals to import from the Module
+ std::map<StringRef, DenseSet<const GlobalValue *>> ModuleToGlobalsToImportMap;
+ auto ModuleLoader = [&Context](const char *argv0,
+ const std::string &Identifier) {
+ return loadFile(argv0, Identifier, Context, false);
+ };
+ ModuleLazyLoaderCache ModuleLoaderCache(ModuleLoader);
for (const auto &Import : Imports) {
// Identify the requested function and its bitcode source file.
size_t Idx = Import.find(':');
@@ -166,19 +228,15 @@ static bool importFunctions(const char *argv0, LLVMContext &Context,
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;
- }
+ auto &SrcModule = ModuleLoaderCache(argv0, FileName);
- if (verifyModule(*M, &errs())) {
+ if (verifyModule(SrcModule, &errs())) {
errs() << argv0 << ": " << FileName
<< ": error: input module is broken!\n";
return false;
}
- Function *F = M->getFunction(FunctionName);
+ Function *F = SrcModule.getFunction(FunctionName);
if (!F) {
errs() << "Ignoring import request for non-existent function "
<< FunctionName << " from " << FileName << "\n";
@@ -196,53 +254,36 @@ static bool importFunctions(const char *argv0, LLVMContext &Context,
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());
- }
+ auto &Entry = ModuleToGlobalsToImportMap[SrcModule.getModuleIdentifier()];
+ Entry.insert(F);
- // 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;
+ F->materialize();
}
- // 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;
- }
+ // Do the actual import of globals now, one Module at a time
+ for (auto &GlobalsToImportPerModule : ModuleToGlobalsToImportMap) {
+ // Get the module for the import
+ auto &GlobalsToImport = GlobalsToImportPerModule.second;
+ std::unique_ptr<Module> SrcModule =
+ ModuleLoaderCache.takeModule(GlobalsToImportPerModule.first);
+ assert(&Context == &SrcModule->getContext() && "Context mismatch");
- if (verifyModule(*M, &errs())) {
- errs() << argv0 << ": " << SME.getKey()
- << ": error: input module is broken!\n";
- return false;
- }
+ // If modules were created with lazy metadata loading, materialize it
+ // now, before linking it (otherwise this will be a noop).
+ SrcModule->materializeMetadata();
+ UpgradeDebugInfo(*SrcModule);
+
+ // Linkage Promotion and renaming
+ if (renameModuleForThinLTO(*SrcModule, *Index, &GlobalsToImport))
+ return true;
+
+ // Instruct the linker to not automatically import linkonce defintion.
+ unsigned Flags = Linker::Flags::DontForceLinkLinkonceODR;
- // Link in all necessary metadata from this module.
- if (L.linkInMetadata(*M, SME.getValue().get()))
+ if (L.linkInModule(std::move(SrcModule), Flags, &GlobalsToImport))
return false;
}
+
return true;
}
@@ -258,41 +299,38 @@ static bool linkFiles(const char *argv0, LLVMContext &Context, Linker &L,
return false;
}
- if (verifyModule(*M, &errs())) {
+ // Note that when ODR merging types cannot verify input files in here When
+ // doing that debug metadata in the src module might already be pointing to
+ // the destination.
+ if (DisableDITypeMap && verifyModule(*M, &errs())) {
errs() << argv0 << ": " << File << ": error: input module is broken!\n";
return false;
}
- // If a function index is supplied, load it so linkInModule can treat
+ // If a module summary 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);
+ if (!SummaryIndex.empty()) {
+ ErrorOr<std::unique_ptr<ModuleSummaryIndex>> IndexOrErr =
+ llvm::getModuleSummaryIndexForFile(SummaryIndex, diagnosticHandler);
std::error_code EC = IndexOrErr.getError();
if (EC) {
errs() << EC.message() << '\n';
return false;
}
- Index = std::move(IndexOrErr.get());
+ auto Index = std::move(IndexOrErr.get());
+
+ // Promotion
+ if (renameModuleForThinLTO(*M, *Index))
+ return true;
}
if (Verbose)
errs() << "Linking in '" << File << "'\n";
- if (L.linkInModule(std::move(M), ApplicableFlags, Index.get()))
+ if (L.linkInModule(std::move(M), ApplicableFlags))
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;
@@ -300,15 +338,18 @@ static bool linkFiles(const char *argv0, LLVMContext &Context, Linker &L,
int main(int argc, char **argv) {
// Print a stack trace if we signal out.
- sys::PrintStackTraceOnErrorSignal();
+ sys::PrintStackTraceOnErrorSignal(argv[0]);
PrettyStackTraceProgram X(argc, argv);
- LLVMContext &Context = getGlobalContext();
+ LLVMContext Context;
Context.setDiagnosticHandler(diagnosticHandlerWithContext, nullptr, true);
llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
cl::ParseCommandLineOptions(argc, argv, "llvm linker\n");
+ if (!DisableDITypeMap)
+ Context.enableDebugTypeODRUniquing();
+
auto Composite = make_unique<Module>("llvm-link", Context);
Linker L(*Composite);
diff --git a/tools/llvm-lto/CMakeLists.txt b/tools/llvm-lto/CMakeLists.txt
index 29b3339e6cea0..de888a74c530b 100644
--- a/tools/llvm-lto/CMakeLists.txt
+++ b/tools/llvm-lto/CMakeLists.txt
@@ -1,7 +1,9 @@
set(LLVM_LINK_COMPONENTS
${LLVM_TARGETS_TO_BUILD}
+ BitReader
BitWriter
Core
+ IRReader
LTO
MC
Object
diff --git a/tools/llvm-lto/LLVMBuild.txt b/tools/llvm-lto/LLVMBuild.txt
index b36f4a981532b..7a86797899dea 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 = BitWriter Core LTO Object Support all-targets
+required_libraries = BitWriter Core IRReader LTO Object Support all-targets
diff --git a/tools/llvm-lto/Makefile b/tools/llvm-lto/Makefile
deleted file mode 100644
index f8ca7e1cac5d0..0000000000000
--- a/tools/llvm-lto/Makefile
+++ /dev/null
@@ -1,17 +0,0 @@
-##===- tools/llvm-lto/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-lto
-LINK_COMPONENTS := lto ipo scalaropts linker bitreader bitwriter mcdisassembler support target vectorize all-targets
-
-# This tool has no plugins, optimize startup time.
-TOOL_NO_EXPORTS := 1
-
-include $(LEVEL)/Makefile.common
diff --git a/tools/llvm-lto/llvm-lto.cpp b/tools/llvm-lto/llvm-lto.cpp
index 232051130cb20..b3b617b941d33 100644
--- a/tools/llvm-lto/llvm-lto.cpp
+++ b/tools/llvm-lto/llvm-lto.cpp
@@ -17,14 +17,19 @@
#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/IR/Verifier.h"
+#include "llvm/IRReader/IRReader.h"
+#include "llvm/LTO/legacy/LTOCodeGenerator.h"
+#include "llvm/LTO/legacy/LTOModule.h"
+#include "llvm/LTO/legacy/ThinLTOCodeGenerator.h"
+#include "llvm/Object/ModuleSummaryIndexObjectFile.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/Path.h"
#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/Signals.h"
+#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/ToolOutputFile.h"
#include "llvm/Support/raw_ostream.h"
@@ -33,59 +38,108 @@
using namespace llvm;
static cl::opt<char>
-OptLevel("O",
- cl::desc("Optimization level. [-O0, -O1, -O2, or -O3] "
- "(default = '-O2')"),
- cl::Prefix,
- cl::ZeroOrMore,
- cl::init('2'));
+ OptLevel("O", cl::desc("Optimization level. [-O0, -O1, -O2, or -O3] "
+ "(default = '-O2')"),
+ cl::Prefix, 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"));
+static cl::opt<bool> DisableInline("disable-inlining", cl::init(false),
+ cl::desc("Do not run the inliner pass"));
static cl::opt<bool>
-DisableGVNLoadPRE("disable-gvn-loadpre", cl::init(false),
- cl::desc("Do not run the GVN load PRE pass"));
+ DisableGVNLoadPRE("disable-gvn-loadpre", cl::init(false),
+ cl::desc("Do not run the GVN load PRE pass"));
-static cl::opt<bool>
-DisableLTOVectorization("disable-lto-vectorization", cl::init(false),
- cl::desc("Do not run loop or slp vectorization during LTO"));
+static cl::opt<bool> DisableLTOVectorization(
+ "disable-lto-vectorization", cl::init(false),
+ cl::desc("Do not run loop or slp vectorization during LTO"));
-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> 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"));
+enum ThinLTOModes {
+ THINLINK,
+ THINDISTRIBUTE,
+ THINEMITIMPORTS,
+ THINPROMOTE,
+ THINIMPORT,
+ THININTERNALIZE,
+ THINOPT,
+ THINCODEGEN,
+ THINALL
+};
-static cl::list<std::string>
-InputFilenames(cl::Positional, cl::OneOrMore,
- cl::desc("<input bitcode files>"));
+cl::opt<ThinLTOModes> ThinLTOMode(
+ "thinlto-action", cl::desc("Perform a single ThinLTO stage:"),
+ cl::values(
+ clEnumValN(
+ THINLINK, "thinlink",
+ "ThinLink: produces the index by linking only the summaries."),
+ clEnumValN(THINDISTRIBUTE, "distributedindexes",
+ "Produces individual indexes for distributed backends."),
+ clEnumValN(THINEMITIMPORTS, "emitimports",
+ "Emit imports files for distributed backends."),
+ clEnumValN(THINPROMOTE, "promote",
+ "Perform pre-import promotion (requires -thinlto-index)."),
+ clEnumValN(THINIMPORT, "import", "Perform both promotion and "
+ "cross-module importing (requires "
+ "-thinlto-index)."),
+ clEnumValN(THININTERNALIZE, "internalize",
+ "Perform internalization driven by -exported-symbol "
+ "(requires -thinlto-index)."),
+ clEnumValN(THINOPT, "optimize", "Perform ThinLTO optimizations."),
+ clEnumValN(THINCODEGEN, "codegen", "CodeGen (expected to match llc)"),
+ clEnumValN(THINALL, "run", "Perform ThinLTO end-to-end"),
+ clEnumValEnd));
static cl::opt<std::string>
-OutputFilename("o", cl::init(""),
- cl::desc("Override output filename"),
- cl::value_desc("filename"));
+ ThinLTOIndex("thinlto-index",
+ cl::desc("Provide the index produced by a ThinLink, required "
+ "to perform the promotion and/or importing."));
+
+static cl::opt<std::string> ThinLTOPrefixReplace(
+ "thinlto-prefix-replace",
+ cl::desc("Control where files for distributed backends are "
+ "created. Expects 'oldprefix;newprefix' and if path "
+ "prefix of output file is oldprefix it will be "
+ "replaced with newprefix."));
+
+static cl::opt<std::string> ThinLTOModuleId(
+ "thinlto-module-id",
+ cl::desc("For the module ID for the file to process, useful to "
+ "match what is in the index."));
-static cl::list<std::string>
-ExportedSymbols("exported-symbol",
- cl::desc("Symbol to export from the resulting object file"),
- cl::ZeroOrMore);
+static cl::opt<std::string>
+ ThinLTOCacheDir("thinlto-cache-dir", cl::desc("Enable ThinLTO caching."));
+
+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>"));
+
+static cl::opt<std::string> OutputFilename("o", cl::init(""),
+ cl::desc("Override output filename"),
+ cl::value_desc("filename"));
+
+static cl::list<std::string> ExportedSymbols(
+ "exported-symbol",
+ cl::desc("List of symbols to export from the resulting object file"),
+ cl::ZeroOrMore);
static cl::list<std::string>
-DSOSymbols("dso-symbol",
- cl::desc("Symbol to put in the symtab in the resulting dso"),
- cl::ZeroOrMore);
+ DSOSymbols("dso-symbol",
+ cl::desc("Symbol to put in the symtab in the resulting dso"),
+ cl::ZeroOrMore);
static cl::opt<bool> ListSymbolsOnly(
"list-symbols-only", cl::init(false),
@@ -98,6 +152,14 @@ static cl::opt<bool> SetMergedModule(
static cl::opt<unsigned> Parallelism("j", cl::Prefix, cl::init(1),
cl::desc("Number of backend threads"));
+static cl::opt<bool> RestoreGlobalsLinkage(
+ "restore-linkage", cl::init(false),
+ cl::desc("Restore original linkage of globals prior to CodeGen"));
+
+static cl::opt<bool> CheckHasObjC(
+ "check-for-objc", cl::init(false),
+ cl::desc("Only check if the module has objective-C defined in it"));
+
namespace {
struct ModuleInfo {
std::vector<bool> CanBeHidden;
@@ -154,8 +216,8 @@ static void diagnosticHandler(const DiagnosticInfo &DI) {
exit(1);
}
-static void diagnosticHandlerWithContenxt(const DiagnosticInfo &DI,
- void *Context) {
+static void diagnosticHandlerWithContext(const DiagnosticInfo &DI,
+ void *Context) {
diagnosticHandler(DI);
}
@@ -174,6 +236,11 @@ static void error(const ErrorOr<T> &V, const Twine &Prefix) {
error(V.getError(), Prefix);
}
+static void maybeVerifyModule(const Module &Mod) {
+ if (!DisableVerify && verifyModule(Mod))
+ error("Broken Module");
+}
+
static std::unique_ptr<LTOModule>
getLocalLTOModule(StringRef Path, std::unique_ptr<MemoryBuffer> &Buffer,
const TargetOptions &Options) {
@@ -182,9 +249,13 @@ getLocalLTOModule(StringRef Path, std::unique_ptr<MemoryBuffer> &Buffer,
error(BufferOrErr, "error loading file '" + Path + "'");
Buffer = std::move(BufferOrErr.get());
CurrentActivity = ("loading file '" + Path + "'").str();
+ std::unique_ptr<LLVMContext> Context = llvm::make_unique<LLVMContext>();
+ Context->setDiagnosticHandler(diagnosticHandlerWithContext, nullptr, true);
ErrorOr<std::unique_ptr<LTOModule>> Ret = LTOModule::createInLocalContext(
- Buffer->getBufferStart(), Buffer->getBufferSize(), Options, Path);
+ std::move(Context), Buffer->getBufferStart(), Buffer->getBufferSize(),
+ Options, Path);
CurrentActivity = "";
+ maybeVerifyModule((*Ret)->getModule());
return std::move(*Ret);
}
@@ -211,16 +282,17 @@ static void listSymbols(const TargetOptions &Options) {
///
/// This is meant to enable testing of ThinLTO combined index generation,
/// currently available via the gold plugin via -thinlto.
-static void createCombinedFunctionIndex() {
- FunctionInfoIndex CombinedIndex;
+static void createCombinedModuleSummaryIndex() {
+ ModuleSummaryIndex 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());
+ ErrorOr<std::unique_ptr<ModuleSummaryIndex>> IndexOrErr =
+ llvm::getModuleSummaryIndexForFile(Filename, diagnosticHandler);
+ error(IndexOrErr, "error " + CurrentActivity);
+ std::unique_ptr<ModuleSummaryIndex> Index = std::move(IndexOrErr.get());
CurrentActivity = "";
- // Skip files without a function summary.
+ // Skip files without a module summary.
if (!Index)
continue;
CombinedIndex.mergeFrom(std::move(Index), ++NextModuleId);
@@ -230,13 +302,400 @@ static void createCombinedFunctionIndex() {
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);
+ WriteIndexToFile(CombinedIndex, OS);
OS.close();
}
+/// Parse the thinlto_prefix_replace option into the \p OldPrefix and
+/// \p NewPrefix strings, if it was specified.
+static void getThinLTOOldAndNewPrefix(std::string &OldPrefix,
+ std::string &NewPrefix) {
+ assert(ThinLTOPrefixReplace.empty() ||
+ ThinLTOPrefixReplace.find(";") != StringRef::npos);
+ StringRef PrefixReplace = ThinLTOPrefixReplace;
+ std::pair<StringRef, StringRef> Split = PrefixReplace.split(";");
+ OldPrefix = Split.first.str();
+ NewPrefix = Split.second.str();
+}
+
+/// Given the original \p Path to an output file, replace any path
+/// prefix matching \p OldPrefix with \p NewPrefix. Also, create the
+/// resulting directory if it does not yet exist.
+static std::string getThinLTOOutputFile(const std::string &Path,
+ const std::string &OldPrefix,
+ const std::string &NewPrefix) {
+ if (OldPrefix.empty() && NewPrefix.empty())
+ return Path;
+ SmallString<128> NewPath(Path);
+ llvm::sys::path::replace_path_prefix(NewPath, OldPrefix, NewPrefix);
+ StringRef ParentPath = llvm::sys::path::parent_path(NewPath.str());
+ if (!ParentPath.empty()) {
+ // Make sure the new directory exists, creating it if necessary.
+ if (std::error_code EC = llvm::sys::fs::create_directories(ParentPath))
+ error(EC, "error creating the directory '" + ParentPath + "'");
+ }
+ return NewPath.str();
+}
+
+namespace thinlto {
+
+std::vector<std::unique_ptr<MemoryBuffer>>
+loadAllFilesForIndex(const ModuleSummaryIndex &Index) {
+ std::vector<std::unique_ptr<MemoryBuffer>> InputBuffers;
+
+ for (auto &ModPath : Index.modulePaths()) {
+ const auto &Filename = ModPath.first();
+ auto CurrentActivity = "loading file '" + Filename + "'";
+ auto InputOrErr = MemoryBuffer::getFile(Filename);
+ error(InputOrErr, "error " + CurrentActivity);
+ InputBuffers.push_back(std::move(*InputOrErr));
+ }
+ return InputBuffers;
+}
+
+std::unique_ptr<ModuleSummaryIndex> loadCombinedIndex() {
+ if (ThinLTOIndex.empty())
+ report_fatal_error("Missing -thinlto-index for ThinLTO promotion stage");
+ auto CurrentActivity = "loading file '" + ThinLTOIndex + "'";
+ ErrorOr<std::unique_ptr<ModuleSummaryIndex>> IndexOrErr =
+ llvm::getModuleSummaryIndexForFile(ThinLTOIndex, diagnosticHandler);
+ error(IndexOrErr, "error " + CurrentActivity);
+ return std::move(IndexOrErr.get());
+}
+
+static std::unique_ptr<Module> loadModule(StringRef Filename,
+ LLVMContext &Ctx) {
+ SMDiagnostic Err;
+ std::unique_ptr<Module> M(parseIRFile(Filename, Err, Ctx));
+ if (!M) {
+ Err.print("llvm-lto", errs());
+ report_fatal_error("Can't load module for file " + Filename);
+ }
+ maybeVerifyModule(*M);
+
+ if (ThinLTOModuleId.getNumOccurrences()) {
+ if (InputFilenames.size() != 1)
+ report_fatal_error("Can't override the module id for multiple files");
+ M->setModuleIdentifier(ThinLTOModuleId);
+ }
+ return M;
+}
+
+static void writeModuleToFile(Module &TheModule, StringRef Filename) {
+ std::error_code EC;
+ raw_fd_ostream OS(Filename, EC, sys::fs::OpenFlags::F_None);
+ error(EC, "error opening the file '" + Filename + "'");
+ maybeVerifyModule(TheModule);
+ WriteBitcodeToFile(&TheModule, OS, /* ShouldPreserveUseListOrder */ true);
+}
+
+class ThinLTOProcessing {
+public:
+ ThinLTOCodeGenerator ThinGenerator;
+
+ ThinLTOProcessing(const TargetOptions &Options) {
+ ThinGenerator.setCodePICModel(getRelocModel());
+ ThinGenerator.setTargetOptions(Options);
+ ThinGenerator.setCacheDir(ThinLTOCacheDir);
+
+ // Add all the exported symbols to the table of symbols to preserve.
+ for (unsigned i = 0; i < ExportedSymbols.size(); ++i)
+ ThinGenerator.preserveSymbol(ExportedSymbols[i]);
+ }
+
+ void run() {
+ switch (ThinLTOMode) {
+ case THINLINK:
+ return thinLink();
+ case THINDISTRIBUTE:
+ return distributedIndexes();
+ case THINEMITIMPORTS:
+ return emitImports();
+ case THINPROMOTE:
+ return promote();
+ case THINIMPORT:
+ return import();
+ case THININTERNALIZE:
+ return internalize();
+ case THINOPT:
+ return optimize();
+ case THINCODEGEN:
+ return codegen();
+ case THINALL:
+ return runAll();
+ }
+ }
+
+private:
+ /// Load the input files, create the combined index, and write it out.
+ void thinLink() {
+ // Perform "ThinLink": just produce the index
+ if (OutputFilename.empty())
+ report_fatal_error(
+ "OutputFilename is necessary to store the combined index.\n");
+
+ LLVMContext Ctx;
+ std::vector<std::unique_ptr<MemoryBuffer>> InputBuffers;
+ for (unsigned i = 0; i < InputFilenames.size(); ++i) {
+ auto &Filename = InputFilenames[i];
+ StringRef CurrentActivity = "loading file '" + Filename + "'";
+ auto InputOrErr = MemoryBuffer::getFile(Filename);
+ error(InputOrErr, "error " + CurrentActivity);
+ InputBuffers.push_back(std::move(*InputOrErr));
+ ThinGenerator.addModule(Filename, InputBuffers.back()->getBuffer());
+ }
+
+ auto CombinedIndex = ThinGenerator.linkCombinedIndex();
+ std::error_code EC;
+ raw_fd_ostream OS(OutputFilename, EC, sys::fs::OpenFlags::F_None);
+ error(EC, "error opening the file '" + OutputFilename + "'");
+ WriteIndexToFile(*CombinedIndex, OS);
+ return;
+ }
+
+ /// Load the combined index from disk, then compute and generate
+ /// individual index files suitable for ThinLTO distributed backend builds
+ /// on the files mentioned on the command line (these must match the index
+ /// content).
+ void distributedIndexes() {
+ if (InputFilenames.size() != 1 && !OutputFilename.empty())
+ report_fatal_error("Can't handle a single output filename and multiple "
+ "input files, do not provide an output filename and "
+ "the output files will be suffixed from the input "
+ "ones.");
+
+ std::string OldPrefix, NewPrefix;
+ getThinLTOOldAndNewPrefix(OldPrefix, NewPrefix);
+
+ auto Index = loadCombinedIndex();
+ for (auto &Filename : InputFilenames) {
+ // Build a map of module to the GUIDs and summary objects that should
+ // be written to its index.
+ std::map<std::string, GVSummaryMapTy> ModuleToSummariesForIndex;
+ ThinLTOCodeGenerator::gatherImportedSummariesForModule(
+ Filename, *Index, ModuleToSummariesForIndex);
+
+ std::string OutputName = OutputFilename;
+ if (OutputName.empty()) {
+ OutputName = Filename + ".thinlto.bc";
+ }
+ OutputName = getThinLTOOutputFile(OutputName, OldPrefix, NewPrefix);
+ std::error_code EC;
+ raw_fd_ostream OS(OutputName, EC, sys::fs::OpenFlags::F_None);
+ error(EC, "error opening the file '" + OutputName + "'");
+ WriteIndexToFile(*Index, OS, &ModuleToSummariesForIndex);
+ }
+ }
+
+ /// Load the combined index from disk, compute the imports, and emit
+ /// the import file lists for each module to disk.
+ void emitImports() {
+ if (InputFilenames.size() != 1 && !OutputFilename.empty())
+ report_fatal_error("Can't handle a single output filename and multiple "
+ "input files, do not provide an output filename and "
+ "the output files will be suffixed from the input "
+ "ones.");
+
+ std::string OldPrefix, NewPrefix;
+ getThinLTOOldAndNewPrefix(OldPrefix, NewPrefix);
+
+ auto Index = loadCombinedIndex();
+ for (auto &Filename : InputFilenames) {
+ std::string OutputName = OutputFilename;
+ if (OutputName.empty()) {
+ OutputName = Filename + ".imports";
+ }
+ OutputName = getThinLTOOutputFile(OutputName, OldPrefix, NewPrefix);
+ ThinLTOCodeGenerator::emitImports(Filename, OutputName, *Index);
+ }
+ }
+
+ /// Load the combined index from disk, then load every file referenced by
+ /// the index and add them to the generator, finally perform the promotion
+ /// on the files mentioned on the command line (these must match the index
+ /// content).
+ void promote() {
+ if (InputFilenames.size() != 1 && !OutputFilename.empty())
+ report_fatal_error("Can't handle a single output filename and multiple "
+ "input files, do not provide an output filename and "
+ "the output files will be suffixed from the input "
+ "ones.");
+
+ auto Index = loadCombinedIndex();
+ for (auto &Filename : InputFilenames) {
+ LLVMContext Ctx;
+ auto TheModule = loadModule(Filename, Ctx);
+
+ ThinGenerator.promote(*TheModule, *Index);
+
+ std::string OutputName = OutputFilename;
+ if (OutputName.empty()) {
+ OutputName = Filename + ".thinlto.promoted.bc";
+ }
+ writeModuleToFile(*TheModule, OutputName);
+ }
+ }
+
+ /// Load the combined index from disk, then load every file referenced by
+ /// the index and add them to the generator, then performs the promotion and
+ /// cross module importing on the files mentioned on the command line
+ /// (these must match the index content).
+ void import() {
+ if (InputFilenames.size() != 1 && !OutputFilename.empty())
+ report_fatal_error("Can't handle a single output filename and multiple "
+ "input files, do not provide an output filename and "
+ "the output files will be suffixed from the input "
+ "ones.");
+
+ auto Index = loadCombinedIndex();
+ auto InputBuffers = loadAllFilesForIndex(*Index);
+ for (auto &MemBuffer : InputBuffers)
+ ThinGenerator.addModule(MemBuffer->getBufferIdentifier(),
+ MemBuffer->getBuffer());
+
+ for (auto &Filename : InputFilenames) {
+ LLVMContext Ctx;
+ auto TheModule = loadModule(Filename, Ctx);
+
+ ThinGenerator.crossModuleImport(*TheModule, *Index);
+
+ std::string OutputName = OutputFilename;
+ if (OutputName.empty()) {
+ OutputName = Filename + ".thinlto.imported.bc";
+ }
+ writeModuleToFile(*TheModule, OutputName);
+ }
+ }
+
+ void internalize() {
+ if (InputFilenames.size() != 1 && !OutputFilename.empty())
+ report_fatal_error("Can't handle a single output filename and multiple "
+ "input files, do not provide an output filename and "
+ "the output files will be suffixed from the input "
+ "ones.");
+
+ if (ExportedSymbols.empty())
+ errs() << "Warning: -internalize will not perform without "
+ "-exported-symbol\n";
+
+ auto Index = loadCombinedIndex();
+ auto InputBuffers = loadAllFilesForIndex(*Index);
+ for (auto &MemBuffer : InputBuffers)
+ ThinGenerator.addModule(MemBuffer->getBufferIdentifier(),
+ MemBuffer->getBuffer());
+
+ for (auto &Filename : InputFilenames) {
+ LLVMContext Ctx;
+ auto TheModule = loadModule(Filename, Ctx);
+
+ ThinGenerator.internalize(*TheModule, *Index);
+
+ std::string OutputName = OutputFilename;
+ if (OutputName.empty()) {
+ OutputName = Filename + ".thinlto.internalized.bc";
+ }
+ writeModuleToFile(*TheModule, OutputName);
+ }
+ }
+
+ void optimize() {
+ if (InputFilenames.size() != 1 && !OutputFilename.empty())
+ report_fatal_error("Can't handle a single output filename and multiple "
+ "input files, do not provide an output filename and "
+ "the output files will be suffixed from the input "
+ "ones.");
+ if (!ThinLTOIndex.empty())
+ errs() << "Warning: -thinlto-index ignored for optimize stage";
+
+ for (auto &Filename : InputFilenames) {
+ LLVMContext Ctx;
+ auto TheModule = loadModule(Filename, Ctx);
+
+ ThinGenerator.optimize(*TheModule);
+
+ std::string OutputName = OutputFilename;
+ if (OutputName.empty()) {
+ OutputName = Filename + ".thinlto.imported.bc";
+ }
+ writeModuleToFile(*TheModule, OutputName);
+ }
+ }
+
+ void codegen() {
+ if (InputFilenames.size() != 1 && !OutputFilename.empty())
+ report_fatal_error("Can't handle a single output filename and multiple "
+ "input files, do not provide an output filename and "
+ "the output files will be suffixed from the input "
+ "ones.");
+ if (!ThinLTOIndex.empty())
+ errs() << "Warning: -thinlto-index ignored for codegen stage";
+
+ for (auto &Filename : InputFilenames) {
+ LLVMContext Ctx;
+ auto TheModule = loadModule(Filename, Ctx);
+
+ auto Buffer = ThinGenerator.codegen(*TheModule);
+ std::string OutputName = OutputFilename;
+ if (OutputName.empty()) {
+ OutputName = Filename + ".thinlto.o";
+ }
+ if (OutputName == "-") {
+ outs() << Buffer->getBuffer();
+ return;
+ }
+
+ std::error_code EC;
+ raw_fd_ostream OS(OutputName, EC, sys::fs::OpenFlags::F_None);
+ error(EC, "error opening the file '" + OutputName + "'");
+ OS << Buffer->getBuffer();
+ }
+ }
+
+ /// Full ThinLTO process
+ void runAll() {
+ if (!OutputFilename.empty())
+ report_fatal_error("Do not provide an output filename for ThinLTO "
+ " processing, the output files will be suffixed from "
+ "the input ones.");
+
+ if (!ThinLTOIndex.empty())
+ errs() << "Warning: -thinlto-index ignored for full ThinLTO process";
+
+ LLVMContext Ctx;
+ std::vector<std::unique_ptr<MemoryBuffer>> InputBuffers;
+ for (unsigned i = 0; i < InputFilenames.size(); ++i) {
+ auto &Filename = InputFilenames[i];
+ StringRef CurrentActivity = "loading file '" + Filename + "'";
+ auto InputOrErr = MemoryBuffer::getFile(Filename);
+ error(InputOrErr, "error " + CurrentActivity);
+ InputBuffers.push_back(std::move(*InputOrErr));
+ ThinGenerator.addModule(Filename, InputBuffers.back()->getBuffer());
+ }
+
+ ThinGenerator.run();
+
+ auto &Binaries = ThinGenerator.getProducedBinaries();
+ if (Binaries.size() != InputFilenames.size())
+ report_fatal_error("Number of output objects does not match the number "
+ "of inputs");
+
+ for (unsigned BufID = 0; BufID < Binaries.size(); ++BufID) {
+ auto OutputName = InputFilenames[BufID] + ".thinlto.o";
+ std::error_code EC;
+ raw_fd_ostream OS(OutputName, EC, sys::fs::OpenFlags::F_None);
+ error(EC, "error opening the file '" + OutputName + "'");
+ OS << Binaries[BufID]->getBuffer();
+ }
+ }
+
+ /// Load the combined index from disk, then load every file referenced by
+};
+
+} // namespace thinlto
+
int main(int argc, char **argv) {
// Print a stack trace if we signal out.
- sys::PrintStackTraceOnErrorSignal();
+ sys::PrintStackTraceOnErrorSignal(argv[0]);
PrettyStackTraceProgram X(argc, argv);
llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
@@ -259,25 +718,49 @@ int main(int argc, char **argv) {
return 0;
}
+ if (CheckHasObjC) {
+ for (auto &Filename : InputFilenames) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
+ MemoryBuffer::getFile(Filename);
+ error(BufferOrErr, "error loading file '" + Filename + "'");
+ auto Buffer = std::move(BufferOrErr.get());
+ LLVMContext Ctx;
+ if (llvm::isBitcodeContainingObjCCategory(*Buffer, Ctx))
+ outs() << "Bitcode " << Filename << " contains ObjC\n";
+ else
+ outs() << "Bitcode " << Filename << " does not contain ObjC\n";
+ }
+ return 0;
+ }
+
+ if (ThinLTOMode.getNumOccurrences()) {
+ if (ThinLTOMode.getNumOccurrences() > 1)
+ report_fatal_error("You can't specify more than one -thinlto-action");
+ thinlto::ThinLTOProcessing ThinLTOProcessor(Options);
+ ThinLTOProcessor.run();
+ return 0;
+ }
+
if (ThinLTO) {
- createCombinedFunctionIndex();
+ createCombinedModuleSummaryIndex();
return 0;
}
unsigned BaseArg = 0;
LLVMContext Context;
- Context.setDiagnosticHandler(diagnosticHandlerWithContenxt, nullptr, true);
+ Context.setDiagnosticHandler(diagnosticHandlerWithContext, nullptr, true);
LTOCodeGenerator CodeGen(Context);
if (UseDiagnosticHandler)
CodeGen.setDiagnosticHandler(handleDiagnostics, nullptr);
- CodeGen.setCodePICModel(RelocModel);
+ CodeGen.setCodePICModel(getRelocModel());
CodeGen.setDebugInfo(LTO_DEBUG_MODEL_DWARF);
CodeGen.setTargetOptions(Options);
+ CodeGen.setShouldRestoreGlobalsLinkage(RestoreGlobalsLinkage);
llvm::StringSet<llvm::MallocAllocator> DSOSymbolsSet;
for (unsigned i = 0; i < DSOSymbols.size(); ++i)
@@ -289,7 +772,6 @@ int main(int argc, char **argv) {
CurrentActivity = "loading file '" + InputFilenames[i] + "'";
ErrorOr<std::unique_ptr<LTOModule>> ModuleOrErr =
LTOModule::createFromFile(Context, InputFilenames[i].c_str(), Options);
- error(ModuleOrErr, "error " + CurrentActivity);
std::unique_ptr<LTOModule> &Module = *ModuleOrErr;
CurrentActivity = "";
@@ -311,8 +793,7 @@ int main(int argc, char **argv) {
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;
+ error("error adding file '" + InputFilenames[i] + "'");
}
}
@@ -346,8 +827,7 @@ int main(int argc, char **argv) {
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;
+ error("error optimizing the code");
}
if (SaveModuleFile) {
@@ -355,10 +835,8 @@ int main(int argc, char **argv) {
ModuleFilename += ".merged.bc";
std::string ErrMsg;
- if (!CodeGen.writeMergedModules(ModuleFilename.c_str())) {
- errs() << argv[0] << ": writing merged module failed.\n";
- return 1;
- }
+ if (!CodeGen.writeMergedModules(ModuleFilename.c_str()))
+ error("writing merged module failed.");
}
std::list<tool_output_file> OSs;
@@ -369,40 +847,29 @@ int main(int argc, char **argv) {
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;
- }
+ if (EC)
+ error("error opening the file '" + PartFilename + "': " + EC.message());
OSPtrs.push_back(&OSs.back().os());
}
- if (!CodeGen.compileOptimized(OSPtrs)) {
+ if (!CodeGen.compileOptimized(OSPtrs))
// Diagnostic messages should have been printed by the handler.
- errs() << argv[0] << ": error compiling the code\n";
- return 1;
- }
+ error("error compiling the code");
for (tool_output_file &OS : OSs)
OS.keep();
} else {
- if (Parallelism != 1) {
- errs() << argv[0] << ": -j must be specified together with -o\n";
- return 1;
- }
+ if (Parallelism != 1)
+ error("-j must be specified together with -o");
- if (SaveModuleFile) {
- errs() << argv[0] << ": -save-merged-module must be specified with -o\n";
- return 1;
- }
+ if (SaveModuleFile)
+ error(": -save-merged-module must be specified with -o");
const char *OutputName = nullptr;
if (!CodeGen.compile_to_file(&OutputName, DisableVerify, DisableInline,
- DisableGVNLoadPRE, DisableLTOVectorization)) {
+ DisableGVNLoadPRE, DisableLTOVectorization))
+ error("error compiling the code");
// Diagnostic messages should have been printed by the handler.
- errs() << argv[0] << ": error compiling the code\n";
- return 1;
- }
outs() << "Wrote native object file '" << OutputName << "'\n";
}
diff --git a/tools/llvm-mc-fuzzer/CMakeLists.txt b/tools/llvm-mc-fuzzer/CMakeLists.txt
index 0d6432b23dfa5..b42b3eee3c981 100644
--- a/tools/llvm-mc-fuzzer/CMakeLists.txt
+++ b/tools/llvm-mc-fuzzer/CMakeLists.txt
@@ -13,6 +13,6 @@ if( LLVM_USE_SANITIZE_COVERAGE )
add_llvm_tool(llvm-mc-fuzzer
llvm-mc-fuzzer.cpp)
target_link_libraries(llvm-mc-fuzzer
- LLVMFuzzerNoMain
+ LLVMFuzzer
)
endif()
diff --git a/tools/llvm-mc-fuzzer/llvm-mc-fuzzer.cpp b/tools/llvm-mc-fuzzer/llvm-mc-fuzzer.cpp
index 3f80e4582ee1b..df8e0112af5bf 100644
--- a/tools/llvm-mc-fuzzer/llvm-mc-fuzzer.cpp
+++ b/tools/llvm-mc-fuzzer/llvm-mc-fuzzer.cpp
@@ -9,13 +9,12 @@
//
//===----------------------------------------------------------------------===//
+#include "FuzzerInterface.h"
#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;
@@ -58,9 +57,10 @@ static cl::list<std::string>
std::string FeaturesStr;
static cl::list<std::string>
- FuzzerArgv("fuzzer-args", cl::Positional,
+ FuzzerArgs("fuzzer-args", cl::Positional,
cl::desc("Options to pass to the fuzzer"), cl::ZeroOrMore,
cl::PositionalEatsArgs);
+static std::vector<char *> ModifiedArgv;
int DisassembleOneInput(const uint8_t *Data, size_t Size) {
char AssemblyText[AssemblyTextBufSize];
@@ -88,7 +88,17 @@ int DisassembleOneInput(const uint8_t *Data, size_t Size) {
return 0;
}
-int main(int argc, char **argv) {
+int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ if (Action == AC_Assemble)
+ errs() << "error: -assemble is not implemented\n";
+ else if (Action == AC_Disassemble)
+ return DisassembleOneInput(Data, Size);
+
+ llvm_unreachable("Unknown action");
+ return 0;
+}
+
+int LLVMFuzzerInitialize(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
@@ -112,11 +122,29 @@ int main(int argc, char **argv) {
// individual instructions that test unique paths. Without this constraint,
// there will be considerable redundancy in the corpus.
+ char **OriginalArgv = *argv;
+
LLVMInitializeAllTargetInfos();
LLVMInitializeAllTargetMCs();
LLVMInitializeAllDisassemblers();
- cl::ParseCommandLineOptions(argc, argv);
+ cl::ParseCommandLineOptions(*argc, OriginalArgv);
+
+ // Rebuild the argv without the arguments llvm-mc-fuzzer consumed so that
+ // the driver can parse its arguments.
+ //
+ // FuzzerArgs cannot provide the non-const pointer that OriginalArgv needs.
+ // Re-use the strings from OriginalArgv instead of copying FuzzerArg to a
+ // non-const buffer to avoid the need to clean up when the fuzzer terminates.
+ ModifiedArgv.push_back(OriginalArgv[0]);
+ for (const auto &FuzzerArg : FuzzerArgs) {
+ for (int i = 1; i < *argc; ++i) {
+ if (FuzzerArg == OriginalArgv[i])
+ ModifiedArgv.push_back(OriginalArgv[i]);
+ }
+ }
+ *argc = ModifiedArgv.size();
+ *argv = ModifiedArgv.data();
// Package up features to be passed to target/subtarget
// We have to pass it via a global since the callback doesn't
@@ -128,11 +156,5 @@ int main(int argc, char **argv) {
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;
+ return 0;
}
diff --git a/tools/llvm-mc/Disassembler.cpp b/tools/llvm-mc/Disassembler.cpp
index 5ffeffc076823..8185947fc5e57 100644
--- a/tools/llvm-mc/Disassembler.cpp
+++ b/tools/llvm-mc/Disassembler.cpp
@@ -16,7 +16,7 @@
#include "llvm/ADT/Triple.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCContext.h"
-#include "llvm/MC/MCDisassembler.h"
+#include "llvm/MC/MCDisassembler/MCDisassembler.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/MC/MCStreamer.h"
diff --git a/tools/llvm-mc/Makefile b/tools/llvm-mc/Makefile
deleted file mode 100644
index b147fadb5747b..0000000000000
--- a/tools/llvm-mc/Makefile
+++ /dev/null
@@ -1,17 +0,0 @@
-##===- tools/llvm-mc/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-mc
-LINK_COMPONENTS := all-targets MCDisassembler MCParser MC support
-
-# This tool has no plugins, optimize startup time.
-TOOL_NO_EXPORTS := 1
-
-include $(LEVEL)/Makefile.common
diff --git a/tools/llvm-mc/llvm-mc.cpp b/tools/llvm-mc/llvm-mc.cpp
index 96e3f7c21a517..00f19cb328c37 100644
--- a/tools/llvm-mc/llvm-mc.cpp
+++ b/tools/llvm-mc/llvm-mc.cpp
@@ -20,11 +20,11 @@
#include "llvm/MC/MCInstrInfo.h"
#include "llvm/MC/MCObjectFileInfo.h"
#include "llvm/MC/MCParser/AsmLexer.h"
+#include "llvm/MC/MCParser/MCTargetAsmParser.h"
#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/MC/MCSectionMachO.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/MC/MCSubtargetInfo.h"
-#include "llvm/MC/MCTargetAsmParser.h"
#include "llvm/MC/MCTargetOptionsCommandFlags.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Compression.h"
@@ -52,9 +52,22 @@ OutputFilename("o", cl::desc("Output filename"),
static cl::opt<bool>
ShowEncoding("show-encoding", cl::desc("Show instruction encodings"));
-static cl::opt<bool>
-CompressDebugSections("compress-debug-sections",
- cl::desc("Compress DWARF debug sections"));
+static cl::opt<bool> RelaxELFRel(
+ "relax-relocations", cl::init(true),
+ cl::desc("Emit R_X86_64_GOTPCRELX instead of R_X86_64_GOTPCREL"));
+
+static cl::opt<DebugCompressionType>
+CompressDebugSections("compress-debug-sections", cl::ValueOptional,
+ cl::init(DebugCompressionType::DCT_None),
+ cl::desc("Choose DWARF debug sections compression:"),
+ cl::values(
+ clEnumValN(DebugCompressionType::DCT_None, "none",
+ "No compression"),
+ clEnumValN(DebugCompressionType::DCT_Zlib, "zlib",
+ "Use zlib compression"),
+ clEnumValN(DebugCompressionType::DCT_ZlibGnu, "zlib-gnu",
+ "Use zlib-gnu compression (depricated)"),
+ clEnumValEnd));
static cl::opt<bool>
ShowInst("show-inst", cl::desc("Show internal instruction representation"));
@@ -74,6 +87,10 @@ PrintImmHex("print-imm-hex", cl::init(false),
static cl::list<std::string>
DefineSymbol("defsym", cl::desc("Defines a symbol to be an integer constant"));
+static cl::opt<bool>
+ PreserveComments("preserve-comments",
+ cl::desc("Preserve Comments in outputted assembly"));
+
enum OutputFileType {
OFT_Null,
OFT_AssemblyFile,
@@ -115,20 +132,8 @@ MAttrs("mattr",
cl::desc("Target specific attributes (-mattr=help for details)"),
cl::value_desc("a1,+a2,-a3,..."));
-static cl::opt<Reloc::Model>
-RelocModel("relocation-model",
- cl::desc("Choose relocation model"),
- cl::init(Reloc::Default),
- cl::values(
- clEnumValN(Reloc::Default, "default",
- "Target default relocation model"),
- clEnumValN(Reloc::Static, "static",
- "Non-relocatable code"),
- clEnumValN(Reloc::PIC_, "pic",
- "Fully relocatable, position independent code"),
- clEnumValN(Reloc::DynamicNoPIC, "dynamic-no-pic",
- "Relocatable external references, non-relocatable code"),
- clEnumValEnd));
+static cl::opt<bool> PIC("position-independent",
+ cl::desc("Position independent"), cl::init(false));
static cl::opt<llvm::CodeModel::Model>
CMModel("code-model",
@@ -249,7 +254,7 @@ static int AsLexInput(SourceMgr &SrcMgr, MCAsmInfo &MAI,
bool Error = false;
while (Lexer.Lex().isNot(AsmToken::Eof)) {
- AsmToken Tok = Lexer.getTok();
+ const AsmToken &Tok = Lexer.getTok();
switch (Tok.getKind()) {
default:
@@ -368,7 +373,7 @@ static int AssembleInput(const char *ProgName, const Target *TheTarget,
int main(int argc, char **argv) {
// Print a stack trace if we signal out.
- sys::PrintStackTraceOnErrorSignal();
+ sys::PrintStackTraceOnErrorSignal(argv[0]);
PrettyStackTraceProgram X(argc, argv);
llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
@@ -419,20 +424,23 @@ int main(int argc, char **argv) {
std::unique_ptr<MCAsmInfo> MAI(TheTarget->createMCAsmInfo(*MRI, TripleName));
assert(MAI && "Unable to create target asm info!");
- if (CompressDebugSections) {
+ MAI->setRelaxELFRelocations(RelaxELFRel);
+
+ if (CompressDebugSections != DebugCompressionType::DCT_None) {
if (!zlib::isAvailable()) {
errs() << ProgName
<< ": build tools with zlib to enable -compress-debug-sections";
return 1;
}
- MAI->setCompressDebugSections(true);
+ MAI->setCompressDebugSections(CompressDebugSections);
}
+ MAI->setPreserveAsmComments(PreserveComments);
// FIXME: This is not pretty. MCContext has a ptr to MCObjectFileInfo and
// MCObjectFileInfo needs a MCContext reference in order to initialize itself.
MCObjectFileInfo MOFI;
MCContext Ctx(MAI.get(), MRI.get(), &MOFI, &SrcMgr);
- MOFI.InitMCObjectFileInfo(TheTriple, RelocModel, CMModel, Ctx);
+ MOFI.InitMCObjectFileInfo(TheTriple, PIC, CMModel, Ctx);
if (SaveTempLabels)
Ctx.setAllowTemporaryLabels(false);
@@ -452,6 +460,12 @@ int main(int argc, char **argv) {
Ctx.setDwarfDebugProducer(StringRef(DwarfDebugProducer));
if (!DebugCompilationDir.empty())
Ctx.setCompilationDir(DebugCompilationDir);
+ else {
+ // If no compilation dir is set, try to use the current directory.
+ SmallString<128> CWD;
+ if (!sys::fs::current_path(CWD))
+ Ctx.setCompilationDir(CWD);
+ }
if (!MainFileName.empty())
Ctx.setMainFileName(MainFileName);
diff --git a/tools/llvm-mcmarkup/Makefile b/tools/llvm-mcmarkup/Makefile
deleted file mode 100644
index 5633a9c301a3b..0000000000000
--- a/tools/llvm-mcmarkup/Makefile
+++ /dev/null
@@ -1,17 +0,0 @@
-##===- tools/llvm-mcmarkup/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-mcmarkup
-LINK_COMPONENTS := support
-
-# This tool has no plugins, optimize startup time.
-TOOL_NO_EXPORTS = 1
-
-include $(LEVEL)/Makefile.common
diff --git a/tools/llvm-mcmarkup/llvm-mcmarkup.cpp b/tools/llvm-mcmarkup/llvm-mcmarkup.cpp
index 56543139d6fac..0be3c715eee4e 100644
--- a/tools/llvm-mcmarkup/llvm-mcmarkup.cpp
+++ b/tools/llvm-mcmarkup/llvm-mcmarkup.cpp
@@ -208,7 +208,7 @@ static void parseMCMarkup(StringRef Filename) {
int main(int argc, char **argv) {
// Print a stack trace if we signal out.
- sys::PrintStackTraceOnErrorSignal();
+ sys::PrintStackTraceOnErrorSignal(argv[0]);
PrettyStackTraceProgram X(argc, argv);
llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
diff --git a/tools/llvm-nm/Makefile b/tools/llvm-nm/Makefile
deleted file mode 100644
index ec20cef4258c1..0000000000000
--- a/tools/llvm-nm/Makefile
+++ /dev/null
@@ -1,17 +0,0 @@
-##===- tools/llvm-nm/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-nm
-LINK_COMPONENTS := all-targets bitreader object
-
-# This tool has no plugins, optimize startup time.
-TOOL_NO_EXPORTS := 1
-
-include $(LEVEL)/Makefile.common
diff --git a/tools/llvm-nm/llvm-nm.cpp b/tools/llvm-nm/llvm-nm.cpp
index b812233034eee..9b2f5220badbc 100644
--- a/tools/llvm-nm/llvm-nm.cpp
+++ b/tools/llvm-nm/llvm-nm.cpp
@@ -16,6 +16,7 @@
//
//===----------------------------------------------------------------------===//
+#include "llvm/ADT/StringSwitch.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/GlobalAlias.h"
#include "llvm/IR/GlobalVariable.h"
@@ -138,6 +139,15 @@ 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::Grouping);
+enum Radix { d, o, x };
+cl::opt<Radix>
+ AddressRadix("radix", cl::desc("Radix (o/d/x) for printing symbol Values"),
+ cl::values(clEnumVal(d, "decimal"), clEnumVal(o, "octal"),
+ clEnumVal(x, "hexadecimal"), clEnumValEnd),
+ cl::init(x));
+cl::alias RadixAlias("t", cl::desc("Alias for --radix"),
+ cl::aliasopt(AddressRadix));
+
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"),
@@ -180,6 +190,52 @@ static bool error(std::error_code EC, Twine Path = Twine()) {
return false;
}
+// This version of error() prints the archive name and member name, for example:
+// "libx.a(foo.o)" after the ToolName before the error message. It sets
+// HadError but returns allowing the code to move on to other archive members.
+static void error(llvm::Error E, StringRef FileName, const Archive::Child &C,
+ StringRef ArchitectureName = StringRef()) {
+ HadError = true;
+ errs() << ToolName << ": " << FileName;
+
+ ErrorOr<StringRef> NameOrErr = C.getName();
+ // TODO: if we have a error getting the name then it would be nice to print
+ // the index of which archive member this is and or its offset in the
+ // archive instead of "???" as the name.
+ if (NameOrErr.getError())
+ errs() << "(" << "???" << ")";
+ else
+ errs() << "(" << NameOrErr.get() << ")";
+
+ if (!ArchitectureName.empty())
+ errs() << " (for architecture " << ArchitectureName << ") ";
+
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(std::move(E), OS, "");
+ OS.flush();
+ errs() << " " << Buf << "\n";
+}
+
+// This version of error() prints the file name and which architecture slice it
+// is from, for example: "foo.o (for architecture i386)" after the ToolName
+// before the error message. It sets HadError but returns allowing the code to
+// move on to other architecture slices.
+static void error(llvm::Error E, StringRef FileName,
+ StringRef ArchitectureName = StringRef()) {
+ HadError = true;
+ errs() << ToolName << ": " << FileName;
+
+ if (!ArchitectureName.empty())
+ errs() << " (for architecture " << ArchitectureName << ") ";
+
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(std::move(E), OS, "");
+ OS.flush();
+ errs() << " " << Buf << "\n";
+}
+
namespace {
struct NMSymbol {
uint64_t Address;
@@ -260,15 +316,11 @@ static void darwinPrintSymbol(SymbolicFile &Obj, SymbolListT::iterator I,
// 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)
+ 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;
+ NSect = (getSymbolNMTypeChar(*IRobj, I->Sym) == 't') ? 1 : 2;
}
}
if (SymFlags & SymbolRef::SF_Weak)
@@ -338,7 +390,7 @@ static void darwinPrintSymbol(SymbolicFile &Obj, SymbolListT::iterator I,
MachO::REFERENCE_FLAG_UNDEFINED_LAZY)
outs() << "undefined [lazy bound]) ";
else if ((NDesc & MachO::REFERENCE_TYPE) ==
- MachO::REFERENCE_FLAG_UNDEFINED_LAZY)
+ MachO::REFERENCE_FLAG_PRIVATE_UNDEFINED_LAZY)
outs() << "undefined [private lazy bound]) ";
else if ((NDesc & MachO::REFERENCE_TYPE) ==
MachO::REFERENCE_FLAG_PRIVATE_UNDEFINED_NON_LAZY)
@@ -367,7 +419,14 @@ static void darwinPrintSymbol(SymbolicFile &Obj, SymbolListT::iterator I,
outs() << "(?,?) ";
break;
}
- section_iterator Sec = *MachO->getSymbolSection(I->Sym.getRawDataRefImpl());
+ Expected<section_iterator> SecOrErr =
+ MachO->getSymbolSection(I->Sym.getRawDataRefImpl());
+ if (!SecOrErr) {
+ consumeError(SecOrErr.takeError());
+ outs() << "(?,?) ";
+ break;
+ }
+ section_iterator Sec = *SecOrErr;
DataRefImpl Ref = Sec->getRawDataRefImpl();
StringRef SectionName;
MachO->getSectionName(Ref, SectionName);
@@ -538,8 +597,8 @@ static void darwinPrintStab(MachOObjectFile *MachO, SymbolListT::iterator I) {
}
static void sortAndPrintSymbolList(SymbolicFile &Obj, bool printName,
- std::string ArchiveName,
- std::string ArchitectureName) {
+ const std::string &ArchiveName,
+ const std::string &ArchitectureName) {
if (!NoSort) {
std::function<bool(const NMSymbol &, const NMSymbol &)> Cmp;
if (NumericSort)
@@ -570,32 +629,49 @@ static void sortAndPrintSymbolList(SymbolicFile &Obj, bool printName,
if (isSymbolList64Bit(Obj)) {
printBlanks = " ";
printDashes = "----------------";
- printFormat = "%016" PRIx64;
+ switch (AddressRadix) {
+ case Radix::o:
+ printFormat = OutputFormat == posix ? "%" PRIo64 : "%016" PRIo64;
+ break;
+ case Radix::x:
+ printFormat = OutputFormat == posix ? "%" PRIx64 : "%016" PRIx64;
+ break;
+ default:
+ printFormat = OutputFormat == posix ? "%" PRId64 : "%016" PRId64;
+ }
} else {
printBlanks = " ";
printDashes = "--------";
- printFormat = "%08" PRIx64;
+ switch (AddressRadix) {
+ case Radix::o:
+ printFormat = OutputFormat == posix ? "%" PRIo64 : "%08" PRIo64;
+ break;
+ case Radix::x:
+ printFormat = OutputFormat == posix ? "%" PRIx64 : "%08" PRIx64;
+ break;
+ default:
+ printFormat = OutputFormat == posix ? "%" PRId64 : "%08" PRId64;
+ }
}
for (SymbolListT::iterator I = SymbolList.begin(), E = SymbolList.end();
I != E; ++I) {
uint32_t SymFlags = I->Sym.getFlags();
bool Undefined = SymFlags & SymbolRef::SF_Undefined;
- if (!Undefined && UndefinedOnly)
- continue;
- if (Undefined && DefinedOnly)
- continue;
bool Global = SymFlags & SymbolRef::SF_Global;
- if (!Global && ExternalOnly)
- continue;
- if (SizeSort && !PrintAddress)
+ if ((!Undefined && UndefinedOnly) || (Undefined && DefinedOnly) ||
+ (!Global && ExternalOnly) || (SizeSort && !PrintAddress))
continue;
if (PrintFileName) {
if (!ArchitectureName.empty())
outs() << "(for architecture " << ArchitectureName << "):";
- if (!ArchiveName.empty())
- outs() << ArchiveName << ":";
- outs() << CurrentFilename << ": ";
+ if (OutputFormat == posix && !ArchiveName.empty())
+ outs() << ArchiveName << "[" << CurrentFilename << "]: ";
+ else {
+ if (!ArchiveName.empty())
+ outs() << ArchiveName << ":";
+ outs() << CurrentFilename << ": ";
+ }
}
if ((JustSymbolName || (UndefinedOnly && isa<MachOObjectFile>(Obj) &&
OutputFormat != darwin)) && OutputFormat != posix) {
@@ -606,8 +682,13 @@ static void sortAndPrintSymbolList(SymbolicFile &Obj, bool printName,
char SymbolAddrStr[18] = "";
char SymbolSizeStr[18] = "";
- if (OutputFormat == sysv || I->TypeChar == 'U')
- strcpy(SymbolAddrStr, printBlanks);
+ if (OutputFormat == sysv || I->TypeChar == 'U') {
+ if (OutputFormat == posix)
+ format(printFormat, I->Address)
+ .print(SymbolAddrStr, sizeof(SymbolAddrStr));
+ else
+ strcpy(SymbolAddrStr, printBlanks);
+ }
if (OutputFormat == sysv)
strcpy(SymbolSizeStr, printBlanks);
@@ -632,9 +713,9 @@ static void sortAndPrintSymbolList(SymbolicFile &Obj, bool printName,
} else if (OutputFormat == posix) {
outs() << I->Name << " " << I->TypeChar << " ";
if (MachO)
- outs() << I->Address << " " << "0" /* SymbolSizeStr */ << "\n";
+ outs() << SymbolAddrStr << " " << "0" /* SymbolSizeStr */ << "\n";
else
- outs() << SymbolAddrStr << SymbolSizeStr << "\n";
+ outs() << SymbolAddrStr << " " << SymbolSizeStr << "\n";
} else if (OutputFormat == bsd || (OutputFormat == darwin && !MachO)) {
if (PrintAddress)
outs() << SymbolAddrStr << ' ';
@@ -663,9 +744,11 @@ static char getSymbolNMTypeChar(ELFObjectFileBase &Obj,
// OK, this is ELF
elf_symbol_iterator SymI(I);
- ErrorOr<elf_section_iterator> SecIOrErr = SymI->getSection();
- if (error(SecIOrErr.getError()))
+ Expected<elf_section_iterator> SecIOrErr = SymI->getSection();
+ if (!SecIOrErr) {
+ consumeError(SecIOrErr.takeError());
return '?';
+ }
elf_section_iterator SecI = *SecIOrErr;
if (SecI != Obj.section_end()) {
@@ -690,9 +773,11 @@ static char getSymbolNMTypeChar(ELFObjectFileBase &Obj,
}
if (SymI->getELFType() == ELF::STT_SECTION) {
- ErrorOr<StringRef> Name = SymI->getName();
- if (error(Name.getError()))
+ Expected<StringRef> Name = SymI->getName();
+ if (!Name) {
+ consumeError(Name.takeError());
return '?';
+ }
return StringSwitch<char>(*Name)
.StartsWith(".debug", 'N')
.StartsWith(".note", 'n')
@@ -707,9 +792,11 @@ static char getSymbolNMTypeChar(COFFObjectFile &Obj, symbol_iterator I) {
// OK, this is COFF.
symbol_iterator SymI(I);
- ErrorOr<StringRef> Name = SymI->getName();
- if (error(Name.getError()))
+ Expected<StringRef> Name = SymI->getName();
+ if (!Name) {
+ consumeError(Name.takeError());
return '?';
+ }
char Ret = StringSwitch<char>(*Name)
.StartsWith(".debug", 'N')
@@ -721,9 +808,11 @@ static char getSymbolNMTypeChar(COFFObjectFile &Obj, symbol_iterator I) {
uint32_t Characteristics = 0;
if (!COFF::isReservedSectionNumber(Symb.getSectionNumber())) {
- ErrorOr<section_iterator> SecIOrErr = SymI->getSection();
- if (error(SecIOrErr.getError()))
+ Expected<section_iterator> SecIOrErr = SymI->getSection();
+ if (!SecIOrErr) {
+ consumeError(SecIOrErr.takeError());
return '?';
+ }
section_iterator SecI = *SecIOrErr;
const coff_section *Section = Obj.getCOFFSection(*SecI);
Characteristics = Section->Characteristics;
@@ -750,18 +839,10 @@ static char getSymbolNMTypeChar(COFFObjectFile &Obj, symbol_iterator I) {
return '?';
}
-static uint8_t getNType(MachOObjectFile &Obj, DataRefImpl Symb) {
- if (Obj.is64Bit()) {
- MachO::nlist_64 STE = Obj.getSymbol64TableEntry(Symb);
- return STE.n_type;
- }
- MachO::nlist STE = Obj.getSymbolTableEntry(Symb);
- return STE.n_type;
-}
-
static char getSymbolNMTypeChar(MachOObjectFile &Obj, basic_symbol_iterator I) {
DataRefImpl Symb = I->getRawDataRefImpl();
- uint8_t NType = getNType(Obj, Symb);
+ uint8_t NType = Obj.is64Bit() ? Obj.getSymbol64TableEntry(Symb).n_type
+ : Obj.getSymbolTableEntry(Symb).n_type;
if (NType & MachO::N_STAB)
return '-';
@@ -772,19 +853,23 @@ static char getSymbolNMTypeChar(MachOObjectFile &Obj, basic_symbol_iterator I) {
case MachO::N_INDR:
return 'i';
case MachO::N_SECT: {
- section_iterator Sec = *Obj.getSymbolSection(Symb);
+ Expected<section_iterator> SecOrErr = Obj.getSymbolSection(Symb);
+ if (!SecOrErr) {
+ consumeError(SecOrErr.takeError());
+ return 's';
+ }
+ section_iterator Sec = *SecOrErr;
DataRefImpl Ref = Sec->getRawDataRefImpl();
StringRef SectionName;
Obj.getSectionName(Ref, SectionName);
StringRef SegmentName = Obj.getSectionFinalSegmentName(Ref);
if (SegmentName == "__TEXT" && SectionName == "__text")
return 't';
- else if (SegmentName == "__DATA" && SectionName == "__data")
+ if (SegmentName == "__DATA" && SectionName == "__data")
return 'd';
- else if (SegmentName == "__DATA" && SectionName == "__bss")
+ if (SegmentName == "__DATA" && SectionName == "__bss")
return 'b';
- else
- return 's';
+ return 's';
}
}
@@ -792,35 +877,27 @@ static char getSymbolNMTypeChar(MachOObjectFile &Obj, basic_symbol_iterator I) {
}
static char getSymbolNMTypeChar(const GlobalValue &GV) {
- if (GV.getType()->getElementType()->isFunctionTy())
- return 't';
// FIXME: should we print 'b'? At the IR level we cannot be sure if this
// will be in bss or not, but we could approximate.
- return 'd';
+ return GV.getValueType()->isFunctionTy() ? 't' : 'd';
}
static char getSymbolNMTypeChar(IRObjectFile &Obj, basic_symbol_iterator I) {
const GlobalValue *GV = Obj.getSymbolGV(I->getRawDataRefImpl());
- if (!GV)
- return 't';
- return getSymbolNMTypeChar(*GV);
+ return !GV ? 't' : getSymbolNMTypeChar(*GV);
}
static bool isObject(SymbolicFile &Obj, basic_symbol_iterator I) {
- auto *ELF = dyn_cast<ELFObjectFileBase>(&Obj);
- if (!ELF)
- return false;
-
- return elf_symbol_iterator(I)->getELFType() == ELF::STT_OBJECT;
+ return !dyn_cast<ELFObjectFileBase>(&Obj)
+ ? false
+ : elf_symbol_iterator(I)->getELFType() == ELF::STT_OBJECT;
}
static char getNMTypeChar(SymbolicFile &Obj, basic_symbol_iterator I) {
uint32_t Symflags = I->getFlags();
if ((Symflags & object::SymbolRef::SF_Weak) && !isa<MachOObjectFile>(Obj)) {
char Ret = isObject(Obj, I) ? 'v' : 'w';
- if (!(Symflags & object::SymbolRef::SF_Undefined))
- Ret = toupper(Ret);
- return Ret;
+ return (!(Symflags & object::SymbolRef::SF_Undefined)) ? toupper(Ret) : Ret;
}
if (Symflags & object::SymbolRef::SF_Undefined)
@@ -835,10 +912,8 @@ static char getNMTypeChar(SymbolicFile &Obj, basic_symbol_iterator I) {
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';
- }
+ if (Ret == 'd' && Host.isOSDarwin() && Symflags & SymbolRef::SF_Const)
+ Ret = 's';
}
else if (COFFObjectFile *COFF = dyn_cast<COFFObjectFile>(&Obj))
Ret = getSymbolNMTypeChar(*COFF, I);
@@ -861,9 +936,8 @@ static char getNMTypeChar(SymbolicFile &Obj, basic_symbol_iterator I) {
// file or zero it is not present.
static unsigned getNsectForSegSect(MachOObjectFile *Obj) {
unsigned Nsect = 1;
- for (section_iterator I = Obj->section_begin(), E = Obj->section_end();
- I != E; ++I) {
- DataRefImpl Ref = I->getRawDataRefImpl();
+ for (auto &S : Obj->sections()) {
+ DataRefImpl Ref = S.getRawDataRefImpl();
StringRef SectionName;
Obj->getSectionName(Ref, SectionName);
StringRef SegmentName = Obj->getSectionFinalSegmentName(Ref);
@@ -883,20 +957,16 @@ static unsigned getNsectInMachO(MachOObjectFile &Obj, BasicSymbolRef Sym) {
DataRefImpl Symb = Sym.getRawDataRefImpl();
if (Obj.is64Bit()) {
MachO::nlist_64 STE = Obj.getSymbol64TableEntry(Symb);
- if ((STE.n_type & MachO::N_TYPE) == MachO::N_SECT)
- return STE.n_sect;
- return 0;
+ return (STE.n_type & MachO::N_TYPE) == MachO::N_SECT ? STE.n_sect : 0;
}
MachO::nlist STE = Obj.getSymbolTableEntry(Symb);
- if ((STE.n_type & MachO::N_TYPE) == MachO::N_SECT)
- return STE.n_sect;
- return 0;
+ return (STE.n_type & MachO::N_TYPE) == MachO::N_SECT ? STE.n_sect : 0;
}
-static void dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName,
- std::string ArchiveName = std::string(),
- std::string ArchitectureName =
- std::string()) {
+static void
+dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName,
+ const std::string &ArchiveName = std::string(),
+ const std::string &ArchitectureName = std::string()) {
auto Symbols = Obj.symbols();
if (DynamicSyms) {
const auto *E = dyn_cast<ELFObjectFileBase>(&Obj);
@@ -945,14 +1015,19 @@ static void dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName,
}
if (PrintAddress && isa<ObjectFile>(Obj)) {
SymbolRef SymRef(Sym);
- ErrorOr<uint64_t> AddressOrErr = SymRef.getAddress();
- if (error(AddressOrErr.getError()))
+ Expected<uint64_t> AddressOrErr = SymRef.getAddress();
+ if (!AddressOrErr) {
+ consumeError(AddressOrErr.takeError());
break;
+ }
S.Address = *AddressOrErr;
}
S.TypeChar = getNMTypeChar(Obj, Sym);
- if (error(Sym.printName(OS)))
- break;
+ std::error_code EC = Sym.printName(OS);
+ if (EC && MachO)
+ OS << "bad string index";
+ else
+ error(EC);
OS << '\0';
S.Sym = Sym;
SymbolList.push_back(S);
@@ -985,10 +1060,10 @@ static bool checkMachOAndArchFlags(SymbolicFile *O, std::string &Filename) {
Triple T;
if (MachO->is64Bit()) {
H_64 = MachO->MachOObjectFile::getHeader64();
- T = MachOObjectFile::getArch(H_64.cputype, H_64.cpusubtype);
+ T = MachOObjectFile::getArchTriple(H_64.cputype, H_64.cpusubtype);
} else {
H = MachO->MachOObjectFile::getHeader();
- T = MachOObjectFile::getArch(H.cputype, H.cpusubtype);
+ T = MachOObjectFile::getArchTriple(H.cputype, H.cpusubtype);
}
if (std::none_of(
ArchFlags.begin(), ArchFlags.end(),
@@ -1005,11 +1080,13 @@ static void dumpSymbolNamesFromFile(std::string &Filename) {
if (error(BufferOrErr.getError(), Filename))
return;
- LLVMContext &Context = getGlobalContext();
- ErrorOr<std::unique_ptr<Binary>> BinaryOrErr = createBinary(
+ LLVMContext Context;
+ Expected<std::unique_ptr<Binary>> BinaryOrErr = createBinary(
BufferOrErr.get()->getMemBufferRef(), NoLLVMBitcode ? nullptr : &Context);
- if (error(BinaryOrErr.getError(), Filename))
+ if (!BinaryOrErr) {
+ error(errorToErrorCode(BinaryOrErr.takeError()), Filename);
return;
+ }
Binary &Bin = *BinaryOrErr.get();
if (Archive *A = dyn_cast<Archive>(&Bin)) {
@@ -1032,27 +1109,31 @@ static void dumpSymbolNamesFromFile(std::string &Filename) {
}
}
- for (Archive::child_iterator I = A->child_begin(), E = A->child_end();
- I != E; ++I) {
- 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())) {
- if (!checkMachOAndArchFlags(O, Filename))
- return;
- if (!PrintFileName) {
- outs() << "\n";
- if (isa<MachOObjectFile>(O)) {
- outs() << Filename << "(" << O->getFileName() << ")";
- } else
- outs() << O->getFileName();
- outs() << ":\n";
+ {
+ Error Err;
+ for (auto &C : A->children(Err)) {
+ Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary(&Context);
+ if (!ChildOrErr) {
+ if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError()))
+ error(std::move(E), Filename, C);
+ continue;
+ }
+ if (SymbolicFile *O = dyn_cast<SymbolicFile>(&*ChildOrErr.get())) {
+ if (!checkMachOAndArchFlags(O, Filename))
+ return;
+ if (!PrintFileName) {
+ outs() << "\n";
+ if (isa<MachOObjectFile>(O)) {
+ outs() << Filename << "(" << O->getFileName() << ")";
+ } else
+ outs() << O->getFileName();
+ outs() << ":\n";
+ }
+ dumpSymbolNamesFromObject(*O, false, Filename);
}
- dumpSymbolNamesFromObject(*O, false, Filename);
}
+ if (Err)
+ error(std::move(Err), A->getFileName());
}
return;
}
@@ -1068,7 +1149,7 @@ static void dumpSymbolNamesFromFile(std::string &Filename) {
I != E; ++I) {
if (ArchFlags[i] == I->getArchTypeName()) {
ArchFound = true;
- ErrorOr<std::unique_ptr<ObjectFile>> ObjOrErr =
+ Expected<std::unique_ptr<ObjectFile>> ObjOrErr =
I->getAsObjectFile();
std::string ArchiveName;
std::string ArchitectureName;
@@ -1086,19 +1167,26 @@ static void dumpSymbolNamesFromFile(std::string &Filename) {
}
dumpSymbolNamesFromObject(Obj, false, ArchiveName,
ArchitectureName);
- } else if (ErrorOr<std::unique_ptr<Archive>> AOrErr =
+ } else if (auto E = isNotObjectErrorInvalidFileType(
+ ObjOrErr.takeError())) {
+ error(std::move(E), Filename, ArchFlags.size() > 1 ?
+ StringRef(I->getArchTypeName()) : StringRef());
+ continue;
+ } else if (Expected<std::unique_ptr<Archive>> AOrErr =
I->getAsArchive()) {
std::unique_ptr<Archive> &A = *AOrErr;
- 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 =
+ Error Err;
+ for (auto &C : A->children(Err)) {
+ Expected<std::unique_ptr<Binary>> ChildOrErr =
C.getAsBinary(&Context);
- if (ChildOrErr.getError())
+ if (!ChildOrErr) {
+ if (auto E = isNotObjectErrorInvalidFileType(
+ ChildOrErr.takeError())) {
+ error(std::move(E), Filename, C, ArchFlags.size() > 1 ?
+ StringRef(I->getArchTypeName()) : StringRef());
+ }
continue;
+ }
if (SymbolicFile *O =
dyn_cast<SymbolicFile>(&*ChildOrErr.get())) {
if (PrintFileName) {
@@ -1118,6 +1206,14 @@ static void dumpSymbolNamesFromFile(std::string &Filename) {
ArchitectureName);
}
}
+ if (Err)
+ error(std::move(Err), A->getFileName());
+ } else {
+ consumeError(AOrErr.takeError());
+ error(Filename + " for architecture " +
+ StringRef(I->getArchTypeName()) +
+ " is not a Mach-O file or an archive file",
+ "Mach-O universal file");
}
}
}
@@ -1137,25 +1233,29 @@ static void dumpSymbolNamesFromFile(std::string &Filename) {
E = UB->end_objects();
I != E; ++I) {
if (HostArchName == I->getArchTypeName()) {
- ErrorOr<std::unique_ptr<ObjectFile>> ObjOrErr = I->getAsObjectFile();
+ Expected<std::unique_ptr<ObjectFile>> ObjOrErr = I->getAsObjectFile();
std::string ArchiveName;
ArchiveName.clear();
if (ObjOrErr) {
ObjectFile &Obj = *ObjOrErr.get();
dumpSymbolNamesFromObject(Obj, false);
- } else if (ErrorOr<std::unique_ptr<Archive>> AOrErr =
+ } else if (auto E = isNotObjectErrorInvalidFileType(
+ ObjOrErr.takeError())) {
+ error(std::move(E), Filename);
+ return;
+ } else if (Expected<std::unique_ptr<Archive>> AOrErr =
I->getAsArchive()) {
std::unique_ptr<Archive> &A = *AOrErr;
- 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 =
+ Error Err;
+ for (auto &C : A->children(Err)) {
+ Expected<std::unique_ptr<Binary>> ChildOrErr =
C.getAsBinary(&Context);
- if (ChildOrErr.getError())
+ if (!ChildOrErr) {
+ if (auto E = isNotObjectErrorInvalidFileType(
+ ChildOrErr.takeError()))
+ error(std::move(E), Filename, C);
continue;
+ }
if (SymbolicFile *O =
dyn_cast<SymbolicFile>(&*ChildOrErr.get())) {
if (PrintFileName)
@@ -1167,6 +1267,14 @@ static void dumpSymbolNamesFromFile(std::string &Filename) {
dumpSymbolNamesFromObject(*O, false, ArchiveName);
}
}
+ if (Err)
+ error(std::move(Err), A->getFileName());
+ } else {
+ consumeError(AOrErr.takeError());
+ error(Filename + " for architecture " +
+ StringRef(I->getArchTypeName()) +
+ " is not a Mach-O file or an archive file",
+ "Mach-O universal file");
}
return;
}
@@ -1178,7 +1286,7 @@ static void dumpSymbolNamesFromFile(std::string &Filename) {
for (MachOUniversalBinary::object_iterator I = UB->begin_objects(),
E = UB->end_objects();
I != E; ++I) {
- ErrorOr<std::unique_ptr<ObjectFile>> ObjOrErr = I->getAsObjectFile();
+ Expected<std::unique_ptr<ObjectFile>> ObjOrErr = I->getAsObjectFile();
std::string ArchiveName;
std::string ArchitectureName;
ArchiveName.clear();
@@ -1197,16 +1305,25 @@ static void dumpSymbolNamesFromFile(std::string &Filename) {
outs() << ":\n";
}
dumpSymbolNamesFromObject(Obj, false, ArchiveName, ArchitectureName);
- } else if (ErrorOr<std::unique_ptr<Archive>> AOrErr = I->getAsArchive()) {
+ } else if (auto E = isNotObjectErrorInvalidFileType(
+ ObjOrErr.takeError())) {
+ error(std::move(E), Filename, moreThanOneArch ?
+ StringRef(I->getArchTypeName()) : StringRef());
+ continue;
+ } else if (Expected<std::unique_ptr<Archive>> AOrErr =
+ I->getAsArchive()) {
std::unique_ptr<Archive> &A = *AOrErr;
- 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 = C.getAsBinary(&Context);
- if (ChildOrErr.getError())
+ Error Err;
+ for (auto &C : A->children(Err)) {
+ Expected<std::unique_ptr<Binary>> ChildOrErr =
+ C.getAsBinary(&Context);
+ if (!ChildOrErr) {
+ if (auto E = isNotObjectErrorInvalidFileType(
+ ChildOrErr.takeError()))
+ error(std::move(E), Filename, C, moreThanOneArch ?
+ StringRef(ArchitectureName) : StringRef());
continue;
+ }
if (SymbolicFile *O = dyn_cast<SymbolicFile>(&*ChildOrErr.get())) {
if (PrintFileName) {
ArchiveName = A->getFileName();
@@ -1226,6 +1343,14 @@ static void dumpSymbolNamesFromFile(std::string &Filename) {
dumpSymbolNamesFromObject(*O, false, ArchiveName, ArchitectureName);
}
}
+ if (Err)
+ error(std::move(Err), A->getFileName());
+ } else {
+ consumeError(AOrErr.takeError());
+ error(Filename + " for architecture " +
+ StringRef(I->getArchTypeName()) +
+ " is not a Mach-O file or an archive file",
+ "Mach-O universal file");
}
}
return;
@@ -1234,14 +1359,12 @@ static void dumpSymbolNamesFromFile(std::string &Filename) {
if (!checkMachOAndArchFlags(O, Filename))
return;
dumpSymbolNamesFromObject(*O, true);
- return;
}
- error("unrecognizable file type", Filename);
}
int main(int argc, char **argv) {
// Print a stack trace if we signal out.
- sys::PrintStackTraceOnErrorSignal();
+ sys::PrintStackTraceOnErrorSignal(argv[0]);
PrettyStackTraceProgram X(argc, argv);
llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
@@ -1251,6 +1374,7 @@ int main(int argc, char **argv) {
if (error(sys::ChangeStdinToBinary()))
return 1;
+ // These calls are needed so that we can read bitcode correctly.
llvm::InitializeAllTargetInfos();
llvm::InitializeAllTargetMCs();
llvm::InitializeAllAsmParsers();
@@ -1270,15 +1394,10 @@ int main(int argc, char **argv) {
PrintAddress = false;
if (OutputFormat == sysv || SizeSort)
PrintSize = true;
-
- switch (InputFilenames.size()) {
- case 0:
+ if (InputFilenames.empty())
InputFilenames.push_back("a.out");
- case 1:
- break;
- default:
+ if (InputFilenames.size() > 1)
MultipleFiles = true;
- }
for (unsigned i = 0; i < ArchFlags.size(); ++i) {
if (ArchFlags[i] == "all") {
@@ -1299,6 +1418,4 @@ int main(int argc, char **argv) {
if (HadError)
return 1;
-
- return 0;
}
diff --git a/tools/llvm-objdump/CMakeLists.txt b/tools/llvm-objdump/CMakeLists.txt
index d0dd4ac0182c3..551847f34eef0 100644
--- a/tools/llvm-objdump/CMakeLists.txt
+++ b/tools/llvm-objdump/CMakeLists.txt
@@ -17,3 +17,7 @@ add_llvm_tool(llvm-objdump
ELFDump.cpp
MachODump.cpp
)
+
+if(HAVE_LIBXAR)
+ target_link_libraries(llvm-objdump ${XAR_LIB})
+endif()
diff --git a/tools/llvm-objdump/COFFDump.cpp b/tools/llvm-objdump/COFFDump.cpp
index 5d21b3320e748..3ec6a1f73750e 100644
--- a/tools/llvm-objdump/COFFDump.cpp
+++ b/tools/llvm-objdump/COFFDump.cpp
@@ -161,13 +161,13 @@ static std::error_code
resolveSectionAndAddress(const COFFObjectFile *Obj, const SymbolRef &Sym,
const coff_section *&ResolvedSection,
uint64_t &ResolvedAddr) {
- ErrorOr<uint64_t> ResolvedAddrOrErr = Sym.getAddress();
- if (std::error_code EC = ResolvedAddrOrErr.getError())
- return EC;
+ Expected<uint64_t> ResolvedAddrOrErr = Sym.getAddress();
+ if (!ResolvedAddrOrErr)
+ return errorToErrorCode(ResolvedAddrOrErr.takeError());
ResolvedAddr = *ResolvedAddrOrErr;
- ErrorOr<section_iterator> Iter = Sym.getSection();
- if (std::error_code EC = Iter.getError())
- return EC;
+ Expected<section_iterator> Iter = Sym.getSection();
+ if (!Iter)
+ return errorToErrorCode(Iter.takeError());
ResolvedSection = Obj->getCOFFSection(**Iter);
return std::error_code();
}
@@ -215,9 +215,9 @@ static std::error_code resolveSymbolName(const std::vector<RelocationRef> &Rels,
SymbolRef Sym;
if (std::error_code EC = resolveSymbol(Rels, Offset, Sym))
return EC;
- ErrorOr<StringRef> NameOrErr = Sym.getName();
- if (std::error_code EC = NameOrErr.getError())
- return EC;
+ Expected<StringRef> NameOrErr = Sym.getName();
+ if (!NameOrErr)
+ return errorToErrorCode(NameOrErr.takeError());
Name = *NameOrErr;
return std::error_code();
}
@@ -252,6 +252,56 @@ printSEHTable(const COFFObjectFile *Obj, uint32_t TableVA, int Count) {
outs() << "\n\n";
}
+template <typename T>
+static void printTLSDirectoryT(const coff_tls_directory<T> *TLSDir) {
+ size_t FormatWidth = sizeof(T) * 2;
+ outs() << "TLS directory:"
+ << "\n StartAddressOfRawData: "
+ << format_hex(TLSDir->StartAddressOfRawData, FormatWidth)
+ << "\n EndAddressOfRawData: "
+ << format_hex(TLSDir->EndAddressOfRawData, FormatWidth)
+ << "\n AddressOfIndex: "
+ << format_hex(TLSDir->AddressOfIndex, FormatWidth)
+ << "\n AddressOfCallBacks: "
+ << format_hex(TLSDir->AddressOfCallBacks, FormatWidth)
+ << "\n SizeOfZeroFill: "
+ << TLSDir->SizeOfZeroFill
+ << "\n Characteristics: "
+ << TLSDir->Characteristics
+ << "\n Alignment: "
+ << TLSDir->getAlignment()
+ << "\n\n";
+}
+
+static void printTLSDirectory(const COFFObjectFile *Obj) {
+ const pe32_header *PE32Header;
+ error(Obj->getPE32Header(PE32Header));
+
+ const pe32plus_header *PE32PlusHeader;
+ error(Obj->getPE32PlusHeader(PE32PlusHeader));
+
+ // Skip if it's not executable.
+ if (!PE32Header && !PE32PlusHeader)
+ return;
+
+ const data_directory *DataDir;
+ error(Obj->getDataDirectory(COFF::TLS_TABLE, DataDir));
+ uintptr_t IntPtr = 0;
+ if (DataDir->RelativeVirtualAddress == 0)
+ return;
+ error(Obj->getRvaPtr(DataDir->RelativeVirtualAddress, IntPtr));
+
+ if (PE32Header) {
+ auto *TLSDir = reinterpret_cast<const coff_tls_directory32 *>(IntPtr);
+ printTLSDirectoryT(TLSDir);
+ } else {
+ auto *TLSDir = reinterpret_cast<const coff_tls_directory64 *>(IntPtr);
+ printTLSDirectoryT(TLSDir);
+ }
+
+ outs() << "\n";
+}
+
static void printLoadConfiguration(const COFFObjectFile *Obj) {
// Skip if it's not executable.
const pe32_header *PE32Header;
@@ -302,11 +352,11 @@ static void printImportTables(const COFFObjectFile *Obj) {
if (I == E)
return;
outs() << "The Import Tables:\n";
- for (; I != E; I = ++I) {
+ for (const ImportDirectoryEntryRef &DirRef : Obj->import_directories()) {
const import_directory_table_entry *Dir;
StringRef Name;
- if (I->getImportTableEntry(Dir)) return;
- if (I->getName(Name)) return;
+ if (DirRef.getImportTableEntry(Dir)) return;
+ if (DirRef.getName(Name)) return;
outs() << format(" lookup %08x time %08x fwd %08x name %08x addr %08x\n\n",
static_cast<uint32_t>(Dir->ImportLookupTableRVA),
@@ -316,17 +366,23 @@ static void printImportTables(const COFFObjectFile *Obj) {
static_cast<uint32_t>(Dir->ImportAddressTableRVA));
outs() << " DLL Name: " << Name << "\n";
outs() << " Hint/Ord Name\n";
- const import_lookup_table_entry32 *entry;
- if (I->getImportLookupEntry(entry))
- return;
- for (; entry->Data; ++entry) {
- if (entry->isOrdinal()) {
- outs() << format(" % 6d\n", entry->getOrdinal());
+ for (const ImportedSymbolRef &Entry : DirRef.imported_symbols()) {
+ bool IsOrdinal;
+ if (Entry.isOrdinal(IsOrdinal))
+ return;
+ if (IsOrdinal) {
+ uint16_t Ordinal;
+ if (Entry.getOrdinal(Ordinal))
+ return;
+ outs() << format(" % 6d\n", Ordinal);
continue;
}
+ uint32_t HintNameRVA;
+ if (Entry.getHintNameRVA(HintNameRVA))
+ return;
uint16_t Hint;
StringRef Name;
- if (Obj->getHintName(entry->getHintNameRVA(), Hint, Name))
+ if (Obj->getHintName(HintNameRVA, Hint, Name))
return;
outs() << format(" % 6d ", Hint) << Name << "\n";
}
@@ -555,6 +611,7 @@ void llvm::printCOFFUnwindInfo(const COFFObjectFile *Obj) {
void llvm::printCOFFFileHeader(const object::ObjectFile *Obj) {
const COFFObjectFile *file = dyn_cast<const COFFObjectFile>(Obj);
+ printTLSDirectory(file);
printLoadConfiguration(file);
printImportTables(file);
printExportTable(file);
@@ -602,6 +659,13 @@ void llvm::printCOFFSymbolTable(const COFFObjectFile *coff) {
SI = SI + Symbol->getNumberOfAuxSymbols();
break;
+ } else if (Symbol->isWeakExternal()) {
+ const coff_aux_weak_external *awe;
+ error(coff->getAuxSymbol<coff_aux_weak_external>(SI + 1, awe));
+
+ outs() << "AUX " << format("indx %d srch %d\n",
+ static_cast<uint32_t>(awe->TagIndex),
+ static_cast<uint32_t>(awe->Characteristics));
} else {
outs() << "AUX Unknown\n";
}
diff --git a/tools/llvm-objdump/MachODump.cpp b/tools/llvm-objdump/MachODump.cpp
index 258c0b520a3d4..4d950f1d7bd7c 100644
--- a/tools/llvm-objdump/MachODump.cpp
+++ b/tools/llvm-objdump/MachODump.cpp
@@ -11,9 +11,9 @@
//
//===----------------------------------------------------------------------===//
+#include "llvm/Object/MachO.h"
#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"
@@ -22,14 +22,13 @@
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCContext.h"
-#include "llvm/MC/MCDisassembler.h"
+#include "llvm/MC/MCDisassembler/MCDisassembler.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCInstPrinter.h"
#include "llvm/MC/MCInstrDesc.h"
#include "llvm/MC/MCInstrInfo.h"
#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/MC/MCSubtargetInfo.h"
-#include "llvm/Object/MachO.h"
#include "llvm/Object/MachOUniversal.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/CommandLine.h"
@@ -43,6 +42,7 @@
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/ToolOutputFile.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cstring>
@@ -52,6 +52,12 @@
#include <cxxabi.h>
#endif
+#ifdef HAVE_LIBXAR
+extern "C" {
+#include <xar/xar.h>
+}
+#endif
+
using namespace llvm;
using namespace object;
@@ -143,11 +149,18 @@ static const Target *GetTarget(const MachOObjectFile *MachOObj,
const char **McpuDefault,
const Target **ThumbTarget) {
// Figure out the target triple.
+ llvm::Triple TT(TripleName);
if (TripleName.empty()) {
- llvm::Triple TT("unknown-unknown-unknown");
- llvm::Triple ThumbTriple = Triple();
- TT = MachOObj->getArch(McpuDefault, &ThumbTriple);
+ TT = MachOObj->getArchTriple(McpuDefault);
TripleName = TT.str();
+ }
+
+ if (TT.getArch() == Triple::arm) {
+ // We've inferred a 32-bit ARM target from the object file. All MachO CPUs
+ // that support ARM are also capable of Thumb mode.
+ llvm::Triple ThumbTriple = TT;
+ std::string ThumbName = (Twine("thumb") + TT.getArchName().substr(3)).str();
+ ThumbTriple.setArchName(ThumbName);
ThumbTripleName = ThumbTriple.str();
}
@@ -172,8 +185,26 @@ static const Target *GetTarget(const MachOObjectFile *MachOObj,
struct SymbolSorter {
bool operator()(const SymbolRef &A, const SymbolRef &B) {
- uint64_t AAddr = (A.getType() != SymbolRef::ST_Function) ? 0 : A.getValue();
- uint64_t BAddr = (B.getType() != SymbolRef::ST_Function) ? 0 : B.getValue();
+ Expected<SymbolRef::Type> ATypeOrErr = A.getType();
+ if (!ATypeOrErr) {
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(ATypeOrErr.takeError(), OS, "");
+ OS.flush();
+ report_fatal_error(Buf);
+ }
+ SymbolRef::Type AType = *ATypeOrErr;
+ Expected<SymbolRef::Type> BTypeOrErr = B.getType();
+ if (!BTypeOrErr) {
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(BTypeOrErr.takeError(), OS, "");
+ OS.flush();
+ report_fatal_error(Buf);
+ }
+ SymbolRef::Type BType = *BTypeOrErr;
+ uint64_t AAddr = (AType != SymbolRef::ST_Function) ? 0 : A.getValue();
+ uint64_t BAddr = (BType != SymbolRef::ST_Function) ? 0 : B.getValue();
return AAddr < BAddr;
}
};
@@ -266,9 +297,14 @@ static void getSectionsAndSymbols(MachOObjectFile *MachOObj,
SmallVectorImpl<uint64_t> &FoundFns,
uint64_t &BaseSegmentAddress) {
for (const SymbolRef &Symbol : MachOObj->symbols()) {
- ErrorOr<StringRef> SymName = Symbol.getName();
- if (std::error_code EC = SymName.getError())
- report_fatal_error(EC.message());
+ Expected<StringRef> SymName = Symbol.getName();
+ if (!SymName) {
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(SymName.takeError(), OS, "");
+ OS.flush();
+ report_fatal_error(Buf);
+ }
if (!SymName->startswith("ltmp"))
Symbols.push_back(Symbol);
}
@@ -324,7 +360,7 @@ static void PrintIndirectSymbolTable(MachOObjectFile *O, bool verbose,
if (cputype & MachO::CPU_ARCH_ABI64)
outs() << format("0x%016" PRIx64, addr + j * stride) << " ";
else
- outs() << format("0x%08" PRIx32, addr + j * stride) << " ";
+ outs() << format("0x%08" PRIx32, (uint32_t)addr + j * stride) << " ";
MachO::dysymtab_command Dysymtab = O->getDysymtabLoadCommand();
uint32_t indirect_symbol = O->getIndirectSymbolTableEntry(Dysymtab, n + j);
if (indirect_symbol == MachO::INDIRECT_SYMBOL_LOCAL) {
@@ -346,9 +382,14 @@ static void PrintIndirectSymbolTable(MachOObjectFile *O, bool verbose,
if (indirect_symbol < Symtab.nsyms) {
symbol_iterator Sym = O->getSymbolByIndex(indirect_symbol);
SymbolRef Symbol = *Sym;
- ErrorOr<StringRef> SymName = Symbol.getName();
- if (std::error_code EC = SymName.getError())
- report_fatal_error(EC.message());
+ Expected<StringRef> SymName = Symbol.getName();
+ if (!SymName) {
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(SymName.takeError(), OS, "");
+ OS.flush();
+ report_fatal_error(Buf);
+ }
outs() << *SymName;
} else {
outs() << "?";
@@ -573,13 +614,26 @@ static void CreateSymbolAddressMap(MachOObjectFile *O,
SymbolAddressMap *AddrMap) {
// Create a map of symbol addresses to symbol names.
for (const SymbolRef &Symbol : O->symbols()) {
- SymbolRef::Type ST = Symbol.getType();
+ Expected<SymbolRef::Type> STOrErr = Symbol.getType();
+ if (!STOrErr) {
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(STOrErr.takeError(), OS, "");
+ OS.flush();
+ report_fatal_error(Buf);
+ }
+ SymbolRef::Type ST = *STOrErr;
if (ST == SymbolRef::ST_Function || ST == SymbolRef::ST_Data ||
ST == SymbolRef::ST_Other) {
uint64_t Address = Symbol.getValue();
- ErrorOr<StringRef> SymNameOrErr = Symbol.getName();
- if (std::error_code EC = SymNameOrErr.getError())
- report_fatal_error(EC.message());
+ Expected<StringRef> SymNameOrErr = Symbol.getName();
+ if (!SymNameOrErr) {
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(SymNameOrErr.takeError(), OS, "");
+ OS.flush();
+ report_fatal_error(Buf);
+ }
StringRef SymName = *SymNameOrErr;
if (!SymName.startswith(".objc"))
(*AddrMap)[Address] = SymName;
@@ -813,9 +867,14 @@ static void DumpLiteralPointerSection(MachOObjectFile *O,
[&](const std::pair<uint64_t, SymbolRef> &P) { return P.first == i; });
if (Reloc != Relocs.end()) {
symbol_iterator RelocSym = Reloc->second;
- ErrorOr<StringRef> SymName = RelocSym->getName();
- if (std::error_code EC = SymName.getError())
- report_fatal_error(EC.message());
+ Expected<StringRef> SymName = RelocSym->getName();
+ if (!SymName) {
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(SymName.takeError(), OS, "");
+ OS.flush();
+ report_fatal_error(Buf);
+ }
outs() << "external relocation entry for symbol:" << *SymName << "\n";
continue;
}
@@ -964,10 +1023,10 @@ static void DumpRawSectionContents(MachOObjectFile *O, const char *sect,
if (O->is64Bit())
outs() << format("%016" PRIx64, addr) << "\t";
else
- outs() << format("%08" PRIx64, sect) << "\t";
+ outs() << format("%08" PRIx64, addr) << "\t";
for (j = 0; j < 4 * sizeof(int32_t) && i + j < size;
j += sizeof(int32_t)) {
- if (i + j + sizeof(int32_t) < size) {
+ if (i + j + sizeof(int32_t) <= size) {
uint32_t long_word;
memcpy(&long_word, sect + i + j, sizeof(int32_t));
if (O->isLittleEndian() != sys::IsLittleEndianHost)
@@ -975,7 +1034,7 @@ static void DumpRawSectionContents(MachOObjectFile *O, const char *sect,
outs() << format("%08" PRIx32, long_word) << " ";
} else {
for (uint32_t k = 0; i + j + k < size; k++) {
- uint8_t byte_word = *(sect + i + j);
+ uint8_t byte_word = *(sect + i + j + k);
outs() << format("%02" PRIx32, (uint32_t)byte_word) << " ";
}
}
@@ -989,6 +1048,12 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
StringRef DisSegName, StringRef DisSectName);
static void DumpProtocolSection(MachOObjectFile *O, const char *sect,
uint32_t size, uint32_t addr);
+#ifdef HAVE_LIBXAR
+static void DumpBitcodeSection(MachOObjectFile *O, const char *sect,
+ uint32_t size, bool verbose,
+ bool PrintXarHeader, bool PrintXarFileHeaders,
+ std::string XarMemberName);
+#endif // defined(HAVE_LIBXAR)
static void DumpSectionContents(StringRef Filename, MachOObjectFile *O,
bool verbose) {
@@ -1050,6 +1115,13 @@ static void DumpSectionContents(StringRef Filename, MachOObjectFile *O,
DumpProtocolSection(O, sect, sect_size, sect_addr);
continue;
}
+#ifdef HAVE_LIBXAR
+ if (SegName == "__LLVM" && SectName == "__bundle") {
+ DumpBitcodeSection(O, sect, sect_size, verbose, !NoSymbolicOperands,
+ ArchiveHeaders, "");
+ continue;
+ }
+#endif // defined(HAVE_LIBXAR)
switch (section_type) {
case MachO::S_REGULAR:
DumpRawSectionContents(O, sect, sect_size, sect_addr);
@@ -1127,10 +1199,10 @@ static bool checkMachOAndArchFlags(ObjectFile *O, StringRef Filename) {
Triple T;
if (MachO->is64Bit()) {
H_64 = MachO->MachOObjectFile::getHeader64();
- T = MachOObjectFile::getArch(H_64.cputype, H_64.cpusubtype);
+ T = MachOObjectFile::getArchTriple(H_64.cputype, H_64.cpusubtype);
} else {
H = MachO->MachOObjectFile::getHeader();
- T = MachOObjectFile::getArch(H.cputype, H.cpusubtype);
+ T = MachOObjectFile::getArchTriple(H.cputype, H.cpusubtype);
}
unsigned i;
for (i = 0; i < ArchFlags.size(); ++i) {
@@ -1159,7 +1231,7 @@ static void ProcessMachO(StringRef Filename, MachOObjectFile *MachOOF,
// If we are doing some processing here on the Mach-O file print the header
// info. And don't print it otherwise like in the case of printing the
// UniversalHeaders or ArchiveHeaders.
- if (Disassemble || PrivateHeaders || ExportsTrie || Rebase || Bind ||
+ if (Disassemble || PrivateHeaders || ExportsTrie || Rebase || Bind || SymbolTable ||
LazyBind || WeakBind || IndirectSymbols || DataInCode || LinkOptHints ||
DylibsUsed || DylibId || ObjcMetaData || (FilterSections.size() != 0)) {
outs() << Filename;
@@ -1192,8 +1264,10 @@ static void ProcessMachO(StringRef Filename, MachOObjectFile *MachOOF,
PrintDylibs(MachOOF, false);
if (DylibId)
PrintDylibs(MachOOF, true);
- if (SymbolTable)
- PrintSymbolTable(MachOOF);
+ if (SymbolTable) {
+ StringRef ArchiveName = ArchiveMemberName == StringRef() ? "" : Filename;
+ PrintSymbolTable(MachOOF, ArchiveName, ArchitectureName);
+ }
if (UnwindInfo)
printMachOUnwindInfo(MachOOF);
if (PrivateHeaders) {
@@ -1214,6 +1288,12 @@ static void ProcessMachO(StringRef Filename, MachOObjectFile *MachOOF,
printLazyBindTable(MachOOF);
if (WeakBind)
printWeakBindTable(MachOOF);
+
+ if (DwarfDumpType != DIDT_Null) {
+ std::unique_ptr<DIContext> DICtx(new DWARFContextInMemory(*MachOOF));
+ // Dump the complete DWARF structure.
+ DICtx->dump(outs(), DwarfDumpType, true /* DumpEH */);
+ }
}
// printUnknownCPUType() helps print_fat_headers for unknown CPU's.
@@ -1323,9 +1403,12 @@ static void printCPUType(uint32_t cputype, uint32_t cpusubtype) {
static void printMachOUniversalHeaders(const object::MachOUniversalBinary *UB,
bool verbose) {
outs() << "Fat headers\n";
- if (verbose)
- outs() << "fat_magic FAT_MAGIC\n";
- else
+ if (verbose) {
+ if (UB->getMagic() == MachO::FAT_MAGIC)
+ outs() << "fat_magic FAT_MAGIC\n";
+ else // UB->getMagic() == MachO::FAT_MAGIC_64
+ outs() << "fat_magic FAT_MAGIC_64\n";
+ } else
outs() << "fat_magic " << format("0x%" PRIx32, MachO::FAT_MAGIC) << "\n";
uint32_t nfat_arch = UB->getNumberOfObjects();
@@ -1452,13 +1535,11 @@ static void printArchiveChild(const Archive::Child &C, bool verbose,
}
static void printArchiveHeaders(Archive *A, bool verbose, bool print_offset) {
- 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;
+ Error Err;
+ for (const auto &C : A->children(Err, false))
printArchiveChild(C, verbose, print_offset);
- }
+ if (Err)
+ report_fatal_error(std::move(Err));
}
// ParseInputMachO() parses the named Mach-O file in Filename and handles the
@@ -1480,29 +1561,31 @@ void llvm::ParseInputMachO(StringRef Filename) {
}
// Attempt to open the binary.
- ErrorOr<OwningBinary<Binary>> BinaryOrErr = createBinary(Filename);
- if (std::error_code EC = BinaryOrErr.getError())
- report_error(Filename, EC);
+ Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(Filename);
+ if (!BinaryOrErr)
+ report_error(Filename, BinaryOrErr.takeError());
Binary &Bin = *BinaryOrErr.get().getBinary();
if (Archive *A = dyn_cast<Archive>(&Bin)) {
outs() << "Archive : " << Filename << "\n";
if (ArchiveHeaders)
printArchiveHeaders(A, !NonVerbose, ArchiveMemberOffsets);
- for (Archive::child_iterator I = A->child_begin(), E = A->child_end();
- I != E; ++I) {
- 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())
+ Error Err;
+ for (auto &C : A->children(Err)) {
+ Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
+ if (!ChildOrErr) {
+ if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError()))
+ report_error(Filename, C, std::move(E));
continue;
+ }
if (MachOObjectFile *O = dyn_cast<MachOObjectFile>(&*ChildOrErr.get())) {
if (!checkMachOAndArchFlags(O, Filename))
return;
ProcessMachO(Filename, O, O->getFileName());
}
}
+ if (Err)
+ report_error(Filename, std::move(Err));
return;
}
if (UniversalHeaders) {
@@ -1521,7 +1604,7 @@ void llvm::ParseInputMachO(StringRef Filename) {
I != E; ++I) {
if (ArchFlags[i] == I->getArchTypeName()) {
ArchFound = true;
- ErrorOr<std::unique_ptr<ObjectFile>> ObjOrErr =
+ Expected<std::unique_ptr<ObjectFile>> ObjOrErr =
I->getAsObjectFile();
std::string ArchitectureName = "";
if (ArchFlags.size() > 1)
@@ -1530,7 +1613,12 @@ void llvm::ParseInputMachO(StringRef Filename) {
ObjectFile &O = *ObjOrErr.get();
if (MachOObjectFile *MachOOF = dyn_cast<MachOObjectFile>(&O))
ProcessMachO(Filename, MachOOF, "", ArchitectureName);
- } else if (ErrorOr<std::unique_ptr<Archive>> AOrErr =
+ } else if (auto E = isNotObjectErrorInvalidFileType(
+ ObjOrErr.takeError())) {
+ report_error(Filename, StringRef(), std::move(E),
+ ArchitectureName);
+ continue;
+ } else if (Expected<std::unique_ptr<Archive>> AOrErr =
I->getAsArchive()) {
std::unique_ptr<Archive> &A = *AOrErr;
outs() << "Archive : " << Filename;
@@ -1539,19 +1627,25 @@ void llvm::ParseInputMachO(StringRef Filename) {
outs() << "\n";
if (ArchiveHeaders)
printArchiveHeaders(A.get(), !NonVerbose, ArchiveMemberOffsets);
- for (Archive::child_iterator AI = A->child_begin(),
- AE = A->child_end();
- AI != AE; ++AI) {
- 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())
+ Error Err;
+ for (auto &C : A->children(Err)) {
+ Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
+ if (!ChildOrErr) {
+ if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError()))
+ report_error(Filename, C, std::move(E), ArchitectureName);
continue;
+ }
if (MachOObjectFile *O =
dyn_cast<MachOObjectFile>(&*ChildOrErr.get()))
ProcessMachO(Filename, O, O->getFileName(), ArchitectureName);
}
+ if (Err)
+ report_error(Filename, std::move(Err));
+ } else {
+ consumeError(AOrErr.takeError());
+ error("Mach-O universal file: " + Filename + " for " +
+ "architecture " + StringRef(I->getArchTypeName()) +
+ " is not a Mach-O file or an archive file");
}
}
}
@@ -1571,32 +1665,42 @@ void llvm::ParseInputMachO(StringRef Filename) {
I != E; ++I) {
if (MachOObjectFile::getHostArch().getArchName() ==
I->getArchTypeName()) {
- ErrorOr<std::unique_ptr<ObjectFile>> ObjOrErr = I->getAsObjectFile();
+ Expected<std::unique_ptr<ObjectFile>> ObjOrErr = I->getAsObjectFile();
std::string ArchiveName;
ArchiveName.clear();
if (ObjOrErr) {
ObjectFile &O = *ObjOrErr.get();
if (MachOObjectFile *MachOOF = dyn_cast<MachOObjectFile>(&O))
ProcessMachO(Filename, MachOOF);
- } else if (ErrorOr<std::unique_ptr<Archive>> AOrErr =
+ } else if (auto E = isNotObjectErrorInvalidFileType(
+ ObjOrErr.takeError())) {
+ report_error(Filename, std::move(E));
+ continue;
+ } else if (Expected<std::unique_ptr<Archive>> AOrErr =
I->getAsArchive()) {
std::unique_ptr<Archive> &A = *AOrErr;
outs() << "Archive : " << Filename << "\n";
if (ArchiveHeaders)
printArchiveHeaders(A.get(), !NonVerbose, ArchiveMemberOffsets);
- for (Archive::child_iterator AI = A->child_begin(),
- AE = A->child_end();
- AI != AE; ++AI) {
- 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())
+ Error Err;
+ for (auto &C : A->children(Err)) {
+ Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
+ if (!ChildOrErr) {
+ if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError()))
+ report_error(Filename, C, std::move(E));
continue;
+ }
if (MachOObjectFile *O =
dyn_cast<MachOObjectFile>(&*ChildOrErr.get()))
ProcessMachO(Filename, O, O->getFileName());
}
+ if (Err)
+ report_error(Filename, std::move(Err));
+ } else {
+ consumeError(AOrErr.takeError());
+ error("Mach-O universal file: " + Filename + " for architecture " +
+ StringRef(I->getArchTypeName()) +
+ " is not a Mach-O file or an archive file");
}
return;
}
@@ -1608,7 +1712,7 @@ void llvm::ParseInputMachO(StringRef Filename) {
for (MachOUniversalBinary::object_iterator I = UB->begin_objects(),
E = UB->end_objects();
I != E; ++I) {
- ErrorOr<std::unique_ptr<ObjectFile>> ObjOrErr = I->getAsObjectFile();
+ Expected<std::unique_ptr<ObjectFile>> ObjOrErr = I->getAsObjectFile();
std::string ArchitectureName = "";
if (moreThanOneArch)
ArchitectureName = I->getArchTypeName();
@@ -1616,7 +1720,12 @@ void llvm::ParseInputMachO(StringRef Filename) {
ObjectFile &Obj = *ObjOrErr.get();
if (MachOObjectFile *MachOOF = dyn_cast<MachOObjectFile>(&Obj))
ProcessMachO(Filename, MachOOF, "", ArchitectureName);
- } else if (ErrorOr<std::unique_ptr<Archive>> AOrErr = I->getAsArchive()) {
+ } else if (auto E = isNotObjectErrorInvalidFileType(
+ ObjOrErr.takeError())) {
+ report_error(StringRef(), Filename, std::move(E), ArchitectureName);
+ continue;
+ } else if (Expected<std::unique_ptr<Archive>> AOrErr =
+ I->getAsArchive()) {
std::unique_ptr<Archive> &A = *AOrErr;
outs() << "Archive : " << Filename;
if (!ArchitectureName.empty())
@@ -1624,14 +1733,14 @@ void llvm::ParseInputMachO(StringRef Filename) {
outs() << "\n";
if (ArchiveHeaders)
printArchiveHeaders(A.get(), !NonVerbose, ArchiveMemberOffsets);
- for (Archive::child_iterator AI = A->child_begin(), AE = A->child_end();
- AI != AE; ++AI) {
- 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())
+ Error Err;
+ for (auto &C : A->children(Err)) {
+ Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
+ if (!ChildOrErr) {
+ if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError()))
+ report_error(Filename, C, std::move(E), ArchitectureName);
continue;
+ }
if (MachOObjectFile *O =
dyn_cast<MachOObjectFile>(&*ChildOrErr.get())) {
if (MachOObjectFile *MachOOF = dyn_cast<MachOObjectFile>(O))
@@ -1639,6 +1748,13 @@ void llvm::ParseInputMachO(StringRef Filename) {
ArchitectureName);
}
}
+ if (Err)
+ report_error(Filename, std::move(Err));
+ } else {
+ consumeError(AOrErr.takeError());
+ error("Mach-O universal file: " + Filename + " for architecture " +
+ StringRef(I->getArchTypeName()) +
+ " is not a Mach-O file or an archive file");
}
}
return;
@@ -1762,9 +1878,14 @@ static int SymbolizerGetOpInfo(void *DisInfo, uint64_t Pc, uint64_t Offset,
}
}
if (reloc_found && isExtern) {
- ErrorOr<StringRef> SymName = Symbol.getName();
- if (std::error_code EC = SymName.getError())
- report_fatal_error(EC.message());
+ Expected<StringRef> SymName = Symbol.getName();
+ if (!SymName) {
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(SymName.takeError(), OS, "");
+ OS.flush();
+ report_fatal_error(Buf);
+ }
const char *name = SymName->data();
op_info->AddSymbol.Present = 1;
op_info->AddSymbol.Name = name;
@@ -1832,9 +1953,14 @@ static int SymbolizerGetOpInfo(void *DisInfo, uint64_t Pc, uint64_t Offset,
// is the offset from the external symbol.
if (info->O->getAnyRelocationPCRel(RE))
op_info->Value -= Pc + Offset + Size;
- ErrorOr<StringRef> SymName = Symbol.getName();
- if (std::error_code EC = SymName.getError())
- report_fatal_error(EC.message());
+ Expected<StringRef> SymName = Symbol.getName();
+ if (!SymName) {
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(SymName.takeError(), OS, "");
+ OS.flush();
+ report_fatal_error(Buf);
+ }
const char *name = SymName->data();
unsigned Type = info->O->getAnyRelocationType(RE);
if (Type == MachO::X86_64_RELOC_SUBTRACTOR) {
@@ -1849,9 +1975,14 @@ static int SymbolizerGetOpInfo(void *DisInfo, uint64_t Pc, uint64_t Offset,
op_info->SubtractSymbol.Name = name;
symbol_iterator RelocSymNext = info->O->getSymbolByIndex(SymbolNum);
Symbol = *RelocSymNext;
- ErrorOr<StringRef> SymNameNext = Symbol.getName();
- if (std::error_code EC = SymNameNext.getError())
- report_fatal_error(EC.message());
+ Expected<StringRef> SymNameNext = Symbol.getName();
+ if (!SymNameNext) {
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(SymNameNext.takeError(), OS, "");
+ OS.flush();
+ report_fatal_error(Buf);
+ }
name = SymNameNext->data();
}
}
@@ -1922,9 +2053,14 @@ static int SymbolizerGetOpInfo(void *DisInfo, uint64_t Pc, uint64_t Offset,
}
if (isExtern) {
- ErrorOr<StringRef> SymName = Symbol.getName();
- if (std::error_code EC = SymName.getError())
- report_fatal_error(EC.message());
+ Expected<StringRef> SymName = Symbol.getName();
+ if (!SymName) {
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(SymName.takeError(), OS, "");
+ OS.flush();
+ report_fatal_error(Buf);
+ }
const char *name = SymName->data();
op_info->AddSymbol.Present = 1;
op_info->AddSymbol.Name = name;
@@ -2042,9 +2178,14 @@ static int SymbolizerGetOpInfo(void *DisInfo, uint64_t Pc, uint64_t Offset,
// NOTE: Scattered relocations don't exist on arm64.
if (!info->O->getPlainRelocationExternal(RE))
return 0;
- ErrorOr<StringRef> SymName = Reloc->getSymbol()->getName();
- if (std::error_code EC = SymName.getError())
- report_fatal_error(EC.message());
+ Expected<StringRef> SymName = Reloc->getSymbol()->getName();
+ if (!SymName) {
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(SymName.takeError(), OS, "");
+ OS.flush();
+ report_fatal_error(Buf);
+ }
const char *name = SymName->data();
op_info->AddSymbol.Present = 1;
op_info->AddSymbol.Name = name;
@@ -2172,9 +2313,14 @@ static const char *GuessIndirectSymbol(uint64_t ReferenceValue,
if (indirect_symbol < Symtab.nsyms) {
symbol_iterator Sym = info->O->getSymbolByIndex(indirect_symbol);
SymbolRef Symbol = *Sym;
- ErrorOr<StringRef> SymName = Symbol.getName();
- if (std::error_code EC = SymName.getError())
- report_fatal_error(EC.message());
+ Expected<StringRef> SymName = Symbol.getName();
+ if (!SymName) {
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(SymName.takeError(), OS, "");
+ OS.flush();
+ report_fatal_error(Buf);
+ }
const char *name = SymName->data();
return name;
}
@@ -2207,9 +2353,14 @@ static const char *GuessIndirectSymbol(uint64_t ReferenceValue,
if (indirect_symbol < Symtab.nsyms) {
symbol_iterator Sym = info->O->getSymbolByIndex(indirect_symbol);
SymbolRef Symbol = *Sym;
- ErrorOr<StringRef> SymName = Symbol.getName();
- if (std::error_code EC = SymName.getError())
- report_fatal_error(EC.message());
+ Expected<StringRef> SymName = Symbol.getName();
+ if (!SymName) {
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(SymName.takeError(), OS, "");
+ OS.flush();
+ report_fatal_error(Buf);
+ }
const char *name = SymName->data();
return name;
}
@@ -2436,9 +2587,14 @@ static const char *get_symbol_64(uint32_t sect_offset, SectionRef S,
const char *SymbolName = nullptr;
if (reloc_found && isExtern) {
n_value = Symbol.getValue();
- ErrorOr<StringRef> NameOrError = Symbol.getName();
- if (std::error_code EC = NameOrError.getError())
- report_fatal_error(EC.message());
+ Expected<StringRef> NameOrError = Symbol.getName();
+ if (!NameOrError) {
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(NameOrError.takeError(), OS, "");
+ OS.flush();
+ report_fatal_error(Buf);
+ }
StringRef Name = *NameOrError;
if (!Name.empty()) {
SymbolName = Name.data();
@@ -5058,6 +5214,9 @@ static void print_image_info32(SectionRef S, struct DisassembleInfo *info) {
struct objc_image_info32 o;
const char *r;
+ if (S == SectionRef())
+ return;
+
StringRef SectName;
S.getName(SectName);
DataRefImpl Ref = S.getRawDataRefImpl();
@@ -5530,6 +5689,373 @@ static void DumpProtocolSection(MachOObjectFile *O, const char *sect,
}
}
+#ifdef HAVE_LIBXAR
+inline void swapStruct(struct xar_header &xar) {
+ sys::swapByteOrder(xar.magic);
+ sys::swapByteOrder(xar.size);
+ sys::swapByteOrder(xar.version);
+ sys::swapByteOrder(xar.toc_length_compressed);
+ sys::swapByteOrder(xar.toc_length_uncompressed);
+ sys::swapByteOrder(xar.cksum_alg);
+}
+
+static void PrintModeVerbose(uint32_t mode) {
+ switch(mode & S_IFMT){
+ case S_IFDIR:
+ outs() << "d";
+ break;
+ case S_IFCHR:
+ outs() << "c";
+ break;
+ case S_IFBLK:
+ outs() << "b";
+ break;
+ case S_IFREG:
+ outs() << "-";
+ break;
+ case S_IFLNK:
+ outs() << "l";
+ break;
+ case S_IFSOCK:
+ outs() << "s";
+ break;
+ default:
+ outs() << "?";
+ break;
+ }
+
+ /* owner permissions */
+ if(mode & S_IREAD)
+ outs() << "r";
+ else
+ outs() << "-";
+ if(mode & S_IWRITE)
+ outs() << "w";
+ else
+ outs() << "-";
+ if(mode & S_ISUID)
+ outs() << "s";
+ else if(mode & S_IEXEC)
+ outs() << "x";
+ else
+ outs() << "-";
+
+ /* group permissions */
+ if(mode & (S_IREAD >> 3))
+ outs() << "r";
+ else
+ outs() << "-";
+ if(mode & (S_IWRITE >> 3))
+ outs() << "w";
+ else
+ outs() << "-";
+ if(mode & S_ISGID)
+ outs() << "s";
+ else if(mode & (S_IEXEC >> 3))
+ outs() << "x";
+ else
+ outs() << "-";
+
+ /* other permissions */
+ if(mode & (S_IREAD >> 6))
+ outs() << "r";
+ else
+ outs() << "-";
+ if(mode & (S_IWRITE >> 6))
+ outs() << "w";
+ else
+ outs() << "-";
+ if(mode & S_ISVTX)
+ outs() << "t";
+ else if(mode & (S_IEXEC >> 6))
+ outs() << "x";
+ else
+ outs() << "-";
+}
+
+static void PrintXarFilesSummary(const char *XarFilename, xar_t xar) {
+ xar_iter_t xi;
+ xar_file_t xf;
+ xar_iter_t xp;
+ const char *key, *type, *mode, *user, *group, *size, *mtime, *name, *m;
+ char *endp;
+ uint32_t mode_value;
+
+ xi = xar_iter_new();
+ if (!xi) {
+ errs() << "Can't obtain an xar iterator for xar archive "
+ << XarFilename << "\n";
+ return;
+ }
+
+ // Go through the xar's files.
+ for (xf = xar_file_first(xar, xi); xf; xf = xar_file_next(xi)) {
+ xp = xar_iter_new();
+ if(!xp){
+ errs() << "Can't obtain an xar iterator for xar archive "
+ << XarFilename << "\n";
+ return;
+ }
+ type = nullptr;
+ mode = nullptr;
+ user = nullptr;
+ group = nullptr;
+ size = nullptr;
+ mtime = nullptr;
+ name = nullptr;
+ for(key = xar_prop_first(xf, xp); key; key = xar_prop_next(xp)){
+ const char *val = nullptr;
+ xar_prop_get(xf, key, &val);
+#if 0 // Useful for debugging.
+ outs() << "key: " << key << " value: " << val << "\n";
+#endif
+ if(strcmp(key, "type") == 0)
+ type = val;
+ if(strcmp(key, "mode") == 0)
+ mode = val;
+ if(strcmp(key, "user") == 0)
+ user = val;
+ if(strcmp(key, "group") == 0)
+ group = val;
+ if(strcmp(key, "data/size") == 0)
+ size = val;
+ if(strcmp(key, "mtime") == 0)
+ mtime = val;
+ if(strcmp(key, "name") == 0)
+ name = val;
+ }
+ if(mode != nullptr){
+ mode_value = strtoul(mode, &endp, 8);
+ if(*endp != '\0')
+ outs() << "(mode: \"" << mode << "\" contains non-octal chars) ";
+ if(strcmp(type, "file") == 0)
+ mode_value |= S_IFREG;
+ PrintModeVerbose(mode_value);
+ outs() << " ";
+ }
+ if(user != nullptr)
+ outs() << format("%10s/", user);
+ if(group != nullptr)
+ outs() << format("%-10s ", group);
+ if(size != nullptr)
+ outs() << format("%7s ", size);
+ if(mtime != nullptr){
+ for(m = mtime; *m != 'T' && *m != '\0'; m++)
+ outs() << *m;
+ if(*m == 'T')
+ m++;
+ outs() << " ";
+ for( ; *m != 'Z' && *m != '\0'; m++)
+ outs() << *m;
+ outs() << " ";
+ }
+ if(name != nullptr)
+ outs() << name;
+ outs() << "\n";
+ }
+}
+
+static void DumpBitcodeSection(MachOObjectFile *O, const char *sect,
+ uint32_t size, bool verbose,
+ bool PrintXarHeader, bool PrintXarFileHeaders,
+ std::string XarMemberName) {
+ if(size < sizeof(struct xar_header)) {
+ outs() << "size of (__LLVM,__bundle) section too small (smaller than size "
+ "of struct xar_header)\n";
+ return;
+ }
+ struct xar_header XarHeader;
+ memcpy(&XarHeader, sect, sizeof(struct xar_header));
+ if (sys::IsLittleEndianHost)
+ swapStruct(XarHeader);
+ if (PrintXarHeader) {
+ if (!XarMemberName.empty())
+ outs() << "In xar member " << XarMemberName << ": ";
+ else
+ outs() << "For (__LLVM,__bundle) section: ";
+ outs() << "xar header\n";
+ if (XarHeader.magic == XAR_HEADER_MAGIC)
+ outs() << " magic XAR_HEADER_MAGIC\n";
+ else
+ outs() << " magic "
+ << format_hex(XarHeader.magic, 10, true)
+ << " (not XAR_HEADER_MAGIC)\n";
+ outs() << " size " << XarHeader.size << "\n";
+ outs() << " version " << XarHeader.version << "\n";
+ outs() << " toc_length_compressed " << XarHeader.toc_length_compressed
+ << "\n";
+ outs() << "toc_length_uncompressed " << XarHeader.toc_length_uncompressed
+ << "\n";
+ outs() << " cksum_alg ";
+ switch (XarHeader.cksum_alg) {
+ case XAR_CKSUM_NONE:
+ outs() << "XAR_CKSUM_NONE\n";
+ break;
+ case XAR_CKSUM_SHA1:
+ outs() << "XAR_CKSUM_SHA1\n";
+ break;
+ case XAR_CKSUM_MD5:
+ outs() << "XAR_CKSUM_MD5\n";
+ break;
+#ifdef XAR_CKSUM_SHA256
+ case XAR_CKSUM_SHA256:
+ outs() << "XAR_CKSUM_SHA256\n";
+ break;
+#endif
+#ifdef XAR_CKSUM_SHA512
+ case XAR_CKSUM_SHA512:
+ outs() << "XAR_CKSUM_SHA512\n";
+ break;
+#endif
+ default:
+ outs() << XarHeader.cksum_alg << "\n";
+ }
+ }
+
+ SmallString<128> XarFilename;
+ int FD;
+ std::error_code XarEC =
+ sys::fs::createTemporaryFile("llvm-objdump", "xar", FD, XarFilename);
+ if (XarEC) {
+ errs() << XarEC.message() << "\n";
+ return;
+ }
+ tool_output_file XarFile(XarFilename, FD);
+ raw_fd_ostream &XarOut = XarFile.os();
+ StringRef XarContents(sect, size);
+ XarOut << XarContents;
+ XarOut.close();
+ if (XarOut.has_error())
+ return;
+
+ xar_t xar = xar_open(XarFilename.c_str(), READ);
+ if (!xar) {
+ errs() << "Can't create temporary xar archive " << XarFilename << "\n";
+ return;
+ }
+
+ SmallString<128> TocFilename;
+ std::error_code TocEC =
+ sys::fs::createTemporaryFile("llvm-objdump", "toc", TocFilename);
+ if (TocEC) {
+ errs() << TocEC.message() << "\n";
+ return;
+ }
+ xar_serialize(xar, TocFilename.c_str());
+
+ if (PrintXarFileHeaders) {
+ if (!XarMemberName.empty())
+ outs() << "In xar member " << XarMemberName << ": ";
+ else
+ outs() << "For (__LLVM,__bundle) section: ";
+ outs() << "xar archive files:\n";
+ PrintXarFilesSummary(XarFilename.c_str(), xar);
+ }
+
+ ErrorOr<std::unique_ptr<MemoryBuffer>> FileOrErr =
+ MemoryBuffer::getFileOrSTDIN(TocFilename.c_str());
+ if (std::error_code EC = FileOrErr.getError()) {
+ errs() << EC.message() << "\n";
+ return;
+ }
+ std::unique_ptr<MemoryBuffer> &Buffer = FileOrErr.get();
+
+ if (!XarMemberName.empty())
+ outs() << "In xar member " << XarMemberName << ": ";
+ else
+ outs() << "For (__LLVM,__bundle) section: ";
+ outs() << "xar table of contents:\n";
+ outs() << Buffer->getBuffer() << "\n";
+
+ // TODO: Go through the xar's files.
+ xar_iter_t xi = xar_iter_new();
+ if(!xi){
+ errs() << "Can't obtain an xar iterator for xar archive "
+ << XarFilename.c_str() << "\n";
+ xar_close(xar);
+ return;
+ }
+ for(xar_file_t xf = xar_file_first(xar, xi); xf; xf = xar_file_next(xi)){
+ const char *key;
+ xar_iter_t xp;
+ const char *member_name, *member_type, *member_size_string;
+ size_t member_size;
+
+ xp = xar_iter_new();
+ if(!xp){
+ errs() << "Can't obtain an xar iterator for xar archive "
+ << XarFilename.c_str() << "\n";
+ xar_close(xar);
+ return;
+ }
+ member_name = NULL;
+ member_type = NULL;
+ member_size_string = NULL;
+ for(key = xar_prop_first(xf, xp); key; key = xar_prop_next(xp)){
+ const char *val = nullptr;
+ xar_prop_get(xf, key, &val);
+#if 0 // Useful for debugging.
+ outs() << "key: " << key << " value: " << val << "\n";
+#endif
+ if(strcmp(key, "name") == 0)
+ member_name = val;
+ if(strcmp(key, "type") == 0)
+ member_type = val;
+ if(strcmp(key, "data/size") == 0)
+ member_size_string = val;
+ }
+ /*
+ * If we find a file with a name, date/size and type properties
+ * and with the type being "file" see if that is a xar file.
+ */
+ if (member_name != NULL && member_type != NULL &&
+ strcmp(member_type, "file") == 0 &&
+ member_size_string != NULL){
+ // Extract the file into a buffer.
+ char *endptr;
+ member_size = strtoul(member_size_string, &endptr, 10);
+ if (*endptr == '\0' && member_size != 0) {
+ char *buffer = (char *) ::operator new (member_size);
+ if (xar_extract_tobuffersz(xar, xf, &buffer, &member_size) == 0) {
+#if 0 // Useful for debugging.
+ outs() << "xar member: " << member_name << " extracted\n";
+#endif
+ // Set the XarMemberName we want to see printed in the header.
+ std::string OldXarMemberName;
+ // If XarMemberName is already set this is nested. So
+ // save the old name and create the nested name.
+ if (!XarMemberName.empty()) {
+ OldXarMemberName = XarMemberName;
+ XarMemberName =
+ (Twine("[") + XarMemberName + "]" + member_name).str();
+ } else {
+ OldXarMemberName = "";
+ XarMemberName = member_name;
+ }
+ // See if this is could be a xar file (nested).
+ if (member_size >= sizeof(struct xar_header)) {
+#if 0 // Useful for debugging.
+ outs() << "could be a xar file: " << member_name << "\n";
+#endif
+ memcpy((char *)&XarHeader, buffer, sizeof(struct xar_header));
+ if (sys::IsLittleEndianHost)
+ swapStruct(XarHeader);
+ if(XarHeader.magic == XAR_HEADER_MAGIC)
+ DumpBitcodeSection(O, buffer, member_size, verbose,
+ PrintXarHeader, PrintXarFileHeaders,
+ XarMemberName);
+ }
+ XarMemberName = OldXarMemberName;
+ }
+ delete buffer;
+ }
+ }
+ xar_iter_free(xp);
+ }
+ xar_close(xar);
+}
+#endif // defined(HAVE_LIBXAR)
+
static void printObjcMetaData(MachOObjectFile *O, bool verbose) {
if (O->is64Bit())
printObjc2_64bit_MetaData(O, verbose);
@@ -5948,7 +6474,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
return;
}
- // Set up thumb disassembler.
+ // Set up separate thumb disassembler if needed.
std::unique_ptr<const MCRegisterInfo> ThumbMRI;
std::unique_ptr<const MCAsmInfo> ThumbAsmInfo;
std::unique_ptr<const MCSubtargetInfo> ThumbSTI;
@@ -6077,13 +6603,26 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
SymbolAddressMap AddrMap;
bool DisSymNameFound = false;
for (const SymbolRef &Symbol : MachOOF->symbols()) {
- SymbolRef::Type ST = Symbol.getType();
+ Expected<SymbolRef::Type> STOrErr = Symbol.getType();
+ if (!STOrErr) {
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(STOrErr.takeError(), OS, "");
+ OS.flush();
+ report_fatal_error(Buf);
+ }
+ SymbolRef::Type ST = *STOrErr;
if (ST == SymbolRef::ST_Function || ST == SymbolRef::ST_Data ||
ST == SymbolRef::ST_Other) {
uint64_t Address = Symbol.getValue();
- ErrorOr<StringRef> SymNameOrErr = Symbol.getName();
- if (std::error_code EC = SymNameOrErr.getError())
- report_fatal_error(EC.message());
+ Expected<StringRef> SymNameOrErr = Symbol.getName();
+ if (!SymNameOrErr) {
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(SymNameOrErr.takeError(), OS, "");
+ OS.flush();
+ report_fatal_error(Buf);
+ }
StringRef SymName = *SymNameOrErr;
AddrMap[Address] = SymName;
if (!DisSymName.empty() && DisSymName == SymName)
@@ -6121,20 +6660,61 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
ThumbSymbolizerInfo.adrp_addr = 0;
ThumbSymbolizerInfo.adrp_inst = 0;
+ unsigned int Arch = MachOOF->getArch();
+
+ // Skip all symbols if this is a stubs file.
+ if (Bytes.size() == 0)
+ return;
+
// Disassemble symbol by symbol.
for (unsigned SymIdx = 0; SymIdx != Symbols.size(); SymIdx++) {
- ErrorOr<StringRef> SymNameOrErr = Symbols[SymIdx].getName();
- if (std::error_code EC = SymNameOrErr.getError())
- report_fatal_error(EC.message());
+ Expected<StringRef> SymNameOrErr = Symbols[SymIdx].getName();
+ if (!SymNameOrErr) {
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(SymNameOrErr.takeError(), OS, "");
+ OS.flush();
+ report_fatal_error(Buf);
+ }
StringRef SymName = *SymNameOrErr;
- SymbolRef::Type ST = Symbols[SymIdx].getType();
+ Expected<SymbolRef::Type> STOrErr = Symbols[SymIdx].getType();
+ if (!STOrErr) {
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(STOrErr.takeError(), OS, "");
+ OS.flush();
+ report_fatal_error(Buf);
+ }
+ SymbolRef::Type ST = *STOrErr;
if (ST != SymbolRef::ST_Function && ST != SymbolRef::ST_Data)
continue;
// Make sure the symbol is defined in this section.
bool containsSym = Sections[SectIdx].containsSymbol(Symbols[SymIdx]);
- if (!containsSym)
+ if (!containsSym) {
+ if (!DisSymName.empty() && DisSymName == SymName) {
+ outs() << "-dis-symname: " << DisSymName << " not in the section\n";
+ return;
+ }
+ continue;
+ }
+ // The __mh_execute_header is special and we need to deal with that fact
+ // this symbol is before the start of the (__TEXT,__text) section and at the
+ // address of the start of the __TEXT segment. This is because this symbol
+ // is an N_SECT symbol in the (__TEXT,__text) but its address is before the
+ // start of the section in a standard MH_EXECUTE filetype.
+ if (!DisSymName.empty() && DisSymName == "__mh_execute_header") {
+ outs() << "-dis-symname: __mh_execute_header not in any section\n";
+ return;
+ }
+ // When this code is trying to disassemble a symbol at a time and in the
+ // case there is only the __mh_execute_header symbol left as in a stripped
+ // executable, we need to deal with this by ignoring this symbol so the
+ // whole section is disassembled and this symbol is then not displayed.
+ if (SymName == "__mh_execute_header" || SymName == "__mh_dylib_header" ||
+ SymName == "__mh_bundle_header" || SymName == "__mh_object_header" ||
+ SymName == "__mh_preload_header" || SymName == "__mh_dylinker_header")
continue;
// If we are only disassembling one symbol see if this is that symbol.
@@ -6142,17 +6722,32 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
continue;
// Start at the address of the symbol relative to the section's address.
+ uint64_t SectSize = Sections[SectIdx].getSize();
uint64_t Start = Symbols[SymIdx].getValue();
uint64_t SectionAddress = Sections[SectIdx].getAddress();
Start -= SectionAddress;
+ if (Start > SectSize) {
+ outs() << "section data ends, " << SymName
+ << " lies outside valid range\n";
+ return;
+ }
+
// Stop disassembling either at the beginning of the next symbol or at
// the end of the section.
bool containsNextSym = false;
uint64_t NextSym = 0;
uint64_t NextSymIdx = SymIdx + 1;
while (Symbols.size() > NextSymIdx) {
- SymbolRef::Type NextSymType = Symbols[NextSymIdx].getType();
+ Expected<SymbolRef::Type> STOrErr = Symbols[NextSymIdx].getType();
+ if (!STOrErr) {
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(STOrErr.takeError(), OS, "");
+ OS.flush();
+ report_fatal_error(Buf);
+ }
+ SymbolRef::Type NextSymType = *STOrErr;
if (NextSymType == SymbolRef::ST_Function) {
containsNextSym =
Sections[SectIdx].containsSymbol(Symbols[NextSymIdx]);
@@ -6163,15 +6758,17 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
++NextSymIdx;
}
- uint64_t SectSize = Sections[SectIdx].getSize();
- uint64_t End = containsNextSym ? NextSym : SectSize;
+ uint64_t End = containsNextSym ? std::min(NextSym, SectSize) : SectSize;
uint64_t Size;
symbolTableWorked = true;
DataRefImpl Symb = Symbols[SymIdx].getRawDataRefImpl();
- bool isThumb =
- (MachOOF->getSymbolFlags(Symb) & SymbolRef::SF_Thumb) && ThumbTarget;
+ bool IsThumb = MachOOF->getSymbolFlags(Symb) & SymbolRef::SF_Thumb;
+
+ // We only need the dedicated Thumb target if there's a real choice
+ // (i.e. we're not targeting M-class) and the function is Thumb.
+ bool UseThumbTarget = IsThumb && ThumbTarget;
outs() << SymName << ":\n";
DILineInfo lastLine;
@@ -6189,7 +6786,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
outs() << format("%8" PRIx64 ":", PC);
}
}
- if (!NoShowRawInsn)
+ if (!NoShowRawInsn || Arch == Triple::arm)
outs() << "\t";
// Check the data in code table here to see if this is data not an
@@ -6215,19 +6812,19 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
raw_svector_ostream Annotations(AnnotationsBytes);
bool gotInst;
- if (isThumb)
+ if (UseThumbTarget)
gotInst = ThumbDisAsm->getInstruction(Inst, Size, Bytes.slice(Index),
PC, DebugOut, Annotations);
else
gotInst = DisAsm->getInstruction(Inst, Size, Bytes.slice(Index), PC,
DebugOut, Annotations);
if (gotInst) {
- if (!NoShowRawInsn) {
+ if (!NoShowRawInsn || Arch == Triple::arm) {
dumpBytes(makeArrayRef(Bytes.data() + Index, Size), outs());
}
formatted_raw_ostream FormattedOS(outs());
StringRef AnnotationsStr = Annotations.str();
- if (isThumb)
+ if (UseThumbTarget)
ThumbIP->printInst(&Inst, FormattedOS, AnnotationsStr, *ThumbSTI);
else
IP->printInst(&Inst, FormattedOS, AnnotationsStr, *STI);
@@ -6249,14 +6846,21 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
outs() << format("\t.byte 0x%02x #bad opcode\n",
*(Bytes.data() + Index) & 0xff);
Size = 1; // skip exactly one illegible byte and move on.
- } else if (Arch == Triple::aarch64) {
+ } else if (Arch == Triple::aarch64 ||
+ (Arch == Triple::arm && !IsThumb)) {
uint32_t opcode = (*(Bytes.data() + Index) & 0xff) |
(*(Bytes.data() + Index + 1) & 0xff) << 8 |
(*(Bytes.data() + Index + 2) & 0xff) << 16 |
(*(Bytes.data() + Index + 3) & 0xff) << 24;
outs() << format("\t.long\t0x%08x\n", opcode);
Size = 4;
- } else {
+ } else if (Arch == Triple::arm) {
+ assert(IsThumb && "ARM mode should have been dealt with above");
+ uint32_t opcode = (*(Bytes.data() + Index) & 0xff) |
+ (*(Bytes.data() + Index + 1) & 0xff) << 8;
+ outs() << format("\t.short\t0x%04x\n", opcode);
+ Size = 2;
+ } else{
errs() << "llvm-objdump: warning: invalid instruction encoding\n";
if (Size == 0)
Size = 1; // skip illegible bytes
@@ -6285,7 +6889,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
outs() << format("%8" PRIx64 ":", PC);
}
}
- if (!NoShowRawInsn) {
+ if (!NoShowRawInsn || Arch == Triple::arm) {
outs() << "\t";
dumpBytes(makeArrayRef(Bytes.data() + Index, InstSize), outs());
}
@@ -6387,9 +6991,14 @@ static void findUnwindRelocNameAddend(const MachOObjectFile *Obj,
const RelocationRef &Reloc, uint64_t Addr,
StringRef &Name, uint64_t &Addend) {
if (Reloc.getSymbol() != Obj->symbol_end()) {
- ErrorOr<StringRef> NameOrErr = Reloc.getSymbol()->getName();
- if (std::error_code EC = NameOrErr.getError())
- report_fatal_error(EC.message());
+ Expected<StringRef> NameOrErr = Reloc.getSymbol()->getName();
+ if (!NameOrErr) {
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(NameOrErr.takeError(), OS, "");
+ OS.flush();
+ report_fatal_error(Buf);
+ }
Name = *NameOrErr;
Addend = Addr;
return;
@@ -6412,12 +7021,25 @@ static void findUnwindRelocNameAddend(const MachOObjectFile *Obj,
// Go back one so that SymbolAddress <= Addr.
--Sym;
- section_iterator SymSection = *Sym->second.getSection();
+ auto SectOrErr = Sym->second.getSection();
+ if (!SectOrErr) {
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(SectOrErr.takeError(), OS, "");
+ OS.flush();
+ report_fatal_error(Buf);
+ }
+ section_iterator SymSection = *SectOrErr;
if (RelocSection == *SymSection) {
// There's a valid symbol in the same section before this reference.
- ErrorOr<StringRef> NameOrErr = Sym->second.getName();
- if (std::error_code EC = NameOrErr.getError())
- report_fatal_error(EC.message());
+ Expected<StringRef> NameOrErr = Sym->second.getName();
+ if (!NameOrErr) {
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(NameOrErr.takeError(), OS, "");
+ OS.flush();
+ report_fatal_error(Buf);
+ }
Name = *NameOrErr;
Addend = Addr - Sym->first;
return;
@@ -6750,268 +7372,18 @@ 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 = *SymRef.getSection();
+ auto SectOrErr = SymRef.getSection();
+ if (!SectOrErr) {
+ // TODO: Actually report errors helpfully.
+ consumeError(SectOrErr.takeError());
+ continue;
+ }
+ section_iterator Section = *SectOrErr;
if (Section == Obj->section_end())
continue;
@@ -7026,8 +7398,6 @@ void llvm::printMachOUnwindInfo(const MachOObjectFile *Obj) {
printMachOCompactUnwindSection(Obj, Symbols, Section);
else if (SectName == "__unwind_info")
printMachOUnwindInfoSection(Obj, Symbols, Section);
- else if (SectName == "__eh_frame")
- printMachOEHFrameSection(Obj, Symbols, Section);
}
}
@@ -7145,6 +7515,10 @@ static void PrintMachHeader(uint32_t magic, uint32_t cputype,
break;
}
break;
+ default:
+ outs() << format(" %7d", cputype);
+ outs() << format(" %10d", cpusubtype & ~MachO::CPU_SUBTYPE_MASK);
+ break;
}
if ((cpusubtype & MachO::CPU_SUBTYPE_MASK) == MachO::CPU_SUBTYPE_LIB64) {
outs() << " LIB64";
@@ -8488,7 +8862,7 @@ static void PrintDylibCommand(MachO::dylib_command dl, const char *Ptr) {
static void PrintLinkEditDataCommand(MachO::linkedit_data_command ld,
uint32_t object_size) {
if (ld.cmd == MachO::LC_CODE_SIGNATURE)
- outs() << " cmd LC_FUNCTION_STARTS\n";
+ outs() << " cmd LC_CODE_SIGNATURE\n";
else if (ld.cmd == MachO::LC_SEGMENT_SPLIT_INFO)
outs() << " cmd LC_SEGMENT_SPLIT_INFO\n";
else if (ld.cmd == MachO::LC_FUNCTION_STARTS)
diff --git a/tools/llvm-objdump/Makefile b/tools/llvm-objdump/Makefile
deleted file mode 100644
index 7c165230dde49..0000000000000
--- a/tools/llvm-objdump/Makefile
+++ /dev/null
@@ -1,17 +0,0 @@
-##===- tools/llvm-objdump/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-objdump
-LINK_COMPONENTS := all-targets DebugInfoDWARF MC MCParser MCDisassembler Object
-
-# This tool has no plugins, optimize startup time.
-TOOL_NO_EXPORTS := 1
-
-include $(LEVEL)/Makefile.common
diff --git a/tools/llvm-objdump/llvm-objdump.cpp b/tools/llvm-objdump/llvm-objdump.cpp
index d5ae5de4b5a33..ed55c918b58a5 100644
--- a/tools/llvm-objdump/llvm-objdump.cpp
+++ b/tools/llvm-objdump/llvm-objdump.cpp
@@ -22,20 +22,21 @@
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/Triple.h"
#include "llvm/CodeGen/FaultMaps.h"
+#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCContext.h"
-#include "llvm/MC/MCDisassembler.h"
+#include "llvm/MC/MCDisassembler/MCDisassembler.h"
+#include "llvm/MC/MCDisassembler/MCRelocationInfo.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/MCRelocationInfo.h"
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/Object/Archive.h"
-#include "llvm/Object/ELFObjectFile.h"
#include "llvm/Object/COFF.h"
+#include "llvm/Object/ELFObjectFile.h"
#include "llvm/Object/MachO.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Support/Casting.h"
@@ -58,6 +59,7 @@
#include <cctype>
#include <cstring>
#include <system_error>
+#include <utility>
using namespace llvm;
using namespace object;
@@ -181,6 +183,11 @@ cl::opt<bool>
cl::opt<bool> PrintFaultMaps("fault-map-section",
cl::desc("Display contents of faultmap section"));
+cl::opt<DIDumpType> llvm::DwarfDumpType(
+ "dwarf", cl::init(DIDT_Null), cl::desc("Dump of dwarf debug sections:"),
+ cl::values(clEnumValN(DIDT_Frames, "frames", ".debug_frame"),
+ clEnumValEnd));
+
static StringRef ToolName;
namespace {
@@ -191,7 +198,7 @@ public:
SectionFilterIterator(FilterPredicate P,
llvm::object::section_iterator const &I,
llvm::object::section_iterator const &E)
- : Predicate(P), Iterator(I), End(E) {
+ : Predicate(std::move(P)), Iterator(I), End(E) {
ScanPredicate();
}
const llvm::object::SectionRef &operator*() const { return *Iterator; }
@@ -218,7 +225,7 @@ private:
class SectionFilter {
public:
SectionFilter(FilterPredicate P, llvm::object::ObjectFile const &O)
- : Predicate(P), Object(O) {}
+ : Predicate(std::move(P)), Object(O) {}
SectionFilterIterator begin() {
return SectionFilterIterator(Predicate, Object.section_begin(),
Object.section_end());
@@ -257,6 +264,12 @@ void llvm::error(std::error_code EC) {
exit(1);
}
+LLVM_ATTRIBUTE_NORETURN void llvm::error(Twine Message) {
+ errs() << ToolName << ": " << Message << ".\n";
+ errs().flush();
+ exit(1);
+}
+
LLVM_ATTRIBUTE_NORETURN void llvm::report_error(StringRef File,
std::error_code EC) {
assert(EC);
@@ -264,6 +277,52 @@ LLVM_ATTRIBUTE_NORETURN void llvm::report_error(StringRef File,
exit(1);
}
+LLVM_ATTRIBUTE_NORETURN void llvm::report_error(StringRef File,
+ llvm::Error E) {
+ assert(E);
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(std::move(E), OS, "");
+ OS.flush();
+ errs() << ToolName << ": '" << File << "': " << Buf;
+ exit(1);
+}
+
+LLVM_ATTRIBUTE_NORETURN void llvm::report_error(StringRef ArchiveName,
+ StringRef FileName,
+ llvm::Error E,
+ StringRef ArchitectureName) {
+ assert(E);
+ errs() << ToolName << ": ";
+ if (ArchiveName != "")
+ errs() << ArchiveName << "(" << FileName << ")";
+ else
+ errs() << FileName;
+ if (!ArchitectureName.empty())
+ errs() << " (for architecture " << ArchitectureName << ")";
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(std::move(E), OS, "");
+ OS.flush();
+ errs() << " " << Buf;
+ exit(1);
+}
+
+LLVM_ATTRIBUTE_NORETURN void llvm::report_error(StringRef ArchiveName,
+ const object::Archive::Child &C,
+ llvm::Error E,
+ StringRef ArchitectureName) {
+ ErrorOr<StringRef> NameOrErr = C.getName();
+ // TODO: if we have a error getting the name then it would be nice to print
+ // the index of which archive member this is and or its offset in the
+ // archive instead of "???" as the name.
+ if (NameOrErr.getError())
+ llvm::report_error(ArchiveName, "???", std::move(E), ArchitectureName);
+ else
+ llvm::report_error(ArchiveName, NameOrErr.get(), std::move(E),
+ ArchitectureName);
+}
+
static const Target *getTarget(const ObjectFile *Obj = nullptr) {
// Figure out the target triple.
llvm::Triple TheTriple("unknown-unknown-unknown");
@@ -308,12 +367,15 @@ public:
ArrayRef<uint8_t> Bytes, uint64_t Address,
raw_ostream &OS, StringRef Annot,
MCSubtargetInfo const &STI) {
- outs() << format("%8" PRIx64 ":", Address);
+ OS << format("%8" PRIx64 ":", Address);
if (!NoShowRawInsn) {
- outs() << "\t";
- dumpBytes(Bytes, outs());
+ OS << "\t";
+ dumpBytes(Bytes, OS);
}
- IP.printInst(MI, outs(), "", STI);
+ if (MI)
+ IP.printInst(MI, OS, "", STI);
+ else
+ OS << " <unknown>";
}
};
PrettyPrinter PrettyPrinterInst;
@@ -334,6 +396,11 @@ public:
ArrayRef<uint8_t> Bytes, uint64_t Address,
raw_ostream &OS, StringRef Annot,
MCSubtargetInfo const &STI) override {
+ if (!MI) {
+ printLead(Bytes, Address, OS);
+ OS << " <unknown>";
+ return;
+ }
std::string Buffer;
{
raw_string_ostream TempStream(Buffer);
@@ -370,12 +437,48 @@ public:
}
};
HexagonPrettyPrinter HexagonPrettyPrinterInst;
+
+class AMDGCNPrettyPrinter : public PrettyPrinter {
+public:
+ void printInst(MCInstPrinter &IP,
+ const MCInst *MI,
+ ArrayRef<uint8_t> Bytes,
+ uint64_t Address,
+ raw_ostream &OS,
+ StringRef Annot,
+ MCSubtargetInfo const &STI) override {
+ if (!MI) {
+ OS << " <unknown>";
+ return;
+ }
+
+ SmallString<40> InstStr;
+ raw_svector_ostream IS(InstStr);
+
+ IP.printInst(MI, IS, "", STI);
+
+ OS << left_justify(IS.str(), 60) << format("// %012" PRIX64 ": ", Address);
+ typedef support::ulittle32_t U32;
+ for (auto D : makeArrayRef(reinterpret_cast<const U32*>(Bytes.data()),
+ Bytes.size() / sizeof(U32)))
+ // D should be explicitly casted to uint32_t here as it is passed
+ // by format to snprintf as vararg.
+ OS << format("%08" PRIX32 " ", static_cast<uint32_t>(D));
+
+ if (!Annot.empty())
+ OS << "// " << Annot;
+ }
+};
+AMDGCNPrettyPrinter AMDGCNPrettyPrinterInst;
+
PrettyPrinter &selectPrettyPrinter(Triple const &Triple) {
switch(Triple.getArch()) {
default:
return PrettyPrinterInst;
case Triple::hexagon:
return HexagonPrettyPrinterInst;
+ case Triple::amdgcn:
+ return AMDGCNPrettyPrinterInst;
}
}
}
@@ -429,18 +532,18 @@ static std::error_code getRelocationValueString(const ELFObjectFile<ELFT> *Obj,
const Elf_Sym *symb = Obj->getSymbol(SI->getRawDataRefImpl());
StringRef Target;
if (symb->getType() == ELF::STT_SECTION) {
- ErrorOr<section_iterator> SymSI = SI->getSection();
- if (std::error_code EC = SymSI.getError())
- return EC;
+ Expected<section_iterator> SymSI = SI->getSection();
+ if (!SymSI)
+ return errorToErrorCode(SymSI.takeError());
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;
} else {
- ErrorOr<StringRef> SymName = symb->getName(StrTab);
+ Expected<StringRef> SymName = symb->getName(StrTab);
if (!SymName)
- return SymName.getError();
+ return errorToErrorCode(SymName.takeError());
Target = *SymName;
}
switch (EF.getHeader()->e_machine) {
@@ -470,6 +573,7 @@ static std::error_code getRelocationValueString(const ELFObjectFile<ELFT> *Obj,
res = "Unknown";
}
break;
+ case ELF::EM_LANAI:
case ELF::EM_AARCH64: {
std::string fmtbuf;
raw_string_ostream fmt(fmtbuf);
@@ -485,6 +589,7 @@ static std::error_code getRelocationValueString(const ELFObjectFile<ELFT> *Obj,
case ELF::EM_ARM:
case ELF::EM_HEXAGON:
case ELF::EM_MIPS:
+ case ELF::EM_BPF:
res = Target;
break;
case ELF::EM_WEBASSEMBLY:
@@ -529,9 +634,9 @@ static std::error_code getRelocationValueString(const COFFObjectFile *Obj,
const RelocationRef &Rel,
SmallVectorImpl<char> &Result) {
symbol_iterator SymI = Rel.getSymbol();
- ErrorOr<StringRef> SymNameOrErr = SymI->getName();
- if (std::error_code EC = SymNameOrErr.getError())
- return EC;
+ Expected<StringRef> SymNameOrErr = SymI->getName();
+ if (!SymNameOrErr)
+ return errorToErrorCode(SymNameOrErr.takeError());
StringRef SymName = *SymNameOrErr;
Result.append(SymName.begin(), SymName.end());
return std::error_code();
@@ -551,14 +656,24 @@ static void printRelocationTargetName(const MachOObjectFile *O,
for (const SymbolRef &Symbol : O->symbols()) {
std::error_code ec;
- ErrorOr<uint64_t> Addr = Symbol.getAddress();
- if ((ec = Addr.getError()))
- report_fatal_error(ec.message());
+ Expected<uint64_t> Addr = Symbol.getAddress();
+ if (!Addr) {
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(Addr.takeError(), OS, "");
+ OS.flush();
+ report_fatal_error(Buf);
+ }
if (*Addr != Val)
continue;
- ErrorOr<StringRef> Name = Symbol.getName();
- if (std::error_code EC = Name.getError())
- report_fatal_error(EC.message());
+ Expected<StringRef> Name = Symbol.getName();
+ if (!Name) {
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(Name.takeError(), OS, "");
+ OS.flush();
+ report_fatal_error(Buf);
+ }
fmt << *Name;
return;
}
@@ -589,8 +704,8 @@ static void printRelocationTargetName(const MachOObjectFile *O,
if (isExtern) {
symbol_iterator SI = O->symbol_begin();
advance(SI, Val);
- ErrorOr<StringRef> SOrErr = SI->getName();
- error(SOrErr.getError());
+ Expected<StringRef> SOrErr = SI->getName();
+ error(errorToErrorCode(SOrErr.takeError()));
S = *SOrErr;
} else {
section_iterator SI = O->section_begin();
@@ -828,12 +943,10 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) {
const Target *TheTarget = getTarget(Obj);
// Package up features to be passed to target/subtarget
- std::string FeaturesStr;
+ SubtargetFeatures Features = Obj->getFeatures();
if (MAttrs.size()) {
- SubtargetFeatures Features;
for (unsigned i = 0; i != MAttrs.size(); ++i)
Features.AddFeature(MAttrs[i]);
- FeaturesStr = Features.getString();
}
std::unique_ptr<const MCRegisterInfo> MRI(
@@ -847,7 +960,7 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) {
if (!AsmInfo)
report_fatal_error("error: no assembly info for target " + TripleName);
std::unique_ptr<const MCSubtargetInfo> STI(
- TheTarget->createMCSubtargetInfo(TripleName, MCPU, FeaturesStr));
+ TheTarget->createMCSubtargetInfo(TripleName, MCPU, Features.getString()));
if (!STI)
report_fatal_error("error: no subtarget info for target " + TripleName);
std::unique_ptr<const MCInstrInfo> MII(TheTarget->createMCInstrInfo());
@@ -891,17 +1004,17 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) {
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());
+ Expected<uint64_t> AddressOrErr = Symbol.getAddress();
+ error(errorToErrorCode(AddressOrErr.takeError()));
uint64_t Address = *AddressOrErr;
- ErrorOr<StringRef> Name = Symbol.getName();
- error(Name.getError());
+ Expected<StringRef> Name = Symbol.getName();
+ error(errorToErrorCode(Name.takeError()));
if (Name->empty())
continue;
- ErrorOr<section_iterator> SectionOrErr = Symbol.getSection();
- error(SectionOrErr.getError());
+ Expected<section_iterator> SectionOrErr = Symbol.getSection();
+ error(errorToErrorCode(SectionOrErr.takeError()));
section_iterator SecI = *SectionOrErr;
if (SecI == Obj->section_end())
continue;
@@ -1031,6 +1144,18 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) {
if (Start >= End)
continue;
+ if (Obj->isELF() && Obj->getArch() == Triple::amdgcn) {
+ // make size 4 bytes folded
+ End = Start + ((End - Start) & ~0x3ull);
+ Start += 256; // add sizeof(amd_kernel_code_t)
+ // cut trailing zeroes - up to 256 bytes (align)
+ const uint64_t EndAlign = 256;
+ const auto Limit = End - (std::min)(EndAlign, End - Start);
+ while (End > Limit &&
+ *reinterpret_cast<const support::ulittle32_t*>(&Bytes[End - 4]) == 0)
+ End -= 4;
+ }
+
outs() << '\n' << Symbols[si].second << ":\n";
#ifndef NDEBUG
@@ -1081,72 +1206,69 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) {
if (Index >= End)
break;
- if (DisAsm->getInstruction(Inst, Size, Bytes.slice(Index),
- SectionAddr + Index, DebugOut,
- CommentStream)) {
- PIP.printInst(*IP, &Inst,
- Bytes.slice(Index, Size),
- 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)) {
- // 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;
- }
+ bool Disassembled = DisAsm->getInstruction(Inst, Size, Bytes.slice(Index),
+ SectionAddr + Index, DebugOut,
+ CommentStream);
+ if (Size == 0)
+ Size = 1;
+ PIP.printInst(*IP, Disassembled ? &Inst : nullptr,
+ Bytes.slice(Index, Size),
+ 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)) {
+ // 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;
}
+ }
- // 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() << '>';
- }
+ // 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() << "+0x" << utohexstr(Disp);
+ outs() << '>';
}
}
}
- outs() << "\n";
- } else {
- errs() << ToolName << ": warning: invalid instruction encoding\n";
- if (Size == 0)
- Size = 1; // skip illegible bytes
}
+ outs() << "\n";
// Print relocation for instruction.
while (rel_cur != rel_end) {
@@ -1270,7 +1392,8 @@ void llvm::PrintSectionContents(const ObjectFile *Obj) {
}
}
-void llvm::PrintSymbolTable(const ObjectFile *o) {
+void llvm::PrintSymbolTable(const ObjectFile *o, StringRef ArchiveName,
+ StringRef ArchitectureName) {
outs() << "SYMBOL TABLE:\n";
if (const COFFObjectFile *coff = dyn_cast<const COFFObjectFile>(o)) {
@@ -1278,20 +1401,26 @@ void llvm::PrintSymbolTable(const ObjectFile *o) {
return;
}
for (const SymbolRef &Symbol : o->symbols()) {
- ErrorOr<uint64_t> AddressOrError = Symbol.getAddress();
- error(AddressOrError.getError());
+ Expected<uint64_t> AddressOrError = Symbol.getAddress();
+ if (!AddressOrError)
+ report_error(ArchiveName, o->getFileName(), AddressOrError.takeError());
uint64_t Address = *AddressOrError;
- SymbolRef::Type Type = Symbol.getType();
+ Expected<SymbolRef::Type> TypeOrError = Symbol.getType();
+ if (!TypeOrError)
+ report_error(ArchiveName, o->getFileName(), TypeOrError.takeError());
+ SymbolRef::Type Type = *TypeOrError;
uint32_t Flags = Symbol.getFlags();
- ErrorOr<section_iterator> SectionOrErr = Symbol.getSection();
- error(SectionOrErr.getError());
+ Expected<section_iterator> SectionOrErr = Symbol.getSection();
+ error(errorToErrorCode(SectionOrErr.takeError()));
section_iterator Section = *SectionOrErr;
StringRef Name;
if (Type == SymbolRef::ST_Debug && Section != o->section_end()) {
Section->getName(Name);
} else {
- ErrorOr<StringRef> NameOrErr = Symbol.getName();
- error(NameOrErr.getError());
+ Expected<StringRef> NameOrErr = Symbol.getName();
+ if (!NameOrErr)
+ report_error(ArchiveName, o->getFileName(), NameOrErr.takeError(),
+ ArchitectureName);
Name = *NameOrErr;
}
@@ -1523,12 +1652,16 @@ static void printFirstPrivateFileHeader(const ObjectFile *o) {
report_fatal_error("Invalid/Unsupported object file format");
}
-static void DumpObject(const ObjectFile *o) {
+static void DumpObject(const ObjectFile *o, const Archive *a = nullptr) {
+ StringRef ArchiveName = a != nullptr ? a->getFileName() : "";
// Avoid other output when using a raw option.
if (!RawClangAST) {
outs() << '\n';
- outs() << o->getFileName()
- << ":\tfile format " << o->getFileFormatName() << "\n\n";
+ if (a)
+ outs() << a->getFileName() << "(" << o->getFileName() << ")";
+ else
+ outs() << o->getFileName();
+ outs() << ":\tfile format " << o->getFileFormatName() << "\n\n";
}
if (Disassemble)
@@ -1540,7 +1673,7 @@ static void DumpObject(const ObjectFile *o) {
if (SectionContents)
PrintSectionContents(o);
if (SymbolTable)
- PrintSymbolTable(o);
+ PrintSymbolTable(o, ArchiveName);
if (UnwindInfo)
PrintUnwindInfo(o);
if (PrivateHeaders)
@@ -1561,23 +1694,30 @@ static void DumpObject(const ObjectFile *o) {
printRawClangAST(o);
if (PrintFaultMaps)
printFaultMaps(o);
+ if (DwarfDumpType != DIDT_Null) {
+ std::unique_ptr<DIContext> DICtx(new DWARFContextInMemory(*o));
+ // Dump the complete DWARF structure.
+ DICtx->dump(outs(), DwarfDumpType, true /* DumpEH */);
+ }
}
/// @brief Dump each object file in \a a;
static void DumpArchive(const Archive *a) {
- 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);
+ Error Err;
+ for (auto &C : a->children(Err)) {
+ Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
+ if (!ChildOrErr) {
+ if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError()))
+ report_error(a->getFileName(), C, std::move(E));
+ continue;
+ }
if (ObjectFile *o = dyn_cast<ObjectFile>(&*ChildOrErr.get()))
- DumpObject(o);
+ DumpObject(o, a);
else
report_error(a->getFileName(), object_error::invalid_file_type);
}
+ if (Err)
+ report_error(a->getFileName(), std::move(Err));
}
/// @brief Open file and figure out how to dump it.
@@ -1592,9 +1732,9 @@ static void DumpInput(StringRef file) {
}
// Attempt to open the binary.
- ErrorOr<OwningBinary<Binary>> BinaryOrErr = createBinary(file);
- if (std::error_code EC = BinaryOrErr.getError())
- report_error(file, EC);
+ Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(file);
+ if (!BinaryOrErr)
+ report_error(file, BinaryOrErr.takeError());
Binary &Binary = *BinaryOrErr.get().getBinary();
if (Archive *a = dyn_cast<Archive>(&Binary))
@@ -1607,7 +1747,7 @@ static void DumpInput(StringRef file) {
int main(int argc, char **argv) {
// Print a stack trace if we signal out.
- sys::PrintStackTraceOnErrorSignal();
+ sys::PrintStackTraceOnErrorSignal(argv[0]);
PrettyStackTraceProgram X(argc, argv);
llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
@@ -1654,7 +1794,8 @@ int main(int argc, char **argv) {
&& !(DylibId && MachOOpt)
&& !(ObjcMetaData && MachOOpt)
&& !(FilterSections.size() != 0 && MachOOpt)
- && !PrintFaultMaps) {
+ && !PrintFaultMaps
+ && DwarfDumpType == DIDT_Null) {
cl::PrintHelpMessage();
return 2;
}
diff --git a/tools/llvm-objdump/llvm-objdump.h b/tools/llvm-objdump/llvm-objdump.h
index 60cabbc98e2f7..5b10ee87ca863 100644
--- a/tools/llvm-objdump/llvm-objdump.h
+++ b/tools/llvm-objdump/llvm-objdump.h
@@ -9,16 +9,20 @@
#ifndef LLVM_TOOLS_LLVM_OBJDUMP_LLVM_OBJDUMP_H
#define LLVM_TOOLS_LLVM_OBJDUMP_LLVM_OBJDUMP_H
-#include "llvm/ADT/StringRef.h"
+#include "llvm/DebugInfo/DIContext.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/DataTypes.h"
+#include "llvm/Object/Archive.h"
namespace llvm {
+class StringRef;
+
namespace object {
class COFFObjectFile;
class MachOObjectFile;
class ObjectFile;
+ class Archive;
class RelocationRef;
}
@@ -55,6 +59,7 @@ extern cl::opt<bool> SectionContents;
extern cl::opt<bool> SymbolTable;
extern cl::opt<bool> UnwindInfo;
extern cl::opt<bool> PrintImmHex;
+extern cl::opt<DIDumpType> DwarfDumpType;
// Various helper functions.
void error(std::error_code ec);
@@ -81,8 +86,21 @@ void printRawClangAST(const object::ObjectFile *o);
void PrintRelocations(const object::ObjectFile *o);
void PrintSectionHeaders(const object::ObjectFile *o);
void PrintSectionContents(const object::ObjectFile *o);
-void PrintSymbolTable(const object::ObjectFile *o);
+void PrintSymbolTable(const object::ObjectFile *o, StringRef ArchiveName,
+ StringRef ArchitectureName = StringRef());
+LLVM_ATTRIBUTE_NORETURN void error(Twine Message);
LLVM_ATTRIBUTE_NORETURN void report_error(StringRef File, std::error_code EC);
+LLVM_ATTRIBUTE_NORETURN void report_error(StringRef File, llvm::Error E);
+LLVM_ATTRIBUTE_NORETURN void report_error(StringRef FileName,
+ StringRef ArchiveName,
+ llvm::Error E,
+ StringRef ArchitectureName
+ = StringRef());
+LLVM_ATTRIBUTE_NORETURN void report_error(StringRef ArchiveName,
+ const object::Archive::Child &C,
+ llvm::Error E,
+ StringRef ArchitectureName
+ = StringRef());
} // end namespace llvm
diff --git a/tools/llvm-pdbdump/BuiltinDumper.cpp b/tools/llvm-pdbdump/BuiltinDumper.cpp
index 43270540f4534..2ce1a78391102 100644
--- a/tools/llvm-pdbdump/BuiltinDumper.cpp
+++ b/tools/llvm-pdbdump/BuiltinDumper.cpp
@@ -14,6 +14,7 @@
#include "llvm/DebugInfo/PDB/PDBSymbolTypeBuiltin.h"
using namespace llvm;
+using namespace llvm::pdb;
BuiltinDumper::BuiltinDumper(LinePrinter &P)
: PDBSymDumper(false), Printer(P) {}
@@ -30,13 +31,31 @@ StringRef BuiltinDumper::getTypeName(const PDBSymbolTypeBuiltin &Symbol) {
return "float";
return "double";
case PDB_BuiltinType::UInt:
- if (Symbol.getLength() == 8)
+ switch (Symbol.getLength()) {
+ case 8:
return "unsigned __int64";
- return "unsigned";
+ case 4:
+ return "unsigned int";
+ case 2:
+ return "unsigned short";
+ case 1:
+ return "unsigned char";
+ default:
+ return "unsigned";
+ }
case PDB_BuiltinType::Int:
- if (Symbol.getLength() == 4)
+ switch (Symbol.getLength()) {
+ case 8:
+ return "__int64";
+ case 4:
+ return "int";
+ case 2:
+ return "short";
+ case 1:
+ return "char";
+ default:
return "int";
- return "__int64";
+ }
case PDB_BuiltinType::Char:
return "char";
case PDB_BuiltinType::WCharT:
diff --git a/tools/llvm-pdbdump/BuiltinDumper.h b/tools/llvm-pdbdump/BuiltinDumper.h
index ac666dbd059b7..7a2f1438669c3 100644
--- a/tools/llvm-pdbdump/BuiltinDumper.h
+++ b/tools/llvm-pdbdump/BuiltinDumper.h
@@ -10,9 +10,11 @@
#ifndef LLVM_TOOLS_LLVMPDBDUMP_BUILTINDUMPER_H
#define LLVM_TOOLS_LLVMPDBDUMP_BUILTINDUMPER_H
+#include "llvm/ADT/StringRef.h"
#include "llvm/DebugInfo/PDB/PDBSymDumper.h"
namespace llvm {
+namespace pdb {
class LinePrinter;
@@ -28,5 +30,6 @@ private:
LinePrinter &Printer;
};
}
+}
#endif
diff --git a/tools/llvm-pdbdump/CMakeLists.txt b/tools/llvm-pdbdump/CMakeLists.txt
index 1907f917079ae..f725edfaf3dae 100644
--- a/tools/llvm-pdbdump/CMakeLists.txt
+++ b/tools/llvm-pdbdump/CMakeLists.txt
@@ -1,6 +1,8 @@
set(LLVM_LINK_COMPONENTS
- Support
+ DebugInfoCodeView
DebugInfoPDB
+ Object
+ Support
)
add_llvm_tool(llvm-pdbdump
@@ -12,7 +14,14 @@ add_llvm_tool(llvm-pdbdump
ExternalSymbolDumper.cpp
FunctionDumper.cpp
LinePrinter.cpp
+ LLVMOutputStyle.cpp
+ PdbYaml.cpp
TypeDumper.cpp
TypedefDumper.cpp
VariableDumper.cpp
+ YAMLOutputStyle.cpp
)
+
+if(LLVM_USE_SANITIZE_COVERAGE)
+ add_subdirectory(fuzzer)
+endif()
diff --git a/tools/llvm-pdbdump/ClassDefinitionDumper.cpp b/tools/llvm-pdbdump/ClassDefinitionDumper.cpp
index 8abf3fa3912af..553bc0b267c2e 100644
--- a/tools/llvm-pdbdump/ClassDefinitionDumper.cpp
+++ b/tools/llvm-pdbdump/ClassDefinitionDumper.cpp
@@ -28,6 +28,7 @@
#include "llvm/Support/Format.h"
using namespace llvm;
+using namespace llvm::pdb;
ClassDefinitionDumper::ClassDefinitionDumper(LinePrinter &P)
: PDBSymDumper(true), Printer(P) {}
@@ -84,7 +85,7 @@ void ClassDefinitionDumper::start(const PDBSymbolTypeUDT &Class) {
auto &AccessGroup = Groups.find((int)Access)->second;
if (auto Func = dyn_cast<PDBSymbolFunc>(Child.get())) {
- if (Func->isCompilerGenerated() && opts::ExcludeCompilerGenerated)
+ if (Func->isCompilerGenerated() && opts::pretty::ExcludeCompilerGenerated)
continue;
if (Func->getLength() == 0 && !Func->isPureVirtual() &&
!Func->isIntroVirtualFunction())
diff --git a/tools/llvm-pdbdump/ClassDefinitionDumper.h b/tools/llvm-pdbdump/ClassDefinitionDumper.h
index 5b48ba879cf7b..304e11dcb6c99 100644
--- a/tools/llvm-pdbdump/ClassDefinitionDumper.h
+++ b/tools/llvm-pdbdump/ClassDefinitionDumper.h
@@ -19,6 +19,7 @@
#include <unordered_map>
namespace llvm {
+namespace pdb {
class LinePrinter;
@@ -58,5 +59,5 @@ private:
int dumpAccessGroup(PDB_MemberAccess Access, const SymbolGroup &Group);
};
}
-
+}
#endif
diff --git a/tools/llvm-pdbdump/CompilandDumper.cpp b/tools/llvm-pdbdump/CompilandDumper.cpp
index 68ceb620627b9..5ec37cbbb5a06 100644
--- a/tools/llvm-pdbdump/CompilandDumper.cpp
+++ b/tools/llvm-pdbdump/CompilandDumper.cpp
@@ -12,7 +12,9 @@
#include "llvm-pdbdump.h"
#include "llvm/DebugInfo/PDB/IPDBEnumChildren.h"
+#include "llvm/DebugInfo/PDB/IPDBLineNumber.h"
#include "llvm/DebugInfo/PDB/IPDBSession.h"
+#include "llvm/DebugInfo/PDB/IPDBSourceFile.h"
#include "llvm/DebugInfo/PDB/PDBExtras.h"
#include "llvm/DebugInfo/PDB/PDBSymbol.h"
#include "llvm/DebugInfo/PDB/PDBSymbolCompiland.h"
@@ -31,9 +33,9 @@
#include "FunctionDumper.h"
#include <utility>
-#include <vector>
using namespace llvm;
+using namespace llvm::pdb;
CompilandDumper::CompilandDumper(LinePrinter &P)
: PDBSymDumper(true), Printer(P) {}
@@ -42,21 +44,65 @@ void CompilandDumper::dump(const PDBSymbolCompilandDetails &Symbol) {}
void CompilandDumper::dump(const PDBSymbolCompilandEnv &Symbol) {}
-void CompilandDumper::start(const PDBSymbolCompiland &Symbol, bool Children) {
+void CompilandDumper::start(const PDBSymbolCompiland &Symbol,
+ CompilandDumpFlags opts) {
std::string FullName = Symbol.getName();
if (Printer.IsCompilandExcluded(FullName))
return;
Printer.NewLine();
WithColor(Printer, PDB_ColorItem::Path).get() << FullName;
- if (!Children)
- return;
- auto ChildrenEnum = Symbol.findAllChildren();
- Printer.Indent();
- while (auto Child = ChildrenEnum->getNext())
- Child->dump(*this);
- Printer.Unindent();
+ if (opts & Flags::Lines) {
+ const IPDBSession &Session = Symbol.getSession();
+ auto Files = Session.getSourceFilesForCompiland(Symbol);
+ Printer.Indent();
+ while (auto File = Files->getNext()) {
+ Printer.NewLine();
+ WithColor(Printer, PDB_ColorItem::Path).get() << File->getFileName();
+
+ auto Lines = Session.findLineNumbers(Symbol, *File);
+ Printer.Indent();
+ while (auto Line = Lines->getNext()) {
+ Printer.NewLine();
+ uint32_t LineStart = Line->getLineNumber();
+ uint32_t LineEnd = Line->getLineNumberEnd();
+
+ Printer << "Line ";
+ PDB_ColorItem StatementColor = Line->isStatement()
+ ? PDB_ColorItem::Keyword
+ : PDB_ColorItem::LiteralValue;
+ WithColor(Printer, StatementColor).get() << LineStart;
+ if (LineStart != LineEnd)
+ WithColor(Printer, StatementColor).get() << " - " << LineEnd;
+
+ Printer << ", Address: ";
+ if (Line->getLength() > 0) {
+ uint64_t AddrStart = Line->getVirtualAddress();
+ uint64_t AddrEnd = AddrStart + Line->getLength() - 1;
+ WithColor(Printer, PDB_ColorItem::Address).get()
+ << "[" << format_hex(AddrStart, 10) << " - "
+ << format_hex(AddrEnd, 10) << "]";
+ Printer << " (" << Line->getLength() << " bytes)";
+ } else {
+ uint64_t AddrStart = Line->getVirtualAddress();
+ WithColor(Printer, PDB_ColorItem::Address).get()
+ << "[" << format_hex(AddrStart, 10) << "] ";
+ Printer << "(0 bytes)";
+ }
+ }
+ Printer.Unindent();
+ }
+ Printer.Unindent();
+ }
+
+ if (opts & Flags::Children) {
+ auto ChildrenEnum = Symbol.findAllChildren();
+ Printer.Indent();
+ while (auto Child = ChildrenEnum->getNext())
+ Child->dump(*this);
+ Printer.Unindent();
+ }
}
void CompilandDumper::dump(const PDBSymbolData &Symbol) {
@@ -112,9 +158,9 @@ void CompilandDumper::dump(const PDBSymbolThunk &Symbol) {
Printer.NewLine();
Printer << "thunk ";
- PDB_ThunkOrdinal Ordinal = Symbol.getThunkOrdinal();
+ codeview::ThunkOrdinal Ordinal = Symbol.getThunkOrdinal();
uint64_t VA = Symbol.getVirtualAddress();
- if (Ordinal == PDB_ThunkOrdinal::TrampIncremental) {
+ if (Ordinal == codeview::ThunkOrdinal::TrampIncremental) {
uint64_t Target = Symbol.getTargetVirtualAddress();
WithColor(Printer, PDB_ColorItem::Address).get() << format_hex(VA, 10);
Printer << " -> ";
diff --git a/tools/llvm-pdbdump/CompilandDumper.h b/tools/llvm-pdbdump/CompilandDumper.h
index 0d1d27cd7a436..462aaeb2611fe 100644
--- a/tools/llvm-pdbdump/CompilandDumper.h
+++ b/tools/llvm-pdbdump/CompilandDumper.h
@@ -13,14 +13,18 @@
#include "llvm/DebugInfo/PDB/PDBSymDumper.h"
namespace llvm {
+namespace pdb {
class LinePrinter;
+typedef int CompilandDumpFlags;
class CompilandDumper : public PDBSymDumper {
public:
+ enum Flags { None = 0x0, Children = 0x1, Symbols = 0x2, Lines = 0x4 };
+
CompilandDumper(LinePrinter &P);
- void start(const PDBSymbolCompiland &Symbol, bool Children);
+ void start(const PDBSymbolCompiland &Symbol, CompilandDumpFlags flags);
void dump(const PDBSymbolCompilandDetails &Symbol) override;
void dump(const PDBSymbolCompilandEnv &Symbol) override;
@@ -35,5 +39,6 @@ private:
LinePrinter &Printer;
};
}
+}
#endif
diff --git a/tools/llvm-pdbdump/EnumDumper.cpp b/tools/llvm-pdbdump/EnumDumper.cpp
index 3514c39f6e033..43b6018ffedf3 100644
--- a/tools/llvm-pdbdump/EnumDumper.cpp
+++ b/tools/llvm-pdbdump/EnumDumper.cpp
@@ -18,13 +18,14 @@
#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h"
using namespace llvm;
+using namespace llvm::pdb;
EnumDumper::EnumDumper(LinePrinter &P) : PDBSymDumper(true), Printer(P) {}
void EnumDumper::start(const PDBSymbolTypeEnum &Symbol) {
WithColor(Printer, PDB_ColorItem::Keyword).get() << "enum ";
WithColor(Printer, PDB_ColorItem::Type).get() << Symbol.getName();
- if (!opts::NoEnumDefs) {
+ if (!opts::pretty::NoEnumDefs) {
auto BuiltinType = Symbol.getUnderlyingType();
if (BuiltinType->getBuiltinType() != PDB_BuiltinType::Int ||
BuiltinType->getLength() != 4) {
diff --git a/tools/llvm-pdbdump/EnumDumper.h b/tools/llvm-pdbdump/EnumDumper.h
index 23de0614247fe..0a34e1f89ada0 100644
--- a/tools/llvm-pdbdump/EnumDumper.h
+++ b/tools/llvm-pdbdump/EnumDumper.h
@@ -13,6 +13,7 @@
#include "llvm/DebugInfo/PDB/PDBSymDumper.h"
namespace llvm {
+namespace pdb {
class LinePrinter;
@@ -26,5 +27,5 @@ private:
LinePrinter &Printer;
};
}
-
+}
#endif
diff --git a/tools/llvm-pdbdump/ExternalSymbolDumper.cpp b/tools/llvm-pdbdump/ExternalSymbolDumper.cpp
index c4e9f474880e3..508a2405772e4 100644
--- a/tools/llvm-pdbdump/ExternalSymbolDumper.cpp
+++ b/tools/llvm-pdbdump/ExternalSymbolDumper.cpp
@@ -15,6 +15,7 @@
#include "llvm/Support/Format.h"
using namespace llvm;
+using namespace llvm::pdb;
ExternalSymbolDumper::ExternalSymbolDumper(LinePrinter &P)
: PDBSymDumper(true), Printer(P) {}
diff --git a/tools/llvm-pdbdump/ExternalSymbolDumper.h b/tools/llvm-pdbdump/ExternalSymbolDumper.h
index d77b09cdebf17..b44b8a6fe98ae 100644
--- a/tools/llvm-pdbdump/ExternalSymbolDumper.h
+++ b/tools/llvm-pdbdump/ExternalSymbolDumper.h
@@ -13,6 +13,7 @@
#include "llvm/DebugInfo/PDB/PDBSymDumper.h"
namespace llvm {
+namespace pdb {
class LinePrinter;
@@ -28,5 +29,6 @@ private:
LinePrinter &Printer;
};
}
+}
#endif
diff --git a/tools/llvm-pdbdump/FunctionDumper.cpp b/tools/llvm-pdbdump/FunctionDumper.cpp
index 9584812e81a94..29ba15d521f0e 100644
--- a/tools/llvm-pdbdump/FunctionDumper.cpp
+++ b/tools/llvm-pdbdump/FunctionDumper.cpp
@@ -13,6 +13,7 @@
#include "llvm-pdbdump.h"
#include "llvm/DebugInfo/PDB/IPDBSession.h"
+#include "llvm/DebugInfo/PDB/PDBExtras.h"
#include "llvm/DebugInfo/PDB/PDBSymbolData.h"
#include "llvm/DebugInfo/PDB/PDBSymbolFunc.h"
#include "llvm/DebugInfo/PDB/PDBSymbolFuncDebugEnd.h"
@@ -27,11 +28,13 @@
#include "llvm/Support/Format.h"
using namespace llvm;
+using namespace llvm::codeview;
+using namespace llvm::pdb;
namespace {
template <class T>
void dumpClassParentWithScopeOperator(const T &Symbol, LinePrinter &Printer,
- llvm::FunctionDumper &Dumper) {
+ FunctionDumper &Dumper) {
uint32_t ClassParentId = Symbol.getClassParentId();
auto ClassParent =
Symbol.getSession().template getConcreteSymbolById<PDBSymbolTypeUDT>(
@@ -59,8 +62,8 @@ void FunctionDumper::start(const PDBSymbolTypeFunctionSig &Symbol,
PDB_CallingConv CC = Symbol.getCallingConvention();
bool ShouldDumpCallingConvention = true;
- if ((ClassParent && CC == PDB_CallingConv::Thiscall) ||
- (!ClassParent && CC == PDB_CallingConv::NearStdcall)) {
+ if ((ClassParent && CC == CallingConvention::ThisCall) ||
+ (!ClassParent && CC == CallingConvention::NearStdCall)) {
ShouldDumpCallingConvention = false;
}
@@ -152,12 +155,12 @@ void FunctionDumper::start(const PDBSymbolFunc &Symbol, PointerType Pointer) {
Printer << " ";
auto ClassParent = Symbol.getClassParent();
- PDB_CallingConv CC = Signature->getCallingConvention();
+ CallingConvention CC = Signature->getCallingConvention();
if (Pointer != FunctionDumper::PointerType::None)
Printer << "(";
- if ((ClassParent && CC != PDB_CallingConv::Thiscall) ||
- (!ClassParent && CC != PDB_CallingConv::NearStdcall)) {
+ if ((ClassParent && CC != CallingConvention::ThisCall) ||
+ (!ClassParent && CC != CallingConvention::NearStdCall)) {
WithColor(Printer, PDB_ColorItem::Keyword).get()
<< Signature->getCallingConvention() << " ";
}
diff --git a/tools/llvm-pdbdump/FunctionDumper.h b/tools/llvm-pdbdump/FunctionDumper.h
index 19a00145a1fd7..c71fafa18ed3b 100644
--- a/tools/llvm-pdbdump/FunctionDumper.h
+++ b/tools/llvm-pdbdump/FunctionDumper.h
@@ -13,7 +13,7 @@
#include "llvm/DebugInfo/PDB/PDBSymDumper.h"
namespace llvm {
-
+namespace pdb {
class LinePrinter;
class FunctionDumper : public PDBSymDumper {
@@ -38,5 +38,6 @@ private:
LinePrinter &Printer;
};
}
+}
#endif
diff --git a/tools/llvm-pdbdump/LLVMOutputStyle.cpp b/tools/llvm-pdbdump/LLVMOutputStyle.cpp
new file mode 100644
index 0000000000000..6d94295d1269b
--- /dev/null
+++ b/tools/llvm-pdbdump/LLVMOutputStyle.cpp
@@ -0,0 +1,775 @@
+//===- LLVMOutputStyle.cpp ------------------------------------ *- C++ --*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "LLVMOutputStyle.h"
+
+#include "llvm-pdbdump.h"
+#include "llvm/DebugInfo/CodeView/EnumTables.h"
+#include "llvm/DebugInfo/CodeView/ModuleSubstreamVisitor.h"
+#include "llvm/DebugInfo/CodeView/SymbolDumper.h"
+#include "llvm/DebugInfo/PDB/PDBExtras.h"
+#include "llvm/DebugInfo/PDB/Raw/DbiStream.h"
+#include "llvm/DebugInfo/PDB/Raw/EnumTables.h"
+#include "llvm/DebugInfo/PDB/Raw/ISectionContribVisitor.h"
+#include "llvm/DebugInfo/PDB/Raw/IndexedStreamData.h"
+#include "llvm/DebugInfo/PDB/Raw/InfoStream.h"
+#include "llvm/DebugInfo/PDB/Raw/ModInfo.h"
+#include "llvm/DebugInfo/PDB/Raw/ModStream.h"
+#include "llvm/DebugInfo/PDB/Raw/PDBFile.h"
+#include "llvm/DebugInfo/PDB/Raw/PublicsStream.h"
+#include "llvm/DebugInfo/PDB/Raw/RawError.h"
+#include "llvm/DebugInfo/PDB/Raw/TpiStream.h"
+#include "llvm/Object/COFF.h"
+
+#include <unordered_map>
+
+using namespace llvm;
+using namespace llvm::codeview;
+using namespace llvm::pdb;
+
+static void printSectionOffset(llvm::raw_ostream &OS,
+ const SectionOffset &Off) {
+ OS << Off.Off << ", " << Off.Isect;
+}
+
+LLVMOutputStyle::LLVMOutputStyle(PDBFile &File)
+ : File(File), P(outs()), TD(&P, false) {}
+
+Error LLVMOutputStyle::dump() {
+ if (auto EC = dumpFileHeaders())
+ return EC;
+
+ if (auto EC = dumpStreamSummary())
+ return EC;
+
+ if (auto EC = dumpStreamBlocks())
+ return EC;
+
+ if (auto EC = dumpStreamData())
+ return EC;
+
+ if (auto EC = dumpInfoStream())
+ return EC;
+
+ if (auto EC = dumpNamedStream())
+ return EC;
+
+ if (auto EC = dumpTpiStream(StreamTPI))
+ return EC;
+
+ if (auto EC = dumpTpiStream(StreamIPI))
+ return EC;
+
+ if (auto EC = dumpDbiStream())
+ return EC;
+
+ if (auto EC = dumpSectionContribs())
+ return EC;
+
+ if (auto EC = dumpSectionMap())
+ return EC;
+
+ if (auto EC = dumpPublicsStream())
+ return EC;
+
+ if (auto EC = dumpSectionHeaders())
+ return EC;
+
+ if (auto EC = dumpFpoStream())
+ return EC;
+
+ flush();
+
+ return Error::success();
+}
+
+Error LLVMOutputStyle::dumpFileHeaders() {
+ if (!opts::raw::DumpHeaders)
+ return Error::success();
+
+ DictScope D(P, "FileHeaders");
+ P.printNumber("BlockSize", File.getBlockSize());
+ P.printNumber("FreeBlockMap", File.getFreeBlockMapBlock());
+ P.printNumber("NumBlocks", File.getBlockCount());
+ P.printNumber("NumDirectoryBytes", File.getNumDirectoryBytes());
+ P.printNumber("Unknown1", File.getUnknown1());
+ P.printNumber("BlockMapAddr", File.getBlockMapIndex());
+ P.printNumber("NumDirectoryBlocks", File.getNumDirectoryBlocks());
+
+ // 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.
+ P.printList("DirectoryBlocks", File.getDirectoryBlockArray());
+ P.printNumber("NumStreams", File.getNumStreams());
+ return Error::success();
+}
+
+Error LLVMOutputStyle::dumpStreamSummary() {
+ if (!opts::raw::DumpStreamSummary)
+ return Error::success();
+
+ // It's OK if we fail to load some of these streams, we still attempt to print
+ // what we can.
+ auto Dbi = File.getPDBDbiStream();
+ auto Tpi = File.getPDBTpiStream();
+ auto Ipi = File.getPDBIpiStream();
+ auto Info = File.getPDBInfoStream();
+
+ ListScope L(P, "Streams");
+ uint32_t StreamCount = File.getNumStreams();
+ std::unordered_map<uint16_t, const ModuleInfoEx *> ModStreams;
+ std::unordered_map<uint16_t, std::string> NamedStreams;
+
+ if (Dbi) {
+ for (auto &ModI : Dbi->modules()) {
+ uint16_t SN = ModI.Info.getModuleStreamIndex();
+ ModStreams[SN] = &ModI;
+ }
+ }
+ if (Info) {
+ for (auto &NSE : Info->named_streams()) {
+ NamedStreams[NSE.second] = NSE.first();
+ }
+ }
+
+ for (uint16_t StreamIdx = 0; StreamIdx < StreamCount; ++StreamIdx) {
+ std::string Label("Stream ");
+ Label += to_string(StreamIdx);
+ std::string Value;
+ if (StreamIdx == OldMSFDirectory)
+ Value = "Old MSF Directory";
+ else if (StreamIdx == StreamPDB)
+ Value = "PDB Stream";
+ else if (StreamIdx == StreamDBI)
+ Value = "DBI Stream";
+ else if (StreamIdx == StreamTPI)
+ Value = "TPI Stream";
+ else if (StreamIdx == StreamIPI)
+ Value = "IPI Stream";
+ else if (Dbi && StreamIdx == Dbi->getGlobalSymbolStreamIndex())
+ Value = "Global Symbol Hash";
+ else if (Dbi && StreamIdx == Dbi->getPublicSymbolStreamIndex())
+ Value = "Public Symbol Hash";
+ else if (Dbi && StreamIdx == Dbi->getSymRecordStreamIndex())
+ Value = "Public Symbol Records";
+ else if (Tpi && StreamIdx == Tpi->getTypeHashStreamIndex())
+ Value = "TPI Hash";
+ else if (Tpi && StreamIdx == Tpi->getTypeHashStreamAuxIndex())
+ Value = "TPI Aux Hash";
+ else if (Ipi && StreamIdx == Ipi->getTypeHashStreamIndex())
+ Value = "IPI Hash";
+ else if (Ipi && StreamIdx == Ipi->getTypeHashStreamAuxIndex())
+ Value = "IPI Aux Hash";
+ else if (Dbi &&
+ StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Exception))
+ Value = "Exception Data";
+ else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Fixup))
+ Value = "Fixup Data";
+ else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::FPO))
+ Value = "FPO Data";
+ else if (Dbi &&
+ StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::NewFPO))
+ Value = "New FPO Data";
+ else if (Dbi &&
+ StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::OmapFromSrc))
+ Value = "Omap From Source Data";
+ else if (Dbi &&
+ StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::OmapToSrc))
+ Value = "Omap To Source Data";
+ else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Pdata))
+ Value = "Pdata";
+ else if (Dbi &&
+ StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::SectionHdr))
+ Value = "Section Header Data";
+ else if (Dbi &&
+ StreamIdx ==
+ Dbi->getDebugStreamIndex(DbgHeaderType::SectionHdrOrig))
+ Value = "Section Header Original Data";
+ else if (Dbi &&
+ StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::TokenRidMap))
+ Value = "Token Rid Data";
+ else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Xdata))
+ Value = "Xdata";
+ else {
+ auto ModIter = ModStreams.find(StreamIdx);
+ auto NSIter = NamedStreams.find(StreamIdx);
+ if (ModIter != ModStreams.end()) {
+ Value = "Module \"";
+ Value += ModIter->second->Info.getModuleName().str();
+ Value += "\"";
+ } else if (NSIter != NamedStreams.end()) {
+ Value = "Named Stream \"";
+ Value += NSIter->second;
+ Value += "\"";
+ } else {
+ Value = "???";
+ }
+ }
+ Value = "[" + Value + "]";
+ Value =
+ Value + " (" + to_string(File.getStreamByteSize(StreamIdx)) + " bytes)";
+
+ P.printString(Label, Value);
+ }
+
+ // Consume errors from missing streams.
+ if (!Dbi)
+ consumeError(Dbi.takeError());
+ if (!Tpi)
+ consumeError(Tpi.takeError());
+ if (!Ipi)
+ consumeError(Ipi.takeError());
+ if (!Info)
+ consumeError(Info.takeError());
+
+ P.flush();
+ return Error::success();
+}
+
+Error LLVMOutputStyle::dumpStreamBlocks() {
+ if (!opts::raw::DumpStreamBlocks)
+ return Error::success();
+
+ ListScope L(P, "StreamBlocks");
+ uint32_t StreamCount = File.getNumStreams();
+ for (uint32_t StreamIdx = 0; StreamIdx < StreamCount; ++StreamIdx) {
+ std::string Name("Stream ");
+ Name += to_string(StreamIdx);
+ auto StreamBlocks = File.getStreamBlockList(StreamIdx);
+ P.printList(Name, StreamBlocks);
+ }
+ return Error::success();
+}
+
+Error LLVMOutputStyle::dumpStreamData() {
+ uint32_t StreamCount = File.getNumStreams();
+ StringRef DumpStreamStr = opts::raw::DumpStreamDataIdx;
+ uint32_t DumpStreamNum;
+ if (DumpStreamStr.getAsInteger(/*Radix=*/0U, DumpStreamNum))
+ return Error::success();
+
+ if (DumpStreamNum >= StreamCount)
+ return make_error<RawError>(raw_error_code::no_stream);
+
+ auto S = MappedBlockStream::createIndexedStream(DumpStreamNum, File);
+ if (!S)
+ return S.takeError();
+ codeview::StreamReader R(**S);
+ while (R.bytesRemaining() > 0) {
+ ArrayRef<uint8_t> Data;
+ uint32_t BytesToReadInBlock = std::min(
+ R.bytesRemaining(), static_cast<uint32_t>(File.getBlockSize()));
+ if (auto EC = R.readBytes(Data, BytesToReadInBlock))
+ return EC;
+ P.printBinaryBlock(
+ "Data",
+ StringRef(reinterpret_cast<const char *>(Data.begin()), Data.size()));
+ }
+ return Error::success();
+}
+
+Error LLVMOutputStyle::dumpInfoStream() {
+ if (!opts::raw::DumpHeaders)
+ return Error::success();
+ auto IS = File.getPDBInfoStream();
+ if (!IS)
+ return IS.takeError();
+
+ DictScope D(P, "PDB Stream");
+ P.printNumber("Version", IS->getVersion());
+ P.printHex("Signature", IS->getSignature());
+ P.printNumber("Age", IS->getAge());
+ P.printObject("Guid", IS->getGuid());
+ return Error::success();
+}
+
+Error LLVMOutputStyle::dumpNamedStream() {
+ if (opts::raw::DumpStreamDataName.empty())
+ return Error::success();
+
+ auto IS = File.getPDBInfoStream();
+ if (!IS)
+ return IS.takeError();
+
+ uint32_t NameStreamIndex =
+ IS->getNamedStreamIndex(opts::raw::DumpStreamDataName);
+ if (NameStreamIndex == 0 || NameStreamIndex >= File.getNumStreams())
+ return make_error<RawError>(raw_error_code::no_stream);
+
+ if (NameStreamIndex != 0) {
+ std::string Name("Stream '");
+ Name += opts::raw::DumpStreamDataName;
+ Name += "'";
+ DictScope D(P, Name);
+ P.printNumber("Index", NameStreamIndex);
+
+ auto NameStream =
+ MappedBlockStream::createIndexedStream(NameStreamIndex, File);
+ if (!NameStream)
+ return NameStream.takeError();
+ codeview::StreamReader Reader(**NameStream);
+
+ NameHashTable NameTable;
+ if (auto EC = NameTable.load(Reader))
+ return EC;
+
+ P.printHex("Signature", NameTable.getSignature());
+ P.printNumber("Version", NameTable.getHashVersion());
+ P.printNumber("Name Count", NameTable.getNameCount());
+ ListScope L(P, "Names");
+ for (uint32_t ID : NameTable.name_ids()) {
+ StringRef Str = NameTable.getStringForID(ID);
+ if (!Str.empty())
+ P.printString(to_string(ID), Str);
+ }
+ }
+ return Error::success();
+}
+
+static void printTypeIndexOffset(raw_ostream &OS,
+ const TypeIndexOffset &TIOff) {
+ OS << "{" << TIOff.Type.getIndex() << ", " << TIOff.Offset << "}";
+}
+
+static void dumpTpiHash(ScopedPrinter &P, TpiStream &Tpi) {
+ if (!opts::raw::DumpTpiHash)
+ return;
+ DictScope DD(P, "Hash");
+ P.printNumber("Number of Hash Buckets", Tpi.NumHashBuckets());
+ P.printNumber("Hash Key Size", Tpi.getHashKeySize());
+ P.printList("Values", Tpi.getHashValues());
+ P.printList("Type Index Offsets", Tpi.getTypeIndexOffsets(),
+ printTypeIndexOffset);
+ P.printList("Hash Adjustments", Tpi.getHashAdjustments(),
+ printTypeIndexOffset);
+}
+
+Error LLVMOutputStyle::dumpTpiStream(uint32_t StreamIdx) {
+ assert(StreamIdx == StreamTPI || StreamIdx == StreamIPI);
+
+ bool DumpRecordBytes = false;
+ bool DumpRecords = false;
+ StringRef Label;
+ StringRef VerLabel;
+ if (StreamIdx == StreamTPI) {
+ DumpRecordBytes = opts::raw::DumpTpiRecordBytes;
+ DumpRecords = opts::raw::DumpTpiRecords;
+ Label = "Type Info Stream (TPI)";
+ VerLabel = "TPI Version";
+ } else if (StreamIdx == StreamIPI) {
+ DumpRecordBytes = opts::raw::DumpIpiRecordBytes;
+ DumpRecords = opts::raw::DumpIpiRecords;
+ Label = "Type Info Stream (IPI)";
+ VerLabel = "IPI Version";
+ }
+ if (!DumpRecordBytes && !DumpRecords && !opts::raw::DumpModuleSyms)
+ return Error::success();
+
+ auto Tpi = (StreamIdx == StreamTPI) ? File.getPDBTpiStream()
+ : File.getPDBIpiStream();
+ if (!Tpi)
+ return Tpi.takeError();
+
+ if (DumpRecords || DumpRecordBytes) {
+ DictScope D(P, Label);
+
+ P.printNumber(VerLabel, Tpi->getTpiVersion());
+ P.printNumber("Record count", Tpi->NumTypeRecords());
+
+ ListScope L(P, "Records");
+
+ bool HadError = false;
+ for (auto &Type : Tpi->types(&HadError)) {
+ DictScope DD(P, "");
+
+ if (DumpRecords) {
+ if (auto EC = TD.dump(Type))
+ return EC;
+ }
+
+ if (DumpRecordBytes)
+ P.printBinaryBlock("Bytes", Type.Data);
+ }
+ dumpTpiHash(P, *Tpi);
+ if (HadError)
+ return make_error<RawError>(raw_error_code::corrupt_file,
+ "TPI stream contained corrupt record");
+ } else if (opts::raw::DumpModuleSyms) {
+ // Even if the user doesn't want to dump type records, we still need to
+ // iterate them in order to build the list of types so that we can print
+ // them when dumping module symbols. So when they want to dump symbols
+ // but not types, use a null output stream.
+ ScopedPrinter *OldP = TD.getPrinter();
+ TD.setPrinter(nullptr);
+
+ bool HadError = false;
+ for (auto &Type : Tpi->types(&HadError)) {
+ if (auto EC = TD.dump(Type))
+ return EC;
+ }
+
+ TD.setPrinter(OldP);
+ dumpTpiHash(P, *Tpi);
+ if (HadError)
+ return make_error<RawError>(raw_error_code::corrupt_file,
+ "TPI stream contained corrupt record");
+ }
+ P.flush();
+ return Error::success();
+}
+
+Error LLVMOutputStyle::dumpDbiStream() {
+ bool DumpModules = opts::raw::DumpModules || opts::raw::DumpModuleSyms ||
+ opts::raw::DumpModuleFiles || opts::raw::DumpLineInfo;
+ if (!opts::raw::DumpHeaders && !DumpModules)
+ return Error::success();
+
+ auto DS = File.getPDBDbiStream();
+ if (!DS)
+ return DS.takeError();
+
+ DictScope D(P, "DBI Stream");
+ P.printNumber("Dbi Version", DS->getDbiVersion());
+ P.printNumber("Age", DS->getAge());
+ P.printBoolean("Incremental Linking", DS->isIncrementallyLinked());
+ P.printBoolean("Has CTypes", DS->hasCTypes());
+ P.printBoolean("Is Stripped", DS->isStripped());
+ P.printObject("Machine Type", DS->getMachineType());
+ P.printNumber("Symbol Record Stream Index", DS->getSymRecordStreamIndex());
+ P.printNumber("Public Symbol Stream Index", DS->getPublicSymbolStreamIndex());
+ P.printNumber("Global Symbol Stream Index", DS->getGlobalSymbolStreamIndex());
+
+ uint16_t Major = DS->getBuildMajorVersion();
+ uint16_t Minor = DS->getBuildMinorVersion();
+ P.printVersion("Toolchain Version", Major, Minor);
+
+ std::string DllName;
+ raw_string_ostream DllStream(DllName);
+ DllStream << "mspdb" << Major << Minor << ".dll version";
+ DllStream.flush();
+ P.printVersion(DllName, Major, Minor, DS->getPdbDllVersion());
+
+ if (DumpModules) {
+ ListScope L(P, "Modules");
+ for (auto &Modi : DS->modules()) {
+ DictScope DD(P);
+ P.printString("Name", Modi.Info.getModuleName().str());
+ P.printNumber("Debug Stream Index", Modi.Info.getModuleStreamIndex());
+ P.printString("Object File Name", Modi.Info.getObjFileName().str());
+ P.printNumber("Num Files", Modi.Info.getNumberOfFiles());
+ P.printNumber("Source File Name Idx", Modi.Info.getSourceFileNameIndex());
+ P.printNumber("Pdb File Name Idx", Modi.Info.getPdbFilePathNameIndex());
+ P.printNumber("Line Info Byte Size", Modi.Info.getLineInfoByteSize());
+ P.printNumber("C13 Line Info Byte Size",
+ Modi.Info.getC13LineInfoByteSize());
+ P.printNumber("Symbol Byte Size", Modi.Info.getSymbolDebugInfoByteSize());
+ P.printNumber("Type Server Index", Modi.Info.getTypeServerIndex());
+ P.printBoolean("Has EC Info", Modi.Info.hasECInfo());
+ if (opts::raw::DumpModuleFiles) {
+ std::string FileListName =
+ to_string(Modi.SourceFiles.size()) + " Contributing Source Files";
+ ListScope LL(P, FileListName);
+ for (auto File : Modi.SourceFiles)
+ P.printString(File.str());
+ }
+ bool HasModuleDI =
+ (Modi.Info.getModuleStreamIndex() < File.getNumStreams());
+ bool ShouldDumpSymbols =
+ (opts::raw::DumpModuleSyms || opts::raw::DumpSymRecordBytes);
+ if (HasModuleDI && (ShouldDumpSymbols || opts::raw::DumpLineInfo)) {
+ auto ModStreamData = MappedBlockStream::createIndexedStream(
+ Modi.Info.getModuleStreamIndex(), File);
+ if (!ModStreamData)
+ return ModStreamData.takeError();
+ ModStream ModS(Modi.Info, std::move(*ModStreamData));
+ if (auto EC = ModS.reload())
+ return EC;
+
+ if (ShouldDumpSymbols) {
+ ListScope SS(P, "Symbols");
+ codeview::CVSymbolDumper SD(P, TD, nullptr, false);
+ bool HadError = false;
+ for (const auto &S : ModS.symbols(&HadError)) {
+ DictScope DD(P, "");
+
+ if (opts::raw::DumpModuleSyms)
+ SD.dump(S);
+ if (opts::raw::DumpSymRecordBytes)
+ P.printBinaryBlock("Bytes", S.Data);
+ }
+ if (HadError)
+ return make_error<RawError>(
+ raw_error_code::corrupt_file,
+ "DBI stream contained corrupt symbol record");
+ }
+ if (opts::raw::DumpLineInfo) {
+ ListScope SS(P, "LineInfo");
+ bool HadError = false;
+ // Define a locally scoped visitor to print the different
+ // substream types types.
+ class RecordVisitor : public codeview::IModuleSubstreamVisitor {
+ public:
+ RecordVisitor(ScopedPrinter &P, PDBFile &F) : P(P), F(F) {}
+ Error visitUnknown(ModuleSubstreamKind Kind,
+ StreamRef Stream) override {
+ DictScope DD(P, "Unknown");
+ ArrayRef<uint8_t> Data;
+ StreamReader R(Stream);
+ if (auto EC = R.readBytes(Data, R.bytesRemaining())) {
+ return make_error<RawError>(
+ raw_error_code::corrupt_file,
+ "DBI stream contained corrupt line info record");
+ }
+ P.printBinaryBlock("Data", Data);
+ return Error::success();
+ }
+ Error
+ visitFileChecksums(StreamRef Data,
+ const FileChecksumArray &Checksums) override {
+ DictScope DD(P, "FileChecksums");
+ for (const auto &C : Checksums) {
+ DictScope DDD(P, "Checksum");
+ if (auto Result = getFileNameForOffset(C.FileNameOffset))
+ P.printString("FileName", Result.get());
+ else
+ return Result.takeError();
+ P.flush();
+ P.printEnum("Kind", uint8_t(C.Kind), getFileChecksumNames());
+ P.printBinaryBlock("Checksum", C.Checksum);
+ }
+ return Error::success();
+ }
+
+ Error visitLines(StreamRef Data, const LineSubstreamHeader *Header,
+ const LineInfoArray &Lines) override {
+ DictScope DD(P, "Lines");
+ for (const auto &L : Lines) {
+ if (auto Result = getFileNameForOffset2(L.NameIndex))
+ P.printString("FileName", Result.get());
+ else
+ return Result.takeError();
+ P.flush();
+ for (const auto &N : L.LineNumbers) {
+ DictScope DDD(P, "Line");
+ LineInfo LI(N.Flags);
+ P.printNumber("Offset", N.Offset);
+ if (LI.isAlwaysStepInto())
+ P.printString("StepInto", StringRef("Always"));
+ else if (LI.isNeverStepInto())
+ P.printString("StepInto", StringRef("Never"));
+ else
+ P.printNumber("LineNumberStart", LI.getStartLine());
+ P.printNumber("EndDelta", LI.getLineDelta());
+ P.printBoolean("IsStatement", LI.isStatement());
+ }
+ for (const auto &C : L.Columns) {
+ DictScope DDD(P, "Column");
+ P.printNumber("Start", C.StartColumn);
+ P.printNumber("End", C.EndColumn);
+ }
+ }
+ return Error::success();
+ }
+
+ private:
+ Expected<StringRef> getFileNameForOffset(uint32_t Offset) {
+ auto ST = F.getStringTable();
+ if (!ST)
+ return ST.takeError();
+
+ return ST->getStringForID(Offset);
+ }
+ Expected<StringRef> getFileNameForOffset2(uint32_t Offset) {
+ auto DS = F.getPDBDbiStream();
+ if (!DS)
+ return DS.takeError();
+ return DS->getFileNameForIndex(Offset);
+ }
+ ScopedPrinter &P;
+ PDBFile &F;
+ };
+
+ RecordVisitor V(P, File);
+ for (const auto &L : ModS.lines(&HadError)) {
+ if (auto EC = codeview::visitModuleSubstream(L, V))
+ return EC;
+ }
+ }
+ }
+ }
+ }
+ return Error::success();
+}
+
+Error LLVMOutputStyle::dumpSectionContribs() {
+ if (!opts::raw::DumpSectionContribs)
+ return Error::success();
+
+ auto Dbi = File.getPDBDbiStream();
+ if (!Dbi)
+ return Dbi.takeError();
+
+ ListScope L(P, "Section Contributions");
+ class Visitor : public ISectionContribVisitor {
+ public:
+ Visitor(ScopedPrinter &P, DbiStream &DS) : P(P), DS(DS) {}
+ void visit(const SectionContrib &SC) override {
+ DictScope D(P, "Contribution");
+ P.printNumber("ISect", SC.ISect);
+ P.printNumber("Off", SC.Off);
+ P.printNumber("Size", SC.Size);
+ P.printFlags("Characteristics", SC.Characteristics,
+ codeview::getImageSectionCharacteristicNames(),
+ COFF::SectionCharacteristics(0x00F00000));
+ {
+ DictScope DD(P, "Module");
+ P.printNumber("Index", SC.Imod);
+ auto M = DS.modules();
+ if (M.size() > SC.Imod) {
+ P.printString("Name", M[SC.Imod].Info.getModuleName());
+ }
+ }
+ P.printNumber("Data CRC", SC.DataCrc);
+ P.printNumber("Reloc CRC", SC.RelocCrc);
+ P.flush();
+ }
+ void visit(const SectionContrib2 &SC) override {
+ visit(SC.Base);
+ P.printNumber("ISect Coff", SC.ISectCoff);
+ P.flush();
+ }
+
+ private:
+ ScopedPrinter &P;
+ DbiStream &DS;
+ };
+ Visitor V(P, *Dbi);
+ Dbi->visitSectionContributions(V);
+ return Error::success();
+}
+
+Error LLVMOutputStyle::dumpSectionMap() {
+ if (!opts::raw::DumpSectionMap)
+ return Error::success();
+
+ auto Dbi = File.getPDBDbiStream();
+ if (!Dbi)
+ return Dbi.takeError();
+
+ ListScope L(P, "Section Map");
+ for (auto &M : Dbi->getSectionMap()) {
+ DictScope D(P, "Entry");
+ P.printFlags("Flags", M.Flags, getOMFSegMapDescFlagNames());
+ P.printNumber("Flags", M.Flags);
+ P.printNumber("Ovl", M.Ovl);
+ P.printNumber("Group", M.Group);
+ P.printNumber("Frame", M.Frame);
+ P.printNumber("SecName", M.SecName);
+ P.printNumber("ClassName", M.ClassName);
+ P.printNumber("Offset", M.Offset);
+ P.printNumber("SecByteLength", M.SecByteLength);
+ P.flush();
+ }
+ return Error::success();
+}
+
+Error LLVMOutputStyle::dumpPublicsStream() {
+ if (!opts::raw::DumpPublics)
+ return Error::success();
+
+ DictScope D(P, "Publics Stream");
+ auto Publics = File.getPDBPublicsStream();
+ if (!Publics)
+ return Publics.takeError();
+
+ auto Dbi = File.getPDBDbiStream();
+ if (!Dbi)
+ return Dbi.takeError();
+
+ P.printNumber("Stream number", Dbi->getPublicSymbolStreamIndex());
+ P.printNumber("SymHash", Publics->getSymHash());
+ P.printNumber("AddrMap", Publics->getAddrMap());
+ P.printNumber("Number of buckets", Publics->getNumBuckets());
+ P.printList("Hash Buckets", Publics->getHashBuckets());
+ P.printList("Address Map", Publics->getAddressMap());
+ P.printList("Thunk Map", Publics->getThunkMap());
+ P.printList("Section Offsets", Publics->getSectionOffsets(),
+ printSectionOffset);
+ ListScope L(P, "Symbols");
+ codeview::CVSymbolDumper SD(P, TD, nullptr, false);
+ bool HadError = false;
+ for (auto S : Publics->getSymbols(&HadError)) {
+ DictScope DD(P, "");
+
+ SD.dump(S);
+ if (opts::raw::DumpSymRecordBytes)
+ P.printBinaryBlock("Bytes", S.Data);
+ }
+ if (HadError)
+ return make_error<RawError>(
+ raw_error_code::corrupt_file,
+ "Public symbol stream contained corrupt record");
+
+ return Error::success();
+}
+
+Error LLVMOutputStyle::dumpSectionHeaders() {
+ if (!opts::raw::DumpSectionHeaders)
+ return Error::success();
+
+ auto Dbi = File.getPDBDbiStream();
+ if (!Dbi)
+ return Dbi.takeError();
+
+ ListScope D(P, "Section Headers");
+ for (const object::coff_section &Section : Dbi->getSectionHeaders()) {
+ DictScope DD(P, "");
+
+ // If a name is 8 characters long, there is no NUL character at end.
+ StringRef Name(Section.Name, strnlen(Section.Name, sizeof(Section.Name)));
+ P.printString("Name", Name);
+ P.printNumber("Virtual Size", Section.VirtualSize);
+ P.printNumber("Virtual Address", Section.VirtualAddress);
+ P.printNumber("Size of Raw Data", Section.SizeOfRawData);
+ P.printNumber("File Pointer to Raw Data", Section.PointerToRawData);
+ P.printNumber("File Pointer to Relocations", Section.PointerToRelocations);
+ P.printNumber("File Pointer to Linenumbers", Section.PointerToLinenumbers);
+ P.printNumber("Number of Relocations", Section.NumberOfRelocations);
+ P.printNumber("Number of Linenumbers", Section.NumberOfLinenumbers);
+ P.printFlags("Characteristics", Section.Characteristics,
+ getImageSectionCharacteristicNames());
+ }
+ return Error::success();
+}
+
+Error LLVMOutputStyle::dumpFpoStream() {
+ if (!opts::raw::DumpFpo)
+ return Error::success();
+
+ auto Dbi = File.getPDBDbiStream();
+ if (!Dbi)
+ return Dbi.takeError();
+
+ ListScope D(P, "New FPO");
+ for (const object::FpoData &Fpo : Dbi->getFpoRecords()) {
+ DictScope DD(P, "");
+ P.printNumber("Offset", Fpo.Offset);
+ P.printNumber("Size", Fpo.Size);
+ P.printNumber("Number of locals", Fpo.NumLocals);
+ P.printNumber("Number of params", Fpo.NumParams);
+ P.printNumber("Size of Prolog", Fpo.getPrologSize());
+ P.printNumber("Number of Saved Registers", Fpo.getNumSavedRegs());
+ P.printBoolean("Has SEH", Fpo.hasSEH());
+ P.printBoolean("Use BP", Fpo.useBP());
+ P.printNumber("Frame Pointer", Fpo.getFP());
+ }
+ return Error::success();
+}
+
+void LLVMOutputStyle::flush() { P.flush(); }
diff --git a/tools/llvm-pdbdump/LLVMOutputStyle.h b/tools/llvm-pdbdump/LLVMOutputStyle.h
new file mode 100644
index 0000000000000..77935d102205e
--- /dev/null
+++ b/tools/llvm-pdbdump/LLVMOutputStyle.h
@@ -0,0 +1,50 @@
+//===- LLVMOutputStyle.h -------------------------------------- *- C++ --*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMPDBDUMP_LLVMOUTPUTSTYLE_H
+#define LLVM_TOOLS_LLVMPDBDUMP_LLVMOUTPUTSTYLE_H
+
+#include "OutputStyle.h"
+
+#include "llvm/DebugInfo/CodeView/TypeDumper.h"
+#include "llvm/Support/ScopedPrinter.h"
+
+namespace llvm {
+namespace pdb {
+class LLVMOutputStyle : public OutputStyle {
+public:
+ LLVMOutputStyle(PDBFile &File);
+
+ Error dump() override;
+
+private:
+ Error dumpFileHeaders();
+ Error dumpStreamSummary();
+ Error dumpStreamBlocks();
+ Error dumpStreamData();
+ Error dumpInfoStream();
+ Error dumpNamedStream();
+ Error dumpTpiStream(uint32_t StreamIdx);
+ Error dumpDbiStream();
+ Error dumpSectionContribs();
+ Error dumpSectionMap();
+ Error dumpPublicsStream();
+ Error dumpSectionHeaders();
+ Error dumpFpoStream();
+
+ void flush();
+
+ PDBFile &File;
+ ScopedPrinter P;
+ codeview::CVTypeDumper TD;
+};
+}
+}
+
+#endif
diff --git a/tools/llvm-pdbdump/LinePrinter.cpp b/tools/llvm-pdbdump/LinePrinter.cpp
index a43727f02b5e5..47c7d3e3c0e74 100644
--- a/tools/llvm-pdbdump/LinePrinter.cpp
+++ b/tools/llvm-pdbdump/LinePrinter.cpp
@@ -16,6 +16,9 @@
#include <algorithm>
+using namespace llvm;
+using namespace llvm::pdb;
+
namespace {
bool IsItemExcluded(llvm::StringRef Item,
std::list<llvm::Regex> &IncludeFilters,
@@ -41,19 +44,19 @@ using namespace llvm;
LinePrinter::LinePrinter(int Indent, llvm::raw_ostream &Stream)
: OS(Stream), IndentSpaces(Indent), CurrentIndent(0) {
- SetFilters(ExcludeTypeFilters, opts::ExcludeTypes.begin(),
- opts::ExcludeTypes.end());
- SetFilters(ExcludeSymbolFilters, opts::ExcludeSymbols.begin(),
- opts::ExcludeSymbols.end());
- 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());
+ SetFilters(ExcludeTypeFilters, opts::pretty::ExcludeTypes.begin(),
+ opts::pretty::ExcludeTypes.end());
+ SetFilters(ExcludeSymbolFilters, opts::pretty::ExcludeSymbols.begin(),
+ opts::pretty::ExcludeSymbols.end());
+ SetFilters(ExcludeCompilandFilters, opts::pretty::ExcludeCompilands.begin(),
+ opts::pretty::ExcludeCompilands.end());
+
+ SetFilters(IncludeTypeFilters, opts::pretty::IncludeTypes.begin(),
+ opts::pretty::IncludeTypes.end());
+ SetFilters(IncludeSymbolFilters, opts::pretty::IncludeSymbols.begin(),
+ opts::pretty::IncludeSymbols.end());
+ SetFilters(IncludeCompilandFilters, opts::pretty::IncludeCompilands.begin(),
+ opts::pretty::IncludeCompilands.end());
}
void LinePrinter::Indent() { CurrentIndent += IndentSpaces; }
diff --git a/tools/llvm-pdbdump/LinePrinter.h b/tools/llvm-pdbdump/LinePrinter.h
index b0a9d2cfc4e82..a4401f8af9552 100644
--- a/tools/llvm-pdbdump/LinePrinter.h
+++ b/tools/llvm-pdbdump/LinePrinter.h
@@ -18,6 +18,7 @@
#include <list>
namespace llvm {
+namespace pdb {
class LinePrinter {
friend class WithColor;
@@ -88,5 +89,6 @@ private:
raw_ostream &OS;
};
}
+}
#endif
diff --git a/tools/llvm-pdbdump/Makefile b/tools/llvm-pdbdump/Makefile
deleted file mode 100644
index 18aafc4076feb..0000000000000
--- a/tools/llvm-pdbdump/Makefile
+++ /dev/null
@@ -1,17 +0,0 @@
-##===- tools/llvm-pdbdump/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-pdbdump
-LINK_COMPONENTS := DebugInfoPDB Object
-
-# This tool has no plugins, optimize startup time.
-TOOL_NO_EXPORTS := 1
-
-include $(LEVEL)/Makefile.common
diff --git a/tools/llvm-pdbdump/OutputStyle.h b/tools/llvm-pdbdump/OutputStyle.h
new file mode 100644
index 0000000000000..dfefc25a215e8
--- /dev/null
+++ b/tools/llvm-pdbdump/OutputStyle.h
@@ -0,0 +1,28 @@
+//===- OutputStyle.h ------------------------------------------ *- C++ --*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMPDBDUMP_OUTPUTSTYLE_H
+#define LLVM_TOOLS_LLVMPDBDUMP_OUTPUTSTYLE_H
+
+#include "llvm/Support/Error.h"
+
+namespace llvm {
+namespace pdb {
+class PDBFile;
+
+class OutputStyle {
+public:
+ virtual ~OutputStyle() {}
+
+ virtual Error dump() = 0;
+};
+}
+}
+
+#endif
diff --git a/tools/llvm-pdbdump/PdbYaml.cpp b/tools/llvm-pdbdump/PdbYaml.cpp
new file mode 100644
index 0000000000000..e83f24e877422
--- /dev/null
+++ b/tools/llvm-pdbdump/PdbYaml.cpp
@@ -0,0 +1,162 @@
+//===- PdbYAML.cpp -------------------------------------------- *- C++ --*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "PdbYaml.h"
+
+#include "llvm/DebugInfo/PDB/PDBExtras.h"
+#include "llvm/DebugInfo/PDB/Raw/PDBFile.h"
+
+using namespace llvm;
+using namespace llvm::yaml;
+using namespace llvm::pdb;
+using namespace llvm::pdb::yaml;
+
+namespace llvm {
+namespace yaml {
+template <> struct ScalarTraits<llvm::pdb::PDB_UniqueId> {
+ static void output(const llvm::pdb::PDB_UniqueId &S, void *,
+ llvm::raw_ostream &OS) {
+ OS << S;
+ }
+
+ static StringRef input(StringRef Scalar, void *Ctx,
+ llvm::pdb::PDB_UniqueId &S) {
+ if (Scalar.size() != 38)
+ return "GUID strings are 38 characters long";
+ if (Scalar[0] != '{' || Scalar[37] != '}')
+ return "GUID is not enclosed in {}";
+ if (Scalar[9] != '-' || Scalar[14] != '-' || Scalar[19] != '-' ||
+ Scalar[24] != '-')
+ return "GUID sections are not properly delineated with dashes";
+
+ char *OutBuffer = S.Guid;
+ for (auto Iter = Scalar.begin(); Iter != Scalar.end();) {
+ if (*Iter == '-' || *Iter == '{' || *Iter == '}') {
+ ++Iter;
+ continue;
+ }
+ uint8_t Value = (llvm::hexDigitValue(*Iter) << 4);
+ ++Iter;
+ Value |= llvm::hexDigitValue(*Iter);
+ ++Iter;
+ *OutBuffer++ = Value;
+ }
+
+ return "";
+ }
+
+ static bool mustQuote(StringRef Scalar) { return needsQuotes(Scalar); }
+};
+
+template <> struct ScalarEnumerationTraits<llvm::pdb::PDB_Machine> {
+ static void enumeration(IO &io, llvm::pdb::PDB_Machine &Value) {
+ io.enumCase(Value, "Invalid", PDB_Machine::Invalid);
+ io.enumCase(Value, "Am33", PDB_Machine::Am33);
+ io.enumCase(Value, "Amd64", PDB_Machine::Amd64);
+ io.enumCase(Value, "Arm", PDB_Machine::Arm);
+ io.enumCase(Value, "ArmNT", PDB_Machine::ArmNT);
+ io.enumCase(Value, "Ebc", PDB_Machine::Ebc);
+ io.enumCase(Value, "x86", PDB_Machine::x86);
+ io.enumCase(Value, "Ia64", PDB_Machine::Ia64);
+ io.enumCase(Value, "M32R", PDB_Machine::M32R);
+ io.enumCase(Value, "Mips16", PDB_Machine::Mips16);
+ io.enumCase(Value, "MipsFpu", PDB_Machine::MipsFpu);
+ io.enumCase(Value, "MipsFpu16", PDB_Machine::MipsFpu16);
+ io.enumCase(Value, "PowerPCFP", PDB_Machine::PowerPCFP);
+ io.enumCase(Value, "R4000", PDB_Machine::R4000);
+ io.enumCase(Value, "SH3", PDB_Machine::SH3);
+ io.enumCase(Value, "SH3DSP", PDB_Machine::SH3DSP);
+ io.enumCase(Value, "Thumb", PDB_Machine::Thumb);
+ io.enumCase(Value, "WceMipsV2", PDB_Machine::WceMipsV2);
+ }
+};
+
+template <> struct ScalarEnumerationTraits<llvm::pdb::PdbRaw_DbiVer> {
+ static void enumeration(IO &io, llvm::pdb::PdbRaw_DbiVer &Value) {
+ io.enumCase(Value, "V41", llvm::pdb::PdbRaw_DbiVer::PdbDbiVC41);
+ io.enumCase(Value, "V50", llvm::pdb::PdbRaw_DbiVer::PdbDbiV50);
+ io.enumCase(Value, "V60", llvm::pdb::PdbRaw_DbiVer::PdbDbiV60);
+ io.enumCase(Value, "V70", llvm::pdb::PdbRaw_DbiVer::PdbDbiV70);
+ io.enumCase(Value, "V110", llvm::pdb::PdbRaw_DbiVer::PdbDbiV110);
+ }
+};
+
+template <> struct ScalarEnumerationTraits<llvm::pdb::PdbRaw_ImplVer> {
+ static void enumeration(IO &io, llvm::pdb::PdbRaw_ImplVer &Value) {
+ io.enumCase(Value, "VC2", llvm::pdb::PdbRaw_ImplVer::PdbImplVC2);
+ io.enumCase(Value, "VC4", llvm::pdb::PdbRaw_ImplVer::PdbImplVC4);
+ io.enumCase(Value, "VC41", llvm::pdb::PdbRaw_ImplVer::PdbImplVC41);
+ io.enumCase(Value, "VC50", llvm::pdb::PdbRaw_ImplVer::PdbImplVC50);
+ io.enumCase(Value, "VC98", llvm::pdb::PdbRaw_ImplVer::PdbImplVC98);
+ io.enumCase(Value, "VC70Dep", llvm::pdb::PdbRaw_ImplVer::PdbImplVC70Dep);
+ io.enumCase(Value, "VC70", llvm::pdb::PdbRaw_ImplVer::PdbImplVC70);
+ io.enumCase(Value, "VC80", llvm::pdb::PdbRaw_ImplVer::PdbImplVC80);
+ io.enumCase(Value, "VC110", llvm::pdb::PdbRaw_ImplVer::PdbImplVC110);
+ io.enumCase(Value, "VC140", llvm::pdb::PdbRaw_ImplVer::PdbImplVC140);
+ }
+};
+}
+}
+
+void MappingTraits<PdbObject>::mapping(IO &IO, PdbObject &Obj) {
+ IO.mapOptional("MSF", Obj.Headers);
+ IO.mapOptional("StreamSizes", Obj.StreamSizes);
+ IO.mapOptional("StreamMap", Obj.StreamMap);
+ IO.mapOptional("PdbStream", Obj.PdbStream);
+ IO.mapOptional("DbiStream", Obj.DbiStream);
+}
+
+void MappingTraits<MsfHeaders>::mapping(IO &IO, MsfHeaders &Obj) {
+ IO.mapRequired("SuperBlock", Obj.SuperBlock);
+ IO.mapRequired("NumDirectoryBlocks", Obj.NumDirectoryBlocks);
+ IO.mapRequired("DirectoryBlocks", Obj.DirectoryBlocks);
+ IO.mapRequired("NumStreams", Obj.NumStreams);
+ IO.mapRequired("FileSize", Obj.FileSize);
+}
+
+void MappingTraits<msf::SuperBlock>::mapping(IO &IO, msf::SuperBlock &SB) {
+ if (!IO.outputting()) {
+ ::memcpy(SB.MagicBytes, msf::Magic, sizeof(msf::Magic));
+ }
+
+ IO.mapRequired("BlockSize", SB.BlockSize);
+ IO.mapRequired("FreeBlockMap", SB.FreeBlockMapBlock);
+ IO.mapRequired("NumBlocks", SB.NumBlocks);
+ IO.mapRequired("NumDirectoryBytes", SB.NumDirectoryBytes);
+ IO.mapRequired("Unknown1", SB.Unknown1);
+ IO.mapRequired("BlockMapAddr", SB.BlockMapAddr);
+}
+
+void MappingTraits<StreamBlockList>::mapping(IO &IO, StreamBlockList &SB) {
+ IO.mapRequired("Stream", SB.Blocks);
+}
+
+void MappingTraits<PdbInfoStream>::mapping(IO &IO, PdbInfoStream &Obj) {
+ IO.mapRequired("Age", Obj.Age);
+ IO.mapRequired("Guid", Obj.Guid);
+ IO.mapRequired("Signature", Obj.Signature);
+ IO.mapRequired("Version", Obj.Version);
+ IO.mapRequired("NamedStreams", Obj.NamedStreams);
+}
+
+void MappingTraits<PdbDbiStream>::mapping(IO &IO, PdbDbiStream &Obj) {
+ IO.mapRequired("VerHeader", Obj.VerHeader);
+ IO.mapRequired("Age", Obj.Age);
+ IO.mapRequired("BuildNumber", Obj.BuildNumber);
+ IO.mapRequired("PdbDllVersion", Obj.PdbDllVersion);
+ IO.mapRequired("PdbDllRbld", Obj.PdbDllRbld);
+ IO.mapRequired("Flags", Obj.Flags);
+ IO.mapRequired("MachineType", Obj.MachineType);
+}
+
+void MappingTraits<NamedStreamMapping>::mapping(IO &IO,
+ NamedStreamMapping &Obj) {
+ IO.mapRequired("Name", Obj.StreamName);
+ IO.mapRequired("StreamNum", Obj.StreamNumber);
+}
diff --git a/tools/llvm-pdbdump/PdbYaml.h b/tools/llvm-pdbdump/PdbYaml.h
new file mode 100644
index 0000000000000..a7389af220572
--- /dev/null
+++ b/tools/llvm-pdbdump/PdbYaml.h
@@ -0,0 +1,112 @@
+//===- PdbYAML.h ---------------------------------------------- *- C++ --*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMPDBDUMP_PDBYAML_H
+#define LLVM_TOOLS_LLVMPDBDUMP_PDBYAML_H
+
+#include "OutputStyle.h"
+
+#include "llvm/ADT/Optional.h"
+#include "llvm/DebugInfo/PDB/PDBTypes.h"
+#include "llvm/DebugInfo/PDB/Raw/MsfCommon.h"
+#include "llvm/DebugInfo/PDB/Raw/PDBFile.h"
+#include "llvm/DebugInfo/PDB/Raw/RawConstants.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/YAMLTraits.h"
+
+#include <vector>
+
+namespace llvm {
+namespace pdb {
+
+namespace yaml {
+struct MsfHeaders {
+ msf::SuperBlock SuperBlock;
+ uint32_t NumDirectoryBlocks;
+ std::vector<uint32_t> DirectoryBlocks;
+ uint32_t NumStreams;
+ uint32_t FileSize;
+};
+
+struct StreamBlockList {
+ std::vector<uint32_t> Blocks;
+};
+
+struct NamedStreamMapping {
+ StringRef StreamName;
+ uint32_t StreamNumber;
+};
+
+struct PdbInfoStream {
+ PdbRaw_ImplVer Version;
+ uint32_t Signature;
+ uint32_t Age;
+ PDB_UniqueId Guid;
+ std::vector<NamedStreamMapping> NamedStreams;
+};
+
+struct PdbDbiStream {
+ PdbRaw_DbiVer VerHeader;
+ uint32_t Age;
+ uint16_t BuildNumber;
+ uint32_t PdbDllVersion;
+ uint16_t PdbDllRbld;
+ uint16_t Flags;
+ PDB_Machine MachineType;
+};
+
+struct PdbObject {
+ Optional<MsfHeaders> Headers;
+ Optional<std::vector<uint32_t>> StreamSizes;
+ Optional<std::vector<StreamBlockList>> StreamMap;
+ Optional<PdbInfoStream> PdbStream;
+ Optional<PdbDbiStream> DbiStream;
+};
+}
+}
+}
+
+namespace llvm {
+namespace yaml {
+
+template <> struct MappingTraits<pdb::yaml::PdbObject> {
+ static void mapping(IO &IO, pdb::yaml::PdbObject &Obj);
+};
+
+template <> struct MappingTraits<pdb::yaml::MsfHeaders> {
+ static void mapping(IO &IO, pdb::yaml::MsfHeaders &Obj);
+};
+
+template <> struct MappingTraits<pdb::msf::SuperBlock> {
+ static void mapping(IO &IO, pdb::msf::SuperBlock &SB);
+};
+
+template <> struct MappingTraits<pdb::yaml::StreamBlockList> {
+ static void mapping(IO &IO, pdb::yaml::StreamBlockList &SB);
+};
+
+template <> struct MappingTraits<pdb::yaml::PdbInfoStream> {
+ static void mapping(IO &IO, pdb::yaml::PdbInfoStream &Obj);
+};
+
+template <> struct MappingTraits<pdb::yaml::PdbDbiStream> {
+ static void mapping(IO &IO, pdb::yaml::PdbDbiStream &Obj);
+};
+
+template <> struct MappingTraits<pdb::yaml::NamedStreamMapping> {
+ static void mapping(IO &IO, pdb::yaml::NamedStreamMapping &Obj);
+};
+}
+}
+
+LLVM_YAML_IS_SEQUENCE_VECTOR(uint32_t)
+LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::pdb::yaml::NamedStreamMapping)
+LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::pdb::yaml::StreamBlockList)
+
+#endif // LLVM_TOOLS_LLVMPDBDUMP_PDBYAML_H
diff --git a/tools/llvm-pdbdump/TypeDumper.cpp b/tools/llvm-pdbdump/TypeDumper.cpp
index 88c0bd65697ed..a49d640455539 100644
--- a/tools/llvm-pdbdump/TypeDumper.cpp
+++ b/tools/llvm-pdbdump/TypeDumper.cpp
@@ -24,6 +24,7 @@
#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h"
using namespace llvm;
+using namespace llvm::pdb;
TypeDumper::TypeDumper(LinePrinter &P) : PDBSymDumper(true), Printer(P) {}
@@ -62,7 +63,7 @@ void TypeDumper::dump(const PDBSymbolTypeEnum &Symbol) {
if (Printer.IsTypeExcluded(Symbol.getName()))
return;
// Dump member enums when dumping their class definition.
- if (Symbol.isNested())
+ if (nullptr != Symbol.getClassParent())
return;
Printer.NewLine();
@@ -87,7 +88,7 @@ void TypeDumper::dump(const PDBSymbolTypeUDT &Symbol) {
Printer.NewLine();
- if (opts::NoClassDefs) {
+ if (opts::pretty::NoClassDefs) {
WithColor(Printer, PDB_ColorItem::Keyword).get() << "class ";
WithColor(Printer, PDB_ColorItem::Identifier).get() << Symbol.getName();
} else {
diff --git a/tools/llvm-pdbdump/TypeDumper.h b/tools/llvm-pdbdump/TypeDumper.h
index 5c0832eccaf98..76a477964f1f7 100644
--- a/tools/llvm-pdbdump/TypeDumper.h
+++ b/tools/llvm-pdbdump/TypeDumper.h
@@ -13,7 +13,7 @@
#include "llvm/DebugInfo/PDB/PDBSymDumper.h"
namespace llvm {
-
+namespace pdb {
class LinePrinter;
class TypeDumper : public PDBSymDumper {
@@ -30,5 +30,5 @@ private:
LinePrinter &Printer;
};
}
-
+}
#endif
diff --git a/tools/llvm-pdbdump/TypedefDumper.cpp b/tools/llvm-pdbdump/TypedefDumper.cpp
index a6b5c53a6b3a9..b1e017613ce12 100644
--- a/tools/llvm-pdbdump/TypedefDumper.cpp
+++ b/tools/llvm-pdbdump/TypedefDumper.cpp
@@ -23,6 +23,7 @@
#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h"
using namespace llvm;
+using namespace llvm::pdb;
TypedefDumper::TypedefDumper(LinePrinter &P) : PDBSymDumper(true), Printer(P) {}
diff --git a/tools/llvm-pdbdump/TypedefDumper.h b/tools/llvm-pdbdump/TypedefDumper.h
index 8cd578cc6d296..c22b58a7e41e4 100644
--- a/tools/llvm-pdbdump/TypedefDumper.h
+++ b/tools/llvm-pdbdump/TypedefDumper.h
@@ -13,6 +13,7 @@
#include "llvm/DebugInfo/PDB/PDBSymDumper.h"
namespace llvm {
+namespace pdb {
class LinePrinter;
@@ -33,5 +34,6 @@ private:
LinePrinter &Printer;
};
}
+}
#endif
diff --git a/tools/llvm-pdbdump/VariableDumper.cpp b/tools/llvm-pdbdump/VariableDumper.cpp
index e5665b5fcafa4..284d7e9b731f9 100644
--- a/tools/llvm-pdbdump/VariableDumper.cpp
+++ b/tools/llvm-pdbdump/VariableDumper.cpp
@@ -27,12 +27,13 @@
#include "llvm/Support/Format.h"
using namespace llvm;
+using namespace llvm::pdb;
VariableDumper::VariableDumper(LinePrinter &P)
: PDBSymDumper(true), Printer(P) {}
void VariableDumper::start(const PDBSymbolData &Var) {
- if (Var.isCompilerGenerated() && opts::ExcludeCompilerGenerated)
+ if (Var.isCompilerGenerated() && opts::pretty::ExcludeCompilerGenerated)
return;
if (Printer.IsSymbolExcluded(Var.getName()))
return;
diff --git a/tools/llvm-pdbdump/VariableDumper.h b/tools/llvm-pdbdump/VariableDumper.h
index db8d8ea0e4305..4f00358878c94 100644
--- a/tools/llvm-pdbdump/VariableDumper.h
+++ b/tools/llvm-pdbdump/VariableDumper.h
@@ -11,10 +11,13 @@
#define LLVM_TOOLS_LLVMPDBDUMP_VARIABLEDUMPER_H
#include "llvm/DebugInfo/PDB/PDBSymDumper.h"
-#include "llvm/ADT/StringRef.h"
namespace llvm {
+class StringRef;
+
+namespace pdb {
+
class LinePrinter;
class VariableDumper : public PDBSymDumper {
@@ -37,5 +40,5 @@ private:
LinePrinter &Printer;
};
}
-
+}
#endif
diff --git a/tools/llvm-pdbdump/YAMLOutputStyle.cpp b/tools/llvm-pdbdump/YAMLOutputStyle.cpp
new file mode 100644
index 0000000000000..a8b6202d5d161
--- /dev/null
+++ b/tools/llvm-pdbdump/YAMLOutputStyle.cpp
@@ -0,0 +1,142 @@
+//===- YAMLOutputStyle.cpp ------------------------------------ *- C++ --*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "YAMLOutputStyle.h"
+
+#include "PdbYaml.h"
+#include "llvm-pdbdump.h"
+
+#include "llvm/DebugInfo/PDB/Raw/DbiStream.h"
+#include "llvm/DebugInfo/PDB/Raw/InfoStream.h"
+#include "llvm/DebugInfo/PDB/Raw/PDBFile.h"
+#include "llvm/DebugInfo/PDB/Raw/RawConstants.h"
+
+using namespace llvm;
+using namespace llvm::pdb;
+
+YAMLOutputStyle::YAMLOutputStyle(PDBFile &File) : File(File), Out(outs()) {}
+
+Error YAMLOutputStyle::dump() {
+ if (opts::pdb2yaml::StreamDirectory)
+ opts::pdb2yaml::StreamMetadata = true;
+
+ if (auto EC = dumpFileHeaders())
+ return EC;
+
+ if (auto EC = dumpStreamMetadata())
+ return EC;
+
+ if (auto EC = dumpStreamDirectory())
+ return EC;
+
+ if (auto EC = dumpPDBStream())
+ return EC;
+
+ if (auto EC = dumpDbiStream())
+ return EC;
+
+ flush();
+ return Error::success();
+}
+
+Error YAMLOutputStyle::dumpFileHeaders() {
+ if (opts::pdb2yaml::NoFileHeaders)
+ return Error::success();
+
+ yaml::MsfHeaders Headers;
+ Obj.Headers.emplace();
+ Obj.Headers->SuperBlock.NumBlocks = File.getBlockCount();
+ Obj.Headers->SuperBlock.BlockMapAddr = File.getBlockMapIndex();
+ Obj.Headers->SuperBlock.BlockSize = File.getBlockSize();
+ auto Blocks = File.getDirectoryBlockArray();
+ Obj.Headers->DirectoryBlocks.assign(Blocks.begin(), Blocks.end());
+ Obj.Headers->NumDirectoryBlocks = File.getNumDirectoryBlocks();
+ Obj.Headers->SuperBlock.NumDirectoryBytes = File.getNumDirectoryBytes();
+ Obj.Headers->NumStreams =
+ opts::pdb2yaml::StreamMetadata ? File.getNumStreams() : 0;
+ Obj.Headers->SuperBlock.FreeBlockMapBlock = File.getFreeBlockMapBlock();
+ Obj.Headers->SuperBlock.Unknown1 = File.getUnknown1();
+ Obj.Headers->FileSize = File.getFileSize();
+
+ return Error::success();
+}
+
+Error YAMLOutputStyle::dumpStreamMetadata() {
+ if (!opts::pdb2yaml::StreamMetadata)
+ return Error::success();
+
+ Obj.StreamSizes.emplace();
+ Obj.StreamSizes->assign(File.getStreamSizes().begin(),
+ File.getStreamSizes().end());
+ return Error::success();
+}
+
+Error YAMLOutputStyle::dumpStreamDirectory() {
+ if (!opts::pdb2yaml::StreamDirectory)
+ return Error::success();
+
+ auto StreamMap = File.getStreamMap();
+ Obj.StreamMap.emplace();
+ for (auto &Stream : StreamMap) {
+ pdb::yaml::StreamBlockList BlockList;
+ BlockList.Blocks.assign(Stream.begin(), Stream.end());
+ Obj.StreamMap->push_back(BlockList);
+ }
+
+ return Error::success();
+}
+
+Error YAMLOutputStyle::dumpPDBStream() {
+ if (!opts::pdb2yaml::PdbStream)
+ return Error::success();
+
+ auto IS = File.getPDBInfoStream();
+ if (!IS)
+ return IS.takeError();
+
+ auto &InfoS = IS.get();
+ Obj.PdbStream.emplace();
+ Obj.PdbStream->Age = InfoS.getAge();
+ Obj.PdbStream->Guid = InfoS.getGuid();
+ Obj.PdbStream->Signature = InfoS.getSignature();
+ Obj.PdbStream->Version = InfoS.getVersion();
+ for (auto &NS : InfoS.named_streams()) {
+ yaml::NamedStreamMapping Mapping;
+ Mapping.StreamName = NS.getKey();
+ Mapping.StreamNumber = NS.getValue();
+ Obj.PdbStream->NamedStreams.push_back(Mapping);
+ }
+
+ return Error::success();
+}
+
+Error YAMLOutputStyle::dumpDbiStream() {
+ if (!opts::pdb2yaml::DbiStream)
+ return Error::success();
+
+ auto DbiS = File.getPDBDbiStream();
+ if (!DbiS)
+ return DbiS.takeError();
+
+ auto &DS = DbiS.get();
+ Obj.DbiStream.emplace();
+ Obj.DbiStream->Age = DS.getAge();
+ Obj.DbiStream->BuildNumber = DS.getBuildNumber();
+ Obj.DbiStream->Flags = DS.getFlags();
+ Obj.DbiStream->MachineType = DS.getMachineType();
+ Obj.DbiStream->PdbDllRbld = DS.getPdbDllRbld();
+ Obj.DbiStream->PdbDllVersion = DS.getPdbDllVersion();
+ Obj.DbiStream->VerHeader = DS.getDbiVersion();
+ return Error::success();
+}
+
+void YAMLOutputStyle::flush() {
+ Out << Obj;
+ outs().flush();
+}
diff --git a/tools/llvm-pdbdump/YAMLOutputStyle.h b/tools/llvm-pdbdump/YAMLOutputStyle.h
new file mode 100644
index 0000000000000..d36dfec5f25ad
--- /dev/null
+++ b/tools/llvm-pdbdump/YAMLOutputStyle.h
@@ -0,0 +1,45 @@
+//===- YAMLOutputStyle.h -------------------------------------- *- C++ --*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMPDBDUMP_YAMLOUTPUTSTYLE_H
+#define LLVM_TOOLS_LLVMPDBDUMP_YAMLOUTPUTSTYLE_H
+
+#include "OutputStyle.h"
+#include "PdbYaml.h"
+
+#include "llvm/DebugInfo/CodeView/TypeDumper.h"
+#include "llvm/Support/ScopedPrinter.h"
+#include "llvm/Support/YAMLTraits.h"
+
+namespace llvm {
+namespace pdb {
+class YAMLOutputStyle : public OutputStyle {
+public:
+ YAMLOutputStyle(PDBFile &File);
+
+ Error dump() override;
+
+private:
+ Error dumpFileHeaders();
+ Error dumpStreamMetadata();
+ Error dumpStreamDirectory();
+ Error dumpPDBStream();
+ Error dumpDbiStream();
+
+ void flush();
+
+ PDBFile &File;
+ llvm::yaml::Output Out;
+
+ yaml::PdbObject Obj;
+};
+} // namespace pdb
+} // namespace llvm
+
+#endif // LLVM_TOOLS_LLVMPDBDUMP_YAMLOUTPUTSTYLE_H
diff --git a/tools/llvm-pdbdump/fuzzer/CMakeLists.txt b/tools/llvm-pdbdump/fuzzer/CMakeLists.txt
new file mode 100644
index 0000000000000..cf5a0f70aab38
--- /dev/null
+++ b/tools/llvm-pdbdump/fuzzer/CMakeLists.txt
@@ -0,0 +1,15 @@
+set(LLVM_LINK_COMPONENTS
+ DebugInfoCodeView
+ DebugInfoPDB
+ Object
+ Support
+ )
+
+add_llvm_executable(llvm-pdbdump-fuzzer
+ EXCLUDE_FROM_ALL
+ llvm-pdbdump-fuzzer.cpp
+ )
+
+target_link_libraries(llvm-pdbdump-fuzzer
+ LLVMFuzzer
+ )
diff --git a/tools/llvm-pdbdump/fuzzer/llvm-pdbdump-fuzzer.cpp b/tools/llvm-pdbdump/fuzzer/llvm-pdbdump-fuzzer.cpp
new file mode 100644
index 0000000000000..e818dda32fc02
--- /dev/null
+++ b/tools/llvm-pdbdump/fuzzer/llvm-pdbdump-fuzzer.cpp
@@ -0,0 +1,104 @@
+//===-- llvm-pdbdump-fuzzer.cpp - Fuzz the llvm-pdbdump tool --------------===//
+//
+// 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 a function that runs llvm-pdbdump
+/// on a single input. This function is then linked into the Fuzzer library.
+///
+//===----------------------------------------------------------------------===//
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/DebugInfo/CodeView/ByteStream.h"
+#include "llvm/DebugInfo/CodeView/SymbolDumper.h"
+#include "llvm/DebugInfo/CodeView/TypeDumper.h"
+#include "llvm/DebugInfo/PDB/Raw/DbiStream.h"
+#include "llvm/DebugInfo/PDB/Raw/IPDBStreamData.h"
+#include "llvm/DebugInfo/PDB/Raw/MappedBlockStream.h"
+#include "llvm/DebugInfo/PDB/Raw/ModStream.h"
+#include "llvm/DebugInfo/PDB/Raw/PDBFile.h"
+#include "llvm/DebugInfo/PDB/Raw/RawSession.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/ScopedPrinter.h"
+
+using namespace llvm;
+
+namespace {
+// We need a class which behaves like an immutable ByteStream, but whose data
+// is backed by an llvm::MemoryBuffer. It also needs to own the underlying
+// MemoryBuffer, so this simple adapter is a good way to achieve that.
+class InputByteStream : public codeview::ByteStream<false> {
+public:
+ explicit InputByteStream(std::unique_ptr<MemoryBuffer> Buffer)
+ : ByteStream(ArrayRef<uint8_t>(Buffer->getBuffer().bytes_begin(),
+ Buffer->getBuffer().bytes_end())),
+ MemBuffer(std::move(Buffer)) {}
+
+ std::unique_ptr<MemoryBuffer> MemBuffer;
+};
+}
+
+extern "C" int LLVMFuzzerTestOneInput(uint8_t *data, size_t size) {
+ std::unique_ptr<MemoryBuffer> Buff = MemoryBuffer::getMemBuffer(
+ StringRef((const char *)data, size), "", false);
+
+ ScopedPrinter P(nulls());
+ codeview::CVTypeDumper TD(&P, false);
+
+ auto InputStream = llvm::make_unique<InputByteStream>(std::move(Buff));
+ std::unique_ptr<pdb::PDBFile> File(new pdb::PDBFile(std::move(InputStream)));
+ if (auto E = File->parseFileHeaders()) {
+ consumeError(std::move(E));
+ return 0;
+ }
+ if (auto E = File->parseStreamData()) {
+ consumeError(std::move(E));
+ return 0;
+ }
+
+ auto DbiS = File->getPDBDbiStream();
+ if (auto E = DbiS.takeError()) {
+ consumeError(std::move(E));
+ return 0;
+ }
+ auto TpiS = File->getPDBTpiStream();
+ if (auto E = TpiS.takeError()) {
+ consumeError(std::move(E));
+ return 0;
+ }
+ auto IpiS = File->getPDBIpiStream();
+ if (auto E = IpiS.takeError()) {
+ consumeError(std::move(E));
+ return 0;
+ }
+ auto InfoS = File->getPDBInfoStream();
+ if (auto E = InfoS.takeError()) {
+ consumeError(std::move(E));
+ return 0;
+ }
+ pdb::DbiStream &DS = DbiS.get();
+
+ for (auto &Modi : DS.modules()) {
+ auto ModStreamData = pdb::MappedBlockStream::createIndexedStream(
+ Modi.Info.getModuleStreamIndex(), *File);
+ if (!ModStreamData) {
+ consumeError(ModStreamData.takeError());
+ return 0;
+ }
+ pdb::ModStream ModS(Modi.Info, std::move(*ModStreamData));
+ if (auto E = ModS.reload()) {
+ consumeError(std::move(E));
+ return 0;
+ }
+ codeview::CVSymbolDumper SD(P, TD, nullptr, false);
+ bool HadError = false;
+ for (auto &S : ModS.symbols(&HadError)) {
+ SD.dump(S);
+ }
+ }
+ return 0;
+}
diff --git a/tools/llvm-pdbdump/llvm-pdbdump.cpp b/tools/llvm-pdbdump/llvm-pdbdump.cpp
index 0e3f0b281fe02..62f215ddc8aef 100644
--- a/tools/llvm-pdbdump/llvm-pdbdump.cpp
+++ b/tools/llvm-pdbdump/llvm-pdbdump.cpp
@@ -17,15 +17,20 @@
#include "CompilandDumper.h"
#include "ExternalSymbolDumper.h"
#include "FunctionDumper.h"
+#include "LLVMOutputStyle.h"
#include "LinePrinter.h"
+#include "OutputStyle.h"
#include "TypeDumper.h"
#include "VariableDumper.h"
+#include "YAMLOutputStyle.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/CodeView/ByteStream.h"
+#include "llvm/DebugInfo/PDB/GenericError.h"
#include "llvm/DebugInfo/PDB/IPDBEnumChildren.h"
#include "llvm/DebugInfo/PDB/IPDBRawSymbol.h"
#include "llvm/DebugInfo/PDB/IPDBSession.h"
@@ -35,388 +40,380 @@
#include "llvm/DebugInfo/PDB/PDBSymbolExe.h"
#include "llvm/DebugInfo/PDB/PDBSymbolFunc.h"
#include "llvm/DebugInfo/PDB/PDBSymbolThunk.h"
+#include "llvm/DebugInfo/PDB/Raw/DbiStream.h"
+#include "llvm/DebugInfo/PDB/Raw/DbiStreamBuilder.h"
+#include "llvm/DebugInfo/PDB/Raw/InfoStream.h"
+#include "llvm/DebugInfo/PDB/Raw/InfoStreamBuilder.h"
+#include "llvm/DebugInfo/PDB/Raw/PDBFile.h"
+#include "llvm/DebugInfo/PDB/Raw/PDBFileBuilder.h"
+#include "llvm/DebugInfo/PDB/Raw/RawConstants.h"
+#include "llvm/DebugInfo/PDB/Raw/RawError.h"
+#include "llvm/DebugInfo/PDB/Raw/RawSession.h"
+#include "llvm/Support/COM.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/ConvertUTF.h"
+#include "llvm/Support/FileOutputBuffer.h"
#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/ScopedPrinter.h"
#include "llvm/Support/Signals.h"
-
-#if defined(HAVE_DIA_SDK)
-#ifndef NOMINMAX
-#define NOMINMAX
-#endif
-#include <Windows.h>
-#endif
+#include "llvm/Support/raw_ostream.h"
using namespace llvm;
+using namespace llvm::codeview;
+using namespace llvm::pdb;
+
+namespace {
+// A simple adapter that acts like a ByteStream but holds ownership over
+// and underlying FileOutputBuffer.
+class FileBufferByteStream : public ByteStream<true> {
+public:
+ FileBufferByteStream(std::unique_ptr<FileOutputBuffer> Buffer)
+ : ByteStream(MutableArrayRef<uint8_t>(Buffer->getBufferStart(),
+ Buffer->getBufferEnd())),
+ FileBuffer(std::move(Buffer)) {}
+
+ Error commit() const override {
+ if (FileBuffer->commit())
+ return llvm::make_error<RawError>(raw_error_code::not_writable);
+ return Error::success();
+ }
-namespace opts {
+private:
+ std::unique_ptr<FileOutputBuffer> FileBuffer;
+};
+}
-enum class PDB_DumpType { ByType, ByObjFile, Both };
+namespace opts {
-cl::list<std::string> InputFilenames(cl::Positional,
- cl::desc("<input PDB files>"),
- cl::OneOrMore);
+cl::SubCommand RawSubcommand("raw", "Dump raw structure of the PDB file");
+cl::SubCommand
+ PrettySubcommand("pretty",
+ "Dump semantic information about types and symbols");
+cl::SubCommand
+ YamlToPdbSubcommand("yaml2pdb",
+ "Generate a PDB file from a YAML description");
+cl::SubCommand
+ PdbToYamlSubcommand("pdb2yaml",
+ "Generate a detailed YAML description of a PDB File");
cl::OptionCategory TypeCategory("Symbol Type Options");
cl::OptionCategory FilterCategory("Filtering Options");
cl::OptionCategory OtherOptions("Other Options");
+namespace pretty {
+cl::list<std::string> InputFilenames(cl::Positional,
+ cl::desc("<input PDB files>"),
+ cl::OneOrMore, cl::sub(PrettySubcommand));
+
cl::opt<bool> Compilands("compilands", cl::desc("Display compilands"),
- cl::cat(TypeCategory));
+ cl::cat(TypeCategory), cl::sub(PrettySubcommand));
cl::opt<bool> Symbols("symbols", cl::desc("Display symbols for each compiland"),
- cl::cat(TypeCategory));
+ cl::cat(TypeCategory), cl::sub(PrettySubcommand));
cl::opt<bool> Globals("globals", cl::desc("Dump global symbols"),
- cl::cat(TypeCategory));
+ cl::cat(TypeCategory), cl::sub(PrettySubcommand));
cl::opt<bool> Externals("externals", cl::desc("Dump external symbols"),
- cl::cat(TypeCategory));
-cl::opt<bool> Types("types", cl::desc("Display types"), cl::cat(TypeCategory));
+ cl::cat(TypeCategory), cl::sub(PrettySubcommand));
+cl::opt<bool> Types("types", cl::desc("Display types"), cl::cat(TypeCategory),
+ cl::sub(PrettySubcommand));
+cl::opt<bool> Lines("lines", cl::desc("Line tables"), cl::cat(TypeCategory),
+ cl::sub(PrettySubcommand));
cl::opt<bool>
All("all", cl::desc("Implies all other options in 'Symbol Types' category"),
- cl::cat(TypeCategory));
+ cl::cat(TypeCategory), cl::sub(PrettySubcommand));
cl::opt<uint64_t> LoadAddress(
"load-address",
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"),
- cl::ZeroOrMore, cl::cat(FilterCategory));
-cl::list<std::string>
- ExcludeSymbols("exclude-symbols",
- cl::desc("Exclude symbols by regular expression"),
- cl::ZeroOrMore, cl::cat(FilterCategory));
-cl::list<std::string>
- ExcludeCompilands("exclude-compilands",
- cl::desc("Exclude compilands by regular expression"),
- cl::ZeroOrMore, cl::cat(FilterCategory));
+ cl::cat(OtherOptions), cl::sub(PrettySubcommand));
+cl::list<std::string> ExcludeTypes(
+ "exclude-types", cl::desc("Exclude types by regular expression"),
+ cl::ZeroOrMore, cl::cat(FilterCategory), cl::sub(PrettySubcommand));
+cl::list<std::string> ExcludeSymbols(
+ "exclude-symbols", cl::desc("Exclude symbols by regular expression"),
+ cl::ZeroOrMore, cl::cat(FilterCategory), cl::sub(PrettySubcommand));
+cl::list<std::string> ExcludeCompilands(
+ "exclude-compilands", cl::desc("Exclude compilands by regular expression"),
+ cl::ZeroOrMore, cl::cat(FilterCategory), cl::sub(PrettySubcommand));
cl::list<std::string> IncludeTypes(
"include-types",
cl::desc("Include only types which match a regular expression"),
- cl::ZeroOrMore, cl::cat(FilterCategory));
+ cl::ZeroOrMore, cl::cat(FilterCategory), cl::sub(PrettySubcommand));
cl::list<std::string> IncludeSymbols(
"include-symbols",
cl::desc("Include only symbols which match a regular expression"),
- cl::ZeroOrMore, cl::cat(FilterCategory));
+ cl::ZeroOrMore, cl::cat(FilterCategory), cl::sub(PrettySubcommand));
cl::list<std::string> IncludeCompilands(
"include-compilands",
cl::desc("Include only compilands those which match a regular expression"),
- cl::ZeroOrMore, cl::cat(FilterCategory));
+ cl::ZeroOrMore, cl::cat(FilterCategory), cl::sub(PrettySubcommand));
cl::opt<bool> ExcludeCompilerGenerated(
"no-compiler-generated",
cl::desc("Don't show compiler generated types and symbols"),
- cl::cat(FilterCategory));
+ cl::cat(FilterCategory), cl::sub(PrettySubcommand));
cl::opt<bool>
ExcludeSystemLibraries("no-system-libs",
cl::desc("Don't show symbols from system libraries"),
- cl::cat(FilterCategory));
+ cl::cat(FilterCategory), cl::sub(PrettySubcommand));
cl::opt<bool> NoClassDefs("no-class-definitions",
cl::desc("Don't display full class definitions"),
- cl::cat(FilterCategory));
+ cl::cat(FilterCategory), cl::sub(PrettySubcommand));
cl::opt<bool> NoEnumDefs("no-enum-definitions",
cl::desc("Don't display full enum definitions"),
- cl::cat(FilterCategory));
+ cl::cat(FilterCategory), cl::sub(PrettySubcommand));
}
+namespace raw {
-static void reportError(StringRef Input, StringRef Message) {
- if (Input == "-")
- Input = "<stdin>";
- errs() << Input << ": " << Message << "\n";
- errs().flush();
- exit(1);
-}
+cl::OptionCategory MsfOptions("MSF Container Options");
+cl::OptionCategory TypeOptions("Type Record Options");
+cl::OptionCategory FileOptions("Module & File Options");
+cl::OptionCategory SymbolOptions("Symbol Options");
+cl::OptionCategory MiscOptions("Miscellaneous Options");
-static void reportError(StringRef Input, std::error_code EC) {
- reportError(Input, EC.message());
-}
+// MSF OPTIONS
+cl::opt<bool> DumpHeaders("headers", cl::desc("dump PDB headers"),
+ cl::cat(MsfOptions), cl::sub(RawSubcommand));
+cl::opt<bool> DumpStreamBlocks("stream-blocks",
+ cl::desc("dump PDB stream blocks"),
+ cl::cat(MsfOptions), cl::sub(RawSubcommand));
+cl::opt<bool> DumpStreamSummary("stream-summary",
+ cl::desc("dump summary of the PDB streams"),
+ cl::cat(MsfOptions), cl::sub(RawSubcommand));
-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();
-}
+// TYPE OPTIONS
+cl::opt<bool>
+ DumpTpiRecords("tpi-records",
+ cl::desc("dump CodeView type records from TPI stream"),
+ cl::cat(TypeOptions), cl::sub(RawSubcommand));
+cl::opt<bool> DumpTpiRecordBytes(
+ "tpi-record-bytes",
+ cl::desc("dump CodeView type record raw bytes from TPI stream"),
+ cl::cat(TypeOptions), cl::sub(RawSubcommand));
+cl::opt<bool> DumpTpiHash("tpi-hash", cl::desc("dump CodeView TPI hash stream"),
+ cl::cat(TypeOptions), cl::sub(RawSubcommand));
+cl::opt<bool>
+ DumpIpiRecords("ipi-records",
+ cl::desc("dump CodeView type records from IPI stream"),
+ cl::cat(TypeOptions), cl::sub(RawSubcommand));
+cl::opt<bool> DumpIpiRecordBytes(
+ "ipi-record-bytes",
+ cl::desc("dump CodeView type record raw bytes from IPI stream"),
+ cl::cat(TypeOptions), cl::sub(RawSubcommand));
+
+// MODULE & FILE OPTIONS
+cl::opt<bool> DumpModules("modules", cl::desc("dump compiland information"),
+ cl::cat(FileOptions), cl::sub(RawSubcommand));
+cl::opt<bool> DumpModuleFiles("module-files", cl::desc("dump file information"),
+ cl::cat(FileOptions), cl::sub(RawSubcommand));
+cl::opt<bool> DumpLineInfo("line-info",
+ cl::desc("dump file and line information"),
+ cl::cat(FileOptions), cl::sub(RawSubcommand));
+
+// SYMBOL OPTIONS
+cl::opt<bool> DumpModuleSyms("module-syms", cl::desc("dump module symbols"),
+ cl::cat(SymbolOptions), cl::sub(RawSubcommand));
+cl::opt<bool> DumpPublics("publics", cl::desc("dump Publics stream data"),
+ cl::cat(SymbolOptions), cl::sub(RawSubcommand));
+cl::opt<bool>
+ DumpSymRecordBytes("sym-record-bytes",
+ cl::desc("dump CodeView symbol record raw bytes"),
+ cl::cat(SymbolOptions), cl::sub(RawSubcommand));
+
+// MISCELLANEOUS OPTIONS
+cl::opt<bool> DumpSectionContribs("section-contribs",
+ cl::desc("dump section contributions"),
+ cl::cat(MiscOptions), cl::sub(RawSubcommand));
+cl::opt<bool> DumpSectionMap("section-map", cl::desc("dump section map"),
+ cl::cat(MiscOptions), cl::sub(RawSubcommand));
+cl::opt<bool> DumpSectionHeaders("section-headers",
+ cl::desc("dump section headers"),
+ cl::cat(MiscOptions), cl::sub(RawSubcommand));
+cl::opt<bool> DumpFpo("fpo", cl::desc("dump FPO records"), cl::cat(MiscOptions),
+ cl::sub(RawSubcommand));
+
+cl::opt<std::string> DumpStreamDataIdx("stream", cl::desc("dump stream data"),
+ cl::cat(MiscOptions),
+ cl::sub(RawSubcommand));
+cl::opt<std::string> DumpStreamDataName("stream-name",
+ cl::desc("dump stream data"),
+ cl::cat(MiscOptions),
+ cl::sub(RawSubcommand));
+
+cl::opt<bool> RawAll("all", cl::desc("Implies most other options."),
+ cl::cat(MiscOptions), cl::sub(RawSubcommand));
-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));
+cl::list<std::string> InputFilenames(cl::Positional,
+ cl::desc("<input PDB files>"),
+ cl::OneOrMore, cl::sub(RawSubcommand));
}
-static std::error_code checkOffset(MemoryBufferRef M, StringRef SR) {
- return checkOffset(M, uintptr_t(SR.data()), SR.size());
-}
+namespace yaml2pdb {
+cl::opt<std::string>
+ YamlPdbOutputFile("pdb", cl::desc("the name of the PDB file to write"),
+ cl::sub(YamlToPdbSubcommand));
-// 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();
+cl::list<std::string> InputFilename(cl::Positional,
+ cl::desc("<input YAML file>"), cl::Required,
+ cl::sub(YamlToPdbSubcommand));
}
-static uint64_t bytesToBlocks(uint64_t NumBytes, uint64_t BlockSize) {
- return RoundUpToAlignment(NumBytes, BlockSize) / BlockSize;
+namespace pdb2yaml {
+cl::opt<bool>
+ NoFileHeaders("no-file-headers",
+ cl::desc("Do not dump MSF file headers (you will not be able "
+ "to generate a fresh PDB from the resulting YAML)"),
+ cl::sub(PdbToYamlSubcommand), cl::init(false));
+
+cl::opt<bool> StreamMetadata(
+ "stream-metadata",
+ cl::desc("Dump the number of streams and each stream's size"),
+ cl::sub(PdbToYamlSubcommand), cl::init(false));
+cl::opt<bool> StreamDirectory(
+ "stream-directory",
+ cl::desc("Dump each stream's block map (implies -stream-metadata)"),
+ cl::sub(PdbToYamlSubcommand), cl::init(false));
+cl::opt<bool> PdbStream("pdb-stream",
+ cl::desc("Dump the PDB Stream (Stream 1)"),
+ cl::sub(PdbToYamlSubcommand), cl::init(false));
+cl::opt<bool> DbiStream("dbi-stream",
+ cl::desc("Dump the DBI Stream (Stream 2)"),
+ cl::sub(PdbToYamlSubcommand), cl::init(false));
+
+cl::list<std::string> InputFilename(cl::Positional,
+ cl::desc("<input PDB file>"), cl::Required,
+ cl::sub(PdbToYamlSubcommand));
}
-
-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';
- }
+static ExitOnError ExitOnErr;
- // 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";
+static void yamlToPdb(StringRef Path) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> ErrorOrBuffer =
+ MemoryBuffer::getFileOrSTDIN(Path, /*FileSize=*/-1,
+ /*RequiresNullTerminator=*/false);
+
+ if (ErrorOrBuffer.getError()) {
+ ExitOnErr(make_error<GenericError>(generic_error_code::invalid_path, Path));
}
- 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;
- }
+ std::unique_ptr<MemoryBuffer> &Buffer = ErrorOrBuffer.get();
+
+ llvm::yaml::Input In(Buffer->getBuffer());
+ pdb::yaml::PdbObject YamlObj;
+ In >> YamlObj;
+ if (!YamlObj.Headers.hasValue())
+ ExitOnErr(make_error<GenericError>(generic_error_code::unspecified,
+ "Yaml does not contain MSF headers"));
+
+ auto OutFileOrError = FileOutputBuffer::create(
+ opts::yaml2pdb::YamlPdbOutputFile, YamlObj.Headers->FileSize);
+ if (OutFileOrError.getError())
+ ExitOnErr(make_error<GenericError>(generic_error_code::invalid_path,
+ opts::yaml2pdb::YamlPdbOutputFile));
+
+ auto FileByteStream =
+ llvm::make_unique<FileBufferByteStream>(std::move(*OutFileOrError));
+ PDBFileBuilder Builder(std::move(FileByteStream));
+
+ ExitOnErr(Builder.initialize(YamlObj.Headers->SuperBlock));
+ ExitOnErr(Builder.getMsfBuilder().setDirectoryBlocksHint(
+ YamlObj.Headers->DirectoryBlocks));
+ if (!YamlObj.StreamSizes.hasValue()) {
+ ExitOnErr(make_error<GenericError>(
+ generic_error_code::unspecified,
+ "Cannot generate a PDB when stream sizes are not known"));
+ }
- // 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));
+ if (YamlObj.StreamMap.hasValue()) {
+ if (YamlObj.StreamMap->size() != YamlObj.StreamSizes->size()) {
+ ExitOnErr(make_error<GenericError>(generic_error_code::unspecified,
+ "YAML specifies different number of "
+ "streams in stream sizes and stream "
+ "map"));
+ }
- StreamBlocks->push_back(Data);
+ auto &Sizes = *YamlObj.StreamSizes;
+ auto &Map = *YamlObj.StreamMap;
+ for (uint32_t I = 0; I < Sizes.size(); ++I) {
+ uint32_t Size = Sizes[I];
+ std::vector<uint32_t> Blocks;
+ for (auto E : Map[I].Blocks)
+ Blocks.push_back(E);
+ ExitOnErr(Builder.getMsfBuilder().addStream(Size, Blocks));
+ }
+ } else {
+ auto &Sizes = *YamlObj.StreamSizes;
+ for (auto S : Sizes) {
+ ExitOnErr(Builder.getMsfBuilder().addStream(S));
}
}
- // 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";
- }
+ if (YamlObj.PdbStream.hasValue()) {
+ auto &InfoBuilder = Builder.getInfoBuilder();
+ InfoBuilder.setAge(YamlObj.PdbStream->Age);
+ InfoBuilder.setGuid(YamlObj.PdbStream->Guid);
+ InfoBuilder.setSignature(YamlObj.PdbStream->Signature);
+ InfoBuilder.setVersion(YamlObj.PdbStream->Version);
+ for (auto &NM : YamlObj.PdbStream->NamedStreams)
+ InfoBuilder.getNamedStreamsBuilder().addMapping(NM.StreamName,
+ NM.StreamNumber);
}
- 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();
- }
+ if (YamlObj.DbiStream.hasValue()) {
+ auto &DbiBuilder = Builder.getDbiBuilder();
+ DbiBuilder.setAge(YamlObj.DbiStream->Age);
+ DbiBuilder.setBuildNumber(YamlObj.DbiStream->BuildNumber);
+ DbiBuilder.setFlags(YamlObj.DbiStream->Flags);
+ DbiBuilder.setMachineType(YamlObj.DbiStream->MachineType);
+ DbiBuilder.setPdbDllRbld(YamlObj.DbiStream->PdbDllRbld);
+ DbiBuilder.setPdbDllVersion(YamlObj.DbiStream->PdbDllVersion);
+ DbiBuilder.setVersionHeader(YamlObj.DbiStream->VerHeader);
}
+
+ auto Pdb = Builder.build();
+ ExitOnErr(Pdb.takeError());
+
+ auto &PdbFile = *Pdb;
+ ExitOnErr(PdbFile->commit());
}
-static void dumpInput(StringRef Path) {
- if (opts::DumpHeaders || !opts::DumpStreamData.empty()) {
- ErrorOr<std::unique_ptr<MemoryBuffer>> ErrorOrBuffer =
- MemoryBuffer::getFileOrSTDIN(Path, /*FileSize=*/-1,
- /*RequiresNullTerminator=*/false);
+static void pdb2Yaml(StringRef Path) {
+ std::unique_ptr<IPDBSession> Session;
+ ExitOnErr(loadDataForPDB(PDB_ReaderType::Raw, Path, Session));
+
+ RawSession *RS = static_cast<RawSession *>(Session.get());
+ PDBFile &File = RS->getPDBFile();
+ auto O = llvm::make_unique<YAMLOutputStyle>(File);
+ O = llvm::make_unique<YAMLOutputStyle>(File);
- if (std::error_code EC = ErrorOrBuffer.getError())
- reportError(Path, EC);
+ ExitOnErr(O->dump());
+}
- std::unique_ptr<MemoryBuffer> &Buffer = ErrorOrBuffer.get();
+static void dumpRaw(StringRef Path) {
+ std::unique_ptr<IPDBSession> Session;
+ ExitOnErr(loadDataForPDB(PDB_ReaderType::Raw, Path, Session));
- dumpStructure(Buffer->getMemBufferRef());
+ RawSession *RS = static_cast<RawSession *>(Session.get());
+ PDBFile &File = RS->getPDBFile();
+ auto O = llvm::make_unique<LLVMOutputStyle>(File);
- outs().flush();
- return;
- }
+ ExitOnErr(O->dump());
+}
+static void dumpPretty(StringRef Path) {
std::unique_ptr<IPDBSession> Session;
- PDB_ErrorCode Error = loadDataForPDB(PDB_ReaderType::DIA, Path, Session);
- switch (Error) {
- case PDB_ErrorCode::Success:
- break;
- case PDB_ErrorCode::NoPdbImpl:
- outs() << "Reading PDBs is not supported on this platform.\n";
- return;
- case PDB_ErrorCode::InvalidPath:
- outs() << "Unable to load PDB at '" << Path
- << "'. Check that the file exists and is readable.\n";
- return;
- case PDB_ErrorCode::InvalidFileFormat:
- outs() << "Unable to load PDB at '" << Path
- << "'. The file has an unrecognized format.\n";
- return;
- default:
- outs() << "Unable to load PDB at '" << Path
- << "'. An unknown error occured.\n";
- return;
- }
- if (opts::LoadAddress)
- Session->setLoadAddress(opts::LoadAddress);
+
+ ExitOnErr(loadDataForPDB(PDB_ReaderType::DIA, Path, Session));
+
+ if (opts::pretty::LoadAddress)
+ Session->setLoadAddress(opts::pretty::LoadAddress);
LinePrinter Printer(2, outs());
@@ -453,19 +450,22 @@ static void dumpInput(StringRef Path) {
outs() << "HasPrivateSymbols ";
Printer.Unindent();
- if (opts::Compilands) {
+ if (opts::pretty::Compilands) {
Printer.NewLine();
WithColor(Printer, PDB_ColorItem::SectionHeader).get()
<< "---COMPILANDS---";
Printer.Indent();
auto Compilands = GlobalScope->findAllChildren<PDBSymbolCompiland>();
CompilandDumper Dumper(Printer);
+ CompilandDumpFlags options = CompilandDumper::Flags::None;
+ if (opts::pretty::Lines)
+ options = options | CompilandDumper::Flags::Lines;
while (auto Compiland = Compilands->getNext())
- Dumper.start(*Compiland, false);
+ Dumper.start(*Compiland, options);
Printer.Unindent();
}
- if (opts::Types) {
+ if (opts::pretty::Types) {
Printer.NewLine();
WithColor(Printer, PDB_ColorItem::SectionHeader).get() << "---TYPES---";
Printer.Indent();
@@ -474,7 +474,7 @@ static void dumpInput(StringRef Path) {
Printer.Unindent();
}
- if (opts::Symbols) {
+ if (opts::pretty::Symbols) {
Printer.NewLine();
WithColor(Printer, PDB_ColorItem::SectionHeader).get() << "---SYMBOLS---";
Printer.Indent();
@@ -485,7 +485,7 @@ static void dumpInput(StringRef Path) {
Printer.Unindent();
}
- if (opts::Globals) {
+ if (opts::pretty::Globals) {
Printer.NewLine();
WithColor(Printer, PDB_ColorItem::SectionHeader).get() << "---GLOBALS---";
Printer.Indent();
@@ -511,59 +511,98 @@ static void dumpInput(StringRef Path) {
}
Printer.Unindent();
}
- if (opts::Externals) {
+ if (opts::pretty::Externals) {
Printer.NewLine();
WithColor(Printer, PDB_ColorItem::SectionHeader).get() << "---EXTERNALS---";
Printer.Indent();
ExternalSymbolDumper Dumper(Printer);
Dumper.start(*GlobalScope);
}
+ if (opts::pretty::Lines) {
+ Printer.NewLine();
+ }
outs().flush();
}
int main(int argc_, const char *argv_[]) {
// Print a stack trace if we signal out.
- sys::PrintStackTraceOnErrorSignal();
+ sys::PrintStackTraceOnErrorSignal(argv_[0]);
PrettyStackTraceProgram X(argc_, argv_);
+ ExitOnErr.setBanner("llvm-pdbdump: ");
+
SmallVector<const char *, 256> argv;
SpecificBumpPtrAllocator<char> ArgAllocator;
- std::error_code EC = sys::Process::GetArgumentVector(
- argv, makeArrayRef(argv_, argc_), ArgAllocator);
- if (EC) {
- errs() << "error: couldn't get arguments: " << EC.message() << '\n';
- return 1;
- }
+ ExitOnErr(errorCodeToError(sys::Process::GetArgumentVector(
+ argv, makeArrayRef(argv_, argc_), ArgAllocator)));
llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
cl::ParseCommandLineOptions(argv.size(), argv.data(), "LLVM PDB Dumper\n");
- if (opts::All) {
- opts::Compilands = true;
- opts::Symbols = true;
- opts::Globals = true;
- opts::Types = true;
- opts::Externals = true;
- }
- if (opts::ExcludeCompilerGenerated) {
- opts::ExcludeTypes.push_back("__vc_attributes");
- opts::ExcludeCompilands.push_back("* Linker *");
- }
- if (opts::ExcludeSystemLibraries) {
- opts::ExcludeCompilands.push_back(
- "f:\\binaries\\Intermediate\\vctools\\crt_bld");
- }
-#if defined(HAVE_DIA_SDK)
- CoInitializeEx(nullptr, COINIT_MULTITHREADED);
-#endif
+ // These options are shared by two subcommands.
+ if ((opts::PdbToYamlSubcommand || opts::RawSubcommand) && opts::raw::RawAll) {
+ opts::raw::DumpHeaders = true;
+ opts::raw::DumpModules = true;
+ opts::raw::DumpModuleFiles = true;
+ opts::raw::DumpModuleSyms = true;
+ opts::raw::DumpPublics = true;
+ opts::raw::DumpSectionHeaders = true;
+ opts::raw::DumpStreamSummary = true;
+ opts::raw::DumpStreamBlocks = true;
+ opts::raw::DumpTpiRecords = true;
+ opts::raw::DumpTpiHash = true;
+ opts::raw::DumpIpiRecords = true;
+ opts::raw::DumpSectionMap = true;
+ opts::raw::DumpSectionContribs = true;
+ opts::raw::DumpLineInfo = true;
+ opts::raw::DumpFpo = true;
+ }
- std::for_each(opts::InputFilenames.begin(), opts::InputFilenames.end(),
- dumpInput);
+ llvm::sys::InitializeCOMRAII COM(llvm::sys::COMThreadingMode::MultiThreaded);
+
+ if (opts::PdbToYamlSubcommand) {
+ pdb2Yaml(opts::pdb2yaml::InputFilename.front());
+ } else if (opts::YamlToPdbSubcommand) {
+ yamlToPdb(opts::yaml2pdb::InputFilename.front());
+ } else if (opts::PrettySubcommand) {
+ if (opts::pretty::Lines)
+ opts::pretty::Compilands = true;
+
+ if (opts::pretty::All) {
+ opts::pretty::Compilands = true;
+ opts::pretty::Symbols = true;
+ opts::pretty::Globals = true;
+ opts::pretty::Types = true;
+ opts::pretty::Externals = true;
+ opts::pretty::Lines = true;
+ }
-#if defined(HAVE_DIA_SDK)
- CoUninitialize();
-#endif
+ // When adding filters for excluded compilands and types, we need to
+ // remember
+ // that these are regexes. So special characters such as * and \ need to be
+ // escaped in the regex. In the case of a literal \, this means it needs to
+ // be escaped again in the C++. So matching a single \ in the input
+ // requires
+ // 4 \es in the C++.
+ if (opts::pretty::ExcludeCompilerGenerated) {
+ opts::pretty::ExcludeTypes.push_back("__vc_attributes");
+ opts::pretty::ExcludeCompilands.push_back("\\* Linker \\*");
+ }
+ if (opts::pretty::ExcludeSystemLibraries) {
+ opts::pretty::ExcludeCompilands.push_back(
+ "f:\\\\binaries\\\\Intermediate\\\\vctools\\\\crt_bld");
+ opts::pretty::ExcludeCompilands.push_back("f:\\\\dd\\\\vctools\\\\crt");
+ opts::pretty::ExcludeCompilands.push_back(
+ "d:\\\\th.obj.x86fre\\\\minkernel");
+ }
+ std::for_each(opts::pretty::InputFilenames.begin(),
+ opts::pretty::InputFilenames.end(), dumpPretty);
+ } else if (opts::RawSubcommand) {
+ std::for_each(opts::raw::InputFilenames.begin(),
+ opts::raw::InputFilenames.end(), dumpRaw);
+ }
+ outs().flush();
return 0;
}
diff --git a/tools/llvm-pdbdump/llvm-pdbdump.h b/tools/llvm-pdbdump/llvm-pdbdump.h
index cb5bec64dec81..0a66515f4a015 100644
--- a/tools/llvm-pdbdump/llvm-pdbdump.h
+++ b/tools/llvm-pdbdump/llvm-pdbdump.h
@@ -14,12 +14,13 @@
#include "llvm/Support/raw_ostream.h"
namespace opts {
+
+namespace pretty {
extern llvm::cl::opt<bool> Compilands;
extern llvm::cl::opt<bool> Symbols;
extern llvm::cl::opt<bool> Globals;
extern llvm::cl::opt<bool> Types;
extern llvm::cl::opt<bool> All;
-
extern llvm::cl::opt<bool> ExcludeCompilerGenerated;
extern llvm::cl::opt<bool> NoClassDefs;
@@ -32,4 +33,37 @@ extern llvm::cl::list<std::string> IncludeSymbols;
extern llvm::cl::list<std::string> IncludeCompilands;
}
-#endif \ No newline at end of file
+namespace raw {
+extern llvm::cl::opt<bool> DumpHeaders;
+extern llvm::cl::opt<bool> DumpStreamBlocks;
+extern llvm::cl::opt<bool> DumpStreamSummary;
+extern llvm::cl::opt<bool> DumpTpiHash;
+extern llvm::cl::opt<bool> DumpTpiRecordBytes;
+extern llvm::cl::opt<bool> DumpTpiRecords;
+extern llvm::cl::opt<bool> DumpIpiRecords;
+extern llvm::cl::opt<bool> DumpIpiRecordBytes;
+extern llvm::cl::opt<std::string> DumpStreamDataIdx;
+extern llvm::cl::opt<std::string> DumpStreamDataName;
+extern llvm::cl::opt<bool> DumpModules;
+extern llvm::cl::opt<bool> DumpModuleFiles;
+extern llvm::cl::opt<bool> DumpModuleSyms;
+extern llvm::cl::opt<bool> DumpPublics;
+extern llvm::cl::opt<bool> DumpSectionContribs;
+extern llvm::cl::opt<bool> DumpLineInfo;
+extern llvm::cl::opt<bool> DumpSectionMap;
+extern llvm::cl::opt<bool> DumpSymRecordBytes;
+extern llvm::cl::opt<bool> DumpSectionHeaders;
+extern llvm::cl::opt<bool> DumpFpo;
+}
+
+namespace pdb2yaml {
+extern llvm::cl::opt<bool> NoFileHeaders;
+extern llvm::cl::opt<bool> StreamMetadata;
+extern llvm::cl::opt<bool> StreamDirectory;
+extern llvm::cl::opt<bool> PdbStream;
+extern llvm::cl::opt<bool> DbiStream;
+extern llvm::cl::list<std::string> InputFilename;
+}
+}
+
+#endif
diff --git a/tools/llvm-profdata/Makefile b/tools/llvm-profdata/Makefile
deleted file mode 100644
index 6f8c16265f37a..0000000000000
--- a/tools/llvm-profdata/Makefile
+++ /dev/null
@@ -1,17 +0,0 @@
-##===- tools/llvm-profdata/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-profdata
-LINK_COMPONENTS := profiledata support
-
-# This tool has no plugins, optimize startup time.
-TOOL_NO_EXPORTS := 1
-
-include $(LEVEL)/Makefile.common
diff --git a/tools/llvm-profdata/llvm-profdata.cpp b/tools/llvm-profdata/llvm-profdata.cpp
index dc6cd0a5784d6..8e4b4c3d4ed55 100644
--- a/tools/llvm-profdata/llvm-profdata.cpp
+++ b/tools/llvm-profdata/llvm-profdata.cpp
@@ -17,6 +17,7 @@
#include "llvm/IR/LLVMContext.h"
#include "llvm/ProfileData/InstrProfReader.h"
#include "llvm/ProfileData/InstrProfWriter.h"
+#include "llvm/ProfileData/ProfileCommon.h"
#include "llvm/ProfileData/SampleProfReader.h"
#include "llvm/ProfileData/SampleProfWriter.h"
#include "llvm/Support/CommandLine.h"
@@ -30,7 +31,6 @@
#include "llvm/Support/Signals.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
-#include <tuple>
using namespace llvm;
@@ -47,38 +47,50 @@ static void exitWithError(const Twine &Message, StringRef Whence = "",
::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?");
- }
+static void exitWithError(Error E, StringRef Whence = "") {
+ if (E.isA<InstrProfError>()) {
+ handleAllErrors(std::move(E), [&](const InstrProfError &IPE) {
+ instrprof_error instrError = IPE.get();
+ StringRef Hint = "";
+ if (instrError == instrprof_error::unrecognized_format) {
+ // Hint for common error of forgetting -sample for sample profiles.
+ Hint = "Perhaps you forgot to use the -sample option?";
+ }
+ exitWithError(IPE.message(), Whence, Hint);
+ });
}
- exitWithError(Error.message(), Whence);
+
+ exitWithError(toString(std::move(E)), Whence);
+}
+
+static void exitWithErrorCode(std::error_code EC, StringRef Whence = "") {
+ exitWithError(EC.message(), Whence);
}
namespace {
enum ProfileKinds { instr, sample };
}
-static void handleMergeWriterError(std::error_code &Error,
- StringRef WhenceFile = "",
+static void handleMergeWriterError(Error E, StringRef WhenceFile = "",
StringRef WhenceFunction = "",
bool ShowHint = true) {
if (!WhenceFile.empty())
errs() << WhenceFile << ": ";
if (!WhenceFunction.empty())
errs() << WhenceFunction << ": ";
- errs() << Error.message() << "\n";
+
+ auto IPE = instrprof_error::success;
+ E = handleErrors(std::move(E),
+ [&IPE](std::unique_ptr<InstrProfError> E) -> Error {
+ IPE = E->get();
+ return Error(std::move(E));
+ });
+ errs() << toString(std::move(E)) << "\n";
if (ShowHint) {
StringRef Hint = "";
- if (Error.category() == instrprof_category()) {
- instrprof_error instrError = static_cast<instrprof_error>(Error.value());
- switch (instrError) {
+ if (IPE != instrprof_error::success) {
+ switch (IPE) {
case instrprof_error::hash_mismatch:
case instrprof_error::count_mismatch:
case instrprof_error::value_site_count_mismatch:
@@ -107,7 +119,7 @@ typedef SmallVector<WeightedFile, 5> WeightedFileVector;
static void mergeInstrProfile(const WeightedFileVector &Inputs,
StringRef OutputFilename,
- ProfileFormat OutputFormat) {
+ ProfileFormat OutputFormat, bool OutputSparse) {
if (OutputFilename.compare("-") == 0)
exitWithError("Cannot write indexed profdata format to stdout.");
@@ -119,23 +131,29 @@ static void mergeInstrProfile(const WeightedFileVector &Inputs,
if (EC)
exitWithErrorCode(EC, OutputFilename);
- InstrProfWriter Writer;
- SmallSet<std::error_code, 4> WriterErrorCodes;
+ InstrProfWriter Writer(OutputSparse);
+ SmallSet<instrprof_error, 4> WriterErrorCodes;
for (const auto &Input : Inputs) {
auto ReaderOrErr = InstrProfReader::create(Input.Filename);
- if (std::error_code ec = ReaderOrErr.getError())
- exitWithErrorCode(ec, Input.Filename);
+ if (Error E = ReaderOrErr.takeError())
+ exitWithError(std::move(E), Input.Filename);
auto Reader = std::move(ReaderOrErr.get());
+ bool IsIRProfile = Reader->isIRLevelProfile();
+ if (Writer.setIsIRLevelProfile(IsIRProfile))
+ exitWithError("Merge IR generated profile with Clang generated profile.");
+
for (auto &I : *Reader) {
- if (std::error_code EC = Writer.addRecord(std::move(I), Input.Weight)) {
+ if (Error E = 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);
+ instrprof_error IPE = InstrProfError::take(std::move(E));
+ bool firstTime = WriterErrorCodes.insert(IPE).second;
+ handleMergeWriterError(make_error<InstrProfError>(IPE), Input.Filename,
+ I.Name, firstTime);
}
}
if (Reader->hasError())
- exitWithErrorCode(Reader->getError(), Input.Filename);
+ exitWithError(Reader->getError(), Input.Filename);
}
if (OutputFormat == PF_Text)
Writer.writeText(Output);
@@ -159,9 +177,9 @@ static void mergeSampleProfile(const WeightedFileVector &Inputs,
auto Writer = std::move(WriterOrErr.get());
StringMap<FunctionSamples> ProfileMap;
SmallVector<std::unique_ptr<sampleprof::SampleProfileReader>, 5> Readers;
+ LLVMContext Context;
for (const auto &Input : Inputs) {
- auto ReaderOrErr =
- SampleProfileReader::create(Input.Filename, getGlobalContext());
+ auto ReaderOrErr = SampleProfileReader::create(Input.Filename, Context);
if (std::error_code EC = ReaderOrErr.getError())
exitWithErrorCode(EC, Input.Filename);
@@ -183,7 +201,7 @@ static void mergeSampleProfile(const WeightedFileVector &Inputs,
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);
+ handleMergeWriterError(errorCodeToError(EC), Input.Filename, FName);
}
}
}
@@ -205,11 +223,53 @@ static WeightedFile parseWeightedFile(const StringRef &WeightedFilename) {
return WeightedFile(FileName, Weight);
}
+static std::unique_ptr<MemoryBuffer>
+getInputFilenamesFileBuf(const StringRef &InputFilenamesFile) {
+ if (InputFilenamesFile == "")
+ return {};
+
+ auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFilenamesFile);
+ if (!BufOrError)
+ exitWithErrorCode(BufOrError.getError(), InputFilenamesFile);
+
+ return std::move(*BufOrError);
+}
+
+static void parseInputFilenamesFile(MemoryBuffer *Buffer,
+ WeightedFileVector &WFV) {
+ if (!Buffer)
+ return;
+
+ SmallVector<StringRef, 8> Entries;
+ StringRef Data = Buffer->getBuffer();
+ Data.split(Entries, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false);
+ for (const StringRef &FileWeightEntry : Entries) {
+ StringRef SanitizedEntry = FileWeightEntry.trim(" \t\v\f\r");
+ // Skip comments.
+ if (SanitizedEntry.startswith("#"))
+ continue;
+ // If there's no comma, it's an unweighted profile.
+ else if (SanitizedEntry.find(',') == StringRef::npos)
+ WFV.emplace_back(SanitizedEntry, 1);
+ else
+ WFV.emplace_back(parseWeightedFile(SanitizedEntry));
+ }
+}
+
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> InputFilenamesFile(
+ "input-files", cl::init(""),
+ cl::desc("Path to file containing newline-separated "
+ "[<weight>,]<filename> entries"));
+ cl::alias InputFilenamesFileA("f", cl::desc("Alias for --input-files"),
+ cl::aliasopt(InputFilenamesFile));
+ cl::opt<bool> DumpInputFileList(
+ "dump-input-file-list", cl::init(false), cl::Hidden,
+ cl::desc("Dump the list of input files and their weights, then exit"));
cl::opt<std::string> OutputFilename("output", cl::value_desc("output"),
cl::init("-"), cl::Required,
cl::desc("Output file"));
@@ -219,7 +279,6 @@ static int merge_main(int argc, const char *argv[]) {
cl::desc("Profile kind:"), cl::init(instr),
cl::values(clEnumVal(instr, "Instrumentation profile (default)"),
clEnumVal(sample, "Sample profile"), clEnumValEnd));
-
cl::opt<ProfileFormat> OutputFormat(
cl::desc("Format of output profile"), cl::init(PF_Binary),
cl::values(clEnumValN(PF_Binary, "binary", "Binary encoding (default)"),
@@ -227,38 +286,63 @@ static int merge_main(int argc, const char *argv[]) {
clEnumValN(PF_GCC, "gcc",
"GCC encoding (only meaningful for -sample)"),
clEnumValEnd));
+ cl::opt<bool> OutputSparse("sparse", cl::init(false),
+ cl::desc("Generate a sparse profile (only meaningful for -instr)"));
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));
+ WeightedInputs.emplace_back(Filename, 1);
for (StringRef WeightedFilename : WeightedInputFilenames)
- WeightedInputs.push_back(parseWeightedFile(WeightedFilename));
+ WeightedInputs.emplace_back(parseWeightedFile(WeightedFilename));
+
+ // Make sure that the file buffer stays alive for the duration of the
+ // weighted input vector's lifetime.
+ auto Buffer = getInputFilenamesFileBuf(InputFilenamesFile);
+ parseInputFilenamesFile(Buffer.get(), WeightedInputs);
+
+ if (WeightedInputs.empty())
+ exitWithError("No input files specified. See " +
+ sys::path::filename(argv[0]) + " -help");
+
+ if (DumpInputFileList) {
+ for (auto &WF : WeightedInputs)
+ outs() << WF.Weight << "," << WF.Filename << "\n";
+ return 0;
+ }
if (ProfileKind == instr)
- mergeInstrProfile(WeightedInputs, OutputFilename, OutputFormat);
+ mergeInstrProfile(WeightedInputs, OutputFilename, OutputFormat,
+ OutputSparse);
else
mergeSampleProfile(WeightedInputs, OutputFilename, OutputFormat);
return 0;
}
-static int showInstrProfile(std::string Filename, bool ShowCounts,
- bool ShowIndirectCallTargets, bool ShowAllFunctions,
- std::string ShowFunction, bool TextFormat,
+static int showInstrProfile(const std::string &Filename, bool ShowCounts,
+ bool ShowIndirectCallTargets,
+ bool ShowDetailedSummary,
+ std::vector<uint32_t> DetailedSummaryCutoffs,
+ bool ShowAllFunctions,
+ const std::string &ShowFunction, bool TextFormat,
raw_fd_ostream &OS) {
auto ReaderOrErr = InstrProfReader::create(Filename);
- if (std::error_code EC = ReaderOrErr.getError())
- exitWithErrorCode(EC, Filename);
+ std::vector<uint32_t> Cutoffs = std::move(DetailedSummaryCutoffs);
+ if (ShowDetailedSummary && Cutoffs.empty()) {
+ Cutoffs = {800000, 900000, 950000, 990000, 999000, 999900, 999990};
+ }
+ InstrProfSummaryBuilder Builder(std::move(Cutoffs));
+ if (Error E = ReaderOrErr.takeError())
+ exitWithError(std::move(E), Filename);
auto Reader = std::move(ReaderOrErr.get());
- uint64_t MaxFunctionCount = 0, MaxBlockCount = 0;
- size_t ShownFunctions = 0, TotalFunctions = 0;
+ bool IsIRInstr = Reader->isIRLevelProfile();
+ size_t ShownFunctions = 0;
+ uint64_t TotalNumValueSites = 0;
+ uint64_t TotalNumValueSitesWithValueProfile = 0;
+ uint64_t TotalNumValues = 0;
for (const auto &Func : *Reader) {
bool Show =
ShowAllFunctions || (!ShowFunction.empty() &&
@@ -272,15 +356,8 @@ static int showInstrProfile(std::string Filename, bool ShowCounts,
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];
- }
+ Builder.addRecord(Func);
if (Show) {
@@ -291,8 +368,9 @@ static int showInstrProfile(std::string Filename, bool ShowCounts,
OS << " " << Func.Name << ":\n"
<< " Hash: " << format("0x%016" PRIx64, Func.Hash) << "\n"
- << " Counters: " << Func.Counts.size() << "\n"
- << " Function count: " << Func.Counts[0] << "\n";
+ << " Counters: " << Func.Counts.size() << "\n";
+ if (!IsIRInstr)
+ OS << " Function count: " << Func.Counts[0] << "\n";
if (ShowIndirectCallTargets)
OS << " Indirect Call Site Count: "
@@ -300,8 +378,9 @@ static int showInstrProfile(std::string Filename, bool ShowCounts,
if (ShowCounts) {
OS << " Block counts: [";
- for (size_t I = 1, E = Func.Counts.size(); I < E; ++I) {
- OS << (I == 1 ? "" : ", ") << Func.Counts[I];
+ size_t Start = (IsIRInstr ? 0 : 1);
+ for (size_t I = Start, E = Func.Counts.size(); I < E; ++I) {
+ OS << (I == Start ? "" : ", ") << Func.Counts[I];
}
OS << "]\n";
}
@@ -310,10 +389,14 @@ static int showInstrProfile(std::string Filename, bool ShowCounts,
InstrProfSymtab &Symtab = Reader->getSymtab();
uint32_t NS = Func.getNumValueSites(IPVK_IndirectCallTarget);
OS << " Indirect Target Results: \n";
+ TotalNumValueSites += NS;
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);
+ TotalNumValues += NV;
+ if (NV)
+ TotalNumValueSitesWithValueProfile++;
for (uint32_t V = 0; V < NV; V++) {
OS << "\t[ " << I << ", ";
OS << Symtab.getFuncName(VD[V].Value) << ", " << VD[V].Count
@@ -323,26 +406,46 @@ static int showInstrProfile(std::string Filename, bool ShowCounts,
}
}
}
-
if (Reader->hasError())
- exitWithErrorCode(Reader->getError(), Filename);
+ exitWithError(Reader->getError(), Filename);
if (ShowCounts && TextFormat)
return 0;
-
+ std::unique_ptr<ProfileSummary> PS(Builder.getSummary());
if (ShowAllFunctions || !ShowFunction.empty())
OS << "Functions shown: " << ShownFunctions << "\n";
- OS << "Total functions: " << TotalFunctions << "\n";
- OS << "Maximum function count: " << MaxFunctionCount << "\n";
- OS << "Maximum internal block count: " << MaxBlockCount << "\n";
+ OS << "Total functions: " << PS->getNumFunctions() << "\n";
+ OS << "Maximum function count: " << PS->getMaxFunctionCount() << "\n";
+ OS << "Maximum internal block count: " << PS->getMaxInternalCount() << "\n";
+ if (ShownFunctions && ShowIndirectCallTargets) {
+ OS << "Total Number of Indirect Call Sites : " << TotalNumValueSites
+ << "\n";
+ OS << "Total Number of Sites With Values : "
+ << TotalNumValueSitesWithValueProfile << "\n";
+ OS << "Total Number of Profiled Values : " << TotalNumValues << "\n";
+ }
+
+ if (ShowDetailedSummary) {
+ OS << "Detailed summary:\n";
+ OS << "Total number of blocks: " << PS->getNumCounts() << "\n";
+ OS << "Total count: " << PS->getTotalCount() << "\n";
+ for (auto Entry : PS->getDetailedSummary()) {
+ OS << Entry.NumCounts << " blocks with count >= " << Entry.MinCount
+ << " account for "
+ << format("%0.6g", (float)Entry.Cutoff / ProfileSummary::Scale * 100)
+ << " percentage of the total counts.\n";
+ }
+ }
return 0;
}
-static int showSampleProfile(std::string Filename, bool ShowCounts,
- bool ShowAllFunctions, std::string ShowFunction,
+static int showSampleProfile(const std::string &Filename, bool ShowCounts,
+ bool ShowAllFunctions,
+ const std::string &ShowFunction,
raw_fd_ostream &OS) {
using namespace sampleprof;
- auto ReaderOrErr = SampleProfileReader::create(Filename, getGlobalContext());
+ LLVMContext Context;
+ auto ReaderOrErr = SampleProfileReader::create(Filename, Context);
if (std::error_code EC = ReaderOrErr.getError())
exitWithErrorCode(EC, Filename);
@@ -370,6 +473,13 @@ static int show_main(int argc, const char *argv[]) {
cl::opt<bool> ShowIndirectCallTargets(
"ic-targets", cl::init(false),
cl::desc("Show indirect call site target values for shown functions"));
+ cl::opt<bool> ShowDetailedSummary("detailed-summary", cl::init(false),
+ cl::desc("Show detailed profile summary"));
+ cl::list<uint32_t> DetailedSummaryCutoffs(
+ cl::CommaSeparated, "detailed-summary-cutoffs",
+ cl::desc(
+ "Cutoff percentages (times 10000) for generating detailed summary"),
+ cl::value_desc("800000,901000,999999"));
cl::opt<bool> ShowAllFunctions("all-functions", cl::init(false),
cl::desc("Details for every function"));
cl::opt<std::string> ShowFunction("function",
@@ -397,8 +507,11 @@ static int show_main(int argc, const char *argv[]) {
if (ShowAllFunctions && !ShowFunction.empty())
errs() << "warning: -function argument ignored: showing all functions\n";
+ std::vector<uint32_t> Cutoffs(DetailedSummaryCutoffs.begin(),
+ DetailedSummaryCutoffs.end());
if (ProfileKind == instr)
return showInstrProfile(Filename, ShowCounts, ShowIndirectCallTargets,
+ ShowDetailedSummary, DetailedSummaryCutoffs,
ShowAllFunctions, ShowFunction, TextFormat, OS);
else
return showSampleProfile(Filename, ShowCounts, ShowAllFunctions,
@@ -407,7 +520,7 @@ static int show_main(int argc, const char *argv[]) {
int main(int argc, const char *argv[]) {
// Print a stack trace if we signal out.
- sys::PrintStackTraceOnErrorSignal();
+ sys::PrintStackTraceOnErrorSignal(argv[0]);
PrettyStackTraceProgram X(argc, argv);
llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
diff --git a/tools/llvm-readobj/ARMAttributeParser.cpp b/tools/llvm-readobj/ARMAttributeParser.cpp
index 688d349d7ec03..877dd71c90706 100644
--- a/tools/llvm-readobj/ARMAttributeParser.cpp
+++ b/tools/llvm-readobj/ARMAttributeParser.cpp
@@ -8,10 +8,10 @@
//===----------------------------------------------------------------------===//
#include "ARMAttributeParser.h"
-#include "StreamWriter.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/LEB128.h"
+#include "llvm/Support/ScopedPrinter.h"
using namespace llvm;
using namespace llvm::ARMBuildAttrs;
@@ -63,6 +63,7 @@ ARMAttributeParser::DisplayRoutines[] = {
ATTRIBUTE_HANDLER(ABI_FP_16bit_format),
ATTRIBUTE_HANDLER(MPextension_use),
ATTRIBUTE_HANDLER(DIV_use),
+ ATTRIBUTE_HANDLER(DSP_extension),
ATTRIBUTE_HANDLER(T2EE_use),
ATTRIBUTE_HANDLER(Virtualization_use),
ATTRIBUTE_HANDLER(nodefaults)
@@ -340,7 +341,7 @@ void ARMAttributeParser::ABI_align_needed(AttrType Tag, const uint8_t *Data,
if (Value < array_lengthof(Strings))
Description = std::string(Strings[Value]);
else if (Value <= 12)
- Description = std::string("8-byte alignment, ") + utostr(1 << Value)
+ Description = std::string("8-byte alignment, ") + utostr(1ULL << Value)
+ std::string("-byte extended alignment");
else
Description = "Invalid";
@@ -361,8 +362,8 @@ void ARMAttributeParser::ABI_align_preserved(AttrType Tag, const uint8_t *Data,
if (Value < array_lengthof(Strings))
Description = std::string(Strings[Value]);
else if (Value <= 12)
- Description = std::string("8-byte stack alignment, ") + utostr(1 << Value)
- + std::string("-byte data alignment");
+ Description = std::string("8-byte stack alignment, ") +
+ utostr(1ULL << Value) + std::string("-byte data alignment");
else
Description = "Invalid";
@@ -517,6 +518,16 @@ void ARMAttributeParser::DIV_use(AttrType Tag, const uint8_t *Data,
PrintAttribute(Tag, Value, ValueDesc);
}
+void ARMAttributeParser::DSP_extension(AttrType Tag, const uint8_t *Data,
+ uint32_t &Offset) {
+ static const char *const Strings[] = { "Not Permitted", "Permitted" };
+
+ uint64_t Value = ParseInteger(Data, Offset);
+ StringRef ValueDesc =
+ (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr;
+ PrintAttribute(Tag, Value, ValueDesc);
+}
+
void ARMAttributeParser::T2EE_use(AttrType Tag, const uint8_t *Data,
uint32_t &Offset) {
static const char *const Strings[] = { "Not Permitted", "Permitted" };
diff --git a/tools/llvm-readobj/ARMAttributeParser.h b/tools/llvm-readobj/ARMAttributeParser.h
index f924c835d3eaf..6936b70ca1230 100644
--- a/tools/llvm-readobj/ARMAttributeParser.h
+++ b/tools/llvm-readobj/ARMAttributeParser.h
@@ -10,14 +10,14 @@
#ifndef LLVM_TOOLS_LLVM_READOBJ_ARMATTRIBUTEPARSER_H
#define LLVM_TOOLS_LLVM_READOBJ_ARMATTRIBUTEPARSER_H
-#include "StreamWriter.h"
#include "llvm/Support/ARMBuildAttributes.h"
+#include "llvm/Support/ScopedPrinter.h"
namespace llvm {
class StringRef;
class ARMAttributeParser {
- StreamWriter &SW;
+ ScopedPrinter &SW;
struct DisplayHandler {
ARMBuildAttrs::AttrType Attribute;
@@ -100,6 +100,8 @@ class ARMAttributeParser {
uint32_t &Offset);
void DIV_use(ARMBuildAttrs::AttrType Tag, const uint8_t *Data,
uint32_t &Offset);
+ void DSP_extension(ARMBuildAttrs::AttrType Tag, const uint8_t *Data,
+ uint32_t &Offset);
void T2EE_use(ARMBuildAttrs::AttrType Tag, const uint8_t *Data,
uint32_t &Offset);
void Virtualization_use(ARMBuildAttrs::AttrType Tag, const uint8_t *Data,
@@ -113,7 +115,7 @@ class ARMAttributeParser {
SmallVectorImpl<uint8_t> &IndexList);
void ParseSubsection(const uint8_t *Data, uint32_t Length);
public:
- ARMAttributeParser(StreamWriter &SW) : SW(SW) {}
+ ARMAttributeParser(ScopedPrinter &SW) : SW(SW) {}
void Parse(ArrayRef<uint8_t> Section);
};
diff --git a/tools/llvm-readobj/ARMEHABIPrinter.h b/tools/llvm-readobj/ARMEHABIPrinter.h
index beb5fd4ea042b..59c9b713d85e1 100644
--- a/tools/llvm-readobj/ARMEHABIPrinter.h
+++ b/tools/llvm-readobj/ARMEHABIPrinter.h
@@ -11,7 +11,7 @@
#define LLVM_TOOLS_LLVM_READOBJ_ARMEHABIPRINTER_H
#include "Error.h"
-#include "StreamWriter.h"
+#include "llvm-readobj.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Object/ELF.h"
#include "llvm/Object/ELFTypes.h"
@@ -19,6 +19,7 @@
#include "llvm/Support/Debug.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/Format.h"
+#include "llvm/Support/ScopedPrinter.h"
#include "llvm/Support/type_traits.h"
namespace llvm {
@@ -26,7 +27,7 @@ namespace ARM {
namespace EHABI {
class OpcodeDecoder {
- StreamWriter &SW;
+ ScopedPrinter &SW;
raw_ostream &OS;
struct RingEntry {
@@ -63,7 +64,7 @@ class OpcodeDecoder {
void PrintRegisters(uint32_t Mask, StringRef Prefix);
public:
- OpcodeDecoder(StreamWriter &SW) : SW(SW), OS(SW.getOStream()) {}
+ OpcodeDecoder(ScopedPrinter &SW) : SW(SW), OS(SW.getOStream()) {}
void Decode(const uint8_t *Opcodes, off_t Offset, size_t Length);
};
@@ -310,7 +311,7 @@ class PrinterContext {
typedef typename object::ELFFile<ET>::Elf_Rel Elf_Rel;
typedef typename object::ELFFile<ET>::Elf_Word Elf_Word;
- StreamWriter &SW;
+ ScopedPrinter &SW;
const object::ELFFile<ET> *ELF;
const Elf_Shdr *Symtab;
ArrayRef<Elf_Word> ShndxTable;
@@ -334,7 +335,7 @@ class PrinterContext {
void PrintOpcodes(const uint8_t *Entry, size_t Length, off_t Offset) const;
public:
- PrinterContext(StreamWriter &SW, const object::ELFFile<ET> *ELF,
+ PrinterContext(ScopedPrinter &SW, const object::ELFFile<ET> *ELF,
const Elf_Shdr *Symtab)
: SW(SW), ELF(ELF), Symtab(Symtab) {}
@@ -354,8 +355,15 @@ PrinterContext<ET>::FunctionAtAddress(unsigned Section,
for (const Elf_Sym &Sym : ELF->symbols(Symtab))
if (Sym.st_shndx == Section && Sym.st_value == Address &&
- Sym.getType() == ELF::STT_FUNC)
- return Sym.getName(StrTable);
+ Sym.getType() == ELF::STT_FUNC) {
+ auto NameOrErr = Sym.getName(StrTable);
+ if (!NameOrErr) {
+ // TODO: Actually report errors helpfully.
+ consumeError(NameOrErr.takeError());
+ return readobj_error::unknown_symbol;
+ }
+ return *NameOrErr;
+ }
return readobj_error::unknown_symbol;
}
diff --git a/tools/llvm-readobj/ARMWinEHPrinter.cpp b/tools/llvm-readobj/ARMWinEHPrinter.cpp
index 650955d1d75c0..1a033b1eb42e8 100644
--- a/tools/llvm-readobj/ARMWinEHPrinter.cpp
+++ b/tools/llvm-readobj/ARMWinEHPrinter.cpp
@@ -198,12 +198,15 @@ Decoder::getSectionContaining(const COFFObjectFile &COFF, uint64_t VA) {
ErrorOr<object::SymbolRef> Decoder::getSymbol(const COFFObjectFile &COFF,
uint64_t VA, bool FunctionOnly) {
for (const auto &Symbol : COFF.symbols()) {
- if (FunctionOnly && Symbol.getType() != SymbolRef::ST_Function)
+ Expected<SymbolRef::Type> Type = Symbol.getType();
+ if (!Type)
+ return errorToErrorCode(Type.takeError());
+ if (FunctionOnly && *Type != SymbolRef::ST_Function)
continue;
- ErrorOr<uint64_t> Address = Symbol.getAddress();
- if (std::error_code EC = Address.getError())
- return EC;
+ Expected<uint64_t> Address = Symbol.getAddress();
+ if (!Address)
+ return errorToErrorCode(Address.takeError());
if (*Address == VA)
return Symbol;
}
@@ -247,7 +250,7 @@ bool Decoder::opcode_10Lxxxxx(const uint8_t *OC, unsigned &Offset,
printRegisters(std::make_pair(RegisterMask, 0));
OS << '\n';
- ++Offset, ++Offset;
+ Offset += 2;
return false;
}
@@ -320,7 +323,7 @@ bool Decoder::opcode_111010xx(const uint8_t *OC, unsigned &Offset,
static_cast<const char *>(Prologue ? "sub" : "add"),
Imm);
- ++Offset, ++Offset;
+ Offset += 2;
return false;
}
@@ -334,7 +337,7 @@ bool Decoder::opcode_1110110L(const uint8_t *OC, unsigned &Offset,
printRegisters(std::make_pair(GPRMask, 0));
OS << '\n';
- ++Offset, ++Offset;
+ Offset += 2;
return false;
}
@@ -350,7 +353,7 @@ bool Decoder::opcode_11101110(const uint8_t *OC, unsigned &Offset,
<< format("0x%02x 0x%02x ; microsoft-specific (type: %u)\n",
OC[Offset + 0], OC[Offset + 1], OC[Offset + 1] & 0x0f);
- ++Offset, ++Offset;
+ Offset += 2;
return false;
}
@@ -366,7 +369,7 @@ bool Decoder::opcode_11101111(const uint8_t *OC, unsigned &Offset,
<< format("0x%02x 0x%02x ; ldr.w lr, [sp], #%u\n",
OC[Offset + 0], OC[Offset + 1], OC[Offset + 1] << 2);
- ++Offset, ++Offset;
+ Offset += 2;
return false;
}
@@ -381,7 +384,7 @@ bool Decoder::opcode_11110101(const uint8_t *OC, unsigned &Offset,
printRegisters(std::make_pair(0, VFPMask));
OS << '\n';
- ++Offset, ++Offset;
+ Offset += 2;
return false;
}
@@ -396,7 +399,7 @@ bool Decoder::opcode_11110110(const uint8_t *OC, unsigned &Offset,
printRegisters(std::make_pair(0, VFPMask));
OS << '\n';
- ++Offset, ++Offset;
+ Offset += 2;
return false;
}
@@ -409,7 +412,7 @@ bool Decoder::opcode_11110111(const uint8_t *OC, unsigned &Offset,
static_cast<const char *>(Prologue ? "sub" : "add"),
Imm);
- ++Offset, ++Offset, ++Offset;
+ Offset += 3;
return false;
}
@@ -424,7 +427,7 @@ bool Decoder::opcode_11111000(const uint8_t *OC, unsigned &Offset,
OC[Offset + 0], OC[Offset + 1], OC[Offset + 2], OC[Offset + 3],
static_cast<const char *>(Prologue ? "sub" : "add"), Imm);
- ++Offset, ++Offset, ++Offset, ++Offset;
+ Offset += 4;
return false;
}
@@ -437,7 +440,7 @@ bool Decoder::opcode_11111001(const uint8_t *OC, unsigned &Offset,
OC[Offset + 0], OC[Offset + 1], OC[Offset + 2],
static_cast<const char *>(Prologue ? "sub" : "add"), Imm);
- ++Offset, ++Offset, ++Offset;
+ Offset += 3;
return false;
}
@@ -452,7 +455,7 @@ bool Decoder::opcode_11111010(const uint8_t *OC, unsigned &Offset,
OC[Offset + 0], OC[Offset + 1], OC[Offset + 2], OC[Offset + 3],
static_cast<const char *>(Prologue ? "sub" : "add"), Imm);
- ++Offset, ++Offset, ++Offset, ++Offset;
+ Offset += 4;
return false;
}
@@ -567,9 +570,14 @@ bool Decoder::dumpXDataRecord(const COFFObjectFile &COFF,
if (!Symbol)
Symbol = getSymbol(COFF, Address, /*FunctionOnly=*/true);
- ErrorOr<StringRef> Name = Symbol->getName();
- if (std::error_code EC = Name.getError())
- report_fatal_error(EC.message());
+ Expected<StringRef> Name = Symbol->getName();
+ if (!Name) {
+ std::string Buf;
+ llvm::raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(Name.takeError(), OS, "");
+ OS.flush();
+ report_fatal_error(Buf);
+ }
ListScope EHS(SW, "ExceptionHandler");
SW.printString("Routine", formatSymbol(*Name, Address));
@@ -601,13 +609,23 @@ bool Decoder::dumpUnpackedEntry(const COFFObjectFile &COFF,
StringRef FunctionName;
uint64_t FunctionAddress;
if (Function) {
- ErrorOr<StringRef> FunctionNameOrErr = Function->getName();
- if (std::error_code EC = FunctionNameOrErr.getError())
- report_fatal_error(EC.message());
+ Expected<StringRef> FunctionNameOrErr = Function->getName();
+ if (!FunctionNameOrErr) {
+ std::string Buf;
+ llvm::raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(FunctionNameOrErr.takeError(), OS, "");
+ OS.flush();
+ report_fatal_error(Buf);
+ }
FunctionName = *FunctionNameOrErr;
- ErrorOr<uint64_t> FunctionAddressOrErr = Function->getAddress();
- if (std::error_code EC = FunctionAddressOrErr.getError())
- report_fatal_error(EC.message());
+ Expected<uint64_t> FunctionAddressOrErr = Function->getAddress();
+ if (!FunctionAddressOrErr) {
+ std::string Buf;
+ llvm::raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(FunctionAddressOrErr.takeError(), OS, "");
+ OS.flush();
+ report_fatal_error(Buf);
+ }
FunctionAddress = *FunctionAddressOrErr;
} else {
const pe32_header *PEHeader;
@@ -619,20 +637,33 @@ bool Decoder::dumpUnpackedEntry(const COFFObjectFile &COFF,
SW.printString("Function", formatSymbol(FunctionName, FunctionAddress));
if (XDataRecord) {
- ErrorOr<StringRef> Name = XDataRecord->getName();
- if (std::error_code EC = Name.getError())
- report_fatal_error(EC.message());
+ Expected<StringRef> Name = XDataRecord->getName();
+ if (!Name) {
+ std::string Buf;
+ llvm::raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(Name.takeError(), OS, "");
+ OS.flush();
+ report_fatal_error(Buf);
+ }
- ErrorOr<uint64_t> AddressOrErr = XDataRecord->getAddress();
- if (std::error_code EC = AddressOrErr.getError())
- report_fatal_error(EC.message());
+ Expected<uint64_t> AddressOrErr = XDataRecord->getAddress();
+ if (!AddressOrErr) {
+ std::string Buf;
+ llvm::raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(AddressOrErr.takeError(), OS, "");
+ OS.flush();
+ report_fatal_error(Buf);
+ }
uint64_t Address = *AddressOrErr;
SW.printString("ExceptionRecord", formatSymbol(*Name, Address));
- ErrorOr<section_iterator> SIOrErr = XDataRecord->getSection();
- if (!SIOrErr)
+ Expected<section_iterator> SIOrErr = XDataRecord->getSection();
+ if (!SIOrErr) {
+ // TODO: Actually report errors helpfully.
+ consumeError(SIOrErr.takeError());
return false;
+ }
section_iterator SI = *SIOrErr;
return dumpXDataRecord(COFF, *SI, FunctionAddress, Address);
@@ -668,11 +699,23 @@ bool Decoder::dumpPackedEntry(const object::COFFObjectFile &COFF,
StringRef FunctionName;
uint64_t FunctionAddress;
if (Function) {
- ErrorOr<StringRef> FunctionNameOrErr = Function->getName();
- if (std::error_code EC = FunctionNameOrErr.getError())
- report_fatal_error(EC.message());
+ Expected<StringRef> FunctionNameOrErr = Function->getName();
+ if (!FunctionNameOrErr) {
+ std::string Buf;
+ llvm::raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(FunctionNameOrErr.takeError(), OS, "");
+ OS.flush();
+ report_fatal_error(Buf);
+ }
FunctionName = *FunctionNameOrErr;
- ErrorOr<uint64_t> FunctionAddressOrErr = Function->getAddress();
+ Expected<uint64_t> FunctionAddressOrErr = Function->getAddress();
+ if (!FunctionAddressOrErr) {
+ std::string Buf;
+ llvm::raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(FunctionAddressOrErr.takeError(), OS, "");
+ OS.flush();
+ report_fatal_error(Buf);
+ }
FunctionAddress = *FunctionAddressOrErr;
} else {
const pe32_header *PEHeader;
diff --git a/tools/llvm-readobj/ARMWinEHPrinter.h b/tools/llvm-readobj/ARMWinEHPrinter.h
index 274ef114841c1..95f521702268a 100644
--- a/tools/llvm-readobj/ARMWinEHPrinter.h
+++ b/tools/llvm-readobj/ARMWinEHPrinter.h
@@ -10,9 +10,9 @@
#ifndef LLVM_TOOLS_LLVM_READOBJ_ARMWINEHPRINTER_H
#define LLVM_TOOLS_LLVM_READOBJ_ARMWINEHPRINTER_H
-#include "StreamWriter.h"
#include "llvm/Object/COFF.h"
#include "llvm/Support/ErrorOr.h"
+#include "llvm/Support/ScopedPrinter.h"
namespace llvm {
namespace ARM {
@@ -22,7 +22,7 @@ class RuntimeFunction;
class Decoder {
static const size_t PDataEntrySize;
- StreamWriter &SW;
+ ScopedPrinter &SW;
raw_ostream &OS;
struct RingEntry {
@@ -107,7 +107,7 @@ class Decoder {
const object::SectionRef Section);
public:
- Decoder(StreamWriter &SW) : SW(SW), OS(SW.getOStream()) {}
+ Decoder(ScopedPrinter &SW) : SW(SW), OS(SW.getOStream()) {}
std::error_code dumpProcedureData(const object::COFFObjectFile &COFF);
};
}
diff --git a/tools/llvm-readobj/CMakeLists.txt b/tools/llvm-readobj/CMakeLists.txt
index a06b678780cb4..7477f5b035d0c 100644
--- a/tools/llvm-readobj/CMakeLists.txt
+++ b/tools/llvm-readobj/CMakeLists.txt
@@ -1,6 +1,8 @@
set(LLVM_LINK_COMPONENTS
+ DebugInfoCodeView
Object
Support
+ DebugInfoCodeView
)
add_llvm_tool(llvm-readobj
@@ -13,6 +15,5 @@ add_llvm_tool(llvm-readobj
llvm-readobj.cpp
MachODumper.cpp
ObjDumper.cpp
- StreamWriter.cpp
Win64EHDumper.cpp
)
diff --git a/tools/llvm-readobj/COFFDumper.cpp b/tools/llvm-readobj/COFFDumper.cpp
index d44da0d574661..348f5b4355643 100644
--- a/tools/llvm-readobj/COFFDumper.cpp
+++ b/tools/llvm-readobj/COFFDumper.cpp
@@ -12,16 +12,28 @@
///
//===----------------------------------------------------------------------===//
-#include "llvm-readobj.h"
#include "ARMWinEHPrinter.h"
+#include "CodeView.h"
#include "Error.h"
#include "ObjDumper.h"
#include "StackMapPrinter.h"
-#include "StreamWriter.h"
#include "Win64EHDumper.h"
+#include "llvm-readobj.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
+#include "llvm/DebugInfo/CodeView/ByteStream.h"
+#include "llvm/DebugInfo/CodeView/CodeView.h"
+#include "llvm/DebugInfo/CodeView/Line.h"
+#include "llvm/DebugInfo/CodeView/MemoryTypeTableBuilder.h"
+#include "llvm/DebugInfo/CodeView/RecordSerialization.h"
+#include "llvm/DebugInfo/CodeView/SymbolDumpDelegate.h"
+#include "llvm/DebugInfo/CodeView/SymbolDumper.h"
+#include "llvm/DebugInfo/CodeView/SymbolRecord.h"
+#include "llvm/DebugInfo/CodeView/TypeDumper.h"
+#include "llvm/DebugInfo/CodeView/TypeIndex.h"
+#include "llvm/DebugInfo/CodeView/TypeRecord.h"
+#include "llvm/DebugInfo/CodeView/TypeStreamMerger.h"
#include "llvm/Object/COFF.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Support/COFF.h"
@@ -29,6 +41,7 @@
#include "llvm/Support/Compiler.h"
#include "llvm/Support/DataExtractor.h"
#include "llvm/Support/Format.h"
+#include "llvm/Support/ScopedPrinter.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/Win64EH.h"
#include "llvm/Support/raw_ostream.h"
@@ -39,16 +52,18 @@
using namespace llvm;
using namespace llvm::object;
+using namespace llvm::codeview;
+using namespace llvm::support;
using namespace llvm::Win64EH;
namespace {
class COFFDumper : public ObjDumper {
public:
- COFFDumper(const llvm::object::COFFObjectFile *Obj, StreamWriter& Writer)
- : ObjDumper(Writer)
- , Obj(Obj) {
- }
+ friend class COFFObjectDumpDelegate;
+ COFFDumper(const llvm::object::COFFObjectFile *Obj, ScopedPrinter &Writer)
+ : ObjDumper(Writer), Obj(Obj),
+ CVTD(&Writer, opts::CodeViewSubsectionBytes) {}
void printFileHeaders() override;
void printSections() override;
@@ -60,11 +75,15 @@ public:
void printCOFFExports() override;
void printCOFFDirectives() override;
void printCOFFBaseReloc() override;
+ void printCOFFDebugDirectory() override;
void printCodeViewDebugInfo() override;
+ void
+ mergeCodeViewTypes(llvm::codeview::MemoryTypeTableBuilder &CVTypes) override;
void printStackMap() const override;
private:
void printSymbol(const SymbolRef &Sym);
- void printRelocation(const SectionRef &Section, const RelocationRef &Reloc);
+ void printRelocation(const SectionRef &Section, const RelocationRef &Reloc,
+ uint64_t Bias = 0);
void printDataDirectory(uint32_t Index, const std::string &FieldName);
void printDOSHeader(const dos_header *DH);
@@ -72,11 +91,33 @@ private:
void printBaseOfDataField(const pe32_header *Hdr);
void printBaseOfDataField(const pe32plus_header *Hdr);
- void printCodeViewSection(const SectionRef &Section);
+ void printCodeViewSymbolSection(StringRef SectionName, const SectionRef &Section);
+ void printCodeViewTypeSection(StringRef SectionName, const SectionRef &Section);
+ StringRef getTypeName(TypeIndex Ty);
+ StringRef getFileNameForFileOffset(uint32_t FileOffset);
+ void printFileNameForOffset(StringRef Label, uint32_t FileOffset);
+ void printTypeIndex(StringRef FieldName, TypeIndex TI) {
+ // Forward to CVTypeDumper for simplicity.
+ CVTD.printTypeIndex(FieldName, TI);
+ }
void printCodeViewSymbolsSubsection(StringRef Subsection,
const SectionRef &Section,
- uint32_t Offset);
+ StringRef SectionContents);
+
+ void printCodeViewFileChecksums(StringRef Subsection);
+
+ void printCodeViewInlineeLines(StringRef Subsection);
+
+ void printRelocatedField(StringRef Label, const coff_section *Sec,
+ uint32_t RelocOffset, uint32_t Offset,
+ StringRef *RelocSym = nullptr);
+
+ void printBinaryBlockWithRelocs(StringRef Label, const SectionRef &Sec,
+ StringRef SectionContents, StringRef Block);
+
+ /// Given a .debug$S section, find the string table and file checksum table.
+ void initializeFileAndStringTables(StringRef Data);
void cacheRelocations();
@@ -84,6 +125,9 @@ private:
SymbolRef &Sym);
std::error_code resolveSymbolName(const coff_section *Section,
uint64_t Offset, StringRef &Name);
+ std::error_code resolveSymbolName(const coff_section *Section,
+ StringRef SectionContents,
+ const void *RelocPtr, StringRef &Name);
void printImportedSymbols(iterator_range<imported_symbol_iterator> Range);
void printDelayImportedSymbols(
const DelayImportDirectoryEntryRef &I,
@@ -94,17 +138,56 @@ private:
const llvm::object::COFFObjectFile *Obj;
bool RelocCached = false;
RelocMapTy RelocMap;
- StringRef CVFileIndexToStringOffsetTable;
+ StringRef CVFileChecksumTable;
StringRef CVStringTable;
+
+ CVTypeDumper CVTD;
};
-} // namespace
+class COFFObjectDumpDelegate : public SymbolDumpDelegate {
+public:
+ COFFObjectDumpDelegate(COFFDumper &CD, const SectionRef &SR,
+ const COFFObjectFile *Obj, StringRef SectionContents)
+ : CD(CD), SR(SR), SectionContents(SectionContents) {
+ Sec = Obj->getCOFFSection(SR);
+ }
+
+ uint32_t getRecordOffset(ArrayRef<uint8_t> Record) override {
+ return Record.data() - SectionContents.bytes_begin();
+ }
+
+ void printRelocatedField(StringRef Label, uint32_t RelocOffset,
+ uint32_t Offset, StringRef *RelocSym) override {
+ CD.printRelocatedField(Label, Sec, RelocOffset, Offset, RelocSym);
+ }
+
+ void printBinaryBlockWithRelocs(StringRef Label,
+ ArrayRef<uint8_t> Block) override {
+ StringRef SBlock(reinterpret_cast<const char *>(Block.data()),
+ Block.size());
+ if (opts::CodeViewSubsectionBytes)
+ CD.printBinaryBlockWithRelocs(Label, SR, SectionContents, SBlock);
+ }
+
+ StringRef getFileNameForFileOffset(uint32_t FileOffset) override {
+ return CD.getFileNameForFileOffset(FileOffset);
+ }
+
+ StringRef getStringTable() override { return CD.CVStringTable; }
+
+private:
+ COFFDumper &CD;
+ const SectionRef &SR;
+ const coff_section *Sec;
+ StringRef SectionContents;
+};
+} // end namespace
namespace llvm {
std::error_code createCOFFDumper(const object::ObjectFile *Obj,
- StreamWriter &Writer,
+ ScopedPrinter &Writer,
std::unique_ptr<ObjDumper> &Result) {
const COFFObjectFile *COFFObj = dyn_cast<COFFObjectFile>(Obj);
if (!COFFObj)
@@ -122,15 +205,19 @@ std::error_code COFFDumper::resolveSymbol(const coff_section *Section,
uint64_t Offset, SymbolRef &Sym) {
cacheRelocations();
const auto &Relocations = RelocMap[Section];
+ auto SymI = Obj->symbol_end();
for (const auto &Relocation : Relocations) {
uint64_t RelocationOffset = Relocation.getOffset();
if (RelocationOffset == Offset) {
- Sym = *Relocation.getSymbol();
- return readobj_error::success;
+ SymI = Relocation.getSymbol();
+ break;
}
}
- return readobj_error::unknown_symbol;
+ if (SymI == Obj->symbol_end())
+ return readobj_error::unknown_symbol;
+ Sym = *SymI;
+ return readobj_error::success;
}
// Given a section and an offset into this section the function returns the name
@@ -141,13 +228,62 @@ std::error_code COFFDumper::resolveSymbolName(const coff_section *Section,
SymbolRef Symbol;
if (std::error_code EC = resolveSymbol(Section, Offset, Symbol))
return EC;
- ErrorOr<StringRef> NameOrErr = Symbol.getName();
- if (std::error_code EC = NameOrErr.getError())
- return EC;
+ Expected<StringRef> NameOrErr = Symbol.getName();
+ if (!NameOrErr)
+ return errorToErrorCode(NameOrErr.takeError());
Name = *NameOrErr;
return std::error_code();
}
+// Helper for when you have a pointer to real data and you want to know about
+// relocations against it.
+std::error_code COFFDumper::resolveSymbolName(const coff_section *Section,
+ StringRef SectionContents,
+ const void *RelocPtr,
+ StringRef &Name) {
+ assert(SectionContents.data() < RelocPtr &&
+ RelocPtr < SectionContents.data() + SectionContents.size() &&
+ "pointer to relocated object is not in section");
+ uint64_t Offset = ptrdiff_t(reinterpret_cast<const char *>(RelocPtr) -
+ SectionContents.data());
+ return resolveSymbolName(Section, Offset, Name);
+}
+
+void COFFDumper::printRelocatedField(StringRef Label, const coff_section *Sec,
+ uint32_t RelocOffset, uint32_t Offset,
+ StringRef *RelocSym) {
+ StringRef SymStorage;
+ StringRef &Symbol = RelocSym ? *RelocSym : SymStorage;
+ if (!resolveSymbolName(Sec, RelocOffset, Symbol))
+ W.printSymbolOffset(Label, Symbol, Offset);
+ else
+ W.printHex(Label, RelocOffset);
+}
+
+void COFFDumper::printBinaryBlockWithRelocs(StringRef Label,
+ const SectionRef &Sec,
+ StringRef SectionContents,
+ StringRef Block) {
+ W.printBinaryBlock(Label, Block);
+
+ assert(SectionContents.begin() < Block.begin() &&
+ SectionContents.end() >= Block.end() &&
+ "Block is not contained in SectionContents");
+ uint64_t OffsetStart = Block.data() - SectionContents.data();
+ uint64_t OffsetEnd = OffsetStart + Block.size();
+
+ W.flush();
+ cacheRelocations();
+ ListScope D(W, "BlockRelocations");
+ const coff_section *Section = Obj->getCOFFSection(Sec);
+ const auto &Relocations = RelocMap[Section];
+ for (const auto &Relocation : Relocations) {
+ uint64_t RelocationOffset = Relocation.getOffset();
+ if (OffsetStart <= RelocationOffset && RelocationOffset < OffsetEnd)
+ printRelocation(Sec, Relocation, OffsetStart);
+ }
+}
+
static const EnumEntry<COFF::MachineTypes> ImageFileMachineType[] = {
LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_UNKNOWN ),
LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_AM33 ),
@@ -324,6 +460,26 @@ static const EnumEntry<COFF::COMDATType> ImageCOMDATSelect[] = {
{ "Newest" , COFF::IMAGE_COMDAT_SELECT_NEWEST }
};
+static const EnumEntry<COFF::DebugType> ImageDebugType[] = {
+ { "Unknown" , COFF::IMAGE_DEBUG_TYPE_UNKNOWN },
+ { "COFF" , COFF::IMAGE_DEBUG_TYPE_COFF },
+ { "CodeView" , COFF::IMAGE_DEBUG_TYPE_CODEVIEW },
+ { "FPO" , COFF::IMAGE_DEBUG_TYPE_FPO },
+ { "Misc" , COFF::IMAGE_DEBUG_TYPE_MISC },
+ { "Exception" , COFF::IMAGE_DEBUG_TYPE_EXCEPTION },
+ { "Fixup" , COFF::IMAGE_DEBUG_TYPE_FIXUP },
+ { "OmapToSrc" , COFF::IMAGE_DEBUG_TYPE_OMAP_TO_SRC },
+ { "OmapFromSrc", COFF::IMAGE_DEBUG_TYPE_OMAP_FROM_SRC },
+ { "Borland" , COFF::IMAGE_DEBUG_TYPE_BORLAND },
+ { "Reserved10" , COFF::IMAGE_DEBUG_TYPE_RESERVED10 },
+ { "CLSID" , COFF::IMAGE_DEBUG_TYPE_CLSID },
+ { "VCFeature" , COFF::IMAGE_DEBUG_TYPE_VC_FEATURE },
+ { "POGO" , COFF::IMAGE_DEBUG_TYPE_POGO },
+ { "ILTCG" , COFF::IMAGE_DEBUG_TYPE_ILTCG },
+ { "MPX" , COFF::IMAGE_DEBUG_TYPE_MPX },
+ { "Repro" , COFF::IMAGE_DEBUG_TYPE_REPRO },
+};
+
static const EnumEntry<COFF::WeakExternalCharacteristics>
WeakExternalCharacteristics[] = {
{ "NoLibrary", COFF::IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY },
@@ -331,6 +487,35 @@ WeakExternalCharacteristics[] = {
{ "Alias" , COFF::IMAGE_WEAK_EXTERN_SEARCH_ALIAS }
};
+static const EnumEntry<uint32_t> SubSectionTypes[] = {
+ LLVM_READOBJ_ENUM_CLASS_ENT(ModuleSubstreamKind, Symbols),
+ LLVM_READOBJ_ENUM_CLASS_ENT(ModuleSubstreamKind, Lines),
+ LLVM_READOBJ_ENUM_CLASS_ENT(ModuleSubstreamKind, StringTable),
+ LLVM_READOBJ_ENUM_CLASS_ENT(ModuleSubstreamKind, FileChecksums),
+ LLVM_READOBJ_ENUM_CLASS_ENT(ModuleSubstreamKind, FrameData),
+ LLVM_READOBJ_ENUM_CLASS_ENT(ModuleSubstreamKind, InlineeLines),
+ LLVM_READOBJ_ENUM_CLASS_ENT(ModuleSubstreamKind, CrossScopeImports),
+ LLVM_READOBJ_ENUM_CLASS_ENT(ModuleSubstreamKind, CrossScopeExports),
+ LLVM_READOBJ_ENUM_CLASS_ENT(ModuleSubstreamKind, ILLines),
+ LLVM_READOBJ_ENUM_CLASS_ENT(ModuleSubstreamKind, FuncMDTokenMap),
+ LLVM_READOBJ_ENUM_CLASS_ENT(ModuleSubstreamKind, TypeMDTokenMap),
+ LLVM_READOBJ_ENUM_CLASS_ENT(ModuleSubstreamKind, MergedAssemblyInput),
+ LLVM_READOBJ_ENUM_CLASS_ENT(ModuleSubstreamKind, CoffSymbolRVA),
+};
+
+static const EnumEntry<uint32_t> FrameDataFlags[] = {
+ LLVM_READOBJ_ENUM_ENT(FrameData, HasSEH),
+ LLVM_READOBJ_ENUM_ENT(FrameData, HasEH),
+ LLVM_READOBJ_ENUM_ENT(FrameData, IsFunctionStart),
+};
+
+static const EnumEntry<uint8_t> FileChecksumKindNames[] = {
+ LLVM_READOBJ_ENUM_CLASS_ENT(FileChecksumKind, None),
+ LLVM_READOBJ_ENUM_CLASS_ENT(FileChecksumKind, MD5),
+ LLVM_READOBJ_ENUM_CLASS_ENT(FileChecksumKind, SHA1),
+ LLVM_READOBJ_ENUM_CLASS_ENT(FileChecksumKind, SHA256),
+};
+
template <typename T>
static std::error_code getSymbolAuxData(const COFFObjectFile *Obj,
COFFSymbolRef Symbol,
@@ -463,8 +648,42 @@ void COFFDumper::printPEHeader(const PEHeader *Hdr) {
"DelayImportDescriptor", "CLRRuntimeHeader", "Reserved"
};
- for (uint32_t i = 0; i < Hdr->NumberOfRvaAndSize; ++i) {
+ for (uint32_t i = 0; i < Hdr->NumberOfRvaAndSize; ++i)
printDataDirectory(i, directory[i]);
+ }
+}
+
+void COFFDumper::printCOFFDebugDirectory() {
+ ListScope LS(W, "DebugDirectory");
+ for (const debug_directory &D : Obj->debug_directories()) {
+ char FormattedTime[20] = {};
+ time_t TDS = D.TimeDateStamp;
+ strftime(FormattedTime, 20, "%Y-%m-%d %H:%M:%S", gmtime(&TDS));
+ DictScope S(W, "DebugEntry");
+ W.printHex("Characteristics", D.Characteristics);
+ W.printHex("TimeDateStamp", FormattedTime, D.TimeDateStamp);
+ W.printHex("MajorVersion", D.MajorVersion);
+ W.printHex("MinorVersion", D.MinorVersion);
+ W.printEnum("Type", D.Type, makeArrayRef(ImageDebugType));
+ W.printHex("SizeOfData", D.SizeOfData);
+ W.printHex("AddressOfRawData", D.AddressOfRawData);
+ W.printHex("PointerToRawData", D.PointerToRawData);
+ if (D.Type == COFF::IMAGE_DEBUG_TYPE_CODEVIEW) {
+ const debug_pdb_info *PDBInfo;
+ StringRef PDBFileName;
+ error(Obj->getDebugPDBInfo(&D, PDBInfo, PDBFileName));
+ DictScope PDBScope(W, "PDBInfo");
+ W.printHex("PDBSignature", PDBInfo->Signature);
+ W.printBinary("PDBGUID", makeArrayRef(PDBInfo->Guid));
+ W.printNumber("PDBAge", PDBInfo->Age);
+ W.printString("PDBFileName", PDBFileName);
+ } else {
+ // FIXME: Type values of 12 and 13 are commonly observed but are not in
+ // the documented type enum. Figure out what they mean.
+ ArrayRef<uint8_t> RawData;
+ error(
+ Obj->getRvaAndSizeAsBytes(D.AddressOfRawData, D.SizeOfData, RawData));
+ W.printBinaryBlock("RawData", RawData);
}
}
}
@@ -476,111 +695,180 @@ void COFFDumper::printBaseOfDataField(const pe32_header *Hdr) {
void COFFDumper::printBaseOfDataField(const pe32plus_header *) {}
void COFFDumper::printCodeViewDebugInfo() {
+ // Print types first to build CVUDTNames, then print symbols.
+ for (const SectionRef &S : Obj->sections()) {
+ StringRef SectionName;
+ error(S.getName(SectionName));
+ if (SectionName == ".debug$T")
+ printCodeViewTypeSection(SectionName, S);
+ }
for (const SectionRef &S : Obj->sections()) {
- StringRef SecName;
- error(S.getName(SecName));
- if (SecName == ".debug$S")
- printCodeViewSection(S);
+ StringRef SectionName;
+ error(S.getName(SectionName));
+ if (SectionName == ".debug$S")
+ printCodeViewSymbolSection(SectionName, S);
}
}
-void COFFDumper::printCodeViewSection(const SectionRef &Section) {
- StringRef Data;
- error(Section.getContents(Data));
+void COFFDumper::initializeFileAndStringTables(StringRef Data) {
+ while (!Data.empty() && (CVFileChecksumTable.data() == nullptr ||
+ CVStringTable.data() == nullptr)) {
+ // The section consists of a number of subsection in the following format:
+ // |SubSectionType|SubSectionSize|Contents...|
+ uint32_t SubType, SubSectionSize;
+ error(consume(Data, SubType));
+ error(consume(Data, SubSectionSize));
+ if (SubSectionSize > Data.size())
+ return error(object_error::parse_failed);
+ switch (ModuleSubstreamKind(SubType)) {
+ case ModuleSubstreamKind::FileChecksums:
+ CVFileChecksumTable = Data.substr(0, SubSectionSize);
+ break;
+ case ModuleSubstreamKind::StringTable:
+ CVStringTable = Data.substr(0, SubSectionSize);
+ break;
+ default:
+ break;
+ }
+ uint32_t PaddedSize = alignTo(SubSectionSize, 4);
+ if (PaddedSize > Data.size())
+ error(object_error::parse_failed);
+ Data = Data.drop_front(PaddedSize);
+ }
+}
+
+void COFFDumper::printCodeViewSymbolSection(StringRef SectionName,
+ const SectionRef &Section) {
+ StringRef SectionContents;
+ error(Section.getContents(SectionContents));
+ StringRef Data = SectionContents;
SmallVector<StringRef, 10> FunctionNames;
StringMap<StringRef> FunctionLineTables;
ListScope D(W, "CodeViewDebugInfo");
- {
- // FIXME: Add more offset correctness checks.
- DataExtractor DE(Data, true, 4);
- uint32_t Offset = 0,
- Magic = DE.getU32(&Offset);
- W.printHex("Magic", Magic);
- if (Magic != COFF::DEBUG_SECTION_MAGIC) {
- error(object_error::parse_failed);
- return;
- }
+ // Print the section to allow correlation with printSections.
+ W.printNumber("Section", SectionName, Obj->getSectionID(Section));
+
+ uint32_t Magic;
+ error(consume(Data, Magic));
+ W.printHex("Magic", Magic);
+ if (Magic != COFF::DEBUG_SECTION_MAGIC)
+ return error(object_error::parse_failed);
+
+ initializeFileAndStringTables(Data);
+
+ // TODO: Convert this over to using ModuleSubstreamVisitor.
+ while (!Data.empty()) {
+ // The section consists of a number of subsection in the following format:
+ // |SubSectionType|SubSectionSize|Contents...|
+ uint32_t SubType, SubSectionSize;
+ error(consume(Data, SubType));
+ error(consume(Data, SubSectionSize));
+
+ ListScope S(W, "Subsection");
+ W.printEnum("SubSectionType", SubType, makeArrayRef(SubSectionTypes));
+ W.printHex("SubSectionSize", SubSectionSize);
+
+ // Get the contents of the subsection.
+ if (SubSectionSize > Data.size())
+ return error(object_error::parse_failed);
+ StringRef Contents = Data.substr(0, SubSectionSize);
+
+ // Add SubSectionSize to the current offset and align that offset to find
+ // the next subsection.
+ size_t SectionOffset = Data.data() - SectionContents.data();
+ size_t NextOffset = SectionOffset + SubSectionSize;
+ NextOffset = alignTo(NextOffset, 4);
+ if (NextOffset > SectionContents.size())
+ return error(object_error::parse_failed);
+ Data = SectionContents.drop_front(NextOffset);
+
+ // Optionally print the subsection bytes in case our parsing gets confused
+ // later.
+ if (opts::CodeViewSubsectionBytes)
+ printBinaryBlockWithRelocs("SubSectionContents", Section, SectionContents,
+ Contents);
+
+ switch (ModuleSubstreamKind(SubType)) {
+ case ModuleSubstreamKind::Symbols:
+ printCodeViewSymbolsSubsection(Contents, Section, SectionContents);
+ break;
+
+ case ModuleSubstreamKind::InlineeLines:
+ printCodeViewInlineeLines(Contents);
+ break;
+
+ case ModuleSubstreamKind::FileChecksums:
+ printCodeViewFileChecksums(Contents);
+ break;
- bool Finished = false;
- while (DE.isValidOffset(Offset) && !Finished) {
- // The section consists of a number of subsection in the following format:
- // |Type|PayloadSize|Payload...|
- uint32_t SubSectionType = DE.getU32(&Offset),
- PayloadSize = DE.getU32(&Offset);
- ListScope S(W, "Subsection");
- W.printHex("Type", SubSectionType);
- W.printHex("PayloadSize", PayloadSize);
- if (PayloadSize > Data.size() - Offset) {
+ case ModuleSubstreamKind::Lines: {
+ // Holds a PC to file:line table. Some data to parse this subsection is
+ // stored in the other subsections, so just check sanity and store the
+ // pointers for deferred processing.
+
+ if (SubSectionSize < 12) {
+ // There should be at least three words to store two function
+ // relocations and size of the code.
error(object_error::parse_failed);
return;
}
- StringRef Contents = Data.substr(Offset, PayloadSize);
- if (opts::CodeViewSubsectionBytes) {
- // Print the raw contents to simplify debugging if anything goes wrong
- // afterwards.
- W.printBinaryBlock("Contents", Contents);
+ StringRef LinkageName;
+ error(resolveSymbolName(Obj->getCOFFSection(Section), SectionOffset,
+ LinkageName));
+ W.printString("LinkageName", LinkageName);
+ if (FunctionLineTables.count(LinkageName) != 0) {
+ // Saw debug info for this function already?
+ error(object_error::parse_failed);
+ return;
}
- switch (SubSectionType) {
- case COFF::DEBUG_SYMBOL_SUBSECTION:
- 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
- // stored in the other subsections, so just check sanity and store the
- // pointers for deferred processing.
-
- if (PayloadSize < 12) {
- // There should be at least three words to store two function
- // relocations and size of the code.
- error(object_error::parse_failed);
- return;
- }
-
- 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?
+ FunctionLineTables[LinkageName] = Contents;
+ FunctionNames.push_back(LinkageName);
+ break;
+ }
+ case ModuleSubstreamKind::FrameData: {
+ // First four bytes is a relocation against the function.
+ const uint32_t *CodePtr;
+ error(consumeObject(Contents, CodePtr));
+ StringRef LinkageName;
+ error(resolveSymbolName(Obj->getCOFFSection(Section), SectionContents,
+ CodePtr, LinkageName));
+ W.printString("LinkageName", LinkageName);
+
+ // To find the active frame description, search this array for the
+ // smallest PC range that includes the current PC.
+ while (!Contents.empty()) {
+ const FrameData *FD;
+ error(consumeObject(Contents, FD));
+
+ if (FD->FrameFunc >= CVStringTable.size())
error(object_error::parse_failed);
- return;
- }
- FunctionLineTables[LinkageName] = Contents;
- FunctionNames.push_back(LinkageName);
- break;
+ StringRef FrameFunc =
+ CVStringTable.drop_front(FD->FrameFunc).split('\0').first;
+
+ DictScope S(W, "FrameData");
+ W.printHex("RvaStart", FD->RvaStart);
+ W.printHex("CodeSize", FD->CodeSize);
+ W.printHex("LocalSize", FD->LocalSize);
+ W.printHex("ParamsSize", FD->ParamsSize);
+ W.printHex("MaxStackSize", FD->MaxStackSize);
+ W.printString("FrameFunc", FrameFunc);
+ W.printHex("PrologSize", FD->PrologSize);
+ W.printHex("SavedRegsSize", FD->SavedRegsSize);
+ W.printFlags("Flags", FD->Flags, makeArrayRef(FrameDataFlags));
}
- case COFF::DEBUG_STRING_TABLE_SUBSECTION:
- if (PayloadSize == 0 || CVStringTable.data() != nullptr ||
- Contents.back() != '\0') {
- // Empty or duplicate or non-null-terminated subsection.
- error(object_error::parse_failed);
- return;
- }
- CVStringTable = Contents;
- break;
- case COFF::DEBUG_INDEX_SUBSECTION:
- // Holds the translation table from file indices
- // to offsets in the string table.
-
- if (PayloadSize == 0 ||
- CVFileIndexToStringOffsetTable.data() != nullptr) {
- // Empty or duplicate subsection.
- error(object_error::parse_failed);
- return;
- }
- CVFileIndexToStringOffsetTable = Contents;
- break;
- }
- Offset += PayloadSize;
+ break;
+ }
- // Align the reading pointer by 4.
- Offset += (-Offset) % 4;
+ // Do nothing for unrecognized subsections.
+ default:
+ break;
}
+ W.flush();
}
// Dump the line tables now that we've read all the subsections and know all
@@ -594,8 +882,7 @@ void COFFDumper::printCodeViewSection(const SectionRef &Section) {
uint32_t Offset = 6; // Skip relocations.
uint16_t Flags = DE.getU16(&Offset);
W.printHex("Flags", Flags);
- bool HasColumnInformation =
- Flags & COFF::DEBUG_LINE_TABLES_HAVE_COLUMN_RECORDS;
+ bool HasColumnInformation = Flags & codeview::LineFlags::HaveColumns;
uint32_t FunctionSize = DE.getU32(&Offset);
W.printHex("CodeSize", FunctionSize);
while (DE.isValidOffset(Offset)) {
@@ -615,28 +902,8 @@ void COFFDumper::printCodeViewSection(const SectionRef &Section) {
return;
}
- uint32_t FilenameOffset;
- {
- DataExtractor SDE(CVFileIndexToStringOffsetTable, true, 4);
- uint32_t OffsetInSDE = OffsetInIndex;
- if (!SDE.isValidOffset(OffsetInSDE)) {
- error(object_error::parse_failed);
- return;
- }
- FilenameOffset = SDE.getU32(&OffsetInSDE);
- }
-
- if (FilenameOffset == 0 || FilenameOffset + 1 >= CVStringTable.size() ||
- CVStringTable.data()[FilenameOffset - 1] != '\0') {
- // Each string in an F3 subsection should be preceded by a null
- // character.
- error(object_error::parse_failed);
- return;
- }
-
- StringRef Filename(CVStringTable.data() + FilenameOffset);
ListScope S(W, "FilenameSegment");
- W.printString("Filename", Filename);
+ printFileNameForOffset("Filename", OffsetInIndex);
for (unsigned LineIdx = 0;
LineIdx != NumLines && DE.isValidOffset(Offset); ++LineIdx) {
// Then go the (PC, LineNumber) pairs. The line number is stored in the
@@ -649,14 +916,15 @@ void COFFDumper::printCodeViewSection(const SectionRef &Section) {
char Buffer[32];
format("+0x%X", PC).snprint(Buffer, 32);
ListScope PCScope(W, Buffer);
- uint32_t LineNumberStart = LineData & COFF::CVL_MaxLineNumber;
- uint32_t LineNumberEndDelta =
- (LineData >> COFF::CVL_LineNumberStartBits) &
- COFF::CVL_LineNumberEndDeltaMask;
- bool IsStatement = LineData & COFF::CVL_IsStatement;
- W.printNumber("LineNumberStart", LineNumberStart);
- W.printNumber("LineNumberEndDelta", LineNumberEndDelta);
- W.printBoolean("IsStatement", IsStatement);
+ LineInfo LI(LineData);
+ if (LI.isAlwaysStepInto())
+ W.printString("StepInto", StringRef("Always"));
+ else if (LI.isNeverStepInto())
+ W.printString("StepInto", StringRef("Never"));
+ else
+ W.printNumber("LineNumberStart", LI.getStartLine());
+ W.printNumber("LineNumberEndDelta", LI.getLineDelta());
+ W.printBoolean("IsStatement", LI.isStatement());
if (HasColumnInformation &&
ColumnDE.isValidOffsetForDataOfSize(ColumnOffset, 4)) {
uint16_t ColStart = ColumnDE.getU16(&ColumnOffset);
@@ -678,85 +946,156 @@ void COFFDumper::printCodeViewSection(const SectionRef &Section) {
void COFFDumper::printCodeViewSymbolsSubsection(StringRef Subsection,
const SectionRef &Section,
- uint32_t OffsetInSection) {
- if (Subsection.size() == 0) {
+ StringRef SectionContents) {
+ ArrayRef<uint8_t> BinaryData(Subsection.bytes_begin(),
+ Subsection.bytes_end());
+ auto CODD = llvm::make_unique<COFFObjectDumpDelegate>(*this, Section, Obj,
+ SectionContents);
+
+ CVSymbolDumper CVSD(W, CVTD, std::move(CODD), opts::CodeViewSubsectionBytes);
+ ByteStream<> Stream(BinaryData);
+ CVSymbolArray Symbols;
+ StreamReader Reader(Stream);
+ if (auto EC = Reader.readArray(Symbols, Reader.getLength())) {
+ consumeError(std::move(EC));
+ W.flush();
+ error(object_error::parse_failed);
+ }
+
+ if (!CVSD.dump(Symbols)) {
+ W.flush();
error(object_error::parse_failed);
- return;
}
- DataExtractor DE(Subsection, true, 4);
- uint32_t Offset = 0;
-
- // Function-level subsections have "procedure start" and "procedure end"
- // commands that should come in pairs and surround relevant info.
- bool InFunctionScope = false;
- while (DE.isValidOffset(Offset)) {
- // Read subsection segments one by one.
- uint16_t Size = DE.getU16(&Offset);
- // The section size includes the size of the type identifier.
- if (Size < 2 || !DE.isValidOffsetForDataOfSize(Offset, Size)) {
+ W.flush();
+}
+
+void COFFDumper::printCodeViewFileChecksums(StringRef Subsection) {
+ StringRef Data = Subsection;
+ while (!Data.empty()) {
+ DictScope S(W, "FileChecksum");
+ const FileChecksum *FC;
+ error(consumeObject(Data, FC));
+ if (FC->FileNameOffset >= CVStringTable.size())
error(object_error::parse_failed);
- return;
- }
- Size -= 2;
- uint16_t Type = DE.getU16(&Offset);
- switch (Type) {
- case COFF::DEBUG_SYMBOL_TYPE_PROC_START: {
- DictScope S(W, "ProcStart");
- if (InFunctionScope || Size < 36) {
- error(object_error::parse_failed);
- return;
- }
- InFunctionScope = true;
-
- // We're currently interested in a limited subset of fields in this
- // segment, just ignore the rest of the fields for now.
- uint8_t Unused[12];
- DE.getU8(&Offset, Unused, 12);
- uint32_t CodeSize = DE.getU32(&Offset);
- DE.getU8(&Offset, Unused, 12);
- StringRef SectionName;
- error(resolveSymbolName(Obj->getCOFFSection(Section),
- OffsetInSection + Offset, SectionName));
- Offset += 4;
- DE.getU8(&Offset, Unused, 3);
- StringRef DisplayName = DE.getCStr(&Offset);
- if (!DE.isValidOffset(Offset)) {
- error(object_error::parse_failed);
- return;
- }
- W.printString("DisplayName", DisplayName);
- W.printString("Section", SectionName);
- W.printHex("CodeSize", CodeSize);
+ StringRef Filename =
+ CVStringTable.drop_front(FC->FileNameOffset).split('\0').first;
+ W.printHex("Filename", Filename, FC->FileNameOffset);
+ W.printHex("ChecksumSize", FC->ChecksumSize);
+ W.printEnum("ChecksumKind", uint8_t(FC->ChecksumKind),
+ makeArrayRef(FileChecksumKindNames));
+ if (FC->ChecksumSize >= Data.size())
+ error(object_error::parse_failed);
+ StringRef ChecksumBytes = Data.substr(0, FC->ChecksumSize);
+ W.printBinary("ChecksumBytes", ChecksumBytes);
+ unsigned PaddedSize = alignTo(FC->ChecksumSize + sizeof(FileChecksum), 4) -
+ sizeof(FileChecksum);
+ if (PaddedSize > Data.size())
+ error(object_error::parse_failed);
+ Data = Data.drop_front(PaddedSize);
+ }
+}
- break;
- }
- case COFF::DEBUG_SYMBOL_TYPE_PROC_END: {
- W.startLine() << "ProcEnd\n";
- if (!InFunctionScope || Size > 0) {
- error(object_error::parse_failed);
- return;
+void COFFDumper::printCodeViewInlineeLines(StringRef Subsection) {
+ StringRef Data = Subsection;
+ uint32_t Signature;
+ error(consume(Data, Signature));
+ bool HasExtraFiles = Signature == unsigned(InlineeLinesSignature::ExtraFiles);
+
+ while (!Data.empty()) {
+ const InlineeSourceLine *ISL;
+ error(consumeObject(Data, ISL));
+ DictScope S(W, "InlineeSourceLine");
+ printTypeIndex("Inlinee", ISL->Inlinee);
+ printFileNameForOffset("FileID", ISL->FileID);
+ W.printNumber("SourceLineNum", ISL->SourceLineNum);
+
+ if (HasExtraFiles) {
+ uint32_t ExtraFileCount;
+ error(consume(Data, ExtraFileCount));
+ W.printNumber("ExtraFileCount", ExtraFileCount);
+ ListScope ExtraFiles(W, "ExtraFiles");
+ for (unsigned I = 0; I < ExtraFileCount; ++I) {
+ uint32_t FileID;
+ error(consume(Data, FileID));
+ printFileNameForOffset("FileID", FileID);
}
- InFunctionScope = false;
- break;
}
- default: {
- if (opts::CodeViewSubsectionBytes) {
- ListScope S(W, "Record");
- W.printHex("Size", Size);
- W.printHex("Type", Type);
-
- StringRef Contents = DE.getData().substr(Offset, Size);
- W.printBinaryBlock("Contents", Contents);
+ }
+}
+
+StringRef COFFDumper::getFileNameForFileOffset(uint32_t FileOffset) {
+ // The file checksum subsection should precede all references to it.
+ if (!CVFileChecksumTable.data() || !CVStringTable.data())
+ error(object_error::parse_failed);
+ // Check if the file checksum table offset is valid.
+ if (FileOffset >= CVFileChecksumTable.size())
+ error(object_error::parse_failed);
+
+ // The string table offset comes first before the file checksum.
+ StringRef Data = CVFileChecksumTable.drop_front(FileOffset);
+ uint32_t StringOffset;
+ error(consume(Data, StringOffset));
+
+ // Check if the string table offset is valid.
+ if (StringOffset >= CVStringTable.size())
+ error(object_error::parse_failed);
+
+ // Return the null-terminated string.
+ return CVStringTable.drop_front(StringOffset).split('\0').first;
+}
+
+void COFFDumper::printFileNameForOffset(StringRef Label, uint32_t FileOffset) {
+ W.printHex(Label, getFileNameForFileOffset(FileOffset), FileOffset);
+}
+
+void COFFDumper::mergeCodeViewTypes(MemoryTypeTableBuilder &CVTypes) {
+ for (const SectionRef &S : Obj->sections()) {
+ StringRef SectionName;
+ error(S.getName(SectionName));
+ if (SectionName == ".debug$T") {
+ StringRef Data;
+ error(S.getContents(Data));
+ uint32_t Magic;
+ error(consume(Data, Magic));
+ if (Magic != 4)
+ error(object_error::parse_failed);
+ ArrayRef<uint8_t> Bytes(reinterpret_cast<const uint8_t *>(Data.data()),
+ Data.size());
+ ByteStream<> Stream(Bytes);
+ CVTypeArray Types;
+ StreamReader Reader(Stream);
+ if (auto EC = Reader.readArray(Types, Reader.getLength())) {
+ consumeError(std::move(EC));
+ W.flush();
+ error(object_error::parse_failed);
}
- Offset += Size;
- break;
- }
+ if (!mergeTypeStreams(CVTypes, Types))
+ return error(object_error::parse_failed);
}
}
+}
- if (InFunctionScope)
- error(object_error::parse_failed);
+void COFFDumper::printCodeViewTypeSection(StringRef SectionName,
+ const SectionRef &Section) {
+ ListScope D(W, "CodeViewTypes");
+ W.printNumber("Section", SectionName, Obj->getSectionID(Section));
+
+ StringRef Data;
+ error(Section.getContents(Data));
+ if (opts::CodeViewSubsectionBytes)
+ W.printBinaryBlock("Data", Data);
+
+ uint32_t Magic;
+ error(consume(Data, Magic));
+ W.printHex("Magic", Magic);
+ if (Magic != COFF::DEBUG_SECTION_MAGIC)
+ return error(object_error::parse_failed);
+
+ if (auto EC = CVTD.dump({Data.bytes_begin(), Data.bytes_end()})) {
+ W.flush();
+ error(llvm::errorToErrorCode(std::move(EC)));
+ }
}
void COFFDumper::printSections() {
@@ -838,16 +1177,16 @@ void COFFDumper::printRelocations() {
}
void COFFDumper::printRelocation(const SectionRef &Section,
- const RelocationRef &Reloc) {
- uint64_t Offset = Reloc.getOffset();
+ const RelocationRef &Reloc, uint64_t Bias) {
+ uint64_t Offset = Reloc.getOffset() - Bias;
uint64_t RelocType = Reloc.getType();
SmallString<32> RelocName;
StringRef SymbolName;
Reloc.getTypeName(RelocName);
symbol_iterator Symbol = Reloc.getSymbol();
if (Symbol != Obj->symbol_end()) {
- ErrorOr<StringRef> SymbolNameOrErr = Symbol->getName();
- error(SymbolNameOrErr.getError());
+ Expected<StringRef> SymbolNameOrErr = Symbol->getName();
+ error(errorToErrorCode(SymbolNameOrErr.takeError()));
SymbolName = *SymbolNameOrErr;
}
@@ -1190,3 +1529,18 @@ void COFFDumper::printStackMap() const {
prettyPrintStackMap(llvm::outs(),
StackMapV1Parser<support::big>(StackMapContentsArray));
}
+
+void llvm::dumpCodeViewMergedTypes(
+ ScopedPrinter &Writer, llvm::codeview::MemoryTypeTableBuilder &CVTypes) {
+ // Flatten it first, then run our dumper on it.
+ ListScope S(Writer, "MergedTypeStream");
+ SmallString<0> Buf;
+ CVTypes.ForEachRecord([&](TypeIndex TI, StringRef Record) {
+ Buf.append(Record.begin(), Record.end());
+ });
+ CVTypeDumper CVTD(&Writer, opts::CodeViewSubsectionBytes);
+ if (auto EC = CVTD.dump({Buf.str().bytes_begin(), Buf.str().bytes_end()})) {
+ Writer.flush();
+ error(llvm::errorToErrorCode(std::move(EC)));
+ }
+}
diff --git a/tools/llvm-readobj/CodeView.h b/tools/llvm-readobj/CodeView.h
new file mode 100644
index 0000000000000..cf713962eb7fa
--- /dev/null
+++ b/tools/llvm-readobj/CodeView.h
@@ -0,0 +1,54 @@
+//===-- CodeView.h - On-disk record types for CodeView ----------*- 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 provides data structures useful for consuming on-disk
+/// CodeView. It is based on information published by Microsoft at
+/// https://github.com/Microsoft/microsoft-pdb/.
+///
+//===----------------------------------------------------------------------===//
+
+// FIXME: Find a home for this in include/llvm/DebugInfo/CodeView/.
+
+#ifndef LLVM_READOBJ_CODEVIEW_H
+#define LLVM_READOBJ_CODEVIEW_H
+
+#include "llvm/DebugInfo/CodeView/CodeView.h"
+#include "llvm/DebugInfo/CodeView/TypeIndex.h"
+#include "llvm/Support/Endian.h"
+
+namespace llvm {
+namespace codeview {
+
+using llvm::support::ulittle16_t;
+using llvm::support::ulittle32_t;
+
+/// Data in the the SUBSEC_FRAMEDATA subection.
+struct FrameData {
+ ulittle32_t RvaStart;
+ ulittle32_t CodeSize;
+ ulittle32_t LocalSize;
+ ulittle32_t ParamsSize;
+ ulittle32_t MaxStackSize;
+ ulittle32_t FrameFunc;
+ ulittle16_t PrologSize;
+ ulittle16_t SavedRegsSize;
+ ulittle32_t Flags;
+ enum : uint32_t {
+ HasSEH = 1 << 0,
+ HasEH = 1 << 1,
+ IsFunctionStart = 1 << 2,
+ };
+};
+
+
+} // namespace codeview
+} // namespace llvm
+
+#endif // LLVM_READOBJ_CODEVIEW_H
diff --git a/tools/llvm-readobj/ELFDumper.cpp b/tools/llvm-readobj/ELFDumper.cpp
index be84f3c0f1bba..06fbe8d3fccba 100644
--- a/tools/llvm-readobj/ELFDumper.cpp
+++ b/tools/llvm-readobj/ELFDumper.cpp
@@ -12,13 +12,12 @@
///
//===----------------------------------------------------------------------===//
-#include "llvm-readobj.h"
#include "ARMAttributeParser.h"
#include "ARMEHABIPrinter.h"
#include "Error.h"
#include "ObjDumper.h"
#include "StackMapPrinter.h"
-#include "StreamWriter.h"
+#include "llvm-readobj.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
@@ -26,8 +25,10 @@
#include "llvm/Support/ARMBuildAttributes.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/Format.h"
+#include "llvm/Support/FormattedStream.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/MipsABIFlags.h"
+#include "llvm/Support/ScopedPrinter.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
@@ -37,12 +38,66 @@ using namespace ELF;
#define LLVM_READOBJ_ENUM_CASE(ns, enum) \
case ns::enum: return #enum;
+#define ENUM_ENT(enum, altName) \
+ { #enum, altName, ELF::enum }
+
+#define ENUM_ENT_1(enum) \
+ { #enum, #enum, ELF::enum }
+
+#define LLVM_READOBJ_PHDR_ENUM(ns, enum) \
+ case ns::enum: \
+ return std::string(#enum).substr(3);
+
+#define TYPEDEF_ELF_TYPES(ELFT) \
+ 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_Ehdr Elf_Ehdr; \
+ typedef typename ELFO::Elf_Word Elf_Word; \
+ typedef typename ELFO::Elf_Hash Elf_Hash; \
+ typedef typename ELFO::Elf_GnuHash Elf_GnuHash; \
+ typedef typename ELFO::uintX_t uintX_t;
+
namespace {
+template <class ELFT> class DumpStyle;
+
+/// Represents a contiguous uniform range in the file. We cannot just create a
+/// range directly because when creating one of these from the .dynamic table
+/// the size, entity size and virtual address are different entries in arbitrary
+/// order (DT_REL, DT_RELSZ, DT_RELENT for example).
+struct DynRegionInfo {
+ DynRegionInfo() : Addr(nullptr), Size(0), EntSize(0) {}
+ DynRegionInfo(const void *A, uint64_t S, uint64_t ES)
+ : Addr(A), Size(S), EntSize(ES) {}
+ /// \brief Address in current address space.
+ const void *Addr;
+ /// \brief Size in bytes of the region.
+ uint64_t Size;
+ /// \brief Size of each entity in the region.
+ uint64_t EntSize;
+
+ template <typename Type> ArrayRef<Type> getAsArrayRef() const {
+ const Type *Start = reinterpret_cast<const Type *>(Addr);
+ if (!Start)
+ return {Start, Start};
+ if (EntSize != sizeof(Type) || Size % EntSize)
+ reportError("Invalid entity size");
+ return {Start, Start + (Size / EntSize)};
+ }
+};
+
template<typename ELFT>
class ELFDumper : public ObjDumper {
public:
- ELFDumper(const ELFFile<ELFT> *Obj, StreamWriter &Writer);
+ ELFDumper(const ELFFile<ELFT> *Obj, ScopedPrinter &Writer);
void printFileHeaders() override;
void printSections() override;
@@ -59,22 +114,29 @@ public:
void printGnuHashTable() override;
void printLoadName() override;
void printVersionInfo() override;
+ void printGroupSections() override;
void printAttributes() override;
void printMipsPLTGOT() override;
void printMipsABIFlags() override;
void printMipsReginfo() override;
+ void printMipsOptions() override;
void printStackMap() const override;
+ void printHashHistogram() override;
+
private:
+ std::unique_ptr<DumpStyle<ELFT>> ELFDumperStyle;
typedef ELFFile<ELFT> ELFO;
typedef typename ELFO::Elf_Shdr Elf_Shdr;
typedef typename ELFO::Elf_Sym Elf_Sym;
+ typedef typename ELFO::Elf_Sym_Range Elf_Sym_Range;
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_Rel_Range Elf_Rel_Range;
typedef typename ELFO::Elf_Rela_Range Elf_Rela_Range;
typedef typename ELFO::Elf_Phdr Elf_Phdr;
typedef typename ELFO::Elf_Half Elf_Half;
@@ -89,55 +151,44 @@ private:
typedef typename ELFO::Elf_Verdef Elf_Verdef;
typedef typename ELFO::Elf_Verdaux Elf_Verdaux;
- /// \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;
- };
+ DynRegionInfo checkDRI(DynRegionInfo DRI) {
+ if (DRI.Addr < Obj->base() ||
+ (const uint8_t *)DRI.Addr + DRI.Size > Obj->base() + Obj->getBufSize())
+ error(llvm::object::object_error::parse_failed);
+ return DRI;
+ }
+
+ DynRegionInfo createDRIFrom(const Elf_Phdr *P, uintX_t EntSize) {
+ return checkDRI({Obj->base() + P->p_offset, P->p_filesz, EntSize});
+ }
- void printSymbolsHelper(bool IsDynamic);
- void printSymbol(const Elf_Sym *Symbol, const Elf_Shdr *SymTab,
- StringRef StrTable, bool IsDynamic);
+ DynRegionInfo createDRIFrom(const Elf_Shdr *S) {
+ return checkDRI({Obj->base() + S->sh_offset, S->sh_size, S->sh_entsize});
+ }
+
+ void parseDynamicTable(ArrayRef<const Elf_Phdr *> LoadSegments);
- void printRelocations(const Elf_Shdr *Sec);
- 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();
+ bool &IsDefault) const;
+ void LoadVersionMap() const;
void LoadVersionNeeds(const Elf_Shdr *ec) const;
void LoadVersionDefs(const Elf_Shdr *sec) const;
const ELFO *Obj;
+ DynRegionInfo DynRelRegion;
DynRegionInfo DynRelaRegion;
- const Elf_Phdr *DynamicProgHeader = nullptr;
+ DynRegionInfo DynPLTRelRegion;
+ DynRegionInfo DynSymRegion;
+ DynRegionInfo DynamicTable;
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;
+ StringRef DynSymtabName;
ArrayRef<Elf_Word> ShndxTable;
const Elf_Shdr *dot_gnu_version_sec = nullptr; // .gnu.version
@@ -169,40 +220,176 @@ private:
public:
Elf_Dyn_Range dynamic_table() const {
- ErrorOr<Elf_Dyn_Range> Ret = Obj->dynamic_table(DynamicProgHeader);
- error(Ret.getError());
- return *Ret;
+ return DynamicTable.getAsArrayRef<Elf_Dyn>();
+ }
+
+ Elf_Sym_Range dynamic_symbols() const {
+ return DynSymRegion.getAsArrayRef<Elf_Sym>();
}
+ Elf_Rel_Range dyn_rels() const;
+ Elf_Rela_Range dyn_relas() const;
std::string getFullSymbolName(const Elf_Sym *Symbol, StringRef StrTable,
- bool IsDynamic);
- const Elf_Shdr *getDotDynSymSec() const { return DotDynSymSec; }
+ bool IsDynamic) const;
+
+ void printSymbolsHelper(bool IsDynamic) const;
const Elf_Shdr *getDotSymtabSec() const { return DotSymtabSec; }
- ArrayRef<Elf_Word> getShndxTable() { return ShndxTable; }
+ ArrayRef<Elf_Word> getShndxTable() const { return ShndxTable; }
+ StringRef getDynamicStringTable() const { return DynamicStringTable; }
+ const DynRegionInfo &getDynRelRegion() const { return DynRelRegion; }
+ const DynRegionInfo &getDynRelaRegion() const { return DynRelaRegion; }
+ const DynRegionInfo &getDynPLTRelRegion() const { return DynPLTRelRegion; }
+ const Elf_Hash *getHashTable() const { return HashTable; }
+ const Elf_GnuHash *getGnuHashTable() const { return GnuHashTable; }
};
-template <class T> T errorOrDefault(ErrorOr<T> Val, T Default = T()) {
- if (!Val) {
- error(Val.getError());
- return Default;
+template <class ELFT>
+void ELFDumper<ELFT>::printSymbolsHelper(bool IsDynamic) const {
+ StringRef StrTable, SymtabName;
+ size_t Entries = 0;
+ Elf_Sym_Range Syms(nullptr, nullptr);
+ if (IsDynamic) {
+ StrTable = DynamicStringTable;
+ Syms = dynamic_symbols();
+ SymtabName = DynSymtabName;
+ if (DynSymRegion.Addr)
+ Entries = DynSymRegion.Size / DynSymRegion.EntSize;
+ } else {
+ if (!DotSymtabSec)
+ return;
+ StrTable = unwrapOrError(Obj->getStringTableForSymtab(*DotSymtabSec));
+ Syms = Obj->symbols(DotSymtabSec);
+ SymtabName = unwrapOrError(Obj->getSectionName(DotSymtabSec));
+ Entries = DotSymtabSec->getEntityCount();
+ }
+ if (Syms.begin() == Syms.end())
+ return;
+ ELFDumperStyle->printSymtabMessage(Obj, SymtabName, Entries);
+ for (const auto &Sym : Syms)
+ ELFDumperStyle->printSymbol(Obj, &Sym, Syms.begin(), StrTable, IsDynamic);
+}
+
+template <typename ELFT> class DumpStyle {
+public:
+ using Elf_Shdr = typename ELFFile<ELFT>::Elf_Shdr;
+ using Elf_Sym = typename ELFFile<ELFT>::Elf_Sym;
+
+ DumpStyle(ELFDumper<ELFT> *Dumper) : Dumper(Dumper) {}
+ virtual ~DumpStyle() {}
+ virtual void printFileHeaders(const ELFFile<ELFT> *Obj) = 0;
+ virtual void printGroupSections(const ELFFile<ELFT> *Obj) = 0;
+ virtual void printRelocations(const ELFFile<ELFT> *Obj) = 0;
+ virtual void printSections(const ELFFile<ELFT> *Obj) = 0;
+ virtual void printSymbols(const ELFFile<ELFT> *Obj) = 0;
+ virtual void printDynamicSymbols(const ELFFile<ELFT> *Obj) = 0;
+ virtual void printDynamicRelocations(const ELFFile<ELFT> *Obj) = 0;
+ virtual void printSymtabMessage(const ELFFile<ELFT> *obj, StringRef Name,
+ size_t Offset) {
+ return;
}
+ virtual void printSymbol(const ELFFile<ELFT> *Obj, const Elf_Sym *Symbol,
+ const Elf_Sym *FirstSym, StringRef StrTable,
+ bool IsDynamic) = 0;
+ virtual void printProgramHeaders(const ELFFile<ELFT> *Obj) = 0;
+ virtual void printHashHistogram(const ELFFile<ELFT> *Obj) = 0;
+ const ELFDumper<ELFT> *dumper() const { return Dumper; }
+private:
+ const ELFDumper<ELFT> *Dumper;
+};
+
+template <typename ELFT> class GNUStyle : public DumpStyle<ELFT> {
+ formatted_raw_ostream OS;
+public:
+ TYPEDEF_ELF_TYPES(ELFT)
+ GNUStyle(ScopedPrinter &W, ELFDumper<ELFT> *Dumper)
+ : DumpStyle<ELFT>(Dumper), OS(W.getOStream()) {}
+ void printFileHeaders(const ELFO *Obj) override;
+ void printGroupSections(const ELFFile<ELFT> *Obj) override;
+ void printRelocations(const ELFO *Obj) override;
+ void printSections(const ELFO *Obj) override;
+ void printSymbols(const ELFO *Obj) override;
+ void printDynamicSymbols(const ELFO *Obj) override;
+ void printDynamicRelocations(const ELFO *Obj) override;
+ virtual void printSymtabMessage(const ELFO *Obj, StringRef Name,
+ size_t Offset) override;
+ void printProgramHeaders(const ELFO *Obj) override;
+ void printHashHistogram(const ELFFile<ELFT> *Obj) override;
+
+private:
+ struct Field {
+ StringRef Str;
+ unsigned Column;
+ Field(StringRef S, unsigned Col) : Str(S), Column(Col) {}
+ Field(unsigned Col) : Str(""), Column(Col) {}
+ };
+
+ template <typename T, typename TEnum>
+ std::string printEnum(T Value, ArrayRef<EnumEntry<TEnum>> EnumValues) {
+ for (const auto &EnumItem : EnumValues)
+ if (EnumItem.Value == Value)
+ return EnumItem.AltName;
+ return to_hexString(Value, false);
+ }
+
+ formatted_raw_ostream &printField(struct Field F) {
+ if (F.Column != 0)
+ OS.PadToColumn(F.Column);
+ OS << F.Str;
+ OS.flush();
+ return OS;
+ }
+ void printRelocation(const ELFO *Obj, const Elf_Shdr *SymTab,
+ const Elf_Rela &R, bool IsRela);
+ void printSymbol(const ELFO *Obj, const Elf_Sym *Symbol, const Elf_Sym *First,
+ StringRef StrTable, bool IsDynamic) override;
+ std::string getSymbolSectionNdx(const ELFO *Obj, const Elf_Sym *Symbol,
+ const Elf_Sym *FirstSym);
+ void printDynamicRelocation(const ELFO *Obj, Elf_Rela R, bool IsRela);
+ bool checkTLSSections(const Elf_Phdr &Phdr, const Elf_Shdr &Sec);
+ bool checkoffsets(const Elf_Phdr &Phdr, const Elf_Shdr &Sec);
+ bool checkVMA(const Elf_Phdr &Phdr, const Elf_Shdr &Sec);
+ bool checkPTDynamic(const Elf_Phdr &Phdr, const Elf_Shdr &Sec);
+};
+
+template <typename ELFT> class LLVMStyle : public DumpStyle<ELFT> {
+public:
+ TYPEDEF_ELF_TYPES(ELFT)
+ LLVMStyle(ScopedPrinter &W, ELFDumper<ELFT> *Dumper)
+ : DumpStyle<ELFT>(Dumper), W(W) {}
+
+ void printFileHeaders(const ELFO *Obj) override;
+ void printGroupSections(const ELFFile<ELFT> *Obj) override;
+ void printRelocations(const ELFO *Obj) override;
+ void printRelocations(const Elf_Shdr *Sec, const ELFO *Obj);
+ void printSections(const ELFO *Obj) override;
+ void printSymbols(const ELFO *Obj) override;
+ void printDynamicSymbols(const ELFO *Obj) override;
+ void printDynamicRelocations(const ELFO *Obj) override;
+ void printProgramHeaders(const ELFO *Obj) override;
+ void printHashHistogram(const ELFFile<ELFT> *Obj) override;
+
+private:
+ void printRelocation(const ELFO *Obj, Elf_Rela Rel, const Elf_Shdr *SymTab);
+ void printDynamicRelocation(const ELFO *Obj, Elf_Rela Rel);
+ void printSymbol(const ELFO *Obj, const Elf_Sym *Symbol, const Elf_Sym *First,
+ StringRef StrTable, bool IsDynamic) override;
+ ScopedPrinter &W;
+};
- return *Val;
-}
} // namespace
namespace llvm {
template <class ELFT>
static std::error_code createELFDumper(const ELFFile<ELFT> *Obj,
- StreamWriter &Writer,
+ ScopedPrinter &Writer,
std::unique_ptr<ObjDumper> &Result) {
Result.reset(new ELFDumper<ELFT>(Obj, Writer));
return readobj_error::success;
}
std::error_code createELFDumper(const object::ObjectFile *Obj,
- StreamWriter &Writer,
+ ScopedPrinter &Writer,
std::unique_ptr<ObjDumper> &Result) {
// Little-endian 32-bit
if (const ELF32LEObjectFile *ELFObj = dyn_cast<ELF32LEObjectFile>(Obj))
@@ -284,9 +471,9 @@ void ELFDumper<ELFT>::LoadVersionDefs(const Elf_Shdr *sec) const {
}
}
-template <class ELFT> void ELFDumper<ELFT>::LoadVersionMap() {
+template <class ELFT> void ELFDumper<ELFT>::LoadVersionMap() const {
// If there is no dynamic symtab or version table, there is nothing to do.
- if (!DynSymStart || !dot_gnu_version_sec)
+ if (!DynSymRegion.Addr || !dot_gnu_version_sec)
return;
// Has the VersionMap already been loaded?
@@ -305,99 +492,157 @@ template <class ELFT> void ELFDumper<ELFT>::LoadVersionMap() {
LoadVersionNeeds(dot_gnu_version_r_sec);
}
-
template <typename ELFO, class ELFT>
-static void printVersionSymbolSection(ELFDumper<ELFT> *Dumper,
- const ELFO *Obj,
+static void printVersionSymbolSection(ELFDumper<ELFT> *Dumper, const ELFO *Obj,
const typename ELFO::Elf_Shdr *Sec,
- StreamWriter &W) {
+ ScopedPrinter &W) {
DictScope SS(W, "Version symbols");
if (!Sec)
return;
- StringRef Name = errorOrDefault(Obj->getSectionName(Sec));
+ StringRef Name = unwrapOrError(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());
+ StringRef StrTable = Dumper->getDynamicStringTable();
// 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)) {
+ for (const typename ELFO::Elf_Sym &Sym : Dumper->dynamic_symbols()) {
DictScope S(W, "Symbol");
std::string FullSymbolName =
- Dumper->getFullSymbolName(&Sym, *StrTableOrErr, true /* IsDynamic */);
+ Dumper->getFullSymbolName(&Sym, StrTable, true /* IsDynamic */);
W.printNumber("Version", *P);
W.printString("Name", FullSymbolName);
P += sizeof(typename ELFO::Elf_Half);
}
}
+static const EnumEntry<unsigned> SymVersionFlags[] = {
+ {"Base", "BASE", VER_FLG_BASE},
+ {"Weak", "WEAK", VER_FLG_WEAK},
+ {"Info", "INFO", VER_FLG_INFO}};
+
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");
+ ScopedPrinter &W) {
+ typedef typename ELFO::Elf_Verdef VerDef;
+ typedef typename ELFO::Elf_Verdaux VerdAux;
+
+ DictScope SD(W, "SHT_GNU_verdef");
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.
+ unsigned VerDefsNum = 0;
for (const typename ELFO::Elf_Dyn &Dyn : Dumper->dynamic_table()) {
if (Dyn.d_tag == DT_VERDEFNUM)
- verdef_entries = Dyn.d_un.d_val;
+ VerDefsNum = 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());
+ const typename ELFO::Elf_Shdr *StrTab =
+ unwrapOrError(Obj->getSection(Sec->sh_link));
- ListScope Entries(W, "Entries");
- for (unsigned i = 0; i < verdef_entries; ++i) {
- if (P + sizeof(typename ELFO::Elf_Verdef) > SecEndAddress)
+ while (VerDefsNum--) {
+ if (P + sizeof(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);
+
+ auto *VD = reinterpret_cast<const VerDef *>(P);
+ DictScope Def(W, "Definition");
+ W.printNumber("Version", VD->vd_version);
+ W.printEnum("Flags", VD->vd_flags, makeArrayRef(SymVersionFlags));
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)));
+ W.printNumber("Hash", VD->vd_hash);
+ W.printString("Name",
+ StringRef((const char *)(Obj->base() + StrTab->sh_offset +
+ VD->getAux()->vda_name)));
+ if (!VD->vd_cnt)
+ report_fatal_error("at least one definition string must exist");
+ if (VD->vd_cnt > 2)
+ report_fatal_error("more than one predecessor is not expected");
+
+ if (VD->vd_cnt == 2) {
+ const uint8_t *PAux = P + VD->vd_aux + VD->getAux()->vda_next;
+ const VerdAux *Aux = reinterpret_cast<const VerdAux *>(PAux);
+ W.printString("Predecessor",
+ StringRef((const char *)(Obj->base() + StrTab->sh_offset +
+ Aux->vda_name)));
+ }
+
P += VD->vd_next;
}
}
+template <typename ELFO, class ELFT>
+static void printVersionDependencySection(ELFDumper<ELFT> *Dumper,
+ const ELFO *Obj,
+ const typename ELFO::Elf_Shdr *Sec,
+ ScopedPrinter &W) {
+ typedef typename ELFO::Elf_Verneed VerNeed;
+ typedef typename ELFO::Elf_Vernaux VernAux;
+
+ DictScope SD(W, "SHT_GNU_verneed");
+ if (!Sec)
+ return;
+
+ unsigned VerNeedNum = 0;
+ for (const typename ELFO::Elf_Dyn &Dyn : Dumper->dynamic_table())
+ if (Dyn.d_tag == DT_VERNEEDNUM)
+ VerNeedNum = Dyn.d_un.d_val;
+
+ const uint8_t *SecData = (const uint8_t *)Obj->base() + Sec->sh_offset;
+ const typename ELFO::Elf_Shdr *StrTab =
+ unwrapOrError(Obj->getSection(Sec->sh_link));
+
+ const uint8_t *P = SecData;
+ for (unsigned I = 0; I < VerNeedNum; ++I) {
+ const VerNeed *Need = reinterpret_cast<const VerNeed *>(P);
+ DictScope Entry(W, "Dependency");
+ W.printNumber("Version", Need->vn_version);
+ W.printNumber("Count", Need->vn_cnt);
+ W.printString("FileName",
+ StringRef((const char *)(Obj->base() + StrTab->sh_offset +
+ Need->vn_file)));
+
+ const uint8_t *PAux = P + Need->vn_aux;
+ for (unsigned J = 0; J < Need->vn_cnt; ++J) {
+ const VernAux *Aux = reinterpret_cast<const VernAux *>(PAux);
+ DictScope Entry(W, "Entry");
+ W.printNumber("Hash", Aux->vna_hash);
+ W.printEnum("Flags", Aux->vna_flags, makeArrayRef(SymVersionFlags));
+ W.printNumber("Index", Aux->vna_other);
+ W.printString("Name",
+ StringRef((const char *)(Obj->base() + StrTab->sh_offset +
+ Aux->vna_name)));
+ PAux += Aux->vna_next;
+ }
+ P += Need->vn_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);
+
+ // Dump version dependency section.
+ printVersionDependencySection(this, Obj, dot_gnu_version_r_sec, W);
}
template <typename ELFT>
StringRef ELFDumper<ELFT>::getSymbolVersion(StringRef StrTab,
const Elf_Sym *symb,
- bool &IsDefault) {
+ bool &IsDefault) const {
// This is a dynamic symbol. Look in the GNU symbol version table.
if (!dot_gnu_version_sec) {
// No version table.
@@ -407,7 +652,7 @@ StringRef ELFDumper<ELFT>::getSymbolVersion(StringRef StrTab,
// Determine the position in the symbol table of this entry.
size_t entry_index = (reinterpret_cast<uintptr_t>(symb) -
- reinterpret_cast<uintptr_t>(DynSymStart)) /
+ reinterpret_cast<uintptr_t>(DynSymRegion.Addr)) /
sizeof(Elf_Sym);
// Get the corresponding version index entry
@@ -446,8 +691,8 @@ StringRef ELFDumper<ELFT>::getSymbolVersion(StringRef StrTab,
template <typename ELFT>
std::string ELFDumper<ELFT>::getFullSymbolName(const Elf_Sym *Symbol,
StringRef StrTable,
- bool IsDynamic) {
- StringRef SymbolName = errorOrDefault(Symbol->getName(StrTable));
+ bool IsDynamic) const {
+ StringRef SymbolName = unwrapOrError(Symbol->getName(StrTable));
if (!IsDynamic)
return SymbolName;
@@ -463,7 +708,7 @@ std::string ELFDumper<ELFT>::getFullSymbolName(const Elf_Sym *Symbol,
template <typename ELFO>
static void
getSectionNameIndex(const ELFO &Obj, const typename ELFO::Elf_Sym *Symbol,
- const typename ELFO::Elf_Shdr *SymTab,
+ const typename ELFO::Elf_Sym *FirstSym,
ArrayRef<typename ELFO::Elf_Word> ShndxTable,
StringRef &SectionName, unsigned &SectionIndex) {
SectionIndex = Symbol->st_shndx;
@@ -482,18 +727,18 @@ getSectionNameIndex(const ELFO &Obj, const typename ELFO::Elf_Sym *Symbol,
else {
if (SectionIndex == SHN_XINDEX)
SectionIndex =
- Obj.getExtendedSymbolTableIndex(Symbol, SymTab, ShndxTable);
- ErrorOr<const typename ELFO::Elf_Shdr *> Sec = Obj.getSection(SectionIndex);
- error(Sec.getError());
- SectionName = errorOrDefault(Obj.getSectionName(*Sec));
+ Obj.getExtendedSymbolTableIndex(Symbol, FirstSym, ShndxTable);
+ const typename ELFO::Elf_Shdr *Sec =
+ unwrapOrError(Obj.getSection(SectionIndex));
+ SectionName = unwrapOrError(Obj.getSectionName(Sec));
}
}
template <class ELFO>
-static const typename ELFO::Elf_Shdr *findSectionByAddress(const ELFO *Obj,
- uint64_t Addr) {
+static const typename ELFO::Elf_Shdr *
+findNotEmptySectionByAddress(const ELFO *Obj, uint64_t Addr) {
for (const auto &Shdr : Obj->sections())
- if (Shdr.sh_addr == Addr)
+ if (Shdr.sh_addr == Addr && Shdr.sh_size > 0)
return &Shdr;
return nullptr;
}
@@ -502,233 +747,239 @@ 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)))
+ if (Name == unwrapOrError(Obj.getSectionName(&Shdr)))
return &Shdr;
}
return nullptr;
}
static const EnumEntry<unsigned> ElfClass[] = {
- { "None", ELF::ELFCLASSNONE },
- { "32-bit", ELF::ELFCLASS32 },
- { "64-bit", ELF::ELFCLASS64 },
+ {"None", "none", ELF::ELFCLASSNONE},
+ {"32-bit", "ELF32", ELF::ELFCLASS32},
+ {"64-bit", "ELF64", ELF::ELFCLASS64},
};
static const EnumEntry<unsigned> ElfDataEncoding[] = {
- { "None", ELF::ELFDATANONE },
- { "LittleEndian", ELF::ELFDATA2LSB },
- { "BigEndian", ELF::ELFDATA2MSB },
+ {"None", "none", ELF::ELFDATANONE},
+ {"LittleEndian", "2's complement, little endian", ELF::ELFDATA2LSB},
+ {"BigEndian", "2's complement, big endian", ELF::ELFDATA2MSB},
};
static const EnumEntry<unsigned> ElfObjectFileType[] = {
- { "None", ELF::ET_NONE },
- { "Relocatable", ELF::ET_REL },
- { "Executable", ELF::ET_EXEC },
- { "SharedObject", ELF::ET_DYN },
- { "Core", ELF::ET_CORE },
+ {"None", "NONE (none)", ELF::ET_NONE},
+ {"Relocatable", "REL (Relocatable file)", ELF::ET_REL},
+ {"Executable", "EXEC (Executable file)", ELF::ET_EXEC},
+ {"SharedObject", "DYN (Shared object file)", ELF::ET_DYN},
+ {"Core", "CORE (Core file)", ELF::ET_CORE},
};
static const EnumEntry<unsigned> ElfOSABI[] = {
- { "SystemV", ELF::ELFOSABI_NONE },
- { "HPUX", ELF::ELFOSABI_HPUX },
- { "NetBSD", ELF::ELFOSABI_NETBSD },
- { "GNU/Linux", ELF::ELFOSABI_LINUX },
- { "GNU/Hurd", ELF::ELFOSABI_HURD },
- { "Solaris", ELF::ELFOSABI_SOLARIS },
- { "AIX", ELF::ELFOSABI_AIX },
- { "IRIX", ELF::ELFOSABI_IRIX },
- { "FreeBSD", ELF::ELFOSABI_FREEBSD },
- { "TRU64", ELF::ELFOSABI_TRU64 },
- { "Modesto", ELF::ELFOSABI_MODESTO },
- { "OpenBSD", ELF::ELFOSABI_OPENBSD },
- { "OpenVMS", ELF::ELFOSABI_OPENVMS },
- { "NSK", ELF::ELFOSABI_NSK },
- { "AROS", ELF::ELFOSABI_AROS },
- { "FenixOS", ELF::ELFOSABI_FENIXOS },
- { "CloudABI", ELF::ELFOSABI_CLOUDABI },
- { "C6000_ELFABI", ELF::ELFOSABI_C6000_ELFABI },
- { "C6000_LINUX" , ELF::ELFOSABI_C6000_LINUX },
- { "ARM", ELF::ELFOSABI_ARM },
- { "Standalone" , ELF::ELFOSABI_STANDALONE }
+ {"SystemV", "UNIX - System V", ELF::ELFOSABI_NONE},
+ {"HPUX", "UNIX - HP-UX", ELF::ELFOSABI_HPUX},
+ {"NetBSD", "UNIX - NetBSD", ELF::ELFOSABI_NETBSD},
+ {"GNU/Linux", "UNIX - GNU", ELF::ELFOSABI_LINUX},
+ {"GNU/Hurd", "GNU/Hurd", ELF::ELFOSABI_HURD},
+ {"Solaris", "UNIX - Solaris", ELF::ELFOSABI_SOLARIS},
+ {"AIX", "UNIX - AIX", ELF::ELFOSABI_AIX},
+ {"IRIX", "UNIX - IRIX", ELF::ELFOSABI_IRIX},
+ {"FreeBSD", "UNIX - FreeBSD", ELF::ELFOSABI_FREEBSD},
+ {"TRU64", "UNIX - TRU64", ELF::ELFOSABI_TRU64},
+ {"Modesto", "Novell - Modesto", ELF::ELFOSABI_MODESTO},
+ {"OpenBSD", "UNIX - OpenBSD", ELF::ELFOSABI_OPENBSD},
+ {"OpenVMS", "VMS - OpenVMS", ELF::ELFOSABI_OPENVMS},
+ {"NSK", "HP - Non-Stop Kernel", ELF::ELFOSABI_NSK},
+ {"AROS", "AROS", ELF::ELFOSABI_AROS},
+ {"FenixOS", "FenixOS", ELF::ELFOSABI_FENIXOS},
+ {"CloudABI", "CloudABI", ELF::ELFOSABI_CLOUDABI},
+ {"C6000_ELFABI", "Bare-metal C6000", ELF::ELFOSABI_C6000_ELFABI},
+ {"C6000_LINUX", "Linux C6000", ELF::ELFOSABI_C6000_LINUX},
+ {"ARM", "ARM", ELF::ELFOSABI_ARM},
+ {"Standalone", "Standalone App", ELF::ELFOSABI_STANDALONE}
};
static const EnumEntry<unsigned> ElfMachineType[] = {
- LLVM_READOBJ_ENUM_ENT(ELF, EM_NONE ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_M32 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_SPARC ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_386 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_68K ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_88K ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_IAMCU ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_860 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_MIPS ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_S370 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_MIPS_RS3_LE ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_PARISC ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_VPP500 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_SPARC32PLUS ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_960 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_PPC ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_PPC64 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_S390 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_SPU ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_V800 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_FR20 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_RH32 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_RCE ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_ARM ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_ALPHA ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_SH ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_SPARCV9 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_TRICORE ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_ARC ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_H8_300 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_H8_300H ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_H8S ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_H8_500 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_IA_64 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_MIPS_X ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_COLDFIRE ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_68HC12 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_MMA ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_PCP ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_NCPU ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_NDR1 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_STARCORE ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_ME16 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_ST100 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_TINYJ ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_X86_64 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_PDSP ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_PDP10 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_PDP11 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_FX66 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_ST9PLUS ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_ST7 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_68HC16 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_68HC11 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_68HC08 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_68HC05 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_SVX ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_ST19 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_VAX ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_CRIS ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_JAVELIN ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_FIREPATH ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_ZSP ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_MMIX ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_HUANY ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_PRISM ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_AVR ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_FR30 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_D10V ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_D30V ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_V850 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_M32R ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_MN10300 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_MN10200 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_PJ ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_OPENRISC ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_ARC_COMPACT ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_XTENSA ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_VIDEOCORE ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_TMM_GPP ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_NS32K ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_TPC ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_SNP1K ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_ST200 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_IP2K ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_MAX ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_CR ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_F2MC16 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_MSP430 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_BLACKFIN ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_SE_C33 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_SEP ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_ARCA ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_UNICORE ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_EXCESS ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_DXP ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_ALTERA_NIOS2 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_CRX ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_XGATE ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_C166 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_M16C ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_DSPIC30F ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_CE ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_M32C ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_TSK3000 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_RS08 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_SHARC ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_ECOG2 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_SCORE7 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_DSP24 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_VIDEOCORE3 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_LATTICEMICO32),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_SE_C17 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_TI_C6000 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_TI_C2000 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_TI_C5500 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_MMDSP_PLUS ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_CYPRESS_M8C ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_R32C ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_TRIMEDIA ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_HEXAGON ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_8051 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_STXP7X ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_NDS32 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_ECOG1 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_ECOG1X ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_MAXQ30 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_XIMO16 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_MANIK ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_CRAYNV2 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_RX ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_METAG ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_MCST_ELBRUS ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_ECOG16 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_CR16 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_ETPU ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_SLE9X ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_L10M ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_K10M ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_AARCH64 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_AVR32 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_STM8 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_TILE64 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_TILEPRO ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_CUDA ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_TILEGX ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_CLOUDSHIELD ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_COREA_1ST ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_COREA_2ND ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_ARC_COMPACT2 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_OPEN8 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_RL78 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_VIDEOCORE5 ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_78KOR ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_56800EX ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_AMDGPU ),
- LLVM_READOBJ_ENUM_ENT(ELF, EM_WEBASSEMBLY ),
+ ENUM_ENT(EM_NONE, "None"),
+ ENUM_ENT(EM_M32, "WE32100"),
+ ENUM_ENT(EM_SPARC, "Sparc"),
+ ENUM_ENT(EM_386, "Intel 80386"),
+ ENUM_ENT(EM_68K, "MC68000"),
+ ENUM_ENT(EM_88K, "MC88000"),
+ ENUM_ENT(EM_IAMCU, "EM_IAMCU"),
+ ENUM_ENT(EM_860, "Intel 80860"),
+ ENUM_ENT(EM_MIPS, "MIPS R3000"),
+ ENUM_ENT(EM_S370, "IBM System/370"),
+ ENUM_ENT(EM_MIPS_RS3_LE, "MIPS R3000 little-endian"),
+ ENUM_ENT(EM_PARISC, "HPPA"),
+ ENUM_ENT(EM_VPP500, "Fujitsu VPP500"),
+ ENUM_ENT(EM_SPARC32PLUS, "Sparc v8+"),
+ ENUM_ENT(EM_960, "Intel 80960"),
+ ENUM_ENT(EM_PPC, "PowerPC"),
+ ENUM_ENT(EM_PPC64, "PowerPC64"),
+ ENUM_ENT(EM_S390, "IBM S/390"),
+ ENUM_ENT(EM_SPU, "SPU"),
+ ENUM_ENT(EM_V800, "NEC V800 series"),
+ ENUM_ENT(EM_FR20, "Fujistsu FR20"),
+ ENUM_ENT(EM_RH32, "TRW RH-32"),
+ ENUM_ENT(EM_RCE, "Motorola RCE"),
+ ENUM_ENT(EM_ARM, "ARM"),
+ ENUM_ENT(EM_ALPHA, "EM_ALPHA"),
+ ENUM_ENT(EM_SH, "Hitachi SH"),
+ ENUM_ENT(EM_SPARCV9, "Sparc v9"),
+ ENUM_ENT(EM_TRICORE, "Siemens Tricore"),
+ ENUM_ENT(EM_ARC, "ARC"),
+ ENUM_ENT(EM_H8_300, "Hitachi H8/300"),
+ ENUM_ENT(EM_H8_300H, "Hitachi H8/300H"),
+ ENUM_ENT(EM_H8S, "Hitachi H8S"),
+ ENUM_ENT(EM_H8_500, "Hitachi H8/500"),
+ ENUM_ENT(EM_IA_64, "Intel IA-64"),
+ ENUM_ENT(EM_MIPS_X, "Stanford MIPS-X"),
+ ENUM_ENT(EM_COLDFIRE, "Motorola Coldfire"),
+ ENUM_ENT(EM_68HC12, "Motorola MC68HC12 Microcontroller"),
+ ENUM_ENT(EM_MMA, "Fujitsu Multimedia Accelerator"),
+ ENUM_ENT(EM_PCP, "Siemens PCP"),
+ ENUM_ENT(EM_NCPU, "Sony nCPU embedded RISC processor"),
+ ENUM_ENT(EM_NDR1, "Denso NDR1 microprocesspr"),
+ ENUM_ENT(EM_STARCORE, "Motorola Star*Core processor"),
+ ENUM_ENT(EM_ME16, "Toyota ME16 processor"),
+ ENUM_ENT(EM_ST100, "STMicroelectronics ST100 processor"),
+ ENUM_ENT(EM_TINYJ, "Advanced Logic Corp. TinyJ embedded processor"),
+ ENUM_ENT(EM_X86_64, "Advanced Micro Devices X86-64"),
+ ENUM_ENT(EM_PDSP, "Sony DSP processor"),
+ ENUM_ENT(EM_PDP10, "Digital Equipment Corp. PDP-10"),
+ ENUM_ENT(EM_PDP11, "Digital Equipment Corp. PDP-11"),
+ ENUM_ENT(EM_FX66, "Siemens FX66 microcontroller"),
+ ENUM_ENT(EM_ST9PLUS, "STMicroelectronics ST9+ 8/16 bit microcontroller"),
+ ENUM_ENT(EM_ST7, "STMicroelectronics ST7 8-bit microcontroller"),
+ ENUM_ENT(EM_68HC16, "Motorola MC68HC16 Microcontroller"),
+ ENUM_ENT(EM_68HC11, "Motorola MC68HC11 Microcontroller"),
+ ENUM_ENT(EM_68HC08, "Motorola MC68HC08 Microcontroller"),
+ ENUM_ENT(EM_68HC05, "Motorola MC68HC05 Microcontroller"),
+ ENUM_ENT(EM_SVX, "Silicon Graphics SVx"),
+ ENUM_ENT(EM_ST19, "STMicroelectronics ST19 8-bit microcontroller"),
+ ENUM_ENT(EM_VAX, "Digital VAX"),
+ ENUM_ENT(EM_CRIS, "Axis Communications 32-bit embedded processor"),
+ ENUM_ENT(EM_JAVELIN, "Infineon Technologies 32-bit embedded cpu"),
+ ENUM_ENT(EM_FIREPATH, "Element 14 64-bit DSP processor"),
+ ENUM_ENT(EM_ZSP, "LSI Logic's 16-bit DSP processor"),
+ ENUM_ENT(EM_MMIX, "Donald Knuth's educational 64-bit processor"),
+ ENUM_ENT(EM_HUANY, "Harvard Universitys's machine-independent object format"),
+ ENUM_ENT(EM_PRISM, "Vitesse Prism"),
+ ENUM_ENT(EM_AVR, "Atmel AVR 8-bit microcontroller"),
+ ENUM_ENT(EM_FR30, "Fujitsu FR30"),
+ ENUM_ENT(EM_D10V, "Mitsubishi D10V"),
+ ENUM_ENT(EM_D30V, "Mitsubishi D30V"),
+ ENUM_ENT(EM_V850, "NEC v850"),
+ ENUM_ENT(EM_M32R, "Renesas M32R (formerly Mitsubishi M32r)"),
+ ENUM_ENT(EM_MN10300, "Matsushita MN10300"),
+ ENUM_ENT(EM_MN10200, "Matsushita MN10200"),
+ ENUM_ENT(EM_PJ, "picoJava"),
+ ENUM_ENT(EM_OPENRISC, "OpenRISC 32-bit embedded processor"),
+ ENUM_ENT(EM_ARC_COMPACT, "EM_ARC_COMPACT"),
+ ENUM_ENT(EM_XTENSA, "Tensilica Xtensa Processor"),
+ ENUM_ENT(EM_VIDEOCORE, "Alphamosaic VideoCore processor"),
+ ENUM_ENT(EM_TMM_GPP, "Thompson Multimedia General Purpose Processor"),
+ ENUM_ENT(EM_NS32K, "National Semiconductor 32000 series"),
+ ENUM_ENT(EM_TPC, "Tenor Network TPC processor"),
+ ENUM_ENT(EM_SNP1K, "EM_SNP1K"),
+ ENUM_ENT(EM_ST200, "STMicroelectronics ST200 microcontroller"),
+ ENUM_ENT(EM_IP2K, "Ubicom IP2xxx 8-bit microcontrollers"),
+ ENUM_ENT(EM_MAX, "MAX Processor"),
+ ENUM_ENT(EM_CR, "National Semiconductor CompactRISC"),
+ ENUM_ENT(EM_F2MC16, "Fujitsu F2MC16"),
+ ENUM_ENT(EM_MSP430, "Texas Instruments msp430 microcontroller"),
+ ENUM_ENT(EM_BLACKFIN, "Analog Devices Blackfin"),
+ ENUM_ENT(EM_SE_C33, "S1C33 Family of Seiko Epson processors"),
+ ENUM_ENT(EM_SEP, "Sharp embedded microprocessor"),
+ ENUM_ENT(EM_ARCA, "Arca RISC microprocessor"),
+ ENUM_ENT(EM_UNICORE, "Unicore"),
+ ENUM_ENT(EM_EXCESS, "eXcess 16/32/64-bit configurable embedded CPU"),
+ ENUM_ENT(EM_DXP, "Icera Semiconductor Inc. Deep Execution Processor"),
+ ENUM_ENT(EM_ALTERA_NIOS2, "Altera Nios"),
+ ENUM_ENT(EM_CRX, "National Semiconductor CRX microprocessor"),
+ ENUM_ENT(EM_XGATE, "Motorola XGATE embedded processor"),
+ ENUM_ENT(EM_C166, "Infineon Technologies xc16x"),
+ ENUM_ENT(EM_M16C, "Renesas M16C"),
+ ENUM_ENT(EM_DSPIC30F, "Microchip Technology dsPIC30F Digital Signal Controller"),
+ ENUM_ENT(EM_CE, "Freescale Communication Engine RISC core"),
+ ENUM_ENT(EM_M32C, "Renesas M32C"),
+ ENUM_ENT(EM_TSK3000, "Altium TSK3000 core"),
+ ENUM_ENT(EM_RS08, "Freescale RS08 embedded processor"),
+ ENUM_ENT(EM_SHARC, "EM_SHARC"),
+ ENUM_ENT(EM_ECOG2, "Cyan Technology eCOG2 microprocessor"),
+ ENUM_ENT(EM_SCORE7, "SUNPLUS S+Core"),
+ ENUM_ENT(EM_DSP24, "New Japan Radio (NJR) 24-bit DSP Processor"),
+ ENUM_ENT(EM_VIDEOCORE3, "Broadcom VideoCore III processor"),
+ ENUM_ENT(EM_LATTICEMICO32, "Lattice Mico32"),
+ ENUM_ENT(EM_SE_C17, "Seiko Epson C17 family"),
+ ENUM_ENT(EM_TI_C6000, "Texas Instruments TMS320C6000 DSP family"),
+ ENUM_ENT(EM_TI_C2000, "Texas Instruments TMS320C2000 DSP family"),
+ ENUM_ENT(EM_TI_C5500, "Texas Instruments TMS320C55x DSP family"),
+ ENUM_ENT(EM_MMDSP_PLUS, "STMicroelectronics 64bit VLIW Data Signal Processor"),
+ ENUM_ENT(EM_CYPRESS_M8C, "Cypress M8C microprocessor"),
+ ENUM_ENT(EM_R32C, "Renesas R32C series microprocessors"),
+ ENUM_ENT(EM_TRIMEDIA, "NXP Semiconductors TriMedia architecture family"),
+ ENUM_ENT(EM_HEXAGON, "Qualcomm Hexagon"),
+ ENUM_ENT(EM_8051, "Intel 8051 and variants"),
+ ENUM_ENT(EM_STXP7X, "STMicroelectronics STxP7x family"),
+ ENUM_ENT(EM_NDS32, "Andes Technology compact code size embedded RISC processor family"),
+ ENUM_ENT(EM_ECOG1, "Cyan Technology eCOG1 microprocessor"),
+ ENUM_ENT(EM_ECOG1X, "Cyan Technology eCOG1X family"),
+ ENUM_ENT(EM_MAXQ30, "Dallas Semiconductor MAXQ30 Core microcontrollers"),
+ ENUM_ENT(EM_XIMO16, "New Japan Radio (NJR) 16-bit DSP Processor"),
+ ENUM_ENT(EM_MANIK, "M2000 Reconfigurable RISC Microprocessor"),
+ ENUM_ENT(EM_CRAYNV2, "Cray Inc. NV2 vector architecture"),
+ ENUM_ENT(EM_RX, "Renesas RX"),
+ ENUM_ENT(EM_METAG, "Imagination Technologies Meta processor architecture"),
+ ENUM_ENT(EM_MCST_ELBRUS, "MCST Elbrus general purpose hardware architecture"),
+ ENUM_ENT(EM_ECOG16, "Cyan Technology eCOG16 family"),
+ ENUM_ENT(EM_CR16, "Xilinx MicroBlaze"),
+ ENUM_ENT(EM_ETPU, "Freescale Extended Time Processing Unit"),
+ ENUM_ENT(EM_SLE9X, "Infineon Technologies SLE9X core"),
+ ENUM_ENT(EM_L10M, "EM_L10M"),
+ ENUM_ENT(EM_K10M, "EM_K10M"),
+ ENUM_ENT(EM_AARCH64, "AArch64"),
+ ENUM_ENT(EM_AVR32, "Atmel AVR 8-bit microcontroller"),
+ ENUM_ENT(EM_STM8, "STMicroeletronics STM8 8-bit microcontroller"),
+ ENUM_ENT(EM_TILE64, "Tilera TILE64 multicore architecture family"),
+ ENUM_ENT(EM_TILEPRO, "Tilera TILEPro multicore architecture family"),
+ ENUM_ENT(EM_CUDA, "NVIDIA CUDA architecture"),
+ ENUM_ENT(EM_TILEGX, "Tilera TILE-Gx multicore architecture family"),
+ ENUM_ENT(EM_CLOUDSHIELD, "EM_CLOUDSHIELD"),
+ ENUM_ENT(EM_COREA_1ST, "EM_COREA_1ST"),
+ ENUM_ENT(EM_COREA_2ND, "EM_COREA_2ND"),
+ ENUM_ENT(EM_ARC_COMPACT2, "EM_ARC_COMPACT2"),
+ ENUM_ENT(EM_OPEN8, "EM_OPEN8"),
+ ENUM_ENT(EM_RL78, "Renesas RL78"),
+ ENUM_ENT(EM_VIDEOCORE5, "Broadcom VideoCore V processor"),
+ ENUM_ENT(EM_78KOR, "EM_78KOR"),
+ ENUM_ENT(EM_56800EX, "EM_56800EX"),
+ ENUM_ENT(EM_AMDGPU, "EM_AMDGPU"),
+ ENUM_ENT(EM_WEBASSEMBLY, "EM_WEBASSEMBLY"),
+ ENUM_ENT(EM_LANAI, "EM_LANAI"),
+ ENUM_ENT(EM_BPF, "EM_BPF"),
};
static const EnumEntry<unsigned> ElfSymbolBindings[] = {
- { "Local", ELF::STB_LOCAL },
- { "Global", ELF::STB_GLOBAL },
- { "Weak", ELF::STB_WEAK },
- { "Unique", ELF::STB_GNU_UNIQUE }
-};
+ {"Local", "LOCAL", ELF::STB_LOCAL},
+ {"Global", "GLOBAL", ELF::STB_GLOBAL},
+ {"Weak", "WEAK", ELF::STB_WEAK},
+ {"Unique", "UNIQUE", ELF::STB_GNU_UNIQUE}};
+
+static const EnumEntry<unsigned> ElfSymbolVisibilities[] = {
+ {"DEFAULT", "DEFAULT", ELF::STV_DEFAULT},
+ {"INTERNAL", "INTERNAL", ELF::STV_INTERNAL},
+ {"HIDDEN", "HIDDEN", ELF::STV_HIDDEN},
+ {"PROTECTED", "PROTECTED", ELF::STV_PROTECTED}};
static const EnumEntry<unsigned> ElfSymbolTypes[] = {
- { "None", ELF::STT_NOTYPE },
- { "Object", ELF::STT_OBJECT },
- { "Function", ELF::STT_FUNC },
- { "Section", ELF::STT_SECTION },
- { "File", ELF::STT_FILE },
- { "Common", ELF::STT_COMMON },
- { "TLS", ELF::STT_TLS },
- { "GNU_IFunc", ELF::STT_GNU_IFUNC }
-};
+ {"None", "NOTYPE", ELF::STT_NOTYPE},
+ {"Object", "OBJECT", ELF::STT_OBJECT},
+ {"Function", "FUNC", ELF::STT_FUNC},
+ {"Section", "SECTION", ELF::STT_SECTION},
+ {"File", "FILE", ELF::STT_FILE},
+ {"Common", "COMMON", ELF::STT_COMMON},
+ {"TLS", "TLS", ELF::STT_TLS},
+ {"GNU_IFunc", "IFUNC", ELF::STT_GNU_IFUNC}};
static const EnumEntry<unsigned> AMDGPUSymbolTypes[] = {
{ "AMDGPU_HSA_KERNEL", ELF::STT_AMDGPU_HSA_KERNEL },
@@ -786,27 +1037,92 @@ static const char *getElfSectionType(unsigned Arch, unsigned Type) {
}
}
+static const char *getGroupType(uint32_t Flag) {
+ if (Flag & ELF::GRP_COMDAT)
+ return "COMDAT";
+ else
+ return "(unknown)";
+}
+
static const EnumEntry<unsigned> ElfSectionFlags[] = {
- LLVM_READOBJ_ENUM_ENT(ELF, SHF_WRITE ),
- LLVM_READOBJ_ENUM_ENT(ELF, SHF_ALLOC ),
- LLVM_READOBJ_ENUM_ENT(ELF, SHF_EXCLUDE ),
- LLVM_READOBJ_ENUM_ENT(ELF, SHF_EXECINSTR ),
- LLVM_READOBJ_ENUM_ENT(ELF, SHF_MERGE ),
- LLVM_READOBJ_ENUM_ENT(ELF, SHF_STRINGS ),
- LLVM_READOBJ_ENUM_ENT(ELF, SHF_INFO_LINK ),
- LLVM_READOBJ_ENUM_ENT(ELF, SHF_LINK_ORDER ),
- LLVM_READOBJ_ENUM_ENT(ELF, SHF_OS_NONCONFORMING),
- LLVM_READOBJ_ENUM_ENT(ELF, SHF_GROUP ),
- LLVM_READOBJ_ENUM_ENT(ELF, SHF_TLS ),
+ ENUM_ENT(SHF_WRITE, "W"),
+ ENUM_ENT(SHF_ALLOC, "A"),
+ ENUM_ENT(SHF_EXCLUDE, "E"),
+ ENUM_ENT(SHF_EXECINSTR, "X"),
+ ENUM_ENT(SHF_MERGE, "M"),
+ ENUM_ENT(SHF_STRINGS, "S"),
+ ENUM_ENT(SHF_INFO_LINK, "I"),
+ ENUM_ENT(SHF_LINK_ORDER, "L"),
+ ENUM_ENT(SHF_OS_NONCONFORMING, "o"),
+ ENUM_ENT(SHF_GROUP, "G"),
+ ENUM_ENT(SHF_TLS, "T"),
+ ENUM_ENT(SHF_MASKOS, "o"),
+ ENUM_ENT(SHF_MASKPROC, "p"),
+ ENUM_ENT_1(SHF_COMPRESSED),
+};
+
+static const EnumEntry<unsigned> ElfXCoreSectionFlags[] = {
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, XCORE_SHF_DP_SECTION)
+};
+
+static const EnumEntry<unsigned> ElfAMDGPUSectionFlags[] = {
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 EnumEntry<unsigned> ElfHexagonSectionFlags[] = {
+ LLVM_READOBJ_ENUM_ENT(ELF, SHF_HEX_GPREL)
+};
+
+static const EnumEntry<unsigned> ElfMipsSectionFlags[] = {
+ LLVM_READOBJ_ENUM_ENT(ELF, SHF_MIPS_NODUPES),
+ LLVM_READOBJ_ENUM_ENT(ELF, SHF_MIPS_NAMES ),
+ LLVM_READOBJ_ENUM_ENT(ELF, SHF_MIPS_LOCAL ),
+ LLVM_READOBJ_ENUM_ENT(ELF, SHF_MIPS_NOSTRIP),
+ LLVM_READOBJ_ENUM_ENT(ELF, SHF_MIPS_GPREL ),
+ LLVM_READOBJ_ENUM_ENT(ELF, SHF_MIPS_MERGE ),
+ LLVM_READOBJ_ENUM_ENT(ELF, SHF_MIPS_ADDR ),
+ LLVM_READOBJ_ENUM_ENT(ELF, SHF_MIPS_STRING )
+};
+
+static const EnumEntry<unsigned> ElfX86_64SectionFlags[] = {
+ LLVM_READOBJ_ENUM_ENT(ELF, SHF_X86_64_LARGE)
+};
+
+static std::string getGNUFlags(uint64_t Flags) {
+ std::string Str;
+ for (auto Entry : ElfSectionFlags) {
+ uint64_t Flag = Entry.Value & Flags;
+ Flags &= ~Entry.Value;
+ switch (Flag) {
+ case ELF::SHF_WRITE:
+ case ELF::SHF_ALLOC:
+ case ELF::SHF_EXECINSTR:
+ case ELF::SHF_MERGE:
+ case ELF::SHF_STRINGS:
+ case ELF::SHF_INFO_LINK:
+ case ELF::SHF_LINK_ORDER:
+ case ELF::SHF_OS_NONCONFORMING:
+ case ELF::SHF_GROUP:
+ case ELF::SHF_TLS:
+ case ELF::SHF_EXCLUDE:
+ Str += Entry.AltName;
+ break;
+ default:
+ if (Flag & ELF::SHF_MASKOS)
+ Str += "o";
+ else if (Flag & ELF::SHF_MASKPROC)
+ Str += "p";
+ else if (Flag)
+ Str += "x";
+ }
+ }
+ return Str;
+}
+
static const char *getElfSegmentType(unsigned Arch, unsigned Type) {
// Check potentially overlapped processor-specific
// program header type.
@@ -851,6 +1167,53 @@ static const char *getElfSegmentType(unsigned Arch, unsigned Type) {
}
}
+static std::string getElfPtType(unsigned Arch, unsigned Type) {
+ switch (Type) {
+ LLVM_READOBJ_PHDR_ENUM(ELF, PT_NULL)
+ LLVM_READOBJ_PHDR_ENUM(ELF, PT_LOAD)
+ LLVM_READOBJ_PHDR_ENUM(ELF, PT_DYNAMIC)
+ LLVM_READOBJ_PHDR_ENUM(ELF, PT_INTERP)
+ LLVM_READOBJ_PHDR_ENUM(ELF, PT_NOTE)
+ LLVM_READOBJ_PHDR_ENUM(ELF, PT_SHLIB)
+ LLVM_READOBJ_PHDR_ENUM(ELF, PT_PHDR)
+ LLVM_READOBJ_PHDR_ENUM(ELF, PT_TLS)
+ LLVM_READOBJ_PHDR_ENUM(ELF, PT_GNU_EH_FRAME)
+ LLVM_READOBJ_PHDR_ENUM(ELF, PT_SUNW_UNWIND)
+ LLVM_READOBJ_PHDR_ENUM(ELF, PT_GNU_STACK)
+ LLVM_READOBJ_PHDR_ENUM(ELF, PT_GNU_RELRO)
+ default:
+ // All machine specific PT_* types
+ 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);
+ }
+ return "";
+ case ELF::EM_ARM:
+ if (Type == ELF::PT_ARM_EXIDX)
+ return "EXIDX";
+ return "";
+ case ELF::EM_MIPS:
+ case ELF::EM_MIPS_RS3_LE:
+ switch (Type) {
+ case PT_MIPS_REGINFO:
+ return "REGINFO";
+ case PT_MIPS_RTPROC:
+ return "RTPROC";
+ case PT_MIPS_OPTIONS:
+ return "OPTIONS";
+ case PT_MIPS_ABIFLAGS:
+ return "ABIFLAGS";
+ }
+ return "";
+ }
+ }
+ return std::string("<unknown>: ") + to_string(format_hex(Type, 1));
+}
+
static const EnumEntry<unsigned> ElfSegmentFlags[] = {
LLVM_READOBJ_ENUM_ENT(ELF, PF_X),
LLVM_READOBJ_ENUM_ENT(ELF, PF_W),
@@ -903,14 +1266,52 @@ static const EnumEntry<unsigned> ElfHeaderMipsFlags[] = {
LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ARCH_64R6)
};
+static const EnumEntry<unsigned> ElfSymOtherFlags[] = {
+ LLVM_READOBJ_ENUM_ENT(ELF, STV_INTERNAL),
+ LLVM_READOBJ_ENUM_ENT(ELF, STV_HIDDEN),
+ LLVM_READOBJ_ENUM_ENT(ELF, STV_PROTECTED)
+};
+
+static const EnumEntry<unsigned> ElfMipsSymOtherFlags[] = {
+ LLVM_READOBJ_ENUM_ENT(ELF, STO_MIPS_OPTIONAL),
+ LLVM_READOBJ_ENUM_ENT(ELF, STO_MIPS_PLT),
+ LLVM_READOBJ_ENUM_ENT(ELF, STO_MIPS_PIC),
+ LLVM_READOBJ_ENUM_ENT(ELF, STO_MIPS_MICROMIPS)
+};
+
+static const EnumEntry<unsigned> ElfMips16SymOtherFlags[] = {
+ LLVM_READOBJ_ENUM_ENT(ELF, STO_MIPS_OPTIONAL),
+ LLVM_READOBJ_ENUM_ENT(ELF, STO_MIPS_PLT),
+ LLVM_READOBJ_ENUM_ENT(ELF, STO_MIPS_MIPS16)
+};
+
+static const char *getElfMipsOptionsOdkType(unsigned Odk) {
+ switch (Odk) {
+ LLVM_READOBJ_ENUM_CASE(ELF, ODK_NULL);
+ LLVM_READOBJ_ENUM_CASE(ELF, ODK_REGINFO);
+ LLVM_READOBJ_ENUM_CASE(ELF, ODK_EXCEPTIONS);
+ LLVM_READOBJ_ENUM_CASE(ELF, ODK_PAD);
+ LLVM_READOBJ_ENUM_CASE(ELF, ODK_HWPATCH);
+ LLVM_READOBJ_ENUM_CASE(ELF, ODK_FILL);
+ LLVM_READOBJ_ENUM_CASE(ELF, ODK_TAGS);
+ LLVM_READOBJ_ENUM_CASE(ELF, ODK_HWAND);
+ LLVM_READOBJ_ENUM_CASE(ELF, ODK_HWOR);
+ LLVM_READOBJ_ENUM_CASE(ELF, ODK_GP_GROUP);
+ LLVM_READOBJ_ENUM_CASE(ELF, ODK_IDENT);
+ LLVM_READOBJ_ENUM_CASE(ELF, ODK_PAGESIZE);
+ default:
+ return "Unknown";
+ }
+}
+
template <typename ELFT>
-ELFDumper<ELFT>::ELFDumper(const ELFFile<ELFT> *Obj, StreamWriter &Writer)
+ELFDumper<ELFT>::ELFDumper(const ELFFile<ELFT> *Obj, ScopedPrinter &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;
+ DynamicTable = createDRIFrom(&Phdr, sizeof(Elf_Dyn));
continue;
}
if (Phdr.p_type != ELF::PT_LOAD || Phdr.p_filesz == 0)
@@ -918,8 +1319,54 @@ ELFDumper<ELFT>::ELFDumper(const ELFFile<ELFT> *Obj, StreamWriter &Writer)
LoadSegments.push_back(&Phdr);
}
+ for (const Elf_Shdr &Sec : Obj->sections()) {
+ switch (Sec.sh_type) {
+ case ELF::SHT_SYMTAB:
+ if (DotSymtabSec != nullptr)
+ reportError("Multilpe SHT_SYMTAB");
+ DotSymtabSec = &Sec;
+ break;
+ case ELF::SHT_DYNSYM:
+ if (DynSymRegion.Size)
+ reportError("Multilpe SHT_DYNSYM");
+ DynSymRegion = createDRIFrom(&Sec);
+ // This is only used (if Elf_Shdr present)for naming section in GNU style
+ DynSymtabName = unwrapOrError(Obj->getSectionName(&Sec));
+ break;
+ case ELF::SHT_SYMTAB_SHNDX:
+ ShndxTable = unwrapOrError(Obj->getSHNDXTable(Sec));
+ break;
+ 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;
+ }
+ }
+
+ parseDynamicTable(LoadSegments);
+
+ if (opts::Output == opts::GNU)
+ ELFDumperStyle.reset(new GNUStyle<ELFT>(Writer, this));
+ else
+ ELFDumperStyle.reset(new LLVMStyle<ELFT>(Writer, this));
+}
+
+template <typename ELFT>
+void ELFDumper<ELFT>::parseDynamicTable(
+ ArrayRef<const Elf_Phdr *> LoadSegments) {
auto toMappedAddr = [&](uint64_t VAddr) -> const uint8_t * {
- const Elf_Phdr **I = std::upper_bound(
+ const Elf_Phdr *const *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");
@@ -944,6 +1391,16 @@ ELFDumper<ELFT>::ELFDumper(const ELFFile<ELFT> *Obj, StreamWriter &Writer)
GnuHashTable =
reinterpret_cast<const Elf_GnuHash *>(toMappedAddr(Dyn.getPtr()));
break;
+ case ELF::DT_STRTAB:
+ StringTableBegin = (const char *)toMappedAddr(Dyn.getPtr());
+ break;
+ case ELF::DT_STRSZ:
+ StringTableSize = Dyn.getVal();
+ break;
+ case ELF::DT_SYMTAB:
+ DynSymRegion.Addr = toMappedAddr(Dyn.getPtr());
+ DynSymRegion.EntSize = sizeof(Elf_Sym);
+ break;
case ELF::DT_RELA:
DynRelaRegion.Addr = toMappedAddr(Dyn.getPtr());
break;
@@ -956,15 +1413,29 @@ ELFDumper<ELFT>::ELFDumper(const ELFFile<ELFT> *Obj, StreamWriter &Writer)
case ELF::DT_SONAME:
SONameOffset = Dyn.getVal();
break;
- case ELF::DT_STRTAB:
- StringTableBegin = (const char *)toMappedAddr(Dyn.getPtr());
+ case ELF::DT_REL:
+ DynRelRegion.Addr = toMappedAddr(Dyn.getPtr());
break;
- case ELF::DT_STRSZ:
- StringTableSize = Dyn.getVal();
+ case ELF::DT_RELSZ:
+ DynRelRegion.Size = Dyn.getVal();
break;
- case ELF::DT_SYMTAB:
- DynSymStart =
- reinterpret_cast<const Elf_Sym *>(toMappedAddr(Dyn.getPtr()));
+ case ELF::DT_RELENT:
+ DynRelRegion.EntSize = Dyn.getVal();
+ break;
+ case ELF::DT_PLTREL:
+ if (Dyn.getVal() == DT_REL)
+ DynPLTRelRegion.EntSize = sizeof(Elf_Rel);
+ else if (Dyn.getVal() == DT_RELA)
+ DynPLTRelRegion.EntSize = sizeof(Elf_Rela);
+ else
+ reportError(Twine("unknown DT_PLTREL value of ") +
+ Twine((uint64_t)Dyn.getVal()));
+ break;
+ case ELF::DT_JMPREL:
+ DynPLTRelRegion.Addr = toMappedAddr(Dyn.getPtr());
+ break;
+ case ELF::DT_PLTRELSZ:
+ DynPLTRelRegion.Size = Dyn.getVal();
break;
}
}
@@ -972,326 +1443,54 @@ ELFDumper<ELFT>::ELFDumper(const ELFFile<ELFT> *Obj, StreamWriter &Writer)
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);
+typename ELFDumper<ELFT>::Elf_Rel_Range ELFDumper<ELFT>::dyn_rels() const {
+ return DynRelRegion.getAsArrayRef<Elf_Rel>();
}
template <typename ELFT>
typename ELFDumper<ELFT>::Elf_Rela_Range ELFDumper<ELFT>::dyn_relas() const {
- return make_range(dyn_rela_begin(), dyn_rela_end());
+ return DynRelaRegion.getAsArrayRef<Elf_Rela>();
}
template<class ELFT>
void ELFDumper<ELFT>::printFileHeaders() {
- const Elf_Ehdr *Header = Obj->getHeader();
-
- {
- DictScope D(W, "ElfHeader");
- {
- DictScope D(W, "Ident");
- W.printBinary("Magic", makeArrayRef(Header->e_ident).slice(ELF::EI_MAG0,
- 4));
- W.printEnum ("Class", Header->e_ident[ELF::EI_CLASS],
- makeArrayRef(ElfClass));
- W.printEnum ("DataEncoding", Header->e_ident[ELF::EI_DATA],
- makeArrayRef(ElfDataEncoding));
- W.printNumber("FileVersion", Header->e_ident[ELF::EI_VERSION]);
-
- // Handle architecture specific OS/ABI values.
- if (Header->e_machine == ELF::EM_AMDGPU &&
- Header->e_ident[ELF::EI_OSABI] == ELF::ELFOSABI_AMDGPU_HSA)
- W.printHex("OS/ABI", "AMDGPU_HSA", ELF::ELFOSABI_AMDGPU_HSA);
- else
- W.printEnum ("OS/ABI", Header->e_ident[ELF::EI_OSABI],
- makeArrayRef(ElfOSABI));
- W.printNumber("ABIVersion", Header->e_ident[ELF::EI_ABIVERSION]);
- W.printBinary("Unused", makeArrayRef(Header->e_ident).slice(ELF::EI_PAD));
- }
-
- W.printEnum ("Type", Header->e_type, makeArrayRef(ElfObjectFileType));
- W.printEnum ("Machine", Header->e_machine, makeArrayRef(ElfMachineType));
- W.printNumber("Version", Header->e_version);
- W.printHex ("Entry", Header->e_entry);
- W.printHex ("ProgramHeaderOffset", Header->e_phoff);
- W.printHex ("SectionHeaderOffset", Header->e_shoff);
- if (Header->e_machine == EM_MIPS)
- W.printFlags("Flags", Header->e_flags, makeArrayRef(ElfHeaderMipsFlags),
- unsigned(ELF::EF_MIPS_ARCH), unsigned(ELF::EF_MIPS_ABI),
- unsigned(ELF::EF_MIPS_MACH));
- else
- W.printFlags("Flags", Header->e_flags);
- W.printNumber("HeaderSize", Header->e_ehsize);
- W.printNumber("ProgramHeaderEntrySize", Header->e_phentsize);
- W.printNumber("ProgramHeaderCount", Header->e_phnum);
- W.printNumber("SectionHeaderEntrySize", Header->e_shentsize);
- W.printNumber("SectionHeaderCount", Header->e_shnum);
- W.printNumber("StringTableSectionIndex", Header->e_shstrndx);
- }
+ ELFDumperStyle->printFileHeaders(Obj);
}
template<class ELFT>
void ELFDumper<ELFT>::printSections() {
- ListScope SectionsD(W, "Sections");
-
- int SectionIndex = -1;
- for (const Elf_Shdr &Sec : Obj->sections()) {
- ++SectionIndex;
-
- StringRef Name = errorOrDefault(Obj->getSectionName(&Sec));
-
- DictScope SectionD(W, "Section");
- W.printNumber("Index", SectionIndex);
- W.printNumber("Name", Name, Sec.sh_name);
- W.printHex("Type",
- getElfSectionType(Obj->getHeader()->e_machine, Sec.sh_type),
- Sec.sh_type);
- W.printFlags("Flags", Sec.sh_flags, makeArrayRef(ElfSectionFlags));
- W.printHex("Address", Sec.sh_addr);
- W.printHex("Offset", Sec.sh_offset);
- W.printNumber("Size", Sec.sh_size);
- W.printNumber("Link", Sec.sh_link);
- W.printNumber("Info", Sec.sh_info);
- W.printNumber("AddressAlignment", Sec.sh_addralign);
- W.printNumber("EntrySize", Sec.sh_entsize);
-
- if (opts::SectionRelocations) {
- ListScope D(W, "Relocations");
- printRelocations(&Sec);
- }
-
- if (opts::SectionSymbols) {
- ListScope D(W, "Symbols");
- 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, Symtab, StrTable, false);
- }
- }
-
- if (opts::SectionData && Sec.sh_type != ELF::SHT_NOBITS) {
- ArrayRef<uint8_t> Data = errorOrDefault(Obj->getSectionContents(&Sec));
- W.printBinaryBlock("SectionData",
- StringRef((const char *)Data.data(), Data.size()));
- }
- }
+ ELFDumperStyle->printSections(Obj);
}
template<class ELFT>
void ELFDumper<ELFT>::printRelocations() {
- ListScope D(W, "Relocations");
-
- int SectionNumber = -1;
- for (const Elf_Shdr &Sec : Obj->sections()) {
- ++SectionNumber;
-
- if (Sec.sh_type != ELF::SHT_REL && Sec.sh_type != ELF::SHT_RELA)
- continue;
-
- StringRef Name = errorOrDefault(Obj->getSectionName(&Sec));
-
- W.startLine() << "Section (" << SectionNumber << ") " << Name << " {\n";
- W.indent();
-
- printRelocations(&Sec);
-
- W.unindent();
- W.startLine() << "}\n";
- }
-}
-
-template<class ELFT>
-void ELFDumper<ELFT>::printDynamicRelocations() {
- W.startLine() << "Dynamic Relocations {\n";
- W.indent();
- for (const Elf_Rela &Rel : dyn_relas()) {
- SmallString<32> RelocName;
- Obj->getRelocationTypeName(Rel.getType(Obj->isMips64EL()), RelocName);
- StringRef SymbolName;
- 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", Rel.r_offset);
- W.printNumber("Type", RelocName, (int)Rel.getType(Obj->isMips64EL()));
- W.printString("Symbol", SymbolName.size() > 0 ? SymbolName : "-");
- W.printHex("Addend", Rel.r_addend);
- }
- else {
- raw_ostream& OS = W.startLine();
- OS << W.hex(Rel.r_offset) << " " << RelocName << " "
- << (SymbolName.size() > 0 ? SymbolName : "-") << " "
- << W.hex(Rel.r_addend) << "\n";
- }
- }
- W.unindent();
- W.startLine() << "}\n";
-}
-
-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 (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(Rela, SymTab);
- }
- break;
- case ELF::SHT_RELA:
- for (const Elf_Rela &R : Obj->relas(Sec))
- printRelocation(R, SymTab);
- break;
- }
+ ELFDumperStyle->printRelocations(Obj);
}
-template <class ELFT>
-void ELFDumper<ELFT>::printRelocation(Elf_Rela Rel, const Elf_Shdr *SymTab) {
- SmallString<32> RelocName;
- Obj->getRelocationTypeName(Rel.getType(Obj->isMips64EL()), RelocName);
- StringRef TargetName;
- 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) {
- DictScope Group(W, "Relocation");
- W.printHex("Offset", Rel.r_offset);
- W.printNumber("Type", RelocName, (int)Rel.getType(Obj->isMips64EL()));
- W.printNumber("Symbol", TargetName.size() > 0 ? TargetName : "-",
- Rel.getSymbol(Obj->isMips64EL()));
- W.printHex("Addend", Rel.r_addend);
- } else {
- raw_ostream& OS = W.startLine();
- OS << W.hex(Rel.r_offset) << " " << RelocName << " "
- << (TargetName.size() > 0 ? TargetName : "-") << " "
- << W.hex(Rel.r_addend) << "\n";
- }
+template <class ELFT> void ELFDumper<ELFT>::printProgramHeaders() {
+ ELFDumperStyle->printProgramHeaders(Obj);
}
-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>::printDynamicRelocations() {
+ ELFDumperStyle->printDynamicRelocations(Obj);
}
template<class ELFT>
void ELFDumper<ELFT>::printSymbols() {
- ListScope Group(W, "Symbols");
- printSymbolsHelper(false);
+ ELFDumperStyle->printSymbols(Obj);
}
template<class ELFT>
void ELFDumper<ELFT>::printDynamicSymbols() {
- ListScope Group(W, "DynamicSymbols");
- printSymbolsHelper(true);
+ ELFDumperStyle->printDynamicSymbols(Obj);
}
-template <class ELFT>
-void ELFDumper<ELFT>::printSymbol(const Elf_Sym *Symbol, const Elf_Shdr *SymTab,
- StringRef StrTable, bool IsDynamic) {
- unsigned SectionIndex = 0;
- StringRef SectionName;
- 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);
- W.printHex ("Value", Symbol->st_value);
- W.printNumber("Size", Symbol->st_size);
- W.printEnum ("Binding", Symbol->getBinding(),
- makeArrayRef(ElfSymbolBindings));
- 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);
+template <class ELFT> void ELFDumper<ELFT>::printHashHistogram() {
+ ELFDumperStyle->printHashHistogram(Obj);
}
-
#define LLVM_READOBJ_TYPE_CASE(name) \
case DT_##name: return #name
@@ -1336,8 +1535,11 @@ static const char *getTypeString(uint64_t Type) {
LLVM_READOBJ_TYPE_CASE(VERNEED);
LLVM_READOBJ_TYPE_CASE(VERNEEDNUM);
LLVM_READOBJ_TYPE_CASE(VERSYM);
+ LLVM_READOBJ_TYPE_CASE(RELACOUNT);
LLVM_READOBJ_TYPE_CASE(RELCOUNT);
LLVM_READOBJ_TYPE_CASE(GNU_HASH);
+ LLVM_READOBJ_TYPE_CASE(TLSDESC_PLT);
+ LLVM_READOBJ_TYPE_CASE(TLSDESC_GOT);
LLVM_READOBJ_TYPE_CASE(MIPS_RLD_VERSION);
LLVM_READOBJ_TYPE_CASE(MIPS_RLD_MAP_REL);
LLVM_READOBJ_TYPE_CASE(MIPS_FLAGS);
@@ -1444,6 +1646,7 @@ StringRef ELFDumper<ELFT>::getDynamicString(uint64_t Value) const {
template <class ELFT>
void ELFDumper<ELFT>::printValue(uint64_t Type, uint64_t Value) {
raw_ostream &OS = W.getOStream();
+ const char* ConvChar = (opts::Output == opts::GNU) ? "0x%" PRIx64 : "0x%" PRIX64;
switch (Type) {
case DT_PLTREL:
if (Value == DT_REL) {
@@ -1478,8 +1681,9 @@ void ELFDumper<ELFT>::printValue(uint64_t Type, uint64_t Value) {
case DT_MIPS_RLD_MAP_REL:
case DT_MIPS_PLTGOT:
case DT_MIPS_OPTIONS:
- OS << format("0x%" PRIX64, Value);
+ OS << format(ConvChar, Value);
break;
+ case DT_RELACOUNT:
case DT_RELCOUNT:
case DT_VERDEFNUM:
case DT_VERNEEDNUM:
@@ -1521,7 +1725,7 @@ void ELFDumper<ELFT>::printValue(uint64_t Type, uint64_t Value) {
printFlags(Value, makeArrayRef(ElfDynamicDTFlags1), OS);
break;
default:
- OS << format("0x%" PRIX64, Value);
+ OS << format(ConvChar, Value);
break;
}
}
@@ -1545,8 +1749,8 @@ template <> void ELFDumper<ELFType<support::little, false>>::printUnwindInfo() {
template<class ELFT>
void ELFDumper<ELFT>::printDynamicTable() {
- auto I = dynamic_table_begin();
- auto E = dynamic_table_end();
+ auto I = dynamic_table().begin();
+ auto E = dynamic_table().end();
if (I == E)
return;
@@ -1574,7 +1778,7 @@ void ELFDumper<ELFT>::printDynamicTable() {
const Elf_Dyn &Entry = *I;
uintX_t Tag = Entry.getTag();
++I;
- W.startLine() << " " << format_hex(Tag, Is64 ? 18 : 10, true) << " "
+ W.startLine() << " " << format_hex(Tag, Is64 ? 18 : 10, opts::Output != opts::GNU) << " "
<< format("%-21s", getTypeString(Tag));
printValue(Tag, Entry.getVal());
OS << "\n";
@@ -1601,24 +1805,6 @@ void ELFDumper<ELFT>::printNeededLibraries() {
}
}
-template<class ELFT>
-void ELFDumper<ELFT>::printProgramHeaders() {
- ListScope L(W, "ProgramHeaders");
-
- for (const Elf_Phdr &Phdr : Obj->program_headers()) {
- DictScope P(W, "ProgramHeader");
- 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() {
@@ -1642,10 +1828,11 @@ void ELFDumper<ELFT>::printGnuHashTable() {
W.printNumber("Shift Count", GnuHashTable->shift2);
W.printHexList("Bloom Filter", GnuHashTable->filter());
W.printList("Buckets", GnuHashTable->buckets());
- if (!DotDynSymSec)
+ Elf_Sym_Range Syms = dynamic_symbols();
+ unsigned NumSyms = std::distance(Syms.begin(), Syms.end());
+ if (!NumSyms)
reportError("No dynamic symbol section");
- W.printHexList("Values",
- GnuHashTable->values(DotDynSymSec->getEntityCount()));
+ W.printHexList("Values", GnuHashTable->values(NumSyms));
}
template <typename ELFT> void ELFDumper<ELFT>::printLoadName() {
@@ -1669,21 +1856,18 @@ template <> void ELFDumper<ELFType<support::little, false>>::printAttributes() {
if (Sec.sh_type != ELF::SHT_ARM_ATTRIBUTES)
continue;
- ErrorOr<ArrayRef<uint8_t>> Contents = Obj->getSectionContents(&Sec);
- if (!Contents)
- continue;
-
- if ((*Contents)[0] != ARMBuildAttrs::Format_Version) {
- errs() << "unrecognised FormatVersion: 0x" << utohexstr((*Contents)[0])
+ ArrayRef<uint8_t> Contents = unwrapOrError(Obj->getSectionContents(&Sec));
+ if (Contents[0] != ARMBuildAttrs::Format_Version) {
+ errs() << "unrecognised FormatVersion: 0x" << utohexstr(Contents[0])
<< '\n';
continue;
}
- W.printHex("FormatVersion", (*Contents)[0]);
- if (Contents->size() == 1)
+ W.printHex("FormatVersion", Contents[0]);
+ if (Contents.size() == 1)
continue;
- ARMAttributeParser(W).Parse(*Contents);
+ ARMAttributeParser(W).Parse(Contents);
}
}
}
@@ -1700,7 +1884,7 @@ public:
typedef typename ELFO::Elf_Rela Elf_Rela;
MipsGOTParser(ELFDumper<ELFT> *Dumper, const ELFO *Obj,
- Elf_Dyn_Range DynTable, StreamWriter &W);
+ Elf_Dyn_Range DynTable, ScopedPrinter &W);
void parseGOT();
void parsePLT();
@@ -1708,7 +1892,7 @@ public:
private:
ELFDumper<ELFT> *Dumper;
const ELFO *Obj;
- StreamWriter &W;
+ ScopedPrinter &W;
llvm::Optional<uint64_t> DtPltGot;
llvm::Optional<uint64_t> DtLocalGotNum;
llvm::Optional<uint64_t> DtGotSym;
@@ -1733,7 +1917,7 @@ private:
template <class ELFT>
MipsGOTParser<ELFT>::MipsGOTParser(ELFDumper<ELFT> *Dumper, const ELFO *Obj,
- Elf_Dyn_Range DynTable, StreamWriter &W)
+ Elf_Dyn_Range DynTable, ScopedPrinter &W)
: Dumper(Dumper), Obj(Obj), W(W) {
for (const auto &Entry : DynTable) {
switch (Entry.getTag()) {
@@ -1773,44 +1957,33 @@ template <class ELFT> void MipsGOTParser<ELFT>::parseGOT() {
return;
}
- const Elf_Shdr *GOTShdr = findSectionByAddress(Obj, *DtPltGot);
- if (!GOTShdr) {
- W.startLine() << "There is no .got section in the file.\n";
- return;
- }
-
- ErrorOr<ArrayRef<uint8_t>> GOT = Obj->getSectionContents(GOTShdr);
- if (!GOT) {
- W.startLine() << "The .got section is empty.\n";
- return;
- }
-
- if (*DtLocalGotNum > getGOTTotal(*GOT)) {
- W.startLine() << "MIPS_LOCAL_GOTNO exceeds a number of GOT entries.\n";
- return;
- }
-
- 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);
+ StringRef StrTable = Dumper->getDynamicStringTable();
+ const Elf_Sym *DynSymBegin = Dumper->dynamic_symbols().begin();
+ const Elf_Sym *DynSymEnd = Dumper->dynamic_symbols().end();
std::size_t DynSymTotal = std::size_t(std::distance(DynSymBegin, DynSymEnd));
- if (*DtGotSym > DynSymTotal) {
- W.startLine() << "MIPS_GOTSYM exceeds a number of dynamic symbols.\n";
- return;
- }
+ if (*DtGotSym > DynSymTotal)
+ report_fatal_error("MIPS_GOTSYM exceeds a number of dynamic symbols");
std::size_t GlobalGotNum = DynSymTotal - *DtGotSym;
- if (*DtLocalGotNum + GlobalGotNum > getGOTTotal(*GOT)) {
- W.startLine() << "Number of global GOT entries exceeds the size of GOT.\n";
+ if (*DtLocalGotNum + GlobalGotNum == 0) {
+ W.startLine() << "GOT is empty.\n";
return;
}
- const GOTEntry *GotBegin = makeGOTIter(*GOT, 0);
- const GOTEntry *GotLocalEnd = makeGOTIter(*GOT, *DtLocalGotNum);
+ const Elf_Shdr *GOTShdr = findNotEmptySectionByAddress(Obj, *DtPltGot);
+ if (!GOTShdr)
+ report_fatal_error("There is no not empty GOT section at 0x" +
+ Twine::utohexstr(*DtPltGot));
+
+ ArrayRef<uint8_t> GOT = unwrapOrError(Obj->getSectionContents(GOTShdr));
+
+ if (*DtLocalGotNum + GlobalGotNum > getGOTTotal(GOT))
+ report_fatal_error("Number of GOT entries exceeds the size of GOT section");
+
+ const GOTEntry *GotBegin = makeGOTIter(GOT, 0);
+ const GOTEntry *GotLocalEnd = makeGOTIter(GOT, *DtLocalGotNum);
const GOTEntry *It = GotBegin;
DictScope GS(W, "Primary GOT");
@@ -1842,16 +2015,16 @@ template <class ELFT> void MipsGOTParser<ELFT>::parseGOT() {
ListScope GS(W, "Global entries");
const GOTEntry *GotGlobalEnd =
- makeGOTIter(*GOT, *DtLocalGotNum + GlobalGotNum);
+ 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++,
- *StrTable, true);
+ printGlobalGotEntry(GOTShdr->sh_addr, GotBegin, It, GotDynSym++, StrTable,
+ true);
}
}
- std::size_t SpecGotNum = getGOTTotal(*GOT) - *DtLocalGotNum - GlobalGotNum;
+ std::size_t SpecGotNum = getGOTTotal(GOT) - *DtLocalGotNum - GlobalGotNum;
W.printNumber("Number of TLS and multi-GOT entries", uint64_t(SpecGotNum));
}
@@ -1865,31 +2038,22 @@ template <class ELFT> void MipsGOTParser<ELFT>::parsePLT() {
return;
}
- const Elf_Shdr *PLTShdr = findSectionByAddress(Obj, *DtMipsPltGot);
- if (!PLTShdr) {
- W.startLine() << "There is no .got.plt section in the file.\n";
- return;
- }
- ErrorOr<ArrayRef<uint8_t>> PLT = Obj->getSectionContents(PLTShdr);
- if (!PLT) {
- W.startLine() << "The .got.plt section is empty.\n";
- return;
- }
+ const Elf_Shdr *PLTShdr = findNotEmptySectionByAddress(Obj, *DtMipsPltGot);
+ if (!PLTShdr)
+ report_fatal_error("There is no not empty PLTGOT section at 0x " +
+ Twine::utohexstr(*DtMipsPltGot));
+ ArrayRef<uint8_t> PLT = unwrapOrError(Obj->getSectionContents(PLTShdr));
- const Elf_Shdr *PLTRelShdr = findSectionByAddress(Obj, *DtJmpRel);
- if (!PLTShdr) {
- 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());
+ const Elf_Shdr *PLTRelShdr = findNotEmptySectionByAddress(Obj, *DtJmpRel);
+ if (!PLTRelShdr)
+ report_fatal_error("There is no not empty RELPLT section at 0x" +
+ Twine::utohexstr(*DtJmpRel));
+ const Elf_Shdr *SymTable =
+ unwrapOrError(Obj->getSection(PLTRelShdr->sh_link));
+ StringRef StrTable = unwrapOrError(Obj->getStringTableForSymtab(*SymTable));
- const GOTEntry *PLTBegin = makeGOTIter(*PLT, 0);
- const GOTEntry *PLTEnd = makeGOTIter(*PLT, getGOTTotal(*PLT));
+ const GOTEntry *PLTBegin = makeGOTIter(PLT, 0);
+ const GOTEntry *PLTEnd = makeGOTIter(PLT, getGOTTotal(PLT));
const GOTEntry *It = PLTBegin;
DictScope GS(W, "PLT GOT");
@@ -1908,7 +2072,7 @@ template <class ELFT> void MipsGOTParser<ELFT>::parsePLT() {
*RE = Obj->rel_end(PLTRelShdr);
RI != RE && It != PLTEnd; ++RI, ++It) {
const Elf_Sym *Sym = Obj->getRelocationSymbol(&*RI, SymTable);
- printPLTEntry(PLTShdr->sh_addr, PLTBegin, It, *StrTable, Sym);
+ printPLTEntry(PLTShdr->sh_addr, PLTBegin, It, StrTable, Sym);
}
break;
case ELF::SHT_RELA:
@@ -1916,7 +2080,7 @@ template <class ELFT> void MipsGOTParser<ELFT>::parsePLT() {
*RE = Obj->rela_end(PLTRelShdr);
RI != RE && It != PLTEnd; ++RI, ++It) {
const Elf_Sym *Sym = Obj->getRelocationSymbol(&*RI, SymTable);
- printPLTEntry(PLTShdr->sh_addr, PLTBegin, It, *StrTable, Sym);
+ printPLTEntry(PLTShdr->sh_addr, PLTBegin, It, StrTable, Sym);
}
break;
}
@@ -1956,7 +2120,7 @@ void MipsGOTParser<ELFT>::printGlobalGotEntry(
unsigned SectionIndex = 0;
StringRef SectionName;
- getSectionNameIndex(*Obj, Sym, Dumper->getDotDynSymSec(),
+ getSectionNameIndex(*Obj, Sym, Dumper->dynamic_symbols().begin(),
Dumper->getShndxTable(), SectionName, SectionIndex);
W.printHex("Section", SectionName, SectionIndex);
@@ -1990,7 +2154,7 @@ void MipsGOTParser<ELFT>::printPLTEntry(uint64_t PLTAddr,
unsigned SectionIndex = 0;
StringRef SectionName;
- getSectionNameIndex(*Obj, Sym, Dumper->getDotDynSymSec(),
+ getSectionNameIndex(*Obj, Sym, Dumper->dynamic_symbols().begin(),
Dumper->getShndxTable(), SectionName, SectionIndex);
W.printHex("Section", SectionName, SectionIndex);
@@ -2086,17 +2250,13 @@ template <class ELFT> void ELFDumper<ELFT>::printMipsABIFlags() {
W.startLine() << "There is no .MIPS.abiflags section in the file.\n";
return;
}
- ErrorOr<ArrayRef<uint8_t>> Sec = Obj->getSectionContents(Shdr);
- if (!Sec) {
- W.startLine() << "The .MIPS.abiflags section is empty.\n";
- return;
- }
- if (Sec->size() != sizeof(Elf_Mips_ABIFlags<ELFT>)) {
+ ArrayRef<uint8_t> Sec = unwrapOrError(Obj->getSectionContents(Shdr));
+ if (Sec.size() != sizeof(Elf_Mips_ABIFlags<ELFT>)) {
W.startLine() << "The .MIPS.abiflags section has a wrong size.\n";
return;
}
- auto *Flags = reinterpret_cast<const Elf_Mips_ABIFlags<ELFT> *>(Sec->data());
+ auto *Flags = reinterpret_cast<const Elf_Mips_ABIFlags<ELFT> *>(Sec.data());
raw_ostream &OS = W.getOStream();
DictScope GS(W, "MIPS ABI Flags");
@@ -2118,38 +2278,68 @@ template <class ELFT> void ELFDumper<ELFT>::printMipsABIFlags() {
W.printHex("Flags 2", Flags->flags2);
}
+template <class ELFT>
+static void printMipsReginfoData(ScopedPrinter &W,
+ const Elf_Mips_RegInfo<ELFT> &Reginfo) {
+ W.printHex("GP", Reginfo.ri_gp_value);
+ W.printHex("General Mask", Reginfo.ri_gprmask);
+ W.printHex("Co-Proc Mask0", Reginfo.ri_cprmask[0]);
+ W.printHex("Co-Proc Mask1", Reginfo.ri_cprmask[1]);
+ W.printHex("Co-Proc Mask2", Reginfo.ri_cprmask[2]);
+ W.printHex("Co-Proc Mask3", Reginfo.ri_cprmask[3]);
+}
+
template <class ELFT> void ELFDumper<ELFT>::printMipsReginfo() {
const Elf_Shdr *Shdr = findSectionByName(*Obj, ".reginfo");
if (!Shdr) {
W.startLine() << "There is no .reginfo section in the file.\n";
return;
}
- ErrorOr<ArrayRef<uint8_t>> Sec = Obj->getSectionContents(Shdr);
- if (!Sec) {
- W.startLine() << "The .reginfo section is empty.\n";
+ ArrayRef<uint8_t> Sec = unwrapOrError(Obj->getSectionContents(Shdr));
+ if (Sec.size() != sizeof(Elf_Mips_RegInfo<ELFT>)) {
+ W.startLine() << "The .reginfo section has a wrong size.\n";
return;
}
- if (Sec->size() != sizeof(Elf_Mips_RegInfo<ELFT>)) {
- W.startLine() << "The .reginfo section has a wrong size.\n";
+
+ DictScope GS(W, "MIPS RegInfo");
+ auto *Reginfo = reinterpret_cast<const Elf_Mips_RegInfo<ELFT> *>(Sec.data());
+ printMipsReginfoData(W, *Reginfo);
+}
+
+template <class ELFT> void ELFDumper<ELFT>::printMipsOptions() {
+ const Elf_Shdr *Shdr = findSectionByName(*Obj, ".MIPS.options");
+ if (!Shdr) {
+ W.startLine() << "There is no .MIPS.options section in the file.\n";
return;
}
- auto *Reginfo = reinterpret_cast<const Elf_Mips_RegInfo<ELFT> *>(Sec->data());
+ DictScope GS(W, "MIPS Options");
- DictScope GS(W, "MIPS RegInfo");
- W.printHex("GP", Reginfo->ri_gp_value);
- W.printHex("General Mask", Reginfo->ri_gprmask);
- W.printHex("Co-Proc Mask0", Reginfo->ri_cprmask[0]);
- W.printHex("Co-Proc Mask1", Reginfo->ri_cprmask[1]);
- W.printHex("Co-Proc Mask2", Reginfo->ri_cprmask[2]);
- W.printHex("Co-Proc Mask3", Reginfo->ri_cprmask[3]);
+ ArrayRef<uint8_t> Sec = unwrapOrError(Obj->getSectionContents(Shdr));
+ while (!Sec.empty()) {
+ if (Sec.size() < sizeof(Elf_Mips_Options<ELFT>)) {
+ W.startLine() << "The .MIPS.options section has a wrong size.\n";
+ return;
+ }
+ auto *O = reinterpret_cast<const Elf_Mips_Options<ELFT> *>(Sec.data());
+ DictScope GS(W, getElfMipsOptionsOdkType(O->kind));
+ switch (O->kind) {
+ case ODK_REGINFO:
+ printMipsReginfoData(W, O->getRegInfo());
+ break;
+ default:
+ W.startLine() << "Unsupported MIPS options tag.\n";
+ break;
+ }
+ Sec = Sec.slice(O->size);
+ }
}
template <class ELFT> void ELFDumper<ELFT>::printStackMap() const {
const Elf_Shdr *StackMapSection = nullptr;
for (const auto &Sec : Obj->sections()) {
- ErrorOr<StringRef> Name = Obj->getSectionName(&Sec);
- if (*Name == ".llvm_stackmaps") {
+ StringRef Name = unwrapOrError(Obj->getSectionName(&Sec));
+ if (Name == ".llvm_stackmaps") {
StackMapSection = &Sec;
break;
}
@@ -2159,10 +2349,1184 @@ template <class ELFT> void ELFDumper<ELFT>::printStackMap() const {
return;
StringRef StackMapContents;
- ErrorOr<ArrayRef<uint8_t>> StackMapContentsArray =
- Obj->getSectionContents(StackMapSection);
+ ArrayRef<uint8_t> StackMapContentsArray =
+ unwrapOrError(Obj->getSectionContents(StackMapSection));
+
+ prettyPrintStackMap(llvm::outs(), StackMapV1Parser<ELFT::TargetEndianness>(
+ StackMapContentsArray));
+}
+
+template <class ELFT> void ELFDumper<ELFT>::printGroupSections() {
+ ELFDumperStyle->printGroupSections(Obj);
+}
+
+static inline void printFields(formatted_raw_ostream &OS, StringRef Str1,
+ StringRef Str2) {
+ OS.PadToColumn(2u);
+ OS << Str1;
+ OS.PadToColumn(37u);
+ OS << Str2 << "\n";
+ OS.flush();
+}
+
+template <class ELFT> void GNUStyle<ELFT>::printFileHeaders(const ELFO *Obj) {
+ const Elf_Ehdr *e = Obj->getHeader();
+ OS << "ELF Header:\n";
+ OS << " Magic: ";
+ std::string Str;
+ for (int i = 0; i < ELF::EI_NIDENT; i++)
+ OS << format(" %02x", static_cast<int>(e->e_ident[i]));
+ OS << "\n";
+ Str = printEnum(e->e_ident[ELF::EI_CLASS], makeArrayRef(ElfClass));
+ printFields(OS, "Class:", Str);
+ Str = printEnum(e->e_ident[ELF::EI_DATA], makeArrayRef(ElfDataEncoding));
+ printFields(OS, "Data:", Str);
+ OS.PadToColumn(2u);
+ OS << "Version:";
+ OS.PadToColumn(37u);
+ OS << to_hexString(e->e_ident[ELF::EI_VERSION]);
+ if (e->e_version == ELF::EV_CURRENT)
+ OS << " (current)";
+ OS << "\n";
+ Str = printEnum(e->e_ident[ELF::EI_OSABI], makeArrayRef(ElfOSABI));
+ printFields(OS, "OS/ABI:", Str);
+ Str = "0x" + to_hexString(e->e_version);
+ Str = to_hexString(e->e_ident[ELF::EI_ABIVERSION]);
+ printFields(OS, "ABI Version:", Str);
+ Str = printEnum(e->e_type, makeArrayRef(ElfObjectFileType));
+ printFields(OS, "Type:", Str);
+ Str = printEnum(e->e_machine, makeArrayRef(ElfMachineType));
+ printFields(OS, "Machine:", Str);
+ Str = "0x" + to_hexString(e->e_version);
+ printFields(OS, "Version:", Str);
+ Str = "0x" + to_hexString(e->e_entry);
+ printFields(OS, "Entry point address:", Str);
+ Str = to_string(e->e_phoff) + " (bytes into file)";
+ printFields(OS, "Start of program headers:", Str);
+ Str = to_string(e->e_shoff) + " (bytes into file)";
+ printFields(OS, "Start of section headers:", Str);
+ Str = "0x" + to_hexString(e->e_flags);
+ printFields(OS, "Flags:", Str);
+ Str = to_string(e->e_ehsize) + " (bytes)";
+ printFields(OS, "Size of this header:", Str);
+ Str = to_string(e->e_phentsize) + " (bytes)";
+ printFields(OS, "Size of program headers:", Str);
+ Str = to_string(e->e_phnum);
+ printFields(OS, "Number of program headers:", Str);
+ Str = to_string(e->e_shentsize) + " (bytes)";
+ printFields(OS, "Size of section headers:", Str);
+ Str = to_string(e->e_shnum);
+ printFields(OS, "Number of section headers:", Str);
+ Str = to_string(e->e_shstrndx);
+ printFields(OS, "Section header string table index:", Str);
+}
+
+template <class ELFT> void GNUStyle<ELFT>::printGroupSections(const ELFO *Obj) {
+ uint32_t SectionIndex = 0;
+ bool HasGroups = false;
+ for (const Elf_Shdr &Sec : Obj->sections()) {
+ if (Sec.sh_type == ELF::SHT_GROUP) {
+ HasGroups = true;
+ const Elf_Shdr *Symtab = unwrapOrError(Obj->getSection(Sec.sh_link));
+ StringRef StrTable = unwrapOrError(Obj->getStringTableForSymtab(*Symtab));
+ const Elf_Sym *Signature =
+ Obj->template getEntry<Elf_Sym>(Symtab, Sec.sh_info);
+ ArrayRef<Elf_Word> Data = unwrapOrError(
+ Obj->template getSectionContentsAsArray<Elf_Word>(&Sec));
+ StringRef Name = unwrapOrError(Obj->getSectionName(&Sec));
+ OS << "\n" << getGroupType(Data[0]) << " group section ["
+ << format_decimal(SectionIndex, 5) << "] `" << Name << "' ["
+ << StrTable.data() + Signature->st_name << "] contains "
+ << (Data.size() - 1) << " sections:\n"
+ << " [Index] Name\n";
+ for (auto &Ndx : Data.slice(1)) {
+ auto Sec = unwrapOrError(Obj->getSection(Ndx));
+ const StringRef Name = unwrapOrError(Obj->getSectionName(Sec));
+ OS << " [" << format_decimal(Ndx, 5) << "] " << Name
+ << "\n";
+ }
+ }
+ ++SectionIndex;
+ }
+ if (!HasGroups)
+ OS << "There are no section groups in this file.\n";
+}
+
+template <class ELFT>
+void GNUStyle<ELFT>::printRelocation(const ELFO *Obj, const Elf_Shdr *SymTab,
+ const Elf_Rela &R, bool IsRela) {
+ std::string Offset, Info, Addend = "", Value;
+ SmallString<32> RelocName;
+ StringRef StrTable = unwrapOrError(Obj->getStringTableForSymtab(*SymTab));
+ StringRef TargetName;
+ const Elf_Sym *Sym = nullptr;
+ unsigned Width = ELFT::Is64Bits ? 16 : 8;
+ unsigned Bias = ELFT::Is64Bits ? 8 : 0;
+
+ // First two fields are bit width dependent. The rest of them are after are
+ // fixed width.
+ Field Fields[5] = {0, 10 + Bias, 19 + 2 * Bias, 42 + 2 * Bias, 53 + 2 * Bias};
+ Obj->getRelocationTypeName(R.getType(Obj->isMips64EL()), RelocName);
+ Sym = Obj->getRelocationSymbol(&R, SymTab);
+ if (Sym && Sym->getType() == ELF::STT_SECTION) {
+ const Elf_Shdr *Sec = unwrapOrError(
+ Obj->getSection(Sym, SymTab, this->dumper()->getShndxTable()));
+ TargetName = unwrapOrError(Obj->getSectionName(Sec));
+ } else if (Sym) {
+ TargetName = unwrapOrError(Sym->getName(StrTable));
+ }
+
+ if (Sym && IsRela) {
+ if (R.r_addend < 0)
+ Addend = " - ";
+ else
+ Addend = " + ";
+ }
+
+ Offset = to_string(format_hex_no_prefix(R.r_offset, Width));
+ Info = to_string(format_hex_no_prefix(R.r_info, Width));
+
+ int64_t RelAddend = R.r_addend;
+ if (IsRela)
+ Addend += to_hexString(std::abs(RelAddend), false);
+
+ if (Sym)
+ Value = to_string(format_hex_no_prefix(Sym->getValue(), Width));
+
+ Fields[0].Str = Offset;
+ Fields[1].Str = Info;
+ Fields[2].Str = RelocName;
+ Fields[3].Str = Value;
+ Fields[4].Str = TargetName;
+ for (auto &field : Fields)
+ printField(field);
+ OS << Addend;
+ OS << "\n";
+}
+
+static inline void printRelocHeader(raw_ostream &OS, bool Is64, bool IsRela) {
+ if (Is64)
+ OS << " Offset Info Type"
+ << " Symbol's Value Symbol's Name";
+ else
+ OS << " Offset Info Type Sym. Value "
+ << "Symbol's Name";
+ if (IsRela)
+ OS << (IsRela ? " + Addend" : "");
+ OS << "\n";
+}
+
+template <class ELFT> void GNUStyle<ELFT>::printRelocations(const ELFO *Obj) {
+ bool HasRelocSections = false;
+ for (const Elf_Shdr &Sec : Obj->sections()) {
+ if (Sec.sh_type != ELF::SHT_REL && Sec.sh_type != ELF::SHT_RELA)
+ continue;
+ HasRelocSections = true;
+ StringRef Name = unwrapOrError(Obj->getSectionName(&Sec));
+ unsigned Entries = Sec.getEntityCount();
+ uintX_t Offset = Sec.sh_offset;
+ OS << "\nRelocation section '" << Name << "' at offset 0x"
+ << to_hexString(Offset, false) << " contains " << Entries
+ << " entries:\n";
+ printRelocHeader(OS, ELFT::Is64Bits, (Sec.sh_type == ELF::SHT_RELA));
+ const Elf_Shdr *SymTab = unwrapOrError(Obj->getSection(Sec.sh_link));
+ if (Sec.sh_type == ELF::SHT_REL) {
+ for (const auto &R : Obj->rels(&Sec)) {
+ Elf_Rela Rela;
+ Rela.r_offset = R.r_offset;
+ Rela.r_info = R.r_info;
+ Rela.r_addend = 0;
+ printRelocation(Obj, SymTab, Rela, false);
+ }
+ } else {
+ for (const auto &R : Obj->relas(&Sec))
+ printRelocation(Obj, SymTab, R, true);
+ }
+ }
+ if (!HasRelocSections)
+ OS << "\nThere are no relocations in this file.\n";
+}
+
+std::string getSectionTypeString(unsigned Arch, unsigned Type) {
+ using namespace ELF;
+ switch (Arch) {
+ case EM_ARM:
+ switch (Type) {
+ case SHT_ARM_EXIDX:
+ return "ARM_EXIDX";
+ case SHT_ARM_PREEMPTMAP:
+ return "ARM_PREEMPTMAP";
+ case SHT_ARM_ATTRIBUTES:
+ return "ARM_ATTRIBUTES";
+ case SHT_ARM_DEBUGOVERLAY:
+ return "ARM_DEBUGOVERLAY";
+ case SHT_ARM_OVERLAYSECTION:
+ return "ARM_OVERLAYSECTION";
+ }
+ case EM_X86_64:
+ switch (Type) {
+ case SHT_X86_64_UNWIND:
+ return "X86_64_UNWIND";
+ }
+ case EM_MIPS:
+ case EM_MIPS_RS3_LE:
+ switch (Type) {
+ case SHT_MIPS_REGINFO:
+ return "MIPS_REGINFO";
+ case SHT_MIPS_OPTIONS:
+ return "MIPS_OPTIONS";
+ case SHT_MIPS_ABIFLAGS:
+ return "MIPS_ABIFLAGS";
+ }
+ }
+ switch (Type) {
+ case SHT_NULL:
+ return "NULL";
+ case SHT_PROGBITS:
+ return "PROGBITS";
+ case SHT_SYMTAB:
+ return "SYMTAB";
+ case SHT_STRTAB:
+ return "STRTAB";
+ case SHT_RELA:
+ return "RELA";
+ case SHT_HASH:
+ return "HASH";
+ case SHT_DYNAMIC:
+ return "DYNAMIC";
+ case SHT_NOTE:
+ return "NOTE";
+ case SHT_NOBITS:
+ return "NOBITS";
+ case SHT_REL:
+ return "REL";
+ case SHT_SHLIB:
+ return "SHLIB";
+ case SHT_DYNSYM:
+ return "DYNSYM";
+ case SHT_INIT_ARRAY:
+ return "INIT_ARRAY";
+ case SHT_FINI_ARRAY:
+ return "FINI_ARRAY";
+ case SHT_PREINIT_ARRAY:
+ return "PREINIT_ARRAY";
+ case SHT_GROUP:
+ return "GROUP";
+ case SHT_SYMTAB_SHNDX:
+ return "SYMTAB SECTION INDICES";
+ // FIXME: Parse processor specific GNU attributes
+ case SHT_GNU_ATTRIBUTES:
+ return "ATTRIBUTES";
+ case SHT_GNU_HASH:
+ return "GNU_HASH";
+ case SHT_GNU_verdef:
+ return "VERDEF";
+ case SHT_GNU_verneed:
+ return "VERNEED";
+ case SHT_GNU_versym:
+ return "VERSYM";
+ default:
+ return "";
+ }
+ return "";
+}
+
+template <class ELFT> void GNUStyle<ELFT>::printSections(const ELFO *Obj) {
+ size_t SectionIndex = 0;
+ std::string Number, Type, Size, Address, Offset, Flags, Link, Info, EntrySize,
+ Alignment;
+ unsigned Bias;
+ unsigned Width;
+
+ if (ELFT::Is64Bits) {
+ Bias = 0;
+ Width = 16;
+ } else {
+ Bias = 8;
+ Width = 8;
+ }
+ OS << "There are " << to_string(Obj->getHeader()->e_shnum)
+ << " section headers, starting at offset "
+ << "0x" << to_hexString(Obj->getHeader()->e_shoff, false) << ":\n\n";
+ OS << "Section Headers:\n";
+ Field Fields[11] = {{"[Nr]", 2},
+ {"Name", 7},
+ {"Type", 25},
+ {"Address", 41},
+ {"Off", 58 - Bias},
+ {"Size", 65 - Bias},
+ {"ES", 72 - Bias},
+ {"Flg", 75 - Bias},
+ {"Lk", 79 - Bias},
+ {"Inf", 82 - Bias},
+ {"Al", 86 - Bias}};
+ for (auto &f : Fields)
+ printField(f);
+ OS << "\n";
+
+ for (const Elf_Shdr &Sec : Obj->sections()) {
+ Number = to_string(SectionIndex);
+ Fields[0].Str = Number;
+ Fields[1].Str = unwrapOrError(Obj->getSectionName(&Sec));
+ Type = getSectionTypeString(Obj->getHeader()->e_machine, Sec.sh_type);
+ Fields[2].Str = Type;
+ Address = to_string(format_hex_no_prefix(Sec.sh_addr, Width));
+ Fields[3].Str = Address;
+ Offset = to_string(format_hex_no_prefix(Sec.sh_offset, 6));
+ Fields[4].Str = Offset;
+ Size = to_string(format_hex_no_prefix(Sec.sh_size, 6));
+ Fields[5].Str = Size;
+ EntrySize = to_string(format_hex_no_prefix(Sec.sh_entsize, 2));
+ Fields[6].Str = EntrySize;
+ Flags = getGNUFlags(Sec.sh_flags);
+ Fields[7].Str = Flags;
+ Link = to_string(Sec.sh_link);
+ Fields[8].Str = Link;
+ Info = to_string(Sec.sh_info);
+ Fields[9].Str = Info;
+ Alignment = to_string(Sec.sh_addralign);
+ Fields[10].Str = Alignment;
+ OS.PadToColumn(Fields[0].Column);
+ OS << "[" << right_justify(Fields[0].Str, 2) << "]";
+ for (int i = 1; i < 7; i++)
+ printField(Fields[i]);
+ OS.PadToColumn(Fields[7].Column);
+ OS << right_justify(Fields[7].Str, 3);
+ OS.PadToColumn(Fields[8].Column);
+ OS << right_justify(Fields[8].Str, 2);
+ OS.PadToColumn(Fields[9].Column);
+ OS << right_justify(Fields[9].Str, 3);
+ OS.PadToColumn(Fields[10].Column);
+ OS << right_justify(Fields[10].Str, 2);
+ OS << "\n";
+ ++SectionIndex;
+ }
+ OS << "Key to Flags:\n"
+ << " W (write), A (alloc), X (execute), M (merge), S (strings), l "
+ "(large)\n"
+ << " I (info), L (link order), G (group), T (TLS), E (exclude),\
+ x (unknown)\n"
+ << " O (extra OS processing required) o (OS specific),\
+ p (processor specific)\n";
+}
+
+template <class ELFT>
+void GNUStyle<ELFT>::printSymtabMessage(const ELFO *Obj, StringRef Name,
+ size_t Entries) {
+ if (Name.size())
+ OS << "\nSymbol table '" << Name << "' contains " << Entries
+ << " entries:\n";
+ else
+ OS << "\n Symbol table for image:\n";
+
+ if (ELFT::Is64Bits)
+ OS << " Num: Value Size Type Bind Vis Ndx Name\n";
+ else
+ OS << " Num: Value Size Type Bind Vis Ndx Name\n";
+}
+
+template <class ELFT>
+std::string GNUStyle<ELFT>::getSymbolSectionNdx(const ELFO *Obj,
+ const Elf_Sym *Symbol,
+ const Elf_Sym *FirstSym) {
+ unsigned SectionIndex = Symbol->st_shndx;
+ switch (SectionIndex) {
+ case ELF::SHN_UNDEF:
+ return "UND";
+ case ELF::SHN_ABS:
+ return "ABS";
+ case ELF::SHN_COMMON:
+ return "COM";
+ case ELF::SHN_XINDEX:
+ SectionIndex = Obj->getExtendedSymbolTableIndex(
+ Symbol, FirstSym, this->dumper()->getShndxTable());
+ default:
+ // Find if:
+ // Processor specific
+ if (SectionIndex >= ELF::SHN_LOPROC && SectionIndex <= ELF::SHN_HIPROC)
+ return std::string("PRC[0x") +
+ to_string(format_hex_no_prefix(SectionIndex, 4)) + "]";
+ // OS specific
+ if (SectionIndex >= ELF::SHN_LOOS && SectionIndex <= ELF::SHN_HIOS)
+ return std::string("OS[0x") +
+ to_string(format_hex_no_prefix(SectionIndex, 4)) + "]";
+ // Architecture reserved:
+ if (SectionIndex >= ELF::SHN_LORESERVE &&
+ SectionIndex <= ELF::SHN_HIRESERVE)
+ return std::string("RSV[0x") +
+ to_string(format_hex_no_prefix(SectionIndex, 4)) + "]";
+ // A normal section with an index
+ return to_string(format_decimal(SectionIndex, 3));
+ }
+}
+
+template <class ELFT>
+void GNUStyle<ELFT>::printSymbol(const ELFO *Obj, const Elf_Sym *Symbol,
+ const Elf_Sym *FirstSym, StringRef StrTable,
+ bool IsDynamic) {
+ static int Idx = 0;
+ static bool Dynamic = true;
+ size_t Width;
+
+ // If this function was called with a different value from IsDynamic
+ // from last call, happens when we move from dynamic to static symbol
+ // table, "Num" field should be reset.
+ if (!Dynamic != !IsDynamic) {
+ Idx = 0;
+ Dynamic = false;
+ }
+ std::string Num, Name, Value, Size, Binding, Type, Visibility, Section;
+ unsigned Bias = 0;
+ if (ELFT::Is64Bits) {
+ Bias = 8;
+ Width = 16;
+ } else {
+ Bias = 0;
+ Width = 8;
+ }
+ Field Fields[8] = {0, 8, 17 + Bias, 23 + Bias,
+ 31 + Bias, 38 + Bias, 47 + Bias, 51 + Bias};
+ Num = to_string(format_decimal(Idx++, 6)) + ":";
+ Value = to_string(format_hex_no_prefix(Symbol->st_value, Width));
+ Size = to_string(format_decimal(Symbol->st_size, 5));
+ unsigned char SymbolType = Symbol->getType();
+ if (Obj->getHeader()->e_machine == ELF::EM_AMDGPU &&
+ SymbolType >= ELF::STT_LOOS && SymbolType < ELF::STT_HIOS)
+ Type = printEnum(SymbolType, makeArrayRef(AMDGPUSymbolTypes));
+ else
+ Type = printEnum(SymbolType, makeArrayRef(ElfSymbolTypes));
+ unsigned Vis = Symbol->getVisibility();
+ Binding = printEnum(Symbol->getBinding(), makeArrayRef(ElfSymbolBindings));
+ Visibility = printEnum(Vis, makeArrayRef(ElfSymbolVisibilities));
+ Section = getSymbolSectionNdx(Obj, Symbol, FirstSym);
+ Name = this->dumper()->getFullSymbolName(Symbol, StrTable, IsDynamic);
+ Fields[0].Str = Num;
+ Fields[1].Str = Value;
+ Fields[2].Str = Size;
+ Fields[3].Str = Type;
+ Fields[4].Str = Binding;
+ Fields[5].Str = Visibility;
+ Fields[6].Str = Section;
+ Fields[7].Str = Name;
+ for (auto &Entry : Fields)
+ printField(Entry);
+ OS << "\n";
+}
+
+template <class ELFT> void GNUStyle<ELFT>::printSymbols(const ELFO *Obj) {
+ this->dumper()->printSymbolsHelper(true);
+ this->dumper()->printSymbolsHelper(false);
+}
+
+template <class ELFT>
+void GNUStyle<ELFT>::printDynamicSymbols(const ELFO *Obj) {
+ this->dumper()->printSymbolsHelper(true);
+}
+
+static inline std::string printPhdrFlags(unsigned Flag) {
+ std::string Str;
+ Str = (Flag & PF_R) ? "R" : " ";
+ Str += (Flag & PF_W) ? "W" : " ";
+ Str += (Flag & PF_X) ? "E" : " ";
+ return Str;
+}
+
+// SHF_TLS sections are only in PT_TLS, PT_LOAD or PT_GNU_RELRO
+// PT_TLS must only have SHF_TLS sections
+template <class ELFT>
+bool GNUStyle<ELFT>::checkTLSSections(const Elf_Phdr &Phdr,
+ const Elf_Shdr &Sec) {
+ return (((Sec.sh_flags & ELF::SHF_TLS) &&
+ ((Phdr.p_type == ELF::PT_TLS) || (Phdr.p_type == ELF::PT_LOAD) ||
+ (Phdr.p_type == ELF::PT_GNU_RELRO))) ||
+ (!(Sec.sh_flags & ELF::SHF_TLS) && Phdr.p_type != ELF::PT_TLS));
+}
+
+// Non-SHT_NOBITS must have its offset inside the segment
+// Only non-zero section can be at end of segment
+template <class ELFT>
+bool GNUStyle<ELFT>::checkoffsets(const Elf_Phdr &Phdr, const Elf_Shdr &Sec) {
+ if (Sec.sh_type == ELF::SHT_NOBITS)
+ return true;
+ bool IsSpecial =
+ (Sec.sh_type == ELF::SHT_NOBITS) && ((Sec.sh_flags & ELF::SHF_TLS) != 0);
+ // .tbss is special, it only has memory in PT_TLS and has NOBITS properties
+ auto SectionSize =
+ (IsSpecial && Phdr.p_type != ELF::PT_TLS) ? 0 : Sec.sh_size;
+ if (Sec.sh_offset >= Phdr.p_offset)
+ return ((Sec.sh_offset + SectionSize <= Phdr.p_filesz + Phdr.p_offset)
+ /*only non-zero sized sections at end*/ &&
+ (Sec.sh_offset + 1 <= Phdr.p_offset + Phdr.p_filesz));
+ return false;
+}
+
+// SHF_ALLOC must have VMA inside segment
+// Only non-zero section can be at end of segment
+template <class ELFT>
+bool GNUStyle<ELFT>::checkVMA(const Elf_Phdr &Phdr, const Elf_Shdr &Sec) {
+ if (!(Sec.sh_flags & ELF::SHF_ALLOC))
+ return true;
+ bool IsSpecial =
+ (Sec.sh_type == ELF::SHT_NOBITS) && ((Sec.sh_flags & ELF::SHF_TLS) != 0);
+ // .tbss is special, it only has memory in PT_TLS and has NOBITS properties
+ auto SectionSize =
+ (IsSpecial && Phdr.p_type != ELF::PT_TLS) ? 0 : Sec.sh_size;
+ if (Sec.sh_addr >= Phdr.p_vaddr)
+ return ((Sec.sh_addr + SectionSize <= Phdr.p_vaddr + Phdr.p_memsz) &&
+ (Sec.sh_addr + 1 <= Phdr.p_vaddr + Phdr.p_memsz));
+ return false;
+}
+
+// No section with zero size must be at start or end of PT_DYNAMIC
+template <class ELFT>
+bool GNUStyle<ELFT>::checkPTDynamic(const Elf_Phdr &Phdr, const Elf_Shdr &Sec) {
+ if (Phdr.p_type != ELF::PT_DYNAMIC || Sec.sh_size != 0 || Phdr.p_memsz == 0)
+ return true;
+ // Is section within the phdr both based on offset and VMA ?
+ return ((Sec.sh_type == ELF::SHT_NOBITS) ||
+ (Sec.sh_offset > Phdr.p_offset &&
+ Sec.sh_offset < Phdr.p_offset + Phdr.p_filesz)) &&
+ (!(Sec.sh_flags & ELF::SHF_ALLOC) ||
+ (Sec.sh_addr > Phdr.p_vaddr && Sec.sh_addr < Phdr.p_memsz));
+}
+
+template <class ELFT>
+void GNUStyle<ELFT>::printProgramHeaders(const ELFO *Obj) {
+ unsigned Bias = ELFT::Is64Bits ? 8 : 0;
+ unsigned Width = ELFT::Is64Bits ? 18 : 10;
+ unsigned SizeWidth = ELFT::Is64Bits ? 8 : 7;
+ std::string Type, Offset, VMA, LMA, FileSz, MemSz, Flag, Align;
+
+ const Elf_Ehdr *Header = Obj->getHeader();
+ Field Fields[8] = {2, 17, 26, 37 + Bias,
+ 48 + Bias, 56 + Bias, 64 + Bias, 68 + Bias};
+ OS << "\nElf file type is "
+ << printEnum(Header->e_type, makeArrayRef(ElfObjectFileType)) << "\n"
+ << "Entry point " << format_hex(Header->e_entry, 3) << "\n"
+ << "There are " << Header->e_phnum << " program headers,"
+ << " starting at offset " << Header->e_phoff << "\n\n"
+ << "Program Headers:\n";
+ if (ELFT::Is64Bits)
+ OS << " Type Offset VirtAddr PhysAddr "
+ << " FileSiz MemSiz Flg Align\n";
+ else
+ OS << " Type Offset VirtAddr PhysAddr FileSiz "
+ << "MemSiz Flg Align\n";
+ for (const auto &Phdr : Obj->program_headers()) {
+ Type = getElfPtType(Header->e_machine, Phdr.p_type);
+ Offset = to_string(format_hex(Phdr.p_offset, 8));
+ VMA = to_string(format_hex(Phdr.p_vaddr, Width));
+ LMA = to_string(format_hex(Phdr.p_paddr, Width));
+ FileSz = to_string(format_hex(Phdr.p_filesz, SizeWidth));
+ MemSz = to_string(format_hex(Phdr.p_memsz, SizeWidth));
+ Flag = printPhdrFlags(Phdr.p_flags);
+ Align = to_string(format_hex(Phdr.p_align, 1));
+ Fields[0].Str = Type;
+ Fields[1].Str = Offset;
+ Fields[2].Str = VMA;
+ Fields[3].Str = LMA;
+ Fields[4].Str = FileSz;
+ Fields[5].Str = MemSz;
+ Fields[6].Str = Flag;
+ Fields[7].Str = Align;
+ for (auto Field : Fields)
+ printField(Field);
+ if (Phdr.p_type == ELF::PT_INTERP) {
+ OS << "\n [Requesting program interpreter: ";
+ OS << reinterpret_cast<const char *>(Obj->base()) + Phdr.p_offset << "]";
+ }
+ OS << "\n";
+ }
+ OS << "\n Section to Segment mapping:\n Segment Sections...\n";
+ int Phnum = 0;
+ for (const Elf_Phdr &Phdr : Obj->program_headers()) {
+ std::string Sections;
+ OS << format(" %2.2d ", Phnum++);
+ for (const Elf_Shdr &Sec : Obj->sections()) {
+ // Check if each section is in a segment and then print mapping.
+ // readelf additionally makes sure it does not print zero sized sections
+ // at end of segments and for PT_DYNAMIC both start and end of section
+ // .tbss must only be shown in PT_TLS section.
+ bool TbssInNonTLS = (Sec.sh_type == ELF::SHT_NOBITS) &&
+ ((Sec.sh_flags & ELF::SHF_TLS) != 0) &&
+ Phdr.p_type != ELF::PT_TLS;
+ if (!TbssInNonTLS && checkTLSSections(Phdr, Sec) &&
+ checkoffsets(Phdr, Sec) && checkVMA(Phdr, Sec) &&
+ checkPTDynamic(Phdr, Sec) && (Sec.sh_type != ELF::SHT_NULL))
+ Sections += unwrapOrError(Obj->getSectionName(&Sec)).str() + " ";
+ }
+ OS << Sections << "\n";
+ OS.flush();
+ }
+}
+
+template <class ELFT>
+void GNUStyle<ELFT>::printDynamicRelocation(const ELFO *Obj, Elf_Rela R,
+ bool IsRela) {
+ SmallString<32> RelocName;
+ StringRef SymbolName;
+ unsigned Width = ELFT::Is64Bits ? 16 : 8;
+ unsigned Bias = ELFT::Is64Bits ? 8 : 0;
+ // First two fields are bit width dependent. The rest of them are after are
+ // fixed width.
+ Field Fields[5] = {0, 10 + Bias, 19 + 2 * Bias, 42 + 2 * Bias, 53 + 2 * Bias};
+
+ uint32_t SymIndex = R.getSymbol(Obj->isMips64EL());
+ const Elf_Sym *Sym = this->dumper()->dynamic_symbols().begin() + SymIndex;
+ Obj->getRelocationTypeName(R.getType(Obj->isMips64EL()), RelocName);
+ SymbolName =
+ unwrapOrError(Sym->getName(this->dumper()->getDynamicStringTable()));
+ std::string Addend = "", Info, Offset, Value;
+ Offset = to_string(format_hex_no_prefix(R.r_offset, Width));
+ Info = to_string(format_hex_no_prefix(R.r_info, Width));
+ Value = to_string(format_hex_no_prefix(Sym->getValue(), Width));
+ int64_t RelAddend = R.r_addend;
+ if (SymbolName.size() && IsRela) {
+ if (R.r_addend < 0)
+ Addend = " - ";
+ else
+ Addend = " + ";
+ }
+
+ if (!SymbolName.size() && Sym->getValue() == 0)
+ Value = "";
+
+ if (IsRela)
+ Addend += to_string(format_hex_no_prefix(std::abs(RelAddend), 1));
+
+
+ Fields[0].Str = Offset;
+ Fields[1].Str = Info;
+ Fields[2].Str = RelocName.c_str();
+ Fields[3].Str = Value;
+ Fields[4].Str = SymbolName;
+ for (auto &Field : Fields)
+ printField(Field);
+ OS << Addend;
+ OS << "\n";
+}
+
+template <class ELFT>
+void GNUStyle<ELFT>::printDynamicRelocations(const ELFO *Obj) {
+ const DynRegionInfo &DynRelRegion = this->dumper()->getDynRelRegion();
+ const DynRegionInfo &DynRelaRegion = this->dumper()->getDynRelaRegion();
+ const DynRegionInfo &DynPLTRelRegion = this->dumper()->getDynPLTRelRegion();
+ if (DynRelaRegion.Size > 0) {
+ OS << "\n'RELA' relocation section at offset "
+ << format_hex(reinterpret_cast<const uint8_t *>(DynRelaRegion.Addr) -
+ Obj->base(),
+ 1) << " contains " << DynRelaRegion.Size << " bytes:\n";
+ printRelocHeader(OS, ELFT::Is64Bits, true);
+ for (const Elf_Rela &Rela : this->dumper()->dyn_relas())
+ printDynamicRelocation(Obj, Rela, true);
+ }
+ if (DynRelRegion.Size > 0) {
+ OS << "\n'REL' relocation section at offset "
+ << format_hex(reinterpret_cast<const uint8_t *>(DynRelRegion.Addr) -
+ Obj->base(),
+ 1) << " contains " << DynRelRegion.Size << " bytes:\n";
+ printRelocHeader(OS, ELFT::Is64Bits, false);
+ for (const Elf_Rel &Rel : this->dumper()->dyn_rels()) {
+ Elf_Rela Rela;
+ Rela.r_offset = Rel.r_offset;
+ Rela.r_info = Rel.r_info;
+ Rela.r_addend = 0;
+ printDynamicRelocation(Obj, Rela, false);
+ }
+ }
+ if (DynPLTRelRegion.Size) {
+ OS << "\n'PLT' relocation section at offset "
+ << format_hex(reinterpret_cast<const uint8_t *>(DynPLTRelRegion.Addr) -
+ Obj->base(),
+ 1) << " contains " << DynPLTRelRegion.Size << " bytes:\n";
+ }
+ if (DynPLTRelRegion.EntSize == sizeof(Elf_Rela)) {
+ printRelocHeader(OS, ELFT::Is64Bits, true);
+ for (const Elf_Rela &Rela : DynPLTRelRegion.getAsArrayRef<Elf_Rela>())
+ printDynamicRelocation(Obj, Rela, true);
+ } else {
+ printRelocHeader(OS, ELFT::Is64Bits, false);
+ for (const Elf_Rel &Rel : DynPLTRelRegion.getAsArrayRef<Elf_Rel>()) {
+ Elf_Rela Rela;
+ Rela.r_offset = Rel.r_offset;
+ Rela.r_info = Rel.r_info;
+ Rela.r_addend = 0;
+ printDynamicRelocation(Obj, Rela, false);
+ }
+ }
+}
+
+// Hash histogram shows statistics of how efficient the hash was for the
+// dynamic symbol table. The table shows number of hash buckets for different
+// lengths of chains as absolute number and percentage of the total buckets.
+// Additionally cumulative coverage of symbols for each set of buckets.
+template <class ELFT>
+void GNUStyle<ELFT>::printHashHistogram(const ELFFile<ELFT> *Obj) {
+
+ const Elf_Hash *HashTable = this->dumper()->getHashTable();
+ const Elf_GnuHash *GnuHashTable = this->dumper()->getGnuHashTable();
+
+ // Print histogram for .hash section
+ if (HashTable) {
+ size_t NBucket = HashTable->nbucket;
+ size_t NChain = HashTable->nchain;
+ ArrayRef<Elf_Word> Buckets = HashTable->buckets();
+ ArrayRef<Elf_Word> Chains = HashTable->chains();
+ size_t TotalSyms = 0;
+ // If hash table is correct, we have at least chains with 0 length
+ size_t MaxChain = 1;
+ size_t CumulativeNonZero = 0;
+
+ if (NChain == 0 || NBucket == 0)
+ return;
+
+ std::vector<size_t> ChainLen(NBucket, 0);
+ // Go over all buckets and and note chain lengths of each bucket (total
+ // unique chain lengths).
+ for (size_t B = 0; B < NBucket; B++) {
+ for (size_t C = Buckets[B]; C > 0 && C < NChain; C = Chains[C])
+ if (MaxChain <= ++ChainLen[B])
+ MaxChain++;
+ TotalSyms += ChainLen[B];
+ }
+
+ if (!TotalSyms)
+ return;
+
+ std::vector<size_t> Count(MaxChain, 0) ;
+ // Count how long is the chain for each bucket
+ for (size_t B = 0; B < NBucket; B++)
+ ++Count[ChainLen[B]];
+ // Print Number of buckets with each chain lengths and their cumulative
+ // coverage of the symbols
+ OS << "Histogram for bucket list length (total of " << NBucket
+ << " buckets)\n"
+ << " Length Number % of total Coverage\n";
+ for (size_t I = 0; I < MaxChain; I++) {
+ CumulativeNonZero += Count[I] * I;
+ OS << format("%7lu %-10lu (%5.1f%%) %5.1f%%\n", I, Count[I],
+ (Count[I] * 100.0) / NBucket,
+ (CumulativeNonZero * 100.0) / TotalSyms);
+ }
+ }
+
+ // Print histogram for .gnu.hash section
+ if (GnuHashTable) {
+ size_t NBucket = GnuHashTable->nbuckets;
+ ArrayRef<Elf_Word> Buckets = GnuHashTable->buckets();
+ unsigned NumSyms = this->dumper()->dynamic_symbols().size();
+ if (!NumSyms)
+ return;
+ ArrayRef<Elf_Word> Chains = GnuHashTable->values(NumSyms);
+ size_t Symndx = GnuHashTable->symndx;
+ size_t TotalSyms = 0;
+ size_t MaxChain = 1;
+ size_t CumulativeNonZero = 0;
+
+ if (Chains.size() == 0 || NBucket == 0)
+ return;
+
+ std::vector<size_t> ChainLen(NBucket, 0);
+
+ for (size_t B = 0; B < NBucket; B++) {
+ if (!Buckets[B])
+ continue;
+ size_t Len = 1;
+ for (size_t C = Buckets[B] - Symndx;
+ C < Chains.size() && (Chains[C] & 1) == 0; C++)
+ if (MaxChain < ++Len)
+ MaxChain++;
+ ChainLen[B] = Len;
+ TotalSyms += Len;
+ }
+ MaxChain++;
+
+ if (!TotalSyms)
+ return;
+
+ std::vector<size_t> Count(MaxChain, 0) ;
+ for (size_t B = 0; B < NBucket; B++)
+ ++Count[ChainLen[B]];
+ // Print Number of buckets with each chain lengths and their cumulative
+ // coverage of the symbols
+ OS << "Histogram for `.gnu.hash' bucket list length (total of " << NBucket
+ << " buckets)\n"
+ << " Length Number % of total Coverage\n";
+ for (size_t I = 0; I <MaxChain; I++) {
+ CumulativeNonZero += Count[I] * I;
+ OS << format("%7lu %-10lu (%5.1f%%) %5.1f%%\n", I, Count[I],
+ (Count[I] * 100.0) / NBucket,
+ (CumulativeNonZero * 100.0) / TotalSyms);
+ }
+ }
+}
+
+template <class ELFT> void LLVMStyle<ELFT>::printFileHeaders(const ELFO *Obj) {
+ const Elf_Ehdr *e = Obj->getHeader();
+ {
+ DictScope D(W, "ElfHeader");
+ {
+ DictScope D(W, "Ident");
+ W.printBinary("Magic", makeArrayRef(e->e_ident).slice(ELF::EI_MAG0, 4));
+ W.printEnum("Class", e->e_ident[ELF::EI_CLASS], makeArrayRef(ElfClass));
+ W.printEnum("DataEncoding", e->e_ident[ELF::EI_DATA],
+ makeArrayRef(ElfDataEncoding));
+ W.printNumber("FileVersion", e->e_ident[ELF::EI_VERSION]);
+
+ // Handle architecture specific OS/ABI values.
+ if (e->e_machine == ELF::EM_AMDGPU &&
+ e->e_ident[ELF::EI_OSABI] == ELF::ELFOSABI_AMDGPU_HSA)
+ W.printHex("OS/ABI", "AMDGPU_HSA", ELF::ELFOSABI_AMDGPU_HSA);
+ else
+ W.printEnum("OS/ABI", e->e_ident[ELF::EI_OSABI],
+ makeArrayRef(ElfOSABI));
+ W.printNumber("ABIVersion", e->e_ident[ELF::EI_ABIVERSION]);
+ W.printBinary("Unused", makeArrayRef(e->e_ident).slice(ELF::EI_PAD));
+ }
+
+ W.printEnum("Type", e->e_type, makeArrayRef(ElfObjectFileType));
+ W.printEnum("Machine", e->e_machine, makeArrayRef(ElfMachineType));
+ W.printNumber("Version", e->e_version);
+ W.printHex("Entry", e->e_entry);
+ W.printHex("ProgramHeaderOffset", e->e_phoff);
+ W.printHex("SectionHeaderOffset", e->e_shoff);
+ if (e->e_machine == EM_MIPS)
+ W.printFlags("Flags", e->e_flags, makeArrayRef(ElfHeaderMipsFlags),
+ unsigned(ELF::EF_MIPS_ARCH), unsigned(ELF::EF_MIPS_ABI),
+ unsigned(ELF::EF_MIPS_MACH));
+ else
+ W.printFlags("Flags", e->e_flags);
+ W.printNumber("HeaderSize", e->e_ehsize);
+ W.printNumber("ProgramHeaderEntrySize", e->e_phentsize);
+ W.printNumber("ProgramHeaderCount", e->e_phnum);
+ W.printNumber("SectionHeaderEntrySize", e->e_shentsize);
+ W.printNumber("SectionHeaderCount", e->e_shnum);
+ W.printNumber("StringTableSectionIndex", e->e_shstrndx);
+ }
+}
+
+template <class ELFT>
+void LLVMStyle<ELFT>::printGroupSections(const ELFO *Obj) {
+ DictScope Lists(W, "Groups");
+ uint32_t SectionIndex = 0;
+ bool HasGroups = false;
+ for (const Elf_Shdr &Sec : Obj->sections()) {
+ if (Sec.sh_type == ELF::SHT_GROUP) {
+ HasGroups = true;
+ const Elf_Shdr *Symtab = unwrapOrError(Obj->getSection(Sec.sh_link));
+ StringRef StrTable = unwrapOrError(Obj->getStringTableForSymtab(*Symtab));
+ const Elf_Sym *Sym = Obj->template getEntry<Elf_Sym>(Symtab, Sec.sh_info);
+ auto Data = unwrapOrError(
+ Obj->template getSectionContentsAsArray<Elf_Word>(&Sec));
+ DictScope D(W, "Group");
+ StringRef Name = unwrapOrError(Obj->getSectionName(&Sec));
+ W.printNumber("Name", Name, Sec.sh_name);
+ W.printNumber("Index", SectionIndex);
+ W.printHex("Type", getGroupType(Data[0]), Data[0]);
+ W.startLine() << "Signature: " << StrTable.data() + Sym->st_name << "\n";
+ {
+ ListScope L(W, "Section(s) in group");
+ size_t Member = 1;
+ while (Member < Data.size()) {
+ auto Sec = unwrapOrError(Obj->getSection(Data[Member]));
+ const StringRef Name = unwrapOrError(Obj->getSectionName(Sec));
+ W.startLine() << Name << " (" << Data[Member++] << ")\n";
+ }
+ }
+ }
+ ++SectionIndex;
+ }
+ if (!HasGroups)
+ W.startLine() << "There are no group sections in the file.\n";
+}
+
+template <class ELFT> void LLVMStyle<ELFT>::printRelocations(const ELFO *Obj) {
+ ListScope D(W, "Relocations");
+
+ int SectionNumber = -1;
+ for (const Elf_Shdr &Sec : Obj->sections()) {
+ ++SectionNumber;
+
+ if (Sec.sh_type != ELF::SHT_REL && Sec.sh_type != ELF::SHT_RELA)
+ continue;
+
+ StringRef Name = unwrapOrError(Obj->getSectionName(&Sec));
+
+ W.startLine() << "Section (" << SectionNumber << ") " << Name << " {\n";
+ W.indent();
+
+ printRelocations(&Sec, Obj);
+
+ W.unindent();
+ W.startLine() << "}\n";
+ }
+}
+
+template <class ELFT>
+void LLVMStyle<ELFT>::printRelocations(const Elf_Shdr *Sec, const ELFO *Obj) {
+ const Elf_Shdr *SymTab = unwrapOrError(Obj->getSection(Sec->sh_link));
+
+ switch (Sec->sh_type) {
+ case ELF::SHT_REL:
+ 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(Obj, Rela, SymTab);
+ }
+ break;
+ case ELF::SHT_RELA:
+ for (const Elf_Rela &R : Obj->relas(Sec))
+ printRelocation(Obj, R, SymTab);
+ break;
+ }
+}
+
+template <class ELFT>
+void LLVMStyle<ELFT>::printRelocation(const ELFO *Obj, Elf_Rela Rel,
+ const Elf_Shdr *SymTab) {
+ SmallString<32> RelocName;
+ Obj->getRelocationTypeName(Rel.getType(Obj->isMips64EL()), RelocName);
+ StringRef TargetName;
+ const Elf_Sym *Sym = Obj->getRelocationSymbol(&Rel, SymTab);
+ if (Sym && Sym->getType() == ELF::STT_SECTION) {
+ const Elf_Shdr *Sec = unwrapOrError(
+ Obj->getSection(Sym, SymTab, this->dumper()->getShndxTable()));
+ TargetName = unwrapOrError(Obj->getSectionName(Sec));
+ } else if (Sym) {
+ StringRef StrTable = unwrapOrError(Obj->getStringTableForSymtab(*SymTab));
+ TargetName = unwrapOrError(Sym->getName(StrTable));
+ }
+
+ if (opts::ExpandRelocs) {
+ DictScope Group(W, "Relocation");
+ W.printHex("Offset", Rel.r_offset);
+ W.printNumber("Type", RelocName, (int)Rel.getType(Obj->isMips64EL()));
+ W.printNumber("Symbol", TargetName.size() > 0 ? TargetName : "-",
+ Rel.getSymbol(Obj->isMips64EL()));
+ W.printHex("Addend", Rel.r_addend);
+ } else {
+ raw_ostream &OS = W.startLine();
+ OS << W.hex(Rel.r_offset) << " " << RelocName << " "
+ << (TargetName.size() > 0 ? TargetName : "-") << " "
+ << W.hex(Rel.r_addend) << "\n";
+ }
+}
+
+template <class ELFT> void LLVMStyle<ELFT>::printSections(const ELFO *Obj) {
+ ListScope SectionsD(W, "Sections");
+
+ int SectionIndex = -1;
+ for (const Elf_Shdr &Sec : Obj->sections()) {
+ ++SectionIndex;
+
+ StringRef Name = unwrapOrError(Obj->getSectionName(&Sec));
+
+ DictScope SectionD(W, "Section");
+ W.printNumber("Index", SectionIndex);
+ W.printNumber("Name", Name, Sec.sh_name);
+ W.printHex("Type",
+ getElfSectionType(Obj->getHeader()->e_machine, Sec.sh_type),
+ Sec.sh_type);
+ std::vector<EnumEntry<unsigned>> SectionFlags(std::begin(ElfSectionFlags),
+ std::end(ElfSectionFlags));
+ switch (Obj->getHeader()->e_machine) {
+ case EM_AMDGPU:
+ SectionFlags.insert(SectionFlags.end(), std::begin(ElfAMDGPUSectionFlags),
+ std::end(ElfAMDGPUSectionFlags));
+ break;
+ case EM_HEXAGON:
+ SectionFlags.insert(SectionFlags.end(),
+ std::begin(ElfHexagonSectionFlags),
+ std::end(ElfHexagonSectionFlags));
+ break;
+ case EM_MIPS:
+ SectionFlags.insert(SectionFlags.end(), std::begin(ElfMipsSectionFlags),
+ std::end(ElfMipsSectionFlags));
+ break;
+ case EM_X86_64:
+ SectionFlags.insert(SectionFlags.end(), std::begin(ElfX86_64SectionFlags),
+ std::end(ElfX86_64SectionFlags));
+ break;
+ case EM_XCORE:
+ SectionFlags.insert(SectionFlags.end(), std::begin(ElfXCoreSectionFlags),
+ std::end(ElfXCoreSectionFlags));
+ break;
+ default:
+ // Nothing to do.
+ break;
+ }
+ W.printFlags("Flags", Sec.sh_flags, makeArrayRef(SectionFlags));
+ W.printHex("Address", Sec.sh_addr);
+ W.printHex("Offset", Sec.sh_offset);
+ W.printNumber("Size", Sec.sh_size);
+ W.printNumber("Link", Sec.sh_link);
+ W.printNumber("Info", Sec.sh_info);
+ W.printNumber("AddressAlignment", Sec.sh_addralign);
+ W.printNumber("EntrySize", Sec.sh_entsize);
+
+ if (opts::SectionRelocations) {
+ ListScope D(W, "Relocations");
+ printRelocations(&Sec, Obj);
+ }
+
+ if (opts::SectionSymbols) {
+ ListScope D(W, "Symbols");
+ const Elf_Shdr *Symtab = this->dumper()->getDotSymtabSec();
+ StringRef StrTable = unwrapOrError(Obj->getStringTableForSymtab(*Symtab));
+
+ for (const Elf_Sym &Sym : Obj->symbols(Symtab)) {
+ const Elf_Shdr *SymSec = unwrapOrError(
+ Obj->getSection(&Sym, Symtab, this->dumper()->getShndxTable()));
+ if (SymSec == &Sec)
+ printSymbol(Obj, &Sym, Obj->symbol_begin(Symtab), StrTable, false);
+ }
+ }
+
+ if (opts::SectionData && Sec.sh_type != ELF::SHT_NOBITS) {
+ ArrayRef<uint8_t> Data = unwrapOrError(Obj->getSectionContents(&Sec));
+ W.printBinaryBlock("SectionData",
+ StringRef((const char *)Data.data(), Data.size()));
+ }
+ }
+}
+
+template <class ELFT>
+void LLVMStyle<ELFT>::printSymbol(const ELFO *Obj, const Elf_Sym *Symbol,
+ const Elf_Sym *First, StringRef StrTable,
+ bool IsDynamic) {
+ unsigned SectionIndex = 0;
+ StringRef SectionName;
+ getSectionNameIndex(*Obj, Symbol, First, this->dumper()->getShndxTable(),
+ SectionName, SectionIndex);
+ std::string FullSymbolName =
+ this->dumper()->getFullSymbolName(Symbol, StrTable, IsDynamic);
+ unsigned char SymbolType = Symbol->getType();
+
+ DictScope D(W, "Symbol");
+ W.printNumber("Name", FullSymbolName, Symbol->st_name);
+ W.printHex("Value", Symbol->st_value);
+ W.printNumber("Size", Symbol->st_size);
+ W.printEnum("Binding", Symbol->getBinding(), makeArrayRef(ElfSymbolBindings));
+ 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));
+ if (Symbol->st_other == 0)
+ // Usually st_other flag is zero. Do not pollute the output
+ // by flags enumeration in that case.
+ W.printNumber("Other", 0);
+ else {
+ std::vector<EnumEntry<unsigned>> SymOtherFlags(std::begin(ElfSymOtherFlags),
+ std::end(ElfSymOtherFlags));
+ if (Obj->getHeader()->e_machine == EM_MIPS) {
+ // Someones in their infinite wisdom decided to make STO_MIPS_MIPS16
+ // flag overlapped with other ST_MIPS_xxx flags. So consider both
+ // cases separately.
+ if ((Symbol->st_other & STO_MIPS_MIPS16) == STO_MIPS_MIPS16)
+ SymOtherFlags.insert(SymOtherFlags.end(),
+ std::begin(ElfMips16SymOtherFlags),
+ std::end(ElfMips16SymOtherFlags));
+ else
+ SymOtherFlags.insert(SymOtherFlags.end(),
+ std::begin(ElfMipsSymOtherFlags),
+ std::end(ElfMipsSymOtherFlags));
+ }
+ W.printFlags("Other", Symbol->st_other, makeArrayRef(SymOtherFlags), 0x3u);
+ }
+ W.printHex("Section", SectionName, SectionIndex);
+}
+
+template <class ELFT> void LLVMStyle<ELFT>::printSymbols(const ELFO *Obj) {
+ ListScope Group(W, "Symbols");
+ this->dumper()->printSymbolsHelper(false);
+}
+
+template <class ELFT>
+void LLVMStyle<ELFT>::printDynamicSymbols(const ELFO *Obj) {
+ ListScope Group(W, "DynamicSymbols");
+ this->dumper()->printSymbolsHelper(true);
+}
+
+template <class ELFT>
+void LLVMStyle<ELFT>::printDynamicRelocations(const ELFO *Obj) {
+ const DynRegionInfo &DynRelRegion = this->dumper()->getDynRelRegion();
+ const DynRegionInfo &DynRelaRegion = this->dumper()->getDynRelaRegion();
+ const DynRegionInfo &DynPLTRelRegion = this->dumper()->getDynPLTRelRegion();
+ if (DynRelRegion.Size && DynRelaRegion.Size)
+ report_fatal_error("There are both REL and RELA dynamic relocations");
+ W.startLine() << "Dynamic Relocations {\n";
+ W.indent();
+ if (DynRelaRegion.Size > 0)
+ for (const Elf_Rela &Rela : this->dumper()->dyn_relas())
+ printDynamicRelocation(Obj, Rela);
+ else
+ for (const Elf_Rel &Rel : this->dumper()->dyn_rels()) {
+ Elf_Rela Rela;
+ Rela.r_offset = Rel.r_offset;
+ Rela.r_info = Rel.r_info;
+ Rela.r_addend = 0;
+ printDynamicRelocation(Obj, Rela);
+ }
+ if (DynPLTRelRegion.EntSize == sizeof(Elf_Rela))
+ for (const Elf_Rela &Rela : DynPLTRelRegion.getAsArrayRef<Elf_Rela>())
+ printDynamicRelocation(Obj, Rela);
+ else
+ for (const Elf_Rel &Rel : DynPLTRelRegion.getAsArrayRef<Elf_Rel>()) {
+ Elf_Rela Rela;
+ Rela.r_offset = Rel.r_offset;
+ Rela.r_info = Rel.r_info;
+ Rela.r_addend = 0;
+ printDynamicRelocation(Obj, Rela);
+ }
+ W.unindent();
+ W.startLine() << "}\n";
+}
+
+template <class ELFT>
+void LLVMStyle<ELFT>::printDynamicRelocation(const ELFO *Obj, Elf_Rela Rel) {
+ SmallString<32> RelocName;
+ Obj->getRelocationTypeName(Rel.getType(Obj->isMips64EL()), RelocName);
+ StringRef SymbolName;
+ uint32_t SymIndex = Rel.getSymbol(Obj->isMips64EL());
+ const Elf_Sym *Sym = this->dumper()->dynamic_symbols().begin() + SymIndex;
+ SymbolName =
+ unwrapOrError(Sym->getName(this->dumper()->getDynamicStringTable()));
+ if (opts::ExpandRelocs) {
+ DictScope Group(W, "Relocation");
+ 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", Rel.r_addend);
+ } else {
+ raw_ostream &OS = W.startLine();
+ OS << W.hex(Rel.r_offset) << " " << RelocName << " "
+ << (SymbolName.size() > 0 ? SymbolName : "-") << " "
+ << W.hex(Rel.r_addend) << "\n";
+ }
+}
+
+template <class ELFT>
+void LLVMStyle<ELFT>::printProgramHeaders(const ELFO *Obj) {
+ ListScope L(W, "ProgramHeaders");
- prettyPrintStackMap(
- llvm::outs(),
- StackMapV1Parser<ELFT::TargetEndianness>(*StackMapContentsArray));
+ for (const Elf_Phdr &Phdr : Obj->program_headers()) {
+ DictScope P(W, "ProgramHeader");
+ 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 <class ELFT>
+void LLVMStyle<ELFT>::printHashHistogram(const ELFFile<ELFT> *Obj) {
+ W.startLine() << "Hash Histogram not implemented!\n";
}
diff --git a/tools/llvm-readobj/Error.cpp b/tools/llvm-readobj/Error.cpp
index 7e6f780c5d1a2..492eb33094976 100644
--- a/tools/llvm-readobj/Error.cpp
+++ b/tools/llvm-readobj/Error.cpp
@@ -17,6 +17,9 @@
using namespace llvm;
namespace {
+// FIXME: This class is only here to support the transition to llvm::Error. It
+// will be removed once this transition is complete. Clients should prefer to
+// deal with the Error value directly, rather than converting to error_code.
class _readobj_error_category : public std::error_category {
public:
const char* name() const LLVM_NOEXCEPT override;
diff --git a/tools/llvm-readobj/MachODumper.cpp b/tools/llvm-readobj/MachODumper.cpp
index 58d2c9fca47dd..3773df250578e 100644
--- a/tools/llvm-readobj/MachODumper.cpp
+++ b/tools/llvm-readobj/MachODumper.cpp
@@ -11,15 +11,15 @@
//
//===----------------------------------------------------------------------===//
-#include "llvm-readobj.h"
#include "Error.h"
#include "ObjDumper.h"
#include "StackMapPrinter.h"
-#include "StreamWriter.h"
+#include "llvm-readobj.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Object/MachO.h"
#include "llvm/Support/Casting.h"
+#include "llvm/Support/ScopedPrinter.h"
using namespace llvm;
using namespace object;
@@ -28,9 +28,8 @@ namespace {
class MachODumper : public ObjDumper {
public:
- MachODumper(const MachOObjectFile *Obj, StreamWriter& Writer)
- : ObjDumper(Writer)
- , Obj(Obj) { }
+ MachODumper(const MachOObjectFile *Obj, ScopedPrinter &Writer)
+ : ObjDumper(Writer), Obj(Obj) {}
void printFileHeaders() override;
void printSections() override;
@@ -69,7 +68,7 @@ private:
namespace llvm {
std::error_code createMachODumper(const object::ObjectFile *Obj,
- StreamWriter &Writer,
+ ScopedPrinter &Writer,
std::unique_ptr<ObjDumper> &Result) {
const MachOObjectFile *MachOObj = dyn_cast<MachOObjectFile>(Obj);
if (!MachOObj)
@@ -239,7 +238,8 @@ static const EnumEntry<unsigned> MachOSymbolFlags[] = {
{ "ReferencedDynamically", 0x10 },
{ "NoDeadStrip", 0x20 },
{ "WeakRef", 0x40 },
- { "WeakDef", 0x80 }
+ { "WeakDef", 0x80 },
+ { "AltEntry", 0x200 },
};
static const EnumEntry<unsigned> MachOSymbolTypes[] = {
@@ -540,8 +540,9 @@ void MachODumper::printRelocation(const MachOObjectFile *Obj,
if (IsExtern) {
symbol_iterator Symbol = Reloc.getSymbol();
if (Symbol != Obj->symbol_end()) {
- ErrorOr<StringRef> TargetNameOrErr = Symbol->getName();
- error(TargetNameOrErr.getError());
+ Expected<StringRef> TargetNameOrErr = Symbol->getName();
+ if (!TargetNameOrErr)
+ error(errorToErrorCode(TargetNameOrErr.takeError()));
TargetName = *TargetNameOrErr;
}
} else if (!IsScattered) {
@@ -604,15 +605,19 @@ void MachODumper::printDynamicSymbols() {
void MachODumper::printSymbol(const SymbolRef &Symbol) {
StringRef SymbolName;
- if (ErrorOr<StringRef> SymbolNameOrErr = Symbol.getName())
+ Expected<StringRef> SymbolNameOrErr = Symbol.getName();
+ if (!SymbolNameOrErr) {
+ // TODO: Actually report errors helpfully.
+ consumeError(SymbolNameOrErr.takeError());
+ } else
SymbolName = *SymbolNameOrErr;
MachOSymbol MOSymbol;
getSymbol(Obj, Symbol.getRawDataRefImpl(), MOSymbol);
StringRef SectionName = "";
- ErrorOr<section_iterator> SecIOrErr = Symbol.getSection();
- error(SecIOrErr.getError());
+ Expected<section_iterator> SecIOrErr = Symbol.getSection();
+ error(errorToErrorCode(SecIOrErr.takeError()));
section_iterator SecI = *SecIOrErr;
if (SecI != Obj->section_end())
error(SecI->getName(SectionName));
diff --git a/tools/llvm-readobj/Makefile b/tools/llvm-readobj/Makefile
deleted file mode 100644
index 958bd0c5b3121..0000000000000
--- a/tools/llvm-readobj/Makefile
+++ /dev/null
@@ -1,18 +0,0 @@
-##===- tools/llvm-readobj/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-readobj
-LINK_COMPONENTS := bitreader object all-targets
-
-# This tool has no plugins, optimize startup time.
-TOOL_NO_EXPORTS := 1
-
-include $(LEVEL)/Makefile.common
-
diff --git a/tools/llvm-readobj/ObjDumper.cpp b/tools/llvm-readobj/ObjDumper.cpp
index f500a05815532..2a0a90e5cfd5d 100644
--- a/tools/llvm-readobj/ObjDumper.cpp
+++ b/tools/llvm-readobj/ObjDumper.cpp
@@ -14,16 +14,13 @@
#include "ObjDumper.h"
#include "Error.h"
-#include "StreamWriter.h"
-#include "llvm/ADT/StringRef.h"
#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/ScopedPrinter.h"
#include "llvm/Support/raw_ostream.h"
namespace llvm {
-ObjDumper::ObjDumper(StreamWriter& Writer)
- : W(Writer) {
-}
+ObjDumper::ObjDumper(ScopedPrinter &Writer) : W(Writer) {}
ObjDumper::~ObjDumper() {
}
diff --git a/tools/llvm-readobj/ObjDumper.h b/tools/llvm-readobj/ObjDumper.h
index db26d6983552d..a39fc260b5493 100644
--- a/tools/llvm-readobj/ObjDumper.h
+++ b/tools/llvm-readobj/ObjDumper.h
@@ -18,12 +18,15 @@ namespace object {
class COFFImportFile;
class ObjectFile;
}
+namespace codeview {
+class MemoryTypeTableBuilder;
+}
-class StreamWriter;
+class ScopedPrinter;
class ObjDumper {
public:
- ObjDumper(StreamWriter& Writer);
+ ObjDumper(ScopedPrinter &Writer);
virtual ~ObjDumper();
virtual void printFileHeaders() = 0;
@@ -42,6 +45,8 @@ public:
virtual void printGnuHashTable() { }
virtual void printLoadName() {}
virtual void printVersionInfo() {}
+ virtual void printGroupSections() {}
+ virtual void printHashHistogram() {}
// Only implemented for ARM ELF at this time.
virtual void printAttributes() { }
@@ -50,13 +55,17 @@ public:
virtual void printMipsPLTGOT() { }
virtual void printMipsABIFlags() { }
virtual void printMipsReginfo() { }
+ virtual void printMipsOptions() { }
// Only implemented for PE/COFF.
virtual void printCOFFImports() { }
virtual void printCOFFExports() { }
virtual void printCOFFDirectives() { }
virtual void printCOFFBaseReloc() { }
+ virtual void printCOFFDebugDirectory() { }
virtual void printCodeViewDebugInfo() { }
+ virtual void
+ mergeCodeViewTypes(llvm::codeview::MemoryTypeTableBuilder &CVTypes) {}
// Only implemented for MachO.
virtual void printMachODataInCode() { }
@@ -69,23 +78,26 @@ public:
virtual void printStackMap() const = 0;
protected:
- StreamWriter& W;
+ ScopedPrinter &W;
};
std::error_code createCOFFDumper(const object::ObjectFile *Obj,
- StreamWriter &Writer,
+ ScopedPrinter &Writer,
std::unique_ptr<ObjDumper> &Result);
std::error_code createELFDumper(const object::ObjectFile *Obj,
- StreamWriter &Writer,
+ ScopedPrinter &Writer,
std::unique_ptr<ObjDumper> &Result);
std::error_code createMachODumper(const object::ObjectFile *Obj,
- StreamWriter &Writer,
+ ScopedPrinter &Writer,
std::unique_ptr<ObjDumper> &Result);
void dumpCOFFImportFile(const object::COFFImportFile *File);
+void dumpCodeViewMergedTypes(ScopedPrinter &Writer,
+ llvm::codeview::MemoryTypeTableBuilder &CVTypes);
+
} // namespace llvm
#endif
diff --git a/tools/llvm-readobj/StreamWriter.cpp b/tools/llvm-readobj/StreamWriter.cpp
deleted file mode 100644
index 871811233a65f..0000000000000
--- a/tools/llvm-readobj/StreamWriter.cpp
+++ /dev/null
@@ -1,79 +0,0 @@
-#include "StreamWriter.h"
-#include "llvm/ADT/StringExtras.h"
-#include "llvm/Support/Format.h"
-#include <cctype>
-
-using namespace llvm::support;
-
-namespace llvm {
-
-raw_ostream &operator<<(raw_ostream &OS, const HexNumber& Value) {
- uint64_t N = Value.Value;
- // Zero is a special case.
- if (N == 0)
- return OS << "0x0";
-
- char NumberBuffer[20];
- char *EndPtr = NumberBuffer + sizeof(NumberBuffer);
- char *CurPtr = EndPtr;
-
- while (N) {
- uintptr_t X = N % 16;
- *--CurPtr = (X < 10 ? '0' + X : 'A' + X - 10);
- N /= 16;
- }
-
- OS << "0x";
- return OS.write(CurPtr, EndPtr - CurPtr);
-}
-
-void StreamWriter::printBinaryImpl(StringRef Label, StringRef Str,
- ArrayRef<uint8_t> Data, bool Block) {
- if (Data.size() > 16)
- Block = true;
-
- if (Block) {
- startLine() << Label;
- if (Str.size() > 0)
- OS << ": " << Str;
- OS << " (\n";
- for (size_t addr = 0, end = Data.size(); addr < end; addr += 16) {
- startLine() << format(" %04" PRIX64 ": ", uint64_t(addr));
- // Dump line of hex.
- for (size_t i = 0; i < 16; ++i) {
- if (i != 0 && i % 4 == 0)
- OS << ' ';
- if (addr + i < end)
- OS << hexdigit((Data[addr + i] >> 4) & 0xF, false)
- << hexdigit(Data[addr + i] & 0xF, false);
- else
- OS << " ";
- }
- // Print ascii.
- OS << " |";
- for (std::size_t i = 0; i < 16 && addr + i < end; ++i) {
- if (std::isprint(Data[addr + i] & 0xFF))
- OS << Data[addr + i];
- else
- OS << ".";
- }
- OS << "|\n";
- }
-
- startLine() << ")\n";
- } else {
- startLine() << Label << ":";
- if (Str.size() > 0)
- OS << " " << Str;
- OS << " (";
- for (size_t i = 0; i < Data.size(); ++i) {
- if (i > 0)
- OS << " ";
-
- OS << format("%02X", static_cast<int>(Data[i]));
- }
- OS << ")\n";
- }
-}
-
-} // namespace llvm
diff --git a/tools/llvm-readobj/StreamWriter.h b/tools/llvm-readobj/StreamWriter.h
deleted file mode 100644
index d2dbb07af2566..0000000000000
--- a/tools/llvm-readobj/StreamWriter.h
+++ /dev/null
@@ -1,320 +0,0 @@
-//===-- StreamWriter.h ----------------------------------------------------===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_TOOLS_LLVM_READOBJ_STREAMWRITER_H
-#define LLVM_TOOLS_LLVM_READOBJ_STREAMWRITER_H
-
-#include "llvm/ADT/ArrayRef.h"
-#include "llvm/ADT/SmallVector.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/Support/DataTypes.h"
-#include "llvm/Support/Endian.h"
-#include "llvm/Support/raw_ostream.h"
-#include <algorithm>
-
-using namespace llvm;
-using namespace llvm::support;
-
-namespace llvm {
-
-template<typename T>
-struct EnumEntry {
- StringRef Name;
- T Value;
-};
-
-struct HexNumber {
- // To avoid sign-extension we have to explicitly cast to the appropriate
- // 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(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;
-};
-
-raw_ostream &operator<<(raw_ostream &OS, const HexNumber& Value);
-
-class StreamWriter {
-public:
- StreamWriter(raw_ostream &OS)
- : OS(OS)
- , IndentLevel(0) {
- }
-
- void flush() {
- OS.flush();
- }
-
- void indent(int Levels = 1) {
- IndentLevel += Levels;
- }
-
- void unindent(int Levels = 1) {
- IndentLevel = std::max(0, IndentLevel - Levels);
- }
-
- void printIndent() {
- for (int i = 0; i < IndentLevel; ++i)
- OS << " ";
- }
-
- template<typename T>
- HexNumber hex(T Value) {
- return HexNumber(Value);
- }
-
- template<typename T, typename TEnum>
- void printEnum(StringRef Label, T Value,
- ArrayRef<EnumEntry<TEnum> > EnumValues) {
- StringRef Name;
- bool Found = false;
- for (const auto &EnumItem : EnumValues) {
- if (EnumItem.Value == Value) {
- Name = EnumItem.Name;
- Found = true;
- break;
- }
- }
-
- if (Found) {
- startLine() << Label << ": " << Name << " (" << hex(Value) << ")\n";
- } else {
- startLine() << Label << ": " << hex(Value) << "\n";
- }
- }
-
- template <typename T, typename TFlag>
- void printFlags(StringRef Label, T Value, ArrayRef<EnumEntry<TFlag>> Flags,
- TFlag EnumMask1 = {}, TFlag EnumMask2 = {},
- TFlag EnumMask3 = {}) {
- typedef EnumEntry<TFlag> FlagEntry;
- typedef SmallVector<FlagEntry, 10> FlagVector;
- FlagVector SetFlags;
-
- for (const auto &Flag : Flags) {
- if (Flag.Value == 0)
- continue;
-
- TFlag EnumMask{};
- if (Flag.Value & EnumMask1)
- EnumMask = EnumMask1;
- else if (Flag.Value & EnumMask2)
- EnumMask = EnumMask2;
- else if (Flag.Value & EnumMask3)
- EnumMask = EnumMask3;
- bool IsEnum = (Flag.Value & EnumMask) != 0;
- if ((!IsEnum && (Value & Flag.Value) == Flag.Value) ||
- (IsEnum && (Value & EnumMask) == Flag.Value)) {
- SetFlags.push_back(Flag);
- }
- }
-
- std::sort(SetFlags.begin(), SetFlags.end(), &flagName<TFlag>);
-
- startLine() << Label << " [ (" << hex(Value) << ")\n";
- for (const auto &Flag : SetFlags) {
- startLine() << " " << Flag.Name << " (" << hex(Flag.Value) << ")\n";
- }
- startLine() << "]\n";
- }
-
- template<typename T>
- void printFlags(StringRef Label, T Value) {
- startLine() << Label << " [ (" << hex(Value) << ")\n";
- uint64_t Flag = 1;
- uint64_t Curr = Value;
- while (Curr > 0) {
- if (Curr & 1)
- startLine() << " " << hex(Flag) << "\n";
- Curr >>= 1;
- Flag <<= 1;
- }
- startLine() << "]\n";
- }
-
- void printNumber(StringRef Label, uint64_t Value) {
- startLine() << Label << ": " << Value << "\n";
- }
-
- void printNumber(StringRef Label, uint32_t Value) {
- startLine() << Label << ": " << Value << "\n";
- }
-
- void printNumber(StringRef Label, uint16_t Value) {
- startLine() << Label << ": " << Value << "\n";
- }
-
- void printNumber(StringRef Label, uint8_t Value) {
- startLine() << Label << ": " << unsigned(Value) << "\n";
- }
-
- void printNumber(StringRef Label, int64_t Value) {
- startLine() << Label << ": " << Value << "\n";
- }
-
- void printNumber(StringRef Label, int32_t Value) {
- startLine() << Label << ": " << Value << "\n";
- }
-
- void printNumber(StringRef Label, int16_t Value) {
- startLine() << Label << ": " << Value << "\n";
- }
-
- void printNumber(StringRef Label, int8_t Value) {
- startLine() << Label << ": " << int(Value) << "\n";
- }
-
- void printBoolean(StringRef Label, bool Value) {
- startLine() << Label << ": " << (Value ? "Yes" : "No") << '\n';
- }
-
- template <typename T>
- void printList(StringRef Label, const T &List) {
- startLine() << Label << ": [";
- bool Comma = false;
- for (const auto &Item : List) {
- if (Comma)
- OS << ", ";
- OS << Item;
- Comma = true;
- }
- 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";
- }
-
- template<typename T>
- void printHex(StringRef Label, StringRef Str, T Value) {
- startLine() << Label << ": " << Str << " (" << hex(Value) << ")\n";
- }
-
- void printString(StringRef Label, StringRef Value) {
- startLine() << Label << ": " << Value << "\n";
- }
-
- void printString(StringRef Label, const std::string &Value) {
- startLine() << Label << ": " << Value << "\n";
- }
-
- template<typename T>
- void printNumber(StringRef Label, StringRef Str, T Value) {
- startLine() << Label << ": " << Str << " (" << Value << ")\n";
- }
-
- void printBinary(StringRef Label, StringRef Str, ArrayRef<uint8_t> Value) {
- printBinaryImpl(Label, Str, Value, false);
- }
-
- void printBinary(StringRef Label, StringRef Str, ArrayRef<char> Value) {
- auto V = makeArrayRef(reinterpret_cast<const uint8_t*>(Value.data()),
- Value.size());
- printBinaryImpl(Label, Str, V, false);
- }
-
- void printBinary(StringRef Label, ArrayRef<uint8_t> Value) {
- printBinaryImpl(Label, StringRef(), Value, false);
- }
-
- void printBinary(StringRef Label, ArrayRef<char> Value) {
- auto V = makeArrayRef(reinterpret_cast<const uint8_t*>(Value.data()),
- Value.size());
- printBinaryImpl(Label, StringRef(), V, false);
- }
-
- void printBinary(StringRef Label, StringRef Value) {
- auto V = makeArrayRef(reinterpret_cast<const uint8_t*>(Value.data()),
- Value.size());
- printBinaryImpl(Label, StringRef(), V, false);
- }
-
- void printBinaryBlock(StringRef Label, StringRef Value) {
- auto V = makeArrayRef(reinterpret_cast<const uint8_t*>(Value.data()),
- Value.size());
- printBinaryImpl(Label, StringRef(), V, true);
- }
-
- raw_ostream& startLine() {
- printIndent();
- return OS;
- }
-
- raw_ostream& getOStream() {
- return OS;
- }
-
-private:
- template<typename T>
- static bool flagName(const EnumEntry<T>& lhs, const EnumEntry<T>& rhs) {
- return lhs.Name < rhs.Name;
- }
-
- void printBinaryImpl(StringRef Label, StringRef Str, ArrayRef<uint8_t> Value,
- bool Block);
-
- raw_ostream &OS;
- int IndentLevel;
-};
-
-struct DictScope {
- DictScope(StreamWriter& W, StringRef N) : W(W) {
- W.startLine() << N << " {\n";
- W.indent();
- }
-
- ~DictScope() {
- W.unindent();
- W.startLine() << "}\n";
- }
-
- StreamWriter& W;
-};
-
-struct ListScope {
- ListScope(StreamWriter& W, StringRef N) : W(W) {
- W.startLine() << N << " [\n";
- W.indent();
- }
-
- ~ListScope() {
- W.unindent();
- W.startLine() << "]\n";
- }
-
- StreamWriter& W;
-};
-
-} // namespace llvm
-
-#endif
diff --git a/tools/llvm-readobj/Win64EHDumper.cpp b/tools/llvm-readobj/Win64EHDumper.cpp
index 2da5ae3200fde..f7e56b3615425 100644
--- a/tools/llvm-readobj/Win64EHDumper.cpp
+++ b/tools/llvm-readobj/Win64EHDumper.cpp
@@ -120,13 +120,17 @@ static std::string formatSymbol(const Dumper::Context &Ctx,
SymbolRef Symbol;
if (!Ctx.ResolveSymbol(Section, Offset, Symbol, Ctx.UserData)) {
- if (ErrorOr<StringRef> Name = Symbol.getName()) {
+ Expected<StringRef> Name = Symbol.getName();
+ if (Name) {
OS << *Name;
if (Displacement > 0)
OS << format(" +0x%X (0x%" PRIX64 ")", Displacement, Offset);
else
OS << format(" (0x%" PRIX64 ")", Offset);
return OS.str();
+ } else {
+ // TODO: Actually report errors helpfully.
+ consumeError(Name.takeError());
}
}
@@ -144,12 +148,14 @@ static std::error_code resolveRelocation(const Dumper::Context &Ctx,
Ctx.ResolveSymbol(Section, Offset, Symbol, Ctx.UserData))
return EC;
- ErrorOr<uint64_t> ResolvedAddressOrErr = Symbol.getAddress();
- if (std::error_code EC = ResolvedAddressOrErr.getError())
- return EC;
+ Expected<uint64_t> ResolvedAddressOrErr = Symbol.getAddress();
+ if (!ResolvedAddressOrErr)
+ return errorToErrorCode(ResolvedAddressOrErr.takeError());
ResolvedAddress = *ResolvedAddressOrErr;
- ErrorOr<section_iterator> SI = Symbol.getSection();
+ Expected<section_iterator> SI = Symbol.getSection();
+ if (!SI)
+ return errorToErrorCode(SI.takeError());
ResolvedSection = Ctx.COFF.getCOFFSection(**SI);
return std::error_code();
}
diff --git a/tools/llvm-readobj/Win64EHDumper.h b/tools/llvm-readobj/Win64EHDumper.h
index a80df9c4f94dd..772f68bf283f7 100644
--- a/tools/llvm-readobj/Win64EHDumper.h
+++ b/tools/llvm-readobj/Win64EHDumper.h
@@ -10,7 +10,7 @@
#ifndef LLVM_TOOLS_LLVM_READOBJ_WIN64EHDUMPER_H
#define LLVM_TOOLS_LLVM_READOBJ_WIN64EHDUMPER_H
-#include "StreamWriter.h"
+#include "llvm/Support/ScopedPrinter.h"
#include "llvm/Support/Win64EH.h"
namespace llvm {
@@ -22,7 +22,7 @@ struct coff_section;
namespace Win64EH {
class Dumper {
- StreamWriter &SW;
+ ScopedPrinter &SW;
raw_ostream &OS;
public:
@@ -53,7 +53,7 @@ private:
uint64_t SectionOffset, const RuntimeFunction &RF);
public:
- Dumper(StreamWriter &SW) : SW(SW), OS(SW.getOStream()) {}
+ Dumper(ScopedPrinter &SW) : SW(SW), OS(SW.getOStream()) {}
void printData(const Context &Ctx);
};
diff --git a/tools/llvm-readobj/llvm-readobj.cpp b/tools/llvm-readobj/llvm-readobj.cpp
index fa8fee2b03ab7..c293919fd9527 100644
--- a/tools/llvm-readobj/llvm-readobj.cpp
+++ b/tools/llvm-readobj/llvm-readobj.cpp
@@ -22,7 +22,7 @@
#include "llvm-readobj.h"
#include "Error.h"
#include "ObjDumper.h"
-#include "StreamWriter.h"
+#include "llvm/DebugInfo/CodeView/MemoryTypeTableBuilder.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/COFFImportFile.h"
#include "llvm/Object/ELFObjectFile.h"
@@ -35,6 +35,7 @@
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/PrettyStackTrace.h"
+#include "llvm/Support/ScopedPrinter.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
@@ -144,6 +145,11 @@ namespace opts {
cl::opt<bool> CodeView("codeview",
cl::desc("Display CodeView debug information"));
+ // -codeview-merged-types
+ cl::opt<bool>
+ CodeViewMergedTypes("codeview-merged-types",
+ cl::desc("Display the merged CodeView type stream"));
+
// -codeview-subsection-bytes
cl::opt<bool> CodeViewSubsectionBytes(
"codeview-subsection-bytes",
@@ -168,6 +174,10 @@ namespace opts {
cl::opt<bool> MipsReginfo("mips-reginfo",
cl::desc("Display the MIPS .reginfo section"));
+ // -mips-options
+ cl::opt<bool> MipsOptions("mips-options",
+ cl::desc("Display the MIPS .MIPS.options section"));
+
// -coff-imports
cl::opt<bool>
COFFImports("coff-imports", cl::desc("Display the PE/COFF import table"));
@@ -186,6 +196,11 @@ namespace opts {
COFFBaseRelocs("coff-basereloc",
cl::desc("Display the PE/COFF .reloc section"));
+ // -coff-debug-directory
+ cl::opt<bool>
+ COFFDebugDirectory("coff-debug-directory",
+ cl::desc("Display the PE/COFF debug directory"));
+
// -macho-data-in-code
cl::opt<bool>
MachODataInCode("macho-data-in-code",
@@ -227,6 +242,22 @@ namespace opts {
cl::desc("Display ELF version sections (if present)"));
cl::alias VersionInfoShort("V", cl::desc("Alias for -version-info"),
cl::aliasopt(VersionInfo));
+
+ cl::opt<bool> SectionGroups("elf-section-groups",
+ cl::desc("Display ELF section group contents"));
+ cl::alias SectionGroupsShort("g", cl::desc("Alias for -elf-sections-groups"),
+ cl::aliasopt(SectionGroups));
+ cl::opt<bool> HashHistogram(
+ "elf-hash-histogram",
+ cl::desc("Display bucket list histogram for hash sections"));
+ cl::alias HashHistogramShort("I", cl::desc("Alias for -elf-hash-histogram"),
+ cl::aliasopt(HashHistogram));
+
+ cl::opt<OutputStyleTy>
+ Output("elf-output-style", cl::desc("Specify ELF dump style"),
+ cl::values(clEnumVal(LLVM, "LLVM default style"),
+ clEnumVal(GNU, "GNU readelf style"), clEnumValEnd),
+ cl::init(LLVM));
} // namespace opts
namespace llvm {
@@ -264,6 +295,17 @@ static void reportError(StringRef Input, StringRef Message) {
reportError(Twine(Input) + ": " + Message);
}
+static void reportError(StringRef Input, Error Err) {
+ if (Input == "-")
+ Input = "<stdin>";
+ std::string ErrMsg;
+ {
+ raw_string_ostream ErrStream(ErrMsg);
+ logAllUnhandledErrors(std::move(Err), ErrStream, Input + ": ");
+ }
+ reportError(ErrMsg);
+}
+
static bool isMipsArch(unsigned Arch) {
switch (Arch) {
case llvm::Triple::mips:
@@ -276,8 +318,11 @@ static bool isMipsArch(unsigned Arch) {
}
}
+static llvm::codeview::MemoryTypeTableBuilder CVTypes;
+
/// @brief Creates an format-specific object file dumper.
-static std::error_code createDumper(const ObjectFile *Obj, StreamWriter &Writer,
+static std::error_code createDumper(const ObjectFile *Obj,
+ ScopedPrinter &Writer,
std::unique_ptr<ObjDumper> &Result) {
if (!Obj)
return readobj_error::unsupported_file_format;
@@ -294,19 +339,20 @@ static std::error_code createDumper(const ObjectFile *Obj, StreamWriter &Writer,
/// @brief Dumps the specified object file.
static void dumpObject(const ObjectFile *Obj) {
- StreamWriter Writer(outs());
+ ScopedPrinter Writer(outs());
std::unique_ptr<ObjDumper> Dumper;
if (std::error_code EC = createDumper(Obj, Writer, Dumper))
reportError(Obj->getFileName(), EC);
- outs() << '\n';
- outs() << "File: " << Obj->getFileName() << "\n";
- outs() << "Format: " << Obj->getFileFormatName() << "\n";
- outs() << "Arch: "
- << Triple::getArchTypeName((llvm::Triple::ArchType)Obj->getArch())
- << "\n";
- outs() << "AddressSize: " << (8*Obj->getBytesInAddress()) << "bit\n";
- Dumper->printLoadName();
+ if (opts::Output == opts::LLVM) {
+ outs() << '\n';
+ outs() << "File: " << Obj->getFileName() << "\n";
+ outs() << "Format: " << Obj->getFileFormatName() << "\n";
+ outs() << "Arch: " << Triple::getArchTypeName(
+ (llvm::Triple::ArchType)Obj->getArch()) << "\n";
+ outs() << "AddressSize: " << (8 * Obj->getBytesInAddress()) << "bit\n";
+ Dumper->printLoadName();
+ }
if (opts::FileHeaders)
Dumper->printFileHeaders();
@@ -334,16 +380,24 @@ static void dumpObject(const ObjectFile *Obj) {
Dumper->printGnuHashTable();
if (opts::VersionInfo)
Dumper->printVersionInfo();
- if (Obj->getArch() == llvm::Triple::arm && Obj->isELF())
- if (opts::ARMAttributes)
- Dumper->printAttributes();
- if (isMipsArch(Obj->getArch()) && Obj->isELF()) {
- if (opts::MipsPLTGOT)
- Dumper->printMipsPLTGOT();
- if (opts::MipsABIFlags)
- Dumper->printMipsABIFlags();
- if (opts::MipsReginfo)
- Dumper->printMipsReginfo();
+ if (Obj->isELF()) {
+ if (Obj->getArch() == llvm::Triple::arm)
+ if (opts::ARMAttributes)
+ Dumper->printAttributes();
+ if (isMipsArch(Obj->getArch())) {
+ if (opts::MipsPLTGOT)
+ Dumper->printMipsPLTGOT();
+ if (opts::MipsABIFlags)
+ Dumper->printMipsABIFlags();
+ if (opts::MipsReginfo)
+ Dumper->printMipsReginfo();
+ if (opts::MipsOptions)
+ Dumper->printMipsOptions();
+ }
+ if (opts::SectionGroups)
+ Dumper->printGroupSections();
+ if (opts::HashHistogram)
+ Dumper->printHashHistogram();
}
if (Obj->isCOFF()) {
if (opts::COFFImports)
@@ -354,8 +408,12 @@ static void dumpObject(const ObjectFile *Obj) {
Dumper->printCOFFDirectives();
if (opts::COFFBaseRelocs)
Dumper->printCOFFBaseReloc();
+ if (opts::COFFDebugDirectory)
+ Dumper->printCOFFDebugDirectory();
if (opts::CodeView)
Dumper->printCodeViewDebugInfo();
+ if (opts::CodeViewMergedTypes)
+ Dumper->mergeCodeViewTypes(CVTypes);
}
if (Obj->isMachO()) {
if (opts::MachODataInCode)
@@ -377,35 +435,43 @@ static void dumpObject(const ObjectFile *Obj) {
/// @brief Dumps each object file in \a Arc;
static void dumpArchive(const Archive *Arc) {
- 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)
- reportError(Arc->getFileName(), EC.message());
+ Error Err;
+ for (auto &Child : Arc->children(Err)) {
+ Expected<std::unique_ptr<Binary>> ChildOrErr = Child.getAsBinary();
+ if (!ChildOrErr) {
+ if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) {
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(ChildOrErr.takeError(), OS, "");
+ OS.flush();
+ reportError(Arc->getFileName(), Buf);
+ }
continue;
}
-
if (ObjectFile *Obj = dyn_cast<ObjectFile>(&*ChildOrErr.get()))
dumpObject(Obj);
else
reportError(Arc->getFileName(), readobj_error::unrecognized_file_format);
}
+ if (Err)
+ reportError(Arc->getFileName(), std::move(Err));
}
/// @brief Dumps each object file in \a MachO Universal Binary;
static void dumpMachOUniversalBinary(const MachOUniversalBinary *UBinary) {
for (const MachOUniversalBinary::ObjectForArch &Obj : UBinary->objects()) {
- ErrorOr<std::unique_ptr<MachOObjectFile>> ObjOrErr = Obj.getAsObjectFile();
+ Expected<std::unique_ptr<MachOObjectFile>> ObjOrErr = Obj.getAsObjectFile();
if (ObjOrErr)
dumpObject(&*ObjOrErr.get());
- else if (ErrorOr<std::unique_ptr<Archive>> AOrErr = Obj.getAsArchive())
+ else if (auto E = isNotObjectErrorInvalidFileType(ObjOrErr.takeError())) {
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(ObjOrErr.takeError(), OS, "");
+ OS.flush();
+ reportError(UBinary->getFileName(), Buf);
+ }
+ else if (Expected<std::unique_ptr<Archive>> AOrErr = Obj.getAsArchive())
dumpArchive(&*AOrErr.get());
- else
- reportError(UBinary->getFileName(), ObjOrErr.getError().message());
}
}
@@ -413,9 +479,9 @@ static void dumpMachOUniversalBinary(const MachOUniversalBinary *UBinary) {
static void dumpInput(StringRef File) {
// Attempt to open the binary.
- ErrorOr<OwningBinary<Binary>> BinaryOrErr = createBinary(File);
- if (std::error_code EC = BinaryOrErr.getError())
- reportError(File, EC);
+ Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(File);
+ if (!BinaryOrErr)
+ reportError(File, errorToErrorCode(BinaryOrErr.takeError()));
Binary &Binary = *BinaryOrErr.get().getBinary();
if (Archive *Arc = dyn_cast<Archive>(&Binary))
@@ -432,7 +498,7 @@ static void dumpInput(StringRef File) {
}
int main(int argc, const char *argv[]) {
- sys::PrintStackTraceOnErrorSignal();
+ sys::PrintStackTraceOnErrorSignal(argv[0]);
PrettyStackTraceProgram X(argc, argv);
llvm_shutdown_obj Y;
@@ -448,5 +514,10 @@ int main(int argc, const char *argv[]) {
std::for_each(opts::InputFilenames.begin(), opts::InputFilenames.end(),
dumpInput);
+ if (opts::CodeViewMergedTypes) {
+ ScopedPrinter W(outs());
+ dumpCodeViewMergedTypes(W, CVTypes);
+ }
+
return 0;
}
diff --git a/tools/llvm-readobj/llvm-readobj.h b/tools/llvm-readobj/llvm-readobj.h
index 5a103920c165e..b169c00291838 100644
--- a/tools/llvm-readobj/llvm-readobj.h
+++ b/tools/llvm-readobj/llvm-readobj.h
@@ -12,6 +12,8 @@
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Compiler.h"
+#include "llvm/Support/ErrorOr.h"
+#include "llvm/Support/Error.h"
#include <string>
namespace llvm {
@@ -22,6 +24,20 @@ namespace llvm {
// Various helper functions.
LLVM_ATTRIBUTE_NORETURN void reportError(Twine Msg);
void error(std::error_code ec);
+ template <class T> T unwrapOrError(ErrorOr<T> EO) {
+ if (EO)
+ return *EO;
+ reportError(EO.getError().message());
+ }
+ template <class T> T unwrapOrError(Expected<T> EO) {
+ if (EO)
+ return *EO;
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(EO.takeError(), OS, "");
+ OS.flush();
+ reportError(Buf);
+ }
bool relocAddressLess(object::RelocationRef A,
object::RelocationRef B);
} // namespace llvm
@@ -42,9 +58,14 @@ namespace opts {
extern llvm::cl::opt<bool> CodeViewSubsectionBytes;
extern llvm::cl::opt<bool> ARMAttributes;
extern llvm::cl::opt<bool> MipsPLTGOT;
+ enum OutputStyleTy { LLVM, GNU };
+ extern llvm::cl::opt<OutputStyleTy> Output;
} // namespace opts
#define LLVM_READOBJ_ENUM_ENT(ns, enum) \
{ #enum, ns::enum }
+#define LLVM_READOBJ_ENUM_CLASS_ENT(enum_class, enum) \
+ { #enum, std::underlying_type<enum_class>::type(enum_class::enum) }
+
#endif
diff --git a/tools/llvm-rtdyld/Makefile b/tools/llvm-rtdyld/Makefile
deleted file mode 100644
index 3e868b9858724..0000000000000
--- a/tools/llvm-rtdyld/Makefile
+++ /dev/null
@@ -1,17 +0,0 @@
-##===- tools/llvm-rtdyld/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-rtdyld
-LINK_COMPONENTS := all-targets support MC object RuntimeDyld MCJIT DebugInfoDWARF
-
-# This tool has no plugins, optimize startup time.
-TOOL_NO_EXPORTS := 1
-
-include $(LEVEL)/Makefile.common
diff --git a/tools/llvm-rtdyld/llvm-rtdyld.cpp b/tools/llvm-rtdyld/llvm-rtdyld.cpp
index 6ee3a44b63bf5..b1460e35de80f 100644
--- a/tools/llvm-rtdyld/llvm-rtdyld.cpp
+++ b/tools/llvm-rtdyld/llvm-rtdyld.cpp
@@ -19,7 +19,7 @@
#include "llvm/ExecutionEngine/RuntimeDyldChecker.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCContext.h"
-#include "llvm/MC/MCDisassembler.h"
+#include "llvm/MC/MCDisassembler/MCDisassembler.h"
#include "llvm/MC/MCInstPrinter.h"
#include "llvm/MC/MCInstrInfo.h"
#include "llvm/MC/MCRegisterInfo.h"
@@ -191,7 +191,7 @@ public:
}
uint8_t *allocateFromSlab(uintptr_t Size, unsigned Alignment, bool isCode) {
- Size = RoundUpToAlignment(Size, Alignment);
+ Size = alignTo(Size, Alignment);
if (CurrentSlabOffset + Size > SlabSize)
report_fatal_error("Can't allocate enough memory. Tune --preallocate");
@@ -254,9 +254,9 @@ uint8_t *TrivialMemoryManager::allocateDataSection(uintptr_t Size,
static const char *ProgramName;
-static int Error(const Twine &Msg) {
+static void ErrorAndExit(const Twine &Msg) {
errs() << ProgramName << ": error: " << Msg << "\n";
- return 1;
+ exit(1);
}
static void loadDylibs() {
@@ -290,13 +290,18 @@ static int printLineInfoForInput(bool LoadObjects, bool UseDebugObj) {
ErrorOr<std::unique_ptr<MemoryBuffer>> InputBuffer =
MemoryBuffer::getFileOrSTDIN(File);
if (std::error_code EC = InputBuffer.getError())
- return Error("unable to read input: '" + EC.message() + "'");
+ ErrorAndExit("unable to read input: '" + EC.message() + "'");
- ErrorOr<std::unique_ptr<ObjectFile>> MaybeObj(
+ Expected<std::unique_ptr<ObjectFile>> MaybeObj(
ObjectFile::createObjectFile((*InputBuffer)->getMemBufferRef()));
- if (std::error_code EC = MaybeObj.getError())
- return Error("unable to create object file: '" + EC.message() + "'");
+ if (!MaybeObj) {
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(MaybeObj.takeError(), OS, "");
+ OS.flush();
+ ErrorAndExit("unable to create object file: '" + Buf + "'");
+ }
ObjectFile &Obj = **MaybeObj;
@@ -309,7 +314,7 @@ static int printLineInfoForInput(bool LoadObjects, bool UseDebugObj) {
Dyld.loadObject(Obj);
if (Dyld.hasError())
- return Error(Dyld.getErrorString());
+ ErrorAndExit(Dyld.getErrorString());
// Resolve all the relocations we can.
Dyld.resolveRelocations();
@@ -330,13 +335,26 @@ static int printLineInfoForInput(bool LoadObjects, bool UseDebugObj) {
// Use symbol info to iterate functions in the object.
for (const auto &P : SymAddr) {
object::SymbolRef Sym = P.first;
- if (Sym.getType() == object::SymbolRef::ST_Function) {
- ErrorOr<StringRef> Name = Sym.getName();
- if (!Name)
+ Expected<SymbolRef::Type> TypeOrErr = Sym.getType();
+ if (!TypeOrErr) {
+ // TODO: Actually report errors helpfully.
+ consumeError(TypeOrErr.takeError());
+ continue;
+ }
+ SymbolRef::Type Type = *TypeOrErr;
+ if (Type == object::SymbolRef::ST_Function) {
+ Expected<StringRef> Name = Sym.getName();
+ if (!Name) {
+ // TODO: Actually report errors helpfully.
+ consumeError(Name.takeError());
continue;
- ErrorOr<uint64_t> AddrOrErr = Sym.getAddress();
- if (!AddrOrErr)
+ }
+ Expected<uint64_t> AddrOrErr = Sym.getAddress();
+ if (!AddrOrErr) {
+ // TODO: Actually report errors helpfully.
+ consumeError(AddrOrErr.takeError());
continue;
+ }
uint64_t Addr = *AddrOrErr;
uint64_t Size = P.second;
@@ -344,7 +362,13 @@ 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 = *Sym.getSection();
+ auto SecOrErr = Sym.getSection();
+ if (!SecOrErr) {
+ // TODO: Actually report errors helpfully.
+ consumeError(SecOrErr.takeError());
+ continue;
+ }
+ object::section_iterator Sec = *SecOrErr;
StringRef SecName;
Sec->getName(SecName);
uint64_t SectionLoadAddress =
@@ -396,19 +420,24 @@ static int executeInput() {
ErrorOr<std::unique_ptr<MemoryBuffer>> InputBuffer =
MemoryBuffer::getFileOrSTDIN(File);
if (std::error_code EC = InputBuffer.getError())
- return Error("unable to read input: '" + EC.message() + "'");
- ErrorOr<std::unique_ptr<ObjectFile>> MaybeObj(
+ ErrorAndExit("unable to read input: '" + EC.message() + "'");
+ Expected<std::unique_ptr<ObjectFile>> MaybeObj(
ObjectFile::createObjectFile((*InputBuffer)->getMemBufferRef()));
- if (std::error_code EC = MaybeObj.getError())
- return Error("unable to create object file: '" + EC.message() + "'");
+ if (!MaybeObj) {
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(MaybeObj.takeError(), OS, "");
+ OS.flush();
+ ErrorAndExit("unable to create object file: '" + Buf + "'");
+ }
ObjectFile &Obj = **MaybeObj;
// Load the object file
Dyld.loadObject(Obj);
if (Dyld.hasError()) {
- return Error(Dyld.getErrorString());
+ ErrorAndExit(Dyld.getErrorString());
}
}
@@ -419,7 +448,7 @@ static int executeInput() {
// Get the address of the entry point (_main by default).
void *MainAddress = Dyld.getSymbolLocalAddress(EntryPoint);
if (!MainAddress)
- return Error("no definition for '" + EntryPoint + "'");
+ ErrorAndExit("no definition for '" + EntryPoint + "'");
// Invalidate the instruction cache for each loaded function.
for (auto &FM : MemMgr.FunctionMemory) {
@@ -428,7 +457,7 @@ static int executeInput() {
// setExecutable will call InvalidateInstructionCache.
std::string ErrorStr;
if (!sys::Memory::setExecutable(FM, &ErrorStr))
- return Error("unable to mark function executable: '" + ErrorStr + "'");
+ ErrorAndExit("unable to mark function executable: '" + ErrorStr + "'");
}
// Dispatch to _main().
@@ -448,12 +477,12 @@ static int checkAllExpressions(RuntimeDyldChecker &Checker) {
ErrorOr<std::unique_ptr<MemoryBuffer>> CheckerFileBuf =
MemoryBuffer::getFileOrSTDIN(CheckerFileName);
if (std::error_code EC = CheckerFileBuf.getError())
- return Error("unable to read input '" + CheckerFileName + "': " +
+ ErrorAndExit("unable to read input '" + CheckerFileName + "': " +
EC.message());
if (!Checker.checkAllRulesInBuffer("# rtdyld-check:",
CheckerFileBuf.get().get()))
- return Error("some checks in '" + CheckerFileName + "' failed");
+ ErrorAndExit("some checks in '" + CheckerFileName + "' failed");
}
return 0;
}
@@ -602,7 +631,7 @@ static int linkAndVerify() {
// Check for missing triple.
if (TripleName == "")
- return Error("-triple required when running in -verify mode.");
+ ErrorAndExit("-triple required when running in -verify mode.");
// Look up the target and build the disassembler.
Triple TheTriple(Triple::normalize(TripleName));
@@ -610,29 +639,29 @@ static int linkAndVerify() {
const Target *TheTarget =
TargetRegistry::lookupTarget("", TheTriple, ErrorStr);
if (!TheTarget)
- return Error("Error accessing target '" + TripleName + "': " + ErrorStr);
+ ErrorAndExit("Error accessing target '" + TripleName + "': " + ErrorStr);
TripleName = TheTriple.getTriple();
std::unique_ptr<MCSubtargetInfo> STI(
TheTarget->createMCSubtargetInfo(TripleName, MCPU, ""));
if (!STI)
- return Error("Unable to create subtarget info!");
+ ErrorAndExit("Unable to create subtarget info!");
std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TripleName));
if (!MRI)
- return Error("Unable to create target register info!");
+ ErrorAndExit("Unable to create target register info!");
std::unique_ptr<MCAsmInfo> MAI(TheTarget->createMCAsmInfo(*MRI, TripleName));
if (!MAI)
- return Error("Unable to create target asm info!");
+ ErrorAndExit("Unable to create target asm info!");
MCContext Ctx(MAI.get(), MRI.get(), nullptr);
std::unique_ptr<MCDisassembler> Disassembler(
TheTarget->createMCDisassembler(*STI, Ctx));
if (!Disassembler)
- return Error("Unable to create disassembler!");
+ ErrorAndExit("Unable to create disassembler!");
std::unique_ptr<MCInstrInfo> MII(TheTarget->createMCInstrInfo());
@@ -659,20 +688,25 @@ static int linkAndVerify() {
MemoryBuffer::getFileOrSTDIN(Filename);
if (std::error_code EC = InputBuffer.getError())
- return Error("unable to read input: '" + EC.message() + "'");
+ ErrorAndExit("unable to read input: '" + EC.message() + "'");
- ErrorOr<std::unique_ptr<ObjectFile>> MaybeObj(
+ Expected<std::unique_ptr<ObjectFile>> MaybeObj(
ObjectFile::createObjectFile((*InputBuffer)->getMemBufferRef()));
- if (std::error_code EC = MaybeObj.getError())
- return Error("unable to create object file: '" + EC.message() + "'");
+ if (!MaybeObj) {
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(MaybeObj.takeError(), OS, "");
+ OS.flush();
+ ErrorAndExit("unable to create object file: '" + Buf + "'");
+ }
ObjectFile &Obj = **MaybeObj;
// Load the object file
Dyld.loadObject(Obj);
if (Dyld.hasError()) {
- return Error(Dyld.getErrorString());
+ ErrorAndExit(Dyld.getErrorString());
}
}
@@ -688,14 +722,14 @@ static int linkAndVerify() {
int ErrorCode = checkAllExpressions(Checker);
if (Dyld.hasError())
- return Error("RTDyld reported an error applying relocations:\n " +
+ ErrorAndExit("RTDyld reported an error applying relocations:\n " +
Dyld.getErrorString());
return ErrorCode;
}
int main(int argc, char **argv) {
- sys::PrintStackTraceOnErrorSignal();
+ sys::PrintStackTraceOnErrorSignal(argv[0]);
PrettyStackTraceProgram X(argc, argv);
ProgramName = argv[0];
diff --git a/tools/llvm-shlib/CMakeLists.txt b/tools/llvm-shlib/CMakeLists.txt
index 2356103a9cd51..3fe672d679a3d 100644
--- a/tools/llvm-shlib/CMakeLists.txt
+++ b/tools/llvm-shlib/CMakeLists.txt
@@ -2,8 +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.
-add_definitions( -DLLVM_VERSION_INFO=\"${PACKAGE_VERSION}\" )
-
set(SOURCES
libllvm.cpp
)
diff --git a/tools/llvm-shlib/Makefile b/tools/llvm-shlib/Makefile
deleted file mode 100644
index 19077a3858a6a..0000000000000
--- a/tools/llvm-shlib/Makefile
+++ /dev/null
@@ -1,116 +0,0 @@
-##===- tools/shlib/Makefile --------------------------------*- Makefile -*-===##
-#
-# The LLVM Compiler Infrastructure
-#
-# This file is distributed under the University of Illinois Open Source
-# License. See LICENSE.TXT for details.
-#
-##===----------------------------------------------------------------------===##
-
-LEVEL := ../..
-
-LIBRARYNAME = LLVM-$(LLVM_VERSION_MAJOR).$(LLVM_VERSION_MINOR)$(LLVM_VERSION_SUFFIX)
-LIBRARYALIASNAME = LLVM-$(LLVMVersion)
-
-NO_BUILD_ARCHIVE := 1
-LINK_LIBS_IN_SHARED := 1
-SHARED_LIBRARY := 1
-SHARED_ALIAS := 1
-
-include $(LEVEL)/Makefile.config
-
-ifeq ($(HOST_OS), $(filter $(HOST_OS), Cygwin MingW))
- EXPORTED_SYMBOL_FILE = $(ObjDir)/$(LIBRARYNAME).exports
-
- ifeq (1,$(ENABLE_EMBED_STDCXX))
- # It is needed to force static-stdc++.a linked.
- SHLIB_FRAG_NAMES += stdc++.a.o
- endif
-
-endif
-
-include $(LEVEL)/Makefile.common
-
-# Include all archives in libLLVM.(so|dylib) except the ones that have
-# their own dynamic libraries and TableGen.
-Archives := $(wildcard $(LibDir)/libLLVM*.a)
-SharedLibraries := $(wildcard $(LibDir)/libLLVM*$(SHLIBEXT))
-ExcludeFromLibLlvm := $(basename $(SharedLibraries)).a %/libLLVMTableGen.a
-IncludeInLibLlvm := $(filter-out $(ExcludeFromLibLlvm), $(Archives))
-LLVMLibsOptions := $(IncludeInLibLlvm:$(LibDir)/lib%.a=-l%)
-LLVMLibsPaths := $(IncludeInLibLlvm)
-
-$(LibName.SO): $(LLVMLibsPaths)
-
-ifeq ($(HOST_OS),Darwin)
- # set dylib internal version number to llvmCore submission number
- ifdef LLVM_SUBMIT_VERSION
- LLVMLibsOptions := $(LLVMLibsOptions) -Wl,-current_version \
- -Wl,$(LLVM_SUBMIT_VERSION).$(LLVM_SUBMIT_SUBVERSION) \
- -Wl,-compatibility_version -Wl,1
- endif
- # Include everything from the .a's into the shared library.
- LLVMLibsOptions := $(LLVMLibsOptions) -all_load
-endif
-
-ifeq ($(HOST_OS), $(filter $(HOST_OS), DragonFly Linux FreeBSD GNU/kFreeBSD OpenBSD GNU Bitrig))
- # Include everything from the .a's into the shared library.
- LLVMLibsOptions := -Wl,--whole-archive $(LLVMLibsOptions) \
- -Wl,--no-whole-archive
-endif
-
-ifeq ($(HOST_OS), $(filter $(HOST_OS), DragonFly Linux FreeBSD GNU/kFreeBSD GNU))
- # Add soname to the library.
- LLVMLibsOptions += -Wl,--soname,lib$(LIBRARYNAME)$(SHLIBEXT)
-endif
-
-ifeq ($(HOST_OS), $(filter $(HOST_OS), Linux GNU GNU/kFreeBSD))
- # Don't allow unresolved symbols.
- LLVMLibsOptions += -Wl,--no-undefined
-endif
-
-ifeq ($(HOST_OS),SunOS)
- # add -z allextract ahead of other libraries on Solaris
- LLVMLibsOptions := -Wl,-z -Wl,allextract $(LLVMLibsOptions)
-endif
-
-ifeq ($(HOST_OS), $(filter $(HOST_OS), Cygwin MingW))
-
-SHLIB_STUBS := $(addprefix $(ObjDir)/, $(SHLIB_FRAG_NAMES))
-SHLIB_FRAGS := $(patsubst %.a.o, $(ObjDir)/%.syms.txt, $(LIBRARYNAME).a.o $(SHLIB_FRAG_NAMES))
-LLVMLibsOptions := $(SHLIB_STUBS) $(LLVMLibsOptions)
-
-$(LibName.SO): $(SHLIB_STUBS)
-
-%.syms.txt: %.a.o
- $(Echo) Collecting global symbols of $(notdir $*)
- $(Verb) $(NM_PATH) -g $< > $@
-
-$(ObjDir)/$(LIBRARYNAME).exports: $(SHLIB_FRAGS) $(ObjDir)/.dir
- $(Echo) Generating exports for $(LIBRARYNAME)
- $(Verb) ($(SED) -n \
- -e "s/^.* T _\([^.][^.]*\)$$/\1/p" \
- -e "s/^.* [BDR] _\([^.][^.]*\)$$/\1 DATA/p" \
- $(SHLIB_FRAGS) \
- | sort -u) > $@
-
-$(ObjDir)/$(LIBRARYNAME).a.o: $(LLVMLibsPaths) $(ObjDir)/.dir
- $(Echo) Linking all LLVMLibs together for $(LIBRARYNAME)
- $(Verb) $(Link) -nostartfiles -Wl,-r -nodefaultlibs -o $@ \
- -Wl,--whole-archive $(LLVMLibsPaths) \
- -Wl,--no-whole-archive
-
-$(ObjDir)/stdc++.a.o: $(ObjDir)/.dir
- $(Echo) Linking all libs together for static libstdc++.a
- $(Verb) $(Link) -nostartfiles -Wl,-r -nodefaultlibs -o $@ \
- -Wl,--whole-archive -lstdc++ \
- -Wl,--no-whole-archive
-# FIXME: workaround to invalidate -lstdc++
- $(Echo) Making dummy -lstdc++ to lib
- $(Verb) $(AR) rc $(ToolDir)/libstdc++.dll.a
-# FIXME: Is install-local needed?
-
-clean-local::
- $(Verb) $(RM) -f $(ToolDir)/libstdc++.dll.a
-
-endif
diff --git a/tools/llvm-shlib/libllvm.cpp b/tools/llvm-shlib/libllvm.cpp
index 8424d660c9d0e..40b4f66b07331 100644
--- a/tools/llvm-shlib/libllvm.cpp
+++ b/tools/llvm-shlib/libllvm.cpp
@@ -11,10 +11,3 @@
// you can't define a target with no sources.
//
//===----------------------------------------------------------------------===//
-
-#include "llvm/Config/config.h"
-
-#if defined(DISABLE_LLVM_DYLIB_ATEXIT)
-extern "C" int __cxa_atexit();
-extern "C" int __cxa_atexit() { return 0; }
-#endif
diff --git a/tools/llvm-size/Makefile b/tools/llvm-size/Makefile
deleted file mode 100644
index 0622eb108978f..0000000000000
--- a/tools/llvm-size/Makefile
+++ /dev/null
@@ -1,17 +0,0 @@
-##===- tools/llvm-size/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-size
-LINK_COMPONENTS := object
-
-# This tool has no plugins, optimize startup time.
-TOOL_NO_EXPORTS = 1
-
-include $(LEVEL)/Makefile.common
diff --git a/tools/llvm-size/llvm-size.cpp b/tools/llvm-size/llvm-size.cpp
index 069cc621f6157..c5966ead4b6c5 100644
--- a/tools/llvm-size/llvm-size.cpp
+++ b/tools/llvm-size/llvm-size.cpp
@@ -15,6 +15,7 @@
#include "llvm/ADT/APInt.h"
#include "llvm/Object/Archive.h"
+#include "llvm/Object/ELFObjectFile.h"
#include "llvm/Object/MachO.h"
#include "llvm/Object/MachOUniversal.h"
#include "llvm/Object/ObjectFile.h"
@@ -49,13 +50,19 @@ static cl::opt<OutputFormatTy> OutputFormatShort(
clEnumValN(darwin, "m", "Darwin -m format"), clEnumValEnd),
cl::init(berkeley));
-static bool berkeleyHeaderPrinted = false;
-static bool moreThanOneFile = false;
+static bool BerkeleyHeaderPrinted = false;
+static bool MoreThanOneFile = false;
cl::opt<bool>
DarwinLongFormat("l", cl::desc("When format is darwin, use long format "
"to include addresses and offsets."));
+cl::opt<bool>
+ ELFCommons("common",
+ cl::desc("Print common symbols in the ELF file. When using "
+ "Berkely format, this is added to bss."),
+ cl::init(false));
+
static cl::list<std::string>
ArchFlags("arch", cl::desc("architecture(s) from a Mach-O file to dump"),
cl::ZeroOrMore);
@@ -77,20 +84,75 @@ RadixShort(cl::desc("Print size in radix:"),
static cl::list<std::string>
InputFilenames(cl::Positional, cl::desc("<input files>"), cl::ZeroOrMore);
+bool HadError = false;
+
static std::string ToolName;
-/// @brief If ec is not success, print the error and return true.
+/// If ec is not success, print the error and return true.
static bool error(std::error_code ec) {
if (!ec)
return false;
- outs() << ToolName << ": error reading file: " << ec.message() << ".\n";
- outs().flush();
+ HadError = true;
+ errs() << ToolName << ": error reading file: " << ec.message() << ".\n";
+ errs().flush();
+ return true;
+}
+
+static bool error(Twine Message) {
+ HadError = true;
+ errs() << ToolName << ": " << Message << ".\n";
+ errs().flush();
return true;
}
-/// @brief Get the length of the string that represents @p num in Radix
-/// including the leading 0x or 0 for hexadecimal and octal respectively.
+// This version of error() prints the archive name and member name, for example:
+// "libx.a(foo.o)" after the ToolName before the error message. It sets
+// HadError but returns allowing the code to move on to other archive members.
+static void error(llvm::Error E, StringRef FileName, const Archive::Child &C,
+ StringRef ArchitectureName = StringRef()) {
+ HadError = true;
+ errs() << ToolName << ": " << FileName;
+
+ ErrorOr<StringRef> NameOrErr = C.getName();
+ // TODO: if we have a error getting the name then it would be nice to print
+ // the index of which archive member this is and or its offset in the
+ // archive instead of "???" as the name.
+ if (NameOrErr.getError())
+ errs() << "(" << "???" << ")";
+ else
+ errs() << "(" << NameOrErr.get() << ")";
+
+ if (!ArchitectureName.empty())
+ errs() << " (for architecture " << ArchitectureName << ") ";
+
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(std::move(E), OS, "");
+ OS.flush();
+ errs() << " " << Buf << "\n";
+}
+
+// This version of error() prints the file name and which architecture slice it // is from, for example: "foo.o (for architecture i386)" after the ToolName
+// before the error message. It sets HadError but returns allowing the code to
+// move on to other architecture slices.
+static void error(llvm::Error E, StringRef FileName,
+ StringRef ArchitectureName = StringRef()) {
+ HadError = true;
+ errs() << ToolName << ": " << FileName;
+
+ if (!ArchitectureName.empty())
+ errs() << " (for architecture " << ArchitectureName << ") ";
+
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(std::move(E), OS, "");
+ OS.flush();
+ errs() << " " << Buf << "\n";
+}
+
+/// Get the length of the string that represents @p num in Radix including the
+/// leading 0x or 0 for hexadecimal and octal respectively.
static size_t getNumLengthAsString(uint64_t num) {
APInt conv(64, num);
SmallString<32> result;
@@ -98,7 +160,7 @@ static size_t getNumLengthAsString(uint64_t num) {
return result.size();
}
-/// @brief Return the printing format for the Radix.
+/// Return the printing format for the Radix.
static const char *getRadixFmt() {
switch (Radix) {
case octal:
@@ -111,11 +173,35 @@ static const char *getRadixFmt() {
return nullptr;
}
-/// @brief Print the size of each Mach-O segment and section in @p MachO.
+/// Remove unneeded ELF sections from calculation
+static bool considerForSize(ObjectFile *Obj, SectionRef Section) {
+ if (!Obj->isELF())
+ return true;
+ switch (static_cast<ELFSectionRef>(Section).getType()) {
+ case ELF::SHT_NULL:
+ case ELF::SHT_SYMTAB:
+ case ELF::SHT_STRTAB:
+ case ELF::SHT_REL:
+ case ELF::SHT_RELA:
+ return false;
+ }
+ return true;
+}
+
+/// Total size of all ELF common symbols
+static uint64_t getCommonSize(ObjectFile *Obj) {
+ uint64_t TotalCommons = 0;
+ for (auto &Sym : Obj->symbols())
+ if (Obj->getSymbolFlags(Sym.getRawDataRefImpl()) & SymbolRef::SF_Common)
+ TotalCommons += Obj->getCommonSymbolSize(Sym.getRawDataRefImpl());
+ return TotalCommons;
+}
+
+/// Print the size of each Mach-O segment and section in @p MachO.
///
/// This is when used when @c OutputFormat is darwin and produces the same
/// output as darwin's size(1) -m output.
-static void PrintDarwinSectionSizes(MachOObjectFile *MachO) {
+static void printDarwinSectionSizes(MachOObjectFile *MachO) {
std::string fmtbuf;
raw_string_ostream fmt(fmtbuf);
const char *radix_fmt = getRadixFmt();
@@ -155,10 +241,11 @@ static void PrintDarwinSectionSizes(MachOObjectFile *MachO) {
outs() << "\ttotal " << format(fmt.str().c_str(), sec_total) << "\n";
} else if (Load.C.cmd == MachO::LC_SEGMENT) {
MachO::segment_command Seg = MachO->getSegmentLoadCommand(Load);
+ uint64_t Seg_vmsize = Seg.vmsize;
outs() << "Segment " << Seg.segname << ": "
- << format(fmt.str().c_str(), Seg.vmsize);
+ << format(fmt.str().c_str(), Seg_vmsize);
if (DarwinLongFormat)
- outs() << " (vmaddr 0x" << format("%" PRIx64, Seg.vmaddr) << " fileoff "
+ outs() << " (vmaddr 0x" << format("%" PRIx32, Seg.vmaddr) << " fileoff "
<< Seg.fileoff << ")";
outs() << "\n";
total += Seg.vmsize;
@@ -170,9 +257,10 @@ static void PrintDarwinSectionSizes(MachOObjectFile *MachO) {
<< format("%.16s", &Sec.sectname) << "): ";
else
outs() << "\tSection " << format("%.16s", &Sec.sectname) << ": ";
- outs() << format(fmt.str().c_str(), Sec.size);
+ uint64_t Sec_size = Sec.size;
+ outs() << format(fmt.str().c_str(), Sec_size);
if (DarwinLongFormat)
- outs() << " (addr 0x" << format("%" PRIx64, Sec.addr) << " offset "
+ outs() << " (addr 0x" << format("%" PRIx32, Sec.addr) << " offset "
<< Sec.offset << ")";
outs() << "\n";
sec_total += Sec.size;
@@ -184,11 +272,11 @@ static void PrintDarwinSectionSizes(MachOObjectFile *MachO) {
outs() << "total " << format(fmt.str().c_str(), total) << "\n";
}
-/// @brief Print the summary sizes of the standard Mach-O segments in @p MachO.
+/// Print the summary sizes of the standard Mach-O segments in @p MachO.
///
/// This is when used when @c OutputFormat is berkeley with a Mach-O file and
/// produces the same output as darwin's size(1) default output.
-static void PrintDarwinSegmentSizes(MachOObjectFile *MachO) {
+static void printDarwinSegmentSizes(MachOObjectFile *MachO) {
uint64_t total_text = 0;
uint64_t total_data = 0;
uint64_t total_objc = 0;
@@ -250,19 +338,19 @@ static void PrintDarwinSegmentSizes(MachOObjectFile *MachO) {
}
uint64_t total = total_text + total_data + total_objc + total_others;
- if (!berkeleyHeaderPrinted) {
+ if (!BerkeleyHeaderPrinted) {
outs() << "__TEXT\t__DATA\t__OBJC\tothers\tdec\thex\n";
- berkeleyHeaderPrinted = true;
+ BerkeleyHeaderPrinted = true;
}
outs() << total_text << "\t" << total_data << "\t" << total_objc << "\t"
<< total_others << "\t" << total << "\t" << format("%" PRIx64, total)
<< "\t";
}
-/// @brief Print the size of each section in @p Obj.
+/// Print the size of each section in @p Obj.
///
/// The format used is determined by @c OutputFormat and @c Radix.
-static void PrintObjectSectionSizes(ObjectFile *Obj) {
+static void printObjectSectionSizes(ObjectFile *Obj) {
uint64_t total = 0;
std::string fmtbuf;
raw_string_ostream fmt(fmtbuf);
@@ -273,11 +361,11 @@ static void PrintObjectSectionSizes(ObjectFile *Obj) {
// let it fall through to OutputFormat berkeley.
MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(Obj);
if (OutputFormat == darwin && MachO)
- PrintDarwinSectionSizes(MachO);
+ printDarwinSectionSizes(MachO);
// If we have a MachOObjectFile and the OutputFormat is berkeley print as
// darwin's default berkeley format for Mach-O files.
else if (MachO && OutputFormat == berkeley)
- PrintDarwinSegmentSizes(MachO);
+ printDarwinSegmentSizes(MachO);
else if (OutputFormat == sysv) {
// Run two passes over all sections. The first gets the lengths needed for
// formatting the output. The second actually does the output.
@@ -285,6 +373,8 @@ static void PrintObjectSectionSizes(ObjectFile *Obj) {
std::size_t max_size_len = strlen("size");
std::size_t max_addr_len = strlen("addr");
for (const SectionRef &Section : Obj->sections()) {
+ if (!considerForSize(Obj, Section))
+ continue;
uint64_t size = Section.getSize();
total += size;
@@ -320,6 +410,8 @@ static void PrintObjectSectionSizes(ObjectFile *Obj) {
// Print each section.
for (const SectionRef &Section : Obj->sections()) {
+ if (!considerForSize(Obj, Section))
+ continue;
StringRef name;
if (error(Section.getName(name)))
return;
@@ -330,6 +422,13 @@ static void PrintObjectSectionSizes(ObjectFile *Obj) {
outs() << format(fmt.str().c_str(), namestr.c_str(), size, addr);
}
+ if (ELFCommons) {
+ uint64_t CommonSize = getCommonSize(Obj);
+ total += CommonSize;
+ outs() << format(fmt.str().c_str(), std::string("*COM*").c_str(),
+ CommonSize, static_cast<uint64_t>(0));
+ }
+
// Print total.
fmtbuf.clear();
fmt << "%-" << max_name_len << "s "
@@ -357,12 +456,15 @@ static void PrintObjectSectionSizes(ObjectFile *Obj) {
total_bss += size;
}
+ if (ELFCommons)
+ total_bss += getCommonSize(Obj);
+
total = total_text + total_data + total_bss;
- if (!berkeleyHeaderPrinted) {
+ if (!BerkeleyHeaderPrinted) {
outs() << " text data bss "
<< (Radix == octal ? "oct" : "dec") << " hex filename\n";
- berkeleyHeaderPrinted = true;
+ BerkeleyHeaderPrinted = true;
}
// Print result.
@@ -377,11 +479,11 @@ static void PrintObjectSectionSizes(ObjectFile *Obj) {
}
}
-/// @brief Checks to see if the @p o ObjectFile is a Mach-O file and if it is
-/// and there is a list of architecture flags specified then check to
-/// make sure this Mach-O file is one of those architectures or all
-/// architectures was specificed. If not then an error is generated and
-/// this routine returns false. Else it returns true.
+/// Checks to see if the @p o ObjectFile is a Mach-O file and if it is and there
+/// is a list of architecture flags specified then check to make sure this
+/// Mach-O file is one of those architectures or all architectures was
+/// specificed. If not then an error is generated and this routine returns
+/// false. Else it returns true.
static bool checkMachOAndArchFlags(ObjectFile *o, StringRef file) {
if (isa<MachOObjectFile>(o) && !ArchAll && ArchFlags.size() != 0) {
MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
@@ -391,10 +493,10 @@ static bool checkMachOAndArchFlags(ObjectFile *o, StringRef file) {
Triple T;
if (MachO->is64Bit()) {
H_64 = MachO->MachOObjectFile::getHeader64();
- T = MachOObjectFile::getArch(H_64.cputype, H_64.cpusubtype);
+ T = MachOObjectFile::getArchTriple(H_64.cputype, H_64.cpusubtype);
} else {
H = MachO->MachOObjectFile::getHeader();
- T = MachOObjectFile::getArch(H.cputype, H.cpusubtype);
+ T = MachOObjectFile::getArchTriple(H.cputype, H.cpusubtype);
}
unsigned i;
for (i = 0; i < ArchFlags.size(); ++i) {
@@ -411,32 +513,26 @@ static bool checkMachOAndArchFlags(ObjectFile *o, StringRef file) {
return true;
}
-/// @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) {
+/// 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) {
// Attempt to open the binary.
- ErrorOr<OwningBinary<Binary>> BinaryOrErr = createBinary(file);
- if (std::error_code EC = BinaryOrErr.getError()) {
- errs() << ToolName << ": " << file << ": " << EC.message() << ".\n";
+ Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(file);
+ if (!BinaryOrErr) {
+ error(errorToErrorCode(BinaryOrErr.takeError()));
return;
}
Binary &Bin = *BinaryOrErr.get().getBinary();
if (Archive *a = dyn_cast<Archive>(&Bin)) {
// This is an archive. Iterate over each member and display its sizes.
- for (object::Archive::child_iterator i = a->child_begin(),
- e = a->child_end();
- i != e; ++i) {
- 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";
+ Error Err;
+ for (auto &C : a->children(Err)) {
+ Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
+ if (!ChildOrErr) {
+ if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError()))
+ error(std::move(E), a->getFileName(), C);
continue;
}
if (ObjectFile *o = dyn_cast<ObjectFile>(&*ChildOrErr.get())) {
@@ -447,7 +543,7 @@ static void PrintFileSectionSizes(StringRef file) {
outs() << o->getFileName() << " (ex " << a->getFileName() << "):\n";
else if (MachO && OutputFormat == darwin)
outs() << a->getFileName() << "(" << o->getFileName() << "):\n";
- PrintObjectSectionSizes(o);
+ printObjectSectionSizes(o);
if (OutputFormat == berkeley) {
if (MachO)
outs() << a->getFileName() << "(" << o->getFileName() << ")\n";
@@ -456,6 +552,8 @@ static void PrintFileSectionSizes(StringRef file) {
}
}
}
+ if (Err)
+ error(std::move(Err), a->getFileName());
} else if (MachOUniversalBinary *UB =
dyn_cast<MachOUniversalBinary>(&Bin)) {
// If we have a list of architecture flags specified dump only those.
@@ -469,43 +567,44 @@ static void PrintFileSectionSizes(StringRef file) {
I != E; ++I) {
if (ArchFlags[i] == I->getArchTypeName()) {
ArchFound = true;
- ErrorOr<std::unique_ptr<ObjectFile>> UO = I->getAsObjectFile();
+ Expected<std::unique_ptr<ObjectFile>> UO = I->getAsObjectFile();
if (UO) {
if (ObjectFile *o = dyn_cast<ObjectFile>(&*UO.get())) {
MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
if (OutputFormat == sysv)
outs() << o->getFileName() << " :\n";
else if (MachO && OutputFormat == darwin) {
- if (moreThanOneFile || ArchFlags.size() > 1)
+ if (MoreThanOneFile || ArchFlags.size() > 1)
outs() << o->getFileName() << " (for architecture "
<< I->getArchTypeName() << "): \n";
}
- PrintObjectSectionSizes(o);
+ printObjectSectionSizes(o);
if (OutputFormat == berkeley) {
- if (!MachO || moreThanOneFile || ArchFlags.size() > 1)
+ if (!MachO || MoreThanOneFile || ArchFlags.size() > 1)
outs() << o->getFileName() << " (for architecture "
<< I->getArchTypeName() << ")";
outs() << "\n";
}
}
- } else if (ErrorOr<std::unique_ptr<Archive>> AOrErr =
+ } else if (auto E = isNotObjectErrorInvalidFileType(
+ UO.takeError())) {
+ error(std::move(E), file, ArchFlags.size() > 1 ?
+ StringRef(I->getArchTypeName()) : StringRef());
+ return;
+ } else if (Expected<std::unique_ptr<Archive>> AOrErr =
I->getAsArchive()) {
std::unique_ptr<Archive> &UA = *AOrErr;
// This is an archive. Iterate over each member and display its
// sizes.
- for (object::Archive::child_iterator i = UA->child_begin(),
- e = UA->child_end();
- i != e; ++i) {
- 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";
+ Error Err;
+ for (auto &C : UA->children(Err)) {
+ Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
+ if (!ChildOrErr) {
+ if (auto E = isNotObjectErrorInvalidFileType(
+ ChildOrErr.takeError()))
+ error(std::move(E), UA->getFileName(), C,
+ ArchFlags.size() > 1 ?
+ StringRef(I->getArchTypeName()) : StringRef());
continue;
}
if (ObjectFile *o = dyn_cast<ObjectFile>(&*ChildOrErr.get())) {
@@ -518,7 +617,7 @@ static void PrintFileSectionSizes(StringRef file) {
<< ")"
<< " (for architecture " << I->getArchTypeName()
<< "):\n";
- PrintObjectSectionSizes(o);
+ printObjectSectionSizes(o);
if (OutputFormat == berkeley) {
if (MachO) {
outs() << UA->getFileName() << "(" << o->getFileName()
@@ -533,6 +632,13 @@ static void PrintFileSectionSizes(StringRef file) {
}
}
}
+ if (Err)
+ error(std::move(Err), UA->getFileName());
+ } else {
+ consumeError(AOrErr.takeError());
+ error("Mach-O universal file: " + file + " for architecture " +
+ StringRef(I->getArchTypeName()) +
+ " is not a Mach-O file or an archive file");
}
}
}
@@ -552,43 +658,40 @@ static void PrintFileSectionSizes(StringRef file) {
E = UB->end_objects();
I != E; ++I) {
if (HostArchName == I->getArchTypeName()) {
- ErrorOr<std::unique_ptr<ObjectFile>> UO = I->getAsObjectFile();
+ Expected<std::unique_ptr<ObjectFile>> UO = I->getAsObjectFile();
if (UO) {
if (ObjectFile *o = dyn_cast<ObjectFile>(&*UO.get())) {
MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
if (OutputFormat == sysv)
outs() << o->getFileName() << " :\n";
else if (MachO && OutputFormat == darwin) {
- if (moreThanOneFile)
+ if (MoreThanOneFile)
outs() << o->getFileName() << " (for architecture "
<< I->getArchTypeName() << "):\n";
}
- PrintObjectSectionSizes(o);
+ printObjectSectionSizes(o);
if (OutputFormat == berkeley) {
- if (!MachO || moreThanOneFile)
+ if (!MachO || MoreThanOneFile)
outs() << o->getFileName() << " (for architecture "
<< I->getArchTypeName() << ")";
outs() << "\n";
}
}
- } else if (ErrorOr<std::unique_ptr<Archive>> AOrErr =
+ } else if (auto E = isNotObjectErrorInvalidFileType(UO.takeError())) {
+ error(std::move(E), file);
+ return;
+ } else if (Expected<std::unique_ptr<Archive>> AOrErr =
I->getAsArchive()) {
std::unique_ptr<Archive> &UA = *AOrErr;
// This is an archive. Iterate over each member and display its
// sizes.
- for (object::Archive::child_iterator i = UA->child_begin(),
- e = UA->child_end();
- i != e; ++i) {
- 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";
+ Error Err;
+ for (auto &C : UA->children(Err)) {
+ Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
+ if (!ChildOrErr) {
+ if (auto E = isNotObjectErrorInvalidFileType(
+ ChildOrErr.takeError()))
+ error(std::move(E), UA->getFileName(), C);
continue;
}
if (ObjectFile *o = dyn_cast<ObjectFile>(&*ChildOrErr.get())) {
@@ -600,7 +703,7 @@ static void PrintFileSectionSizes(StringRef file) {
outs() << UA->getFileName() << "(" << o->getFileName() << ")"
<< " (for architecture " << I->getArchTypeName()
<< "):\n";
- PrintObjectSectionSizes(o);
+ printObjectSectionSizes(o);
if (OutputFormat == berkeley) {
if (MachO)
outs() << UA->getFileName() << "(" << o->getFileName()
@@ -611,6 +714,13 @@ static void PrintFileSectionSizes(StringRef file) {
}
}
}
+ if (Err)
+ error(std::move(Err), UA->getFileName());
+ } else {
+ consumeError(AOrErr.takeError());
+ error("Mach-O universal file: " + file + " for architecture " +
+ StringRef(I->getArchTypeName()) +
+ " is not a Mach-O file or an archive file");
}
return;
}
@@ -618,45 +728,46 @@ static void PrintFileSectionSizes(StringRef file) {
}
// Either all architectures have been specified or none have been specified
// and this does not contain the host architecture so dump all the slices.
- bool moreThanOneArch = UB->getNumberOfObjects() > 1;
+ bool MoreThanOneArch = UB->getNumberOfObjects() > 1;
for (MachOUniversalBinary::object_iterator I = UB->begin_objects(),
E = UB->end_objects();
I != E; ++I) {
- ErrorOr<std::unique_ptr<ObjectFile>> UO = I->getAsObjectFile();
+ Expected<std::unique_ptr<ObjectFile>> UO = I->getAsObjectFile();
if (UO) {
if (ObjectFile *o = dyn_cast<ObjectFile>(&*UO.get())) {
MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
if (OutputFormat == sysv)
outs() << o->getFileName() << " :\n";
else if (MachO && OutputFormat == darwin) {
- if (moreThanOneFile || moreThanOneArch)
+ if (MoreThanOneFile || MoreThanOneArch)
outs() << o->getFileName() << " (for architecture "
<< I->getArchTypeName() << "):";
outs() << "\n";
}
- PrintObjectSectionSizes(o);
+ printObjectSectionSizes(o);
if (OutputFormat == berkeley) {
- if (!MachO || moreThanOneFile || moreThanOneArch)
+ if (!MachO || MoreThanOneFile || MoreThanOneArch)
outs() << o->getFileName() << " (for architecture "
<< I->getArchTypeName() << ")";
outs() << "\n";
}
}
- } else if (ErrorOr<std::unique_ptr<Archive>> AOrErr =
+ } else if (auto E = isNotObjectErrorInvalidFileType(UO.takeError())) {
+ error(std::move(E), file, MoreThanOneArch ?
+ StringRef(I->getArchTypeName()) : StringRef());
+ return;
+ } else if (Expected<std::unique_ptr<Archive>> AOrErr =
I->getAsArchive()) {
std::unique_ptr<Archive> &UA = *AOrErr;
// This is an archive. Iterate over each member and display its sizes.
- for (object::Archive::child_iterator i = UA->child_begin(),
- e = UA->child_end();
- i != e; ++i) {
- 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";
+ Error Err;
+ for (auto &C : UA->children(Err)) {
+ Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
+ if (!ChildOrErr) {
+ if (auto E = isNotObjectErrorInvalidFileType(
+ ChildOrErr.takeError()))
+ error(std::move(E), UA->getFileName(), C, MoreThanOneArch ?
+ StringRef(I->getArchTypeName()) : StringRef());
continue;
}
if (ObjectFile *o = dyn_cast<ObjectFile>(&*ChildOrErr.get())) {
@@ -667,7 +778,7 @@ static void PrintFileSectionSizes(StringRef file) {
else if (MachO && OutputFormat == darwin)
outs() << UA->getFileName() << "(" << o->getFileName() << ")"
<< " (for architecture " << I->getArchTypeName() << "):\n";
- PrintObjectSectionSizes(o);
+ printObjectSectionSizes(o);
if (OutputFormat == berkeley) {
if (MachO)
outs() << UA->getFileName() << "(" << o->getFileName() << ")"
@@ -679,6 +790,13 @@ static void PrintFileSectionSizes(StringRef file) {
}
}
}
+ if (Err)
+ error(std::move(Err), UA->getFileName());
+ } else {
+ consumeError(AOrErr.takeError());
+ error("Mach-O universal file: " + file + " for architecture " +
+ StringRef(I->getArchTypeName()) +
+ " is not a Mach-O file or an archive file");
}
}
} else if (ObjectFile *o = dyn_cast<ObjectFile>(&Bin)) {
@@ -686,10 +804,10 @@ static void PrintFileSectionSizes(StringRef file) {
return;
if (OutputFormat == sysv)
outs() << o->getFileName() << " :\n";
- PrintObjectSectionSizes(o);
+ printObjectSectionSizes(o);
if (OutputFormat == berkeley) {
MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
- if (!MachO || moreThanOneFile)
+ if (!MachO || MoreThanOneFile)
outs() << o->getFileName();
outs() << "\n";
}
@@ -704,7 +822,7 @@ static void PrintFileSectionSizes(StringRef file) {
int main(int argc, char **argv) {
// Print a stack trace if we signal out.
- sys::PrintStackTraceOnErrorSignal();
+ sys::PrintStackTraceOnErrorSignal(argv[0]);
PrettyStackTraceProgram X(argc, argv);
llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
@@ -731,9 +849,10 @@ int main(int argc, char **argv) {
if (InputFilenames.size() == 0)
InputFilenames.push_back("a.out");
- moreThanOneFile = InputFilenames.size() > 1;
+ MoreThanOneFile = InputFilenames.size() > 1;
std::for_each(InputFilenames.begin(), InputFilenames.end(),
- PrintFileSectionSizes);
+ printFileSectionSizes);
- return 0;
+ if (HadError)
+ return 1;
}
diff --git a/tools/llvm-split/Makefile b/tools/llvm-split/Makefile
deleted file mode 100644
index ef1243dbf1ffa..0000000000000
--- a/tools/llvm-split/Makefile
+++ /dev/null
@@ -1,17 +0,0 @@
-##===- 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 := llvm-split
-LINK_COMPONENTS := transformutils bitwriter core irreader support
-
-# This tool has no plugins, optimize startup time.
-TOOL_NO_EXPORTS := 1
-
-include $(LEVEL)/Makefile.common
diff --git a/tools/llvm-split/llvm-split.cpp b/tools/llvm-split/llvm-split.cpp
index 059770fbf4ac3..024363547f937 100644
--- a/tools/llvm-split/llvm-split.cpp
+++ b/tools/llvm-split/llvm-split.cpp
@@ -14,6 +14,7 @@
#include "llvm/ADT/StringExtras.h"
#include "llvm/Bitcode/ReaderWriter.h"
#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Verifier.h"
#include "llvm/IRReader/IRReader.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
@@ -35,8 +36,12 @@ OutputFilename("o", cl::desc("Override output filename"),
static cl::opt<unsigned> NumOutputs("j", cl::Prefix, cl::init(2),
cl::desc("Number of output files"));
+static cl::opt<bool>
+ PreserveLocals("preserve-locals", cl::Prefix, cl::init(false),
+ cl::desc("Split without externalizing locals"));
+
int main(int argc, char **argv) {
- LLVMContext &Context = getGlobalContext();
+ LLVMContext Context;
SMDiagnostic Err;
cl::ParseCommandLineOptions(argc, argv, "LLVM module splitter\n");
@@ -57,11 +62,12 @@ int main(int argc, char **argv) {
exit(1);
}
+ verifyModule(*MPart);
WriteBitcodeToFile(MPart.get(), Out->os());
// Declare success.
Out->keep();
- });
+ }, PreserveLocals);
return 0;
}
diff --git a/tools/llvm-stress/Makefile b/tools/llvm-stress/Makefile
deleted file mode 100644
index 29245af7298cb..0000000000000
--- a/tools/llvm-stress/Makefile
+++ /dev/null
@@ -1,15 +0,0 @@
-##===- tools/llvm-stress/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-stress
-LINK_COMPONENTS := object
-LINK_COMPONENTS := bitreader bitwriter asmparser irreader instrumentation scalaropts ipo
-
-include $(LEVEL)/Makefile.common
diff --git a/tools/llvm-stress/llvm-stress.cpp b/tools/llvm-stress/llvm-stress.cpp
index 99d2afdcd3012..0b887ea9b4cd4 100644
--- a/tools/llvm-stress/llvm-stress.cpp
+++ b/tools/llvm-stress/llvm-stress.cpp
@@ -17,10 +17,10 @@
#include "llvm/IR/IRPrintingPasses.h"
#include "llvm/IR/Instruction.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"
-#include "llvm/IR/LegacyPassManager.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/ManagedStatic.h"
@@ -28,8 +28,6 @@
#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/ToolOutputFile.h"
#include <algorithm>
-#include <set>
-#include <sstream>
#include <vector>
namespace llvm {
@@ -43,6 +41,8 @@ static cl::opt<std::string>
OutputFilename("o", cl::desc("Override output filename"),
cl::value_desc("filename"));
+static LLVMContext Context;
+
namespace cl {
template <> class parser<Type*> final : public basic_parser<Type*> {
public:
@@ -50,7 +50,6 @@ public:
// Parse options as IR types. Return true on error.
bool parse(Option &O, StringRef, StringRef Arg, Type *&Value) {
- auto &Context = getGlobalContext();
if (Arg == "half") Value = Type::getHalfTy(Context);
else if (Arg == "fp128") Value = Type::getFP128Ty(Context);
else if (Arg == "x86_fp80") Value = Type::getX86_FP80Ty(Context);
@@ -687,7 +686,7 @@ int main(int argc, char **argv) {
cl::ParseCommandLineOptions(argc, argv, "llvm codegen stress-tester\n");
llvm_shutdown_obj Y;
- auto M = make_unique<Module>("/tmp/autogen.bc", getGlobalContext());
+ auto M = make_unique<Module>("/tmp/autogen.bc", Context);
Function *F = GenEmptyFunction(M.get());
// Pick an initial seed value
diff --git a/tools/llvm-symbolizer/Makefile b/tools/llvm-symbolizer/Makefile
deleted file mode 100644
index 8272d61280c75..0000000000000
--- a/tools/llvm-symbolizer/Makefile
+++ /dev/null
@@ -1,17 +0,0 @@
-##===- tools/llvm-symbolizer/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-symbolizer
-LINK_COMPONENTS := DebugInfoDWARF DebugInfoPDB Object Support Symbolize
-
-# This tool has no plugins, optimize startup time.
-TOOL_NO_EXPORTS := 1
-
-include $(LEVEL)/Makefile.common
diff --git a/tools/llvm-symbolizer/llvm-symbolizer.cpp b/tools/llvm-symbolizer/llvm-symbolizer.cpp
index 950349377bf79..7ee4ba19603b7 100644
--- a/tools/llvm-symbolizer/llvm-symbolizer.cpp
+++ b/tools/llvm-symbolizer/llvm-symbolizer.cpp
@@ -86,10 +86,12 @@ static cl::opt<int> ClPrintSourceContextLines(
"print-source-context-lines", cl::init(0),
cl::desc("Print N number of source file context"));
-static bool error(std::error_code ec) {
- if (!ec)
+template<typename T>
+static bool error(Expected<T> &ResOrErr) {
+ if (ResOrErr)
return false;
- errs() << "LLVMSymbolizer: error reading file: " << ec.message() << ".\n";
+ logAllUnhandledErrors(ResOrErr.takeError(), errs(),
+ "LLVMSymbolizer: error reading file: ");
return true;
}
@@ -138,7 +140,7 @@ static bool parseCommand(StringRef InputString, bool &IsData,
int main(int argc, char **argv) {
// Print stack trace if we signal out.
- sys::PrintStackTraceOnErrorSignal();
+ sys::PrintStackTraceOnErrorSignal(argv[0]);
PrettyStackTraceProgram X(argc, argv);
llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
@@ -185,14 +187,14 @@ int main(int argc, char **argv) {
}
if (IsData) {
auto ResOrErr = Symbolizer.symbolizeData(ModuleName, ModuleOffset);
- Printer << (error(ResOrErr.getError()) ? DIGlobal() : ResOrErr.get());
+ Printer << (error(ResOrErr) ? DIGlobal() : ResOrErr.get());
} else if (ClPrintInlining) {
auto ResOrErr = Symbolizer.symbolizeInlinedCode(ModuleName, ModuleOffset);
- Printer << (error(ResOrErr.getError()) ? DIInliningInfo()
+ Printer << (error(ResOrErr) ? DIInliningInfo()
: ResOrErr.get());
} else {
auto ResOrErr = Symbolizer.symbolizeCode(ModuleName, ModuleOffset);
- Printer << (error(ResOrErr.getError()) ? DILineInfo() : ResOrErr.get());
+ Printer << (error(ResOrErr) ? DILineInfo() : ResOrErr.get());
}
outs() << "\n";
outs().flush();
diff --git a/tools/lto/CMakeLists.txt b/tools/lto/CMakeLists.txt
index 62447eec389bc..8a1571ba03f12 100644
--- a/tools/lto/CMakeLists.txt
+++ b/tools/lto/CMakeLists.txt
@@ -1,5 +1,6 @@
set(LLVM_LINK_COMPONENTS
${LLVM_TARGETS_TO_BUILD}
+ BitReader
Core
LTO
MC
@@ -18,7 +19,8 @@ set(LLVM_EXPORTED_SYMBOL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/lto.exports)
add_llvm_library(LTO SHARED ${SOURCES})
install(FILES ${LLVM_MAIN_INCLUDE_DIR}/llvm-c/lto.h
- DESTINATION include/llvm-c)
+ DESTINATION include/llvm-c
+ COMPONENT LTO)
if (APPLE)
set(LTO_VERSION ${LLVM_VERSION_MAJOR})
diff --git a/tools/lto/Makefile b/tools/lto/Makefile
deleted file mode 100644
index 530c05a47318b..0000000000000
--- a/tools/lto/Makefile
+++ /dev/null
@@ -1,42 +0,0 @@
-##===- tools/lto/Makefile ----------------------------------*- Makefile -*-===##
-#
-# The LLVM Compiler Infrastructure
-#
-# This file is distributed under the University of Illinois Open Source
-# License. See LICENSE.TXT for details.
-#
-##===----------------------------------------------------------------------===##
-
-LEVEL := ../..
-LIBRARYNAME := LTO
-LINK_COMPONENTS := all-targets core lto mc mcdisassembler support
-LINK_LIBS_IN_SHARED := 1
-SHARED_LIBRARY := 1
-
-EXPORTED_SYMBOL_FILE = $(PROJ_SRC_DIR)/lto.exports
-
-include $(LEVEL)/Makefile.common
-
-ifeq ($(HOST_OS),Darwin)
- # Special hack to allow libLTO to have an offset version number.
- ifdef LLVM_LTO_VERSION_OFFSET
- LTO_LIBRARY_VERSION := $(shell expr $(LLVM_SUBMIT_VERSION) + \
- $(LLVM_LTO_VERSION_OFFSET))
- else
- LTO_LIBRARY_VERSION := $(LLVM_SUBMIT_VERSION)
- endif
-
- # set dylib internal version number to llvmCore submission number
- ifdef LLVM_SUBMIT_VERSION
- LLVMLibsOptions := $(LLVMLibsOptions) -Wl,-current_version \
- -Wl,$(LTO_LIBRARY_VERSION).$(LLVM_SUBMIT_SUBVERSION) \
- -Wl,-compatibility_version -Wl,1
- endif
-
- # If we're doing an Apple-style build, add the LTO object path.
- ifeq ($(RC_XBS),YES)
- TempFile := $(shell mkdir -p ${OBJROOT}/dSYMs ; mktemp ${OBJROOT}/dSYMs/llvm-lto.XXXXXX)
- LLVMLibsOptions := $(LLVMLibsOptions) \
- -Wl,-object_path_lto -Wl,$(TempFile)
- endif
-endif
diff --git a/tools/lto/lto.cpp b/tools/lto/lto.cpp
index d8f99c050a34c..7109cf4d90970 100644
--- a/tools/lto/lto.cpp
+++ b/tools/lto/lto.cpp
@@ -14,15 +14,18 @@
#include "llvm-c/lto.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/Bitcode/ReaderWriter.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"
+#include "llvm/LTO/legacy/LTOCodeGenerator.h"
+#include "llvm/LTO/legacy/LTOModule.h"
+#include "llvm/LTO/legacy/ThinLTOCodeGenerator.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/raw_ostream.h"
// extra command-line flags needed for LTOCodeGenerator
static cl::opt<char>
@@ -81,7 +84,6 @@ static void diagnosticHandler(const DiagnosticInfo &DI, void *Context) {
DiagnosticPrinterRawOStream DP(Stream);
DI.print(DP);
}
- sLastErrorString += '\n';
}
// Initialize the configured targets if they have not been initialized.
@@ -100,7 +102,8 @@ static void lto_initialize() {
InitializeAllAsmPrinters();
InitializeAllDisassemblers();
- LTOContext = &getGlobalContext();
+ static LLVMContext Context;
+ LTOContext = &Context;
LTOContext->setDiagnosticHandler(diagnosticHandler, nullptr, true);
initialized = true;
}
@@ -111,23 +114,24 @@ 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() : LTOCodeGenerator(*LTOContext) {
- setDiagnosticHandler(handleLibLTODiagnostic, nullptr); }
+ LibLTOCodeGenerator() : LTOCodeGenerator(*LTOContext) { init(); }
LibLTOCodeGenerator(std::unique_ptr<LLVMContext> Context)
: LTOCodeGenerator(*Context), OwnedContext(std::move(Context)) {
- setDiagnosticHandler(handleLibLTODiagnostic, nullptr); }
+ init();
+ }
// Reset the module first in case MergedModule is created in OwnedContext.
// Module must be destructed before its context gets destructed.
~LibLTOCodeGenerator() { resetMergedModule(); }
+ void init() { setDiagnosticHandler(handleLibLTODiagnostic, nullptr); }
+
std::unique_ptr<MemoryBuffer> NativeObjectFile;
std::unique_ptr<LLVMContext> OwnedContext;
};
@@ -135,6 +139,7 @@ struct LibLTOCodeGenerator : LTOCodeGenerator {
}
DEFINE_SIMPLE_CONVERSION_FUNCTIONS(LibLTOCodeGenerator, lto_code_gen_t)
+DEFINE_SIMPLE_CONVERSION_FUNCTIONS(ThinLTOCodeGenerator, thinlto_code_gen_t)
DEFINE_SIMPLE_CONVERSION_FUNCTIONS(LTOModule, lto_module_t)
// Convert the subtarget features into a string to pass to LTOCodeGenerator.
@@ -176,6 +181,14 @@ bool lto_module_is_object_file_for_target(const char* path,
return LTOModule::isBitcodeForTarget(Buffer->get(), target_triplet_prefix);
}
+bool lto_module_has_objc_category(const void *mem, size_t length) {
+ std::unique_ptr<MemoryBuffer> Buffer(LTOModule::makeBuffer(mem, length));
+ if (!Buffer)
+ return false;
+ LLVMContext Ctx;
+ return llvm::isBitcodeContainingObjCCategory(*Buffer, Ctx);
+}
+
bool lto_module_is_object_file_in_memory(const void* mem, size_t length) {
return LTOModule::isBitcodeFile(mem, length);
}
@@ -249,8 +262,14 @@ lto_module_t lto_module_create_in_local_context(const void *mem, size_t length,
const char *path) {
lto_initialize();
llvm::TargetOptions Options = InitTargetOptionsFromCodeGenFlags();
+
+ // Create a local context. Ownership will be transfered to LTOModule.
+ std::unique_ptr<LLVMContext> Context = llvm::make_unique<LLVMContext>();
+ Context->setDiagnosticHandler(diagnosticHandler, nullptr, true);
+
ErrorOr<std::unique_ptr<LTOModule>> M =
- LTOModule::createInLocalContext(mem, length, Options, path);
+ LTOModule::createInLocalContext(std::move(Context), mem, length, Options,
+ path);
if (!M)
return nullptr;
return wrap(M->release());
@@ -262,8 +281,8 @@ lto_module_t lto_module_create_in_codegen_context(const void *mem,
lto_code_gen_t cg) {
lto_initialize();
llvm::TargetOptions Options = InitTargetOptionsFromCodeGenFlags();
- ErrorOr<std::unique_ptr<LTOModule>> M = LTOModule::createInContext(
- mem, length, Options, path, &unwrap(cg)->getContext());
+ ErrorOr<std::unique_ptr<LTOModule>> M = LTOModule::createFromBuffer(
+ unwrap(cg)->getContext(), mem, length, Options, path);
return wrap(M->release());
}
@@ -347,7 +366,7 @@ bool lto_codegen_set_pic_model(lto_code_gen_t cg, lto_codegen_model model) {
unwrap(cg)->setCodePICModel(Reloc::DynamicNoPIC);
return false;
case LTO_CODEGEN_PIC_MODEL_DEFAULT:
- unwrap(cg)->setCodePICModel(Reloc::Default);
+ unwrap(cg)->setCodePICModel(None);
return false;
}
sLastErrorString = "Unknown PIC model";
@@ -435,3 +454,116 @@ void lto_codegen_set_should_embed_uselists(lto_code_gen_t cg,
lto_bool_t ShouldEmbedUselists) {
unwrap(cg)->setShouldEmbedUselists(ShouldEmbedUselists);
}
+
+// ThinLTO API below
+
+thinlto_code_gen_t thinlto_create_codegen(void) {
+ lto_initialize();
+ ThinLTOCodeGenerator *CodeGen = new ThinLTOCodeGenerator();
+ CodeGen->setTargetOptions(InitTargetOptionsFromCodeGenFlags());
+
+ return wrap(CodeGen);
+}
+
+void thinlto_codegen_dispose(thinlto_code_gen_t cg) { delete unwrap(cg); }
+
+void thinlto_codegen_add_module(thinlto_code_gen_t cg, const char *Identifier,
+ const char *Data, int Length) {
+ unwrap(cg)->addModule(Identifier, StringRef(Data, Length));
+}
+
+void thinlto_codegen_process(thinlto_code_gen_t cg) { unwrap(cg)->run(); }
+
+unsigned int thinlto_module_get_num_objects(thinlto_code_gen_t cg) {
+ return unwrap(cg)->getProducedBinaries().size();
+}
+LTOObjectBuffer thinlto_module_get_object(thinlto_code_gen_t cg,
+ unsigned int index) {
+ assert(index < unwrap(cg)->getProducedBinaries().size() && "Index overflow");
+ auto &MemBuffer = unwrap(cg)->getProducedBinaries()[index];
+ return LTOObjectBuffer{MemBuffer->getBufferStart(),
+ MemBuffer->getBufferSize()};
+}
+
+void thinlto_codegen_disable_codegen(thinlto_code_gen_t cg,
+ lto_bool_t disable) {
+ unwrap(cg)->disableCodeGen(disable);
+}
+
+void thinlto_codegen_set_codegen_only(thinlto_code_gen_t cg,
+ lto_bool_t CodeGenOnly) {
+ unwrap(cg)->setCodeGenOnly(CodeGenOnly);
+}
+
+void thinlto_debug_options(const char *const *options, int number) {
+ // if options were requested, set them
+ if (number && options) {
+ std::vector<const char *> CodegenArgv(1, "libLTO");
+ for (auto Arg : ArrayRef<const char *>(options, number))
+ CodegenArgv.push_back(Arg);
+ cl::ParseCommandLineOptions(CodegenArgv.size(), CodegenArgv.data());
+ }
+}
+
+lto_bool_t lto_module_is_thinlto(lto_module_t mod) {
+ return unwrap(mod)->isThinLTO();
+}
+
+void thinlto_codegen_add_must_preserve_symbol(thinlto_code_gen_t cg,
+ const char *Name, int Length) {
+ unwrap(cg)->preserveSymbol(StringRef(Name, Length));
+}
+
+void thinlto_codegen_add_cross_referenced_symbol(thinlto_code_gen_t cg,
+ const char *Name, int Length) {
+ unwrap(cg)->crossReferenceSymbol(StringRef(Name, Length));
+}
+
+void thinlto_codegen_set_cpu(thinlto_code_gen_t cg, const char *cpu) {
+ return unwrap(cg)->setCpu(cpu);
+}
+
+void thinlto_codegen_set_cache_dir(thinlto_code_gen_t cg,
+ const char *cache_dir) {
+ return unwrap(cg)->setCacheDir(cache_dir);
+}
+
+void thinlto_codegen_set_cache_pruning_interval(thinlto_code_gen_t cg,
+ int interval) {
+ return unwrap(cg)->setCachePruningInterval(interval);
+}
+
+void thinlto_codegen_set_cache_entry_expiration(thinlto_code_gen_t cg,
+ unsigned expiration) {
+ return unwrap(cg)->setCacheEntryExpiration(expiration);
+}
+
+void thinlto_codegen_set_final_cache_size_relative_to_available_space(
+ thinlto_code_gen_t cg, unsigned Percentage) {
+ return unwrap(cg)->setMaxCacheSizeRelativeToAvailableSpace(Percentage);
+}
+
+void thinlto_codegen_set_savetemps_dir(thinlto_code_gen_t cg,
+ const char *save_temps_dir) {
+ return unwrap(cg)->setSaveTempsDir(save_temps_dir);
+}
+
+lto_bool_t thinlto_codegen_set_pic_model(thinlto_code_gen_t cg,
+ lto_codegen_model model) {
+ 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(None);
+ return false;
+ }
+ sLastErrorString = "Unknown PIC model";
+ return true;
+}
diff --git a/tools/lto/lto.exports b/tools/lto/lto.exports
index 8bc2b0f9d312f..74091c2641b77 100644
--- a/tools/lto/lto.exports
+++ b/tools/lto/lto.exports
@@ -18,6 +18,7 @@ lto_module_is_object_file
lto_module_is_object_file_for_target
lto_module_is_object_file_in_memory
lto_module_is_object_file_in_memory_for_target
+lto_module_has_objc_category
lto_module_dispose
lto_api_version
lto_codegen_set_diagnostic_handler
@@ -45,3 +46,22 @@ LLVMCreateDisasmCPU
LLVMDisasmDispose
LLVMDisasmInstruction
LLVMSetDisasmOptions
+thinlto_create_codegen
+thinlto_codegen_dispose
+thinlto_codegen_add_module
+thinlto_codegen_process
+thinlto_module_get_num_objects
+thinlto_module_get_object
+thinlto_codegen_set_pic_model
+thinlto_codegen_set_cache_dir
+thinlto_codegen_set_cache_pruning_interval
+thinlto_codegen_set_cache_entry_expiration
+thinlto_codegen_set_savetemps_dir
+thinlto_codegen_set_cpu
+thinlto_debug_options
+lto_module_is_thinlto
+thinlto_codegen_add_must_preserve_symbol
+thinlto_codegen_add_cross_referenced_symbol
+thinlto_codegen_set_final_cache_size_relative_to_available_space
+thinlto_codegen_set_codegen_only
+thinlto_codegen_disable_codegen \ No newline at end of file
diff --git a/tools/obj2yaml/CMakeLists.txt b/tools/obj2yaml/CMakeLists.txt
index 3cdac5c748752..9b89552506025 100644
--- a/tools/obj2yaml/CMakeLists.txt
+++ b/tools/obj2yaml/CMakeLists.txt
@@ -1,8 +1,13 @@
set(LLVM_LINK_COMPONENTS
Object
+ ObjectYAML
Support
)
add_llvm_tool(obj2yaml
- obj2yaml.cpp coff2yaml.cpp elf2yaml.cpp Error.cpp
+ obj2yaml.cpp
+ coff2yaml.cpp
+ elf2yaml.cpp
+ macho2yaml.cpp
+ Error.cpp
)
diff --git a/tools/obj2yaml/Error.cpp b/tools/obj2yaml/Error.cpp
index abef8af58cbfe..9d1af680a7399 100644
--- a/tools/obj2yaml/Error.cpp
+++ b/tools/obj2yaml/Error.cpp
@@ -13,6 +13,9 @@
using namespace llvm;
namespace {
+// FIXME: This class is only here to support the transition to llvm::Error. It
+// will be removed once this transition is complete. Clients should prefer to
+// deal with the Error value directly, rather than converting to error_code.
class _obj2yaml_error_category : public std::error_category {
public:
const char *name() const LLVM_NOEXCEPT override;
@@ -34,14 +37,26 @@ std::string _obj2yaml_error_category::message(int ev) const {
return "Unrecognized file type.";
case obj2yaml_error::unsupported_obj_file_format:
return "Unsupported object file format.";
+ case obj2yaml_error::not_implemented:
+ return "Feature not yet implemented.";
}
llvm_unreachable("An enumerator of obj2yaml_error does not have a message "
"defined.");
}
namespace llvm {
- const std::error_category &obj2yaml_category() {
+
+const std::error_category &obj2yaml_category() {
static _obj2yaml_error_category o;
return o;
}
+
+char Obj2YamlError::ID = 0;
+
+void Obj2YamlError::log(raw_ostream &OS) const { OS << ErrMsg << "\n"; }
+
+std::error_code Obj2YamlError::convertToErrorCode() const {
+ return std::error_code(static_cast<int>(Code), obj2yaml_category());
+}
+
} // namespace llvm
diff --git a/tools/obj2yaml/Error.h b/tools/obj2yaml/Error.h
index 982f59e236cc0..f5111f257ce36 100644
--- a/tools/obj2yaml/Error.h
+++ b/tools/obj2yaml/Error.h
@@ -10,6 +10,8 @@
#ifndef LLVM_TOOLS_OBJ2YAML_ERROR_H
#define LLVM_TOOLS_OBJ2YAML_ERROR_H
+#include "llvm/Support/Error.h"
+
#include <system_error>
namespace llvm {
@@ -19,13 +21,30 @@ enum class obj2yaml_error {
success = 0,
file_not_found,
unrecognized_file_format,
- unsupported_obj_file_format
+ unsupported_obj_file_format,
+ not_implemented
};
inline std::error_code make_error_code(obj2yaml_error e) {
return std::error_code(static_cast<int>(e), obj2yaml_category());
}
+class Obj2YamlError : public ErrorInfo<Obj2YamlError> {
+public:
+ static char ID;
+ Obj2YamlError(obj2yaml_error C) : Code(C) {}
+ Obj2YamlError(std::string ErrMsg) : ErrMsg(std::move(ErrMsg)) {}
+ Obj2YamlError(obj2yaml_error C, std::string ErrMsg)
+ : ErrMsg(std::move(ErrMsg)), Code(C) {}
+ void log(raw_ostream &OS) const override;
+ const std::string &getErrorMessage() const { return ErrMsg; }
+ std::error_code convertToErrorCode() const override;
+
+private:
+ std::string ErrMsg;
+ obj2yaml_error Code;
+};
+
} // namespace llvm
namespace std {
diff --git a/tools/obj2yaml/Makefile b/tools/obj2yaml/Makefile
deleted file mode 100644
index 6cbef6998dc6c..0000000000000
--- a/tools/obj2yaml/Makefile
+++ /dev/null
@@ -1,17 +0,0 @@
-##===- utils/obj2yaml/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 = obj2yaml
-LINK_COMPONENTS := object
-
-# This tool has no plugins, optimize startup time.
-TOOL_NO_EXPORTS = 1
-
-include $(LEVEL)/Makefile.common
diff --git a/tools/obj2yaml/coff2yaml.cpp b/tools/obj2yaml/coff2yaml.cpp
index f675bfe4e618e..c734601ede76b 100644
--- a/tools/obj2yaml/coff2yaml.cpp
+++ b/tools/obj2yaml/coff2yaml.cpp
@@ -9,7 +9,7 @@
#include "obj2yaml.h"
#include "llvm/Object/COFF.h"
-#include "llvm/Object/COFFYAML.h"
+#include "llvm/ObjectYAML/COFFYAML.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/YAMLTraits.h"
@@ -109,6 +109,7 @@ void COFFDumper::dumpSections(unsigned NumSections) {
NewYAMLSection.Header.VirtualAddress = ObjSection.getAddress();
NewYAMLSection.Header.VirtualSize = COFFSection->VirtualSize;
NewYAMLSection.Alignment = ObjSection.getAlignment();
+ assert(NewYAMLSection.Alignment <= 8192);
ArrayRef<uint8_t> sectionData;
if (!ObjSection.isBSS())
@@ -120,9 +121,14 @@ void COFFDumper::dumpSections(unsigned NumSections) {
const object::coff_relocation *reloc = Obj.getCOFFRelocation(Reloc);
COFFYAML::Relocation Rel;
object::symbol_iterator Sym = Reloc.getSymbol();
- ErrorOr<StringRef> SymbolNameOrErr = Sym->getName();
- if (std::error_code EC = SymbolNameOrErr.getError())
- report_fatal_error(EC.message());
+ Expected<StringRef> SymbolNameOrErr = Sym->getName();
+ if (!SymbolNameOrErr) {
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(SymbolNameOrErr.takeError(), OS, "");
+ OS.flush();
+ report_fatal_error(Buf);
+ }
Rel.SymbolName = *SymbolNameOrErr;
Rel.VirtualAddress = reloc->VirtualAddress;
Rel.Type = reloc->Type;
diff --git a/tools/obj2yaml/elf2yaml.cpp b/tools/obj2yaml/elf2yaml.cpp
index f2b0138073451..782832d54571f 100644
--- a/tools/obj2yaml/elf2yaml.cpp
+++ b/tools/obj2yaml/elf2yaml.cpp
@@ -11,7 +11,7 @@
#include "obj2yaml.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Object/ELFObjectFile.h"
-#include "llvm/Object/ELFYAML.h"
+#include "llvm/ObjectYAML/ELFYAML.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/YAMLTraits.h"
@@ -179,10 +179,10 @@ ELFDumper<ELFT>::dumpSymbol(const Elf_Sym *Sym, const Elf_Shdr *SymTab,
S.Size = Sym->st_size;
S.Other = Sym->st_other;
- ErrorOr<StringRef> NameOrErr = Sym->getName(StrTable);
- if (std::error_code EC = NameOrErr.getError())
- return EC;
- S.Name = NameOrErr.get();
+ Expected<StringRef> SymbolNameOrErr = Sym->getName(StrTable);
+ if (!SymbolNameOrErr)
+ return errorToErrorCode(SymbolNameOrErr.takeError());
+ S.Name = SymbolNameOrErr.get();
ErrorOr<const Elf_Shdr *> ShdrOrErr = Obj.getSection(Sym, SymTab, ShndxTable);
if (std::error_code EC = ShdrOrErr.getError())
@@ -191,7 +191,7 @@ ELFDumper<ELFT>::dumpSymbol(const Elf_Sym *Sym, const Elf_Shdr *SymTab,
if (!Shdr)
return obj2yaml_error::success;
- NameOrErr = Obj.getSectionName(Shdr);
+ ErrorOr<StringRef> NameOrErr = Obj.getSectionName(Shdr);
if (std::error_code EC = NameOrErr.getError())
return EC;
S.Section = NameOrErr.get();
@@ -217,9 +217,9 @@ std::error_code ELFDumper<ELFT>::dumpRelocation(const RelT *Rel,
return EC;
StringRef StrTab = *StrTabOrErr;
- ErrorOr<StringRef> NameOrErr = Sym->getName(StrTab);
- if (std::error_code EC = NameOrErr.getError())
- return EC;
+ Expected<StringRef> NameOrErr = Sym->getName(StrTab);
+ if (!NameOrErr)
+ return errorToErrorCode(NameOrErr.takeError());
R.Symbol = NameOrErr.get();
return obj2yaml_error::success;
@@ -368,9 +368,9 @@ ErrorOr<ELFYAML::Group *> ELFDumper<ELFT>::dumpGroup(const Elf_Shdr *Shdr) {
auto sectionContents = Obj.getSectionContents(Shdr);
if (std::error_code ec = sectionContents.getError())
return ec;
- ErrorOr<StringRef> symbolName = symbol->getName(StrTab);
- if (std::error_code EC = symbolName.getError())
- return EC;
+ Expected<StringRef> symbolName = symbol->getName(StrTab);
+ if (!symbolName)
+ return errorToErrorCode(symbolName.takeError());
S->Info = *symbolName;
const Elf_Word *groupMembers =
reinterpret_cast<const Elf_Word *>(sectionContents->data());
diff --git a/tools/obj2yaml/macho2yaml.cpp b/tools/obj2yaml/macho2yaml.cpp
new file mode 100644
index 0000000000000..c9a1385f17300
--- /dev/null
+++ b/tools/obj2yaml/macho2yaml.cpp
@@ -0,0 +1,527 @@
+//===------ macho2yaml.cpp - obj2yaml conversion tool -----------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Error.h"
+#include "obj2yaml.h"
+#include "llvm/Object/MachOUniversal.h"
+#include "llvm/ObjectYAML/ObjectYAML.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/LEB128.h"
+
+#include <string.h> // for memcpy
+
+using namespace llvm;
+
+class MachODumper {
+
+ template <typename StructType>
+ const char *processLoadCommandData(
+ MachOYAML::LoadCommand &LC,
+ const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd);
+
+ const object::MachOObjectFile &Obj;
+ void dumpHeader(std::unique_ptr<MachOYAML::Object> &Y);
+ void dumpLoadCommands(std::unique_ptr<MachOYAML::Object> &Y);
+ void dumpLinkEdit(std::unique_ptr<MachOYAML::Object> &Y);
+ void dumpRebaseOpcodes(std::unique_ptr<MachOYAML::Object> &Y);
+ void dumpBindOpcodes(std::vector<MachOYAML::BindOpcode> &BindOpcodes,
+ ArrayRef<uint8_t> OpcodeBuffer, bool Lazy = false);
+ void dumpExportTrie(std::unique_ptr<MachOYAML::Object> &Y);
+ void dumpSymbols(std::unique_ptr<MachOYAML::Object> &Y);
+
+public:
+ MachODumper(const object::MachOObjectFile &O) : Obj(O) {}
+ Expected<std::unique_ptr<MachOYAML::Object>> dump();
+};
+
+#define HANDLE_LOAD_COMMAND(LCName, LCValue, LCStruct) \
+ case MachO::LCName: \
+ memcpy((void *) & (LC.Data.LCStruct##_data), LoadCmd.Ptr, \
+ sizeof(MachO::LCStruct)); \
+ if (Obj.isLittleEndian() != sys::IsLittleEndianHost) \
+ MachO::swapStruct(LC.Data.LCStruct##_data); \
+ EndPtr = processLoadCommandData<MachO::LCStruct>(LC, LoadCmd); \
+ break;
+
+template <typename SectionType>
+MachOYAML::Section constructSectionCommon(SectionType Sec) {
+ MachOYAML::Section TempSec;
+ memcpy(reinterpret_cast<void *>(&TempSec.sectname[0]), &Sec.sectname[0], 16);
+ memcpy(reinterpret_cast<void *>(&TempSec.segname[0]), &Sec.segname[0], 16);
+ TempSec.addr = Sec.addr;
+ TempSec.size = Sec.size;
+ TempSec.offset = Sec.offset;
+ TempSec.align = Sec.align;
+ TempSec.reloff = Sec.reloff;
+ TempSec.nreloc = Sec.nreloc;
+ TempSec.flags = Sec.flags;
+ TempSec.reserved1 = Sec.reserved1;
+ TempSec.reserved2 = Sec.reserved2;
+ TempSec.reserved3 = 0;
+ return TempSec;
+}
+
+template <typename SectionType>
+MachOYAML::Section constructSection(SectionType Sec);
+
+template <> MachOYAML::Section constructSection(MachO::section Sec) {
+ MachOYAML::Section TempSec = constructSectionCommon(Sec);
+ TempSec.reserved3 = 0;
+ return TempSec;
+}
+
+template <> MachOYAML::Section constructSection(MachO::section_64 Sec) {
+ MachOYAML::Section TempSec = constructSectionCommon(Sec);
+ TempSec.reserved3 = Sec.reserved3;
+ return TempSec;
+}
+
+template <typename SectionType, typename SegmentType>
+const char *
+extractSections(const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd,
+ std::vector<MachOYAML::Section> &Sections,
+ bool IsLittleEndian) {
+ auto End = LoadCmd.Ptr + LoadCmd.C.cmdsize;
+ const SectionType *Curr =
+ reinterpret_cast<const SectionType *>(LoadCmd.Ptr + sizeof(SegmentType));
+ for (; reinterpret_cast<const void *>(Curr) < End; Curr++) {
+ if (IsLittleEndian != sys::IsLittleEndianHost) {
+ SectionType Sec;
+ memcpy((void *)&Sec, Curr, sizeof(SectionType));
+ MachO::swapStruct(Sec);
+ Sections.push_back(constructSection(Sec));
+ } else {
+ Sections.push_back(constructSection(*Curr));
+ }
+ }
+ return reinterpret_cast<const char *>(Curr);
+}
+
+template <typename StructType>
+const char *MachODumper::processLoadCommandData(
+ MachOYAML::LoadCommand &LC,
+ const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd) {
+ return LoadCmd.Ptr + sizeof(StructType);
+}
+
+template <>
+const char *MachODumper::processLoadCommandData<MachO::segment_command>(
+ MachOYAML::LoadCommand &LC,
+ const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd) {
+ return extractSections<MachO::section, MachO::segment_command>(
+ LoadCmd, LC.Sections, Obj.isLittleEndian());
+}
+
+template <>
+const char *MachODumper::processLoadCommandData<MachO::segment_command_64>(
+ MachOYAML::LoadCommand &LC,
+ const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd) {
+ return extractSections<MachO::section_64, MachO::segment_command_64>(
+ LoadCmd, LC.Sections, Obj.isLittleEndian());
+}
+
+template <typename StructType>
+const char *
+readString(MachOYAML::LoadCommand &LC,
+ const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd) {
+ auto Start = LoadCmd.Ptr + sizeof(StructType);
+ auto MaxSize = LoadCmd.C.cmdsize - sizeof(StructType);
+ auto Size = strnlen(Start, MaxSize);
+ LC.PayloadString = StringRef(Start, Size).str();
+ return Start + Size;
+}
+
+template <>
+const char *MachODumper::processLoadCommandData<MachO::dylib_command>(
+ MachOYAML::LoadCommand &LC,
+ const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd) {
+ return readString<MachO::dylib_command>(LC, LoadCmd);
+}
+
+template <>
+const char *MachODumper::processLoadCommandData<MachO::dylinker_command>(
+ MachOYAML::LoadCommand &LC,
+ const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd) {
+ return readString<MachO::dylinker_command>(LC, LoadCmd);
+}
+
+template <>
+const char *MachODumper::processLoadCommandData<MachO::rpath_command>(
+ MachOYAML::LoadCommand &LC,
+ const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd) {
+ return readString<MachO::rpath_command>(LC, LoadCmd);
+}
+
+Expected<std::unique_ptr<MachOYAML::Object>> MachODumper::dump() {
+ auto Y = make_unique<MachOYAML::Object>();
+ dumpHeader(Y);
+ dumpLoadCommands(Y);
+ dumpLinkEdit(Y);
+ return std::move(Y);
+}
+
+void MachODumper::dumpHeader(std::unique_ptr<MachOYAML::Object> &Y) {
+ Y->Header.magic = Obj.getHeader().magic;
+ Y->Header.cputype = Obj.getHeader().cputype;
+ Y->Header.cpusubtype = Obj.getHeader().cpusubtype;
+ Y->Header.filetype = Obj.getHeader().filetype;
+ Y->Header.ncmds = Obj.getHeader().ncmds;
+ Y->Header.sizeofcmds = Obj.getHeader().sizeofcmds;
+ Y->Header.flags = Obj.getHeader().flags;
+ Y->Header.reserved = 0;
+}
+
+void MachODumper::dumpLoadCommands(std::unique_ptr<MachOYAML::Object> &Y) {
+ for (auto LoadCmd : Obj.load_commands()) {
+ MachOYAML::LoadCommand LC;
+ const char *EndPtr = LoadCmd.Ptr;
+ switch (LoadCmd.C.cmd) {
+ default:
+ memcpy((void *)&(LC.Data.load_command_data), LoadCmd.Ptr,
+ sizeof(MachO::load_command));
+ if (Obj.isLittleEndian() != sys::IsLittleEndianHost)
+ MachO::swapStruct(LC.Data.load_command_data);
+ EndPtr = processLoadCommandData<MachO::load_command>(LC, LoadCmd);
+ break;
+#include "llvm/Support/MachO.def"
+ }
+ auto RemainingBytes = LoadCmd.C.cmdsize - (EndPtr - LoadCmd.Ptr);
+ if (!std::all_of(EndPtr, &EndPtr[RemainingBytes],
+ [](const char C) { return C == 0; })) {
+ LC.PayloadBytes.insert(LC.PayloadBytes.end(), EndPtr,
+ &EndPtr[RemainingBytes]);
+ RemainingBytes = 0;
+ }
+ LC.ZeroPadBytes = RemainingBytes;
+ Y->LoadCommands.push_back(std::move(LC));
+ }
+}
+
+void MachODumper::dumpLinkEdit(std::unique_ptr<MachOYAML::Object> &Y) {
+ dumpRebaseOpcodes(Y);
+ dumpBindOpcodes(Y->LinkEdit.BindOpcodes, Obj.getDyldInfoBindOpcodes());
+ dumpBindOpcodes(Y->LinkEdit.WeakBindOpcodes,
+ Obj.getDyldInfoWeakBindOpcodes());
+ dumpBindOpcodes(Y->LinkEdit.LazyBindOpcodes, Obj.getDyldInfoLazyBindOpcodes(),
+ true);
+ dumpExportTrie(Y);
+ dumpSymbols(Y);
+}
+
+void MachODumper::dumpRebaseOpcodes(std::unique_ptr<MachOYAML::Object> &Y) {
+ MachOYAML::LinkEditData &LEData = Y->LinkEdit;
+
+ auto RebaseOpcodes = Obj.getDyldInfoRebaseOpcodes();
+ for (auto OpCode = RebaseOpcodes.begin(); OpCode != RebaseOpcodes.end();
+ ++OpCode) {
+ MachOYAML::RebaseOpcode RebaseOp;
+ RebaseOp.Opcode =
+ static_cast<MachO::RebaseOpcode>(*OpCode & MachO::REBASE_OPCODE_MASK);
+ RebaseOp.Imm = *OpCode & MachO::REBASE_IMMEDIATE_MASK;
+
+ unsigned Count;
+ uint64_t ULEB = 0;
+
+ switch (RebaseOp.Opcode) {
+ case MachO::REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB:
+
+ ULEB = decodeULEB128(OpCode + 1, &Count);
+ RebaseOp.ExtraData.push_back(ULEB);
+ OpCode += Count;
+ // Intentionally no break here -- This opcode has two ULEB values
+ case MachO::REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
+ case MachO::REBASE_OPCODE_ADD_ADDR_ULEB:
+ case MachO::REBASE_OPCODE_DO_REBASE_ULEB_TIMES:
+ case MachO::REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB:
+
+ ULEB = decodeULEB128(OpCode + 1, &Count);
+ RebaseOp.ExtraData.push_back(ULEB);
+ OpCode += Count;
+ break;
+ default:
+ break;
+ }
+
+ LEData.RebaseOpcodes.push_back(RebaseOp);
+
+ if (RebaseOp.Opcode == MachO::REBASE_OPCODE_DONE)
+ break;
+ }
+}
+
+StringRef ReadStringRef(const uint8_t *Start) {
+ const uint8_t *Itr = Start;
+ for (; *Itr; ++Itr)
+ ;
+ return StringRef(reinterpret_cast<const char *>(Start), Itr - Start);
+}
+
+void MachODumper::dumpBindOpcodes(
+ std::vector<MachOYAML::BindOpcode> &BindOpcodes,
+ ArrayRef<uint8_t> OpcodeBuffer, bool Lazy) {
+ for (auto OpCode = OpcodeBuffer.begin(); OpCode != OpcodeBuffer.end();
+ ++OpCode) {
+ MachOYAML::BindOpcode BindOp;
+ BindOp.Opcode =
+ static_cast<MachO::BindOpcode>(*OpCode & MachO::BIND_OPCODE_MASK);
+ BindOp.Imm = *OpCode & MachO::BIND_IMMEDIATE_MASK;
+
+ unsigned Count;
+ uint64_t ULEB = 0;
+ int64_t SLEB = 0;
+
+ switch (BindOp.Opcode) {
+ case MachO::BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
+ ULEB = decodeULEB128(OpCode + 1, &Count);
+ BindOp.ULEBExtraData.push_back(ULEB);
+ OpCode += Count;
+ // Intentionally no break here -- this opcode has two ULEB values
+
+ case MachO::BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
+ case MachO::BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
+ case MachO::BIND_OPCODE_ADD_ADDR_ULEB:
+ case MachO::BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
+ ULEB = decodeULEB128(OpCode + 1, &Count);
+ BindOp.ULEBExtraData.push_back(ULEB);
+ OpCode += Count;
+ break;
+
+ case MachO::BIND_OPCODE_SET_ADDEND_SLEB:
+ SLEB = decodeSLEB128(OpCode + 1, &Count);
+ BindOp.SLEBExtraData.push_back(SLEB);
+ OpCode += Count;
+ break;
+
+ case MachO::BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
+ BindOp.Symbol = ReadStringRef(OpCode + 1);
+ OpCode += BindOp.Symbol.size() + 1;
+ break;
+ default:
+ break;
+ }
+
+ BindOpcodes.push_back(BindOp);
+
+ // Lazy bindings have DONE opcodes between operations, so we need to keep
+ // processing after a DONE.
+ if (!Lazy && BindOp.Opcode == MachO::BIND_OPCODE_DONE)
+ break;
+ }
+}
+
+/*!
+ * /brief processes a node from the export trie, and its children.
+ *
+ * To my knowledge there is no documentation of the encoded format of this data
+ * other than in the heads of the Apple linker engineers. To that end hopefully
+ * this comment and the implementation below can serve to light the way for
+ * anyone crazy enough to come down this path in the future.
+ *
+ * This function reads and preserves the trie structure of the export trie. To
+ * my knowledge there is no code anywhere else that reads the data and preserves
+ * the Trie. LD64 (sources available at opensource.apple.com) has a similar
+ * implementation that parses the export trie into a vector. That code as well
+ * as LLVM's libObject MachO implementation were the basis for this.
+ *
+ * The export trie is an encoded trie. The node serialization is a bit awkward.
+ * The below pseudo-code is the best description I've come up with for it.
+ *
+ * struct SerializedNode {
+ * ULEB128 TerminalSize;
+ * struct TerminalData { <-- This is only present if TerminalSize > 0
+ * ULEB128 Flags;
+ * ULEB128 Address; <-- Present if (! Flags & REEXPORT )
+ * ULEB128 Other; <-- Present if ( Flags & REEXPORT ||
+ * Flags & STUB_AND_RESOLVER )
+ * char[] ImportName; <-- Present if ( Flags & REEXPORT )
+ * }
+ * uint8_t ChildrenCount;
+ * Pair<char[], ULEB128> ChildNameOffsetPair[ChildrenCount];
+ * SerializedNode Children[ChildrenCount]
+ * }
+ *
+ * Terminal nodes are nodes that represent actual exports. They can appear
+ * anywhere in the tree other than at the root; they do not need to be leaf
+ * nodes. When reading the data out of the trie this routine reads it in-order,
+ * but it puts the child names and offsets directly into the child nodes. This
+ * results in looping over the children twice during serialization and
+ * de-serialization, but it makes the YAML representation more human readable.
+ *
+ * Below is an example of the graph from a "Hello World" executable:
+ *
+ * -------
+ * | '' |
+ * -------
+ * |
+ * -------
+ * | '_' |
+ * -------
+ * |
+ * |----------------------------------------|
+ * | |
+ * ------------------------ ---------------------
+ * | '_mh_execute_header' | | 'main' |
+ * | Flags: 0x00000000 | | Flags: 0x00000000 |
+ * | Addr: 0x00000000 | | Addr: 0x00001160 |
+ * ------------------------ ---------------------
+ *
+ * This graph represents the trie for the exports "__mh_execute_header" and
+ * "_main". In the graph only the "_main" and "__mh_execute_header" nodes are
+ * terminal.
+*/
+
+const uint8_t *processExportNode(const uint8_t *CurrPtr,
+ const uint8_t *const End,
+ MachOYAML::ExportEntry &Entry) {
+ if (CurrPtr >= End)
+ return CurrPtr;
+ unsigned Count = 0;
+ Entry.TerminalSize = decodeULEB128(CurrPtr, &Count);
+ CurrPtr += Count;
+ if (Entry.TerminalSize != 0) {
+ Entry.Flags = decodeULEB128(CurrPtr, &Count);
+ CurrPtr += Count;
+ if (Entry.Flags & MachO::EXPORT_SYMBOL_FLAGS_REEXPORT) {
+ Entry.Address = 0;
+ Entry.Other = decodeULEB128(CurrPtr, &Count);
+ CurrPtr += Count;
+ Entry.ImportName = std::string(reinterpret_cast<const char *>(CurrPtr));
+ } else {
+ Entry.Address = decodeULEB128(CurrPtr, &Count);
+ CurrPtr += Count;
+ if (Entry.Flags & MachO::EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) {
+ Entry.Other = decodeULEB128(CurrPtr, &Count);
+ CurrPtr += Count;
+ } else
+ Entry.Other = 0;
+ }
+ }
+ uint8_t childrenCount = *CurrPtr++;
+ if (childrenCount == 0)
+ return CurrPtr;
+
+ Entry.Children.insert(Entry.Children.begin(), (size_t)childrenCount,
+ MachOYAML::ExportEntry());
+ for (auto &Child : Entry.Children) {
+ Child.Name = std::string(reinterpret_cast<const char *>(CurrPtr));
+ CurrPtr += Child.Name.length() + 1;
+ Child.NodeOffset = decodeULEB128(CurrPtr, &Count);
+ CurrPtr += Count;
+ }
+ for (auto &Child : Entry.Children) {
+ CurrPtr = processExportNode(CurrPtr, End, Child);
+ }
+ return CurrPtr;
+}
+
+void MachODumper::dumpExportTrie(std::unique_ptr<MachOYAML::Object> &Y) {
+ MachOYAML::LinkEditData &LEData = Y->LinkEdit;
+ auto ExportsTrie = Obj.getDyldInfoExportsTrie();
+ processExportNode(ExportsTrie.begin(), ExportsTrie.end(), LEData.ExportTrie);
+}
+
+template <typename nlist_t>
+MachOYAML::NListEntry constructNameList(const nlist_t &nlist) {
+ MachOYAML::NListEntry NL;
+ NL.n_strx = nlist.n_strx;
+ NL.n_type = nlist.n_type;
+ NL.n_sect = nlist.n_sect;
+ NL.n_desc = nlist.n_desc;
+ NL.n_value = nlist.n_value;
+ return NL;
+}
+
+void MachODumper::dumpSymbols(std::unique_ptr<MachOYAML::Object> &Y) {
+ MachOYAML::LinkEditData &LEData = Y->LinkEdit;
+
+ for (auto Symbol : Obj.symbols()) {
+ MachOYAML::NListEntry NLE =
+ Obj.is64Bit() ? constructNameList<MachO::nlist_64>(
+ *reinterpret_cast<const MachO::nlist_64 *>(
+ Symbol.getRawDataRefImpl().p))
+ : constructNameList<MachO::nlist>(
+ *reinterpret_cast<const MachO::nlist *>(
+ Symbol.getRawDataRefImpl().p));
+ LEData.NameList.push_back(NLE);
+ }
+
+ StringRef RemainingTable = Obj.getStringTableData();
+ while (RemainingTable.size() > 0) {
+ auto SymbolPair = RemainingTable.split('\0');
+ RemainingTable = SymbolPair.second;
+ if (SymbolPair.first.empty())
+ break;
+ LEData.StringTable.push_back(SymbolPair.first);
+ }
+}
+
+Error macho2yaml(raw_ostream &Out, const object::MachOObjectFile &Obj) {
+ MachODumper Dumper(Obj);
+ Expected<std::unique_ptr<MachOYAML::Object>> YAML = Dumper.dump();
+ if (!YAML)
+ return YAML.takeError();
+
+ yaml::YamlObjectFile YAMLFile;
+ YAMLFile.MachO = std::move(YAML.get());
+
+ yaml::Output Yout(Out);
+ Yout << YAMLFile;
+ return Error::success();
+}
+
+Error macho2yaml(raw_ostream &Out, const object::MachOUniversalBinary &Obj) {
+ yaml::YamlObjectFile YAMLFile;
+ YAMLFile.FatMachO.reset(new MachOYAML::UniversalBinary());
+ MachOYAML::UniversalBinary &YAML = *YAMLFile.FatMachO;
+ YAML.Header.magic = Obj.getMagic();
+ YAML.Header.nfat_arch = Obj.getNumberOfObjects();
+
+ for (auto Slice : Obj.objects()) {
+ MachOYAML::FatArch arch;
+ arch.cputype = Slice.getCPUType();
+ arch.cpusubtype = Slice.getCPUSubType();
+ arch.offset = Slice.getOffset();
+ arch.size = Slice.getSize();
+ arch.align = Slice.getAlign();
+ arch.reserved = Slice.getReserved();
+ YAML.FatArchs.push_back(arch);
+
+ auto SliceObj = Slice.getAsObjectFile();
+ if (!SliceObj)
+ return SliceObj.takeError();
+
+ MachODumper Dumper(*SliceObj.get());
+ Expected<std::unique_ptr<MachOYAML::Object>> YAMLObj = Dumper.dump();
+ if (!YAMLObj)
+ return YAMLObj.takeError();
+ YAML.Slices.push_back(*YAMLObj.get());
+ }
+
+ yaml::Output Yout(Out);
+ Yout << YAML;
+ return Error::success();
+}
+
+std::error_code macho2yaml(raw_ostream &Out, const object::Binary &Binary) {
+ if (const auto *MachOObj = dyn_cast<object::MachOUniversalBinary>(&Binary)) {
+ if (auto Err = macho2yaml(Out, *MachOObj)) {
+ return errorToErrorCode(std::move(Err));
+ }
+ return obj2yaml_error::success;
+ }
+
+ if (const auto *MachOObj = dyn_cast<object::MachOObjectFile>(&Binary)) {
+ if (auto Err = macho2yaml(Out, *MachOObj)) {
+ return errorToErrorCode(std::move(Err));
+ }
+ return obj2yaml_error::success;
+ }
+
+ return obj2yaml_error::unsupported_obj_file_format;
+}
diff --git a/tools/obj2yaml/obj2yaml.cpp b/tools/obj2yaml/obj2yaml.cpp
index ee6284da6e417..3f9373ee17e38 100644
--- a/tools/obj2yaml/obj2yaml.cpp
+++ b/tools/obj2yaml/obj2yaml.cpp
@@ -29,11 +29,15 @@ static std::error_code dumpObject(const ObjectFile &Obj) {
}
static std::error_code dumpInput(StringRef File) {
- ErrorOr<OwningBinary<Binary>> BinaryOrErr = createBinary(File);
- if (std::error_code EC = BinaryOrErr.getError())
- return EC;
+ Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(File);
+ if (!BinaryOrErr)
+ return errorToErrorCode(BinaryOrErr.takeError());
Binary &Binary = *BinaryOrErr.get().getBinary();
+ // Universal MachO is not a subclass of ObjectFile, so it needs to be handled
+ // here with the other binary types.
+ if (Binary.isMachO() || Binary.isMachOUniversalBinary())
+ return macho2yaml(outs(), Binary);
// TODO: If this is an archive, then burst it and dump each entry
if (ObjectFile *Obj = dyn_cast<ObjectFile>(&Binary))
return dumpObject(*Obj);
@@ -46,7 +50,7 @@ cl::opt<std::string> InputFilename(cl::Positional, cl::desc("<input file>"),
int main(int argc, char *argv[]) {
cl::ParseCommandLineOptions(argc, argv);
- sys::PrintStackTraceOnErrorSignal();
+ sys::PrintStackTraceOnErrorSignal(argv[0]);
PrettyStackTraceProgram X(argc, argv);
llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
diff --git a/tools/obj2yaml/obj2yaml.h b/tools/obj2yaml/obj2yaml.h
index 643ab7bc434d3..28c74751c0d95 100644
--- a/tools/obj2yaml/obj2yaml.h
+++ b/tools/obj2yaml/obj2yaml.h
@@ -21,5 +21,7 @@ std::error_code coff2yaml(llvm::raw_ostream &Out,
const llvm::object::COFFObjectFile &Obj);
std::error_code elf2yaml(llvm::raw_ostream &Out,
const llvm::object::ObjectFile &Obj);
+std::error_code macho2yaml(llvm::raw_ostream &Out,
+ const llvm::object::Binary &Obj);
#endif
diff --git a/tools/opt/AnalysisWrappers.cpp b/tools/opt/AnalysisWrappers.cpp
index 4bdc268f8d759..cfdd2cf1582b0 100644
--- a/tools/opt/AnalysisWrappers.cpp
+++ b/tools/opt/AnalysisWrappers.cpp
@@ -71,23 +71,3 @@ char ExternalFunctionsPassedConstants::ID = 0;
static RegisterPass<ExternalFunctionsPassedConstants>
P1("print-externalfnconstants",
"Print external fn callsites passed constants");
-
-namespace {
- struct CallGraphPrinter : public ModulePass {
- static char ID; // Pass ID, replacement for typeid
- CallGraphPrinter() : ModulePass(ID) {}
-
- void getAnalysisUsage(AnalysisUsage &AU) const override {
- AU.setPreservesAll();
- AU.addRequiredTransitive<CallGraphWrapperPass>();
- }
- bool runOnModule(Module &M) override {
- getAnalysis<CallGraphWrapperPass>().print(errs(), &M);
- return false;
- }
- };
-}
-
-char CallGraphPrinter::ID = 0;
-static RegisterPass<CallGraphPrinter>
- P2("print-callgraph", "Print a call graph");
diff --git a/tools/opt/BreakpointPrinter.cpp b/tools/opt/BreakpointPrinter.cpp
index 363a7cd80074e..33b3edcd12378 100644
--- a/tools/opt/BreakpointPrinter.cpp
+++ b/tools/opt/BreakpointPrinter.cpp
@@ -25,7 +25,6 @@ namespace {
struct BreakpointPrinter : public ModulePass {
raw_ostream &Out;
static char ID;
- DITypeIdentifierMap TypeIdentifierMap;
BreakpointPrinter(raw_ostream &out) : ModulePass(ID), Out(out) {}
@@ -37,18 +36,13 @@ struct BreakpointPrinter : public ModulePass {
}
} else if (auto *TY = dyn_cast<DIType>(Context)) {
if (!TY->getName().empty()) {
- getContextName(TY->getScope().resolve(TypeIdentifierMap), N);
+ getContextName(TY->getScope().resolve(), N);
N = N + TY->getName().str() + "::";
}
}
}
bool runOnModule(Module &M) override {
- TypeIdentifierMap.clear();
- NamedMDNode *CU_Nodes = M.getNamedMetadata("llvm.dbg.cu");
- if (CU_Nodes)
- TypeIdentifierMap = generateDITypeIdentifierMap(CU_Nodes);
-
StringSet<> Processed;
if (NamedMDNode *NMD = M.getNamedMetadata("llvm.dbg.sp"))
for (unsigned i = 0, e = NMD->getNumOperands(); i != e; ++i) {
@@ -56,7 +50,7 @@ struct BreakpointPrinter : public ModulePass {
auto *SP = cast_or_null<DISubprogram>(NMD->getOperand(i));
if (!SP)
continue;
- getContextName(SP->getScope().resolve(TypeIdentifierMap), Name);
+ getContextName(SP->getScope().resolve(), Name);
Name = Name + SP->getDisplayName().str();
if (!Name.empty() && Processed.insert(Name).second) {
Out << Name << "\n";
diff --git a/tools/opt/Makefile b/tools/opt/Makefile
deleted file mode 100644
index 2422eb4e40567..0000000000000
--- a/tools/opt/Makefile
+++ /dev/null
@@ -1,17 +0,0 @@
-##===- tools/opt/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 := opt
-LINK_COMPONENTS := bitreader bitwriter asmparser irreader instrumentation scalaropts objcarcopts ipo vectorize all-targets codegen passes
-
-# Support plugins.
-NO_DEAD_STRIP := 1
-
-include $(LEVEL)/Makefile.common
diff --git a/tools/opt/NewPMDriver.cpp b/tools/opt/NewPMDriver.cpp
index 3030d65743af2..f7b2f18d09d90 100644
--- a/tools/opt/NewPMDriver.cpp
+++ b/tools/opt/NewPMDriver.cpp
@@ -15,7 +15,9 @@
#include "NewPMDriver.h"
#include "llvm/ADT/StringRef.h"
+#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Analysis/CGSCCPassManager.h"
+#include "llvm/Analysis/LoopPassManager.h"
#include "llvm/Bitcode/BitcodeWriterPass.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/IRPrintingPasses.h"
@@ -36,6 +38,15 @@ static cl::opt<bool>
DebugPM("debug-pass-manager", cl::Hidden,
cl::desc("Print pass management debugging information"));
+// This flag specifies a textual description of the alias analysis pipeline to
+// use when querying for aliasing information. It only works in concert with
+// the "passes" flag above.
+static cl::opt<std::string>
+ AAPipeline("aa-pipeline",
+ cl::desc("A textual description of the alias analysis "
+ "pipeline for handling managed aliasing queries"),
+ cl::Hidden);
+
bool llvm::runPassPipeline(StringRef Arg0, LLVMContext &Context, Module &M,
TargetMachine *TM, tool_output_file *Out,
StringRef PassPipeline, OutputKind OK,
@@ -44,22 +55,28 @@ bool llvm::runPassPipeline(StringRef Arg0, LLVMContext &Context, Module &M,
bool ShouldPreserveBitcodeUseListOrder) {
PassBuilder PB(TM);
+ // Specially handle the alias analysis manager so that we can register
+ // a custom pipeline of AA passes with it.
+ AAManager AA;
+ if (!PB.parseAAPipeline(AA, AAPipeline)) {
+ errs() << Arg0 << ": unable to parse AA pipeline description.\n";
+ return false;
+ }
+
+ LoopAnalysisManager LAM(DebugPM);
FunctionAnalysisManager FAM(DebugPM);
CGSCCAnalysisManager CGAM(DebugPM);
ModuleAnalysisManager MAM(DebugPM);
+ // Register the AA manager first so that our version is the one used.
+ FAM.registerPass([&] { return std::move(AA); });
+
// Register all the basic analyses with the managers.
PB.registerModuleAnalyses(MAM);
PB.registerCGSCCAnalyses(CGAM);
PB.registerFunctionAnalyses(FAM);
-
- // Cross register the analysis managers through their proxies.
- MAM.registerPass(FunctionAnalysisManagerModuleProxy(FAM));
- MAM.registerPass(CGSCCAnalysisManagerModuleProxy(CGAM));
- CGAM.registerPass(FunctionAnalysisManagerCGSCCProxy(FAM));
- CGAM.registerPass(ModuleAnalysisManagerCGSCCProxy(MAM));
- FAM.registerPass(CGSCCAnalysisManagerFunctionProxy(CGAM));
- FAM.registerPass(ModuleAnalysisManagerFunctionProxy(MAM));
+ PB.registerLoopAnalyses(LAM);
+ PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);
ModulePassManager MPM(DebugPM);
if (VK > VK_NoVerifier)
@@ -92,7 +109,7 @@ bool llvm::runPassPipeline(StringRef Arg0, LLVMContext &Context, Module &M,
cl::PrintOptionValues();
// Now that we have all of the passes ready, run them.
- MPM.run(M, &MAM);
+ MPM.run(M, MAM);
// Declare success.
if (OK != OK_NoOutput)
diff --git a/tools/opt/NewPMDriver.h b/tools/opt/NewPMDriver.h
index 349a7b1267f8e..66a470d49ba8b 100644
--- a/tools/opt/NewPMDriver.h
+++ b/tools/opt/NewPMDriver.h
@@ -21,9 +21,8 @@
#ifndef LLVM_TOOLS_OPT_NEWPMDRIVER_H
#define LLVM_TOOLS_OPT_NEWPMDRIVER_H
-#include "llvm/ADT/StringRef.h"
-
namespace llvm {
+class StringRef;
class LLVMContext;
class Module;
class TargetMachine;
diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp
index fe1605aa8436f..0a4ce1dfe8efa 100644
--- a/tools/opt/opt.cpp
+++ b/tools/opt/opt.cpp
@@ -96,12 +96,16 @@ static cl::opt<bool>
OutputAssembly("S", cl::desc("Write output as LLVM assembly"));
static cl::opt<bool>
-NoVerify("disable-verify", cl::desc("Do not verify result module"), cl::Hidden);
+NoVerify("disable-verify", cl::desc("Do not run the verifier"), cl::Hidden);
static cl::opt<bool>
VerifyEach("verify-each", cl::desc("Verify after each transform"));
static cl::opt<bool>
+ DisableDITypeMap("disable-debug-info-type-map",
+ cl::desc("Don't use a uniquing type map for debug info"));
+
+static cl::opt<bool>
StripDebug("strip-debug",
cl::desc("Strip debugger symbol info from translation unit"));
@@ -136,6 +140,10 @@ static cl::opt<bool>
OptLevelO3("O3",
cl::desc("Optimization level 3. Similar to clang -O3"));
+static cl::opt<unsigned>
+CodeGenOptLevel("codegen-opt-level",
+ cl::desc("Override optimization level for codegen hooks"));
+
static cl::opt<std::string>
TargetTriple("mtriple", cl::desc("Override target triple for module"));
@@ -158,6 +166,12 @@ DisableSLPVectorization("disable-slp-vectorization",
cl::desc("Disable the slp vectorization pass"),
cl::init(false));
+static cl::opt<bool> EmitSummaryIndex("module-summary",
+ cl::desc("Emit module summary index"),
+ cl::init(false));
+
+static cl::opt<bool> EmitModuleHash("module-hash", cl::desc("Emit module hash"),
+ cl::init(false));
static cl::opt<bool>
DisableSimplifyLibCalls("disable-simplify-libcalls",
@@ -196,6 +210,16 @@ static cl::opt<bool>
cl::desc("Run all passes twice, re-using the same pass manager."),
cl::init(false), cl::Hidden);
+static cl::opt<bool> DiscardValueNames(
+ "discard-value-names",
+ cl::desc("Discard names from Value (other than GlobalValue)."),
+ cl::init(false), cl::Hidden);
+
+static cl::opt<bool> PassRemarksWithHotness(
+ "pass-remarks-with-hotness",
+ cl::desc("With PGO, include profile count in optimization remarks"),
+ cl::Hidden);
+
static inline void addPass(legacy::PassManagerBase &PM, Pass *P) {
// Add the pass to the pass manager...
PM.add(P);
@@ -211,8 +235,10 @@ static inline void addPass(legacy::PassManagerBase &PM, Pass *P) {
/// OptLevel - Optimization Level
static void AddOptimizationPasses(legacy::PassManagerBase &MPM,
legacy::FunctionPassManager &FPM,
- unsigned OptLevel, unsigned SizeLevel) {
- FPM.add(createVerifierPass()); // Verify that input is correct
+ TargetMachine *TM, unsigned OptLevel,
+ unsigned SizeLevel) {
+ if (!NoVerify || VerifyEach)
+ FPM.add(createVerifierPass()); // Verify that input is correct
PassManagerBuilder Builder;
Builder.OptLevel = OptLevel;
@@ -240,6 +266,14 @@ static void AddOptimizationPasses(legacy::PassManagerBase &MPM,
Builder.SLPVectorize =
DisableSLPVectorization ? false : OptLevel > 1 && SizeLevel < 2;
+ // Add target-specific passes that need to run as early as possible.
+ if (TM)
+ Builder.addExtension(
+ PassManagerBuilder::EP_EarlyAsPossible,
+ [&](const PassManagerBuilder &, legacy::PassManagerBase &PM) {
+ TM->addEarlyAsPossiblePasses(PM);
+ });
+
Builder.populateFunctionPassManager(FPM);
Builder.populateModulePassManager(MPM);
}
@@ -260,6 +294,8 @@ static void AddStandardLinkPasses(legacy::PassManagerBase &PM) {
//
static CodeGenOpt::Level GetCodeGenOptLevel() {
+ if (CodeGenOptLevel.getNumOccurrences())
+ return static_cast<CodeGenOpt::Level>(unsigned(CodeGenOptLevel));
if (OptLevelO1)
return CodeGenOpt::Less;
if (OptLevelO2)
@@ -281,10 +317,9 @@ static TargetMachine* GetTargetMachine(Triple TheTriple, StringRef CPUStr,
return nullptr;
}
- return TheTarget->createTargetMachine(TheTriple.getTriple(),
- CPUStr, FeaturesStr, Options,
- RelocModel, CMModel,
- GetCodeGenOptLevel());
+ return TheTarget->createTargetMachine(TheTriple.getTriple(), CPUStr,
+ FeaturesStr, Options, getRelocModel(),
+ CMModel, GetCodeGenOptLevel());
}
#ifdef LINK_POLLY_INTO_TOOLS
@@ -297,14 +332,14 @@ void initializePollyPasses(llvm::PassRegistry &Registry);
// main for opt
//
int main(int argc, char **argv) {
- sys::PrintStackTraceOnErrorSignal();
+ sys::PrintStackTraceOnErrorSignal(argv[0]);
llvm::PrettyStackTraceProgram X(argc, argv);
// Enable debug stream buffering.
EnableDebugBuffering = true;
llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
- LLVMContext &Context = getGlobalContext();
+ LLVMContext Context;
InitializeAllTargets();
InitializeAllTargetMCs();
@@ -329,7 +364,12 @@ int main(int argc, char **argv) {
initializeRewriteSymbolsPass(Registry);
initializeWinEHPreparePass(Registry);
initializeDwarfEHPreparePass(Registry);
+ initializeSafeStackPass(Registry);
initializeSjLjEHPreparePass(Registry);
+ initializePreISelIntrinsicLoweringLegacyPassPass(Registry);
+ initializeGlobalMergePass(Registry);
+ initializeInterleavedAccessPass(Registry);
+ initializeUnreachableBlockElimLegacyPassPass(Registry);
#ifdef LINK_POLLY_INTO_TOOLS
polly::initializePollyPasses(Registry);
@@ -345,6 +385,13 @@ int main(int argc, char **argv) {
SMDiagnostic Err;
+ Context.setDiscardValueNames(DiscardValueNames);
+ if (!DisableDITypeMap)
+ Context.enableDebugTypeODRUniquing();
+
+ if (PassRemarksWithHotness)
+ Context.setDiagnosticHotnessRequested(true);
+
// Load the input module...
std::unique_ptr<Module> M = parseIRFile(InputFilename, Err, Context);
@@ -491,27 +538,27 @@ int main(int argc, char **argv) {
}
if (OptLevelO1 && OptLevelO1.getPosition() < PassList.getPosition(i)) {
- AddOptimizationPasses(Passes, *FPasses, 1, 0);
+ AddOptimizationPasses(Passes, *FPasses, TM.get(), 1, 0);
OptLevelO1 = false;
}
if (OptLevelO2 && OptLevelO2.getPosition() < PassList.getPosition(i)) {
- AddOptimizationPasses(Passes, *FPasses, 2, 0);
+ AddOptimizationPasses(Passes, *FPasses, TM.get(), 2, 0);
OptLevelO2 = false;
}
if (OptLevelOs && OptLevelOs.getPosition() < PassList.getPosition(i)) {
- AddOptimizationPasses(Passes, *FPasses, 2, 1);
+ AddOptimizationPasses(Passes, *FPasses, TM.get(), 2, 1);
OptLevelOs = false;
}
if (OptLevelOz && OptLevelOz.getPosition() < PassList.getPosition(i)) {
- AddOptimizationPasses(Passes, *FPasses, 2, 2);
+ AddOptimizationPasses(Passes, *FPasses, TM.get(), 2, 2);
OptLevelOz = false;
}
if (OptLevelO3 && OptLevelO3.getPosition() < PassList.getPosition(i)) {
- AddOptimizationPasses(Passes, *FPasses, 3, 0);
+ AddOptimizationPasses(Passes, *FPasses, TM.get(), 3, 0);
OptLevelO3 = false;
}
@@ -563,21 +610,21 @@ int main(int argc, char **argv) {
}
if (OptLevelO1)
- AddOptimizationPasses(Passes, *FPasses, 1, 0);
+ AddOptimizationPasses(Passes, *FPasses, TM.get(), 1, 0);
if (OptLevelO2)
- AddOptimizationPasses(Passes, *FPasses, 2, 0);
+ AddOptimizationPasses(Passes, *FPasses, TM.get(), 2, 0);
if (OptLevelOs)
- AddOptimizationPasses(Passes, *FPasses, 2, 1);
+ AddOptimizationPasses(Passes, *FPasses, TM.get(), 2, 1);
if (OptLevelOz)
- AddOptimizationPasses(Passes, *FPasses, 2, 2);
+ AddOptimizationPasses(Passes, *FPasses, TM.get(), 2, 2);
if (OptLevelO3)
- AddOptimizationPasses(Passes, *FPasses, 3, 0);
+ AddOptimizationPasses(Passes, *FPasses, TM.get(), 3, 0);
- if (OptLevelO1 || OptLevelO2 || OptLevelOs || OptLevelOz || OptLevelO3) {
+ if (FPasses) {
FPasses->doInitialization();
for (Function &F : *M)
FPasses->run(F);
@@ -605,10 +652,15 @@ int main(int argc, char **argv) {
BOS = make_unique<raw_svector_ostream>(Buffer);
OS = BOS.get();
}
- if (OutputAssembly)
+ if (OutputAssembly) {
+ if (EmitSummaryIndex)
+ report_fatal_error("Text output is incompatible with -module-summary");
+ if (EmitModuleHash)
+ report_fatal_error("Text output is incompatible with -module-hash");
Passes.add(createPrintModulePass(*OS, "", PreserveAssemblyUseListOrder));
- else
- Passes.add(createBitcodeWriterPass(*OS, PreserveBitcodeUseListOrder));
+ } else
+ Passes.add(createBitcodeWriterPass(*OS, PreserveBitcodeUseListOrder,
+ EmitSummaryIndex, EmitModuleHash));
}
// Before executing passes, print the final values of the LLVM options.
diff --git a/tools/sancov/Makefile b/tools/sancov/Makefile
deleted file mode 100644
index 7dba1a7a594a8..0000000000000
--- a/tools/sancov/Makefile
+++ /dev/null
@@ -1,18 +0,0 @@
-##===- 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
index a07cdbe097a3b..55b03709dde34 100644
--- a/tools/sancov/sancov.cc
+++ b/tools/sancov/sancov.cc
@@ -11,10 +11,11 @@
// coverage.
//===----------------------------------------------------------------------===//
#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/Twine.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/MCDisassembler/MCDisassembler.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCInstPrinter.h"
#include "llvm/MC/MCInstrAnalysis.h"
@@ -24,16 +25,20 @@
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/Binary.h"
+#include "llvm/Object/ELFObjectFile.h"
#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/Casting.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/MD5.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/PrettyStackTrace.h"
+#include "llvm/Support/Regex.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/SpecialCaseList.h"
#include "llvm/Support/TargetRegistry.h"
@@ -41,9 +46,11 @@
#include "llvm/Support/ToolOutputFile.h"
#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
#include <set>
#include <stdio.h>
#include <string>
+#include <utility>
#include <vector>
using namespace llvm;
@@ -54,29 +61,34 @@ namespace {
enum ActionType {
PrintAction,
+ PrintCovPointsAction,
CoveredFunctionsAction,
- NotCoveredFunctionsAction
+ NotCoveredFunctionsAction,
+ HtmlReportAction,
+ StatsAction
};
cl::opt<ActionType> Action(
cl::desc("Action (required)"), cl::Required,
cl::values(clEnumValN(PrintAction, "print", "Print coverage addresses"),
+ clEnumValN(PrintCovPointsAction, "print-coverage-pcs",
+ "Print coverage instrumentation points addresses."),
clEnumValN(CoveredFunctionsAction, "covered-functions",
"Print all covered funcions."),
clEnumValN(NotCoveredFunctionsAction, "not-covered-functions",
"Print all not covered funcions."),
+ clEnumValN(HtmlReportAction, "html-report",
+ "Print HTML coverage report."),
+ clEnumValN(StatsAction, "print-coverage-stats",
+ "Print coverage statistics."),
clEnumValEnd));
-static cl::list<std::string> ClInputFiles(cl::Positional, cl::OneOrMore,
- cl::desc("<filenames...>"));
+static cl::list<std::string>
+ ClInputFiles(cl::Positional, cl::OneOrMore,
+ cl::desc("(<binary file>|<.sancov file>)..."));
-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<bool> ClDemangle("demangle", cl::init(true),
+ cl::desc("Print demangled function name."));
static cl::opt<std::string> ClStripPathPrefix(
"strip_path_prefix", cl::init(""),
@@ -90,7 +102,9 @@ 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_*";
+static const char *const DefaultBlacklistStr = "fun:__sanitizer_.*\n"
+ "src:/usr/include/.*\n"
+ "src:.*/libc\\+\\+/.*\n";
// --------- FORMAT SPECIFICATION ---------
@@ -103,7 +117,12 @@ static const uint32_t BinCoverageMagic = 0xC0BFFFFF;
static const uint32_t Bitness32 = 0xFFFFFF32;
static const uint32_t Bitness64 = 0xFFFFFF64;
-// ---------
+// --------- ERROR HANDLING ---------
+
+static void Fail(const llvm::Twine &E) {
+ errs() << "Error: " << E << "\n";
+ exit(1);
+}
static void FailIfError(std::error_code Error) {
if (!Error)
@@ -116,11 +135,21 @@ template <typename T> static void FailIfError(const ErrorOr<T> &E) {
FailIfError(E.getError());
}
-static void FailIfNotEmpty(const std::string &E) {
- if (E.empty())
+static void FailIfError(Error Err) {
+ if (Err) {
+ logAllUnhandledErrors(std::move(Err), errs(), "Error: ");
+ exit(1);
+ }
+}
+
+template <typename T> static void FailIfError(Expected<T> &E) {
+ FailIfError(E.takeError());
+}
+
+static void FailIfNotEmpty(const llvm::Twine &E) {
+ if (E.str().empty())
return;
- errs() << "Error: " << E << "\n";
- exit(1);
+ Fail(E);
}
template <typename T>
@@ -128,8 +157,24 @@ static void FailIfEmpty(const std::unique_ptr<T> &Ptr,
const std::string &Message) {
if (Ptr.get())
return;
- errs() << "Error: " << Message << "\n";
- exit(1);
+ Fail(Message);
+}
+
+// ---------
+
+// Produces std::map<K, std::vector<E>> grouping input
+// elements by FuncTy result.
+template <class RangeTy, class FuncTy>
+static inline auto group_by(const RangeTy &R, FuncTy F)
+ -> std::map<typename std::decay<decltype(F(*R.begin()))>::type,
+ std::vector<typename std::decay<decltype(*R.begin())>::type>> {
+ std::map<typename std::decay<decltype(F(*R.begin()))>::type,
+ std::vector<typename std::decay<decltype(*R.begin())>::type>>
+ Result;
+ for (const auto &E : R) {
+ Result[F(E)].push_back(E);
+ }
+ return Result;
}
template <typename T>
@@ -149,8 +194,18 @@ struct FileLoc {
uint32_t Line;
};
-struct FunctionLoc {
- bool operator<(const FunctionLoc &RHS) const {
+struct FileFn {
+ bool operator<(const FileFn &RHS) const {
+ return std::tie(FileName, FunctionName) <
+ std::tie(RHS.FileName, RHS.FunctionName);
+ }
+
+ std::string FileName;
+ std::string FunctionName;
+};
+
+struct FnLoc {
+ bool operator<(const FnLoc &RHS) const {
return std::tie(Loc, FunctionName) < std::tie(RHS.Loc, RHS.FunctionName);
}
@@ -167,53 +222,98 @@ std::string stripPathPrefix(std::string 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;
-
+static std::unique_ptr<symbolize::LLVMSymbolizer> createSymbolizer() {
symbolize::LLVMSymbolizer::Options SymbolizerOptions;
SymbolizerOptions.Demangle = ClDemangle;
SymbolizerOptions.UseSymbolTable = true;
- symbolize::LLVMSymbolizer Symbolizer(SymbolizerOptions);
+ return std::unique_ptr<symbolize::LLVMSymbolizer>(
+ new symbolize::LLVMSymbolizer(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;
- }
+// A DILineInfo with address.
+struct AddrInfo : public DILineInfo {
+ uint64_t Addr;
+
+ AddrInfo(const DILineInfo &DI, uint64_t Addr) : DILineInfo(DI), Addr(Addr) {
+ FileName = normalizeFilename(FileName);
}
- return Fns;
-}
+private:
+ static std::string normalizeFilename(const std::string &FileName) {
+ SmallString<256> S(FileName);
+ sys::path::remove_dots(S, /* remove_dot_dot */ true);
+ return S.str().str();
+ }
+};
-// 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);
+class Blacklists {
+public:
+ Blacklists()
+ : DefaultBlacklist(createDefaultBlacklist()),
+ UserBlacklist(createUserBlacklist()) {}
+
+ // AddrInfo contains normalized filename. It is important to check it rather
+ // than DILineInfo.
+ bool isBlacklisted(const AddrInfo &AI) {
+ if (DefaultBlacklist && DefaultBlacklist->inSection("fun", AI.FunctionName))
+ return true;
+ if (DefaultBlacklist && DefaultBlacklist->inSection("src", AI.FileName))
+ return true;
+ if (UserBlacklist && UserBlacklist->inSection("fun", AI.FunctionName))
+ return true;
+ if (UserBlacklist && UserBlacklist->inSection("src", AI.FileName))
+ return true;
+ return false;
+ }
- std::set<FunctionLoc> Result;
- std::string LastFileName;
- std::set<std::string> ProcessedFunctions;
+private:
+ static std::unique_ptr<SpecialCaseList> createDefaultBlacklist() {
+ if (!ClUseDefaultBlacklist)
+ return std::unique_ptr<SpecialCaseList>();
+ std::unique_ptr<MemoryBuffer> MB =
+ MemoryBuffer::getMemBuffer(DefaultBlacklistStr);
+ std::string Error;
+ auto Blacklist = SpecialCaseList::create(MB.get(), Error);
+ FailIfNotEmpty(Error);
+ return Blacklist;
+ }
- for (const auto &P : Fns) {
- std::string FileName = P.first.FileName;
- std::string FunctionName = P.second;
+ static std::unique_ptr<SpecialCaseList> createUserBlacklist() {
+ if (ClBlacklist.empty())
+ return std::unique_ptr<SpecialCaseList>();
- if (LastFileName != FileName)
- ProcessedFunctions.clear();
- LastFileName = FileName;
+ return SpecialCaseList::createOrDie({{ClBlacklist}});
+ }
+ std::unique_ptr<SpecialCaseList> DefaultBlacklist;
+ std::unique_ptr<SpecialCaseList> UserBlacklist;
+};
- if (!ProcessedFunctions.insert(FunctionName).second)
- continue;
+// Collect all debug info for given addresses.
+static std::vector<AddrInfo> getAddrInfo(const std::string &ObjectFile,
+ const std::set<uint64_t> &Addrs,
+ bool InlinedCode) {
+ std::vector<AddrInfo> Result;
+ auto Symbolizer(createSymbolizer());
+ Blacklists B;
- Result.insert(FunctionLoc{P.first, P.second});
+ for (auto Addr : Addrs) {
+ auto LineInfo = Symbolizer->symbolizeCode(ObjectFile, Addr);
+ FailIfError(LineInfo);
+ auto LineAddrInfo = AddrInfo(*LineInfo, Addr);
+ if (B.isBlacklisted(LineAddrInfo))
+ continue;
+ Result.push_back(LineAddrInfo);
+ if (InlinedCode) {
+ auto InliningInfo = Symbolizer->symbolizeInlinedCode(ObjectFile, Addr);
+ FailIfError(InliningInfo);
+ for (uint32_t I = 0; I < InliningInfo->getNumberOfFrames(); ++I) {
+ auto FrameInfo = InliningInfo->getFrame(I);
+ auto FrameAddrInfo = AddrInfo(FrameInfo, Addr);
+ if (B.isBlacklisted(FrameAddrInfo))
+ continue;
+ Result.push_back(FrameAddrInfo);
+ }
+ }
}
return Result;
@@ -226,22 +326,20 @@ 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);
+ Expected<uint64_t> AddressOrErr = Symbol.getAddress();
+ FailIfError(errorToErrorCode(AddressOrErr.takeError()));
- ErrorOr<StringRef> NameOrErr = Symbol.getName();
- FailIfError(NameOrErr);
+ Expected<StringRef> NameOrErr = Symbol.getName();
+ FailIfError(errorToErrorCode(NameOrErr.takeError()));
StringRef Name = NameOrErr.get();
if (Name == "__sanitizer_cov" || Name == "__sanitizer_cov_with_check" ||
Name == "__sanitizer_cov_trace_func_enter") {
- Result.insert(AddressOrErr.get());
+ if (!(Symbol.getFlags() & object::BasicSymbolRef::SF_Undefined))
+ Result.insert(AddressOrErr.get());
}
}
- if (Result.empty())
- FailIfNotEmpty("__sanitizer_cov* functions not found");
-
return Result;
}
@@ -284,8 +382,10 @@ static void getObjectCoveragePoints(const object::ObjectFile &O,
FailIfEmpty(MIA, "no instruction analysis info for target " + TripleName);
auto SanCovAddrs = findSanitizerCovFunctions(O);
+ if (SanCovAddrs.empty())
+ Fail("__sanitizer_cov* functions not found");
- for (const auto Section : O.sections()) {
+ for (object::SectionRef Section : O.sections()) {
if (Section.isVirtual() || !Section.isText()) // llvm-objdump does the same.
continue;
uint64_t SectionAddr = Section.getAddress();
@@ -293,9 +393,6 @@ static void getObjectCoveragePoints(const object::ObjectFile &O,
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()),
@@ -310,98 +407,170 @@ static void getObjectCoveragePoints(const object::ObjectFile &O,
Size = 1;
continue;
}
+ uint64_t Addr = Index + SectionAddr;
+ // Sanitizer coverage uses the address of the next instruction - 1.
+ uint64_t CovPoint = Addr + Size - 1;
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);
- }
- }
+ MIA->evaluateBranch(Inst, SectionAddr + Index, Size, Target) &&
+ SanCovAddrs.find(Target) != SanCovAddrs.end())
+ Addrs->insert(CovPoint);
}
}
}
-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);
+static void
+visitObjectFiles(const object::Archive &A,
+ function_ref<void(const object::ObjectFile &)> Fn) {
+ Error Err;
+ for (auto &C : A.children(Err)) {
+ Expected<std::unique_ptr<object::Binary>> ChildOrErr = C.getAsBinary();
+ FailIfError(errorToErrorCode(ChildOrErr.takeError()));
+ if (auto *O = dyn_cast<object::ObjectFile>(&*ChildOrErr.get()))
+ Fn(*O);
else
FailIfError(object::object_error::invalid_file_type);
}
+ FailIfError(std::move(Err));
}
-// 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 =
+static void
+visitObjectFiles(const std::string &FileName,
+ function_ref<void(const object::ObjectFile &)> Fn) {
+ Expected<object::OwningBinary<object::Binary>> BinaryOrErr =
object::createBinary(FileName);
- FailIfError(BinaryOrErr);
+ if (!BinaryOrErr)
+ FailIfError(errorToErrorCode(BinaryOrErr.takeError()));
object::Binary &Binary = *BinaryOrErr.get().getBinary();
if (object::Archive *A = dyn_cast<object::Archive>(&Binary))
- getArchiveCoveragePoints(*A, &Result);
+ visitObjectFiles(*A, Fn);
else if (object::ObjectFile *O = dyn_cast<object::ObjectFile>(&Binary))
- getObjectCoveragePoints(*O, &Result);
+ Fn(*O);
else
FailIfError(object::object_error::invalid_file_type);
+}
+std::set<uint64_t> findSanitizerCovFunctions(const std::string &FileName) {
+ std::set<uint64_t> Result;
+ visitObjectFiles(FileName, [&](const object::ObjectFile &O) {
+ auto Addrs = findSanitizerCovFunctions(O);
+ Result.insert(Addrs.begin(), Addrs.end());
+ });
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;
+// 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(const std::string &FileName) {
+ std::set<uint64_t> Result;
+ visitObjectFiles(FileName, [&](const object::ObjectFile &O) {
+ getObjectCoveragePoints(O, &Result);
+ });
+ return Result;
+}
+
+static void printCovPoints(const std::string &ObjFile, raw_ostream &OS) {
+ for (uint64_t Addr : getCoveragePoints(ObjFile)) {
+ OS << "0x";
+ OS.write_hex(Addr);
+ OS << "\n";
+ }
+}
+
+static std::string escapeHtml(const std::string &S) {
+ std::string Result;
+ Result.reserve(S.size());
+ for (char Ch : S) {
+ switch (Ch) {
+ case '&':
+ Result.append("&amp;");
+ break;
+ case '\'':
+ Result.append("&apos;");
+ break;
+ case '"':
+ Result.append("&quot;");
+ break;
+ case '<':
+ Result.append("&lt;");
+ break;
+ case '>':
+ Result.append("&gt;");
+ break;
+ default:
+ Result.push_back(Ch);
+ break;
+ }
+ }
+ return Result;
}
-static std::unique_ptr<SpecialCaseList> createUserBlacklist() {
- if (ClBlacklist.empty())
- return std::unique_ptr<SpecialCaseList>();
+// Adds leading zeroes wrapped in 'lz' style.
+// Leading zeroes help locate 000% coverage.
+static std::string formatHtmlPct(size_t Pct) {
+ Pct = std::max(std::size_t{0}, std::min(std::size_t{100}, Pct));
- return SpecialCaseList::createOrDie({{ClBlacklist}});
+ std::string Num = std::to_string(Pct);
+ std::string Zeroes(3 - Num.size(), '0');
+ if (!Zeroes.empty())
+ Zeroes = "<span class='lz'>" + Zeroes + "</span>";
+
+ return Zeroes + Num;
}
-static void printFunctionLocs(const std::set<FunctionLoc> &FnLocs,
- raw_ostream &OS) {
- std::unique_ptr<SpecialCaseList> DefaultBlacklist = createDefaultBlacklist();
- std::unique_ptr<SpecialCaseList> UserBlacklist = createUserBlacklist();
+static std::string anchorName(const std::string &Anchor) {
+ llvm::MD5 Hasher;
+ llvm::MD5::MD5Result Hash;
+ Hasher.update(Anchor);
+ Hasher.final(Hash);
- 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;
+ SmallString<32> HexString;
+ llvm::MD5::stringifyResult(Hash, HexString);
+ return HexString.str().str();
+}
- OS << stripPathPrefix(FnLoc.Loc.FileName) << ":" << FnLoc.Loc.Line << " "
- << FnLoc.FunctionName << "\n";
+static ErrorOr<bool> isCoverageFile(const std::string &FileName) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
+ MemoryBuffer::getFile(FileName);
+ if (!BufOrErr) {
+ errs() << "Warning: " << BufOrErr.getError().message() << "("
+ << BufOrErr.getError().value()
+ << "), filename: " << llvm::sys::path::filename(FileName) << "\n";
+ return BufOrErr.getError();
+ }
+ std::unique_ptr<MemoryBuffer> Buf = std::move(BufOrErr.get());
+ if (Buf->getBufferSize() < 8) {
+ return false;
}
+ const FileHeader *Header =
+ reinterpret_cast<const FileHeader *>(Buf->getBufferStart());
+ return Header->Magic == BinCoverageMagic;
+}
+
+struct CoverageStats {
+ CoverageStats() : AllPoints(0), CovPoints(0), AllFns(0), CovFns(0) {}
+
+ size_t AllPoints;
+ size_t CovPoints;
+ size_t AllFns;
+ size_t CovFns;
+};
+
+static raw_ostream &operator<<(raw_ostream &OS, const CoverageStats &Stats) {
+ OS << "all-edges: " << Stats.AllPoints << "\n";
+ OS << "cov-edges: " << Stats.CovPoints << "\n";
+ OS << "all-functions: " << Stats.AllFns << "\n";
+ OS << "cov-functions: " << Stats.CovFns << "\n";
+ return OS;
}
class CoverageData {
- public:
+public:
// Read single file coverage data.
- static ErrorOr<std::unique_ptr<CoverageData>> read(std::string FileName) {
+ static ErrorOr<std::unique_ptr<CoverageData>>
+ read(const std::string &FileName) {
ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
MemoryBuffer::getFile(FileName);
if (!BufOrErr)
@@ -471,37 +640,570 @@ class CoverageData {
}
}
+protected:
+ explicit CoverageData(std::unique_ptr<std::set<uint64_t>> Addrs)
+ : Addrs(std::move(Addrs)) {}
+
+ friend class CoverageDataWithObjectFile;
+
+ std::unique_ptr<std::set<uint64_t>> Addrs;
+};
+
+// Coverage data translated into source code line-level information.
+// Fetches debug info in constructor and calculates various information per
+// request.
+class SourceCoverageData {
+public:
+ enum LineStatus {
+ // coverage information for the line is not available.
+ // default value in maps.
+ UNKNOWN = 0,
+ // the line is fully covered.
+ COVERED = 1,
+ // the line is fully uncovered.
+ NOT_COVERED = 2,
+ // some points in the line a covered, some are not.
+ MIXED = 3
+ };
+
+ SourceCoverageData(std::string ObjectFile, const std::set<uint64_t> &Addrs)
+ : AllCovPoints(getCoveragePoints(ObjectFile)) {
+ if (!std::includes(AllCovPoints.begin(), AllCovPoints.end(), Addrs.begin(),
+ Addrs.end())) {
+ Fail("Coverage points in binary and .sancov file do not match.");
+ }
+
+ AllAddrInfo = getAddrInfo(ObjectFile, AllCovPoints, true);
+ CovAddrInfo = getAddrInfo(ObjectFile, Addrs, true);
+ }
+
+ // Compute number of coverage points hit/total in a file.
+ // file_name -> <coverage, all_coverage>
+ std::map<std::string, std::pair<size_t, size_t>> computeFileCoverage() {
+ std::map<std::string, std::pair<size_t, size_t>> FileCoverage;
+ auto AllCovPointsByFile =
+ group_by(AllAddrInfo, [](const AddrInfo &AI) { return AI.FileName; });
+ auto CovPointsByFile =
+ group_by(CovAddrInfo, [](const AddrInfo &AI) { return AI.FileName; });
+
+ for (const auto &P : AllCovPointsByFile) {
+ const std::string &FileName = P.first;
+
+ FileCoverage[FileName] =
+ std::make_pair(CovPointsByFile[FileName].size(),
+ AllCovPointsByFile[FileName].size());
+ }
+ return FileCoverage;
+ }
+
+ // line_number -> line_status.
+ typedef std::map<int, LineStatus> LineStatusMap;
+ // file_name -> LineStatusMap
+ typedef std::map<std::string, LineStatusMap> FileLineStatusMap;
+
+ // fills in the {file_name -> {line_no -> status}} map.
+ FileLineStatusMap computeLineStatusMap() {
+ FileLineStatusMap StatusMap;
+
+ auto AllLocs = group_by(AllAddrInfo, [](const AddrInfo &AI) {
+ return FileLoc{AI.FileName, AI.Line};
+ });
+ auto CovLocs = group_by(CovAddrInfo, [](const AddrInfo &AI) {
+ return FileLoc{AI.FileName, AI.Line};
+ });
+
+ for (const auto &P : AllLocs) {
+ const FileLoc &Loc = P.first;
+ auto I = CovLocs.find(Loc);
+
+ if (I == CovLocs.end()) {
+ StatusMap[Loc.FileName][Loc.Line] = NOT_COVERED;
+ } else {
+ StatusMap[Loc.FileName][Loc.Line] =
+ (I->second.size() == P.second.size()) ? COVERED : MIXED;
+ }
+ }
+ return StatusMap;
+ }
+
+ std::set<FileFn> computeAllFunctions() const {
+ std::set<FileFn> Fns;
+ for (const auto &AI : AllAddrInfo) {
+ Fns.insert(FileFn{AI.FileName, AI.FunctionName});
+ }
+ return Fns;
+ }
+
+ std::set<FileFn> computeCoveredFunctions() const {
+ std::set<FileFn> Fns;
+ auto CovFns = group_by(CovAddrInfo, [](const AddrInfo &AI) {
+ return FileFn{AI.FileName, AI.FunctionName};
+ });
+
+ for (const auto &P : CovFns) {
+ Fns.insert(P.first);
+ }
+ return Fns;
+ }
+
+ std::set<FileFn> computeNotCoveredFunctions() const {
+ std::set<FileFn> Fns;
+
+ auto AllFns = group_by(AllAddrInfo, [](const AddrInfo &AI) {
+ return FileFn{AI.FileName, AI.FunctionName};
+ });
+ auto CovFns = group_by(CovAddrInfo, [](const AddrInfo &AI) {
+ return FileFn{AI.FileName, AI.FunctionName};
+ });
+
+ for (const auto &P : AllFns) {
+ if (CovFns.find(P.first) == CovFns.end()) {
+ Fns.insert(P.first);
+ }
+ }
+ return Fns;
+ }
+
+ // Compute % coverage for each function.
+ std::map<FileFn, int> computeFunctionsCoverage() const {
+ std::map<FileFn, int> FnCoverage;
+ auto AllFns = group_by(AllAddrInfo, [](const AddrInfo &AI) {
+ return FileFn{AI.FileName, AI.FunctionName};
+ });
+
+ auto CovFns = group_by(CovAddrInfo, [](const AddrInfo &AI) {
+ return FileFn{AI.FileName, AI.FunctionName};
+ });
+
+ for (const auto &P : AllFns) {
+ FileFn F = P.first;
+ FnCoverage[F] = CovFns[F].size() * 100 / P.second.size();
+ }
+
+ return FnCoverage;
+ }
+
+ typedef std::map<FileLoc, std::set<std::string>> FunctionLocs;
+ // finds first line number in a file for each function.
+ FunctionLocs resolveFunctions(const std::set<FileFn> &Fns) const {
+ std::vector<AddrInfo> FnAddrs;
+ for (const auto &AI : AllAddrInfo) {
+ if (Fns.find(FileFn{AI.FileName, AI.FunctionName}) != Fns.end())
+ FnAddrs.push_back(AI);
+ }
+
+ auto GroupedAddrs = group_by(FnAddrs, [](const AddrInfo &AI) {
+ return FnLoc{FileLoc{AI.FileName, AI.Line}, AI.FunctionName};
+ });
+
+ FunctionLocs Result;
+ std::string LastFileName;
+ std::set<std::string> ProcessedFunctions;
+
+ for (const auto &P : GroupedAddrs) {
+ const FnLoc &Loc = P.first;
+ std::string FileName = Loc.Loc.FileName;
+ std::string FunctionName = Loc.FunctionName;
+
+ if (LastFileName != FileName)
+ ProcessedFunctions.clear();
+ LastFileName = FileName;
+
+ if (!ProcessedFunctions.insert(FunctionName).second)
+ continue;
+
+ auto FLoc = FileLoc{FileName, Loc.Loc.Line};
+ Result[FLoc].insert(FunctionName);
+ }
+ return Result;
+ }
+
+ std::set<std::string> files() const {
+ std::set<std::string> Files;
+ for (const auto &AI : AllAddrInfo) {
+ Files.insert(AI.FileName);
+ }
+ return Files;
+ }
+
+ void collectStats(CoverageStats *Stats) const {
+ Stats->AllPoints += AllCovPoints.size();
+ Stats->AllFns += computeAllFunctions().size();
+ Stats->CovFns += computeCoveredFunctions().size();
+ }
+
+private:
+ const std::set<uint64_t> AllCovPoints;
+
+ std::vector<AddrInfo> AllAddrInfo;
+ std::vector<AddrInfo> CovAddrInfo;
+};
+
+static void printFunctionLocs(const SourceCoverageData::FunctionLocs &FnLocs,
+ raw_ostream &OS) {
+ for (const auto &Fns : FnLocs) {
+ for (const auto &Fn : Fns.second) {
+ OS << stripPathPrefix(Fns.first.FileName) << ":" << Fns.first.Line << " "
+ << Fn << "\n";
+ }
+ }
+}
+
+// Holder for coverage data + filename of corresponding object file.
+class CoverageDataWithObjectFile : public CoverageData {
+public:
+ static ErrorOr<std::unique_ptr<CoverageDataWithObjectFile>>
+ readAndMerge(const std::string &ObjectFile,
+ const std::vector<std::string> &FileNames) {
+ auto MergedDataOrError = CoverageData::readAndMerge(FileNames);
+ if (!MergedDataOrError)
+ return MergedDataOrError.getError();
+ return std::unique_ptr<CoverageDataWithObjectFile>(
+ new CoverageDataWithObjectFile(ObjectFile,
+ std::move(MergedDataOrError.get())));
+ }
+
+ std::string object_file() const { return ObjectFile; }
+
// Print list of covered functions.
// Line format: <file_name>:<line> <function_name>
- void printCoveredFunctions(raw_ostream &OS) {
- printFunctionLocs(computeFunctionLocs(*Addrs), OS);
+ void printCoveredFunctions(raw_ostream &OS) const {
+ SourceCoverageData SCovData(ObjectFile, *Addrs);
+ auto CoveredFns = SCovData.computeCoveredFunctions();
+ printFunctionLocs(SCovData.resolveFunctions(CoveredFns), 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);
+ void printNotCoveredFunctions(raw_ostream &OS) const {
+ SourceCoverageData SCovData(ObjectFile, *Addrs);
+ auto NotCoveredFns = SCovData.computeNotCoveredFunctions();
+ printFunctionLocs(SCovData.resolveFunctions(NotCoveredFns), OS);
+ }
+
+ void printReport(raw_ostream &OS) const {
+ SourceCoverageData SCovData(ObjectFile, *Addrs);
+ auto LineStatusMap = SCovData.computeLineStatusMap();
+
+ std::set<FileFn> AllFns = SCovData.computeAllFunctions();
+ // file_loc -> set[function_name]
+ auto AllFnsByLoc = SCovData.resolveFunctions(AllFns);
+ auto FileCoverage = SCovData.computeFileCoverage();
+
+ auto FnCoverage = SCovData.computeFunctionsCoverage();
+ auto FnCoverageByFile =
+ group_by(FnCoverage, [](const std::pair<FileFn, int> &FileFn) {
+ return FileFn.first.FileName;
+ });
+
+ // TOC
+
+ size_t NotCoveredFilesCount = 0;
+ std::set<std::string> Files = SCovData.files();
+
+ // Covered Files.
+ OS << "<details open><summary>Touched Files</summary>\n";
+ OS << "<table>\n";
+ OS << "<tr><th>File</th><th>Coverage %</th>";
+ OS << "<th>Hit (Total) Fns</th></tr>\n";
+ for (const auto &FileName : Files) {
+ std::pair<size_t, size_t> FC = FileCoverage[FileName];
+ if (FC.first == 0) {
+ NotCoveredFilesCount++;
+ continue;
+ }
+ size_t CovPct = FC.second == 0 ? 100 : 100 * FC.first / FC.second;
+
+ OS << "<tr><td><a href=\"#" << anchorName(FileName) << "\">"
+ << stripPathPrefix(FileName) << "</a></td>"
+ << "<td>" << formatHtmlPct(CovPct) << "%</td>"
+ << "<td>" << FC.first << " (" << FC.second << ")"
+ << "</tr>\n";
+ }
+ OS << "</table>\n";
+ OS << "</details>\n";
+
+ // Not covered files.
+ if (NotCoveredFilesCount) {
+ OS << "<details><summary>Not Touched Files</summary>\n";
+ OS << "<table>\n";
+ for (const auto &FileName : Files) {
+ std::pair<size_t, size_t> FC = FileCoverage[FileName];
+ if (FC.first == 0)
+ OS << "<tr><td>" << stripPathPrefix(FileName) << "</td>\n";
+ }
+ OS << "</table>\n";
+ OS << "</details>\n";
+ } else {
+ OS << "<p>Congratulations! All source files are touched.</p>\n";
+ }
+
+ // Source
+ for (const auto &FileName : Files) {
+ std::pair<size_t, size_t> FC = FileCoverage[FileName];
+ if (FC.first == 0)
+ continue;
+ OS << "<a name=\"" << anchorName(FileName) << "\"></a>\n";
+ OS << "<h2>" << stripPathPrefix(FileName) << "</h2>\n";
+ OS << "<details open><summary>Function Coverage</summary>";
+ OS << "<div class='fnlist'>\n";
+
+ auto &FileFnCoverage = FnCoverageByFile[FileName];
+
+ for (const auto &P : FileFnCoverage) {
+ std::string FunctionName = P.first.FunctionName;
+
+ OS << "<div class='fn' style='order: " << P.second << "'>";
+ OS << "<span class='pct'>" << formatHtmlPct(P.second)
+ << "%</span>&nbsp;";
+ OS << "<span class='name'><a href=\"#"
+ << anchorName(FileName + "::" + FunctionName) << "\">";
+ OS << escapeHtml(FunctionName) << "</a></span>";
+ OS << "</div>\n";
+ }
+ OS << "</div></details>\n";
+
+ ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
+ MemoryBuffer::getFile(FileName);
+ if (!BufOrErr) {
+ OS << "Error reading file: " << FileName << " : "
+ << BufOrErr.getError().message() << "("
+ << BufOrErr.getError().value() << ")\n";
+ continue;
+ }
+
+ OS << "<pre>\n";
+ const auto &LineStatuses = LineStatusMap[FileName];
+ for (line_iterator I = line_iterator(*BufOrErr.get(), false);
+ !I.is_at_eof(); ++I) {
+ uint32_t Line = I.line_number();
+ { // generate anchors (if any);
+ FileLoc Loc = FileLoc{FileName, Line};
+ auto It = AllFnsByLoc.find(Loc);
+ if (It != AllFnsByLoc.end()) {
+ for (const std::string &Fn : It->second) {
+ OS << "<a name=\"" << anchorName(FileName + "::" + Fn)
+ << "\"></a>";
+ };
+ }
+ }
- std::set<FunctionLoc> NotCoveredFns;
- std::set_difference(AllFns.begin(), AllFns.end(), CoveredFns.begin(),
- CoveredFns.end(),
- std::inserter(NotCoveredFns, NotCoveredFns.end()));
- printFunctionLocs(NotCoveredFns, OS);
+ OS << "<span ";
+ auto LIT = LineStatuses.find(I.line_number());
+ auto Status = (LIT != LineStatuses.end()) ? LIT->second
+ : SourceCoverageData::UNKNOWN;
+ switch (Status) {
+ case SourceCoverageData::UNKNOWN:
+ OS << "class=unknown";
+ break;
+ case SourceCoverageData::COVERED:
+ OS << "class=covered";
+ break;
+ case SourceCoverageData::NOT_COVERED:
+ OS << "class=notcovered";
+ break;
+ case SourceCoverageData::MIXED:
+ OS << "class=mixed";
+ break;
+ }
+ OS << ">";
+ OS << escapeHtml(*I) << "</span>\n";
+ }
+ OS << "</pre>\n";
+ }
+ }
+
+ void collectStats(CoverageStats *Stats) const {
+ Stats->CovPoints += Addrs->size();
+
+ SourceCoverageData SCovData(ObjectFile, *Addrs);
+ SCovData.collectStats(Stats);
}
private:
- explicit CoverageData(std::unique_ptr<std::set<uint64_t>> Addrs)
- : Addrs(std::move(Addrs)) {}
+ CoverageDataWithObjectFile(std::string ObjectFile,
+ std::unique_ptr<CoverageData> Coverage)
+ : CoverageData(std::move(Coverage->Addrs)),
+ ObjectFile(std::move(ObjectFile)) {}
+ const std::string ObjectFile;
+};
- std::unique_ptr<std::set<uint64_t>> Addrs;
+// Multiple coverage files data organized by object file.
+class CoverageDataSet {
+public:
+ static ErrorOr<std::unique_ptr<CoverageDataSet>>
+ readCmdArguments(std::vector<std::string> FileNames) {
+ // Short name => file name.
+ std::map<std::string, std::string> ObjFiles;
+ std::string FirstObjFile;
+ std::set<std::string> CovFiles;
+
+ // Partition input values into coverage/object files.
+ for (const auto &FileName : FileNames) {
+ auto ErrorOrIsCoverage = isCoverageFile(FileName);
+ if (!ErrorOrIsCoverage)
+ continue;
+ if (ErrorOrIsCoverage.get()) {
+ CovFiles.insert(FileName);
+ } else {
+ auto ShortFileName = llvm::sys::path::filename(FileName);
+ if (ObjFiles.find(ShortFileName) != ObjFiles.end()) {
+ Fail("Duplicate binary file with a short name: " + ShortFileName);
+ }
+
+ ObjFiles[ShortFileName] = FileName;
+ if (FirstObjFile.empty())
+ FirstObjFile = FileName;
+ }
+ }
+
+ Regex SancovRegex("(.*)\\.[0-9]+\\.sancov");
+ SmallVector<StringRef, 2> Components;
+
+ // Object file => list of corresponding coverage file names.
+ auto CoverageByObjFile = group_by(CovFiles, [&](std::string FileName) {
+ auto ShortFileName = llvm::sys::path::filename(FileName);
+ auto Ok = SancovRegex.match(ShortFileName, &Components);
+ if (!Ok) {
+ Fail("Can't match coverage file name against "
+ "<module_name>.<pid>.sancov pattern: " +
+ FileName);
+ }
+
+ auto Iter = ObjFiles.find(Components[1]);
+ if (Iter == ObjFiles.end()) {
+ Fail("Object file for coverage not found: " + FileName);
+ }
+ return Iter->second;
+ });
+
+ // Read coverage.
+ std::vector<std::unique_ptr<CoverageDataWithObjectFile>> MergedCoverage;
+ for (const auto &Pair : CoverageByObjFile) {
+ if (findSanitizerCovFunctions(Pair.first).empty()) {
+ for (const auto &FileName : Pair.second) {
+ CovFiles.erase(FileName);
+ }
+
+ errs()
+ << "Ignoring " << Pair.first
+ << " and its coverage because __sanitizer_cov* functions were not "
+ "found.\n";
+ continue;
+ }
+
+ auto DataOrError =
+ CoverageDataWithObjectFile::readAndMerge(Pair.first, Pair.second);
+ FailIfError(DataOrError);
+ MergedCoverage.push_back(std::move(DataOrError.get()));
+ }
+
+ return std::unique_ptr<CoverageDataSet>(
+ new CoverageDataSet(FirstObjFile, &MergedCoverage, CovFiles));
+ }
+
+ void printCoveredFunctions(raw_ostream &OS) const {
+ for (const auto &Cov : Coverage) {
+ Cov->printCoveredFunctions(OS);
+ }
+ }
+
+ void printNotCoveredFunctions(raw_ostream &OS) const {
+ for (const auto &Cov : Coverage) {
+ Cov->printNotCoveredFunctions(OS);
+ }
+ }
+
+ void printStats(raw_ostream &OS) const {
+ CoverageStats Stats;
+ for (const auto &Cov : Coverage) {
+ Cov->collectStats(&Stats);
+ }
+ OS << Stats;
+ }
+
+ void printReport(raw_ostream &OS) const {
+ auto Title =
+ (llvm::sys::path::filename(MainObjFile) + " Coverage Report").str();
+
+ OS << "<html>\n";
+ OS << "<head>\n";
+
+ // Stylesheet
+ OS << "<style>\n";
+ OS << ".covered { background: #7F7; }\n";
+ OS << ".notcovered { background: #F77; }\n";
+ OS << ".mixed { background: #FF7; }\n";
+ OS << "summary { font-weight: bold; }\n";
+ OS << "details > summary + * { margin-left: 1em; }\n";
+ OS << ".fnlist { display: flex; flex-flow: column nowrap; }\n";
+ OS << ".fn { display: flex; flex-flow: row nowrap; }\n";
+ OS << ".pct { width: 3em; text-align: right; margin-right: 1em; }\n";
+ OS << ".name { flex: 2; }\n";
+ OS << ".lz { color: lightgray; }\n";
+ OS << "</style>\n";
+ OS << "<title>" << Title << "</title>\n";
+ OS << "</head>\n";
+ OS << "<body>\n";
+
+ // Title
+ OS << "<h1>" << Title << "</h1>\n";
+
+ // Modules TOC.
+ if (Coverage.size() > 1) {
+ for (const auto &CovData : Coverage) {
+ OS << "<li><a href=\"#module_" << anchorName(CovData->object_file())
+ << "\">" << llvm::sys::path::filename(CovData->object_file())
+ << "</a></li>\n";
+ }
+ }
+
+ for (const auto &CovData : Coverage) {
+ if (Coverage.size() > 1) {
+ OS << "<h2>" << llvm::sys::path::filename(CovData->object_file())
+ << "</h2>\n";
+ }
+ OS << "<a name=\"module_" << anchorName(CovData->object_file())
+ << "\"></a>\n";
+ CovData->printReport(OS);
+ }
+
+ // About
+ OS << "<details><summary>About</summary>\n";
+ OS << "Coverage files:<ul>";
+ for (const auto &InputFile : CoverageFiles) {
+ llvm::sys::fs::file_status Status;
+ llvm::sys::fs::status(InputFile, Status);
+ OS << "<li>" << stripPathPrefix(InputFile) << " ("
+ << Status.getLastModificationTime().str() << ")</li>\n";
+ }
+ OS << "</ul></details>\n";
+
+ OS << "</body>\n";
+ OS << "</html>\n";
+ }
+
+ bool empty() const { return Coverage.empty(); }
+
+private:
+ explicit CoverageDataSet(
+ const std::string &MainObjFile,
+ std::vector<std::unique_ptr<CoverageDataWithObjectFile>> *Data,
+ const std::set<std::string> &CoverageFiles)
+ : MainObjFile(MainObjFile), CoverageFiles(CoverageFiles) {
+ Data->swap(this->Coverage);
+ }
+
+ const std::string MainObjFile;
+ std::vector<std::unique_ptr<CoverageDataWithObjectFile>> Coverage;
+ const std::set<std::string> CoverageFiles;
};
+
} // namespace
int main(int argc, char **argv) {
// Print stack trace if we signal out.
- sys::PrintStackTraceOnErrorSignal();
+ sys::PrintStackTraceOnErrorSignal(argv[0]);
PrettyStackTraceProgram X(argc, argv);
llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
@@ -511,23 +1213,46 @@ int main(int argc, char **argv) {
cl::ParseCommandLineOptions(argc, argv, "Sanitizer Coverage Processing Tool");
- auto CovData = CoverageData::readAndMerge(ClInputFiles);
- FailIfError(CovData);
-
- switch (Action) {
- case PrintAction: {
+ // -print doesn't need object files.
+ if (Action == PrintAction) {
+ auto CovData = CoverageData::readAndMerge(ClInputFiles);
+ FailIfError(CovData);
CovData.get()->printAddrs(outs());
return 0;
+ } else if (Action == PrintCovPointsAction) {
+ // -print-coverage-points doesn't need coverage files.
+ for (const std::string &ObjFile : ClInputFiles) {
+ printCovPoints(ObjFile, outs());
+ }
+ return 0;
+ }
+
+ auto CovDataSet = CoverageDataSet::readCmdArguments(ClInputFiles);
+ FailIfError(CovDataSet);
+
+ if (CovDataSet.get()->empty()) {
+ Fail("No coverage files specified.");
}
+
+ switch (Action) {
case CoveredFunctionsAction: {
- CovData.get()->printCoveredFunctions(outs());
+ CovDataSet.get()->printCoveredFunctions(outs());
return 0;
}
case NotCoveredFunctionsAction: {
- CovData.get()->printNotCoveredFunctions(outs());
+ CovDataSet.get()->printNotCoveredFunctions(outs());
return 0;
}
+ case HtmlReportAction: {
+ CovDataSet.get()->printReport(outs());
+ return 0;
+ }
+ case StatsAction: {
+ CovDataSet.get()->printStats(outs());
+ return 0;
+ }
+ case PrintAction:
+ case PrintCovPointsAction:
+ llvm_unreachable("unsupported action");
}
-
- llvm_unreachable("unsupported action");
}
diff --git a/tools/sanstats/CMakeLists.txt b/tools/sanstats/CMakeLists.txt
new file mode 100644
index 0000000000000..abfbdab3a0451
--- /dev/null
+++ b/tools/sanstats/CMakeLists.txt
@@ -0,0 +1,8 @@
+set(LLVM_LINK_COMPONENTS
+ Support
+ Symbolize
+ )
+
+add_llvm_tool(sanstats
+ sanstats.cpp
+ )
diff --git a/tools/sanstats/sanstats.cpp b/tools/sanstats/sanstats.cpp
new file mode 100644
index 0000000000000..b2216eab119e1
--- /dev/null
+++ b/tools/sanstats/sanstats.cpp
@@ -0,0 +1,138 @@
+//===- sanstats.cpp - Sanitizer statistics dumper -------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This tool dumps statistics information from files in the format produced
+// by clang's -fsanitize-stats feature.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/DebugInfo/Symbolize/Symbolize.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/ErrorOr.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Transforms/Utils/SanitizerStats.h"
+#include <stdint.h>
+
+using namespace llvm;
+
+static cl::opt<std::string> ClInputFile(cl::Positional, cl::Required,
+ cl::desc("<filename>"));
+
+static cl::opt<bool> ClDemangle("demangle", cl::init(false),
+ cl::desc("Print demangled function name."));
+
+inline uint64_t KindFromData(uint64_t Data, char SizeofPtr) {
+ return Data >> (SizeofPtr * 8 - kSanitizerStatKindBits);
+}
+
+inline uint64_t CountFromData(uint64_t Data, char SizeofPtr) {
+ return Data & ((1ull << (SizeofPtr * 8 - kSanitizerStatKindBits)) - 1);
+}
+
+uint64_t ReadLE(char Size, const char *Begin, const char *End) {
+ uint64_t Result = 0;
+ char Pos = 0;
+ while (Begin < End && Pos != Size) {
+ Result |= uint64_t(uint8_t(*Begin)) << (Pos * 8);
+ ++Begin;
+ ++Pos;
+ }
+ return Result;
+}
+
+const char *ReadModule(char SizeofPtr, const char *Begin, const char *End) {
+ const char *FilenameBegin = Begin;
+ while (Begin != End && *Begin)
+ ++Begin;
+ if (Begin == End)
+ return nullptr;
+ StringRef Filename(FilenameBegin, Begin - FilenameBegin);
+
+ ++Begin;
+ if (Begin == End)
+ return nullptr;
+
+ symbolize::LLVMSymbolizer::Options SymbolizerOptions;
+ SymbolizerOptions.Demangle = ClDemangle;
+ SymbolizerOptions.UseSymbolTable = true;
+ symbolize::LLVMSymbolizer Symbolizer(SymbolizerOptions);
+
+ while (1) {
+ uint64_t Addr = ReadLE(SizeofPtr, Begin, End);
+ Begin += SizeofPtr;
+ uint64_t Data = ReadLE(SizeofPtr, Begin, End);
+ Begin += SizeofPtr;
+
+ if (Begin > End)
+ return nullptr;
+ if (Addr == 0 && Data == 0)
+ return Begin;
+ if (Begin == End)
+ return nullptr;
+
+ if (Expected<DILineInfo> LineInfo =
+ Symbolizer.symbolizeCode(Filename, Addr)) {
+ llvm::outs() << LineInfo->FileName << ':' << LineInfo->Line << ' '
+ << LineInfo->FunctionName << ' ';
+ } else {
+ logAllUnhandledErrors(LineInfo.takeError(), llvm::outs(), "<error> ");
+ }
+
+ switch (KindFromData(Data, SizeofPtr)) {
+ case SanStat_CFI_VCall:
+ llvm::outs() << "cfi-vcall";
+ break;
+ case SanStat_CFI_NVCall:
+ llvm::outs() << "cfi-nvcall";
+ break;
+ case SanStat_CFI_DerivedCast:
+ llvm::outs() << "cfi-derived-cast";
+ break;
+ case SanStat_CFI_UnrelatedCast:
+ llvm::outs() << "cfi-unrelated-cast";
+ break;
+ case SanStat_CFI_ICall:
+ llvm::outs() << "cfi-icall";
+ break;
+ default:
+ llvm::outs() << "<unknown>";
+ break;
+ }
+
+ llvm::outs() << " " << CountFromData(Data, SizeofPtr) << '\n';
+ }
+}
+
+int main(int argc, char **argv) {
+ cl::ParseCommandLineOptions(argc, argv,
+ "Sanitizer Statistics Processing Tool");
+
+ ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr =
+ MemoryBuffer::getFile(ClInputFile, -1, false);
+ if (!MBOrErr) {
+ errs() << argv[0] << ": " << ClInputFile << ": "
+ << MBOrErr.getError().message() << '\n';
+ return 1;
+ }
+ std::unique_ptr<MemoryBuffer> MB = std::move(MBOrErr.get());
+ const char *Begin = MB->getBufferStart(), *End = MB->getBufferEnd();
+ if (Begin == End) {
+ errs() << argv[0] << ": " << ClInputFile << ": short read\n";
+ return 1;
+ }
+ char SizeofPtr = *Begin++;
+ while (Begin != End) {
+ Begin = ReadModule(SizeofPtr, Begin, End);
+ if (Begin == nullptr) {
+ errs() << argv[0] << ": " << ClInputFile << ": short read\n";
+ return 1;
+ }
+ assert(Begin <= End);
+ }
+}
diff --git a/tools/verify-uselistorder/Makefile b/tools/verify-uselistorder/Makefile
deleted file mode 100644
index 90d2aa89128b8..0000000000000
--- a/tools/verify-uselistorder/Makefile
+++ /dev/null
@@ -1,17 +0,0 @@
-##===- tools/verify-uselistorder/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 := verify-uselistorder
-LINK_COMPONENTS := AsmParser BitReader BitWriter Core IRReader Support
-
-# This tool has no plugins, optimize startup time.
-TOOL_NO_EXPORTS := 1
-
-include $(LEVEL)/Makefile.common
diff --git a/tools/verify-uselistorder/verify-uselistorder.cpp b/tools/verify-uselistorder/verify-uselistorder.cpp
index c3fec1326acb0..b19ecdaf6698f 100644
--- a/tools/verify-uselistorder/verify-uselistorder.cpp
+++ b/tools/verify-uselistorder/verify-uselistorder.cpp
@@ -191,6 +191,8 @@ ValueMapping::ValueMapping(const Module &M) {
map(&G);
for (const GlobalAlias &A : M.aliases())
map(&A);
+ for (const GlobalIFunc &IF : M.ifuncs())
+ map(&IF);
for (const Function &F : M)
map(&F);
@@ -200,6 +202,8 @@ ValueMapping::ValueMapping(const Module &M) {
map(G.getInitializer());
for (const GlobalAlias &A : M.aliases())
map(A.getAliasee());
+ for (const GlobalIFunc &IF : M.ifuncs())
+ map(IF.getResolver());
for (const Function &F : M) {
if (F.hasPrefixData())
map(F.getPrefixData());
@@ -463,6 +467,8 @@ static void changeUseLists(Module &M, Changer changeValueUseList) {
changeValueUseList(&G);
for (GlobalAlias &A : M.aliases())
changeValueUseList(&A);
+ for (GlobalIFunc &IF : M.ifuncs())
+ changeValueUseList(&IF);
for (Function &F : M)
changeValueUseList(&F);
@@ -472,6 +478,8 @@ static void changeUseLists(Module &M, Changer changeValueUseList) {
changeValueUseList(G.getInitializer());
for (GlobalAlias &A : M.aliases())
changeValueUseList(A.getAliasee());
+ for (GlobalIFunc &IF : M.ifuncs())
+ changeValueUseList(IF.getResolver());
for (Function &F : M) {
if (F.hasPrefixData())
changeValueUseList(F.getPrefixData());
@@ -518,14 +526,14 @@ static void reverseUseLists(Module &M) {
}
int main(int argc, char **argv) {
- sys::PrintStackTraceOnErrorSignal();
+ sys::PrintStackTraceOnErrorSignal(argv[0]);
llvm::PrettyStackTraceProgram X(argc, argv);
// Enable debug stream buffering.
EnableDebugBuffering = true;
llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
- LLVMContext &Context = getGlobalContext();
+ LLVMContext Context;
cl::ParseCommandLineOptions(argc, argv,
"llvm tool to verify use-list order\n");
diff --git a/tools/xcode-toolchain/CMakeLists.txt b/tools/xcode-toolchain/CMakeLists.txt
index 4eacbd320efcb..92b24581962b3 100644
--- a/tools/xcode-toolchain/CMakeLists.txt
+++ b/tools/xcode-toolchain/CMakeLists.txt
@@ -69,4 +69,24 @@ add_custom_target(install-xcode-toolchain
COMMAND "${CMAKE_COMMAND}"
-DCMAKE_INSTALL_PREFIX=${LLVMToolchainDir}/usr/
-P "${CMAKE_BINARY_DIR}/cmake_install.cmake"
- ${cmake_3_2_USES_TERMINAL})
+ USES_TERMINAL)
+
+if(LLVM_DISTRIBUTION_COMPONENTS)
+ if(CMAKE_CONFIGURATION_TYPES)
+ message(FATAL_ERROR "LLVM_DISTRIBUTION_COMPONENTS cannot be specified with multi-configuration generators (i.e. Xcode or Visual Studio)")
+ endif()
+
+ add_custom_target(install-distribution-toolchain
+ DEPENDS ${LLVMToolchainDir}/ToolchainInfo.plist distribution)
+
+ foreach(target ${LLVM_DISTRIBUTION_COMPONENTS})
+ add_custom_target(install-distribution-${target}
+ DEPENDS ${target}
+ COMMAND "${CMAKE_COMMAND}"
+ -DCMAKE_INSTALL_COMPONENT=${target}
+ -DCMAKE_INSTALL_PREFIX=${LLVMToolchainDir}/usr/
+ -P "${CMAKE_BINARY_DIR}/cmake_install.cmake"
+ USES_TERMINAL)
+ add_dependencies(install-distribution-toolchain install-distribution-${target})
+ endforeach()
+endif()
diff --git a/tools/yaml2obj/CMakeLists.txt b/tools/yaml2obj/CMakeLists.txt
index 52e9df4d766ae..885a69f5d3c36 100644
--- a/tools/yaml2obj/CMakeLists.txt
+++ b/tools/yaml2obj/CMakeLists.txt
@@ -1,6 +1,7 @@
set(LLVM_LINK_COMPONENTS
MC
Object
+ ObjectYAML
Support
)
@@ -8,4 +9,5 @@ add_llvm_tool(yaml2obj
yaml2obj.cpp
yaml2coff.cpp
yaml2elf.cpp
+ yaml2macho.cpp
)
diff --git a/tools/yaml2obj/Makefile b/tools/yaml2obj/Makefile
deleted file mode 100644
index 912f0e31ae7c1..0000000000000
--- a/tools/yaml2obj/Makefile
+++ /dev/null
@@ -1,17 +0,0 @@
-##===- utils/yaml2obj/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 = yaml2obj
-LINK_COMPONENTS := object
-
-# This tool has no plugins, optimize startup time.
-TOOL_NO_EXPORTS = 1
-
-include $(LEVEL)/Makefile.common
diff --git a/tools/yaml2obj/yaml2coff.cpp b/tools/yaml2obj/yaml2coff.cpp
index 61d985192552b..8f3f52179528d 100644
--- a/tools/yaml2obj/yaml2coff.cpp
+++ b/tools/yaml2obj/yaml2coff.cpp
@@ -14,12 +14,11 @@
#include "yaml2obj.h"
#include "llvm/ADT/STLExtras.h"
-#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Object/COFF.h"
-#include "llvm/Object/COFFYAML.h"
+#include "llvm/ObjectYAML/ObjectYAML.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/SourceMgr.h"
@@ -76,14 +75,24 @@ struct COFFParser {
unsigned Index = getStringIndex(Name);
std::string str = utostr(Index);
if (str.size() > 7) {
- errs() << "String table got too large";
+ errs() << "String table got too large\n";
return false;
}
Sec.Header.Name[0] = '/';
std::copy(str.begin(), str.end(), Sec.Header.Name + 1);
}
- Sec.Header.Characteristics |= (Log2_32(Sec.Alignment) + 1) << 20;
+ if (Sec.Alignment) {
+ if (Sec.Alignment > 8192) {
+ errs() << "Section alignment is too large\n";
+ return false;
+ }
+ if (!isPowerOf2_32(Sec.Alignment)) {
+ errs() << "Section alignment is not a power of 2\n";
+ return false;
+ }
+ Sec.Header.Characteristics |= (Log2_32(Sec.Alignment) + 1) << 20;
+ }
}
return true;
}
@@ -173,12 +182,12 @@ static bool layoutCOFF(COFFParser &CP) {
// Assign each section data address consecutively.
for (COFFYAML::Section &S : CP.Obj.Sections) {
if (S.SectionData.binary_size() > 0) {
- CurrentSectionDataOffset = RoundUpToAlignment(
- CurrentSectionDataOffset, CP.isPE() ? CP.getFileAlignment() : 4);
+ CurrentSectionDataOffset = alignTo(CurrentSectionDataOffset,
+ CP.isPE() ? CP.getFileAlignment() : 4);
S.Header.SizeOfRawData = S.SectionData.binary_size();
if (CP.isPE())
S.Header.SizeOfRawData =
- RoundUpToAlignment(S.Header.SizeOfRawData, CP.getFileAlignment());
+ alignTo(S.Header.SizeOfRawData, CP.getFileAlignment());
S.Header.PointerToRawData = CurrentSectionDataOffset;
CurrentSectionDataOffset += S.Header.SizeOfRawData;
if (!S.Relocations.empty()) {
@@ -292,10 +301,9 @@ static uint32_t initializeOptionalHeader(COFFParser &CP, uint16_t Magic, T Heade
Header->FileAlignment = CP.Obj.OptionalHeader->Header.FileAlignment;
uint32_t SizeOfCode = 0, SizeOfInitializedData = 0,
SizeOfUninitializedData = 0;
- uint32_t SizeOfHeaders = RoundUpToAlignment(
- CP.SectionTableStart + CP.SectionTableSize, Header->FileAlignment);
- uint32_t SizeOfImage =
- RoundUpToAlignment(SizeOfHeaders, Header->SectionAlignment);
+ uint32_t SizeOfHeaders = alignTo(CP.SectionTableStart + CP.SectionTableSize,
+ Header->FileAlignment);
+ uint32_t SizeOfImage = alignTo(SizeOfHeaders, Header->SectionAlignment);
uint32_t BaseOfData = 0;
for (const COFFYAML::Section &S : CP.Obj.Sections) {
if (S.Header.Characteristics & COFF::IMAGE_SCN_CNT_CODE)
@@ -309,8 +317,7 @@ static uint32_t initializeOptionalHeader(COFFParser &CP, uint16_t Magic, T Heade
else if (S.Name.equals(".data"))
BaseOfData = S.Header.VirtualAddress; // RVA
if (S.Header.VirtualAddress)
- SizeOfImage +=
- RoundUpToAlignment(S.Header.VirtualSize, Header->SectionAlignment);
+ SizeOfImage += alignTo(S.Header.VirtualSize, Header->SectionAlignment);
}
Header->SizeOfCode = SizeOfCode;
Header->SizeOfInitializedData = SizeOfInitializedData;
@@ -525,14 +532,7 @@ static bool writeCOFF(COFFParser &CP, raw_ostream &OS) {
return true;
}
-int yaml2coff(yaml::Input &YIn, raw_ostream &Out) {
- COFFYAML::Object Doc;
- YIn >> Doc;
- if (YIn.error()) {
- errs() << "yaml2obj: Failed to parse YAML file!\n";
- return 1;
- }
-
+int yaml2coff(llvm::COFFYAML::Object &Doc, raw_ostream &Out) {
COFFParser CP(Doc);
if (!CP.parse()) {
errs() << "yaml2obj: Failed to parse YAML file!\n";
diff --git a/tools/yaml2obj/yaml2elf.cpp b/tools/yaml2obj/yaml2elf.cpp
index d03986e9c88d5..c98093431a7ef 100644
--- a/tools/yaml2obj/yaml2elf.cpp
+++ b/tools/yaml2obj/yaml2elf.cpp
@@ -16,7 +16,7 @@
#include "llvm/ADT/ArrayRef.h"
#include "llvm/MC/StringTableBuilder.h"
#include "llvm/Object/ELFObjectFile.h"
-#include "llvm/Object/ELFYAML.h"
+#include "llvm/ObjectYAML/ELFYAML.h"
#include "llvm/Support/ELF.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/YAMLTraits.h"
@@ -38,7 +38,7 @@ class ContiguousBlobAccumulator {
if (Align == 0)
Align = 1;
uint64_t CurrentOffset = InitialOffset + OS.tell();
- uint64_t AlignedOffset = RoundUpToAlignment(CurrentOffset, Align);
+ uint64_t AlignedOffset = alignTo(CurrentOffset, Align);
for (; CurrentOffset != AlignedOffset; ++CurrentOffset)
OS.write('\0');
return AlignedOffset; // == CurrentOffset;
@@ -558,13 +558,7 @@ static bool isLittleEndian(const ELFYAML::Object &Doc) {
return Doc.Header.Data == ELFYAML::ELF_ELFDATA(ELF::ELFDATA2LSB);
}
-int yaml2elf(yaml::Input &YIn, raw_ostream &Out) {
- ELFYAML::Object Doc;
- YIn >> Doc;
- if (YIn.error()) {
- errs() << "yaml2obj: Failed to parse YAML file!\n";
- return 1;
- }
+int yaml2elf(llvm::ELFYAML::Object &Doc, raw_ostream &Out) {
using object::ELFType;
typedef ELFType<support::little, true> LE64;
typedef ELFType<support::big, true> BE64;
diff --git a/tools/yaml2obj/yaml2macho.cpp b/tools/yaml2obj/yaml2macho.cpp
new file mode 100644
index 0000000000000..fb29e206be35e
--- /dev/null
+++ b/tools/yaml2obj/yaml2macho.cpp
@@ -0,0 +1,539 @@
+//===- yaml2macho - Convert YAML to a Mach object file --------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief The Mach component of yaml2obj.
+///
+//===----------------------------------------------------------------------===//
+
+#include "yaml2obj.h"
+#include "llvm/ObjectYAML/ObjectYAML.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/LEB128.h"
+#include "llvm/Support/MachO.h"
+#include "llvm/Support/YAMLTraits.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include "llvm/Support/Format.h"
+
+using namespace llvm;
+
+namespace {
+
+class MachOWriter {
+public:
+ MachOWriter(MachOYAML::Object &Obj) : Obj(Obj), is64Bit(true), fileStart(0) {
+ is64Bit = Obj.Header.magic == MachO::MH_MAGIC_64 ||
+ Obj.Header.magic == MachO::MH_CIGAM_64;
+ memset(reinterpret_cast<void *>(&Header), 0, sizeof(MachO::mach_header_64));
+ }
+
+ Error writeMachO(raw_ostream &OS);
+
+private:
+ Error writeHeader(raw_ostream &OS);
+ Error writeLoadCommands(raw_ostream &OS);
+ Error writeSectionData(raw_ostream &OS);
+ Error writeLinkEditData(raw_ostream &OS);
+ void writeBindOpcodes(raw_ostream &OS,
+ std::vector<MachOYAML::BindOpcode> &BindOpcodes);
+ // LinkEdit writers
+ Error writeRebaseOpcodes(raw_ostream &OS);
+ Error writeBasicBindOpcodes(raw_ostream &OS);
+ Error writeWeakBindOpcodes(raw_ostream &OS);
+ Error writeLazyBindOpcodes(raw_ostream &OS);
+ Error writeNameList(raw_ostream &OS);
+ Error writeStringTable(raw_ostream &OS);
+ Error writeExportTrie(raw_ostream &OS);
+
+ void dumpExportEntry(raw_ostream &OS, MachOYAML::ExportEntry &Entry);
+ void ZeroToOffset(raw_ostream &OS, size_t offset);
+
+ MachOYAML::Object &Obj;
+ bool is64Bit;
+ uint64_t fileStart;
+
+ MachO::mach_header_64 Header;
+};
+
+Error MachOWriter::writeMachO(raw_ostream &OS) {
+ fileStart = OS.tell();
+ if (auto Err = writeHeader(OS))
+ return Err;
+ if (auto Err = writeLoadCommands(OS))
+ return Err;
+ if (auto Err = writeSectionData(OS))
+ return Err;
+ return Error::success();
+}
+
+Error MachOWriter::writeHeader(raw_ostream &OS) {
+ Header.magic = Obj.Header.magic;
+ Header.cputype = Obj.Header.cputype;
+ Header.cpusubtype = Obj.Header.cpusubtype;
+ Header.filetype = Obj.Header.filetype;
+ Header.ncmds = Obj.Header.ncmds;
+ Header.sizeofcmds = Obj.Header.sizeofcmds;
+ Header.flags = Obj.Header.flags;
+ Header.reserved = Obj.Header.reserved;
+
+ auto header_size =
+ is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header);
+ OS.write((const char *)&Header, header_size);
+
+ return Error::success();
+}
+
+template <typename SectionType>
+SectionType constructSection(MachOYAML::Section Sec) {
+ SectionType TempSec;
+ memcpy(reinterpret_cast<void *>(&TempSec.sectname[0]), &Sec.sectname[0], 16);
+ memcpy(reinterpret_cast<void *>(&TempSec.segname[0]), &Sec.segname[0], 16);
+ TempSec.addr = Sec.addr;
+ TempSec.size = Sec.size;
+ TempSec.offset = Sec.offset;
+ TempSec.align = Sec.align;
+ TempSec.reloff = Sec.reloff;
+ TempSec.nreloc = Sec.nreloc;
+ TempSec.flags = Sec.flags;
+ TempSec.reserved1 = Sec.reserved1;
+ TempSec.reserved2 = Sec.reserved2;
+ return TempSec;
+}
+
+template <typename StructType>
+size_t writeLoadCommandData(MachOYAML::LoadCommand &LC, raw_ostream &OS) {
+ return 0;
+}
+
+template <>
+size_t writeLoadCommandData<MachO::segment_command>(MachOYAML::LoadCommand &LC,
+ raw_ostream &OS) {
+ size_t BytesWritten = 0;
+ for (const auto &Sec : LC.Sections) {
+ auto TempSec = constructSection<MachO::section>(Sec);
+ OS.write(reinterpret_cast<const char *>(&(TempSec)),
+ sizeof(MachO::section));
+ BytesWritten += sizeof(MachO::section);
+ }
+ return BytesWritten;
+}
+
+template <>
+size_t
+writeLoadCommandData<MachO::segment_command_64>(MachOYAML::LoadCommand &LC,
+ raw_ostream &OS) {
+ size_t BytesWritten = 0;
+ for (const auto &Sec : LC.Sections) {
+ auto TempSec = constructSection<MachO::section_64>(Sec);
+ TempSec.reserved3 = Sec.reserved3;
+ OS.write(reinterpret_cast<const char *>(&(TempSec)),
+ sizeof(MachO::section_64));
+ BytesWritten += sizeof(MachO::section_64);
+ }
+ return BytesWritten;
+}
+
+size_t writePayloadString(MachOYAML::LoadCommand &LC, raw_ostream &OS) {
+ size_t BytesWritten = 0;
+ if (!LC.PayloadString.empty()) {
+ OS.write(LC.PayloadString.c_str(), LC.PayloadString.length());
+ BytesWritten = LC.PayloadString.length();
+ }
+ return BytesWritten;
+}
+
+template <>
+size_t writeLoadCommandData<MachO::dylib_command>(MachOYAML::LoadCommand &LC,
+ raw_ostream &OS) {
+ return writePayloadString(LC, OS);
+}
+
+template <>
+size_t writeLoadCommandData<MachO::dylinker_command>(MachOYAML::LoadCommand &LC,
+ raw_ostream &OS) {
+ return writePayloadString(LC, OS);
+}
+
+template <>
+size_t writeLoadCommandData<MachO::rpath_command>(MachOYAML::LoadCommand &LC,
+ raw_ostream &OS) {
+ return writePayloadString(LC, OS);
+}
+
+void ZeroFillBytes(raw_ostream &OS, size_t Size) {
+ std::vector<uint8_t> FillData;
+ FillData.insert(FillData.begin(), Size, 0);
+ OS.write(reinterpret_cast<char *>(FillData.data()), Size);
+}
+
+void Fill(raw_ostream &OS, size_t Size, uint32_t Data) {
+ std::vector<uint32_t> FillData;
+ FillData.insert(FillData.begin(), (Size / 4) + 1, Data);
+ OS.write(reinterpret_cast<char *>(FillData.data()), Size);
+}
+
+void MachOWriter::ZeroToOffset(raw_ostream &OS, size_t Offset) {
+ auto currOffset = OS.tell() - fileStart;
+ if (currOffset < Offset)
+ ZeroFillBytes(OS, Offset - currOffset);
+}
+
+Error MachOWriter::writeLoadCommands(raw_ostream &OS) {
+ for (auto &LC : Obj.LoadCommands) {
+ size_t BytesWritten = 0;
+#define HANDLE_LOAD_COMMAND(LCName, LCValue, LCStruct) \
+ case MachO::LCName: \
+ OS.write(reinterpret_cast<const char *>(&(LC.Data.LCStruct##_data)), \
+ sizeof(MachO::LCStruct)); \
+ BytesWritten = sizeof(MachO::LCStruct); \
+ BytesWritten += writeLoadCommandData<MachO::LCStruct>(LC, OS); \
+ break;
+
+ switch (LC.Data.load_command_data.cmd) {
+ default:
+ OS.write(reinterpret_cast<const char *>(&(LC.Data.load_command_data)),
+ sizeof(MachO::load_command));
+ BytesWritten = sizeof(MachO::load_command);
+ BytesWritten += writeLoadCommandData<MachO::load_command>(LC, OS);
+ break;
+#include "llvm/Support/MachO.def"
+ }
+
+ if (LC.PayloadBytes.size() > 0) {
+ OS.write(reinterpret_cast<const char *>(LC.PayloadBytes.data()),
+ LC.PayloadBytes.size());
+ BytesWritten += LC.PayloadBytes.size();
+ }
+
+ if (LC.ZeroPadBytes > 0) {
+ ZeroFillBytes(OS, LC.ZeroPadBytes);
+ BytesWritten += LC.ZeroPadBytes;
+ }
+
+ // Fill remaining bytes with 0. This will only get hit in partially
+ // specified test cases.
+ auto BytesRemaining = LC.Data.load_command_data.cmdsize - BytesWritten;
+ if (BytesRemaining > 0) {
+ ZeroFillBytes(OS, BytesRemaining);
+ }
+ }
+ return Error::success();
+}
+
+Error MachOWriter::writeSectionData(raw_ostream &OS) {
+ for (auto &LC : Obj.LoadCommands) {
+ switch (LC.Data.load_command_data.cmd) {
+ case MachO::LC_SEGMENT:
+ case MachO::LC_SEGMENT_64:
+ auto currOffset = OS.tell() - fileStart;
+ auto segname = LC.Data.segment_command_data.segname;
+ uint64_t segOff = is64Bit ? LC.Data.segment_command_64_data.fileoff
+ : LC.Data.segment_command_data.fileoff;
+
+ if (0 == strncmp(&segname[0], "__LINKEDIT", 16)) {
+ if (auto Err = writeLinkEditData(OS))
+ return Err;
+ } else {
+ // Zero Fill any data between the end of the last thing we wrote and the
+ // start of this section.
+ if (currOffset < segOff) {
+ ZeroFillBytes(OS, segOff - currOffset);
+ }
+
+ for (auto &Sec : LC.Sections) {
+ // Zero Fill any data between the end of the last thing we wrote and
+ // the
+ // start of this section.
+ assert(
+ OS.tell() - fileStart <= Sec.offset &&
+ "Wrote too much data somewhere, section offsets don't line up.");
+ currOffset = OS.tell() - fileStart;
+ if (currOffset < Sec.offset) {
+ ZeroFillBytes(OS, Sec.offset - currOffset);
+ }
+
+ // Fills section data with 0xDEADBEEF
+ Fill(OS, Sec.size, 0xDEADBEEFu);
+ }
+ }
+ uint64_t segSize = is64Bit ? LC.Data.segment_command_64_data.filesize
+ : LC.Data.segment_command_data.filesize;
+ ZeroToOffset(OS, segOff + segSize);
+ break;
+ }
+ }
+ return Error::success();
+}
+
+void MachOWriter::writeBindOpcodes(
+ raw_ostream &OS, std::vector<MachOYAML::BindOpcode> &BindOpcodes) {
+
+ for (auto Opcode : BindOpcodes) {
+ uint8_t OpByte = Opcode.Opcode | Opcode.Imm;
+ OS.write(reinterpret_cast<char *>(&OpByte), 1);
+ for (auto Data : Opcode.ULEBExtraData) {
+ encodeULEB128(Data, OS);
+ }
+ for (auto Data : Opcode.SLEBExtraData) {
+ encodeSLEB128(Data, OS);
+ }
+ if (!Opcode.Symbol.empty()) {
+ OS.write(Opcode.Symbol.data(), Opcode.Symbol.size());
+ OS.write('\0');
+ }
+ }
+}
+
+void MachOWriter::dumpExportEntry(raw_ostream &OS,
+ MachOYAML::ExportEntry &Entry) {
+ encodeSLEB128(Entry.TerminalSize, OS);
+ if (Entry.TerminalSize > 0) {
+ encodeSLEB128(Entry.Flags, OS);
+ if (Entry.Flags & MachO::EXPORT_SYMBOL_FLAGS_REEXPORT) {
+ encodeSLEB128(Entry.Other, OS);
+ OS << Entry.ImportName;
+ OS.write('\0');
+ } else {
+ encodeSLEB128(Entry.Address, OS);
+ if (Entry.Flags & MachO::EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER)
+ encodeSLEB128(Entry.Other, OS);
+ }
+ }
+ OS.write(static_cast<uint8_t>(Entry.Children.size()));
+ for (auto EE : Entry.Children) {
+ OS << EE.Name;
+ OS.write('\0');
+ encodeSLEB128(EE.NodeOffset, OS);
+ }
+ for (auto EE : Entry.Children)
+ dumpExportEntry(OS, EE);
+}
+
+Error MachOWriter::writeExportTrie(raw_ostream &OS) {
+ dumpExportEntry(OS, Obj.LinkEdit.ExportTrie);
+ return Error::success();
+}
+
+template <typename NListType>
+void writeNListEntry(MachOYAML::NListEntry &NLE, raw_ostream &OS) {
+ NListType ListEntry;
+ ListEntry.n_strx = NLE.n_strx;
+ ListEntry.n_type = NLE.n_type;
+ ListEntry.n_sect = NLE.n_sect;
+ ListEntry.n_desc = NLE.n_desc;
+ ListEntry.n_value = NLE.n_value;
+ OS.write(reinterpret_cast<const char *>(&ListEntry), sizeof(NListType));
+}
+
+Error MachOWriter::writeLinkEditData(raw_ostream &OS) {
+ typedef Error (MachOWriter::*writeHandler)(raw_ostream &);
+ typedef std::pair<uint64_t, writeHandler> writeOperation;
+ std::vector<writeOperation> WriteQueue;
+
+ MachO::dyld_info_command *DyldInfoOnlyCmd = 0;
+ MachO::symtab_command *SymtabCmd = 0;
+ for (auto &LC : Obj.LoadCommands) {
+ switch (LC.Data.load_command_data.cmd) {
+ case MachO::LC_SYMTAB:
+ SymtabCmd = &LC.Data.symtab_command_data;
+ WriteQueue.push_back(
+ std::make_pair(SymtabCmd->symoff, &MachOWriter::writeNameList));
+ WriteQueue.push_back(
+ std::make_pair(SymtabCmd->stroff, &MachOWriter::writeStringTable));
+ break;
+ case MachO::LC_DYLD_INFO_ONLY:
+ DyldInfoOnlyCmd = &LC.Data.dyld_info_command_data;
+ WriteQueue.push_back(std::make_pair(DyldInfoOnlyCmd->rebase_off,
+ &MachOWriter::writeRebaseOpcodes));
+ WriteQueue.push_back(std::make_pair(DyldInfoOnlyCmd->bind_off,
+ &MachOWriter::writeBasicBindOpcodes));
+ WriteQueue.push_back(std::make_pair(DyldInfoOnlyCmd->weak_bind_off,
+ &MachOWriter::writeWeakBindOpcodes));
+ WriteQueue.push_back(std::make_pair(DyldInfoOnlyCmd->lazy_bind_off,
+ &MachOWriter::writeLazyBindOpcodes));
+ WriteQueue.push_back(std::make_pair(DyldInfoOnlyCmd->export_off,
+ &MachOWriter::writeExportTrie));
+ break;
+ }
+ }
+
+ std::sort(WriteQueue.begin(), WriteQueue.end(),
+ [](const writeOperation &a, const writeOperation &b) {
+ return a.first < b.first;
+ });
+
+ for (auto writeOp : WriteQueue) {
+ ZeroToOffset(OS, writeOp.first);
+ if (auto Err = (this->*writeOp.second)(OS))
+ return Err;
+ }
+
+ return Error::success();
+}
+
+Error MachOWriter::writeRebaseOpcodes(raw_ostream &OS) {
+ MachOYAML::LinkEditData &LinkEdit = Obj.LinkEdit;
+
+ for (auto Opcode : LinkEdit.RebaseOpcodes) {
+ uint8_t OpByte = Opcode.Opcode | Opcode.Imm;
+ OS.write(reinterpret_cast<char *>(&OpByte), 1);
+ for (auto Data : Opcode.ExtraData) {
+ encodeULEB128(Data, OS);
+ }
+ }
+ return Error::success();
+}
+
+Error MachOWriter::writeBasicBindOpcodes(raw_ostream &OS) {
+ writeBindOpcodes(OS, Obj.LinkEdit.BindOpcodes);
+ return Error::success();
+}
+
+Error MachOWriter::writeWeakBindOpcodes(raw_ostream &OS) {
+ writeBindOpcodes(OS, Obj.LinkEdit.WeakBindOpcodes);
+ return Error::success();
+}
+
+Error MachOWriter::writeLazyBindOpcodes(raw_ostream &OS) {
+ writeBindOpcodes(OS, Obj.LinkEdit.LazyBindOpcodes);
+ return Error::success();
+}
+
+Error MachOWriter::writeNameList(raw_ostream &OS) {
+ for (auto NLE : Obj.LinkEdit.NameList) {
+ if (is64Bit)
+ writeNListEntry<MachO::nlist_64>(NLE, OS);
+ else
+ writeNListEntry<MachO::nlist>(NLE, OS);
+ }
+ return Error::success();
+}
+
+Error MachOWriter::writeStringTable(raw_ostream &OS) {
+ for (auto Str : Obj.LinkEdit.StringTable) {
+ OS.write(Str.data(), Str.size());
+ OS.write('\0');
+ }
+ return Error::success();
+}
+
+class UniversalWriter {
+public:
+ UniversalWriter(yaml::YamlObjectFile &ObjectFile)
+ : ObjectFile(ObjectFile), fileStart(0) {}
+
+ Error writeMachO(raw_ostream &OS);
+
+private:
+ Error writeFatHeader(raw_ostream &OS);
+ Error writeFatArchs(raw_ostream &OS);
+
+ void ZeroToOffset(raw_ostream &OS, size_t offset);
+
+ yaml::YamlObjectFile &ObjectFile;
+ uint64_t fileStart;
+};
+
+Error UniversalWriter::writeMachO(raw_ostream &OS) {
+ fileStart = OS.tell();
+ if (ObjectFile.MachO) {
+ MachOWriter Writer(*ObjectFile.MachO);
+ return Writer.writeMachO(OS);
+ }
+ if (auto Err = writeFatHeader(OS))
+ return Err;
+ if (auto Err = writeFatArchs(OS))
+ return Err;
+ auto &FatFile = *ObjectFile.FatMachO;
+ assert(FatFile.FatArchs.size() == FatFile.Slices.size());
+ for (size_t i = 0; i < FatFile.Slices.size(); i++) {
+ ZeroToOffset(OS, FatFile.FatArchs[i].offset);
+ MachOWriter Writer(FatFile.Slices[i]);
+ if (auto Err = Writer.writeMachO(OS))
+ return Err;
+ auto SliceEnd = FatFile.FatArchs[i].offset + FatFile.FatArchs[i].size;
+ ZeroToOffset(OS, SliceEnd);
+ }
+ return Error::success();
+}
+
+Error UniversalWriter::writeFatHeader(raw_ostream &OS) {
+ auto &FatFile = *ObjectFile.FatMachO;
+ MachO::fat_header header;
+ header.magic = FatFile.Header.magic;
+ header.nfat_arch = FatFile.Header.nfat_arch;
+ if (sys::IsLittleEndianHost)
+ swapStruct(header);
+ OS.write(reinterpret_cast<const char *>(&header), sizeof(MachO::fat_header));
+ return Error::success();
+}
+
+template <typename FatArchType>
+FatArchType constructFatArch(MachOYAML::FatArch &Arch) {
+ FatArchType FatArch;
+ FatArch.cputype = Arch.cputype;
+ FatArch.cpusubtype = Arch.cpusubtype;
+ FatArch.offset = Arch.offset;
+ FatArch.size = Arch.size;
+ FatArch.align = Arch.align;
+ return FatArch;
+}
+
+template <typename StructType>
+void writeFatArch(MachOYAML::FatArch &LC, raw_ostream &OS) {}
+
+template <>
+void writeFatArch<MachO::fat_arch>(MachOYAML::FatArch &Arch, raw_ostream &OS) {
+ auto FatArch = constructFatArch<MachO::fat_arch>(Arch);
+ if (sys::IsLittleEndianHost)
+ swapStruct(FatArch);
+ OS.write(reinterpret_cast<const char *>(&FatArch), sizeof(MachO::fat_arch));
+}
+
+template <>
+void writeFatArch<MachO::fat_arch_64>(MachOYAML::FatArch &Arch,
+ raw_ostream &OS) {
+ auto FatArch = constructFatArch<MachO::fat_arch_64>(Arch);
+ FatArch.reserved = Arch.reserved;
+ if (sys::IsLittleEndianHost)
+ swapStruct(FatArch);
+ OS.write(reinterpret_cast<const char *>(&FatArch),
+ sizeof(MachO::fat_arch_64));
+}
+
+Error UniversalWriter::writeFatArchs(raw_ostream &OS) {
+ auto &FatFile = *ObjectFile.FatMachO;
+ bool is64Bit = FatFile.Header.magic == MachO::FAT_MAGIC_64;
+ for (auto Arch : FatFile.FatArchs) {
+ if (is64Bit)
+ writeFatArch<MachO::fat_arch_64>(Arch, OS);
+ else
+ writeFatArch<MachO::fat_arch>(Arch, OS);
+ }
+
+ return Error::success();
+}
+
+void UniversalWriter::ZeroToOffset(raw_ostream &OS, size_t Offset) {
+ auto currOffset = OS.tell() - fileStart;
+ if (currOffset < Offset)
+ ZeroFillBytes(OS, Offset - currOffset);
+}
+
+} // end anonymous namespace
+
+int yaml2macho(yaml::YamlObjectFile &Doc, raw_ostream &Out) {
+ UniversalWriter Writer(Doc);
+ if (auto Err = Writer.writeMachO(Out)) {
+ errs() << toString(std::move(Err));
+ return 1;
+ }
+ return 0;
+}
diff --git a/tools/yaml2obj/yaml2obj.cpp b/tools/yaml2obj/yaml2obj.cpp
index af4d868906724..f746d84a38985 100644
--- a/tools/yaml2obj/yaml2obj.cpp
+++ b/tools/yaml2obj/yaml2obj.cpp
@@ -16,6 +16,7 @@
#include "yaml2obj.h"
#include "llvm/ADT/StringExtras.h"
+#include "llvm/ObjectYAML/ObjectYAML.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/ManagedStatic.h"
@@ -32,27 +33,6 @@ using namespace llvm;
static cl::opt<std::string>
Input(cl::Positional, cl::desc("<input>"), cl::init("-"));
-// TODO: The "right" way to tell what kind of object file a given YAML file
-// corresponds to is to look at YAML "tags" (e.g. `!Foo`). Then, different
-// tags (`!ELF`, `!COFF`, etc.) would be used to discriminate between them.
-// Interpreting the tags is needed eventually for when writing test cases,
-// so that we can e.g. have `!Archive` contain a sequence of `!ELF`, and
-// just Do The Right Thing. However, interpreting these tags and acting on
-// them appropriately requires some work in the YAML parser and the YAMLIO
-// library.
-enum YAMLObjectFormat {
- YOF_COFF,
- YOF_ELF
-};
-
-cl::opt<YAMLObjectFormat> Format(
- "format",
- cl::desc("Interpret input as this type of object file"),
- cl::values(
- clEnumValN(YOF_COFF, "coff", "COFF object file format"),
- clEnumValN(YOF_ELF, "elf", "ELF object file format"),
- clEnumValEnd));
-
cl::opt<unsigned>
DocNum("docnum", cl::init(1),
cl::desc("Read specified document from input (default = 1)"));
@@ -60,14 +40,26 @@ DocNum("docnum", cl::init(1),
static cl::opt<std::string> OutputFilename("o", cl::desc("Output filename"),
cl::value_desc("filename"));
-typedef int (*ConvertFuncPtr)(yaml::Input & YIn, raw_ostream &Out);
-
-static int convertYAML(yaml::Input &YIn, raw_ostream &Out,
- ConvertFuncPtr Convert) {
+static int convertYAML(yaml::Input &YIn, raw_ostream &Out) {
unsigned CurDocNum = 0;
do {
- if (++CurDocNum == DocNum)
- return Convert(YIn, Out);
+ if (++CurDocNum == DocNum) {
+ yaml::YamlObjectFile Doc;
+ YIn >> Doc;
+ if (YIn.error()) {
+ errs() << "yaml2obj: Failed to parse YAML file!\n";
+ return 1;
+ }
+
+ if (Doc.Elf)
+ return yaml2elf(*Doc.Elf, Out);
+ if (Doc.Coff)
+ return yaml2coff(*Doc.Coff, Out);
+ if (Doc.MachO || Doc.FatMachO)
+ return yaml2macho(Doc, Out);
+ errs() << "yaml2obj: Unknown document type!\n";
+ return 1;
+ }
} while (YIn.nextDocument());
errs() << "yaml2obj: Cannot find the " << DocNum
@@ -77,7 +69,7 @@ static int convertYAML(yaml::Input &YIn, raw_ostream &Out,
int main(int argc, char **argv) {
cl::ParseCommandLineOptions(argc, argv);
- sys::PrintStackTraceOnErrorSignal();
+ sys::PrintStackTraceOnErrorSignal(argv[0]);
PrettyStackTraceProgram X(argc, argv);
llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
@@ -97,19 +89,9 @@ int main(int argc, char **argv) {
if (!Buf)
return 1;
- ConvertFuncPtr Convert = nullptr;
- if (Format == YOF_COFF)
- Convert = yaml2coff;
- else if (Format == YOF_ELF)
- Convert = yaml2elf;
- else {
- errs() << "Not yet implemented\n";
- return 1;
- }
-
yaml::Input YIn(Buf.get()->getBuffer());
- int Res = convertYAML(YIn, Out->os(), Convert);
+ int Res = convertYAML(YIn, Out->os());
if (Res == 0)
Out->keep();
diff --git a/tools/yaml2obj/yaml2obj.h b/tools/yaml2obj/yaml2obj.h
index 7290a9af2c649..b5025e860bd75 100644
--- a/tools/yaml2obj/yaml2obj.h
+++ b/tools/yaml2obj/yaml2obj.h
@@ -14,11 +14,23 @@
namespace llvm {
class raw_ostream;
+
+namespace COFFYAML {
+struct Object;
+}
+
+namespace ELFYAML {
+struct Object;
+}
+
namespace yaml {
class Input;
+struct YamlObjectFile;
}
}
-int yaml2coff(llvm::yaml::Input &YIn, llvm::raw_ostream &Out);
-int yaml2elf(llvm::yaml::Input &YIn, llvm::raw_ostream &Out);
+
+int yaml2coff(llvm::COFFYAML::Object &Doc, llvm::raw_ostream &Out);
+int yaml2elf(llvm::ELFYAML::Object &Doc, llvm::raw_ostream &Out);
+int yaml2macho(llvm::yaml::YamlObjectFile &Doc, llvm::raw_ostream &Out);
#endif