aboutsummaryrefslogtreecommitdiff
path: root/llvm/tools
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/tools')
-rw-r--r--llvm/tools/bugpoint/CrashDebugger.cpp2
-rw-r--r--llvm/tools/bugpoint/FindBugs.cpp2
-rw-r--r--llvm/tools/bugpoint/ListReducer.h2
-rw-r--r--llvm/tools/bugpoint/ToolRunner.cpp4
-rw-r--r--llvm/tools/llc/llc.cpp43
-rw-r--r--llvm/tools/lli/ExecutionUtils.cpp146
-rw-r--r--llvm/tools/lli/ExecutionUtils.h60
-rw-r--r--llvm/tools/lli/lli.cpp154
-rw-r--r--llvm/tools/llvm-ar/llvm-ar.cpp13
-rw-r--r--llvm/tools/llvm-as/llvm-as.cpp2
-rw-r--r--llvm/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp29
-rw-r--r--llvm/tools/llvm-cov/CodeCoverage.cpp42
-rw-r--r--llvm/tools/llvm-cov/CoverageExporterJson.cpp3
-rw-r--r--llvm/tools/llvm-cov/CoverageExporterLcov.cpp5
-rw-r--r--llvm/tools/llvm-cov/CoverageSummaryInfo.cpp6
-rw-r--r--llvm/tools/llvm-cov/CoverageSummaryInfo.h5
-rw-r--r--llvm/tools/llvm-cov/CoverageViewOptions.h1
-rw-r--r--llvm/tools/llvm-cov/TestingSupport.cpp19
-rw-r--r--llvm/tools/llvm-cov/gcov.cpp6
-rw-r--r--llvm/tools/llvm-cov/llvm-cov.cpp2
-rw-r--r--llvm/tools/llvm-cxxdump/llvm-cxxdump.cpp4
-rw-r--r--llvm/tools/llvm-cxxfilt/Opts.td28
-rw-r--r--llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp133
-rw-r--r--llvm/tools/llvm-cxxmap/llvm-cxxmap.cpp32
-rw-r--r--llvm/tools/llvm-diff/DiffConsumer.cpp35
-rw-r--r--llvm/tools/llvm-diff/DiffConsumer.h18
-rw-r--r--llvm/tools/llvm-diff/DiffLog.cpp16
-rw-r--r--llvm/tools/llvm-diff/DiffLog.h18
-rw-r--r--llvm/tools/llvm-diff/DifferenceEngine.cpp265
-rw-r--r--llvm/tools/llvm-diff/DifferenceEngine.h11
-rw-r--r--llvm/tools/llvm-diff/llvm-diff.cpp12
-rw-r--r--llvm/tools/llvm-dis/llvm-dis.cpp147
-rw-r--r--llvm/tools/llvm-dwarfdump/SectionSizes.cpp24
-rw-r--r--llvm/tools/llvm-dwarfdump/Statistics.cpp252
-rw-r--r--llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp80
-rw-r--r--llvm/tools/llvm-dwarfdump/llvm-dwarfdump.h4
-rw-r--r--llvm/tools/llvm-dwp/DWPError.cpp3
-rw-r--r--llvm/tools/llvm-dwp/DWPError.h23
-rw-r--r--llvm/tools/llvm-dwp/DWPStringPool.h56
-rw-r--r--llvm/tools/llvm-dwp/llvm-dwp.cpp634
-rw-r--r--llvm/tools/llvm-link/llvm-link.cpp108
-rw-r--r--llvm/tools/llvm-lto/llvm-lto.cpp150
-rw-r--r--llvm/tools/llvm-lto2/llvm-lto2.cpp32
-rw-r--r--llvm/tools/llvm-mc/llvm-mc.cpp229
-rw-r--r--llvm/tools/llvm-mca/CodeRegion.h2
-rw-r--r--llvm/tools/llvm-mca/CodeRegionGenerator.cpp12
-rw-r--r--llvm/tools/llvm-mca/CodeRegionGenerator.h6
-rw-r--r--llvm/tools/llvm-mca/PipelinePrinter.cpp109
-rw-r--r--llvm/tools/llvm-mca/PipelinePrinter.h20
-rw-r--r--llvm/tools/llvm-mca/Views/BottleneckAnalysis.cpp15
-rw-r--r--llvm/tools/llvm-mca/Views/BottleneckAnalysis.h30
-rw-r--r--llvm/tools/llvm-mca/Views/DispatchStatistics.cpp16
-rw-r--r--llvm/tools/llvm-mca/Views/DispatchStatistics.h1
-rw-r--r--llvm/tools/llvm-mca/Views/InstructionInfoView.cpp4
-rw-r--r--llvm/tools/llvm-mca/Views/InstructionView.cpp33
-rw-r--r--llvm/tools/llvm-mca/Views/InstructionView.h26
-rw-r--r--llvm/tools/llvm-mca/Views/RegisterFileStatistics.cpp27
-rw-r--r--llvm/tools/llvm-mca/Views/RegisterFileStatistics.h1
-rw-r--r--llvm/tools/llvm-mca/Views/RetireControlUnitStatistics.cpp3
-rw-r--r--llvm/tools/llvm-mca/Views/RetireControlUnitStatistics.h1
-rw-r--r--llvm/tools/llvm-mca/Views/SchedulerStatistics.h1
-rw-r--r--llvm/tools/llvm-mca/Views/SummaryView.cpp7
-rw-r--r--llvm/tools/llvm-mca/Views/SummaryView.h2
-rw-r--r--llvm/tools/llvm-mca/Views/TimelineView.cpp22
-rw-r--r--llvm/tools/llvm-mca/Views/TimelineView.h2
-rw-r--r--llvm/tools/llvm-mca/Views/View.h20
-rw-r--r--llvm/tools/llvm-mca/lib/AMDGPU/AMDGPUCustomBehaviour.cpp33
-rw-r--r--llvm/tools/llvm-mca/lib/AMDGPU/AMDGPUCustomBehaviour.h57
-rw-r--r--llvm/tools/llvm-mca/llvm-mca.cpp194
-rw-r--r--llvm/tools/llvm-modextract/llvm-modextract.cpp18
-rw-r--r--llvm/tools/llvm-nm/Opts.td76
-rw-r--r--llvm/tools/llvm-nm/llvm-nm.cpp443
-rw-r--r--llvm/tools/llvm-objcopy/Buffer.cpp79
-rw-r--r--llvm/tools/llvm-objcopy/Buffer.h68
-rw-r--r--llvm/tools/llvm-objcopy/COFF/COFFConfig.h21
-rw-r--r--llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp29
-rw-r--r--llvm/tools/llvm-objcopy/COFF/COFFObjcopy.h10
-rw-r--r--llvm/tools/llvm-objcopy/COFF/Reader.h1
-rw-r--r--llvm/tools/llvm-objcopy/COFF/Writer.cpp24
-rw-r--r--llvm/tools/llvm-objcopy/COFF/Writer.h9
-rw-r--r--llvm/tools/llvm-objcopy/CommonConfig.h (renamed from llvm/tools/llvm-objcopy/CopyConfig.h)131
-rw-r--r--llvm/tools/llvm-objcopy/CommonOpts.td6
-rw-r--r--llvm/tools/llvm-objcopy/ConfigManager.cpp (renamed from llvm/tools/llvm-objcopy/CopyConfig.cpp)300
-rw-r--r--llvm/tools/llvm-objcopy/ConfigManager.h80
-rw-r--r--llvm/tools/llvm-objcopy/ELF/ELFConfig.cpp133
-rw-r--r--llvm/tools/llvm-objcopy/ELF/ELFConfig.h28
-rw-r--r--llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp302
-rw-r--r--llvm/tools/llvm-objcopy/ELF/ELFObjcopy.h20
-rw-r--r--llvm/tools/llvm-objcopy/ELF/Object.cpp190
-rw-r--r--llvm/tools/llvm-objcopy/ELF/Object.h29
-rw-r--r--llvm/tools/llvm-objcopy/MachO/MachOConfig.h21
-rw-r--r--llvm/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp21
-rw-r--r--llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp111
-rw-r--r--llvm/tools/llvm-objcopy/MachO/MachOObjcopy.h13
-rw-r--r--llvm/tools/llvm-objcopy/MachO/MachOReader.cpp67
-rw-r--r--llvm/tools/llvm-objcopy/MachO/MachOReader.h1
-rw-r--r--llvm/tools/llvm-objcopy/MachO/MachOWriter.cpp79
-rw-r--r--llvm/tools/llvm-objcopy/MachO/MachOWriter.h9
-rw-r--r--llvm/tools/llvm-objcopy/MachO/Object.cpp12
-rw-r--r--llvm/tools/llvm-objcopy/MachO/Object.h6
-rw-r--r--llvm/tools/llvm-objcopy/MultiFormatConfig.h37
-rw-r--r--llvm/tools/llvm-objcopy/ObjcopyOpts.td13
-rw-r--r--llvm/tools/llvm-objcopy/llvm-objcopy.cpp260
-rw-r--r--llvm/tools/llvm-objcopy/llvm-objcopy.h6
-rw-r--r--llvm/tools/llvm-objcopy/wasm/WasmConfig.h21
-rw-r--r--llvm/tools/llvm-objcopy/wasm/WasmObjcopy.cpp103
-rw-r--r--llvm/tools/llvm-objcopy/wasm/WasmObjcopy.h9
-rw-r--r--llvm/tools/llvm-objcopy/wasm/Writer.cpp23
-rw-r--r--llvm/tools/llvm-objcopy/wasm/Writer.h5
-rw-r--r--llvm/tools/llvm-objdump/COFFDump.cpp3
-rw-r--r--llvm/tools/llvm-objdump/ELFDump.cpp14
-rw-r--r--llvm/tools/llvm-objdump/MachODump.cpp411
-rw-r--r--llvm/tools/llvm-objdump/MachODump.h42
-rw-r--r--llvm/tools/llvm-objdump/ObjdumpOptID.h13
-rw-r--r--llvm/tools/llvm-objdump/ObjdumpOpts.td323
-rw-r--r--llvm/tools/llvm-objdump/OtoolOpts.td68
-rw-r--r--llvm/tools/llvm-objdump/SourcePrinter.cpp483
-rw-r--r--llvm/tools/llvm-objdump/SourcePrinter.h166
-rw-r--r--llvm/tools/llvm-objdump/XCOFFDump.cpp24
-rw-r--r--llvm/tools/llvm-objdump/llvm-objdump.cpp1441
-rw-r--r--llvm/tools/llvm-objdump/llvm-objdump.h53
-rw-r--r--llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp8
-rw-r--r--llvm/tools/llvm-pdbutil/InputFile.cpp3
-rw-r--r--llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp5
-rw-r--r--llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp2
-rw-r--r--llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp5
-rw-r--r--llvm/tools/llvm-profdata/llvm-profdata.cpp212
-rw-r--r--llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp214
-rw-r--r--llvm/tools/llvm-readobj/ARMWinEHPrinter.h10
-rw-r--r--llvm/tools/llvm-readobj/COFFDumper.cpp44
-rw-r--r--llvm/tools/llvm-readobj/ELFDumper.cpp867
-rw-r--r--llvm/tools/llvm-readobj/MachODumper.cpp7
-rw-r--r--llvm/tools/llvm-readobj/ObjDumper.cpp37
-rw-r--r--llvm/tools/llvm-readobj/ObjDumper.h6
-rw-r--r--llvm/tools/llvm-readobj/Opts.td128
-rw-r--r--llvm/tools/llvm-readobj/WasmDumper.cpp8
-rw-r--r--llvm/tools/llvm-readobj/XCOFFDumper.cpp171
-rw-r--r--llvm/tools/llvm-readobj/llvm-readobj.cpp638
-rw-r--r--llvm/tools/llvm-readobj/llvm-readobj.h18
-rw-r--r--llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp124
-rw-r--r--llvm/tools/llvm-sim/llvm-sim.cpp149
-rw-r--r--llvm/tools/llvm-size/Opts.td32
-rw-r--r--llvm/tools/llvm-size/llvm-size.cpp189
-rw-r--r--llvm/tools/llvm-stress/llvm-stress.cpp25
-rw-r--r--llvm/tools/llvm-strings/Opts.td23
-rw-r--r--llvm/tools/llvm-strings/llvm-strings.cpp117
-rw-r--r--llvm/tools/llvm-symbolizer/Opts.td27
-rw-r--r--llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp134
-rw-r--r--llvm/tools/llvm-tapi-diff/DiffEngine.cpp556
-rw-r--r--llvm/tools/llvm-tapi-diff/DiffEngine.h169
-rw-r--r--llvm/tools/llvm-tapi-diff/llvm-tapi-diff.cpp89
-rw-r--r--llvm/tools/llvm-xray/xray-account.cpp2
-rw-r--r--llvm/tools/llvm-xray/xray-color-helper.cpp1
-rw-r--r--llvm/tools/llvm-xray/xray-color-helper.h3
-rw-r--r--llvm/tools/llvm-xray/xray-converter.cpp16
-rw-r--r--llvm/tools/llvm-xray/xray-extract.cpp2
-rw-r--r--llvm/tools/llvm-xray/xray-graph-diff.cpp7
-rw-r--r--llvm/tools/llvm-xray/xray-graph.cpp2
-rw-r--r--llvm/tools/opt/NewPMDriver.cpp53
-rw-r--r--llvm/tools/opt/NewPMDriver.h4
-rw-r--r--llvm/tools/opt/opt.cpp192
161 files changed, 8468 insertions, 5190 deletions
diff --git a/llvm/tools/bugpoint/CrashDebugger.cpp b/llvm/tools/bugpoint/CrashDebugger.cpp
index d06bca9a1258..2601ee318f7d 100644
--- a/llvm/tools/bugpoint/CrashDebugger.cpp
+++ b/llvm/tools/bugpoint/CrashDebugger.cpp
@@ -425,7 +425,7 @@ void simpleSimplifyCfg(Function &F, SmallVectorImpl<BasicBlock *> &BBs) {
}
/// ReduceCrashingBlocks reducer - This works by setting the terminators of
/// all terminators except the specified basic blocks to a 'ret' instruction,
-/// then running the simplify-cfg pass. This has the effect of chopping up
+/// then running the simplifycfg pass. This has the effect of chopping up
/// the CFG really fast which can reduce large functions quickly.
///
class ReduceCrashingBlocks : public ListReducer<const BasicBlock *> {
diff --git a/llvm/tools/bugpoint/FindBugs.cpp b/llvm/tools/bugpoint/FindBugs.cpp
index 2b1146da9680..771329024c09 100644
--- a/llvm/tools/bugpoint/FindBugs.cpp
+++ b/llvm/tools/bugpoint/FindBugs.cpp
@@ -41,7 +41,7 @@ BugDriver::runManyPasses(const std::vector<std::string> &AllPasses) {
//
// Step 1: Randomize the order of the optimizer passes.
//
- std::shuffle(PassesToRun.begin(), PassesToRun.end(), randomness);
+ llvm::shuffle(PassesToRun.begin(), PassesToRun.end(), randomness);
//
// Step 2: Run optimizer passes on the program and check for success.
diff --git a/llvm/tools/bugpoint/ListReducer.h b/llvm/tools/bugpoint/ListReducer.h
index 04f2207a31ed..06f8ddb25534 100644
--- a/llvm/tools/bugpoint/ListReducer.h
+++ b/llvm/tools/bugpoint/ListReducer.h
@@ -92,7 +92,7 @@ template <typename ElTy> struct ListReducer {
// distribution (improving the speed of convergence).
if (ShufflingEnabled && NumOfIterationsWithoutProgress > MaxIterations) {
std::vector<ElTy> ShuffledList(TheList);
- std::shuffle(ShuffledList.begin(), ShuffledList.end(), randomness);
+ llvm::shuffle(ShuffledList.begin(), ShuffledList.end(), randomness);
errs() << "\n\n*** Testing shuffled set...\n\n";
// Check that random shuffle doesn't lose the bug
Expected<TestResult> Result = doTest(ShuffledList, empty);
diff --git a/llvm/tools/bugpoint/ToolRunner.cpp b/llvm/tools/bugpoint/ToolRunner.cpp
index c4ea1dad122b..b81ab07980dd 100644
--- a/llvm/tools/bugpoint/ToolRunner.cpp
+++ b/llvm/tools/bugpoint/ToolRunner.cpp
@@ -607,12 +607,12 @@ AbstractInterpreter::createJIT(const char *Argv0, std::string &Message,
static bool IsARMArchitecture(std::vector<StringRef> Args) {
for (size_t I = 0; I < Args.size(); ++I) {
- if (!Args[I].equals_lower("-arch"))
+ if (!Args[I].equals_insensitive("-arch"))
continue;
++I;
if (I == Args.size())
break;
- if (Args[I].startswith_lower("arm"))
+ if (Args[I].startswith_insensitive("arm"))
return true;
}
diff --git a/llvm/tools/llc/llc.cpp b/llvm/tools/llc/llc.cpp
index 48f0adf7c726..6a1e2bae2096 100644
--- a/llvm/tools/llc/llc.cpp
+++ b/llvm/tools/llc/llc.cpp
@@ -126,9 +126,10 @@ static cl::opt<bool> DisableSimplifyLibCalls("disable-simplify-libcalls",
static cl::opt<bool> ShowMCEncoding("show-mc-encoding", cl::Hidden,
cl::desc("Show encoding in .s output"));
-static cl::opt<bool> EnableDwarfDirectory(
- "enable-dwarf-directory", cl::Hidden,
- cl::desc("Use .file directives with an explicit directory."));
+static cl::opt<bool>
+ DwarfDirectory("dwarf-directory", cl::Hidden,
+ cl::desc("Use .file directives with an explicit directory"),
+ cl::init(true));
static cl::opt<bool> AsmVerbose("asm-verbose",
cl::desc("Add comments to directives."),
@@ -276,7 +277,7 @@ static std::unique_ptr<ToolOutputFile> GetOutputStream(const char *TargetName,
std::error_code EC;
sys::fs::OpenFlags OpenFlags = sys::fs::OF_None;
if (!Binary)
- OpenFlags |= sys::fs::OF_Text;
+ OpenFlags |= sys::fs::OF_TextWithCRLF;
auto FDOut = std::make_unique<ToolOutputFile>(OutputFilename, EC, OpenFlags);
if (EC) {
reportError(EC.message());
@@ -290,6 +291,22 @@ struct LLCDiagnosticHandler : public DiagnosticHandler {
bool *HasError;
LLCDiagnosticHandler(bool *HasErrorPtr) : HasError(HasErrorPtr) {}
bool handleDiagnostics(const DiagnosticInfo &DI) override {
+ if (DI.getKind() == llvm::DK_SrcMgr) {
+ const auto &DISM = cast<DiagnosticInfoSrcMgr>(DI);
+ const SMDiagnostic &SMD = DISM.getSMDiag();
+
+ if (SMD.getKind() == SourceMgr::DK_Error)
+ *HasError = true;
+
+ SMD.print(nullptr, errs());
+
+ // For testing purposes, we print the LocCookie here.
+ if (DISM.isInlineAsmDiag() && DISM.getLocCookie())
+ WithColor::note() << "!srcloc = " << DISM.getLocCookie() << "\n";
+
+ return true;
+ }
+
if (DI.getSeverity() == DS_Error)
*HasError = true;
@@ -305,19 +322,6 @@ struct LLCDiagnosticHandler : public DiagnosticHandler {
}
};
-static void InlineAsmDiagHandler(const SMDiagnostic &SMD, void *Context,
- unsigned LocCookie) {
- bool *HasError = static_cast<bool *>(Context);
- if (SMD.getKind() == SourceMgr::DK_Error)
- *HasError = true;
-
- SMD.print(nullptr, errs());
-
- // For testing purposes, we print the LocCookie here.
- if (LocCookie)
- WithColor::note() << "!srcloc = " << LocCookie << "\n";
-}
-
// main - Entry point for the llc compiler.
//
int main(int argc, char **argv) {
@@ -349,8 +353,10 @@ int main(int argc, char **argv) {
initializeVectorization(*Registry);
initializeScalarizeMaskedMemIntrinLegacyPassPass(*Registry);
initializeExpandReductionsPass(*Registry);
+ initializeExpandVectorPredicationPass(*Registry);
initializeHardwareLoopsPass(*Registry);
initializeTransformUtils(*Registry);
+ initializeReplaceWithVeclibLegacyPass(*Registry);
// Initialize debugging passes.
initializeScavengerTestPass(*Registry);
@@ -366,7 +372,6 @@ int main(int argc, char **argv) {
bool HasError = false;
Context.setDiagnosticHandler(
std::make_unique<LLCDiagnosticHandler>(&HasError));
- Context.setInlineAsmDiagnosticHandler(InlineAsmDiagHandler, &HasError);
Expected<std::unique_ptr<ToolOutputFile>> RemarksFileOrErr =
setupLLVMOptimizationRemarks(Context, RemarksFilename, RemarksPasses,
@@ -469,7 +474,7 @@ static int compileModule(char **argv, LLVMContext &Context) {
TargetMachine::parseBinutilsVersion(BinutilsVersion);
Options.DisableIntegratedAS = NoIntegratedAssembler;
Options.MCOptions.ShowMCEncoding = ShowMCEncoding;
- Options.MCOptions.MCUseDwarfDirectory = EnableDwarfDirectory;
+ Options.MCOptions.MCUseDwarfDirectory = DwarfDirectory;
Options.MCOptions.AsmVerbose = AsmVerbose;
Options.MCOptions.PreserveAsmComments = PreserveComments;
Options.MCOptions.IASSearchPaths = IncludeDirs;
diff --git a/llvm/tools/lli/ExecutionUtils.cpp b/llvm/tools/lli/ExecutionUtils.cpp
new file mode 100644
index 000000000000..55370ed40f2b
--- /dev/null
+++ b/llvm/tools/lli/ExecutionUtils.cpp
@@ -0,0 +1,146 @@
+//===---- ExecutionUtils.cpp - Utilities for executing functions in lli ---===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ExecutionUtils.h"
+
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <cstdint>
+#include <vector>
+
+// Declarations follow the GDB JIT interface (version 1, 2009) and must match
+// those of the DYLD used for testing. See:
+//
+// llvm/lib/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.cpp
+// llvm/lib/ExecutionEngine/GDBRegistrationListener.cpp
+//
+typedef enum {
+ JIT_NOACTION = 0,
+ JIT_REGISTER_FN,
+ JIT_UNREGISTER_FN
+} jit_actions_t;
+
+struct jit_code_entry {
+ struct jit_code_entry *next_entry;
+ struct jit_code_entry *prev_entry;
+ const char *symfile_addr;
+ uint64_t symfile_size;
+};
+
+struct jit_descriptor {
+ uint32_t version;
+ // This should be jit_actions_t, but we want to be specific about the
+ // bit-width.
+ uint32_t action_flag;
+ struct jit_code_entry *relevant_entry;
+ struct jit_code_entry *first_entry;
+};
+
+namespace llvm {
+
+template <typename... Ts> static void outsv(const char *Fmt, Ts &&...Vals) {
+ outs() << formatv(Fmt, Vals...);
+}
+
+static const char *actionFlagToStr(uint32_t ActionFlag) {
+ switch (ActionFlag) {
+ case JIT_NOACTION:
+ return "JIT_NOACTION";
+ case JIT_REGISTER_FN:
+ return "JIT_REGISTER_FN";
+ case JIT_UNREGISTER_FN:
+ return "JIT_UNREGISTER_FN";
+ }
+ return "<invalid action_flag>";
+}
+
+// Sample output:
+//
+// Reading __jit_debug_descriptor at 0x0000000000404048
+//
+// Version: 0
+// Action: JIT_REGISTER_FN
+//
+// Entry Symbol File Size Previous Entry
+// [ 0] 0x0000000000451290 0x0000000000002000 200 0x0000000000000000
+// [ 1] 0x0000000000451260 0x0000000000001000 100 0x0000000000451290
+// ...
+//
+static void dumpDebugDescriptor(void *Addr) {
+ outsv("Reading __jit_debug_descriptor at {0}\n\n", Addr);
+
+ jit_descriptor *Descriptor = reinterpret_cast<jit_descriptor *>(Addr);
+ outsv("Version: {0}\n", Descriptor->version);
+ outsv("Action: {0}\n\n", actionFlagToStr(Descriptor->action_flag));
+ outsv("{0,11} {1,24} {2,15} {3,14}\n", "Entry", "Symbol File", "Size",
+ "Previous Entry");
+
+ unsigned Idx = 0;
+ for (auto *Entry = Descriptor->first_entry; Entry; Entry = Entry->next_entry)
+ outsv("[{0,2}] {1:X16} {2:X16} {3,8:D} {4}\n", Idx++, Entry,
+ reinterpret_cast<const void *>(Entry->symfile_addr),
+ Entry->symfile_size, Entry->prev_entry);
+}
+
+static LLIBuiltinFunctionGenerator *Generator = nullptr;
+
+static void dumpDebugObjects(void *Addr) {
+ jit_descriptor *Descriptor = reinterpret_cast<jit_descriptor *>(Addr);
+ for (auto *Entry = Descriptor->first_entry; Entry; Entry = Entry->next_entry)
+ Generator->appendDebugObject(Entry->symfile_addr, Entry->symfile_size);
+}
+
+LLIBuiltinFunctionGenerator::LLIBuiltinFunctionGenerator(
+ std::vector<BuiltinFunctionKind> Enabled, orc::MangleAndInterner &Mangle)
+ : TestOut(nullptr) {
+ Generator = this;
+ for (BuiltinFunctionKind F : Enabled) {
+ switch (F) {
+ case BuiltinFunctionKind::DumpDebugDescriptor:
+ expose(Mangle("__dump_jit_debug_descriptor"), &dumpDebugDescriptor);
+ break;
+ case BuiltinFunctionKind::DumpDebugObjects:
+ expose(Mangle("__dump_jit_debug_objects"), &dumpDebugObjects);
+ TestOut = createToolOutput();
+ break;
+ }
+ }
+}
+
+Error LLIBuiltinFunctionGenerator::tryToGenerate(
+ orc::LookupState &LS, orc::LookupKind K, orc::JITDylib &JD,
+ orc::JITDylibLookupFlags JDLookupFlags,
+ const orc::SymbolLookupSet &Symbols) {
+ orc::SymbolMap NewSymbols;
+ for (const auto &NameFlags : Symbols) {
+ auto It = BuiltinFunctions.find(NameFlags.first);
+ if (It != BuiltinFunctions.end())
+ NewSymbols.insert(*It);
+ }
+
+ if (NewSymbols.empty())
+ return Error::success();
+
+ return JD.define(absoluteSymbols(std::move(NewSymbols)));
+}
+
+// static
+std::unique_ptr<ToolOutputFile>
+LLIBuiltinFunctionGenerator::createToolOutput() {
+ std::error_code EC;
+ auto TestOut = std::make_unique<ToolOutputFile>("-", EC, sys::fs::OF_None);
+ if (EC) {
+ errs() << "Error creating tool output file: " << EC.message() << '\n';
+ exit(1);
+ }
+ return TestOut;
+}
+
+} // namespace llvm
diff --git a/llvm/tools/lli/ExecutionUtils.h b/llvm/tools/lli/ExecutionUtils.h
new file mode 100644
index 000000000000..fcd1db05cca3
--- /dev/null
+++ b/llvm/tools/lli/ExecutionUtils.h
@@ -0,0 +1,60 @@
+//===- ExecutionUtils.h - Utilities for executing code in lli ---*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Contains utilities for executing code in lli.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLI_EXECUTIONUTILS_H
+#define LLVM_TOOLS_LLI_EXECUTIONUTILS_H
+
+#include "llvm/ExecutionEngine/JITSymbol.h"
+#include "llvm/ExecutionEngine/Orc/Core.h"
+#include "llvm/ExecutionEngine/Orc/Mangling.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/ToolOutputFile.h"
+
+#include <memory>
+#include <utility>
+
+namespace llvm {
+
+enum class BuiltinFunctionKind {
+ DumpDebugDescriptor,
+ DumpDebugObjects,
+};
+
+// Utility class to expose symbols for special-purpose functions to the JIT.
+class LLIBuiltinFunctionGenerator : public orc::DefinitionGenerator {
+public:
+ LLIBuiltinFunctionGenerator(std::vector<BuiltinFunctionKind> Enabled,
+ orc::MangleAndInterner &Mangle);
+
+ Error tryToGenerate(orc::LookupState &LS, orc::LookupKind K,
+ orc::JITDylib &JD, orc::JITDylibLookupFlags JDLookupFlags,
+ const orc::SymbolLookupSet &Symbols) override;
+
+ void appendDebugObject(const char *Addr, size_t Size) {
+ TestOut->os().write(Addr, Size);
+ }
+
+private:
+ orc::SymbolMap BuiltinFunctions;
+ std::unique_ptr<ToolOutputFile> TestOut;
+
+ template <typename T> void expose(orc::SymbolStringPtr Name, T *Handler) {
+ BuiltinFunctions[Name] = JITEvaluatedSymbol(
+ pointerToJITTargetAddress(Handler), JITSymbolFlags::Exported);
+ }
+
+ static std::unique_ptr<ToolOutputFile> createToolOutput();
+};
+
+} // end namespace llvm
+
+#endif // LLVM_TOOLS_LLI_EXECUTIONUTILS_H
diff --git a/llvm/tools/lli/lli.cpp b/llvm/tools/lli/lli.cpp
index 70c838126946..af614c01b9a8 100644
--- a/llvm/tools/lli/lli.cpp
+++ b/llvm/tools/lli/lli.cpp
@@ -12,6 +12,7 @@
//
//===----------------------------------------------------------------------===//
+#include "ExecutionUtils.h"
#include "RemoteJITUtils.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/Triple.h"
@@ -22,15 +23,21 @@
#include "llvm/ExecutionEngine/GenericValue.h"
#include "llvm/ExecutionEngine/Interpreter.h"
#include "llvm/ExecutionEngine/JITEventListener.h"
+#include "llvm/ExecutionEngine/JITSymbol.h"
#include "llvm/ExecutionEngine/MCJIT.h"
#include "llvm/ExecutionEngine/ObjectCache.h"
+#include "llvm/ExecutionEngine/Orc/DebugObjectManagerPlugin.h"
#include "llvm/ExecutionEngine/Orc/DebugUtils.h"
+#include "llvm/ExecutionEngine/Orc/EPCDebugObjectRegistrar.h"
+#include "llvm/ExecutionEngine/Orc/EPCEHFrameRegistrar.h"
#include "llvm/ExecutionEngine/Orc/ExecutionUtils.h"
#include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h"
#include "llvm/ExecutionEngine/Orc/LLJIT.h"
-#include "llvm/ExecutionEngine/Orc/MachOPlatform.h"
#include "llvm/ExecutionEngine/Orc/OrcRemoteTargetClient.h"
#include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h"
+#include "llvm/ExecutionEngine/Orc/SymbolStringPool.h"
+#include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h"
+#include "llvm/ExecutionEngine/Orc/TargetProcess/RegisterEHFrames.h"
#include "llvm/ExecutionEngine/Orc/TargetProcess/TargetExecutionUtils.h"
#include "llvm/ExecutionEngine/SectionMemoryManager.h"
#include "llvm/IR/IRBuilder.h"
@@ -76,7 +83,8 @@ static codegen::RegisterCodeGenFlags CGF;
namespace {
- enum class JITKind { MCJIT, OrcLazy };
+ enum class JITKind { MCJIT, Orc, OrcLazy };
+ enum class JITLinkerKind { Default, RuntimeDyld, JITLink };
cl::opt<std::string>
InputFile(cl::desc("<input bitcode>"), cl::Positional, cl::init("-"));
@@ -90,11 +98,22 @@ namespace {
cl::opt<JITKind> UseJITKind(
"jit-kind", cl::desc("Choose underlying JIT kind."),
- cl::init(JITKind::MCJIT),
+ cl::init(JITKind::Orc),
cl::values(clEnumValN(JITKind::MCJIT, "mcjit", "MCJIT"),
+ clEnumValN(JITKind::Orc, "orc", "Orc JIT"),
clEnumValN(JITKind::OrcLazy, "orc-lazy",
"Orc-based lazy JIT.")));
+ cl::opt<JITLinkerKind>
+ JITLinker("jit-linker", cl::desc("Choose the dynamic linker/loader."),
+ cl::init(JITLinkerKind::Default),
+ cl::values(clEnumValN(JITLinkerKind::Default, "default",
+ "Default for platform and JIT-kind"),
+ clEnumValN(JITLinkerKind::RuntimeDyld, "rtdyld",
+ "RuntimeDyld"),
+ clEnumValN(JITLinkerKind::JITLink, "jitlink",
+ "Orc-specific linker")));
+
cl::opt<unsigned>
LazyJITCompileThreads("compile-threads",
cl::desc("Choose the number of compile threads "
@@ -208,7 +227,7 @@ namespace {
cl::desc("Do not resolve lli process symbols in JIT'd code"),
cl::init(false));
- enum class LLJITPlatform { DetectHost, GenericIR, MachO };
+ enum class LLJITPlatform { Inactive, DetectHost, GenericIR };
cl::opt<LLJITPlatform>
Platform("lljit-platform", cl::desc("Platform to use with LLJIT"),
@@ -217,8 +236,8 @@ namespace {
"Select based on JIT target triple"),
clEnumValN(LLJITPlatform::GenericIR, "GenericIR",
"Use LLJITGenericIRPlatform"),
- clEnumValN(LLJITPlatform::MachO, "MachO",
- "Use LLJITMachOPlatform")),
+ clEnumValN(LLJITPlatform::Inactive, "Inactive",
+ "Disable platform support explicitly")),
cl::Hidden);
enum class DumpKind {
@@ -243,9 +262,28 @@ namespace {
"will overwrite existing files).")),
cl::Hidden);
+ cl::list<BuiltinFunctionKind> GenerateBuiltinFunctions(
+ "generate",
+ cl::desc("Provide built-in functions for access by JITed code "
+ "(jit-kind=orc-lazy only)"),
+ cl::values(clEnumValN(BuiltinFunctionKind::DumpDebugDescriptor,
+ "__dump_jit_debug_descriptor",
+ "Dump __jit_debug_descriptor contents to stdout"),
+ clEnumValN(BuiltinFunctionKind::DumpDebugObjects,
+ "__dump_jit_debug_objects",
+ "Dump __jit_debug_descriptor in-memory debug "
+ "objects as tool output")),
+ cl::Hidden);
+
ExitOnError ExitOnErr;
}
+LLVM_ATTRIBUTE_USED void linkComponents() {
+ errs() << (void *)&llvm_orc_registerEHFrameSectionWrapper
+ << (void *)&llvm_orc_deregisterEHFrameSectionWrapper
+ << (void *)&llvm_orc_registerJITLoaderGDBWrapper;
+}
+
//===----------------------------------------------------------------------===//
// Object cache
//
@@ -288,7 +326,8 @@ public:
return nullptr;
// Load the object from the cache filename
ErrorOr<std::unique_ptr<MemoryBuffer>> IRObjectBuffer =
- MemoryBuffer::getFile(CacheName, -1, false);
+ MemoryBuffer::getFile(CacheName, /*IsText=*/false,
+ /*RequiresNullTerminator=*/false);
// If the file isn't there, that's OK.
if (!IRObjectBuffer)
return nullptr;
@@ -378,7 +417,7 @@ static void reportError(SMDiagnostic Err, const char *ProgName) {
}
Error loadDylibs();
-int runOrcLazyJIT(const char *ProgName);
+int runOrcJIT(const char *ProgName);
void disallowOrcOptions();
//===----------------------------------------------------------------------===//
@@ -405,11 +444,12 @@ int main(int argc, char **argv, char * const *envp) {
ExitOnErr(loadDylibs());
- if (UseJITKind == JITKind::OrcLazy)
- return runOrcLazyJIT(argv[0]);
- else
+ if (UseJITKind == JITKind::MCJIT)
disallowOrcOptions();
+ else
+ return runOrcJIT(argv[0]);
+ // Old lli implementation based on ExecutionEngine and MCJIT.
LLVMContext Context;
// Load the bitcode...
@@ -677,7 +717,8 @@ int main(int argc, char **argv, char * const *envp) {
}
// Create a remote target client running over the channel.
- llvm::orc::ExecutionSession ES;
+ llvm::orc::ExecutionSession ES(
+ std::make_unique<orc::UnsupportedExecutorProcessControl>());
ES.setErrorReporter([&](Error Err) { ExitOnErr(std::move(Err)); });
typedef orc::remote::OrcRemoteTargetClient MyRemote;
auto R = ExitOnErr(MyRemote::Create(*C, ES));
@@ -748,7 +789,8 @@ static std::function<void(Module &)> createDebugDumper() {
case DumpKind::DumpModsToDisk:
return [](Module &M) {
std::error_code EC;
- raw_fd_ostream Out(M.getModuleIdentifier() + ".ll", EC, sys::fs::OF_Text);
+ raw_fd_ostream Out(M.getModuleIdentifier() + ".ll", EC,
+ sys::fs::OF_TextWithCRLF);
if (EC) {
errs() << "Couldn't open " << M.getModuleIdentifier()
<< " for dumping.\nError:" << EC.message() << "\n";
@@ -791,7 +833,7 @@ loadModule(StringRef Path, orc::ThreadSafeContext TSCtx) {
return orc::ThreadSafeModule(std::move(M), std::move(TSCtx));
}
-int runOrcLazyJIT(const char *ProgName) {
+int runOrcJIT(const char *ProgName) {
// Start setting up the JIT environment.
// Parse the main module.
@@ -829,6 +871,17 @@ int runOrcLazyJIT(const char *ProgName) {
.setRelocationModel(codegen::getExplicitRelocModel())
.setCodeModel(codegen::getExplicitCodeModel());
+ // FIXME: Setting a dummy call-through manager in non-lazy mode prevents the
+ // JIT builder to instantiate a default (which would fail with an error for
+ // unsupported architectures).
+ if (UseJITKind != JITKind::OrcLazy) {
+ auto ES = std::make_unique<orc::ExecutionSession>(
+ ExitOnErr(orc::SelfExecutorProcessControl::Create()));
+ Builder.setLazyCallthroughManager(
+ std::make_unique<orc::LazyCallThroughManager>(*ES, 0, nullptr));
+ Builder.setExecutionSession(std::move(ES));
+ }
+
Builder.setLazyCompileFailureAddr(
pointerToJITTargetAddress(exitOnLazyCallThroughFailure));
Builder.setNumCompileThreads(LazyJITCompileThreads);
@@ -859,32 +912,43 @@ int runOrcLazyJIT(const char *ProgName) {
// Set up LLJIT platform.
{
LLJITPlatform P = Platform;
- if (P == LLJITPlatform::DetectHost) {
- if (TT->isOSBinFormatMachO())
- P = LLJITPlatform::MachO;
- else
- P = LLJITPlatform::GenericIR;
- }
+ if (P == LLJITPlatform::DetectHost)
+ P = LLJITPlatform::GenericIR;
switch (P) {
case LLJITPlatform::GenericIR:
// Nothing to do: LLJITBuilder will use this by default.
break;
- case LLJITPlatform::MachO:
- Builder.setPlatformSetUp(orc::setUpMachOPlatform);
- ExitOnErr(orc::enableObjCRegistration("libobjc.dylib"));
+ case LLJITPlatform::Inactive:
+ Builder.setPlatformSetUp(orc::setUpInactivePlatform);
break;
default:
llvm_unreachable("Unrecognized platform value");
}
}
+ std::unique_ptr<orc::ExecutorProcessControl> EPC = nullptr;
+ if (JITLinker == JITLinkerKind::JITLink) {
+ EPC = ExitOnErr(orc::SelfExecutorProcessControl::Create(
+ std::make_shared<orc::SymbolStringPool>()));
+
+ Builder.setObjectLinkingLayerCreator([&EPC](orc::ExecutionSession &ES,
+ const Triple &) {
+ auto L = std::make_unique<orc::ObjectLinkingLayer>(ES, EPC->getMemMgr());
+ L->addPlugin(std::make_unique<orc::EHFrameRegistrationPlugin>(
+ ES, ExitOnErr(orc::EPCEHFrameRegistrar::Create(ES))));
+ L->addPlugin(std::make_unique<orc::DebugObjectManagerPlugin>(
+ ES, ExitOnErr(orc::createJITLoaderGDBRegistrar(ES))));
+ return L;
+ });
+ }
+
auto J = ExitOnErr(Builder.create());
- if (TT->isOSBinFormatELF())
- static_cast<llvm::orc::RTDyldObjectLinkingLayer &>(J->getObjLinkingLayer())
- .registerJITEventListener(
- *JITEventListener::createGDBRegistrationListener());
+ auto *ObjLayer = &J->getObjLinkingLayer();
+ if (auto *RTDyldObjLayer = dyn_cast<orc::RTDyldObjectLinkingLayer>(ObjLayer))
+ RTDyldObjLayer->registerJITEventListener(
+ *JITEventListener::createGDBRegistrationListener());
if (PerModuleLazy)
J->setPartitionFunction(orc::CompileOnDemandLayer::compileWholeModule);
@@ -916,8 +980,22 @@ int runOrcLazyJIT(const char *ProgName) {
return Name != MainName;
})));
+ if (GenerateBuiltinFunctions.size() > 0)
+ J->getMainJITDylib().addGenerator(
+ std::make_unique<LLIBuiltinFunctionGenerator>(GenerateBuiltinFunctions,
+ Mangle));
+
+ // Regular modules are greedy: They materialize as a whole and trigger
+ // materialization for all required symbols recursively. Lazy modules go
+ // through partitioning and they replace outgoing calls with reexport stubs
+ // that resolve on call-through.
+ auto AddModule = [&](orc::JITDylib &JD, orc::ThreadSafeModule M) {
+ return UseJITKind == JITKind::OrcLazy ? J->addLazyIRModule(JD, std::move(M))
+ : J->addIRModule(JD, std::move(M));
+ };
+
// Add the main module.
- ExitOnErr(J->addLazyIRModule(std::move(MainModule)));
+ ExitOnErr(AddModule(J->getMainJITDylib(), std::move(MainModule)));
// Create JITDylibs and add any extra modules.
{
@@ -945,7 +1023,7 @@ int runOrcLazyJIT(const char *ProgName) {
assert(EMIdx != 0 && "ExtraModule should have index > 0");
auto JDItr = std::prev(IdxToDylib.lower_bound(EMIdx));
auto &JD = *JDItr->second;
- ExitOnErr(J->addLazyIRModule(JD, std::move(M)));
+ ExitOnErr(AddModule(JD, std::move(M)));
}
for (auto EAItr = ExtraArchives.begin(), EAEnd = ExtraArchives.end();
@@ -978,13 +1056,19 @@ int runOrcLazyJIT(const char *ProgName) {
AltEntryThreads.push_back(std::thread([EntryPoint]() { EntryPoint(); }));
}
- // Run main.
- auto MainSym = ExitOnErr(J->lookup("main"));
+ // Resolve and run the main function.
+ JITEvaluatedSymbol MainSym = ExitOnErr(J->lookup(EntryFunc));
+ int Result;
- typedef int (*MainFnPtr)(int, char *[]);
- auto Result = orc::runAsMain(
- jitTargetAddressToFunction<MainFnPtr>(MainSym.getAddress()), InputArgv,
- StringRef(InputFile));
+ if (EPC) {
+ // ExecutorProcessControl-based execution with JITLink.
+ Result = ExitOnErr(EPC->runAsMain(MainSym.getAddress(), InputArgv));
+ } else {
+ // Manual in-process execution with RuntimeDyld.
+ using MainFnTy = int(int, char *[]);
+ auto MainFn = jitTargetAddressToFunction<MainFnTy *>(MainSym.getAddress());
+ Result = orc::runAsMain(MainFn, InputArgv, StringRef(InputFile));
+ }
// Wait for -entry-point threads.
for (auto &AltEntryThread : AltEntryThreads)
diff --git a/llvm/tools/llvm-ar/llvm-ar.cpp b/llvm/tools/llvm-ar/llvm-ar.cpp
index 4c26c8cad3fa..0e1dce6bc2e8 100644
--- a/llvm/tools/llvm-ar/llvm-ar.cpp
+++ b/llvm/tools/llvm-ar/llvm-ar.cpp
@@ -126,9 +126,9 @@ MODIFIERS:
)";
static void printHelpMessage() {
- if (Stem.contains_lower("ranlib"))
+ if (Stem.contains_insensitive("ranlib"))
outs() << RanlibHelp;
- else if (Stem.contains_lower("ar"))
+ else if (Stem.contains_insensitive("ar"))
outs() << ArHelp;
}
@@ -270,7 +270,8 @@ static void getArchive() {
}
static object::Archive &readLibrary(const Twine &Library) {
- auto BufOrErr = MemoryBuffer::getFile(Library, -1, false);
+ auto BufOrErr = MemoryBuffer::getFile(Library, /*IsText=*/false,
+ /*RequiresNullTerminator=*/false);
failIfError(BufOrErr.getError(), "could not open library " + Library);
ArchiveBuffers.push_back(std::move(*BufOrErr));
auto LibOrErr =
@@ -995,8 +996,8 @@ static void performOperation(ArchiveOperation Operation,
static int performOperation(ArchiveOperation Operation,
std::vector<NewArchiveMember> *NewMembers) {
// Create or open the archive object.
- ErrorOr<std::unique_ptr<MemoryBuffer>> Buf =
- MemoryBuffer::getFile(ArchiveName, -1, false);
+ ErrorOr<std::unique_ptr<MemoryBuffer>> Buf = MemoryBuffer::getFile(
+ ArchiveName, /*IsText=*/false, /*RequiresNullTerminator=*/false);
std::error_code EC = Buf.getError();
if (EC && EC != errc::no_such_file_or_directory)
fail("unable to open '" + ArchiveName + "': " + EC.message());
@@ -1275,7 +1276,7 @@ int main(int argc, char **argv) {
// Lib.exe -> lib (see D44808, MSBuild runs Lib.exe)
// dlltool.exe -> dlltool
// arm-pokymllib32-linux-gnueabi-llvm-ar-10 -> ar
- auto I = Stem.rfind_lower(Tool);
+ auto I = Stem.rfind_insensitive(Tool);
return I != StringRef::npos &&
(I + Tool.size() == Stem.size() || !isAlnum(Stem[I + Tool.size()]));
};
diff --git a/llvm/tools/llvm-as/llvm-as.cpp b/llvm/tools/llvm-as/llvm-as.cpp
index f2b52890a7f5..307a7f9b7999 100644
--- a/llvm/tools/llvm-as/llvm-as.cpp
+++ b/llvm/tools/llvm-as/llvm-as.cpp
@@ -115,9 +115,9 @@ static void WriteOutputFile(const Module *M, const ModuleSummaryIndex *Index) {
int main(int argc, char **argv) {
InitLLVM X(argc, argv);
- LLVMContext Context;
cl::HideUnrelatedOptions(AsCat);
cl::ParseCommandLineOptions(argc, argv, "llvm .ll -> .bc assembler\n");
+ LLVMContext Context;
// Parse the file now...
SMDiagnostic Err;
diff --git a/llvm/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp b/llvm/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp
index 639a6d1ec02c..f4851bfb2a9c 100644
--- a/llvm/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp
+++ b/llvm/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp
@@ -32,37 +32,48 @@
#include "llvm/Support/Error.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/WithColor.h"
#include "llvm/Support/raw_ostream.h"
#include <memory>
using namespace llvm;
-static cl::opt<std::string>
- InputFilename(cl::Positional, cl::desc("<input bitcode>"), cl::init("-"));
+static cl::OptionCategory BCAnalyzerCategory("BC Analyzer Options");
+
+static cl::opt<std::string> InputFilename(cl::Positional,
+ cl::desc("<input bitcode>"),
+ cl::init("-"),
+ cl::cat(BCAnalyzerCategory));
-static cl::opt<bool> Dump("dump", cl::desc("Dump low level bitcode trace"));
+static cl::opt<bool> Dump("dump", cl::desc("Dump low level bitcode trace"),
+ cl::cat(BCAnalyzerCategory));
//===----------------------------------------------------------------------===//
// Bitcode specific analysis.
//===----------------------------------------------------------------------===//
static cl::opt<bool> NoHistogram("disable-histogram",
- cl::desc("Do not print per-code histogram"));
+ cl::desc("Do not print per-code histogram"),
+ cl::cat(BCAnalyzerCategory));
static cl::opt<bool> NonSymbolic("non-symbolic",
cl::desc("Emit numeric info in dump even if"
- " symbolic info is available"));
+ " symbolic info is available"),
+ cl::cat(BCAnalyzerCategory));
static cl::opt<std::string>
BlockInfoFilename("block-info",
- cl::desc("Use the BLOCK_INFO from the given file"));
+ cl::desc("Use the BLOCK_INFO from the given file"),
+ cl::cat(BCAnalyzerCategory));
static cl::opt<bool>
ShowBinaryBlobs("show-binary-blobs",
- cl::desc("Print binary blobs using hex escapes"));
+ cl::desc("Print binary blobs using hex escapes"),
+ cl::cat(BCAnalyzerCategory));
static cl::opt<std::string> CheckHash(
"check-hash",
- cl::desc("Check module hash using the argument as a string table"));
+ cl::desc("Check module hash using the argument as a string table"),
+ cl::cat(BCAnalyzerCategory));
static Error reportError(StringRef Message) {
return createStringError(std::errc::illegal_byte_sequence, Message.data());
@@ -85,6 +96,8 @@ static Expected<std::unique_ptr<MemoryBuffer>> openBitcodeFile(StringRef Path) {
int main(int argc, char **argv) {
InitLLVM X(argc, argv);
+
+ cl::HideUnrelatedOptions({&BCAnalyzerCategory, &getColorCategory()});
cl::ParseCommandLineOptions(argc, argv, "llvm-bcanalyzer file analyzer\n");
ExitOnError ExitOnErr("llvm-bcanalyzer: ");
diff --git a/llvm/tools/llvm-cov/CodeCoverage.cpp b/llvm/tools/llvm-cov/CodeCoverage.cpp
index baa968820b63..02c0106cbc29 100644
--- a/llvm/tools/llvm-cov/CodeCoverage.cpp
+++ b/llvm/tools/llvm-cov/CodeCoverage.cpp
@@ -80,6 +80,12 @@ private:
/// directory, recursively collect all of the paths within the directory.
void collectPaths(const std::string &Path);
+ /// Check if the two given files are the same file.
+ bool isEquivalentFile(StringRef FilePath1, StringRef FilePath2);
+
+ /// Retrieve a file status with a cache.
+ Optional<sys::fs::file_status> getFileStatus(StringRef FilePath);
+
/// Return a memory buffer for the given source file.
ErrorOr<const MemoryBuffer &> getSourceFile(StringRef SourceFile);
@@ -153,6 +159,9 @@ private:
/// remapped to, when using -path-equivalence.
Optional<std::pair<std::string, std::string>> PathRemapping;
+ /// File status cache used when finding the same file.
+ StringMap<Optional<sys::fs::file_status>> FileStatusCache;
+
/// The architecture the coverage mapping data targets.
std::vector<StringRef> CoverageArches;
@@ -239,6 +248,27 @@ void CodeCoverageTool::collectPaths(const std::string &Path) {
}
}
+Optional<sys::fs::file_status>
+CodeCoverageTool::getFileStatus(StringRef FilePath) {
+ auto It = FileStatusCache.try_emplace(FilePath);
+ auto &CachedStatus = It.first->getValue();
+ if (!It.second)
+ return CachedStatus;
+
+ sys::fs::file_status Status;
+ if (!sys::fs::status(FilePath, Status))
+ CachedStatus = Status;
+ return CachedStatus;
+}
+
+bool CodeCoverageTool::isEquivalentFile(StringRef FilePath1,
+ StringRef FilePath2) {
+ auto Status1 = getFileStatus(FilePath1);
+ auto Status2 = getFileStatus(FilePath2);
+ return Status1.hasValue() && Status2.hasValue() &&
+ sys::fs::equivalent(Status1.getValue(), Status2.getValue());
+}
+
ErrorOr<const MemoryBuffer &>
CodeCoverageTool::getSourceFile(StringRef SourceFile) {
// If we've remapped filenames, look up the real location for this file.
@@ -249,7 +279,7 @@ CodeCoverageTool::getSourceFile(StringRef SourceFile) {
SourceFile = Loc->second;
}
for (const auto &Files : LoadedSourceFiles)
- if (sys::fs::equivalent(SourceFile, Files.first))
+ if (isEquivalentFile(SourceFile, Files.first))
return *Files.second;
auto Buffer = MemoryBuffer::getFile(SourceFile);
if (auto EC = Buffer.getError()) {
@@ -404,7 +434,8 @@ std::unique_ptr<CoverageMapping> CodeCoverageTool::load() {
warning("profile data may be out of date - object is newer",
ObjectFilename);
auto CoverageOrErr =
- CoverageMapping::load(ObjectFilenames, PGOFilename, CoverageArches);
+ CoverageMapping::load(ObjectFilenames, PGOFilename, CoverageArches,
+ ViewOpts.CompilationDirectory);
if (Error E = CoverageOrErr.takeError()) {
error("Failed to load coverage: " + toString(std::move(E)),
join(ObjectFilenames.begin(), ObjectFilenames.end(), ", "));
@@ -445,7 +476,7 @@ void CodeCoverageTool::remapPathNames(const CoverageMapping &Coverage) {
SmallString<128> NativePath;
sys::path::native(Path, NativePath);
sys::path::remove_dots(NativePath, true);
- if (!sys::path::is_separator(NativePath.back()))
+ if (!NativePath.empty() && !sys::path::is_separator(NativePath.back()))
NativePath += sys::path::get_separator();
return NativePath.c_str();
};
@@ -709,6 +740,10 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
cl::alias NumThreadsA("j", cl::desc("Alias for --num-threads"),
cl::aliasopt(NumThreads));
+ cl::opt<std::string> CompilationDirectory(
+ "compilation-dir", cl::init(""),
+ cl::desc("Directory used as a base for relative coverage mapping paths"));
+
auto commandLineParser = [&, this](int argc, const char **argv) -> int {
cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n");
ViewOpts.Debug = DebugDump;
@@ -843,6 +878,7 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
ViewOpts.ShowInstantiationSummary = InstantiationSummary;
ViewOpts.ExportSummaryOnly = SummaryOnly;
ViewOpts.NumThreads = NumThreads;
+ ViewOpts.CompilationDirectory = CompilationDirectory;
return 0;
};
diff --git a/llvm/tools/llvm-cov/CoverageExporterJson.cpp b/llvm/tools/llvm-cov/CoverageExporterJson.cpp
index d1446f379f00..d341abe8dfc8 100644
--- a/llvm/tools/llvm-cov/CoverageExporterJson.cpp
+++ b/llvm/tools/llvm-cov/CoverageExporterJson.cpp
@@ -123,8 +123,7 @@ collectNestedBranches(const coverage::CoverageMapping &Coverage,
// Recursively collect branches from nested expansions.
auto NestedExpansions = ExpansionCoverage.getExpansions();
auto NestedExBranches = collectNestedBranches(Coverage, NestedExpansions);
- Branches.insert(Branches.end(), NestedExBranches.begin(),
- NestedExBranches.end());
+ append_range(Branches, NestedExBranches);
// Add branches from this level of expansion.
auto ExBranches = ExpansionCoverage.getBranches();
diff --git a/llvm/tools/llvm-cov/CoverageExporterLcov.cpp b/llvm/tools/llvm-cov/CoverageExporterLcov.cpp
index 99ca037e7b5e..6cf5d9285b90 100644
--- a/llvm/tools/llvm-cov/CoverageExporterLcov.cpp
+++ b/llvm/tools/llvm-cov/CoverageExporterLcov.cpp
@@ -91,8 +91,7 @@ collectNestedBranches(const coverage::CoverageMapping &Coverage,
auto NestedExpansions = ExpansionCoverage.getExpansions();
auto NestedExBranches = collectNestedBranches(Coverage, NestedExpansions,
ViewDepth + 1, SrcLine);
- Branches.insert(Branches.end(), NestedExBranches.begin(),
- NestedExBranches.end());
+ append_range(Branches, NestedExBranches);
// Add branches from this level of expansion.
auto ExBranches = ExpansionCoverage.getBranches();
@@ -123,7 +122,7 @@ void renderBranchExecutionCounts(raw_ostream &OS,
collectNestedBranches(Coverage, FileCoverage.getExpansions());
// Append Expansion Branches to Source Branches.
- Branches.insert(Branches.end(), ExBranches.begin(), ExBranches.end());
+ append_range(Branches, ExBranches);
// Sort branches based on line number to ensure branches corresponding to the
// same source line are counted together.
diff --git a/llvm/tools/llvm-cov/CoverageSummaryInfo.cpp b/llvm/tools/llvm-cov/CoverageSummaryInfo.cpp
index 4a0a86168908..10e059adeb7d 100644
--- a/llvm/tools/llvm-cov/CoverageSummaryInfo.cpp
+++ b/llvm/tools/llvm-cov/CoverageSummaryInfo.cpp
@@ -100,11 +100,7 @@ FunctionCoverageSummary::get(const InstantiationGroup &Group,
for (const auto &FCS : Summaries.drop_front()) {
Summary.RegionCoverage.merge(FCS.RegionCoverage);
Summary.LineCoverage.merge(FCS.LineCoverage);
-
- // Sum branch coverage across instantiation groups for the summary rather
- // than "merge" the maximum count. This is a clearer view into whether all
- // created branches are covered.
- Summary.BranchCoverage += FCS.BranchCoverage;
+ Summary.BranchCoverage.merge(FCS.BranchCoverage);
}
return Summary;
}
diff --git a/llvm/tools/llvm-cov/CoverageSummaryInfo.h b/llvm/tools/llvm-cov/CoverageSummaryInfo.h
index 4bc1c24a079f..62e7cad1012b 100644
--- a/llvm/tools/llvm-cov/CoverageSummaryInfo.h
+++ b/llvm/tools/llvm-cov/CoverageSummaryInfo.h
@@ -123,6 +123,11 @@ public:
return *this;
}
+ void merge(const BranchCoverageInfo &RHS) {
+ Covered = std::max(Covered, RHS.Covered);
+ NumBranches = std::max(NumBranches, RHS.NumBranches);
+ }
+
size_t getCovered() const { return Covered; }
size_t getNumBranches() const { return NumBranches; }
diff --git a/llvm/tools/llvm-cov/CoverageViewOptions.h b/llvm/tools/llvm-cov/CoverageViewOptions.h
index eee4ba74e165..045fb1787bce 100644
--- a/llvm/tools/llvm-cov/CoverageViewOptions.h
+++ b/llvm/tools/llvm-cov/CoverageViewOptions.h
@@ -49,6 +49,7 @@ struct CoverageViewOptions {
std::string ProjectTitle;
std::string CreatedTimeStr;
unsigned NumThreads;
+ std::string CompilationDirectory;
/// Change the output's stream color if the colors are enabled.
ColoredRawOstream colored_ostream(raw_ostream &OS,
diff --git a/llvm/tools/llvm-cov/TestingSupport.cpp b/llvm/tools/llvm-cov/TestingSupport.cpp
index b99bd83157d0..9c6b25f2f585 100644
--- a/llvm/tools/llvm-cov/TestingSupport.cpp
+++ b/llvm/tools/llvm-cov/TestingSupport.cpp
@@ -10,6 +10,7 @@
#include "llvm/ProfileData/InstrProf.h"
#include "llvm/Support/Alignment.h"
#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
#include "llvm/Support/LEB128.h"
#include "llvm/Support/raw_ostream.h"
#include <functional>
@@ -47,7 +48,7 @@ int convertForTestingMain(int argc, const char *argv[]) {
// Look for the sections that we are interested in.
int FoundSectionCount = 0;
- SectionRef ProfileNames, CoverageMapping;
+ SectionRef ProfileNames, CoverageMapping, CoverageRecords;
auto ObjFormat = OF->getTripleObjectFormat();
for (const auto &Section : OF->sections()) {
StringRef Name;
@@ -64,16 +65,20 @@ int convertForTestingMain(int argc, const char *argv[]) {
} else if (Name == llvm::getInstrProfSectionName(
IPSK_covmap, ObjFormat, /*AddSegmentInfo=*/false)) {
CoverageMapping = Section;
+ } else if (Name == llvm::getInstrProfSectionName(
+ IPSK_covfun, ObjFormat, /*AddSegmentInfo=*/false)) {
+ CoverageRecords = Section;
} else
continue;
++FoundSectionCount;
}
- if (FoundSectionCount != 2)
+ if (FoundSectionCount != 3)
return 1;
// Get the contents of the given sections.
uint64_t ProfileNamesAddress = ProfileNames.getAddress();
StringRef CoverageMappingData;
+ StringRef CoverageRecordsData;
StringRef ProfileNamesData;
if (Expected<StringRef> E = CoverageMapping.getContents())
CoverageMappingData = *E;
@@ -81,6 +86,12 @@ int convertForTestingMain(int argc, const char *argv[]) {
consumeError(E.takeError());
return 1;
}
+ if (Expected<StringRef> E = CoverageRecords.getContents())
+ CoverageRecordsData = *E;
+ else {
+ consumeError(E.takeError());
+ return 1;
+ }
if (Expected<StringRef> E = ProfileNames.getContents())
ProfileNamesData = *E;
else {
@@ -103,6 +114,10 @@ int convertForTestingMain(int argc, const char *argv[]) {
for (unsigned Pad = offsetToAlignment(OS.tell(), Align(8)); Pad; --Pad)
OS.write(uint8_t(0));
OS << CoverageMappingData;
+ // Coverage records data is expected to have an alignment of 8.
+ for (unsigned Pad = offsetToAlignment(OS.tell(), Align(8)); Pad; --Pad)
+ OS.write(uint8_t(0));
+ OS << CoverageRecordsData;
return 0;
}
diff --git a/llvm/tools/llvm-cov/gcov.cpp b/llvm/tools/llvm-cov/gcov.cpp
index d42e7cd3b551..9a1ebebc87fc 100644
--- a/llvm/tools/llvm-cov/gcov.cpp
+++ b/llvm/tools/llvm-cov/gcov.cpp
@@ -46,7 +46,8 @@ static void reportCoverage(StringRef SourceFile, StringRef ObjectDir,
// Open .gcda and .gcda without requiring a NUL terminator. The concurrent
// modification may nullify the NUL terminator condition.
ErrorOr<std::unique_ptr<MemoryBuffer>> GCNO_Buff =
- MemoryBuffer::getFileOrSTDIN(GCNO, -1, /*RequiresNullTerminator=*/false);
+ MemoryBuffer::getFileOrSTDIN(GCNO, /*IsText=*/false,
+ /*RequiresNullTerminator=*/false);
if (std::error_code EC = GCNO_Buff.getError()) {
errs() << GCNO << ": " << EC.message() << "\n";
return;
@@ -58,7 +59,8 @@ static void reportCoverage(StringRef SourceFile, StringRef ObjectDir,
}
ErrorOr<std::unique_ptr<MemoryBuffer>> GCDA_Buff =
- MemoryBuffer::getFileOrSTDIN(GCDA, -1, /*RequiresNullTerminator=*/false);
+ MemoryBuffer::getFileOrSTDIN(GCDA, /*IsText=*/false,
+ /*RequiresNullTerminator=*/false);
if (std::error_code EC = GCDA_Buff.getError()) {
if (EC != errc::no_such_file_or_directory) {
errs() << GCDA << ": " << EC.message() << "\n";
diff --git a/llvm/tools/llvm-cov/llvm-cov.cpp b/llvm/tools/llvm-cov/llvm-cov.cpp
index 172ec9f3cedf..0e320c0965f9 100644
--- a/llvm/tools/llvm-cov/llvm-cov.cpp
+++ b/llvm/tools/llvm-cov/llvm-cov.cpp
@@ -60,7 +60,7 @@ int main(int argc, const char **argv) {
InitLLVM X(argc, argv);
// If argv[0] is or ends with 'gcov', always be gcov compatible
- if (sys::path::stem(argv[0]).endswith_lower("gcov"))
+ if (sys::path::stem(argv[0]).endswith_insensitive("gcov"))
return gcovMain(argc, argv);
// Check if we are invoking a specific tool command.
diff --git a/llvm/tools/llvm-cxxdump/llvm-cxxdump.cpp b/llvm/tools/llvm-cxxdump/llvm-cxxdump.cpp
index 03e1bab9417e..f214288e951b 100644
--- a/llvm/tools/llvm-cxxdump/llvm-cxxdump.cpp
+++ b/llvm/tools/llvm-cxxdump/llvm-cxxdump.cpp
@@ -33,9 +33,10 @@ using namespace llvm::object;
using namespace llvm::support;
namespace opts {
+cl::OptionCategory CXXDumpCategory("CXX Dump Options");
cl::list<std::string> InputFilenames(cl::Positional,
cl::desc("<input object files>"),
- cl::ZeroOrMore);
+ cl::ZeroOrMore, cl::cat(CXXDumpCategory));
} // namespace opts
namespace llvm {
@@ -549,6 +550,7 @@ int main(int argc, const char *argv[]) {
// Register the target printer for --version.
cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion);
+ cl::HideUnrelatedOptions({&opts::CXXDumpCategory, &getColorCategory()});
cl::ParseCommandLineOptions(argc, argv, "LLVM C++ ABI Data Dumper\n");
// Default to stdin if no filename is specified.
diff --git a/llvm/tools/llvm-cxxfilt/Opts.td b/llvm/tools/llvm-cxxfilt/Opts.td
new file mode 100644
index 000000000000..93f865245fe6
--- /dev/null
+++ b/llvm/tools/llvm-cxxfilt/Opts.td
@@ -0,0 +1,28 @@
+include "llvm/Option/OptParser.td"
+
+class F<string letter, string help> : Flag<["-"], letter>, HelpText<help>;
+class FF<string name, string help> : Flag<["--"], name>, HelpText<help>;
+
+multiclass BB<string name, string help1, string help2> {
+ def NAME: Flag<["--"], name>, HelpText<help1>;
+ def no_ # NAME: Flag<["--"], "no-" # name>, HelpText<help2>;
+}
+
+multiclass Eq<string name, string help> {
+ def NAME #_EQ : Joined<["--"], name #"=">,
+ HelpText<help>;
+ def : Separate<["--"], name>, Alias<!cast<Joined>(NAME #_EQ)>;
+}
+
+def help : FF<"help", "Display this help">;
+defm strip_underscore : BB<"strip-underscore", "Strip the leading underscore", "Don't strip the leading underscore">;
+def types : FF<"types", "">;
+def version : FF<"version", "Display the version">;
+
+defm : Eq<"format", "Specify mangling format. Currently ignored because only 'gnu' is supported">;
+def : F<"s", "Alias for --format">;
+
+def : F<"_", "Alias for --strip-underscore">, Alias<strip_underscore>;
+def : F<"h", "Alias for --help">, Alias<help>;
+def : F<"n", "Alias for --no-strip-underscore">, Alias<no_strip_underscore>;
+def : F<"t", "Alias for --types">, Alias<types>;
diff --git a/llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp b/llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp
index 93d6322a167e..d8bf8dbccce0 100644
--- a/llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp
+++ b/llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp
@@ -9,69 +9,59 @@
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/Triple.h"
#include "llvm/Demangle/Demangle.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/Option.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/WithColor.h"
#include "llvm/Support/raw_ostream.h"
#include <cstdlib>
#include <iostream>
using namespace llvm;
-enum Style {
- Auto, ///< auto-detect mangling
- GNU, ///< GNU
- Lucid, ///< Lucid compiler (lcc)
- ARM,
- HP, ///< HP compiler (xCC)
- EDG, ///< EDG compiler
- GNUv3, ///< GNU C++ v3 ABI
- Java, ///< Java (gcj)
- GNAT ///< ADA compiler (gnat)
+namespace {
+enum ID {
+ OPT_INVALID = 0, // This is not an option ID.
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ OPT_##ID,
+#include "Opts.inc"
+#undef OPTION
};
-static cl::opt<Style>
- Format("format", cl::desc("decoration style"),
- cl::values(clEnumValN(Auto, "auto", "auto-detect style"),
- clEnumValN(GNU, "gnu", "GNU (itanium) style")),
- cl::init(Auto));
-static cl::alias FormatShort("s", cl::desc("alias for --format"),
- cl::aliasopt(Format));
-
-static cl::opt<bool> StripUnderscore("strip-underscore",
- cl::desc("strip the leading underscore"),
- cl::init(false));
-static cl::alias StripUnderscoreShort("_",
- cl::desc("alias for --strip-underscore"),
- cl::aliasopt(StripUnderscore));
-static cl::opt<bool>
- NoStripUnderscore("no-strip-underscore",
- cl::desc("do not strip the leading underscore"),
- cl::init(false));
-static cl::alias
- NoStripUnderscoreShort("n", cl::desc("alias for --no-strip-underscore"),
- cl::aliasopt(NoStripUnderscore));
-
-static cl::opt<bool>
- Types("types",
- cl::desc("attempt to demangle types as well as function names"),
- cl::init(false));
-static cl::alias TypesShort("t", cl::desc("alias for --types"),
- cl::aliasopt(Types));
-
-static cl::list<std::string>
-Decorated(cl::Positional, cl::desc("<mangled>"), cl::ZeroOrMore);
-
-static cl::extrahelp
- HelpResponse("\nPass @FILE as argument to read options from FILE.\n");
-
-static bool shouldStripUnderscore() {
- if (StripUnderscore)
- return true;
- if (NoStripUnderscore)
- return false;
- // If none of them are set, use the default value for platform.
- // macho has symbols prefix with "_" so strip by default.
- return Triple(sys::getProcessTriple()).isOSBinFormatMachO();
+
+#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
+#include "Opts.inc"
+#undef PREFIX
+
+const opt::OptTable::Info InfoTable[] = {
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ { \
+ PREFIX, NAME, HELPTEXT, \
+ METAVAR, OPT_##ID, opt::Option::KIND##Class, \
+ PARAM, FLAGS, OPT_##GROUP, \
+ OPT_##ALIAS, ALIASARGS, VALUES},
+#include "Opts.inc"
+#undef OPTION
+};
+
+class CxxfiltOptTable : public opt::OptTable {
+public:
+ CxxfiltOptTable() : OptTable(InfoTable) { setGroupedShortOptions(true); }
+};
+} // namespace
+
+static bool StripUnderscore;
+static bool Types;
+
+static StringRef ToolName;
+
+static void error(const Twine &Message) {
+ WithColor::error(errs(), ToolName) << Message << '\n';
+ exit(1);
}
static std::string demangle(const std::string &Mangled) {
@@ -79,7 +69,7 @@ static std::string demangle(const std::string &Mangled) {
std::string Prefix;
const char *DecoratedStr = Mangled.c_str();
- if (shouldStripUnderscore())
+ if (StripUnderscore)
if (DecoratedStr[0] == '_')
++DecoratedStr;
size_t DecoratedLength = strlen(DecoratedStr);
@@ -97,6 +87,11 @@ static std::string demangle(const std::string &Mangled) {
Undecorated = itaniumDemangle(DecoratedStr + 6, nullptr, nullptr, &Status);
}
+ if (!Undecorated &&
+ (DecoratedLength >= 2 && strncmp(DecoratedStr, "_R", 2) == 0)) {
+ Undecorated = rustDemangle(DecoratedStr, nullptr, nullptr, &Status);
+ }
+
std::string Result(Undecorated ? Prefix + Undecorated : Mangled);
free(Undecorated);
return Result;
@@ -154,9 +149,37 @@ static void demangleLine(llvm::raw_ostream &OS, StringRef Mangled, bool Split) {
int main(int argc, char **argv) {
InitLLVM X(argc, argv);
+ BumpPtrAllocator A;
+ StringSaver Saver(A);
+ CxxfiltOptTable Tbl;
+ ToolName = argv[0];
+ opt::InputArgList Args = Tbl.parseArgs(argc, argv, OPT_UNKNOWN, Saver,
+ [&](StringRef Msg) { error(Msg); });
+ if (Args.hasArg(OPT_help)) {
+ Tbl.printHelp(outs(),
+ (Twine(ToolName) + " [options] <mangled>").str().c_str(),
+ "LLVM symbol undecoration tool");
+ // TODO Replace this with OptTable API once it adds extrahelp support.
+ outs() << "\nPass @FILE as argument to read options from FILE.\n";
+ return 0;
+ }
+ if (Args.hasArg(OPT_version)) {
+ outs() << ToolName << '\n';
+ cl::PrintVersionMessage();
+ return 0;
+ }
+
+ // The default value depends on the default triple. Mach-O has symbols
+ // prefixed with "_", so strip by default.
+ if (opt::Arg *A =
+ Args.getLastArg(OPT_strip_underscore, OPT_no_strip_underscore))
+ StripUnderscore = A->getOption().matches(OPT_strip_underscore);
+ else
+ StripUnderscore = Triple(sys::getProcessTriple()).isOSBinFormatMachO();
- cl::ParseCommandLineOptions(argc, argv, "llvm symbol undecoration tool\n");
+ Types = Args.hasArg(OPT_types);
+ std::vector<std::string> Decorated = Args.getAllArgValues(OPT_INPUT);
if (Decorated.empty())
for (std::string Mangled; std::getline(std::cin, Mangled);)
demangleLine(llvm::outs(), Mangled, true);
diff --git a/llvm/tools/llvm-cxxmap/llvm-cxxmap.cpp b/llvm/tools/llvm-cxxmap/llvm-cxxmap.cpp
index b53a6364c89e..1e18e379f23c 100644
--- a/llvm/tools/llvm-cxxmap/llvm-cxxmap.cpp
+++ b/llvm/tools/llvm-cxxmap/llvm-cxxmap.cpp
@@ -11,10 +11,11 @@
//
//===----------------------------------------------------------------------===//
-#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/LineIterator.h"
#include "llvm/Support/MemoryBuffer.h"
@@ -24,23 +25,33 @@
using namespace llvm;
+cl::OptionCategory CXXMapCategory("CXX Map Options");
+
cl::opt<std::string> OldSymbolFile(cl::Positional, cl::Required,
- cl::desc("<symbol-file>"));
+ cl::desc("<symbol-file>"),
+ cl::cat(CXXMapCategory));
cl::opt<std::string> NewSymbolFile(cl::Positional, cl::Required,
- cl::desc("<symbol-file>"));
+ cl::desc("<symbol-file>"),
+ cl::cat(CXXMapCategory));
cl::opt<std::string> RemappingFile("remapping-file", cl::Required,
- cl::desc("Remapping file"));
-cl::alias RemappingFileA("r", cl::aliasopt(RemappingFile));
+ cl::desc("Remapping file"),
+ cl::cat(CXXMapCategory));
+cl::alias RemappingFileA("r", cl::aliasopt(RemappingFile),
+ cl::cat(CXXMapCategory));
cl::opt<std::string> OutputFilename("output", cl::value_desc("output"),
- cl::init("-"), cl::desc("Output file"));
-cl::alias OutputFilenameA("o", cl::aliasopt(OutputFilename));
+ cl::init("-"), cl::desc("Output file"),
+ cl::cat(CXXMapCategory));
+cl::alias OutputFilenameA("o", cl::aliasopt(OutputFilename),
+ cl::cat(CXXMapCategory));
cl::opt<bool> WarnAmbiguous(
"Wambiguous",
- cl::desc("Warn on equivalent symbols in the output symbol list"));
+ cl::desc("Warn on equivalent symbols in the output symbol list"),
+ cl::cat(CXXMapCategory));
cl::opt<bool> WarnIncomplete(
"Wincomplete",
- cl::desc("Warn on input symbols missing from output symbol list"));
+ cl::desc("Warn on input symbols missing from output symbol list"),
+ cl::cat(CXXMapCategory));
static void warn(Twine Message, Twine Whence = "",
std::string Hint = "") {
@@ -130,6 +141,7 @@ static void remapSymbols(MemoryBuffer &OldSymbolFile,
int main(int argc, const char *argv[]) {
InitLLVM X(argc, argv);
+ cl::HideUnrelatedOptions({&CXXMapCategory, &getColorCategory()});
cl::ParseCommandLineOptions(argc, argv, "LLVM C++ mangled name remapper\n");
auto OldSymbolBufOrError = MemoryBuffer::getFileOrSTDIN(OldSymbolFile);
@@ -145,7 +157,7 @@ int main(int argc, const char *argv[]) {
exitWithErrorCode(RemappingBufOrError.getError(), RemappingFile);
std::error_code EC;
- raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::OF_Text);
+ raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::OF_TextWithCRLF);
if (EC)
exitWithErrorCode(EC, OutputFilename);
diff --git a/llvm/tools/llvm-diff/DiffConsumer.cpp b/llvm/tools/llvm-diff/DiffConsumer.cpp
index 6228ff2bae98..a703f42f14c3 100644
--- a/llvm/tools/llvm-diff/DiffConsumer.cpp
+++ b/llvm/tools/llvm-diff/DiffConsumer.cpp
@@ -17,34 +17,33 @@
using namespace llvm;
-static void ComputeNumbering(Function *F, DenseMap<Value*,unsigned> &Numbering){
+static void ComputeNumbering(const Function *F,
+ DenseMap<const Value *, unsigned> &Numbering) {
unsigned IN = 0;
// Arguments get the first numbers.
- for (Function::arg_iterator
- AI = F->arg_begin(), AE = F->arg_end(); AI != AE; ++AI)
- if (!AI->hasName())
- Numbering[&*AI] = IN++;
+ for (const auto &Arg : F->args())
+ if (!Arg.hasName())
+ Numbering[&Arg] = IN++;
// Walk the basic blocks in order.
- for (Function::iterator FI = F->begin(), FE = F->end(); FI != FE; ++FI) {
- if (!FI->hasName())
- Numbering[&*FI] = IN++;
+ for (const auto &Func : *F) {
+ if (!Func.hasName())
+ Numbering[&Func] = IN++;
// Walk the instructions in order.
- for (BasicBlock::iterator BI = FI->begin(), BE = FI->end(); BI != BE; ++BI)
+ for (const auto &BB : Func)
// void instructions don't get numbers.
- if (!BI->hasName() && !BI->getType()->isVoidTy())
- Numbering[&*BI] = IN++;
+ if (!BB.hasName() && !BB.getType()->isVoidTy())
+ Numbering[&BB] = IN++;
}
assert(!Numbering.empty() && "asked for numbering but numbering was no-op");
}
-
void Consumer::anchor() { }
-void DiffConsumer::printValue(Value *V, bool isL) {
+void DiffConsumer::printValue(const Value *V, bool isL) {
if (V->hasName()) {
out << (isa<GlobalValue>(V) ? '@' : '%') << V->getName();
return;
@@ -99,16 +98,16 @@ void DiffConsumer::header() {
// Extra newline between functions.
if (Differences) out << "\n";
- Function *L = cast<Function>(I->L);
- Function *R = cast<Function>(I->R);
+ const Function *L = cast<Function>(I->L);
+ const Function *R = cast<Function>(I->R);
if (L->getName() != R->getName())
out << "in function " << L->getName()
<< " / " << R->getName() << ":\n";
else
out << "in function " << L->getName() << ":\n";
} else if (isa<BasicBlock>(I->L)) {
- BasicBlock *L = cast<BasicBlock>(I->L);
- BasicBlock *R = cast<BasicBlock>(I->R);
+ const BasicBlock *L = cast<BasicBlock>(I->L);
+ const BasicBlock *R = cast<BasicBlock>(I->R);
if (L->hasName() && R->hasName() && L->getName() == R->getName())
out << " in block %" << L->getName() << ":\n";
else {
@@ -139,7 +138,7 @@ bool DiffConsumer::hadDifferences() const {
return Differences;
}
-void DiffConsumer::enterContext(Value *L, Value *R) {
+void DiffConsumer::enterContext(const Value *L, const Value *R) {
contexts.push_back(DiffContext(L, R));
Indent += 2;
}
diff --git a/llvm/tools/llvm-diff/DiffConsumer.h b/llvm/tools/llvm-diff/DiffConsumer.h
index 6cb8f2eb7eeb..f7b2f2450eec 100644
--- a/llvm/tools/llvm-diff/DiffConsumer.h
+++ b/llvm/tools/llvm-diff/DiffConsumer.h
@@ -34,7 +34,7 @@ class StringRef;
/// Right are IR "containers" of some sort which are being
/// considered for structural equivalence: global variables,
/// functions, blocks, instructions, etc.
- virtual void enterContext(Value *Left, Value *Right) = 0;
+ virtual void enterContext(const Value *Left, const Value *Right) = 0;
/// Record that a local context has been exited.
virtual void exitContext() = 0;
@@ -55,14 +55,14 @@ class StringRef;
class DiffConsumer : public Consumer {
private:
struct DiffContext {
- DiffContext(Value *L, Value *R)
- : L(L), R(R), Differences(false), IsFunction(isa<Function>(L)) {}
- Value *L;
- Value *R;
+ DiffContext(const Value *L, const Value *R)
+ : L(L), R(R), Differences(false), IsFunction(isa<Function>(L)) {}
+ const Value *L;
+ const Value *R;
bool Differences;
bool IsFunction;
- DenseMap<Value*,unsigned> LNumbering;
- DenseMap<Value*,unsigned> RNumbering;
+ DenseMap<const Value *, unsigned> LNumbering;
+ DenseMap<const Value *, unsigned> RNumbering;
};
raw_ostream &out;
@@ -70,7 +70,7 @@ class StringRef;
bool Differences;
unsigned Indent;
- void printValue(Value *V, bool isL);
+ void printValue(const Value *V, bool isL);
void header();
void indent();
@@ -79,7 +79,7 @@ class StringRef;
: out(errs()), Differences(false), Indent(0) {}
bool hadDifferences() const;
- void enterContext(Value *L, Value *R) override;
+ void enterContext(const Value *L, const Value *R) override;
void exitContext() override;
void log(StringRef text) override;
void logf(const LogBuilder &Log) override;
diff --git a/llvm/tools/llvm-diff/DiffLog.cpp b/llvm/tools/llvm-diff/DiffLog.cpp
index 6484197521f2..d31a345d255c 100644
--- a/llvm/tools/llvm-diff/DiffLog.cpp
+++ b/llvm/tools/llvm-diff/DiffLog.cpp
@@ -24,18 +24,18 @@ LogBuilder::~LogBuilder() {
StringRef LogBuilder::getFormat() const { return Format; }
unsigned LogBuilder::getNumArguments() const { return Arguments.size(); }
-Value *LogBuilder::getArgument(unsigned I) const { return Arguments[I]; }
+const Value *LogBuilder::getArgument(unsigned I) const { return Arguments[I]; }
DiffLogBuilder::~DiffLogBuilder() { consumer.logd(*this); }
-void DiffLogBuilder::addMatch(Instruction *L, Instruction *R) {
+void DiffLogBuilder::addMatch(const Instruction *L, const Instruction *R) {
Diff.push_back(DiffRecord(L, R));
}
-void DiffLogBuilder::addLeft(Instruction *L) {
+void DiffLogBuilder::addLeft(const Instruction *L) {
// HACK: VS 2010 has a bug in the stdlib that requires this.
Diff.push_back(DiffRecord(L, DiffRecord::second_type(nullptr)));
}
-void DiffLogBuilder::addRight(Instruction *R) {
+void DiffLogBuilder::addRight(const Instruction *R) {
// HACK: VS 2010 has a bug in the stdlib that requires this.
Diff.push_back(DiffRecord(DiffRecord::first_type(nullptr), R));
}
@@ -46,5 +46,9 @@ DiffChange DiffLogBuilder::getLineKind(unsigned I) const {
return (Diff[I].first ? (Diff[I].second ? DC_match : DC_left)
: DC_right);
}
-Instruction *DiffLogBuilder::getLeft(unsigned I) const { return Diff[I].first; }
-Instruction *DiffLogBuilder::getRight(unsigned I) const { return Diff[I].second; }
+const Instruction *DiffLogBuilder::getLeft(unsigned I) const {
+ return Diff[I].first;
+}
+const Instruction *DiffLogBuilder::getRight(unsigned I) const {
+ return Diff[I].second;
+}
diff --git a/llvm/tools/llvm-diff/DiffLog.h b/llvm/tools/llvm-diff/DiffLog.h
index 0c8952496155..d8b07b971198 100644
--- a/llvm/tools/llvm-diff/DiffLog.h
+++ b/llvm/tools/llvm-diff/DiffLog.h
@@ -34,7 +34,7 @@ namespace llvm {
/// might be initializing this format.
StringRef Format;
- SmallVector<Value*, 4> Arguments;
+ SmallVector<const Value *, 4> Arguments;
public:
LogBuilder(Consumer &c, StringRef Format) : consumer(&c), Format(Format) {}
@@ -44,7 +44,7 @@ namespace llvm {
L.consumer = nullptr;
}
- LogBuilder &operator<<(Value *V) {
+ LogBuilder &operator<<(const Value *V) {
Arguments.push_back(V);
return *this;
}
@@ -53,12 +53,12 @@ namespace llvm {
StringRef getFormat() const;
unsigned getNumArguments() const;
- Value *getArgument(unsigned I) const;
+ const Value *getArgument(unsigned I) const;
};
/// A temporary-object class for building up diff messages.
class DiffLogBuilder {
- typedef std::pair<Instruction*,Instruction*> DiffRecord;
+ typedef std::pair<const Instruction *, const Instruction *> DiffRecord;
SmallVector<DiffRecord, 20> Diff;
Consumer &consumer;
@@ -67,15 +67,15 @@ namespace llvm {
DiffLogBuilder(Consumer &c) : consumer(c) {}
~DiffLogBuilder();
- void addMatch(Instruction *L, Instruction *R);
+ void addMatch(const Instruction *L, const Instruction *R);
// HACK: VS 2010 has a bug in the stdlib that requires this.
- void addLeft(Instruction *L);
- void addRight(Instruction *R);
+ void addLeft(const Instruction *L);
+ void addRight(const Instruction *R);
unsigned getNumLines() const;
DiffChange getLineKind(unsigned I) const;
- Instruction *getLeft(unsigned I) const;
- Instruction *getRight(unsigned I) const;
+ const Instruction *getLeft(unsigned I) const;
+ const Instruction *getRight(unsigned I) const;
};
}
diff --git a/llvm/tools/llvm-diff/DifferenceEngine.cpp b/llvm/tools/llvm-diff/DifferenceEngine.cpp
index 64c0dc61e806..eb746cd2a865 100644
--- a/llvm/tools/llvm-diff/DifferenceEngine.cpp
+++ b/llvm/tools/llvm-diff/DifferenceEngine.cpp
@@ -113,22 +113,29 @@ public:
class FunctionDifferenceEngine {
DifferenceEngine &Engine;
+ // Some initializers may reference the variable we're currently checking. This
+ // can cause an infinite loop. The Saved[LR]HS ivars can be checked to prevent
+ // recursing.
+ const Value *SavedLHS;
+ const Value *SavedRHS;
+
/// The current mapping from old local values to new local values.
- DenseMap<Value*, Value*> Values;
+ DenseMap<const Value *, const Value *> Values;
/// The current mapping from old blocks to new blocks.
- DenseMap<BasicBlock*, BasicBlock*> Blocks;
+ DenseMap<const BasicBlock *, const BasicBlock *> Blocks;
- DenseSet<std::pair<Value*, Value*> > TentativeValues;
+ DenseSet<std::pair<const Value *, const Value *>> TentativeValues;
- unsigned getUnprocPredCount(BasicBlock *Block) const {
+ unsigned getUnprocPredCount(const BasicBlock *Block) const {
unsigned Count = 0;
- for (pred_iterator I = pred_begin(Block), E = pred_end(Block); I != E; ++I)
+ for (const_pred_iterator I = pred_begin(Block), E = pred_end(Block); I != E;
+ ++I)
if (!Blocks.count(*I)) Count++;
return Count;
}
- typedef std::pair<BasicBlock*, BasicBlock*> BlockPair;
+ typedef std::pair<const BasicBlock *, const BasicBlock *> BlockPair;
/// A type which sorts a priority queue by the number of unprocessed
/// predecessor blocks it has remaining.
@@ -138,7 +145,7 @@ class FunctionDifferenceEngine {
const FunctionDifferenceEngine &fde;
explicit QueueSorter(const FunctionDifferenceEngine &fde) : fde(fde) {}
- bool operator()(const BlockPair &Old, const BlockPair &New) {
+ bool operator()(BlockPair &Old, BlockPair &New) {
return fde.getUnprocPredCount(Old.first)
< fde.getUnprocPredCount(New.first);
}
@@ -151,8 +158,8 @@ class FunctionDifferenceEngine {
/// if they haven't already been processed.
///
/// Returns true if there was a problem unifying them.
- bool tryUnify(BasicBlock *L, BasicBlock *R) {
- BasicBlock *&Ref = Blocks[L];
+ bool tryUnify(const BasicBlock *L, const BasicBlock *R) {
+ const BasicBlock *&Ref = Blocks[L];
if (Ref) {
if (Ref == R) return false;
@@ -167,10 +174,10 @@ class FunctionDifferenceEngine {
Queue.insert(BlockPair(L, R));
return false;
}
-
+
/// Unifies two instructions, given that they're known not to have
/// structural differences.
- void unify(Instruction *L, Instruction *R) {
+ void unify(const Instruction *L, const Instruction *R) {
DifferenceEngine::Context C(Engine, L, R);
bool Result = diff(L, R, true, true);
@@ -187,15 +194,15 @@ class FunctionDifferenceEngine {
}
}
- void diff(BasicBlock *L, BasicBlock *R) {
+ void diff(const BasicBlock *L, const BasicBlock *R) {
DifferenceEngine::Context C(Engine, L, R);
- BasicBlock::iterator LI = L->begin(), LE = L->end();
- BasicBlock::iterator RI = R->begin();
+ BasicBlock::const_iterator LI = L->begin(), LE = L->end();
+ BasicBlock::const_iterator RI = R->begin();
do {
assert(LI != LE && RI != R->end());
- Instruction *LeftI = &*LI, *RightI = &*RI;
+ const Instruction *LeftI = &*LI, *RightI = &*RI;
// If the instructions differ, start the more sophisticated diff
// algorithm at the start of the block.
@@ -219,10 +226,11 @@ class FunctionDifferenceEngine {
unify(&*LI, &*RI);
}
- bool matchForBlockDiff(Instruction *L, Instruction *R);
- void runBlockDiff(BasicBlock::iterator LI, BasicBlock::iterator RI);
+ bool matchForBlockDiff(const Instruction *L, const Instruction *R);
+ void runBlockDiff(BasicBlock::const_iterator LI,
+ BasicBlock::const_iterator RI);
- bool diffCallSites(CallBase &L, CallBase &R, bool Complain) {
+ bool diffCallSites(const CallBase &L, const CallBase &R, bool Complain) {
// FIXME: call attributes
if (!equivalentAsOperands(L.getCalledOperand(), R.getCalledOperand())) {
if (Complain) Engine.log("called functions differ");
@@ -242,7 +250,8 @@ class FunctionDifferenceEngine {
return false;
}
- bool diff(Instruction *L, Instruction *R, bool Complain, bool TryUnify) {
+ bool diff(const Instruction *L, const Instruction *R, bool Complain,
+ bool TryUnify) {
// FIXME: metadata (if Complain is set)
// Different opcodes always imply different operations.
@@ -273,8 +282,8 @@ class FunctionDifferenceEngine {
// Terminators.
} else if (isa<InvokeInst>(L)) {
- InvokeInst &LI = cast<InvokeInst>(*L);
- InvokeInst &RI = cast<InvokeInst>(*R);
+ const InvokeInst &LI = cast<InvokeInst>(*L);
+ const InvokeInst &RI = cast<InvokeInst>(*R);
if (diffCallSites(LI, RI, Complain))
return true;
@@ -284,9 +293,30 @@ class FunctionDifferenceEngine {
}
return false;
+ } else if (isa<CallBrInst>(L)) {
+ const CallBrInst &LI = cast<CallBrInst>(*L);
+ const CallBrInst &RI = cast<CallBrInst>(*R);
+ if (LI.getNumIndirectDests() != RI.getNumIndirectDests()) {
+ if (Complain)
+ Engine.log("callbr # of indirect destinations differ");
+ return true;
+ }
+
+ // Perform the "try unify" step so that we can equate the indirect
+ // destinations before checking the call site.
+ for (unsigned I = 0; I < LI.getNumIndirectDests(); I++)
+ tryUnify(LI.getIndirectDest(I), RI.getIndirectDest(I));
+
+ if (diffCallSites(LI, RI, Complain))
+ return true;
+
+ if (TryUnify)
+ tryUnify(LI.getDefaultDest(), RI.getDefaultDest());
+ return false;
+
} else if (isa<BranchInst>(L)) {
- BranchInst *LI = cast<BranchInst>(L);
- BranchInst *RI = cast<BranchInst>(R);
+ const BranchInst *LI = cast<BranchInst>(L);
+ const BranchInst *RI = cast<BranchInst>(R);
if (LI->isConditional() != RI->isConditional()) {
if (Complain) Engine.log("branch conditionality differs");
return true;
@@ -303,8 +333,8 @@ class FunctionDifferenceEngine {
return false;
} else if (isa<IndirectBrInst>(L)) {
- IndirectBrInst *LI = cast<IndirectBrInst>(L);
- IndirectBrInst *RI = cast<IndirectBrInst>(R);
+ const IndirectBrInst *LI = cast<IndirectBrInst>(L);
+ const IndirectBrInst *RI = cast<IndirectBrInst>(R);
if (LI->getNumDestinations() != RI->getNumDestinations()) {
if (Complain) Engine.log("indirectbr # of destinations differ");
return true;
@@ -323,8 +353,8 @@ class FunctionDifferenceEngine {
return false;
} else if (isa<SwitchInst>(L)) {
- SwitchInst *LI = cast<SwitchInst>(L);
- SwitchInst *RI = cast<SwitchInst>(R);
+ const SwitchInst *LI = cast<SwitchInst>(L);
+ const SwitchInst *RI = cast<SwitchInst>(R);
if (!equivalentAsOperands(LI->getCondition(), RI->getCondition())) {
if (Complain) Engine.log("switch conditions differ");
return true;
@@ -333,13 +363,13 @@ class FunctionDifferenceEngine {
bool Difference = false;
- DenseMap<ConstantInt*,BasicBlock*> LCases;
+ DenseMap<const ConstantInt *, const BasicBlock *> LCases;
for (auto Case : LI->cases())
LCases[Case.getCaseValue()] = Case.getCaseSuccessor();
for (auto Case : RI->cases()) {
- ConstantInt *CaseValue = Case.getCaseValue();
- BasicBlock *LCase = LCases[CaseValue];
+ const ConstantInt *CaseValue = Case.getCaseValue();
+ const BasicBlock *LCase = LCases[CaseValue];
if (LCase) {
if (TryUnify)
tryUnify(LCase, Case.getCaseSuccessor());
@@ -351,8 +381,10 @@ class FunctionDifferenceEngine {
}
}
if (!Difference)
- for (DenseMap<ConstantInt*,BasicBlock*>::iterator
- I = LCases.begin(), E = LCases.end(); I != E; ++I) {
+ for (DenseMap<const ConstantInt *, const BasicBlock *>::iterator
+ I = LCases.begin(),
+ E = LCases.end();
+ I != E; ++I) {
if (Complain)
Engine.logf("left switch has extra case %l") << I->first;
Difference = true;
@@ -378,14 +410,15 @@ class FunctionDifferenceEngine {
return false;
}
- bool equivalentAsOperands(Constant *L, Constant *R) {
+public:
+ bool equivalentAsOperands(const Constant *L, const Constant *R) {
// Use equality as a preliminary filter.
if (L == R)
return true;
if (L->getValueID() != R->getValueID())
return false;
-
+
// Ask the engine about global values.
if (isa<GlobalValue>(L))
return Engine.equivalentAsOperands(cast<GlobalValue>(L),
@@ -409,8 +442,8 @@ class FunctionDifferenceEngine {
// If L and R are ConstantVectors, compare each element
if (isa<ConstantVector>(L)) {
- ConstantVector *CVL = cast<ConstantVector>(L);
- ConstantVector *CVR = cast<ConstantVector>(R);
+ const ConstantVector *CVL = cast<ConstantVector>(L);
+ const ConstantVector *CVR = cast<ConstantVector>(R);
if (CVL->getType()->getNumElements() != CVR->getType()->getNumElements())
return false;
for (unsigned i = 0; i < CVL->getType()->getNumElements(); i++) {
@@ -420,12 +453,68 @@ class FunctionDifferenceEngine {
return true;
}
+ // If L and R are ConstantArrays, compare the element count and types.
+ if (isa<ConstantArray>(L)) {
+ const ConstantArray *CAL = cast<ConstantArray>(L);
+ const ConstantArray *CAR = cast<ConstantArray>(R);
+ // Sometimes a type may be equivalent, but not uniquified---e.g. it may
+ // contain a GEP instruction. Do a deeper comparison of the types.
+ if (CAL->getType()->getNumElements() != CAR->getType()->getNumElements())
+ return false;
+
+ for (unsigned I = 0; I < CAL->getType()->getNumElements(); ++I) {
+ if (!equivalentAsOperands(CAL->getAggregateElement(I),
+ CAR->getAggregateElement(I)))
+ return false;
+ }
+
+ return true;
+ }
+
+ // If L and R are ConstantStructs, compare each field and type.
+ if (isa<ConstantStruct>(L)) {
+ const ConstantStruct *CSL = cast<ConstantStruct>(L);
+ const ConstantStruct *CSR = cast<ConstantStruct>(R);
+
+ const StructType *LTy = cast<StructType>(CSL->getType());
+ const StructType *RTy = cast<StructType>(CSR->getType());
+
+ // The StructTypes should have the same attributes. Don't use
+ // isLayoutIdentical(), because that just checks the element pointers,
+ // which may not work here.
+ if (LTy->getNumElements() != RTy->getNumElements() ||
+ LTy->isPacked() != RTy->isPacked())
+ return false;
+
+ for (unsigned I = 0; I < LTy->getNumElements(); I++) {
+ const Value *LAgg = CSL->getAggregateElement(I);
+ const Value *RAgg = CSR->getAggregateElement(I);
+
+ if (LAgg == SavedLHS || RAgg == SavedRHS) {
+ if (LAgg != SavedLHS || RAgg != SavedRHS)
+ // If the left and right operands aren't both re-analyzing the
+ // variable, then the initialiers don't match, so report "false".
+ // Otherwise, we skip these operands..
+ return false;
+
+ continue;
+ }
+
+ if (!equivalentAsOperands(LAgg, RAgg)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
return false;
}
- bool equivalentAsOperands(ConstantExpr *L, ConstantExpr *R) {
+ bool equivalentAsOperands(const ConstantExpr *L, const ConstantExpr *R) {
if (L == R)
return true;
+
if (L->getOpcode() != R->getOpcode())
return false;
@@ -447,14 +536,28 @@ class FunctionDifferenceEngine {
if (L->getNumOperands() != R->getNumOperands())
return false;
- for (unsigned I = 0, E = L->getNumOperands(); I != E; ++I)
- if (!equivalentAsOperands(L->getOperand(I), R->getOperand(I)))
+ for (unsigned I = 0, E = L->getNumOperands(); I != E; ++I) {
+ const auto *LOp = L->getOperand(I);
+ const auto *ROp = R->getOperand(I);
+
+ if (LOp == SavedLHS || ROp == SavedRHS) {
+ if (LOp != SavedLHS || ROp != SavedRHS)
+ // If the left and right operands aren't both re-analyzing the
+ // variable, then the initialiers don't match, so report "false".
+ // Otherwise, we skip these operands..
+ return false;
+
+ continue;
+ }
+
+ if (!equivalentAsOperands(LOp, ROp))
return false;
+ }
return true;
}
- bool equivalentAsOperands(Value *L, Value *R) {
+ bool equivalentAsOperands(const Value *L, const Value *R) {
// Fall out if the values have different kind.
// This possibly shouldn't take priority over oracles.
if (L->getValueID() != R->getValueID())
@@ -483,17 +586,19 @@ class FunctionDifferenceEngine {
FunctionDifferenceEngine *this_() { return this; }
public:
- FunctionDifferenceEngine(DifferenceEngine &Engine) :
- Engine(Engine), Queue(QueueSorter(*this_())) {}
+ FunctionDifferenceEngine(DifferenceEngine &Engine,
+ const Value *SavedLHS = nullptr,
+ const Value *SavedRHS = nullptr)
+ : Engine(Engine), SavedLHS(SavedLHS), SavedRHS(SavedRHS),
+ Queue(QueueSorter(*this_())) {}
- void diff(Function *L, Function *R) {
+ void diff(const Function *L, const Function *R) {
if (L->arg_size() != R->arg_size())
Engine.log("different argument counts");
// Map the arguments.
- for (Function::arg_iterator
- LI = L->arg_begin(), LE = L->arg_end(),
- RI = R->arg_begin(), RE = R->arg_end();
+ for (Function::const_arg_iterator LI = L->arg_begin(), LE = L->arg_end(),
+ RI = R->arg_begin(), RE = R->arg_end();
LI != LE && RI != RE; ++LI, ++RI)
Values[&*LI] = &*RI;
@@ -509,15 +614,15 @@ struct DiffEntry {
llvm::SmallVector<char, 8> Path; // actually of DifferenceEngine::DiffChange
};
-bool FunctionDifferenceEngine::matchForBlockDiff(Instruction *L,
- Instruction *R) {
+bool FunctionDifferenceEngine::matchForBlockDiff(const Instruction *L,
+ const Instruction *R) {
return !diff(L, R, false, false);
}
-void FunctionDifferenceEngine::runBlockDiff(BasicBlock::iterator LStart,
- BasicBlock::iterator RStart) {
- BasicBlock::iterator LE = LStart->getParent()->end();
- BasicBlock::iterator RE = RStart->getParent()->end();
+void FunctionDifferenceEngine::runBlockDiff(BasicBlock::const_iterator LStart,
+ BasicBlock::const_iterator RStart) {
+ BasicBlock::const_iterator LE = LStart->getParent()->end();
+ BasicBlock::const_iterator RE = RStart->getParent()->end();
unsigned NL = std::distance(LStart, LE);
@@ -540,14 +645,14 @@ void FunctionDifferenceEngine::runBlockDiff(BasicBlock::iterator LStart,
Cur[I].Path.push_back(DC_left);
}
- for (BasicBlock::iterator RI = RStart; RI != RE; ++RI) {
+ for (BasicBlock::const_iterator RI = RStart; RI != RE; ++RI) {
// Initialize the first row.
Next[0] = Cur[0];
Next[0].Cost += RightCost;
Next[0].Path.push_back(DC_right);
unsigned Index = 1;
- for (BasicBlock::iterator LI = LStart; LI != LE; ++LI, ++Index) {
+ for (BasicBlock::const_iterator LI = LStart; LI != LE; ++LI, ++Index) {
if (matchForBlockDiff(&*LI, &*RI)) {
Next[Index] = Cur[Index-1];
Next[Index].Cost += MatchCost;
@@ -572,7 +677,7 @@ void FunctionDifferenceEngine::runBlockDiff(BasicBlock::iterator LStart,
TentativeValues.clear();
SmallVectorImpl<char> &Path = Cur[NL].Path;
- BasicBlock::iterator LI = LStart, RI = RStart;
+ BasicBlock::const_iterator LI = LStart, RI = RStart;
DiffLogBuilder Diff(Engine.getConsumer());
@@ -595,7 +700,7 @@ void FunctionDifferenceEngine::runBlockDiff(BasicBlock::iterator LStart,
case DC_match:
assert(LI != LE && RI != RE);
{
- Instruction *L = &*LI, *R = &*RI;
+ const Instruction *L = &*LI, *R = &*RI;
unify(L, R);
Diff.addMatch(L, R);
}
@@ -628,16 +733,16 @@ void FunctionDifferenceEngine::runBlockDiff(BasicBlock::iterator LStart,
// If the terminators have different kinds, but one is an invoke and the
// other is an unconditional branch immediately following a call, unify
// the results and the destinations.
- Instruction *LTerm = LStart->getParent()->getTerminator();
- Instruction *RTerm = RStart->getParent()->getTerminator();
+ const Instruction *LTerm = LStart->getParent()->getTerminator();
+ const Instruction *RTerm = RStart->getParent()->getTerminator();
if (isa<BranchInst>(LTerm) && isa<InvokeInst>(RTerm)) {
if (cast<BranchInst>(LTerm)->isConditional()) return;
- BasicBlock::iterator I = LTerm->getIterator();
+ BasicBlock::const_iterator I = LTerm->getIterator();
if (I == LStart->getParent()->begin()) return;
--I;
if (!isa<CallInst>(*I)) return;
- CallInst *LCall = cast<CallInst>(&*I);
- InvokeInst *RInvoke = cast<InvokeInst>(RTerm);
+ const CallInst *LCall = cast<CallInst>(&*I);
+ const InvokeInst *RInvoke = cast<InvokeInst>(RTerm);
if (!equivalentAsOperands(LCall->getCalledOperand(),
RInvoke->getCalledOperand()))
return;
@@ -646,12 +751,12 @@ void FunctionDifferenceEngine::runBlockDiff(BasicBlock::iterator LStart,
tryUnify(LTerm->getSuccessor(0), RInvoke->getNormalDest());
} else if (isa<InvokeInst>(LTerm) && isa<BranchInst>(RTerm)) {
if (cast<BranchInst>(RTerm)->isConditional()) return;
- BasicBlock::iterator I = RTerm->getIterator();
+ BasicBlock::const_iterator I = RTerm->getIterator();
if (I == RStart->getParent()->begin()) return;
--I;
if (!isa<CallInst>(*I)) return;
- CallInst *RCall = cast<CallInst>(I);
- InvokeInst *LInvoke = cast<InvokeInst>(LTerm);
+ const CallInst *RCall = cast<CallInst>(I);
+ const InvokeInst *LInvoke = cast<InvokeInst>(LTerm);
if (!equivalentAsOperands(LInvoke->getCalledOperand(),
RCall->getCalledOperand()))
return;
@@ -660,12 +765,11 @@ void FunctionDifferenceEngine::runBlockDiff(BasicBlock::iterator LStart,
tryUnify(LInvoke->getNormalDest(), RTerm->getSuccessor(0));
}
}
-
}
void DifferenceEngine::Oracle::anchor() { }
-void DifferenceEngine::diff(Function *L, Function *R) {
+void DifferenceEngine::diff(const Function *L, const Function *R) {
Context C(*this, L, R);
// FIXME: types
@@ -683,15 +787,15 @@ void DifferenceEngine::diff(Function *L, Function *R) {
FunctionDifferenceEngine(*this).diff(L, R);
}
-void DifferenceEngine::diff(Module *L, Module *R) {
+void DifferenceEngine::diff(const Module *L, const Module *R) {
StringSet<> LNames;
- SmallVector<std::pair<Function*,Function*>, 20> Queue;
+ SmallVector<std::pair<const Function *, const Function *>, 20> Queue;
unsigned LeftAnonCount = 0;
unsigned RightAnonCount = 0;
- for (Module::iterator I = L->begin(), E = L->end(); I != E; ++I) {
- Function *LFn = &*I;
+ for (Module::const_iterator I = L->begin(), E = L->end(); I != E; ++I) {
+ const Function *LFn = &*I;
StringRef Name = LFn->getName();
if (Name.empty()) {
++LeftAnonCount;
@@ -706,8 +810,8 @@ void DifferenceEngine::diff(Module *L, Module *R) {
logf("function %l exists only in left module") << LFn;
}
- for (Module::iterator I = R->begin(), E = R->end(); I != E; ++I) {
- Function *RFn = &*I;
+ for (Module::const_iterator I = R->begin(), E = R->end(); I != E; ++I) {
+ const Function *RFn = &*I;
StringRef Name = RFn->getName();
if (Name.empty()) {
++RightAnonCount;
@@ -718,7 +822,6 @@ void DifferenceEngine::diff(Module *L, Module *R) {
logf("function %r exists only in right module") << RFn;
}
-
if (LeftAnonCount != 0 || RightAnonCount != 0) {
SmallString<32> Tmp;
logf(("not comparing " + Twine(LeftAnonCount) +
@@ -727,20 +830,24 @@ void DifferenceEngine::diff(Module *L, Module *R) {
.toStringRef(Tmp));
}
- for (SmallVectorImpl<std::pair<Function*,Function*> >::iterator
- I = Queue.begin(), E = Queue.end(); I != E; ++I)
+ for (SmallVectorImpl<std::pair<const Function *, const Function *>>::iterator
+ I = Queue.begin(),
+ E = Queue.end();
+ I != E; ++I)
diff(I->first, I->second);
}
-bool DifferenceEngine::equivalentAsOperands(GlobalValue *L, GlobalValue *R) {
+bool DifferenceEngine::equivalentAsOperands(const GlobalValue *L,
+ const GlobalValue *R) {
if (globalValueOracle) return (*globalValueOracle)(L, R);
if (isa<GlobalVariable>(L) && isa<GlobalVariable>(R)) {
- GlobalVariable *GVL = cast<GlobalVariable>(L);
- GlobalVariable *GVR = cast<GlobalVariable>(R);
+ const GlobalVariable *GVL = cast<GlobalVariable>(L);
+ const GlobalVariable *GVR = cast<GlobalVariable>(R);
if (GVL->hasLocalLinkage() && GVL->hasUniqueInitializer() &&
GVR->hasLocalLinkage() && GVR->hasUniqueInitializer())
- return GVL->getInitializer() == GVR->getInitializer();
+ return FunctionDifferenceEngine(*this, GVL, GVR)
+ .equivalentAsOperands(GVL->getInitializer(), GVR->getInitializer());
}
return L->getName() == R->getName();
diff --git a/llvm/tools/llvm-diff/DifferenceEngine.h b/llvm/tools/llvm-diff/DifferenceEngine.h
index da1b6526a6e2..436a35566360 100644
--- a/llvm/tools/llvm-diff/DifferenceEngine.h
+++ b/llvm/tools/llvm-diff/DifferenceEngine.h
@@ -33,7 +33,8 @@ namespace llvm {
public:
/// A RAII object for recording the current context.
struct Context {
- Context(DifferenceEngine &Engine, Value *L, Value *R) : Engine(Engine) {
+ Context(DifferenceEngine &Engine, const Value *L, const Value *R)
+ : Engine(Engine) {
Engine.consumer.enterContext(L, R);
}
@@ -50,7 +51,7 @@ namespace llvm {
class Oracle {
virtual void anchor();
public:
- virtual bool operator()(Value *L, Value *R) = 0;
+ virtual bool operator()(const Value *L, const Value *R) = 0;
protected:
virtual ~Oracle() {}
@@ -59,8 +60,8 @@ namespace llvm {
DifferenceEngine(Consumer &consumer)
: consumer(consumer), globalValueOracle(nullptr) {}
- void diff(Module *L, Module *R);
- void diff(Function *L, Function *R);
+ void diff(const Module *L, const Module *R);
+ void diff(const Function *L, const Function *R);
void log(StringRef text) {
consumer.log(text);
}
@@ -78,7 +79,7 @@ namespace llvm {
}
/// Determines whether two global values are equivalent.
- bool equivalentAsOperands(GlobalValue *L, GlobalValue *R);
+ bool equivalentAsOperands(const GlobalValue *L, const GlobalValue *R);
private:
Consumer &consumer;
diff --git a/llvm/tools/llvm-diff/llvm-diff.cpp b/llvm/tools/llvm-diff/llvm-diff.cpp
index aaf7989e2e3d..8a11179e741e 100644
--- a/llvm/tools/llvm-diff/llvm-diff.cpp
+++ b/llvm/tools/llvm-diff/llvm-diff.cpp
@@ -55,16 +55,20 @@ static void diffGlobal(DifferenceEngine &Engine, Module &L, Module &R,
errs() << "No function named @" << Name << " in right module\n";
}
+cl::OptionCategory DiffCategory("Diff Options");
+
static cl::opt<std::string> LeftFilename(cl::Positional,
- cl::desc("<first file>"),
- cl::Required);
+ cl::desc("<first file>"), cl::Required,
+ cl::cat(DiffCategory));
static cl::opt<std::string> RightFilename(cl::Positional,
cl::desc("<second file>"),
- cl::Required);
+ cl::Required, cl::cat(DiffCategory));
static cl::list<std::string> GlobalsToCompare(cl::Positional,
- cl::desc("<globals to compare>"));
+ cl::desc("<globals to compare>"),
+ cl::cat(DiffCategory));
int main(int argc, char **argv) {
+ cl::HideUnrelatedOptions({&DiffCategory, &getColorCategory()});
cl::ParseCommandLineOptions(argc, argv);
LLVMContext Context;
diff --git a/llvm/tools/llvm-dis/llvm-dis.cpp b/llvm/tools/llvm-dis/llvm-dis.cpp
index 5d609468a380..62d165fb8413 100644
--- a/llvm/tools/llvm-dis/llvm-dis.cpp
+++ b/llvm/tools/llvm-dis/llvm-dis.cpp
@@ -35,37 +35,44 @@
#include <system_error>
using namespace llvm;
-static cl::opt<std::string>
-InputFilename(cl::Positional, cl::desc("<input bitcode>"), cl::init("-"));
+static cl::OptionCategory DisCategory("Disassembler Options");
-static cl::opt<std::string>
-OutputFilename("o", cl::desc("Override output filename"),
- cl::value_desc("filename"));
+static cl::list<std::string> InputFilenames(cl::Positional, cl::ZeroOrMore,
+ cl::desc("[input bitcode]..."),
+ cl::cat(DisCategory));
-static cl::opt<bool>
-Force("f", cl::desc("Enable binary output on terminals"));
+static cl::opt<std::string> OutputFilename("o",
+ cl::desc("Override output filename"),
+ cl::value_desc("filename"),
+ cl::cat(DisCategory));
-static cl::opt<bool>
-DontPrint("disable-output", cl::desc("Don't output the .ll file"), cl::Hidden);
+static cl::opt<bool> Force("f", cl::desc("Enable binary output on terminals"),
+ cl::cat(DisCategory));
+
+static cl::opt<bool> DontPrint("disable-output",
+ cl::desc("Don't output the .ll file"),
+ cl::Hidden, cl::cat(DisCategory));
static cl::opt<bool>
SetImporting("set-importing",
cl::desc("Set lazy loading to pretend to import a module"),
- cl::Hidden);
+ cl::Hidden, cl::cat(DisCategory));
static cl::opt<bool>
ShowAnnotations("show-annotations",
- cl::desc("Add informational comments to the .ll file"));
+ cl::desc("Add informational comments to the .ll file"),
+ cl::cat(DisCategory));
static cl::opt<bool> PreserveAssemblyUseListOrder(
"preserve-ll-uselistorder",
cl::desc("Preserve use-list order when writing LLVM assembly."),
- cl::init(false), cl::Hidden);
+ cl::init(false), cl::Hidden, cl::cat(DisCategory));
static cl::opt<bool>
MaterializeMetadata("materialize-metadata",
cl::desc("Load module without materializing metadata, "
- "then materialize only the metadata"));
+ "then materialize only the metadata"),
+ cl::cat(DisCategory));
namespace {
@@ -151,77 +158,89 @@ int main(int argc, char **argv) {
ExitOnErr.setBanner(std::string(argv[0]) + ": error: ");
+ cl::HideUnrelatedOptions({&DisCategory, &getColorCategory()});
+ cl::ParseCommandLineOptions(argc, argv, "llvm .bc -> .ll disassembler\n");
+
LLVMContext Context;
Context.setDiagnosticHandler(
std::make_unique<LLVMDisDiagnosticHandler>(argv[0]));
- cl::ParseCommandLineOptions(argc, argv, "llvm .bc -> .ll disassembler\n");
- std::unique_ptr<MemoryBuffer> MB =
- ExitOnErr(errorOrToExpected(MemoryBuffer::getFileOrSTDIN(InputFilename)));
+ if (InputFilenames.size() < 1) {
+ InputFilenames.push_back("-");
+ } else if (InputFilenames.size() > 1 && !OutputFilename.empty()) {
+ errs()
+ << "error: output file name cannot be set for multiple input files\n";
+ return 1;
+ }
+
+ for (std::string InputFilename : InputFilenames) {
+ std::unique_ptr<MemoryBuffer> MB = ExitOnErr(
+ errorOrToExpected(MemoryBuffer::getFileOrSTDIN(InputFilename)));
- BitcodeFileContents IF = ExitOnErr(llvm::getBitcodeFileContents(*MB));
+ BitcodeFileContents IF = ExitOnErr(llvm::getBitcodeFileContents(*MB));
- const size_t N = IF.Mods.size();
+ const size_t N = IF.Mods.size();
- if (OutputFilename == "-" && N > 1)
+ if (OutputFilename == "-" && N > 1)
errs() << "only single module bitcode files can be written to stdout\n";
- for (size_t i = 0; i < N; ++i) {
- BitcodeModule MB = IF.Mods[i];
- std::unique_ptr<Module> M = ExitOnErr(MB.getLazyModule(Context, MaterializeMetadata,
- SetImporting));
- if (MaterializeMetadata)
- ExitOnErr(M->materializeMetadata());
- else
- ExitOnErr(M->materializeAll());
-
- BitcodeLTOInfo LTOInfo = ExitOnErr(MB.getLTOInfo());
- std::unique_ptr<ModuleSummaryIndex> Index;
- if (LTOInfo.HasSummary)
- Index = ExitOnErr(MB.getSummary());
+ for (size_t I = 0; I < N; ++I) {
+ BitcodeModule MB = IF.Mods[I];
+ std::unique_ptr<Module> M = ExitOnErr(
+ MB.getLazyModule(Context, MaterializeMetadata, SetImporting));
+ if (MaterializeMetadata)
+ ExitOnErr(M->materializeMetadata());
+ else
+ ExitOnErr(M->materializeAll());
- std::string FinalFilename(OutputFilename);
+ BitcodeLTOInfo LTOInfo = ExitOnErr(MB.getLTOInfo());
+ std::unique_ptr<ModuleSummaryIndex> Index;
+ if (LTOInfo.HasSummary)
+ Index = ExitOnErr(MB.getSummary());
- // Just use stdout. We won't actually print anything on it.
- if (DontPrint)
- FinalFilename = "-";
+ std::string FinalFilename(OutputFilename);
- if (FinalFilename.empty()) { // Unspecified output, infer it.
- if (InputFilename == "-") {
+ // Just use stdout. We won't actually print anything on it.
+ if (DontPrint)
FinalFilename = "-";
+
+ if (FinalFilename.empty()) { // Unspecified output, infer it.
+ if (InputFilename == "-") {
+ FinalFilename = "-";
+ } else {
+ StringRef IFN = InputFilename;
+ FinalFilename = (IFN.endswith(".bc") ? IFN.drop_back(3) : IFN).str();
+ if (N > 1)
+ FinalFilename += std::string(".") + std::to_string(I);
+ FinalFilename += ".ll";
+ }
} else {
- StringRef IFN = InputFilename;
- FinalFilename = (IFN.endswith(".bc") ? IFN.drop_back(3) : IFN).str();
if (N > 1)
- FinalFilename += std::string(".") + std::to_string(i);
- FinalFilename += ".ll";
+ FinalFilename += std::string(".") + std::to_string(I);
}
- } else {
- if (N > 1)
- FinalFilename += std::string(".") + std::to_string(i);
- }
- std::error_code EC;
- std::unique_ptr<ToolOutputFile> Out(
- new ToolOutputFile(FinalFilename, EC, sys::fs::OF_Text));
- if (EC) {
- errs() << EC.message() << '\n';
- return 1;
- }
+ std::error_code EC;
+ std::unique_ptr<ToolOutputFile> Out(
+ new ToolOutputFile(FinalFilename, EC, sys::fs::OF_TextWithCRLF));
+ if (EC) {
+ errs() << EC.message() << '\n';
+ return 1;
+ }
- std::unique_ptr<AssemblyAnnotationWriter> Annotator;
- if (ShowAnnotations)
- Annotator.reset(new CommentWriter());
+ std::unique_ptr<AssemblyAnnotationWriter> Annotator;
+ if (ShowAnnotations)
+ Annotator.reset(new CommentWriter());
- // All that llvm-dis does is write the assembly to a file.
- if (!DontPrint) {
- M->print(Out->os(), Annotator.get(), PreserveAssemblyUseListOrder);
- if (Index)
- Index->print(Out->os());
- }
+ // All that llvm-dis does is write the assembly to a file.
+ if (!DontPrint) {
+ M->print(Out->os(), Annotator.get(), PreserveAssemblyUseListOrder);
+ if (Index)
+ Index->print(Out->os());
+ }
- // Declare success.
- Out->keep();
+ // Declare success.
+ Out->keep();
+ }
}
return 0;
diff --git a/llvm/tools/llvm-dwarfdump/SectionSizes.cpp b/llvm/tools/llvm-dwarfdump/SectionSizes.cpp
index 8c456d50baa7..731bf05fd758 100644
--- a/llvm/tools/llvm-dwarfdump/SectionSizes.cpp
+++ b/llvm/tools/llvm-dwarfdump/SectionSizes.cpp
@@ -18,10 +18,8 @@ static size_t getNameColumnWidth(const SectionSizes &Sizes,
const StringRef SectionNameTitle) {
// The minimum column width should be the size of "SECTION".
size_t Width = SectionNameTitle.size();
- for (const auto &DebugSec : Sizes.DebugSectionSizes) {
- StringRef SectionName = DebugSec.getKey();
- Width = std::max(Width, SectionName.size());
- }
+ for (const auto &It : Sizes.DebugSectionSizes)
+ Width = std::max(Width, It.first.size());
return Width;
}
@@ -29,8 +27,8 @@ static size_t getSizeColumnWidth(const SectionSizes &Sizes,
const StringRef SectionSizeTitle) {
// The minimum column width should be the size of the column title.
size_t Width = SectionSizeTitle.size();
- for (const auto &DebugSec : Sizes.DebugSectionSizes) {
- size_t NumWidth = std::to_string(DebugSec.getValue()).size();
+ for (const auto &It : Sizes.DebugSectionSizes) {
+ size_t NumWidth = std::to_string(It.second).size();
Width = std::max(Width, NumWidth);
}
return Width;
@@ -59,13 +57,13 @@ static void prettyPrintSectionSizes(const ObjectFile &Obj,
OS << "-";
OS << '\n';
- for (const auto &DebugSec : Sizes.DebugSectionSizes) {
- OS << left_justify(DebugSec.getKey(), NameColWidth) << " ";
+ for (const auto &It : Sizes.DebugSectionSizes) {
+ OS << left_justify(It.first, NameColWidth) << " ";
- auto NumBytes = std::to_string(DebugSec.getValue());
+ std::string NumBytes = std::to_string(It.second);
OS << right_justify(NumBytes, SizeColWidth) << " ("
- << format("%0.2f", DebugSec.getValue() /
- static_cast<double>(Sizes.TotalObjectSize) * 100)
+ << format("%0.2f",
+ It.second / static_cast<double>(Sizes.TotalObjectSize) * 100)
<< "%)\n";
}
@@ -95,11 +93,11 @@ void dwarfdump::calculateSectionSizes(const ObjectFile &Obj,
LLVM_DEBUG(dbgs() << SectionName.str() << ": " << Section.getSize()
<< '\n');
- if (!Section.isDebugSection(SectionName))
+ if (!Section.isDebugSection())
continue;
Sizes.TotalDebugSectionsSize += Section.getSize();
- Sizes.DebugSectionSizes[SectionName] += Section.getSize();
+ Sizes.DebugSectionSizes[std::string(SectionName)] += Section.getSize();
}
}
diff --git a/llvm/tools/llvm-dwarfdump/Statistics.cpp b/llvm/tools/llvm-dwarfdump/Statistics.cpp
index 82da06eab1d6..19a971afa311 100644
--- a/llvm/tools/llvm-dwarfdump/Statistics.cpp
+++ b/llvm/tools/llvm-dwarfdump/Statistics.cpp
@@ -8,9 +8,7 @@
#include "llvm-dwarfdump.h"
#include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringSet.h"
-#include "llvm/DebugInfo/DIContext.h"
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h"
#include "llvm/Object/ObjectFile.h"
@@ -21,13 +19,23 @@ using namespace llvm;
using namespace llvm::dwarfdump;
using namespace llvm::object;
+namespace {
/// This represents the number of categories of debug location coverage being
/// calculated. The first category is the number of variables with 0% location
/// coverage, but the last category is the number of variables with 100%
/// location coverage.
constexpr int NumOfCoverageCategories = 12;
-namespace {
+/// This is used for zero location coverage bucket.
+constexpr unsigned ZeroCoverageBucket = 0;
+
+/// This represents variables DIE offsets.
+using AbstractOriginVarsTy = llvm::SmallVector<uint64_t>;
+/// This maps function DIE offset to its variables.
+using AbstractOriginVarsTyMap = llvm::DenseMap<uint64_t, AbstractOriginVarsTy>;
+/// This represents function DIE offsets containing an abstract_origin.
+using FunctionsWithAbstractOriginTy = llvm::SmallVector<uint64_t>;
+
/// Holds statistics for one function (or other entity that has a PC range and
/// contains variables, such as a compile unit).
struct PerFunctionStats {
@@ -164,12 +172,14 @@ static void collectLocStats(uint64_t ScopeBytesCovered, uint64_t BytesInScope,
};
unsigned CoverageBucket = getCoverageBucket();
+
VarParamLocStats[CoverageBucket]++;
if (IsParam)
ParamLocStats[CoverageBucket]++;
else if (IsLocalVar)
LocalVarLocStats[CoverageBucket]++;
}
+
/// Construct an identifier for a given DIE from its Prefix, Name, DeclFileName
/// and DeclLine. The identifier aims to be unique for any unique entities,
/// but keeping the same among different instances of the same entity.
@@ -210,12 +220,18 @@ static uint64_t calculateOverlap(DWARFAddressRange A, DWARFAddressRange B) {
}
/// Collect debug info quality metrics for one DIE.
-static void collectStatsForDie(DWARFDie Die, std::string FnPrefix,
- std::string VarPrefix, uint64_t BytesInScope,
- uint32_t InlineDepth,
+static void collectStatsForDie(DWARFDie Die, const std::string &FnPrefix,
+ const std::string &VarPrefix,
+ uint64_t BytesInScope, uint32_t InlineDepth,
StringMap<PerFunctionStats> &FnStatMap,
GlobalStats &GlobalStats,
- LocationStats &LocStats) {
+ LocationStats &LocStats,
+ AbstractOriginVarsTy *AbstractOriginVariables) {
+ const dwarf::Tag Tag = Die.getTag();
+ // Skip CU node.
+ if (Tag == dwarf::DW_TAG_compile_unit)
+ return;
+
bool HasLoc = false;
bool HasSrcLoc = false;
bool HasType = false;
@@ -223,19 +239,22 @@ static void collectStatsForDie(DWARFDie Die, std::string FnPrefix,
uint64_t ScopeBytesCovered = 0;
uint64_t BytesEntryValuesCovered = 0;
auto &FnStats = FnStatMap[FnPrefix];
- bool IsParam = Die.getTag() == dwarf::DW_TAG_formal_parameter;
- bool IsLocalVar = Die.getTag() == dwarf::DW_TAG_variable;
- bool IsConstantMember = Die.getTag() == dwarf::DW_TAG_member &&
+ bool IsParam = Tag == dwarf::DW_TAG_formal_parameter;
+ bool IsLocalVar = Tag == dwarf::DW_TAG_variable;
+ bool IsConstantMember = Tag == dwarf::DW_TAG_member &&
Die.find(dwarf::DW_AT_const_value);
- if (Die.getTag() == dwarf::DW_TAG_call_site ||
- Die.getTag() == dwarf::DW_TAG_GNU_call_site) {
+ // For zero covered inlined variables the locstats will be
+ // calculated later.
+ bool DeferLocStats = false;
+
+ if (Tag == dwarf::DW_TAG_call_site || Tag == dwarf::DW_TAG_GNU_call_site) {
GlobalStats.CallSiteDIEs++;
return;
}
- if (Die.getTag() == dwarf::DW_TAG_call_site_parameter ||
- Die.getTag() == dwarf::DW_TAG_GNU_call_site_parameter) {
+ if (Tag == dwarf::DW_TAG_call_site_parameter ||
+ Tag == dwarf::DW_TAG_GNU_call_site_parameter) {
GlobalStats.CallSiteParamDIEs++;
return;
}
@@ -256,6 +275,21 @@ static void collectStatsForDie(DWARFDie Die, std::string FnPrefix,
if (Die.findRecursively(dwarf::DW_AT_type))
HasType = true;
+ if (Die.find(dwarf::DW_AT_abstract_origin)) {
+ if (Die.find(dwarf::DW_AT_location) || Die.find(dwarf::DW_AT_const_value)) {
+ if (AbstractOriginVariables) {
+ auto Offset = Die.find(dwarf::DW_AT_abstract_origin);
+ // Do not track this variable any more, since it has location
+ // coverage.
+ llvm::erase_value(*AbstractOriginVariables, (*Offset).getRawUValue());
+ }
+ } else {
+ // The locstats will be handled at the end of
+ // the collectStatsRecursive().
+ DeferLocStats = true;
+ }
+ }
+
auto IsEntryValue = [&](ArrayRef<uint8_t> D) -> bool {
DWARFUnit *U = Die.getDwarfUnit();
DataExtractor Data(toStringRef(D),
@@ -315,7 +349,7 @@ static void collectStatsForDie(DWARFDie Die, std::string FnPrefix,
}
// Calculate the debug location statistics.
- if (BytesInScope) {
+ if (BytesInScope && !DeferLocStats) {
LocStats.NumVarParam++;
if (IsParam)
LocStats.NumParam++;
@@ -389,27 +423,73 @@ static void collectStatsForDie(DWARFDie Die, std::string FnPrefix,
}
}
+/// Recursively collect variables from subprogram with DW_AT_inline attribute.
+static void collectAbstractOriginFnInfo(
+ DWARFDie Die, uint64_t SPOffset,
+ AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo) {
+ DWARFDie Child = Die.getFirstChild();
+ while (Child) {
+ const dwarf::Tag ChildTag = Child.getTag();
+ if (ChildTag == dwarf::DW_TAG_formal_parameter ||
+ ChildTag == dwarf::DW_TAG_variable)
+ GlobalAbstractOriginFnInfo[SPOffset].push_back(Child.getOffset());
+ else if (ChildTag == dwarf::DW_TAG_lexical_block)
+ collectAbstractOriginFnInfo(Child, SPOffset, GlobalAbstractOriginFnInfo);
+ Child = Child.getSibling();
+ }
+}
+
/// Recursively collect debug info quality metrics.
-static void collectStatsRecursive(DWARFDie Die, std::string FnPrefix,
- std::string VarPrefix, uint64_t BytesInScope,
- uint32_t InlineDepth,
- StringMap<PerFunctionStats> &FnStatMap,
- GlobalStats &GlobalStats,
- LocationStats &LocStats) {
+static void collectStatsRecursive(
+ DWARFDie Die, std::string FnPrefix, std::string VarPrefix,
+ uint64_t BytesInScope, uint32_t InlineDepth,
+ StringMap<PerFunctionStats> &FnStatMap, GlobalStats &GlobalStats,
+ LocationStats &LocStats,
+ AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo,
+ FunctionsWithAbstractOriginTy &FnsWithAbstractOriginToBeProcessed,
+ AbstractOriginVarsTy *AbstractOriginVarsPtr = nullptr) {
+ // Skip NULL nodes.
+ if (Die.isNULL())
+ return;
+
const dwarf::Tag Tag = Die.getTag();
// Skip function types.
if (Tag == dwarf::DW_TAG_subroutine_type)
return;
// Handle any kind of lexical scope.
+ const bool HasAbstractOrigin = Die.find(dwarf::DW_AT_abstract_origin) != None;
const bool IsFunction = Tag == dwarf::DW_TAG_subprogram;
const bool IsBlock = Tag == dwarf::DW_TAG_lexical_block;
const bool IsInlinedFunction = Tag == dwarf::DW_TAG_inlined_subroutine;
- if (IsFunction || IsInlinedFunction || IsBlock) {
+ // We want to know how many variables (with abstract_origin) don't have
+ // location info.
+ const bool IsCandidateForZeroLocCovTracking =
+ (IsInlinedFunction || (IsFunction && HasAbstractOrigin));
+
+ AbstractOriginVarsTy AbstractOriginVars;
+
+ // Get the vars of the inlined fn, so the locstats
+ // reports the missing vars (with coverage 0%).
+ if (IsCandidateForZeroLocCovTracking) {
+ auto OffsetFn = Die.find(dwarf::DW_AT_abstract_origin);
+ if (OffsetFn) {
+ uint64_t OffsetOfInlineFnCopy = (*OffsetFn).getRawUValue();
+ if (GlobalAbstractOriginFnInfo.count(OffsetOfInlineFnCopy)) {
+ AbstractOriginVars = GlobalAbstractOriginFnInfo[OffsetOfInlineFnCopy];
+ AbstractOriginVarsPtr = &AbstractOriginVars;
+ } else {
+ // This means that the DW_AT_inline fn copy is out of order,
+ // so this abstract origin instance will be processed later.
+ FnsWithAbstractOriginToBeProcessed.push_back(Die.getOffset());
+ AbstractOriginVarsPtr = nullptr;
+ }
+ }
+ }
+ if (IsFunction || IsInlinedFunction || IsBlock) {
// Reset VarPrefix when entering a new function.
- if (Die.getTag() == dwarf::DW_TAG_subprogram ||
- Die.getTag() == dwarf::DW_TAG_inlined_subroutine)
+ if (IsFunction || IsInlinedFunction)
VarPrefix = "v";
// Ignore forward declarations.
@@ -434,9 +514,15 @@ static void collectStatsRecursive(DWARFDie Die, std::string FnPrefix,
// Count the function.
if (!IsBlock) {
- // Skip over abstract origins.
- if (Die.find(dwarf::DW_AT_inline))
+ // Skip over abstract origins, but collect variables
+ // from it so it can be used for location statistics
+ // for inlined instancies.
+ if (Die.find(dwarf::DW_AT_inline)) {
+ uint64_t SPOffset = Die.getOffset();
+ collectAbstractOriginFnInfo(Die, SPOffset, GlobalAbstractOriginFnInfo);
return;
+ }
+
std::string FnID = constructDieID(Die);
// We've seen an instance of this function.
auto &FnStats = FnStatMap[FnID];
@@ -465,7 +551,7 @@ static void collectStatsRecursive(DWARFDie Die, std::string FnPrefix,
} else {
// Not a scope, visit the Die itself. It could be a variable.
collectStatsForDie(Die, FnPrefix, VarPrefix, BytesInScope, InlineDepth,
- FnStatMap, GlobalStats, LocStats);
+ FnStatMap, GlobalStats, LocStats, AbstractOriginVarsPtr);
}
// Set InlineDepth correctly for child recursion
@@ -485,10 +571,33 @@ static void collectStatsRecursive(DWARFDie Die, std::string FnPrefix,
if (Child.getTag() == dwarf::DW_TAG_formal_parameter)
ChildVarPrefix += 'p' + toHex(FormalParameterIndex++) + '.';
- collectStatsRecursive(Child, FnPrefix, ChildVarPrefix, BytesInScope,
- InlineDepth, FnStatMap, GlobalStats, LocStats);
+ collectStatsRecursive(
+ Child, FnPrefix, ChildVarPrefix, BytesInScope, InlineDepth, FnStatMap,
+ GlobalStats, LocStats, GlobalAbstractOriginFnInfo,
+ FnsWithAbstractOriginToBeProcessed, AbstractOriginVarsPtr);
Child = Child.getSibling();
}
+
+ if (!IsCandidateForZeroLocCovTracking)
+ return;
+
+ // After we have processed all vars of the inlined function (or function with
+ // an abstract_origin), we want to know how many variables have no location.
+ for (auto Offset : AbstractOriginVars) {
+ LocStats.NumVarParam++;
+ LocStats.VarParamLocStats[ZeroCoverageBucket]++;
+ auto FnDie = Die.getDwarfUnit()->getDIEForOffset(Offset);
+ if (!FnDie)
+ continue;
+ auto Tag = FnDie.getTag();
+ if (Tag == dwarf::DW_TAG_formal_parameter) {
+ LocStats.NumParam++;
+ LocStats.ParamLocStats[ZeroCoverageBucket]++;
+ } else if (Tag == dwarf::DW_TAG_variable) {
+ LocStats.NumVar++;
+ LocStats.LocalVarLocStats[ZeroCoverageBucket]++;
+ }
+ }
}
/// Print human-readable output.
@@ -536,9 +645,63 @@ static void printLocationStats(json::OStream &J, const char *Key,
}
static void printSectionSizes(json::OStream &J, const SectionSizes &Sizes) {
- for (const auto &DebugSec : Sizes.DebugSectionSizes)
- J.attribute((Twine("#bytes in ") + DebugSec.getKey()).str(),
- int64_t(DebugSec.getValue()));
+ for (const auto &It : Sizes.DebugSectionSizes)
+ J.attribute((Twine("#bytes in ") + It.first).str(), int64_t(It.second));
+}
+
+/// Stop tracking variables that contain abstract_origin with a location.
+/// This is used for out-of-order DW_AT_inline subprograms only.
+static void updateVarsWithAbstractOriginLocCovInfo(
+ DWARFDie FnDieWithAbstractOrigin,
+ AbstractOriginVarsTy &AbstractOriginVars) {
+ DWARFDie Child = FnDieWithAbstractOrigin.getFirstChild();
+ while (Child) {
+ const dwarf::Tag ChildTag = Child.getTag();
+ if ((ChildTag == dwarf::DW_TAG_formal_parameter ||
+ ChildTag == dwarf::DW_TAG_variable) &&
+ (Child.find(dwarf::DW_AT_location) ||
+ Child.find(dwarf::DW_AT_const_value))) {
+ auto OffsetVar = Child.find(dwarf::DW_AT_abstract_origin);
+ if (OffsetVar)
+ llvm::erase_value(AbstractOriginVars, (*OffsetVar).getRawUValue());
+ } else if (ChildTag == dwarf::DW_TAG_lexical_block)
+ updateVarsWithAbstractOriginLocCovInfo(Child, AbstractOriginVars);
+ Child = Child.getSibling();
+ }
+}
+
+/// Collect zero location coverage for inlined variables which refer to
+/// a DW_AT_inline copy of subprogram that is out of order in the DWARF.
+/// Also cover the variables of a concrete function (represented with
+/// the DW_TAG_subprogram) with an abstract_origin attribute.
+static void collectZeroLocCovForVarsWithAbstractOrigin(
+ DWARFUnit *DwUnit, GlobalStats &GlobalStats, LocationStats &LocStats,
+ AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo,
+ FunctionsWithAbstractOriginTy &FnsWithAbstractOriginToBeProcessed) {
+ for (auto FnOffset : FnsWithAbstractOriginToBeProcessed) {
+ DWARFDie FnDieWithAbstractOrigin = DwUnit->getDIEForOffset(FnOffset);
+ auto FnCopy = FnDieWithAbstractOrigin.find(dwarf::DW_AT_abstract_origin);
+ AbstractOriginVarsTy AbstractOriginVars;
+ if (!FnCopy)
+ continue;
+
+ AbstractOriginVars = GlobalAbstractOriginFnInfo[(*FnCopy).getRawUValue()];
+ updateVarsWithAbstractOriginLocCovInfo(FnDieWithAbstractOrigin,
+ AbstractOriginVars);
+
+ for (auto Offset : AbstractOriginVars) {
+ LocStats.NumVarParam++;
+ LocStats.VarParamLocStats[ZeroCoverageBucket]++;
+ auto Tag = DwUnit->getDIEForOffset(Offset).getTag();
+ if (Tag == dwarf::DW_TAG_formal_parameter) {
+ LocStats.NumParam++;
+ LocStats.ParamLocStats[ZeroCoverageBucket]++;
+ } else if (Tag == dwarf::DW_TAG_variable) {
+ LocStats.NumVar++;
+ LocStats.LocalVarLocStats[ZeroCoverageBucket]++;
+ }
+ }
+ }
}
/// \}
@@ -558,10 +721,27 @@ bool dwarfdump::collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
GlobalStats GlobalStats;
LocationStats LocStats;
StringMap<PerFunctionStats> Statistics;
- for (const auto &CU : static_cast<DWARFContext *>(&DICtx)->compile_units())
- if (DWARFDie CUDie = CU->getNonSkeletonUnitDIE(false))
+ for (const auto &CU : static_cast<DWARFContext *>(&DICtx)->compile_units()) {
+ if (DWARFDie CUDie = CU->getNonSkeletonUnitDIE(false)) {
+ // These variables are being reset for each CU, since there could be
+ // a situation where we have two subprogram DIEs with the same offsets
+ // in two diferent CUs, and we can end up using wrong variables info
+ // when trying to resolve abstract_origin attribute.
+ // TODO: Handle LTO cases where the abstract origin of
+ // the function is in a different CU than the one it's
+ // referenced from or inlined into.
+ AbstractOriginVarsTyMap GlobalAbstractOriginFnInfo;
+ FunctionsWithAbstractOriginTy FnsWithAbstractOriginToBeProcessed;
+
collectStatsRecursive(CUDie, "/", "g", 0, 0, Statistics, GlobalStats,
- LocStats);
+ LocStats, GlobalAbstractOriginFnInfo,
+ FnsWithAbstractOriginToBeProcessed);
+
+ collectZeroLocCovForVarsWithAbstractOrigin(
+ CUDie.getDwarfUnit(), GlobalStats, LocStats,
+ GlobalAbstractOriginFnInfo, FnsWithAbstractOriginToBeProcessed);
+ }
+ }
/// Collect the sizes of debug sections.
SectionSizes Sizes;
@@ -570,7 +750,7 @@ bool dwarfdump::collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
/// The version number should be increased every time the algorithm is changed
/// (including bug fixes). New metrics may be added without increasing the
/// version.
- unsigned Version = 6;
+ unsigned Version = 8;
unsigned VarParamTotal = 0;
unsigned VarParamUnique = 0;
unsigned VarParamWithLoc = 0;
diff --git a/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp b/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp
index 4322f125a84d..a324ff710af5 100644
--- a/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp
+++ b/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp
@@ -130,7 +130,8 @@ cl::OptionCategory SectionCategory("Section-specific Dump Options",
static opt<bool> DumpAll("all", desc("Dump all debug info sections"),
cat(SectionCategory));
-static alias DumpAllAlias("a", desc("Alias for -all"), aliasopt(DumpAll));
+static alias DumpAllAlias("a", desc("Alias for --all"), aliasopt(DumpAll),
+ cl::NotHidden);
// Options for dumping specific sections.
static unsigned DumpType = DIDT_Null;
@@ -143,7 +144,8 @@ static std::array<llvm::Optional<uint64_t>, (unsigned)DIDT_ID_Count>
#include "llvm/BinaryFormat/Dwarf.def"
#undef HANDLE_DWARF_SECTION
-static alias DumpDebugFrameAlias("eh-frame", desc("Alias for -debug-frame"),
+// The aliased DumpDebugFrame is created by the Dwarf.def x-macro just above.
+static alias DumpDebugFrameAlias("eh-frame", desc("Alias for --debug-frame"),
NotHidden, cat(SectionCategory),
aliasopt(DumpDebugFrame));
static list<std::string>
@@ -164,19 +166,21 @@ static list<std::string>
"accelerator tables are available, the slower but more complete "
"-name option can be used instead."),
value_desc("name"), cat(DwarfDumpCategory));
-static alias FindAlias("f", desc("Alias for -find."), aliasopt(Find));
+static alias FindAlias("f", desc("Alias for --find."), aliasopt(Find),
+ cl::NotHidden);
static opt<bool> IgnoreCase("ignore-case",
desc("Ignore case distinctions when searching."),
value_desc("i"), cat(DwarfDumpCategory));
-static alias IgnoreCaseAlias("i", desc("Alias for -ignore-case."),
- aliasopt(IgnoreCase));
+static alias IgnoreCaseAlias("i", desc("Alias for --ignore-case."),
+ aliasopt(IgnoreCase), cl::NotHidden);
static list<std::string> Name(
"name",
desc("Find and print all debug info entries whose name (DW_AT_name "
"attribute) matches the exact text in <pattern>. When used with the "
"the -regex option <pattern> is interpreted as a regular expression."),
value_desc("pattern"), cat(DwarfDumpCategory));
-static alias NameAlias("n", desc("Alias for -name"), aliasopt(Name));
+static alias NameAlias("n", desc("Alias for --name"), aliasopt(Name),
+ cl::NotHidden);
static opt<uint64_t>
Lookup("lookup",
desc("Lookup <address> in the debug information and print out any "
@@ -193,34 +197,36 @@ static opt<bool>
desc("Treat any <pattern> strings as regular expressions when "
"searching instead of just as an exact string match."),
cat(DwarfDumpCategory));
-static alias RegexAlias("x", desc("Alias for -regex"), aliasopt(UseRegex));
+static alias RegexAlias("x", desc("Alias for --regex"), aliasopt(UseRegex),
+ cl::NotHidden);
static opt<bool>
ShowChildren("show-children",
desc("Show a debug info entry's children when selectively "
"printing entries."),
cat(DwarfDumpCategory));
-static alias ShowChildrenAlias("c", desc("Alias for -show-children."),
- aliasopt(ShowChildren));
+static alias ShowChildrenAlias("c", desc("Alias for --show-children."),
+ aliasopt(ShowChildren), cl::NotHidden);
static opt<bool>
ShowParents("show-parents",
desc("Show a debug info entry's parents when selectively "
"printing entries."),
cat(DwarfDumpCategory));
-static alias ShowParentsAlias("p", desc("Alias for -show-parents."),
- aliasopt(ShowParents));
+static alias ShowParentsAlias("p", desc("Alias for --show-parents."),
+ aliasopt(ShowParents), cl::NotHidden);
static opt<bool>
ShowForm("show-form",
desc("Show DWARF form types after the DWARF attribute types."),
cat(DwarfDumpCategory));
-static alias ShowFormAlias("F", desc("Alias for -show-form."),
- aliasopt(ShowForm), cat(DwarfDumpCategory));
+static alias ShowFormAlias("F", desc("Alias for --show-form."),
+ aliasopt(ShowForm), cat(DwarfDumpCategory),
+ cl::NotHidden);
static opt<unsigned>
ChildRecurseDepth("recurse-depth",
desc("Only recurse to a depth of N when displaying "
"children of debug info entries."),
cat(DwarfDumpCategory), init(-1U), value_desc("N"));
-static alias ChildRecurseDepthAlias("r", desc("Alias for -recurse-depth."),
- aliasopt(ChildRecurseDepth));
+static alias ChildRecurseDepthAlias("r", desc("Alias for --recurse-depth."),
+ aliasopt(ChildRecurseDepth), cl::NotHidden);
static opt<unsigned>
ParentRecurseDepth("parent-recurse-depth",
desc("Only recurse to a depth of N when displaying "
@@ -245,25 +251,30 @@ static opt<bool> Quiet("quiet", desc("Use with -verify to not emit to STDOUT."),
cat(DwarfDumpCategory));
static opt<bool> DumpUUID("uuid", desc("Show the UUID for each architecture."),
cat(DwarfDumpCategory));
-static alias DumpUUIDAlias("u", desc("Alias for -uuid."), aliasopt(DumpUUID));
+static alias DumpUUIDAlias("u", desc("Alias for --uuid."), aliasopt(DumpUUID),
+ cl::NotHidden);
static opt<bool> Verbose("verbose",
desc("Print more low-level encoding details."),
cat(DwarfDumpCategory));
-static alias VerboseAlias("v", desc("Alias for -verbose."), aliasopt(Verbose),
- cat(DwarfDumpCategory));
+static alias VerboseAlias("v", desc("Alias for --verbose."), aliasopt(Verbose),
+ cat(DwarfDumpCategory), cl::NotHidden);
static cl::extrahelp
HelpResponse("\nPass @FILE as argument to read options from FILE.\n");
} // namespace
/// @}
//===----------------------------------------------------------------------===//
-static void error(StringRef Prefix, std::error_code EC) {
- if (!EC)
+static void error(StringRef Prefix, Error Err) {
+ if (!Err)
return;
- WithColor::error() << Prefix << ": " << EC.message() << "\n";
+ WithColor::error() << Prefix << ": " << toString(std::move(Err)) << "\n";
exit(1);
}
+static void error(StringRef Prefix, std::error_code EC) {
+ error(Prefix, errorCodeToError(EC));
+}
+
static DIDumpOptions getDumpOpts(DWARFContext &C) {
DIDumpOptions DumpOpts;
DumpOpts.DumpType = DumpType;
@@ -484,7 +495,7 @@ static bool verifyObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
// fails.
raw_ostream &stream = Quiet ? nulls() : OS;
stream << "Verifying " << Filename.str() << ":\tfile format "
- << Obj.getFileFormatName() << "\n";
+ << Obj.getFileFormatName() << "\n";
bool Result = DICtx.verify(stream, getDumpOpts(DICtx));
if (Result)
stream << "No errors.\n";
@@ -502,13 +513,13 @@ static bool handleArchive(StringRef Filename, Archive &Arch,
Error Err = Error::success();
for (auto Child : Arch.children(Err)) {
auto BuffOrErr = Child.getMemoryBufferRef();
- error(Filename, errorToErrorCode(BuffOrErr.takeError()));
+ error(Filename, BuffOrErr.takeError());
auto NameOrErr = Child.getName();
- error(Filename, errorToErrorCode(NameOrErr.takeError()));
+ error(Filename, NameOrErr.takeError());
std::string Name = (Filename + "(" + NameOrErr.get() + ")").str();
Result &= handleBuffer(Name, BuffOrErr.get(), HandleObj, OS);
}
- error(Filename, errorToErrorCode(std::move(Err)));
+ error(Filename, std::move(Err));
return Result;
}
@@ -516,7 +527,7 @@ static bool handleArchive(StringRef Filename, Archive &Arch,
static bool handleBuffer(StringRef Filename, MemoryBufferRef Buffer,
HandlerFn HandleObj, raw_ostream &OS) {
Expected<std::unique_ptr<Binary>> BinOrErr = object::createBinary(Buffer);
- error(Filename, errorToErrorCode(BinOrErr.takeError()));
+ error(Filename, BinOrErr.takeError());
bool Result = true;
auto RecoverableErrorHandler = [&](Error E) {
@@ -530,8 +541,7 @@ static bool handleBuffer(StringRef Filename, MemoryBufferRef Buffer,
if (!HandleObj(*Obj, *DICtx, Filename, OS))
Result = false;
}
- }
- else if (auto *Fat = dyn_cast<MachOUniversalBinary>(BinOrErr->get()))
+ } else if (auto *Fat = dyn_cast<MachOUniversalBinary>(BinOrErr->get()))
for (auto &ObjForArch : Fat->objects()) {
std::string ObjName =
(Filename + "(" + ObjForArch.getArchFlagName() + ")").str();
@@ -547,7 +557,7 @@ static bool handleBuffer(StringRef Filename, MemoryBufferRef Buffer,
} else
consumeError(MachOOrErr.takeError());
if (auto ArchiveOrErr = ObjForArch.getAsArchive()) {
- error(ObjName, errorToErrorCode(ArchiveOrErr.takeError()));
+ error(ObjName, ArchiveOrErr.takeError());
if (!handleArchive(ObjName, *ArchiveOrErr.get(), HandleObj, OS))
Result = false;
continue;
@@ -562,7 +572,7 @@ static bool handleBuffer(StringRef Filename, MemoryBufferRef Buffer,
static bool handleFile(StringRef Filename, HandlerFn HandleObj,
raw_ostream &OS) {
ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr =
- MemoryBuffer::getFileOrSTDIN(Filename);
+ MemoryBuffer::getFileOrSTDIN(Filename);
error(Filename, BuffOrErr.getError());
std::unique_ptr<MemoryBuffer> Buffer = std::move(BuffOrErr.get());
return handleBuffer(Filename, *Buffer, HandleObj, OS);
@@ -613,7 +623,8 @@ int main(int argc, char **argv) {
llvm::InitializeAllTargetInfos();
llvm::InitializeAllTargetMCs();
- HideUnrelatedOptions({&DwarfDumpCategory, &SectionCategory, &ColorCategory});
+ HideUnrelatedOptions(
+ {&DwarfDumpCategory, &SectionCategory, &getColorCategory()});
cl::ParseCommandLineOptions(
argc, argv,
"pretty-print DWARF debug information in object files"
@@ -628,8 +639,8 @@ int main(int argc, char **argv) {
}
std::error_code EC;
- ToolOutputFile OutputFile(OutputFilename, EC, sys::fs::OF_Text);
- error("Unable to open output file" + OutputFilename, EC);
+ ToolOutputFile OutputFile(OutputFilename, EC, sys::fs::OF_TextWithCRLF);
+ error("unable to open output file " + OutputFilename, EC);
// Don't remove output file if we exit with an error.
OutputFile.keep();
@@ -659,7 +670,8 @@ int main(int argc, char **argv) {
}
// Unless dumping a specific DIE, default to --show-children.
- if (!ShowChildren && !Verify && !OffsetRequested && Name.empty() && Find.empty())
+ if (!ShowChildren && !Verify && !OffsetRequested && Name.empty() &&
+ Find.empty())
ShowChildren = true;
// Defaults to a.out if no filenames specified.
diff --git a/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.h b/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.h
index dc41298265d2..cf7da56c91f8 100644
--- a/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.h
+++ b/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.h
@@ -9,6 +9,8 @@
#ifndef LLVM_TOOLS_LLVM_DWARFDUMP_LLVM_DWARFDUMP_H
#define LLVM_TOOLS_LLVM_DWARFDUMP_LLVM_DWARFDUMP_H
+#include "llvm/ADT/MapVector.h"
+#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/Twine.h"
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/Object/ObjectFile.h"
@@ -21,7 +23,7 @@ namespace dwarfdump {
struct SectionSizes {
/// Map of .debug section names and their sizes across all such-named
/// sections.
- StringMap<uint64_t> DebugSectionSizes;
+ MapVector<std::string, uint64_t, StringMap<uint64_t>> DebugSectionSizes;
/// Total number of bytes of all sections.
uint64_t TotalObjectSize = 0;
/// Total number of bytes of all debug sections.
diff --git a/llvm/tools/llvm-dwp/DWPError.cpp b/llvm/tools/llvm-dwp/DWPError.cpp
deleted file mode 100644
index 21d53ed6d198..000000000000
--- a/llvm/tools/llvm-dwp/DWPError.cpp
+++ /dev/null
@@ -1,3 +0,0 @@
-#include "DWPError.h"
-using namespace llvm;
-char DWPError::ID;
diff --git a/llvm/tools/llvm-dwp/DWPError.h b/llvm/tools/llvm-dwp/DWPError.h
deleted file mode 100644
index 62025ed4caa5..000000000000
--- a/llvm/tools/llvm-dwp/DWPError.h
+++ /dev/null
@@ -1,23 +0,0 @@
-#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/llvm/tools/llvm-dwp/DWPStringPool.h b/llvm/tools/llvm-dwp/DWPStringPool.h
deleted file mode 100644
index e423076f4333..000000000000
--- a/llvm/tools/llvm-dwp/DWPStringPool.h
+++ /dev/null
@@ -1,56 +0,0 @@
-#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/llvm/tools/llvm-dwp/llvm-dwp.cpp b/llvm/tools/llvm-dwp/llvm-dwp.cpp
index 9aed3526b0aa..1f583728c141 100644
--- a/llvm/tools/llvm-dwp/llvm-dwp.cpp
+++ b/llvm/tools/llvm-dwp/llvm-dwp.cpp
@@ -10,39 +10,22 @@
// package files).
//
//===----------------------------------------------------------------------===//
-#include "DWPError.h"
-#include "DWPStringPool.h"
-#include "llvm/ADT/MapVector.h"
-#include "llvm/ADT/Optional.h"
-#include "llvm/ADT/STLExtras.h"
-#include "llvm/DebugInfo/DWARF/DWARFContext.h"
-#include "llvm/DebugInfo/DWARF/DWARFFormValue.h"
-#include "llvm/DebugInfo/DWARF/DWARFUnitIndex.h"
+#include "llvm/DWP/DWP.h"
+#include "llvm/DWP/DWPError.h"
+#include "llvm/DWP/DWPStringPool.h"
#include "llvm/MC/MCAsmBackend.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCCodeEmitter.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCInstrInfo.h"
-#include "llvm/MC/MCObjectFileInfo.h"
#include "llvm/MC/MCObjectWriter.h"
-#include "llvm/MC/MCRegisterInfo.h"
-#include "llvm/MC/MCStreamer.h"
#include "llvm/MC/MCTargetOptionsCommandFlags.h"
-#include "llvm/Object/Decompressor.h"
-#include "llvm/Object/ObjectFile.h"
#include "llvm/Support/CommandLine.h"
-#include "llvm/Support/DataExtractor.h"
-#include "llvm/Support/Error.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/InitLLVM.h"
-#include "llvm/Support/MathExtras.h"
-#include "llvm/Support/MemoryBuffer.h"
-#include "llvm/Support/Path.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/ToolOutputFile.h"
-#include "llvm/Support/WithColor.h"
-#include "llvm/Support/raw_ostream.h"
using namespace llvm;
using namespace llvm::object;
@@ -64,449 +47,6 @@ static cl::opt<std::string> OutputFilename(cl::Required, "o",
cl::value_desc("filename"),
cl::cat(DwpCategory));
-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;
-
- DenseMap<uint64_t, uint32_t> OffsetRemapping;
-
- DataExtractor Data(CurStrSection, true, 0);
- uint64_t LocalOffset = 0;
- uint64_t PrevOffset = 0;
- while (const char *s = Data.getCStr(&LocalOffset)) {
- OffsetRemapping[PrevOffset] =
- Strings.getOffset(s, LocalOffset - PrevOffset);
- PrevOffset = LocalOffset;
- }
-
- Data = DataExtractor(CurStrOffsetSection, true, 0);
-
- Out.SwitchSection(StrOffsetSection);
-
- uint64_t Offset = 0;
- uint64_t Size = CurStrOffsetSection.size();
- while (Offset < Size) {
- auto OldOffset = Data.getU32(&Offset);
- auto NewOffset = OffsetRemapping[OldOffset];
- Out.emitIntValue(NewOffset, 4);
- }
-}
-
-static uint64_t getCUAbbrev(StringRef Abbrev, uint64_t AbbrCode) {
- uint64_t CurCode;
- uint64_t Offset = 0;
- DataExtractor AbbrevData(Abbrev, true, 0);
- while ((CurCode = AbbrevData.getULEB128(&Offset)) != AbbrCode) {
- // Tag
- AbbrevData.getULEB128(&Offset);
- // DW_CHILDREN
- AbbrevData.getU8(&Offset);
- // Attributes
- while (AbbrevData.getULEB128(&Offset) | AbbrevData.getULEB128(&Offset))
- ;
- }
- return Offset;
-}
-
-struct CompileUnitIdentifiers {
- uint64_t Signature = 0;
- const char *Name = "";
- const char *DWOName = "";
-};
-
-static Expected<const char *>
-getIndexedString(dwarf::Form Form, DataExtractor InfoData,
- uint64_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);
- uint64_t StrOffsetsOffset = 4 * StrIndex;
- uint64_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) {
- uint64_t Offset = 0;
- DataExtractor InfoData(Info, true, 0);
- dwarf::DwarfFormat Format = dwarf::DwarfFormat::DWARF32;
- uint64_t Length = InfoData.getU32(&Offset);
- CompileUnitIdentifiers ID;
- Optional<uint64_t> Signature = None;
- // If the length is 0xffffffff, then this indictes that this is a DWARF 64
- // stream and the length is actually encoded into a 64 bit value that follows.
- if (Length == 0xffffffffU) {
- Format = dwarf::DwarfFormat::DWARF64;
- Length = InfoData.getU64(&Offset);
- }
- uint16_t Version = InfoData.getU16(&Offset);
- if (Version >= 5) {
- auto UnitType = InfoData.getU8(&Offset);
- if (UnitType != dwarf::DW_UT_split_compile)
- return make_error<DWPError>(
- std::string("unit type DW_UT_split_compile type not found in "
- "debug_info header. Unexpected unit type 0x" +
- utostr(UnitType) + " found"));
- }
- InfoData.getU32(&Offset); // Abbrev offset (should be zero)
- uint8_t AddrSize = InfoData.getU8(&Offset);
- if (Version >= 5)
- Signature = InfoData.getU64(&Offset);
- uint32_t AbbrCode = InfoData.getULEB128(&Offset);
-
- DataExtractor AbbrevData(Abbrev, true, 0);
- uint64_t AbbrevOffset = getCUAbbrev(Abbrev, AbbrCode);
- auto Tag = static_cast<dwarf::Tag>(AbbrevData.getULEB128(&AbbrevOffset));
- 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;
- dwarf::Form Form;
- while ((Name = AbbrevData.getULEB128(&AbbrevOffset)) |
- (Form = static_cast<dwarf::Form>(AbbrevData.getULEB128(&AbbrevOffset))) &&
- (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:
- case dwarf::DW_AT_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:
- Signature = InfoData.getU64(&Offset);
- break;
- default:
- DWARFFormValue::skipValue(Form, InfoData, &Offset,
- dwarf::FormParams({Version, AddrSize, Format}));
- }
- }
- if (!Signature)
- return make_error<DWPError>("compile unit missing dwo_id");
- ID.Signature = *Signature;
- return ID;
-}
-
-struct UnitIndexEntry {
- DWARFUnitIndex::Entry::SectionContribution Contributions[8];
- std::string Name;
- std::string DWOName;
- StringRef DWPName;
-};
-
-static bool isSupportedSectionKind(DWARFSectionKind Kind) {
- return Kind != DW_SECT_EXT_unknown;
-}
-
-// Convert an internal section identifier into the index to use with
-// UnitIndexEntry::Contributions.
-static unsigned getContributionIndex(DWARFSectionKind Kind) {
- // Assuming the pre-standard DWP format.
- assert(serializeSectionKind(Kind, 2) >= DW_SECT_INFO);
- return serializeSectionKind(Kind, 2) - DW_SECT_INFO;
-}
-
-// Convert a UnitIndexEntry::Contributions index to the corresponding on-disk
-// value of the section identifier.
-static unsigned getOnDiskSectionId(unsigned Index) {
- return Index + DW_SECT_INFO;
-}
-
-static StringRef getSubsection(StringRef Section,
- const DWARFUnitIndex::Entry &Entry,
- DWARFSectionKind Kind) {
- const auto *Off = Entry.getContribution(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);
- for (const DWARFUnitIndex::Entry &E : TUIndex.getRows()) {
- auto *I = E.getContributions();
- 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()) {
- if (!isSupportedSectionKind(Kind))
- continue;
- auto &C = Entry.Contributions[getContributionIndex(Kind)];
- C.Offset += I->Offset;
- C.Length = I->Length;
- ++I;
- }
- unsigned TypesIndex = getContributionIndex(DW_SECT_EXT_TYPES);
- auto &C = Entry.Contributions[TypesIndex];
- Out.emitBytes(Types.substr(
- C.Offset - TUEntry.Contributions[TypesIndex].Offset, C.Length));
- C.Offset = TypesOffset;
- TypesOffset += C.Length;
- }
-}
-
-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);
- uint64_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[getContributionIndex(DW_SECT_EXT_TYPES)];
- 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,
- 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.second.Contributions); ++i)
- if (ContributionOffsets[i])
- Out.emitIntValue(E.second.Contributions[i].*Field, 4);
-}
-
-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)
- ++Columns;
-
- std::vector<unsigned> Buckets(NextPowerOf2(3 * IndexEntries.size() / 2));
- uint64_t Mask = Buckets.size() - 1;
- 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.begin()[Buckets[H] - 1].first &&
- "Duplicate unit");
- H = (H + HP) & Mask;
- }
- Buckets[H] = i + 1;
- ++i;
- }
-
- Out.SwitchSection(Section);
- Out.emitIntValue(2, 4); // Version
- Out.emitIntValue(Columns, 4); // Columns
- Out.emitIntValue(IndexEntries.size(), 4); // Num Units
- Out.emitIntValue(Buckets.size(), 4); // Num Buckets
-
- // Write the signatures.
- for (const auto &I : Buckets)
- Out.emitIntValue(I ? IndexEntries.begin()[I - 1].first : 0, 8);
-
- // Write the indexes.
- for (const auto &I : Buckets)
- Out.emitIntValue(I, 4);
-
- // Write the column headers (which sections will appear in the table)
- for (size_t i = 0; i != ContributionOffsets.size(); ++i)
- if (ContributionOffsets[i])
- Out.emitIntValue(getOnDiskSectionId(i), 4);
-
- // Write the offsets.
- writeIndexTable(Out, ContributionOffsets, IndexEntries,
- &DWARFUnitIndex::Entry::SectionContribution::Offset);
-
- // Write the lengths.
- writeIndexTable(Out, ContributionOffsets, IndexEntries,
- &DWARFUnitIndex::Entry::SectionContribution::Length);
-}
-
-static std::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 createError(StringRef Name, Error E) {
- return make_error<DWPError>(
- ("failure while decompressing compressed section: '" + Name + "', " +
- llvm::toString(std::move(E)))
- .str());
-}
-
-static Error
-handleCompressedSection(std::deque<SmallString<32>> &UncompressedSections,
- StringRef &Name, StringRef &Contents) {
- if (!Decompressor::isGnuStyle(Name))
- return Error::success();
-
- Expected<Decompressor> Dec =
- Decompressor::create(Name, Contents, false /*IsLE*/, false /*Is64Bit*/);
- if (!Dec)
- return createError(Name, Dec.takeError());
-
- UncompressedSections.emplace_back();
- if (Error E = Dec->resizeAndDecompress(UncompressedSections.back()))
- return createError(Name, std::move(E));
-
- Name = Name.substr(2); // Drop ".z"
- Contents = UncompressedSections.back();
- return Error::success();
-}
-
-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::success();
-
- if (Section.isVirtual())
- return Error::success();
-
- Expected<StringRef> NameOrErr = Section.getName();
- if (!NameOrErr)
- return NameOrErr.takeError();
- StringRef Name = *NameOrErr;
-
- Expected<StringRef> ContentsOrErr = Section.getContents();
- if (!ContentsOrErr)
- return ContentsOrErr.takeError();
- StringRef Contents = *ContentsOrErr;
-
- if (auto Err = handleCompressedSection(UncompressedSections, Name, Contents))
- return Err;
-
- Name = Name.substr(Name.find_first_not_of("._"));
-
- auto SectionPair = KnownSections.find(Name);
- if (SectionPair == KnownSections.end())
- return Error::success();
-
- if (DWARFSectionKind Kind = SectionPair->second.second) {
- auto Index = getContributionIndex(Kind);
- if (Kind != DW_SECT_EXT_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::success();
-}
-
-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 Expected<SmallVector<std::string, 16>>
getDWOFilenames(StringRef ExecFilename) {
auto ErrOrObj = object::ObjectFile::createObjectFile(ExecFilename);
@@ -526,8 +66,8 @@ getDWOFilenames(StringRef ExecFilename) {
std::string DWOCompDir =
dwarf::toString(Die.find(dwarf::DW_AT_comp_dir), "");
if (!DWOCompDir.empty()) {
- SmallString<16> DWOPath;
- sys::path::append(DWOPath, DWOCompDir, DWOName);
+ SmallString<16> DWOPath(std::move(DWOName));
+ sys::fs::make_absolute(DWOCompDir, DWOPath);
DWOPaths.emplace_back(DWOPath.data(), DWOPath.size());
} else {
DWOPaths.push_back(std::move(DWOName));
@@ -536,160 +76,6 @@ getDWOFilenames(StringRef ExecFilename) {
return std::move(DWOPaths);
}
-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_EXT_TYPES}},
- {"debug_str_offsets.dwo", {StrOffsetSection, DW_SECT_STR_OFFSETS}},
- {"debug_str.dwo", {StrSection, static_cast<DWARFSectionKind>(0)}},
- {"debug_loc.dwo", {MCOFI.getDwarfLocDWOSection(), DW_SECT_EXT_LOC}},
- {"debug_line.dwo", {MCOFI.getDwarfLineDWOSection(), DW_SECT_LINE}},
- {"debug_abbrev.dwo", {MCOFI.getDwarfAbbrevDWOSection(), DW_SECT_ABBREV}},
- {"debug_cu_index", {CUIndexSection, static_cast<DWARFSectionKind>(0)}},
- {"debug_tu_index", {TUIndexSection, static_cast<DWARFSectionKind>(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.takeError();
-
- auto &Obj = *ErrOrObj->getBinary();
- Objects.push_back(std::move(*ErrOrObj));
-
- UnitIndexEntry CurEntry = {};
-
- StringRef CurStrSection;
- StringRef CurStrOffsetSection;
- std::vector<StringRef> CurTypesSection;
- StringRef InfoSection;
- StringRef AbbrevSection;
- 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;
-
- if (InfoSection.empty())
- continue;
-
- writeStringsAndOffsets(Out, Strings, StrOffsetSection, CurStrSection,
- CurStrOffsetSection);
-
- if (CurCUIndexSection.empty()) {
- Expected<CompileUnitIdentifiers> EID = getCUIdentifiers(
- AbbrevSection, InfoSection, CurStrOffsetSection, CurStrSection);
- if (!EID)
- return createFileError(Input, 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[getContributionIndex(DW_SECT_EXT_TYPES)]);
- continue;
- }
-
- DWARFUnitIndex CUIndex(DW_SECT_INFO);
- DataExtractor CUIndexData(CurCUIndexSection, Obj.isLittleEndian(), 0);
- if (!CUIndex.parse(CUIndexData))
- return make_error<DWPError>("failed to parse cu_index");
- if (CUIndex.getVersion() != 2)
- return make_error<DWPError>(
- "unsupported cu_index version: " + utostr(CUIndex.getVersion()) +
- " (only version 2 is supported)");
-
- for (const DWARFUnitIndex::Entry &E : CUIndex.getRows()) {
- auto *I = E.getContributions();
- 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 createFileError(Input, 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()) {
- if (!isSupportedSectionKind(Kind))
- continue;
- auto &C = NewEntry.Contributions[getContributionIndex(Kind)];
- C.Offset += I->Offset;
- C.Length = I->Length;
- ++I;
- }
- }
-
- if (!CurTypesSection.empty()) {
- if (CurTypesSection.size() != 1)
- return make_error<DWPError>("multiple type unit sections in .dwp file");
- DWARFUnitIndex TUIndex(DW_SECT_EXT_TYPES);
- DataExtractor TUIndexData(CurTUIndexSection, Obj.isLittleEndian(), 0);
- if (!TUIndex.parse(TUIndexData))
- return make_error<DWPError>("failed to parse tu_index");
- if (TUIndex.getVersion() != 2)
- return make_error<DWPError>(
- "unsupported tu_index version: " + utostr(TUIndex.getVersion()) +
- " (only version 2 is supported)");
-
- addAllTypesFromDWP(
- Out, TypeIndexEntries, TUIndex, TypesSection, CurTypesSection.front(),
- CurEntry,
- ContributionOffsets[getContributionIndex(DW_SECT_EXT_TYPES)]);
- }
- }
-
- // 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[getContributionIndex(DW_SECT_EXT_TYPES)] = 0;
- // Unlie about the info contribution
- ContributionOffsets[0] = 1;
-
- writeIndex(Out, MCOFI.getDwarfCUIndexSection(), ContributionOffsets,
- IndexEntries);
-
- return Error::success();
-}
-
static int error(const Twine &Error, const Twine &Context) {
errs() << Twine("while processing ") + Context + ":\n";
errs() << Twine("error: ") + Error + "\n";
@@ -707,6 +93,7 @@ static Expected<Triple> readTargetTriple(StringRef FileName) {
int main(int argc, char **argv) {
InitLLVM X(argc, argv);
+ cl::HideUnrelatedOptions({&DwpCategory, &getColorCategory()});
cl::ParseCommandLineOptions(argc, argv, "merge split dwarf (.dwo) files\n");
llvm::InitializeAllTargetInfos();
@@ -756,15 +143,16 @@ int main(int argc, char **argv) {
if (!MAI)
return error("no asm info for target " + TripleName, Context);
- MCObjectFileInfo MOFI;
- MCContext MC(MAI.get(), MRI.get(), &MOFI);
- MOFI.InitMCObjectFileInfo(*ErrOrTriple, /*PIC*/ false, MC);
-
std::unique_ptr<MCSubtargetInfo> MSTI(
TheTarget->createMCSubtargetInfo(TripleName, "", ""));
if (!MSTI)
return error("no subtarget info for target " + TripleName, Context);
+ MCContext MC(*ErrOrTriple, MAI.get(), MRI.get(), MSTI.get());
+ std::unique_ptr<MCObjectFileInfo> MOFI(
+ TheTarget->createMCObjectFileInfo(MC, /*PIC=*/false));
+ MC.setObjectFileInfo(MOFI.get());
+
MCTargetOptions Options;
auto MAB = TheTarget->createMCAsmBackend(*MSTI, *MRI, Options);
if (!MAB)
diff --git a/llvm/tools/llvm-link/llvm-link.cpp b/llvm/tools/llvm-link/llvm-link.cpp
index eed49c438335..9abe8efaa4e8 100644
--- a/llvm/tools/llvm-link/llvm-link.cpp
+++ b/llvm/tools/llvm-link/llvm-link.cpp
@@ -41,21 +41,25 @@
#include <utility>
using namespace llvm;
-static cl::list<std::string>
-InputFilenames(cl::Positional, cl::OneOrMore,
- cl::desc("<input bitcode files>"));
+static cl::OptionCategory LinkCategory("Link Options");
+
+static cl::list<std::string> InputFilenames(cl::Positional, cl::OneOrMore,
+ cl::desc("<input bitcode files>"),
+ cl::cat(LinkCategory));
static cl::list<std::string> OverridingInputs(
"override", cl::ZeroOrMore, cl::value_desc("filename"),
cl::desc(
- "input bitcode file which can override previously defined symbol(s)"));
+ "input bitcode file which can override previously defined symbol(s)"),
+ cl::cat(LinkCategory));
// Option to simulate function importing for testing. This enables using
// llvm-link to simulate ThinLTO backend processes.
static cl::list<std::string> Imports(
"import", cl::ZeroOrMore, cl::value_desc("function:filename"),
cl::desc("Pair of function name and filename, where function should be "
- "imported from bitcode in filename"));
+ "imported from bitcode in filename"),
+ cl::cat(LinkCategory));
// Option to support testing of function importing. The module summary
// must be specified in the case were we request imports via the -import
@@ -64,51 +68,61 @@ static cl::list<std::string> Imports(
// consistent promotion and renaming of locals.
static cl::opt<std::string>
SummaryIndex("summary-index", cl::desc("Module summary index filename"),
- cl::init(""), cl::value_desc("filename"));
+ cl::init(""), cl::value_desc("filename"),
+ cl::cat(LinkCategory));
static cl::opt<std::string>
-OutputFilename("o", cl::desc("Override output filename"), cl::init("-"),
- cl::value_desc("filename"));
+ OutputFilename("o", cl::desc("Override output filename"), cl::init("-"),
+ cl::value_desc("filename"), cl::cat(LinkCategory));
-static cl::opt<bool>
-Internalize("internalize", cl::desc("Internalize linked symbols"));
+static cl::opt<bool> Internalize("internalize",
+ cl::desc("Internalize linked symbols"),
+ cl::cat(LinkCategory));
static cl::opt<bool>
DisableDITypeMap("disable-debug-info-type-map",
- cl::desc("Don't use a uniquing type map for debug info"));
+ cl::desc("Don't use a uniquing type map for debug info"),
+ cl::cat(LinkCategory));
-static cl::opt<bool>
-OnlyNeeded("only-needed", cl::desc("Link only needed symbols"));
+static cl::opt<bool> OnlyNeeded("only-needed",
+ cl::desc("Link only needed symbols"),
+ cl::cat(LinkCategory));
-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"),
+ cl::cat(LinkCategory));
-static cl::opt<bool>
- DisableLazyLoad("disable-lazy-loading",
- cl::desc("Disable lazy module loading"));
+static cl::opt<bool> DisableLazyLoad("disable-lazy-loading",
+ cl::desc("Disable lazy module loading"),
+ cl::cat(LinkCategory));
-static cl::opt<bool>
- OutputAssembly("S", cl::desc("Write output as LLVM assembly"), cl::Hidden);
+static cl::opt<bool> OutputAssembly("S",
+ cl::desc("Write output as LLVM assembly"),
+ cl::Hidden, cl::cat(LinkCategory));
-static cl::opt<bool>
-Verbose("v", cl::desc("Print information about actions taken"));
+static cl::opt<bool> Verbose("v",
+ cl::desc("Print information about actions taken"),
+ cl::cat(LinkCategory));
-static cl::opt<bool>
-DumpAsm("d", cl::desc("Print assembly as linked"), cl::Hidden);
+static cl::opt<bool> DumpAsm("d", cl::desc("Print assembly as linked"),
+ cl::Hidden, cl::cat(LinkCategory));
-static cl::opt<bool>
-SuppressWarnings("suppress-warnings", cl::desc("Suppress all linking warnings"),
- cl::init(false));
+static cl::opt<bool> SuppressWarnings("suppress-warnings",
+ cl::desc("Suppress all linking warnings"),
+ cl::init(false), cl::cat(LinkCategory));
static cl::opt<bool> PreserveBitcodeUseListOrder(
"preserve-bc-uselistorder",
cl::desc("Preserve use-list order when writing LLVM bitcode."),
- cl::init(true), cl::Hidden);
+ cl::init(true), cl::Hidden, cl::cat(LinkCategory));
static cl::opt<bool> PreserveAssemblyUseListOrder(
"preserve-ll-uselistorder",
cl::desc("Preserve use-list order when writing LLVM assembly."),
- cl::init(false), cl::Hidden);
+ cl::init(false), cl::Hidden, cl::cat(LinkCategory));
+
+static cl::opt<bool> NoVerify("disable-verify",
+ cl::desc("Do not run the verifier"), cl::Hidden,
+ cl::cat(LinkCategory));
static ExitOnError ExitOnErr;
@@ -158,9 +172,8 @@ static std::unique_ptr<Module> loadArFile(const char *Argv0,
Expected<StringRef> Ename = C.getName();
if (Error E = Ename.takeError()) {
errs() << Argv0 << ": ";
- WithColor::error()
- << " failed to read name of archive member"
- << ArchiveName << "'\n";
+ WithColor::error() << " failed to read name of archive member"
+ << ArchiveName << "'\n";
return nullptr;
}
std::string ChildName = Ename.get().str();
@@ -177,10 +190,10 @@ static std::unique_ptr<Module> loadArFile(const char *Argv0,
return nullptr;
};
- if (!isBitcode(reinterpret_cast<const unsigned char *>
- (MemBuf.get().getBufferStart()),
- reinterpret_cast<const unsigned char *>
- (MemBuf.get().getBufferEnd()))) {
+ if (!isBitcode(reinterpret_cast<const unsigned char *>(
+ MemBuf.get().getBufferStart()),
+ reinterpret_cast<const unsigned char *>(
+ MemBuf.get().getBufferEnd()))) {
errs() << Argv0 << ": ";
WithColor::error() << " member of archive is not a bitcode file: '"
<< ChildName << "'\n";
@@ -246,8 +259,10 @@ public:
Module &ModuleLazyLoaderCache::operator()(const char *argv0,
const std::string &Identifier) {
auto &Module = ModuleMap[Identifier];
- if (!Module)
+ if (!Module) {
Module = createLazyModule(argv0, Identifier);
+ assert(Module && "Failed to create lazy module!");
+ }
return *Module;
}
} // anonymous namespace
@@ -276,7 +291,7 @@ struct LLVMLinkDiagnosticHandler : public DiagnosticHandler {
return true;
}
};
-}
+} // namespace
/// Import any functions requested via the -import option.
static bool importFunctions(const char *argv0, Module &DestModule) {
@@ -309,7 +324,7 @@ static bool importFunctions(const char *argv0, Module &DestModule) {
// Load the specified source module.
auto &SrcModule = ModuleLoaderCache(argv0, FileName);
- if (verifyModule(SrcModule, &errs())) {
+ if (!NoVerify && verifyModule(SrcModule, &errs())) {
errs() << argv0 << ": " << FileName;
WithColor::error() << "input module is broken!\n";
return false;
@@ -347,8 +362,7 @@ static bool importFunctions(const char *argv0, Module &DestModule) {
}
static bool linkFiles(const char *argv0, LLVMContext &Context, Linker &L,
- const cl::list<std::string> &Files,
- unsigned Flags) {
+ const cl::list<std::string> &Files, unsigned Flags) {
// Filter out flags that don't apply to the first file we load.
unsigned ApplicableFlags = Flags & Linker::Flags::OverrideFromSrc;
// Similar to some flags, internalization doesn't apply to the first file.
@@ -370,7 +384,7 @@ static bool linkFiles(const char *argv0, LLVMContext &Context, Linker &L,
// 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())) {
+ if (DisableDITypeMap && !NoVerify && verifyModule(*M, &errs())) {
errs() << argv0 << ": " << File << ": ";
WithColor::error() << "input module is broken!\n";
return false;
@@ -431,8 +445,9 @@ int main(int argc, char **argv) {
ExitOnErr.setBanner(std::string(argv[0]) + ": ");
LLVMContext Context;
- Context.setDiagnosticHandler(
- std::make_unique<LLVMLinkDiagnosticHandler>(), true);
+ Context.setDiagnosticHandler(std::make_unique<LLVMLinkDiagnosticHandler>(),
+ true);
+ cl::HideUnrelatedOptions({&LinkCategory, &getColorCategory()});
cl::ParseCommandLineOptions(argc, argv, "llvm linker\n");
if (!DisableDITypeMap)
@@ -463,13 +478,14 @@ int main(int argc, char **argv) {
std::error_code EC;
ToolOutputFile Out(OutputFilename, EC,
- OutputAssembly ? sys::fs::OF_Text : sys::fs::OF_None);
+ OutputAssembly ? sys::fs::OF_TextWithCRLF
+ : sys::fs::OF_None);
if (EC) {
WithColor::error() << EC.message() << '\n';
return 1;
}
- if (verifyModule(*Composite, &errs())) {
+ if (!NoVerify && verifyModule(*Composite, &errs())) {
errs() << argv[0] << ": ";
WithColor::error() << "linked module is broken!\n";
return 1;
diff --git a/llvm/tools/llvm-lto/llvm-lto.cpp b/llvm/tools/llvm-lto/llvm-lto.cpp
index 1745b7449714..45bfa84fb826 100644
--- a/llvm/tools/llvm-lto/llvm-lto.cpp
+++ b/llvm/tools/llvm-lto/llvm-lto.cpp
@@ -64,31 +64,38 @@ using namespace llvm;
static codegen::RegisterCodeGenFlags CGF;
+static cl::OptionCategory LTOCategory("LTO Options");
+
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'), cl::cat(LTOCategory));
static cl::opt<bool>
IndexStats("thinlto-index-stats",
cl::desc("Print statistic for the index in every input files"),
- cl::init(false));
+ cl::init(false), cl::cat(LTOCategory));
static cl::opt<bool> DisableVerify(
"disable-verify", cl::init(false),
- cl::desc("Do not run the verifier during the optimization pipeline"));
+ cl::desc("Do not run the verifier during the optimization pipeline"),
+ cl::cat(LTOCategory));
static cl::opt<bool> EnableFreestanding(
"lto-freestanding", cl::init(false),
- cl::desc("Enable Freestanding (disable builtins / TLI) during LTO"));
+ cl::desc("Enable Freestanding (disable builtins / TLI) during LTO"),
+ cl::cat(LTOCategory));
static cl::opt<bool> UseDiagnosticHandler(
"use-diagnostic-handler", cl::init(false),
- cl::desc("Use a diagnostic handler to test the handler interface"));
+ cl::desc("Use a diagnostic handler to test the handler interface"),
+ cl::cat(LTOCategory));
static cl::opt<bool>
ThinLTO("thinlto", cl::init(false),
- cl::desc("Only write combined global index for ThinLTO backends"));
+ cl::desc("Only write combined global index for ThinLTO backends"),
+ cl::cat(LTOCategory));
enum ThinLTOModes {
THINLINK,
@@ -114,113 +121,144 @@ cl::opt<ThinLTOModes> ThinLTOMode(
"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(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")));
+ clEnumValN(THINALL, "run", "Perform ThinLTO end-to-end")),
+ cl::cat(LTOCategory));
static cl::opt<std::string>
ThinLTOIndex("thinlto-index",
cl::desc("Provide the index produced by a ThinLink, required "
- "to perform the promotion and/or importing."));
+ "to perform the promotion and/or importing."),
+ cl::cat(LTOCategory));
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."));
+ "replaced with newprefix."),
+ cl::cat(LTOCategory));
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."));
+ "match what is in the index."),
+ cl::cat(LTOCategory));
-static cl::opt<std::string>
- ThinLTOCacheDir("thinlto-cache-dir", cl::desc("Enable ThinLTO caching."));
+static cl::opt<std::string> ThinLTOCacheDir("thinlto-cache-dir",
+ cl::desc("Enable ThinLTO caching."),
+ cl::cat(LTOCategory));
-static cl::opt<int>
- ThinLTOCachePruningInterval("thinlto-cache-pruning-interval",
- cl::init(1200), cl::desc("Set ThinLTO cache pruning interval."));
+static cl::opt<int> ThinLTOCachePruningInterval(
+ "thinlto-cache-pruning-interval", cl::init(1200),
+ cl::desc("Set ThinLTO cache pruning interval."), cl::cat(LTOCategory));
static cl::opt<uint64_t> ThinLTOCacheMaxSizeBytes(
"thinlto-cache-max-size-bytes",
- cl::desc("Set ThinLTO cache pruning directory maximum size in bytes."));
+ cl::desc("Set ThinLTO cache pruning directory maximum size in bytes."),
+ cl::cat(LTOCategory));
-static cl::opt<int>
- ThinLTOCacheMaxSizeFiles("thinlto-cache-max-size-files", cl::init(1000000),
- cl::desc("Set ThinLTO cache pruning directory maximum number of files."));
+static cl::opt<int> ThinLTOCacheMaxSizeFiles(
+ "thinlto-cache-max-size-files", cl::init(1000000),
+ cl::desc("Set ThinLTO cache pruning directory maximum number of files."),
+ cl::cat(LTOCategory));
-static cl::opt<unsigned>
- ThinLTOCacheEntryExpiration("thinlto-cache-entry-expiration", cl::init(604800) /* 1w */,
- cl::desc("Set ThinLTO cache entry expiration time."));
+static cl::opt<unsigned> ThinLTOCacheEntryExpiration(
+ "thinlto-cache-entry-expiration", cl::init(604800) /* 1w */,
+ cl::desc("Set ThinLTO cache entry expiration time."), cl::cat(LTOCategory));
static cl::opt<std::string> ThinLTOSaveTempsPrefix(
"thinlto-save-temps",
cl::desc("Save ThinLTO temp files using filenames created by adding "
- "suffixes to the given file path prefix."));
+ "suffixes to the given file path prefix."),
+ cl::cat(LTOCategory));
static cl::opt<std::string> ThinLTOGeneratedObjectsDir(
"thinlto-save-objects",
cl::desc("Save ThinLTO generated object files using filenames created in "
- "the given directory."));
+ "the given directory."),
+ cl::cat(LTOCategory));
static cl::opt<bool> SaveLinkedModuleFile(
"save-linked-module", cl::init(false),
- cl::desc("Write linked LTO module to file before optimize"));
+ cl::desc("Write linked LTO module to file before optimize"),
+ cl::cat(LTOCategory));
static cl::opt<bool>
SaveModuleFile("save-merged-module", cl::init(false),
- cl::desc("Write merged LTO module to file before CodeGen"));
+ cl::desc("Write merged LTO module to file before CodeGen"),
+ cl::cat(LTOCategory));
static cl::list<std::string> InputFilenames(cl::Positional, cl::OneOrMore,
- cl::desc("<input bitcode files>"));
+ cl::desc("<input bitcode files>"),
+ cl::cat(LTOCategory));
static cl::opt<std::string> OutputFilename("o", cl::init(""),
cl::desc("Override output filename"),
- cl::value_desc("filename"));
+ cl::value_desc("filename"),
+ cl::cat(LTOCategory));
static cl::list<std::string> ExportedSymbols(
"exported-symbol",
cl::desc("List of symbols to export from the resulting object file"),
- cl::ZeroOrMore);
+ cl::ZeroOrMore, cl::cat(LTOCategory));
static cl::list<std::string>
DSOSymbols("dso-symbol",
cl::desc("Symbol to put in the symtab in the resulting dso"),
- cl::ZeroOrMore);
+ cl::ZeroOrMore, cl::cat(LTOCategory));
static cl::opt<bool> ListSymbolsOnly(
"list-symbols-only", cl::init(false),
- cl::desc("Instead of running LTO, list the symbols in each IR file"));
+ cl::desc("Instead of running LTO, list the symbols in each IR file"),
+ cl::cat(LTOCategory));
static cl::opt<bool> ListDependentLibrariesOnly(
"list-dependent-libraries-only", cl::init(false),
- cl::desc("Instead of running LTO, list the dependent libraries in each IR file"));
+ cl::desc(
+ "Instead of running LTO, list the dependent libraries in each IR file"),
+ cl::cat(LTOCategory));
-static cl::opt<bool> SetMergedModule(
- "set-merged-module", cl::init(false),
- cl::desc("Use the first input module as the merged module"));
+static cl::opt<bool>
+ SetMergedModule("set-merged-module", cl::init(false),
+ cl::desc("Use the first input module as the merged module"),
+ cl::cat(LTOCategory));
static cl::opt<unsigned> Parallelism("j", cl::Prefix, cl::init(1),
- cl::desc("Number of backend threads"));
+ cl::desc("Number of backend threads"),
+ cl::cat(LTOCategory));
static cl::opt<bool> RestoreGlobalsLinkage(
"restore-linkage", cl::init(false),
- cl::desc("Restore original linkage of globals prior to CodeGen"));
+ cl::desc("Restore original linkage of globals prior to CodeGen"),
+ cl::cat(LTOCategory));
static cl::opt<bool> CheckHasObjC(
"check-for-objc", cl::init(false),
- cl::desc("Only check if the module has objective-C defined in it"));
+ cl::desc("Only check if the module has objective-C defined in it"),
+ cl::cat(LTOCategory));
static cl::opt<bool> PrintMachOCPUOnly(
"print-macho-cpu-only", cl::init(false),
- cl::desc("Instead of running LTO, print the mach-o cpu in each IR file"));
+ cl::desc("Instead of running LTO, print the mach-o cpu in each IR file"),
+ cl::cat(LTOCategory));
+
+static cl::opt<bool> UseNewPM(
+ "use-new-pm", cl::desc("Run LTO passes using the new pass manager"),
+ cl::init(LLVM_ENABLE_NEW_PASS_MANAGER), cl::Hidden, cl::cat(LTOCategory));
+
+static cl::opt<bool>
+ DebugPassManager("debug-pass-manager", cl::init(false), cl::Hidden,
+ cl::desc("Print pass management debugging information"),
+ cl::cat(LTOCategory));
namespace {
@@ -552,6 +590,8 @@ public:
ThinGenerator.setCacheMaxSizeFiles(ThinLTOCacheMaxSizeFiles);
ThinGenerator.setCacheMaxSizeBytes(ThinLTOCacheMaxSizeBytes);
ThinGenerator.setFreestanding(EnableFreestanding);
+ ThinGenerator.setUseNewPM(UseNewPM);
+ ThinGenerator.setDebugPassManager(DebugPassManager);
// Add all the exported symbols to the table of symbols to preserve.
for (unsigned i = 0; i < ExportedSymbols.size(); ++i)
@@ -884,6 +924,7 @@ private:
int main(int argc, char **argv) {
InitLLVM X(argc, argv);
+ cl::HideUnrelatedOptions({&LTOCategory, &getColorCategory()});
cl::ParseCommandLineOptions(argc, argv, "llvm LTO linker\n");
if (OptLevel < '0' || OptLevel > '3')
@@ -1014,6 +1055,8 @@ int main(int argc, char **argv) {
CodeGen.setOptLevel(OptLevel - '0');
CodeGen.setAttrs(codegen::getMAttrs());
+ CodeGen.setUseNewPM(UseNewPM);
+
if (auto FT = codegen::getExplicitFileType())
CodeGen.setFileType(FT.getValue());
@@ -1041,25 +1084,24 @@ int main(int argc, char **argv) {
error("writing merged module failed.");
}
- std::list<ToolOutputFile> OSs;
- std::vector<raw_pwrite_stream *> OSPtrs;
- for (unsigned I = 0; I != Parallelism; ++I) {
+ auto AddStream =
+ [&](size_t Task) -> std::unique_ptr<lto::NativeObjectStream> {
std::string PartFilename = OutputFilename;
if (Parallelism != 1)
- PartFilename += "." + utostr(I);
+ PartFilename += "." + utostr(Task);
+
std::error_code EC;
- OSs.emplace_back(PartFilename, EC, sys::fs::OF_None);
+ auto S =
+ std::make_unique<raw_fd_ostream>(PartFilename, EC, sys::fs::OF_None);
if (EC)
error("error opening the file '" + PartFilename + "': " + EC.message());
- OSPtrs.push_back(&OSs.back().os());
- }
+ return std::make_unique<lto::NativeObjectStream>(std::move(S));
+ };
- if (!CodeGen.compileOptimized(OSPtrs))
+ if (!CodeGen.compileOptimized(AddStream, Parallelism))
// Diagnostic messages should have been printed by the handler.
error("error compiling the code");
- for (ToolOutputFile &OS : OSs)
- OS.keep();
} else {
if (Parallelism != 1)
error("-j must be specified together with -o");
diff --git a/llvm/tools/llvm-lto2/llvm-lto2.cpp b/llvm/tools/llvm-lto2/llvm-lto2.cpp
index ca4278fafb89..c0bff1eabee2 100644
--- a/llvm/tools/llvm-lto2/llvm-lto2.cpp
+++ b/llvm/tools/llvm-lto2/llvm-lto2.cpp
@@ -325,12 +325,12 @@ static int run(int argc, char **argv) {
std::vector<SymbolResolution> Res;
for (const InputFile::Symbol &Sym : Input->symbols()) {
auto I = CommandLineResolutions.find({F, std::string(Sym.getName())});
- // If it isn't found, look for "$", which would have been added
+ // If it isn't found, look for ".", which would have been added
// (followed by a hash) when the symbol was promoted during module
// splitting if it was defined in one part and used in the other.
- // Try looking up the symbol name before the "$".
+ // Try looking up the symbol name before the suffix.
if (I == CommandLineResolutions.end()) {
- auto SplitName = Sym.getName().rsplit("$");
+ auto SplitName = Sym.getName().rsplit(".");
I = CommandLineResolutions.find({F, std::string(SplitName.first)});
}
if (I == CommandLineResolutions.end()) {
@@ -418,7 +418,8 @@ static int dumpSymtab(int argc, char **argv) {
outs() << '\n';
}
- std::vector<StringRef> ComdatTable = Input->getComdatTable();
+ ArrayRef<std::pair<StringRef, Comdat::SelectionKind>> ComdatTable =
+ Input->getComdatTable();
for (const InputFile::Symbol &Sym : Input->symbols()) {
switch (Sym.getVisibility()) {
case GlobalValue::HiddenVisibility:
@@ -447,8 +448,27 @@ static int dumpSymtab(int argc, char **argv) {
<< Sym.getCommonAlignment() << '\n';
int Comdat = Sym.getComdatIndex();
- if (Comdat != -1)
- outs() << " comdat " << ComdatTable[Comdat] << '\n';
+ if (Comdat != -1) {
+ outs() << " comdat ";
+ switch (ComdatTable[Comdat].second) {
+ case Comdat::Any:
+ outs() << "any";
+ break;
+ case Comdat::ExactMatch:
+ outs() << "exactmatch";
+ break;
+ case Comdat::Largest:
+ outs() << "largest";
+ break;
+ case Comdat::NoDeduplicate:
+ outs() << "nodeduplicate";
+ break;
+ case Comdat::SameSize:
+ outs() << "samesize";
+ break;
+ }
+ outs() << ' ' << ComdatTable[Comdat].first << '\n';
+ }
if (TT.isOSBinFormatCOFF() && Sym.isWeak() && Sym.isIndirect())
outs() << " fallback " << Sym.getCOFFWeakExternalFallback() << '\n';
diff --git a/llvm/tools/llvm-mc/llvm-mc.cpp b/llvm/tools/llvm-mc/llvm-mc.cpp
index f3a66187dd0a..24c601b7033f 100644
--- a/llvm/tools/llvm-mc/llvm-mc.cpp
+++ b/llvm/tools/llvm-mc/llvm-mc.cpp
@@ -43,23 +43,33 @@ using namespace llvm;
static mc::RegisterMCTargetOptionsFlags MOF;
-static cl::opt<std::string>
-InputFilename(cl::Positional, cl::desc("<input file>"), cl::init("-"));
+static cl::OptionCategory MCCategory("MC Options");
+
+static cl::opt<std::string> InputFilename(cl::Positional,
+ cl::desc("<input file>"),
+ cl::init("-"), cl::cat(MCCategory));
+
+static cl::list<std::string>
+ DisassemblerOptions("M", cl::desc("Disassembler options"),
+ cl::cat(MCCategory));
static cl::opt<std::string> OutputFilename("o", cl::desc("Output filename"),
cl::value_desc("filename"),
- cl::init("-"));
+ cl::init("-"), cl::cat(MCCategory));
static cl::opt<std::string> SplitDwarfFile("split-dwarf-file",
cl::desc("DWO output filename"),
- cl::value_desc("filename"));
+ cl::value_desc("filename"),
+ cl::cat(MCCategory));
-static cl::opt<bool>
-ShowEncoding("show-encoding", cl::desc("Show instruction encodings"));
+static cl::opt<bool> ShowEncoding("show-encoding",
+ cl::desc("Show instruction encodings"),
+ cl::cat(MCCategory));
static cl::opt<bool> RelaxELFRel(
"relax-relocations", cl::init(true),
- cl::desc("Emit R_X86_64_GOTPCRELX instead of R_X86_64_GOTPCREL"));
+ cl::desc("Emit R_X86_64_GOTPCRELX instead of R_X86_64_GOTPCREL"),
+ cl::cat(MCCategory));
static cl::opt<DebugCompressionType> CompressDebugSections(
"compress-debug-sections", cl::ValueOptional,
@@ -69,29 +79,37 @@ static cl::opt<DebugCompressionType> CompressDebugSections(
clEnumValN(DebugCompressionType::Z, "zlib",
"Use zlib compression"),
clEnumValN(DebugCompressionType::GNU, "zlib-gnu",
- "Use zlib-gnu compression (deprecated)")));
+ "Use zlib-gnu compression (deprecated)")),
+ cl::cat(MCCategory));
static cl::opt<bool>
-ShowInst("show-inst", cl::desc("Show internal instruction representation"));
+ ShowInst("show-inst", cl::desc("Show internal instruction representation"),
+ cl::cat(MCCategory));
static cl::opt<bool>
-ShowInstOperands("show-inst-operands",
- cl::desc("Show instructions operands as parsed"));
+ ShowInstOperands("show-inst-operands",
+ cl::desc("Show instructions operands as parsed"),
+ cl::cat(MCCategory));
static cl::opt<unsigned>
-OutputAsmVariant("output-asm-variant",
- cl::desc("Syntax variant to use for output printing"));
+ OutputAsmVariant("output-asm-variant",
+ cl::desc("Syntax variant to use for output printing"),
+ cl::cat(MCCategory));
static cl::opt<bool>
-PrintImmHex("print-imm-hex", cl::init(false),
- cl::desc("Prefer hex format for immediate values"));
+ PrintImmHex("print-imm-hex", cl::init(false),
+ cl::desc("Prefer hex format for immediate values"),
+ cl::cat(MCCategory));
static cl::list<std::string>
-DefineSymbol("defsym", cl::desc("Defines a symbol to be an integer constant"));
+ DefineSymbol("defsym",
+ cl::desc("Defines a symbol to be an integer constant"),
+ cl::cat(MCCategory));
static cl::opt<bool>
PreserveComments("preserve-comments",
- cl::desc("Preserve Comments in outputted assembly"));
+ cl::desc("Preserve Comments in outputted assembly"),
+ cl::cat(MCCategory));
enum OutputFileType {
OFT_Null,
@@ -99,82 +117,101 @@ enum OutputFileType {
OFT_ObjectFile
};
static cl::opt<OutputFileType>
-FileType("filetype", cl::init(OFT_AssemblyFile),
- cl::desc("Choose an output file type:"),
- cl::values(
- clEnumValN(OFT_AssemblyFile, "asm",
- "Emit an assembly ('.s') file"),
- clEnumValN(OFT_Null, "null",
- "Don't emit anything (for timing purposes)"),
- clEnumValN(OFT_ObjectFile, "obj",
- "Emit a native object ('.o') file")));
-
-static cl::list<std::string>
-IncludeDirs("I", cl::desc("Directory of include files"),
- cl::value_desc("directory"), cl::Prefix);
+ FileType("filetype", cl::init(OFT_AssemblyFile),
+ cl::desc("Choose an output file type:"),
+ cl::values(clEnumValN(OFT_AssemblyFile, "asm",
+ "Emit an assembly ('.s') file"),
+ clEnumValN(OFT_Null, "null",
+ "Don't emit anything (for timing purposes)"),
+ clEnumValN(OFT_ObjectFile, "obj",
+ "Emit a native object ('.o') file")),
+ cl::cat(MCCategory));
+
+static cl::list<std::string> IncludeDirs("I",
+ cl::desc("Directory of include files"),
+ cl::value_desc("directory"),
+ cl::Prefix, cl::cat(MCCategory));
static cl::opt<std::string>
-ArchName("arch", cl::desc("Target arch to assemble for, "
- "see -version for available targets"));
+ ArchName("arch",
+ cl::desc("Target arch to assemble for, "
+ "see -version for available targets"),
+ cl::cat(MCCategory));
static cl::opt<std::string>
-TripleName("triple", cl::desc("Target triple to assemble for, "
- "see -version for available targets"));
+ TripleName("triple",
+ cl::desc("Target triple to assemble for, "
+ "see -version for available targets"),
+ cl::cat(MCCategory));
static cl::opt<std::string>
-MCPU("mcpu",
- cl::desc("Target a specific cpu type (-mcpu=help for details)"),
- cl::value_desc("cpu-name"),
- cl::init(""));
+ MCPU("mcpu",
+ cl::desc("Target a specific cpu type (-mcpu=help for details)"),
+ cl::value_desc("cpu-name"), cl::init(""), cl::cat(MCCategory));
static cl::list<std::string>
-MAttrs("mattr",
- cl::CommaSeparated,
- cl::desc("Target specific attributes (-mattr=help for details)"),
- cl::value_desc("a1,+a2,-a3,..."));
+ MAttrs("mattr", cl::CommaSeparated,
+ cl::desc("Target specific attributes (-mattr=help for details)"),
+ cl::value_desc("a1,+a2,-a3,..."), cl::cat(MCCategory));
static cl::opt<bool> PIC("position-independent",
- cl::desc("Position independent"), cl::init(false));
+ cl::desc("Position independent"), cl::init(false),
+ cl::cat(MCCategory));
static cl::opt<bool>
LargeCodeModel("large-code-model",
cl::desc("Create cfi directives that assume the code might "
- "be more than 2gb away"));
+ "be more than 2gb away"),
+ cl::cat(MCCategory));
static cl::opt<bool>
-NoInitialTextSection("n", cl::desc("Don't assume assembly file starts "
- "in the text section"));
+ NoInitialTextSection("n",
+ cl::desc("Don't assume assembly file starts "
+ "in the text section"),
+ cl::cat(MCCategory));
static cl::opt<bool>
-GenDwarfForAssembly("g", cl::desc("Generate dwarf debugging info for assembly "
- "source files"));
+ GenDwarfForAssembly("g",
+ cl::desc("Generate dwarf debugging info for assembly "
+ "source files"),
+ cl::cat(MCCategory));
static cl::opt<std::string>
-DebugCompilationDir("fdebug-compilation-dir",
- cl::desc("Specifies the debug info's compilation dir"));
+ DebugCompilationDir("fdebug-compilation-dir",
+ cl::desc("Specifies the debug info's compilation dir"),
+ cl::cat(MCCategory));
-static cl::list<std::string>
-DebugPrefixMap("fdebug-prefix-map",
- cl::desc("Map file source paths in debug info"),
- cl::value_desc("= separated key-value pairs"));
+static cl::list<std::string> DebugPrefixMap(
+ "fdebug-prefix-map", cl::desc("Map file source paths in debug info"),
+ cl::value_desc("= separated key-value pairs"), cl::cat(MCCategory));
-static cl::opt<std::string>
-MainFileName("main-file-name",
- cl::desc("Specifies the name we should consider the input file"));
+static cl::opt<std::string> MainFileName(
+ "main-file-name",
+ cl::desc("Specifies the name we should consider the input file"),
+ cl::cat(MCCategory));
static cl::opt<bool> SaveTempLabels("save-temp-labels",
- cl::desc("Don't discard temporary labels"));
+ cl::desc("Don't discard temporary labels"),
+ cl::cat(MCCategory));
static cl::opt<bool> LexMasmIntegers(
"masm-integers",
- cl::desc("Enable binary and hex masm integers (0b110 and 0ABCh)"));
+ cl::desc("Enable binary and hex masm integers (0b110 and 0ABCh)"),
+ cl::cat(MCCategory));
static cl::opt<bool> LexMasmHexFloats(
"masm-hexfloats",
- cl::desc("Enable MASM-style hex float initializers (3F800000r)"));
+ cl::desc("Enable MASM-style hex float initializers (3F800000r)"),
+ cl::cat(MCCategory));
+
+static cl::opt<bool> LexMotorolaIntegers(
+ "motorola-integers",
+ cl::desc("Enable binary and hex Motorola integers (%110 and $ABC)"),
+ cl::cat(MCCategory));
static cl::opt<bool> NoExecStack("no-exec-stack",
- cl::desc("File doesn't need an exec stack"));
+ cl::desc("File doesn't need an exec stack"),
+ cl::cat(MCCategory));
enum ActionType {
AC_AsLex,
@@ -183,17 +220,16 @@ enum ActionType {
AC_MDisassemble,
};
-static cl::opt<ActionType>
-Action(cl::desc("Action to perform:"),
- cl::init(AC_Assemble),
- cl::values(clEnumValN(AC_AsLex, "as-lex",
- "Lex tokens from a .s file"),
- clEnumValN(AC_Assemble, "assemble",
- "Assemble a .s file (default)"),
- clEnumValN(AC_Disassemble, "disassemble",
- "Disassemble strings of hex bytes"),
- clEnumValN(AC_MDisassemble, "mdis",
- "Marked up disassembly of strings of hex bytes")));
+static cl::opt<ActionType> Action(
+ cl::desc("Action to perform:"), cl::init(AC_Assemble),
+ cl::values(clEnumValN(AC_AsLex, "as-lex", "Lex tokens from a .s file"),
+ clEnumValN(AC_Assemble, "assemble",
+ "Assemble a .s file (default)"),
+ clEnumValN(AC_Disassemble, "disassemble",
+ "Disassemble strings of hex bytes"),
+ clEnumValN(AC_MDisassemble, "mdis",
+ "Marked up disassembly of strings of hex bytes")),
+ cl::cat(MCCategory));
static const Target *GetTarget(const char *ProgName) {
// Figure out the target triple.
@@ -305,6 +341,7 @@ static int AssembleInput(const char *ProgName, const Target *TheTarget,
Parser->setTargetParser(*TAP);
Parser->getLexer().setLexMasmIntegers(LexMasmIntegers);
Parser->getLexer().setLexMasmHexFloats(LexMasmHexFloats);
+ Parser->getLexer().setLexMotorolaIntegers(LexMotorolaIntegers);
int Res = Parser->Run(NoInitialTextSection);
@@ -323,6 +360,7 @@ int main(int argc, char **argv) {
// Register the target printer for --version.
cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion);
+ cl::HideUnrelatedOptions({&MCCategory, &getColorCategory()});
cl::ParseCommandLineOptions(argc, argv, "llvm machine code playground\n");
const MCTargetOptions MCOptions = mc::InitMCTargetOptionsFromFlags();
setDwarfDebugFlags(argc, argv);
@@ -338,7 +376,7 @@ int main(int argc, char **argv) {
Triple TheTriple(TripleName);
ErrorOr<std::unique_ptr<MemoryBuffer>> BufferPtr =
- MemoryBuffer::getFileOrSTDIN(InputFilename);
+ MemoryBuffer::getFileOrSTDIN(InputFilename, /*IsText=*/true);
if (std::error_code EC = BufferPtr.getError()) {
WithColor::error(errs(), ProgName)
<< InputFilename << ": " << EC.message() << '\n';
@@ -374,11 +412,26 @@ int main(int argc, char **argv) {
}
MAI->setPreserveAsmComments(PreserveComments);
+ // Package up features to be passed to target/subtarget
+ std::string FeaturesStr;
+ if (MAttrs.size()) {
+ SubtargetFeatures Features;
+ for (unsigned i = 0; i != MAttrs.size(); ++i)
+ Features.AddFeature(MAttrs[i]);
+ FeaturesStr = Features.getString();
+ }
+
+ std::unique_ptr<MCSubtargetInfo> STI(
+ TheTarget->createMCSubtargetInfo(TripleName, MCPU, FeaturesStr));
+ assert(STI && "Unable to create subtarget info!");
+
// 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, &MCOptions);
- MOFI.InitMCObjectFileInfo(TheTriple, PIC, Ctx, LargeCodeModel);
+ MCContext Ctx(TheTriple, MAI.get(), MRI.get(), STI.get(), &SrcMgr,
+ &MCOptions);
+ std::unique_ptr<MCObjectFileInfo> MOFI(
+ TheTarget->createMCObjectFileInfo(Ctx, PIC, LargeCodeModel));
+ Ctx.setObjectFileInfo(MOFI.get());
if (SaveTempLabels)
Ctx.setAllowTemporaryLabels(false);
@@ -438,17 +491,9 @@ int main(int argc, char **argv) {
if (GenDwarfForAssembly)
Ctx.setGenDwarfRootFile(InputFilename, Buffer->getBuffer());
- // Package up features to be passed to target/subtarget
- std::string FeaturesStr;
- if (MAttrs.size()) {
- SubtargetFeatures Features;
- for (unsigned i = 0; i != MAttrs.size(); ++i)
- Features.AddFeature(MAttrs[i]);
- FeaturesStr = Features.getString();
- }
-
- sys::fs::OpenFlags Flags = (FileType == OFT_AssemblyFile) ? sys::fs::OF_Text
- : sys::fs::OF_None;
+ sys::fs::OpenFlags Flags = (FileType == OFT_AssemblyFile)
+ ? sys::fs::OF_TextWithCRLF
+ : sys::fs::OF_None;
std::unique_ptr<ToolOutputFile> Out = GetOutputStream(OutputFilename, Flags);
if (!Out)
return 1;
@@ -471,10 +516,6 @@ int main(int argc, char **argv) {
std::unique_ptr<MCInstrInfo> MCII(TheTarget->createMCInstrInfo());
assert(MCII && "Unable to create instruction info!");
- std::unique_ptr<MCSubtargetInfo> STI(
- TheTarget->createMCSubtargetInfo(TripleName, MCPU, FeaturesStr));
- assert(STI && "Unable to create subtarget info!");
-
MCInstPrinter *IP = nullptr;
if (FileType == OFT_AssemblyFile) {
IP = TheTarget->createMCInstPrinter(Triple(TripleName), OutputAsmVariant,
@@ -488,6 +529,12 @@ int main(int argc, char **argv) {
return 1;
}
+ for (StringRef Opt : DisassemblerOptions)
+ if (!IP->applyTargetSpecificCLOption(Opt)) {
+ WithColor::error() << "invalid disassembler option '" << Opt << "'\n";
+ return 1;
+ }
+
// Set the display preference for hex vs. decimal immediates.
IP->setPrintImmHex(PrintImmHex);
diff --git a/llvm/tools/llvm-mca/CodeRegion.h b/llvm/tools/llvm-mca/CodeRegion.h
index d2b05fa80c54..0b2590767dfa 100644
--- a/llvm/tools/llvm-mca/CodeRegion.h
+++ b/llvm/tools/llvm-mca/CodeRegion.h
@@ -53,7 +53,7 @@ class CodeRegion {
// An optional descriptor for this region.
llvm::StringRef Description;
// Instructions that form this region.
- llvm::SmallVector<llvm::MCInst, 8> Instructions;
+ llvm::SmallVector<llvm::MCInst, 16> Instructions;
// Source location range.
llvm::SMLoc RangeStart;
llvm::SMLoc RangeEnd;
diff --git a/llvm/tools/llvm-mca/CodeRegionGenerator.cpp b/llvm/tools/llvm-mca/CodeRegionGenerator.cpp
index 831b76ab80cf..6ad2a65592b9 100644
--- a/llvm/tools/llvm-mca/CodeRegionGenerator.cpp
+++ b/llvm/tools/llvm-mca/CodeRegionGenerator.cpp
@@ -106,11 +106,21 @@ void MCACommentConsumer::HandleComment(SMLoc Loc, StringRef CommentText) {
Regions.beginRegion(Comment, Loc);
}
-Expected<const CodeRegions &> AsmCodeRegionGenerator::parseCodeRegions() {
+Expected<const CodeRegions &> AsmCodeRegionGenerator::parseCodeRegions(
+ const std::unique_ptr<MCInstPrinter> &IP) {
MCTargetOptions Opts;
Opts.PreserveAsmComments = false;
MCStreamerWrapper Str(Ctx, Regions);
+ // Need to initialize an MCTargetStreamer otherwise
+ // certain asm directives will cause a segfault.
+ // Using nulls() so that anything emitted by the MCTagetStreamer
+ // doesn't show up in the llvm-mca output.
+ raw_ostream &OSRef = nulls();
+ formatted_raw_ostream FOSRef(OSRef);
+ TheTarget.createAsmTargetStreamer(Str, FOSRef, IP.get(),
+ /*IsVerboseAsm=*/true);
+
// Create a MCAsmParser and setup the lexer to recognize llvm-mca ASM
// comments.
std::unique_ptr<MCAsmParser> Parser(
diff --git a/llvm/tools/llvm-mca/CodeRegionGenerator.h b/llvm/tools/llvm-mca/CodeRegionGenerator.h
index 9a10aa2c148b..1c11784ca3fb 100644
--- a/llvm/tools/llvm-mca/CodeRegionGenerator.h
+++ b/llvm/tools/llvm-mca/CodeRegionGenerator.h
@@ -39,7 +39,8 @@ protected:
public:
CodeRegionGenerator(SourceMgr &SM) : Regions(SM) {}
virtual ~CodeRegionGenerator();
- virtual Expected<const CodeRegions &> parseCodeRegions() = 0;
+ virtual Expected<const CodeRegions &>
+ parseCodeRegions(const std::unique_ptr<MCInstPrinter> &IP) = 0;
};
/// This class is responsible for parsing input ASM and generating
@@ -60,7 +61,8 @@ public:
AssemblerDialect(0) {}
unsigned getAssemblerDialect() const { return AssemblerDialect; }
- Expected<const CodeRegions &> parseCodeRegions() override;
+ Expected<const CodeRegions &>
+ parseCodeRegions(const std::unique_ptr<MCInstPrinter> &IP) override;
};
} // namespace mca
diff --git a/llvm/tools/llvm-mca/PipelinePrinter.cpp b/llvm/tools/llvm-mca/PipelinePrinter.cpp
index e7dfbfdce26d..955b825891fa 100644
--- a/llvm/tools/llvm-mca/PipelinePrinter.cpp
+++ b/llvm/tools/llvm-mca/PipelinePrinter.cpp
@@ -12,14 +12,119 @@
//===----------------------------------------------------------------------===//
#include "PipelinePrinter.h"
+#include "CodeRegion.h"
+#include "Views/InstructionView.h"
#include "Views/View.h"
namespace llvm {
namespace mca {
+void PipelinePrinter::printRegionHeader(llvm::raw_ostream &OS) const {
+ StringRef RegionName;
+ if (!Region.getDescription().empty())
+ RegionName = Region.getDescription();
+
+ OS << "\n[" << RegionIdx << "] Code Region";
+ if (!RegionName.empty())
+ OS << " - " << RegionName;
+ OS << "\n\n";
+}
+
+json::Object PipelinePrinter::getJSONReportRegion() const {
+ json::Object JO;
+
+ StringRef RegionName = "";
+ if (!Region.getDescription().empty())
+ RegionName = Region.getDescription();
+
+ JO.try_emplace("Name", RegionName);
+ for (const auto &V : Views)
+ if (V->isSerializable())
+ JO.try_emplace(V->getNameAsString().str(), V->toJSON());
+
+ return JO;
+}
+
+json::Object PipelinePrinter::getJSONSimulationParameters() const {
+ json::Object SimParameters({{"-mcpu", STI.getCPU()},
+ {"-mtriple", STI.getTargetTriple().getTriple()},
+ {"-march", STI.getTargetTriple().getArchName()}});
+
+ const MCSchedModel &SM = STI.getSchedModel();
+ if (!SM.isOutOfOrder())
+ return SimParameters;
+
+ if (PO.RegisterFileSize)
+ SimParameters.try_emplace("-register-file-size", PO.RegisterFileSize);
+
+ if (!PO.AssumeNoAlias)
+ SimParameters.try_emplace("-noalias", PO.AssumeNoAlias);
+
+ if (PO.DecodersThroughput)
+ SimParameters.try_emplace("-decoder-throughput", PO.DecodersThroughput);
+
+ if (PO.MicroOpQueueSize)
+ SimParameters.try_emplace("-micro-op-queue-size", PO.MicroOpQueueSize);
+
+ if (PO.DispatchWidth)
+ SimParameters.try_emplace("-dispatch", PO.DispatchWidth);
+
+ if (PO.LoadQueueSize)
+ SimParameters.try_emplace("-lqueue", PO.LoadQueueSize);
+
+ if (PO.StoreQueueSize)
+ SimParameters.try_emplace("-squeue", PO.StoreQueueSize);
+
+ return SimParameters;
+}
+
+json::Object PipelinePrinter::getJSONTargetInfo() const {
+ json::Array Resources;
+ const MCSchedModel &SM = STI.getSchedModel();
+ StringRef MCPU = STI.getCPU();
+
+ for (unsigned I = 1, E = SM.getNumProcResourceKinds(); I < E; ++I) {
+ const MCProcResourceDesc &ProcResource = *SM.getProcResource(I);
+ unsigned NumUnits = ProcResource.NumUnits;
+ if (ProcResource.SubUnitsIdxBegin || !NumUnits)
+ continue;
+
+ for (unsigned J = 0; J < NumUnits; ++J) {
+ std::string ResourceName = ProcResource.Name;
+ if (NumUnits > 1) {
+ ResourceName += ".";
+ ResourceName += J;
+ }
+
+ Resources.push_back(ResourceName);
+ }
+ }
+
+ return json::Object({{"CPUName", MCPU}, {"Resources", std::move(Resources)}});
+}
+
+void PipelinePrinter::printReport(json::Object &JO) const {
+ if (!RegionIdx) {
+ JO.try_emplace("TargetInfo", getJSONTargetInfo());
+ JO.try_emplace("SimulationParameters", getJSONSimulationParameters());
+ // Construct an array of regions.
+ JO.try_emplace("CodeRegions", json::Array());
+ }
+
+ json::Array *Regions = JO.getArray("CodeRegions");
+ assert(Regions && "This array must exist!");
+ Regions->push_back(getJSONReportRegion());
+}
+
void PipelinePrinter::printReport(llvm::raw_ostream &OS) const {
+ // Don't print the header of this region if it is the default region, and if
+ // it doesn't have an end location.
+ if (Region.startLoc().isValid() || Region.endLoc().isValid())
+ printRegionHeader(OS);
+
for (const auto &V : Views)
- V->printView(OutputKind, OS);
+ V->printView(OS);
}
-} // namespace mca.
+
+} // namespace mca
} // namespace llvm
diff --git a/llvm/tools/llvm-mca/PipelinePrinter.h b/llvm/tools/llvm-mca/PipelinePrinter.h
index ae18140d32b7..1365f75be0f5 100644
--- a/llvm/tools/llvm-mca/PipelinePrinter.h
+++ b/llvm/tools/llvm-mca/PipelinePrinter.h
@@ -18,6 +18,8 @@
#include "Views/View.h"
#include "llvm/ADT/SmallVector.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/MCA/Context.h"
#include "llvm/MCA/Pipeline.h"
#include "llvm/Support/raw_ostream.h"
@@ -26,6 +28,8 @@
namespace llvm {
namespace mca {
+class CodeRegion;
+
/// A printer class that knows how to collects statistics on the
/// code analyzed by the llvm-mca tool.
///
@@ -35,12 +39,21 @@ namespace mca {
/// resource pressure.
class PipelinePrinter {
Pipeline &P;
+ const CodeRegion &Region;
+ unsigned RegionIdx;
+ const MCSubtargetInfo &STI;
+ const PipelineOptions &PO;
llvm::SmallVector<std::unique_ptr<View>, 8> Views;
- View::OutputKind OutputKind;
+
+ void printRegionHeader(llvm::raw_ostream &OS) const;
+ json::Object getJSONReportRegion() const;
+ json::Object getJSONTargetInfo() const;
+ json::Object getJSONSimulationParameters() const;
public:
- PipelinePrinter(Pipeline &pipeline, View::OutputKind OutputKind)
- : P(pipeline), OutputKind(OutputKind) {}
+ PipelinePrinter(Pipeline &Pipe, const CodeRegion &R, unsigned Idx,
+ const MCSubtargetInfo &STI, const PipelineOptions &PO)
+ : P(Pipe), Region(R), RegionIdx(Idx), STI(STI), PO(PO), Views() {}
void addView(std::unique_ptr<View> V) {
P.addEventListener(V.get());
@@ -48,6 +61,7 @@ public:
}
void printReport(llvm::raw_ostream &OS) const;
+ void printReport(json::Object &JO) const;
};
} // namespace mca
} // namespace llvm
diff --git a/llvm/tools/llvm-mca/Views/BottleneckAnalysis.cpp b/llvm/tools/llvm-mca/Views/BottleneckAnalysis.cpp
index 38a8e2ef9c53..5b110d6602df 100644
--- a/llvm/tools/llvm-mca/Views/BottleneckAnalysis.cpp
+++ b/llvm/tools/llvm-mca/Views/BottleneckAnalysis.cpp
@@ -66,8 +66,6 @@ void PressureTracker::onInstructionExecuted(unsigned IID) { IPI.erase(IID); }
void PressureTracker::handleInstructionIssuedEvent(
const HWInstructionIssuedEvent &Event) {
unsigned IID = Event.IR.getSourceIndex();
- using ResourceRef = HWInstructionIssuedEvent::ResourceRef;
- using ResourceUse = std::pair<ResourceRef, ResourceCycles>;
for (const ResourceUse &Use : Event.UsedResources) {
const ResourceRef &RR = Use.first;
unsigned Index = ProcResID2ResourceUsersIndex[RR.first];
@@ -200,8 +198,8 @@ void DependencyGraph::initializeRootSet(
}
}
-void DependencyGraph::propagateThroughEdges(
- SmallVectorImpl<unsigned> &RootSet, unsigned Iterations) {
+void DependencyGraph::propagateThroughEdges(SmallVectorImpl<unsigned> &RootSet,
+ unsigned Iterations) {
SmallVector<unsigned, 8> ToVisit;
// A critical sequence is computed as the longest path from a node of the
@@ -223,14 +221,14 @@ void DependencyGraph::propagateThroughEdges(
// The `unvisited nodes` set initially contains all the nodes from the
// RootSet. A node N is added to the `unvisited nodes` if all its
// predecessors have been visited already.
- //
+ //
// For simplicity, every node tracks the number of unvisited incoming edges in
// field `NumVisitedPredecessors`. When the value of that field drops to
// zero, then the corresponding node is added to a `ToVisit` set.
//
// At the end of every iteration of the outer loop, set `ToVisit` becomes our
// new `unvisited nodes` set.
- //
+ //
// The algorithm terminates when the set of unvisited nodes (i.e. our RootSet)
// is empty. This algorithm works under the assumption that the graph is
// acyclic.
@@ -269,8 +267,9 @@ void DependencyGraph::getCriticalSequence(
// that node is the last instruction of our critical sequence.
// Field N.Depth would tell us the total length of the sequence.
//
- // To obtain the sequence of critical edges, we simply follow the chain of critical
- // predecessors starting from node N (field DGNode::CriticalPredecessor).
+ // To obtain the sequence of critical edges, we simply follow the chain of
+ // critical predecessors starting from node N (field
+ // DGNode::CriticalPredecessor).
const auto It = std::max_element(
Nodes.begin(), Nodes.end(),
[](const DGNode &Lhs, const DGNode &Rhs) { return Lhs.Cost < Rhs.Cost; });
diff --git a/llvm/tools/llvm-mca/Views/BottleneckAnalysis.h b/llvm/tools/llvm-mca/Views/BottleneckAnalysis.h
index 427937d9e3d7..cd5af0afcf5b 100644
--- a/llvm/tools/llvm-mca/Views/BottleneckAnalysis.h
+++ b/llvm/tools/llvm-mca/Views/BottleneckAnalysis.h
@@ -33,9 +33,9 @@
/// In particular, this occurs when there is a delta between the number of uOps
/// dispatched and the number of uOps issued to the underlying pipelines.
///
-/// The bottleneck analysis view is also responsible for identifying and printing
-/// the most "critical" sequence of dependent instructions according to the
-/// simulated run.
+/// The bottleneck analysis view is also responsible for identifying and
+/// printing the most "critical" sequence of dependent instructions according to
+/// the simulated run.
///
/// Below is the critical sequence computed for the dot-product example on
/// btver2:
@@ -62,13 +62,14 @@
/// and edges of the graph represent data dependencies or processor resource
/// interferences.
///
-/// Edges are dynamically 'discovered' by observing instruction state transitions
-/// and backend pressure increase events. Edges are internally ranked based on
-/// their "criticality". A dependency is considered to be critical if it takes a
-/// long time to execute, and if it contributes to backend pressure increases.
-/// Criticality is internally measured in terms of cycles; it is computed for
-/// every edge in the graph as a function of the edge latency and the number of
-/// backend pressure increase cycles contributed by that edge.
+/// Edges are dynamically 'discovered' by observing instruction state
+/// transitions and backend pressure increase events. Edges are internally
+/// ranked based on their "criticality". A dependency is considered to be
+/// critical if it takes a long time to execute, and if it contributes to
+/// backend pressure increases. Criticality is internally measured in terms of
+/// cycles; it is computed for every edge in the graph as a function of the edge
+/// latency and the number of backend pressure increase cycles contributed by
+/// that edge.
///
/// At the end of simulation, costs are propagated to nodes through the edges of
/// the graph, and the most expensive path connecting the root-set (a
@@ -217,8 +218,8 @@ struct DependencyEdge {
// Loop carried dependencies are carefully expanded by the bottleneck analysis
// to guarantee that the graph stays acyclic. To this end, extra nodes are
// pre-allocated at construction time to describe instructions from "past and
-// future" iterations. The graph is kept acyclic mainly because it simplifies the
-// complexity of the algorithm that computes the critical sequence.
+// future" iterations. The graph is kept acyclic mainly because it simplifies
+// the complexity of the algorithm that computes the critical sequence.
class DependencyGraph {
struct DGNode {
unsigned NumPredecessors;
@@ -239,7 +240,8 @@ class DependencyGraph {
void pruneEdges(unsigned Iterations);
void initializeRootSet(SmallVectorImpl<unsigned> &RootSet) const;
- void propagateThroughEdges(SmallVectorImpl<unsigned> &RootSet, unsigned Iterations);
+ void propagateThroughEdges(SmallVectorImpl<unsigned> &RootSet,
+ unsigned Iterations);
#ifndef NDEBUG
void dumpDependencyEdge(raw_ostream &OS, const DependencyEdge &DE,
@@ -333,7 +335,7 @@ public:
void printView(raw_ostream &OS) const override;
StringRef getNameAsString() const override { return "BottleneckAnalysis"; }
- json::Value toJSON() const override { return "not implemented"; }
+ bool isSerializable() const override { return false; }
#ifndef NDEBUG
void dump(raw_ostream &OS, MCInstPrinter &MCIP) const { DG.dump(OS, MCIP); }
diff --git a/llvm/tools/llvm-mca/Views/DispatchStatistics.cpp b/llvm/tools/llvm-mca/Views/DispatchStatistics.cpp
index a1c0cf208d35..3dc17c8754d8 100644
--- a/llvm/tools/llvm-mca/Views/DispatchStatistics.cpp
+++ b/llvm/tools/llvm-mca/Views/DispatchStatistics.cpp
@@ -1,5 +1,4 @@
-//===--------------------- DispatchStatistics.cpp ---------------------*- C++
-//-*-===//
+//===--------------------- DispatchStatistics.cpp ---------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -77,10 +76,23 @@ void DispatchStatistics::printDispatchStalls(raw_ostream &OS) const {
printStalls(SS, HWStalls[HWStallEvent::StoreQueueFull], NumCycles);
SS << "\nGROUP - Static restrictions on the dispatch group: ";
printStalls(SS, HWStalls[HWStallEvent::DispatchGroupStall], NumCycles);
+ SS << "\nUSH - Uncategorised Structural Hazard: ";
+ printStalls(SS, HWStalls[HWStallEvent::CustomBehaviourStall], NumCycles);
SS << '\n';
SS.flush();
OS << Buffer;
}
+json::Value DispatchStatistics::toJSON() const {
+ json::Object JO({{"RAT", HWStalls[HWStallEvent::RegisterFileStall]},
+ {"RCU", HWStalls[HWStallEvent::RetireControlUnitStall]},
+ {"SCHEDQ", HWStalls[HWStallEvent::SchedulerQueueFull]},
+ {"LQ", HWStalls[HWStallEvent::LoadQueueFull]},
+ {"SQ", HWStalls[HWStallEvent::StoreQueueFull]},
+ {"GROUP", HWStalls[HWStallEvent::DispatchGroupStall]},
+ {"USH", HWStalls[HWStallEvent::CustomBehaviourStall]}});
+ return JO;
+}
+
} // namespace mca
} // namespace llvm
diff --git a/llvm/tools/llvm-mca/Views/DispatchStatistics.h b/llvm/tools/llvm-mca/Views/DispatchStatistics.h
index 8d999fb0acfe..81b582f74a6b 100644
--- a/llvm/tools/llvm-mca/Views/DispatchStatistics.h
+++ b/llvm/tools/llvm-mca/Views/DispatchStatistics.h
@@ -79,6 +79,7 @@ public:
printDispatchHistogram(OS);
}
StringRef getNameAsString() const override { return "DispatchStatistics"; }
+ json::Value toJSON() const override;
};
} // namespace mca
} // namespace llvm
diff --git a/llvm/tools/llvm-mca/Views/InstructionInfoView.cpp b/llvm/tools/llvm-mca/Views/InstructionInfoView.cpp
index 2248f63fe7e9..3f6abf4af2cf 100644
--- a/llvm/tools/llvm-mca/Views/InstructionInfoView.cpp
+++ b/llvm/tools/llvm-mca/Views/InstructionInfoView.cpp
@@ -93,7 +93,7 @@ void InstructionInfoView::collectData(
MutableArrayRef<InstructionInfoViewData> IIVD) const {
const llvm::MCSubtargetInfo &STI = getSubTargetInfo();
const MCSchedModel &SM = STI.getSchedModel();
- for (const auto &I : zip(getSource(), IIVD)) {
+ for (const auto I : zip(getSource(), IIVD)) {
const MCInst &Inst = std::get<0>(I);
InstructionInfoViewData &IIVDEntry = std::get<1>(I);
const MCInstrDesc &MCDesc = MCII.get(Inst.getOpcode());
@@ -147,7 +147,7 @@ json::Value InstructionInfoView::toJSON() const {
JO.try_emplace("Instruction", (unsigned)I.index());
InstInfo.push_back(std::move(JO));
}
- return json::Value(std::move(InstInfo));
+ return json::Object({{"InstructionList", json::Value(std::move(InstInfo))}});
}
} // namespace mca.
} // namespace llvm
diff --git a/llvm/tools/llvm-mca/Views/InstructionView.cpp b/llvm/tools/llvm-mca/Views/InstructionView.cpp
index 7f7a5b7cdbbb..3b174a064985 100644
--- a/llvm/tools/llvm-mca/Views/InstructionView.cpp
+++ b/llvm/tools/llvm-mca/Views/InstructionView.cpp
@@ -1,4 +1,4 @@
-//===----------------------- View.cpp ---------------------------*- C++ -*-===//
+//===----------------------- InstructionView.cpp ----------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -11,15 +11,18 @@
///
//===----------------------------------------------------------------------===//
-#include <sstream>
#include "Views/InstructionView.h"
#include "llvm/MC/MCInst.h"
+#include "llvm/MC/MCInstPrinter.h"
#include "llvm/MC/MCSubtargetInfo.h"
namespace llvm {
namespace mca {
-StringRef InstructionView::printInstructionString(const llvm::MCInst &MCI) const {
+InstructionView::~InstructionView() = default;
+
+StringRef
+InstructionView::printInstructionString(const llvm::MCInst &MCI) const {
InstructionString = "";
MCIP.printInst(&MCI, 0, "", STI, InstrStream);
InstrStream.flush();
@@ -28,33 +31,13 @@ StringRef InstructionView::printInstructionString(const llvm::MCInst &MCI) const
}
json::Value InstructionView::toJSON() const {
- json::Object JO;
json::Array SourceInfo;
for (const auto &MCI : getSource()) {
StringRef Instruction = printInstructionString(MCI);
SourceInfo.push_back(Instruction.str());
}
- JO.try_emplace("Instructions", std::move(SourceInfo));
-
- json::Array Resources;
- const MCSchedModel &SM = STI.getSchedModel();
- for (unsigned I = 1, E = SM.getNumProcResourceKinds(); I < E; ++I) {
- const MCProcResourceDesc &ProcResource = *SM.getProcResource(I);
- unsigned NumUnits = ProcResource.NumUnits;
- // Skip groups and invalid resources with zero units.
- if (ProcResource.SubUnitsIdxBegin || !NumUnits)
- continue;
- for (unsigned J = 0; J < NumUnits; ++J) {
- std::stringstream ResNameStream;
- ResNameStream << ProcResource.Name;
- if (NumUnits > 1)
- ResNameStream << "." << J;
- Resources.push_back(ResNameStream.str());
- }
- }
- JO.try_emplace("Resources", json::Object({{"CPUName", MCPU}, {"Resources", std::move(Resources)}}));
-
- return JO;
+ return SourceInfo;
}
+
} // namespace mca
} // namespace llvm
diff --git a/llvm/tools/llvm-mca/Views/InstructionView.h b/llvm/tools/llvm-mca/Views/InstructionView.h
index 2a260b97d8fb..1843b0513dfc 100644
--- a/llvm/tools/llvm-mca/Views/InstructionView.h
+++ b/llvm/tools/llvm-mca/Views/InstructionView.h
@@ -1,4 +1,4 @@
-//===----------------------- InstrucionView.h -----------------------------*- C++ -*-===//
+//===----------------------- InstructionView.h ------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -16,9 +16,8 @@
#define LLVM_TOOLS_LLVM_MCA_INSTRUCTIONVIEW_H
#include "Views/View.h"
-#include "llvm/MC/MCInstPrinter.h"
-#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/JSON.h"
+#include "llvm/Support/raw_ostream.h"
namespace llvm {
namespace mca {
@@ -28,7 +27,6 @@ class InstructionView : public View {
const llvm::MCSubtargetInfo &STI;
llvm::MCInstPrinter &MCIP;
llvm::ArrayRef<llvm::MCInst> Source;
- StringRef MCPU;
mutable std::string InstructionString;
mutable raw_string_ostream InstrStream;
@@ -36,17 +34,13 @@ class InstructionView : public View {
public:
void printView(llvm::raw_ostream &) const override {}
InstructionView(const llvm::MCSubtargetInfo &STI,
- llvm::MCInstPrinter &Printer,
- llvm::ArrayRef<llvm::MCInst> S,
- StringRef MCPU = StringRef())
- : STI(STI), MCIP(Printer), Source(S), MCPU(MCPU),
- InstrStream(InstructionString) {}
+ llvm::MCInstPrinter &Printer, llvm::ArrayRef<llvm::MCInst> S)
+ : STI(STI), MCIP(Printer), Source(S), InstrStream(InstructionString) {}
+
+ virtual ~InstructionView();
- virtual ~InstructionView() = default;
+ StringRef getNameAsString() const override { return "Instructions"; }
- StringRef getNameAsString() const override {
- return "Instructions and CPU resources";
- }
// Return a reference to a string representing a given machine instruction.
// The result should be used or copied before the next call to
// printInstructionString() as it will overwrite the previous result.
@@ -55,12 +49,10 @@ public:
llvm::MCInstPrinter &getInstPrinter() const { return MCIP; }
llvm::ArrayRef<llvm::MCInst> getSource() const { return Source; }
+
json::Value toJSON() const override;
- virtual void printViewJSON(llvm::raw_ostream &OS) override {
- json::Value JV = toJSON();
- OS << formatv("{0:2}", JV) << "\n";
- }
};
+
} // namespace mca
} // namespace llvm
diff --git a/llvm/tools/llvm-mca/Views/RegisterFileStatistics.cpp b/llvm/tools/llvm-mca/Views/RegisterFileStatistics.cpp
index 58736ee0d18c..4ef8053bff41 100644
--- a/llvm/tools/llvm-mca/Views/RegisterFileStatistics.cpp
+++ b/llvm/tools/llvm-mca/Views/RegisterFileStatistics.cpp
@@ -60,18 +60,21 @@ void RegisterFileStatistics::updateMoveElimInfo(const Instruction &Inst) {
if (!Inst.isOptimizableMove())
return;
- assert(Inst.getDefs().size() == 1 && "Expected a single definition!");
- assert(Inst.getUses().size() == 1 && "Expected a single register use!");
- const WriteState &WS = Inst.getDefs()[0];
- const ReadState &RS = Inst.getUses()[0];
-
- MoveEliminationInfo &Info =
- MoveElimInfo[Inst.getDefs()[0].getRegisterFileID()];
- Info.TotalMoveEliminationCandidates++;
- if (WS.isEliminated())
- Info.CurrentMovesEliminated++;
- if (WS.isWriteZero() && RS.isReadZero())
- Info.TotalMovesThatPropagateZero++;
+ if (Inst.getDefs().size() != Inst.getUses().size())
+ return;
+
+ for (size_t I = 0, E = Inst.getDefs().size(); I < E; ++I) {
+ const WriteState &WS = Inst.getDefs()[I];
+ const ReadState &RS = Inst.getUses()[E - (I + 1)];
+
+ MoveEliminationInfo &Info =
+ MoveElimInfo[Inst.getDefs()[0].getRegisterFileID()];
+ Info.TotalMoveEliminationCandidates++;
+ if (WS.isEliminated())
+ Info.CurrentMovesEliminated++;
+ if (WS.isWriteZero() && RS.isReadZero())
+ Info.TotalMovesThatPropagateZero++;
+ }
}
void RegisterFileStatistics::onEvent(const HWInstructionEvent &Event) {
diff --git a/llvm/tools/llvm-mca/Views/RegisterFileStatistics.h b/llvm/tools/llvm-mca/Views/RegisterFileStatistics.h
index cf384dbfe337..ec5c5f431e12 100644
--- a/llvm/tools/llvm-mca/Views/RegisterFileStatistics.h
+++ b/llvm/tools/llvm-mca/Views/RegisterFileStatistics.h
@@ -76,6 +76,7 @@ public:
StringRef getNameAsString() const override {
return "RegisterFileStatistics";
}
+ bool isSerializable() const override { return false; }
};
} // namespace mca
} // namespace llvm
diff --git a/llvm/tools/llvm-mca/Views/RetireControlUnitStatistics.cpp b/llvm/tools/llvm-mca/Views/RetireControlUnitStatistics.cpp
index 61c115b27be1..1c40428fb018 100644
--- a/llvm/tools/llvm-mca/Views/RetireControlUnitStatistics.cpp
+++ b/llvm/tools/llvm-mca/Views/RetireControlUnitStatistics.cpp
@@ -71,7 +71,8 @@ void RetireControlUnitStatistics::printView(raw_ostream &OS) const {
}
unsigned AvgUsage = (double)SumOfUsedEntries / NumCycles;
- double MaxUsagePercentage = ((double)MaxUsedEntries / TotalROBEntries) * 100.0;
+ double MaxUsagePercentage =
+ ((double)MaxUsedEntries / TotalROBEntries) * 100.0;
double NormalizedMaxPercentage = floor((MaxUsagePercentage * 10) + 0.5) / 10;
double AvgUsagePercentage = ((double)AvgUsage / TotalROBEntries) * 100.0;
double NormalizedAvgPercentage = floor((AvgUsagePercentage * 10) + 0.5) / 10;
diff --git a/llvm/tools/llvm-mca/Views/RetireControlUnitStatistics.h b/llvm/tools/llvm-mca/Views/RetireControlUnitStatistics.h
index 662a223662e6..86b46e93aa7c 100644
--- a/llvm/tools/llvm-mca/Views/RetireControlUnitStatistics.h
+++ b/llvm/tools/llvm-mca/Views/RetireControlUnitStatistics.h
@@ -55,6 +55,7 @@ public:
StringRef getNameAsString() const override {
return "RetireControlUnitStatistics";
}
+ bool isSerializable() const override { return false; }
};
} // namespace mca
diff --git a/llvm/tools/llvm-mca/Views/SchedulerStatistics.h b/llvm/tools/llvm-mca/Views/SchedulerStatistics.h
index 734046c3112f..66f4b0011866 100644
--- a/llvm/tools/llvm-mca/Views/SchedulerStatistics.h
+++ b/llvm/tools/llvm-mca/Views/SchedulerStatistics.h
@@ -89,6 +89,7 @@ public:
void printView(llvm::raw_ostream &OS) const override;
StringRef getNameAsString() const override { return "SchedulerStatistics"; }
+ bool isSerializable() const override { return false; }
};
} // namespace mca
} // namespace llvm
diff --git a/llvm/tools/llvm-mca/Views/SummaryView.cpp b/llvm/tools/llvm-mca/Views/SummaryView.cpp
index c0fe3b5193a7..bf258b4c26b1 100644
--- a/llvm/tools/llvm-mca/Views/SummaryView.cpp
+++ b/llvm/tools/llvm-mca/Views/SummaryView.cpp
@@ -1,4 +1,4 @@
-//===--------------------- SummaryView.cpp -------------------*- C++ -*-===//
+//===--------------------- SummaryView.cpp ----------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -24,9 +24,8 @@ namespace mca {
SummaryView::SummaryView(const MCSchedModel &Model, ArrayRef<MCInst> S,
unsigned Width)
- : SM(Model), Source(S), DispatchWidth(Width?Width: Model.IssueWidth),
- LastInstructionIdx(0),
- TotalCycles(0), NumMicroOps(0),
+ : SM(Model), Source(S), DispatchWidth(Width ? Width : Model.IssueWidth),
+ LastInstructionIdx(0), TotalCycles(0), NumMicroOps(0),
ProcResourceUsage(Model.getNumProcResourceKinds(), 0),
ProcResourceMasks(Model.getNumProcResourceKinds()),
ResIdx2ProcResID(Model.getNumProcResourceKinds(), 0) {
diff --git a/llvm/tools/llvm-mca/Views/SummaryView.h b/llvm/tools/llvm-mca/Views/SummaryView.h
index 2622e869ef23..e2c7cfd19e94 100644
--- a/llvm/tools/llvm-mca/Views/SummaryView.h
+++ b/llvm/tools/llvm-mca/Views/SummaryView.h
@@ -1,4 +1,4 @@
-//===--------------------- SummaryView.h ---------------------*- C++ -*-===//
+//===--------------------- SummaryView.h ------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
diff --git a/llvm/tools/llvm-mca/Views/TimelineView.cpp b/llvm/tools/llvm-mca/Views/TimelineView.cpp
index c8b481bc7ce6..9a949761bb75 100644
--- a/llvm/tools/llvm-mca/Views/TimelineView.cpp
+++ b/llvm/tools/llvm-mca/Views/TimelineView.cpp
@@ -21,8 +21,8 @@ TimelineView::TimelineView(const MCSubtargetInfo &sti, MCInstPrinter &Printer,
llvm::ArrayRef<llvm::MCInst> S, unsigned Iterations,
unsigned Cycles)
: InstructionView(sti, Printer, S), CurrentCycle(0),
- MaxCycle(Cycles == 0 ? 80 : Cycles), LastCycle(0), WaitTime(S.size()),
- UsedBuffer(S.size()) {
+ MaxCycle(Cycles == 0 ? std::numeric_limits<unsigned>::max() : Cycles),
+ LastCycle(0), WaitTime(S.size()), UsedBuffer(S.size()) {
unsigned NumInstructions = getSource().size();
assert(Iterations && "Invalid number of iterations specified!");
NumInstructions *= Iterations;
@@ -77,8 +77,10 @@ void TimelineView::onEvent(const HWInstructionEvent &Event) {
"Instruction cannot be ready if it hasn't been dispatched yet!");
WTEntry.CyclesSpentInSQWhileReady +=
TVEntry.CycleIssued - TVEntry.CycleReady;
- WTEntry.CyclesSpentAfterWBAndBeforeRetire +=
- (CurrentCycle - 1) - TVEntry.CycleExecuted;
+ if (CurrentCycle > TVEntry.CycleExecuted) {
+ WTEntry.CyclesSpentAfterWBAndBeforeRetire +=
+ (CurrentCycle - 1) - TVEntry.CycleExecuted;
+ }
break;
}
case HWInstructionEvent::Ready:
@@ -243,7 +245,8 @@ void TimelineView::printTimelineViewEntry(formatted_raw_ostream &OS,
for (unsigned I = Entry.CycleExecuted + 1, E = Entry.CycleRetired; I < E; ++I)
OS << TimelineView::DisplayChar::RetireLag;
- OS << TimelineView::DisplayChar::Retired;
+ if (Entry.CycleExecuted < Entry.CycleRetired)
+ OS << TimelineView::DisplayChar::Retired;
// Skip other columns.
for (unsigned I = Entry.CycleRetired + 1, E = LastCycle; I <= E; ++I)
@@ -285,7 +288,14 @@ void TimelineView::printTimeline(raw_ostream &OS) const {
for (unsigned Iteration = 0; Iteration < Iterations; ++Iteration) {
for (const MCInst &Inst : Source) {
const TimelineViewEntry &Entry = Timeline[IID];
- if (Entry.CycleRetired == 0)
+ // When an instruction is retired after timeline-max-cycles,
+ // its CycleRetired is left at 0. However, it's possible for
+ // a 0 latency instruction to be retired during cycle 0 and we
+ // don't want to early exit in that case. The CycleExecuted
+ // attribute is set correctly whether or not it is greater
+ // than timeline-max-cycles so we can use that to ensure
+ // we don't early exit because of a 0 latency instruction.
+ if (Entry.CycleRetired == 0 && Entry.CycleExecuted != 0)
return;
unsigned SourceIndex = IID % Source.size();
diff --git a/llvm/tools/llvm-mca/Views/TimelineView.h b/llvm/tools/llvm-mca/Views/TimelineView.h
index 81f2b0335081..81be8244b779 100644
--- a/llvm/tools/llvm-mca/Views/TimelineView.h
+++ b/llvm/tools/llvm-mca/Views/TimelineView.h
@@ -125,7 +125,7 @@ class TimelineView : public InstructionView {
unsigned LastCycle;
struct TimelineViewEntry {
- int CycleDispatched; // A negative value is an "invalid cycle".
+ int CycleDispatched; // A negative value is an "invalid cycle".
unsigned CycleReady;
unsigned CycleIssued;
unsigned CycleExecuted;
diff --git a/llvm/tools/llvm-mca/Views/View.h b/llvm/tools/llvm-mca/Views/View.h
index 85464bfda662..c604733d4ec9 100644
--- a/llvm/tools/llvm-mca/Views/View.h
+++ b/llvm/tools/llvm-mca/Views/View.h
@@ -17,32 +17,22 @@
#include "llvm/MC/MCInstPrinter.h"
#include "llvm/MCA/HWEventListener.h"
-#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/JSON.h"
+#include "llvm/Support/raw_ostream.h"
namespace llvm {
namespace mca {
class View : public HWEventListener {
public:
- enum OutputKind { OK_READABLE, OK_JSON };
-
- void printView(OutputKind OutputKind, llvm::raw_ostream &OS) {
- if (OutputKind == OK_JSON)
- printViewJSON(OS);
- else
- printView(OS);
- }
+ virtual ~View() = default;
virtual void printView(llvm::raw_ostream &OS) const = 0;
- virtual void printViewJSON(llvm::raw_ostream &OS) {
- json::Object JO;
- JO.try_emplace(getNameAsString().str(), toJSON());
- OS << formatv("{0:2}", json::Value(std::move(JO))) << "\n";
- }
- virtual ~View() = default;
virtual StringRef getNameAsString() const = 0;
+
virtual json::Value toJSON() const { return "not implemented"; }
+ virtual bool isSerializable() const { return true; }
+
void anchor() override;
};
} // namespace mca
diff --git a/llvm/tools/llvm-mca/lib/AMDGPU/AMDGPUCustomBehaviour.cpp b/llvm/tools/llvm-mca/lib/AMDGPU/AMDGPUCustomBehaviour.cpp
new file mode 100644
index 000000000000..a655f3faf1bf
--- /dev/null
+++ b/llvm/tools/llvm-mca/lib/AMDGPU/AMDGPUCustomBehaviour.cpp
@@ -0,0 +1,33 @@
+//===------------------ AMDGPUCustomBehaviour.cpp ---------------*-C++ -* -===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file implements methods from the AMDGPUCustomBehaviour class.
+///
+//===----------------------------------------------------------------------===//
+
+#include "AMDGPUCustomBehaviour.h"
+#include "MCTargetDesc/AMDGPUMCTargetDesc.h"
+#include "SIInstrInfo.h"
+#include "llvm/Support/WithColor.h"
+
+namespace llvm {
+namespace mca {
+
+AMDGPUCustomBehaviour::AMDGPUCustomBehaviour(const MCSubtargetInfo &STI,
+ const SourceMgr &SrcMgr,
+ const MCInstrInfo &MCII)
+ : CustomBehaviour(STI, SrcMgr, MCII) {}
+
+unsigned AMDGPUCustomBehaviour::checkCustomHazard(ArrayRef<InstRef> IssuedInst,
+ const InstRef &IR) {
+ return 0;
+}
+
+} // namespace mca
+} // namespace llvm
diff --git a/llvm/tools/llvm-mca/lib/AMDGPU/AMDGPUCustomBehaviour.h b/llvm/tools/llvm-mca/lib/AMDGPU/AMDGPUCustomBehaviour.h
new file mode 100644
index 000000000000..0dd21c7b4c44
--- /dev/null
+++ b/llvm/tools/llvm-mca/lib/AMDGPU/AMDGPUCustomBehaviour.h
@@ -0,0 +1,57 @@
+//===------------------- AMDGPUCustomBehaviour.h ----------------*-C++ -* -===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file defines the AMDGPUCustomBehaviour class which inherits from
+/// CustomBehaviour.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_MCA_LIB_AMDGPU_AMDGPUCUSTOMBEHAVIOUR_H
+#define LLVM_TOOLS_LLVM_MCA_LIB_AMDGPU_AMDGPUCUSTOMBEHAVIOUR_H
+
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/MCA/CustomBehaviour.h"
+#include "llvm/Support/TargetParser.h"
+
+namespace llvm {
+namespace mca {
+
+class AMDGPUInstrPostProcess : public InstrPostProcess {
+public:
+ AMDGPUInstrPostProcess(const MCSubtargetInfo &STI, const MCInstrInfo &MCII)
+ : InstrPostProcess(STI, MCII) {}
+
+ ~AMDGPUInstrPostProcess() {}
+
+ void postProcessInstruction(std::unique_ptr<Instruction> &Inst,
+ const MCInst &MCI) override {}
+};
+
+class AMDGPUCustomBehaviour : public CustomBehaviour {
+public:
+ AMDGPUCustomBehaviour(const MCSubtargetInfo &STI, const SourceMgr &SrcMgr,
+ const MCInstrInfo &MCII);
+
+ ~AMDGPUCustomBehaviour() {}
+
+ /// This method is used to determine if an instruction
+ /// should be allowed to be dispatched. The return value is
+ /// how many cycles until the instruction can be dispatched.
+ /// This method is called after MCA has already checked for
+ /// register and hardware dependencies so this method should only
+ /// implement custom behaviour and dependencies that are not picked up
+ /// by MCA naturally.
+ unsigned checkCustomHazard(ArrayRef<InstRef> IssuedInst,
+ const InstRef &IR) override;
+};
+
+} // namespace mca
+} // namespace llvm
+
+#endif /* LLVM_TOOLS_LLVM_MCA_LIB_AMDGPU_AMDGPUCUSTOMBEHAVIOUR_H */
diff --git a/llvm/tools/llvm-mca/llvm-mca.cpp b/llvm/tools/llvm-mca/llvm-mca.cpp
index 13a2c6363579..a473cd8f1719 100644
--- a/llvm/tools/llvm-mca/llvm-mca.cpp
+++ b/llvm/tools/llvm-mca/llvm-mca.cpp
@@ -32,6 +32,9 @@
#include "Views/SchedulerStatistics.h"
#include "Views/SummaryView.h"
#include "Views/TimelineView.h"
+#ifdef HAS_AMDGPU
+#include "lib/AMDGPU/AMDGPUCustomBehaviour.h"
+#endif
#include "llvm/MC/MCAsmBackend.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCCodeEmitter.h"
@@ -42,6 +45,7 @@
#include "llvm/MC/MCTargetOptionsCommandFlags.h"
#include "llvm/MCA/CodeEmitter.h"
#include "llvm/MCA/Context.h"
+#include "llvm/MCA/CustomBehaviour.h"
#include "llvm/MCA/InstrBuilder.h"
#include "llvm/MCA/Pipeline.h"
#include "llvm/MCA/Stages/EntryStage.h"
@@ -91,15 +95,13 @@ static cl::opt<std::string>
cl::desc("Target a specific cpu type (-mcpu=help for details)"),
cl::value_desc("cpu-name"), cl::cat(ToolOptions), cl::init("native"));
-static cl::opt<std::string>
- MATTR("mattr",
- cl::desc("Additional target features."),
- cl::cat(ToolOptions));
+static cl::opt<std::string> MATTR("mattr",
+ cl::desc("Additional target features."),
+ cl::cat(ToolOptions));
-static cl::opt<bool>
- PrintJson("json",
- cl::desc("Print the output in json format"),
- cl::cat(ToolOptions), cl::init(false));
+static cl::opt<bool> PrintJson("json",
+ cl::desc("Print the output in json format"),
+ cl::cat(ToolOptions), cl::init(false));
static cl::opt<int>
OutputAsmVariant("output-asm-variant",
@@ -172,11 +174,11 @@ static cl::opt<unsigned> TimelineMaxIterations(
cl::desc("Maximum number of iterations to print in timeline view"),
cl::cat(ViewOptions), cl::init(0));
-static cl::opt<unsigned> TimelineMaxCycles(
- "timeline-max-cycles",
- cl::desc(
- "Maximum number of cycles in the timeline view. Defaults to 80 cycles"),
- cl::cat(ViewOptions), cl::init(80));
+static cl::opt<unsigned>
+ TimelineMaxCycles("timeline-max-cycles",
+ cl::desc("Maximum number of cycles in the timeline view, "
+ "or 0 for unlimited. Defaults to 80 cycles"),
+ cl::cat(ViewOptions), cl::init(80));
static cl::opt<bool>
AssumeNoAlias("noalias",
@@ -220,6 +222,12 @@ static cl::opt<bool> ShowEncoding(
cl::desc("Print encoding information in the instruction info view"),
cl::cat(ViewOptions), cl::init(false));
+static cl::opt<bool> DisableCustomBehaviour(
+ "disable-cb",
+ cl::desc(
+ "Disable custom behaviour (use the default class which does nothing)."),
+ cl::cat(ViewOptions), cl::init(false));
+
namespace {
const Target *getTarget(const char *ProgName) {
@@ -236,6 +244,9 @@ const Target *getTarget(const char *ProgName) {
return nullptr;
}
+ // Update TripleName with the updated triple from the target lookup.
+ TripleName = TheTriple.str();
+
// Return the found target.
return TheTarget;
}
@@ -244,8 +255,8 @@ ErrorOr<std::unique_ptr<ToolOutputFile>> getOutputStream() {
if (OutputFilename == "")
OutputFilename = "-";
std::error_code EC;
- auto Out =
- std::make_unique<ToolOutputFile>(OutputFilename, EC, sys::fs::OF_Text);
+ auto Out = std::make_unique<ToolOutputFile>(OutputFilename, EC,
+ sys::fs::OF_TextWithCRLF);
if (!EC)
return std::move(Out);
return EC;
@@ -257,14 +268,15 @@ static void processOptionImpl(cl::opt<bool> &O, const cl::opt<bool> &Default) {
O = Default.getValue();
}
-static void processViewOptions() {
+static void processViewOptions(bool IsOutOfOrder) {
if (!EnableAllViews.getNumOccurrences() &&
!EnableAllStats.getNumOccurrences())
return;
if (EnableAllViews.getNumOccurrences()) {
processOptionImpl(PrintSummaryView, EnableAllViews);
- processOptionImpl(EnableBottleneckAnalysis, EnableAllViews);
+ if (IsOutOfOrder)
+ processOptionImpl(EnableBottleneckAnalysis, EnableAllViews);
processOptionImpl(PrintResourcePressureView, EnableAllViews);
processOptionImpl(PrintTimelineView, EnableAllViews);
processOptionImpl(PrintInstructionInfoView, EnableAllViews);
@@ -277,7 +289,41 @@ static void processViewOptions() {
processOptionImpl(PrintRegisterFileStats, Default);
processOptionImpl(PrintDispatchStats, Default);
processOptionImpl(PrintSchedulerStats, Default);
- processOptionImpl(PrintRetireStats, Default);
+ if (IsOutOfOrder)
+ processOptionImpl(PrintRetireStats, Default);
+}
+
+std::unique_ptr<mca::InstrPostProcess>
+createInstrPostProcess(const Triple &TheTriple, const MCSubtargetInfo &STI,
+ const MCInstrInfo &MCII) {
+ // Might be a good idea to have a separate flag so that InstrPostProcess
+ // can be used with or without CustomBehaviour
+ if (DisableCustomBehaviour)
+ return std::make_unique<mca::InstrPostProcess>(STI, MCII);
+#ifdef HAS_AMDGPU
+ if (TheTriple.isAMDGPU())
+ return std::make_unique<mca::AMDGPUInstrPostProcess>(STI, MCII);
+#endif
+ return std::make_unique<mca::InstrPostProcess>(STI, MCII);
+}
+
+std::unique_ptr<mca::CustomBehaviour>
+createCustomBehaviour(const Triple &TheTriple, const MCSubtargetInfo &STI,
+ const mca::SourceMgr &SrcMgr, const MCInstrInfo &MCII) {
+ // Build the appropriate CustomBehaviour object for the current target.
+ // The CustomBehaviour class should never depend on the source code,
+ // but it can depend on the list of mca::Instruction and any classes
+ // that can be built using just the target info. If you need extra
+ // information from the source code or the list of MCInst, consider
+ // adding that information to the mca::Instruction class and setting
+ // it during InstrBuilder::createInstruction().
+ if (DisableCustomBehaviour)
+ return std::make_unique<mca::CustomBehaviour>(STI, SrcMgr, MCII);
+#ifdef HAS_AMDGPU
+ if (TheTriple.isAMDGPU())
+ return std::make_unique<mca::AMDGPUCustomBehaviour>(STI, SrcMgr, MCII);
+#endif
+ return std::make_unique<mca::CustomBehaviour>(STI, SrcMgr, MCII);
}
// Returns true on success.
@@ -327,9 +373,6 @@ int main(int argc, char **argv) {
return 1;
}
- // Apply overrides to llvm-mca specific options.
- processViewOptions();
-
if (MCPU == "native")
MCPU = std::string(llvm::sys::getHostCPUName());
@@ -339,10 +382,10 @@ int main(int argc, char **argv) {
if (!STI->isCPUStringValid(MCPU))
return 1;
- if (!PrintInstructionTables && !STI->getSchedModel().isOutOfOrder()) {
- WithColor::error() << "please specify an out-of-order cpu. '" << MCPU
- << "' is an in-order cpu.\n";
- return 1;
+ bool IsOutOfOrder = STI->getSchedModel().isOutOfOrder();
+ if (!PrintInstructionTables && !IsOutOfOrder) {
+ WithColor::warning() << "support for in-order CPU '" << MCPU
+ << "' is experimental.\n";
}
if (!STI->getSchedModel().hasInstrSchedModel()) {
@@ -358,6 +401,9 @@ int main(int argc, char **argv) {
return 1;
}
+ // Apply overrides to llvm-mca specific options.
+ processViewOptions(IsOutOfOrder);
+
std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TripleName));
assert(MRI && "Unable to create target register info!");
@@ -366,15 +412,15 @@ int main(int argc, char **argv) {
TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions));
assert(MAI && "Unable to create target asm info!");
- MCObjectFileInfo MOFI;
SourceMgr SrcMgr;
// Tell SrcMgr about this buffer, which is what the parser will pick up.
SrcMgr.AddNewSourceBuffer(std::move(*BufferPtr), SMLoc());
- MCContext Ctx(MAI.get(), MRI.get(), &MOFI, &SrcMgr);
-
- MOFI.InitMCObjectFileInfo(TheTriple, /* PIC= */ false, Ctx);
+ MCContext Ctx(TheTriple, MAI.get(), MRI.get(), STI.get(), &SrcMgr);
+ std::unique_ptr<MCObjectFileInfo> MOFI(
+ TheTarget->createMCObjectFileInfo(Ctx, /*PIC=*/false));
+ Ctx.setObjectFileInfo(MOFI.get());
std::unique_ptr<buffer_ostream> BOS;
@@ -384,9 +430,28 @@ int main(int argc, char **argv) {
std::unique_ptr<MCInstrAnalysis> MCIA(
TheTarget->createMCInstrAnalysis(MCII.get()));
+ // Need to initialize an MCInstPrinter as it is
+ // required for initializing the MCTargetStreamer
+ // which needs to happen within the CRG.parseCodeRegions() call below.
+ // Without an MCTargetStreamer, certain assembly directives can trigger a
+ // segfault. (For example, the .cv_fpo_proc directive on x86 will segfault if
+ // we don't initialize the MCTargetStreamer.)
+ unsigned IPtempOutputAsmVariant =
+ OutputAsmVariant == -1 ? 0 : OutputAsmVariant;
+ std::unique_ptr<MCInstPrinter> IPtemp(TheTarget->createMCInstPrinter(
+ Triple(TripleName), IPtempOutputAsmVariant, *MAI, *MCII, *MRI));
+ if (!IPtemp) {
+ WithColor::error()
+ << "unable to create instruction printer for target triple '"
+ << TheTriple.normalize() << "' with assembly variant "
+ << IPtempOutputAsmVariant << ".\n";
+ return 1;
+ }
+
// Parse the input and create CodeRegions that llvm-mca can analyze.
mca::AsmCodeRegionGenerator CRG(*TheTarget, SrcMgr, Ctx, *MAI, *STI, *MCII);
- Expected<const mca::CodeRegions &> RegionsOrErr = CRG.parseCodeRegions();
+ Expected<const mca::CodeRegions &> RegionsOrErr =
+ CRG.parseCodeRegions(std::move(IPtemp));
if (!RegionsOrErr) {
if (auto Err =
handleErrors(RegionsOrErr.takeError(), [](const StringError &E) {
@@ -456,24 +521,19 @@ int main(int argc, char **argv) {
*STI, *MRI, mc::InitMCTargetOptionsFromFlags()));
assert(MAB && "Unable to create asm backend!");
+ json::Object JSONOutput;
for (const std::unique_ptr<mca::CodeRegion> &Region : Regions) {
// Skip empty code regions.
if (Region->empty())
continue;
- // Don't print the header of this region if it is the default region, and
- // it doesn't have an end location.
- if (Region->startLoc().isValid() || Region->endLoc().isValid()) {
- TOF->os() << "\n[" << RegionIdx++ << "] Code Region";
- StringRef Desc = Region->getDescription();
- if (!Desc.empty())
- TOF->os() << " - " << Desc;
- TOF->os() << "\n\n";
- }
+ IB.clear();
// Lower the MCInst sequence into an mca::Instruction sequence.
ArrayRef<MCInst> Insts = Region->getInstructions();
mca::CodeEmitter CE(*STI, *MAB, *MCE, Insts);
+ std::unique_ptr<mca::InstrPostProcess> IPP =
+ createInstrPostProcess(TheTriple, *STI, *MCII);
std::vector<std::unique_ptr<mca::Instruction>> LoweredSequence;
for (const MCInst &MCI : Insts) {
Expected<std::unique_ptr<mca::Instruction>> Inst =
@@ -496,6 +556,8 @@ int main(int argc, char **argv) {
return 1;
}
+ IPP->postProcessInstruction(Inst.get(), MCI);
+
LoweredSequence.emplace_back(std::move(Inst.get()));
}
@@ -506,7 +568,12 @@ int main(int argc, char **argv) {
auto P = std::make_unique<mca::Pipeline>();
P->appendStage(std::make_unique<mca::EntryStage>(S));
P->appendStage(std::make_unique<mca::InstructionTables>(SM));
- mca::PipelinePrinter Printer(*P, mca::View::OK_READABLE);
+
+ mca::PipelinePrinter Printer(*P, *Region, RegionIdx, *STI, PO);
+ if (PrintJson) {
+ Printer.addView(
+ std::make_unique<mca::InstructionView>(*STI, *IP, Insts));
+ }
// Create the views for this pipeline, execute, and emit a report.
if (PrintInstructionInfoView) {
@@ -519,26 +586,47 @@ int main(int argc, char **argv) {
if (!runPipeline(*P))
return 1;
- Printer.printReport(TOF->os());
+ if (PrintJson) {
+ Printer.printReport(JSONOutput);
+ } else {
+ Printer.printReport(TOF->os());
+ }
+
+ ++RegionIdx;
continue;
}
+ // Create the CustomBehaviour object for enforcing Target Specific
+ // behaviours and dependencies that aren't expressed well enough
+ // in the tablegen. CB cannot depend on the list of MCInst or
+ // the source code (but it can depend on the list of
+ // mca::Instruction or any objects that can be reconstructed
+ // from the target information).
+ std::unique_ptr<mca::CustomBehaviour> CB =
+ createCustomBehaviour(TheTriple, *STI, S, *MCII);
+
// Create a basic pipeline simulating an out-of-order backend.
- auto P = MCA.createDefaultPipeline(PO, S);
- mca::PipelinePrinter Printer(*P, PrintJson ? mca::View::OK_JSON
- : mca::View::OK_READABLE);
+ auto P = MCA.createDefaultPipeline(PO, S, *CB);
+
+ mca::PipelinePrinter Printer(*P, *Region, RegionIdx, *STI, PO);
// When we output JSON, we add a view that contains the instructions
// and CPU resource information.
- if (PrintJson)
- Printer.addView(
- std::make_unique<mca::InstructionView>(*STI, *IP, Insts, MCPU));
+ if (PrintJson) {
+ auto IV = std::make_unique<mca::InstructionView>(*STI, *IP, Insts);
+ Printer.addView(std::move(IV));
+ }
if (PrintSummaryView)
Printer.addView(
std::make_unique<mca::SummaryView>(SM, Insts, DispatchWidth));
if (EnableBottleneckAnalysis) {
+ if (!IsOutOfOrder) {
+ WithColor::warning()
+ << "bottleneck analysis is not supported for in-order CPU '" << MCPU
+ << "'.\n";
+ }
Printer.addView(std::make_unique<mca::BottleneckAnalysis>(
*STI, *IP, Insts, S.getNumIterations()));
}
@@ -574,12 +662,18 @@ int main(int argc, char **argv) {
if (!runPipeline(*P))
return 1;
- Printer.printReport(TOF->os());
+ if (PrintJson) {
+ Printer.printReport(JSONOutput);
+ } else {
+ Printer.printReport(TOF->os());
+ }
- // Clear the InstrBuilder internal state in preparation for another round.
- IB.clear();
+ ++RegionIdx;
}
+ if (PrintJson)
+ TOF->os() << formatv("{0:2}", json::Value(std::move(JSONOutput))) << "\n";
+
TOF->keep();
return 0;
}
diff --git a/llvm/tools/llvm-modextract/llvm-modextract.cpp b/llvm/tools/llvm-modextract/llvm-modextract.cpp
index 7c4099625842..9a44cbf68d0d 100644
--- a/llvm/tools/llvm-modextract/llvm-modextract.cpp
+++ b/llvm/tools/llvm-modextract/llvm-modextract.cpp
@@ -21,21 +21,29 @@
using namespace llvm;
+static cl::OptionCategory ModextractCategory("Modextract Options");
+
static cl::opt<bool>
- BinaryExtract("b", cl::desc("Whether to perform binary extraction"));
+ BinaryExtract("b", cl::desc("Whether to perform binary extraction"),
+ cl::cat(ModextractCategory));
static cl::opt<std::string> OutputFilename("o", cl::Required,
cl::desc("Output filename"),
- cl::value_desc("filename"));
+ cl::value_desc("filename"),
+ cl::cat(ModextractCategory));
-static cl::opt<std::string>
- InputFilename(cl::Positional, cl::desc("<input bitcode>"), cl::init("-"));
+static cl::opt<std::string> InputFilename(cl::Positional,
+ cl::desc("<input bitcode>"),
+ cl::init("-"),
+ cl::cat(ModextractCategory));
static cl::opt<unsigned> ModuleIndex("n", cl::Required,
cl::desc("Index of module to extract"),
- cl::value_desc("index"));
+ cl::value_desc("index"),
+ cl::cat(ModextractCategory));
int main(int argc, char **argv) {
+ cl::HideUnrelatedOptions({&ModextractCategory, &getColorCategory()});
cl::ParseCommandLineOptions(argc, argv, "Module extractor");
ExitOnError ExitOnErr("llvm-modextract: error: ");
diff --git a/llvm/tools/llvm-nm/Opts.td b/llvm/tools/llvm-nm/Opts.td
new file mode 100644
index 000000000000..3a790890909a
--- /dev/null
+++ b/llvm/tools/llvm-nm/Opts.td
@@ -0,0 +1,76 @@
+include "llvm/Option/OptParser.td"
+
+class F<string letter, string help> : Flag<["-"], letter>, HelpText<help>;
+class FF<string name, string help> : Flag<["--"], name>, HelpText<help>;
+
+multiclass BB<string name, string help1, string help2> {
+ def NAME: Flag<["--"], name>, HelpText<help1>;
+ def no_ # NAME: Flag<["--"], "no-" # name>, HelpText<help2>;
+}
+
+multiclass Eq<string name, string help> {
+ def NAME #_EQ : Joined<["--"], name #"=">, HelpText<help>;
+ def : Separate<["--"], name>, Alias<!cast<Joined>(NAME #_EQ)>;
+}
+
+def debug_syms : FF<"debug-syms", "Show all symbols, even debugger only">;
+def defined_only : FF<"defined-only", "Show only defined symbols">;
+defm demangle : BB<"demangle", "Demangle C++ symbol names", "Don't demangle symbol names">;
+def dynamic : FF<"dynamic", "Display dynamic symbols instead of normal symbols">;
+def extern_only : FF<"extern-only", "Show only external symbols">;
+defm format : Eq<"format", "Specify output format: bsd (default), posix, sysv, darwin, just-symbols">, MetaVarName<"<format>">;
+def help : FF<"help", "Display this help">;
+def no_llvm_bc : FF<"no-llvm-bc", "Disable LLVM bitcode reader">;
+def no_sort : FF<"no-sort", "Show symbols in order encountered">;
+def no_weak : FF<"no-weak", "Show only non-weak symbols">;
+def numeric_sort : FF<"numeric-sort", "Sort symbols by address">;
+def print_armap : FF<"print-armap", "Print the archive map">;
+def print_file_name : FF<"print-file-name", "Precede each symbol with the object file it came from">;
+def print_size : FF<"print-size", "Show symbol size as well as address">;
+def quiet : FF<"quiet", "Suppress 'no symbols' diagnostic">;
+defm radix : Eq<"radix", "Radix (o/d/x) for printing symbol Values">, MetaVarName<"<radix>">;
+def reverse_sort : FF<"reverse-sort", "Sort in reverse order">;
+def size_sort : FF<"size-sort", "Sort symbols by size">;
+def special_syms : FF<"special-syms", "Do not filter special symbols from the output">;
+def undefined_only : FF<"undefined-only", "Show only undefined symbols">;
+def version : FF<"version", "Display the version">;
+def without_aliases : FF<"without-aliases", "Exclude aliases from output">, Flags<[HelpHidden]>;
+
+// Mach-O specific options.
+def grp_mach_o : OptionGroup<"kind">, HelpText<"llvm-nm Mach-O Specific Options">;
+
+def add_dyldinfo : FF<"add-dyldinfo", "Add symbols from the dyldinfo not already in the symbol table">, Group<grp_mach_o>;
+def add_inlinedinfo : FF<"add-inlinedinfo", "Add symbols from the inlined libraries, TBD only">, Group<grp_mach_o>;
+def arch_EQ : Joined<["--"], "arch=">, HelpText<"architecture(s) from a Mach-O file to dump">, Group<grp_mach_o>;
+def : Separate<["--", "-"], "arch">, Alias<arch_EQ>;
+def dyldinfo_only : FF<"dyldinfo-only", "Show only symbols from the dyldinfo">, Group<grp_mach_o>;
+def no_dyldinfo : FF<"no-dyldinfo", "Don't add any symbols from the dyldinfo">, Group<grp_mach_o>;
+def s : F<"s", "Dump only symbols from this segment and section name">, Group<grp_mach_o>;
+def x : F<"x", "Print symbol entry in hex">, Group<grp_mach_o>;
+
+def : FF<"just-symbol-name", "Alias for --format=just-symbols">, Alias<format_EQ>, AliasArgs<["just-symbols"]>, Flags<[HelpHidden]>;
+def : FF<"portability", "Alias for --format=posix">, Alias<format_EQ>, AliasArgs<["posix"]>;
+
+def : F<"a", "Alias for --debug-syms">, Alias<debug_syms>;
+def : F<"A", "Alias for --print-file-name">, Alias<print_file_name>;
+def : F<"B", "Alias for --format=bsd">, Alias<format_EQ>, AliasArgs<["bsd"]>;
+def : F<"C", "Alias for --demangle">, Alias<demangle>;
+def : F<"D", "Alias for --dynamic">, Alias<dynamic>;
+def : JoinedOrSeparate<["-"], "f">, HelpText<"Alias for --format">, Alias<format_EQ>, MetaVarName<"<format>">;
+def : F<"h", "Alias for --help">, Alias<help>;
+def : F<"g", "Alias for --extern-only">, Alias<extern_only>;
+def : F<"j", "Alias for --format=just-symbols">, Alias<format_EQ>, AliasArgs<["just-symbols"]>;
+def : F<"m", "Alias for --format=darwin">, Alias<format_EQ>, AliasArgs<["darwin"]>;
+def : F<"M", "Deprecated alias for --print-armap">, Alias<print_armap>, Flags<[HelpHidden]>;
+def : F<"n", "Alias for --numeric-sort">, Alias<numeric_sort>;
+def : F<"o", "Alias for --print-file-name">, Alias<print_file_name>;
+def : F<"p", "Alias for --no-sort">, Alias<no_sort>;
+def : F<"P", "Alias for --format=posix">, Alias<format_EQ>, AliasArgs<["posix"]>;
+def : F<"r", "Alias for --reverse-sort">, Alias<reverse_sort>;
+def : F<"S", "Alias for --print-size">, Alias<print_size>;
+def : JoinedOrSeparate<["-"], "t">, HelpText<"Alias for --radix">, Alias<radix_EQ>, MetaVarName<"<radix>">;
+def : F<"u", "Alias for --undefined-only">, Alias<undefined_only>;
+def : F<"U", "Deprecated alias for --defined-only">, Alias<defined_only>, Flags<[HelpHidden]>;
+def : F<"v", "Alias for --numeric-sort">, Alias<numeric_sort>;
+def : F<"V", "Alias for --version">, Alias<version>;
+def : F<"W", "Deprecated alias for --no-weak">, Alias<no_weak>, Flags<[HelpHidden]>;
diff --git a/llvm/tools/llvm-nm/llvm-nm.cpp b/llvm/tools/llvm-nm/llvm-nm.cpp
index c678108807c5..ffb427a3f2bd 100644
--- a/llvm/tools/llvm-nm/llvm-nm.cpp
+++ b/llvm/tools/llvm-nm/llvm-nm.cpp
@@ -31,6 +31,9 @@
#include "llvm/Object/TapiFile.h"
#include "llvm/Object/TapiUniversal.h"
#include "llvm/Object/Wasm.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/Option.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Format.h"
@@ -47,192 +50,86 @@ using namespace llvm;
using namespace object;
namespace {
-enum OutputFormatTy { bsd, sysv, posix, darwin };
-
-cl::OptionCategory NMCat("llvm-nm Options");
-
-cl::opt<OutputFormatTy> OutputFormat(
- "format", cl::desc("Specify output format"),
- cl::values(clEnumVal(bsd, "BSD format"), clEnumVal(sysv, "System V format"),
- clEnumVal(posix, "POSIX.2 format"),
- clEnumVal(darwin, "Darwin -m format")),
- cl::init(bsd), cl::cat(NMCat));
-cl::alias OutputFormat2("f", cl::desc("Alias for --format"),
- cl::aliasopt(OutputFormat));
-
-cl::list<std::string> InputFilenames(cl::Positional, cl::desc("<input files>"),
- cl::ZeroOrMore);
-
-cl::opt<bool> UndefinedOnly("undefined-only",
- cl::desc("Show only undefined symbols"),
- cl::cat(NMCat));
-cl::alias UndefinedOnly2("u", cl::desc("Alias for --undefined-only"),
- cl::aliasopt(UndefinedOnly), cl::Grouping);
-
-cl::opt<bool> DynamicSyms("dynamic",
- cl::desc("Display the dynamic symbols instead "
- "of normal symbols."),
- cl::cat(NMCat));
-cl::alias DynamicSyms2("D", cl::desc("Alias for --dynamic"),
- cl::aliasopt(DynamicSyms), cl::Grouping);
-
-cl::opt<bool> DefinedOnly("defined-only", cl::desc("Show only defined symbols"),
- cl::cat(NMCat));
-cl::alias DefinedOnly2("U", cl::desc("Alias for --defined-only"),
- cl::aliasopt(DefinedOnly), cl::Grouping);
-
-cl::opt<bool> ExternalOnly("extern-only",
- cl::desc("Show only external symbols"),
- cl::ZeroOrMore, cl::cat(NMCat));
-cl::alias ExternalOnly2("g", cl::desc("Alias for --extern-only"),
- cl::aliasopt(ExternalOnly), cl::Grouping,
- cl::ZeroOrMore);
-
-cl::opt<bool> NoWeakSymbols("no-weak", cl::desc("Show only non-weak symbols"),
- cl::cat(NMCat));
-cl::alias NoWeakSymbols2("W", cl::desc("Alias for --no-weak"),
- cl::aliasopt(NoWeakSymbols), cl::Grouping);
-
-cl::opt<bool> BSDFormat("B", cl::desc("Alias for --format=bsd"), cl::Grouping,
- cl::cat(NMCat));
-cl::opt<bool> POSIXFormat("P", cl::desc("Alias for --format=posix"),
- cl::Grouping, cl::cat(NMCat));
-cl::alias Portability("portability", cl::desc("Alias for --format=posix"),
- cl::aliasopt(POSIXFormat), cl::NotHidden);
-cl::opt<bool> DarwinFormat("m", cl::desc("Alias for --format=darwin"),
- cl::Grouping, cl::cat(NMCat));
-
-static cl::list<std::string>
- ArchFlags("arch", cl::desc("architecture(s) from a Mach-O file to dump"),
- cl::ZeroOrMore, cl::cat(NMCat));
-bool ArchAll = false;
-
-cl::opt<bool> PrintFileName(
- "print-file-name",
- cl::desc("Precede each symbol with the object file it came from"),
- cl::cat(NMCat));
-
-cl::alias PrintFileNameA("A", cl::desc("Alias for --print-file-name"),
- cl::aliasopt(PrintFileName), cl::Grouping);
-cl::alias PrintFileNameo("o", cl::desc("Alias for --print-file-name"),
- cl::aliasopt(PrintFileName), cl::Grouping);
-
-cl::opt<bool> DebugSyms("debug-syms",
- cl::desc("Show all symbols, even debugger only"),
- cl::cat(NMCat));
-cl::alias DebugSymsa("a", cl::desc("Alias for --debug-syms"),
- cl::aliasopt(DebugSyms), cl::Grouping);
-
-cl::opt<bool> NumericSort("numeric-sort", cl::desc("Sort symbols by address"),
- cl::cat(NMCat));
-cl::alias NumericSortn("n", cl::desc("Alias for --numeric-sort"),
- cl::aliasopt(NumericSort), cl::Grouping);
-cl::alias NumericSortv("v", cl::desc("Alias for --numeric-sort"),
- cl::aliasopt(NumericSort), cl::Grouping);
-
-cl::opt<bool> NoSort("no-sort", cl::desc("Show symbols in order encountered"),
- cl::cat(NMCat));
-cl::alias NoSortp("p", cl::desc("Alias for --no-sort"), cl::aliasopt(NoSort),
- cl::Grouping);
-
-cl::opt<bool> Demangle("demangle", cl::ZeroOrMore,
- cl::desc("Demangle C++ symbol names"), cl::cat(NMCat));
-cl::alias DemangleC("C", cl::desc("Alias for --demangle"),
- cl::aliasopt(Demangle), cl::Grouping);
-cl::opt<bool> NoDemangle("no-demangle", cl::init(false), cl::ZeroOrMore,
- cl::desc("Don't demangle symbol names"),
- cl::cat(NMCat));
-
-cl::opt<bool> ReverseSort("reverse-sort", cl::desc("Sort in reverse order"),
- cl::cat(NMCat));
-cl::alias ReverseSortr("r", cl::desc("Alias for --reverse-sort"),
- cl::aliasopt(ReverseSort), cl::Grouping);
-
-cl::opt<bool> PrintSize("print-size",
- cl::desc("Show symbol size as well as address"),
- cl::cat(NMCat));
-cl::alias PrintSizeS("S", cl::desc("Alias for --print-size"),
- cl::aliasopt(PrintSize), cl::Grouping);
-bool MachOPrintSizeWarning = false;
-
-cl::opt<bool> SizeSort("size-sort", cl::desc("Sort symbols by size"),
- cl::cat(NMCat));
-
-cl::opt<bool> WithoutAliases("without-aliases", cl::Hidden,
- cl::desc("Exclude aliases from output"),
- cl::cat(NMCat));
-
-cl::opt<bool> ArchiveMap("print-armap", cl::desc("Print the archive map"),
- cl::cat(NMCat));
-cl::alias ArchiveMaps("M", cl::desc("Alias for --print-armap"),
- cl::aliasopt(ArchiveMap), cl::Grouping);
+using namespace llvm::opt; // for HelpHidden in Opts.inc
+enum ID {
+ OPT_INVALID = 0, // This is not an option ID.
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ OPT_##ID,
+#include "Opts.inc"
+#undef OPTION
+};
+
+#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
+#include "Opts.inc"
+#undef PREFIX
+
+static const opt::OptTable::Info InfoTable[] = {
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ { \
+ PREFIX, NAME, HELPTEXT, \
+ METAVAR, OPT_##ID, opt::Option::KIND##Class, \
+ PARAM, FLAGS, OPT_##GROUP, \
+ OPT_##ALIAS, ALIASARGS, VALUES},
+#include "Opts.inc"
+#undef OPTION
+};
+
+class NmOptTable : public opt::OptTable {
+public:
+ NmOptTable() : OptTable(InfoTable) { setGroupedShortOptions(true); }
+};
+
+enum OutputFormatTy { bsd, sysv, posix, darwin, just_symbols };
+} // namespace
+static bool ArchiveMap;
+static bool DebugSyms;
+static bool DefinedOnly;
+static bool Demangle;
+static bool DynamicSyms;
+static bool ExternalOnly;
+static OutputFormatTy OutputFormat;
+static bool NoLLVMBitcode;
+static bool NoSort;
+static bool NoWeakSymbols;
+static bool NumericSort;
+static bool PrintFileName;
+static bool PrintSize;
+static bool Quiet;
+static bool ReverseSort;
+static bool SpecialSyms;
+static bool SizeSort;
+static bool UndefinedOnly;
+static bool WithoutAliases;
+
+namespace {
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")),
- cl::init(x), cl::cat(NMCat));
-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::cat(NMCat));
-cl::alias JustSymbolNames("j", cl::desc("Alias for --just-symbol-name"),
- cl::aliasopt(JustSymbolName), cl::Grouping);
-
-cl::opt<bool>
- SpecialSyms("special-syms",
- cl::desc("Do not filter special symbols from the output"),
- cl::cat(NMCat));
-
-cl::list<std::string> SegSect("s", cl::multi_val(2), cl::ZeroOrMore,
- cl::value_desc("segment section"), cl::Hidden,
- cl::desc("Dump only symbols from this segment "
- "and section name, Mach-O only"),
- cl::cat(NMCat));
-
-cl::opt<bool> FormatMachOasHex("x",
- cl::desc("Print symbol entry in hex, "
- "Mach-O only"),
- cl::Grouping, cl::cat(NMCat));
-cl::opt<bool> AddDyldInfo("add-dyldinfo",
- cl::desc("Add symbols from the dyldinfo not already "
- "in the symbol table, Mach-O only"),
- cl::cat(NMCat));
-cl::opt<bool> NoDyldInfo("no-dyldinfo",
- cl::desc("Don't add any symbols from the dyldinfo, "
- "Mach-O only"),
- cl::cat(NMCat));
-cl::opt<bool> DyldInfoOnly("dyldinfo-only",
- cl::desc("Show only symbols from the dyldinfo, "
- "Mach-O only"),
- cl::cat(NMCat));
-
-cl::opt<bool> NoLLVMBitcode("no-llvm-bc",
- cl::desc("Disable LLVM bitcode reader"),
- cl::cat(NMCat));
-
-cl::opt<bool> AddInlinedInfo("add-inlinedinfo",
- cl::desc("Add symbols from the inlined libraries, "
- "TBD(Mach-O) only"),
- cl::cat(NMCat));
-
-cl::extrahelp HelpResponse("\nPass @FILE as argument to read options from FILE.\n");
-
-bool PrintAddress = true;
-
-bool MultipleFiles = false;
-
-bool HadError = false;
-
-std::string ToolName;
-} // anonymous namespace
+} // namespace
+static Radix AddressRadix;
+
+// Mach-O specific options.
+static bool ArchAll = false;
+static std::vector<StringRef> ArchFlags;
+static bool AddDyldInfo;
+static bool AddInlinedInfo;
+static bool DyldInfoOnly;
+static bool FormatMachOasHex;
+static bool NoDyldInfo;
+static std::vector<StringRef> SegSect;
+static bool MachOPrintSizeWarning = false;
+
+// Miscellaneous states.
+static bool PrintAddress = true;
+static bool MultipleFiles = false;
+static bool HadError = false;
+
+static StringRef ToolName;
static void error(Twine Message, Twine Path = Twine()) {
HadError = true;
- WithColor::error(errs(), ToolName) << Path << ": " << Message << ".\n";
+ WithColor::error(errs(), ToolName) << Path << ": " << Message << "\n";
}
static bool error(std::error_code EC, Twine Path = Twine()) {
@@ -262,13 +159,13 @@ static void error(llvm::Error E, StringRef FileName, const Archive::Child &C,
errs() << "(" << NameOrErr.get() << ")";
if (!ArchitectureName.empty())
- errs() << " (for architecture " << ArchitectureName << ") ";
+ errs() << " (for architecture " << ArchitectureName << ")";
std::string Buf;
raw_string_ostream OS(Buf);
logAllUnhandledErrors(std::move(E), OS);
OS.flush();
- errs() << " " << Buf << "\n";
+ errs() << ": " << Buf << "\n";
}
// This version of error() prints the file name and which architecture slice it
@@ -281,13 +178,13 @@ static void error(llvm::Error E, StringRef FileName,
WithColor::error(errs(), ToolName) << FileName;
if (!ArchitectureName.empty())
- errs() << " (for architecture " << ArchitectureName << ") ";
+ errs() << " (for architecture " << ArchitectureName << ")";
std::string Buf;
raw_string_ostream OS(Buf);
logAllUnhandledErrors(std::move(E), OS);
OS.flush();
- errs() << " " << Buf << "\n";
+ errs() << ": " << Buf << "\n";
}
namespace {
@@ -737,16 +634,6 @@ static void writeFileName(raw_ostream &S, StringRef ArchiveName,
}
}
-static bool isSpecialSym(SymbolicFile &Obj, StringRef Name) {
- auto *ELFObj = dyn_cast<ELFObjectFileBase>(&Obj);
- if (!ELFObj)
- return false;
- uint16_t EMachine = ELFObj->getEMachine();
- if (EMachine != ELF::EM_ARM && EMachine != ELF::EM_AARCH64)
- return false;
- return !Name.empty() && Name[0] == '$';
-}
-
static void sortAndPrintSymbolList(SymbolicFile &Obj, bool printName,
StringRef ArchiveName,
StringRef ArchitectureName) {
@@ -769,10 +656,10 @@ static void sortAndPrintSymbolList(SymbolicFile &Obj, bool printName,
}
if (!PrintFileName) {
- if (OutputFormat == posix && MultipleFiles && printName) {
+ if ((OutputFormat == bsd || OutputFormat == posix ||
+ OutputFormat == just_symbols) &&
+ MultipleFiles && printName) {
outs() << '\n' << CurrentFilename << ":\n";
- } else if (OutputFormat == bsd && MultipleFiles && printName) {
- outs() << "\n" << CurrentFilename << ":\n";
} else if (OutputFormat == sysv) {
outs() << "\n\nSymbols from " << CurrentFilename << ":\n\n";
if (isSymbolList64Bit(Obj))
@@ -835,13 +722,14 @@ static void sortAndPrintSymbolList(SymbolicFile &Obj, bool printName,
bool Undefined = SymFlags & SymbolRef::SF_Undefined;
bool Global = SymFlags & SymbolRef::SF_Global;
bool Weak = SymFlags & SymbolRef::SF_Weak;
+ bool FormatSpecific = SymFlags & SymbolRef::SF_FormatSpecific;
if ((!Undefined && UndefinedOnly) || (Undefined && DefinedOnly) ||
(!Global && ExternalOnly) || (Weak && NoWeakSymbols) ||
- (!SpecialSyms && isSpecialSym(Obj, Name)))
+ (FormatSpecific && !(SpecialSyms || DebugSyms)))
continue;
if (PrintFileName)
writeFileName(outs(), ArchiveName, ArchitectureName);
- if ((JustSymbolName ||
+ if ((OutputFormat == just_symbols ||
(UndefinedOnly && MachO && OutputFormat != darwin)) &&
OutputFormat != posix) {
outs() << Name << "\n";
@@ -1142,13 +1030,16 @@ static char getNMSectionTagAndName(SymbolicFile &Obj, basic_symbol_iterator I,
}
}
- if ((Symflags & object::SymbolRef::SF_Weak) && !isa<MachOObjectFile>(Obj)) {
- char Ret = isObject(Obj, I) ? 'v' : 'w';
- return (!(Symflags & object::SymbolRef::SF_Undefined)) ? toupper(Ret) : Ret;
+ if (Symflags & object::SymbolRef::SF_Undefined) {
+ if (isa<MachOObjectFile>(Obj) || !(Symflags & object::SymbolRef::SF_Weak))
+ return 'U';
+ return isObject(Obj, I) ? 'v' : 'w';
}
-
- if (Symflags & object::SymbolRef::SF_Undefined)
- return 'U';
+ if (isa<ELFObjectFileBase>(&Obj))
+ if (ELFSymbolRef(*I).getELFType() == ELF::STT_GNU_IFUNC)
+ return 'i';
+ if (!isa<MachOObjectFile>(Obj) && (Symflags & object::SymbolRef::SF_Weak))
+ return isObject(Obj, I) ? 'V' : 'W';
if (Symflags & object::SymbolRef::SF_Common)
return 'C';
@@ -1169,8 +1060,6 @@ static char getNMSectionTagAndName(SymbolicFile &Obj, basic_symbol_iterator I,
else if (TapiFile *Tapi = dyn_cast<TapiFile>(&Obj))
Ret = getSymbolNMTypeChar(*Tapi, I);
else if (ELFObjectFileBase *ELF = dyn_cast<ELFObjectFileBase>(&Obj)) {
- if (ELFSymbolRef(*I).getELFType() == ELF::STT_GNU_IFUNC)
- return 'i';
Ret = getSymbolNMTypeChar(*ELF, I);
if (ELFSymbolRef(*I).getBinding() == ELF::STB_GNU_UNIQUE)
return Ret;
@@ -1806,7 +1695,14 @@ static void dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName,
error(SymFlagsOrErr.takeError(), Obj.getFileName());
return;
}
- if (!DebugSyms && (*SymFlagsOrErr & SymbolRef::SF_FormatSpecific))
+
+ // Don't drop format specifc symbols for ARM and AArch64 ELF targets, they
+ // are used to repesent mapping symbols and needed to honor the
+ // --special-syms option.
+ auto *ELFObj = dyn_cast<ELFObjectFileBase>(&Obj);
+ if ((!ELFObj || (ELFObj->getEMachine() != ELF::EM_ARM &&
+ ELFObj->getEMachine() != ELF::EM_AARCH64)) &&
+ !DebugSyms && (*SymFlagsOrErr & SymbolRef::SF_FormatSpecific))
continue;
if (WithoutAliases && (*SymFlagsOrErr & SymbolRef::SF_Indirect))
continue;
@@ -1860,7 +1756,7 @@ static void dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName,
CurrentFilename = Obj.getFileName();
- if (Symbols.empty() && SymbolList.empty()) {
+ if (Symbols.empty() && SymbolList.empty() && !Quiet) {
writeFileName(errs(), ArchiveName, ArchitectureName);
errs() << "no symbols\n";
}
@@ -2228,8 +2124,80 @@ static void dumpSymbolNamesFromFile(std::string &Filename) {
int main(int argc, char **argv) {
InitLLVM X(argc, argv);
- cl::HideUnrelatedOptions(NMCat);
- cl::ParseCommandLineOptions(argc, argv, "llvm symbol table dumper\n");
+ BumpPtrAllocator A;
+ StringSaver Saver(A);
+ NmOptTable Tbl;
+ ToolName = argv[0];
+ opt::InputArgList Args =
+ Tbl.parseArgs(argc, argv, OPT_UNKNOWN, Saver, [&](StringRef Msg) {
+ error(Msg);
+ exit(1);
+ });
+ if (Args.hasArg(OPT_help)) {
+ Tbl.printHelp(
+ outs(),
+ (Twine(ToolName) + " [options] <input object files>").str().c_str(),
+ "LLVM symbol table dumper");
+ // TODO Replace this with OptTable API once it adds extrahelp support.
+ outs() << "\nPass @FILE as argument to read options from FILE.\n";
+ return 0;
+ }
+ if (Args.hasArg(OPT_version)) {
+ // This needs to contain the word "GNU", libtool looks for that string.
+ outs() << "llvm-nm, compatible with GNU nm" << '\n';
+ cl::PrintVersionMessage();
+ return 0;
+ }
+
+ DebugSyms = Args.hasArg(OPT_debug_syms);
+ DefinedOnly = Args.hasArg(OPT_defined_only);
+ Demangle = Args.hasFlag(OPT_demangle, OPT_no_demangle, false);
+ DynamicSyms = Args.hasArg(OPT_dynamic);
+ ExternalOnly = Args.hasArg(OPT_extern_only);
+ StringRef V = Args.getLastArgValue(OPT_format_EQ, "bsd");
+ if (V == "bsd")
+ OutputFormat = bsd;
+ else if (V == "posix")
+ OutputFormat = posix;
+ else if (V == "sysv")
+ OutputFormat = sysv;
+ else if (V == "darwin")
+ OutputFormat = darwin;
+ else if (V == "just-symbols")
+ OutputFormat = just_symbols;
+ else
+ error("--format value should be one of: bsd, posix, sysv, darwin, "
+ "just-symbols");
+ NoLLVMBitcode = Args.hasArg(OPT_no_llvm_bc);
+ NoSort = Args.hasArg(OPT_no_sort);
+ NoWeakSymbols = Args.hasArg(OPT_no_weak);
+ NumericSort = Args.hasArg(OPT_numeric_sort);
+ ArchiveMap = Args.hasArg(OPT_print_armap);
+ PrintFileName = Args.hasArg(OPT_print_file_name);
+ PrintSize = Args.hasArg(OPT_print_size);
+ ReverseSort = Args.hasArg(OPT_reverse_sort);
+ Quiet = Args.hasArg(OPT_quiet);
+ V = Args.getLastArgValue(OPT_radix_EQ, "x");
+ if (V == "o")
+ AddressRadix = Radix::o;
+ else if (V == "d")
+ AddressRadix = Radix::d;
+ else if (V == "x")
+ AddressRadix = Radix::x;
+ else
+ error("--radix value should be one of: 'o' (octal), 'd' (decimal), 'x' "
+ "(hexadecimal)");
+ SizeSort = Args.hasArg(OPT_size_sort);
+ SpecialSyms = Args.hasArg(OPT_special_syms);
+ UndefinedOnly = Args.hasArg(OPT_undefined_only);
+ WithoutAliases = Args.hasArg(OPT_without_aliases);
+
+ // Mach-O specific options.
+ FormatMachOasHex = Args.hasArg(OPT_x);
+ AddDyldInfo = Args.hasArg(OPT_add_dyldinfo);
+ AddInlinedInfo = Args.hasArg(OPT_add_inlinedinfo);
+ DyldInfoOnly = Args.hasArg(OPT_dyldinfo_only);
+ NoDyldInfo = Args.hasArg(OPT_no_dyldinfo);
// llvm-nm only reads binary files.
if (error(sys::ChangeStdinToBinary()))
@@ -2240,14 +2208,6 @@ int main(int argc, char **argv) {
llvm::InitializeAllTargetMCs();
llvm::InitializeAllAsmParsers();
- ToolName = argv[0];
- if (BSDFormat)
- OutputFormat = bsd;
- if (POSIXFormat)
- OutputFormat = posix;
- if (DarwinFormat)
- OutputFormat = darwin;
-
// The relative order of these is important. If you pass --size-sort it should
// only print out the size. However, if you pass -S --size-sort, it should
// print out both the size and address.
@@ -2255,29 +2215,44 @@ int main(int argc, char **argv) {
PrintAddress = false;
if (OutputFormat == sysv || SizeSort)
PrintSize = true;
- if (InputFilenames.empty())
- InputFilenames.push_back("a.out");
- if (InputFilenames.size() > 1)
- MultipleFiles = true;
-
- // If both --demangle and --no-demangle are specified then pick the last one.
- if (NoDemangle.getPosition() > Demangle.getPosition())
- Demangle = !NoDemangle;
- for (unsigned i = 0; i < ArchFlags.size(); ++i) {
- if (ArchFlags[i] == "all") {
- ArchAll = true;
- } else {
- if (!MachOObjectFile::isValidArch(ArchFlags[i]))
- error("Unknown architecture named '" + ArchFlags[i] + "'",
+ for (const auto *A : Args.filtered(OPT_arch_EQ)) {
+ SmallVector<StringRef, 2> Values;
+ llvm::SplitString(A->getValue(), Values, ",");
+ for (StringRef V : Values) {
+ if (V == "all")
+ ArchAll = true;
+ else if (MachOObjectFile::isValidArch(V))
+ ArchFlags.push_back(V);
+ else
+ error("Unknown architecture named '" + V + "'",
"for the --arch option");
}
}
+ // Mach-O takes -s to accept two arguments. We emulate this by iterating over
+ // both OPT_s and OPT_INPUT.
+ std::vector<std::string> InputFilenames;
+ int SegSectArgs = 0;
+ for (opt::Arg *A : Args.filtered(OPT_s, OPT_INPUT)) {
+ if (SegSectArgs > 0) {
+ --SegSectArgs;
+ SegSect.push_back(A->getValue());
+ } else if (A->getOption().matches(OPT_s)) {
+ SegSectArgs = 2;
+ } else {
+ InputFilenames.push_back(A->getValue());
+ }
+ }
if (!SegSect.empty() && SegSect.size() != 2)
error("bad number of arguments (must be two arguments)",
"for the -s option");
+ if (InputFilenames.empty())
+ InputFilenames.push_back("a.out");
+ if (InputFilenames.size() > 1)
+ MultipleFiles = true;
+
if (NoDyldInfo && (AddDyldInfo || DyldInfoOnly))
error("--no-dyldinfo can't be used with --add-dyldinfo or --dyldinfo-only");
diff --git a/llvm/tools/llvm-objcopy/Buffer.cpp b/llvm/tools/llvm-objcopy/Buffer.cpp
deleted file mode 100644
index 06b2a20a762f..000000000000
--- a/llvm/tools/llvm-objcopy/Buffer.cpp
+++ /dev/null
@@ -1,79 +0,0 @@
-//===- Buffer.cpp ---------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "Buffer.h"
-#include "llvm/Support/FileOutputBuffer.h"
-#include "llvm/Support/FileSystem.h"
-#include "llvm/Support/MemoryBuffer.h"
-#include "llvm/Support/Process.h"
-#include <memory>
-
-namespace llvm {
-namespace objcopy {
-
-Buffer::~Buffer() {}
-
-static Error createEmptyFile(StringRef FileName) {
- // Create an empty tempfile and atomically swap it in place with the desired
- // output file.
- Expected<sys::fs::TempFile> Temp =
- sys::fs::TempFile::create(FileName + ".temp-empty-%%%%%%%");
- return Temp ? Temp->keep(FileName) : Temp.takeError();
-}
-
-Error FileBuffer::allocate(size_t Size) {
- // When a 0-sized file is requested, skip allocation but defer file
- // creation/truncation until commit() to avoid side effects if something
- // happens between allocate() and commit().
- if (Size == 0) {
- EmptyFile = true;
- return Error::success();
- }
-
- Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr =
- FileOutputBuffer::create(getName(), Size, FileOutputBuffer::F_executable);
- // FileOutputBuffer::create() returns an Error that is just a wrapper around
- // std::error_code. Wrap it in FileError to include the actual filename.
- if (!BufferOrErr)
- return createFileError(getName(), BufferOrErr.takeError());
- Buf = std::move(*BufferOrErr);
- return Error::success();
-}
-
-Error FileBuffer::commit() {
- if (EmptyFile)
- return createEmptyFile(getName());
-
- assert(Buf && "allocate() not called before commit()!");
- Error Err = Buf->commit();
- // FileOutputBuffer::commit() returns an Error that is just a wrapper around
- // std::error_code. Wrap it in FileError to include the actual filename.
- return Err ? createFileError(getName(), std::move(Err)) : std::move(Err);
-}
-
-uint8_t *FileBuffer::getBufferStart() {
- return reinterpret_cast<uint8_t *>(Buf->getBufferStart());
-}
-
-Error MemBuffer::allocate(size_t Size) {
- Buf = WritableMemoryBuffer::getNewMemBuffer(Size, getName());
- return Error::success();
-}
-
-Error MemBuffer::commit() { return Error::success(); }
-
-uint8_t *MemBuffer::getBufferStart() {
- return reinterpret_cast<uint8_t *>(Buf->getBufferStart());
-}
-
-std::unique_ptr<WritableMemoryBuffer> MemBuffer::releaseMemoryBuffer() {
- return std::move(Buf);
-}
-
-} // end namespace objcopy
-} // end namespace llvm
diff --git a/llvm/tools/llvm-objcopy/Buffer.h b/llvm/tools/llvm-objcopy/Buffer.h
deleted file mode 100644
index 487d5585c364..000000000000
--- a/llvm/tools/llvm-objcopy/Buffer.h
+++ /dev/null
@@ -1,68 +0,0 @@
-//===- Buffer.h -------------------------------------------------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_TOOLS_OBJCOPY_BUFFER_H
-#define LLVM_TOOLS_OBJCOPY_BUFFER_H
-
-#include "llvm/ADT/StringRef.h"
-#include "llvm/Support/FileOutputBuffer.h"
-#include "llvm/Support/MemoryBuffer.h"
-#include <memory>
-
-namespace llvm {
-namespace objcopy {
-
-// The class Buffer abstracts out the common interface of FileOutputBuffer and
-// WritableMemoryBuffer so that the hierarchy of Writers depends on this
-// abstract interface and doesn't depend on a particular implementation.
-// TODO: refactor the buffer classes in LLVM to enable us to use them here
-// directly.
-class Buffer {
- StringRef Name;
-
-public:
- virtual ~Buffer();
- virtual Error allocate(size_t Size) = 0;
- virtual uint8_t *getBufferStart() = 0;
- virtual Error commit() = 0;
-
- explicit Buffer(StringRef Name) : Name(Name) {}
- StringRef getName() const { return Name; }
-};
-
-class FileBuffer : public Buffer {
- std::unique_ptr<FileOutputBuffer> Buf;
- // Indicates that allocate(0) was called, and commit() should create or
- // truncate a file instead of using a FileOutputBuffer.
- bool EmptyFile = false;
-
-public:
- Error allocate(size_t Size) override;
- uint8_t *getBufferStart() override;
- Error commit() override;
-
- explicit FileBuffer(StringRef FileName) : Buffer(FileName) {}
-};
-
-class MemBuffer : public Buffer {
- std::unique_ptr<WritableMemoryBuffer> Buf;
-
-public:
- Error allocate(size_t Size) override;
- uint8_t *getBufferStart() override;
- Error commit() override;
-
- explicit MemBuffer(StringRef Name) : Buffer(Name) {}
-
- std::unique_ptr<WritableMemoryBuffer> releaseMemoryBuffer();
-};
-
-} // end namespace objcopy
-} // end namespace llvm
-
-#endif // LLVM_TOOLS_OBJCOPY_BUFFER_H
diff --git a/llvm/tools/llvm-objcopy/COFF/COFFConfig.h b/llvm/tools/llvm-objcopy/COFF/COFFConfig.h
new file mode 100644
index 000000000000..3897ff47724b
--- /dev/null
+++ b/llvm/tools/llvm-objcopy/COFF/COFFConfig.h
@@ -0,0 +1,21 @@
+//===- COFFConfig.h ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_OBJCOPY_COFF_COFFCONFIG_H
+#define LLVM_TOOLS_LLVM_OBJCOPY_COFF_COFFCONFIG_H
+
+namespace llvm {
+namespace objcopy {
+
+// Coff specific configuration for copying/stripping a single file.
+struct COFFConfig {};
+
+} // namespace objcopy
+} // namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_OBJCOPY_COFF_COFFCONFIG_H
diff --git a/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp b/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
index b5de8a45a80f..e50ac2e12e2f 100644
--- a/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
+++ b/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
@@ -7,8 +7,8 @@
//===----------------------------------------------------------------------===//
#include "COFFObjcopy.h"
-#include "Buffer.h"
-#include "CopyConfig.h"
+#include "COFFConfig.h"
+#include "CommonConfig.h"
#include "Object.h"
#include "Reader.h"
#include "Writer.h"
@@ -131,7 +131,7 @@ static void setSectionFlags(Section &Sec, SectionFlag AllFlags) {
Sec.Header.Characteristics = NewCharacteristics;
}
-static Error handleArgs(const CopyConfig &Config, Object &Obj) {
+static Error handleArgs(const CommonConfig &Config, Object &Obj) {
// Perform the actual section removals.
Obj.removeSections([&Config](const Section &Sec) {
// Contrary to --only-keep-debug, --only-section fully removes sections that
@@ -249,30 +249,11 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj) {
if (Error E = addGnuDebugLink(Obj, Config.AddGnuDebugLink))
return E;
- if (Config.AllowBrokenLinks || !Config.BuildIdLinkDir.empty() ||
- Config.BuildIdLinkInput || Config.BuildIdLinkOutput ||
- !Config.SplitDWO.empty() || !Config.SymbolsPrefix.empty() ||
- !Config.AllocSectionsPrefix.empty() || !Config.DumpSection.empty() ||
- !Config.KeepSection.empty() || Config.NewSymbolVisibility ||
- !Config.SymbolsToGlobalize.empty() || !Config.SymbolsToKeep.empty() ||
- !Config.SymbolsToLocalize.empty() || !Config.SymbolsToWeaken.empty() ||
- !Config.SymbolsToKeepGlobal.empty() || !Config.SectionsToRename.empty() ||
- !Config.SetSectionAlignment.empty() || Config.ExtractDWO ||
- Config.LocalizeHidden || Config.PreserveDates || Config.StripDWO ||
- Config.StripNonAlloc || Config.StripSections ||
- Config.StripSwiftSymbols || Config.Weaken ||
- Config.DecompressDebugSections ||
- Config.DiscardMode == DiscardType::Locals ||
- !Config.SymbolsToAdd.empty() || Config.EntryExpr) {
- return createStringError(llvm::errc::invalid_argument,
- "option not supported by llvm-objcopy for COFF");
- }
-
return Error::success();
}
-Error executeObjcopyOnBinary(const CopyConfig &Config, COFFObjectFile &In,
- Buffer &Out) {
+Error executeObjcopyOnBinary(const CommonConfig &Config, const COFFConfig &,
+ COFFObjectFile &In, raw_ostream &Out) {
COFFReader Reader(In);
Expected<std::unique_ptr<Object>> ObjOrErr = Reader.create();
if (!ObjOrErr)
diff --git a/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.h b/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.h
index 858759e52c4a..2c7ccd34653d 100644
--- a/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.h
+++ b/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.h
@@ -11,18 +11,20 @@
namespace llvm {
class Error;
+class raw_ostream;
namespace object {
class COFFObjectFile;
} // end namespace object
namespace objcopy {
-struct CopyConfig;
-class Buffer;
+struct CommonConfig;
+struct COFFConfig;
namespace coff {
-Error executeObjcopyOnBinary(const CopyConfig &Config,
- object::COFFObjectFile &In, Buffer &Out);
+
+Error executeObjcopyOnBinary(const CommonConfig &Config, const COFFConfig &,
+ object::COFFObjectFile &In, raw_ostream &Out);
} // end namespace coff
} // end namespace objcopy
diff --git a/llvm/tools/llvm-objcopy/COFF/Reader.h b/llvm/tools/llvm-objcopy/COFF/Reader.h
index ec15369db0b8..48c050b6ea11 100644
--- a/llvm/tools/llvm-objcopy/COFF/Reader.h
+++ b/llvm/tools/llvm-objcopy/COFF/Reader.h
@@ -9,7 +9,6 @@
#ifndef LLVM_TOOLS_OBJCOPY_COFF_READER_H
#define LLVM_TOOLS_OBJCOPY_COFF_READER_H
-#include "Buffer.h"
#include "llvm/BinaryFormat/COFF.h"
#include "llvm/Object/COFF.h"
#include "llvm/Support/Error.h"
diff --git a/llvm/tools/llvm-objcopy/COFF/Writer.cpp b/llvm/tools/llvm-objcopy/COFF/Writer.cpp
index 6b560890a4c1..e7be64faab65 100644
--- a/llvm/tools/llvm-objcopy/COFF/Writer.cpp
+++ b/llvm/tools/llvm-objcopy/COFF/Writer.cpp
@@ -12,6 +12,7 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/BinaryFormat/COFF.h"
#include "llvm/Object/COFF.h"
+#include "llvm/Support/Errc.h"
#include "llvm/Support/ErrorHandling.h"
#include <cstddef>
#include <cstdint>
@@ -240,7 +241,7 @@ Error COFFWriter::finalize(bool IsBigObj) {
}
void COFFWriter::writeHeaders(bool IsBigObj) {
- uint8_t *Ptr = Buf.getBufferStart();
+ uint8_t *Ptr = reinterpret_cast<uint8_t *>(Buf->getBufferStart());
if (Obj.IsPE) {
memcpy(Ptr, &Obj.DosHeader, sizeof(Obj.DosHeader));
Ptr += sizeof(Obj.DosHeader);
@@ -302,7 +303,8 @@ void COFFWriter::writeHeaders(bool IsBigObj) {
void COFFWriter::writeSections() {
for (const auto &S : Obj.getSections()) {
- uint8_t *Ptr = Buf.getBufferStart() + S.Header.PointerToRawData;
+ uint8_t *Ptr = reinterpret_cast<uint8_t *>(Buf->getBufferStart()) +
+ S.Header.PointerToRawData;
ArrayRef<uint8_t> Contents = S.getContents();
std::copy(Contents.begin(), Contents.end(), Ptr);
@@ -331,7 +333,8 @@ void COFFWriter::writeSections() {
}
template <class SymbolTy> void COFFWriter::writeSymbolStringTables() {
- uint8_t *Ptr = Buf.getBufferStart() + Obj.CoffFileHeader.PointerToSymbolTable;
+ uint8_t *Ptr = reinterpret_cast<uint8_t *>(Buf->getBufferStart()) +
+ Obj.CoffFileHeader.PointerToSymbolTable;
for (const auto &S : Obj.getSymbols()) {
// Convert symbols back to the right size, from coff_symbol32.
copySymbol<SymbolTy, coff_symbol32>(*reinterpret_cast<SymbolTy *>(Ptr),
@@ -366,8 +369,11 @@ Error COFFWriter::write(bool IsBigObj) {
if (Error E = finalize(IsBigObj))
return E;
- if (Error E = Buf.allocate(FileSize))
- return E;
+ Buf = WritableMemoryBuffer::getNewMemBuffer(FileSize);
+ if (!Buf)
+ return createStringError(llvm::errc::not_enough_memory,
+ "failed to allocate memory buffer of " +
+ Twine::utohexstr(FileSize) + " bytes.");
writeHeaders(IsBigObj);
writeSections();
@@ -380,7 +386,10 @@ Error COFFWriter::write(bool IsBigObj) {
if (Error E = patchDebugDirectory())
return E;
- return Buf.commit();
+ // TODO: Implement direct writing to the output stream (without intermediate
+ // memory buffer Buf).
+ Out.write(Buf->getBufferStart(), Buf->getBufferSize());
+ return Error::success();
}
Expected<uint32_t> COFFWriter::virtualAddressToFileAddress(uint32_t RVA) {
@@ -412,7 +421,8 @@ Error COFFWriter::patchDebugDirectory() {
"debug directory extends past end of section");
size_t Offset = Dir->RelativeVirtualAddress - S.Header.VirtualAddress;
- uint8_t *Ptr = Buf.getBufferStart() + S.Header.PointerToRawData + Offset;
+ uint8_t *Ptr = reinterpret_cast<uint8_t *>(Buf->getBufferStart()) +
+ S.Header.PointerToRawData + Offset;
uint8_t *End = Ptr + Dir->Size;
while (Ptr < End) {
debug_directory *Debug = reinterpret_cast<debug_directory *>(Ptr);
diff --git a/llvm/tools/llvm-objcopy/COFF/Writer.h b/llvm/tools/llvm-objcopy/COFF/Writer.h
index 3c0bdcbd5d6f..eed43b3e5814 100644
--- a/llvm/tools/llvm-objcopy/COFF/Writer.h
+++ b/llvm/tools/llvm-objcopy/COFF/Writer.h
@@ -9,9 +9,9 @@
#ifndef LLVM_TOOLS_OBJCOPY_COFF_WRITER_H
#define LLVM_TOOLS_OBJCOPY_COFF_WRITER_H
-#include "Buffer.h"
#include "llvm/MC/StringTableBuilder.h"
#include "llvm/Support/Error.h"
+#include "llvm/Support/MemoryBuffer.h"
#include <cstddef>
#include <utility>
@@ -23,7 +23,8 @@ struct Object;
class COFFWriter {
Object &Obj;
- Buffer &Buf;
+ std::unique_ptr<WritableMemoryBuffer> Buf;
+ raw_ostream &Out;
size_t FileSize;
size_t FileAlignment;
@@ -51,8 +52,8 @@ public:
virtual ~COFFWriter() {}
Error write();
- COFFWriter(Object &Obj, Buffer &Buf)
- : Obj(Obj), Buf(Buf), StrTabBuilder(StringTableBuilder::WinCOFF) {}
+ COFFWriter(Object &Obj, raw_ostream &Out)
+ : Obj(Obj), Out(Out), StrTabBuilder(StringTableBuilder::WinCOFF) {}
};
} // end namespace coff
diff --git a/llvm/tools/llvm-objcopy/CopyConfig.h b/llvm/tools/llvm-objcopy/CommonConfig.h
index 07eac9d2bb1b..131ce5c59114 100644
--- a/llvm/tools/llvm-objcopy/CopyConfig.h
+++ b/llvm/tools/llvm-objcopy/CommonConfig.h
@@ -1,4 +1,4 @@
-//===- CopyConfig.h -------------------------------------------------------===//
+//===- CommonConfig.h -------------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -6,20 +6,17 @@
//
//===----------------------------------------------------------------------===//
-#ifndef LLVM_TOOLS_LLVM_OBJCOPY_COPY_CONFIG_H
-#define LLVM_TOOLS_LLVM_OBJCOPY_COPY_CONFIG_H
+#ifndef LLVM_TOOLS_LLVM_OBJCOPY_COMMONCONFIG_H
+#define LLVM_TOOLS_LLVM_OBJCOPY_COMMONCONFIG_H
-#include "ELF/ELFConfig.h"
#include "llvm/ADT/ArrayRef.h"
-#include "llvm/ADT/BitmaskEnum.h"
+#include "llvm/ADT/CachedHashString.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Object/ELFTypes.h"
-#include "llvm/Support/Allocator.h"
-#include "llvm/Support/Error.h"
#include "llvm/Support/GlobPattern.h"
#include "llvm/Support/Regex.h"
// Necessary for llvm::DebugCompressionType::None
@@ -99,7 +96,7 @@ enum class MatchStyle {
class NameOrPattern {
StringRef Name;
- // Regex is shared between multiple CopyConfig instances.
+ // Regex is shared between multiple CommonConfig instances.
std::shared_ptr<Regex> R;
std::shared_ptr<GlobPattern> G;
bool IsPositiveMatch = true;
@@ -117,6 +114,11 @@ public:
llvm::function_ref<Error(Error)> ErrorCallback);
bool isPositiveMatch() const { return IsPositiveMatch; }
+ Optional<StringRef> getName() const {
+ if (!R && !G)
+ return Name;
+ return None;
+ }
bool operator==(StringRef S) const {
return R ? R->match(S) : G ? G->match(S) : Name == S;
}
@@ -126,30 +128,66 @@ public:
// Matcher that checks symbol or section names against the command line flags
// provided for that option.
class NameMatcher {
- std::vector<NameOrPattern> PosMatchers;
+ DenseSet<CachedHashStringRef> PosNames;
+ std::vector<NameOrPattern> PosPatterns;
std::vector<NameOrPattern> NegMatchers;
public:
Error addMatcher(Expected<NameOrPattern> Matcher) {
if (!Matcher)
return Matcher.takeError();
- if (Matcher->isPositiveMatch())
- PosMatchers.push_back(std::move(*Matcher));
- else
+ if (Matcher->isPositiveMatch()) {
+ if (Optional<StringRef> MaybeName = Matcher->getName())
+ PosNames.insert(CachedHashStringRef(*MaybeName));
+ else
+ PosPatterns.push_back(std::move(*Matcher));
+ } else {
NegMatchers.push_back(std::move(*Matcher));
+ }
return Error::success();
}
bool matches(StringRef S) const {
- return is_contained(PosMatchers, S) && !is_contained(NegMatchers, S);
+ return (PosNames.contains(CachedHashStringRef(S)) ||
+ is_contained(PosPatterns, S)) &&
+ !is_contained(NegMatchers, S);
+ }
+ bool empty() const {
+ return PosNames.empty() && PosPatterns.empty() && NegMatchers.empty();
}
- bool empty() const { return PosMatchers.empty() && NegMatchers.empty(); }
};
-// Configuration for copying/stripping a single file.
-struct CopyConfig {
- // Format-specific options to be initialized lazily when needed.
- Optional<elf::ELFCopyConfig> ELF;
+enum class SymbolFlag {
+ Global,
+ Local,
+ Weak,
+ Default,
+ Hidden,
+ Protected,
+ File,
+ Section,
+ Object,
+ Function,
+ IndirectFunction,
+ Debug,
+ Constructor,
+ Warning,
+ Indirect,
+ Synthetic,
+ UniqueObject,
+};
+
+// Symbol info specified by --add-symbol option. Symbol flags not supported
+// by a concrete format should be ignored.
+struct NewSymbolInfo {
+ StringRef SymbolName;
+ StringRef SectionName;
+ uint64_t Value = 0;
+ std::vector<SymbolFlag> Flags;
+ std::vector<StringRef> BeforeSyms;
+};
+// Configuration for copying/stripping a single file.
+struct CommonConfig {
// Main input/output options
StringRef InputFilename;
FileFormat InputFormat = FileFormat::Unspecified;
@@ -163,20 +201,15 @@ struct CopyConfig {
StringRef AddGnuDebugLink;
// Cached gnu_debuglink's target CRC
uint32_t GnuDebugLinkCRC32;
- StringRef BuildIdLinkDir;
- Optional<StringRef> BuildIdLinkInput;
- Optional<StringRef> BuildIdLinkOutput;
Optional<StringRef> ExtractPartition;
StringRef SplitDWO;
StringRef SymbolsPrefix;
StringRef AllocSectionsPrefix;
DiscardType DiscardMode = DiscardType::None;
- Optional<StringRef> NewSymbolVisibility;
// Repeated options
std::vector<StringRef> AddSection;
std::vector<StringRef> DumpSection;
- std::vector<StringRef> SymbolsToAdd;
std::vector<StringRef> RPathToAdd;
std::vector<StringRef> RPathToPrepend;
DenseMap<StringRef, StringRef> RPathsToUpdate;
@@ -212,12 +245,16 @@ struct CopyConfig {
// --change-start is used.
std::function<uint64_t(uint64_t)> EntryExpr;
+ // Symbol info specified by --add-symbol option.
+ std::vector<NewSymbolInfo> SymbolsToAdd;
+
// Boolean options
bool AllowBrokenLinks = false;
bool DeterministicArchives = true;
bool ExtractDWO = false;
bool ExtractMainPartition = false;
bool KeepFileSymbols = false;
+ bool KeepUndefined = false;
bool LocalizeHidden = false;
bool OnlyKeepDebug = false;
bool PreserveDates = false;
@@ -235,55 +272,9 @@ struct CopyConfig {
bool RemoveAllRpaths = false;
DebugCompressionType CompressionType = DebugCompressionType::None;
-
- // parseELFConfig performs ELF-specific command-line parsing. Fills `ELF` on
- // success or returns an Error otherwise.
- Error parseELFConfig() {
- if (!ELF) {
- Expected<elf::ELFCopyConfig> ELFConfig = elf::parseConfig(*this);
- if (!ELFConfig)
- return ELFConfig.takeError();
- ELF = *ELFConfig;
- }
- return Error::success();
- }
-};
-
-// Configuration for the overall invocation of this tool. When invoked as
-// objcopy, will always contain exactly one CopyConfig. When invoked as strip,
-// will contain one or more CopyConfigs.
-struct DriverConfig {
- SmallVector<CopyConfig, 1> CopyConfigs;
- BumpPtrAllocator Alloc;
};
-// ParseObjcopyOptions returns the config and sets the input arguments. If a
-// help flag is set then ParseObjcopyOptions will print the help messege and
-// exit. ErrorCallback is used to handle recoverable errors. An Error returned
-// by the callback aborts the parsing and is then returned by this function.
-Expected<DriverConfig>
-parseObjcopyOptions(ArrayRef<const char *> ArgsArr,
- llvm::function_ref<Error(Error)> ErrorCallback);
-
-// ParseInstallNameToolOptions returns the config and sets the input arguments.
-// If a help flag is set then ParseInstallNameToolOptions will print the help
-// messege and exit.
-Expected<DriverConfig>
-parseInstallNameToolOptions(ArrayRef<const char *> ArgsArr);
-
-// ParseBitcodeStripOptions returns the config and sets the input arguments.
-// If a help flag is set then ParseBitcodeStripOptions will print the help
-// messege and exit.
-Expected<DriverConfig> parseBitcodeStripOptions(ArrayRef<const char *> ArgsArr);
-
-// ParseStripOptions returns the config and sets the input arguments. If a
-// help flag is set then ParseStripOptions will print the help messege and
-// exit. ErrorCallback is used to handle recoverable errors. An Error returned
-// by the callback aborts the parsing and is then returned by this function.
-Expected<DriverConfig>
-parseStripOptions(ArrayRef<const char *> ArgsArr,
- llvm::function_ref<Error(Error)> ErrorCallback);
} // namespace objcopy
} // namespace llvm
-#endif
+#endif // LLVM_TOOLS_LLVM_OBJCOPY_COMMONCONFIG_H
diff --git a/llvm/tools/llvm-objcopy/CommonOpts.td b/llvm/tools/llvm-objcopy/CommonOpts.td
index 6481d1d1df05..4222532a1a38 100644
--- a/llvm/tools/llvm-objcopy/CommonOpts.td
+++ b/llvm/tools/llvm-objcopy/CommonOpts.td
@@ -84,6 +84,9 @@ def K : JoinedOrSeparate<["-"], "K">,
def keep_file_symbols : Flag<["--"], "keep-file-symbols">,
HelpText<"Do not remove file symbols">;
+def keep_undefined : Flag<["--"], "keep-undefined">,
+ HelpText<"Do not remove undefined symbols">;
+
def only_keep_debug
: Flag<["--"], "only-keep-debug">,
HelpText<
@@ -99,7 +102,8 @@ def X : Flag<["-"], "X">,
def discard_all
: Flag<["--"], "discard-all">,
- HelpText<"Remove all local symbols except file and section symbols">;
+ HelpText<"Remove all local symbols except file and section symbols. Also "
+ "remove all debug sections">;
def x : Flag<["-"], "x">,
Alias<discard_all>,
HelpText<"Alias for --discard-all">;
diff --git a/llvm/tools/llvm-objcopy/CopyConfig.cpp b/llvm/tools/llvm-objcopy/ConfigManager.cpp
index ba74759a34c2..9f7d06b99418 100644
--- a/llvm/tools/llvm-objcopy/CopyConfig.cpp
+++ b/llvm/tools/llvm-objcopy/ConfigManager.cpp
@@ -1,4 +1,4 @@
-//===- CopyConfig.cpp -----------------------------------------------------===//
+//===- ConfigManager.cpp --------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -6,8 +6,7 @@
//
//===----------------------------------------------------------------------===//
-#include "CopyConfig.h"
-
+#include "ConfigManager.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
@@ -18,12 +17,13 @@
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Compression.h"
#include "llvm/Support/Errc.h"
+#include "llvm/Support/Error.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/StringSaver.h"
#include <memory>
-namespace llvm {
-namespace objcopy {
+using namespace llvm;
+using namespace llvm::objcopy;
namespace {
enum ObjcopyID {
@@ -60,7 +60,9 @@ static const opt::OptTable::Info ObjcopyInfoTable[] = {
class ObjcopyOptTable : public opt::OptTable {
public:
- ObjcopyOptTable() : OptTable(ObjcopyInfoTable) {}
+ ObjcopyOptTable() : OptTable(ObjcopyInfoTable) {
+ setGroupedShortOptions(true);
+ }
};
enum InstallNameToolID {
@@ -164,7 +166,7 @@ static const opt::OptTable::Info StripInfoTable[] = {
class StripOptTable : public opt::OptTable {
public:
- StripOptTable() : OptTable(StripInfoTable) {}
+ StripOptTable() : OptTable(StripInfoTable) { setGroupedShortOptions(true); }
};
} // namespace
@@ -244,9 +246,10 @@ parseSetSectionAlignment(StringRef FlagValue) {
"bad format for --set-section-alignment: missing section name");
uint64_t NewAlign;
if (Split.second.getAsInteger(0, NewAlign))
- return createStringError(errc::invalid_argument,
- "invalid alignment for --set-section-alignment: '%s'",
- Split.second.str().c_str());
+ return createStringError(
+ errc::invalid_argument,
+ "invalid alignment for --set-section-alignment: '%s'",
+ Split.second.str().c_str());
return std::make_pair(Split.first, NewAlign);
}
@@ -272,10 +275,12 @@ parseSetSectionFlagValue(StringRef FlagValue) {
return SFU;
}
+namespace {
struct TargetInfo {
FileFormat Format;
MachineInfo Machine;
};
+} // namespace
// FIXME: consolidate with the bfd parsing used by lld.
static const StringMap<MachineInfo> TargetMap{
@@ -337,10 +342,9 @@ getOutputTargetInfoByTargetName(StringRef TargetName) {
return {TargetInfo{Format, MI}};
}
-static Error
-addSymbolsFromFile(NameMatcher &Symbols, BumpPtrAllocator &Alloc,
- StringRef Filename, MatchStyle MS,
- llvm::function_ref<Error(Error)> ErrorCallback) {
+static Error addSymbolsFromFile(NameMatcher &Symbols, BumpPtrAllocator &Alloc,
+ StringRef Filename, MatchStyle MS,
+ function_ref<Error(Error)> ErrorCallback) {
StringSaver Saver(Alloc);
SmallVector<StringRef, 16> Lines;
auto BufOrErr = MemoryBuffer::getFile(Filename);
@@ -363,7 +367,7 @@ addSymbolsFromFile(NameMatcher &Symbols, BumpPtrAllocator &Alloc,
Expected<NameOrPattern>
NameOrPattern::create(StringRef Pattern, MatchStyle MS,
- llvm::function_ref<Error(Error)> ErrorCallback) {
+ function_ref<Error(Error)> ErrorCallback) {
switch (MS) {
case MatchStyle::Literal:
return NameOrPattern(Pattern);
@@ -457,7 +461,7 @@ static void printHelp(const opt::OptTable &OptTable, raw_ostream &OS,
HelpText = " [options] input";
break;
}
- OptTable.PrintHelp(OS, (ToolName + HelpText).str().c_str(),
+ OptTable.printHelp(OS, (ToolName + HelpText).str().c_str(),
(ToolName + " tool").str().c_str());
// TODO: Replace this with libOption call once it adds extrahelp support.
// The CommandLine library has a cl::extrahelp class to support this,
@@ -465,19 +469,186 @@ static void printHelp(const opt::OptTable &OptTable, raw_ostream &OS,
OS << "\nPass @FILE as argument to read options from FILE.\n";
}
+static Expected<NewSymbolInfo> parseNewSymbolInfo(StringRef FlagValue) {
+ // Parse value given with --add-symbol option and create the
+ // new symbol if possible. The value format for --add-symbol is:
+ //
+ // <name>=[<section>:]<value>[,<flags>]
+ //
+ // where:
+ // <name> - symbol name, can be empty string
+ // <section> - optional section name. If not given ABS symbol is created
+ // <value> - symbol value, can be decimal or hexadecimal number prefixed
+ // with 0x.
+ // <flags> - optional flags affecting symbol type, binding or visibility.
+ NewSymbolInfo SI;
+ StringRef Value;
+ std::tie(SI.SymbolName, Value) = FlagValue.split('=');
+ if (Value.empty())
+ return createStringError(
+ errc::invalid_argument,
+ "bad format for --add-symbol, missing '=' after '%s'",
+ SI.SymbolName.str().c_str());
+
+ if (Value.contains(':')) {
+ std::tie(SI.SectionName, Value) = Value.split(':');
+ if (SI.SectionName.empty() || Value.empty())
+ return createStringError(
+ errc::invalid_argument,
+ "bad format for --add-symbol, missing section name or symbol value");
+ }
+
+ SmallVector<StringRef, 6> Flags;
+ Value.split(Flags, ',');
+ if (Flags[0].getAsInteger(0, SI.Value))
+ return createStringError(errc::invalid_argument, "bad symbol value: '%s'",
+ Flags[0].str().c_str());
+
+ using Functor = std::function<void()>;
+ SmallVector<StringRef, 6> UnsupportedFlags;
+ for (size_t I = 1, NumFlags = Flags.size(); I < NumFlags; ++I)
+ static_cast<Functor>(
+ StringSwitch<Functor>(Flags[I])
+ .CaseLower("global",
+ [&] { SI.Flags.push_back(SymbolFlag::Global); })
+ .CaseLower("local", [&] { SI.Flags.push_back(SymbolFlag::Local); })
+ .CaseLower("weak", [&] { SI.Flags.push_back(SymbolFlag::Weak); })
+ .CaseLower("default",
+ [&] { SI.Flags.push_back(SymbolFlag::Default); })
+ .CaseLower("hidden",
+ [&] { SI.Flags.push_back(SymbolFlag::Hidden); })
+ .CaseLower("protected",
+ [&] { SI.Flags.push_back(SymbolFlag::Protected); })
+ .CaseLower("file", [&] { SI.Flags.push_back(SymbolFlag::File); })
+ .CaseLower("section",
+ [&] { SI.Flags.push_back(SymbolFlag::Section); })
+ .CaseLower("object",
+ [&] { SI.Flags.push_back(SymbolFlag::Object); })
+ .CaseLower("function",
+ [&] { SI.Flags.push_back(SymbolFlag::Function); })
+ .CaseLower(
+ "indirect-function",
+ [&] { SI.Flags.push_back(SymbolFlag::IndirectFunction); })
+ .CaseLower("debug", [&] { SI.Flags.push_back(SymbolFlag::Debug); })
+ .CaseLower("constructor",
+ [&] { SI.Flags.push_back(SymbolFlag::Constructor); })
+ .CaseLower("warning",
+ [&] { SI.Flags.push_back(SymbolFlag::Warning); })
+ .CaseLower("indirect",
+ [&] { SI.Flags.push_back(SymbolFlag::Indirect); })
+ .CaseLower("synthetic",
+ [&] { SI.Flags.push_back(SymbolFlag::Synthetic); })
+ .CaseLower("unique-object",
+ [&] { SI.Flags.push_back(SymbolFlag::UniqueObject); })
+ .StartsWithLower("before=",
+ [&] {
+ StringRef SymNamePart =
+ Flags[I].split('=').second;
+
+ if (!SymNamePart.empty())
+ SI.BeforeSyms.push_back(SymNamePart);
+ })
+ .Default([&] { UnsupportedFlags.push_back(Flags[I]); }))();
+ if (!UnsupportedFlags.empty())
+ return createStringError(errc::invalid_argument,
+ "unsupported flag%s for --add-symbol: '%s'",
+ UnsupportedFlags.size() > 1 ? "s" : "",
+ join(UnsupportedFlags, "', '").c_str());
+
+ return SI;
+}
+
+Expected<const ELFConfig &> ConfigManager::getELFConfig() const {
+ if (Common.StripSwiftSymbols || Common.KeepUndefined)
+ return createStringError(llvm::errc::invalid_argument,
+ "option not supported by llvm-objcopy for ELF");
+
+ return ELF;
+}
+
+Expected<const COFFConfig &> ConfigManager::getCOFFConfig() const {
+ if (Common.AllowBrokenLinks || !Common.SplitDWO.empty() ||
+ !Common.SymbolsPrefix.empty() || !Common.AllocSectionsPrefix.empty() ||
+ !Common.DumpSection.empty() || !Common.KeepSection.empty() ||
+ ELF.NewSymbolVisibility || !Common.SymbolsToGlobalize.empty() ||
+ !Common.SymbolsToKeep.empty() || !Common.SymbolsToLocalize.empty() ||
+ !Common.SymbolsToWeaken.empty() || !Common.SymbolsToKeepGlobal.empty() ||
+ !Common.SectionsToRename.empty() || !Common.SetSectionAlignment.empty() ||
+ Common.ExtractDWO || Common.LocalizeHidden || Common.PreserveDates ||
+ Common.StripDWO || Common.StripNonAlloc || Common.StripSections ||
+ Common.StripSwiftSymbols || Common.KeepUndefined || Common.Weaken ||
+ Common.DecompressDebugSections ||
+ Common.DiscardMode == DiscardType::Locals ||
+ !Common.SymbolsToAdd.empty() || Common.EntryExpr) {
+ return createStringError(llvm::errc::invalid_argument,
+ "option not supported by llvm-objcopy for COFF");
+ }
+
+ return COFF;
+}
+
+Expected<const MachOConfig &> ConfigManager::getMachOConfig() const {
+ if (Common.AllowBrokenLinks || !Common.SplitDWO.empty() ||
+ !Common.SymbolsPrefix.empty() || !Common.AllocSectionsPrefix.empty() ||
+ !Common.KeepSection.empty() || ELF.NewSymbolVisibility ||
+ !Common.SymbolsToGlobalize.empty() || !Common.SymbolsToKeep.empty() ||
+ !Common.SymbolsToLocalize.empty() || !Common.SymbolsToWeaken.empty() ||
+ !Common.SymbolsToKeepGlobal.empty() || !Common.SectionsToRename.empty() ||
+ !Common.UnneededSymbolsToRemove.empty() ||
+ !Common.SetSectionAlignment.empty() || !Common.SetSectionFlags.empty() ||
+ Common.ExtractDWO || Common.LocalizeHidden || Common.PreserveDates ||
+ Common.StripAllGNU || Common.StripDWO || Common.StripNonAlloc ||
+ Common.StripSections || Common.Weaken || Common.DecompressDebugSections ||
+ Common.StripUnneeded || Common.DiscardMode == DiscardType::Locals ||
+ !Common.SymbolsToAdd.empty() || Common.EntryExpr) {
+ return createStringError(llvm::errc::invalid_argument,
+ "option not supported by llvm-objcopy for MachO");
+ }
+
+ return MachO;
+}
+
+Expected<const WasmConfig &> ConfigManager::getWasmConfig() const {
+ if (!Common.AddGnuDebugLink.empty() || Common.ExtractPartition ||
+ !Common.SplitDWO.empty() || !Common.SymbolsPrefix.empty() ||
+ !Common.AllocSectionsPrefix.empty() ||
+ Common.DiscardMode != DiscardType::None || ELF.NewSymbolVisibility ||
+ !Common.SymbolsToAdd.empty() || !Common.RPathToAdd.empty() ||
+ !Common.SymbolsToGlobalize.empty() || !Common.SymbolsToLocalize.empty() ||
+ !Common.SymbolsToKeep.empty() || !Common.SymbolsToRemove.empty() ||
+ !Common.UnneededSymbolsToRemove.empty() ||
+ !Common.SymbolsToWeaken.empty() || !Common.SymbolsToKeepGlobal.empty() ||
+ !Common.SectionsToRename.empty() || !Common.SetSectionAlignment.empty() ||
+ !Common.SetSectionFlags.empty() || !Common.SymbolsToRename.empty()) {
+ return createStringError(
+ llvm::errc::invalid_argument,
+ "only flags for section dumping, removal, and addition are supported");
+ }
+
+ return Wasm;
+}
+
// ParseObjcopyOptions returns the config and sets the input arguments. If a
// help flag is set then ParseObjcopyOptions will print the help messege and
// exit.
Expected<DriverConfig>
-parseObjcopyOptions(ArrayRef<const char *> ArgsArr,
- llvm::function_ref<Error(Error)> ErrorCallback) {
+objcopy::parseObjcopyOptions(ArrayRef<const char *> RawArgsArr,
+ function_ref<Error(Error)> ErrorCallback) {
DriverConfig DC;
ObjcopyOptTable T;
+
+ const char *const *DashDash =
+ std::find_if(RawArgsArr.begin(), RawArgsArr.end(),
+ [](StringRef Str) { return Str == "--"; });
+ ArrayRef<const char *> ArgsArr = makeArrayRef(RawArgsArr.begin(), DashDash);
+ if (DashDash != RawArgsArr.end())
+ DashDash = std::next(DashDash);
+
unsigned MissingArgumentIndex, MissingArgumentCount;
llvm::opt::InputArgList InputArgs =
T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount);
- if (InputArgs.size() == 0) {
+ if (InputArgs.size() == 0 && DashDash == RawArgsArr.end()) {
printHelp(T, errs(), ToolType::Objcopy);
exit(1);
}
@@ -501,6 +672,7 @@ parseObjcopyOptions(ArrayRef<const char *> ArgsArr,
for (auto Arg : InputArgs.filtered(OBJCOPY_INPUT))
Positional.push_back(Arg->getValue());
+ std::copy(DashDash, RawArgsArr.end(), std::back_inserter(Positional));
if (Positional.empty())
return createStringError(errc::invalid_argument, "no input file specified");
@@ -509,7 +681,9 @@ parseObjcopyOptions(ArrayRef<const char *> ArgsArr,
return createStringError(errc::invalid_argument,
"too many positional arguments");
- CopyConfig Config;
+ ConfigManager ConfigMgr;
+ CommonConfig &Config = ConfigMgr.Common;
+ ELFConfig &ELFConfig = ConfigMgr.ELF;
Config.InputFilename = Positional[0];
Config.OutputFilename = Positional[Positional.size() == 1 ? 0 : 1];
if (InputArgs.hasArg(OBJCOPY_target) &&
@@ -548,10 +722,24 @@ parseObjcopyOptions(ArrayRef<const char *> ArgsArr,
.Case("ihex", FileFormat::IHex)
.Default(FileFormat::Unspecified);
- if (InputArgs.hasArg(OBJCOPY_new_symbol_visibility))
- Config.NewSymbolVisibility =
+ if (InputArgs.hasArg(OBJCOPY_new_symbol_visibility)) {
+ const uint8_t Invalid = 0xff;
+ StringRef VisibilityStr =
InputArgs.getLastArgValue(OBJCOPY_new_symbol_visibility);
+ ELFConfig.NewSymbolVisibility = StringSwitch<uint8_t>(VisibilityStr)
+ .Case("default", ELF::STV_DEFAULT)
+ .Case("hidden", ELF::STV_HIDDEN)
+ .Case("internal", ELF::STV_INTERNAL)
+ .Case("protected", ELF::STV_PROTECTED)
+ .Default(Invalid);
+
+ if (ELFConfig.NewSymbolVisibility == Invalid)
+ return createStringError(errc::invalid_argument,
+ "'%s' is not a valid symbol visibility",
+ VisibilityStr.str().c_str());
+ }
+
Config.OutputFormat = StringSwitch<FileFormat>(OutputFormat)
.Case("binary", FileFormat::Binary)
.Case("ihex", FileFormat::IHex)
@@ -607,13 +795,6 @@ parseObjcopyOptions(ArrayRef<const char *> ArgsArr,
Config.GnuDebugLinkCRC32 =
llvm::crc32(arrayRefFromStringRef(Debug->getBuffer()));
}
- Config.BuildIdLinkDir = InputArgs.getLastArgValue(OBJCOPY_build_id_link_dir);
- if (InputArgs.hasArg(OBJCOPY_build_id_link_input))
- Config.BuildIdLinkInput =
- InputArgs.getLastArgValue(OBJCOPY_build_id_link_input);
- if (InputArgs.hasArg(OBJCOPY_build_id_link_output))
- Config.BuildIdLinkOutput =
- InputArgs.getLastArgValue(OBJCOPY_build_id_link_output);
Config.SplitDWO = InputArgs.getLastArgValue(OBJCOPY_split_dwo);
Config.SymbolsPrefix = InputArgs.getLastArgValue(OBJCOPY_prefix_symbols);
Config.AllocSectionsPrefix =
@@ -706,8 +887,14 @@ parseObjcopyOptions(ArrayRef<const char *> ArgsArr,
"bad format for --add-section: missing file name");
Config.AddSection.push_back(ArgValue);
}
- for (auto Arg : InputArgs.filtered(OBJCOPY_dump_section))
- Config.DumpSection.push_back(Arg->getValue());
+ for (auto *Arg : InputArgs.filtered(OBJCOPY_dump_section)) {
+ StringRef Value(Arg->getValue());
+ if (Value.split('=').second.empty())
+ return createStringError(
+ errc::invalid_argument,
+ "bad format for --dump-section, expected section=file");
+ Config.DumpSection.push_back(Value);
+ }
Config.StripAll = InputArgs.hasArg(OBJCOPY_strip_all);
Config.StripAllGNU = InputArgs.hasArg(OBJCOPY_strip_all_gnu);
Config.StripDebug = InputArgs.hasArg(OBJCOPY_strip_debug);
@@ -727,6 +914,7 @@ parseObjcopyOptions(ArrayRef<const char *> ArgsArr,
: DiscardType::Locals;
Config.OnlyKeepDebug = InputArgs.hasArg(OBJCOPY_only_keep_debug);
Config.KeepFileSymbols = InputArgs.hasArg(OBJCOPY_keep_file_symbols);
+ Config.KeepUndefined = InputArgs.hasArg(OBJCOPY_keep_undefined);
Config.DecompressDebugSections =
InputArgs.hasArg(OBJCOPY_decompress_debug_sections);
if (Config.DiscardMode == DiscardType::All) {
@@ -797,8 +985,13 @@ parseObjcopyOptions(ArrayRef<const char *> ArgsArr,
addSymbolsFromFile(Config.SymbolsToKeep, DC.Alloc, Arg->getValue(),
SymbolMatchStyle, ErrorCallback))
return std::move(E);
- for (auto Arg : InputArgs.filtered(OBJCOPY_add_symbol))
- Config.SymbolsToAdd.push_back(Arg->getValue());
+ for (auto *Arg : InputArgs.filtered(OBJCOPY_add_symbol)) {
+ Expected<NewSymbolInfo> SymInfo = parseNewSymbolInfo(Arg->getValue());
+ if (!SymInfo)
+ return SymInfo.takeError();
+
+ Config.SymbolsToAdd.push_back(*SymInfo);
+ }
Config.AllowBrokenLinks = InputArgs.hasArg(OBJCOPY_allow_broken_links);
@@ -852,7 +1045,7 @@ parseObjcopyOptions(ArrayRef<const char *> ArgsArr,
"cannot specify --extract-partition together with "
"--extract-main-partition");
- DC.CopyConfigs.push_back(std::move(Config));
+ DC.CopyConfigs.push_back(std::move(ConfigMgr));
return std::move(DC);
}
@@ -860,9 +1053,10 @@ parseObjcopyOptions(ArrayRef<const char *> ArgsArr,
// If a help flag is set then ParseInstallNameToolOptions will print the help
// messege and exit.
Expected<DriverConfig>
-parseInstallNameToolOptions(ArrayRef<const char *> ArgsArr) {
+objcopy::parseInstallNameToolOptions(ArrayRef<const char *> ArgsArr) {
DriverConfig DC;
- CopyConfig Config;
+ ConfigManager ConfigMgr;
+ CommonConfig &Config = ConfigMgr.Common;
InstallNameToolOptTable T;
unsigned MissingArgumentIndex, MissingArgumentCount;
llvm::opt::InputArgList InputArgs =
@@ -986,14 +1180,15 @@ parseInstallNameToolOptions(ArrayRef<const char *> ArgsArr) {
Config.InputFilename = Positional[0];
Config.OutputFilename = Positional[0];
- DC.CopyConfigs.push_back(std::move(Config));
+ DC.CopyConfigs.push_back(std::move(ConfigMgr));
return std::move(DC);
}
Expected<DriverConfig>
-parseBitcodeStripOptions(ArrayRef<const char *> ArgsArr) {
+objcopy::parseBitcodeStripOptions(ArrayRef<const char *> ArgsArr) {
DriverConfig DC;
- CopyConfig Config;
+ ConfigManager ConfigMgr;
+ CommonConfig &Config = ConfigMgr.Common;
BitcodeStripOptTable T;
unsigned MissingArgumentIndex, MissingArgumentCount;
opt::InputArgList InputArgs =
@@ -1030,7 +1225,7 @@ parseBitcodeStripOptions(ArrayRef<const char *> ArgsArr) {
Config.InputFilename = Positional[0];
Config.OutputFilename = Positional[0];
- DC.CopyConfigs.push_back(std::move(Config));
+ DC.CopyConfigs.push_back(std::move(ConfigMgr));
return std::move(DC);
}
@@ -1038,14 +1233,21 @@ parseBitcodeStripOptions(ArrayRef<const char *> ArgsArr) {
// help flag is set then ParseStripOptions will print the help messege and
// exit.
Expected<DriverConfig>
-parseStripOptions(ArrayRef<const char *> ArgsArr,
- llvm::function_ref<Error(Error)> ErrorCallback) {
+objcopy::parseStripOptions(ArrayRef<const char *> RawArgsArr,
+ function_ref<Error(Error)> ErrorCallback) {
+ const char *const *DashDash =
+ std::find_if(RawArgsArr.begin(), RawArgsArr.end(),
+ [](StringRef Str) { return Str == "--"; });
+ ArrayRef<const char *> ArgsArr = makeArrayRef(RawArgsArr.begin(), DashDash);
+ if (DashDash != RawArgsArr.end())
+ DashDash = std::next(DashDash);
+
StripOptTable T;
unsigned MissingArgumentIndex, MissingArgumentCount;
llvm::opt::InputArgList InputArgs =
T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount);
- if (InputArgs.size() == 0) {
+ if (InputArgs.size() == 0 && DashDash == RawArgsArr.end()) {
printHelp(T, errs(), ToolType::Strip);
exit(1);
}
@@ -1067,6 +1269,7 @@ parseStripOptions(ArrayRef<const char *> ArgsArr,
Arg->getAsString(InputArgs).c_str());
for (auto Arg : InputArgs.filtered(STRIP_INPUT))
Positional.push_back(Arg->getValue());
+ std::copy(DashDash, RawArgsArr.end(), std::back_inserter(Positional));
if (Positional.empty())
return createStringError(errc::invalid_argument, "no input file specified");
@@ -1076,7 +1279,8 @@ parseStripOptions(ArrayRef<const char *> ArgsArr,
errc::invalid_argument,
"multiple input files cannot be used in combination with -o");
- CopyConfig Config;
+ ConfigManager ConfigMgr;
+ CommonConfig &Config = ConfigMgr.Common;
if (InputArgs.hasArg(STRIP_regex) && InputArgs.hasArg(STRIP_wildcard))
return createStringError(errc::invalid_argument,
@@ -1104,6 +1308,7 @@ parseStripOptions(ArrayRef<const char *> ArgsArr,
Config.StripSwiftSymbols = InputArgs.hasArg(STRIP_strip_swift_symbols);
Config.OnlyKeepDebug = InputArgs.hasArg(STRIP_only_keep_debug);
Config.KeepFileSymbols = InputArgs.hasArg(STRIP_keep_file_symbols);
+ Config.KeepUndefined = InputArgs.hasArg(STRIP_keep_undefined);
for (auto Arg : InputArgs.filtered(STRIP_keep_section))
if (Error E = Config.KeepSection.addMatcher(NameOrPattern::create(
@@ -1148,7 +1353,7 @@ parseStripOptions(ArrayRef<const char *> ArgsArr,
Config.InputFilename = Positional[0];
Config.OutputFilename =
InputArgs.getLastArgValue(STRIP_output, Positional[0]);
- DC.CopyConfigs.push_back(std::move(Config));
+ DC.CopyConfigs.push_back(std::move(ConfigMgr));
} else {
StringMap<unsigned> InputFiles;
for (StringRef Filename : Positional) {
@@ -1164,7 +1369,7 @@ parseStripOptions(ArrayRef<const char *> ArgsArr,
}
Config.InputFilename = Filename;
Config.OutputFilename = Filename;
- DC.CopyConfigs.push_back(Config);
+ DC.CopyConfigs.push_back(ConfigMgr);
}
}
@@ -1175,6 +1380,3 @@ parseStripOptions(ArrayRef<const char *> ArgsArr,
return std::move(DC);
}
-
-} // namespace objcopy
-} // namespace llvm
diff --git a/llvm/tools/llvm-objcopy/ConfigManager.h b/llvm/tools/llvm-objcopy/ConfigManager.h
new file mode 100644
index 000000000000..c0d0e8bbc721
--- /dev/null
+++ b/llvm/tools/llvm-objcopy/ConfigManager.h
@@ -0,0 +1,80 @@
+//===- ConfigManager.h ----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_OBJCOPY_CONFIGMANAGER_H
+#define LLVM_TOOLS_LLVM_OBJCOPY_CONFIGMANAGER_H
+
+#include "COFF/COFFConfig.h"
+#include "CommonConfig.h"
+#include "ELF/ELFConfig.h"
+#include "MachO/MachOConfig.h"
+#include "MultiFormatConfig.h"
+#include "wasm/WasmConfig.h"
+#include "llvm/Support/Allocator.h"
+#include <vector>
+
+namespace llvm {
+namespace objcopy {
+
+// ConfigManager keeps all configurations and prepare
+// format-specific options.
+struct ConfigManager : public MultiFormatConfig {
+ virtual ~ConfigManager() {}
+
+ const CommonConfig &getCommonConfig() const override { return Common; }
+ Expected<const ELFConfig &> getELFConfig() const override;
+ Expected<const COFFConfig &> getCOFFConfig() const override;
+ Expected<const MachOConfig &> getMachOConfig() const override;
+ Expected<const WasmConfig &> getWasmConfig() const override;
+
+ // All configs.
+ CommonConfig Common;
+ ELFConfig ELF;
+ COFFConfig COFF;
+ MachOConfig MachO;
+ WasmConfig Wasm;
+};
+
+// Configuration for the overall invocation of this tool. When invoked as
+// objcopy, will always contain exactly one CopyConfig. When invoked as strip,
+// will contain one or more CopyConfigs.
+struct DriverConfig {
+ SmallVector<ConfigManager, 1> CopyConfigs;
+ BumpPtrAllocator Alloc;
+};
+
+// ParseObjcopyOptions returns the config and sets the input arguments. If a
+// help flag is set then ParseObjcopyOptions will print the help messege and
+// exit. ErrorCallback is used to handle recoverable errors. An Error returned
+// by the callback aborts the parsing and is then returned by this function.
+Expected<DriverConfig>
+parseObjcopyOptions(ArrayRef<const char *> ArgsArr,
+ llvm::function_ref<Error(Error)> ErrorCallback);
+
+// ParseInstallNameToolOptions returns the config and sets the input arguments.
+// If a help flag is set then ParseInstallNameToolOptions will print the help
+// messege and exit.
+Expected<DriverConfig>
+parseInstallNameToolOptions(ArrayRef<const char *> ArgsArr);
+
+// ParseBitcodeStripOptions returns the config and sets the input arguments.
+// If a help flag is set then ParseBitcodeStripOptions will print the help
+// messege and exit.
+Expected<DriverConfig> parseBitcodeStripOptions(ArrayRef<const char *> ArgsArr);
+
+// ParseStripOptions returns the config and sets the input arguments. If a
+// help flag is set then ParseStripOptions will print the help messege and
+// exit. ErrorCallback is used to handle recoverable errors. An Error returned
+// by the callback aborts the parsing and is then returned by this function.
+Expected<DriverConfig>
+parseStripOptions(ArrayRef<const char *> ArgsArr,
+ llvm::function_ref<Error(Error)> ErrorCallback);
+} // namespace objcopy
+} // namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_OBJCOPY_CONFIGMANAGER_H
diff --git a/llvm/tools/llvm-objcopy/ELF/ELFConfig.cpp b/llvm/tools/llvm-objcopy/ELF/ELFConfig.cpp
deleted file mode 100644
index 40993760add7..000000000000
--- a/llvm/tools/llvm-objcopy/ELF/ELFConfig.cpp
+++ /dev/null
@@ -1,133 +0,0 @@
-//===- ELFConfig.cpp ------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "CopyConfig.h"
-#include "llvm/ADT/Optional.h"
-#include "llvm/ADT/StringSwitch.h"
-#include "llvm/Support/Errc.h"
-#include "llvm/Support/Error.h"
-
-namespace llvm {
-namespace objcopy {
-namespace elf {
-
-static Expected<NewSymbolInfo> parseNewSymbolInfo(StringRef FlagValue,
- uint8_t DefaultVisibility) {
- // Parse value given with --add-symbol option and create the
- // new symbol if possible. The value format for --add-symbol is:
- //
- // <name>=[<section>:]<value>[,<flags>]
- //
- // where:
- // <name> - symbol name, can be empty string
- // <section> - optional section name. If not given ABS symbol is created
- // <value> - symbol value, can be decimal or hexadecimal number prefixed
- // with 0x.
- // <flags> - optional flags affecting symbol type, binding or visibility:
- // The following are currently supported:
- //
- // global, local, weak, default, hidden, file, section, object,
- // indirect-function.
- //
- // The following flags are ignored and provided for GNU
- // compatibility only:
- //
- // warning, debug, constructor, indirect, synthetic,
- // unique-object, before=<symbol>.
- NewSymbolInfo SI;
- StringRef Value;
- std::tie(SI.SymbolName, Value) = FlagValue.split('=');
- if (Value.empty())
- return createStringError(
- errc::invalid_argument,
- "bad format for --add-symbol, missing '=' after '%s'",
- SI.SymbolName.str().c_str());
-
- if (Value.contains(':')) {
- std::tie(SI.SectionName, Value) = Value.split(':');
- if (SI.SectionName.empty() || Value.empty())
- return createStringError(
- errc::invalid_argument,
- "bad format for --add-symbol, missing section name or symbol value");
- }
-
- SmallVector<StringRef, 6> Flags;
- Value.split(Flags, ',');
- if (Flags[0].getAsInteger(0, SI.Value))
- return createStringError(errc::invalid_argument, "bad symbol value: '%s'",
- Flags[0].str().c_str());
-
- SI.Visibility = DefaultVisibility;
-
- using Functor = std::function<void(void)>;
- SmallVector<StringRef, 6> UnsupportedFlags;
- for (size_t I = 1, NumFlags = Flags.size(); I < NumFlags; ++I)
- static_cast<Functor>(
- StringSwitch<Functor>(Flags[I])
- .CaseLower("global", [&SI] { SI.Bind = ELF::STB_GLOBAL; })
- .CaseLower("local", [&SI] { SI.Bind = ELF::STB_LOCAL; })
- .CaseLower("weak", [&SI] { SI.Bind = ELF::STB_WEAK; })
- .CaseLower("default", [&SI] { SI.Visibility = ELF::STV_DEFAULT; })
- .CaseLower("hidden", [&SI] { SI.Visibility = ELF::STV_HIDDEN; })
- .CaseLower("protected",
- [&SI] { SI.Visibility = ELF::STV_PROTECTED; })
- .CaseLower("file", [&SI] { SI.Type = ELF::STT_FILE; })
- .CaseLower("section", [&SI] { SI.Type = ELF::STT_SECTION; })
- .CaseLower("object", [&SI] { SI.Type = ELF::STT_OBJECT; })
- .CaseLower("function", [&SI] { SI.Type = ELF::STT_FUNC; })
- .CaseLower("indirect-function",
- [&SI] { SI.Type = ELF::STT_GNU_IFUNC; })
- .CaseLower("debug", [] {})
- .CaseLower("constructor", [] {})
- .CaseLower("warning", [] {})
- .CaseLower("indirect", [] {})
- .CaseLower("synthetic", [] {})
- .CaseLower("unique-object", [] {})
- .StartsWithLower("before", [] {})
- .Default([&] { UnsupportedFlags.push_back(Flags[I]); }))();
- if (!UnsupportedFlags.empty())
- return createStringError(errc::invalid_argument,
- "unsupported flag%s for --add-symbol: '%s'",
- UnsupportedFlags.size() > 1 ? "s" : "",
- join(UnsupportedFlags, "', '").c_str());
- return SI;
-}
-
-Expected<ELFCopyConfig> parseConfig(const CopyConfig &Config) {
- ELFCopyConfig ELFConfig;
- if (Config.NewSymbolVisibility) {
- const uint8_t Invalid = 0xff;
- ELFConfig.NewSymbolVisibility =
- StringSwitch<uint8_t>(*Config.NewSymbolVisibility)
- .Case("default", ELF::STV_DEFAULT)
- .Case("hidden", ELF::STV_HIDDEN)
- .Case("internal", ELF::STV_INTERNAL)
- .Case("protected", ELF::STV_PROTECTED)
- .Default(Invalid);
-
- if (ELFConfig.NewSymbolVisibility == Invalid)
- return createStringError(errc::invalid_argument,
- "'%s' is not a valid symbol visibility",
- Config.NewSymbolVisibility->str().c_str());
- }
-
- for (StringRef Arg : Config.SymbolsToAdd) {
- Expected<elf::NewSymbolInfo> NSI = parseNewSymbolInfo(
- Arg,
- ELFConfig.NewSymbolVisibility.getValueOr((uint8_t)ELF::STV_DEFAULT));
- if (!NSI)
- return NSI.takeError();
- ELFConfig.SymbolsToAdd.push_back(*NSI);
- }
-
- return ELFConfig;
-}
-
-} // end namespace elf
-} // end namespace objcopy
-} // end namespace llvm
diff --git a/llvm/tools/llvm-objcopy/ELF/ELFConfig.h b/llvm/tools/llvm-objcopy/ELF/ELFConfig.h
index 977efbc4166f..42d407da17ff 100644
--- a/llvm/tools/llvm-objcopy/ELF/ELFConfig.h
+++ b/llvm/tools/llvm-objcopy/ELF/ELFConfig.h
@@ -6,39 +6,23 @@
//
//===----------------------------------------------------------------------===//
-#ifndef LLVM_TOOLS_OBJCOPY_ELFCONFIG_H
-#define LLVM_TOOLS_OBJCOPY_ELFCONFIG_H
+#ifndef LLVM_TOOLS_LLVM_OBJCOPY_ELF_ELFCONFIG_H
+#define LLVM_TOOLS_LLVM_OBJCOPY_ELF_ELFCONFIG_H
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Object/ELFTypes.h"
-#include "llvm/Support/Error.h"
#include <vector>
namespace llvm {
namespace objcopy {
-struct CopyConfig;
-namespace elf {
-
-struct NewSymbolInfo {
- StringRef SymbolName;
- StringRef SectionName;
- uint64_t Value = 0;
- uint8_t Type = ELF::STT_NOTYPE;
- uint8_t Bind = ELF::STB_GLOBAL;
- uint8_t Visibility = ELF::STV_DEFAULT;
-};
-
-struct ELFCopyConfig {
- Optional<uint8_t> NewSymbolVisibility;
- std::vector<NewSymbolInfo> SymbolsToAdd;
+// ELF specific configuration for copying/stripping a single file.
+struct ELFConfig {
+ uint8_t NewSymbolVisibility = (uint8_t)ELF::STV_DEFAULT;
};
-Expected<ELFCopyConfig> parseConfig(const CopyConfig &Config);
-
-} // namespace elf
} // namespace objcopy
} // namespace llvm
-#endif
+#endif // LLVM_TOOLS_LLVM_OBJCOPY_ELF_ELFCONFIG_H
diff --git a/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp b/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp
index c53a34bc46a3..986eeca6256c 100644
--- a/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp
+++ b/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp
@@ -7,9 +7,10 @@
//===----------------------------------------------------------------------===//
#include "ELFObjcopy.h"
-#include "Buffer.h"
-#include "CopyConfig.h"
+#include "CommonConfig.h"
+#include "ELFConfig.h"
#include "Object.h"
+#include "llvm-objcopy.h"
#include "llvm/ADT/BitmaskEnum.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/Optional.h"
@@ -44,12 +45,12 @@
#include <system_error>
#include <utility>
-namespace llvm {
-namespace objcopy {
-namespace elf {
+using namespace llvm;
+using namespace llvm::ELF;
+using namespace llvm::objcopy;
+using namespace llvm::objcopy::elf;
+using namespace llvm::object;
-using namespace object;
-using namespace ELF;
using SectionPred = std::function<bool(const SectionBase &Sec)>;
static bool isDebugSection(const SectionBase &Sec) {
@@ -70,7 +71,7 @@ static bool onlyKeepDWOPred(const Object &Obj, const SectionBase &Sec) {
return !isDWOSection(Sec);
}
-uint64_t getNewShfFlags(SectionFlag AllFlags) {
+static uint64_t getNewShfFlags(SectionFlag AllFlags) {
uint64_t NewFlags = 0;
if (AllFlags & SectionFlag::SecAlloc)
NewFlags |= ELF::SHF_ALLOC;
@@ -132,77 +133,40 @@ static ElfType getOutputElfType(const MachineInfo &MI) {
return MI.IsLittleEndian ? ELFT_ELF32LE : ELFT_ELF32BE;
}
-static std::unique_ptr<Writer> createELFWriter(const CopyConfig &Config,
- Object &Obj, Buffer &Buf,
+static std::unique_ptr<Writer> createELFWriter(const CommonConfig &Config,
+ Object &Obj, raw_ostream &Out,
ElfType OutputElfType) {
// Depending on the initial ELFT and OutputFormat we need a different Writer.
switch (OutputElfType) {
case ELFT_ELF32LE:
- return std::make_unique<ELFWriter<ELF32LE>>(Obj, Buf, !Config.StripSections,
+ return std::make_unique<ELFWriter<ELF32LE>>(Obj, Out, !Config.StripSections,
Config.OnlyKeepDebug);
case ELFT_ELF64LE:
- return std::make_unique<ELFWriter<ELF64LE>>(Obj, Buf, !Config.StripSections,
+ return std::make_unique<ELFWriter<ELF64LE>>(Obj, Out, !Config.StripSections,
Config.OnlyKeepDebug);
case ELFT_ELF32BE:
- return std::make_unique<ELFWriter<ELF32BE>>(Obj, Buf, !Config.StripSections,
+ return std::make_unique<ELFWriter<ELF32BE>>(Obj, Out, !Config.StripSections,
Config.OnlyKeepDebug);
case ELFT_ELF64BE:
- return std::make_unique<ELFWriter<ELF64BE>>(Obj, Buf, !Config.StripSections,
+ return std::make_unique<ELFWriter<ELF64BE>>(Obj, Out, !Config.StripSections,
Config.OnlyKeepDebug);
}
llvm_unreachable("Invalid output format");
}
-static std::unique_ptr<Writer> createWriter(const CopyConfig &Config,
- Object &Obj, Buffer &Buf,
+static std::unique_ptr<Writer> createWriter(const CommonConfig &Config,
+ Object &Obj, raw_ostream &Out,
ElfType OutputElfType) {
switch (Config.OutputFormat) {
case FileFormat::Binary:
- return std::make_unique<BinaryWriter>(Obj, Buf);
+ return std::make_unique<BinaryWriter>(Obj, Out);
case FileFormat::IHex:
- return std::make_unique<IHexWriter>(Obj, Buf);
+ return std::make_unique<IHexWriter>(Obj, Out);
default:
- return createELFWriter(Config, Obj, Buf, OutputElfType);
+ return createELFWriter(Config, Obj, Out, OutputElfType);
}
}
-template <class ELFT>
-static Expected<ArrayRef<uint8_t>>
-findBuildID(const CopyConfig &Config, const object::ELFFile<ELFT> &In) {
- auto PhdrsOrErr = In.program_headers();
- if (auto Err = PhdrsOrErr.takeError())
- return createFileError(Config.InputFilename, std::move(Err));
-
- for (const auto &Phdr : *PhdrsOrErr) {
- if (Phdr.p_type != PT_NOTE)
- continue;
- Error Err = Error::success();
- for (auto Note : In.notes(Phdr, Err))
- if (Note.getType() == NT_GNU_BUILD_ID && Note.getName() == ELF_NOTE_GNU)
- return Note.getDesc();
- if (Err)
- return createFileError(Config.InputFilename, std::move(Err));
- }
-
- return createFileError(Config.InputFilename,
- createStringError(llvm::errc::invalid_argument,
- "could not find build ID"));
-}
-
-static Expected<ArrayRef<uint8_t>>
-findBuildID(const CopyConfig &Config, const object::ELFObjectFileBase &In) {
- if (auto *O = dyn_cast<ELFObjectFile<ELF32LE>>(&In))
- return findBuildID(Config, O->getELFFile());
- else if (auto *O = dyn_cast<ELFObjectFile<ELF64LE>>(&In))
- return findBuildID(Config, O->getELFFile());
- else if (auto *O = dyn_cast<ELFObjectFile<ELF32BE>>(&In))
- return findBuildID(Config, O->getELFFile());
- else if (auto *O = dyn_cast<ELFObjectFile<ELF64BE>>(&In))
- return findBuildID(Config, O->getELFFile());
-
- llvm_unreachable("Bad file format");
-}
-
template <class... Ts>
static Error makeStringError(std::error_code EC, const Twine &Msg,
Ts &&... Args) {
@@ -210,83 +174,6 @@ static Error makeStringError(std::error_code EC, const Twine &Msg,
return createStringError(EC, FullMsg.c_str(), std::forward<Ts>(Args)...);
}
-#define MODEL_8 "%%%%%%%%"
-#define MODEL_16 MODEL_8 MODEL_8
-#define MODEL_32 (MODEL_16 MODEL_16)
-
-static Error linkToBuildIdDir(const CopyConfig &Config, StringRef ToLink,
- StringRef Suffix,
- ArrayRef<uint8_t> BuildIdBytes) {
- SmallString<128> Path = Config.BuildIdLinkDir;
- sys::path::append(Path, llvm::toHex(BuildIdBytes[0], /*LowerCase*/ true));
- if (auto EC = sys::fs::create_directories(Path))
- return createFileError(
- Path.str(),
- makeStringError(EC, "cannot create build ID link directory"));
-
- sys::path::append(Path,
- llvm::toHex(BuildIdBytes.slice(1), /*LowerCase*/ true));
- Path += Suffix;
- SmallString<128> TmpPath;
- // create_hard_link races so we need to link to a temporary path but
- // we want to make sure that we choose a filename that does not exist.
- // By using 32 model characters we get 128-bits of entropy. It is
- // unlikely that this string has ever existed before much less exists
- // on this disk or in the current working directory.
- // Additionally we prepend the original Path for debugging but also
- // because it ensures that we're linking within a directory on the same
- // partition on the same device which is critical. It has the added
- // win of yet further decreasing the odds of a conflict.
- sys::fs::createUniquePath(Twine(Path) + "-" + MODEL_32 + ".tmp", TmpPath,
- /*MakeAbsolute*/ false);
- if (auto EC = sys::fs::create_hard_link(ToLink, TmpPath)) {
- Path.push_back('\0');
- return makeStringError(EC, "cannot link '%s' to '%s'", ToLink.data(),
- Path.data());
- }
- // We then atomically rename the link into place which will just move the
- // link. If rename fails something is more seriously wrong so just return
- // an error.
- if (auto EC = sys::fs::rename(TmpPath, Path)) {
- Path.push_back('\0');
- return makeStringError(EC, "cannot link '%s' to '%s'", ToLink.data(),
- Path.data());
- }
- // If `Path` was already a hard-link to the same underlying file then the
- // temp file will be left so we need to remove it. Remove will not cause
- // an error by default if the file is already gone so just blindly remove
- // it rather than checking.
- if (auto EC = sys::fs::remove(TmpPath)) {
- TmpPath.push_back('\0');
- return makeStringError(EC, "could not remove '%s'", TmpPath.data());
- }
- return Error::success();
-}
-
-static Error splitDWOToFile(const CopyConfig &Config, const Reader &Reader,
- StringRef File, ElfType OutputElfType) {
- Expected<std::unique_ptr<Object>> DWOFile = Reader.create(false);
- if (!DWOFile)
- return DWOFile.takeError();
-
- auto OnlyKeepDWOPred = [&DWOFile](const SectionBase &Sec) {
- return onlyKeepDWOPred(**DWOFile, Sec);
- };
- if (Error E =
- (*DWOFile)->removeSections(Config.AllowBrokenLinks, OnlyKeepDWOPred))
- return E;
- if (Config.OutputArch) {
- (*DWOFile)->Machine = Config.OutputArch.getValue().EMachine;
- (*DWOFile)->OSABI = Config.OutputArch.getValue().OSABI;
- }
- FileBuffer FB(File);
- std::unique_ptr<Writer> Writer =
- createWriter(Config, **DWOFile, FB, OutputElfType);
- if (Error E = Writer->finalize())
- return E;
- return Writer->write();
-}
-
static Error dumpSectionToFile(StringRef SecName, StringRef Filename,
Object &Obj) {
for (auto &Sec : Obj.sections()) {
@@ -357,7 +244,7 @@ static bool isUnneededSymbol(const Symbol &Sym) {
Sym.Type != STT_SECTION;
}
-static Error updateAndRemoveSymbols(const CopyConfig &Config, Object &Obj) {
+static Error updateAndRemoveSymbols(const CommonConfig &Config, Object &Obj) {
// TODO: update or remove symbols only if there is an option that affects
// them.
if (!Obj.SymbolTable)
@@ -452,7 +339,7 @@ static Error updateAndRemoveSymbols(const CopyConfig &Config, Object &Obj) {
return Obj.removeSymbols(RemoveSymbolsPred);
}
-static Error replaceAndRemoveSections(const CopyConfig &Config, Object &Obj) {
+static Error replaceAndRemoveSections(const CommonConfig &Config, Object &Obj) {
SectionPred RemovePred = [](const SectionBase &) { return false; };
// Removes:
@@ -462,7 +349,7 @@ static Error replaceAndRemoveSections(const CopyConfig &Config, Object &Obj) {
};
}
- if (Config.StripDWO || !Config.SplitDWO.empty())
+ if (Config.StripDWO)
RemovePred = [RemovePred](const SectionBase &Sec) {
return isDWOSection(Sec) || RemovePred(Sec);
};
@@ -613,6 +500,60 @@ static Error replaceAndRemoveSections(const CopyConfig &Config, Object &Obj) {
return Obj.removeSections(Config.AllowBrokenLinks, RemovePred);
}
+// Add symbol to the Object symbol table with the specified properties.
+static void addSymbol(Object &Obj, const NewSymbolInfo &SymInfo,
+ uint8_t DefaultVisibility) {
+ SectionBase *Sec = Obj.findSection(SymInfo.SectionName);
+ uint64_t Value = Sec ? Sec->Addr + SymInfo.Value : SymInfo.Value;
+
+ uint8_t Bind = ELF::STB_GLOBAL;
+ uint8_t Type = ELF::STT_NOTYPE;
+ uint8_t Visibility = DefaultVisibility;
+
+ for (SymbolFlag FlagValue : SymInfo.Flags)
+ switch (FlagValue) {
+ case SymbolFlag::Global:
+ Bind = ELF::STB_GLOBAL;
+ break;
+ case SymbolFlag::Local:
+ Bind = ELF::STB_LOCAL;
+ break;
+ case SymbolFlag::Weak:
+ Bind = ELF::STB_WEAK;
+ break;
+ case SymbolFlag::Default:
+ Visibility = ELF::STV_DEFAULT;
+ break;
+ case SymbolFlag::Hidden:
+ Visibility = ELF::STV_HIDDEN;
+ break;
+ case SymbolFlag::Protected:
+ Visibility = ELF::STV_PROTECTED;
+ break;
+ case SymbolFlag::File:
+ Type = ELF::STT_FILE;
+ break;
+ case SymbolFlag::Section:
+ Type = ELF::STT_SECTION;
+ break;
+ case SymbolFlag::Object:
+ Type = ELF::STT_OBJECT;
+ break;
+ case SymbolFlag::Function:
+ Type = ELF::STT_FUNC;
+ break;
+ case SymbolFlag::IndirectFunction:
+ Type = ELF::STT_GNU_IFUNC;
+ break;
+ default: /* Other flag values are ignored for ELF. */
+ break;
+ };
+
+ Obj.SymbolTable->addSymbol(
+ SymInfo.SymbolName, Bind, Type, Sec, Value, Visibility,
+ Sec ? (uint16_t)SYMBOL_SIMPLE_INDEX : (uint16_t)SHN_ABS, 0);
+}
+
// This function handles the high level operations of GNU objcopy including
// handling command line options. It's important to outline certain properties
// we expect to hold of the command line operations. Any operation that "keeps"
@@ -620,21 +561,19 @@ static Error replaceAndRemoveSections(const CopyConfig &Config, Object &Obj) {
// any previous removals. Lastly whether or not something is removed shouldn't
// depend a) on the order the options occur in or b) on some opaque priority
// system. The only priority is that keeps/copies overrule removes.
-static Error handleArgs(const CopyConfig &Config, Object &Obj,
- const Reader &Reader, ElfType OutputElfType) {
- if (Config.StripSwiftSymbols)
- return createStringError(llvm::errc::invalid_argument,
- "option not supported by llvm-objcopy for ELF");
- if (!Config.SplitDWO.empty())
- if (Error E =
- splitDWOToFile(Config, Reader, Config.SplitDWO, OutputElfType))
- return E;
-
+static Error handleArgs(const CommonConfig &Config, const ELFConfig &ELFConfig,
+ Object &Obj) {
if (Config.OutputArch) {
Obj.Machine = Config.OutputArch.getValue().EMachine;
Obj.OSABI = Config.OutputArch.getValue().OSABI;
}
+ if (!Config.SplitDWO.empty() && Config.ExtractDWO) {
+ return Obj.removeSections(
+ Config.AllowBrokenLinks,
+ [&Obj](const SectionBase &Sec) { return onlyKeepDWOPred(Obj, Sec); });
+ }
+
// Dump sections before add/remove for compatibility with GNU objcopy.
for (StringRef Flag : Config.DumpSection) {
StringRef SectionName;
@@ -748,17 +687,12 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj,
// If the symbol table was previously removed, we need to create a new one
// before adding new symbols.
- if (!Obj.SymbolTable && !Config.ELF->SymbolsToAdd.empty())
+ if (!Obj.SymbolTable && !Config.SymbolsToAdd.empty())
if (Error E = Obj.addNewSymbolTable())
return E;
- for (const NewSymbolInfo &SI : Config.ELF->SymbolsToAdd) {
- SectionBase *Sec = Obj.findSection(SI.SectionName);
- uint64_t Value = Sec ? Sec->Addr + SI.Value : SI.Value;
- Obj.SymbolTable->addSymbol(
- SI.SymbolName, SI.Bind, SI.Type, Sec, Value, SI.Visibility,
- Sec ? (uint16_t)SYMBOL_SIMPLE_INDEX : (uint16_t)SHN_ABS, 0);
- }
+ for (const NewSymbolInfo &SI : Config.SymbolsToAdd)
+ addSymbol(Obj, SI, ELFConfig.NewSymbolVisibility);
// --set-section-flags works with sections added by --add-section.
if (!Config.SetSectionFlags.empty()) {
@@ -776,8 +710,8 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj,
return Error::success();
}
-static Error writeOutput(const CopyConfig &Config, Object &Obj, Buffer &Out,
- ElfType OutputElfType) {
+static Error writeOutput(const CommonConfig &Config, Object &Obj,
+ raw_ostream &Out, ElfType OutputElfType) {
std::unique_ptr<Writer> Writer =
createWriter(Config, Obj, Out, OutputElfType);
if (Error E = Writer->finalize())
@@ -785,8 +719,9 @@ static Error writeOutput(const CopyConfig &Config, Object &Obj, Buffer &Out,
return Writer->write();
}
-Error executeObjcopyOnIHex(const CopyConfig &Config, MemoryBuffer &In,
- Buffer &Out) {
+Error objcopy::elf::executeObjcopyOnIHex(const CommonConfig &Config,
+ const ELFConfig &ELFConfig,
+ MemoryBuffer &In, raw_ostream &Out) {
IHexReader Reader(&In);
Expected<std::unique_ptr<Object>> Obj = Reader.create(true);
if (!Obj)
@@ -794,16 +729,16 @@ Error executeObjcopyOnIHex(const CopyConfig &Config, MemoryBuffer &In,
const ElfType OutputElfType =
getOutputElfType(Config.OutputArch.getValueOr(MachineInfo()));
- if (Error E = handleArgs(Config, **Obj, Reader, OutputElfType))
+ if (Error E = handleArgs(Config, ELFConfig, **Obj))
return E;
return writeOutput(Config, **Obj, Out, OutputElfType);
}
-Error executeObjcopyOnRawBinary(const CopyConfig &Config, MemoryBuffer &In,
- Buffer &Out) {
- uint8_t NewSymbolVisibility =
- Config.ELF->NewSymbolVisibility.getValueOr((uint8_t)ELF::STV_DEFAULT);
- BinaryReader Reader(&In, NewSymbolVisibility);
+Error objcopy::elf::executeObjcopyOnRawBinary(const CommonConfig &Config,
+ const ELFConfig &ELFConfig,
+ MemoryBuffer &In,
+ raw_ostream &Out) {
+ BinaryReader Reader(&In, ELFConfig.NewSymbolVisibility);
Expected<std::unique_ptr<Object>> Obj = Reader.create(true);
if (!Obj)
return Obj.takeError();
@@ -812,13 +747,15 @@ Error executeObjcopyOnRawBinary(const CopyConfig &Config, MemoryBuffer &In,
// (-B<arch>).
const ElfType OutputElfType =
getOutputElfType(Config.OutputArch.getValueOr(MachineInfo()));
- if (Error E = handleArgs(Config, **Obj, Reader, OutputElfType))
+ if (Error E = handleArgs(Config, ELFConfig, **Obj))
return E;
return writeOutput(Config, **Obj, Out, OutputElfType);
}
-Error executeObjcopyOnBinary(const CopyConfig &Config,
- object::ELFObjectFileBase &In, Buffer &Out) {
+Error objcopy::elf::executeObjcopyOnBinary(const CommonConfig &Config,
+ const ELFConfig &ELFConfig,
+ object::ELFObjectFileBase &In,
+ raw_ostream &Out) {
ELFReader Reader(&In, Config.ExtractPartition);
Expected<std::unique_ptr<Object>> Obj =
Reader.create(!Config.SymbolsToAdd.empty());
@@ -828,41 +765,12 @@ Error executeObjcopyOnBinary(const CopyConfig &Config,
const ElfType OutputElfType =
Config.OutputArch ? getOutputElfType(Config.OutputArch.getValue())
: getOutputElfType(In);
- ArrayRef<uint8_t> BuildIdBytes;
- if (!Config.BuildIdLinkDir.empty()) {
- auto BuildIdBytesOrErr = findBuildID(Config, In);
- if (auto E = BuildIdBytesOrErr.takeError())
- return E;
- BuildIdBytes = *BuildIdBytesOrErr;
-
- if (BuildIdBytes.size() < 2)
- return createFileError(
- Config.InputFilename,
- createStringError(object_error::parse_failed,
- "build ID is smaller than two bytes"));
- }
-
- if (!Config.BuildIdLinkDir.empty() && Config.BuildIdLinkInput)
- if (Error E =
- linkToBuildIdDir(Config, Config.InputFilename,
- Config.BuildIdLinkInput.getValue(), BuildIdBytes))
- return E;
-
- if (Error E = handleArgs(Config, **Obj, Reader, OutputElfType))
+ if (Error E = handleArgs(Config, ELFConfig, **Obj))
return createFileError(Config.InputFilename, std::move(E));
if (Error E = writeOutput(Config, **Obj, Out, OutputElfType))
return createFileError(Config.InputFilename, std::move(E));
- if (!Config.BuildIdLinkDir.empty() && Config.BuildIdLinkOutput)
- if (Error E =
- linkToBuildIdDir(Config, Config.OutputFilename,
- Config.BuildIdLinkOutput.getValue(), BuildIdBytes))
- return createFileError(Config.OutputFilename, std::move(E));
return Error::success();
}
-
-} // end namespace elf
-} // end namespace objcopy
-} // end namespace llvm
diff --git a/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.h b/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.h
index e13e237e29c4..852661e68f37 100644
--- a/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.h
+++ b/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.h
@@ -12,22 +12,26 @@
namespace llvm {
class Error;
class MemoryBuffer;
+class raw_ostream;
namespace object {
class ELFObjectFileBase;
} // end namespace object
namespace objcopy {
-struct CopyConfig;
-class Buffer;
+struct CommonConfig;
+struct ELFConfig;
namespace elf {
-Error executeObjcopyOnIHex(const CopyConfig &Config, MemoryBuffer &In,
- Buffer &Out);
-Error executeObjcopyOnRawBinary(const CopyConfig &Config, MemoryBuffer &In,
- Buffer &Out);
-Error executeObjcopyOnBinary(const CopyConfig &Config,
- object::ELFObjectFileBase &In, Buffer &Out);
+Error executeObjcopyOnIHex(const CommonConfig &Config,
+ const ELFConfig &ELFConfig, MemoryBuffer &In,
+ raw_ostream &Out);
+Error executeObjcopyOnRawBinary(const CommonConfig &Config,
+ const ELFConfig &ELFConfig, MemoryBuffer &In,
+ raw_ostream &Out);
+Error executeObjcopyOnBinary(const CommonConfig &Config,
+ const ELFConfig &ELFConfig,
+ object::ELFObjectFileBase &In, raw_ostream &Out);
} // end namespace elf
} // end namespace objcopy
diff --git a/llvm/tools/llvm-objcopy/ELF/Object.cpp b/llvm/tools/llvm-objcopy/ELF/Object.cpp
index 0ff82f951b62..ba91d08e5540 100644
--- a/llvm/tools/llvm-objcopy/ELF/Object.cpp
+++ b/llvm/tools/llvm-objcopy/ELF/Object.cpp
@@ -29,16 +29,14 @@
#include <utility>
#include <vector>
-namespace llvm {
-namespace objcopy {
-namespace elf {
-
-using namespace object;
-using namespace ELF;
+using namespace llvm;
+using namespace llvm::ELF;
+using namespace llvm::objcopy::elf;
+using namespace llvm::object;
template <class ELFT> void ELFWriter<ELFT>::writePhdr(const Segment &Seg) {
- uint8_t *B = Buf.getBufferStart() + Obj.ProgramHdrSegment.Offset +
- Seg.Index * sizeof(Elf_Phdr);
+ uint8_t *B = reinterpret_cast<uint8_t *>(Buf->getBufferStart()) +
+ Obj.ProgramHdrSegment.Offset + Seg.Index * sizeof(Elf_Phdr);
Elf_Phdr &Phdr = *reinterpret_cast<Elf_Phdr *>(B);
Phdr.p_type = Seg.Type;
Phdr.p_flags = Seg.Flags;
@@ -67,7 +65,8 @@ void SectionBase::replaceSectionReferences(
void SectionBase::onRemove() {}
template <class ELFT> void ELFWriter<ELFT>::writeShdr(const SectionBase &Sec) {
- uint8_t *B = Buf.getBufferStart() + Sec.HeaderOffset;
+ uint8_t *B =
+ reinterpret_cast<uint8_t *>(Buf->getBufferStart()) + Sec.HeaderOffset;
Elf_Shdr &Shdr = *reinterpret_cast<Elf_Shdr *>(B);
Shdr.sh_name = Sec.NameIndex;
Shdr.sh_type = Sec.Type;
@@ -191,7 +190,7 @@ template <class T> static T checkedGetHex(StringRef S) {
// Fills exactly Len bytes of buffer with hexadecimal characters
// representing value 'X'
template <class T, class Iterator>
-static Iterator utohexstr(T X, Iterator It, size_t Len) {
+static Iterator toHexStr(T X, Iterator It, size_t Len) {
// Fill range with '0'
std::fill(It, It + Len, '0');
@@ -220,13 +219,13 @@ IHexLineData IHexRecord::getLine(uint8_t Type, uint16_t Addr,
assert(Line.size());
auto Iter = Line.begin();
*Iter++ = ':';
- Iter = utohexstr(Data.size(), Iter, 2);
- Iter = utohexstr(Addr, Iter, 4);
- Iter = utohexstr(Type, Iter, 2);
+ Iter = toHexStr(Data.size(), Iter, 2);
+ Iter = toHexStr(Addr, Iter, 4);
+ Iter = toHexStr(Type, Iter, 2);
for (uint8_t X : Data)
- Iter = utohexstr(X, Iter, 2);
+ Iter = toHexStr(X, Iter, 2);
StringRef S(Line.data() + 1, std::distance(Line.begin() + 1, Iter));
- Iter = utohexstr(getChecksum(S), Iter, 2);
+ Iter = toHexStr(getChecksum(S), Iter, 2);
*Iter++ = '\r';
*Iter++ = '\n';
assert(Iter == Line.end());
@@ -474,7 +473,7 @@ Error ELFSectionWriter<ELFT>::visit(const DecompressedSection &Sec) {
return createStringError(errc::invalid_argument,
"'" + Sec.Name + "': " + toString(std::move(Err)));
- uint8_t *Buf = Out.getBufferStart() + Sec.Offset;
+ uint8_t *Buf = reinterpret_cast<uint8_t *>(Out.getBufferStart()) + Sec.Offset;
std::copy(DecompressedContent.begin(), DecompressedContent.end(), Buf);
return Error::success();
@@ -519,7 +518,7 @@ Error BinarySectionWriter::visit(const CompressedSection &Sec) {
template <class ELFT>
Error ELFSectionWriter<ELFT>::visit(const CompressedSection &Sec) {
- uint8_t *Buf = Out.getBufferStart() + Sec.Offset;
+ uint8_t *Buf = reinterpret_cast<uint8_t *>(Out.getBufferStart()) + Sec.Offset;
if (Sec.CompressionType == DebugCompressionType::None) {
std::copy(Sec.OriginalData.begin(), Sec.OriginalData.end(), Buf);
return Error::success();
@@ -624,7 +623,8 @@ void StringTableSection::prepareForLayout() {
}
Error SectionWriter::visit(const StringTableSection &Sec) {
- Sec.StrTabBuilder.write(Out.getBufferStart() + Sec.Offset);
+ Sec.StrTabBuilder.write(reinterpret_cast<uint8_t *>(Out.getBufferStart()) +
+ Sec.Offset);
return Error::success();
}
@@ -638,7 +638,7 @@ Error StringTableSection::accept(MutableSectionVisitor &Visitor) {
template <class ELFT>
Error ELFSectionWriter<ELFT>::visit(const SectionIndexSection &Sec) {
- uint8_t *Buf = Out.getBufferStart() + Sec.Offset;
+ uint8_t *Buf = reinterpret_cast<uint8_t *>(Out.getBufferStart()) + Sec.Offset;
llvm::copy(Sec.Indexes, reinterpret_cast<Elf_Word *>(Buf));
return Error::success();
}
@@ -979,7 +979,7 @@ static void writeRel(const RelRange &Relocations, T *Buf) {
template <class ELFT>
Error ELFSectionWriter<ELFT>::visit(const RelocationSection &Sec) {
- uint8_t *Buf = Out.getBufferStart() + Sec.Offset;
+ uint8_t *Buf = reinterpret_cast<uint8_t *>(Out.getBufferStart()) + Sec.Offset;
if (Sec.Type == SHT_REL)
writeRel(Sec.Relocations, reinterpret_cast<Elf_Rel *>(Buf));
else
@@ -1069,6 +1069,12 @@ Error Section::removeSectionReferences(
void GroupSection::finalize() {
this->Info = Sym ? Sym->Index : 0;
this->Link = SymTab ? SymTab->Index : 0;
+ // Linker deduplication for GRP_COMDAT is based on Sym->Name. The local/global
+ // status is not part of the equation. If Sym is localized, the intention is
+ // likely to make the group fully localized. Drop GRP_COMDAT to suppress
+ // deduplication. See https://groups.google.com/g/generic-abi/c/2X6mR-s2zoc
+ if ((FlagWord & GRP_COMDAT) && Sym && Sym->Binding == STB_LOCAL)
+ this->FlagWord &= ~GRP_COMDAT;
}
Error GroupSection::removeSectionReferences(
@@ -1160,7 +1166,8 @@ GnuDebugLinkSection::GnuDebugLinkSection(StringRef File,
template <class ELFT>
Error ELFSectionWriter<ELFT>::visit(const GnuDebugLinkSection &Sec) {
- unsigned char *Buf = Out.getBufferStart() + Sec.Offset;
+ unsigned char *Buf =
+ reinterpret_cast<uint8_t *>(Out.getBufferStart()) + Sec.Offset;
Elf_Word *CRC =
reinterpret_cast<Elf_Word *>(Buf + Sec.Size - sizeof(Elf_Word));
*CRC = Sec.CRC32;
@@ -1180,7 +1187,7 @@ template <class ELFT>
Error ELFSectionWriter<ELFT>::visit(const GroupSection &Sec) {
ELF::Elf32_Word *Buf =
reinterpret_cast<ELF::Elf32_Word *>(Out.getBufferStart() + Sec.Offset);
- *Buf++ = Sec.FlagWord;
+ support::endian::write32<ELFT::TargetEndianness>(Buf++, Sec.FlagWord);
for (SectionBase *S : Sec.GroupMembers)
support::endian::write32<ELFT::TargetEndianness>(Buf++, S->Index);
return Error::success();
@@ -1202,6 +1209,10 @@ static bool sectionWithinSegment(const SectionBase &Sec, const Segment &Seg) {
// not the first.
uint64_t SecSize = Sec.Size ? Sec.Size : 1;
+ // Ignore just added sections.
+ if (Sec.OriginalOffset == std::numeric_limits<uint64_t>::max())
+ return false;
+
if (Sec.Type == SHT_NOBITS) {
if (!(Sec.Flags & SHF_ALLOC))
return false;
@@ -1290,8 +1301,9 @@ void BinaryELFBuilder::addData(SymbolTableSection *SymTab) {
DataSection.Flags = ELF::SHF_ALLOC | ELF::SHF_WRITE;
std::string SanitizedFilename = MemBuf->getBufferIdentifier().str();
- std::replace_if(std::begin(SanitizedFilename), std::end(SanitizedFilename),
- [](char C) { return !isalnum(C); }, '_');
+ std::replace_if(
+ std::begin(SanitizedFilename), std::end(SanitizedFilename),
+ [](char C) { return !isAlnum(C); }, '_');
Twine Prefix = Twine("_binary_") + SanitizedFilename;
SymTab->addSymbol(Prefix + "_start", STB_GLOBAL, STT_NOTYPE, &DataSection,
@@ -1505,7 +1517,8 @@ Error ELFBuilder<ELFT>::initGroupSection(GroupSection *GroupSec) {
reinterpret_cast<const ELF::Elf32_Word *>(GroupSec->Contents.data());
const ELF::Elf32_Word *End =
Word + GroupSec->Contents.size() / sizeof(ELF::Elf32_Word);
- GroupSec->setFlagWord(*Word++);
+ GroupSec->setFlagWord(
+ support::endian::read32<ELFT::TargetEndianness>(Word++));
for (; Word != End; ++Word) {
uint32_t Index = support::endian::read32<ELFT::TargetEndianness>(Word);
Expected<SectionBase *> Sec = SecTable.getSection(
@@ -1778,7 +1791,7 @@ template <class ELFT> Error ELFBuilder<ELFT>::readSectionHeaders() {
Sec->OriginalIndex = Sec->Index;
Sec->OriginalData =
ArrayRef<uint8_t>(ElfFile.base() + Shdr.sh_offset,
- (Shdr.sh_type == SHT_NOBITS) ? 0 : Shdr.sh_size);
+ (Shdr.sh_type == SHT_NOBITS) ? (size_t)0 : Shdr.sh_size);
}
return Error::success();
@@ -1971,7 +1984,7 @@ Expected<std::unique_ptr<Object>> ELFReader::create(bool EnsureSymtab) const {
}
template <class ELFT> void ELFWriter<ELFT>::writeEhdr() {
- Elf_Ehdr &Ehdr = *reinterpret_cast<Elf_Ehdr *>(Buf.getBufferStart());
+ Elf_Ehdr &Ehdr = *reinterpret_cast<Elf_Ehdr *>(Buf->getBufferStart());
std::fill(Ehdr.e_ident, Ehdr.e_ident + 16, 0);
Ehdr.e_ident[EI_MAG0] = 0x7f;
Ehdr.e_ident[EI_MAG1] = 'E';
@@ -2036,7 +2049,7 @@ template <class ELFT> void ELFWriter<ELFT>::writeShdrs() {
// This reference serves to write the dummy section header at the begining
// of the file. It is not used for anything else
Elf_Shdr &Shdr =
- *reinterpret_cast<Elf_Shdr *>(Buf.getBufferStart() + Obj.SHOff);
+ *reinterpret_cast<Elf_Shdr *>(Buf->getBufferStart() + Obj.SHOff);
Shdr.sh_name = 0;
Shdr.sh_type = SHT_NULL;
Shdr.sh_flags = 0;
@@ -2076,7 +2089,7 @@ template <class ELFT> Error ELFWriter<ELFT>::writeSectionData() {
template <class ELFT> void ELFWriter<ELFT>::writeSegmentData() {
for (Segment &Seg : Obj.segments()) {
size_t Size = std::min<size_t>(Seg.FileSize, Seg.getContents().size());
- std::memcpy(Buf.getBufferStart() + Seg.Offset, Seg.getContents().data(),
+ std::memcpy(Buf->getBufferStart() + Seg.Offset, Seg.getContents().data(),
Size);
}
@@ -2087,12 +2100,12 @@ template <class ELFT> void ELFWriter<ELFT>::writeSegmentData() {
continue;
uint64_t Offset =
Sec.OriginalOffset - Parent->OriginalOffset + Parent->Offset;
- std::memset(Buf.getBufferStart() + Offset, 0, Sec.Size);
+ std::memset(Buf->getBufferStart() + Offset, 0, Sec.Size);
}
}
template <class ELFT>
-ELFWriter<ELFT>::ELFWriter(Object &Obj, Buffer &Buf, bool WSH,
+ELFWriter<ELFT>::ELFWriter(Object &Obj, raw_ostream &Buf, bool WSH,
bool OnlyKeepDebug)
: Writer(Obj, Buf), WriteSectionHeaders(WSH && Obj.HadShdrs),
OnlyKeepDebug(OnlyKeepDebug) {}
@@ -2311,18 +2324,19 @@ static uint64_t layoutSegmentsForOnlyKeepDebug(std::vector<Segment *> &Segments,
uint64_t HdrEnd) {
uint64_t MaxOffset = 0;
for (Segment *Seg : Segments) {
- // An empty segment contains no section (see sectionWithinSegment). If it
- // has a parent segment, copy the parent segment's offset field. This works
- // for empty PT_TLS. We don't handle empty segments without a parent for
- // now.
- if (Seg->ParentSegment != nullptr && Seg->MemSize == 0)
- Seg->Offset = Seg->ParentSegment->Offset;
-
- const SectionBase *FirstSec = Seg->firstSection();
- if (Seg->Type == PT_PHDR || !FirstSec)
+ if (Seg->Type == PT_PHDR)
continue;
- uint64_t Offset = FirstSec->Offset;
+ // The segment offset is generally the offset of the first section.
+ //
+ // For a segment containing no section (see sectionWithinSegment), if it has
+ // a parent segment, copy the parent segment's offset field. This works for
+ // empty PT_TLS. If no parent segment, use 0: the segment is not useful for
+ // debugging anyway.
+ const SectionBase *FirstSec = Seg->firstSection();
+ uint64_t Offset =
+ FirstSec ? FirstSec->Offset
+ : (Seg->ParentSegment ? Seg->ParentSegment->Offset : 0);
uint64_t FileSize = 0;
for (const SectionBase *Sec : Seg->Sections) {
uint64_t Size = Sec->Type == SHT_NOBITS ? 0 : Sec->Size;
@@ -2409,7 +2423,11 @@ template <class ELFT> Error ELFWriter<ELFT>::write() {
return E;
if (WriteSectionHeaders)
writeShdrs();
- return Buf.commit();
+
+ // TODO: Implement direct writing to the output stream (without intermediate
+ // memory buffer Buf).
+ Out.write(Buf->getBufferStart(), Buf->getBufferSize());
+ return Error::success();
}
static Error removeUnneededSections(Object &Obj) {
@@ -2450,8 +2468,10 @@ template <class ELFT> Error ELFWriter<ELFT>::finalize() {
bool NeedsLargeIndexes = false;
if (Obj.sections().size() >= SHN_LORESERVE) {
SectionTableRef Sections = Obj.sections();
+ // Sections doesn't include the null section header, so account for this
+ // when skipping the first N sections.
NeedsLargeIndexes =
- any_of(drop_begin(Sections, SHN_LORESERVE),
+ any_of(drop_begin(Sections, SHN_LORESERVE - 1),
[](const SectionBase &Sec) { return Sec.HasSymbol; });
// TODO: handle case where only one section needs the large index table but
// only needs it because the large index table hasn't been removed yet.
@@ -2529,9 +2549,14 @@ template <class ELFT> Error ELFWriter<ELFT>::finalize() {
Sec.finalize();
}
- if (Error E = Buf.allocate(totalSize()))
- return E;
- SecWriter = std::make_unique<ELFSectionWriter<ELFT>>(Buf);
+ size_t TotalSize = totalSize();
+ Buf = WritableMemoryBuffer::getNewMemBuffer(TotalSize);
+ if (!Buf)
+ return createStringError(errc::not_enough_memory,
+ "failed to allocate memory buffer of " +
+ Twine::utohexstr(TotalSize) + " bytes");
+
+ SecWriter = std::make_unique<ELFSectionWriter<ELFT>>(*Buf);
return Error::success();
}
@@ -2540,7 +2565,10 @@ Error BinaryWriter::write() {
if (Error Err = Sec.accept(*SecWriter))
return Err;
- return Buf.commit();
+ // TODO: Implement direct writing to the output stream (without intermediate
+ // memory buffer Buf).
+ Out.write(Buf->getBufferStart(), Buf->getBufferSize());
+ return Error::success();
}
Error BinaryWriter::finalize() {
@@ -2553,7 +2581,7 @@ Error BinaryWriter::finalize() {
if (Sec.ParentSegment != nullptr)
Sec.Addr =
Sec.Offset - Sec.ParentSegment->Offset + Sec.ParentSegment->PAddr;
- if (Sec.Size > 0)
+ if (Sec.Type != SHT_NOBITS && Sec.Size > 0)
MinAddr = std::min(MinAddr, Sec.Addr);
}
@@ -2568,9 +2596,12 @@ Error BinaryWriter::finalize() {
TotalSize = std::max(TotalSize, Sec.Offset + Sec.Size);
}
- if (Error E = Buf.allocate(TotalSize))
- return E;
- SecWriter = std::make_unique<BinarySectionWriter>(Buf);
+ Buf = WritableMemoryBuffer::getNewMemBuffer(TotalSize);
+ if (!Buf)
+ return createStringError(errc::not_enough_memory,
+ "failed to allocate memory buffer of " +
+ Twine::utohexstr(TotalSize) + " bytes");
+ SecWriter = std::make_unique<BinarySectionWriter>(*Buf);
return Error::success();
}
@@ -2608,7 +2639,7 @@ uint64_t IHexWriter::writeEndOfFileRecord(uint8_t *Buf) {
}
Error IHexWriter::write() {
- IHexSectionWriter Writer(Buf);
+ IHexSectionWriter Writer(*Buf);
// Write sections.
for (const SectionBase *Sec : Sections)
if (Error Err = Sec->accept(Writer))
@@ -2616,11 +2647,17 @@ Error IHexWriter::write() {
uint64_t Offset = Writer.getBufferOffset();
// Write entry point address.
- Offset += writeEntryPointRecord(Buf.getBufferStart() + Offset);
+ Offset += writeEntryPointRecord(
+ reinterpret_cast<uint8_t *>(Buf->getBufferStart()) + Offset);
// Write EOF.
- Offset += writeEndOfFileRecord(Buf.getBufferStart() + Offset);
+ Offset += writeEndOfFileRecord(
+ reinterpret_cast<uint8_t *>(Buf->getBufferStart()) + Offset);
assert(Offset == TotalSize);
- return Buf.commit();
+
+ // TODO: Implement direct writing to the output stream (without intermediate
+ // memory buffer Buf).
+ Out.write(Buf->getBufferStart(), Buf->getBufferSize());
+ return Error::success();
}
Error IHexWriter::checkSection(const SectionBase &Sec) {
@@ -2634,37 +2671,27 @@ Error IHexWriter::checkSection(const SectionBase &Sec) {
}
Error IHexWriter::finalize() {
- bool UseSegments = false;
- auto ShouldWrite = [](const SectionBase &Sec) {
- return (Sec.Flags & ELF::SHF_ALLOC) && (Sec.Type != ELF::SHT_NOBITS);
- };
- auto IsInPtLoad = [](const SectionBase &Sec) {
- return Sec.ParentSegment && Sec.ParentSegment->Type == ELF::PT_LOAD;
- };
-
// We can't write 64-bit addresses.
if (addressOverflows32bit(Obj.Entry))
return createStringError(errc::invalid_argument,
- "Entry point address 0x%llx overflows 32 bits.",
+ "Entry point address 0x%llx overflows 32 bits",
Obj.Entry);
- // If any section we're to write has segment then we
- // switch to using physical addresses. Otherwise we
- // use section virtual address.
- for (const SectionBase &Sec : Obj.sections())
- if (ShouldWrite(Sec) && IsInPtLoad(Sec)) {
- UseSegments = true;
- break;
- }
-
for (const SectionBase &Sec : Obj.sections())
- if (ShouldWrite(Sec) && (!UseSegments || IsInPtLoad(Sec))) {
+ if ((Sec.Flags & ELF::SHF_ALLOC) && Sec.Type != ELF::SHT_NOBITS &&
+ Sec.Size > 0) {
if (Error E = checkSection(Sec))
return E;
Sections.insert(&Sec);
}
- IHexSectionWriterBase LengthCalc(Buf);
+ std::unique_ptr<WritableMemoryBuffer> EmptyBuffer =
+ WritableMemoryBuffer::getNewMemBuffer(0);
+ if (!EmptyBuffer)
+ return createStringError(errc::not_enough_memory,
+ "failed to allocate memory buffer of 0 bytes");
+
+ IHexSectionWriterBase LengthCalc(*EmptyBuffer);
for (const SectionBase *Sec : Sections)
if (Error Err = Sec->accept(LengthCalc))
return Err;
@@ -2674,11 +2701,20 @@ Error IHexWriter::finalize() {
TotalSize = LengthCalc.getBufferOffset() +
(Obj.Entry ? IHexRecord::getLineLength(4) : 0) +
IHexRecord::getLineLength(0);
- if (Error E = Buf.allocate(TotalSize))
- return E;
+
+ Buf = WritableMemoryBuffer::getNewMemBuffer(TotalSize);
+ if (!Buf)
+ return createStringError(errc::not_enough_memory,
+ "failed to allocate memory buffer of " +
+ Twine::utohexstr(TotalSize) + " bytes");
+
return Error::success();
}
+namespace llvm {
+namespace objcopy {
+namespace elf {
+
template class ELFBuilder<ELF64LE>;
template class ELFBuilder<ELF64BE>;
template class ELFBuilder<ELF32LE>;
diff --git a/llvm/tools/llvm-objcopy/ELF/Object.h b/llvm/tools/llvm-objcopy/ELF/Object.h
index 0205c2d4f398..6fd26afa3ca1 100644
--- a/llvm/tools/llvm-objcopy/ELF/Object.h
+++ b/llvm/tools/llvm-objcopy/ELF/Object.h
@@ -9,8 +9,7 @@
#ifndef LLVM_TOOLS_OBJCOPY_OBJECT_H
#define LLVM_TOOLS_OBJCOPY_OBJECT_H
-#include "Buffer.h"
-#include "CopyConfig.h"
+#include "CommonConfig.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
@@ -19,6 +18,7 @@
#include "llvm/Object/ELFObjectFile.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/FileOutputBuffer.h"
+#include "llvm/Support/MemoryBuffer.h"
#include <cstddef>
#include <cstdint>
#include <functional>
@@ -106,7 +106,7 @@ public:
class SectionWriter : public SectionVisitor {
protected:
- Buffer &Out;
+ WritableMemoryBuffer &Out;
public:
virtual ~SectionWriter() = default;
@@ -123,7 +123,7 @@ public:
virtual Error visit(const CompressedSection &Sec) override = 0;
virtual Error visit(const DecompressedSection &Sec) override = 0;
- explicit SectionWriter(Buffer &Buf) : Out(Buf) {}
+ explicit SectionWriter(WritableMemoryBuffer &Buf) : Out(Buf) {}
};
template <class ELFT> class ELFSectionWriter : public SectionWriter {
@@ -143,7 +143,7 @@ public:
Error visit(const CompressedSection &Sec) override;
Error visit(const DecompressedSection &Sec) override;
- explicit ELFSectionWriter(Buffer &Buf) : SectionWriter(Buf) {}
+ explicit ELFSectionWriter(WritableMemoryBuffer &Buf) : SectionWriter(Buf) {}
};
template <class ELFT> class ELFSectionSizer : public MutableSectionVisitor {
@@ -187,7 +187,8 @@ public:
Error visit(const CompressedSection &Sec) override;
Error visit(const DecompressedSection &Sec) override;
- explicit BinarySectionWriter(Buffer &Buf) : SectionWriter(Buf) {}
+ explicit BinarySectionWriter(WritableMemoryBuffer &Buf)
+ : SectionWriter(Buf) {}
};
using IHexLineData = SmallVector<char, 64>;
@@ -283,7 +284,8 @@ protected:
virtual void writeData(uint8_t Type, uint16_t Addr, ArrayRef<uint8_t> Data);
public:
- explicit IHexSectionWriterBase(Buffer &Buf) : BinarySectionWriter(Buf) {}
+ explicit IHexSectionWriterBase(WritableMemoryBuffer &Buf)
+ : BinarySectionWriter(Buf) {}
uint64_t getBufferOffset() const { return Offset; }
Error visit(const Section &Sec) final;
@@ -296,7 +298,7 @@ public:
// Real IHEX section writer
class IHexSectionWriter : public IHexSectionWriterBase {
public:
- IHexSectionWriter(Buffer &Buf) : IHexSectionWriterBase(Buf) {}
+ IHexSectionWriter(WritableMemoryBuffer &Buf) : IHexSectionWriterBase(Buf) {}
void writeData(uint8_t Type, uint16_t Addr, ArrayRef<uint8_t> Data) override;
Error visit(const StringTableSection &Sec) override;
@@ -305,14 +307,15 @@ public:
class Writer {
protected:
Object &Obj;
- Buffer &Buf;
+ std::unique_ptr<WritableMemoryBuffer> Buf;
+ raw_ostream &Out;
public:
virtual ~Writer();
virtual Error finalize() = 0;
virtual Error write() = 0;
- Writer(Object &O, Buffer &B) : Obj(O), Buf(B) {}
+ Writer(Object &O, raw_ostream &Out) : Obj(O), Out(Out) {}
};
template <class ELFT> class ELFWriter : public Writer {
@@ -349,7 +352,7 @@ public:
Error finalize() override;
Error write() override;
- ELFWriter(Object &Obj, Buffer &Buf, bool WSH, bool OnlyKeepDebug);
+ ELFWriter(Object &Obj, raw_ostream &Out, bool WSH, bool OnlyKeepDebug);
};
class BinaryWriter : public Writer {
@@ -362,7 +365,7 @@ public:
~BinaryWriter() {}
Error finalize() override;
Error write() override;
- BinaryWriter(Object &Obj, Buffer &Buf) : Writer(Obj, Buf) {}
+ BinaryWriter(Object &Obj, raw_ostream &Out) : Writer(Obj, Out) {}
};
class IHexWriter : public Writer {
@@ -381,7 +384,7 @@ public:
~IHexWriter() {}
Error finalize() override;
Error write() override;
- IHexWriter(Object &Obj, Buffer &Buf) : Writer(Obj, Buf) {}
+ IHexWriter(Object &Obj, raw_ostream &Out) : Writer(Obj, Out) {}
};
class SectionBase {
diff --git a/llvm/tools/llvm-objcopy/MachO/MachOConfig.h b/llvm/tools/llvm-objcopy/MachO/MachOConfig.h
new file mode 100644
index 000000000000..7c5dbfde19a0
--- /dev/null
+++ b/llvm/tools/llvm-objcopy/MachO/MachOConfig.h
@@ -0,0 +1,21 @@
+//===- MachOConfig.h --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_OBJCOPY_MACHO_MACHOCONFIG_H
+#define LLVM_TOOLS_LLVM_OBJCOPY_MACHO_MACHOCONFIG_H
+
+namespace llvm {
+namespace objcopy {
+
+// Mach-O specific configuration for copying/stripping a single file.
+struct MachOConfig {};
+
+} // namespace objcopy
+} // namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_OBJCOPY_MACHO_MACHOCONFIG_H
diff --git a/llvm/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp b/llvm/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp
index 8e2bf36238ec..6ed21806fe5e 100644
--- a/llvm/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp
+++ b/llvm/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp
@@ -11,9 +11,8 @@
#include "llvm/Support/Errc.h"
#include "llvm/Support/ErrorHandling.h"
-namespace llvm {
-namespace objcopy {
-namespace macho {
+using namespace llvm;
+using namespace llvm::objcopy::macho;
StringTableBuilder::Kind
MachOLayoutBuilder::getStringTableBuilderKind(const Object &O, bool Is64Bit) {
@@ -252,7 +251,10 @@ Error MachOLayoutBuilder::layoutTail(uint64_t Offset) {
uint64_t StartOfFunctionStarts = StartOfExportTrie + O.Exports.Trie.size();
uint64_t StartOfDataInCode =
StartOfFunctionStarts + O.FunctionStarts.Data.size();
- uint64_t StartOfSymbols = StartOfDataInCode + O.DataInCode.Data.size();
+ uint64_t StartOfLinkerOptimizationHint =
+ StartOfDataInCode + O.DataInCode.Data.size();
+ uint64_t StartOfSymbols =
+ StartOfLinkerOptimizationHint + O.LinkerOptimizationHint.Data.size();
uint64_t StartOfIndirectSymbols =
StartOfSymbols + NListSize * O.SymTable.Symbols.size();
uint64_t StartOfSymbolStrings =
@@ -321,6 +323,11 @@ Error MachOLayoutBuilder::layoutTail(uint64_t Offset) {
MLC.linkedit_data_command_data.dataoff = StartOfDataInCode;
MLC.linkedit_data_command_data.datasize = O.DataInCode.Data.size();
break;
+ case MachO::LC_LINKER_OPTIMIZATION_HINT:
+ MLC.linkedit_data_command_data.dataoff = StartOfLinkerOptimizationHint;
+ MLC.linkedit_data_command_data.datasize =
+ O.LinkerOptimizationHint.Data.size();
+ break;
case MachO::LC_FUNCTION_STARTS:
MLC.linkedit_data_command_data.dataoff = StartOfFunctionStarts;
MLC.linkedit_data_command_data.datasize = O.FunctionStarts.Data.size();
@@ -371,6 +378,8 @@ Error MachOLayoutBuilder::layoutTail(uint64_t Offset) {
case MachO::LC_LOAD_WEAK_DYLIB:
case MachO::LC_UUID:
case MachO::LC_SOURCE_VERSION:
+ case MachO::LC_THREAD:
+ case MachO::LC_UNIXTHREAD:
// Nothing to update.
break;
default:
@@ -392,7 +401,3 @@ Error MachOLayoutBuilder::layout() {
Offset = layoutRelocations(Offset);
return layoutTail(Offset);
}
-
-} // end namespace macho
-} // end namespace objcopy
-} // end namespace llvm
diff --git a/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp b/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp
index fef4a0ae5594..823306916bbe 100644
--- a/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp
+++ b/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp
@@ -7,22 +7,25 @@
//===----------------------------------------------------------------------===//
#include "MachOObjcopy.h"
-#include "../CopyConfig.h"
#include "../llvm-objcopy.h"
+#include "CommonConfig.h"
#include "MachOReader.h"
#include "MachOWriter.h"
+#include "MultiFormatConfig.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/Object/ArchiveWriter.h"
#include "llvm/Object/MachOUniversal.h"
#include "llvm/Object/MachOUniversalWriter.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/Error.h"
+#include "llvm/Support/FileOutputBuffer.h"
+#include "llvm/Support/SmallVectorMemoryBuffer.h"
-namespace llvm {
-namespace objcopy {
-namespace macho {
+using namespace llvm;
+using namespace llvm::objcopy;
+using namespace llvm::objcopy::macho;
+using namespace llvm::object;
-using namespace object;
using SectionPred = std::function<bool(const std::unique_ptr<Section> &Sec)>;
using LoadCommandPred = std::function<bool(const LoadCommand &LC)>;
@@ -46,7 +49,7 @@ static StringRef getPayloadString(const LoadCommand &LC) {
.rtrim('\0');
}
-static Error removeSections(const CopyConfig &Config, Object &Obj) {
+static Error removeSections(const CommonConfig &Config, Object &Obj) {
SectionPred RemovePred = [](const std::unique_ptr<Section> &) {
return false;
};
@@ -77,14 +80,14 @@ static Error removeSections(const CopyConfig &Config, Object &Obj) {
return Obj.removeSections(RemovePred);
}
-static void markSymbols(const CopyConfig &Config, Object &Obj) {
+static void markSymbols(const CommonConfig &, Object &Obj) {
// Symbols referenced from the indirect symbol table must not be removed.
for (IndirectSymbolEntry &ISE : Obj.IndirectSymTable.Symbols)
if (ISE.Symbol)
(*ISE.Symbol)->Referenced = true;
}
-static void updateAndRemoveSymbols(const CopyConfig &Config, Object &Obj) {
+static void updateAndRemoveSymbols(const CommonConfig &Config, Object &Obj) {
for (SymbolEntry &Sym : Obj.SymTable) {
auto I = Config.SymbolsToRename.find(Sym.Name);
if (I != Config.SymbolsToRename.end())
@@ -94,6 +97,10 @@ static void updateAndRemoveSymbols(const CopyConfig &Config, Object &Obj) {
auto RemovePred = [Config, &Obj](const std::unique_ptr<SymbolEntry> &N) {
if (N->Referenced)
return false;
+ if (Config.KeepUndefined && N->isUndefinedSymbol())
+ return false;
+ if (N->n_desc & MachO::REFERENCED_DYNAMICALLY)
+ return false;
if (Config.StripAll)
return true;
if (Config.DiscardMode == DiscardType::All && !(N->n_type & MachO::N_EXT))
@@ -132,7 +139,7 @@ static LoadCommand buildRPathLoadCommand(StringRef Path) {
return LC;
}
-static Error processLoadCommands(const CopyConfig &Config, Object &Obj) {
+static Error processLoadCommands(const CommonConfig &Config, Object &Obj) {
// Remove RPaths.
DenseSet<StringRef> RPathsToRemove(Config.RPathsToRemove.begin(),
Config.RPathsToRemove.end());
@@ -326,26 +333,7 @@ static Error isValidMachOCannonicalName(StringRef Name) {
return Error::success();
}
-static Error handleArgs(const CopyConfig &Config, Object &Obj) {
- if (Config.AllowBrokenLinks || !Config.BuildIdLinkDir.empty() ||
- Config.BuildIdLinkInput || Config.BuildIdLinkOutput ||
- !Config.SplitDWO.empty() || !Config.SymbolsPrefix.empty() ||
- !Config.AllocSectionsPrefix.empty() || !Config.KeepSection.empty() ||
- Config.NewSymbolVisibility || !Config.SymbolsToGlobalize.empty() ||
- !Config.SymbolsToKeep.empty() || !Config.SymbolsToLocalize.empty() ||
- !Config.SymbolsToWeaken.empty() || !Config.SymbolsToKeepGlobal.empty() ||
- !Config.SectionsToRename.empty() ||
- !Config.UnneededSymbolsToRemove.empty() ||
- !Config.SetSectionAlignment.empty() || !Config.SetSectionFlags.empty() ||
- Config.ExtractDWO || Config.LocalizeHidden || Config.PreserveDates ||
- Config.StripAllGNU || Config.StripDWO || Config.StripNonAlloc ||
- Config.StripSections || Config.Weaken || Config.DecompressDebugSections ||
- Config.StripUnneeded || Config.DiscardMode == DiscardType::Locals ||
- !Config.SymbolsToAdd.empty() || Config.EntryExpr) {
- return createStringError(llvm::errc::invalid_argument,
- "option not supported by llvm-objcopy for MachO");
- }
-
+static Error handleArgs(const CommonConfig &Config, Object &Obj) {
// Dump sections before add/remove for compatibility with GNU objcopy.
for (StringRef Flag : Config.DumpSection) {
StringRef SectionName;
@@ -385,8 +373,10 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj) {
return Error::success();
}
-Error executeObjcopyOnBinary(const CopyConfig &Config,
- object::MachOObjectFile &In, Buffer &Out) {
+Error objcopy::macho::executeObjcopyOnBinary(const CommonConfig &Config,
+ const MachOConfig &,
+ object::MachOObjectFile &In,
+ raw_ostream &Out) {
MachOReader Reader(In);
Expected<std::unique_ptr<Object>> O = Reader.create();
if (!O)
@@ -414,9 +404,9 @@ Error executeObjcopyOnBinary(const CopyConfig &Config,
return Writer.write();
}
-Error executeObjcopyOnMachOUniversalBinary(CopyConfig &Config,
- const MachOUniversalBinary &In,
- Buffer &Out) {
+Error objcopy::macho::executeObjcopyOnMachOUniversalBinary(
+ const MultiFormatConfig &Config, const MachOUniversalBinary &In,
+ raw_ostream &Out) {
SmallVector<OwningBinary<Binary>, 2> Binaries;
SmallVector<Slice, 2> Slices;
for (const auto &O : In.objects()) {
@@ -429,7 +419,7 @@ Error executeObjcopyOnMachOUniversalBinary(CopyConfig &Config,
Expected<std::unique_ptr<MemoryBuffer>> OutputBufferOrErr =
writeArchiveToBuffer(*NewArchiveMembersOrErr,
(*ArOrErr)->hasSymbolTable(), (*ArOrErr)->kind(),
- Config.DeterministicArchives,
+ Config.getCommonConfig().DeterministicArchives,
(*ArOrErr)->isThin());
if (!OutputBufferOrErr)
return OutputBufferOrErr.takeError();
@@ -453,36 +443,39 @@ Error executeObjcopyOnMachOUniversalBinary(CopyConfig &Config,
Expected<std::unique_ptr<MachOObjectFile>> ObjOrErr = O.getAsObjectFile();
if (!ObjOrErr) {
consumeError(ObjOrErr.takeError());
- return createStringError(std::errc::invalid_argument,
- "slice for '%s' of the universal Mach-O binary "
- "'%s' is not a Mach-O object or an archive",
- O.getArchFlagName().c_str(),
- Config.InputFilename.str().c_str());
+ return createStringError(
+ std::errc::invalid_argument,
+ "slice for '%s' of the universal Mach-O binary "
+ "'%s' is not a Mach-O object or an archive",
+ O.getArchFlagName().c_str(),
+ Config.getCommonConfig().InputFilename.str().c_str());
}
std::string ArchFlagName = O.getArchFlagName();
- MemBuffer MB(ArchFlagName);
- if (Error E = executeObjcopyOnBinary(Config, **ObjOrErr, MB))
+
+ SmallVector<char, 0> Buffer;
+ raw_svector_ostream MemStream(Buffer);
+
+ Expected<const MachOConfig &> MachO = Config.getMachOConfig();
+ if (!MachO)
+ return MachO.takeError();
+
+ if (Error E = executeObjcopyOnBinary(Config.getCommonConfig(), *MachO,
+ **ObjOrErr, MemStream))
return E;
- std::unique_ptr<WritableMemoryBuffer> OutputBuffer =
- MB.releaseMemoryBuffer();
- Expected<std::unique_ptr<Binary>> BinaryOrErr =
- object::createBinary(*OutputBuffer);
+
+ std::unique_ptr<MemoryBuffer> MB =
+ std::make_unique<SmallVectorMemoryBuffer>(std::move(Buffer),
+ ArchFlagName);
+ Expected<std::unique_ptr<Binary>> BinaryOrErr = object::createBinary(*MB);
if (!BinaryOrErr)
return BinaryOrErr.takeError();
- Binaries.emplace_back(std::move(*BinaryOrErr), std::move(OutputBuffer));
+ Binaries.emplace_back(std::move(*BinaryOrErr), std::move(MB));
Slices.emplace_back(*cast<MachOObjectFile>(Binaries.back().getBinary()),
O.getAlign());
}
- Expected<std::unique_ptr<MemoryBuffer>> B =
- writeUniversalBinaryToBuffer(Slices);
- if (!B)
- return B.takeError();
- if (Error E = Out.allocate((*B)->getBufferSize()))
- return E;
- memcpy(Out.getBufferStart(), (*B)->getBufferStart(), (*B)->getBufferSize());
- return Out.commit();
-}
-} // end namespace macho
-} // end namespace objcopy
-} // end namespace llvm
+ if (Error Err = writeUniversalBinaryToStream(Slices, Out))
+ return Err;
+
+ return Error::success();
+}
diff --git a/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.h b/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.h
index c3f5391f79b6..e30940a8d6eb 100644
--- a/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.h
+++ b/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.h
@@ -11,6 +11,7 @@
namespace llvm {
class Error;
+class raw_ostream;
namespace object {
class MachOObjectFile;
@@ -18,15 +19,17 @@ class MachOUniversalBinary;
} // end namespace object
namespace objcopy {
-struct CopyConfig;
-class Buffer;
+struct CommonConfig;
+struct MachOConfig;
+class MultiFormatConfig;
namespace macho {
-Error executeObjcopyOnBinary(const CopyConfig &Config,
- object::MachOObjectFile &In, Buffer &Out);
+Error executeObjcopyOnBinary(const CommonConfig &Config, const MachOConfig &,
+ object::MachOObjectFile &In, raw_ostream &Out);
Error executeObjcopyOnMachOUniversalBinary(
- CopyConfig &Config, const object::MachOUniversalBinary &In, Buffer &Out);
+ const MultiFormatConfig &Config, const object::MachOUniversalBinary &In,
+ raw_ostream &Out);
} // end namespace macho
} // end namespace objcopy
diff --git a/llvm/tools/llvm-objcopy/MachO/MachOReader.cpp b/llvm/tools/llvm-objcopy/MachO/MachOReader.cpp
index 548a85bd497e..7d1c29b42c2e 100644
--- a/llvm/tools/llvm-objcopy/MachO/MachOReader.cpp
+++ b/llvm/tools/llvm-objcopy/MachO/MachOReader.cpp
@@ -13,9 +13,9 @@
#include "llvm/Support/Errc.h"
#include <memory>
-namespace llvm {
-namespace objcopy {
-namespace macho {
+using namespace llvm;
+using namespace llvm::objcopy;
+using namespace llvm::objcopy::macho;
void MachOReader::readHeader(Object &O) const {
O.Header.Magic = MachOObj.getHeader().magic;
@@ -28,7 +28,7 @@ void MachOReader::readHeader(Object &O) const {
}
template <typename SectionType>
-Section constructSectionCommon(SectionType Sec, uint32_t Index) {
+static Section constructSectionCommon(const SectionType &Sec, uint32_t Index) {
StringRef SegName(Sec.segname, strnlen(Sec.segname, sizeof(Sec.segname)));
StringRef SectName(Sec.sectname, strnlen(Sec.sectname, sizeof(Sec.sectname)));
Section S(SegName, SectName);
@@ -46,39 +46,34 @@ Section constructSectionCommon(SectionType Sec, uint32_t Index) {
return S;
}
-template <typename SectionType>
-Section constructSection(SectionType Sec, uint32_t Index);
-
-template <> Section constructSection(MachO::section Sec, uint32_t Index) {
+Section constructSection(const MachO::section &Sec, uint32_t Index) {
return constructSectionCommon(Sec, Index);
}
-template <> Section constructSection(MachO::section_64 Sec, uint32_t Index) {
+Section constructSection(const MachO::section_64 &Sec, uint32_t Index) {
Section S = constructSectionCommon(Sec, Index);
S.Reserved3 = Sec.reserved3;
return S;
}
template <typename SectionType, typename SegmentType>
-Expected<std::vector<std::unique_ptr<Section>>>
-extractSections(const object::MachOObjectFile::LoadCommandInfo &LoadCmd,
- const object::MachOObjectFile &MachOObj,
- uint32_t &NextSectionIndex) {
- auto End = LoadCmd.Ptr + LoadCmd.C.cmdsize;
- const SectionType *Curr =
- reinterpret_cast<const SectionType *>(LoadCmd.Ptr + sizeof(SegmentType));
+Expected<std::vector<std::unique_ptr<Section>>> static extractSections(
+ const object::MachOObjectFile::LoadCommandInfo &LoadCmd,
+ const object::MachOObjectFile &MachOObj, uint32_t &NextSectionIndex) {
std::vector<std::unique_ptr<Section>> Sections;
- for (; reinterpret_cast<const void *>(Curr) < End; Curr++) {
- if (MachOObj.isLittleEndian() != sys::IsLittleEndianHost) {
- SectionType Sec;
- memcpy((void *)&Sec, Curr, sizeof(SectionType));
+ for (auto Curr = reinterpret_cast<const SectionType *>(LoadCmd.Ptr +
+ sizeof(SegmentType)),
+ End = reinterpret_cast<const SectionType *>(LoadCmd.Ptr +
+ LoadCmd.C.cmdsize);
+ Curr < End; ++Curr) {
+ SectionType Sec;
+ memcpy((void *)&Sec, Curr, sizeof(SectionType));
+
+ if (MachOObj.isLittleEndian() != sys::IsLittleEndianHost)
MachO::swapStruct(Sec);
- Sections.push_back(
- std::make_unique<Section>(constructSection(Sec, NextSectionIndex)));
- } else {
- Sections.push_back(
- std::make_unique<Section>(constructSection(*Curr, NextSectionIndex)));
- }
+
+ Sections.push_back(
+ std::make_unique<Section>(constructSection(Sec, NextSectionIndex)));
Section &S = *Sections.back();
@@ -95,6 +90,7 @@ extractSections(const object::MachOObjectFile::LoadCommandInfo &LoadCmd,
S.Content =
StringRef(reinterpret_cast<const char *>(Data->data()), Data->size());
+ const uint32_t CPUType = MachOObj.getHeader().cputype;
S.Relocations.reserve(S.NReloc);
for (auto RI = MachOObj.section_rel_begin(SecRef->getRawDataRefImpl()),
RE = MachOObj.section_rel_end(SecRef->getRawDataRefImpl());
@@ -103,6 +99,10 @@ extractSections(const object::MachOObjectFile::LoadCommandInfo &LoadCmd,
R.Symbol = nullptr; // We'll fill this field later.
R.Info = MachOObj.getRelocation(RI->getRawDataRefImpl());
R.Scattered = MachOObj.isRelocationScattered(R.Info);
+ unsigned Type = MachOObj.getAnyRelocationType(R.Info);
+ // TODO Support CPU_TYPE_ARM.
+ R.IsAddend = !R.Scattered && (CPUType == MachO::CPU_TYPE_ARM64 &&
+ Type == MachO::ARM64_RELOC_ADDEND);
R.Extern = !R.Scattered && MachOObj.getPlainRelocationExternal(R.Info);
S.Relocations.push_back(R);
}
@@ -151,6 +151,9 @@ Error MachOReader::readLoadCommands(Object &O) const {
case MachO::LC_DATA_IN_CODE:
O.DataInCodeCommandIndex = O.LoadCommands.size();
break;
+ case MachO::LC_LINKER_OPTIMIZATION_HINT:
+ O.LinkerOptimizationHintCommandIndex = O.LoadCommands.size();
+ break;
case MachO::LC_FUNCTION_STARTS:
O.FunctionStartsCommandIndex = O.LoadCommands.size();
break;
@@ -223,7 +226,7 @@ void MachOReader::setSymbolInRelocationInfo(Object &O) const {
for (LoadCommand &LC : O.LoadCommands)
for (std::unique_ptr<Section> &Sec : LC.Sections)
for (auto &Reloc : Sec->Relocations)
- if (!Reloc.Scattered) {
+ if (!Reloc.Scattered && !Reloc.IsAddend) {
const uint32_t SymbolNum =
Reloc.getPlainRelocationSymbolNum(MachOObj.isLittleEndian());
if (Reloc.Extern) {
@@ -276,6 +279,11 @@ void MachOReader::readDataInCodeData(Object &O) const {
return readLinkData(O, O.DataInCodeCommandIndex, O.DataInCode);
}
+void MachOReader::readLinkerOptimizationHint(Object &O) const {
+ return readLinkData(O, O.LinkerOptimizationHintCommandIndex,
+ O.LinkerOptimizationHint);
+}
+
void MachOReader::readFunctionStartsData(Object &O) const {
return readLinkData(O, O.FunctionStartsCommandIndex, O.FunctionStarts);
}
@@ -330,12 +338,9 @@ Expected<std::unique_ptr<Object>> MachOReader::create() const {
readExportInfo(*Obj);
readCodeSignature(*Obj);
readDataInCodeData(*Obj);
+ readLinkerOptimizationHint(*Obj);
readFunctionStartsData(*Obj);
readIndirectSymbolTable(*Obj);
readSwiftVersion(*Obj);
return std::move(Obj);
}
-
-} // end namespace macho
-} // end namespace objcopy
-} // end namespace llvm
diff --git a/llvm/tools/llvm-objcopy/MachO/MachOReader.h b/llvm/tools/llvm-objcopy/MachO/MachOReader.h
index b446e02865e5..ca3a0214cb6d 100644
--- a/llvm/tools/llvm-objcopy/MachO/MachOReader.h
+++ b/llvm/tools/llvm-objcopy/MachO/MachOReader.h
@@ -39,6 +39,7 @@ class MachOReader : public Reader {
void readLinkData(Object &O, Optional<size_t> LCIndex, LinkData &LD) const;
void readCodeSignature(Object &O) const;
void readDataInCodeData(Object &O) const;
+ void readLinkerOptimizationHint(Object &O) const;
void readFunctionStartsData(Object &O) const;
void readIndirectSymbolTable(Object &O) const;
void readSwiftVersion(Object &O) const;
diff --git a/llvm/tools/llvm-objcopy/MachO/MachOWriter.cpp b/llvm/tools/llvm-objcopy/MachO/MachOWriter.cpp
index 56dd08df3b09..295098ed4118 100644
--- a/llvm/tools/llvm-objcopy/MachO/MachOWriter.cpp
+++ b/llvm/tools/llvm-objcopy/MachO/MachOWriter.cpp
@@ -16,9 +16,8 @@
#include "llvm/Support/ErrorHandling.h"
#include <memory>
-namespace llvm {
-namespace objcopy {
-namespace macho {
+using namespace llvm;
+using namespace llvm::objcopy::macho;
size_t MachOWriter::headerSize() const {
return Is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header);
@@ -108,6 +107,16 @@ size_t MachOWriter::totalSize() const {
LinkEditDataCommand.datasize);
}
+ if (O.LinkerOptimizationHintCommandIndex) {
+ const MachO::linkedit_data_command &LinkEditDataCommand =
+ O.LoadCommands[*O.LinkerOptimizationHintCommandIndex]
+ .MachOLoadCommand.linkedit_data_command_data;
+
+ if (LinkEditDataCommand.dataoff)
+ Ends.push_back(LinkEditDataCommand.dataoff +
+ LinkEditDataCommand.datasize);
+ }
+
if (O.FunctionStartsCommandIndex) {
const MachO::linkedit_data_command &LinkEditDataCommand =
O.LoadCommands[*O.FunctionStartsCommandIndex]
@@ -159,11 +168,12 @@ void MachOWriter::writeHeader() {
auto HeaderSize =
Is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header);
- memcpy(B.getBufferStart(), &Header, HeaderSize);
+ memcpy(Buf->getBufferStart(), &Header, HeaderSize);
}
void MachOWriter::writeLoadCommands() {
- uint8_t *Begin = B.getBufferStart() + headerSize();
+ uint8_t *Begin =
+ reinterpret_cast<uint8_t *>(Buf->getBufferStart()) + headerSize();
for (const LoadCommand &LC : O.LoadCommands) {
// Construct a load command.
MachO::macho_load_command MLC = LC.MachOLoadCommand;
@@ -257,11 +267,11 @@ void MachOWriter::writeSections() {
assert(Sec->Offset && "Section offset can not be zero");
assert((Sec->Size == Sec->Content.size()) && "Incorrect section size");
- memcpy(B.getBufferStart() + Sec->Offset, Sec->Content.data(),
+ memcpy(Buf->getBufferStart() + Sec->Offset, Sec->Content.data(),
Sec->Content.size());
for (size_t Index = 0; Index < Sec->Relocations.size(); ++Index) {
RelocationInfo RelocInfo = Sec->Relocations[Index];
- if (!RelocInfo.Scattered) {
+ if (!RelocInfo.Scattered && !RelocInfo.IsAddend) {
const uint32_t SymbolNum = RelocInfo.Extern
? (*RelocInfo.Symbol)->Index
: (*RelocInfo.Sec)->Index;
@@ -270,7 +280,7 @@ void MachOWriter::writeSections() {
if (IsLittleEndian != sys::IsLittleEndianHost)
MachO::swapStruct(
reinterpret_cast<MachO::any_relocation_info &>(RelocInfo.Info));
- memcpy(B.getBufferStart() + Sec->RelOff +
+ memcpy(Buf->getBufferStart() + Sec->RelOff +
Index * sizeof(MachO::any_relocation_info),
&RelocInfo.Info, sizeof(RelocInfo.Info));
}
@@ -300,7 +310,7 @@ void MachOWriter::writeStringTable() {
O.LoadCommands[*O.SymTabCommandIndex]
.MachOLoadCommand.symtab_command_data;
- uint8_t *StrTable = (uint8_t *)B.getBufferStart() + SymTabCommand.stroff;
+ uint8_t *StrTable = (uint8_t *)Buf->getBufferStart() + SymTabCommand.stroff;
LayoutBuilder.getStringTableBuilder().write(StrTable);
}
@@ -311,7 +321,7 @@ void MachOWriter::writeSymbolTable() {
O.LoadCommands[*O.SymTabCommandIndex]
.MachOLoadCommand.symtab_command_data;
- char *SymTable = (char *)B.getBufferStart() + SymTabCommand.symoff;
+ char *SymTable = (char *)Buf->getBufferStart() + SymTabCommand.symoff;
for (auto Iter = O.SymTable.Symbols.begin(), End = O.SymTable.Symbols.end();
Iter != End; Iter++) {
SymbolEntry *Sym = Iter->get();
@@ -330,7 +340,7 @@ void MachOWriter::writeRebaseInfo() {
const MachO::dyld_info_command &DyLdInfoCommand =
O.LoadCommands[*O.DyLdInfoCommandIndex]
.MachOLoadCommand.dyld_info_command_data;
- char *Out = (char *)B.getBufferStart() + DyLdInfoCommand.rebase_off;
+ char *Out = (char *)Buf->getBufferStart() + DyLdInfoCommand.rebase_off;
assert((DyLdInfoCommand.rebase_size == O.Rebases.Opcodes.size()) &&
"Incorrect rebase opcodes size");
memcpy(Out, O.Rebases.Opcodes.data(), O.Rebases.Opcodes.size());
@@ -342,7 +352,7 @@ void MachOWriter::writeBindInfo() {
const MachO::dyld_info_command &DyLdInfoCommand =
O.LoadCommands[*O.DyLdInfoCommandIndex]
.MachOLoadCommand.dyld_info_command_data;
- char *Out = (char *)B.getBufferStart() + DyLdInfoCommand.bind_off;
+ char *Out = (char *)Buf->getBufferStart() + DyLdInfoCommand.bind_off;
assert((DyLdInfoCommand.bind_size == O.Binds.Opcodes.size()) &&
"Incorrect bind opcodes size");
memcpy(Out, O.Binds.Opcodes.data(), O.Binds.Opcodes.size());
@@ -354,7 +364,7 @@ void MachOWriter::writeWeakBindInfo() {
const MachO::dyld_info_command &DyLdInfoCommand =
O.LoadCommands[*O.DyLdInfoCommandIndex]
.MachOLoadCommand.dyld_info_command_data;
- char *Out = (char *)B.getBufferStart() + DyLdInfoCommand.weak_bind_off;
+ char *Out = (char *)Buf->getBufferStart() + DyLdInfoCommand.weak_bind_off;
assert((DyLdInfoCommand.weak_bind_size == O.WeakBinds.Opcodes.size()) &&
"Incorrect weak bind opcodes size");
memcpy(Out, O.WeakBinds.Opcodes.data(), O.WeakBinds.Opcodes.size());
@@ -366,7 +376,7 @@ void MachOWriter::writeLazyBindInfo() {
const MachO::dyld_info_command &DyLdInfoCommand =
O.LoadCommands[*O.DyLdInfoCommandIndex]
.MachOLoadCommand.dyld_info_command_data;
- char *Out = (char *)B.getBufferStart() + DyLdInfoCommand.lazy_bind_off;
+ char *Out = (char *)Buf->getBufferStart() + DyLdInfoCommand.lazy_bind_off;
assert((DyLdInfoCommand.lazy_bind_size == O.LazyBinds.Opcodes.size()) &&
"Incorrect lazy bind opcodes size");
memcpy(Out, O.LazyBinds.Opcodes.data(), O.LazyBinds.Opcodes.size());
@@ -378,7 +388,7 @@ void MachOWriter::writeExportInfo() {
const MachO::dyld_info_command &DyLdInfoCommand =
O.LoadCommands[*O.DyLdInfoCommandIndex]
.MachOLoadCommand.dyld_info_command_data;
- char *Out = (char *)B.getBufferStart() + DyLdInfoCommand.export_off;
+ char *Out = (char *)Buf->getBufferStart() + DyLdInfoCommand.export_off;
assert((DyLdInfoCommand.export_size == O.Exports.Trie.size()) &&
"Incorrect export trie size");
memcpy(Out, O.Exports.Trie.data(), O.Exports.Trie.size());
@@ -393,7 +403,7 @@ void MachOWriter::writeIndirectSymbolTable() {
.MachOLoadCommand.dysymtab_command_data;
uint32_t *Out =
- (uint32_t *)(B.getBufferStart() + DySymTabCommand.indirectsymoff);
+ (uint32_t *)(Buf->getBufferStart() + DySymTabCommand.indirectsymoff);
for (const IndirectSymbolEntry &Sym : O.IndirectSymTable.Symbols) {
uint32_t Entry = (Sym.Symbol) ? (*Sym.Symbol)->Index : Sym.OriginalIndex;
if (IsLittleEndian != sys::IsLittleEndianHost)
@@ -407,7 +417,7 @@ void MachOWriter::writeLinkData(Optional<size_t> LCIndex, const LinkData &LD) {
return;
const MachO::linkedit_data_command &LinkEditDataCommand =
O.LoadCommands[*LCIndex].MachOLoadCommand.linkedit_data_command_data;
- char *Out = (char *)B.getBufferStart() + LinkEditDataCommand.dataoff;
+ char *Out = (char *)Buf->getBufferStart() + LinkEditDataCommand.dataoff;
assert((LinkEditDataCommand.datasize == LD.Data.size()) &&
"Incorrect data size");
memcpy(Out, LD.Data.data(), LD.Data.size());
@@ -421,6 +431,11 @@ void MachOWriter::writeDataInCodeData() {
return writeLinkData(O.DataInCodeCommandIndex, O.DataInCode);
}
+void MachOWriter::writeLinkerOptimizationHint() {
+ return writeLinkData(O.LinkerOptimizationHintCommandIndex,
+ O.LinkerOptimizationHint);
+}
+
void MachOWriter::writeFunctionStartsData() {
return writeLinkData(O.FunctionStartsCommandIndex, O.FunctionStarts);
}
@@ -490,6 +505,16 @@ void MachOWriter::writeTail() {
&MachOWriter::writeDataInCodeData);
}
+ if (O.LinkerOptimizationHintCommandIndex) {
+ const MachO::linkedit_data_command &LinkEditDataCommand =
+ O.LoadCommands[*O.LinkerOptimizationHintCommandIndex]
+ .MachOLoadCommand.linkedit_data_command_data;
+
+ if (LinkEditDataCommand.dataoff)
+ Queue.emplace_back(LinkEditDataCommand.dataoff,
+ &MachOWriter::writeLinkerOptimizationHint);
+ }
+
if (O.FunctionStartsCommandIndex) {
const MachO::linkedit_data_command &LinkEditDataCommand =
O.LoadCommands[*O.FunctionStartsCommandIndex]
@@ -511,16 +536,20 @@ void MachOWriter::writeTail() {
Error MachOWriter::finalize() { return LayoutBuilder.layout(); }
Error MachOWriter::write() {
- if (Error E = B.allocate(totalSize()))
- return E;
- memset(B.getBufferStart(), 0, totalSize());
+ size_t TotalSize = totalSize();
+ Buf = WritableMemoryBuffer::getNewMemBuffer(TotalSize);
+ if (!Buf)
+ return createStringError(errc::not_enough_memory,
+ "failed to allocate memory buffer of " +
+ Twine::utohexstr(TotalSize) + " bytes");
+ memset(Buf->getBufferStart(), 0, totalSize());
writeHeader();
writeLoadCommands();
writeSections();
writeTail();
- return B.commit();
-}
-} // end namespace macho
-} // end namespace objcopy
-} // end namespace llvm
+ // TODO: Implement direct writing to the output stream (without intermediate
+ // memory buffer Buf).
+ Out.write(Buf->getBufferStart(), Buf->getBufferSize());
+ return Error::success();
+}
diff --git a/llvm/tools/llvm-objcopy/MachO/MachOWriter.h b/llvm/tools/llvm-objcopy/MachO/MachOWriter.h
index c2c6f5a55e9a..c8c06d644e9f 100644
--- a/llvm/tools/llvm-objcopy/MachO/MachOWriter.h
+++ b/llvm/tools/llvm-objcopy/MachO/MachOWriter.h
@@ -6,7 +6,6 @@
//
//===----------------------------------------------------------------------===//
-#include "../Buffer.h"
#include "MachOLayoutBuilder.h"
#include "MachOObjcopy.h"
#include "Object.h"
@@ -24,7 +23,8 @@ class MachOWriter {
bool Is64Bit;
bool IsLittleEndian;
uint64_t PageSize;
- Buffer &B;
+ std::unique_ptr<WritableMemoryBuffer> Buf;
+ raw_ostream &Out;
MachOLayoutBuilder LayoutBuilder;
size_t headerSize() const;
@@ -48,14 +48,15 @@ class MachOWriter {
void writeLinkData(Optional<size_t> LCIndex, const LinkData &LD);
void writeCodeSignatureData();
void writeDataInCodeData();
+ void writeLinkerOptimizationHint();
void writeFunctionStartsData();
void writeTail();
public:
MachOWriter(Object &O, bool Is64Bit, bool IsLittleEndian, uint64_t PageSize,
- Buffer &B)
+ raw_ostream &Out)
: O(O), Is64Bit(Is64Bit), IsLittleEndian(IsLittleEndian),
- PageSize(PageSize), B(B), LayoutBuilder(O, Is64Bit, PageSize) {}
+ PageSize(PageSize), Out(Out), LayoutBuilder(O, Is64Bit, PageSize) {}
size_t totalSize() const;
Error finalize();
diff --git a/llvm/tools/llvm-objcopy/MachO/Object.cpp b/llvm/tools/llvm-objcopy/MachO/Object.cpp
index cdb97531fb66..b4f98fa84cb5 100644
--- a/llvm/tools/llvm-objcopy/MachO/Object.cpp
+++ b/llvm/tools/llvm-objcopy/MachO/Object.cpp
@@ -10,9 +10,8 @@
#include "llvm/ADT/SmallPtrSet.h"
#include <unordered_set>
-namespace llvm {
-namespace objcopy {
-namespace macho {
+using namespace llvm;
+using namespace llvm::objcopy::macho;
const SymbolEntry *SymbolTable::getSymbolByIndex(uint32_t Index) const {
assert(Index < Symbols.size() && "invalid symbol index");
@@ -47,6 +46,9 @@ void Object::updateLoadCommandIndexes() {
case MachO::LC_DATA_IN_CODE:
DataInCodeCommandIndex = Index;
break;
+ case MachO::LC_LINKER_OPTIMIZATION_HINT:
+ LinkerOptimizationHintCommandIndex = Index;
+ break;
case MachO::LC_FUNCTION_STARTS:
FunctionStartsCommandIndex = Index;
break;
@@ -190,7 +192,3 @@ Optional<uint64_t> LoadCommand::getSegmentVMAddr() const {
return None;
}
}
-
-} // end namespace macho
-} // end namespace objcopy
-} // end namespace llvm
diff --git a/llvm/tools/llvm-objcopy/MachO/Object.h b/llvm/tools/llvm-objcopy/MachO/Object.h
index 0bb4b344b2eb..207502e2241b 100644
--- a/llvm/tools/llvm-objcopy/MachO/Object.h
+++ b/llvm/tools/llvm-objcopy/MachO/Object.h
@@ -180,6 +180,9 @@ struct RelocationInfo {
Optional<const Section *> Sec;
// True if Info is a scattered_relocation_info.
bool Scattered;
+ // True if the type is an ADDEND. r_symbolnum holds the addend instead of a
+ // symbol index.
+ bool IsAddend;
// True if the r_symbolnum points to a section number (i.e. r_extern=0).
bool Extern;
MachO::any_relocation_info Info;
@@ -310,6 +313,7 @@ struct Object {
ExportInfo Exports;
IndirectSymbolTable IndirectSymTable;
LinkData DataInCode;
+ LinkData LinkerOptimizationHint;
LinkData FunctionStarts;
LinkData CodeSignature;
@@ -325,6 +329,8 @@ struct Object {
Optional<size_t> DySymTabCommandIndex;
/// The index LC_DATA_IN_CODE load comamnd if present.
Optional<size_t> DataInCodeCommandIndex;
+ /// The index of LC_LINKER_OPTIMIZATIN_HINT load comamnd if present.
+ Optional<size_t> LinkerOptimizationHintCommandIndex;
/// The index LC_FUNCTION_STARTS load comamnd if present.
Optional<size_t> FunctionStartsCommandIndex;
diff --git a/llvm/tools/llvm-objcopy/MultiFormatConfig.h b/llvm/tools/llvm-objcopy/MultiFormatConfig.h
new file mode 100644
index 000000000000..31d9883d6d3a
--- /dev/null
+++ b/llvm/tools/llvm-objcopy/MultiFormatConfig.h
@@ -0,0 +1,37 @@
+//===- MultiFormatConfig.h --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_OBJCOPY_MULTIFORMATCONFIG_H
+#define LLVM_TOOLS_LLVM_OBJCOPY_MULTIFORMATCONFIG_H
+
+#include "llvm/Support/Error.h"
+
+namespace llvm {
+namespace objcopy {
+
+struct CommonConfig;
+struct ELFConfig;
+struct COFFConfig;
+struct MachOConfig;
+struct WasmConfig;
+
+class MultiFormatConfig {
+public:
+ virtual ~MultiFormatConfig() {}
+
+ virtual const CommonConfig &getCommonConfig() const = 0;
+ virtual Expected<const ELFConfig &> getELFConfig() const = 0;
+ virtual Expected<const COFFConfig &> getCOFFConfig() const = 0;
+ virtual Expected<const MachOConfig &> getMachOConfig() const = 0;
+ virtual Expected<const WasmConfig &> getWasmConfig() const = 0;
+};
+
+} // namespace objcopy
+} // namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_OBJCOPY_MULTIFORMATCONFIG_H
diff --git a/llvm/tools/llvm-objcopy/ObjcopyOpts.td b/llvm/tools/llvm-objcopy/ObjcopyOpts.td
index 9e6b6f0005cd..63abbe4c2020 100644
--- a/llvm/tools/llvm-objcopy/ObjcopyOpts.td
+++ b/llvm/tools/llvm-objcopy/ObjcopyOpts.td
@@ -196,19 +196,6 @@ defm prefix_alloc_sections
: Eq<"prefix-alloc-sections", "Add <prefix> to the start of every allocated section name">,
MetaVarName<"prefix">;
-defm build_id_link_dir
- : Eq<"build-id-link-dir", "Set directory for --build-id-link-input and "
- "--build-id-link-output to <dir>">,
- MetaVarName<"dir">;
-defm build_id_link_input
- : Eq<"build-id-link-input", "Hard-link the input to <dir>/xx/xxx<suffix> "
- "name derived from hex build ID">,
- MetaVarName<"suffix">;
-defm build_id_link_output
- : Eq<"build-id-link-output", "Hard-link the output to <dir>/xx/xxx<suffix> "
- "name derived from hex build ID">,
- MetaVarName<"suffix">;
-
defm set_start : Eq<"set-start", "Set the start address to <addr>. Overrides "
"any previous --change-start or --adjust-start values.">,
MetaVarName<"addr">;
diff --git a/llvm/tools/llvm-objcopy/llvm-objcopy.cpp b/llvm/tools/llvm-objcopy/llvm-objcopy.cpp
index 7fd2acd11e99..ad166487eb78 100644
--- a/llvm/tools/llvm-objcopy/llvm-objcopy.cpp
+++ b/llvm/tools/llvm-objcopy/llvm-objcopy.cpp
@@ -6,17 +6,23 @@
//
//===----------------------------------------------------------------------===//
-#include "Buffer.h"
+#include "llvm-objcopy.h"
+#include "COFF/COFFConfig.h"
#include "COFF/COFFObjcopy.h"
-#include "CopyConfig.h"
+#include "CommonConfig.h"
+#include "ConfigManager.h"
+#include "ELF/ELFConfig.h"
#include "ELF/ELFObjcopy.h"
+#include "MachO/MachOConfig.h"
#include "MachO/MachOObjcopy.h"
+#include "wasm/WasmConfig.h"
#include "wasm/WasmObjcopy.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
+#include "llvm/BinaryFormat/ELF.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/ArchiveWriter.h"
#include "llvm/Object/Binary.h"
@@ -32,6 +38,7 @@
#include "llvm/Option/Option.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Errc.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/ErrorOr.h"
@@ -40,6 +47,7 @@
#include "llvm/Support/Memory.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Process.h"
+#include "llvm/Support/SmallVectorMemoryBuffer.h"
#include "llvm/Support/StringSaver.h"
#include "llvm/Support/WithColor.h"
#include "llvm/Support/raw_ostream.h"
@@ -51,13 +59,14 @@
#include <system_error>
#include <utility>
-namespace llvm {
-namespace objcopy {
+using namespace llvm;
+using namespace llvm::objcopy;
+using namespace llvm::object;
// The name this program was invoked as.
-StringRef ToolName;
+static StringRef ToolName;
-ErrorSuccess reportWarning(Error E) {
+static ErrorSuccess reportWarning(Error E) {
assert(E);
WithColor::warning(errs(), ToolName) << toString(std::move(E)) << '\n';
return Error::success();
@@ -72,7 +81,7 @@ static Expected<DriverConfig> getDriverConfig(ArrayRef<const char *> Args) {
// strip-10.exe -> strip
// powerpc64-unknown-freebsd13-objcopy -> objcopy
// llvm-install-name-tool -> install-name-tool
- auto I = Stem.rfind_lower(Tool);
+ auto I = Stem.rfind_insensitive(Tool);
return I != StringRef::npos &&
(I + Tool.size() == Stem.size() || !isAlnum(Stem[I + Tool.size()]));
};
@@ -87,13 +96,6 @@ static Expected<DriverConfig> getDriverConfig(ArrayRef<const char *> Args) {
return parseObjcopyOptions(Args, reportWarning);
}
-} // end namespace objcopy
-} // end namespace llvm
-
-using namespace llvm;
-using namespace llvm::object;
-using namespace llvm::objcopy;
-
// For regular archives this function simply calls llvm::writeArchive,
// For thin archives it writes the archive file itself as well as its members.
static Error deepWriteArchive(StringRef ArcName,
@@ -108,20 +110,21 @@ static Error deepWriteArchive(StringRef ArcName,
return Error::success();
for (const NewArchiveMember &Member : NewMembers) {
- // Internally, FileBuffer will use the buffer created by
- // FileOutputBuffer::create, for regular files (that is the case for
- // deepWriteArchive) FileOutputBuffer::create will return OnDiskBuffer.
+ // For regular files (as is the case for deepWriteArchive),
+ // FileOutputBuffer::create will return OnDiskBuffer.
// OnDiskBuffer uses a temporary file and then renames it. So in reality
// there is no inefficiency / duplicated in-memory buffers in this case. For
// now in-memory buffers can not be completely avoided since
// NewArchiveMember still requires them even though writeArchive does not
// write them on disk.
- FileBuffer FB(Member.MemberName);
- if (Error E = FB.allocate(Member.Buf->getBufferSize()))
- return E;
+ Expected<std::unique_ptr<FileOutputBuffer>> FB =
+ FileOutputBuffer::create(Member.MemberName, Member.Buf->getBufferSize(),
+ FileOutputBuffer::F_executable);
+ if (!FB)
+ return FB.takeError();
std::copy(Member.Buf->getBufferStart(), Member.Buf->getBufferEnd(),
- FB.getBufferStart());
- if (Error E = FB.commit())
+ (*FB)->getBufferStart());
+ if (Error E = (*FB)->commit())
return E;
}
return Error::success();
@@ -129,18 +132,22 @@ static Error deepWriteArchive(StringRef ArcName,
/// The function executeObjcopyOnIHex does the dispatch based on the format
/// of the output specified by the command line options.
-static Error executeObjcopyOnIHex(CopyConfig &Config, MemoryBuffer &In,
- Buffer &Out) {
+static Error executeObjcopyOnIHex(ConfigManager &ConfigMgr, MemoryBuffer &In,
+ raw_ostream &Out) {
// TODO: support output formats other than ELF.
- if (Error E = Config.parseELFConfig())
- return E;
- return elf::executeObjcopyOnIHex(Config, In, Out);
+ Expected<const ELFConfig &> ELFConfig = ConfigMgr.getELFConfig();
+ if (!ELFConfig)
+ return ELFConfig.takeError();
+
+ return elf::executeObjcopyOnIHex(ConfigMgr.getCommonConfig(), *ELFConfig, In,
+ Out);
}
/// The function executeObjcopyOnRawBinary does the dispatch based on the format
/// of the output specified by the command line options.
-static Error executeObjcopyOnRawBinary(CopyConfig &Config, MemoryBuffer &In,
- Buffer &Out) {
+static Error executeObjcopyOnRawBinary(ConfigManager &ConfigMgr,
+ MemoryBuffer &In, raw_ostream &Out) {
+ const CommonConfig &Config = ConfigMgr.getCommonConfig();
switch (Config.OutputFormat) {
case FileFormat::ELF:
// FIXME: Currently, we call elf::executeObjcopyOnRawBinary even if the
@@ -149,9 +156,11 @@ static Error executeObjcopyOnRawBinary(CopyConfig &Config, MemoryBuffer &In,
case FileFormat::Binary:
case FileFormat::IHex:
case FileFormat::Unspecified:
- if (Error E = Config.parseELFConfig())
- return E;
- return elf::executeObjcopyOnRawBinary(Config, In, Out);
+ Expected<const ELFConfig &> ELFConfig = ConfigMgr.getELFConfig();
+ if (!ELFConfig)
+ return ELFConfig.takeError();
+
+ return elf::executeObjcopyOnRawBinary(Config, *ELFConfig, In, Out);
}
llvm_unreachable("unsupported output format");
@@ -159,23 +168,41 @@ static Error executeObjcopyOnRawBinary(CopyConfig &Config, MemoryBuffer &In,
/// The function executeObjcopyOnBinary does the dispatch based on the format
/// of the input binary (ELF, MachO or COFF).
-static Error executeObjcopyOnBinary(CopyConfig &Config, object::Binary &In,
- Buffer &Out) {
+static Error executeObjcopyOnBinary(const MultiFormatConfig &Config,
+ object::Binary &In, raw_ostream &Out) {
if (auto *ELFBinary = dyn_cast<object::ELFObjectFileBase>(&In)) {
- if (Error E = Config.parseELFConfig())
- return E;
- return elf::executeObjcopyOnBinary(Config, *ELFBinary, Out);
- } else if (auto *COFFBinary = dyn_cast<object::COFFObjectFile>(&In))
- return coff::executeObjcopyOnBinary(Config, *COFFBinary, Out);
- else if (auto *MachOBinary = dyn_cast<object::MachOObjectFile>(&In))
- return macho::executeObjcopyOnBinary(Config, *MachOBinary, Out);
- else if (auto *MachOUniversalBinary =
- dyn_cast<object::MachOUniversalBinary>(&In))
+ Expected<const ELFConfig &> ELFConfig = Config.getELFConfig();
+ if (!ELFConfig)
+ return ELFConfig.takeError();
+
+ return elf::executeObjcopyOnBinary(Config.getCommonConfig(), *ELFConfig,
+ *ELFBinary, Out);
+ } else if (auto *COFFBinary = dyn_cast<object::COFFObjectFile>(&In)) {
+ Expected<const COFFConfig &> COFFConfig = Config.getCOFFConfig();
+ if (!COFFConfig)
+ return COFFConfig.takeError();
+
+ return coff::executeObjcopyOnBinary(Config.getCommonConfig(), *COFFConfig,
+ *COFFBinary, Out);
+ } else if (auto *MachOBinary = dyn_cast<object::MachOObjectFile>(&In)) {
+ Expected<const MachOConfig &> MachOConfig = Config.getMachOConfig();
+ if (!MachOConfig)
+ return MachOConfig.takeError();
+
+ return macho::executeObjcopyOnBinary(Config.getCommonConfig(), *MachOConfig,
+ *MachOBinary, Out);
+ } else if (auto *MachOUniversalBinary =
+ dyn_cast<object::MachOUniversalBinary>(&In)) {
return macho::executeObjcopyOnMachOUniversalBinary(
Config, *MachOUniversalBinary, Out);
- else if (auto *WasmBinary = dyn_cast<object::WasmObjectFile>(&In))
- return objcopy::wasm::executeObjcopyOnBinary(Config, *WasmBinary, Out);
- else
+ } else if (auto *WasmBinary = dyn_cast<object::WasmObjectFile>(&In)) {
+ Expected<const WasmConfig &> WasmConfig = Config.getWasmConfig();
+ if (!WasmConfig)
+ return WasmConfig.takeError();
+
+ return objcopy::wasm::executeObjcopyOnBinary(Config.getCommonConfig(),
+ *WasmConfig, *WasmBinary, Out);
+ } else
return createStringError(object_error::invalid_file_type,
"unsupported object file format");
}
@@ -184,7 +211,7 @@ namespace llvm {
namespace objcopy {
Expected<std::vector<NewArchiveMember>>
-createNewArchiveMembers(CopyConfig &Config, const Archive &Ar) {
+createNewArchiveMembers(const MultiFormatConfig &Config, const Archive &Ar) {
std::vector<NewArchiveMember> NewArchiveMembers;
Error Err = Error::success();
for (const Archive::Child &Child : Ar.children(Err)) {
@@ -197,32 +224,38 @@ createNewArchiveMembers(CopyConfig &Config, const Archive &Ar) {
return createFileError(Ar.getFileName() + "(" + *ChildNameOrErr + ")",
ChildOrErr.takeError());
- MemBuffer MB(ChildNameOrErr.get());
- if (Error E = executeObjcopyOnBinary(Config, *ChildOrErr->get(), MB))
+ SmallVector<char, 0> Buffer;
+ raw_svector_ostream MemStream(Buffer);
+
+ if (Error E = executeObjcopyOnBinary(Config, *ChildOrErr->get(), MemStream))
return std::move(E);
- Expected<NewArchiveMember> Member =
- NewArchiveMember::getOldMember(Child, Config.DeterministicArchives);
+ Expected<NewArchiveMember> Member = NewArchiveMember::getOldMember(
+ Child, Config.getCommonConfig().DeterministicArchives);
if (!Member)
return createFileError(Ar.getFileName(), Member.takeError());
- Member->Buf = MB.releaseMemoryBuffer();
+
+ Member->Buf = std::make_unique<SmallVectorMemoryBuffer>(
+ std::move(Buffer), ChildNameOrErr.get());
Member->MemberName = Member->Buf->getBufferIdentifier();
NewArchiveMembers.push_back(std::move(*Member));
}
if (Err)
- return createFileError(Config.InputFilename, std::move(Err));
+ return createFileError(Config.getCommonConfig().InputFilename,
+ std::move(Err));
return std::move(NewArchiveMembers);
}
} // end namespace objcopy
} // end namespace llvm
-static Error executeObjcopyOnArchive(CopyConfig &Config,
+static Error executeObjcopyOnArchive(const ConfigManager &ConfigMgr,
const object::Archive &Ar) {
Expected<std::vector<NewArchiveMember>> NewArchiveMembersOrErr =
- createNewArchiveMembers(Config, Ar);
+ createNewArchiveMembers(ConfigMgr, Ar);
if (!NewArchiveMembersOrErr)
return NewArchiveMembersOrErr.takeError();
+ const CommonConfig &Config = ConfigMgr.getCommonConfig();
return deepWriteArchive(Config.OutputFilename, *NewArchiveMembersOrErr,
Ar.hasSymbolTable(), Ar.kind(),
Config.DeterministicArchives, Ar.isThin());
@@ -230,8 +263,9 @@ static Error executeObjcopyOnArchive(CopyConfig &Config,
static Error restoreStatOnFile(StringRef Filename,
const sys::fs::file_status &Stat,
- bool PreserveDates) {
+ const ConfigManager &ConfigMgr) {
int FD;
+ const CommonConfig &Config = ConfigMgr.getCommonConfig();
// Writing to stdout should not be treated as an error here, just
// do not set access/modification times or permissions.
@@ -242,7 +276,7 @@ static Error restoreStatOnFile(StringRef Filename,
sys::fs::openFileForWrite(Filename, FD, sys::fs::CD_OpenExisting))
return createFileError(Filename, EC);
- if (PreserveDates)
+ if (Config.PreserveDates)
if (auto EC = sys::fs::setLastAccessAndModificationTime(
FD, Stat.getLastAccessedTime(), Stat.getLastModificationTime()))
return createFileError(Filename, EC);
@@ -250,17 +284,23 @@ static Error restoreStatOnFile(StringRef Filename,
sys::fs::file_status OStat;
if (std::error_code EC = sys::fs::status(FD, OStat))
return createFileError(Filename, EC);
- if (OStat.type() == sys::fs::file_type::regular_file)
+ if (OStat.type() == sys::fs::file_type::regular_file) {
+#ifndef _WIN32
+ // Keep ownership if llvm-objcopy is called under root.
+ if (Config.InputFilename == Config.OutputFilename && OStat.getUser() == 0)
+ sys::fs::changeFileOwnership(FD, Stat.getUser(), Stat.getGroup());
+#endif
+
+ sys::fs::perms Perm = Stat.permissions();
+ if (Config.InputFilename != Config.OutputFilename)
+ Perm = static_cast<sys::fs::perms>(Perm & ~sys::fs::getUmask() & ~06000);
#ifdef _WIN32
- if (auto EC = sys::fs::setPermissions(
- Filename, static_cast<sys::fs::perms>(Stat.permissions() &
- ~sys::fs::getUmask())))
+ if (auto EC = sys::fs::setPermissions(Filename, Perm))
#else
- if (auto EC = sys::fs::setPermissions(
- FD, static_cast<sys::fs::perms>(Stat.permissions() &
- ~sys::fs::getUmask())))
+ if (auto EC = sys::fs::setPermissions(FD, Perm))
#endif
return createFileError(Filename, EC);
+ }
if (auto EC = sys::Process::SafelyCloseFileDescriptor(FD))
return createFileError(Filename, EC);
@@ -271,7 +311,9 @@ static Error restoreStatOnFile(StringRef Filename,
/// The function executeObjcopy does the higher level dispatch based on the type
/// of input (raw binary, archive or single object file) and takes care of the
/// format-agnostic modifications, i.e. preserving dates.
-static Error executeObjcopy(CopyConfig &Config) {
+static Error executeObjcopy(ConfigManager &ConfigMgr) {
+ CommonConfig &Config = ConfigMgr.Common;
+
sys::fs::file_status Stat;
if (Config.InputFilename != "-") {
if (auto EC = sys::fs::status(Config.InputFilename, Stat))
@@ -280,61 +322,85 @@ static Error executeObjcopy(CopyConfig &Config) {
Stat.permissions(static_cast<sys::fs::perms>(0777));
}
- using ProcessRawFn = Error (*)(CopyConfig &, MemoryBuffer &, Buffer &);
- ProcessRawFn ProcessRaw;
- switch (Config.InputFormat) {
- case FileFormat::Binary:
- ProcessRaw = executeObjcopyOnRawBinary;
- break;
- case FileFormat::IHex:
- ProcessRaw = executeObjcopyOnIHex;
- break;
- default:
- ProcessRaw = nullptr;
- }
+ std::function<Error(raw_ostream & OutFile)> ObjcopyFunc;
+
+ OwningBinary<llvm::object::Binary> BinaryHolder;
+ std::unique_ptr<MemoryBuffer> MemoryBufferHolder;
- if (ProcessRaw) {
- auto BufOrErr = MemoryBuffer::getFileOrSTDIN(Config.InputFilename);
+ if (Config.InputFormat == FileFormat::Binary ||
+ Config.InputFormat == FileFormat::IHex) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
+ MemoryBuffer::getFileOrSTDIN(Config.InputFilename);
if (!BufOrErr)
return createFileError(Config.InputFilename, BufOrErr.getError());
- FileBuffer FB(Config.OutputFilename);
- if (Error E = ProcessRaw(Config, *BufOrErr->get(), FB))
- return E;
+ MemoryBufferHolder = std::move(*BufOrErr);
+
+ if (Config.InputFormat == FileFormat::Binary)
+ ObjcopyFunc = [&](raw_ostream &OutFile) -> Error {
+ // Handle FileFormat::Binary.
+ return executeObjcopyOnRawBinary(ConfigMgr, *MemoryBufferHolder,
+ OutFile);
+ };
+ else
+ ObjcopyFunc = [&](raw_ostream &OutFile) -> Error {
+ // Handle FileFormat::IHex.
+ return executeObjcopyOnIHex(ConfigMgr, *MemoryBufferHolder, OutFile);
+ };
} else {
Expected<OwningBinary<llvm::object::Binary>> BinaryOrErr =
createBinary(Config.InputFilename);
if (!BinaryOrErr)
return createFileError(Config.InputFilename, BinaryOrErr.takeError());
+ BinaryHolder = std::move(*BinaryOrErr);
- if (Archive *Ar = dyn_cast<Archive>(BinaryOrErr.get().getBinary())) {
- if (Error E = executeObjcopyOnArchive(Config, *Ar))
+ if (Archive *Ar = dyn_cast<Archive>(BinaryHolder.getBinary())) {
+ // Handle Archive.
+ if (Error E = executeObjcopyOnArchive(ConfigMgr, *Ar))
return E;
} else {
- FileBuffer FB(Config.OutputFilename);
- if (Error E = executeObjcopyOnBinary(Config,
- *BinaryOrErr.get().getBinary(), FB))
+ // Handle llvm::object::Binary.
+ ObjcopyFunc = [&](raw_ostream &OutFile) -> Error {
+ return executeObjcopyOnBinary(ConfigMgr, *BinaryHolder.getBinary(),
+ OutFile);
+ };
+ }
+ }
+
+ if (ObjcopyFunc) {
+ if (Config.SplitDWO.empty()) {
+ // Apply transformations described by Config and store result into
+ // Config.OutputFilename using specified ObjcopyFunc function.
+ if (Error E = writeToOutput(Config.OutputFilename, ObjcopyFunc))
+ return E;
+ } else {
+ Config.ExtractDWO = true;
+ Config.StripDWO = false;
+ // Copy .dwo tables from the Config.InputFilename into Config.SplitDWO
+ // file using specified ObjcopyFunc function.
+ if (Error E = writeToOutput(Config.SplitDWO, ObjcopyFunc))
+ return E;
+ Config.ExtractDWO = false;
+ Config.StripDWO = true;
+ // Apply transformations described by Config, remove .dwo tables and
+ // store result into Config.OutputFilename using specified ObjcopyFunc
+ // function.
+ if (Error E = writeToOutput(Config.OutputFilename, ObjcopyFunc))
return E;
}
}
- if (Error E =
- restoreStatOnFile(Config.OutputFilename, Stat, Config.PreserveDates))
+ if (Error E = restoreStatOnFile(Config.OutputFilename, Stat, ConfigMgr))
return E;
if (!Config.SplitDWO.empty()) {
Stat.permissions(static_cast<sys::fs::perms>(0666));
- if (Error E =
- restoreStatOnFile(Config.SplitDWO, Stat, Config.PreserveDates))
+ if (Error E = restoreStatOnFile(Config.SplitDWO, Stat, ConfigMgr))
return E;
}
return Error::success();
}
-namespace {
-
-} // anonymous namespace
-
int main(int argc, char **argv) {
InitLLVM X(argc, argv);
ToolName = argv[0];
@@ -360,8 +426,8 @@ int main(int argc, char **argv) {
WithColor::error(errs(), ToolName));
return 1;
}
- for (CopyConfig &CopyConfig : DriverConfig->CopyConfigs) {
- if (Error E = executeObjcopy(CopyConfig)) {
+ for (ConfigManager &ConfigMgr : DriverConfig->CopyConfigs) {
+ if (Error E = executeObjcopy(ConfigMgr)) {
logAllUnhandledErrors(std::move(E), WithColor::error(errs(), ToolName));
return 1;
}
diff --git a/llvm/tools/llvm-objcopy/llvm-objcopy.h b/llvm/tools/llvm-objcopy/llvm-objcopy.h
index 97a166769f95..182c95dc64c8 100644
--- a/llvm/tools/llvm-objcopy/llvm-objcopy.h
+++ b/llvm/tools/llvm-objcopy/llvm-objcopy.h
@@ -10,6 +10,7 @@
#define LLVM_TOOLS_OBJCOPY_OBJCOPY_H
#include "llvm/Support/Error.h"
+#include "llvm/Support/raw_ostream.h"
namespace llvm {
@@ -22,9 +23,10 @@ class Archive;
} // end namespace object
namespace objcopy {
-struct CopyConfig;
+class MultiFormatConfig;
Expected<std::vector<NewArchiveMember>>
-createNewArchiveMembers(CopyConfig &Config, const object::Archive &Ar);
+createNewArchiveMembers(const MultiFormatConfig &Config,
+ const object::Archive &Ar);
} // end namespace objcopy
} // end namespace llvm
diff --git a/llvm/tools/llvm-objcopy/wasm/WasmConfig.h b/llvm/tools/llvm-objcopy/wasm/WasmConfig.h
new file mode 100644
index 000000000000..4e40926ae453
--- /dev/null
+++ b/llvm/tools/llvm-objcopy/wasm/WasmConfig.h
@@ -0,0 +1,21 @@
+//===- WasmConfig.h ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_OBJCOPY_WASM_WASMCONFIG_H
+#define LLVM_TOOLS_LLVM_OBJCOPY_WASM_WASMCONFIG_H
+
+namespace llvm {
+namespace objcopy {
+
+// Wasm specific configuration for copying/stripping a single file.
+struct WasmConfig {};
+
+} // namespace objcopy
+} // namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_OBJCOPY_WASM_WASMCONFIG_H
diff --git a/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.cpp b/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.cpp
index eb0e5635cef9..397d09757e54 100644
--- a/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.cpp
+++ b/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.cpp
@@ -7,18 +7,35 @@
//===----------------------------------------------------------------------===//
#include "WasmObjcopy.h"
-#include "Buffer.h"
-#include "CopyConfig.h"
+#include "CommonConfig.h"
#include "Object.h"
#include "Reader.h"
#include "Writer.h"
#include "llvm/Support/Errc.h"
+#include "llvm/Support/FileOutputBuffer.h"
namespace llvm {
namespace objcopy {
namespace wasm {
using namespace object;
+using SectionPred = std::function<bool(const Section &Sec)>;
+
+static bool isDebugSection(const Section &Sec) {
+ return Sec.Name.startswith(".debug");
+}
+
+static bool isLinkerSection(const Section &Sec) {
+ return Sec.Name.startswith("reloc.") || Sec.Name == "linking";
+}
+
+static bool isNameSection(const Section &Sec) { return Sec.Name == "name"; }
+
+// Sections which are known to be "comments" or informational and do not affect
+// program semantics.
+static bool isCommentSection(const Section &Sec) {
+ return Sec.Name == "producers";
+}
static Error dumpSectionToFile(StringRef SecName, StringRef Filename,
Object &Obj) {
@@ -39,7 +56,60 @@ static Error dumpSectionToFile(StringRef SecName, StringRef Filename,
return createStringError(errc::invalid_argument, "section '%s' not found",
SecName.str().c_str());
}
-static Error handleArgs(const CopyConfig &Config, Object &Obj) {
+
+static void removeSections(const CommonConfig &Config, Object &Obj) {
+ SectionPred RemovePred = [](const Section &) { return false; };
+
+ // Explicitly-requested sections.
+ if (!Config.ToRemove.empty()) {
+ RemovePred = [&Config](const Section &Sec) {
+ return Config.ToRemove.matches(Sec.Name);
+ };
+ }
+
+ if (Config.StripDebug) {
+ RemovePred = [RemovePred](const Section &Sec) {
+ return RemovePred(Sec) || isDebugSection(Sec);
+ };
+ }
+
+ if (Config.StripAll) {
+ RemovePred = [RemovePred](const Section &Sec) {
+ return RemovePred(Sec) || isDebugSection(Sec) || isLinkerSection(Sec) ||
+ isNameSection(Sec) || isCommentSection(Sec);
+ };
+ }
+
+ if (Config.OnlyKeepDebug) {
+ RemovePred = [&Config](const Section &Sec) {
+ // Keep debug sections, unless explicitly requested to remove.
+ // Remove everything else, including known sections.
+ return Config.ToRemove.matches(Sec.Name) || !isDebugSection(Sec);
+ };
+ }
+
+ if (!Config.OnlySection.empty()) {
+ RemovePred = [&Config](const Section &Sec) {
+ // Explicitly keep these sections regardless of previous removes.
+ // Remove everything else, inluding known sections.
+ return !Config.OnlySection.matches(Sec.Name);
+ };
+ }
+
+ if (!Config.KeepSection.empty()) {
+ RemovePred = [&Config, RemovePred](const Section &Sec) {
+ // Explicitly keep these sections regardless of previous removes.
+ if (Config.KeepSection.matches(Sec.Name))
+ return false;
+ // Otherwise defer to RemovePred.
+ return RemovePred(Sec);
+ };
+ }
+
+ Obj.removeSections(RemovePred);
+}
+
+static Error handleArgs(const CommonConfig &Config, Object &Obj) {
// Only support AddSection, DumpSection, RemoveSection for now.
for (StringRef Flag : Config.DumpSection) {
StringRef SecName;
@@ -49,11 +119,7 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj) {
return createFileError(FileName, std::move(E));
}
- Obj.removeSections([&Config](const Section &Sec) {
- if (Config.ToRemove.matches(Sec.Name))
- return true;
- return false;
- });
+ removeSections(Config, Obj);
for (StringRef Flag : Config.AddSection) {
StringRef SecName, FileName;
@@ -72,28 +138,11 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj) {
Obj.addSectionWithOwnedContents(Sec, std::move(Buf));
}
- if (!Config.AddGnuDebugLink.empty() || !Config.BuildIdLinkDir.empty() ||
- Config.BuildIdLinkInput || Config.BuildIdLinkOutput ||
- Config.ExtractPartition || !Config.SplitDWO.empty() ||
- !Config.SymbolsPrefix.empty() || !Config.AllocSectionsPrefix.empty() ||
- Config.DiscardMode != DiscardType::None || Config.NewSymbolVisibility ||
- !Config.SymbolsToAdd.empty() || !Config.RPathToAdd.empty() ||
- !Config.OnlySection.empty() || !Config.SymbolsToGlobalize.empty() ||
- !Config.SymbolsToKeep.empty() || !Config.SymbolsToLocalize.empty() ||
- !Config.SymbolsToRemove.empty() ||
- !Config.UnneededSymbolsToRemove.empty() ||
- !Config.SymbolsToWeaken.empty() || !Config.SymbolsToKeepGlobal.empty() ||
- !Config.SectionsToRename.empty() || !Config.SetSectionAlignment.empty() ||
- !Config.SetSectionFlags.empty() || !Config.SymbolsToRename.empty()) {
- return createStringError(
- llvm::errc::invalid_argument,
- "only add-section, dump-section, and remove-section are supported");
- }
return Error::success();
}
-Error executeObjcopyOnBinary(const CopyConfig &Config,
- object::WasmObjectFile &In, Buffer &Out) {
+Error executeObjcopyOnBinary(const CommonConfig &Config, const WasmConfig &,
+ object::WasmObjectFile &In, raw_ostream &Out) {
Reader TheReader(In);
Expected<std::unique_ptr<Object>> ObjOrErr = TheReader.create();
if (!ObjOrErr)
diff --git a/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.h b/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.h
index 3557d5c0a50d..28268e38c584 100644
--- a/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.h
+++ b/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.h
@@ -11,18 +11,19 @@
namespace llvm {
class Error;
+class raw_ostream;
namespace object {
class WasmObjectFile;
} // end namespace object
namespace objcopy {
-struct CopyConfig;
-class Buffer;
+struct CommonConfig;
+struct WasmConfig;
namespace wasm {
-Error executeObjcopyOnBinary(const CopyConfig &Config,
- object::WasmObjectFile &In, Buffer &Out);
+Error executeObjcopyOnBinary(const CommonConfig &Config, const WasmConfig &,
+ object::WasmObjectFile &In, raw_ostream &Out);
} // end namespace wasm
} // end namespace objcopy
diff --git a/llvm/tools/llvm-objcopy/wasm/Writer.cpp b/llvm/tools/llvm-objcopy/wasm/Writer.cpp
index 50d26507b498..2fad9e60c50f 100644
--- a/llvm/tools/llvm-objcopy/wasm/Writer.cpp
+++ b/llvm/tools/llvm-objcopy/wasm/Writer.cpp
@@ -9,6 +9,7 @@
#include "Writer.h"
#include "llvm/BinaryFormat/Wasm.h"
#include "llvm/Support/Endian.h"
+#include "llvm/Support/Errc.h"
#include "llvm/Support/LEB128.h"
#include "llvm/Support/raw_ostream.h"
@@ -54,23 +55,23 @@ size_t Writer::finalize() {
}
Error Writer::write() {
- size_t FileSize = finalize();
- if (Error E = Buf.allocate(FileSize))
- return E;
+ size_t TotalSize = finalize();
+ Out.reserveExtraSpace(TotalSize);
// Write the header.
- uint8_t *Ptr = Buf.getBufferStart();
- Ptr = std::copy(Obj.Header.Magic.begin(), Obj.Header.Magic.end(), Ptr);
- support::endian::write32le(Ptr, Obj.Header.Version);
- Ptr += sizeof(Obj.Header.Version);
+ Out.write(Obj.Header.Magic.data(), Obj.Header.Magic.size());
+ uint32_t Version;
+ support::endian::write32le(&Version, Obj.Header.Version);
+ Out.write(reinterpret_cast<const char *>(&Version), sizeof(Version));
// Write each section.
for (size_t I = 0, S = SectionHeaders.size(); I < S; ++I) {
- Ptr = std::copy(SectionHeaders[I].begin(), SectionHeaders[I].end(), Ptr);
- ArrayRef<uint8_t> Contents = Obj.Sections[I].Contents;
- Ptr = std::copy(Contents.begin(), Contents.end(), Ptr);
+ Out.write(SectionHeaders[I].data(), SectionHeaders[I].size());
+ Out.write(reinterpret_cast<const char *>(Obj.Sections[I].Contents.data()),
+ Obj.Sections[I].Contents.size());
}
- return Buf.commit();
+
+ return Error::success();
}
} // end namespace wasm
diff --git a/llvm/tools/llvm-objcopy/wasm/Writer.h b/llvm/tools/llvm-objcopy/wasm/Writer.h
index da48ee730c3b..4404cd8caf84 100644
--- a/llvm/tools/llvm-objcopy/wasm/Writer.h
+++ b/llvm/tools/llvm-objcopy/wasm/Writer.h
@@ -9,7 +9,6 @@
#ifndef LLVM_TOOLS_LLVM_OBJCOPY_WASM_WRITER_H
#define LLVM_TOOLS_LLVM_OBJCOPY_WASM_WRITER_H
-#include "Buffer.h"
#include "Object.h"
#include <cstdint>
#include <vector>
@@ -20,13 +19,13 @@ namespace wasm {
class Writer {
public:
- Writer(Object &Obj, Buffer &Buf) : Obj(Obj), Buf(Buf) {}
+ Writer(Object &Obj, raw_ostream &Out) : Obj(Obj), Out(Out) {}
Error write();
private:
using SectionHeader = SmallVector<char, 8>;
Object &Obj;
- Buffer &Buf;
+ raw_ostream &Out;
std::vector<SectionHeader> SectionHeaders;
/// Generate a wasm section section header for S.
diff --git a/llvm/tools/llvm-objdump/COFFDump.cpp b/llvm/tools/llvm-objdump/COFFDump.cpp
index b9d69d62e4e7..09a900182d24 100644
--- a/llvm/tools/llvm-objdump/COFFDump.cpp
+++ b/llvm/tools/llvm-objdump/COFFDump.cpp
@@ -454,8 +454,7 @@ static bool getPDataSection(const COFFObjectFile *Obj,
continue;
const coff_section *Pdata = Obj->getCOFFSection(Section);
- for (const RelocationRef &Reloc : Section.relocations())
- Rels.push_back(Reloc);
+ append_range(Rels, Section.relocations());
// Sort relocations by address.
llvm::sort(Rels, isRelocAddressLess);
diff --git a/llvm/tools/llvm-objdump/ELFDump.cpp b/llvm/tools/llvm-objdump/ELFDump.cpp
index 1c4d59179cc7..da7415834c63 100644
--- a/llvm/tools/llvm-objdump/ELFDump.cpp
+++ b/llvm/tools/llvm-objdump/ELFDump.cpp
@@ -74,7 +74,10 @@ static Error getRelocationValueString(const ELFObjectFile<ELFT> *Obj,
const typename ELFT::Rela *ERela = Obj->getRela(Rel);
Addend = ERela->r_addend;
Undef = ERela->getSymbol(false) == 0;
- } else if ((*SecOrErr)->sh_type != ELF::SHT_REL) {
+ } else if ((*SecOrErr)->sh_type == ELF::SHT_REL) {
+ const typename ELFT::Rel *ERel = Obj->getRel(Rel);
+ Undef = ERel->getSymbol(false) == 0;
+ } else {
return make_error<BinaryError>();
}
@@ -177,7 +180,7 @@ static void printDynamicSection(const ELFFile<ELFT> &Elf, StringRef Filename) {
MaxLen = std::max(MaxLen, Elf.getDynamicTagAsString(Dyn.d_tag).size());
std::string TagFmt = " %-" + std::to_string(MaxLen) + "s ";
- outs() << "Dynamic Section:\n";
+ outs() << "\nDynamic Section:\n";
for (const typename ELFT::Dyn &Dyn : DynamicEntries) {
if (Dyn.d_tag == ELF::DT_NULL)
continue;
@@ -205,7 +208,7 @@ static void printDynamicSection(const ELFFile<ELFT> &Elf, StringRef Filename) {
template <class ELFT>
static void printProgramHeaders(const ELFFile<ELFT> &Obj, StringRef FileName) {
- outs() << "Program Header:\n";
+ outs() << "\nProgram Header:\n";
auto ProgramHeaderOrError = Obj.program_headers();
if (!ProgramHeaderOrError) {
reportWarning("unable to read program headers: " +
@@ -272,13 +275,12 @@ static void printProgramHeaders(const ELFFile<ELFT> &Obj, StringRef FileName) {
<< ((Phdr.p_flags & ELF::PF_W) ? "w" : "-")
<< ((Phdr.p_flags & ELF::PF_X) ? "x" : "-") << "\n";
}
- outs() << "\n";
}
template <class ELFT>
static void printSymbolVersionDependency(ArrayRef<uint8_t> Contents,
StringRef StrTab) {
- outs() << "Version References:\n";
+ outs() << "\nVersion References:\n";
const uint8_t *Buf = Contents.data();
while (Buf) {
@@ -304,7 +306,7 @@ template <class ELFT>
static void printSymbolVersionDefinition(const typename ELFT::Shdr &Shdr,
ArrayRef<uint8_t> Contents,
StringRef StrTab) {
- outs() << "Version definitions:\n";
+ outs() << "\nVersion definitions:\n";
const uint8_t *Buf = Contents.data();
uint32_t VerdefIndex = 1;
diff --git a/llvm/tools/llvm-objdump/MachODump.cpp b/llvm/tools/llvm-objdump/MachODump.cpp
index 51212d52c18b..7c1fdf03542f 100644
--- a/llvm/tools/llvm-objdump/MachODump.cpp
+++ b/llvm/tools/llvm-objdump/MachODump.cpp
@@ -12,6 +12,7 @@
#include "MachODump.h"
+#include "ObjdumpOptID.h"
#include "llvm-objdump.h"
#include "llvm-c/Disassembler.h"
#include "llvm/ADT/STLExtras.h"
@@ -34,8 +35,8 @@
#include "llvm/MC/MCTargetOptions.h"
#include "llvm/Object/MachO.h"
#include "llvm/Object/MachOUniversal.h"
+#include "llvm/Option/ArgList.h"
#include "llvm/Support/Casting.h"
-#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/Format.h"
@@ -52,7 +53,7 @@
#include <cstring>
#include <system_error>
-#ifdef HAVE_LIBXAR
+#ifdef LLVM_HAVE_LIBXAR
extern "C" {
#include <xar/xar.h>
}
@@ -62,125 +63,63 @@ using namespace llvm;
using namespace llvm::object;
using namespace llvm::objdump;
-cl::OptionCategory objdump::MachOCat("llvm-objdump MachO Specific Options");
-
-cl::opt<bool> objdump::FirstPrivateHeader(
- "private-header",
- cl::desc("Display only the first format specific file header"),
- cl::cat(MachOCat));
-
-cl::opt<bool> objdump::ExportsTrie("exports-trie",
- cl::desc("Display mach-o exported symbols"),
- cl::cat(MachOCat));
-
-cl::opt<bool> objdump::Rebase("rebase",
- cl::desc("Display mach-o rebasing info"),
- cl::cat(MachOCat));
-
-cl::opt<bool> objdump::Bind("bind", cl::desc("Display mach-o binding info"),
- cl::cat(MachOCat));
-
-cl::opt<bool> objdump::LazyBind("lazy-bind",
- cl::desc("Display mach-o lazy binding info"),
- cl::cat(MachOCat));
-
-cl::opt<bool> objdump::WeakBind("weak-bind",
- cl::desc("Display mach-o weak binding info"),
- cl::cat(MachOCat));
-
-static cl::opt<bool>
- UseDbg("g", cl::Grouping,
- cl::desc("Print line information from debug info if available"),
- cl::cat(MachOCat));
-
-static cl::opt<std::string> DSYMFile("dsym",
- cl::desc("Use .dSYM file for debug info"),
- cl::cat(MachOCat));
-
-static cl::opt<bool> FullLeadingAddr("full-leading-addr",
- cl::desc("Print full leading address"),
- cl::cat(MachOCat));
-
-static cl::opt<bool> NoLeadingHeaders("no-leading-headers",
- cl::desc("Print no leading headers"),
- cl::cat(MachOCat));
-
-cl::opt<bool> objdump::UniversalHeaders(
- "universal-headers",
- cl::desc("Print Mach-O universal headers (requires --macho)"),
- cl::cat(MachOCat));
-
-static cl::opt<bool> ArchiveMemberOffsets(
- "archive-member-offsets",
- cl::desc("Print the offset to each archive member for Mach-O archives "
- "(requires --macho and --archive-headers)"),
- cl::cat(MachOCat));
-
-cl::opt<bool> objdump::IndirectSymbols(
- "indirect-symbols",
- cl::desc(
- "Print indirect symbol table for Mach-O objects (requires --macho)"),
- cl::cat(MachOCat));
-
-cl::opt<bool> objdump::DataInCode(
- "data-in-code",
- cl::desc(
- "Print the data in code table for Mach-O objects (requires --macho)"),
- cl::cat(MachOCat));
-
-cl::opt<bool>
- objdump::LinkOptHints("link-opt-hints",
- cl::desc("Print the linker optimization hints for "
- "Mach-O objects (requires --macho)"),
- cl::cat(MachOCat));
-
-cl::opt<bool>
- objdump::InfoPlist("info-plist",
- cl::desc("Print the info plist section as strings for "
- "Mach-O objects (requires --macho)"),
- cl::cat(MachOCat));
-
-cl::opt<bool>
- objdump::DylibsUsed("dylibs-used",
- cl::desc("Print the shared libraries used for linked "
- "Mach-O files (requires --macho)"),
- cl::cat(MachOCat));
-
-cl::opt<bool> objdump::DylibId("dylib-id",
- cl::desc("Print the shared library's id for the "
- "dylib Mach-O file (requires --macho)"),
- cl::cat(MachOCat));
-
-static cl::opt<bool>
- NonVerbose("non-verbose",
- cl::desc("Print the info for Mach-O objects in non-verbose or "
- "numeric form (requires --macho)"),
- cl::cat(MachOCat));
-
-cl::opt<bool>
- objdump::ObjcMetaData("objc-meta-data",
- cl::desc("Print the Objective-C runtime meta data "
- "for Mach-O files (requires --macho)"),
- cl::cat(MachOCat));
-
-static cl::opt<std::string> DisSymName(
- "dis-symname",
- cl::desc("disassemble just this symbol's instructions (requires --macho)"),
- cl::cat(MachOCat));
-
-static cl::opt<bool> NoSymbolicOperands(
- "no-symbolic-operands",
- cl::desc("do not symbolic operands when disassembling (requires --macho)"),
- cl::cat(MachOCat));
-
-static cl::list<std::string>
- ArchFlags("arch", cl::desc("architecture(s) from a Mach-O file to dump"),
- cl::ZeroOrMore, cl::cat(MachOCat));
+bool objdump::FirstPrivateHeader;
+bool objdump::ExportsTrie;
+bool objdump::Rebase;
+bool objdump::Rpaths;
+bool objdump::Bind;
+bool objdump::LazyBind;
+bool objdump::WeakBind;
+static bool UseDbg;
+static std::string DSYMFile;
+bool objdump::FullLeadingAddr;
+bool objdump::LeadingHeaders;
+bool objdump::UniversalHeaders;
+static bool ArchiveMemberOffsets;
+bool objdump::IndirectSymbols;
+bool objdump::DataInCode;
+bool objdump::FunctionStarts;
+bool objdump::LinkOptHints;
+bool objdump::InfoPlist;
+bool objdump::DylibsUsed;
+bool objdump::DylibId;
+bool objdump::Verbose;
+bool objdump::ObjcMetaData;
+std::string objdump::DisSymName;
+bool objdump::SymbolicOperands;
+static std::vector<std::string> ArchFlags;
static bool ArchAll = false;
-
static std::string ThumbTripleName;
+void objdump::parseMachOOptions(const llvm::opt::InputArgList &InputArgs) {
+ FirstPrivateHeader = InputArgs.hasArg(OBJDUMP_private_header);
+ ExportsTrie = InputArgs.hasArg(OBJDUMP_exports_trie);
+ Rebase = InputArgs.hasArg(OBJDUMP_rebase);
+ Rpaths = InputArgs.hasArg(OBJDUMP_rpaths);
+ Bind = InputArgs.hasArg(OBJDUMP_bind);
+ LazyBind = InputArgs.hasArg(OBJDUMP_lazy_bind);
+ WeakBind = InputArgs.hasArg(OBJDUMP_weak_bind);
+ UseDbg = InputArgs.hasArg(OBJDUMP_g);
+ DSYMFile = InputArgs.getLastArgValue(OBJDUMP_dsym_EQ).str();
+ FullLeadingAddr = InputArgs.hasArg(OBJDUMP_full_leading_addr);
+ LeadingHeaders = !InputArgs.hasArg(OBJDUMP_no_leading_headers);
+ UniversalHeaders = InputArgs.hasArg(OBJDUMP_universal_headers);
+ ArchiveMemberOffsets = InputArgs.hasArg(OBJDUMP_archive_member_offsets);
+ IndirectSymbols = InputArgs.hasArg(OBJDUMP_indirect_symbols);
+ DataInCode = InputArgs.hasArg(OBJDUMP_data_in_code);
+ FunctionStarts = InputArgs.hasArg(OBJDUMP_function_starts);
+ LinkOptHints = InputArgs.hasArg(OBJDUMP_link_opt_hints);
+ InfoPlist = InputArgs.hasArg(OBJDUMP_info_plist);
+ DylibsUsed = InputArgs.hasArg(OBJDUMP_dylibs_used);
+ DylibId = InputArgs.hasArg(OBJDUMP_dylib_id);
+ Verbose = !InputArgs.hasArg(OBJDUMP_non_verbose);
+ ObjcMetaData = InputArgs.hasArg(OBJDUMP_objc_meta_data);
+ DisSymName = InputArgs.getLastArgValue(OBJDUMP_dis_symname).str();
+ SymbolicOperands = !InputArgs.hasArg(OBJDUMP_no_symbolic_operands);
+ ArchFlags = InputArgs.getAllArgValues(OBJDUMP_arch_EQ);
+}
+
static const Target *GetTarget(const MachOObjectFile *MachOObj,
const char **McpuDefault,
const Target **ThumbTarget) {
@@ -245,7 +184,7 @@ typedef std::pair<uint64_t, DiceRef> DiceTableEntry;
typedef std::vector<DiceTableEntry> DiceTable;
typedef DiceTable::iterator dice_table_iterator;
-#ifdef HAVE_LIBXAR
+#ifdef LLVM_HAVE_LIBXAR
namespace {
struct ScopedXarFile {
xar_t xar;
@@ -272,7 +211,7 @@ struct ScopedXarIter {
operator xar_iter_t() { return iter; }
};
} // namespace
-#endif // defined(HAVE_LIBXAR)
+#endif // defined(LLVM_HAVE_LIBXAR)
// This is used to search for a data in code table entry for the PC being
// disassembled. The j parameter has the PC in j.first. A single data in code
@@ -296,19 +235,19 @@ static uint64_t DumpDataInCode(const uint8_t *bytes, uint64_t Length,
default:
case MachO::DICE_KIND_DATA:
if (Length >= 4) {
- if (!NoShowRawInsn)
+ if (ShowRawInsn)
dumpBytes(makeArrayRef(bytes, 4), outs());
Value = bytes[3] << 24 | bytes[2] << 16 | bytes[1] << 8 | bytes[0];
outs() << "\t.long " << Value;
Size = 4;
} else if (Length >= 2) {
- if (!NoShowRawInsn)
+ if (ShowRawInsn)
dumpBytes(makeArrayRef(bytes, 2), outs());
Value = bytes[1] << 8 | bytes[0];
outs() << "\t.short " << Value;
Size = 2;
} else {
- if (!NoShowRawInsn)
+ if (ShowRawInsn)
dumpBytes(makeArrayRef(bytes, 2), outs());
Value = bytes[0];
outs() << "\t.byte " << Value;
@@ -320,14 +259,14 @@ static uint64_t DumpDataInCode(const uint8_t *bytes, uint64_t Length,
outs() << "\t@ data in code kind = " << Kind << "\n";
break;
case MachO::DICE_KIND_JUMP_TABLE8:
- if (!NoShowRawInsn)
+ if (ShowRawInsn)
dumpBytes(makeArrayRef(bytes, 1), outs());
Value = bytes[0];
outs() << "\t.byte " << format("%3u", Value) << "\t@ KIND_JUMP_TABLE8\n";
Size = 1;
break;
case MachO::DICE_KIND_JUMP_TABLE16:
- if (!NoShowRawInsn)
+ if (ShowRawInsn)
dumpBytes(makeArrayRef(bytes, 2), outs());
Value = bytes[1] << 8 | bytes[0];
outs() << "\t.short " << format("%5u", Value & 0xffff)
@@ -336,7 +275,7 @@ static uint64_t DumpDataInCode(const uint8_t *bytes, uint64_t Length,
break;
case MachO::DICE_KIND_JUMP_TABLE32:
case MachO::DICE_KIND_ABS_JUMP_TABLE32:
- if (!NoShowRawInsn)
+ if (ShowRawInsn)
dumpBytes(makeArrayRef(bytes, 4), outs());
Value = bytes[3] << 24 | bytes[2] << 16 | bytes[1] << 8 | bytes[0];
outs() << "\t.long " << Value;
@@ -362,8 +301,7 @@ static void getSectionsAndSymbols(MachOObjectFile *MachOObj,
Symbols.push_back(Symbol);
}
- for (const SectionRef &Section : MachOObj->sections())
- Sections.push_back(Section);
+ append_range(Sections, MachOObj->sections());
bool BaseSegmentAddressSet = false;
for (const auto &Command : MachOObj->load_commands()) {
@@ -461,7 +399,7 @@ static void printRelocationTargetName(const MachOObjectFile *O,
if (isExtern) {
symbol_iterator SI = O->symbol_begin();
- advance(SI, Val);
+ std::advance(SI, Val);
S = unwrapOrError(SI->getName(), FileName);
} else {
section_iterator SI = O->section_begin();
@@ -473,7 +411,7 @@ static void printRelocationTargetName(const MachOObjectFile *O,
uint32_t I = Val - 1;
while (I != 0 && SI != O->section_end()) {
--I;
- advance(SI, 1);
+ std::advance(SI, 1);
}
if (SI == O->section_end()) {
Fmt << Val << " (?,?)";
@@ -1104,6 +1042,43 @@ static void PrintRelocations(const MachOObjectFile *O, const bool verbose) {
}
}
+static void PrintFunctionStarts(MachOObjectFile *O) {
+ uint64_t BaseSegmentAddress = 0;
+ for (const MachOObjectFile::LoadCommandInfo &Command : O->load_commands()) {
+ if (Command.C.cmd == MachO::LC_SEGMENT) {
+ MachO::segment_command SLC = O->getSegmentLoadCommand(Command);
+ if (StringRef(SLC.segname) == "__TEXT") {
+ BaseSegmentAddress = SLC.vmaddr;
+ break;
+ }
+ } else if (Command.C.cmd == MachO::LC_SEGMENT_64) {
+ MachO::segment_command_64 SLC = O->getSegment64LoadCommand(Command);
+ if (StringRef(SLC.segname) == "__TEXT") {
+ BaseSegmentAddress = SLC.vmaddr;
+ break;
+ }
+ }
+ }
+
+ SmallVector<uint64_t, 8> FunctionStarts;
+ for (const MachOObjectFile::LoadCommandInfo &LC : O->load_commands()) {
+ if (LC.C.cmd == MachO::LC_FUNCTION_STARTS) {
+ MachO::linkedit_data_command FunctionStartsLC =
+ O->getLinkeditDataLoadCommand(LC);
+ O->ReadULEB128s(FunctionStartsLC.dataoff, FunctionStarts);
+ break;
+ }
+ }
+
+ for (uint64_t S : FunctionStarts) {
+ uint64_t Addr = BaseSegmentAddress + S;
+ if (O->is64Bit())
+ outs() << format("%016" PRIx64, Addr) << "\n";
+ else
+ outs() << format("%08" PRIx32, static_cast<uint32_t>(Addr)) << "\n";
+ }
+}
+
static void PrintDataInCodeTable(MachOObjectFile *O, bool verbose) {
MachO::linkedit_data_command DIC = O->getDataInCodeLoadCommand();
uint32_t nentries = DIC.datasize / sizeof(struct MachO::data_in_code_entry);
@@ -1260,6 +1235,16 @@ static void PrintDylibs(MachOObjectFile *O, bool JustId) {
}
}
+static void printRpaths(MachOObjectFile *O) {
+ for (const auto &Command : O->load_commands()) {
+ if (Command.C.cmd == MachO::LC_RPATH) {
+ auto Rpath = O->getRpathCommand(Command);
+ const char *P = (const char *)(Command.Ptr) + Rpath.path;
+ outs() << P << "\n";
+ }
+ }
+}
+
typedef DenseMap<uint64_t, StringRef> SymbolAddressMap;
static void CreateSymbolAddressMap(MachOObjectFile *O,
@@ -1712,12 +1697,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
+#ifdef LLVM_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)
+#endif // defined(LLVM_HAVE_LIBXAR)
static void DumpSectionContents(StringRef Filename, MachOObjectFile *O,
bool verbose) {
@@ -1770,7 +1755,7 @@ static void DumpSectionContents(StringRef Filename, MachOObjectFile *O,
uint32_t sect_size = BytesStr.size();
uint64_t sect_addr = Section.getAddress();
- if (!NoLeadingHeaders)
+ if (LeadingHeaders)
outs() << "Contents of (" << SegName << "," << SectName
<< ") section\n";
@@ -1788,13 +1773,13 @@ static void DumpSectionContents(StringRef Filename, MachOObjectFile *O,
DumpProtocolSection(O, sect, sect_size, sect_addr);
continue;
}
-#ifdef HAVE_LIBXAR
+#ifdef LLVM_HAVE_LIBXAR
if (SegName == "__LLVM" && SectName == "__bundle") {
- DumpBitcodeSection(O, sect, sect_size, verbose, !NoSymbolicOperands,
+ DumpBitcodeSection(O, sect, sect_size, verbose, SymbolicOperands,
ArchiveHeaders, "");
continue;
}
-#endif // defined(HAVE_LIBXAR)
+#endif // defined(LLVM_HAVE_LIBXAR)
switch (section_type) {
case MachO::S_REGULAR:
DumpRawSectionContents(O, sect, sect_size, sect_addr);
@@ -1803,20 +1788,20 @@ static void DumpSectionContents(StringRef Filename, MachOObjectFile *O,
outs() << "zerofill section and has no contents in the file\n";
break;
case MachO::S_CSTRING_LITERALS:
- DumpCstringSection(O, sect, sect_size, sect_addr, !NoLeadingAddr);
+ DumpCstringSection(O, sect, sect_size, sect_addr, LeadingAddr);
break;
case MachO::S_4BYTE_LITERALS:
- DumpLiteral4Section(O, sect, sect_size, sect_addr, !NoLeadingAddr);
+ DumpLiteral4Section(O, sect, sect_size, sect_addr, LeadingAddr);
break;
case MachO::S_8BYTE_LITERALS:
- DumpLiteral8Section(O, sect, sect_size, sect_addr, !NoLeadingAddr);
+ DumpLiteral8Section(O, sect, sect_size, sect_addr, LeadingAddr);
break;
case MachO::S_16BYTE_LITERALS:
- DumpLiteral16Section(O, sect, sect_size, sect_addr, !NoLeadingAddr);
+ DumpLiteral16Section(O, sect, sect_size, sect_addr, LeadingAddr);
break;
case MachO::S_LITERAL_POINTERS:
DumpLiteralPointerSection(O, Section, sect, sect_size, sect_addr,
- !NoLeadingAddr);
+ LeadingAddr);
break;
case MachO::S_MOD_INIT_FUNC_POINTERS:
case MachO::S_MOD_TERM_FUNC_POINTERS:
@@ -1853,7 +1838,7 @@ static void DumpInfoPlistSectionContents(StringRef Filename,
DataRefImpl Ref = Section.getRawDataRefImpl();
StringRef SegName = O->getSectionFinalSegmentName(Ref);
if (SegName == "__TEXT" && SectName == "__info_plist") {
- if (!NoLeadingHeaders)
+ if (LeadingHeaders)
outs() << "Contents of (" << SegName << "," << SectName << ") section\n";
StringRef BytesStr =
unwrapOrError(Section.getContents(), O->getFileName());
@@ -1911,9 +1896,9 @@ static void ProcessMachO(StringRef Name, MachOObjectFile *MachOOF,
// UniversalHeaders or ArchiveHeaders.
if (Disassemble || Relocations || PrivateHeaders || ExportsTrie || Rebase ||
Bind || SymbolTable || LazyBind || WeakBind || IndirectSymbols ||
- DataInCode || LinkOptHints || DylibsUsed || DylibId || ObjcMetaData ||
- (!FilterSections.empty())) {
- if (!NoLeadingHeaders) {
+ DataInCode || FunctionStarts || LinkOptHints || DylibsUsed || DylibId ||
+ Rpaths || ObjcMetaData || (!FilterSections.empty())) {
+ if (LeadingHeaders) {
outs() << Name;
if (!ArchiveMemberName.empty())
outs() << '(' << ArchiveMemberName << ')';
@@ -1964,19 +1949,21 @@ static void ProcessMachO(StringRef Name, MachOObjectFile *MachOOF,
DisassembleMachO(FileName, MachOOF, "__TEXT", "__text");
}
if (IndirectSymbols)
- PrintIndirectSymbols(MachOOF, !NonVerbose);
+ PrintIndirectSymbols(MachOOF, Verbose);
if (DataInCode)
- PrintDataInCodeTable(MachOOF, !NonVerbose);
+ PrintDataInCodeTable(MachOOF, Verbose);
+ if (FunctionStarts)
+ PrintFunctionStarts(MachOOF);
if (LinkOptHints)
PrintLinkOptHints(MachOOF);
if (Relocations)
- PrintRelocations(MachOOF, !NonVerbose);
+ PrintRelocations(MachOOF, Verbose);
if (SectionHeaders)
printSectionHeaders(MachOOF);
if (SectionContents)
printSectionContents(MachOOF);
if (!FilterSections.empty())
- DumpSectionContents(FileName, MachOOF, !NonVerbose);
+ DumpSectionContents(FileName, MachOOF, Verbose);
if (InfoPlist)
DumpInfoPlistSectionContents(FileName, MachOOF);
if (DylibsUsed)
@@ -1994,11 +1981,13 @@ static void ProcessMachO(StringRef Name, MachOObjectFile *MachOOF,
if (FirstPrivateHeader)
printMachOFileHeader(MachOOF);
if (ObjcMetaData)
- printObjcMetaData(MachOOF, !NonVerbose);
+ printObjcMetaData(MachOOF, Verbose);
if (ExportsTrie)
printExportsTrie(MachOOF);
if (Rebase)
printRebaseTable(MachOOF);
+ if (Rpaths)
+ printRpaths(MachOOF);
if (Bind)
printBindTable(MachOOF);
if (LazyBind)
@@ -2333,7 +2322,7 @@ void objdump::parseInputMachO(StringRef Filename) {
if (Archive *A = dyn_cast<Archive>(&Bin)) {
outs() << "Archive : " << Filename << "\n";
if (ArchiveHeaders)
- printArchiveHeaders(Filename, A, !NonVerbose, ArchiveMemberOffsets);
+ printArchiveHeaders(Filename, A, Verbose, ArchiveMemberOffsets);
Error Err = Error::success();
unsigned I = -1;
@@ -2380,7 +2369,7 @@ void objdump::parseInputMachO(MachOUniversalBinary *UB) {
auto Filename = UB->getFileName();
if (UniversalHeaders)
- printMachOUniversalHeaders(UB, !NonVerbose);
+ printMachOUniversalHeaders(UB, Verbose);
// If we have a list of architecture flags specified dump only those.
if (!ArchAll && !ArchFlags.empty()) {
@@ -2414,7 +2403,7 @@ void objdump::parseInputMachO(MachOUniversalBinary *UB) {
outs() << " (architecture " << ArchitectureName << ")";
outs() << "\n";
if (ArchiveHeaders)
- printArchiveHeaders(Filename, A.get(), !NonVerbose,
+ printArchiveHeaders(Filename, A.get(), Verbose,
ArchiveMemberOffsets, ArchitectureName);
Error Err = Error::success();
unsigned I = -1;
@@ -2475,7 +2464,7 @@ void objdump::parseInputMachO(MachOUniversalBinary *UB) {
std::unique_ptr<Archive> &A = *AOrErr;
outs() << "Archive : " << Filename << "\n";
if (ArchiveHeaders)
- printArchiveHeaders(Filename, A.get(), !NonVerbose,
+ printArchiveHeaders(Filename, A.get(), Verbose,
ArchiveMemberOffsets);
Error Err = Error::success();
unsigned I = -1;
@@ -2528,8 +2517,8 @@ void objdump::parseInputMachO(MachOUniversalBinary *UB) {
outs() << " (architecture " << ArchitectureName << ")";
outs() << "\n";
if (ArchiveHeaders)
- printArchiveHeaders(Filename, A.get(), !NonVerbose,
- ArchiveMemberOffsets, ArchitectureName);
+ printArchiveHeaders(Filename, A.get(), Verbose, ArchiveMemberOffsets,
+ ArchitectureName);
Error Err = Error::success();
unsigned I = -1;
for (auto &C : A->children(Err)) {
@@ -6115,8 +6104,7 @@ static void printObjc2_64bit_MetaData(MachOObjectFile *O, bool verbose) {
CreateSymbolAddressMap(O, &AddrMap);
std::vector<SectionRef> Sections;
- for (const SectionRef &Section : O->sections())
- Sections.push_back(Section);
+ append_range(Sections, O->sections());
struct DisassembleInfo info(O, &AddrMap, &Sections, verbose);
@@ -6197,8 +6185,7 @@ static void printObjc2_32bit_MetaData(MachOObjectFile *O, bool verbose) {
CreateSymbolAddressMap(O, &AddrMap);
std::vector<SectionRef> Sections;
- for (const SectionRef &Section : O->sections())
- Sections.push_back(Section);
+ append_range(Sections, O->sections());
struct DisassembleInfo info(O, &AddrMap, &Sections, verbose);
@@ -6292,8 +6279,7 @@ static bool printObjc1_32bit_MetaData(MachOObjectFile *O, bool verbose) {
CreateSymbolAddressMap(O, &AddrMap);
std::vector<SectionRef> Sections;
- for (const SectionRef &Section : O->sections())
- Sections.push_back(Section);
+ append_range(Sections, O->sections());
struct DisassembleInfo info(O, &AddrMap, &Sections, verbose);
@@ -6450,8 +6436,7 @@ static void DumpProtocolSection(MachOObjectFile *O, const char *sect,
CreateSymbolAddressMap(O, &AddrMap);
std::vector<SectionRef> Sections;
- for (const SectionRef &Section : O->sections())
- Sections.push_back(Section);
+ append_range(Sections, O->sections());
struct DisassembleInfo info(O, &AddrMap, &Sections, true);
@@ -6475,7 +6460,7 @@ static void DumpProtocolSection(MachOObjectFile *O, const char *sect,
}
}
-#ifdef HAVE_LIBXAR
+#ifdef LLVM_HAVE_LIBXAR
static inline void swapStruct(struct xar_header &xar) {
sys::swapByteOrder(xar.magic);
sys::swapByteOrder(xar.size);
@@ -6838,7 +6823,7 @@ static void DumpBitcodeSection(MachOObjectFile *O, const char *sect,
}
}
}
-#endif // defined(HAVE_LIBXAR)
+#endif // defined(LLVM_HAVE_LIBXAR)
static void printObjcMetaData(MachOObjectFile *O, bool verbose) {
if (O->is64Bit())
@@ -7165,17 +7150,15 @@ static void emitComments(raw_svector_ostream &CommentStream,
// Get the default information for printing a comment.
StringRef CommentBegin = MAI.getCommentString();
unsigned CommentColumn = MAI.getCommentColumn();
- bool IsFirst = true;
+ ListSeparator LS("\n");
while (!Comments.empty()) {
- if (!IsFirst)
- FormattedOS << '\n';
+ FormattedOS << LS;
// Emit a line of comments.
FormattedOS.PadToColumn(CommentColumn);
size_t Position = Comments.find('\n');
FormattedOS << CommentBegin << ' ' << Comments.substr(0, Position);
// Move after the newline character.
Comments = Comments.substr(Position + 1);
- IsFirst = false;
}
FormattedOS.flush();
@@ -7245,7 +7228,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
std::unique_ptr<const MCSubtargetInfo> STI(
TheTarget->createMCSubtargetInfo(TripleName, MachOMCPU, FeaturesStr));
CHECK_TARGET_INFO_CREATION(STI);
- MCContext Ctx(AsmInfo.get(), MRI.get(), nullptr);
+ MCContext Ctx(Triple(TripleName), AsmInfo.get(), MRI.get(), STI.get());
std::unique_ptr<MCDisassembler> DisAsm(
TheTarget->createMCDisassembler(*STI, Ctx));
CHECK_TARGET_INFO_CREATION(DisAsm);
@@ -7295,7 +7278,8 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
ThumbTarget->createMCSubtargetInfo(ThumbTripleName, MachOMCPU,
FeaturesStr));
CHECK_THUMB_TARGET_INFO_CREATION(ThumbSTI);
- ThumbCtx.reset(new MCContext(ThumbAsmInfo.get(), ThumbMRI.get(), nullptr));
+ ThumbCtx.reset(new MCContext(Triple(ThumbTripleName), ThumbAsmInfo.get(),
+ ThumbMRI.get(), ThumbSTI.get()));
ThumbDisAsm.reset(ThumbTarget->createMCDisassembler(*ThumbSTI, *ThumbCtx));
CHECK_THUMB_TARGET_INFO_CREATION(ThumbDisAsm);
MCContext *PtrThumbCtx = ThumbCtx.get();
@@ -7334,7 +7318,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
BaseSegmentAddress);
// Sort the symbols by address, just in case they didn't come in that way.
- llvm::sort(Symbols, SymbolSorter());
+ llvm::stable_sort(Symbols, SymbolSorter());
// Build a data in code table that is sorted on by the address of each entry.
uint64_t BaseAddress = 0;
@@ -7489,13 +7473,13 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
return;
}
// Set up the block of info used by the Symbolizer call backs.
- SymbolizerInfo.verbose = !NoSymbolicOperands;
+ SymbolizerInfo.verbose = SymbolicOperands;
SymbolizerInfo.O = MachOOF;
SymbolizerInfo.S = Sections[SectIdx];
SymbolizerInfo.AddrMap = &AddrMap;
SymbolizerInfo.Sections = &Sections;
// Same for the ThumbSymbolizer
- ThumbSymbolizerInfo.verbose = !NoSymbolicOperands;
+ ThumbSymbolizerInfo.verbose = SymbolicOperands;
ThumbSymbolizerInfo.O = MachOOF;
ThumbSymbolizerInfo.S = Sections[SectIdx];
ThumbSymbolizerInfo.AddrMap = &AddrMap;
@@ -7620,7 +7604,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
outs() << SymName << ":\n";
uint64_t PC = SectAddress + Index;
- if (!NoLeadingAddr) {
+ if (LeadingAddr) {
if (FullLeadingAddr) {
if (MachOOF->is64Bit())
outs() << format("%016" PRIx64, PC);
@@ -7630,7 +7614,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
outs() << format("%8" PRIx64 ":", PC);
}
}
- if (!NoShowRawInsn || Arch == Triple::arm)
+ if (ShowRawInsn || Arch == Triple::arm)
outs() << "\t";
if (DumpAndSkipDataInCode(PC, Bytes.data() + Index, Dices, Size))
@@ -7647,7 +7631,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
gotInst = DisAsm->getInstruction(Inst, Size, Bytes.slice(Index), PC,
Annotations);
if (gotInst) {
- if (!NoShowRawInsn || Arch == Triple::arm) {
+ if (ShowRawInsn || Arch == Triple::arm) {
dumpBytes(makeArrayRef(Bytes.data() + Index, Size), outs());
}
formatted_raw_ostream FormattedOS(outs());
@@ -7717,7 +7701,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
raw_svector_ostream Annotations(AnnotationsBytes);
if (DisAsm->getInstruction(Inst, InstSize, Bytes.slice(Index), PC,
Annotations)) {
- if (!NoLeadingAddr) {
+ if (LeadingAddr) {
if (FullLeadingAddr) {
if (MachOOF->is64Bit())
outs() << format("%016" PRIx64, PC);
@@ -7727,7 +7711,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
outs() << format("%8" PRIx64 ":", PC);
}
}
- if (!NoShowRawInsn || Arch == Triple::arm) {
+ if (ShowRawInsn || Arch == Triple::arm) {
outs() << "\t";
dumpBytes(makeArrayRef(Bytes.data() + Index, InstSize), outs());
}
@@ -8012,12 +7996,23 @@ static void printCompressedSecondLevelUnwindPage(
(void)Kind;
assert(Kind == 3 && "kind for a compressed 2nd level index should be 3");
+ uint32_t NumCommonEncodings = CommonEncodings.size();
uint16_t EntriesStart = readNext<uint16_t>(PageData, Pos);
uint16_t NumEntries = readNext<uint16_t>(PageData, Pos);
- uint16_t EncodingsStart = readNext<uint16_t>(PageData, Pos);
- readNext<uint16_t>(PageData, Pos);
- StringRef PageEncodings = PageData.substr(EncodingsStart, StringRef::npos);
+ uint16_t PageEncodingsStart = readNext<uint16_t>(PageData, Pos);
+ uint16_t NumPageEncodings = readNext<uint16_t>(PageData, Pos);
+ SmallVector<uint32_t, 64> PageEncodings;
+ if (NumPageEncodings) {
+ outs() << " Page encodings: (count = " << NumPageEncodings << ")\n";
+ Pos = PageEncodingsStart;
+ for (unsigned i = 0; i < NumPageEncodings; ++i) {
+ uint32_t Encoding = readNext<uint32_t>(PageData, Pos);
+ PageEncodings.push_back(Encoding);
+ outs() << " encoding[" << (i + NumCommonEncodings)
+ << "]: " << format("0x%08" PRIx32, Encoding) << '\n';
+ }
+ }
Pos = EntriesStart;
for (unsigned i = 0; i < NumEntries; ++i) {
@@ -8026,12 +8021,10 @@ static void printCompressedSecondLevelUnwindPage(
uint32_t EncodingIdx = Entry >> 24;
uint32_t Encoding;
- if (EncodingIdx < CommonEncodings.size())
+ if (EncodingIdx < NumCommonEncodings)
Encoding = CommonEncodings[EncodingIdx];
else
- Encoding = read<uint32_t>(PageEncodings,
- sizeof(uint32_t) *
- (EncodingIdx - CommonEncodings.size()));
+ Encoding = PageEncodings[EncodingIdx - NumCommonEncodings];
outs() << " [" << i << "]: "
<< "function offset=" << format("0x%08" PRIx32, FunctionOffset)
@@ -10233,7 +10226,7 @@ static void PrintMachHeader(const MachOObjectFile *Obj, bool verbose) {
void objdump::printMachOFileHeader(const object::ObjectFile *Obj) {
const MachOObjectFile *file = dyn_cast<const MachOObjectFile>(Obj);
- PrintMachHeader(file, !NonVerbose);
+ PrintMachHeader(file, Verbose);
}
void objdump::printMachOLoadCommands(const object::ObjectFile *Obj) {
@@ -10251,7 +10244,7 @@ void objdump::printMachOLoadCommands(const object::ObjectFile *Obj) {
filetype = H.filetype;
cputype = H.cputype;
}
- PrintLoadCommands(file, filetype, cputype, !NonVerbose);
+ PrintLoadCommands(file, filetype, cputype, Verbose);
}
//===----------------------------------------------------------------------===//
@@ -10292,30 +10285,16 @@ static void printMachOExportsTrie(const object::MachOObjectFile *Obj) {
Entry.address() + BaseSegmentAddress);
outs() << Entry.name();
if (WeakDef || ThreadLocal || Resolver || Abs) {
- bool NeedsComma = false;
+ ListSeparator LS;
outs() << " [";
- if (WeakDef) {
- outs() << "weak_def";
- NeedsComma = true;
- }
- if (ThreadLocal) {
- if (NeedsComma)
- outs() << ", ";
- outs() << "per-thread";
- NeedsComma = true;
- }
- if (Abs) {
- if (NeedsComma)
- outs() << ", ";
- outs() << "absolute";
- NeedsComma = true;
- }
- if (Resolver) {
- if (NeedsComma)
- outs() << ", ";
- outs() << format("resolver=0x%08llX", Entry.other());
- NeedsComma = true;
- }
+ if (WeakDef)
+ outs() << LS << "weak_def";
+ if (ThreadLocal)
+ outs() << LS << "per-thread";
+ if (Abs)
+ outs() << LS << "absolute";
+ if (Resolver)
+ outs() << LS << format("resolver=0x%08llX", Entry.other());
outs() << "]";
}
if (ReExport) {
@@ -10486,7 +10465,7 @@ static const char *get_dyld_bind_info_symbolname(uint64_t ReferenceValue,
}
void objdump::printLazyBindTable(ObjectFile *o) {
- outs() << "Lazy bind table:\n";
+ outs() << "\nLazy bind table:\n";
if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o))
printMachOLazyBindTable(MachO);
else
@@ -10496,7 +10475,7 @@ void objdump::printLazyBindTable(ObjectFile *o) {
}
void objdump::printWeakBindTable(ObjectFile *o) {
- outs() << "Weak bind table:\n";
+ outs() << "\nWeak bind table:\n";
if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o))
printMachOWeakBindTable(MachO);
else
@@ -10506,7 +10485,7 @@ void objdump::printWeakBindTable(ObjectFile *o) {
}
void objdump::printExportsTrie(const ObjectFile *o) {
- outs() << "Exports trie:\n";
+ outs() << "\nExports trie:\n";
if (const MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o))
printMachOExportsTrie(MachO);
else
@@ -10516,7 +10495,7 @@ void objdump::printExportsTrie(const ObjectFile *o) {
}
void objdump::printRebaseTable(ObjectFile *o) {
- outs() << "Rebase table:\n";
+ outs() << "\nRebase table:\n";
if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o))
printMachORebaseTable(MachO);
else
@@ -10526,7 +10505,7 @@ void objdump::printRebaseTable(ObjectFile *o) {
}
void objdump::printBindTable(ObjectFile *o) {
- outs() << "Bind table:\n";
+ outs() << "\nBind table:\n";
if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o))
printMachOBindTable(MachO);
else
diff --git a/llvm/tools/llvm-objdump/MachODump.h b/llvm/tools/llvm-objdump/MachODump.h
index adf6c3404f79..7568062bd6b0 100644
--- a/llvm/tools/llvm-objdump/MachODump.h
+++ b/llvm/tools/llvm-objdump/MachODump.h
@@ -24,24 +24,36 @@ class ObjectFile;
class RelocationRef;
} // namespace object
+namespace opt {
+class InputArgList;
+} // namespace opt
+
namespace objdump {
+void parseMachOOptions(const llvm::opt::InputArgList &InputArgs);
+
// MachO specific options
-extern cl::OptionCategory MachOCat;
-extern cl::opt<bool> Bind;
-extern cl::opt<bool> DataInCode;
-extern cl::opt<bool> DylibsUsed;
-extern cl::opt<bool> DylibId;
-extern cl::opt<bool> ExportsTrie;
-extern cl::opt<bool> FirstPrivateHeader;
-extern cl::opt<bool> IndirectSymbols;
-extern cl::opt<bool> InfoPlist;
-extern cl::opt<bool> LazyBind;
-extern cl::opt<bool> LinkOptHints;
-extern cl::opt<bool> ObjcMetaData;
-extern cl::opt<bool> Rebase;
-extern cl::opt<bool> UniversalHeaders;
-extern cl::opt<bool> WeakBind;
+extern bool Bind;
+extern bool DataInCode;
+extern std::string DisSymName;
+extern bool DylibId;
+extern bool DylibsUsed;
+extern bool ExportsTrie;
+extern bool FirstPrivateHeader;
+extern bool FullLeadingAddr;
+extern bool FunctionStarts;
+extern bool IndirectSymbols;
+extern bool InfoPlist;
+extern bool LazyBind;
+extern bool LeadingHeaders;
+extern bool LinkOptHints;
+extern bool ObjcMetaData;
+extern bool Rebase;
+extern bool Rpaths;
+extern bool SymbolicOperands;
+extern bool UniversalHeaders;
+extern bool Verbose;
+extern bool WeakBind;
Error getMachORelocationValueString(const object::MachOObjectFile *Obj,
const object::RelocationRef &RelRef,
diff --git a/llvm/tools/llvm-objdump/ObjdumpOptID.h b/llvm/tools/llvm-objdump/ObjdumpOptID.h
new file mode 100644
index 000000000000..65f6c60ad884
--- /dev/null
+++ b/llvm/tools/llvm-objdump/ObjdumpOptID.h
@@ -0,0 +1,13 @@
+#ifndef LLVM_TOOLS_LLVM_OBJDUMP_OBJDUMP_OPT_ID_H
+#define LLVM_TOOLS_LLVM_OBJDUMP_OBJDUMP_OPT_ID_H
+
+enum ObjdumpOptID {
+ OBJDUMP_INVALID = 0, // This is not an option ID.
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ OBJDUMP_##ID,
+#include "ObjdumpOpts.inc"
+#undef OPTION
+};
+
+#endif // LLVM_TOOLS_LLVM_OBJDUMP_OBJDUMP_OPT_ID_H
diff --git a/llvm/tools/llvm-objdump/ObjdumpOpts.td b/llvm/tools/llvm-objdump/ObjdumpOpts.td
new file mode 100644
index 000000000000..1b19733c65d0
--- /dev/null
+++ b/llvm/tools/llvm-objdump/ObjdumpOpts.td
@@ -0,0 +1,323 @@
+include "llvm/Option/OptParser.td"
+
+def help : Flag<["--"], "help">,
+ HelpText<"Display available options (--help-hidden for more)">;
+
+def help_hidden : Flag<["--"], "help-hidden">,
+ Flags<[HelpHidden]>,
+ HelpText<"Display all available options">;
+
+def version : Flag<["--"], "version">,
+ HelpText<"Display the version of this program">;
+def : Flag<["-"], "v">, Alias<version>, HelpText<"Alias for --version">;
+
+def adjust_vma_EQ : Joined<["--"], "adjust-vma=">,
+ MetaVarName<"offset">,
+ HelpText<"Increase the displayed address by the specified offset">;
+
+def all_headers : Flag<["--"], "all-headers">,
+ HelpText<"Display all available header information">;
+def : Flag<["-"], "x">, Alias<all_headers>, HelpText<"Alias for --all-headers">;
+
+def arch_name_EQ : Joined<["--"], "arch-name=">,
+ HelpText<"Target arch to disassemble for, "
+ "see --version for available targets">;
+def archive_headers : Flag<["--"], "archive-headers">,
+ HelpText<"Display archive header information">;
+
+def : Flag<["-"], "a">, Alias<archive_headers>,
+ HelpText<"Alias for --archive-headers">;
+
+def demangle : Flag<["--"], "demangle">, HelpText<"Demangle symbol names">;
+def : Flag<["-"], "C">, Alias<demangle>, HelpText<"Alias for --demangle">;
+
+def disassemble : Flag<["--"], "disassemble">,
+ HelpText<"Display assembler mnemonics for the machine instructions">;
+def : Flag<["-"], "d">, Alias<disassemble>, HelpText<"Alias for --disassemble">;
+
+def disassemble_all : Flag<["--"], "disassemble-all">,
+ HelpText<"Display assembler mnemonics for the machine instructions">;
+def : Flag<["-"], "D">, Alias<disassemble_all>,
+ HelpText<"Alias for --disassemble-all">;
+
+def symbol_description : Flag<["--"], "symbol-description">,
+ HelpText<"Add symbol description for disassembly. This "
+ "option is for XCOFF files only.">;
+
+def disassemble_symbols_EQ : Joined<["--"], "disassemble-symbols=">,
+ HelpText<"List of symbols to disassemble. "
+ "Accept demangled names when --demangle is "
+ "specified, otherwise accept mangled names">;
+
+def disassemble_zeroes : Flag<["--"], "disassemble-zeroes">,
+ HelpText<"Do not skip blocks of zeroes when disassembling">;
+def : Flag<["-"], "z">, Alias<disassemble_zeroes>,
+ HelpText<"Alias for --disassemble-zeroes">;
+
+def disassembler_options_EQ : Joined<["--"], "disassembler-options=">,
+ MetaVarName<"options">,
+ HelpText<"Pass target specific disassembler options">;
+def : JoinedOrSeparate<["-"], "M">, Alias<disassembler_options_EQ>,
+ HelpText<"Alias for --disassembler-options=">;
+
+def dynamic_reloc : Flag<["--"], "dynamic-reloc">,
+ HelpText<"Display the dynamic relocation entries in the file">;
+def : Flag<["-"], "R">, Alias<dynamic_reloc>,
+ HelpText<"Alias for --dynamic-reloc">;
+
+def dwarf_EQ : Joined<["--"], "dwarf=">,
+ HelpText<"Dump of dwarf debug sections">, Values<"frames">;
+
+def fault_map_section : Flag<["--"], "fault-map-section">,
+ HelpText<"Display contents of faultmap section">;
+
+def file_headers : Flag<["--"], "file-headers">,
+ HelpText<"Display the contents of the overall file header">;
+def : Flag<["-"], "f">, Alias<file_headers>,
+ HelpText<"Alias for --file-headers">;
+
+def full_contents : Flag<["--"], "full-contents">,
+ HelpText<"Display the content of each section">;
+def : Flag<["-"], "s">, Alias<full_contents>,
+ HelpText<"Alias for --full-contents">;
+
+def line_numbers : Flag<["--"], "line-numbers">,
+ HelpText<"Display source line numbers with "
+ "disassembly. Implies disassemble object">;
+def : Flag<["-"], "l">, Alias<line_numbers>,
+ HelpText<"Alias for --line-numbers">;
+
+def macho : Flag<["--"], "macho">,
+ HelpText<"Use MachO specific object file parser">;
+def : Flag<["-"], "m">, Alias<macho>, HelpText<"Alias for --macho">;
+
+def mcpu_EQ : Joined<["--"], "mcpu=">,
+ MetaVarName<"cpu-name">,
+ HelpText<"Target a specific cpu type (--mcpu=help for details)">;
+
+def mattr_EQ : Joined<["--"], "mattr=">,
+ MetaVarName<"a1,+a2,-a3,...">,
+ HelpText<"Target specific attributes (--mattr=help for details)">;
+
+def no_show_raw_insn : Flag<["--"], "no-show-raw-insn">,
+ HelpText<"When disassembling instructions, "
+ "do not print the instruction bytes.">;
+
+def no_leading_addr : Flag<["--"], "no-leading-addr">,
+ HelpText<"Print no leading address">;
+
+def raw_clang_ast : Flag<["--"], "raw-clang-ast">,
+ HelpText<"Dump the raw binary contents of the clang AST section">;
+
+def reloc : Flag<["--"], "reloc">,
+ HelpText<"Display the relocation entries in the file">;
+def : Flag<["-"], "r">, Alias<reloc>, HelpText<"Alias for --reloc">;
+
+def print_imm_hex : Flag<["--"], "print-imm-hex">,
+ HelpText<"Use hex format for immediate values">;
+
+def no_print_imm_hex : Flag<["--"], "no-print-imm-hex">,
+ HelpText<"Do not use hex format for immediate values (default)">;
+def : Flag<["--"], "print-imm-hex=false">, Alias<no_print_imm_hex>;
+
+def private_headers : Flag<["--"], "private-headers">,
+ HelpText<"Display format specific file headers">;
+def : Flag<["-"], "p">, Alias<private_headers>,
+ HelpText<"Alias for --private-headers">;
+
+def section_EQ : Joined<["--"], "section=">,
+ HelpText<"Operate on the specified sections only. "
+ "With --macho dump segment,section">;
+def : Separate<["--"], "section">, Alias<section_EQ>;
+def : JoinedOrSeparate<["-"], "j">, Alias<section_EQ>,
+ HelpText<"Alias for --section">;
+
+def section_headers : Flag<["--"], "section-headers">,
+ HelpText<"Display summaries of the headers for each section.">;
+def : Flag<["--"], "headers">, Alias<section_headers>,
+ HelpText<"Alias for --section-headers">;
+def : Flag<["-"], "h">, Alias<section_headers>,
+ HelpText<"Alias for --section-headers">;
+
+def show_lma : Flag<["--"], "show-lma">,
+ HelpText<"Display LMA column when dumping ELF section headers">;
+
+def source : Flag<["--"], "source">,
+ HelpText<"Display source inlined with disassembly. Implies disassemble object">;
+def : Flag<["-"], "S">, Alias<source>, HelpText<"Alias for --source">;
+
+def start_address_EQ : Joined<["--"], "start-address=">,
+ MetaVarName<"address">,
+ HelpText<"Disassemble beginning at address">;
+def stop_address_EQ : Joined<["--"], "stop-address=">,
+ MetaVarName<"address">,
+ HelpText<"Stop disassembly at address">;
+
+def syms : Flag<["--"], "syms">,
+ HelpText<"Display the symbol table">;
+def : Flag<["-"], "t">, Alias<syms>, HelpText<"Alias for --syms">;
+
+def symbolize_operands : Flag<["--"], "symbolize-operands">,
+ HelpText<"Symbolize instruction operands when disassembling">;
+
+def dynamic_syms : Flag<["--"], "dynamic-syms">,
+ HelpText<"Display the contents of the dynamic symbol table">;
+def : Flag<["-"], "T">, Alias<dynamic_syms>,
+ HelpText<"Alias for --dynamic-syms">;
+
+def triple_EQ : Joined<["--"], "triple=">,
+ HelpText<"Target triple to disassemble for, "
+ "see --version for available targets">;
+def : Separate<["--"], "triple">,
+ Alias<triple_EQ>;
+
+def unwind_info : Flag<["--"], "unwind-info">,
+ HelpText<"Display unwind information">;
+def : Flag<["-"], "u">, Alias<unwind_info>,
+ HelpText<"Alias for --unwind-info">;
+
+def wide : Flag<["--"], "wide">,
+ HelpText<"Ignored for compatibility with GNU objdump">;
+def : Flag<["-"], "w">, Alias<wide>;
+
+def prefix : Separate<["--"], "prefix">,
+ HelpText<"Add prefix to absolute paths">;
+
+def prefix_strip : Separate<["--"], "prefix-strip">,
+ HelpText<"Strip out initial directories from absolute "
+ "paths. No effect without --prefix">;
+
+def debug_vars_EQ : Joined<["--"], "debug-vars=">,
+ Values<"unicode,ascii">;
+def : Flag<["--"], "debug-vars">,
+ HelpText<"Print the locations (in registers or memory) of "
+ "source-level variables alongside disassembly">,
+ Alias<debug_vars_EQ>, AliasArgs<["unicode"]>;
+
+def debug_vars_indent_EQ : Joined<["--"], "debug-vars-indent=">,
+ HelpText<"Distance to indent the source-level variable display, "
+ "relative to the start of the disassembly">;
+
+def x86_asm_syntax_att : Flag<["--"], "x86-asm-syntax=att">,
+ HelpText<"Emit AT&T-style disassembly">;
+
+def x86_asm_syntax_intel : Flag<["--"], "x86-asm-syntax=intel">,
+ HelpText<"Emit Intel-style disassembly">;
+
+
+def grp_mach_o : OptionGroup<"kind">, HelpText<"llvm-objdump MachO Specific Options">;
+
+def private_header : Flag<["--"], "private-header">,
+ HelpText<"Display only the first format specific file header">,
+ Group<grp_mach_o>;
+
+def exports_trie : Flag<["--"], "exports-trie">,
+ HelpText<"Display mach-o exported symbols">,
+ Group<grp_mach_o>;
+
+def rebase : Flag<["--"], "rebase">,
+ HelpText<"Display mach-o rebasing info">,
+ Group<grp_mach_o>;
+
+def bind : Flag<["--"], "bind">,
+ HelpText<"Display mach-o binding info">,
+ Group<grp_mach_o>;
+
+def lazy_bind : Flag<["--"], "lazy-bind">,
+ HelpText<"Display mach-o lazy binding info">,
+ Group<grp_mach_o>;
+
+def weak_bind : Flag<["--"], "weak-bind">,
+ HelpText<"Display mach-o weak binding info">,
+ Group<grp_mach_o>;
+
+def g : Flag<["-"], "g">,
+ HelpText<"Print line information from debug info if available">,
+ Group<grp_mach_o>;
+
+def dsym_EQ : Joined<["--"], "dsym=">,
+ HelpText<"Use .dSYM file for debug info">,
+ Group<grp_mach_o>;
+def : Separate<["--"], "dsym">,
+ Alias<dsym_EQ>,
+ Group<grp_mach_o>;
+
+def full_leading_addr : Flag<["--"], "full-leading-addr">,
+ HelpText<"Print full leading address">,
+ Group<grp_mach_o>;
+
+def no_leading_headers : Flag<["--"], "no-leading-headers">,
+ HelpText<"Print no leading headers">,
+ Group<grp_mach_o>;
+
+def universal_headers : Flag<["--"], "universal-headers">,
+ HelpText<"Print Mach-O universal headers (requires --macho)">,
+ Group<grp_mach_o>;
+
+def archive_member_offsets : Flag<["--"], "archive-member-offsets">,
+ HelpText<"Print the offset to each archive member for Mach-O archives "
+ "(requires --macho and --archive-headers)">,
+ Group<grp_mach_o>;
+
+def indirect_symbols : Flag<["--"], "indirect-symbols">,
+ HelpText<"Print indirect symbol table for Mach-O objects (requires --macho)">,
+ Group<grp_mach_o>;
+
+def data_in_code : Flag<["--"], "data-in-code">,
+ HelpText<"Print the data in code table for Mach-O objects (requires --macho)">,
+ Group<grp_mach_o>;
+
+def function_starts : Flag<["--"], "function-starts">,
+ HelpText<"Print the function starts table for "
+ "Mach-O objects (requires --macho)">,
+ Group<grp_mach_o>;
+
+def link_opt_hints : Flag<["--"], "link-opt-hints">,
+ HelpText<"Print the linker optimization hints for "
+ "Mach-O objects (requires --macho)">,
+ Group<grp_mach_o>;
+
+def info_plist : Flag<["--"], "info-plist">,
+ HelpText<"Print the info plist section as strings for "
+ "Mach-O objects (requires --macho)">,
+ Group<grp_mach_o>;
+
+def dylibs_used : Flag<["--"], "dylibs-used">,
+ HelpText<"Print the shared libraries used for linked "
+ "Mach-O files (requires --macho)">,
+ Group<grp_mach_o>;
+
+def dylib_id : Flag<["--"], "dylib-id">,
+ HelpText<"Print the shared library's id for the "
+ "dylib Mach-O file (requires --macho)">,
+ Group<grp_mach_o>;
+
+def rpaths : Flag<["--"], "rpaths">,
+ HelpText<"Print the runtime search paths for the "
+ "Mach-O file (requires --macho)">,
+ Group<grp_mach_o>;
+
+def non_verbose : Flag<["--"], "non-verbose">,
+ HelpText<"Print the info for Mach-O objects in non-verbose or "
+ "numeric form (requires --macho)">,
+ Group<grp_mach_o>;
+
+def objc_meta_data : Flag<["--"], "objc-meta-data">,
+ HelpText<"Print the Objective-C runtime meta data "
+ "for Mach-O files (requires --macho)">,
+ Group<grp_mach_o>;
+
+def dis_symname : Separate<["--"], "dis-symname">,
+ HelpText<"disassemble just this symbol's instructions (requires --macho)">,
+ Group<grp_mach_o>;
+
+def no_symbolic_operands : Flag<["--"], "no-symbolic-operands">,
+ HelpText<"do not symbolic operands when disassembling (requires --macho)">,
+ Group<grp_mach_o>;
+
+def arch_EQ : Joined<["--"], "arch=">,
+ HelpText<"architecture(s) from a Mach-O file to dump">,
+ Group<grp_mach_o>;
+def : Separate<["--"], "arch">,
+ Alias<arch_EQ>,
+ Group<grp_mach_o>;
diff --git a/llvm/tools/llvm-objdump/OtoolOpts.td b/llvm/tools/llvm-objdump/OtoolOpts.td
new file mode 100644
index 000000000000..61ea701ed75d
--- /dev/null
+++ b/llvm/tools/llvm-objdump/OtoolOpts.td
@@ -0,0 +1,68 @@
+include "llvm/Option/OptParser.td"
+
+def help : Flag<["--"], "help">, HelpText<"print help">;
+def help_hidden : Flag<["--"], "help-hidden">,
+ HelpText<"print help for hidden flags">;
+
+def arch : Separate<["-"], "arch">,
+ HelpText<"select slice of universal Mach-O file">;
+def C : Flag<["-"], "C">, HelpText<"print linker optimization hints">;
+def d : Flag<["-"], "d">, HelpText<"print data section">;
+def D : Flag<["-"], "D">, HelpText<"print shared library id">;
+def f : Flag<["-"], "f">, HelpText<"print universal headers">;
+def G : Flag<["-"], "G">, HelpText<"print data-in-code table">;
+def h : Flag<["-"], "h">, HelpText<"print mach header">;
+def I : Flag<["-"], "I">, HelpText<"print indirect symbol table">;
+def j : Flag<["-"], "j">, HelpText<"print opcode bytes">;
+def l : Flag<["-"], "l">, HelpText<"print load commnads">;
+def L : Flag<["-"], "L">, HelpText<"print used shared libraries">;
+def mcpu_EQ : Joined<["-"], "mcpu=">, HelpText<"select cpu for disassembly">;
+def o : Flag<["-"], "o">, HelpText<"print Objective-C segment">;
+def p : Separate<["-"], "p">,
+ MetaVarName<"<function name>">,
+ HelpText<"start disassembly at <function name>">;
+def P : Flag<["-"], "P">, HelpText<"print __TEXT,__info_plist section as strings">;
+def : Flag<["-"], "q">, Flags<[HelpHidden]>,
+ HelpText<"use LLVM's disassembler (default)">;
+def r : Flag<["-"], "r">, HelpText<"print relocation entries">;
+def s : MultiArg<["-"], "s", 2>,
+ MetaVarName<"<segname> <sectname>">,
+ HelpText<"print contents of section">;
+def t : Flag<["-"], "t">, HelpText<"print text section">;
+def version : Flag<["--"], "version">, HelpText<"print version">;
+def v : Flag<["-"], "v">,
+ HelpText<"verbose output / disassemble when printing text sections">;
+def V : Flag<["-"], "V">,
+ HelpText<"symbolize disassembled operands (implies -v)">;
+def x : Flag<["-"], "x">, HelpText<"print all text sections">;
+def X : Flag<["-"], "X">, HelpText<"omit leading addresses or headers">;
+
+// Not (yet?) implemented:
+// def a : Flag<["-"], "a">, HelpText<"print archive header">;
+// -c print argument strings of a core file
+// -m don't use archive(member) syntax
+// -dyld_info
+// -dyld_opcodes
+// -chained_fixups
+// -addr_slide=arg
+// -function_offsets
+
+
+// Obsolete and unsupported:
+def grp_obsolete : OptionGroup<"kind">,
+ HelpText<"Obsolete and unsupported flags">;
+
+def : Flag<["-"], "B">, Flags<[HelpHidden]>, Group<grp_obsolete>,
+ HelpText<"force Thum disassembly (ARM 32-bit objects only)">;
+def : Flag<["-"], "H">, Flags<[HelpHidden]>, Group<grp_obsolete>,
+ HelpText<"print two-level hints table">;
+def : Flag<["-"], "M">, Flags<[HelpHidden]>, Group<grp_obsolete>,
+ HelpText<"print module table of shared library">;
+def : Flag<["-"], "R">, Flags<[HelpHidden]>, Group<grp_obsolete>,
+ HelpText<"print reference table of shared library">;
+def : Flag<["-"], "S">, Flags<[HelpHidden]>, Group<grp_obsolete>,
+ HelpText<"print table of contents of library">;
+def : Flag<["-"], "T">, Flags<[HelpHidden]>, Group<grp_obsolete>,
+ HelpText<"print table of contents of shared library">;
+def : Flag<["-"], "Q">, Flags<[HelpHidden]>, Group<grp_obsolete>,
+ HelpText<"llvm-otool cannot use otool-classic's disassembler">;
diff --git a/llvm/tools/llvm-objdump/SourcePrinter.cpp b/llvm/tools/llvm-objdump/SourcePrinter.cpp
new file mode 100644
index 000000000000..8befac546204
--- /dev/null
+++ b/llvm/tools/llvm-objdump/SourcePrinter.cpp
@@ -0,0 +1,483 @@
+//===-- SourcePrinter.cpp - source interleaving utilities ----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the LiveVariablePrinter and SourcePrinter classes to
+// keep track of DWARF info as the current address is updated, and print out the
+// source file line and variable liveness as needed.
+//
+//===----------------------------------------------------------------------===//
+
+#include "SourcePrinter.h"
+#include "llvm-objdump.h"
+#include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/Support/FormatVariadic.h"
+
+#define DEBUG_TYPE "objdump"
+
+namespace llvm {
+namespace objdump {
+
+unsigned getInstStartColumn(const MCSubtargetInfo &STI) {
+ return !ShowRawInsn ? 16 : STI.getTargetTriple().isX86() ? 40 : 24;
+}
+
+bool LiveVariable::liveAtAddress(object::SectionedAddress Addr) {
+ if (LocExpr.Range == None)
+ return false;
+ return LocExpr.Range->SectionIndex == Addr.SectionIndex &&
+ LocExpr.Range->LowPC <= Addr.Address &&
+ LocExpr.Range->HighPC > Addr.Address;
+}
+
+void LiveVariable::print(raw_ostream &OS, const MCRegisterInfo &MRI) const {
+ DataExtractor Data({LocExpr.Expr.data(), LocExpr.Expr.size()},
+ Unit->getContext().isLittleEndian(), 0);
+ DWARFExpression Expression(Data, Unit->getAddressByteSize());
+ Expression.printCompact(OS, MRI);
+}
+
+void LiveVariablePrinter::addVariable(DWARFDie FuncDie, DWARFDie VarDie) {
+ uint64_t FuncLowPC, FuncHighPC, SectionIndex;
+ FuncDie.getLowAndHighPC(FuncLowPC, FuncHighPC, SectionIndex);
+ const char *VarName = VarDie.getName(DINameKind::ShortName);
+ DWARFUnit *U = VarDie.getDwarfUnit();
+
+ Expected<DWARFLocationExpressionsVector> Locs =
+ VarDie.getLocations(dwarf::DW_AT_location);
+ if (!Locs) {
+ // If the variable doesn't have any locations, just ignore it. We don't
+ // report an error or warning here as that could be noisy on optimised
+ // code.
+ consumeError(Locs.takeError());
+ return;
+ }
+
+ for (const DWARFLocationExpression &LocExpr : *Locs) {
+ if (LocExpr.Range) {
+ LiveVariables.emplace_back(LocExpr, VarName, U, FuncDie);
+ } else {
+ // If the LocExpr does not have an associated range, it is valid for
+ // the whole of the function.
+ // TODO: technically it is not valid for any range covered by another
+ // LocExpr, does that happen in reality?
+ DWARFLocationExpression WholeFuncExpr{
+ DWARFAddressRange(FuncLowPC, FuncHighPC, SectionIndex), LocExpr.Expr};
+ LiveVariables.emplace_back(WholeFuncExpr, VarName, U, FuncDie);
+ }
+ }
+}
+
+void LiveVariablePrinter::addFunction(DWARFDie D) {
+ for (const DWARFDie &Child : D.children()) {
+ if (Child.getTag() == dwarf::DW_TAG_variable ||
+ Child.getTag() == dwarf::DW_TAG_formal_parameter)
+ addVariable(D, Child);
+ else
+ addFunction(Child);
+ }
+}
+
+// Get the column number (in characters) at which the first live variable
+// line should be printed.
+unsigned LiveVariablePrinter::getIndentLevel() const {
+ return DbgIndent + getInstStartColumn(STI);
+}
+
+// Indent to the first live-range column to the right of the currently
+// printed line, and return the index of that column.
+// TODO: formatted_raw_ostream uses "column" to mean a number of characters
+// since the last \n, and we use it to mean the number of slots in which we
+// put live variable lines. Pick a less overloaded word.
+unsigned LiveVariablePrinter::moveToFirstVarColumn(formatted_raw_ostream &OS) {
+ // Logical column number: column zero is the first column we print in, each
+ // logical column is 2 physical columns wide.
+ unsigned FirstUnprintedLogicalColumn =
+ std::max((int)(OS.getColumn() - getIndentLevel() + 1) / 2, 0);
+ // Physical column number: the actual column number in characters, with
+ // zero being the left-most side of the screen.
+ unsigned FirstUnprintedPhysicalColumn =
+ getIndentLevel() + FirstUnprintedLogicalColumn * 2;
+
+ if (FirstUnprintedPhysicalColumn > OS.getColumn())
+ OS.PadToColumn(FirstUnprintedPhysicalColumn);
+
+ return FirstUnprintedLogicalColumn;
+}
+
+unsigned LiveVariablePrinter::findFreeColumn() {
+ for (unsigned ColIdx = 0; ColIdx < ActiveCols.size(); ++ColIdx)
+ if (!ActiveCols[ColIdx].isActive())
+ return ColIdx;
+
+ size_t OldSize = ActiveCols.size();
+ ActiveCols.grow(std::max<size_t>(OldSize * 2, 1));
+ return OldSize;
+}
+
+void LiveVariablePrinter::dump() const {
+ for (const LiveVariable &LV : LiveVariables) {
+ dbgs() << LV.VarName << " @ " << LV.LocExpr.Range << ": ";
+ LV.print(dbgs(), MRI);
+ dbgs() << "\n";
+ }
+}
+
+void LiveVariablePrinter::addCompileUnit(DWARFDie D) {
+ if (D.getTag() == dwarf::DW_TAG_subprogram)
+ addFunction(D);
+ else
+ for (const DWARFDie &Child : D.children())
+ addFunction(Child);
+}
+
+/// Update to match the state of the instruction between ThisAddr and
+/// NextAddr. In the common case, any live range active at ThisAddr is
+/// live-in to the instruction, and any live range active at NextAddr is
+/// live-out of the instruction. If IncludeDefinedVars is false, then live
+/// ranges starting at NextAddr will be ignored.
+void LiveVariablePrinter::update(object::SectionedAddress ThisAddr,
+ object::SectionedAddress NextAddr,
+ bool IncludeDefinedVars) {
+ // First, check variables which have already been assigned a column, so
+ // that we don't change their order.
+ SmallSet<unsigned, 8> CheckedVarIdxs;
+ for (unsigned ColIdx = 0, End = ActiveCols.size(); ColIdx < End; ++ColIdx) {
+ if (!ActiveCols[ColIdx].isActive())
+ continue;
+ CheckedVarIdxs.insert(ActiveCols[ColIdx].VarIdx);
+ LiveVariable &LV = LiveVariables[ActiveCols[ColIdx].VarIdx];
+ ActiveCols[ColIdx].LiveIn = LV.liveAtAddress(ThisAddr);
+ ActiveCols[ColIdx].LiveOut = LV.liveAtAddress(NextAddr);
+ LLVM_DEBUG(dbgs() << "pass 1, " << ThisAddr.Address << "-"
+ << NextAddr.Address << ", " << LV.VarName << ", Col "
+ << ColIdx << ": LiveIn=" << ActiveCols[ColIdx].LiveIn
+ << ", LiveOut=" << ActiveCols[ColIdx].LiveOut << "\n");
+
+ if (!ActiveCols[ColIdx].LiveIn && !ActiveCols[ColIdx].LiveOut)
+ ActiveCols[ColIdx].VarIdx = Column::NullVarIdx;
+ }
+
+ // Next, look for variables which don't already have a column, but which
+ // are now live.
+ if (IncludeDefinedVars) {
+ for (unsigned VarIdx = 0, End = LiveVariables.size(); VarIdx < End;
+ ++VarIdx) {
+ if (CheckedVarIdxs.count(VarIdx))
+ continue;
+ LiveVariable &LV = LiveVariables[VarIdx];
+ bool LiveIn = LV.liveAtAddress(ThisAddr);
+ bool LiveOut = LV.liveAtAddress(NextAddr);
+ if (!LiveIn && !LiveOut)
+ continue;
+
+ unsigned ColIdx = findFreeColumn();
+ LLVM_DEBUG(dbgs() << "pass 2, " << ThisAddr.Address << "-"
+ << NextAddr.Address << ", " << LV.VarName << ", Col "
+ << ColIdx << ": LiveIn=" << LiveIn
+ << ", LiveOut=" << LiveOut << "\n");
+ ActiveCols[ColIdx].VarIdx = VarIdx;
+ ActiveCols[ColIdx].LiveIn = LiveIn;
+ ActiveCols[ColIdx].LiveOut = LiveOut;
+ ActiveCols[ColIdx].MustDrawLabel = true;
+ }
+ }
+}
+
+enum class LineChar {
+ RangeStart,
+ RangeMid,
+ RangeEnd,
+ LabelVert,
+ LabelCornerNew,
+ LabelCornerActive,
+ LabelHoriz,
+};
+const char *LiveVariablePrinter::getLineChar(LineChar C) const {
+ bool IsASCII = DbgVariables == DVASCII;
+ switch (C) {
+ case LineChar::RangeStart:
+ return IsASCII ? "^" : (const char *)u8"\u2548";
+ case LineChar::RangeMid:
+ return IsASCII ? "|" : (const char *)u8"\u2503";
+ case LineChar::RangeEnd:
+ return IsASCII ? "v" : (const char *)u8"\u253b";
+ case LineChar::LabelVert:
+ return IsASCII ? "|" : (const char *)u8"\u2502";
+ case LineChar::LabelCornerNew:
+ return IsASCII ? "/" : (const char *)u8"\u250c";
+ case LineChar::LabelCornerActive:
+ return IsASCII ? "|" : (const char *)u8"\u2520";
+ case LineChar::LabelHoriz:
+ return IsASCII ? "-" : (const char *)u8"\u2500";
+ }
+ llvm_unreachable("Unhandled LineChar enum");
+}
+
+/// Print live ranges to the right of an existing line. This assumes the
+/// line is not an instruction, so doesn't start or end any live ranges, so
+/// we only need to print active ranges or empty columns. If AfterInst is
+/// true, this is being printed after the last instruction fed to update(),
+/// otherwise this is being printed before it.
+void LiveVariablePrinter::printAfterOtherLine(formatted_raw_ostream &OS,
+ bool AfterInst) {
+ if (ActiveCols.size()) {
+ unsigned FirstUnprintedColumn = moveToFirstVarColumn(OS);
+ for (size_t ColIdx = FirstUnprintedColumn, End = ActiveCols.size();
+ ColIdx < End; ++ColIdx) {
+ if (ActiveCols[ColIdx].isActive()) {
+ if ((AfterInst && ActiveCols[ColIdx].LiveOut) ||
+ (!AfterInst && ActiveCols[ColIdx].LiveIn))
+ OS << getLineChar(LineChar::RangeMid);
+ else if (!AfterInst && ActiveCols[ColIdx].LiveOut)
+ OS << getLineChar(LineChar::LabelVert);
+ else
+ OS << " ";
+ }
+ OS << " ";
+ }
+ }
+ OS << "\n";
+}
+
+/// Print any live variable range info needed to the right of a
+/// non-instruction line of disassembly. This is where we print the variable
+/// names and expressions, with thin line-drawing characters connecting them
+/// to the live range which starts at the next instruction. If MustPrint is
+/// true, we have to print at least one line (with the continuation of any
+/// already-active live ranges) because something has already been printed
+/// earlier on this line.
+void LiveVariablePrinter::printBetweenInsts(formatted_raw_ostream &OS,
+ bool MustPrint) {
+ bool PrintedSomething = false;
+ for (unsigned ColIdx = 0, End = ActiveCols.size(); ColIdx < End; ++ColIdx) {
+ if (ActiveCols[ColIdx].isActive() && ActiveCols[ColIdx].MustDrawLabel) {
+ // First we need to print the live range markers for any active
+ // columns to the left of this one.
+ OS.PadToColumn(getIndentLevel());
+ for (unsigned ColIdx2 = 0; ColIdx2 < ColIdx; ++ColIdx2) {
+ if (ActiveCols[ColIdx2].isActive()) {
+ if (ActiveCols[ColIdx2].MustDrawLabel && !ActiveCols[ColIdx2].LiveIn)
+ OS << getLineChar(LineChar::LabelVert) << " ";
+ else
+ OS << getLineChar(LineChar::RangeMid) << " ";
+ } else
+ OS << " ";
+ }
+
+ // Then print the variable name and location of the new live range,
+ // with box drawing characters joining it to the live range line.
+ OS << getLineChar(ActiveCols[ColIdx].LiveIn ? LineChar::LabelCornerActive
+ : LineChar::LabelCornerNew)
+ << getLineChar(LineChar::LabelHoriz) << " ";
+ WithColor(OS, raw_ostream::GREEN)
+ << LiveVariables[ActiveCols[ColIdx].VarIdx].VarName;
+ OS << " = ";
+ {
+ WithColor ExprColor(OS, raw_ostream::CYAN);
+ LiveVariables[ActiveCols[ColIdx].VarIdx].print(OS, MRI);
+ }
+
+ // If there are any columns to the right of the expression we just
+ // printed, then continue their live range lines.
+ unsigned FirstUnprintedColumn = moveToFirstVarColumn(OS);
+ for (unsigned ColIdx2 = FirstUnprintedColumn, End = ActiveCols.size();
+ ColIdx2 < End; ++ColIdx2) {
+ if (ActiveCols[ColIdx2].isActive() && ActiveCols[ColIdx2].LiveIn)
+ OS << getLineChar(LineChar::RangeMid) << " ";
+ else
+ OS << " ";
+ }
+
+ OS << "\n";
+ PrintedSomething = true;
+ }
+ }
+
+ for (unsigned ColIdx = 0, End = ActiveCols.size(); ColIdx < End; ++ColIdx)
+ if (ActiveCols[ColIdx].isActive())
+ ActiveCols[ColIdx].MustDrawLabel = false;
+
+ // If we must print something (because we printed a line/column number),
+ // but don't have any new variables to print, then print a line which
+ // just continues any existing live ranges.
+ if (MustPrint && !PrintedSomething)
+ printAfterOtherLine(OS, false);
+}
+
+/// Print the live variable ranges to the right of a disassembled instruction.
+void LiveVariablePrinter::printAfterInst(formatted_raw_ostream &OS) {
+ if (!ActiveCols.size())
+ return;
+ unsigned FirstUnprintedColumn = moveToFirstVarColumn(OS);
+ for (unsigned ColIdx = FirstUnprintedColumn, End = ActiveCols.size();
+ ColIdx < End; ++ColIdx) {
+ if (!ActiveCols[ColIdx].isActive())
+ OS << " ";
+ else if (ActiveCols[ColIdx].LiveIn && ActiveCols[ColIdx].LiveOut)
+ OS << getLineChar(LineChar::RangeMid) << " ";
+ else if (ActiveCols[ColIdx].LiveOut)
+ OS << getLineChar(LineChar::RangeStart) << " ";
+ else if (ActiveCols[ColIdx].LiveIn)
+ OS << getLineChar(LineChar::RangeEnd) << " ";
+ else
+ llvm_unreachable("var must be live in or out!");
+ }
+}
+
+bool SourcePrinter::cacheSource(const DILineInfo &LineInfo) {
+ std::unique_ptr<MemoryBuffer> Buffer;
+ if (LineInfo.Source) {
+ Buffer = MemoryBuffer::getMemBuffer(*LineInfo.Source);
+ } else {
+ auto BufferOrError = MemoryBuffer::getFile(LineInfo.FileName);
+ if (!BufferOrError) {
+ if (MissingSources.insert(LineInfo.FileName).second)
+ reportWarning("failed to find source " + LineInfo.FileName,
+ Obj->getFileName());
+ return false;
+ }
+ Buffer = std::move(*BufferOrError);
+ }
+ // Chomp the file to get lines
+ const char *BufferStart = Buffer->getBufferStart(),
+ *BufferEnd = Buffer->getBufferEnd();
+ std::vector<StringRef> &Lines = LineCache[LineInfo.FileName];
+ const char *Start = BufferStart;
+ for (const char *I = BufferStart; I != BufferEnd; ++I)
+ if (*I == '\n') {
+ Lines.emplace_back(Start, I - Start - (BufferStart < I && I[-1] == '\r'));
+ Start = I + 1;
+ }
+ if (Start < BufferEnd)
+ Lines.emplace_back(Start, BufferEnd - Start);
+ SourceCache[LineInfo.FileName] = std::move(Buffer);
+ return true;
+}
+
+void SourcePrinter::printSourceLine(formatted_raw_ostream &OS,
+ object::SectionedAddress Address,
+ StringRef ObjectFilename,
+ LiveVariablePrinter &LVP,
+ StringRef Delimiter) {
+ if (!Symbolizer)
+ return;
+
+ DILineInfo LineInfo = DILineInfo();
+ Expected<DILineInfo> ExpectedLineInfo =
+ Symbolizer->symbolizeCode(*Obj, Address);
+ std::string ErrorMessage;
+ if (ExpectedLineInfo) {
+ LineInfo = *ExpectedLineInfo;
+ } else if (!WarnedInvalidDebugInfo) {
+ WarnedInvalidDebugInfo = true;
+ // TODO Untested.
+ reportWarning("failed to parse debug information: " +
+ toString(ExpectedLineInfo.takeError()),
+ ObjectFilename);
+ }
+
+ if (!objdump::Prefix.empty() &&
+ sys::path::is_absolute_gnu(LineInfo.FileName)) {
+ // FileName has at least one character since is_absolute_gnu is false for
+ // an empty string.
+ assert(!LineInfo.FileName.empty());
+ if (PrefixStrip > 0) {
+ uint32_t Level = 0;
+ auto StrippedNameStart = LineInfo.FileName.begin();
+
+ // Path.h iterator skips extra separators. Therefore it cannot be used
+ // here to keep compatibility with GNU Objdump.
+ for (auto Pos = StrippedNameStart + 1, End = LineInfo.FileName.end();
+ Pos != End && Level < PrefixStrip; ++Pos) {
+ if (sys::path::is_separator(*Pos)) {
+ StrippedNameStart = Pos;
+ ++Level;
+ }
+ }
+
+ LineInfo.FileName =
+ std::string(StrippedNameStart, LineInfo.FileName.end());
+ }
+
+ SmallString<128> FilePath;
+ sys::path::append(FilePath, Prefix, LineInfo.FileName);
+
+ LineInfo.FileName = std::string(FilePath);
+ }
+
+ if (PrintLines)
+ printLines(OS, LineInfo, Delimiter, LVP);
+ if (PrintSource)
+ printSources(OS, LineInfo, ObjectFilename, Delimiter, LVP);
+ OldLineInfo = LineInfo;
+}
+
+void SourcePrinter::printLines(formatted_raw_ostream &OS,
+ const DILineInfo &LineInfo, StringRef Delimiter,
+ LiveVariablePrinter &LVP) {
+ bool PrintFunctionName = LineInfo.FunctionName != DILineInfo::BadString &&
+ LineInfo.FunctionName != OldLineInfo.FunctionName;
+ if (PrintFunctionName) {
+ OS << Delimiter << LineInfo.FunctionName;
+ // If demangling is successful, FunctionName will end with "()". Print it
+ // only if demangling did not run or was unsuccessful.
+ if (!StringRef(LineInfo.FunctionName).endswith("()"))
+ OS << "()";
+ OS << ":\n";
+ }
+ if (LineInfo.FileName != DILineInfo::BadString && LineInfo.Line != 0 &&
+ (OldLineInfo.Line != LineInfo.Line ||
+ OldLineInfo.FileName != LineInfo.FileName || PrintFunctionName)) {
+ OS << Delimiter << LineInfo.FileName << ":" << LineInfo.Line;
+ LVP.printBetweenInsts(OS, true);
+ }
+}
+
+void SourcePrinter::printSources(formatted_raw_ostream &OS,
+ const DILineInfo &LineInfo,
+ StringRef ObjectFilename, StringRef Delimiter,
+ LiveVariablePrinter &LVP) {
+ if (LineInfo.FileName == DILineInfo::BadString || LineInfo.Line == 0 ||
+ (OldLineInfo.Line == LineInfo.Line &&
+ OldLineInfo.FileName == LineInfo.FileName))
+ return;
+
+ if (SourceCache.find(LineInfo.FileName) == SourceCache.end())
+ if (!cacheSource(LineInfo))
+ return;
+ auto LineBuffer = LineCache.find(LineInfo.FileName);
+ if (LineBuffer != LineCache.end()) {
+ if (LineInfo.Line > LineBuffer->second.size()) {
+ reportWarning(
+ formatv(
+ "debug info line number {0} exceeds the number of lines in {1}",
+ LineInfo.Line, LineInfo.FileName),
+ ObjectFilename);
+ return;
+ }
+ // Vector begins at 0, line numbers are non-zero
+ OS << Delimiter << LineBuffer->second[LineInfo.Line - 1];
+ LVP.printBetweenInsts(OS, true);
+ }
+}
+
+SourcePrinter::SourcePrinter(const object::ObjectFile *Obj,
+ StringRef DefaultArch)
+ : Obj(Obj) {
+ symbolize::LLVMSymbolizer::Options SymbolizerOpts;
+ SymbolizerOpts.PrintFunctions =
+ DILineInfoSpecifier::FunctionNameKind::LinkageName;
+ SymbolizerOpts.Demangle = Demangle;
+ SymbolizerOpts.DefaultArch = std::string(DefaultArch);
+ Symbolizer.reset(new symbolize::LLVMSymbolizer(SymbolizerOpts));
+}
+
+} // namespace objdump
+} // namespace llvm
diff --git a/llvm/tools/llvm-objdump/SourcePrinter.h b/llvm/tools/llvm-objdump/SourcePrinter.h
new file mode 100644
index 000000000000..21d5bdcf8a49
--- /dev/null
+++ b/llvm/tools/llvm-objdump/SourcePrinter.h
@@ -0,0 +1,166 @@
+//===-- SourcePrinter.h - source interleaving utilities --------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_OBJDUMP_SOURCEPRINTER_H
+#define LLVM_TOOLS_LLVM_OBJDUMP_SOURCEPRINTER_H
+
+#include "llvm/ADT/IndexedMap.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/DebugInfo/DWARF/DWARFContext.h"
+#include "llvm/DebugInfo/Symbolize/Symbolize.h"
+#include "llvm/Support/FormattedStream.h"
+#include <unordered_map>
+#include <vector>
+
+namespace llvm {
+namespace objdump {
+
+/// Stores a single expression representing the location of a source-level
+/// variable, along with the PC range for which that expression is valid.
+struct LiveVariable {
+ DWARFLocationExpression LocExpr;
+ const char *VarName;
+ DWARFUnit *Unit;
+ const DWARFDie FuncDie;
+
+ LiveVariable(const DWARFLocationExpression &LocExpr, const char *VarName,
+ DWARFUnit *Unit, const DWARFDie FuncDie)
+ : LocExpr(LocExpr), VarName(VarName), Unit(Unit), FuncDie(FuncDie) {}
+
+ bool liveAtAddress(object::SectionedAddress Addr);
+
+ void print(raw_ostream &OS, const MCRegisterInfo &MRI) const;
+};
+
+/// Helper class for printing source variable locations alongside disassembly.
+class LiveVariablePrinter {
+ // Information we want to track about one column in which we are printing a
+ // variable live range.
+ struct Column {
+ unsigned VarIdx = NullVarIdx;
+ bool LiveIn = false;
+ bool LiveOut = false;
+ bool MustDrawLabel = false;
+
+ bool isActive() const { return VarIdx != NullVarIdx; }
+
+ static constexpr unsigned NullVarIdx = std::numeric_limits<unsigned>::max();
+ };
+
+ // All live variables we know about in the object/image file.
+ std::vector<LiveVariable> LiveVariables;
+
+ // The columns we are currently drawing.
+ IndexedMap<Column> ActiveCols;
+
+ const MCRegisterInfo &MRI;
+ const MCSubtargetInfo &STI;
+
+ void addVariable(DWARFDie FuncDie, DWARFDie VarDie);
+
+ void addFunction(DWARFDie D);
+
+ // Get the column number (in characters) at which the first live variable
+ // line should be printed.
+ unsigned getIndentLevel() const;
+
+ // Indent to the first live-range column to the right of the currently
+ // printed line, and return the index of that column.
+ // TODO: formatted_raw_ostream uses "column" to mean a number of characters
+ // since the last \n, and we use it to mean the number of slots in which we
+ // put live variable lines. Pick a less overloaded word.
+ unsigned moveToFirstVarColumn(formatted_raw_ostream &OS);
+
+ unsigned findFreeColumn();
+
+public:
+ LiveVariablePrinter(const MCRegisterInfo &MRI, const MCSubtargetInfo &STI)
+ : LiveVariables(), ActiveCols(Column()), MRI(MRI), STI(STI) {}
+
+ void dump() const;
+
+ void addCompileUnit(DWARFDie D);
+
+ /// Update to match the state of the instruction between ThisAddr and
+ /// NextAddr. In the common case, any live range active at ThisAddr is
+ /// live-in to the instruction, and any live range active at NextAddr is
+ /// live-out of the instruction. If IncludeDefinedVars is false, then live
+ /// ranges starting at NextAddr will be ignored.
+ void update(object::SectionedAddress ThisAddr,
+ object::SectionedAddress NextAddr, bool IncludeDefinedVars);
+
+ enum class LineChar {
+ RangeStart,
+ RangeMid,
+ RangeEnd,
+ LabelVert,
+ LabelCornerNew,
+ LabelCornerActive,
+ LabelHoriz,
+ };
+ const char *getLineChar(LineChar C) const;
+
+ /// Print live ranges to the right of an existing line. This assumes the
+ /// line is not an instruction, so doesn't start or end any live ranges, so
+ /// we only need to print active ranges or empty columns. If AfterInst is
+ /// true, this is being printed after the last instruction fed to update(),
+ /// otherwise this is being printed before it.
+ void printAfterOtherLine(formatted_raw_ostream &OS, bool AfterInst);
+
+ /// Print any live variable range info needed to the right of a
+ /// non-instruction line of disassembly. This is where we print the variable
+ /// names and expressions, with thin line-drawing characters connecting them
+ /// to the live range which starts at the next instruction. If MustPrint is
+ /// true, we have to print at least one line (with the continuation of any
+ /// already-active live ranges) because something has already been printed
+ /// earlier on this line.
+ void printBetweenInsts(formatted_raw_ostream &OS, bool MustPrint);
+
+ /// Print the live variable ranges to the right of a disassembled instruction.
+ void printAfterInst(formatted_raw_ostream &OS);
+};
+
+class SourcePrinter {
+protected:
+ DILineInfo OldLineInfo;
+ const object::ObjectFile *Obj = nullptr;
+ std::unique_ptr<symbolize::LLVMSymbolizer> Symbolizer;
+ // File name to file contents of source.
+ std::unordered_map<std::string, std::unique_ptr<MemoryBuffer>> SourceCache;
+ // Mark the line endings of the cached source.
+ std::unordered_map<std::string, std::vector<StringRef>> LineCache;
+ // Keep track of missing sources.
+ StringSet<> MissingSources;
+ // Only emit 'invalid debug info' warning once.
+ bool WarnedInvalidDebugInfo = false;
+
+private:
+ bool cacheSource(const DILineInfo &LineInfoFile);
+
+ void printLines(formatted_raw_ostream &OS, const DILineInfo &LineInfo,
+ StringRef Delimiter, LiveVariablePrinter &LVP);
+
+ void printSources(formatted_raw_ostream &OS, const DILineInfo &LineInfo,
+ StringRef ObjectFilename, StringRef Delimiter,
+ LiveVariablePrinter &LVP);
+
+public:
+ SourcePrinter() = default;
+ SourcePrinter(const object::ObjectFile *Obj, StringRef DefaultArch);
+ virtual ~SourcePrinter() = default;
+ virtual void printSourceLine(formatted_raw_ostream &OS,
+ object::SectionedAddress Address,
+ StringRef ObjectFilename,
+ LiveVariablePrinter &LVP,
+ StringRef Delimiter = "; ");
+};
+
+} // namespace objdump
+} // namespace llvm
+
+#endif
diff --git a/llvm/tools/llvm-objdump/XCOFFDump.cpp b/llvm/tools/llvm-objdump/XCOFFDump.cpp
index df37abbd3881..c4cc5fe7e21c 100644
--- a/llvm/tools/llvm-objdump/XCOFFDump.cpp
+++ b/llvm/tools/llvm-objdump/XCOFFDump.cpp
@@ -46,22 +46,30 @@ Error objdump::getXCOFFRelocationValueString(const XCOFFObjectFile *Obj,
Optional<XCOFF::StorageMappingClass>
objdump::getXCOFFSymbolCsectSMC(const XCOFFObjectFile *Obj,
const SymbolRef &Sym) {
- XCOFFSymbolRef SymRef(Sym.getRawDataRefImpl(), Obj);
+ const XCOFFSymbolRef SymRef = Obj->toSymbolRef(Sym.getRawDataRefImpl());
- if (SymRef.hasCsectAuxEnt())
- return SymRef.getXCOFFCsectAuxEnt32()->StorageMappingClass;
+ if (!SymRef.isCsectSymbol())
+ return None;
- return None;
+ auto CsectAuxEntOrErr = SymRef.getXCOFFCsectAuxRef();
+ if (!CsectAuxEntOrErr)
+ return None;
+
+ return CsectAuxEntOrErr.get().getStorageMappingClass();
}
bool objdump::isLabel(const XCOFFObjectFile *Obj, const SymbolRef &Sym) {
- XCOFFSymbolRef SymRef(Sym.getRawDataRefImpl(), Obj);
+ const XCOFFSymbolRef SymRef = Obj->toSymbolRef(Sym.getRawDataRefImpl());
+
+ if (!SymRef.isCsectSymbol())
+ return false;
- if (SymRef.hasCsectAuxEnt())
- return SymRef.getXCOFFCsectAuxEnt32()->isLabel();
+ auto CsectAuxEntOrErr = SymRef.getXCOFFCsectAuxRef();
+ if (!CsectAuxEntOrErr)
+ return false;
- return false;
+ return CsectAuxEntOrErr.get().isLabel();
}
std::string objdump::getXCOFFSymbolDescription(const SymbolInfoTy &SymbolInfo,
diff --git a/llvm/tools/llvm-objdump/llvm-objdump.cpp b/llvm/tools/llvm-objdump/llvm-objdump.cpp
index 3134f989603a..48ae92f734c7 100644
--- a/llvm/tools/llvm-objdump/llvm-objdump.cpp
+++ b/llvm/tools/llvm-objdump/llvm-objdump.cpp
@@ -19,18 +19,19 @@
#include "COFFDump.h"
#include "ELFDump.h"
#include "MachODump.h"
+#include "ObjdumpOptID.h"
+#include "SourcePrinter.h"
#include "WasmDump.h"
#include "XCOFFDump.h"
#include "llvm/ADT/IndexedMap.h"
#include "llvm/ADT/Optional.h"
-#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SetOperations.h"
+#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/ADT/Triple.h"
#include "llvm/ADT/Twine.h"
-#include "llvm/CodeGen/FaultMaps.h"
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/DebugInfo/Symbolize/Symbolize.h"
#include "llvm/Demangle/Demangle.h"
@@ -50,12 +51,15 @@
#include "llvm/Object/COFF.h"
#include "llvm/Object/COFFImportFile.h"
#include "llvm/Object/ELFObjectFile.h"
+#include "llvm/Object/FaultMapParser.h"
#include "llvm/Object/MachO.h"
#include "llvm/Object/MachOUniversal.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Object/Wasm.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/Option.h"
#include "llvm/Support/Casting.h"
-#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/FileSystem.h"
@@ -81,307 +85,143 @@
using namespace llvm;
using namespace llvm::object;
using namespace llvm::objdump;
+using namespace llvm::opt;
-#define DEBUG_TYPE "objdump"
+namespace {
+
+class CommonOptTable : public opt::OptTable {
+public:
+ CommonOptTable(ArrayRef<Info> OptionInfos, const char *Usage,
+ const char *Description)
+ : OptTable(OptionInfos), Usage(Usage), Description(Description) {
+ setGroupedShortOptions(true);
+ }
+
+ void printHelp(StringRef Argv0, bool ShowHidden = false) const {
+ Argv0 = sys::path::filename(Argv0);
+ opt::OptTable::printHelp(outs(), (Argv0 + Usage).str().c_str(), Description,
+ ShowHidden, ShowHidden);
+ // TODO Replace this with OptTable API once it adds extrahelp support.
+ outs() << "\nPass @FILE as argument to read options from FILE.\n";
+ }
+
+private:
+ const char *Usage;
+ const char *Description;
+};
+
+// ObjdumpOptID is in ObjdumpOptID.h
+
+#define PREFIX(NAME, VALUE) const char *const OBJDUMP_##NAME[] = VALUE;
+#include "ObjdumpOpts.inc"
+#undef PREFIX
+
+static constexpr opt::OptTable::Info ObjdumpInfoTable[] = {
+#define OBJDUMP_nullptr nullptr
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ {OBJDUMP_##PREFIX, NAME, HELPTEXT, \
+ METAVAR, OBJDUMP_##ID, opt::Option::KIND##Class, \
+ PARAM, FLAGS, OBJDUMP_##GROUP, \
+ OBJDUMP_##ALIAS, ALIASARGS, VALUES},
+#include "ObjdumpOpts.inc"
+#undef OPTION
+#undef OBJDUMP_nullptr
+};
-static cl::OptionCategory ObjdumpCat("llvm-objdump Options");
-
-static cl::opt<uint64_t> AdjustVMA(
- "adjust-vma",
- cl::desc("Increase the displayed address by the specified offset"),
- cl::value_desc("offset"), cl::init(0), cl::cat(ObjdumpCat));
-
-static cl::opt<bool>
- AllHeaders("all-headers",
- cl::desc("Display all available header information"),
- cl::cat(ObjdumpCat));
-static cl::alias AllHeadersShort("x", cl::desc("Alias for --all-headers"),
- cl::NotHidden, cl::Grouping,
- cl::aliasopt(AllHeaders));
-
-static cl::opt<std::string>
- ArchName("arch-name",
- cl::desc("Target arch to disassemble for, "
- "see --version for available targets"),
- cl::cat(ObjdumpCat));
-
-cl::opt<bool>
- objdump::ArchiveHeaders("archive-headers",
- cl::desc("Display archive header information"),
- cl::cat(ObjdumpCat));
-static cl::alias ArchiveHeadersShort("a",
- cl::desc("Alias for --archive-headers"),
- cl::NotHidden, cl::Grouping,
- cl::aliasopt(ArchiveHeaders));
-
-cl::opt<bool> objdump::Demangle("demangle", cl::desc("Demangle symbols names"),
- cl::init(false), cl::cat(ObjdumpCat));
-static cl::alias DemangleShort("C", cl::desc("Alias for --demangle"),
- cl::NotHidden, cl::Grouping,
- cl::aliasopt(Demangle));
-
-cl::opt<bool> objdump::Disassemble(
- "disassemble",
- cl::desc("Display assembler mnemonics for the machine instructions"),
- cl::cat(ObjdumpCat));
-static cl::alias DisassembleShort("d", cl::desc("Alias for --disassemble"),
- cl::NotHidden, cl::Grouping,
- cl::aliasopt(Disassemble));
-
-cl::opt<bool> objdump::DisassembleAll(
- "disassemble-all",
- cl::desc("Display assembler mnemonics for the machine instructions"),
- cl::cat(ObjdumpCat));
-static cl::alias DisassembleAllShort("D",
- cl::desc("Alias for --disassemble-all"),
- cl::NotHidden, cl::Grouping,
- cl::aliasopt(DisassembleAll));
-
-cl::opt<bool> objdump::SymbolDescription(
- "symbol-description",
- cl::desc("Add symbol description for disassembly. This "
- "option is for XCOFF files only"),
- cl::init(false), cl::cat(ObjdumpCat));
-
-static cl::list<std::string>
- DisassembleSymbols("disassemble-symbols", cl::CommaSeparated,
- cl::desc("List of symbols to disassemble. "
- "Accept demangled names when --demangle is "
- "specified, otherwise accept mangled names"),
- cl::cat(ObjdumpCat));
-
-static cl::opt<bool> DisassembleZeroes(
- "disassemble-zeroes",
- cl::desc("Do not skip blocks of zeroes when disassembling"),
- cl::cat(ObjdumpCat));
-static cl::alias
- DisassembleZeroesShort("z", cl::desc("Alias for --disassemble-zeroes"),
- cl::NotHidden, cl::Grouping,
- cl::aliasopt(DisassembleZeroes));
-
-static cl::list<std::string>
- DisassemblerOptions("disassembler-options",
- cl::desc("Pass target specific disassembler options"),
- cl::value_desc("options"), cl::CommaSeparated,
- cl::cat(ObjdumpCat));
-static cl::alias
- DisassemblerOptionsShort("M", cl::desc("Alias for --disassembler-options"),
- cl::NotHidden, cl::Grouping, cl::Prefix,
- cl::CommaSeparated,
- cl::aliasopt(DisassemblerOptions));
-
-cl::opt<DIDumpType> objdump::DwarfDumpType(
- "dwarf", cl::init(DIDT_Null), cl::desc("Dump of dwarf debug sections:"),
- cl::values(clEnumValN(DIDT_DebugFrame, "frames", ".debug_frame")),
- cl::cat(ObjdumpCat));
-
-static cl::opt<bool> DynamicRelocations(
- "dynamic-reloc",
- cl::desc("Display the dynamic relocation entries in the file"),
- cl::cat(ObjdumpCat));
-static cl::alias DynamicRelocationShort("R",
- cl::desc("Alias for --dynamic-reloc"),
- cl::NotHidden, cl::Grouping,
- cl::aliasopt(DynamicRelocations));
-
-static cl::opt<bool>
- FaultMapSection("fault-map-section",
- cl::desc("Display contents of faultmap section"),
- cl::cat(ObjdumpCat));
-
-static cl::opt<bool>
- FileHeaders("file-headers",
- cl::desc("Display the contents of the overall file header"),
- cl::cat(ObjdumpCat));
-static cl::alias FileHeadersShort("f", cl::desc("Alias for --file-headers"),
- cl::NotHidden, cl::Grouping,
- cl::aliasopt(FileHeaders));
-
-cl::opt<bool>
- objdump::SectionContents("full-contents",
- cl::desc("Display the content of each section"),
- cl::cat(ObjdumpCat));
-static cl::alias SectionContentsShort("s",
- cl::desc("Alias for --full-contents"),
- cl::NotHidden, cl::Grouping,
- cl::aliasopt(SectionContents));
-
-static cl::list<std::string> InputFilenames(cl::Positional,
- cl::desc("<input object files>"),
- cl::ZeroOrMore,
- cl::cat(ObjdumpCat));
-
-static cl::opt<bool>
- PrintLines("line-numbers",
- cl::desc("Display source line numbers with "
- "disassembly. Implies disassemble object"),
- cl::cat(ObjdumpCat));
-static cl::alias PrintLinesShort("l", cl::desc("Alias for --line-numbers"),
- cl::NotHidden, cl::Grouping,
- cl::aliasopt(PrintLines));
-
-static cl::opt<bool> MachOOpt("macho",
- cl::desc("Use MachO specific object file parser"),
- cl::cat(ObjdumpCat));
-static cl::alias MachOm("m", cl::desc("Alias for --macho"), cl::NotHidden,
- cl::Grouping, cl::aliasopt(MachOOpt));
-
-cl::opt<std::string> objdump::MCPU(
- "mcpu", cl::desc("Target a specific cpu type (--mcpu=help for details)"),
- cl::value_desc("cpu-name"), cl::init(""), cl::cat(ObjdumpCat));
-
-cl::list<std::string> objdump::MAttrs(
- "mattr", cl::CommaSeparated,
- cl::desc("Target specific attributes (--mattr=help for details)"),
- cl::value_desc("a1,+a2,-a3,..."), cl::cat(ObjdumpCat));
-
-cl::opt<bool> objdump::NoShowRawInsn(
- "no-show-raw-insn",
- cl::desc(
- "When disassembling instructions, do not print the instruction bytes."),
- cl::cat(ObjdumpCat));
-
-cl::opt<bool> objdump::NoLeadingAddr("no-leading-addr",
- cl::desc("Print no leading address"),
- cl::cat(ObjdumpCat));
-
-static cl::opt<bool> RawClangAST(
- "raw-clang-ast",
- cl::desc("Dump the raw binary contents of the clang AST section"),
- cl::cat(ObjdumpCat));
-
-cl::opt<bool>
- objdump::Relocations("reloc",
- cl::desc("Display the relocation entries in the file"),
- cl::cat(ObjdumpCat));
-static cl::alias RelocationsShort("r", cl::desc("Alias for --reloc"),
- cl::NotHidden, cl::Grouping,
- cl::aliasopt(Relocations));
-
-cl::opt<bool>
- objdump::PrintImmHex("print-imm-hex",
- cl::desc("Use hex format for immediate values"),
- cl::cat(ObjdumpCat));
-
-cl::opt<bool>
- objdump::PrivateHeaders("private-headers",
- cl::desc("Display format specific file headers"),
- cl::cat(ObjdumpCat));
-static cl::alias PrivateHeadersShort("p",
- cl::desc("Alias for --private-headers"),
- cl::NotHidden, cl::Grouping,
- cl::aliasopt(PrivateHeaders));
-
-cl::list<std::string>
- objdump::FilterSections("section",
- cl::desc("Operate on the specified sections only. "
- "With --macho dump segment,section"),
- cl::cat(ObjdumpCat));
-static cl::alias FilterSectionsj("j", cl::desc("Alias for --section"),
- cl::NotHidden, cl::Grouping, cl::Prefix,
- cl::aliasopt(FilterSections));
-
-cl::opt<bool> objdump::SectionHeaders(
- "section-headers",
- cl::desc("Display summaries of the headers for each section."),
- cl::cat(ObjdumpCat));
-static cl::alias SectionHeadersShort("headers",
- cl::desc("Alias for --section-headers"),
- cl::NotHidden,
- cl::aliasopt(SectionHeaders));
-static cl::alias SectionHeadersShorter("h",
- cl::desc("Alias for --section-headers"),
- cl::NotHidden, cl::Grouping,
- cl::aliasopt(SectionHeaders));
-
-static cl::opt<bool>
- ShowLMA("show-lma",
- cl::desc("Display LMA column when dumping ELF section headers"),
- cl::cat(ObjdumpCat));
-
-static cl::opt<bool> PrintSource(
- "source",
- cl::desc(
- "Display source inlined with disassembly. Implies disassemble object"),
- cl::cat(ObjdumpCat));
-static cl::alias PrintSourceShort("S", cl::desc("Alias for --source"),
- cl::NotHidden, cl::Grouping,
- cl::aliasopt(PrintSource));
-
-static cl::opt<uint64_t>
- StartAddress("start-address", cl::desc("Disassemble beginning at address"),
- cl::value_desc("address"), cl::init(0), cl::cat(ObjdumpCat));
-static cl::opt<uint64_t> StopAddress("stop-address",
- cl::desc("Stop disassembly at address"),
- cl::value_desc("address"),
- cl::init(UINT64_MAX), cl::cat(ObjdumpCat));
-
-cl::opt<bool> objdump::SymbolTable("syms", cl::desc("Display the symbol table"),
- cl::cat(ObjdumpCat));
-static cl::alias SymbolTableShort("t", cl::desc("Alias for --syms"),
- cl::NotHidden, cl::Grouping,
- cl::aliasopt(SymbolTable));
-
-static cl::opt<bool> SymbolizeOperands(
- "symbolize-operands",
- cl::desc("Symbolize instruction operands when disassembling"),
- cl::cat(ObjdumpCat));
-
-static cl::opt<bool> DynamicSymbolTable(
- "dynamic-syms",
- cl::desc("Display the contents of the dynamic symbol table"),
- cl::cat(ObjdumpCat));
-static cl::alias DynamicSymbolTableShort("T",
- cl::desc("Alias for --dynamic-syms"),
- cl::NotHidden, cl::Grouping,
- cl::aliasopt(DynamicSymbolTable));
-
-cl::opt<std::string>
- objdump::TripleName("triple",
- cl::desc("Target triple to disassemble for, see "
- "--version for available targets"),
- cl::cat(ObjdumpCat));
-
-cl::opt<bool> objdump::UnwindInfo("unwind-info",
- cl::desc("Display unwind information"),
- cl::cat(ObjdumpCat));
-static cl::alias UnwindInfoShort("u", cl::desc("Alias for --unwind-info"),
- cl::NotHidden, cl::Grouping,
- cl::aliasopt(UnwindInfo));
-
-static cl::opt<bool>
- Wide("wide", cl::desc("Ignored for compatibility with GNU objdump"),
- cl::cat(ObjdumpCat));
-static cl::alias WideShort("w", cl::Grouping, cl::aliasopt(Wide));
-
-cl::opt<std::string> objdump::Prefix("prefix",
- cl::desc("Add prefix to absolute paths"),
- cl::cat(ObjdumpCat));
-
-enum DebugVarsFormat {
- DVDisabled,
- DVUnicode,
- DVASCII,
+class ObjdumpOptTable : public CommonOptTable {
+public:
+ ObjdumpOptTable()
+ : CommonOptTable(ObjdumpInfoTable, " [options] <input object files>",
+ "llvm object file dumper") {}
+};
+
+enum OtoolOptID {
+ OTOOL_INVALID = 0, // This is not an option ID.
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ OTOOL_##ID,
+#include "OtoolOpts.inc"
+#undef OPTION
+};
+
+#define PREFIX(NAME, VALUE) const char *const OTOOL_##NAME[] = VALUE;
+#include "OtoolOpts.inc"
+#undef PREFIX
+
+static constexpr opt::OptTable::Info OtoolInfoTable[] = {
+#define OTOOL_nullptr nullptr
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ {OTOOL_##PREFIX, NAME, HELPTEXT, \
+ METAVAR, OTOOL_##ID, opt::Option::KIND##Class, \
+ PARAM, FLAGS, OTOOL_##GROUP, \
+ OTOOL_##ALIAS, ALIASARGS, VALUES},
+#include "OtoolOpts.inc"
+#undef OPTION
+#undef OTOOL_nullptr
+};
+
+class OtoolOptTable : public CommonOptTable {
+public:
+ OtoolOptTable()
+ : CommonOptTable(OtoolInfoTable, " [option...] [file...]",
+ "Mach-O object file displaying tool") {}
};
-static cl::opt<DebugVarsFormat> DbgVariables(
- "debug-vars", cl::init(DVDisabled),
- cl::desc("Print the locations (in registers or memory) of "
- "source-level variables alongside disassembly"),
- cl::ValueOptional,
- cl::values(clEnumValN(DVUnicode, "", "unicode"),
- clEnumValN(DVUnicode, "unicode", "unicode"),
- clEnumValN(DVASCII, "ascii", "unicode")),
- cl::cat(ObjdumpCat));
-
-static cl::opt<int>
- DbgIndent("debug-vars-indent", cl::init(40),
- cl::desc("Distance to indent the source-level variable display, "
- "relative to the start of the disassembly"),
- cl::cat(ObjdumpCat));
-
-static cl::extrahelp
- HelpResponse("\nPass @FILE as argument to read options from FILE.\n");
+} // namespace
+
+#define DEBUG_TYPE "objdump"
+
+static uint64_t AdjustVMA;
+static bool AllHeaders;
+static std::string ArchName;
+bool objdump::ArchiveHeaders;
+bool objdump::Demangle;
+bool objdump::Disassemble;
+bool objdump::DisassembleAll;
+bool objdump::SymbolDescription;
+static std::vector<std::string> DisassembleSymbols;
+static bool DisassembleZeroes;
+static std::vector<std::string> DisassemblerOptions;
+DIDumpType objdump::DwarfDumpType;
+static bool DynamicRelocations;
+static bool FaultMapSection;
+static bool FileHeaders;
+bool objdump::SectionContents;
+static std::vector<std::string> InputFilenames;
+bool objdump::PrintLines;
+static bool MachOOpt;
+std::string objdump::MCPU;
+std::vector<std::string> objdump::MAttrs;
+bool objdump::ShowRawInsn;
+bool objdump::LeadingAddr;
+static bool RawClangAST;
+bool objdump::Relocations;
+bool objdump::PrintImmHex;
+bool objdump::PrivateHeaders;
+std::vector<std::string> objdump::FilterSections;
+bool objdump::SectionHeaders;
+static bool ShowLMA;
+bool objdump::PrintSource;
+
+static uint64_t StartAddress;
+static bool HasStartAddressFlag;
+static uint64_t StopAddress = UINT64_MAX;
+static bool HasStopAddressFlag;
+
+bool objdump::SymbolTable;
+static bool SymbolizeOperands;
+static bool DynamicSymbolTable;
+std::string objdump::TripleName;
+bool objdump::UnwindInfo;
+static bool Wide;
+std::string objdump::Prefix;
+uint32_t objdump::PrefixStrip;
+
+DebugVarsFormat objdump::DbgVariables = DVDisabled;
+
+int objdump::DbgIndent = 52;
static StringSet<> DisasmSymbolSet;
StringSet<> objdump::FoundSectionSet;
@@ -588,515 +428,7 @@ namespace {
/// Get the column at which we want to start printing the instruction
/// disassembly, taking into account anything which appears to the left of it.
unsigned getInstStartColumn(const MCSubtargetInfo &STI) {
- return NoShowRawInsn ? 16 : STI.getTargetTriple().isX86() ? 40 : 24;
-}
-
-/// Stores a single expression representing the location of a source-level
-/// variable, along with the PC range for which that expression is valid.
-struct LiveVariable {
- DWARFLocationExpression LocExpr;
- const char *VarName;
- DWARFUnit *Unit;
- const DWARFDie FuncDie;
-
- LiveVariable(const DWARFLocationExpression &LocExpr, const char *VarName,
- DWARFUnit *Unit, const DWARFDie FuncDie)
- : LocExpr(LocExpr), VarName(VarName), Unit(Unit), FuncDie(FuncDie) {}
-
- bool liveAtAddress(object::SectionedAddress Addr) {
- if (LocExpr.Range == None)
- return false;
- return LocExpr.Range->SectionIndex == Addr.SectionIndex &&
- LocExpr.Range->LowPC <= Addr.Address &&
- LocExpr.Range->HighPC > Addr.Address;
- }
-
- void print(raw_ostream &OS, const MCRegisterInfo &MRI) const {
- DataExtractor Data({LocExpr.Expr.data(), LocExpr.Expr.size()},
- Unit->getContext().isLittleEndian(), 0);
- DWARFExpression Expression(Data, Unit->getAddressByteSize());
- Expression.printCompact(OS, MRI);
- }
-};
-
-/// Helper class for printing source variable locations alongside disassembly.
-class LiveVariablePrinter {
- // Information we want to track about one column in which we are printing a
- // variable live range.
- struct Column {
- unsigned VarIdx = NullVarIdx;
- bool LiveIn = false;
- bool LiveOut = false;
- bool MustDrawLabel = false;
-
- bool isActive() const { return VarIdx != NullVarIdx; }
-
- static constexpr unsigned NullVarIdx = std::numeric_limits<unsigned>::max();
- };
-
- // All live variables we know about in the object/image file.
- std::vector<LiveVariable> LiveVariables;
-
- // The columns we are currently drawing.
- IndexedMap<Column> ActiveCols;
-
- const MCRegisterInfo &MRI;
- const MCSubtargetInfo &STI;
-
- void addVariable(DWARFDie FuncDie, DWARFDie VarDie) {
- uint64_t FuncLowPC, FuncHighPC, SectionIndex;
- FuncDie.getLowAndHighPC(FuncLowPC, FuncHighPC, SectionIndex);
- const char *VarName = VarDie.getName(DINameKind::ShortName);
- DWARFUnit *U = VarDie.getDwarfUnit();
-
- Expected<DWARFLocationExpressionsVector> Locs =
- VarDie.getLocations(dwarf::DW_AT_location);
- if (!Locs) {
- // If the variable doesn't have any locations, just ignore it. We don't
- // report an error or warning here as that could be noisy on optimised
- // code.
- consumeError(Locs.takeError());
- return;
- }
-
- for (const DWARFLocationExpression &LocExpr : *Locs) {
- if (LocExpr.Range) {
- LiveVariables.emplace_back(LocExpr, VarName, U, FuncDie);
- } else {
- // If the LocExpr does not have an associated range, it is valid for
- // the whole of the function.
- // TODO: technically it is not valid for any range covered by another
- // LocExpr, does that happen in reality?
- DWARFLocationExpression WholeFuncExpr{
- DWARFAddressRange(FuncLowPC, FuncHighPC, SectionIndex),
- LocExpr.Expr};
- LiveVariables.emplace_back(WholeFuncExpr, VarName, U, FuncDie);
- }
- }
- }
-
- void addFunction(DWARFDie D) {
- for (const DWARFDie &Child : D.children()) {
- if (Child.getTag() == dwarf::DW_TAG_variable ||
- Child.getTag() == dwarf::DW_TAG_formal_parameter)
- addVariable(D, Child);
- else
- addFunction(Child);
- }
- }
-
- // Get the column number (in characters) at which the first live variable
- // line should be printed.
- unsigned getIndentLevel() const {
- return DbgIndent + getInstStartColumn(STI);
- }
-
- // Indent to the first live-range column to the right of the currently
- // printed line, and return the index of that column.
- // TODO: formatted_raw_ostream uses "column" to mean a number of characters
- // since the last \n, and we use it to mean the number of slots in which we
- // put live variable lines. Pick a less overloaded word.
- unsigned moveToFirstVarColumn(formatted_raw_ostream &OS) {
- // Logical column number: column zero is the first column we print in, each
- // logical column is 2 physical columns wide.
- unsigned FirstUnprintedLogicalColumn =
- std::max((int)(OS.getColumn() - getIndentLevel() + 1) / 2, 0);
- // Physical column number: the actual column number in characters, with
- // zero being the left-most side of the screen.
- unsigned FirstUnprintedPhysicalColumn =
- getIndentLevel() + FirstUnprintedLogicalColumn * 2;
-
- if (FirstUnprintedPhysicalColumn > OS.getColumn())
- OS.PadToColumn(FirstUnprintedPhysicalColumn);
-
- return FirstUnprintedLogicalColumn;
- }
-
- unsigned findFreeColumn() {
- for (unsigned ColIdx = 0; ColIdx < ActiveCols.size(); ++ColIdx)
- if (!ActiveCols[ColIdx].isActive())
- return ColIdx;
-
- size_t OldSize = ActiveCols.size();
- ActiveCols.grow(std::max<size_t>(OldSize * 2, 1));
- return OldSize;
- }
-
-public:
- LiveVariablePrinter(const MCRegisterInfo &MRI, const MCSubtargetInfo &STI)
- : LiveVariables(), ActiveCols(Column()), MRI(MRI), STI(STI) {}
-
- void dump() const {
- for (const LiveVariable &LV : LiveVariables) {
- dbgs() << LV.VarName << " @ " << LV.LocExpr.Range << ": ";
- LV.print(dbgs(), MRI);
- dbgs() << "\n";
- }
- }
-
- void addCompileUnit(DWARFDie D) {
- if (D.getTag() == dwarf::DW_TAG_subprogram)
- addFunction(D);
- else
- for (const DWARFDie &Child : D.children())
- addFunction(Child);
- }
-
- /// Update to match the state of the instruction between ThisAddr and
- /// NextAddr. In the common case, any live range active at ThisAddr is
- /// live-in to the instruction, and any live range active at NextAddr is
- /// live-out of the instruction. If IncludeDefinedVars is false, then live
- /// ranges starting at NextAddr will be ignored.
- void update(object::SectionedAddress ThisAddr,
- object::SectionedAddress NextAddr, bool IncludeDefinedVars) {
- // First, check variables which have already been assigned a column, so
- // that we don't change their order.
- SmallSet<unsigned, 8> CheckedVarIdxs;
- for (unsigned ColIdx = 0, End = ActiveCols.size(); ColIdx < End; ++ColIdx) {
- if (!ActiveCols[ColIdx].isActive())
- continue;
- CheckedVarIdxs.insert(ActiveCols[ColIdx].VarIdx);
- LiveVariable &LV = LiveVariables[ActiveCols[ColIdx].VarIdx];
- ActiveCols[ColIdx].LiveIn = LV.liveAtAddress(ThisAddr);
- ActiveCols[ColIdx].LiveOut = LV.liveAtAddress(NextAddr);
- LLVM_DEBUG(dbgs() << "pass 1, " << ThisAddr.Address << "-"
- << NextAddr.Address << ", " << LV.VarName << ", Col "
- << ColIdx << ": LiveIn=" << ActiveCols[ColIdx].LiveIn
- << ", LiveOut=" << ActiveCols[ColIdx].LiveOut << "\n");
-
- if (!ActiveCols[ColIdx].LiveIn && !ActiveCols[ColIdx].LiveOut)
- ActiveCols[ColIdx].VarIdx = Column::NullVarIdx;
- }
-
- // Next, look for variables which don't already have a column, but which
- // are now live.
- if (IncludeDefinedVars) {
- for (unsigned VarIdx = 0, End = LiveVariables.size(); VarIdx < End;
- ++VarIdx) {
- if (CheckedVarIdxs.count(VarIdx))
- continue;
- LiveVariable &LV = LiveVariables[VarIdx];
- bool LiveIn = LV.liveAtAddress(ThisAddr);
- bool LiveOut = LV.liveAtAddress(NextAddr);
- if (!LiveIn && !LiveOut)
- continue;
-
- unsigned ColIdx = findFreeColumn();
- LLVM_DEBUG(dbgs() << "pass 2, " << ThisAddr.Address << "-"
- << NextAddr.Address << ", " << LV.VarName << ", Col "
- << ColIdx << ": LiveIn=" << LiveIn
- << ", LiveOut=" << LiveOut << "\n");
- ActiveCols[ColIdx].VarIdx = VarIdx;
- ActiveCols[ColIdx].LiveIn = LiveIn;
- ActiveCols[ColIdx].LiveOut = LiveOut;
- ActiveCols[ColIdx].MustDrawLabel = true;
- }
- }
- }
-
- enum class LineChar {
- RangeStart,
- RangeMid,
- RangeEnd,
- LabelVert,
- LabelCornerNew,
- LabelCornerActive,
- LabelHoriz,
- };
- const char *getLineChar(LineChar C) const {
- bool IsASCII = DbgVariables == DVASCII;
- switch (C) {
- case LineChar::RangeStart:
- return IsASCII ? "^" : (const char *)u8"\u2548";
- case LineChar::RangeMid:
- return IsASCII ? "|" : (const char *)u8"\u2503";
- case LineChar::RangeEnd:
- return IsASCII ? "v" : (const char *)u8"\u253b";
- case LineChar::LabelVert:
- return IsASCII ? "|" : (const char *)u8"\u2502";
- case LineChar::LabelCornerNew:
- return IsASCII ? "/" : (const char *)u8"\u250c";
- case LineChar::LabelCornerActive:
- return IsASCII ? "|" : (const char *)u8"\u2520";
- case LineChar::LabelHoriz:
- return IsASCII ? "-" : (const char *)u8"\u2500";
- }
- llvm_unreachable("Unhandled LineChar enum");
- }
-
- /// Print live ranges to the right of an existing line. This assumes the
- /// line is not an instruction, so doesn't start or end any live ranges, so
- /// we only need to print active ranges or empty columns. If AfterInst is
- /// true, this is being printed after the last instruction fed to update(),
- /// otherwise this is being printed before it.
- void printAfterOtherLine(formatted_raw_ostream &OS, bool AfterInst) {
- if (ActiveCols.size()) {
- unsigned FirstUnprintedColumn = moveToFirstVarColumn(OS);
- for (size_t ColIdx = FirstUnprintedColumn, End = ActiveCols.size();
- ColIdx < End; ++ColIdx) {
- if (ActiveCols[ColIdx].isActive()) {
- if ((AfterInst && ActiveCols[ColIdx].LiveOut) ||
- (!AfterInst && ActiveCols[ColIdx].LiveIn))
- OS << getLineChar(LineChar::RangeMid);
- else if (!AfterInst && ActiveCols[ColIdx].LiveOut)
- OS << getLineChar(LineChar::LabelVert);
- else
- OS << " ";
- }
- OS << " ";
- }
- }
- OS << "\n";
- }
-
- /// Print any live variable range info needed to the right of a
- /// non-instruction line of disassembly. This is where we print the variable
- /// names and expressions, with thin line-drawing characters connecting them
- /// to the live range which starts at the next instruction. If MustPrint is
- /// true, we have to print at least one line (with the continuation of any
- /// already-active live ranges) because something has already been printed
- /// earlier on this line.
- void printBetweenInsts(formatted_raw_ostream &OS, bool MustPrint) {
- bool PrintedSomething = false;
- for (unsigned ColIdx = 0, End = ActiveCols.size(); ColIdx < End; ++ColIdx) {
- if (ActiveCols[ColIdx].isActive() && ActiveCols[ColIdx].MustDrawLabel) {
- // First we need to print the live range markers for any active
- // columns to the left of this one.
- OS.PadToColumn(getIndentLevel());
- for (unsigned ColIdx2 = 0; ColIdx2 < ColIdx; ++ColIdx2) {
- if (ActiveCols[ColIdx2].isActive()) {
- if (ActiveCols[ColIdx2].MustDrawLabel &&
- !ActiveCols[ColIdx2].LiveIn)
- OS << getLineChar(LineChar::LabelVert) << " ";
- else
- OS << getLineChar(LineChar::RangeMid) << " ";
- } else
- OS << " ";
- }
-
- // Then print the variable name and location of the new live range,
- // with box drawing characters joining it to the live range line.
- OS << getLineChar(ActiveCols[ColIdx].LiveIn
- ? LineChar::LabelCornerActive
- : LineChar::LabelCornerNew)
- << getLineChar(LineChar::LabelHoriz) << " ";
- WithColor(OS, raw_ostream::GREEN)
- << LiveVariables[ActiveCols[ColIdx].VarIdx].VarName;
- OS << " = ";
- {
- WithColor ExprColor(OS, raw_ostream::CYAN);
- LiveVariables[ActiveCols[ColIdx].VarIdx].print(OS, MRI);
- }
-
- // If there are any columns to the right of the expression we just
- // printed, then continue their live range lines.
- unsigned FirstUnprintedColumn = moveToFirstVarColumn(OS);
- for (unsigned ColIdx2 = FirstUnprintedColumn, End = ActiveCols.size();
- ColIdx2 < End; ++ColIdx2) {
- if (ActiveCols[ColIdx2].isActive() && ActiveCols[ColIdx2].LiveIn)
- OS << getLineChar(LineChar::RangeMid) << " ";
- else
- OS << " ";
- }
-
- OS << "\n";
- PrintedSomething = true;
- }
- }
-
- for (unsigned ColIdx = 0, End = ActiveCols.size(); ColIdx < End; ++ColIdx)
- if (ActiveCols[ColIdx].isActive())
- ActiveCols[ColIdx].MustDrawLabel = false;
-
- // If we must print something (because we printed a line/column number),
- // but don't have any new variables to print, then print a line which
- // just continues any existing live ranges.
- if (MustPrint && !PrintedSomething)
- printAfterOtherLine(OS, false);
- }
-
- /// Print the live variable ranges to the right of a disassembled instruction.
- void printAfterInst(formatted_raw_ostream &OS) {
- if (!ActiveCols.size())
- return;
- unsigned FirstUnprintedColumn = moveToFirstVarColumn(OS);
- for (unsigned ColIdx = FirstUnprintedColumn, End = ActiveCols.size();
- ColIdx < End; ++ColIdx) {
- if (!ActiveCols[ColIdx].isActive())
- OS << " ";
- else if (ActiveCols[ColIdx].LiveIn && ActiveCols[ColIdx].LiveOut)
- OS << getLineChar(LineChar::RangeMid) << " ";
- else if (ActiveCols[ColIdx].LiveOut)
- OS << getLineChar(LineChar::RangeStart) << " ";
- else if (ActiveCols[ColIdx].LiveIn)
- OS << getLineChar(LineChar::RangeEnd) << " ";
- else
- llvm_unreachable("var must be live in or out!");
- }
- }
-};
-
-class SourcePrinter {
-protected:
- DILineInfo OldLineInfo;
- const ObjectFile *Obj = nullptr;
- std::unique_ptr<symbolize::LLVMSymbolizer> Symbolizer;
- // File name to file contents of source.
- std::unordered_map<std::string, std::unique_ptr<MemoryBuffer>> SourceCache;
- // Mark the line endings of the cached source.
- std::unordered_map<std::string, std::vector<StringRef>> LineCache;
- // Keep track of missing sources.
- StringSet<> MissingSources;
- // Only emit 'no debug info' warning once.
- bool WarnedNoDebugInfo;
-
-private:
- bool cacheSource(const DILineInfo& LineInfoFile);
-
- void printLines(formatted_raw_ostream &OS, const DILineInfo &LineInfo,
- StringRef Delimiter, LiveVariablePrinter &LVP);
-
- void printSources(formatted_raw_ostream &OS, const DILineInfo &LineInfo,
- StringRef ObjectFilename, StringRef Delimiter,
- LiveVariablePrinter &LVP);
-
-public:
- SourcePrinter() = default;
- SourcePrinter(const ObjectFile *Obj, StringRef DefaultArch)
- : Obj(Obj), WarnedNoDebugInfo(false) {
- symbolize::LLVMSymbolizer::Options SymbolizerOpts;
- SymbolizerOpts.PrintFunctions =
- DILineInfoSpecifier::FunctionNameKind::LinkageName;
- SymbolizerOpts.Demangle = Demangle;
- SymbolizerOpts.DefaultArch = std::string(DefaultArch);
- Symbolizer.reset(new symbolize::LLVMSymbolizer(SymbolizerOpts));
- }
- virtual ~SourcePrinter() = default;
- virtual void printSourceLine(formatted_raw_ostream &OS,
- object::SectionedAddress Address,
- StringRef ObjectFilename,
- LiveVariablePrinter &LVP,
- StringRef Delimiter = "; ");
-};
-
-bool SourcePrinter::cacheSource(const DILineInfo &LineInfo) {
- std::unique_ptr<MemoryBuffer> Buffer;
- if (LineInfo.Source) {
- Buffer = MemoryBuffer::getMemBuffer(*LineInfo.Source);
- } else {
- auto BufferOrError = MemoryBuffer::getFile(LineInfo.FileName);
- if (!BufferOrError) {
- if (MissingSources.insert(LineInfo.FileName).second)
- reportWarning("failed to find source " + LineInfo.FileName,
- Obj->getFileName());
- return false;
- }
- Buffer = std::move(*BufferOrError);
- }
- // Chomp the file to get lines
- const char *BufferStart = Buffer->getBufferStart(),
- *BufferEnd = Buffer->getBufferEnd();
- std::vector<StringRef> &Lines = LineCache[LineInfo.FileName];
- const char *Start = BufferStart;
- for (const char *I = BufferStart; I != BufferEnd; ++I)
- if (*I == '\n') {
- Lines.emplace_back(Start, I - Start - (BufferStart < I && I[-1] == '\r'));
- Start = I + 1;
- }
- if (Start < BufferEnd)
- Lines.emplace_back(Start, BufferEnd - Start);
- SourceCache[LineInfo.FileName] = std::move(Buffer);
- return true;
-}
-
-void SourcePrinter::printSourceLine(formatted_raw_ostream &OS,
- object::SectionedAddress Address,
- StringRef ObjectFilename,
- LiveVariablePrinter &LVP,
- StringRef Delimiter) {
- if (!Symbolizer)
- return;
-
- DILineInfo LineInfo = DILineInfo();
- auto ExpectedLineInfo = Symbolizer->symbolizeCode(*Obj, Address);
- std::string ErrorMessage;
- if (!ExpectedLineInfo)
- ErrorMessage = toString(ExpectedLineInfo.takeError());
- else
- LineInfo = *ExpectedLineInfo;
-
- if (LineInfo.FileName == DILineInfo::BadString) {
- if (!WarnedNoDebugInfo) {
- std::string Warning =
- "failed to parse debug information for " + ObjectFilename.str();
- if (!ErrorMessage.empty())
- Warning += ": " + ErrorMessage;
- reportWarning(Warning, ObjectFilename);
- WarnedNoDebugInfo = true;
- }
- }
-
- if (!Prefix.empty() && sys::path::is_absolute_gnu(LineInfo.FileName)) {
- SmallString<128> FilePath;
- sys::path::append(FilePath, Prefix, LineInfo.FileName);
-
- LineInfo.FileName = std::string(FilePath);
- }
-
- if (PrintLines)
- printLines(OS, LineInfo, Delimiter, LVP);
- if (PrintSource)
- printSources(OS, LineInfo, ObjectFilename, Delimiter, LVP);
- OldLineInfo = LineInfo;
-}
-
-void SourcePrinter::printLines(formatted_raw_ostream &OS,
- const DILineInfo &LineInfo, StringRef Delimiter,
- LiveVariablePrinter &LVP) {
- bool PrintFunctionName = LineInfo.FunctionName != DILineInfo::BadString &&
- LineInfo.FunctionName != OldLineInfo.FunctionName;
- if (PrintFunctionName) {
- OS << Delimiter << LineInfo.FunctionName;
- // If demangling is successful, FunctionName will end with "()". Print it
- // only if demangling did not run or was unsuccessful.
- if (!StringRef(LineInfo.FunctionName).endswith("()"))
- OS << "()";
- OS << ":\n";
- }
- if (LineInfo.FileName != DILineInfo::BadString && LineInfo.Line != 0 &&
- (OldLineInfo.Line != LineInfo.Line ||
- OldLineInfo.FileName != LineInfo.FileName || PrintFunctionName)) {
- OS << Delimiter << LineInfo.FileName << ":" << LineInfo.Line;
- LVP.printBetweenInsts(OS, true);
- }
-}
-
-void SourcePrinter::printSources(formatted_raw_ostream &OS,
- const DILineInfo &LineInfo,
- StringRef ObjectFilename, StringRef Delimiter,
- LiveVariablePrinter &LVP) {
- if (LineInfo.FileName == DILineInfo::BadString || LineInfo.Line == 0 ||
- (OldLineInfo.Line == LineInfo.Line &&
- OldLineInfo.FileName == LineInfo.FileName))
- return;
-
- if (SourceCache.find(LineInfo.FileName) == SourceCache.end())
- if (!cacheSource(LineInfo))
- return;
- auto LineBuffer = LineCache.find(LineInfo.FileName);
- if (LineBuffer != LineCache.end()) {
- if (LineInfo.Line > LineBuffer->second.size()) {
- reportWarning(
- formatv(
- "debug info line number {0} exceeds the number of lines in {1}",
- LineInfo.Line, LineInfo.FileName),
- ObjectFilename);
- return;
- }
- // Vector begins at 0, line numbers are non-zero
- OS << Delimiter << LineBuffer->second[LineInfo.Line - 1];
- LVP.printBetweenInsts(OS, true);
- }
+ return !ShowRawInsn ? 16 : STI.getTargetTriple().isX86() ? 40 : 24;
}
static bool isAArch64Elf(const ObjectFile *Obj) {
@@ -1139,9 +471,9 @@ public:
LVP.printBetweenInsts(OS, false);
size_t Start = OS.tell();
- if (!NoLeadingAddr)
+ if (LeadingAddr)
OS << format("%8" PRIx64 ":", Address.Address);
- if (!NoShowRawInsn) {
+ if (ShowRawInsn) {
OS << ' ';
dumpBytes(Bytes, OS);
}
@@ -1172,9 +504,9 @@ public:
formatted_raw_ostream &OS) {
uint32_t opcode =
(Bytes[3] << 24) | (Bytes[2] << 16) | (Bytes[1] << 8) | Bytes[0];
- if (!NoLeadingAddr)
+ if (LeadingAddr)
OS << format("%8" PRIx64 ":", Address);
- if (!NoShowRawInsn) {
+ if (ShowRawInsn) {
OS << "\t";
dumpBytes(Bytes.slice(0, 4), OS);
OS << format("\t%08" PRIx32, opcode);
@@ -1309,9 +641,9 @@ public:
LiveVariablePrinter &LVP) override {
if (SP && (PrintSource || PrintLines))
SP->printSourceLine(OS, Address, ObjectFilename, LVP);
- if (!NoLeadingAddr)
+ if (LeadingAddr)
OS << format("%8" PRId64 ":", Address.Address / 8);
- if (!NoShowRawInsn) {
+ if (ShowRawInsn) {
OS << "\t";
dumpBytes(Bytes, OS);
}
@@ -1404,6 +736,43 @@ addDynamicElfSymbols(const ObjectFile *Obj,
llvm_unreachable("Unsupported binary format");
}
+static Optional<SectionRef> getWasmCodeSection(const WasmObjectFile *Obj) {
+ for (auto SecI : Obj->sections()) {
+ const WasmSection &Section = Obj->getWasmSection(SecI);
+ if (Section.Type == wasm::WASM_SEC_CODE)
+ return SecI;
+ }
+ return None;
+}
+
+static void
+addMissingWasmCodeSymbols(const WasmObjectFile *Obj,
+ std::map<SectionRef, SectionSymbolsTy> &AllSymbols) {
+ Optional<SectionRef> Section = getWasmCodeSection(Obj);
+ if (!Section)
+ return;
+ SectionSymbolsTy &Symbols = AllSymbols[*Section];
+
+ std::set<uint64_t> SymbolAddresses;
+ for (const auto &Sym : Symbols)
+ SymbolAddresses.insert(Sym.Addr);
+
+ for (const wasm::WasmFunction &Function : Obj->functions()) {
+ uint64_t Address = Function.CodeSectionOffset;
+ // Only add fallback symbols for functions not already present in the symbol
+ // table.
+ if (SymbolAddresses.count(Address))
+ continue;
+ // This function has no symbol, so it should have no SymbolName.
+ assert(Function.SymbolName.empty());
+ // We use DebugName for the name, though it may be empty if there is no
+ // "name" custom section, or that section is missing a name for this
+ // function.
+ StringRef Name = Function.DebugName;
+ Symbols.emplace_back(Address, Name, ELF::STT_NOTYPE);
+ }
+}
+
static void addPltEntries(const ObjectFile *Obj,
std::map<SectionRef, SectionSymbolsTy> &AllSymbols,
StringSaver &Saver) {
@@ -1479,8 +848,7 @@ getRelocsMap(object::ObjectFile const &Obj) {
if (Relocated == Obj.section_end() || !checkSectionFilter(*Relocated).Keep)
continue;
std::vector<RelocationRef> &V = Ret[*Relocated];
- for (const RelocationRef &R : Sec.relocations())
- V.push_back(R);
+ append_range(V, Sec.relocations());
// Sort relocations by address.
llvm::stable_sort(V, isRelocAddressLess);
}
@@ -1641,6 +1009,63 @@ collectLocalBranchTargets(ArrayRef<uint8_t> Bytes, const MCInstrAnalysis *MIA,
}
}
+// Create an MCSymbolizer for the target and add it to the MCDisassembler.
+// This is currently only used on AMDGPU, and assumes the format of the
+// void * argument passed to AMDGPU's createMCSymbolizer.
+static void addSymbolizer(
+ MCContext &Ctx, const Target *Target, StringRef TripleName,
+ MCDisassembler *DisAsm, uint64_t SectionAddr, ArrayRef<uint8_t> Bytes,
+ SectionSymbolsTy &Symbols,
+ std::vector<std::unique_ptr<std::string>> &SynthesizedLabelNames) {
+
+ std::unique_ptr<MCRelocationInfo> RelInfo(
+ Target->createMCRelocationInfo(TripleName, Ctx));
+ if (!RelInfo)
+ return;
+ std::unique_ptr<MCSymbolizer> Symbolizer(Target->createMCSymbolizer(
+ TripleName, nullptr, nullptr, &Symbols, &Ctx, std::move(RelInfo)));
+ MCSymbolizer *SymbolizerPtr = &*Symbolizer;
+ DisAsm->setSymbolizer(std::move(Symbolizer));
+
+ if (!SymbolizeOperands)
+ return;
+
+ // Synthesize labels referenced by branch instructions by
+ // disassembling, discarding the output, and collecting the referenced
+ // addresses from the symbolizer.
+ for (size_t Index = 0; Index != Bytes.size();) {
+ MCInst Inst;
+ uint64_t Size;
+ DisAsm->getInstruction(Inst, Size, Bytes.slice(Index), SectionAddr + Index,
+ nulls());
+ if (Size == 0)
+ Size = 1;
+ Index += Size;
+ }
+ ArrayRef<uint64_t> LabelAddrsRef = SymbolizerPtr->getReferencedAddresses();
+ // Copy and sort to remove duplicates.
+ std::vector<uint64_t> LabelAddrs;
+ LabelAddrs.insert(LabelAddrs.end(), LabelAddrsRef.begin(),
+ LabelAddrsRef.end());
+ llvm::sort(LabelAddrs);
+ LabelAddrs.resize(std::unique(LabelAddrs.begin(), LabelAddrs.end()) -
+ LabelAddrs.begin());
+ // Add the labels.
+ for (unsigned LabelNum = 0; LabelNum != LabelAddrs.size(); ++LabelNum) {
+ auto Name = std::make_unique<std::string>();
+ *Name = (Twine("L") + Twine(LabelNum)).str();
+ SynthesizedLabelNames.push_back(std::move(Name));
+ Symbols.push_back(SymbolInfoTy(
+ LabelAddrs[LabelNum], *SynthesizedLabelNames.back(), ELF::STT_NOTYPE));
+ }
+ llvm::stable_sort(Symbols);
+ // Recreate the symbolizer with the new symbols list.
+ RelInfo.reset(Target->createMCRelocationInfo(TripleName, Ctx));
+ Symbolizer.reset(Target->createMCSymbolizer(
+ TripleName, nullptr, nullptr, &Symbols, &Ctx, std::move(RelInfo)));
+ DisAsm->setSymbolizer(std::move(Symbolizer));
+}
+
static StringRef getSegmentName(const MachOObjectFile *MachO,
const SectionRef &Section) {
if (MachO) {
@@ -1651,6 +1076,29 @@ static StringRef getSegmentName(const MachOObjectFile *MachO,
return "";
}
+static void emitPostInstructionInfo(formatted_raw_ostream &FOS,
+ const MCAsmInfo &MAI,
+ const MCSubtargetInfo &STI,
+ StringRef Comments,
+ LiveVariablePrinter &LVP) {
+ do {
+ if (!Comments.empty()) {
+ // Emit a line of comments.
+ StringRef Comment;
+ std::tie(Comment, Comments) = Comments.split('\n');
+ // MAI.getCommentColumn() assumes that instructions are printed at the
+ // position of 8, while getInstStartColumn() returns the actual position.
+ unsigned CommentColumn =
+ MAI.getCommentColumn() - 8 + getInstStartColumn(STI);
+ FOS.PadToColumn(CommentColumn);
+ FOS << MAI.getCommentString() << ' ' << Comment;
+ }
+ LVP.printAfterInst(FOS);
+ FOS << '\n';
+ } while (!Comments.empty());
+ FOS.flush();
+}
+
static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj,
MCContext &Ctx, MCDisassembler *PrimaryDisAsm,
MCDisassembler *SecondaryDisAsm,
@@ -1688,10 +1136,15 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj,
if (Obj->isELF() && getElfSymbolType(Obj, Symbol) == ELF::STT_SECTION)
continue;
- // Don't ask a Mach-O STAB symbol for its section unless you know that
- // STAB symbol's section field refers to a valid section index. Otherwise
- // the symbol may error trying to load a section that does not exist.
if (MachO) {
+ // __mh_(execute|dylib|dylinker|bundle|preload|object)_header are special
+ // symbols that support MachO header introspection. They do not bind to
+ // code locations and are irrelevant for disassembly.
+ if (NameOrErr->startswith("__mh_") && NameOrErr->endswith("_header"))
+ continue;
+ // Don't ask a Mach-O STAB symbol for its section unless you know that
+ // STAB symbol's section field refers to a valid section index. Otherwise
+ // the symbol may error trying to load a section that does not exist.
DataRefImpl SymDRI = Symbol.getRawDataRefImpl();
uint8_t NType = (MachO->is64Bit() ?
MachO->getSymbol64TableEntry(SymDRI).n_type:
@@ -1710,6 +1163,9 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj,
if (AllSymbols.empty() && Obj->isELF())
addDynamicElfSymbols(Obj, AllSymbols);
+ if (Obj->isWasm())
+ addMissingWasmCodeSymbols(cast<WasmObjectFile>(Obj), AllSymbols);
+
BumpPtrAllocator A;
StringSaver Saver(A);
addPltEntries(Obj, AllSymbols, Saver);
@@ -1803,16 +1259,14 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj,
llvm::sort(MappingSymbols);
+ ArrayRef<uint8_t> Bytes = arrayRefFromStringRef(
+ unwrapOrError(Section.getContents(), Obj->getFileName()));
+
+ std::vector<std::unique_ptr<std::string>> SynthesizedLabelNames;
if (Obj->isELF() && Obj->getArch() == Triple::amdgcn) {
// AMDGPU disassembler uses symbolizer for printing labels
- std::unique_ptr<MCRelocationInfo> RelInfo(
- TheTarget->createMCRelocationInfo(TripleName, Ctx));
- if (RelInfo) {
- std::unique_ptr<MCSymbolizer> Symbolizer(
- TheTarget->createMCSymbolizer(
- TripleName, nullptr, nullptr, &Symbols, &Ctx, std::move(RelInfo)));
- DisAsm->setSymbolizer(std::move(Symbolizer));
- }
+ addSymbolizer(Ctx, TheTarget, TripleName, DisAsm, SectionAddr, Bytes,
+ Symbols, SynthesizedLabelNames);
}
StringRef SegmentName = getSegmentName(MachO, Section);
@@ -1828,9 +1282,6 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj,
SmallString<40> Comments;
raw_svector_ostream CommentStream(Comments);
- ArrayRef<uint8_t> Bytes = arrayRefFromStringRef(
- unwrapOrError(Section.getContents(), Obj->getFileName()));
-
uint64_t VMAAdjustment = 0;
if (shouldAdjustVA(Section))
VMAAdjustment = AdjustVMA;
@@ -1877,7 +1328,7 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj,
}
outs() << '\n';
- if (!NoLeadingAddr)
+ if (LeadingAddr)
outs() << format(Is64Bits ? "%016" PRIx64 " " : "%08" PRIx64 " ",
SectionAddr + Start + VMAAdjustment);
if (Obj->isXCOFF() && SymbolDescription) {
@@ -2008,18 +1459,22 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj,
LVP.update({Index, Section.getIndex()},
{Index + Size, Section.getIndex()}, Index + Size != End);
+ IP->setCommentStream(CommentStream);
+
PIP.printInst(
*IP, Disassembled ? &Inst : nullptr, Bytes.slice(Index, Size),
{SectionAddr + Index + VMAAdjustment, Section.getIndex()}, FOS,
"", *STI, &SP, Obj->getFileName(), &Rels, LVP);
- FOS << CommentStream.str();
- Comments.clear();
+
+ IP->setCommentStream(llvm::nulls());
// If disassembly has failed, avoid analysing invalid/incomplete
// instruction information. Otherwise, try to resolve the target
// address (jump target or memory operand address) and print it on the
// right of the instruction.
if (Disassembled && MIA) {
+ // Branch targets are printed just after the instructions.
+ llvm::raw_ostream *TargetOS = &FOS;
uint64_t Target;
bool PrintTarget =
MIA->evaluateBranch(Inst, SectionAddr + Index, Size, Target);
@@ -2030,8 +1485,11 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj,
Target = *MaybeTarget;
PrintTarget = true;
// Do not print real address when symbolizing.
- if (!SymbolizeOperands)
- FOS << " # " << Twine::utohexstr(Target);
+ if (!SymbolizeOperands) {
+ // Memory operand addresses are printed as comments.
+ TargetOS = &CommentStream;
+ *TargetOS << "0x" << Twine::utohexstr(Target);
+ }
}
if (PrintTarget) {
// In a relocatable object, the target's section must reside in
@@ -2090,28 +1548,34 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj,
if (Demangle)
TargetName = demangle(TargetName);
- FOS << " <";
+ *TargetOS << " <";
if (!Disp) {
// Always Print the binary symbol precisely corresponding to
// the target address.
- FOS << TargetName;
+ *TargetOS << TargetName;
} else if (!LabelAvailable) {
// Always Print the binary symbol plus an offset if there's no
// local label corresponding to the target address.
- FOS << TargetName << "+0x" << Twine::utohexstr(Disp);
+ *TargetOS << TargetName << "+0x" << Twine::utohexstr(Disp);
} else {
- FOS << AllLabels[Target];
+ *TargetOS << AllLabels[Target];
}
- FOS << ">";
+ *TargetOS << ">";
} else if (LabelAvailable) {
- FOS << " <" << AllLabels[Target] << ">";
+ *TargetOS << " <" << AllLabels[Target] << ">";
}
+ // By convention, each record in the comment stream should be
+ // terminated.
+ if (TargetOS == &CommentStream)
+ *TargetOS << "\n";
}
}
}
- LVP.printAfterInst(FOS);
- FOS << "\n";
+ assert(Ctx.getAsmInfo());
+ emitPostInstructionInfo(FOS, *Ctx.getAsmInfo(), *STI,
+ CommentStream.str(), LVP);
+ Comments.clear();
// Hexagon does this in pretty printer
if (Obj->getArch() != Triple::hexagon) {
@@ -2193,10 +1657,11 @@ static void disassembleObject(const ObjectFile *Obj, bool InlineRelocs) {
if (!MII)
reportError(Obj->getFileName(),
"no instruction info for target " + TripleName);
- MCObjectFileInfo MOFI;
- MCContext Ctx(AsmInfo.get(), MRI.get(), &MOFI);
+ MCContext Ctx(Triple(TripleName), AsmInfo.get(), MRI.get(), STI.get());
// FIXME: for now initialize MCObjectFileInfo with default values
- MOFI.InitMCObjectFileInfo(Triple(TripleName), false, Ctx);
+ std::unique_ptr<MCObjectFileInfo> MOFI(
+ TheTarget->createMCObjectFileInfo(Ctx, /*PIC=*/false));
+ Ctx.setObjectFileInfo(MOFI.get());
std::unique_ptr<MCDisassembler> DisAsm(
TheTarget->createMCDisassembler(*STI, Ctx));
@@ -2273,7 +1738,7 @@ void objdump::printRelocations(const ObjectFile *Obj) {
for (std::pair<SectionRef, std::vector<SectionRef>> &P : SecToRelSec) {
StringRef SecName = unwrapOrError(P.first.getName(), Obj->getFileName());
- outs() << "RELOCATION RECORDS FOR [" << SecName << "]:\n";
+ outs() << "\nRELOCATION RECORDS FOR [" << SecName << "]:\n";
uint32_t OffsetPadding = (Obj->getBytesInAddress() > 4 ? 16 : 8);
uint32_t TypePadding = 24;
outs() << left_justify("OFFSET", OffsetPadding) << " "
@@ -2296,7 +1761,6 @@ void objdump::printRelocations(const ObjectFile *Obj) {
<< "\n";
}
}
- outs() << "\n";
}
}
@@ -2356,16 +1820,13 @@ void objdump::printSectionHeaders(const ObjectFile *Obj) {
size_t NameWidth = getMaxSectionNameWidth(Obj);
size_t AddressWidth = 2 * Obj->getBytesInAddress();
bool HasLMAColumn = shouldDisplayLMA(Obj);
+ outs() << "\nSections:\n";
if (HasLMAColumn)
- outs() << "Sections:\n"
- "Idx "
- << left_justify("Name", NameWidth) << " Size "
+ outs() << "Idx " << left_justify("Name", NameWidth) << " Size "
<< left_justify("VMA", AddressWidth) << " "
<< left_justify("LMA", AddressWidth) << " Type\n";
else
- outs() << "Sections:\n"
- "Idx "
- << left_justify("Name", NameWidth) << " Size "
+ outs() << "Idx " << left_justify("Name", NameWidth) << " Size "
<< left_justify("VMA", AddressWidth) << " Type\n";
uint64_t Idx;
@@ -2379,9 +1840,11 @@ void objdump::printSectionHeaders(const ObjectFile *Obj) {
std::string Type = Section.isText() ? "TEXT" : "";
if (Section.isData())
- Type += Type.empty() ? "DATA" : " DATA";
+ Type += Type.empty() ? "DATA" : ", DATA";
if (Section.isBSS())
- Type += Type.empty() ? "BSS" : " BSS";
+ Type += Type.empty() ? "BSS" : ", BSS";
+ if (Section.isDebugSection())
+ Type += Type.empty() ? "DEBUG" : ", DEBUG";
if (HasLMAColumn)
outs() << format("%3" PRIu64 " %-*s %08" PRIx64 " ", Idx, NameWidth,
@@ -2394,7 +1857,6 @@ void objdump::printSectionHeaders(const ObjectFile *Obj) {
Name.str().c_str(), Size)
<< format_hex_no_prefix(VMA, AddressWidth) << " " << Type << "\n";
}
- outs() << "\n";
}
void objdump::printSectionContents(const ObjectFile *Obj) {
@@ -2450,7 +1912,7 @@ void objdump::printSectionContents(const ObjectFile *Obj) {
void objdump::printSymbolTable(const ObjectFile *O, StringRef ArchiveName,
StringRef ArchitectureName, bool DumpDynamic) {
if (O->isCOFF() && !DumpDynamic) {
- outs() << "SYMBOL TABLE:\n";
+ outs() << "\nSYMBOL TABLE:\n";
printCOFFSymbolTable(cast<const COFFObjectFile>(O));
return;
}
@@ -2458,13 +1920,13 @@ void objdump::printSymbolTable(const ObjectFile *O, StringRef ArchiveName,
const StringRef FileName = O->getFileName();
if (!DumpDynamic) {
- outs() << "SYMBOL TABLE:\n";
+ outs() << "\nSYMBOL TABLE:\n";
for (auto I = O->symbol_begin(); I != O->symbol_end(); ++I)
printSymbol(O, *I, FileName, ArchiveName, ArchitectureName, DumpDynamic);
return;
}
- outs() << "DYNAMIC SYMBOL TABLE:\n";
+ outs() << "\nDYNAMIC SYMBOL TABLE:\n";
if (!O->isELF()) {
reportWarning(
"this operation is not currently supported for this file format",
@@ -2736,7 +2198,7 @@ static void printFileHeaders(const ObjectFile *O) {
StringRef Fmt = O->getBytesInAddress() > 4 ? "%016" PRIx64 : "%08" PRIx64;
outs() << "start address: "
- << "0x" << format(Fmt.data(), Address) << "\n\n";
+ << "0x" << format(Fmt.data(), Address) << "\n";
}
static void printArchiveChild(StringRef Filename, const Archive::Child &C) {
@@ -2809,11 +2271,11 @@ static void checkForInvalidStartStopAddress(ObjectFile *Obj,
return;
}
- if (StartAddress.getNumOccurrences() == 0)
+ if (!HasStartAddressFlag)
reportWarning("no section has address less than 0x" +
Twine::utohexstr(Stop) + " specified by --stop-address",
Obj->getFileName());
- else if (StopAddress.getNumOccurrences() == 0)
+ else if (!HasStopAddressFlag)
reportWarning("no section has address greater than or equal to 0x" +
Twine::utohexstr(Start) + " specified by --start-address",
Obj->getFileName());
@@ -2833,10 +2295,10 @@ static void dumpObject(ObjectFile *O, const Archive *A = nullptr,
outs() << A->getFileName() << "(" << O->getFileName() << ")";
else
outs() << O->getFileName();
- outs() << ":\tfile format " << O->getFileFormatName().lower() << "\n\n";
+ outs() << ":\tfile format " << O->getFileFormatName().lower() << "\n";
}
- if (StartAddress.getNumOccurrences() || StopAddress.getNumOccurrences())
+ if (HasStartAddressFlag || HasStopAddressFlag)
checkForInvalidStartStopAddress(O, StartAddress, StopAddress);
// Note: the order here matches GNU objdump for compatability.
@@ -2956,33 +2418,246 @@ static void dumpInput(StringRef file) {
reportError(errorCodeToError(object_error::invalid_file_type), file);
}
+template <typename T>
+static void parseIntArg(const llvm::opt::InputArgList &InputArgs, int ID,
+ T &Value) {
+ if (const opt::Arg *A = InputArgs.getLastArg(ID)) {
+ StringRef V(A->getValue());
+ if (!llvm::to_integer(V, Value, 0)) {
+ reportCmdLineError(A->getSpelling() +
+ ": expected a non-negative integer, but got '" + V +
+ "'");
+ }
+ }
+}
+
+static std::vector<std::string>
+commaSeparatedValues(const llvm::opt::InputArgList &InputArgs, int ID) {
+ std::vector<std::string> Values;
+ for (StringRef Value : InputArgs.getAllArgValues(ID)) {
+ llvm::SmallVector<StringRef, 2> SplitValues;
+ llvm::SplitString(Value, SplitValues, ",");
+ for (StringRef SplitValue : SplitValues)
+ Values.push_back(SplitValue.str());
+ }
+ return Values;
+}
+
+static void parseOtoolOptions(const llvm::opt::InputArgList &InputArgs) {
+ MachOOpt = true;
+ FullLeadingAddr = true;
+ PrintImmHex = true;
+
+ ArchName = InputArgs.getLastArgValue(OTOOL_arch).str();
+ LinkOptHints = InputArgs.hasArg(OTOOL_C);
+ if (InputArgs.hasArg(OTOOL_d))
+ FilterSections.push_back("__DATA,__data");
+ DylibId = InputArgs.hasArg(OTOOL_D);
+ UniversalHeaders = InputArgs.hasArg(OTOOL_f);
+ DataInCode = InputArgs.hasArg(OTOOL_G);
+ FirstPrivateHeader = InputArgs.hasArg(OTOOL_h);
+ IndirectSymbols = InputArgs.hasArg(OTOOL_I);
+ ShowRawInsn = InputArgs.hasArg(OTOOL_j);
+ PrivateHeaders = InputArgs.hasArg(OTOOL_l);
+ DylibsUsed = InputArgs.hasArg(OTOOL_L);
+ MCPU = InputArgs.getLastArgValue(OTOOL_mcpu_EQ).str();
+ ObjcMetaData = InputArgs.hasArg(OTOOL_o);
+ DisSymName = InputArgs.getLastArgValue(OTOOL_p).str();
+ InfoPlist = InputArgs.hasArg(OTOOL_P);
+ Relocations = InputArgs.hasArg(OTOOL_r);
+ if (const Arg *A = InputArgs.getLastArg(OTOOL_s)) {
+ auto Filter = (A->getValue(0) + StringRef(",") + A->getValue(1)).str();
+ FilterSections.push_back(Filter);
+ }
+ if (InputArgs.hasArg(OTOOL_t))
+ FilterSections.push_back("__TEXT,__text");
+ Verbose = InputArgs.hasArg(OTOOL_v) || InputArgs.hasArg(OTOOL_V) ||
+ InputArgs.hasArg(OTOOL_o);
+ SymbolicOperands = InputArgs.hasArg(OTOOL_V);
+ if (InputArgs.hasArg(OTOOL_x))
+ FilterSections.push_back(",__text");
+ LeadingAddr = LeadingHeaders = !InputArgs.hasArg(OTOOL_X);
+
+ InputFilenames = InputArgs.getAllArgValues(OTOOL_INPUT);
+ if (InputFilenames.empty())
+ reportCmdLineError("no input file");
+
+ for (const Arg *A : InputArgs) {
+ const Option &O = A->getOption();
+ if (O.getGroup().isValid() && O.getGroup().getID() == OTOOL_grp_obsolete) {
+ reportCmdLineWarning(O.getPrefixedName() +
+ " is obsolete and not implemented");
+ }
+ }
+}
+
+static void parseObjdumpOptions(const llvm::opt::InputArgList &InputArgs) {
+ parseIntArg(InputArgs, OBJDUMP_adjust_vma_EQ, AdjustVMA);
+ AllHeaders = InputArgs.hasArg(OBJDUMP_all_headers);
+ ArchName = InputArgs.getLastArgValue(OBJDUMP_arch_name_EQ).str();
+ ArchiveHeaders = InputArgs.hasArg(OBJDUMP_archive_headers);
+ Demangle = InputArgs.hasArg(OBJDUMP_demangle);
+ Disassemble = InputArgs.hasArg(OBJDUMP_disassemble);
+ DisassembleAll = InputArgs.hasArg(OBJDUMP_disassemble_all);
+ SymbolDescription = InputArgs.hasArg(OBJDUMP_symbol_description);
+ DisassembleSymbols =
+ commaSeparatedValues(InputArgs, OBJDUMP_disassemble_symbols_EQ);
+ DisassembleZeroes = InputArgs.hasArg(OBJDUMP_disassemble_zeroes);
+ if (const opt::Arg *A = InputArgs.getLastArg(OBJDUMP_dwarf_EQ)) {
+ DwarfDumpType =
+ StringSwitch<DIDumpType>(A->getValue()).Case("frames", DIDT_DebugFrame);
+ }
+ DynamicRelocations = InputArgs.hasArg(OBJDUMP_dynamic_reloc);
+ FaultMapSection = InputArgs.hasArg(OBJDUMP_fault_map_section);
+ FileHeaders = InputArgs.hasArg(OBJDUMP_file_headers);
+ SectionContents = InputArgs.hasArg(OBJDUMP_full_contents);
+ PrintLines = InputArgs.hasArg(OBJDUMP_line_numbers);
+ InputFilenames = InputArgs.getAllArgValues(OBJDUMP_INPUT);
+ MachOOpt = InputArgs.hasArg(OBJDUMP_macho);
+ MCPU = InputArgs.getLastArgValue(OBJDUMP_mcpu_EQ).str();
+ MAttrs = commaSeparatedValues(InputArgs, OBJDUMP_mattr_EQ);
+ ShowRawInsn = !InputArgs.hasArg(OBJDUMP_no_show_raw_insn);
+ LeadingAddr = !InputArgs.hasArg(OBJDUMP_no_leading_addr);
+ RawClangAST = InputArgs.hasArg(OBJDUMP_raw_clang_ast);
+ Relocations = InputArgs.hasArg(OBJDUMP_reloc);
+ PrintImmHex =
+ InputArgs.hasFlag(OBJDUMP_print_imm_hex, OBJDUMP_no_print_imm_hex, false);
+ PrivateHeaders = InputArgs.hasArg(OBJDUMP_private_headers);
+ FilterSections = InputArgs.getAllArgValues(OBJDUMP_section_EQ);
+ SectionHeaders = InputArgs.hasArg(OBJDUMP_section_headers);
+ ShowLMA = InputArgs.hasArg(OBJDUMP_show_lma);
+ PrintSource = InputArgs.hasArg(OBJDUMP_source);
+ parseIntArg(InputArgs, OBJDUMP_start_address_EQ, StartAddress);
+ HasStartAddressFlag = InputArgs.hasArg(OBJDUMP_start_address_EQ);
+ parseIntArg(InputArgs, OBJDUMP_stop_address_EQ, StopAddress);
+ HasStopAddressFlag = InputArgs.hasArg(OBJDUMP_stop_address_EQ);
+ SymbolTable = InputArgs.hasArg(OBJDUMP_syms);
+ SymbolizeOperands = InputArgs.hasArg(OBJDUMP_symbolize_operands);
+ DynamicSymbolTable = InputArgs.hasArg(OBJDUMP_dynamic_syms);
+ TripleName = InputArgs.getLastArgValue(OBJDUMP_triple_EQ).str();
+ UnwindInfo = InputArgs.hasArg(OBJDUMP_unwind_info);
+ Wide = InputArgs.hasArg(OBJDUMP_wide);
+ Prefix = InputArgs.getLastArgValue(OBJDUMP_prefix).str();
+ parseIntArg(InputArgs, OBJDUMP_prefix_strip, PrefixStrip);
+ if (const opt::Arg *A = InputArgs.getLastArg(OBJDUMP_debug_vars_EQ)) {
+ DbgVariables = StringSwitch<DebugVarsFormat>(A->getValue())
+ .Case("ascii", DVASCII)
+ .Case("unicode", DVUnicode);
+ }
+ parseIntArg(InputArgs, OBJDUMP_debug_vars_indent_EQ, DbgIndent);
+
+ parseMachOOptions(InputArgs);
+
+ // Parse -M (--disassembler-options) and deprecated
+ // --x86-asm-syntax={att,intel}.
+ //
+ // Note, for x86, the asm dialect (AssemblerDialect) is initialized when the
+ // MCAsmInfo is constructed. MCInstPrinter::applyTargetSpecificCLOption is
+ // called too late. For now we have to use the internal cl::opt option.
+ const char *AsmSyntax = nullptr;
+ for (const auto *A : InputArgs.filtered(OBJDUMP_disassembler_options_EQ,
+ OBJDUMP_x86_asm_syntax_att,
+ OBJDUMP_x86_asm_syntax_intel)) {
+ switch (A->getOption().getID()) {
+ case OBJDUMP_x86_asm_syntax_att:
+ AsmSyntax = "--x86-asm-syntax=att";
+ continue;
+ case OBJDUMP_x86_asm_syntax_intel:
+ AsmSyntax = "--x86-asm-syntax=intel";
+ continue;
+ }
+
+ SmallVector<StringRef, 2> Values;
+ llvm::SplitString(A->getValue(), Values, ",");
+ for (StringRef V : Values) {
+ if (V == "att")
+ AsmSyntax = "--x86-asm-syntax=att";
+ else if (V == "intel")
+ AsmSyntax = "--x86-asm-syntax=intel";
+ else
+ DisassemblerOptions.push_back(V.str());
+ }
+ }
+ if (AsmSyntax) {
+ const char *Argv[] = {"llvm-objdump", AsmSyntax};
+ llvm::cl::ParseCommandLineOptions(2, Argv);
+ }
+
+ // objdump defaults to a.out if no filenames specified.
+ if (InputFilenames.empty())
+ InputFilenames.push_back("a.out");
+}
+
int main(int argc, char **argv) {
using namespace llvm;
InitLLVM X(argc, argv);
- const cl::OptionCategory *OptionFilters[] = {&ObjdumpCat, &MachOCat};
- cl::HideUnrelatedOptions(OptionFilters);
+
+ ToolName = argv[0];
+ std::unique_ptr<CommonOptTable> T;
+ OptSpecifier Unknown, HelpFlag, HelpHiddenFlag, VersionFlag;
+
+ StringRef Stem = sys::path::stem(ToolName);
+ auto Is = [=](StringRef Tool) {
+ // We need to recognize the following filenames:
+ //
+ // llvm-objdump -> objdump
+ // llvm-otool-10.exe -> otool
+ // powerpc64-unknown-freebsd13-objdump -> objdump
+ auto I = Stem.rfind_insensitive(Tool);
+ return I != StringRef::npos &&
+ (I + Tool.size() == Stem.size() || !isAlnum(Stem[I + Tool.size()]));
+ };
+ if (Is("otool")) {
+ T = std::make_unique<OtoolOptTable>();
+ Unknown = OTOOL_UNKNOWN;
+ HelpFlag = OTOOL_help;
+ HelpHiddenFlag = OTOOL_help_hidden;
+ VersionFlag = OTOOL_version;
+ } else {
+ T = std::make_unique<ObjdumpOptTable>();
+ Unknown = OBJDUMP_UNKNOWN;
+ HelpFlag = OBJDUMP_help;
+ HelpHiddenFlag = OBJDUMP_help_hidden;
+ VersionFlag = OBJDUMP_version;
+ }
+
+ BumpPtrAllocator A;
+ StringSaver Saver(A);
+ opt::InputArgList InputArgs =
+ T->parseArgs(argc, argv, Unknown, Saver,
+ [&](StringRef Msg) { reportCmdLineError(Msg); });
+
+ if (InputArgs.size() == 0 || InputArgs.hasArg(HelpFlag)) {
+ T->printHelp(ToolName);
+ return 0;
+ }
+ if (InputArgs.hasArg(HelpHiddenFlag)) {
+ T->printHelp(ToolName, /*show_hidden=*/true);
+ return 0;
+ }
// Initialize targets and assembly printers/parsers.
InitializeAllTargetInfos();
InitializeAllTargetMCs();
InitializeAllDisassemblers();
- // Register the target printer for --version.
- cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion);
+ if (InputArgs.hasArg(VersionFlag)) {
+ cl::PrintVersionMessage();
+ if (!Is("otool")) {
+ outs() << '\n';
+ TargetRegistry::printRegisteredTargetsForVersion(outs());
+ }
+ return 0;
+ }
- cl::ParseCommandLineOptions(argc, argv, "llvm object file dumper\n", nullptr,
- /*EnvVar=*/nullptr,
- /*LongOptionsUseDoubleDash=*/true);
+ if (Is("otool"))
+ parseOtoolOptions(InputArgs);
+ else
+ parseObjdumpOptions(InputArgs);
if (StartAddress >= StopAddress)
reportCmdLineError("start address should be less than stop address");
- ToolName = argv[0];
-
- // Defaults to a.out if no filenames specified.
- if (InputFilenames.empty())
- InputFilenames.push_back("a.out");
-
// Removes trailing separators from prefix.
while (!Prefix.empty() && sys::path::is_separator(Prefix.back()))
Prefix.pop_back();
@@ -3001,10 +2676,10 @@ int main(int argc, char **argv) {
!DynamicSymbolTable && !UnwindInfo && !FaultMapSection &&
!(MachOOpt &&
(Bind || DataInCode || DylibId || DylibsUsed || ExportsTrie ||
- FirstPrivateHeader || IndirectSymbols || InfoPlist || LazyBind ||
- LinkOptHints || ObjcMetaData || Rebase || UniversalHeaders ||
- WeakBind || !FilterSections.empty()))) {
- cl::PrintHelpMessage();
+ FirstPrivateHeader || FunctionStarts || IndirectSymbols || InfoPlist ||
+ LazyBind || LinkOptHints || ObjcMetaData || Rebase || Rpaths ||
+ UniversalHeaders || WeakBind || !FilterSections.empty()))) {
+ T->printHelp(ToolName);
return 2;
}
diff --git a/llvm/tools/llvm-objdump/llvm-objdump.h b/llvm/tools/llvm-objdump/llvm-objdump.h
index 99bf191a301e..33fb3f207f8e 100644
--- a/llvm/tools/llvm-objdump/llvm-objdump.h
+++ b/llvm/tools/llvm-objdump/llvm-objdump.h
@@ -1,3 +1,4 @@
+//===--- llvm-objdump.h -----------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -12,7 +13,6 @@
#include "llvm/DebugInfo/DIContext.h"
#include "llvm/MC/MCDisassembler/MCDisassembler.h"
#include "llvm/Object/Archive.h"
-#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/DataTypes.h"
@@ -30,26 +30,37 @@ class RelocationRef;
namespace objdump {
-extern cl::opt<bool> ArchiveHeaders;
-extern cl::opt<bool> Demangle;
-extern cl::opt<bool> Disassemble;
-extern cl::opt<bool> DisassembleAll;
-extern cl::opt<DIDumpType> DwarfDumpType;
-extern cl::list<std::string> FilterSections;
-extern cl::list<std::string> MAttrs;
-extern cl::opt<std::string> MCPU;
-extern cl::opt<bool> NoShowRawInsn;
-extern cl::opt<bool> NoLeadingAddr;
-extern cl::opt<std::string> Prefix;
-extern cl::opt<bool> PrintImmHex;
-extern cl::opt<bool> PrivateHeaders;
-extern cl::opt<bool> Relocations;
-extern cl::opt<bool> SectionHeaders;
-extern cl::opt<bool> SectionContents;
-extern cl::opt<bool> SymbolDescription;
-extern cl::opt<bool> SymbolTable;
-extern cl::opt<std::string> TripleName;
-extern cl::opt<bool> UnwindInfo;
+enum DebugVarsFormat {
+ DVDisabled,
+ DVUnicode,
+ DVASCII,
+};
+
+extern bool ArchiveHeaders;
+extern int DbgIndent;
+extern DebugVarsFormat DbgVariables;
+extern bool Demangle;
+extern bool Disassemble;
+extern bool DisassembleAll;
+extern DIDumpType DwarfDumpType;
+extern std::vector<std::string> FilterSections;
+extern bool LeadingAddr;
+extern std::vector<std::string> MAttrs;
+extern std::string MCPU;
+extern std::string Prefix;
+extern uint32_t PrefixStrip;
+extern bool PrintImmHex;
+extern bool PrintLines;
+extern bool PrintSource;
+extern bool PrivateHeaders;
+extern bool Relocations;
+extern bool SectionHeaders;
+extern bool SectionContents;
+extern bool ShowRawInsn;
+extern bool SymbolDescription;
+extern bool SymbolTable;
+extern std::string TripleName;
+extern bool UnwindInfo;
extern StringSet<> FoundSectionSet;
diff --git a/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp b/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp
index babdb56a718c..ef299ea9d482 100644
--- a/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp
+++ b/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp
@@ -350,13 +350,13 @@ static bool isMyCode(const SymbolGroup &Group) {
StringRef Name = Group.name();
if (Name.startswith("Import:"))
return false;
- if (Name.endswith_lower(".dll"))
+ if (Name.endswith_insensitive(".dll"))
return false;
- if (Name.equals_lower("* linker *"))
+ if (Name.equals_insensitive("* linker *"))
return false;
- if (Name.startswith_lower("f:\\binaries\\Intermediate\\vctools"))
+ if (Name.startswith_insensitive("f:\\binaries\\Intermediate\\vctools"))
return false;
- if (Name.startswith_lower("f:\\dd\\vctools\\crt"))
+ if (Name.startswith_insensitive("f:\\dd\\vctools\\crt"))
return false;
return true;
}
diff --git a/llvm/tools/llvm-pdbutil/InputFile.cpp b/llvm/tools/llvm-pdbutil/InputFile.cpp
index b316882de64d..40b35625b6f8 100644
--- a/llvm/tools/llvm-pdbutil/InputFile.cpp
+++ b/llvm/tools/llvm-pdbutil/InputFile.cpp
@@ -288,7 +288,8 @@ Expected<InputFile> InputFile::open(StringRef Path, bool AllowUnknownFile) {
formatv("File {0} is not a supported file type", Path),
inconvertibleErrorCode());
- auto Result = MemoryBuffer::getFile(Path, -1LL, false);
+ auto Result = MemoryBuffer::getFile(Path, /*IsText=*/false,
+ /*RequiresNullTerminator=*/false);
if (!Result)
return make_error<StringError>(
formatv("File {0} could not be opened", Path), Result.getError());
diff --git a/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp b/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp
index 787785c34b78..3e8b1e88657b 100644
--- a/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp
+++ b/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp
@@ -261,6 +261,9 @@ static std::string formatMachineType(CPUType Cpu) {
RETURN_CASE(CPUType, ARM_WMMX, "arm wmmx");
RETURN_CASE(CPUType, ARM7, "arm 7");
RETURN_CASE(CPUType, ARM64, "arm64");
+ RETURN_CASE(CPUType, ARM64EC, "arm64ec");
+ RETURN_CASE(CPUType, ARM64X, "arm64x");
+ RETURN_CASE(CPUType, HybridX86ARM64, "hybrid x86 arm64");
RETURN_CASE(CPUType, Omni, "omni");
RETURN_CASE(CPUType, Ia64, "intel itanium ia64");
RETURN_CASE(CPUType, Ia64_2, "intel itanium ia64 2");
@@ -559,7 +562,7 @@ Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR,
P.format(" `{0}`", Constant.Name);
AutoIndent Indent(P, 7);
P.formatLine("type = {0}, value = {1}", typeIndex(Constant.Type),
- Constant.Value.toString(10));
+ toString(Constant.Value, 10));
return Error::success();
}
diff --git a/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp b/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp
index 8e46a97272d5..08006e9c62d4 100644
--- a/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp
+++ b/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp
@@ -557,7 +557,7 @@ Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
EnumeratorRecord &Enum) {
P.format(" [{0} = {1}]", Enum.Name,
- Enum.Value.toString(10, Enum.Value.isSigned()));
+ toString(Enum.Value, 10, Enum.Value.isSigned()));
return Error::success();
}
diff --git a/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp b/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp
index 19f4880ab5eb..b152ebd6dccb 100644
--- a/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp
+++ b/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp
@@ -748,7 +748,7 @@ static ExitOnError ExitOnErr;
static void yamlToPdb(StringRef Path) {
BumpPtrAllocator Allocator;
ErrorOr<std::unique_ptr<MemoryBuffer>> ErrorOrBuffer =
- MemoryBuffer::getFileOrSTDIN(Path, /*FileSize=*/-1,
+ MemoryBuffer::getFileOrSTDIN(Path, /*IsText=*/false,
/*RequiresNullTerminator=*/false);
if (ErrorOrBuffer.getError()) {
@@ -868,7 +868,6 @@ static void pdb2Yaml(StringRef Path) {
auto &File = loadPDB(Path, Session);
auto O = std::make_unique<YAMLOutputStyle>(File);
- O = std::make_unique<YAMLOutputStyle>(File);
ExitOnErr(O->dump());
}
@@ -1431,6 +1430,8 @@ int main(int Argc, const char **Argv) {
InitLLVM X(Argc, Argv);
ExitOnErr.setBanner("llvm-pdbutil: ");
+ cl::HideUnrelatedOptions(
+ {&opts::TypeCategory, &opts::FilterCategory, &opts::OtherOptions});
cl::ParseCommandLineOptions(Argc, Argv, "LLVM PDB Dumper\n");
if (opts::BytesSubcommand) {
diff --git a/llvm/tools/llvm-profdata/llvm-profdata.cpp b/llvm/tools/llvm-profdata/llvm-profdata.cpp
index 8dc43924c067..66d70120ac9b 100644
--- a/llvm/tools/llvm-profdata/llvm-profdata.cpp
+++ b/llvm/tools/llvm-profdata/llvm-profdata.cpp
@@ -20,6 +20,7 @@
#include "llvm/ProfileData/SampleProfReader.h"
#include "llvm/ProfileData/SampleProfWriter.h"
#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Discriminator.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Format.h"
@@ -54,6 +55,14 @@ static void warn(Twine Message, std::string Whence = "",
WithColor::note() << Hint << "\n";
}
+static void warn(Error E, StringRef Whence = "") {
+ if (E.isA<InstrProfError>()) {
+ handleAllErrors(std::move(E), [&](const InstrProfError &IPE) {
+ warn(IPE.message(), std::string(Whence), std::string(""));
+ });
+ }
+}
+
static void exitWithError(Twine Message, std::string Whence = "",
std::string Hint = "") {
WithColor::error();
@@ -243,7 +252,8 @@ static void loadInput(const WeightedFile &Input, SymbolRemapper *Remapper,
auto Reader = std::move(ReaderOrErr.get());
bool IsIRProfile = Reader->isIRLevelProfile();
bool HasCSIRProfile = Reader->hasCSIRLevelProfile();
- if (WC->Writer.setIsIRLevelProfile(IsIRProfile, HasCSIRProfile)) {
+ if (Error E = WC->Writer.setIsIRLevelProfile(IsIRProfile, HasCSIRProfile)) {
+ consumeError(std::move(E));
WC->Errors.emplace_back(
make_error<StringError>(
"Merge IR generated profile with Clang generated profile.",
@@ -297,16 +307,19 @@ static void writeInstrProfile(StringRef OutputFilename,
InstrProfWriter &Writer) {
std::error_code EC;
raw_fd_ostream Output(OutputFilename.data(), EC,
- OutputFormat == PF_Text ? sys::fs::OF_Text
+ OutputFormat == PF_Text ? sys::fs::OF_TextWithCRLF
: sys::fs::OF_None);
if (EC)
exitWithErrorCode(EC, OutputFilename);
if (OutputFormat == PF_Text) {
if (Error E = Writer.writeText(Output))
- exitWithError(std::move(E));
+ warn(std::move(E));
} else {
- Writer.write(Output);
+ if (Output.is_displayed())
+ exitWithError("cannot write a non-text format profile to the terminal");
+ if (Error E = Writer.write(Output))
+ warn(std::move(E));
}
}
@@ -315,12 +328,9 @@ static void mergeInstrProfile(const WeightedFileVector &Inputs,
StringRef OutputFilename,
ProfileFormat OutputFormat, bool OutputSparse,
unsigned NumThreads, FailureMode FailMode) {
- if (OutputFilename.compare("-") == 0)
- exitWithError("Cannot write indexed profdata format to stdout.");
-
if (OutputFormat != PF_Binary && OutputFormat != PF_Compact_Binary &&
OutputFormat != PF_Ext_Binary && OutputFormat != PF_Text)
- exitWithError("Unknown format is specified.");
+ exitWithError("unknown format is specified");
std::mutex ErrorLock;
SmallSet<instrprof_error, 4> WriterErrorCodes;
@@ -383,7 +393,7 @@ static void mergeInstrProfile(const WeightedFileVector &Inputs,
}
if (NumErrors == Inputs.size() ||
(NumErrors > 0 && FailMode == failIfAnyAreInvalid))
- exitWithError("No profiles could be merged.");
+ exitWithError("no profile can be merged");
writeInstrProfile(OutputFilename, OutputFormat, Contexts[0]->Writer);
}
@@ -441,6 +451,25 @@ static void updateInstrProfileEntry(InstrProfileEntry &IFE,
const uint64_t ColdPercentileIdx = 15;
const uint64_t HotPercentileIdx = 11;
+using sampleprof::FSDiscriminatorPass;
+
+// Internal options to set FSDiscriminatorPass. Used in merge and show
+// commands.
+static cl::opt<FSDiscriminatorPass> FSDiscriminatorPassOption(
+ "fs-discriminator-pass", cl::init(PassLast), cl::Hidden,
+ cl::desc("Zero out the discriminator bits for the FS discrimiantor "
+ "pass beyond this value. The enum values are defined in "
+ "Support/Discriminator.h"),
+ cl::values(clEnumVal(Base, "Use base discriminators only"),
+ clEnumVal(Pass1, "Use base and pass 1 discriminators"),
+ clEnumVal(Pass2, "Use base and pass 1-2 discriminators"),
+ clEnumVal(Pass3, "Use base and pass 1-3 discriminators"),
+ clEnumVal(PassLast, "Use all discriminator bits (default)")));
+
+static unsigned getDiscriminatorMask() {
+ return getN1Bits(getFSPassBitEnd(FSDiscriminatorPassOption.getValue()));
+}
+
/// Adjust the instr profile in \p WC based on the sample profile in
/// \p Reader.
static void
@@ -522,18 +551,18 @@ static void supplementInstrProfile(
unsigned SupplMinSizeThreshold, float ZeroCounterThreshold,
unsigned InstrProfColdThreshold) {
if (OutputFilename.compare("-") == 0)
- exitWithError("Cannot write indexed profdata format to stdout.");
+ exitWithError("cannot write indexed profdata format to stdout");
if (Inputs.size() != 1)
- exitWithError("Expect one input to be an instr profile.");
+ exitWithError("expect one input to be an instr profile");
if (Inputs[0].Weight != 1)
- exitWithError("Expect instr profile doesn't have weight.");
+ exitWithError("expect instr profile doesn't have weight");
StringRef InstrFilename = Inputs[0].Filename;
// Read sample profile.
LLVMContext Context;
- auto ReaderOrErr =
- sampleprof::SampleProfileReader::create(SampleFilename.str(), Context);
+ auto ReaderOrErr = sampleprof::SampleProfileReader::create(
+ SampleFilename.str(), Context, FSDiscriminatorPassOption);
if (std::error_code EC = ReaderOrErr.getError())
exitWithErrorCode(EC, SampleFilename);
auto Reader = std::move(ReaderOrErr.get());
@@ -564,12 +593,13 @@ remapSamples(const sampleprof::FunctionSamples &Samples,
Result.addTotalSamples(Samples.getTotalSamples());
Result.addHeadSamples(Samples.getHeadSamples());
for (const auto &BodySample : Samples.getBodySamples()) {
- Result.addBodySamples(BodySample.first.LineOffset,
- BodySample.first.Discriminator,
+ uint32_t MaskedDiscriminator =
+ BodySample.first.Discriminator & getDiscriminatorMask();
+ Result.addBodySamples(BodySample.first.LineOffset, MaskedDiscriminator,
BodySample.second.getSamples());
for (const auto &Target : BodySample.second.getCallTargets()) {
Result.addCalledTargetSamples(BodySample.first.LineOffset,
- BodySample.first.Discriminator,
+ MaskedDiscriminator,
Remapper(Target.first()), Target.second);
}
}
@@ -656,15 +686,19 @@ static void
mergeSampleProfile(const WeightedFileVector &Inputs, SymbolRemapper *Remapper,
StringRef OutputFilename, ProfileFormat OutputFormat,
StringRef ProfileSymbolListFile, bool CompressAllSections,
- bool UseMD5, bool GenPartialProfile, FailureMode FailMode) {
+ bool UseMD5, bool GenPartialProfile,
+ bool SampleMergeColdContext, bool SampleTrimColdContext,
+ bool SampleColdContextFrameDepth, FailureMode FailMode) {
using namespace sampleprof;
StringMap<FunctionSamples> ProfileMap;
SmallVector<std::unique_ptr<sampleprof::SampleProfileReader>, 5> Readers;
LLVMContext Context;
sampleprof::ProfileSymbolList WriterList;
Optional<bool> ProfileIsProbeBased;
+ Optional<bool> ProfileIsCS;
for (const auto &Input : Inputs) {
- auto ReaderOrErr = SampleProfileReader::create(Input.Filename, Context);
+ auto ReaderOrErr = SampleProfileReader::create(Input.Filename, Context,
+ FSDiscriminatorPassOption);
if (std::error_code EC = ReaderOrErr.getError()) {
warnOrExitGivenError(FailMode, EC, Input.Filename);
continue;
@@ -683,11 +717,14 @@ mergeSampleProfile(const WeightedFileVector &Inputs, SymbolRemapper *Remapper,
}
StringMap<FunctionSamples> &Profiles = Reader->getProfiles();
- if (ProfileIsProbeBased &&
+ if (ProfileIsProbeBased.hasValue() &&
ProfileIsProbeBased != FunctionSamples::ProfileIsProbeBased)
exitWithError(
"cannot merge probe-based profile with non-probe-based profile");
ProfileIsProbeBased = FunctionSamples::ProfileIsProbeBased;
+ if (ProfileIsCS.hasValue() && ProfileIsCS != FunctionSamples::ProfileIsCS)
+ exitWithError("cannot merge CS profile with non-CS profile");
+ ProfileIsCS = FunctionSamples::ProfileIsCS;
for (StringMap<FunctionSamples>::iterator I = Profiles.begin(),
E = Profiles.end();
I != E; ++I) {
@@ -696,7 +733,7 @@ mergeSampleProfile(const WeightedFileVector &Inputs, SymbolRemapper *Remapper,
Remapper ? remapSamples(I->second, *Remapper, Result)
: FunctionSamples();
FunctionSamples &Samples = Remapper ? Remapped : I->second;
- StringRef FName = Samples.getName();
+ StringRef FName = Samples.getNameWithContext();
MergeResult(Result, ProfileMap[FName].merge(Samples, Input.Weight));
if (Result != sampleprof_error::success) {
std::error_code EC = make_error_code(Result);
@@ -709,6 +746,22 @@ mergeSampleProfile(const WeightedFileVector &Inputs, SymbolRemapper *Remapper,
if (ReaderList)
WriterList.merge(*ReaderList);
}
+
+ if (ProfileIsCS && (SampleMergeColdContext || SampleTrimColdContext)) {
+ // Use threshold calculated from profile summary unless specified.
+ SampleProfileSummaryBuilder Builder(ProfileSummaryBuilder::DefaultCutoffs);
+ auto Summary = Builder.computeSummaryForProfiles(ProfileMap);
+ uint64_t SampleProfColdThreshold =
+ ProfileSummaryBuilder::getColdCountThreshold(
+ (Summary->getDetailedSummary()));
+
+ // Trim and merge cold context profile using cold threshold above;
+ SampleContextTrimmer(ProfileMap)
+ .trimAndMergeColdContextProfiles(
+ SampleProfColdThreshold, SampleTrimColdContext,
+ SampleMergeColdContext, SampleColdContextFrameDepth);
+ }
+
auto WriterOrErr =
SampleProfileWriter::create(OutputFilename, FormatMap[OutputFormat]);
if (std::error_code EC = WriterOrErr.getError())
@@ -720,7 +773,8 @@ mergeSampleProfile(const WeightedFileVector &Inputs, SymbolRemapper *Remapper,
auto Buffer = getInputFileBuf(ProfileSymbolListFile);
handleExtBinaryWriter(*Writer, OutputFormat, Buffer.get(), WriterList,
CompressAllSections, UseMD5, GenPartialProfile);
- Writer->write(ProfileMap);
+ if (std::error_code EC = Writer->write(ProfileMap))
+ exitWithErrorCode(std::move(EC));
}
static WeightedFile parseWeightedFile(const StringRef &WeightedFilename) {
@@ -729,7 +783,7 @@ static WeightedFile parseWeightedFile(const StringRef &WeightedFilename) {
uint64_t Weight;
if (WeightStr.getAsInteger(10, Weight) || Weight < 1)
- exitWithError("Input weight must be a positive integer.");
+ exitWithError("input weight must be a positive integer");
return {std::string(FileName), Weight};
}
@@ -808,8 +862,7 @@ static int merge_main(int argc, const char *argv[]) {
cl::alias RemappingFileA("r", cl::desc("Alias for --remapping-file"),
cl::aliasopt(RemappingFile));
cl::opt<std::string> OutputFilename("output", cl::value_desc("output"),
- cl::init("-"), cl::Required,
- cl::desc("Output file"));
+ cl::init("-"), cl::desc("Output file"));
cl::alias OutputFilenameA("o", cl::desc("Alias for --output"),
cl::aliasopt(OutputFilename));
cl::opt<ProfileKinds> ProfileKind(
@@ -851,6 +904,18 @@ static int merge_main(int argc, const char *argv[]) {
"use-md5", cl::init(false), cl::Hidden,
cl::desc("Choose to use MD5 to represent string in name table (only "
"meaningful for -extbinary)"));
+ cl::opt<bool> SampleMergeColdContext(
+ "sample-merge-cold-context", cl::init(false), cl::Hidden,
+ cl::desc(
+ "Merge context sample profiles whose count is below cold threshold"));
+ cl::opt<bool> SampleTrimColdContext(
+ "sample-trim-cold-context", cl::init(false), cl::Hidden,
+ cl::desc(
+ "Trim context sample profiles whose count is below cold threshold"));
+ cl::opt<uint32_t> SampleColdContextFrameDepth(
+ "sample-frame-depth-for-cold-context", cl::init(1), cl::ZeroOrMore,
+ cl::desc("Keep the last K frames while merging cold profile. 1 means the "
+ "context-less base profile"));
cl::opt<bool> GenPartialProfile(
"gen-partial-profile", cl::init(false), cl::Hidden,
cl::desc("Generate a partial profile (only meaningful for -extbinary)"));
@@ -866,16 +931,16 @@ static int merge_main(int argc, const char *argv[]) {
"sample profile, if the ratio of the number of zero counters "
"divided by the the total number of counters is above the "
"threshold, the profile of the function will be regarded as "
- "being harmful for performance and will be dropped. "));
+ "being harmful for performance and will be dropped."));
cl::opt<unsigned> SupplMinSizeThreshold(
"suppl-min-size-threshold", cl::init(10), cl::Hidden,
cl::desc("If the size of a function is smaller than the threshold, "
"assume it can be inlined by PGO early inliner and it won't "
- "be adjusted based on sample profile. "));
+ "be adjusted based on sample profile."));
cl::opt<unsigned> InstrProfColdThreshold(
"instr-prof-cold-threshold", cl::init(0), cl::Hidden,
cl::desc("User specified cold threshold for instr profile which will "
- "override the cold threshold got from profile summary. "));
+ "override the cold threshold got from profile summary."));
cl::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n");
@@ -891,7 +956,7 @@ static int merge_main(int argc, const char *argv[]) {
parseInputFilenamesFile(Buffer.get(), WeightedInputs);
if (WeightedInputs.empty())
- exitWithError("No input files specified. See " +
+ exitWithError("no input files specified. See " +
sys::path::filename(argv[0]) + " -help");
if (DumpInputFileList) {
@@ -921,7 +986,9 @@ static int merge_main(int argc, const char *argv[]) {
else
mergeSampleProfile(WeightedInputs, Remapper.get(), OutputFilename,
OutputFormat, ProfileSymbolListFile, CompressAllSections,
- UseMD5, GenPartialProfile, FailureMode);
+ UseMD5, GenPartialProfile, SampleMergeColdContext,
+ SampleTrimColdContext, SampleColdContextFrameDepth,
+ FailureMode);
return 0;
}
@@ -938,7 +1005,7 @@ static void overlapInstrProfile(const std::string &BaseFilename,
OverlapStats Overlap;
Error E = Overlap.accumulateCounts(BaseFilename, TestFilename, IsCS);
if (E)
- exitWithError(std::move(E), "Error in getting profile count sums");
+ exitWithError(std::move(E), "error in getting profile count sums");
if (Overlap.Base.CountSum < 1.0f) {
OS << "Sum of edge counts for profile " << BaseFilename << " is 0.\n";
exit(0);
@@ -1548,14 +1615,15 @@ void SampleOverlapAggregator::computeSampleProfileOverlap(raw_fd_ostream &OS) {
StringMap<const FunctionSamples *> BaseFuncProf;
const auto &BaseProfiles = BaseReader->getProfiles();
for (const auto &BaseFunc : BaseProfiles) {
- BaseFuncProf.try_emplace(BaseFunc.second.getName(), &(BaseFunc.second));
+ BaseFuncProf.try_emplace(BaseFunc.second.getNameWithContext(),
+ &(BaseFunc.second));
}
ProfOverlap.UnionCount = BaseFuncProf.size();
const auto &TestProfiles = TestReader->getProfiles();
for (const auto &TestFunc : TestProfiles) {
SampleOverlapStats FuncOverlap;
- FuncOverlap.TestName = TestFunc.second.getName();
+ FuncOverlap.TestName = TestFunc.second.getNameWithContext();
assert(TestStats.count(FuncOverlap.TestName) &&
"TestStats should have records for all functions in test profile "
"except inlinees");
@@ -1582,7 +1650,7 @@ void SampleOverlapAggregator::computeSampleProfileOverlap(raw_fd_ostream &OS) {
// Two functions match with each other. Compute function-level overlap and
// aggregate them into profile-level overlap.
- FuncOverlap.BaseName = Match->second->getName();
+ FuncOverlap.BaseName = Match->second->getNameWithContext();
assert(BaseStats.count(FuncOverlap.BaseName) &&
"BaseStats should have records for all functions in base profile "
"except inlinees");
@@ -1631,10 +1699,11 @@ void SampleOverlapAggregator::computeSampleProfileOverlap(raw_fd_ostream &OS) {
// Traverse through functions in base profile but not in test profile.
for (const auto &F : BaseFuncProf) {
- assert(BaseStats.count(F.second->getName()) &&
+ assert(BaseStats.count(F.second->getNameWithContext()) &&
"BaseStats should have records for all functions in base profile "
"except inlinees");
- const FuncSampleStats &FuncStats = BaseStats[F.second->getName()];
+ const FuncSampleStats &FuncStats =
+ BaseStats[F.second->getNameWithContext()];
++ProfOverlap.BaseUniqueCount;
ProfOverlap.BaseUniqueSample += FuncStats.SampleSum;
@@ -1665,7 +1734,7 @@ void SampleOverlapAggregator::initializeSampleProfileOverlap() {
FuncSampleStats FuncStats;
getFuncSampleStats(I.second, FuncStats, BaseHotThreshold);
ProfOverlap.BaseSample += FuncStats.SampleSum;
- BaseStats.try_emplace(I.second.getName(), FuncStats);
+ BaseStats.try_emplace(I.second.getNameWithContext(), FuncStats);
}
const auto &TestProf = TestReader->getProfiles();
@@ -1674,7 +1743,7 @@ void SampleOverlapAggregator::initializeSampleProfileOverlap() {
FuncSampleStats FuncStats;
getFuncSampleStats(I.second, FuncStats, TestHotThreshold);
ProfOverlap.TestSample += FuncStats.SampleSum;
- TestStats.try_emplace(I.second.getName(), FuncStats);
+ TestStats.try_emplace(I.second.getNameWithContext(), FuncStats);
}
ProfOverlap.BaseName = StringRef(BaseFilename);
@@ -1815,11 +1884,13 @@ std::error_code SampleOverlapAggregator::loadProfiles() {
using namespace sampleprof;
LLVMContext Context;
- auto BaseReaderOrErr = SampleProfileReader::create(BaseFilename, Context);
+ auto BaseReaderOrErr = SampleProfileReader::create(BaseFilename, Context,
+ FSDiscriminatorPassOption);
if (std::error_code EC = BaseReaderOrErr.getError())
exitWithErrorCode(EC, BaseFilename);
- auto TestReaderOrErr = SampleProfileReader::create(TestFilename, Context);
+ auto TestReaderOrErr = SampleProfileReader::create(TestFilename, Context,
+ FSDiscriminatorPassOption);
if (std::error_code EC = TestReaderOrErr.getError())
exitWithErrorCode(EC, TestFilename);
@@ -1833,6 +1904,8 @@ std::error_code SampleOverlapAggregator::loadProfiles() {
if (BaseReader->profileIsProbeBased() != TestReader->profileIsProbeBased())
exitWithError(
"cannot compare probe-based profile with non-probe-based profile");
+ if (BaseReader->profileIsCS() != TestReader->profileIsCS())
+ exitWithError("cannot compare CS profile with non-CS profile");
// Load BaseHotThreshold and TestHotThreshold as 99-percentile threshold in
// profile summary.
@@ -1888,21 +1961,24 @@ static int overlap_main(int argc, const char *argv[]) {
cl::opt<std::string> Output("output", cl::value_desc("output"), cl::init("-"),
cl::desc("Output file"));
cl::alias OutputA("o", cl::desc("Alias for --output"), cl::aliasopt(Output));
- cl::opt<bool> IsCS("cs", cl::init(false),
- cl::desc("For context sensitive counts"));
+ cl::opt<bool> IsCS(
+ "cs", cl::init(false),
+ cl::desc("For context sensitive PGO counts. Does not work with CSSPGO."));
cl::opt<unsigned long long> ValueCutoff(
"value-cutoff", cl::init(-1),
cl::desc(
- "Function level overlap information for every function in test "
+ "Function level overlap information for every function (with calling "
+ "context for csspgo) in test "
"profile with max count value greater then the parameter value"));
cl::opt<std::string> FuncNameFilter(
"function",
- cl::desc("Function level overlap information for matching functions"));
+ cl::desc("Function level overlap information for matching functions. For "
+ "CSSPGO this takes a a function name with calling context"));
cl::opt<unsigned long long> SimilarityCutoff(
"similarity-cutoff", cl::init(0),
- cl::desc(
- "For sample profiles, list function names for overlapped functions "
- "with similarities below the cutoff (percentage times 10000)."));
+ cl::desc("For sample profiles, list function names (with calling context "
+ "for csspgo) for overlapped functions "
+ "with similarities below the cutoff (percentage times 10000)."));
cl::opt<ProfileKinds> ProfileKind(
cl::desc("Profile kind:"), cl::init(instr),
cl::values(clEnumVal(instr, "Instrumentation profile (default)"),
@@ -1910,7 +1986,7 @@ static int overlap_main(int argc, const char *argv[]) {
cl::ParseCommandLineOptions(argc, argv, "LLVM profile data overlap tool\n");
std::error_code EC;
- raw_fd_ostream OS(Output.data(), EC, sys::fs::OF_Text);
+ raw_fd_ostream OS(Output.data(), EC, sys::fs::OF_TextWithCRLF);
if (EC)
exitWithErrorCode(EC, Output);
@@ -1926,7 +2002,8 @@ static int overlap_main(int argc, const char *argv[]) {
return 0;
}
-typedef struct ValueSitesStats {
+namespace {
+struct ValueSitesStats {
ValueSitesStats()
: TotalNumValueSites(0), TotalNumValueSitesWithValueProfile(0),
TotalNumValues(0) {}
@@ -1934,7 +2011,8 @@ typedef struct ValueSitesStats {
uint64_t TotalNumValueSitesWithValueProfile;
uint64_t TotalNumValues;
std::vector<unsigned> ValueSitesHistogram;
-} ValueSitesStats;
+};
+} // namespace
static void traverseAllValueSites(const InstrProfRecord &Func, uint32_t VK,
ValueSitesStats &Stats, raw_fd_ostream &OS,
@@ -1991,7 +2069,7 @@ static int showInstrProfile(const std::string &Filename, bool ShowCounts,
bool ShowAllFunctions, bool ShowCS,
uint64_t ValueCutoff, bool OnlyListBelow,
const std::string &ShowFunction, bool TextFormat,
- raw_fd_ostream &OS) {
+ bool ShowBinaryIds, raw_fd_ostream &OS) {
auto ReaderOrErr = InstrProfReader::create(Filename);
std::vector<uint32_t> Cutoffs = std::move(DetailedSummaryCutoffs);
if (ShowDetailedSummary && Cutoffs.empty()) {
@@ -2173,6 +2251,11 @@ static int showInstrProfile(const std::string &Filename, bool ShowCounts,
OS << "Total count: " << PS->getTotalCount() << "\n";
PS->printDetailedSummary(OS);
}
+
+ if (ShowBinaryIds)
+ if (Error E = Reader->printBinaryIds(OS))
+ exitWithError(std::move(E), Filename);
+
return 0;
}
@@ -2307,9 +2390,9 @@ showHotFunctionList(const StringMap<sampleprof::FunctionSamples> &Profiles,
(ProfileTotalSample > 0)
? (Func.getTotalSamples() * 100.0) / ProfileTotalSample
: 0;
- PrintValues.emplace_back(
- HotFuncInfo(Func.getName(), Func.getTotalSamples(), TotalSamplePercent,
- FuncPair.second.second, Func.getEntrySamples()));
+ PrintValues.emplace_back(HotFuncInfo(
+ Func.getNameWithContext(), Func.getTotalSamples(), TotalSamplePercent,
+ FuncPair.second.second, Func.getEntrySamples()));
}
dumpHotFunctionList(ColumnTitle, ColumnOffset, PrintValues, HotFuncCount,
Profiles.size(), HotFuncSample, ProfileTotalSample,
@@ -2326,12 +2409,12 @@ static int showSampleProfile(const std::string &Filename, bool ShowCounts,
raw_fd_ostream &OS) {
using namespace sampleprof;
LLVMContext Context;
- auto ReaderOrErr = SampleProfileReader::create(Filename, Context);
+ auto ReaderOrErr =
+ SampleProfileReader::create(Filename, Context, FSDiscriminatorPassOption);
if (std::error_code EC = ReaderOrErr.getError())
exitWithErrorCode(EC, Filename);
auto Reader = std::move(ReaderOrErr.get());
-
if (ShowSectionInfoOnly) {
showSectionInfo(Reader.get(), OS);
return 0;
@@ -2423,12 +2506,11 @@ static int show_main(int argc, const char *argv[]) {
cl::desc("Show the information of each section in the sample profile. "
"The flag is only usable when the sample profile is in "
"extbinary format"));
+ cl::opt<bool> ShowBinaryIds("binary-ids", cl::init(false),
+ cl::desc("Show binary ids in the profile. "));
cl::ParseCommandLineOptions(argc, argv, "LLVM profile data summary\n");
- if (OutputFilename.empty())
- OutputFilename = "-";
-
if (Filename == OutputFilename) {
errs() << sys::path::filename(argv[0])
<< ": Input file name cannot be the same as the output file name!\n";
@@ -2436,7 +2518,7 @@ static int show_main(int argc, const char *argv[]) {
}
std::error_code EC;
- raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::OF_Text);
+ raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::OF_TextWithCRLF);
if (EC)
exitWithErrorCode(EC, OutputFilename);
@@ -2444,11 +2526,11 @@ static int show_main(int argc, const char *argv[]) {
WithColor::warning() << "-function argument ignored: showing all functions\n";
if (ProfileKind == instr)
- return showInstrProfile(Filename, ShowCounts, TopNFunctions,
- ShowIndirectCallTargets, ShowMemOPSizes,
- ShowDetailedSummary, DetailedSummaryCutoffs,
- ShowAllFunctions, ShowCS, ValueCutoff,
- OnlyListBelow, ShowFunction, TextFormat, OS);
+ return showInstrProfile(
+ Filename, ShowCounts, TopNFunctions, ShowIndirectCallTargets,
+ ShowMemOPSizes, ShowDetailedSummary, DetailedSummaryCutoffs,
+ ShowAllFunctions, ShowCS, ValueCutoff, OnlyListBelow, ShowFunction,
+ TextFormat, ShowBinaryIds, OS);
else
return showSampleProfile(Filename, ShowCounts, ShowAllFunctions,
ShowDetailedSummary, ShowFunction,
diff --git a/llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp b/llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp
index 5995a09514c8..99ee639fc45d 100644
--- a/llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp
+++ b/llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp
@@ -100,7 +100,7 @@ static std::string formatSymbol(StringRef Name, uint64_t Address,
OS << Name << " ";
if (Offset)
- OS << format("+0x%X (0x%" PRIX64 ")", Offset, Address);
+ OS << format("+0x%" PRIX64 " (0x%" PRIX64 ")", Offset, Address);
else if (!Name.empty())
OS << format("(0x%" PRIX64 ")", Address);
else
@@ -184,31 +184,16 @@ void Decoder::printRegisters(const std::pair<uint16_t, uint32_t> &RegisterMask)
const uint16_t VFPMask = std::get<1>(RegisterMask);
OS << '{';
- bool Comma = false;
- for (unsigned RI = 0, RE = 11; RI < RE; ++RI) {
- if (GPRMask & (1 << RI)) {
- if (Comma)
- OS << ", ";
- OS << GPRRegisterNames[RI];
- Comma = true;
- }
- }
- for (unsigned RI = 0, RE = 32; RI < RE; ++RI) {
- if (VFPMask & (1 << RI)) {
- if (Comma)
- OS << ", ";
- OS << "d" << unsigned(RI);
- Comma = true;
- }
- }
- for (unsigned RI = 11, RE = 16; RI < RE; ++RI) {
- if (GPRMask & (1 << RI)) {
- if (Comma)
- OS << ", ";
- OS << GPRRegisterNames[RI];
- Comma = true;
- }
- }
+ ListSeparator LS;
+ for (unsigned RI = 0, RE = 11; RI < RE; ++RI)
+ if (GPRMask & (1 << RI))
+ OS << LS << GPRRegisterNames[RI];
+ for (unsigned RI = 0, RE = 32; RI < RE; ++RI)
+ if (VFPMask & (1 << RI))
+ OS << LS << "d" << unsigned(RI);
+ for (unsigned RI = 11, RE = 16; RI < RE; ++RI)
+ if (GPRMask & (1 << RI))
+ OS << LS << GPRRegisterNames[RI];
OS << '}';
}
@@ -253,6 +238,65 @@ ErrorOr<SymbolRef> Decoder::getRelocatedSymbol(const COFFObjectFile &,
return inconvertibleErrorCode();
}
+SymbolRef Decoder::getPreferredSymbol(const COFFObjectFile &COFF,
+ SymbolRef Sym) {
+ // The symbol resolved by getRelocatedSymbol can be any internal
+ // nondescriptive symbol; try to resolve a more descriptive one.
+ COFFSymbolRef CoffSym = COFF.getCOFFSymbol(Sym);
+ if (CoffSym.getStorageClass() != COFF::IMAGE_SYM_CLASS_LABEL)
+ return Sym;
+ for (const auto &S : COFF.symbols()) {
+ COFFSymbolRef CS = COFF.getCOFFSymbol(S);
+ if (CS.getSectionNumber() == CoffSym.getSectionNumber() &&
+ CS.getValue() == CoffSym.getValue()) {
+ if (CS.isExternal())
+ return S;
+ if (CS.getStorageClass() != COFF::IMAGE_SYM_CLASS_LABEL) {
+ Sym = S;
+ CoffSym = CS;
+ }
+ }
+ }
+ return Sym;
+}
+
+ErrorOr<SymbolRef> Decoder::getSymbolForLocation(
+ const COFFObjectFile &COFF, const SectionRef &Section,
+ uint64_t OffsetInSection, uint64_t ImmediateOffset, uint64_t &SymbolAddress,
+ uint64_t &SymbolOffset, bool FunctionOnly) {
+ // Try to locate a relocation that points at the offset in the section
+ ErrorOr<SymbolRef> SymOrErr =
+ getRelocatedSymbol(COFF, Section, OffsetInSection);
+ if (SymOrErr) {
+ // We found a relocation symbol; the immediate offset needs to be added
+ // to the symbol address.
+ SymbolOffset = ImmediateOffset;
+
+ Expected<uint64_t> AddressOrErr = SymOrErr->getAddress();
+ if (!AddressOrErr) {
+ std::string Buf;
+ llvm::raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(AddressOrErr.takeError(), OS);
+ OS.flush();
+ report_fatal_error(Buf);
+ }
+ // We apply SymbolOffset here directly. We return it separately to allow
+ // the caller to print it as an offset on the symbol name.
+ SymbolAddress = *AddressOrErr + SymbolOffset;
+ } else {
+ // No matching relocation found; operating on a linked image. Try to
+ // find a descriptive symbol if possible. The immediate offset contains
+ // the image relative address, and we shouldn't add any offset to the
+ // symbol.
+ SymbolAddress = COFF.getImageBase() + ImmediateOffset;
+ SymbolOffset = 0;
+ SymOrErr = getSymbol(COFF, SymbolAddress, FunctionOnly);
+ }
+ if (SymOrErr && FunctionOnly) // Resolve label symbols into function names
+ SymOrErr = getPreferredSymbol(COFF, *SymOrErr);
+ return SymOrErr;
+}
+
bool Decoder::opcode_0xxxxxxx(const uint8_t *OC, unsigned &Offset,
unsigned Length, bool Prologue) {
uint8_t Imm = OC[Offset] & 0x7f;
@@ -934,16 +978,16 @@ bool Decoder::dumpXDataRecord(const COFFObjectFile &COFF,
}
if (XData.X()) {
- const uint64_t Address = COFF.getImageBase() + XData.ExceptionHandlerRVA();
const uint32_t Parameter = XData.ExceptionHandlerParameter();
- const size_t HandlerOffset = HeaderWords(XData)
- + (XData.E() ? 0 : XData.EpilogueCount())
- + XData.CodeWords();
-
- ErrorOr<SymbolRef> Symbol = getRelocatedSymbol(
- COFF, Section, Offset + HandlerOffset * sizeof(uint32_t));
- if (!Symbol)
- Symbol = getSymbol(COFF, Address, /*FunctionOnly=*/true);
+ const size_t HandlerOffset = HeaderWords(XData) +
+ (XData.E() ? 0 : XData.EpilogueCount()) +
+ XData.CodeWords();
+
+ uint64_t Address, SymbolOffset;
+ ErrorOr<SymbolRef> Symbol = getSymbolForLocation(
+ COFF, Section, Offset + HandlerOffset * sizeof(uint32_t),
+ XData.ExceptionHandlerRVA(), Address, SymbolOffset,
+ /*FunctionOnly=*/true);
if (!Symbol) {
ListScope EHS(SW, "ExceptionHandler");
SW.printHex("Routine", Address);
@@ -961,7 +1005,7 @@ bool Decoder::dumpXDataRecord(const COFFObjectFile &COFF,
}
ListScope EHS(SW, "ExceptionHandler");
- SW.printString("Routine", formatSymbol(*Name, Address));
+ SW.printString("Routine", formatSymbol(*Name, Address, SymbolOffset));
SW.printHex("Parameter", Parameter);
}
@@ -974,14 +1018,15 @@ bool Decoder::dumpUnpackedEntry(const COFFObjectFile &COFF,
assert(RF.Flag() == RuntimeFunctionFlag::RFF_Unpacked &&
"packed entry cannot be treated as an unpacked entry");
- ErrorOr<SymbolRef> Function = getRelocatedSymbol(COFF, Section, Offset);
- if (!Function)
- Function = getSymbol(COFF, COFF.getImageBase() + RF.BeginAddress,
- /*FunctionOnly=*/true);
+ uint64_t FunctionAddress, FunctionOffset;
+ ErrorOr<SymbolRef> Function = getSymbolForLocation(
+ COFF, Section, Offset, RF.BeginAddress, FunctionAddress, FunctionOffset,
+ /*FunctionOnly=*/true);
- ErrorOr<SymbolRef> XDataRecord = getRelocatedSymbol(COFF, Section, Offset + 4);
- if (!XDataRecord)
- XDataRecord = getSymbol(COFF, RF.ExceptionInformationRVA());
+ uint64_t XDataAddress, XDataOffset;
+ ErrorOr<SymbolRef> XDataRecord = getSymbolForLocation(
+ COFF, Section, Offset + 4, RF.ExceptionInformationRVA(), XDataAddress,
+ XDataOffset);
if (!RF.BeginAddress && !Function)
return false;
@@ -989,7 +1034,6 @@ bool Decoder::dumpUnpackedEntry(const COFFObjectFile &COFF,
return false;
StringRef FunctionName;
- uint64_t FunctionAddress;
if (Function) {
Expected<StringRef> FunctionNameOrErr = Function->getName();
if (!FunctionNameOrErr) {
@@ -1000,20 +1044,10 @@ bool Decoder::dumpUnpackedEntry(const COFFObjectFile &COFF,
report_fatal_error(Buf);
}
FunctionName = *FunctionNameOrErr;
- 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 {
- FunctionAddress = COFF.getImageBase() + RF.BeginAddress;
}
- SW.printString("Function", formatSymbol(FunctionName, FunctionAddress));
+ SW.printString("Function",
+ formatSymbol(FunctionName, FunctionAddress, FunctionOffset));
if (XDataRecord) {
Expected<StringRef> Name = XDataRecord->getName();
@@ -1025,17 +1059,8 @@ bool Decoder::dumpUnpackedEntry(const COFFObjectFile &COFF,
report_fatal_error(Buf);
}
- 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));
+ SW.printString("ExceptionRecord",
+ formatSymbol(*Name, XDataAddress, XDataOffset));
Expected<section_iterator> SIOrErr = XDataRecord->getSection();
if (!SIOrErr) {
@@ -1045,18 +1070,15 @@ bool Decoder::dumpUnpackedEntry(const COFFObjectFile &COFF,
}
section_iterator SI = *SIOrErr;
- // FIXME: Do we need to add an offset from the relocation?
- return dumpXDataRecord(COFF, *SI, FunctionAddress,
- RF.ExceptionInformationRVA());
+ return dumpXDataRecord(COFF, *SI, FunctionAddress, XDataAddress);
} else {
- uint64_t Address = COFF.getImageBase() + RF.ExceptionInformationRVA();
- SW.printString("ExceptionRecord", formatSymbol("", Address));
+ SW.printString("ExceptionRecord", formatSymbol("", XDataAddress));
- ErrorOr<SectionRef> Section = getSectionContaining(COFF, Address);
+ ErrorOr<SectionRef> Section = getSectionContaining(COFF, XDataAddress);
if (!Section)
return false;
- return dumpXDataRecord(COFF, *Section, FunctionAddress, Address);
+ return dumpXDataRecord(COFF, *Section, FunctionAddress, XDataAddress);
}
}
@@ -1067,12 +1089,12 @@ bool Decoder::dumpPackedEntry(const object::COFFObjectFile &COFF,
RF.Flag() == RuntimeFunctionFlag::RFF_PackedFragment) &&
"unpacked entry cannot be treated as a packed entry");
- ErrorOr<SymbolRef> Function = getRelocatedSymbol(COFF, Section, Offset);
- if (!Function)
- Function = getSymbol(COFF, RF.BeginAddress, /*FunctionOnly=*/true);
+ uint64_t FunctionAddress, FunctionOffset;
+ ErrorOr<SymbolRef> Function = getSymbolForLocation(
+ COFF, Section, Offset, RF.BeginAddress, FunctionAddress, FunctionOffset,
+ /*FunctionOnly=*/true);
StringRef FunctionName;
- uint64_t FunctionAddress;
if (Function) {
Expected<StringRef> FunctionNameOrErr = Function->getName();
if (!FunctionNameOrErr) {
@@ -1083,20 +1105,10 @@ bool Decoder::dumpPackedEntry(const object::COFFObjectFile &COFF,
report_fatal_error(Buf);
}
FunctionName = *FunctionNameOrErr;
- 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 {
- FunctionAddress = COFF.getPE32Header()->ImageBase + RF.BeginAddress;
}
- SW.printString("Function", formatSymbol(FunctionName, FunctionAddress));
+ SW.printString("Function",
+ formatSymbol(FunctionName, FunctionAddress, FunctionOffset));
if (!isAArch64)
SW.printBoolean("Fragment",
RF.Flag() == RuntimeFunctionFlag::RFF_PackedFragment);
@@ -1119,12 +1131,12 @@ bool Decoder::dumpPackedARM64Entry(const object::COFFObjectFile &COFF,
RF.Flag() == RuntimeFunctionFlag::RFF_PackedFragment) &&
"unpacked entry cannot be treated as a packed entry");
- ErrorOr<SymbolRef> Function = getRelocatedSymbol(COFF, Section, Offset);
- if (!Function)
- Function = getSymbol(COFF, RF.BeginAddress, /*FunctionOnly=*/true);
+ uint64_t FunctionAddress, FunctionOffset;
+ ErrorOr<SymbolRef> Function = getSymbolForLocation(
+ COFF, Section, Offset, RF.BeginAddress, FunctionAddress, FunctionOffset,
+ /*FunctionOnly=*/true);
StringRef FunctionName;
- uint64_t FunctionAddress;
if (Function) {
Expected<StringRef> FunctionNameOrErr = Function->getName();
if (!FunctionNameOrErr) {
@@ -1135,20 +1147,10 @@ bool Decoder::dumpPackedARM64Entry(const object::COFFObjectFile &COFF,
report_fatal_error(Buf);
}
FunctionName = *FunctionNameOrErr;
- 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 {
- FunctionAddress = COFF.getPE32PlusHeader()->ImageBase + RF.BeginAddress;
}
- SW.printString("Function", formatSymbol(FunctionName, FunctionAddress));
+ SW.printString("Function",
+ formatSymbol(FunctionName, FunctionAddress, FunctionOffset));
SW.printBoolean("Fragment",
RF.Flag() == RuntimeFunctionFlag::RFF_PackedFragment);
SW.printNumber("FunctionLength", RF.FunctionLength());
diff --git a/llvm/tools/llvm-readobj/ARMWinEHPrinter.h b/llvm/tools/llvm-readobj/ARMWinEHPrinter.h
index 3263841a267b..efe16850c7fa 100644
--- a/llvm/tools/llvm-readobj/ARMWinEHPrinter.h
+++ b/llvm/tools/llvm-readobj/ARMWinEHPrinter.h
@@ -146,6 +146,16 @@ class Decoder {
getRelocatedSymbol(const object::COFFObjectFile &COFF,
const object::SectionRef &Section, uint64_t Offset);
+ ErrorOr<object::SymbolRef>
+ getSymbolForLocation(const object::COFFObjectFile &COFF,
+ const object::SectionRef &Section,
+ uint64_t OffsetInSection, uint64_t ImmediateOffset,
+ uint64_t &SymbolAddress, uint64_t &SymbolOffset,
+ bool FunctionOnly = false);
+
+ object::SymbolRef getPreferredSymbol(const object::COFFObjectFile &COFF,
+ object::SymbolRef Sym);
+
bool dumpXDataRecord(const object::COFFObjectFile &COFF,
const object::SectionRef &Section,
uint64_t FunctionAddress, uint64_t VA);
diff --git a/llvm/tools/llvm-readobj/COFFDumper.cpp b/llvm/tools/llvm-readobj/COFFDumper.cpp
index 684967f93393..96124cc03484 100644
--- a/llvm/tools/llvm-readobj/COFFDumper.cpp
+++ b/llvm/tools/llvm-readobj/COFFDumper.cpp
@@ -71,6 +71,8 @@ struct LoadConfigTables {
uint64_t GuardIatTableCount = 0;
uint64_t GuardLJmpTableVA = 0;
uint64_t GuardLJmpTableCount = 0;
+ uint64_t GuardEHContTableVA = 0;
+ uint64_t GuardEHContTableCount = 0;
};
class COFFDumper : public ObjDumper {
@@ -593,8 +595,7 @@ void COFFDumper::cacheRelocations() {
for (const SectionRef &S : Obj->sections()) {
const coff_section *Section = Obj->getCOFFSection(S);
- for (const RelocationRef &Reloc : S.relocations())
- RelocMap[Section].push_back(Reloc);
+ append_range(RelocMap[Section], S.relocations());
// Sort relocations by address.
llvm::sort(RelocMap[Section], [](RelocationRef L, RelocationRef R) {
@@ -794,19 +795,19 @@ void COFFDumper::printCOFFLoadConfig() {
printRVATable(Tables.SEHTableVA, Tables.SEHTableCount, 4);
}
+ auto PrintGuardFlags = [](raw_ostream &OS, const uint8_t *Entry) {
+ uint8_t Flags = *reinterpret_cast<const uint8_t *>(Entry + 4);
+ if (Flags)
+ OS << " flags " << utohexstr(Flags);
+ };
+
if (Tables.GuardFidTableVA) {
ListScope LS(W, "GuardFidTable");
- if (Tables.GuardFlags & uint32_t(coff_guard_flags::FidTableHasFlags)) {
- auto PrintGuardFlags = [](raw_ostream &OS, const uint8_t *Entry) {
- uint8_t Flags = *reinterpret_cast<const uint8_t *>(Entry + 4);
- if (Flags)
- OS << " flags " << utohexstr(Flags);
- };
+ if (Tables.GuardFlags & uint32_t(coff_guard_flags::FidTableHasFlags))
printRVATable(Tables.GuardFidTableVA, Tables.GuardFidTableCount, 5,
PrintGuardFlags);
- } else {
+ else
printRVATable(Tables.GuardFidTableVA, Tables.GuardFidTableCount, 4);
- }
}
if (Tables.GuardIatTableVA) {
@@ -818,6 +819,12 @@ void COFFDumper::printCOFFLoadConfig() {
ListScope LS(W, "GuardLJmpTable");
printRVATable(Tables.GuardLJmpTableVA, Tables.GuardLJmpTableCount, 4);
}
+
+ if (Tables.GuardEHContTableVA) {
+ ListScope LS(W, "GuardEHContTable");
+ printRVATable(Tables.GuardEHContTableVA, Tables.GuardEHContTableCount, 5,
+ PrintGuardFlags);
+ }
}
template <typename T>
@@ -876,8 +883,8 @@ void COFFDumper::printCOFFLoadConfig(const T *Conf, LoadConfigTables &Tables) {
Tables.GuardFidTableCount = Conf->GuardCFFunctionCount;
Tables.GuardFlags = Conf->GuardFlags;
- // Print the rest. (2017)
- if (Conf->Size < sizeof(T))
+ // Print everything before Reserved3. (2017)
+ if (Conf->Size < offsetof(T, Reserved3))
return;
W.printHex("GuardAddressTakenIatEntryTable",
Conf->GuardAddressTakenIatEntryTable);
@@ -903,6 +910,17 @@ void COFFDumper::printCOFFLoadConfig(const T *Conf, LoadConfigTables &Tables) {
Tables.GuardLJmpTableVA = Conf->GuardLongJumpTargetTable;
Tables.GuardLJmpTableCount = Conf->GuardLongJumpTargetCount;
+
+ // Print the rest. (2019)
+ if (Conf->Size < sizeof(T))
+ return;
+ W.printHex("EnclaveConfigurationPointer", Conf->EnclaveConfigurationPointer);
+ W.printHex("VolatileMetadataPointer", Conf->VolatileMetadataPointer);
+ W.printHex("GuardEHContinuationTable", Conf->GuardEHContinuationTable);
+ W.printNumber("GuardEHContinuationCount", Conf->GuardEHContinuationCount);
+
+ Tables.GuardEHContTableVA = Conf->GuardEHContinuationTable;
+ Tables.GuardEHContTableCount = Conf->GuardEHContinuationCount;
}
void COFFDumper::printBaseOfDataField(const pe32_header *Hdr) {
@@ -1856,7 +1874,7 @@ void COFFDumper::printResourceDirectoryTable(
OS << ": (ID " << Entry.Identifier.ID << ")";
}
}
- Name = StringRef(IDStr);
+ Name = IDStr;
ListScope ResourceType(W, Level.str() + Name.str());
if (Entry.Offset.isSubDir()) {
W.printHex("Table Offset", Entry.Offset.value());
diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 0f508f8dc0f2..f221acba979a 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -298,6 +298,13 @@ protected:
std::vector<GroupSection> getGroups();
+ // Returns the function symbol index for the given address. Matches the
+ // symbol's section with FunctionSec when specified.
+ // Returns None if no function symbol can be found for the address or in case
+ // it is not defined in the specified section.
+ SmallVector<uint32_t>
+ getSymbolIndexesForFunctionAddress(uint64_t SymValue,
+ Optional<const Elf_Shdr *> FunctionSec);
bool printFunctionStackSize(uint64_t SymValue,
Optional<const Elf_Shdr *> FunctionSec,
const Elf_Shdr &StackSizeSec, DataExtractor Data,
@@ -306,11 +313,18 @@ protected:
unsigned Ndx, const Elf_Shdr *SymTab,
const Elf_Shdr *FunctionSec, const Elf_Shdr &StackSizeSec,
const RelocationResolver &Resolver, DataExtractor Data);
- virtual void printStackSizeEntry(uint64_t Size, StringRef FuncName) = 0;
+ virtual void printStackSizeEntry(uint64_t Size,
+ ArrayRef<std::string> FuncNames) = 0;
void printRelocatableStackSizes(std::function<void()> PrintHeader);
void printNonRelocatableStackSizes(std::function<void()> PrintHeader);
+ /// Retrieves sections with corresponding relocation sections based on
+ /// IsMatch.
+ void getSectionAndRelocations(
+ std::function<bool(const Elf_Shdr &)> IsMatch,
+ llvm::MapVector<const Elf_Shdr *, const Elf_Shdr *> &SecToRelocMap);
+
const object::ELFObjectFile<ELFT> &ObjF;
const ELFFile<ELFT> &Obj;
StringRef FileName;
@@ -349,10 +363,10 @@ protected:
const Elf_GnuHash *GnuHashTable = nullptr;
const Elf_Shdr *DotSymtabSec = nullptr;
const Elf_Shdr *DotDynsymSec = nullptr;
- const Elf_Shdr *DotCGProfileSec = nullptr;
const Elf_Shdr *DotAddrsigSec = nullptr;
DenseMap<const Elf_Shdr *, ArrayRef<Elf_Word>> ShndxTables;
Optional<uint64_t> SONameOffset;
+ Optional<DenseMap<uint64_t, std::vector<uint32_t>>> AddressToIndexMap;
const Elf_Shdr *SymbolVersionSection = nullptr; // .gnu.version
const Elf_Shdr *SymbolVersionNeedSection = nullptr; // .gnu.version_r
@@ -550,6 +564,7 @@ public:
void printVersionDependencySection(const Elf_Shdr *Sec) override;
void printHashHistograms() override;
void printCGProfile() override;
+ void printBBAddrMaps() override;
void printAddrsig() override;
void printNotes() override;
void printELFLinkerOptions() override;
@@ -631,7 +646,8 @@ private:
void printGNUVersionSectionProlog(const typename ELFT::Shdr &Sec,
const Twine &Label, unsigned EntriesNum);
- void printStackSizeEntry(uint64_t Size, StringRef FuncName) override;
+ void printStackSizeEntry(uint64_t Size,
+ ArrayRef<std::string> FuncNames) override;
void printMipsGOT(const MipsGOTParser<ELFT> &Parser) override;
void printMipsPLT(const MipsGOTParser<ELFT> &Parser) override;
@@ -660,6 +676,7 @@ public:
void printVersionDependencySection(const Elf_Shdr *Sec) override;
void printHashHistograms() override;
void printCGProfile() override;
+ void printBBAddrMaps() override;
void printAddrsig() override;
void printNotes() override;
void printELFLinkerOptions() override;
@@ -678,7 +695,8 @@ private:
bool /*NonVisibilityBitsUsed*/) const override;
void printProgramHeaders() override;
void printSectionMapping() override {}
- void printStackSizeEntry(uint64_t Size, StringRef FuncName) override;
+ void printStackSizeEntry(uint64_t Size,
+ ArrayRef<std::string> FuncNames) override;
void printMipsGOT(const MipsGOTParser<ELFT> &Parser) override;
void printMipsPLT(const MipsGOTParser<ELFT> &Parser) override;
@@ -1138,7 +1156,7 @@ static const EnumEntry<unsigned> ElfMachineType[] = {
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_CR16, "National Semiconductor CompactRISC 16-bit processor"),
ENUM_ENT(EM_ETPU, "Freescale Extended Time Processing Unit"),
ENUM_ENT(EM_SLE9X, "Infineon Technologies SLE9X core"),
ENUM_ENT(EM_L10M, "EM_L10M"),
@@ -1148,6 +1166,7 @@ static const EnumEntry<unsigned> ElfMachineType[] = {
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_MICROBLAZE, "Xilinx MicroBlaze 32-bit RISC soft processor core"),
ENUM_ENT(EM_CUDA, "NVIDIA CUDA architecture"),
ENUM_ENT(EM_TILEGX, "Tilera TILE-Gx multicore architecture family"),
ENUM_ENT(EM_CLOUDSHIELD, "EM_CLOUDSHIELD"),
@@ -1201,6 +1220,7 @@ static const EnumEntry<unsigned> ElfSectionFlags[] = {
ENUM_ENT(SHF_GROUP, "G"),
ENUM_ENT(SHF_TLS, "T"),
ENUM_ENT(SHF_COMPRESSED, "C"),
+ ENUM_ENT(SHF_GNU_RETAIN, "R"),
ENUM_ENT(SHF_EXCLUDE, "E"),
};
@@ -1427,7 +1447,7 @@ static const EnumEntry<unsigned> ElfHeaderMipsFlags[] = {
ENUM_ENT(EF_MIPS_ARCH_64R6, "mips64r6")
};
-static const EnumEntry<unsigned> ElfHeaderAMDGPUFlags[] = {
+static const EnumEntry<unsigned> ElfHeaderAMDGPUFlagsABIVersion3[] = {
LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_NONE),
LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_R600),
LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_R630),
@@ -1465,16 +1485,78 @@ static const EnumEntry<unsigned> ElfHeaderAMDGPUFlags[] = {
LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX906),
LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX908),
LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX909),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX90A),
LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX90C),
LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1010),
LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1011),
LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1012),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1013),
LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1030),
LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1031),
LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1032),
LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1033),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_XNACK),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_SRAM_ECC)
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1034),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1035),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_FEATURE_XNACK_V3),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_FEATURE_SRAMECC_V3)
+};
+
+static const EnumEntry<unsigned> ElfHeaderAMDGPUFlagsABIVersion4[] = {
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_NONE),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_R600),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_R630),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_RS880),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_RV670),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_RV710),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_RV730),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_RV770),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_CEDAR),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_CYPRESS),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_JUNIPER),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_REDWOOD),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_SUMO),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_BARTS),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_CAICOS),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_CAYMAN),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_TURKS),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX600),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX601),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX602),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX700),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX701),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX702),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX703),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX704),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX705),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX801),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX802),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX803),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX805),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX810),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX900),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX902),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX904),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX906),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX908),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX909),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX90A),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX90C),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1010),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1011),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1012),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1013),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1030),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1031),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1032),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1033),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1034),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1035),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_FEATURE_XNACK_ANY_V4),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_FEATURE_XNACK_OFF_V4),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_FEATURE_XNACK_ON_V4),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_FEATURE_SRAMECC_ANY_V4),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_FEATURE_SRAMECC_OFF_V4),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_FEATURE_SRAMECC_ON_V4)
};
static const EnumEntry<unsigned> ElfHeaderRISCVFlags[] = {
@@ -1485,6 +1567,29 @@ static const EnumEntry<unsigned> ElfHeaderRISCVFlags[] = {
ENUM_ENT(EF_RISCV_RVE, "RVE")
};
+static const EnumEntry<unsigned> ElfHeaderAVRFlags[] = {
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_AVR1),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_AVR2),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_AVR25),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_AVR3),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_AVR31),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_AVR35),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_AVR4),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_AVR5),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_AVR51),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_AVR6),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_AVRTINY),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_XMEGA1),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_XMEGA2),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_XMEGA3),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_XMEGA4),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_XMEGA5),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_XMEGA6),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_XMEGA7),
+ ENUM_ENT(EF_AVR_LINKRELAX_PREPARED, "relaxable"),
+};
+
+
static const EnumEntry<unsigned> ElfSymOtherFlags[] = {
LLVM_READOBJ_ENUM_ENT(ELF, STV_INTERNAL),
LLVM_READOBJ_ENUM_ENT(ELF, STV_HIDDEN),
@@ -1743,10 +1848,6 @@ ELFDumper<ELFT>::ELFDumper(const object::ELFObjectFile<ELFT> &O,
if (!SymbolVersionNeedSection)
SymbolVersionNeedSection = &Sec;
break;
- case ELF::SHT_LLVM_CALL_GRAPH_PROFILE:
- if (!DotCGProfileSec)
- DotCGProfileSec = &Sec;
- break;
case ELF::SHT_LLVM_ADDRSIG:
if (!DotAddrsigSec)
DotAddrsigSec = &Sec;
@@ -3170,6 +3271,9 @@ template <class ELFT> void GNUELFDumper<ELFT>::printFileHeaders() {
unsigned(ELF::EF_MIPS_MACH));
else if (e.e_machine == EM_RISCV)
ElfFlags = printFlags(e.e_flags, makeArrayRef(ElfHeaderRISCVFlags));
+ else if (e.e_machine == EM_AVR)
+ ElfFlags = printFlags(e.e_flags, makeArrayRef(ElfHeaderAVRFlags),
+ unsigned(ELF::EF_AVR_ARCH_MASK));
Str = "0x" + to_hexString(e.e_flags);
if (!ElfFlags.empty())
Str = Str + ", " + ElfFlags;
@@ -3483,15 +3587,14 @@ static void printSectionDescription(formatted_raw_ostream &OS,
OS << " L (link order), O (extra OS processing required), G (group), T "
"(TLS),\n";
OS << " C (compressed), x (unknown), o (OS specific), E (exclude),\n";
+ OS << " R (retain)";
if (EMachine == EM_X86_64)
- OS << " l (large), ";
+ OS << ", l (large)";
else if (EMachine == EM_ARM)
- OS << " y (purecode), ";
- else
- OS << " ";
+ OS << ", y (purecode)";
- OS << "p (processor specific)\n";
+ OS << ", p (processor specific)\n";
}
template <class ELFT> void GNUELFDumper<ELFT>::printSectionHeaders() {
@@ -3934,21 +4037,17 @@ template <class ELFT> void GNUELFDumper<ELFT>::printSectionDetails() {
uint64_t Flags = S.sh_flags;
uint64_t UnknownFlags = 0;
- bool NeedsComma = false;
+ ListSeparator LS;
while (Flags) {
// Take the least significant bit as a flag.
uint64_t Flag = Flags & -Flags;
Flags -= Flag;
auto It = FlagToName.find(Flag);
- if (It != FlagToName.end()) {
- if (NeedsComma)
- OS << ", ";
- NeedsComma = true;
- OS << It->second;
- } else {
+ if (It != FlagToName.end())
+ OS << LS << It->second;
+ else
UnknownFlags |= Flag;
- }
}
auto PrintUnknownFlags = [&](uint64_t Mask, StringRef Name) {
@@ -3956,12 +4055,9 @@ template <class ELFT> void GNUELFDumper<ELFT>::printSectionDetails() {
if (!FlagsToPrint)
return;
- if (NeedsComma)
- OS << ", ";
- OS << Name << " ("
+ OS << LS << Name << " ("
<< to_string(format_hex_no_prefix(FlagsToPrint, AddrSize)) << ")";
UnknownFlags &= ~Mask;
- NeedsComma = true;
};
PrintUnknownFlags(SHF_MASKOS, "OS");
@@ -4623,6 +4719,10 @@ template <class ELFT> void GNUELFDumper<ELFT>::printCGProfile() {
OS << "GNUStyle::printCGProfile not implemented\n";
}
+template <class ELFT> void GNUELFDumper<ELFT>::printBBAddrMaps() {
+ OS << "GNUStyle::printBBAddrMaps not implemented\n";
+}
+
static Expected<std::vector<uint64_t>> toULEB128Array(ArrayRef<uint8_t> Data) {
std::vector<uint64_t> Ret;
const uint8_t *Cur = Data.begin();
@@ -4736,47 +4836,6 @@ static std::string getGNUProperty(uint32_t Type, uint32_t DataSize,
if (PrData)
OS << format("<unknown flags: 0x%x>", PrData);
return OS.str();
- case GNU_PROPERTY_X86_ISA_1_NEEDED:
- case GNU_PROPERTY_X86_ISA_1_USED:
- OS << "x86 ISA "
- << (Type == GNU_PROPERTY_X86_ISA_1_NEEDED ? "needed: " : "used: ");
- if (DataSize != 4) {
- OS << format("<corrupt length: 0x%x>", DataSize);
- return OS.str();
- }
- PrData = support::endian::read32<ELFT::TargetEndianness>(Data.data());
- if (PrData == 0) {
- OS << "<None>";
- return OS.str();
- }
- DumpBit(GNU_PROPERTY_X86_ISA_1_CMOV, "CMOV");
- DumpBit(GNU_PROPERTY_X86_ISA_1_SSE, "SSE");
- DumpBit(GNU_PROPERTY_X86_ISA_1_SSE2, "SSE2");
- DumpBit(GNU_PROPERTY_X86_ISA_1_SSE3, "SSE3");
- DumpBit(GNU_PROPERTY_X86_ISA_1_SSSE3, "SSSE3");
- DumpBit(GNU_PROPERTY_X86_ISA_1_SSE4_1, "SSE4_1");
- DumpBit(GNU_PROPERTY_X86_ISA_1_SSE4_2, "SSE4_2");
- DumpBit(GNU_PROPERTY_X86_ISA_1_AVX, "AVX");
- DumpBit(GNU_PROPERTY_X86_ISA_1_AVX2, "AVX2");
- DumpBit(GNU_PROPERTY_X86_ISA_1_FMA, "FMA");
- DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512F, "AVX512F");
- DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512CD, "AVX512CD");
- DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512ER, "AVX512ER");
- DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512PF, "AVX512PF");
- DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512VL, "AVX512VL");
- DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512DQ, "AVX512DQ");
- DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512BW, "AVX512BW");
- DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512_4FMAPS, "AVX512_4FMAPS");
- DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512_4VNNIW, "AVX512_4VNNIW");
- DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512_BITALG, "AVX512_BITALG");
- DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512_IFMA, "AVX512_IFMA");
- DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512_VBMI, "AVX512_VBMI");
- DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512_VBMI2, "AVX512_VBMI2");
- DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512_VNNI, "AVX512_VNNI");
- if (PrData)
- OS << format("<unknown flags: 0x%x>", PrData);
- return OS.str();
- break;
case GNU_PROPERTY_X86_FEATURE_2_NEEDED:
case GNU_PROPERTY_X86_FEATURE_2_USED:
OS << "x86 feature "
@@ -4803,6 +4862,26 @@ static std::string getGNUProperty(uint32_t Type, uint32_t DataSize,
if (PrData)
OS << format("<unknown flags: 0x%x>", PrData);
return OS.str();
+ case GNU_PROPERTY_X86_ISA_1_NEEDED:
+ case GNU_PROPERTY_X86_ISA_1_USED:
+ OS << "x86 ISA "
+ << (Type == GNU_PROPERTY_X86_ISA_1_NEEDED ? "needed: " : "used: ");
+ if (DataSize != 4) {
+ OS << format("<corrupt length: 0x%x>", DataSize);
+ return OS.str();
+ }
+ PrData = support::endian::read32<ELFT::TargetEndianness>(Data.data());
+ if (PrData == 0) {
+ OS << "<None>";
+ return OS.str();
+ }
+ DumpBit(GNU_PROPERTY_X86_ISA_1_BASELINE, "x86-64-baseline");
+ DumpBit(GNU_PROPERTY_X86_ISA_1_V2, "x86-64-v2");
+ DumpBit(GNU_PROPERTY_X86_ISA_1_V3, "x86-64-v3");
+ DumpBit(GNU_PROPERTY_X86_ISA_1_V4, "x86-64-v4");
+ if (PrData)
+ OS << format("<unknown flags: 0x%x>", PrData);
+ return OS.str();
}
}
@@ -4877,11 +4956,12 @@ static StringRef getGNUGoldVersion(ArrayRef<uint8_t> Desc) {
}
template <typename ELFT>
-static void printGNUNote(raw_ostream &OS, uint32_t NoteType,
+static bool printGNUNote(raw_ostream &OS, uint32_t NoteType,
ArrayRef<uint8_t> Desc) {
+ // Return true if we were able to pretty-print the note, false otherwise.
switch (NoteType) {
default:
- return;
+ return false;
case ELF::NT_GNU_ABI_TAG: {
const GNUAbiTag &AbiTag = getGNUAbiTag<ELFT>(Desc);
if (!AbiTag.IsValid)
@@ -4904,6 +4984,54 @@ static void printGNUNote(raw_ostream &OS, uint32_t NoteType,
break;
}
OS << '\n';
+ return true;
+}
+
+static const EnumEntry<unsigned> FreeBSDFeatureCtlFlags[] = {
+ {"ASLR_DISABLE", NT_FREEBSD_FCTL_ASLR_DISABLE},
+ {"PROTMAX_DISABLE", NT_FREEBSD_FCTL_PROTMAX_DISABLE},
+ {"STKGAP_DISABLE", NT_FREEBSD_FCTL_STKGAP_DISABLE},
+ {"WXNEEDED", NT_FREEBSD_FCTL_WXNEEDED},
+ {"LA48", NT_FREEBSD_FCTL_LA48},
+ {"ASG_DISABLE", NT_FREEBSD_FCTL_ASG_DISABLE},
+};
+
+struct FreeBSDNote {
+ std::string Type;
+ std::string Value;
+};
+
+template <typename ELFT>
+static Optional<FreeBSDNote>
+getFreeBSDNote(uint32_t NoteType, ArrayRef<uint8_t> Desc, bool IsCore) {
+ if (IsCore)
+ return None; // No pretty-printing yet.
+ switch (NoteType) {
+ case ELF::NT_FREEBSD_ABI_TAG:
+ if (Desc.size() != 4)
+ return None;
+ return FreeBSDNote{
+ "ABI tag",
+ utostr(support::endian::read32<ELFT::TargetEndianness>(Desc.data()))};
+ case ELF::NT_FREEBSD_ARCH_TAG:
+ return FreeBSDNote{"Arch tag", toStringRef(Desc).str()};
+ case ELF::NT_FREEBSD_FEATURE_CTL: {
+ if (Desc.size() != 4)
+ return None;
+ unsigned Value =
+ support::endian::read32<ELFT::TargetEndianness>(Desc.data());
+ std::string FlagsStr;
+ raw_string_ostream OS(FlagsStr);
+ printFlags(Value, makeArrayRef(FreeBSDFeatureCtlFlags), OS);
+ if (OS.str().empty())
+ OS << "0x" << utohexstr(Value);
+ else
+ OS << "(0x" << utohexstr(Value) << ")";
+ return FreeBSDNote{"Feature flags", OS.str()};
+ }
+ default:
+ return None;
+ }
}
struct AMDNote {
@@ -4916,15 +5044,98 @@ static AMDNote getAMDNote(uint32_t NoteType, ArrayRef<uint8_t> Desc) {
switch (NoteType) {
default:
return {"", ""};
- case ELF::NT_AMD_AMDGPU_HSA_METADATA:
+ case ELF::NT_AMD_HSA_CODE_OBJECT_VERSION: {
+ struct CodeObjectVersion {
+ uint32_t MajorVersion;
+ uint32_t MinorVersion;
+ };
+ if (Desc.size() != sizeof(CodeObjectVersion))
+ return {"AMD HSA Code Object Version",
+ "Invalid AMD HSA Code Object Version"};
+ std::string VersionString;
+ raw_string_ostream StrOS(VersionString);
+ auto Version = reinterpret_cast<const CodeObjectVersion *>(Desc.data());
+ StrOS << "[Major: " << Version->MajorVersion
+ << ", Minor: " << Version->MinorVersion << "]";
+ return {"AMD HSA Code Object Version", VersionString};
+ }
+ case ELF::NT_AMD_HSA_HSAIL: {
+ struct HSAILProperties {
+ uint32_t HSAILMajorVersion;
+ uint32_t HSAILMinorVersion;
+ uint8_t Profile;
+ uint8_t MachineModel;
+ uint8_t DefaultFloatRound;
+ };
+ if (Desc.size() != sizeof(HSAILProperties))
+ return {"AMD HSA HSAIL Properties", "Invalid AMD HSA HSAIL Properties"};
+ auto Properties = reinterpret_cast<const HSAILProperties *>(Desc.data());
+ std::string HSAILPropetiesString;
+ raw_string_ostream StrOS(HSAILPropetiesString);
+ StrOS << "[HSAIL Major: " << Properties->HSAILMajorVersion
+ << ", HSAIL Minor: " << Properties->HSAILMinorVersion
+ << ", Profile: " << uint32_t(Properties->Profile)
+ << ", Machine Model: " << uint32_t(Properties->MachineModel)
+ << ", Default Float Round: "
+ << uint32_t(Properties->DefaultFloatRound) << "]";
+ return {"AMD HSA HSAIL Properties", HSAILPropetiesString};
+ }
+ case ELF::NT_AMD_HSA_ISA_VERSION: {
+ struct IsaVersion {
+ uint16_t VendorNameSize;
+ uint16_t ArchitectureNameSize;
+ uint32_t Major;
+ uint32_t Minor;
+ uint32_t Stepping;
+ };
+ if (Desc.size() < sizeof(IsaVersion))
+ return {"AMD HSA ISA Version", "Invalid AMD HSA ISA Version"};
+ auto Isa = reinterpret_cast<const IsaVersion *>(Desc.data());
+ if (Desc.size() < sizeof(IsaVersion) +
+ Isa->VendorNameSize + Isa->ArchitectureNameSize ||
+ Isa->VendorNameSize == 0 || Isa->ArchitectureNameSize == 0)
+ return {"AMD HSA ISA Version", "Invalid AMD HSA ISA Version"};
+ std::string IsaString;
+ raw_string_ostream StrOS(IsaString);
+ StrOS << "[Vendor: "
+ << StringRef((const char*)Desc.data() + sizeof(IsaVersion), Isa->VendorNameSize - 1)
+ << ", Architecture: "
+ << StringRef((const char*)Desc.data() + sizeof(IsaVersion) + Isa->VendorNameSize,
+ Isa->ArchitectureNameSize - 1)
+ << ", Major: " << Isa->Major << ", Minor: " << Isa->Minor
+ << ", Stepping: " << Isa->Stepping << "]";
+ return {"AMD HSA ISA Version", IsaString};
+ }
+ case ELF::NT_AMD_HSA_METADATA: {
+ if (Desc.size() == 0)
+ return {"AMD HSA Metadata", ""};
return {
- "HSA Metadata",
- std::string(reinterpret_cast<const char *>(Desc.data()), Desc.size())};
- case ELF::NT_AMD_AMDGPU_ISA:
+ "AMD HSA Metadata",
+ std::string(reinterpret_cast<const char *>(Desc.data()), Desc.size() - 1)};
+ }
+ case ELF::NT_AMD_HSA_ISA_NAME: {
+ if (Desc.size() == 0)
+ return {"AMD HSA ISA Name", ""};
return {
- "ISA Version",
+ "AMD HSA ISA Name",
std::string(reinterpret_cast<const char *>(Desc.data()), Desc.size())};
}
+ case ELF::NT_AMD_PAL_METADATA: {
+ struct PALMetadata {
+ uint32_t Key;
+ uint32_t Value;
+ };
+ if (Desc.size() % sizeof(PALMetadata) != 0)
+ return {"AMD PAL Metadata", "Invalid AMD PAL Metadata"};
+ auto Isa = reinterpret_cast<const PALMetadata *>(Desc.data());
+ std::string MetadataString;
+ raw_string_ostream StrOS(MetadataString);
+ for (size_t I = 0, E = Desc.size() / sizeof(PALMetadata); I < E; ++I) {
+ StrOS << "[" << Isa[I].Key << ": " << Isa[I].Value << "]";
+ }
+ return {"AMD PAL Metadata", MetadataString};
+ }
+ }
}
struct AMDGPUNote {
@@ -4942,16 +5153,22 @@ static AMDGPUNote getAMDGPUNote(uint32_t NoteType, ArrayRef<uint8_t> Desc) {
StringRef(reinterpret_cast<const char *>(Desc.data()), Desc.size());
msgpack::Document MsgPackDoc;
if (!MsgPackDoc.readFromBlob(MsgPackString, /*Multi=*/false))
- return {"AMDGPU Metadata", "Invalid AMDGPU Metadata"};
+ return {"", ""};
AMDGPU::HSAMD::V3::MetadataVerifier Verifier(true);
- std::string HSAMetadataString;
+ std::string MetadataString;
if (!Verifier.verify(MsgPackDoc.getRoot()))
- HSAMetadataString = "Invalid AMDGPU Metadata\n";
-
- raw_string_ostream StrOS(HSAMetadataString);
+ MetadataString = "Invalid AMDGPU Metadata\n";
+
+ raw_string_ostream StrOS(MetadataString);
+ if (MsgPackDoc.getRoot().isScalar()) {
+ // TODO: passing a scalar root to toYAML() asserts:
+ // (PolymorphicTraits<T>::getKind(Val) != NodeKind::Scalar &&
+ // "plain scalar documents are not supported")
+ // To avoid this crash we print the raw data instead.
+ return {"", ""};
+ }
MsgPackDoc.toYAML(StrOS);
-
return {"AMDGPU Metadata", StrOS.str()};
}
}
@@ -5049,7 +5266,7 @@ static const NoteType GNUNoteTypes[] = {
{ELF::NT_GNU_PROPERTY_TYPE_0, "NT_GNU_PROPERTY_TYPE_0 (property note)"},
};
-static const NoteType FreeBSDNoteTypes[] = {
+static const NoteType FreeBSDCoreNoteTypes[] = {
{ELF::NT_FREEBSD_THRMISC, "NT_THRMISC (thrmisc structure)"},
{ELF::NT_FREEBSD_PROCSTAT_PROC, "NT_PROCSTAT_PROC (proc data)"},
{ELF::NT_FREEBSD_PROCSTAT_FILES, "NT_PROCSTAT_FILES (files data)"},
@@ -5063,12 +5280,22 @@ static const NoteType FreeBSDNoteTypes[] = {
{ELF::NT_FREEBSD_PROCSTAT_AUXV, "NT_PROCSTAT_AUXV (auxv data)"},
};
+static const NoteType FreeBSDNoteTypes[] = {
+ {ELF::NT_FREEBSD_ABI_TAG, "NT_FREEBSD_ABI_TAG (ABI version tag)"},
+ {ELF::NT_FREEBSD_NOINIT_TAG, "NT_FREEBSD_NOINIT_TAG (no .init tag)"},
+ {ELF::NT_FREEBSD_ARCH_TAG, "NT_FREEBSD_ARCH_TAG (architecture tag)"},
+ {ELF::NT_FREEBSD_FEATURE_CTL,
+ "NT_FREEBSD_FEATURE_CTL (FreeBSD feature control)"},
+};
+
static const NoteType AMDNoteTypes[] = {
- {ELF::NT_AMD_AMDGPU_HSA_METADATA,
- "NT_AMD_AMDGPU_HSA_METADATA (HSA Metadata)"},
- {ELF::NT_AMD_AMDGPU_ISA, "NT_AMD_AMDGPU_ISA (ISA Version)"},
- {ELF::NT_AMD_AMDGPU_PAL_METADATA,
- "NT_AMD_AMDGPU_PAL_METADATA (PAL Metadata)"},
+ {ELF::NT_AMD_HSA_CODE_OBJECT_VERSION,
+ "NT_AMD_HSA_CODE_OBJECT_VERSION (AMD HSA Code Object Version)"},
+ {ELF::NT_AMD_HSA_HSAIL, "NT_AMD_HSA_HSAIL (AMD HSA HSAIL Properties)"},
+ {ELF::NT_AMD_HSA_ISA_VERSION, "NT_AMD_HSA_ISA_VERSION (AMD HSA ISA Version)"},
+ {ELF::NT_AMD_HSA_METADATA, "NT_AMD_HSA_METADATA (AMD HSA Metadata)"},
+ {ELF::NT_AMD_HSA_ISA_NAME, "NT_AMD_HSA_ISA_NAME (AMD HSA ISA Name)"},
+ {ELF::NT_AMD_PAL_METADATA, "NT_AMD_PAL_METADATA (AMD PAL Metadata)"},
};
static const NoteType AMDGPUNoteTypes[] = {
@@ -5141,8 +5368,7 @@ static const NoteType CoreNoteTypes[] = {
};
template <class ELFT>
-const StringRef getNoteTypeName(const typename ELFT::Note &Note,
- unsigned ELFType) {
+StringRef getNoteTypeName(const typename ELFT::Note &Note, unsigned ELFType) {
uint32_t Type = Note.getType();
auto FindNote = [&](ArrayRef<NoteType> V) -> StringRef {
for (const NoteType &N : V)
@@ -5154,8 +5380,17 @@ const StringRef getNoteTypeName(const typename ELFT::Note &Note,
StringRef Name = Note.getName();
if (Name == "GNU")
return FindNote(GNUNoteTypes);
- if (Name == "FreeBSD")
- return FindNote(FreeBSDNoteTypes);
+ if (Name == "FreeBSD") {
+ if (ELFType == ELF::ET_CORE) {
+ // FreeBSD also places the generic core notes in the FreeBSD namespace.
+ StringRef Result = FindNote(FreeBSDCoreNoteTypes);
+ if (!Result.empty())
+ return Result;
+ return FindNote(CoreNoteTypes);
+ } else {
+ return FindNote(FreeBSDNoteTypes);
+ }
+ }
if (Name == "AMD")
return FindNote(AMDNoteTypes);
if (Name == "AMDGPU")
@@ -5172,12 +5407,13 @@ static void printNotesHelper(
llvm::function_ref<void(Optional<StringRef>, typename ELFT::Off,
typename ELFT::Addr)>
StartNotesFn,
- llvm::function_ref<Error(const typename ELFT::Note &)> ProcessNoteFn,
+ llvm::function_ref<Error(const typename ELFT::Note &, bool)> ProcessNoteFn,
llvm::function_ref<void()> FinishNotesFn) {
const ELFFile<ELFT> &Obj = Dumper.getElfObject().getELFFile();
+ bool IsCoreFile = Obj.getHeader().e_type == ELF::ET_CORE;
ArrayRef<typename ELFT::Shdr> Sections = cantFail(Obj.sections());
- if (Obj.getHeader().e_type != ELF::ET_CORE && !Sections.empty()) {
+ if (!IsCoreFile && !Sections.empty()) {
for (const typename ELFT::Shdr &S : Sections) {
if (S.sh_type != SHT_NOTE)
continue;
@@ -5186,7 +5422,7 @@ static void printNotesHelper(
Error Err = Error::success();
size_t I = 0;
for (const typename ELFT::Note Note : Obj.notes(S, Err)) {
- if (Error E = ProcessNoteFn(Note))
+ if (Error E = ProcessNoteFn(Note, IsCoreFile))
Dumper.reportUniqueWarning(
"unable to read note with index " + Twine(I) + " from the " +
describe(Obj, S) + ": " + toString(std::move(E)));
@@ -5217,7 +5453,7 @@ static void printNotesHelper(
Error Err = Error::success();
size_t Index = 0;
for (const typename ELFT::Note Note : Obj.notes(P, Err)) {
- if (Error E = ProcessNoteFn(Note))
+ if (Error E = ProcessNoteFn(Note, IsCoreFile))
Dumper.reportUniqueWarning("unable to read note with index " +
Twine(Index) +
" from the PT_NOTE segment with index " +
@@ -5233,9 +5469,17 @@ static void printNotesHelper(
}
template <class ELFT> void GNUELFDumper<ELFT>::printNotes() {
+ bool IsFirstHeader = true;
auto PrintHeader = [&](Optional<StringRef> SecName,
const typename ELFT::Off Offset,
const typename ELFT::Addr Size) {
+ // Print a newline between notes sections to match GNU readelf.
+ if (!IsFirstHeader) {
+ OS << '\n';
+ } else {
+ IsFirstHeader = false;
+ }
+
OS << "Displaying notes found ";
if (SecName)
@@ -5247,7 +5491,7 @@ template <class ELFT> void GNUELFDumper<ELFT>::printNotes() {
OS << " Owner Data size \tDescription\n";
};
- auto ProcessNote = [&](const Elf_Note &Note) -> Error {
+ auto ProcessNote = [&](const Elf_Note &Note, bool IsCore) -> Error {
StringRef Name = Note.getName();
ArrayRef<uint8_t> Descriptor = Note.getDesc();
Elf_Word Type = Note.getType();
@@ -5264,28 +5508,42 @@ template <class ELFT> void GNUELFDumper<ELFT>::printNotes() {
OS << "Unknown note type: (" << format_hex(Type, 10) << ")\n";
// Print the description, or fallback to printing raw bytes for unknown
- // owners.
+ // owners/if we fail to pretty-print the contents.
if (Name == "GNU") {
- printGNUNote<ELFT>(OS, Type, Descriptor);
+ if (printGNUNote<ELFT>(OS, Type, Descriptor))
+ return Error::success();
+ } else if (Name == "FreeBSD") {
+ if (Optional<FreeBSDNote> N =
+ getFreeBSDNote<ELFT>(Type, Descriptor, IsCore)) {
+ OS << " " << N->Type << ": " << N->Value << '\n';
+ return Error::success();
+ }
} else if (Name == "AMD") {
const AMDNote N = getAMDNote<ELFT>(Type, Descriptor);
- if (!N.Type.empty())
+ if (!N.Type.empty()) {
OS << " " << N.Type << ":\n " << N.Value << '\n';
+ return Error::success();
+ }
} else if (Name == "AMDGPU") {
const AMDGPUNote N = getAMDGPUNote<ELFT>(Type, Descriptor);
- if (!N.Type.empty())
+ if (!N.Type.empty()) {
OS << " " << N.Type << ":\n " << N.Value << '\n';
+ return Error::success();
+ }
} else if (Name == "CORE") {
if (Type == ELF::NT_FILE) {
DataExtractor DescExtractor(Descriptor,
ELFT::TargetEndianness == support::little,
sizeof(Elf_Addr));
- if (Expected<CoreNote> NoteOrErr = readCoreNote(DescExtractor))
+ if (Expected<CoreNote> NoteOrErr = readCoreNote(DescExtractor)) {
printCoreNote<ELFT>(OS, *NoteOrErr);
- else
+ return Error::success();
+ } else {
return NoteOrErr.takeError();
+ }
}
- } else if (!Descriptor.empty()) {
+ }
+ if (!Descriptor.empty()) {
OS << " description data:";
for (uint8_t B : Descriptor)
OS << " " << format("%02x", B);
@@ -5461,64 +5719,81 @@ template <class ELFT> void GNUELFDumper<ELFT>::printDependentLibs() {
}
template <class ELFT>
-bool ELFDumper<ELFT>::printFunctionStackSize(
- uint64_t SymValue, Optional<const Elf_Shdr *> FunctionSec,
- const Elf_Shdr &StackSizeSec, DataExtractor Data, uint64_t *Offset) {
- uint32_t FuncSymIndex = 0;
- if (this->DotSymtabSec) {
- if (Expected<Elf_Sym_Range> SymsOrError = Obj.symbols(this->DotSymtabSec)) {
- uint32_t Index = (uint32_t)-1;
- for (const Elf_Sym &Sym : *SymsOrError) {
- ++Index;
-
- if (Sym.st_shndx == ELF::SHN_UNDEF || Sym.getType() != ELF::STT_FUNC)
- continue;
-
- if (Expected<uint64_t> SymAddrOrErr =
- ObjF.toSymbolRef(this->DotSymtabSec, Index).getAddress()) {
- if (SymValue != *SymAddrOrErr)
+SmallVector<uint32_t> ELFDumper<ELFT>::getSymbolIndexesForFunctionAddress(
+ uint64_t SymValue, Optional<const Elf_Shdr *> FunctionSec) {
+ SmallVector<uint32_t> SymbolIndexes;
+ if (!this->AddressToIndexMap.hasValue()) {
+ // Populate the address to index map upon the first invocation of this
+ // function.
+ this->AddressToIndexMap.emplace();
+ if (this->DotSymtabSec) {
+ if (Expected<Elf_Sym_Range> SymsOrError =
+ Obj.symbols(this->DotSymtabSec)) {
+ uint32_t Index = (uint32_t)-1;
+ for (const Elf_Sym &Sym : *SymsOrError) {
+ ++Index;
+
+ if (Sym.st_shndx == ELF::SHN_UNDEF || Sym.getType() != ELF::STT_FUNC)
continue;
- } else {
- std::string Name = this->getStaticSymbolName(Index);
- reportUniqueWarning("unable to get address of symbol '" + Name +
- "': " + toString(SymAddrOrErr.takeError()));
- break;
- }
- // Check if the symbol is in the right section. FunctionSec == None
- // means "any section".
- if (FunctionSec) {
- if (Expected<const Elf_Shdr *> SecOrErr =
- Obj.getSection(Sym, this->DotSymtabSec,
- this->getShndxTable(this->DotSymtabSec))) {
- if (*FunctionSec != *SecOrErr)
- continue;
- } else {
+ Expected<uint64_t> SymAddrOrErr =
+ ObjF.toSymbolRef(this->DotSymtabSec, Index).getAddress();
+ if (!SymAddrOrErr) {
std::string Name = this->getStaticSymbolName(Index);
- // Note: it is impossible to trigger this error currently, it is
- // untested.
- reportUniqueWarning("unable to get section of symbol '" + Name +
- "': " + toString(SecOrErr.takeError()));
- break;
+ reportUniqueWarning("unable to get address of symbol '" + Name +
+ "': " + toString(SymAddrOrErr.takeError()));
+ return SymbolIndexes;
}
+
+ (*this->AddressToIndexMap)[*SymAddrOrErr].push_back(Index);
}
+ } else {
+ reportUniqueWarning("unable to read the symbol table: " +
+ toString(SymsOrError.takeError()));
+ }
+ }
+ }
- FuncSymIndex = Index;
- break;
+ auto Symbols = this->AddressToIndexMap->find(SymValue);
+ if (Symbols == this->AddressToIndexMap->end())
+ return SymbolIndexes;
+
+ for (uint32_t Index : Symbols->second) {
+ // Check if the symbol is in the right section. FunctionSec == None
+ // means "any section".
+ if (FunctionSec) {
+ const Elf_Sym &Sym = *cantFail(Obj.getSymbol(this->DotSymtabSec, Index));
+ if (Expected<const Elf_Shdr *> SecOrErr =
+ Obj.getSection(Sym, this->DotSymtabSec,
+ this->getShndxTable(this->DotSymtabSec))) {
+ if (*FunctionSec != *SecOrErr)
+ continue;
+ } else {
+ std::string Name = this->getStaticSymbolName(Index);
+ // Note: it is impossible to trigger this error currently, it is
+ // untested.
+ reportUniqueWarning("unable to get section of symbol '" + Name +
+ "': " + toString(SecOrErr.takeError()));
+ return SymbolIndexes;
}
- } else {
- reportUniqueWarning("unable to read the symbol table: " +
- toString(SymsOrError.takeError()));
}
+
+ SymbolIndexes.push_back(Index);
}
- std::string FuncName = "?";
- if (!FuncSymIndex)
+ return SymbolIndexes;
+}
+
+template <class ELFT>
+bool ELFDumper<ELFT>::printFunctionStackSize(
+ uint64_t SymValue, Optional<const Elf_Shdr *> FunctionSec,
+ const Elf_Shdr &StackSizeSec, DataExtractor Data, uint64_t *Offset) {
+ SmallVector<uint32_t> FuncSymIndexes =
+ this->getSymbolIndexesForFunctionAddress(SymValue, FunctionSec);
+ if (FuncSymIndexes.empty())
reportUniqueWarning(
"could not identify function symbol for stack size entry in " +
describe(StackSizeSec));
- else
- FuncName = this->getStaticSymbolName(FuncSymIndex);
// Extract the size. The expectation is that Offset is pointing to the right
// place, i.e. past the function address.
@@ -5530,17 +5805,27 @@ bool ELFDumper<ELFT>::printFunctionStackSize(
toString(std::move(Err)));
return false;
}
- printStackSizeEntry(StackSize, FuncName);
+
+ if (FuncSymIndexes.empty()) {
+ printStackSizeEntry(StackSize, {"?"});
+ } else {
+ SmallVector<std::string> FuncSymNames;
+ for (uint32_t Index : FuncSymIndexes)
+ FuncSymNames.push_back(this->getStaticSymbolName(Index));
+ printStackSizeEntry(StackSize, FuncSymNames);
+ }
+
return true;
}
template <class ELFT>
void GNUELFDumper<ELFT>::printStackSizeEntry(uint64_t Size,
- StringRef FuncName) {
+ ArrayRef<std::string> FuncNames) {
OS.PadToColumn(2);
OS << format_decimal(Size, 11);
OS.PadToColumn(18);
- OS << FuncName << "\n";
+
+ OS << join(FuncNames.begin(), FuncNames.end(), ", ") << "\n";
}
template <class ELFT>
@@ -5628,28 +5913,15 @@ void ELFDumper<ELFT>::printNonRelocatableStackSizes(
}
template <class ELFT>
-void ELFDumper<ELFT>::printRelocatableStackSizes(
- std::function<void()> PrintHeader) {
- // Build a map between stack size sections and their corresponding relocation
- // sections.
- llvm::MapVector<const Elf_Shdr *, const Elf_Shdr *> StackSizeRelocMap;
+void ELFDumper<ELFT>::getSectionAndRelocations(
+ std::function<bool(const Elf_Shdr &)> IsMatch,
+ llvm::MapVector<const Elf_Shdr *, const Elf_Shdr *> &SecToRelocMap) {
for (const Elf_Shdr &Sec : cantFail(Obj.sections())) {
- StringRef SectionName;
- if (Expected<StringRef> NameOrErr = Obj.getSectionName(Sec))
- SectionName = *NameOrErr;
- else
- consumeError(NameOrErr.takeError());
-
- // A stack size section that we haven't encountered yet is mapped to the
- // null section until we find its corresponding relocation section.
- if (SectionName == ".stack_sizes")
- if (StackSizeRelocMap
- .insert(std::make_pair(&Sec, (const Elf_Shdr *)nullptr))
+ if (IsMatch(Sec))
+ if (SecToRelocMap.insert(std::make_pair(&Sec, (const Elf_Shdr *)nullptr))
.second)
continue;
- // Check relocation sections if they are relocating contents of a
- // stack sizes section.
if (Sec.sh_type != ELF::SHT_RELA && Sec.sh_type != ELF::SHT_REL)
continue;
@@ -5660,14 +5932,28 @@ void ELFDumper<ELFT>::printRelocatableStackSizes(
toString(RelSecOrErr.takeError()));
continue;
}
-
const Elf_Shdr *ContentsSec = *RelSecOrErr;
- if (this->getPrintableSectionName(**RelSecOrErr) != ".stack_sizes")
- continue;
-
- // Insert a mapping from the stack sizes section to its relocation section.
- StackSizeRelocMap[ContentsSec] = &Sec;
+ if (IsMatch(*ContentsSec))
+ SecToRelocMap[ContentsSec] = &Sec;
}
+}
+
+template <class ELFT>
+void ELFDumper<ELFT>::printRelocatableStackSizes(
+ std::function<void()> PrintHeader) {
+ // Build a map between stack size sections and their corresponding relocation
+ // sections.
+ llvm::MapVector<const Elf_Shdr *, const Elf_Shdr *> StackSizeRelocMap;
+ auto IsMatch = [&](const Elf_Shdr &Sec) -> bool {
+ StringRef SectionName;
+ if (Expected<StringRef> NameOrErr = Obj.getSectionName(Sec))
+ SectionName = *NameOrErr;
+ else
+ consumeError(NameOrErr.takeError());
+
+ return SectionName == ".stack_sizes";
+ };
+ getSectionAndRelocations(IsMatch, StackSizeRelocMap);
for (const auto &StackSizeMapEntry : StackSizeRelocMap) {
PrintHeader();
@@ -5728,7 +6014,7 @@ void GNUELFDumper<ELFT>::printStackSizes() {
OS.PadToColumn(9);
OS << "Size";
OS.PadToColumn(18);
- OS << "Function\n";
+ OS << "Functions\n";
HeaderHasBeenPrinted = true;
};
@@ -5977,11 +6263,32 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printFileHeaders() {
W.printFlags("Flags", E.e_flags, makeArrayRef(ElfHeaderMipsFlags),
unsigned(ELF::EF_MIPS_ARCH), unsigned(ELF::EF_MIPS_ABI),
unsigned(ELF::EF_MIPS_MACH));
- else if (E.e_machine == EM_AMDGPU)
- W.printFlags("Flags", E.e_flags, makeArrayRef(ElfHeaderAMDGPUFlags),
- unsigned(ELF::EF_AMDGPU_MACH));
- else if (E.e_machine == EM_RISCV)
+ else if (E.e_machine == EM_AMDGPU) {
+ switch (E.e_ident[ELF::EI_ABIVERSION]) {
+ default:
+ W.printHex("Flags", E.e_flags);
+ break;
+ case 0:
+ // ELFOSABI_AMDGPU_PAL, ELFOSABI_AMDGPU_MESA3D support *_V3 flags.
+ LLVM_FALLTHROUGH;
+ case ELF::ELFABIVERSION_AMDGPU_HSA_V3:
+ W.printFlags("Flags", E.e_flags,
+ makeArrayRef(ElfHeaderAMDGPUFlagsABIVersion3),
+ unsigned(ELF::EF_AMDGPU_MACH));
+ break;
+ case ELF::ELFABIVERSION_AMDGPU_HSA_V4:
+ W.printFlags("Flags", E.e_flags,
+ makeArrayRef(ElfHeaderAMDGPUFlagsABIVersion4),
+ unsigned(ELF::EF_AMDGPU_MACH),
+ unsigned(ELF::EF_AMDGPU_FEATURE_XNACK_V4),
+ unsigned(ELF::EF_AMDGPU_FEATURE_SRAMECC_V4));
+ break;
+ }
+ } else if (E.e_machine == EM_RISCV)
W.printFlags("Flags", E.e_flags, makeArrayRef(ElfHeaderRISCVFlags));
+ else if (E.e_machine == EM_AVR)
+ W.printFlags("Flags", E.e_flags, makeArrayRef(ElfHeaderAVRFlags),
+ unsigned(ELF::EF_AVR_ARCH_MASK));
else
W.printFlags("Flags", E.e_flags);
W.printNumber("HeaderSize", E.e_ehsize);
@@ -6407,28 +6714,142 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printHashHistograms() {
W.startLine() << "Hash Histogram not implemented!\n";
}
+// Returns true if rel/rela section exists, and populates SymbolIndices.
+// Otherwise returns false.
+template <class ELFT>
+static bool getSymbolIndices(const typename ELFT::Shdr *CGRelSection,
+ const ELFFile<ELFT> &Obj,
+ const LLVMELFDumper<ELFT> *Dumper,
+ SmallVector<uint32_t, 128> &SymbolIndices) {
+ if (!CGRelSection) {
+ Dumper->reportUniqueWarning(
+ "relocation section for a call graph section doesn't exist");
+ return false;
+ }
+
+ if (CGRelSection->sh_type == SHT_REL) {
+ typename ELFT::RelRange CGProfileRel;
+ Expected<typename ELFT::RelRange> CGProfileRelOrError =
+ Obj.rels(*CGRelSection);
+ if (!CGProfileRelOrError) {
+ Dumper->reportUniqueWarning("unable to load relocations for "
+ "SHT_LLVM_CALL_GRAPH_PROFILE section: " +
+ toString(CGProfileRelOrError.takeError()));
+ return false;
+ }
+
+ CGProfileRel = *CGProfileRelOrError;
+ for (const typename ELFT::Rel &Rel : CGProfileRel)
+ SymbolIndices.push_back(Rel.getSymbol(Obj.isMips64EL()));
+ } else {
+ // MC unconditionally produces SHT_REL, but GNU strip/objcopy may convert
+ // the format to SHT_RELA
+ // (https://sourceware.org/bugzilla/show_bug.cgi?id=28035)
+ typename ELFT::RelaRange CGProfileRela;
+ Expected<typename ELFT::RelaRange> CGProfileRelaOrError =
+ Obj.relas(*CGRelSection);
+ if (!CGProfileRelaOrError) {
+ Dumper->reportUniqueWarning("unable to load relocations for "
+ "SHT_LLVM_CALL_GRAPH_PROFILE section: " +
+ toString(CGProfileRelaOrError.takeError()));
+ return false;
+ }
+
+ CGProfileRela = *CGProfileRelaOrError;
+ for (const typename ELFT::Rela &Rela : CGProfileRela)
+ SymbolIndices.push_back(Rela.getSymbol(Obj.isMips64EL()));
+ }
+
+ return true;
+}
+
template <class ELFT> void LLVMELFDumper<ELFT>::printCGProfile() {
- ListScope L(W, "CGProfile");
- if (!this->DotCGProfileSec)
- return;
+ llvm::MapVector<const Elf_Shdr *, const Elf_Shdr *> SecToRelocMap;
- Expected<ArrayRef<Elf_CGProfile>> CGProfileOrErr =
- this->Obj.template getSectionContentsAsArray<Elf_CGProfile>(
- *this->DotCGProfileSec);
- if (!CGProfileOrErr) {
- this->reportUniqueWarning(
- "unable to dump the SHT_LLVM_CALL_GRAPH_PROFILE section: " +
- toString(CGProfileOrErr.takeError()));
- return;
+ auto IsMatch = [](const Elf_Shdr &Sec) -> bool {
+ return Sec.sh_type == ELF::SHT_LLVM_CALL_GRAPH_PROFILE;
+ };
+ this->getSectionAndRelocations(IsMatch, SecToRelocMap);
+
+ for (const auto &CGMapEntry : SecToRelocMap) {
+ const Elf_Shdr *CGSection = CGMapEntry.first;
+ const Elf_Shdr *CGRelSection = CGMapEntry.second;
+
+ Expected<ArrayRef<Elf_CGProfile>> CGProfileOrErr =
+ this->Obj.template getSectionContentsAsArray<Elf_CGProfile>(*CGSection);
+ if (!CGProfileOrErr) {
+ this->reportUniqueWarning(
+ "unable to load the SHT_LLVM_CALL_GRAPH_PROFILE section: " +
+ toString(CGProfileOrErr.takeError()));
+ return;
+ }
+
+ SmallVector<uint32_t, 128> SymbolIndices;
+ bool UseReloc =
+ getSymbolIndices<ELFT>(CGRelSection, this->Obj, this, SymbolIndices);
+ if (UseReloc && SymbolIndices.size() != CGProfileOrErr->size() * 2) {
+ this->reportUniqueWarning(
+ "number of from/to pairs does not match number of frequencies");
+ UseReloc = false;
+ }
+
+ ListScope L(W, "CGProfile");
+ for (uint32_t I = 0, Size = CGProfileOrErr->size(); I != Size; ++I) {
+ const Elf_CGProfile &CGPE = (*CGProfileOrErr)[I];
+ DictScope D(W, "CGProfileEntry");
+ if (UseReloc) {
+ uint32_t From = SymbolIndices[I * 2];
+ uint32_t To = SymbolIndices[I * 2 + 1];
+ W.printNumber("From", this->getStaticSymbolName(From), From);
+ W.printNumber("To", this->getStaticSymbolName(To), To);
+ }
+ W.printNumber("Weight", CGPE.cgp_weight);
+ }
}
+}
- for (const Elf_CGProfile &CGPE : *CGProfileOrErr) {
- DictScope D(W, "CGProfileEntry");
- W.printNumber("From", this->getStaticSymbolName(CGPE.cgp_from),
- CGPE.cgp_from);
- W.printNumber("To", this->getStaticSymbolName(CGPE.cgp_to),
- CGPE.cgp_to);
- W.printNumber("Weight", CGPE.cgp_weight);
+template <class ELFT> void LLVMELFDumper<ELFT>::printBBAddrMaps() {
+ bool IsRelocatable = this->Obj.getHeader().e_type == ELF::ET_REL;
+ for (const Elf_Shdr &Sec : cantFail(this->Obj.sections())) {
+ if (Sec.sh_type != SHT_LLVM_BB_ADDR_MAP)
+ continue;
+ Optional<const Elf_Shdr *> FunctionSec = None;
+ if (IsRelocatable)
+ FunctionSec =
+ unwrapOrError(this->FileName, this->Obj.getSection(Sec.sh_link));
+ ListScope L(W, "BBAddrMap");
+ Expected<std::vector<Elf_BBAddrMap>> BBAddrMapOrErr =
+ this->Obj.decodeBBAddrMap(Sec);
+ if (!BBAddrMapOrErr) {
+ this->reportUniqueWarning("unable to dump " + this->describe(Sec) + ": " +
+ toString(BBAddrMapOrErr.takeError()));
+ continue;
+ }
+ for (const Elf_BBAddrMap &AM : *BBAddrMapOrErr) {
+ DictScope D(W, "Function");
+ W.printHex("At", AM.Addr);
+ SmallVector<uint32_t> FuncSymIndex =
+ this->getSymbolIndexesForFunctionAddress(AM.Addr, FunctionSec);
+ std::string FuncName = "<?>";
+ if (FuncSymIndex.empty())
+ this->reportUniqueWarning(
+ "could not identify function symbol for address (0x" +
+ Twine::utohexstr(AM.Addr) + ") in " + this->describe(Sec));
+ else
+ FuncName = this->getStaticSymbolName(FuncSymIndex.front());
+ W.printString("Name", FuncName);
+
+ ListScope L(W, "BB entries");
+ for (const typename Elf_BBAddrMap::BBEntry &BBE : AM.BBEntries) {
+ DictScope L(W);
+ W.printHex("Offset", BBE.Offset);
+ W.printHex("Size", BBE.Size);
+ W.printBoolean("HasReturn", BBE.HasReturn);
+ W.printBoolean("HasTailCall", BBE.HasTailCall);
+ W.printBoolean("IsEHPad", BBE.IsEHPad);
+ W.printBoolean("CanFallThrough", BBE.CanFallThrough);
+ }
+ }
}
}
@@ -6449,15 +6870,17 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printAddrsig() {
}
template <typename ELFT>
-static void printGNUNoteLLVMStyle(uint32_t NoteType, ArrayRef<uint8_t> Desc,
+static bool printGNUNoteLLVMStyle(uint32_t NoteType, ArrayRef<uint8_t> Desc,
ScopedPrinter &W) {
+ // Return true if we were able to pretty-print the note, false otherwise.
switch (NoteType) {
default:
- return;
+ return false;
case ELF::NT_GNU_ABI_TAG: {
const GNUAbiTag &AbiTag = getGNUAbiTag<ELFT>(Desc);
if (!AbiTag.IsValid) {
W.printString("ABI", "<corrupt GNU_ABI_TAG>");
+ return false;
} else {
W.printString("OS", AbiTag.OSName);
W.printString("ABI", AbiTag.ABI);
@@ -6477,6 +6900,7 @@ static void printGNUNoteLLVMStyle(uint32_t NoteType, ArrayRef<uint8_t> Desc,
W.printString(Property);
break;
}
+ return true;
}
static void printCoreNoteLLVMStyle(const CoreNote &Note, ScopedPrinter &W) {
@@ -6505,7 +6929,7 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printNotes() {
auto EndNotes = [&] { NoteScope.reset(); };
- auto ProcessNote = [&](const Elf_Note &Note) -> Error {
+ auto ProcessNote = [&](const Elf_Note &Note, bool IsCore) -> Error {
DictScope D2(W, "Note");
StringRef Name = Note.getName();
ArrayRef<uint8_t> Descriptor = Note.getDesc();
@@ -6524,28 +6948,42 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printNotes() {
"Unknown (" + to_string(format_hex(Type, 10)) + ")");
// Print the description, or fallback to printing raw bytes for unknown
- // owners.
+ // owners/if we fail to pretty-print the contents.
if (Name == "GNU") {
- printGNUNoteLLVMStyle<ELFT>(Type, Descriptor, W);
+ if (printGNUNoteLLVMStyle<ELFT>(Type, Descriptor, W))
+ return Error::success();
+ } else if (Name == "FreeBSD") {
+ if (Optional<FreeBSDNote> N =
+ getFreeBSDNote<ELFT>(Type, Descriptor, IsCore)) {
+ W.printString(N->Type, N->Value);
+ return Error::success();
+ }
} else if (Name == "AMD") {
const AMDNote N = getAMDNote<ELFT>(Type, Descriptor);
- if (!N.Type.empty())
+ if (!N.Type.empty()) {
W.printString(N.Type, N.Value);
+ return Error::success();
+ }
} else if (Name == "AMDGPU") {
const AMDGPUNote N = getAMDGPUNote<ELFT>(Type, Descriptor);
- if (!N.Type.empty())
+ if (!N.Type.empty()) {
W.printString(N.Type, N.Value);
+ return Error::success();
+ }
} else if (Name == "CORE") {
if (Type == ELF::NT_FILE) {
DataExtractor DescExtractor(Descriptor,
ELFT::TargetEndianness == support::little,
sizeof(Elf_Addr));
- if (Expected<CoreNote> Note = readCoreNote(DescExtractor))
- printCoreNoteLLVMStyle(*Note, W);
- else
- return Note.takeError();
+ if (Expected<CoreNote> N = readCoreNote(DescExtractor)) {
+ printCoreNoteLLVMStyle(*N, W);
+ return Error::success();
+ } else {
+ return N.takeError();
+ }
}
- } else if (!Descriptor.empty()) {
+ }
+ if (!Descriptor.empty()) {
W.printBinaryBlock("Description data", Descriptor);
}
return Error::success();
@@ -6614,9 +7052,10 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printStackSizes() {
}
template <class ELFT>
-void LLVMELFDumper<ELFT>::printStackSizeEntry(uint64_t Size, StringRef FuncName) {
+void LLVMELFDumper<ELFT>::printStackSizeEntry(uint64_t Size,
+ ArrayRef<std::string> FuncNames) {
DictScope D(W, "Entry");
- W.printString("Function", FuncName);
+ W.printList("Functions", FuncNames);
W.printHex("Size", Size);
}
diff --git a/llvm/tools/llvm-readobj/MachODumper.cpp b/llvm/tools/llvm-readobj/MachODumper.cpp
index c13b1f3bf2a0..433ca9335324 100644
--- a/llvm/tools/llvm-readobj/MachODumper.cpp
+++ b/llvm/tools/llvm-readobj/MachODumper.cpp
@@ -256,11 +256,14 @@ static const EnumEntry<unsigned> MachOSymbolRefTypes[] = {
};
static const EnumEntry<unsigned> MachOSymbolFlags[] = {
+ { "ThumbDef", 0x8 },
{ "ReferencedDynamically", 0x10 },
{ "NoDeadStrip", 0x20 },
{ "WeakRef", 0x40 },
{ "WeakDef", 0x80 },
+ { "SymbolResolver", 0x100 },
{ "AltEntry", 0x200 },
+ { "ColdFunc", 0x400 },
};
static const EnumEntry<unsigned> MachOSymbolTypes[] = {
@@ -651,9 +654,9 @@ void MachODumper::printSymbol(const SymbolRef &Symbol) {
makeArrayRef(MachOSymbolTypes));
}
W.printHex("Section", SectionName, MOSymbol.SectionIndex);
- W.printEnum("RefType", static_cast<uint16_t>(MOSymbol.Flags & 0xF),
+ W.printEnum("RefType", static_cast<uint16_t>(MOSymbol.Flags & 0x7),
makeArrayRef(MachOSymbolRefTypes));
- W.printFlags("Flags", static_cast<uint16_t>(MOSymbol.Flags & ~0xF),
+ W.printFlags("Flags", static_cast<uint16_t>(MOSymbol.Flags & ~0x7),
makeArrayRef(MachOSymbolFlags));
W.printHex("Value", MOSymbol.Value);
}
diff --git a/llvm/tools/llvm-readobj/ObjDumper.cpp b/llvm/tools/llvm-readobj/ObjDumper.cpp
index fc91d81f0784..87c229356e20 100644
--- a/llvm/tools/llvm-readobj/ObjDumper.cpp
+++ b/llvm/tools/llvm-readobj/ObjDumper.cpp
@@ -52,6 +52,25 @@ static void printAsPrintable(raw_ostream &W, const uint8_t *Start, size_t Len) {
W << (isPrint(Start[i]) ? static_cast<char>(Start[i]) : '.');
}
+void ObjDumper::printAsStringList(StringRef StringContent) {
+ const uint8_t *StrContent = StringContent.bytes_begin();
+ const uint8_t *CurrentWord = StrContent;
+ const uint8_t *StrEnd = StringContent.bytes_end();
+
+ while (CurrentWord <= StrEnd) {
+ size_t WordSize = strnlen(reinterpret_cast<const char *>(CurrentWord),
+ StrEnd - CurrentWord);
+ if (!WordSize) {
+ CurrentWord++;
+ continue;
+ }
+ W.startLine() << format("[%6tx] ", CurrentWord - StrContent);
+ printAsPrintable(W.startLine(), CurrentWord, WordSize);
+ W.startLine() << '\n';
+ CurrentWord += WordSize + 1;
+ }
+}
+
static std::vector<object::SectionRef>
getSectionRefsByNameOrIndex(const object::ObjectFile &Obj,
ArrayRef<std::string> Sections) {
@@ -109,23 +128,7 @@ void ObjDumper::printSectionsAsString(const object::ObjectFile &Obj,
StringRef SectionContent =
unwrapOrError(Obj.getFileName(), Section.getContents());
-
- const uint8_t *SecContent = SectionContent.bytes_begin();
- const uint8_t *CurrentWord = SecContent;
- const uint8_t *SecEnd = SectionContent.bytes_end();
-
- while (CurrentWord <= SecEnd) {
- size_t WordSize = strnlen(reinterpret_cast<const char *>(CurrentWord),
- SecEnd - CurrentWord);
- if (!WordSize) {
- CurrentWord++;
- continue;
- }
- W.startLine() << format("[%6tx] ", CurrentWord - SecContent);
- printAsPrintable(W.startLine(), CurrentWord, WordSize);
- W.startLine() << '\n';
- CurrentWord += WordSize + 1;
- }
+ printAsStringList(SectionContent);
}
}
diff --git a/llvm/tools/llvm-readobj/ObjDumper.h b/llvm/tools/llvm-readobj/ObjDumper.h
index d4e166b504cf..7e1c0ca35127 100644
--- a/llvm/tools/llvm-readobj/ObjDumper.h
+++ b/llvm/tools/llvm-readobj/ObjDumper.h
@@ -72,6 +72,7 @@ public:
virtual void printGroupSections() {}
virtual void printHashHistograms() {}
virtual void printCGProfile() {}
+ virtual void printBBAddrMaps() {}
virtual void printAddrsig() {}
virtual void printNotes() {}
virtual void printELFLinkerOptions() {}
@@ -104,8 +105,13 @@ public:
virtual void printMachOIndirectSymbols() { }
virtual void printMachOLinkerOptions() { }
+ // Currently only implemented for XCOFF.
+ virtual void printStringTable() { }
+
virtual void printStackMap() const = 0;
+ void printAsStringList(StringRef StringContent);
+
void printSectionsAsString(const object::ObjectFile &Obj,
ArrayRef<std::string> Sections);
void printSectionsAsHex(const object::ObjectFile &Obj,
diff --git a/llvm/tools/llvm-readobj/Opts.td b/llvm/tools/llvm-readobj/Opts.td
new file mode 100644
index 000000000000..493b93769eb4
--- /dev/null
+++ b/llvm/tools/llvm-readobj/Opts.td
@@ -0,0 +1,128 @@
+include "llvm/Option/OptParser.td"
+
+class F<string letter, string help> : Flag<["-"], letter>, HelpText<help>;
+class FF<string name, string help> : Flag<["--"], name>, HelpText<help>;
+
+multiclass BB<string name, string help1, string help2> {
+ def NAME: Flag<["--"], name>, HelpText<help1>;
+ def no_ # NAME: Flag<["--"], "no-" # name>, HelpText<help2>;
+}
+
+multiclass Eq<string name, string help> {
+ def NAME #_EQ : Joined<["--"], name #"=">, HelpText<help>;
+ def : Separate<["--"], name>, Alias<!cast<Joined>(NAME #_EQ)>;
+}
+
+def addrsig : FF<"addrsig", "Display address-significance table">;
+def all : FF<"all", "Equivalent to setting: --file-header, --program-headers, --section-headers, "
+ "--symbols, --relocations, --dynamic-table, --notes, --version-info, --unwind, "
+ "--section-groups and --histogram">;
+def arch_specific : FF<"arch-specific", "Display architecture-specific information">;
+def bb_addr_map : FF<"bb-addr-map", "Display the BB address map section">;
+def cg_profile : FF<"cg-profile", "Display call graph profile section">;
+defm demangle : BB<"demangle", "Demangle symbol names", "Do not demangle symbol names (default)">;
+def dependent_libraries : FF<"dependent-libraries", "Display the dependent libraries section">;
+def dyn_relocations : FF<"dyn-relocations", "Display the dynamic relocation entries in the file">;
+def dyn_syms : FF<"dyn-syms", "Display the dynamic symbol table">;
+def expand_relocs : FF<"expand-relocs", "Expand each shown relocation to multiple lines">;
+def file_header : FF<"file-header", "Display file header">;
+def headers : FF<"headers", "Equivalent to setting: --file-header, --program-headers, --section-headers">;
+defm hex_dump : Eq<"hex-dump", "Display the specified section(s) as hexadecimal bytes">, MetaVarName<"<name or index>">;
+def relocs : FF<"relocs", "Display the relocation entries in the file">;
+def section_data : FF<"section-data", "Display section data for each section shown">;
+def section_details : FF<"section-details", "Display the section details">;
+def section_headers : FF<"section-headers", "Display section headers">;
+def section_mapping : FF<"section-mapping", "Display the section to segment mapping">;
+def section_mapping_EQ_false : FF<"section-mapping=false", "Don't display the section to segment mapping">, Flags<[HelpHidden]>;
+def section_relocations : FF<"section-relocations", "Display relocations for each section shown">;
+def section_symbols : FF<"section-symbols", "Display symbols for each section shown">;
+def stack_sizes : FF<"stack-sizes", "Display contents of all stack sizes sections">;
+def stackmap : FF<"stackmap", "Display contents of stackmap section">;
+defm string_dump : Eq<"string-dump", "Display the specified section(s) as a list of strings">, MetaVarName<"<name or index>">;
+def string_table : FF<"string-table", "Display the string table (only for XCOFF now)">;
+def symbols : FF<"symbols", "Display the symbol table. Also display the dynamic symbol table when using GNU output style for ELF">;
+def unwind : FF<"unwind", "Display unwind information">;
+
+// ELF specific options.
+def grp_elf : OptionGroup<"kind">, HelpText<"OPTIONS (ELF specific)">;
+def dynamic_table : FF<"dynamic-table", "Display the dynamic section table">, Group<grp_elf>;
+def elf_linker_options : FF<"elf-linker-options", "Display the .linker-options section">, Group<grp_elf>;
+defm elf_output_style : Eq<"elf-output-style", "Specify ELF dump style">, Group<grp_elf>;
+def histogram : FF<"histogram", "Display bucket list histogram for hash sections">, Group<grp_elf>;
+def section_groups : FF<"section-groups", "Display section groups">, Group<grp_elf>;
+def gnu_hash_table : FF<"gnu-hash-table", "Display .gnu.hash section">, Group<grp_elf>;
+def hash_symbols : FF<"hash-symbols", "Display the dynamic symbols derived from the hash section">, Group<grp_elf>;
+def hash_table : FF<"hash-table", "Display .hash section">, Group<grp_elf>;
+def needed_libs : FF<"needed-libs", "Display the needed libraries">, Group<grp_elf>;
+def notes : FF<"notes", "Display notes">, Group<grp_elf>;
+def program_headers : FF<"program-headers", "Display program headers">, Group<grp_elf>;
+def raw_relr : FF<"raw-relr", "Do not decode relocations in SHT_RELR section, display raw contents">, Group<grp_elf>;
+def version_info : FF<"version-info", "Display version sections">, Group<grp_elf>;
+
+// Mach-O specific options.
+def grp_mach_o : OptionGroup<"kind">, HelpText<"OPTIONS (Mach-O specific)">;
+def macho_data_in_code : FF<"macho-data-in-code", "Display Data in Code command">, Group<grp_mach_o>;
+def macho_dysymtab : FF<"macho-dysymtab", "Display Dysymtab command">, Group<grp_mach_o>;
+def macho_indirect_symbols : FF<"macho-indirect-symbols", "Display indirect symbols">, Group<grp_mach_o>;
+def macho_linker_options : FF<"macho-linker-options", "Display linker options">, Group<grp_mach_o>;
+def macho_segment : FF<"macho-segment", "Display Segment command">, Group<grp_mach_o>;
+def macho_version_min : FF<"macho-version-min", "Display version min command">, Group<grp_mach_o>;
+
+// PE/COFF specific options.
+def grp_coff : OptionGroup<"kind">, HelpText<"OPTIONS (PE/COFF specific)">;
+def codeview : FF<"codeview", "Display CodeView debug information">, Group<grp_coff>;
+def codeview_ghash : FF<"codeview-ghash", "Enable global hashing for CodeView type stream de-duplication">, Group<grp_coff>;
+def codeview_merged_types : FF<"codeview-merged-types", "Display the merged CodeView type stream">, Group<grp_coff>;
+def codeview_subsection_bytes : FF<"codeview-subsection-bytes", "Dump raw contents of codeview debug sections and records">, Group<grp_coff>;
+def coff_basereloc : FF<"coff-basereloc", "Display .reloc section">, Group<grp_coff>;
+def coff_debug_directory : FF<"coff-debug-directory", "Display debug directory">, Group<grp_coff>;
+def coff_directives : FF<"coff-directives", "Display .drectve section">, Group<grp_coff>;
+def coff_exports : FF<"coff-exports", "Display export table">, Group<grp_coff>;
+def coff_imports : FF<"coff-imports", "Display import table">, Group<grp_coff>;
+def coff_load_config : FF<"coff-load-config", "Display load config">, Group<grp_coff>;
+def coff_resources : FF<"coff-resources", "Display .rsrc section">, Group<grp_coff>;
+def coff_tls_directory : FF<"coff-tls-directory", "Display TLS directory">, Group<grp_coff>;
+
+def help : FF<"help", "Display this help">;
+def version : FF<"version", "Display the version">;
+
+// Ignored for GNU readelf compatibility.
+def : F<"W", "Ignored for GNU readelf compatibility">;
+def : FF<"wide", "Ignored for GNU readelf compatibility">;
+
+// Traditional llvm-readobj Aliases.
+def : Flag<["--"], "dt">, Alias<dyn_syms>, HelpText<"Alias for --dyn-syms">;
+def : Flag<["--"], "sd">, Alias<section_data>, HelpText<"Alias for --section-data">;
+def : Flag<["--"], "st">, Alias<section_symbols>, HelpText<"Alias for --section-symbols">;
+def : Flag<["--"], "sr">, Alias<section_relocations>, HelpText<"Alias for --section-relocations">;
+
+// Aliases.
+def : FF<"dyn-symbols", "Alias for --dyn-syms">, Alias<dyn_syms>;
+def : FF<"dynamic", "Alias for --dynamic-table">, Alias<dynamic_table>;
+def : FF<"elf-cg-profile", "Alias for --cg-profile">, Alias<cg_profile>, Flags<[HelpHidden]>;
+def : FF<"elf-hash-histogram", "Alias for --histogram">, Alias<histogram>, Flags<[HelpHidden]>;
+def : FF<"elf-section-groups", "Alias for --section-groups">, Alias<section_groups>, Flags<[HelpHidden]>;
+def : FF<"file-headers", "Alias for --file-header">, Alias<file_header>, Flags<[HelpHidden]>;
+def : FF<"relocations", "Alias for --relocs">, Alias<relocs>;
+def : FF<"sections", "Alias for --section-headers">, Alias<section_headers>;
+def : FF<"segments", "Alias for --program-headers">, Alias<program_headers>, Group<grp_elf>;
+def : FF<"syms", "Alias for --symbols">, Alias<symbols>;
+
+def : F<"A", "Alias for --arch-specific">, Alias<arch_specific>;
+def : F<"a", "Alias for --all">, Alias<all>;
+def : F<"C", "Alias for --demangle">, Alias<demangle>;
+def : F<"d", "Alias for --dynamic-table">, Alias<dynamic_table>, Group<grp_elf>;
+def : F<"e", "Alias for --headers">, Alias<headers>;
+def : F<"g", "Alias for --section-groups">, Alias<section_groups>, Group<grp_elf>;
+def : F<"h", "Alias for --file-header">, Alias<file_header>;
+def : F<"I", "Alias for --histogram">, Alias<histogram>, Group<grp_elf>;
+def : F<"l", "Alias for --program-headers">, Alias<program_headers>;
+def : F<"n", "Alias for --notes">, Alias<notes>;
+def : JoinedOrSeparate<["-"], "p">, Alias<string_dump_EQ>, HelpText<"Alias for --string-dump">, MetaVarName<"<name or index>">;
+def : F<"r", "Alias for --relocs">, Alias<relocs>;
+def : F<"S", "Alias for --section-headers">, Alias<section_headers>;
+def : F<"s", "Alias for --symbols">, Alias<symbols>;
+def : F<"t", "Alias for --section-details">, Alias<section_details>;
+def : F<"u", "Alias for --unwind">, Alias<unwind>;
+def : F<"V", "Alias for --version-info">, Alias<version_info>, Group<grp_elf>;
+def : JoinedOrSeparate<["-"], "x">, Alias<hex_dump_EQ>, HelpText<"Alias for --hex-dump">, MetaVarName<"<name or index>">;
diff --git a/llvm/tools/llvm-readobj/WasmDumper.cpp b/llvm/tools/llvm-readobj/WasmDumper.cpp
index fb7134d20a85..f7dcaa35656f 100644
--- a/llvm/tools/llvm-readobj/WasmDumper.cpp
+++ b/llvm/tools/llvm-readobj/WasmDumper.cpp
@@ -23,8 +23,8 @@ namespace {
static const EnumEntry<unsigned> WasmSymbolTypes[] = {
#define ENUM_ENTRY(X) \
{ #X, wasm::WASM_SYMBOL_TYPE_##X }
- ENUM_ENTRY(FUNCTION), ENUM_ENTRY(DATA), ENUM_ENTRY(GLOBAL),
- ENUM_ENTRY(SECTION), ENUM_ENTRY(EVENT), ENUM_ENTRY(TABLE),
+ ENUM_ENTRY(FUNCTION), ENUM_ENTRY(DATA), ENUM_ENTRY(GLOBAL),
+ ENUM_ENTRY(SECTION), ENUM_ENTRY(TAG), ENUM_ENTRY(TABLE),
#undef ENUM_ENTRY
};
@@ -33,7 +33,7 @@ static const EnumEntry<uint32_t> WasmSectionTypes[] = {
{ #X, wasm::WASM_SEC_##X }
ENUM_ENTRY(CUSTOM), ENUM_ENTRY(TYPE), ENUM_ENTRY(IMPORT),
ENUM_ENTRY(FUNCTION), ENUM_ENTRY(TABLE), ENUM_ENTRY(MEMORY),
- ENUM_ENTRY(GLOBAL), ENUM_ENTRY(EVENT), ENUM_ENTRY(EXPORT),
+ ENUM_ENTRY(GLOBAL), ENUM_ENTRY(TAG), ENUM_ENTRY(EXPORT),
ENUM_ENTRY(START), ENUM_ENTRY(ELEM), ENUM_ENTRY(CODE),
ENUM_ENTRY(DATA), ENUM_ENTRY(DATACOUNT),
#undef ENUM_ENTRY
@@ -192,7 +192,7 @@ void WasmDumper::printSectionHeaders() {
ListScope Group(W, "Memories");
for (const wasm::WasmLimits &Memory : Obj->memories()) {
DictScope Group(W, "Memory");
- W.printNumber("InitialPages", Memory.Initial);
+ W.printNumber("MinPages", Memory.Minimum);
if (Memory.Flags & wasm::WASM_LIMITS_FLAG_HAS_MAX) {
W.printNumber("MaxPages", WasmSec.Offset);
}
diff --git a/llvm/tools/llvm-readobj/XCOFFDumper.cpp b/llvm/tools/llvm-readobj/XCOFFDumper.cpp
index 8f0f18cedceb..94ef96e447ce 100644
--- a/llvm/tools/llvm-readobj/XCOFFDumper.cpp
+++ b/llvm/tools/llvm-readobj/XCOFFDumper.cpp
@@ -34,13 +34,14 @@ public:
void printUnwindInfo() override;
void printStackMap() const override;
void printNeededLibraries() override;
+ void printStringTable() override;
private:
template <typename T> void printSectionHeaders(ArrayRef<T> Sections);
template <typename T> void printGenericSectionHeader(T &Sec) const;
template <typename T> void printOverflowSectionHeader(T &Sec) const;
void printFileAuxEnt(const XCOFFFileAuxEnt *AuxEntPtr);
- void printCsectAuxEnt32(const XCOFFCsectAuxEnt32 *AuxEntPtr);
+ void printCsectAuxEnt(XCOFFCsectAuxRef AuxEntRef);
void printSectAuxEntForStat(const XCOFFSectAuxEntForStat *AuxEntPtr);
void printSymbol(const SymbolRef &);
void printRelocations(ArrayRef<XCOFFSectionHeader32> Sections);
@@ -164,10 +165,17 @@ static const EnumEntry<XCOFF::CFileStringType> FileStringType[] = {
#undef ECase
};
+static const EnumEntry<XCOFF::SymbolAuxType> SymAuxType[] = {
+#define ECase(X) \
+ { #X, XCOFF::X }
+ ECase(AUX_EXCEPT), ECase(AUX_FCN), ECase(AUX_SYM), ECase(AUX_FILE),
+ ECase(AUX_CSECT), ECase(AUX_SECT)
+#undef ECase
+};
+
void XCOFFDumper::printFileAuxEnt(const XCOFFFileAuxEnt *AuxEntPtr) {
- if (Obj.is64Bit())
- report_fatal_error(
- "Printing for File Auxiliary Entry in 64-bit is unimplemented.");
+ assert((!Obj.is64Bit() || AuxEntPtr->AuxType == XCOFF::AUX_FILE) &&
+ "Mismatched auxiliary type!");
StringRef FileName =
unwrapOrError(Obj.getFileName(), Obj.getCFileName(AuxEntPtr));
DictScope SymDs(W, "File Auxiliary Entry");
@@ -176,19 +184,22 @@ void XCOFFDumper::printFileAuxEnt(const XCOFFFileAuxEnt *AuxEntPtr) {
W.printString("Name", FileName);
W.printEnum("Type", static_cast<uint8_t>(AuxEntPtr->Type),
makeArrayRef(FileStringType));
+ if (Obj.is64Bit()) {
+ W.printEnum("Auxiliary Type", static_cast<uint8_t>(AuxEntPtr->AuxType),
+ makeArrayRef(SymAuxType));
+ }
}
static const EnumEntry<XCOFF::StorageMappingClass> CsectStorageMappingClass[] =
{
#define ECase(X) \
{ #X, XCOFF::X }
- ECase(XMC_PR), ECase(XMC_RO), ECase(XMC_DB),
- ECase(XMC_GL), ECase(XMC_XO), ECase(XMC_SV),
- ECase(XMC_SV64), ECase(XMC_SV3264), ECase(XMC_TI),
- ECase(XMC_TB), ECase(XMC_RW), ECase(XMC_TC0),
- ECase(XMC_TC), ECase(XMC_TD), ECase(XMC_DS),
- ECase(XMC_UA), ECase(XMC_BS), ECase(XMC_UC),
- ECase(XMC_TL), ECase(XMC_TE)
+ ECase(XMC_PR), ECase(XMC_RO), ECase(XMC_DB), ECase(XMC_GL),
+ ECase(XMC_XO), ECase(XMC_SV), ECase(XMC_SV64), ECase(XMC_SV3264),
+ ECase(XMC_TI), ECase(XMC_TB), ECase(XMC_RW), ECase(XMC_TC0),
+ ECase(XMC_TC), ECase(XMC_TD), ECase(XMC_DS), ECase(XMC_UA),
+ ECase(XMC_BS), ECase(XMC_UC), ECase(XMC_TL), ECase(XMC_UL),
+ ECase(XMC_TE)
#undef ECase
};
@@ -199,27 +210,32 @@ static const EnumEntry<XCOFF::SymbolType> CsectSymbolTypeClass[] = {
#undef ECase
};
-void XCOFFDumper::printCsectAuxEnt32(const XCOFFCsectAuxEnt32 *AuxEntPtr) {
- assert(!Obj.is64Bit() && "32-bit interface called on 64-bit object file.");
+void XCOFFDumper::printCsectAuxEnt(XCOFFCsectAuxRef AuxEntRef) {
+ assert((!Obj.is64Bit() || AuxEntRef.getAuxType64() == XCOFF::AUX_CSECT) &&
+ "Mismatched auxiliary type!");
DictScope SymDs(W, "CSECT Auxiliary Entry");
- W.printNumber("Index",
- Obj.getSymbolIndex(reinterpret_cast<uintptr_t>(AuxEntPtr)));
- if (AuxEntPtr->isLabel())
- W.printNumber("ContainingCsectSymbolIndex", AuxEntPtr->SectionOrLength);
- else
- W.printNumber("SectionLen", AuxEntPtr->SectionOrLength);
- W.printHex("ParameterHashIndex", AuxEntPtr->ParameterHashIndex);
- W.printHex("TypeChkSectNum", AuxEntPtr->TypeChkSectNum);
+ W.printNumber("Index", Obj.getSymbolIndex(AuxEntRef.getEntryAddress()));
+ W.printNumber(AuxEntRef.isLabel() ? "ContainingCsectSymbolIndex"
+ : "SectionLen",
+ AuxEntRef.getSectionOrLength());
+ W.printHex("ParameterHashIndex", AuxEntRef.getParameterHashIndex());
+ W.printHex("TypeChkSectNum", AuxEntRef.getTypeChkSectNum());
// Print out symbol alignment and type.
- W.printNumber("SymbolAlignmentLog2", AuxEntPtr->getAlignmentLog2());
- W.printEnum("SymbolType", AuxEntPtr->getSymbolType(),
+ W.printNumber("SymbolAlignmentLog2", AuxEntRef.getAlignmentLog2());
+ W.printEnum("SymbolType", AuxEntRef.getSymbolType(),
makeArrayRef(CsectSymbolTypeClass));
W.printEnum("StorageMappingClass",
- static_cast<uint8_t>(AuxEntPtr->StorageMappingClass),
+ static_cast<uint8_t>(AuxEntRef.getStorageMappingClass()),
makeArrayRef(CsectStorageMappingClass));
- W.printHex("StabInfoIndex", AuxEntPtr->StabInfoIndex);
- W.printHex("StabSectNum", AuxEntPtr->StabSectNum);
+
+ if (Obj.is64Bit()) {
+ W.printEnum("Auxiliary Type", static_cast<uint8_t>(XCOFF::AUX_CSECT),
+ makeArrayRef(SymAuxType));
+ } else {
+ W.printHex("StabInfoIndex", AuxEntRef.getStabInfoIndex32());
+ W.printHex("StabSectNum", AuxEntRef.getStabSectNum32());
+ }
}
void XCOFFDumper::printSectAuxEntForStat(
@@ -301,53 +317,62 @@ static const EnumEntry<XCOFF::CFileCpuId> CFileCpuIdClass[] = {
};
void XCOFFDumper::printSymbol(const SymbolRef &S) {
- if (Obj.is64Bit())
- report_fatal_error("64-bit support is unimplemented.");
-
DataRefImpl SymbolDRI = S.getRawDataRefImpl();
- const XCOFFSymbolEntry *SymbolEntPtr = Obj.toSymbolEntry(SymbolDRI);
+ XCOFFSymbolRef SymbolEntRef = Obj.toSymbolRef(SymbolDRI);
- XCOFFSymbolRef XCOFFSymRef(SymbolDRI, &Obj);
- uint8_t NumberOfAuxEntries = XCOFFSymRef.getNumberOfAuxEntries();
+ uint8_t NumberOfAuxEntries = SymbolEntRef.getNumberOfAuxEntries();
DictScope SymDs(W, "Symbol");
StringRef SymbolName =
- unwrapOrError(Obj.getFileName(), Obj.getSymbolName(SymbolDRI));
+ unwrapOrError(Obj.getFileName(), SymbolEntRef.getName());
- W.printNumber("Index",
- Obj.getSymbolIndex(reinterpret_cast<uintptr_t>(SymbolEntPtr)));
+ W.printNumber("Index", Obj.getSymbolIndex(SymbolEntRef.getEntryAddress()));
W.printString("Name", SymbolName);
- W.printHex(GetSymbolValueName(SymbolEntPtr->StorageClass),
- SymbolEntPtr->Value);
+ W.printHex(GetSymbolValueName(SymbolEntRef.getStorageClass()),
+ SymbolEntRef.getValue());
StringRef SectionName =
- unwrapOrError(Obj.getFileName(), Obj.getSymbolSectionName(SymbolEntPtr));
+ unwrapOrError(Obj.getFileName(), Obj.getSymbolSectionName(SymbolEntRef));
W.printString("Section", SectionName);
- if (XCOFFSymRef.getStorageClass() == XCOFF::C_FILE) {
- W.printEnum("Source Language ID",
- SymbolEntPtr->CFileLanguageIdAndTypeId.LanguageId,
+ if (SymbolEntRef.getStorageClass() == XCOFF::C_FILE) {
+ W.printEnum("Source Language ID", SymbolEntRef.getLanguageIdForCFile(),
makeArrayRef(CFileLangIdClass));
- W.printEnum("CPU Version ID",
- SymbolEntPtr->CFileLanguageIdAndTypeId.CpuTypeId,
+ W.printEnum("CPU Version ID", SymbolEntRef.getCPUTypeIddForCFile(),
makeArrayRef(CFileCpuIdClass));
} else
- W.printHex("Type", SymbolEntPtr->SymbolType);
+ W.printHex("Type", SymbolEntRef.getSymbolType());
- W.printEnum("StorageClass", static_cast<uint8_t>(SymbolEntPtr->StorageClass),
+ W.printEnum("StorageClass",
+ static_cast<uint8_t>(SymbolEntRef.getStorageClass()),
makeArrayRef(SymStorageClass));
- W.printNumber("NumberOfAuxEntries", SymbolEntPtr->NumberOfAuxEntries);
+ W.printNumber("NumberOfAuxEntries", NumberOfAuxEntries);
if (NumberOfAuxEntries == 0)
return;
- switch (XCOFFSymRef.getStorageClass()) {
+ switch (SymbolEntRef.getStorageClass()) {
case XCOFF::C_FILE:
// If the symbol is C_FILE and has auxiliary entries...
- for (int i = 1; i <= NumberOfAuxEntries; i++) {
+ for (int I = 1; I <= NumberOfAuxEntries; I++) {
+ uintptr_t AuxAddress = XCOFFObjectFile::getAdvancedSymbolEntryAddress(
+ SymbolEntRef.getEntryAddress(), I);
+
+ if (Obj.is64Bit() &&
+ *Obj.getSymbolAuxType(AuxAddress) != XCOFF::SymbolAuxType::AUX_FILE) {
+ W.startLine() << "!Unexpected raw auxiliary entry data:\n";
+ W.startLine() << format_bytes(
+ ArrayRef<uint8_t>(
+ reinterpret_cast<const uint8_t *>(AuxAddress),
+ XCOFF::SymbolTableEntrySize),
+ 0, XCOFF::SymbolTableEntrySize)
+ << "\n";
+ continue;
+ }
+
const XCOFFFileAuxEnt *FileAuxEntPtr =
- reinterpret_cast<const XCOFFFileAuxEnt *>(SymbolEntPtr + i);
+ reinterpret_cast<const XCOFFFileAuxEnt *>(AuxAddress);
#ifndef NDEBUG
Obj.checkSymbolEntryPointer(reinterpret_cast<uintptr_t>(FileAuxEntPtr));
#endif
@@ -356,34 +381,52 @@ void XCOFFDumper::printSymbol(const SymbolRef &S) {
break;
case XCOFF::C_EXT:
case XCOFF::C_WEAKEXT:
- case XCOFF::C_HIDEXT:
+ case XCOFF::C_HIDEXT: {
// If the symbol is for a function, and it has more than 1 auxiliary entry,
// then one of them must be function auxiliary entry which we do not
// support yet.
- if (XCOFFSymRef.isFunction() && NumberOfAuxEntries >= 2)
+ if (SymbolEntRef.isFunction() && NumberOfAuxEntries >= 2)
report_fatal_error("Function auxiliary entry printing is unimplemented.");
// If there is more than 1 auxiliary entry, instead of printing out
- // error information, print out the raw Auxiliary entry from 1st till
- // the last - 1. The last one must be a CSECT Auxiliary Entry.
- for (int i = 1; i < NumberOfAuxEntries; i++) {
+ // error information, print out the raw Auxiliary entry.
+ // For 32-bit object, print from first to the last - 1. The last one must be
+ // a CSECT Auxiliary Entry.
+ // For 64-bit object, print from first to last and skips if SymbolAuxType is
+ // AUX_CSECT.
+ for (int I = 1; I <= NumberOfAuxEntries; I++) {
+ if (I == NumberOfAuxEntries && !Obj.is64Bit())
+ break;
+
+ uintptr_t AuxAddress = XCOFFObjectFile::getAdvancedSymbolEntryAddress(
+ SymbolEntRef.getEntryAddress(), I);
+ if (Obj.is64Bit() &&
+ *Obj.getSymbolAuxType(AuxAddress) == XCOFF::SymbolAuxType::AUX_CSECT)
+ continue;
+
W.startLine() << "!Unexpected raw auxiliary entry data:\n";
W.startLine() << format_bytes(
- ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(SymbolEntPtr + i),
+ ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(AuxAddress),
XCOFF::SymbolTableEntrySize));
}
- // The symbol's last auxiliary entry is a CSECT Auxiliary Entry.
- printCsectAuxEnt32(XCOFFSymRef.getXCOFFCsectAuxEnt32());
+ auto ErrOrCsectAuxRef = SymbolEntRef.getXCOFFCsectAuxRef();
+ if (!ErrOrCsectAuxRef)
+ reportUniqueWarning(ErrOrCsectAuxRef.takeError());
+ else
+ printCsectAuxEnt(*ErrOrCsectAuxRef);
+
break;
+ }
case XCOFF::C_STAT:
if (NumberOfAuxEntries > 1)
report_fatal_error(
"C_STAT symbol should not have more than 1 auxiliary entry.");
const XCOFFSectAuxEntForStat *StatAuxEntPtr;
- StatAuxEntPtr =
- reinterpret_cast<const XCOFFSectAuxEntForStat *>(SymbolEntPtr + 1);
+ StatAuxEntPtr = reinterpret_cast<const XCOFFSectAuxEntForStat *>(
+ XCOFFObjectFile::getAdvancedSymbolEntryAddress(
+ SymbolEntRef.getEntryAddress(), 1));
#ifndef NDEBUG
Obj.checkSymbolEntryPointer(reinterpret_cast<uintptr_t>(StatAuxEntPtr));
#endif
@@ -399,7 +442,9 @@ void XCOFFDumper::printSymbol(const SymbolRef &S) {
for (int i = 1; i <= NumberOfAuxEntries; i++) {
W.startLine() << "!Unexpected raw auxiliary entry data:\n";
W.startLine() << format_bytes(
- ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(SymbolEntPtr + i),
+ ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(
+ XCOFFObjectFile::getAdvancedSymbolEntryAddress(
+ SymbolEntRef.getEntryAddress(), i)),
XCOFF::SymbolTableEntrySize));
}
break;
@@ -412,6 +457,12 @@ void XCOFFDumper::printSymbols() {
printSymbol(S);
}
+void XCOFFDumper::printStringTable() {
+ DictScope DS(W, "StringTable");
+ StringRef StrTable = Obj.getStringTable();
+ printAsStringList(StrTable);
+}
+
void XCOFFDumper::printDynamicSymbols() {
llvm_unreachable("Unimplemented functionality for XCOFFDumper");
}
diff --git a/llvm/tools/llvm-readobj/llvm-readobj.cpp b/llvm/tools/llvm-readobj/llvm-readobj.cpp
index 41cd4414d051..0b49f03f4275 100644
--- a/llvm/tools/llvm-readobj/llvm-readobj.cpp
+++ b/llvm/tools/llvm-readobj/llvm-readobj.cpp
@@ -31,6 +31,9 @@
#include "llvm/Object/Wasm.h"
#include "llvm/Object/WindowsResource.h"
#include "llvm/Object/XCOFFObjectFile.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/Option.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/DataTypes.h"
@@ -47,339 +50,107 @@
using namespace llvm;
using namespace llvm::object;
+namespace {
+using namespace llvm::opt; // for HelpHidden in Opts.inc
+enum ID {
+ OPT_INVALID = 0, // This is not an option ID.
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ OPT_##ID,
+#include "Opts.inc"
+#undef OPTION
+};
+
+#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
+#include "Opts.inc"
+#undef PREFIX
+
+static const opt::OptTable::Info InfoTable[] = {
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ { \
+ PREFIX, NAME, HELPTEXT, \
+ METAVAR, OPT_##ID, opt::Option::KIND##Class, \
+ PARAM, FLAGS, OPT_##GROUP, \
+ OPT_##ALIAS, ALIASARGS, VALUES},
+#include "Opts.inc"
+#undef OPTION
+};
+
+class ReadobjOptTable : public opt::OptTable {
+public:
+ ReadobjOptTable() : OptTable(InfoTable) { setGroupedShortOptions(true); }
+};
+
+enum OutputFormatTy { bsd, sysv, posix, darwin, just_symbols };
+} // namespace
+
namespace opts {
- cl::list<std::string> InputFilenames(cl::Positional,
- cl::desc("<input object files>"),
- cl::ZeroOrMore);
-
- // --all, -a
- cl::opt<bool>
- All("all",
- cl::desc("Equivalent to setting: --file-headers, --program-headers, "
- "--section-headers, --symbols, --relocations, "
- "--dynamic-table, --notes, --version-info, --unwind, "
- "--section-groups and --elf-hash-histogram."));
- cl::alias AllShort("a", cl::desc("Alias for --all"), cl::aliasopt(All));
-
- // --dependent-libraries
- cl::opt<bool>
- DependentLibraries("dependent-libraries",
- cl::desc("Display the dependent libraries section"));
-
- // --headers, -e
- cl::opt<bool>
- Headers("headers",
- cl::desc("Equivalent to setting: --file-headers, --program-headers, "
- "--section-headers"));
- cl::alias HeadersShort("e", cl::desc("Alias for --headers"),
- cl::aliasopt(Headers));
-
- // --wide, -W
- cl::opt<bool>
- WideOutput("wide", cl::desc("Ignored for compatibility with GNU readelf"),
- cl::Hidden);
- cl::alias WideOutputShort("W",
- cl::desc("Alias for --wide"),
- cl::aliasopt(WideOutput));
-
- // --file-headers, --file-header, -h
- cl::opt<bool> FileHeaders("file-headers",
- cl::desc("Display file headers "));
- cl::alias FileHeadersShort("h", cl::desc("Alias for --file-headers"),
- cl::aliasopt(FileHeaders), cl::NotHidden);
- cl::alias FileHeadersSingular("file-header",
- cl::desc("Alias for --file-headers"),
- cl::aliasopt(FileHeaders));
-
- // --section-headers, --sections, -S
- // Also -s in llvm-readobj mode.
- cl::opt<bool> SectionHeaders("section-headers",
- cl::desc("Display all section headers."));
- cl::alias SectionsShortUpper("S", cl::desc("Alias for --section-headers"),
- cl::aliasopt(SectionHeaders), cl::NotHidden);
- cl::alias SectionHeadersAlias("sections",
- cl::desc("Alias for --section-headers"),
- cl::aliasopt(SectionHeaders), cl::NotHidden);
-
- // --section-relocations
- // Also --sr in llvm-readobj mode.
- cl::opt<bool> SectionRelocations("section-relocations",
- cl::desc("Display relocations for each section shown."));
-
- // --section-symbols
- // Also --st in llvm-readobj mode.
- cl::opt<bool> SectionSymbols("section-symbols",
- cl::desc("Display symbols for each section shown."));
-
- // --section-data
- // Also --sd in llvm-readobj mode.
- cl::opt<bool> SectionData("section-data",
- cl::desc("Display section data for each section shown."));
-
- // --section-mapping
- cl::opt<cl::boolOrDefault>
- SectionMapping("section-mapping",
- cl::desc("Display the section to segment mapping."));
-
- // --relocations, --relocs, -r
- cl::opt<bool> Relocations("relocations",
- cl::desc("Display the relocation entries in the file"));
- cl::alias RelocationsShort("r", cl::desc("Alias for --relocations"),
- cl::aliasopt(Relocations), cl::NotHidden);
- cl::alias RelocationsGNU("relocs", cl::desc("Alias for --relocations"),
- cl::aliasopt(Relocations));
-
- // --notes, -n
- cl::opt<bool> Notes("notes", cl::desc("Display the ELF notes in the file"));
- cl::alias NotesShort("n", cl::desc("Alias for --notes"), cl::aliasopt(Notes));
-
- // --dyn-relocations
- cl::opt<bool> DynRelocs("dyn-relocations",
- cl::desc("Display the dynamic relocation entries in the file"));
-
- // --section-details
- // Also -t in llvm-readelf mode.
- cl::opt<bool> SectionDetails("section-details",
- cl::desc("Display the section details"));
-
- // --symbols
- // Also -s in llvm-readelf mode, or -t in llvm-readobj mode.
- cl::opt<bool>
- Symbols("symbols",
- cl::desc("Display the symbol table. Also display the dynamic "
- "symbol table when using GNU output style for ELF"));
- cl::alias SymbolsGNU("syms", cl::desc("Alias for --symbols"),
- cl::aliasopt(Symbols));
-
- // --dyn-symbols, --dyn-syms
- // Also --dt in llvm-readobj mode.
- cl::opt<bool> DynamicSymbols("dyn-symbols",
- cl::desc("Display the dynamic symbol table"));
- cl::alias DynSymsGNU("dyn-syms", cl::desc("Alias for --dyn-symbols"),
- cl::aliasopt(DynamicSymbols));
-
- // --hash-symbols
- cl::opt<bool> HashSymbols(
- "hash-symbols",
- cl::desc("Display the dynamic symbols derived from the hash section"));
-
- // --unwind, -u
- cl::opt<bool> UnwindInfo("unwind",
- cl::desc("Display unwind information"));
- cl::alias UnwindInfoShort("u",
- cl::desc("Alias for --unwind"),
- cl::aliasopt(UnwindInfo));
-
- // --dynamic-table, --dynamic, -d
- cl::opt<bool> DynamicTable("dynamic-table",
- cl::desc("Display the ELF .dynamic section table"));
- cl::alias DynamicTableShort("d", cl::desc("Alias for --dynamic-table"),
- cl::aliasopt(DynamicTable), cl::NotHidden);
- cl::alias DynamicTableAlias("dynamic", cl::desc("Alias for --dynamic-table"),
- cl::aliasopt(DynamicTable));
-
- // --needed-libs
- cl::opt<bool> NeededLibraries("needed-libs",
- cl::desc("Display the needed libraries"));
-
- // --program-headers, --segments, -l
- cl::opt<bool> ProgramHeaders("program-headers",
- cl::desc("Display ELF program headers"));
- cl::alias ProgramHeadersShort("l", cl::desc("Alias for --program-headers"),
- cl::aliasopt(ProgramHeaders), cl::NotHidden);
- cl::alias SegmentsAlias("segments", cl::desc("Alias for --program-headers"),
- cl::aliasopt(ProgramHeaders));
-
- // --string-dump, -p
- cl::list<std::string> StringDump(
- "string-dump", cl::value_desc("number|name"),
- cl::desc("Display the specified section(s) as a list of strings"),
- cl::ZeroOrMore);
- cl::alias StringDumpShort("p", cl::desc("Alias for --string-dump"),
- cl::aliasopt(StringDump), cl::Prefix);
-
- // --hex-dump, -x
- cl::list<std::string>
- HexDump("hex-dump", cl::value_desc("number|name"),
- cl::desc("Display the specified section(s) as hexadecimal bytes"),
- cl::ZeroOrMore);
- cl::alias HexDumpShort("x", cl::desc("Alias for --hex-dump"),
- cl::aliasopt(HexDump), cl::Prefix);
-
- // --demangle, -C
- cl::opt<bool> Demangle("demangle",
- cl::desc("Demangle symbol names in output"));
- cl::alias DemangleShort("C", cl::desc("Alias for --demangle"),
- cl::aliasopt(Demangle), cl::NotHidden);
-
- // --hash-table
- cl::opt<bool> HashTable("hash-table",
- cl::desc("Display ELF hash table"));
-
- // --gnu-hash-table
- cl::opt<bool> GnuHashTable("gnu-hash-table",
- cl::desc("Display ELF .gnu.hash section"));
-
- // --expand-relocs
- cl::opt<bool> ExpandRelocs("expand-relocs",
- cl::desc("Expand each shown relocation to multiple lines"));
-
- // --raw-relr
- cl::opt<bool> RawRelr("raw-relr",
- cl::desc("Do not decode relocations in SHT_RELR section, display raw contents"));
-
- // --codeview
- 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-ghash
- cl::opt<bool> CodeViewEnableGHash(
- "codeview-ghash",
- cl::desc(
- "Enable global hashing for CodeView type stream de-duplication"));
-
- // --codeview-subsection-bytes
- cl::opt<bool> CodeViewSubsectionBytes(
- "codeview-subsection-bytes",
- cl::desc("Dump raw contents of codeview debug sections and records"));
-
- // --arch-specific
- cl::opt<bool> ArchSpecificInfo("arch-specific",
- cl::desc("Displays architecture-specific information, if there is any."));
- cl::alias ArchSpecifcInfoShort("A", cl::desc("Alias for --arch-specific"),
- cl::aliasopt(ArchSpecificInfo), cl::NotHidden);
-
- // --coff-imports
- cl::opt<bool>
- COFFImports("coff-imports", cl::desc("Display the PE/COFF import table"));
-
- // --coff-exports
- cl::opt<bool>
- COFFExports("coff-exports", cl::desc("Display the PE/COFF export table"));
-
- // --coff-directives
- cl::opt<bool>
- COFFDirectives("coff-directives",
- cl::desc("Display the PE/COFF .drectve section"));
-
- // --coff-basereloc
- cl::opt<bool>
- 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"));
-
- // --coff-tls-directory
- cl::opt<bool> COFFTLSDirectory("coff-tls-directory",
- cl::desc("Display the PE/COFF TLS directory"));
-
- // --coff-resources
- cl::opt<bool> COFFResources("coff-resources",
- cl::desc("Display the PE/COFF .rsrc section"));
-
- // --coff-load-config
- cl::opt<bool>
- COFFLoadConfig("coff-load-config",
- cl::desc("Display the PE/COFF load config"));
-
- // --elf-linker-options
- cl::opt<bool>
- ELFLinkerOptions("elf-linker-options",
- cl::desc("Display the ELF .linker-options section"));
-
- // --macho-data-in-code
- cl::opt<bool>
- MachODataInCode("macho-data-in-code",
- cl::desc("Display MachO Data in Code command"));
-
- // --macho-indirect-symbols
- cl::opt<bool>
- MachOIndirectSymbols("macho-indirect-symbols",
- cl::desc("Display MachO indirect symbols"));
-
- // --macho-linker-options
- cl::opt<bool>
- MachOLinkerOptions("macho-linker-options",
- cl::desc("Display MachO linker options"));
-
- // --macho-segment
- cl::opt<bool>
- MachOSegment("macho-segment",
- cl::desc("Display MachO Segment command"));
-
- // --macho-version-min
- cl::opt<bool>
- MachOVersionMin("macho-version-min",
- cl::desc("Display MachO version min command"));
-
- // --macho-dysymtab
- cl::opt<bool>
- MachODysymtab("macho-dysymtab",
- cl::desc("Display MachO Dysymtab command"));
-
- // --stackmap
- cl::opt<bool>
- PrintStackMap("stackmap",
- cl::desc("Display contents of stackmap section"));
-
- // --stack-sizes
- cl::opt<bool>
- PrintStackSizes("stack-sizes",
- cl::desc("Display contents of all stack sizes sections"));
-
- // --version-info, -V
- cl::opt<bool>
- VersionInfo("version-info",
- cl::desc("Display ELF version sections (if present)"));
- cl::alias VersionInfoShort("V", cl::desc("Alias for -version-info"),
- cl::aliasopt(VersionInfo));
-
- // --elf-section-groups, --section-groups, -g
- cl::opt<bool> SectionGroups("elf-section-groups",
- cl::desc("Display ELF section group contents"));
- cl::alias SectionGroupsAlias("section-groups",
- cl::desc("Alias for -elf-sections-groups"),
- cl::aliasopt(SectionGroups));
- cl::alias SectionGroupsShort("g", cl::desc("Alias for -elf-sections-groups"),
- cl::aliasopt(SectionGroups));
-
- // --elf-hash-histogram, --histogram, -I
- 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::alias HistogramAlias("histogram",
- cl::desc("Alias for --elf-hash-histogram"),
- cl::aliasopt(HashHistogram));
-
- // --cg-profile
- cl::opt<bool> CGProfile("cg-profile",
- cl::desc("Display callgraph profile section"));
- cl::alias ELFCGProfile("elf-cg-profile", cl::desc("Alias for --cg-profile"),
- cl::aliasopt(CGProfile));
-
- // -addrsig
- cl::opt<bool> Addrsig("addrsig",
- cl::desc("Display address-significance table"));
-
- // -elf-output-style
- 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")),
- cl::init(LLVM));
-
- cl::extrahelp
- HelpResponse("\nPass @FILE as argument to read options from FILE.\n");
+static bool Addrsig;
+static bool All;
+static bool ArchSpecificInfo;
+static bool BBAddrMap;
+bool ExpandRelocs;
+static bool CGProfile;
+bool Demangle;
+static bool DependentLibraries;
+static bool DynRelocs;
+static bool DynamicSymbols;
+static bool FileHeaders;
+static bool Headers;
+static std::vector<std::string> HexDump;
+static bool PrintStackMap;
+static bool PrintStackSizes;
+static bool Relocations;
+bool SectionData;
+static bool SectionDetails;
+static bool SectionHeaders;
+bool SectionRelocations;
+bool SectionSymbols;
+static std::vector<std::string> StringDump;
+static bool StringTable;
+static bool Symbols;
+static bool UnwindInfo;
+static cl::boolOrDefault SectionMapping;
+
+// ELF specific options.
+static bool DynamicTable;
+static bool ELFLinkerOptions;
+static bool GnuHashTable;
+static bool HashSymbols;
+static bool HashTable;
+static bool HashHistogram;
+static bool NeededLibraries;
+static bool Notes;
+static bool ProgramHeaders;
+bool RawRelr;
+static bool SectionGroups;
+static bool VersionInfo;
+
+// Mach-O specific options.
+static bool MachODataInCode;
+static bool MachODysymtab;
+static bool MachOIndirectSymbols;
+static bool MachOLinkerOptions;
+static bool MachOSegment;
+static bool MachOVersionMin;
+
+// PE/COFF specific options.
+static bool CodeView;
+static bool CodeViewEnableGHash;
+static bool CodeViewMergedTypes;
+bool CodeViewSubsectionBytes;
+static bool COFFBaseRelocs;
+static bool COFFDebugDirectory;
+static bool COFFDirectives;
+static bool COFFExports;
+static bool COFFImports;
+static bool COFFLoadConfig;
+static bool COFFResources;
+static bool COFFTLSDirectory;
+
+OutputStyleTy Output = OutputStyleTy::LLVM;
+static std::vector<std::string> InputFilenames;
} // namespace opts
static StringRef ToolName;
@@ -419,6 +190,87 @@ void reportWarning(Error Err, StringRef Input) {
} // namespace llvm
+static void parseOptions(const opt::InputArgList &Args) {
+ opts::Addrsig = Args.hasArg(OPT_addrsig);
+ opts::All = Args.hasArg(OPT_all);
+ opts::ArchSpecificInfo = Args.hasArg(OPT_arch_specific);
+ opts::BBAddrMap = Args.hasArg(OPT_bb_addr_map);
+ opts::CGProfile = Args.hasArg(OPT_cg_profile);
+ opts::Demangle = Args.hasFlag(OPT_demangle, OPT_no_demangle, false);
+ opts::DependentLibraries = Args.hasArg(OPT_dependent_libraries);
+ opts::DynRelocs = Args.hasArg(OPT_dyn_relocations);
+ opts::DynamicSymbols = Args.hasArg(OPT_dyn_syms);
+ opts::ExpandRelocs = Args.hasArg(OPT_expand_relocs);
+ opts::FileHeaders = Args.hasArg(OPT_file_header);
+ opts::Headers = Args.hasArg(OPT_headers);
+ opts::HexDump = Args.getAllArgValues(OPT_hex_dump_EQ);
+ opts::Relocations = Args.hasArg(OPT_relocs);
+ opts::SectionData = Args.hasArg(OPT_section_data);
+ opts::SectionDetails = Args.hasArg(OPT_section_details);
+ opts::SectionHeaders = Args.hasArg(OPT_section_headers);
+ opts::SectionRelocations = Args.hasArg(OPT_section_relocations);
+ opts::SectionSymbols = Args.hasArg(OPT_section_symbols);
+ if (Args.hasArg(OPT_section_mapping))
+ opts::SectionMapping = cl::BOU_TRUE;
+ else if (Args.hasArg(OPT_section_mapping_EQ_false))
+ opts::SectionMapping = cl::BOU_FALSE;
+ else
+ opts::SectionMapping = cl::BOU_UNSET;
+ opts::PrintStackSizes = Args.hasArg(OPT_stack_sizes);
+ opts::PrintStackMap = Args.hasArg(OPT_stackmap);
+ opts::StringDump = Args.getAllArgValues(OPT_string_dump_EQ);
+ opts::StringTable = Args.hasArg(OPT_string_table);
+ opts::Symbols = Args.hasArg(OPT_symbols);
+ opts::UnwindInfo = Args.hasArg(OPT_unwind);
+
+ // ELF specific options.
+ opts::DynamicTable = Args.hasArg(OPT_dynamic_table);
+ opts::ELFLinkerOptions = Args.hasArg(OPT_elf_linker_options);
+ if (Arg *A = Args.getLastArg(OPT_elf_output_style_EQ)) {
+ StringRef V(A->getValue());
+ if (V == "LLVM")
+ opts::Output = opts::OutputStyleTy::LLVM;
+ else if (V == "GNU")
+ opts::Output = opts::OutputStyleTy::GNU;
+ else
+ error("--elf-output-style value should be either 'LLVM' or 'GNU'");
+ }
+ opts::GnuHashTable = Args.hasArg(OPT_gnu_hash_table);
+ opts::HashSymbols = Args.hasArg(OPT_hash_symbols);
+ opts::HashTable = Args.hasArg(OPT_hash_table);
+ opts::HashHistogram = Args.hasArg(OPT_histogram);
+ opts::NeededLibraries = Args.hasArg(OPT_needed_libs);
+ opts::Notes = Args.hasArg(OPT_notes);
+ opts::ProgramHeaders = Args.hasArg(OPT_program_headers);
+ opts::RawRelr = Args.hasArg(OPT_raw_relr);
+ opts::SectionGroups = Args.hasArg(OPT_section_groups);
+ opts::VersionInfo = Args.hasArg(OPT_version_info);
+
+ // Mach-O specific options.
+ opts::MachODataInCode = Args.hasArg(OPT_macho_data_in_code);
+ opts::MachODysymtab = Args.hasArg(OPT_macho_dysymtab);
+ opts::MachOIndirectSymbols = Args.hasArg(OPT_macho_indirect_symbols);
+ opts::MachOLinkerOptions = Args.hasArg(OPT_macho_linker_options);
+ opts::MachOSegment = Args.hasArg(OPT_macho_segment);
+ opts::MachOVersionMin = Args.hasArg(OPT_macho_version_min);
+
+ // PE/COFF specific options.
+ opts::CodeView = Args.hasArg(OPT_codeview);
+ opts::CodeViewEnableGHash = Args.hasArg(OPT_codeview_ghash);
+ opts::CodeViewMergedTypes = Args.hasArg(OPT_codeview_merged_types);
+ opts::CodeViewSubsectionBytes = Args.hasArg(OPT_codeview_subsection_bytes);
+ opts::COFFBaseRelocs = Args.hasArg(OPT_coff_basereloc);
+ opts::COFFDebugDirectory = Args.hasArg(OPT_coff_debug_directory);
+ opts::COFFDirectives = Args.hasArg(OPT_coff_directives);
+ opts::COFFExports = Args.hasArg(OPT_coff_exports);
+ opts::COFFImports = Args.hasArg(OPT_coff_imports);
+ opts::COFFLoadConfig = Args.hasArg(OPT_coff_load_config);
+ opts::COFFResources = Args.hasArg(OPT_coff_resources);
+ opts::COFFTLSDirectory = Args.hasArg(OPT_coff_tls_directory);
+
+ opts::InputFilenames = Args.getAllArgValues(OPT_INPUT);
+}
+
namespace {
struct ReadObjTypeTableBuilder {
ReadObjTypeTableBuilder()
@@ -529,6 +381,8 @@ static void dumpObject(ObjectFile &Obj, ScopedPrinter &Writer,
Dumper->printGnuHashTable();
if (opts::VersionInfo)
Dumper->printVersionInfo();
+ if (opts::StringTable)
+ Dumper->printStringTable();
if (Obj.isELF()) {
if (opts::DependentLibraries)
Dumper->printDependentLibs();
@@ -542,6 +396,8 @@ static void dumpObject(ObjectFile &Obj, ScopedPrinter &Writer,
Dumper->printHashHistograms();
if (opts::CGProfile)
Dumper->printCGProfile();
+ if (opts::BBAddrMap)
+ Dumper->printBBAddrMaps();
if (opts::Addrsig)
Dumper->printAddrsig();
if (opts::Notes)
@@ -646,93 +502,73 @@ static void dumpWindowsResourceFile(WindowsResource *WinRes,
/// Opens \a File and dumps it.
static void dumpInput(StringRef File, ScopedPrinter &Writer) {
- // Attempt to open the binary.
- Expected<OwningBinary<Binary>> BinaryOrErr =
- createBinary(File, /*Context=*/nullptr, /*InitContent=*/false);
+ ErrorOr<std::unique_ptr<MemoryBuffer>> FileOrErr =
+ MemoryBuffer::getFileOrSTDIN(File, /*IsText=*/false,
+ /*RequiresNullTerminator=*/false);
+ if (std::error_code EC = FileOrErr.getError())
+ return reportError(errorCodeToError(EC), File);
+
+ std::unique_ptr<MemoryBuffer> &Buffer = FileOrErr.get();
+ file_magic Type = identify_magic(Buffer->getBuffer());
+ if (Type == file_magic::bitcode) {
+ reportWarning(createStringError(errc::invalid_argument,
+ "bitcode files are not supported"),
+ File);
+ return;
+ }
+
+ Expected<std::unique_ptr<Binary>> BinaryOrErr = createBinary(
+ Buffer->getMemBufferRef(), /*Context=*/nullptr, /*InitContent=*/false);
if (!BinaryOrErr)
reportError(BinaryOrErr.takeError(), File);
- Binary &Binary = *BinaryOrErr.get().getBinary();
- if (Archive *Arc = dyn_cast<Archive>(&Binary))
+ std::unique_ptr<Binary> Bin = std::move(*BinaryOrErr);
+ if (Archive *Arc = dyn_cast<Archive>(Bin.get()))
dumpArchive(Arc, Writer);
else if (MachOUniversalBinary *UBinary =
- dyn_cast<MachOUniversalBinary>(&Binary))
+ dyn_cast<MachOUniversalBinary>(Bin.get()))
dumpMachOUniversalBinary(UBinary, Writer);
- else if (ObjectFile *Obj = dyn_cast<ObjectFile>(&Binary))
+ else if (ObjectFile *Obj = dyn_cast<ObjectFile>(Bin.get()))
dumpObject(*Obj, Writer);
- else if (COFFImportFile *Import = dyn_cast<COFFImportFile>(&Binary))
+ else if (COFFImportFile *Import = dyn_cast<COFFImportFile>(Bin.get()))
dumpCOFFImportFile(Import, Writer);
- else if (WindowsResource *WinRes = dyn_cast<WindowsResource>(&Binary))
+ else if (WindowsResource *WinRes = dyn_cast<WindowsResource>(Bin.get()))
dumpWindowsResourceFile(WinRes, Writer);
else
llvm_unreachable("unrecognized file type");
- CVTypes.Binaries.push_back(std::move(*BinaryOrErr));
-}
-
-/// Registers aliases that should only be allowed by readobj.
-static void registerReadobjAliases() {
- // -s has meant --sections for a very long time in llvm-readobj despite
- // meaning --symbols in readelf.
- static cl::alias SectionsShort("s", cl::desc("Alias for --section-headers"),
- cl::aliasopt(opts::SectionHeaders),
- cl::NotHidden);
-
- // llvm-readelf reserves it for --section-details.
- static cl::alias SymbolsShort("t", cl::desc("Alias for --symbols"),
- cl::aliasopt(opts::Symbols), cl::NotHidden);
-
- // The following two-letter aliases are only provided for readobj, as readelf
- // allows single-letter args to be grouped together.
- static cl::alias SectionRelocationsShort(
- "sr", cl::desc("Alias for --section-relocations"),
- cl::aliasopt(opts::SectionRelocations));
- static cl::alias SectionDataShort("sd", cl::desc("Alias for --section-data"),
- cl::aliasopt(opts::SectionData));
- static cl::alias SectionSymbolsShort("st",
- cl::desc("Alias for --section-symbols"),
- cl::aliasopt(opts::SectionSymbols));
- static cl::alias DynamicSymbolsShort("dt",
- cl::desc("Alias for --dyn-symbols"),
- cl::aliasopt(opts::DynamicSymbols));
-}
-
-/// Registers aliases that should only be allowed by readelf.
-static void registerReadelfAliases() {
- // -s is here because for readobj it means --sections.
- static cl::alias SymbolsShort("s", cl::desc("Alias for --symbols"),
- cl::aliasopt(opts::Symbols), cl::NotHidden,
- cl::Grouping);
-
- // -t is here because for readobj it is an alias for --symbols.
- static cl::alias SectionDetailsShort(
- "t", cl::desc("Alias for --section-details"),
- cl::aliasopt(opts::SectionDetails), cl::NotHidden);
-
- // Allow all single letter flags to be grouped together.
- for (auto &OptEntry : cl::getRegisteredOptions()) {
- StringRef ArgName = OptEntry.getKey();
- cl::Option *Option = OptEntry.getValue();
- if (ArgName.size() == 1)
- apply(Option, cl::Grouping);
- }
+ CVTypes.Binaries.push_back(
+ OwningBinary<Binary>(std::move(Bin), std::move(Buffer)));
}
-int main(int argc, const char *argv[]) {
+int main(int argc, char *argv[]) {
InitLLVM X(argc, argv);
+ BumpPtrAllocator A;
+ StringSaver Saver(A);
+ ReadobjOptTable Tbl;
ToolName = argv[0];
-
- // Register the target printer for --version.
- cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion);
-
- if (sys::path::stem(argv[0]).contains("readelf")) {
- opts::Output = opts::GNU;
- registerReadelfAliases();
- } else {
- registerReadobjAliases();
+ opt::InputArgList Args =
+ Tbl.parseArgs(argc, argv, OPT_UNKNOWN, Saver, [&](StringRef Msg) {
+ error(Msg);
+ exit(1);
+ });
+ if (Args.hasArg(OPT_help)) {
+ Tbl.printHelp(
+ outs(),
+ (Twine(ToolName) + " [options] <input object files>").str().c_str(),
+ "LLVM Object Reader");
+ // TODO Replace this with OptTable API once it adds extrahelp support.
+ outs() << "\nPass @FILE as argument to read options from FILE.\n";
+ return 0;
+ }
+ if (Args.hasArg(OPT_version)) {
+ cl::PrintVersionMessage();
+ return 0;
}
- cl::ParseCommandLineOptions(argc, argv, "LLVM Object Reader\n");
+ if (sys::path::stem(argv[0]).contains("readelf"))
+ opts::Output = opts::GNU;
+ parseOptions(Args);
// Default to print error if no filename is specified.
if (opts::InputFilenames.empty()) {
diff --git a/llvm/tools/llvm-readobj/llvm-readobj.h b/llvm/tools/llvm-readobj/llvm-readobj.h
index d9813f5dea62..43d19b4d3f5c 100644
--- a/llvm/tools/llvm-readobj/llvm-readobj.h
+++ b/llvm/tools/llvm-readobj/llvm-readobj.h
@@ -32,15 +32,15 @@ namespace llvm {
} // namespace llvm
namespace opts {
- extern llvm::cl::opt<bool> SectionRelocations;
- extern llvm::cl::opt<bool> SectionSymbols;
- extern llvm::cl::opt<bool> SectionData;
- extern llvm::cl::opt<bool> ExpandRelocs;
- extern llvm::cl::opt<bool> RawRelr;
- extern llvm::cl::opt<bool> CodeViewSubsectionBytes;
- extern llvm::cl::opt<bool> Demangle;
- enum OutputStyleTy { LLVM, GNU };
- extern llvm::cl::opt<OutputStyleTy> Output;
+extern bool SectionRelocations;
+extern bool SectionSymbols;
+extern bool SectionData;
+extern bool ExpandRelocs;
+extern bool RawRelr;
+extern bool CodeViewSubsectionBytes;
+extern bool Demangle;
+enum OutputStyleTy { LLVM, GNU };
+extern OutputStyleTy Output;
} // namespace opts
#define LLVM_READOBJ_ENUM_ENT(ns, enum) \
diff --git a/llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp b/llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp
index 919943129311..f02d8981b30e 100644
--- a/llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp
+++ b/llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp
@@ -27,6 +27,7 @@
#include "llvm/Object/SymbolSize.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/DynamicLibrary.h"
+#include "llvm/Support/FileSystem.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/MSVCErrorWorkarounds.h"
#include "llvm/Support/Memory.h"
@@ -43,9 +44,11 @@
using namespace llvm;
using namespace llvm::object;
-static cl::list<std::string>
-InputFileList(cl::Positional, cl::ZeroOrMore,
- cl::desc("<input files>"));
+static cl::OptionCategory RTDyldCategory("RTDyld Options");
+
+static cl::list<std::string> InputFileList(cl::Positional, cl::ZeroOrMore,
+ cl::desc("<input files>"),
+ cl::cat(RTDyldCategory));
enum ActionType {
AC_Execute,
@@ -55,94 +58,93 @@ enum ActionType {
AC_Verify
};
-static cl::opt<ActionType>
-Action(cl::desc("Action to perform:"),
- cl::init(AC_Execute),
- cl::values(clEnumValN(AC_Execute, "execute",
- "Load, link, and execute the inputs."),
- clEnumValN(AC_PrintLineInfo, "printline",
- "Load, link, and print line information for each function."),
- clEnumValN(AC_PrintDebugLineInfo, "printdebugline",
- "Load, link, and print line information for each function using the debug object"),
- clEnumValN(AC_PrintObjectLineInfo, "printobjline",
- "Like -printlineinfo but does not load the object first"),
- clEnumValN(AC_Verify, "verify",
- "Load, link and verify the resulting memory image.")));
+static cl::opt<ActionType> Action(
+ cl::desc("Action to perform:"), cl::init(AC_Execute),
+ cl::values(
+ clEnumValN(AC_Execute, "execute",
+ "Load, link, and execute the inputs."),
+ clEnumValN(AC_PrintLineInfo, "printline",
+ "Load, link, and print line information for each function."),
+ clEnumValN(AC_PrintDebugLineInfo, "printdebugline",
+ "Load, link, and print line information for each function "
+ "using the debug object"),
+ clEnumValN(AC_PrintObjectLineInfo, "printobjline",
+ "Like -printlineinfo but does not load the object first"),
+ clEnumValN(AC_Verify, "verify",
+ "Load, link and verify the resulting memory image.")),
+ cl::cat(RTDyldCategory));
static cl::opt<std::string>
-EntryPoint("entry",
- cl::desc("Function to call as entry point."),
- cl::init("_main"));
+ EntryPoint("entry", cl::desc("Function to call as entry point."),
+ cl::init("_main"), cl::cat(RTDyldCategory));
-static cl::list<std::string>
-Dylibs("dylib",
- cl::desc("Add library."),
- cl::ZeroOrMore);
+static cl::list<std::string> Dylibs("dylib", cl::desc("Add library."),
+ cl::ZeroOrMore, cl::cat(RTDyldCategory));
static cl::list<std::string> InputArgv("args", cl::Positional,
cl::desc("<program arguments>..."),
- cl::ZeroOrMore, cl::PositionalEatsArgs);
+ cl::ZeroOrMore, cl::PositionalEatsArgs,
+ cl::cat(RTDyldCategory));
static cl::opt<std::string>
-TripleName("triple", cl::desc("Target triple for disassembler"));
+ TripleName("triple", cl::desc("Target triple for disassembler"),
+ cl::cat(RTDyldCategory));
static cl::opt<std::string>
-MCPU("mcpu",
- cl::desc("Target a specific cpu type (-mcpu=help for details)"),
- cl::value_desc("cpu-name"),
- cl::init(""));
+ MCPU("mcpu",
+ cl::desc("Target a specific cpu type (-mcpu=help for details)"),
+ cl::value_desc("cpu-name"), cl::init(""), cl::cat(RTDyldCategory));
static cl::list<std::string>
-CheckFiles("check",
- cl::desc("File containing RuntimeDyld verifier checks."),
- cl::ZeroOrMore);
+ CheckFiles("check",
+ cl::desc("File containing RuntimeDyld verifier checks."),
+ cl::ZeroOrMore, cl::cat(RTDyldCategory));
static cl::opt<uint64_t>
PreallocMemory("preallocate",
cl::desc("Allocate memory upfront rather than on-demand"),
- cl::init(0));
+ cl::init(0), cl::cat(RTDyldCategory));
static cl::opt<uint64_t> TargetAddrStart(
"target-addr-start",
cl::desc("For -verify only: start of phony target address "
"range."),
cl::init(4096), // Start at "page 1" - no allocating at "null".
- cl::Hidden);
+ cl::Hidden, cl::cat(RTDyldCategory));
static cl::opt<uint64_t> TargetAddrEnd(
"target-addr-end",
cl::desc("For -verify only: end of phony target address range."),
- cl::init(~0ULL), cl::Hidden);
+ cl::init(~0ULL), cl::Hidden, cl::cat(RTDyldCategory));
static cl::opt<uint64_t> TargetSectionSep(
"target-section-sep",
cl::desc("For -verify only: Separation between sections in "
"phony target address space."),
- cl::init(0), cl::Hidden);
-
-static cl::list<std::string>
-SpecificSectionMappings("map-section",
- cl::desc("For -verify only: Map a section to a "
- "specific address."),
- cl::ZeroOrMore,
- cl::Hidden);
+ cl::init(0), cl::Hidden, cl::cat(RTDyldCategory));
static cl::list<std::string>
-DummySymbolMappings("dummy-extern",
- cl::desc("For -verify only: Inject a symbol into the extern "
- "symbol table."),
- cl::ZeroOrMore,
- cl::Hidden);
-
-static cl::opt<bool>
-PrintAllocationRequests("print-alloc-requests",
- cl::desc("Print allocation requests made to the memory "
- "manager by RuntimeDyld"),
- cl::Hidden);
+ SpecificSectionMappings("map-section",
+ cl::desc("For -verify only: Map a section to a "
+ "specific address."),
+ cl::ZeroOrMore, cl::Hidden,
+ cl::cat(RTDyldCategory));
+
+static cl::list<std::string> DummySymbolMappings(
+ "dummy-extern",
+ cl::desc("For -verify only: Inject a symbol into the extern "
+ "symbol table."),
+ cl::ZeroOrMore, cl::Hidden, cl::cat(RTDyldCategory));
+
+static cl::opt<bool> PrintAllocationRequests(
+ "print-alloc-requests",
+ cl::desc("Print allocation requests made to the memory "
+ "manager by RuntimeDyld"),
+ cl::Hidden, cl::cat(RTDyldCategory));
static cl::opt<bool> ShowTimes("show-times",
cl::desc("Show times for llvm-rtdyld phases"),
- cl::init(false));
+ cl::init(false), cl::cat(RTDyldCategory));
ExitOnError ExitOnErr;
@@ -756,7 +758,7 @@ static int linkAndVerify() {
if (!MAI)
ErrorAndExit("Unable to create target asm info!");
- MCContext Ctx(MAI.get(), MRI.get(), nullptr);
+ MCContext Ctx(Triple(TripleName), MAI.get(), MRI.get(), STI.get());
std::unique_ptr<MCDisassembler> Disassembler(
TheTarget->createMCDisassembler(*STI, Ctx));
@@ -840,7 +842,7 @@ static int linkAndVerify() {
char *CSymAddr = static_cast<char *>(SymAddr);
StringRef SecContent = Dyld.getSectionContent(SectionID);
uint64_t SymSize = SecContent.size() - (CSymAddr - SecContent.data());
- SymInfo.setContent(StringRef(CSymAddr, SymSize));
+ SymInfo.setContent(ArrayRef<char>(CSymAddr, SymSize));
}
}
return SymInfo;
@@ -867,7 +869,8 @@ static int linkAndVerify() {
return SectionID.takeError();
RuntimeDyldChecker::MemoryRegionInfo SecInfo;
SecInfo.setTargetAddress(Dyld.getSectionLoadAddress(*SectionID));
- SecInfo.setContent(Dyld.getSectionContent(*SectionID));
+ StringRef SecContent = Dyld.getSectionContent(*SectionID);
+ SecInfo.setContent(ArrayRef<char>(SecContent.data(), SecContent.size()));
return SecInfo;
};
@@ -886,8 +889,10 @@ static int linkAndVerify() {
RuntimeDyldChecker::MemoryRegionInfo StubMemInfo;
StubMemInfo.setTargetAddress(Dyld.getSectionLoadAddress(SI.SectionID) +
SI.Offset);
+ StringRef SecContent =
+ Dyld.getSectionContent(SI.SectionID).substr(SI.Offset);
StubMemInfo.setContent(
- Dyld.getSectionContent(SI.SectionID).substr(SI.Offset));
+ ArrayRef<char>(SecContent.data(), SecContent.size()));
return StubMemInfo;
};
@@ -962,6 +967,7 @@ int main(int argc, char **argv) {
llvm::InitializeAllTargetMCs();
llvm::InitializeAllDisassemblers();
+ cl::HideUnrelatedOptions({&RTDyldCategory, &getColorCategory()});
cl::ParseCommandLineOptions(argc, argv, "llvm MC-JIT tool\n");
ExitOnErr.setBanner(std::string(argv[0]) + ": ");
diff --git a/llvm/tools/llvm-sim/llvm-sim.cpp b/llvm/tools/llvm-sim/llvm-sim.cpp
new file mode 100644
index 000000000000..26e370ff30f1
--- /dev/null
+++ b/llvm/tools/llvm-sim/llvm-sim.cpp
@@ -0,0 +1,149 @@
+//===-- llvm-sim.cpp - Find similar sections of programs -------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This program finds similar sections of a Module, and exports them as a JSON
+// file.
+//
+// To find similarities contained across multiple modules, please use llvm-link
+// first to merge the modules.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Analysis/IRSimilarityIdentifier.h"
+#include "llvm/IRReader/IRReader.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/JSON.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/ToolOutputFile.h"
+
+using namespace llvm;
+using namespace IRSimilarity;
+
+static cl::opt<std::string> OutputFilename("o", cl::desc("Output Filename"),
+ cl::init("-"),
+ cl::value_desc("filename"));
+
+static cl::opt<std::string> InputSourceFile(cl::Positional,
+ cl::desc("<Source file>"),
+ cl::init("-"),
+ cl::value_desc("filename"));
+
+/// Retrieve the unique number \p I was mapped to in parseBitcodeFile.
+///
+/// \param I - The Instruction to find the instruction number for.
+/// \param LLVMInstNum - The mapping of Instructions to their location in the
+/// module represented by an unsigned integer.
+/// \returns The instruction number for \p I if it exists.
+Optional<unsigned>
+getPositionInModule(const Instruction *I,
+ const DenseMap<Instruction *, unsigned> &LLVMInstNum) {
+ assert(I && "Instruction is nullptr!");
+ DenseMap<Instruction *, unsigned>::const_iterator It = LLVMInstNum.find(I);
+ if (It == LLVMInstNum.end())
+ return None;
+ return It->second;
+}
+
+/// Exports the given SimilarityGroups to a JSON file at \p FilePath.
+///
+/// \param FilePath - The path to the output location.
+/// \param SimSections - The similarity groups to process.
+/// \param LLVMInstNum - The mapping of Instructions to their location in the
+/// module represented by an unsigned integer.
+/// \returns A nonzero error code if there was a failure creating the file.
+std::error_code
+exportToFile(const StringRef FilePath,
+ const SimilarityGroupList &SimSections,
+ const DenseMap<Instruction *, unsigned> &LLVMInstNum) {
+ std::error_code EC;
+ std::unique_ptr<ToolOutputFile> Out(
+ new ToolOutputFile(FilePath, EC, sys::fs::OF_None));
+ if (EC)
+ return EC;
+
+ json::OStream J(Out->os(), 1);
+ J.objectBegin();
+
+ unsigned SimOption = 1;
+ // Process each list of SimilarityGroups organized by the Module.
+ for (const SimilarityGroup &G : SimSections) {
+ std::string SimOptionStr = std::to_string(SimOption);
+ J.attributeBegin(SimOptionStr);
+ J.arrayBegin();
+ // For each file there is a list of the range where the similarity
+ // exists.
+ for (const IRSimilarityCandidate &C : G) {
+ Optional<unsigned> Start =
+ getPositionInModule((*C.front()).Inst, LLVMInstNum);
+ Optional<unsigned> End =
+ getPositionInModule((*C.back()).Inst, LLVMInstNum);
+
+ assert(Start.hasValue() &&
+ "Could not find instruction number for first instruction");
+ assert(End.hasValue() &&
+ "Could not find instruction number for last instruction");
+
+ J.object([&] {
+ J.attribute("start", Start.getValue());
+ J.attribute("end", End.getValue());
+ });
+ }
+ J.arrayEnd();
+ J.attributeEnd();
+ SimOption++;
+ }
+ J.objectEnd();
+
+ Out->keep();
+
+ return EC;
+}
+
+int main(int argc, const char *argv[]) {
+ InitLLVM X(argc, argv);
+
+ cl::ParseCommandLineOptions(argc, argv, "LLVM IR Similarity Visualizer\n");
+
+ LLVMContext CurrContext;
+ SMDiagnostic Err;
+ std::unique_ptr<Module> ModuleToAnalyze =
+ parseIRFile(InputSourceFile, Err, CurrContext);
+
+ if (!ModuleToAnalyze) {
+ Err.print(argv[0], errs());
+ return 1;
+ }
+
+ // Mapping from an Instruction pointer to its occurrence in a sequential
+ // list of all the Instructions in a Module.
+ DenseMap<Instruction *, unsigned> LLVMInstNum;
+
+ // We give each instruction a number, which gives us a start and end value
+ // for the beginning and end of each IRSimilarityCandidate.
+ unsigned InstructionNumber = 1;
+ for (Function &F : *ModuleToAnalyze)
+ for (BasicBlock &BB : F)
+ for (Instruction &I : BB.instructionsWithoutDebug())
+ LLVMInstNum[&I]= InstructionNumber++;
+
+ // The similarity identifier we will use to find the similar sections.
+ IRSimilarityIdentifier SimIdent;
+ SimilarityGroupList SimilaritySections =
+ SimIdent.findSimilarity(*ModuleToAnalyze);
+
+ std::error_code E =
+ exportToFile(OutputFilename, SimilaritySections, LLVMInstNum);
+ if (E) {
+ errs() << argv[0] << ": " << E.message() << '\n';
+ return 2;
+ }
+
+ return 0;
+}
diff --git a/llvm/tools/llvm-size/Opts.td b/llvm/tools/llvm-size/Opts.td
new file mode 100644
index 000000000000..edae43f1abd2
--- /dev/null
+++ b/llvm/tools/llvm-size/Opts.td
@@ -0,0 +1,32 @@
+include "llvm/Option/OptParser.td"
+
+class F<string letter, string help> : Flag<["-"], letter>, HelpText<help>;
+class FF<string name, string help> : Flag<["--"], name>, HelpText<help>;
+
+multiclass Eq<string name, string help> {
+ def NAME #_EQ : Joined<["--"], name #"=">,
+ HelpText<help>;
+ def : Separate<["--"], name>, Alias<!cast<Joined>(NAME #_EQ)>;
+}
+
+def common : FF<"common", "Print common symbols in the ELF file. When using Berkeley format, this is added to bss">;
+defm format : Eq<"format", "Specify output format">;
+def help : FF<"help", "Display this help">;
+defm radix : Eq<"radix", "Print size in radix">;
+def totals : FF<"totals", "Print totals of all objects - Berkeley format only">;
+def version : FF<"version", "Display the version">;
+
+// Mach-O specific options.
+def grp_mach_o : OptionGroup<"kind">, HelpText<"OPTIONS (Mach-O specific)">;
+def arch_EQ : Joined<["--"], "arch=">, HelpText<"architecture(s) from a Mach-O file to dump">, Group<grp_mach_o>;
+def : Separate<["--", "-"], "arch">, Alias<arch_EQ>;
+def l : F<"l", "When format is darwin, use long format to include addresses and offsets">, Group<grp_mach_o>;
+
+def : F<"A", "Alias for --format">, Alias<format_EQ>, AliasArgs<["sysv"]>;
+def : F<"B", "Alias for --format">, Alias<format_EQ>, AliasArgs<["berkeley"]>;
+def : F<"d", "Alias for --radix=10">, Alias<radix_EQ>, AliasArgs<["10"]>;
+def : F<"h", "Alias for --help">, Alias<help>;
+def : F<"m", "Alias for --format">, Alias<format_EQ>, AliasArgs<["darwin"]>;
+def : F<"o", "Alias for --radix=8">, Alias<radix_EQ>, AliasArgs<["8"]>;
+def : F<"t", "Alias for --totals">, Alias<totals>;
+def : F<"x", "Alias for --radix=16">, Alias<radix_EQ>, AliasArgs<["16"]>;
diff --git a/llvm/tools/llvm-size/llvm-size.cpp b/llvm/tools/llvm-size/llvm-size.cpp
index 4f98a4c0ec10..ec9a4cde56b6 100644
--- a/llvm/tools/llvm-size/llvm-size.cpp
+++ b/llvm/tools/llvm-size/llvm-size.cpp
@@ -18,6 +18,9 @@
#include "llvm/Object/MachO.h"
#include "llvm/Object/MachOUniversal.h"
#include "llvm/Object/ObjectFile.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/Option.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
@@ -33,23 +36,56 @@
using namespace llvm;
using namespace object;
-cl::OptionCategory SizeCat("llvm-size Options");
+namespace {
+using namespace llvm::opt; // for HelpHidden in Opts.inc
+enum ID {
+ OPT_INVALID = 0, // This is not an option ID.
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ OPT_##ID,
+#include "Opts.inc"
+#undef OPTION
+};
+
+#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
+#include "Opts.inc"
+#undef PREFIX
+
+const opt::OptTable::Info InfoTable[] = {
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ { \
+ PREFIX, NAME, HELPTEXT, \
+ METAVAR, OPT_##ID, opt::Option::KIND##Class, \
+ PARAM, FLAGS, OPT_##GROUP, \
+ OPT_##ALIAS, ALIASARGS, VALUES},
+#include "Opts.inc"
+#undef OPTION
+};
+
+class SizeOptTable : public opt::OptTable {
+public:
+ SizeOptTable() : OptTable(InfoTable) { setGroupedShortOptions(true); }
+};
enum OutputFormatTy { berkeley, sysv, darwin };
-static cl::opt<OutputFormatTy>
- OutputFormat("format", cl::desc("Specify output format"),
- cl::values(clEnumVal(sysv, "System V format"),
- clEnumVal(berkeley, "Berkeley format"),
- clEnumVal(darwin, "Darwin -m format")),
- cl::init(berkeley), cl::cat(SizeCat));
-
-static cl::opt<OutputFormatTy>
- OutputFormatShort(cl::desc("Specify output format"),
- cl::values(clEnumValN(sysv, "A", "System V format"),
- clEnumValN(berkeley, "B", "Berkeley format"),
- clEnumValN(darwin, "m", "Darwin -m format")),
- cl::init(berkeley), cl::cat(SizeCat));
+enum RadixTy { octal = 8, decimal = 10, hexadecimal = 16 };
+} // namespace
+
+static bool ArchAll = false;
+static std::vector<StringRef> ArchFlags;
+static bool ELFCommons;
+static OutputFormatTy OutputFormat;
+static bool DarwinLongFormat;
+static RadixTy Radix;
+static bool TotalSizes;
+
+static std::vector<std::string> InputFilenames;
+
+static std::string ToolName;
+// States
+static bool HadError = false;
static bool BerkeleyHeaderPrinted = false;
static bool MoreThanOneFile = false;
static uint64_t TotalObjectText = 0;
@@ -57,59 +93,13 @@ static uint64_t TotalObjectData = 0;
static uint64_t TotalObjectBss = 0;
static uint64_t TotalObjectTotal = 0;
-cl::opt<bool>
- DarwinLongFormat("l",
- cl::desc("When format is darwin, use long format "
- "to include addresses and offsets."),
- cl::cat(SizeCat));
-
-cl::opt<bool>
- ELFCommons("common",
- cl::desc("Print common symbols in the ELF file. When using "
- "Berkeley format, this is added to bss."),
- cl::init(false), cl::cat(SizeCat));
-
-static cl::list<std::string>
- ArchFlags("arch", cl::desc("architecture(s) from a Mach-O file to dump"),
- cl::ZeroOrMore, cl::cat(SizeCat));
-static bool ArchAll = false;
-
-enum RadixTy { octal = 8, decimal = 10, hexadecimal = 16 };
-static cl::opt<RadixTy> Radix(
- "radix", cl::desc("Print size in radix"), cl::init(decimal),
- cl::values(clEnumValN(octal, "8", "Print size in octal"),
- clEnumValN(decimal, "10", "Print size in decimal"),
- clEnumValN(hexadecimal, "16", "Print size in hexadecimal")),
- cl::cat(SizeCat));
-
-static cl::opt<RadixTy> RadixShort(
- cl::desc("Print size in radix:"),
- cl::values(clEnumValN(octal, "o", "Print size in octal"),
- clEnumValN(decimal, "d", "Print size in decimal"),
- clEnumValN(hexadecimal, "x", "Print size in hexadecimal")),
- cl::init(decimal), cl::cat(SizeCat));
-
-static cl::opt<bool>
- TotalSizes("totals",
- cl::desc("Print totals of all objects - Berkeley format only"),
- cl::init(false), cl::cat(SizeCat));
-
-static cl::alias TotalSizesShort("t", cl::desc("Short for --totals"),
- cl::aliasopt(TotalSizes));
-
-static cl::list<std::string>
- InputFilenames(cl::Positional, cl::desc("<input files>"), cl::ZeroOrMore);
-
-static cl::extrahelp
- HelpResponse("\nPass @FILE as argument to read options from FILE.\n");
-
-static bool HadError = false;
-
-static std::string ToolName;
-
-static void error(const Twine &Message, StringRef File) {
+static void error(const Twine &Message, StringRef File = "") {
HadError = true;
- WithColor::error(errs(), ToolName) << "'" << File << "': " << Message << "\n";
+ if (File.empty())
+ WithColor::error(errs(), ToolName) << Message << '\n';
+ else
+ WithColor::error(errs(), ToolName)
+ << "'" << File << "': " << Message << '\n';
}
// This version of error() prints the archive name and member name, for example:
@@ -874,27 +864,66 @@ static void printBerkeleyTotals() {
int main(int argc, char **argv) {
InitLLVM X(argc, argv);
- cl::HideUnrelatedOptions(SizeCat);
- cl::ParseCommandLineOptions(argc, argv, "llvm object size dumper\n");
-
+ BumpPtrAllocator A;
+ StringSaver Saver(A);
+ SizeOptTable Tbl;
ToolName = argv[0];
- if (OutputFormatShort.getNumOccurrences())
- OutputFormat = static_cast<OutputFormatTy>(OutputFormatShort);
- if (RadixShort.getNumOccurrences())
- Radix = RadixShort.getValue();
-
- for (StringRef Arch : ArchFlags) {
- if (Arch == "all") {
- ArchAll = true;
- } else {
- if (!MachOObjectFile::isValidArch(Arch)) {
+ opt::InputArgList Args = Tbl.parseArgs(argc, argv, OPT_UNKNOWN, Saver,
+ [&](StringRef Msg) { error(Msg); });
+ if (Args.hasArg(OPT_help)) {
+ Tbl.printHelp(
+ outs(),
+ (Twine(ToolName) + " [options] <input object files>").str().c_str(),
+ "LLVM object size dumper");
+ // TODO Replace this with OptTable API once it adds extrahelp support.
+ outs() << "\nPass @FILE as argument to read options from FILE.\n";
+ return 0;
+ }
+ if (Args.hasArg(OPT_version)) {
+ outs() << ToolName << '\n';
+ cl::PrintVersionMessage();
+ return 0;
+ }
+
+ ELFCommons = Args.hasArg(OPT_common);
+ DarwinLongFormat = Args.hasArg(OPT_l);
+ TotalSizes = Args.hasArg(OPT_totals);
+ StringRef V = Args.getLastArgValue(OPT_format_EQ, "berkeley");
+ if (V == "berkeley")
+ OutputFormat = berkeley;
+ else if (V == "darwin")
+ OutputFormat = darwin;
+ else if (V == "sysv")
+ OutputFormat = sysv;
+ else
+ error("--format value should be one of: 'berkeley', 'darwin', 'sysv'");
+ V = Args.getLastArgValue(OPT_radix_EQ, "10");
+ if (V == "8")
+ Radix = RadixTy::octal;
+ else if (V == "10")
+ Radix = RadixTy::decimal;
+ else if (V == "16")
+ Radix = RadixTy::hexadecimal;
+ else
+ error("--radix value should be one of: 8, 10, 16 ");
+
+ for (const auto *A : Args.filtered(OPT_arch_EQ)) {
+ SmallVector<StringRef, 2> Values;
+ llvm::SplitString(A->getValue(), Values, ",");
+ for (StringRef V : Values) {
+ if (V == "all")
+ ArchAll = true;
+ else if (MachOObjectFile::isValidArch(V))
+ ArchFlags.push_back(V);
+ else {
outs() << ToolName << ": for the -arch option: Unknown architecture "
- << "named '" << Arch << "'";
+ << "named '" << V << "'";
return 1;
}
}
}
+ InputFilenames = Args.getAllArgValues(OPT_INPUT);
if (InputFilenames.empty())
InputFilenames.push_back("a.out");
diff --git a/llvm/tools/llvm-stress/llvm-stress.cpp b/llvm/tools/llvm-stress/llvm-stress.cpp
index 538240d65738..ece322999107 100644
--- a/llvm/tools/llvm-stress/llvm-stress.cpp
+++ b/llvm/tools/llvm-stress/llvm-stress.cpp
@@ -52,16 +52,20 @@
namespace llvm {
-static cl::opt<unsigned> SeedCL("seed",
- cl::desc("Seed used for randomness"), cl::init(0));
+static cl::OptionCategory StressCategory("Stress Options");
-static cl::opt<unsigned> SizeCL("size",
- cl::desc("The estimated size of the generated function (# of instrs)"),
- cl::init(100));
+static cl::opt<unsigned> SeedCL("seed", cl::desc("Seed used for randomness"),
+ cl::init(0), cl::cat(StressCategory));
-static cl::opt<std::string>
-OutputFilename("o", cl::desc("Override output filename"),
- cl::value_desc("filename"));
+static cl::opt<unsigned> SizeCL(
+ "size",
+ cl::desc("The estimated size of the generated function (# of instrs)"),
+ cl::init(100), cl::cat(StressCategory));
+
+static cl::opt<std::string> OutputFilename("o",
+ cl::desc("Override output filename"),
+ cl::value_desc("filename"),
+ cl::cat(StressCategory));
static LLVMContext Context;
@@ -632,7 +636,7 @@ struct SelectModifier: public Modifier {
// If the value type is a vector, and we allow vector select, then in 50%
// of the cases generate a vector select.
- if (isa<FixedVectorType>(Val0->getType()) && (getRandom() % 1)) {
+ if (isa<FixedVectorType>(Val0->getType()) && (getRandom() & 1)) {
unsigned NumElem =
cast<FixedVectorType>(Val0->getType())->getNumElements();
CondTy = FixedVectorType::get(CondTy, NumElem);
@@ -718,7 +722,7 @@ static void IntroduceControlFlow(Function *F, Random &R) {
BoolInst.push_back(&Instr);
}
- std::shuffle(BoolInst.begin(), BoolInst.end(), R);
+ llvm::shuffle(BoolInst.begin(), BoolInst.end(), R);
for (auto *Instr : BoolInst) {
BasicBlock *Curr = Instr->getParent();
@@ -738,6 +742,7 @@ int main(int argc, char **argv) {
using namespace llvm;
InitLLVM X(argc, argv);
+ cl::HideUnrelatedOptions({&StressCategory, &getColorCategory()});
cl::ParseCommandLineOptions(argc, argv, "llvm codegen stress-tester\n");
auto M = std::make_unique<Module>("/tmp/autogen.bc", Context);
diff --git a/llvm/tools/llvm-strings/Opts.td b/llvm/tools/llvm-strings/Opts.td
new file mode 100644
index 000000000000..2ad77fae6c15
--- /dev/null
+++ b/llvm/tools/llvm-strings/Opts.td
@@ -0,0 +1,23 @@
+include "llvm/Option/OptParser.td"
+
+class F<string letter, string help> : Flag<["-"], letter>, HelpText<help>;
+class FF<string name, string help> : Flag<["--"], name>, HelpText<help>;
+
+multiclass Eq<string name, string help> {
+ def NAME #_EQ : Joined<["--"], name #"=">,
+ HelpText<help>;
+ def : Separate<["--"], name>, Alias<!cast<Joined>(NAME #_EQ)>;
+}
+
+def all : FF<"all", "Silently ignored. Present for GNU strings compatibility">;
+defm bytes : Eq<"bytes", "Print sequences of the specified length">;
+def help : FF<"help", "Display this help">;
+def print_file_name : Flag<["--"], "print-file-name">, HelpText<"Print the name of the file before each string">;
+defm radix : Eq<"radix", "Print the offset within the file with the specified radix: o (octal), d (decimal), x (hexadecimal)">, MetaVarName<"<radix>">;
+def version : FF<"version", "Display the version">;
+
+def : F<"a", "Alias for --all">, Alias<all>;
+def : F<"f", "Alias for --print-file-name">, Alias<print_file_name>;
+def : F<"h", "Alias for --help">, Alias<help>;
+def : JoinedOrSeparate<["-"], "n">, Alias<bytes_EQ>, HelpText<"Alias for --bytes">;
+def : JoinedOrSeparate<["-"], "t">, Alias<radix_EQ>, HelpText<"Alias for --radix">, MetaVarName<"<radix>">;
diff --git a/llvm/tools/llvm-strings/llvm-strings.cpp b/llvm/tools/llvm-strings/llvm-strings.cpp
index 51313d73401e..0b068749917b 100644
--- a/llvm/tools/llvm-strings/llvm-strings.cpp
+++ b/llvm/tools/llvm-strings/llvm-strings.cpp
@@ -11,51 +11,81 @@
//
//===----------------------------------------------------------------------===//
+#include "Opts.inc"
#include "llvm/Object/Binary.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/Option.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Program.h"
+#include "llvm/Support/WithColor.h"
#include <cctype>
#include <string>
using namespace llvm;
using namespace llvm::object;
+namespace {
+enum ID {
+ OPT_INVALID = 0, // This is not an option ID.
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ OPT_##ID,
+#include "Opts.inc"
+#undef OPTION
+};
+
+#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
+#include "Opts.inc"
+#undef PREFIX
+
+static const opt::OptTable::Info InfoTable[] = {
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ { \
+ PREFIX, NAME, HELPTEXT, \
+ METAVAR, OPT_##ID, opt::Option::KIND##Class, \
+ PARAM, FLAGS, OPT_##GROUP, \
+ OPT_##ALIAS, ALIASARGS, VALUES},
+#include "Opts.inc"
+#undef OPTION
+};
+
+class StringsOptTable : public opt::OptTable {
+public:
+ StringsOptTable() : OptTable(InfoTable) { setGroupedShortOptions(true); }
+};
+} // namespace
+
+const char ToolName[] = "llvm-strings";
+
static cl::list<std::string> InputFileNames(cl::Positional,
cl::desc("<input object files>"),
cl::ZeroOrMore);
-static cl::opt<bool>
- PrintFileName("print-file-name",
- cl::desc("Print the name of the file before each string"));
-static cl::alias PrintFileNameShort("f", cl::desc(""),
- cl::aliasopt(PrintFileName));
-
-static cl::opt<int>
- MinLength("bytes", cl::desc("Print sequences of the specified length"),
- cl::init(4));
-static cl::alias MinLengthShort("n", cl::desc(""), cl::aliasopt(MinLength));
-
-static cl::opt<bool>
- AllSections("all",
- cl::desc("Check all sections, not just the data section"));
-static cl::alias AllSectionsShort("a", cl::desc(""),
- cl::aliasopt(AllSections));
+static int MinLength = 4;
+static bool PrintFileName;
enum radix { none, octal, hexadecimal, decimal };
-static cl::opt<radix>
- Radix("radix", cl::desc("print the offset within the file"),
- cl::values(clEnumValN(octal, "o", "octal"),
- clEnumValN(hexadecimal, "x", "hexadecimal"),
- clEnumValN(decimal, "d", "decimal")),
- cl::init(none));
-static cl::alias RadixShort("t", cl::desc(""), cl::aliasopt(Radix));
+static radix Radix;
-static cl::extrahelp
- HelpResponse("\nPass @FILE as argument to read options from FILE.\n");
+LLVM_ATTRIBUTE_NORETURN static void reportCmdLineError(const Twine &Message) {
+ WithColor::error(errs(), ToolName) << Message << "\n";
+ exit(1);
+}
+
+template <typename T>
+static void parseIntArg(const opt::InputArgList &Args, int ID, T &Value) {
+ if (const opt::Arg *A = Args.getLastArg(ID)) {
+ StringRef V(A->getValue());
+ if (!llvm::to_integer(V, Value, 0) || Value <= 0)
+ reportCmdLineError("expected a positive integer, but got '" + V + "'");
+ }
+}
static void strings(raw_ostream &OS, StringRef FileName, StringRef Contents) {
auto print = [&OS, FileName](unsigned Offset, StringRef L) {
@@ -96,13 +126,48 @@ static void strings(raw_ostream &OS, StringRef FileName, StringRef Contents) {
int main(int argc, char **argv) {
InitLLVM X(argc, argv);
+ BumpPtrAllocator A;
+ StringSaver Saver(A);
+ StringsOptTable Tbl;
+ opt::InputArgList Args =
+ Tbl.parseArgs(argc, argv, OPT_UNKNOWN, Saver,
+ [&](StringRef Msg) { reportCmdLineError(Msg); });
+ if (Args.hasArg(OPT_help)) {
+ Tbl.printHelp(
+ outs(),
+ (Twine(ToolName) + " [options] <input object files>").str().c_str(),
+ "llvm string dumper");
+ // TODO Replace this with OptTable API once it adds extrahelp support.
+ outs() << "\nPass @FILE as argument to read options from FILE.\n";
+ return 0;
+ }
+ if (Args.hasArg(OPT_version)) {
+ outs() << ToolName << '\n';
+ cl::PrintVersionMessage();
+ return 0;
+ }
+
+ parseIntArg(Args, OPT_bytes_EQ, MinLength);
+ PrintFileName = Args.hasArg(OPT_print_file_name);
+ StringRef R = Args.getLastArgValue(OPT_radix_EQ);
+ if (R.empty())
+ Radix = none;
+ else if (R == "o")
+ Radix = octal;
+ else if (R == "d")
+ Radix = decimal;
+ else if (R == "x")
+ Radix = hexadecimal;
+ else
+ reportCmdLineError("--radix value should be one of: '' (no offset), 'o' "
+ "(octal), 'd' (decimal), 'x' (hexadecimal)");
- cl::ParseCommandLineOptions(argc, argv, "llvm string dumper\n");
if (MinLength == 0) {
errs() << "invalid minimum string length 0\n";
return EXIT_FAILURE;
}
+ std::vector<std::string> InputFileNames = Args.getAllArgValues(OPT_INPUT);
if (InputFileNames.empty())
InputFileNames.push_back("-");
diff --git a/llvm/tools/llvm-symbolizer/Opts.td b/llvm/tools/llvm-symbolizer/Opts.td
index ac23639f130e..6026e24d6ffa 100644
--- a/llvm/tools/llvm-symbolizer/Opts.td
+++ b/llvm/tools/llvm-symbolizer/Opts.td
@@ -1,17 +1,20 @@
include "llvm/Option/OptParser.td"
multiclass B<string name, string help1, string help2> {
- def NAME: Flag<["--", "-"], name>, HelpText<help1>;
- def no_ # NAME: Flag<["--", "-"], "no-" # name>, HelpText<help2>;
+ def NAME: Flag<["--"], name>, HelpText<help1>;
+ def no_ # NAME: Flag<["--"], "no-" # name>, HelpText<help2>;
}
multiclass Eq<string name, string help> {
- def NAME #_EQ : Joined<["--", "-"], name #"=">,
+ def NAME #_EQ : Joined<["--"], name #"=">,
HelpText<help>;
- def : Separate<["--", "-"], name>, Alias<!cast<Joined>(NAME #_EQ)>;
+ def : Separate<["--"], name>, Alias<!cast<Joined>(NAME #_EQ)>;
}
-class F<string name, string help>: Flag<["--", "-"], name>, HelpText<help>;
+class F<string name, string help>: Flag<["--"], name>, HelpText<help>;
+
+def grp_mach_o : OptionGroup<"kind">,
+ HelpText<"llvm-symbolizer Mach-O Specific Options">;
def addresses : F<"addresses", "Show address before line information">;
defm adjust_vma
@@ -19,13 +22,19 @@ defm adjust_vma
MetaVarName<"<offset>">;
def basenames : Flag<["--"], "basenames">, HelpText<"Strip directory names from paths">;
defm debug_file_directory : Eq<"debug-file-directory", "Path to directory where to look for debug files">, MetaVarName<"<dir>">;
-defm default_arch : Eq<"default-arch", "Default architecture (for multi-arch objects)">;
+defm default_arch
+ : Eq<"default-arch", "Default architecture (for multi-arch objects)">,
+ Group<grp_mach_o>;
defm demangle : B<"demangle", "Demangle function names", "Don't demangle function names">;
def functions : F<"functions", "Print function name for a given address">;
def functions_EQ : Joined<["--"], "functions=">, HelpText<"Print function name for a given address">, Values<"none,short,linkage">;
def help : F<"help", "Display this help">;
defm dwp : Eq<"dwp", "Path to DWP file to be use for any split CUs">, MetaVarName<"<file>">;
-defm dsym_hint : Eq<"dsym-hint", "Path to .dSYM bundles to search for debug info for the object files">, MetaVarName<"<dir>">;
+defm dsym_hint
+ : Eq<"dsym-hint",
+ "Path to .dSYM bundles to search for debug info for the object files">,
+ MetaVarName<"<dir>">,
+ Group<grp_mach_o>;
defm fallback_debug_path : Eq<"fallback-debug-path", "Fallback path for debug binaries">, MetaVarName<"<dir>">;
defm inlines : B<"inlines", "Print all inlined frames for a given address",
"Do not print inlined frames">;
@@ -33,9 +42,9 @@ defm obj
: Eq<"obj", "Path to object file to be symbolized (if not provided, "
"object file should be specified for each input line)">, MetaVarName<"<file>">;
defm output_style
- : Eq<"output-style", "Specify print style. Supported styles: LLVM, GNU">,
+ : Eq<"output-style", "Specify print style. Supported styles: LLVM, GNU, JSON">,
MetaVarName<"style">,
- Values<"LLVM,GNU">;
+ Values<"LLVM,GNU,JSON">;
def pretty_print : F<"pretty-print", "Make the output more human friendly">;
defm print_source_context_lines : Eq<"print-source-context-lines", "Print N lines of source file context">;
def relative_address : F<"relative-address", "Interpret addresses as addresses relative to the image base">;
diff --git a/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp b/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp
index 9c68acee0ae2..227ce12a6d9a 100644
--- a/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp
+++ b/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp
@@ -66,23 +66,35 @@ static const opt::OptTable::Info InfoTable[] = {
class SymbolizerOptTable : public opt::OptTable {
public:
- SymbolizerOptTable() : OptTable(InfoTable, true) {}
+ SymbolizerOptTable() : OptTable(InfoTable) {
+ setGroupedShortOptions(true);
+ }
};
} // namespace
-static cl::list<std::string> ClInputAddresses(cl::Positional,
- cl::desc("<input addresses>..."),
- cl::ZeroOrMore);
+template <typename T>
+static void print(const Request &Request, Expected<T> &ResOrErr,
+ DIPrinter &Printer) {
+ if (ResOrErr) {
+ // No error, print the result.
+ Printer.print(Request, *ResOrErr);
+ return;
+ }
+
+ // Handle the error.
+ bool PrintEmpty = true;
+ handleAllErrors(std::move(ResOrErr.takeError()),
+ [&](const ErrorInfoBase &EI) {
+ PrintEmpty = Printer.printError(
+ Request, EI, "LLVMSymbolizer: error reading file: ");
+ });
-template<typename T>
-static bool error(Expected<T> &ResOrErr) {
- if (ResOrErr)
- return false;
- logAllUnhandledErrors(ResOrErr.takeError(), errs(),
- "LLVMSymbolizer: error reading file: ");
- return true;
+ if (PrintEmpty)
+ Printer.print(Request, T());
}
+enum class OutputStyle { LLVM, GNU, JSON };
+
enum class Command {
Code,
Data,
@@ -136,7 +148,7 @@ static bool parseCommand(StringRef BinaryName, bool IsAddr2Line,
}
static void symbolizeInput(const opt::InputArgList &Args, uint64_t AdjustVMA,
- bool IsAddr2Line, DIPrinter::OutputStyle OutputStyle,
+ bool IsAddr2Line, OutputStyle Style,
StringRef InputString, LLVMSymbolizer &Symbolizer,
DIPrinter &Printer) {
Command Cmd;
@@ -144,57 +156,49 @@ static void symbolizeInput(const opt::InputArgList &Args, uint64_t AdjustVMA,
uint64_t Offset = 0;
if (!parseCommand(Args.getLastArgValue(OPT_obj_EQ), IsAddr2Line,
StringRef(InputString), Cmd, ModuleName, Offset)) {
- outs() << InputString << "\n";
+ Printer.printInvalidCommand({ModuleName, None}, InputString);
return;
}
- if (Args.hasArg(OPT_addresses)) {
- outs() << "0x";
- outs().write_hex(Offset);
- StringRef Delimiter = Args.hasArg(OPT_pretty_print) ? ": " : "\n";
- outs() << Delimiter;
- }
- Offset -= AdjustVMA;
+ uint64_t AdjustedOffset = Offset - AdjustVMA;
if (Cmd == Command::Data) {
- auto ResOrErr = Symbolizer.symbolizeData(
- ModuleName, {Offset, object::SectionedAddress::UndefSection});
- Printer << (error(ResOrErr) ? DIGlobal() : ResOrErr.get());
+ Expected<DIGlobal> ResOrErr = Symbolizer.symbolizeData(
+ ModuleName, {AdjustedOffset, object::SectionedAddress::UndefSection});
+ print({ModuleName, Offset}, ResOrErr, Printer);
} else if (Cmd == Command::Frame) {
- auto ResOrErr = Symbolizer.symbolizeFrame(
- ModuleName, {Offset, object::SectionedAddress::UndefSection});
- if (!error(ResOrErr)) {
- for (DILocal Local : *ResOrErr)
- Printer << Local;
- if (ResOrErr->empty())
- outs() << "??\n";
- }
+ Expected<std::vector<DILocal>> ResOrErr = Symbolizer.symbolizeFrame(
+ ModuleName, {AdjustedOffset, object::SectionedAddress::UndefSection});
+ print({ModuleName, Offset}, ResOrErr, Printer);
} else if (Args.hasFlag(OPT_inlines, OPT_no_inlines, !IsAddr2Line)) {
- auto ResOrErr = Symbolizer.symbolizeInlinedCode(
- ModuleName, {Offset, object::SectionedAddress::UndefSection});
- Printer << (error(ResOrErr) ? DIInliningInfo() : ResOrErr.get());
- } else if (OutputStyle == DIPrinter::OutputStyle::GNU) {
+ Expected<DIInliningInfo> ResOrErr = Symbolizer.symbolizeInlinedCode(
+ ModuleName, {AdjustedOffset, object::SectionedAddress::UndefSection});
+ print({ModuleName, Offset}, ResOrErr, Printer);
+ } else if (Style == OutputStyle::GNU) {
// With PrintFunctions == FunctionNameKind::LinkageName (default)
// and UseSymbolTable == true (also default), Symbolizer.symbolizeCode()
// may override the name of an inlined function with the name of the topmost
// caller function in the inlining chain. This contradicts the existing
// behavior of addr2line. Symbolizer.symbolizeInlinedCode() overrides only
// the topmost function, which suits our needs better.
- auto ResOrErr = Symbolizer.symbolizeInlinedCode(
- ModuleName, {Offset, object::SectionedAddress::UndefSection});
- Printer << (error(ResOrErr) ? DILineInfo() : ResOrErr.get().getFrame(0));
+ Expected<DIInliningInfo> ResOrErr = Symbolizer.symbolizeInlinedCode(
+ ModuleName, {AdjustedOffset, object::SectionedAddress::UndefSection});
+ Expected<DILineInfo> Res0OrErr =
+ !ResOrErr
+ ? Expected<DILineInfo>(ResOrErr.takeError())
+ : ((ResOrErr->getNumberOfFrames() == 0) ? DILineInfo()
+ : ResOrErr->getFrame(0));
+ print({ModuleName, Offset}, Res0OrErr, Printer);
} else {
- auto ResOrErr = Symbolizer.symbolizeCode(
- ModuleName, {Offset, object::SectionedAddress::UndefSection});
- Printer << (error(ResOrErr) ? DILineInfo() : ResOrErr.get());
+ Expected<DILineInfo> ResOrErr = Symbolizer.symbolizeCode(
+ ModuleName, {AdjustedOffset, object::SectionedAddress::UndefSection});
+ print({ModuleName, Offset}, ResOrErr, Printer);
}
- if (OutputStyle == DIPrinter::OutputStyle::LLVM)
- outs() << "\n";
}
static void printHelp(StringRef ToolName, const SymbolizerOptTable &Tbl,
raw_ostream &OS) {
const char HelpText[] = " [options] addresses...";
- Tbl.PrintHelp(OS, (ToolName + HelpText).str().c_str(),
+ Tbl.printHelp(OS, (ToolName + HelpText).str().c_str(),
ToolName.str().c_str());
// TODO Replace this with OptTable API once it adds extrahelp support.
OS << "\nPass @FILE as argument to read options from FILE.\n";
@@ -204,7 +208,6 @@ static opt::InputArgList parseOptions(int Argc, char *Argv[], bool IsAddr2Line,
StringSaver &Saver,
SymbolizerOptTable &Tbl) {
StringRef ToolName = IsAddr2Line ? "llvm-addr2line" : "llvm-symbolizer";
- Tbl.setGroupedShortOptions(true);
// The environment variable specifies initial options which can be overridden
// by commnad line options.
Tbl.setInitialOptionsFromEnvironment(IsAddr2Line ? "LLVM_ADDR2LINE_OPTS"
@@ -268,7 +271,7 @@ int main(int argc, char **argv) {
LLVMSymbolizer::Options Opts;
uint64_t AdjustVMA;
- unsigned SourceContextLines;
+ PrinterConfig Config;
parseIntArg(Args, OPT_adjust_vma_EQ, AdjustVMA);
if (const opt::Arg *A = Args.getLastArg(OPT_basenames, OPT_relativenames)) {
Opts.PathStyle =
@@ -285,7 +288,8 @@ int main(int argc, char **argv) {
Opts.FallbackDebugPath =
Args.getLastArgValue(OPT_fallback_debug_path_EQ).str();
Opts.PrintFunctions = decideHowToPrintFunctions(Args, IsAddr2Line);
- parseIntArg(Args, OPT_print_source_context_lines_EQ, SourceContextLines);
+ parseIntArg(Args, OPT_print_source_context_lines_EQ,
+ Config.SourceContextLines);
Opts.RelativeAddresses = Args.hasArg(OPT_relative_address);
Opts.UntagAddresses =
Args.hasFlag(OPT_untag_addresses, OPT_no_untag_addresses, !IsAddr2Line);
@@ -297,6 +301,10 @@ int main(int argc, char **argv) {
}
#endif
Opts.UseSymbolTable = true;
+ Config.PrintAddress = Args.hasArg(OPT_addresses);
+ Config.PrintFunctions = Opts.PrintFunctions != FunctionNameKind::None;
+ Config.Pretty = Args.hasArg(OPT_pretty_print);
+ Config.Verbose = Args.hasArg(OPT_verbose);
for (const opt::Arg *A : Args.filtered(OPT_dsym_hint_EQ)) {
StringRef Hint(A->getValue());
@@ -308,18 +316,24 @@ int main(int argc, char **argv) {
}
}
- auto OutputStyle =
- IsAddr2Line ? DIPrinter::OutputStyle::GNU : DIPrinter::OutputStyle::LLVM;
+ auto Style = IsAddr2Line ? OutputStyle::GNU : OutputStyle::LLVM;
if (const opt::Arg *A = Args.getLastArg(OPT_output_style_EQ)) {
- OutputStyle = strcmp(A->getValue(), "GNU") == 0
- ? DIPrinter::OutputStyle::GNU
- : DIPrinter::OutputStyle::LLVM;
+ if (strcmp(A->getValue(), "GNU") == 0)
+ Style = OutputStyle::GNU;
+ else if (strcmp(A->getValue(), "JSON") == 0)
+ Style = OutputStyle::JSON;
+ else
+ Style = OutputStyle::LLVM;
}
LLVMSymbolizer Symbolizer(Opts);
- DIPrinter Printer(outs(), Opts.PrintFunctions != FunctionNameKind::None,
- Args.hasArg(OPT_pretty_print), SourceContextLines,
- Args.hasArg(OPT_verbose), OutputStyle);
+ std::unique_ptr<DIPrinter> Printer;
+ if (Style == OutputStyle::GNU)
+ Printer = std::make_unique<GNUPrinter>(outs(), errs(), Config);
+ else if (Style == OutputStyle::JSON)
+ Printer = std::make_unique<JSONPrinter>(outs(), Config);
+ else
+ Printer = std::make_unique<LLVMPrinter>(outs(), errs(), Config);
std::vector<std::string> InputAddresses = Args.getAllArgValues(OPT_INPUT);
if (InputAddresses.empty()) {
@@ -331,14 +345,16 @@ int main(int argc, char **argv) {
std::string StrippedInputString(InputString);
llvm::erase_if(StrippedInputString,
[](char c) { return c == '\r' || c == '\n'; });
- symbolizeInput(Args, AdjustVMA, IsAddr2Line, OutputStyle,
- StrippedInputString, Symbolizer, Printer);
+ symbolizeInput(Args, AdjustVMA, IsAddr2Line, Style, StrippedInputString,
+ Symbolizer, *Printer);
outs().flush();
}
} else {
+ Printer->listBegin();
for (StringRef Address : InputAddresses)
- symbolizeInput(Args, AdjustVMA, IsAddr2Line, OutputStyle, Address,
- Symbolizer, Printer);
+ symbolizeInput(Args, AdjustVMA, IsAddr2Line, Style, Address, Symbolizer,
+ *Printer);
+ Printer->listEnd();
}
return 0;
diff --git a/llvm/tools/llvm-tapi-diff/DiffEngine.cpp b/llvm/tools/llvm-tapi-diff/DiffEngine.cpp
new file mode 100644
index 000000000000..ff60d52f5c65
--- /dev/null
+++ b/llvm/tools/llvm-tapi-diff/DiffEngine.cpp
@@ -0,0 +1,556 @@
+//===-- DiffEngine.cpp - Structural file comparison -----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the implementation of the llvm-tapi difference
+// engine, which structurally compares two tbd files.
+//
+//===----------------------------------------------------------------------===/
+#include "DiffEngine.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/TextAPI/InterfaceFile.h"
+#include "llvm/TextAPI/Symbol.h"
+#include "llvm/TextAPI/Target.h"
+
+using namespace llvm;
+using namespace MachO;
+using namespace object;
+
+StringRef setOrderIndicator(InterfaceInputOrder Order) {
+ return ((Order == lhs) ? "< " : "> ");
+}
+
+// The following template specialization implementations
+// need to be explicitly placed into the llvm namespace
+// to work around a GCC 4.8 bug.
+namespace llvm {
+
+template <typename T, DiffAttrKind U>
+inline void DiffScalarVal<T, U>::print(raw_ostream &OS, std::string Indent) {
+ OS << Indent << "\t" << setOrderIndicator(Order) << Val << "\n";
+}
+
+template <>
+inline void
+DiffScalarVal<StringRef, AD_Diff_Scalar_Str>::print(raw_ostream &OS,
+ std::string Indent) {
+ OS << Indent << "\t\t" << setOrderIndicator(Order) << Val << "\n";
+}
+
+template <>
+inline void
+DiffScalarVal<uint8_t, AD_Diff_Scalar_Unsigned>::print(raw_ostream &OS,
+ std::string Indent) {
+ OS << Indent << "\t" << setOrderIndicator(Order) << std::to_string(Val)
+ << "\n";
+}
+
+template <>
+inline void
+DiffScalarVal<bool, AD_Diff_Scalar_Bool>::print(raw_ostream &OS,
+ std::string Indent) {
+ OS << Indent << "\t" << setOrderIndicator(Order)
+ << ((Val == true) ? "true" : "false") << "\n";
+}
+
+} // end namespace llvm
+
+StringLiteral SymScalar::getSymbolNamePrefix(MachO::SymbolKind Kind) {
+ switch (Kind) {
+ case MachO::SymbolKind::GlobalSymbol:
+ return StringLiteral("");
+ case MachO::SymbolKind::ObjectiveCClass:
+ return ObjC2MetaClassNamePrefix;
+ case MachO::SymbolKind ::ObjectiveCClassEHType:
+ return ObjC2EHTypePrefix;
+ case MachO::SymbolKind ::ObjectiveCInstanceVariable:
+ return ObjC2IVarPrefix;
+ }
+ llvm_unreachable("Unknown llvm::MachO::SymbolKind enum");
+}
+
+std::string SymScalar::stringifySymbolFlag(MachO::SymbolFlags Flag) {
+ switch (Flag) {
+ case MachO::SymbolFlags::None:
+ return "";
+ case MachO::SymbolFlags::ThreadLocalValue:
+ return "Thread-Local";
+ case MachO::SymbolFlags::WeakDefined:
+ return "Weak-Defined";
+ case MachO::SymbolFlags::WeakReferenced:
+ return "Weak-Referenced";
+ case MachO::SymbolFlags::Undefined:
+ return "Undefined";
+ case MachO::SymbolFlags::Rexported:
+ return "Reexported";
+ }
+ llvm_unreachable("Unknown llvm::MachO::SymbolFlags enum");
+}
+
+void SymScalar::print(raw_ostream &OS, std::string Indent, MachO::Target Targ) {
+ if (Val->getKind() == MachO::SymbolKind::ObjectiveCClass) {
+ if (Targ.Arch == MachO::AK_i386 &&
+ Targ.Platform == MachO::PlatformKind::macOS) {
+ OS << Indent << "\t\t" << ((Order == lhs) ? "< " : "> ")
+ << ObjC1ClassNamePrefix << Val->getName()
+ << getFlagString(Val->getFlags()) << "\n";
+ return;
+ }
+ OS << Indent << "\t\t" << ((Order == lhs) ? "< " : "> ")
+ << ObjC2ClassNamePrefix << Val->getName()
+ << getFlagString(Val->getFlags()) << "\n";
+ }
+ OS << Indent << "\t\t" << ((Order == lhs) ? "< " : "> ")
+ << getSymbolNamePrefix(Val->getKind()) << Val->getName()
+ << getFlagString(Val->getFlags()) << "\n";
+}
+
+bool checkSymbolEquality(llvm::MachO::InterfaceFile::const_symbol_range LHS,
+ llvm::MachO::InterfaceFile::const_symbol_range RHS) {
+ return std::equal(LHS.begin(), LHS.end(), RHS.begin(),
+ [&](auto LHS, auto RHS) { return *LHS == *RHS; });
+}
+
+template <typename TargetVecT, typename ValTypeT, typename V>
+void addDiffForTargSlice(V Val, Target Targ, DiffOutput &Diff,
+ InterfaceInputOrder Order) {
+ auto TargetVector = llvm::find_if(
+ Diff.Values, [&](const std::unique_ptr<AttributeDiff> &RawTVec) {
+ if (TargetVecT *TVec = dyn_cast<TargetVecT>(RawTVec.get()))
+ return TVec->Targ == Targ;
+ return false;
+ });
+ if (TargetVector != Diff.Values.end()) {
+ ValTypeT NewVal(Order, Val);
+ cast<TargetVecT>(TargetVector->get())->TargValues.push_back(NewVal);
+ } else {
+ auto NewTargetVec = std::make_unique<TargetVecT>(Targ);
+ ValTypeT NewVal(Order, Val);
+ NewTargetVec->TargValues.push_back(NewVal);
+ Diff.Values.push_back(std::move(NewTargetVec));
+ }
+}
+
+DiffOutput getSingleAttrDiff(const std::vector<InterfaceFileRef> &IRefVec,
+ std::string Name, InterfaceInputOrder Order) {
+ DiffOutput Diff(Name);
+ Diff.Kind = AD_Str_Vec;
+ for (const auto &IRef : IRefVec)
+ for (auto Targ : IRef.targets())
+ addDiffForTargSlice<DiffStrVec,
+ DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>(
+ IRef.getInstallName(), Targ, Diff, Order);
+ return Diff;
+}
+
+DiffOutput
+getSingleAttrDiff(const std::vector<std::pair<Target, std::string>> &PairVec,
+ std::string Name, InterfaceInputOrder Order) {
+ DiffOutput Diff(Name);
+ Diff.Kind = AD_Str_Vec;
+ for (const auto &Pair : PairVec)
+ addDiffForTargSlice<DiffStrVec,
+ DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>(
+ StringRef(Pair.second), Pair.first, Diff, Order);
+ return Diff;
+}
+
+DiffOutput getSingleAttrDiff(InterfaceFile::const_symbol_range SymRange,
+ std::string Name, InterfaceInputOrder Order) {
+ DiffOutput Diff(Name);
+ Diff.Kind = AD_Sym_Vec;
+ for (const auto *Sym : SymRange)
+ for (auto Targ : Sym->targets())
+ addDiffForTargSlice<DiffSymVec, SymScalar>(Sym, Targ, Diff, Order);
+ return Diff;
+}
+
+template <typename T>
+DiffOutput getSingleAttrDiff(T SingleAttr, std::string Attribute) {
+ DiffOutput Diff(Attribute);
+ Diff.Kind = SingleAttr.getKind();
+ Diff.Values.push_back(std::make_unique<T>(SingleAttr));
+ return Diff;
+}
+
+template <typename T, DiffAttrKind U>
+void diffAttribute(std::string Name, std::vector<DiffOutput> &Output,
+ DiffScalarVal<T, U> Attr) {
+ Output.push_back(getSingleAttrDiff(Attr, Name));
+}
+
+template <typename T>
+void diffAttribute(std::string Name, std::vector<DiffOutput> &Output,
+ const T &Val, InterfaceInputOrder Order) {
+ Output.push_back(getSingleAttrDiff(Val, Name, Order));
+}
+
+std::vector<DiffOutput> getSingleIF(InterfaceFile *Interface,
+ InterfaceInputOrder Order) {
+ std::vector<DiffOutput> Output;
+ diffAttribute("Install Name", Output,
+ DiffScalarVal<StringRef, AD_Diff_Scalar_Str>(
+ Order, Interface->getInstallName()));
+ diffAttribute("Current Version", Output,
+ DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>(
+ Order, Interface->getCurrentVersion()));
+ diffAttribute("Compatibility Version", Output,
+ DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>(
+ Order, Interface->getCompatibilityVersion()));
+ diffAttribute("Swift ABI Version", Output,
+ DiffScalarVal<uint8_t, AD_Diff_Scalar_Unsigned>(
+ Order, Interface->getSwiftABIVersion()));
+ diffAttribute("InstallAPI", Output,
+ DiffScalarVal<bool, AD_Diff_Scalar_Bool>(
+ Order, Interface->isInstallAPI()));
+ diffAttribute("Two Level Namespace", Output,
+ DiffScalarVal<bool, AD_Diff_Scalar_Bool>(
+ Order, Interface->isTwoLevelNamespace()));
+ diffAttribute("Application Extension Safe", Output,
+ DiffScalarVal<bool, AD_Diff_Scalar_Bool>(
+ Order, Interface->isApplicationExtensionSafe()));
+ diffAttribute("Reexported Libraries", Output,
+ Interface->reexportedLibraries(), Order);
+ diffAttribute("Allowable Clients", Output, Interface->allowableClients(),
+ Order);
+ diffAttribute("Parent Umbrellas", Output, Interface->umbrellas(), Order);
+ diffAttribute("Symbols", Output, Interface->symbols(), Order);
+ for (auto Doc : Interface->documents()) {
+ DiffOutput Documents("Inlined Reexported Frameworks/Libraries");
+ Documents.Kind = AD_Inline_Doc;
+ Documents.Values.push_back(std::make_unique<InlineDoc>(
+ InlineDoc(Doc->getInstallName(), getSingleIF(Doc.get(), Order))));
+ Output.push_back(std::move(Documents));
+ }
+ return Output;
+}
+
+void findAndAddDiff(const std::vector<InterfaceFileRef> &CollectedIRefVec,
+ const std::vector<InterfaceFileRef> &LookupIRefVec,
+ DiffOutput &Result, InterfaceInputOrder Order) {
+ Result.Kind = AD_Str_Vec;
+ for (const auto &IRef : CollectedIRefVec)
+ for (auto Targ : IRef.targets()) {
+ auto FoundIRef = llvm::find_if(LookupIRefVec, [&](const auto LIRef) {
+ auto FoundTarg = llvm::find(LIRef.targets(), Targ);
+ return (FoundTarg != LIRef.targets().end() &&
+ IRef.getInstallName() == LIRef.getInstallName());
+ });
+ if (FoundIRef == LookupIRefVec.end())
+ addDiffForTargSlice<DiffStrVec,
+ DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>(
+ IRef.getInstallName(), Targ, Result, Order);
+ }
+}
+
+void findAndAddDiff(
+ const std::vector<std::pair<Target, std::string>> &CollectedPairs,
+ const std::vector<std::pair<Target, std::string>> &LookupPairs,
+ DiffOutput &Result, InterfaceInputOrder Order) {
+ Result.Kind = AD_Str_Vec;
+ for (const auto &Pair : CollectedPairs) {
+ auto FoundPair = llvm::find(LookupPairs, Pair);
+ if (FoundPair == LookupPairs.end())
+ addDiffForTargSlice<DiffStrVec,
+ DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>(
+ StringRef(Pair.second), Pair.first, Result, Order);
+ }
+}
+
+void findAndAddDiff(InterfaceFile::const_symbol_range CollectedSyms,
+ InterfaceFile::const_symbol_range LookupSyms,
+ DiffOutput &Result, InterfaceInputOrder Order) {
+ Result.Kind = AD_Sym_Vec;
+ for (const auto *Sym : CollectedSyms)
+ for (const auto Targ : Sym->targets()) {
+ auto FoundSym = llvm::find_if(LookupSyms, [&](const auto LSym) {
+ auto FoundTarg = llvm::find(LSym->targets(), Targ);
+ return (Sym->getName() == LSym->getName() &&
+ Sym->getKind() == LSym->getKind() &&
+ Sym->getFlags() == LSym->getFlags() &&
+ FoundTarg != LSym->targets().end());
+ });
+ if (FoundSym == LookupSyms.end())
+ addDiffForTargSlice<DiffSymVec, SymScalar>(Sym, Targ, Result, Order);
+ }
+}
+
+template <typename T>
+DiffOutput recordDifferences(T LHS, T RHS, std::string Attr) {
+ DiffOutput Diff(Attr);
+ if (LHS.getKind() == RHS.getKind()) {
+ Diff.Kind = LHS.getKind();
+ Diff.Values.push_back(std::make_unique<T>(LHS));
+ Diff.Values.push_back(std::make_unique<T>(RHS));
+ }
+ return Diff;
+}
+
+template <typename T>
+DiffOutput recordDifferences(const std::vector<T> &LHS,
+ const std::vector<T> &RHS, std::string Attr) {
+ DiffOutput Diff(Attr);
+ Diff.Kind = AD_Str_Vec;
+ findAndAddDiff(LHS, RHS, Diff, lhs);
+ findAndAddDiff(RHS, LHS, Diff, rhs);
+ return Diff;
+}
+
+DiffOutput recordDifferences(llvm::MachO::InterfaceFile::const_symbol_range LHS,
+ llvm::MachO::InterfaceFile::const_symbol_range RHS,
+ std::string Attr) {
+ DiffOutput Diff(Attr);
+ Diff.Kind = AD_Sym_Vec;
+ findAndAddDiff(LHS, RHS, Diff, lhs);
+ findAndAddDiff(RHS, LHS, Diff, rhs);
+ return Diff;
+}
+
+std::vector<DiffOutput>
+DiffEngine::findDifferences(const InterfaceFile *IFLHS,
+ const InterfaceFile *IFRHS) {
+ std::vector<DiffOutput> Output;
+ if (IFLHS->getInstallName() != IFRHS->getInstallName())
+ Output.push_back(recordDifferences(
+ DiffScalarVal<StringRef, AD_Diff_Scalar_Str>(lhs,
+ IFLHS->getInstallName()),
+ DiffScalarVal<StringRef, AD_Diff_Scalar_Str>(rhs,
+ IFRHS->getInstallName()),
+ "Install Name"));
+
+ if (IFLHS->getCurrentVersion() != IFRHS->getCurrentVersion())
+ Output.push_back(recordDifferences(
+ DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>(
+ lhs, IFLHS->getCurrentVersion()),
+ DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>(
+ rhs, IFRHS->getCurrentVersion()),
+ "Current Version"));
+ if (IFLHS->getCompatibilityVersion() != IFRHS->getCompatibilityVersion())
+ Output.push_back(recordDifferences(
+ DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>(
+ lhs, IFLHS->getCompatibilityVersion()),
+ DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>(
+ rhs, IFRHS->getCompatibilityVersion()),
+ "Compatibility Version"));
+ if (IFLHS->getSwiftABIVersion() != IFRHS->getSwiftABIVersion())
+ Output.push_back(
+ recordDifferences(DiffScalarVal<uint8_t, AD_Diff_Scalar_Unsigned>(
+ lhs, IFLHS->getSwiftABIVersion()),
+ DiffScalarVal<uint8_t, AD_Diff_Scalar_Unsigned>(
+ rhs, IFRHS->getSwiftABIVersion()),
+ "Swift ABI Version"));
+ if (IFLHS->isInstallAPI() != IFRHS->isInstallAPI())
+ Output.push_back(recordDifferences(
+ DiffScalarVal<bool, AD_Diff_Scalar_Bool>(lhs, IFLHS->isInstallAPI()),
+ DiffScalarVal<bool, AD_Diff_Scalar_Bool>(rhs, IFRHS->isInstallAPI()),
+ "InstallAPI"));
+
+ if (IFLHS->isTwoLevelNamespace() != IFRHS->isTwoLevelNamespace())
+ Output.push_back(recordDifferences(DiffScalarVal<bool, AD_Diff_Scalar_Bool>(
+ lhs, IFLHS->isTwoLevelNamespace()),
+ DiffScalarVal<bool, AD_Diff_Scalar_Bool>(
+ rhs, IFRHS->isTwoLevelNamespace()),
+ "Two Level Namespace"));
+
+ if (IFLHS->isApplicationExtensionSafe() !=
+ IFRHS->isApplicationExtensionSafe())
+ Output.push_back(
+ recordDifferences(DiffScalarVal<bool, AD_Diff_Scalar_Bool>(
+ lhs, IFLHS->isApplicationExtensionSafe()),
+ DiffScalarVal<bool, AD_Diff_Scalar_Bool>(
+ rhs, IFRHS->isApplicationExtensionSafe()),
+ "Application Extension Safe"));
+
+ if (IFLHS->reexportedLibraries() != IFRHS->reexportedLibraries())
+ Output.push_back(recordDifferences(IFLHS->reexportedLibraries(),
+ IFRHS->reexportedLibraries(),
+ "Reexported Libraries"));
+
+ if (IFLHS->allowableClients() != IFRHS->allowableClients())
+ Output.push_back(recordDifferences(IFLHS->allowableClients(),
+ IFRHS->allowableClients(),
+ "Allowable Clients"));
+
+ if (IFLHS->umbrellas() != IFRHS->umbrellas())
+ Output.push_back(recordDifferences(IFLHS->umbrellas(), IFRHS->umbrellas(),
+ "Parent Umbrellas"));
+
+ if (!checkSymbolEquality(IFLHS->symbols(), IFRHS->symbols()))
+ Output.push_back(
+ recordDifferences(IFLHS->symbols(), IFRHS->symbols(), "Symbols"));
+
+ if (IFLHS->documents() != IFRHS->documents()) {
+ DiffOutput Docs("Inlined Reexported Frameworks/Libraries");
+ Docs.Kind = AD_Inline_Doc;
+ std::vector<StringRef> DocsInserted;
+ // Iterate through inline frameworks/libraries from interface file and find
+ // match based on install name.
+ for (auto DocLHS : IFLHS->documents()) {
+ auto Pair = llvm::find_if(IFRHS->documents(), [&](const auto &DocRHS) {
+ return (DocLHS->getInstallName() == DocRHS->getInstallName());
+ });
+ // If a match found, recursively get differences between the pair.
+ if (Pair != IFRHS->documents().end()) {
+ InlineDoc PairDiff =
+ InlineDoc(DocLHS->getInstallName(),
+ findDifferences(DocLHS.get(), Pair->get()));
+ if (!PairDiff.DocValues.empty())
+ Docs.Values.push_back(
+ std::make_unique<InlineDoc>(std::move(PairDiff)));
+ }
+ // If a match is not found, get attributes from single item.
+ else
+ Docs.Values.push_back(std::make_unique<InlineDoc>(InlineDoc(
+ DocLHS->getInstallName(), getSingleIF(DocLHS.get(), lhs))));
+ DocsInserted.push_back(DocLHS->getInstallName());
+ }
+ for (auto DocRHS : IFRHS->documents()) {
+ auto WasGathered =
+ llvm::find_if(DocsInserted, [&](const auto &GatheredDoc) {
+ return (GatheredDoc == DocRHS->getInstallName());
+ });
+ if (WasGathered == DocsInserted.end())
+ Docs.Values.push_back(std::make_unique<InlineDoc>(InlineDoc(
+ DocRHS->getInstallName(), getSingleIF(DocRHS.get(), rhs))));
+ }
+ if (!Docs.Values.empty())
+ Output.push_back(std::move(Docs));
+ }
+ return Output;
+}
+
+template <typename T>
+void printSingleVal(std::string Indent, const DiffOutput &Attr,
+ raw_ostream &OS) {
+ if (Attr.Values.empty())
+ return;
+ OS << Indent << Attr.Name << "\n";
+ for (auto &RawItem : Attr.Values)
+ if (T *Item = dyn_cast<T>(RawItem.get()))
+ Item->print(OS, Indent);
+}
+
+template <typename T>
+T *castValues(const std::unique_ptr<AttributeDiff> &RawAttr) {
+ T *CastAttr = cast<T>(RawAttr.get());
+ return CastAttr;
+}
+
+template <typename T> void sortTargetValues(std::vector<T> &TargValues) {
+ llvm::stable_sort(TargValues, [](const auto &ValA, const auto &ValB) {
+ return ValA.getOrder() < ValB.getOrder();
+ });
+ llvm::stable_sort(TargValues, [](const auto &ValA, const auto &ValB) {
+ return ValA.getOrder() == ValB.getOrder() && ValA.getVal() < ValB.getVal();
+ });
+}
+
+template <typename T>
+void printVecVal(std::string Indent, const DiffOutput &Attr, raw_ostream &OS) {
+ if (Attr.Values.empty())
+ return;
+
+ OS << Indent << Attr.Name << "\n";
+
+ std::vector<T *> SortedAttrs;
+
+ llvm::transform(Attr.Values, std::back_inserter(SortedAttrs), castValues<T>);
+
+ llvm::sort(SortedAttrs, [&](const auto &ValA, const auto &ValB) {
+ return ValA->Targ < ValB->Targ;
+ });
+
+ for (auto *Vec : SortedAttrs) {
+ sortTargetValues<DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>(
+ Vec->TargValues);
+ OS << Indent << "\t" << getTargetTripleName(Vec->Targ) << "\n";
+ for (auto &Item : Vec->TargValues)
+ Item.print(OS, Indent);
+ }
+}
+
+template <>
+void printVecVal<DiffSymVec>(std::string Indent, const DiffOutput &Attr,
+ raw_ostream &OS) {
+ if (Attr.Values.empty())
+ return;
+
+ OS << Indent << Attr.Name << "\n";
+
+ std::vector<DiffSymVec *> SortedAttrs;
+
+ llvm::transform(Attr.Values, std::back_inserter(SortedAttrs),
+ castValues<DiffSymVec>);
+
+ llvm::sort(SortedAttrs, [&](const auto &ValA, const auto &ValB) {
+ return ValA->Targ < ValB->Targ;
+ });
+ for (auto *SymVec : SortedAttrs) {
+ sortTargetValues<SymScalar>(SymVec->TargValues);
+ OS << Indent << "\t" << getTargetTripleName(SymVec->Targ) << "\n";
+ for (auto &Item : SymVec->TargValues)
+ Item.print(OS, Indent, SymVec->Targ);
+ }
+}
+
+void DiffEngine::printDifferences(raw_ostream &OS,
+ const std::vector<DiffOutput> &Diffs,
+ int IndentCounter) {
+ std::string Indent = std::string(IndentCounter, '\t');
+ for (auto &Attr : Diffs) {
+ switch (Attr.Kind) {
+ case AD_Diff_Scalar_Str:
+ if (IndentCounter == 0)
+ printSingleVal<DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>(Indent,
+ Attr, OS);
+ break;
+ case AD_Diff_Scalar_PackedVersion:
+ printSingleVal<
+ DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>>(Indent,
+ Attr, OS);
+ break;
+ case AD_Diff_Scalar_Unsigned:
+ printSingleVal<DiffScalarVal<uint8_t, AD_Diff_Scalar_Unsigned>>(Indent,
+ Attr, OS);
+ break;
+ case AD_Diff_Scalar_Bool:
+ printSingleVal<DiffScalarVal<bool, AD_Diff_Scalar_Bool>>(Indent, Attr,
+ OS);
+ break;
+ case AD_Str_Vec:
+ printVecVal<DiffStrVec>(Indent, Attr, OS);
+ break;
+ case AD_Sym_Vec:
+ printVecVal<DiffSymVec>(Indent, Attr, OS);
+ break;
+ case AD_Inline_Doc:
+ if (!Attr.Values.empty()) {
+ OS << Indent << Attr.Name << "\n";
+ for (auto &Item : Attr.Values)
+ if (InlineDoc *Doc = dyn_cast<InlineDoc>(Item.get()))
+ if (!Doc->DocValues.empty()) {
+ OS << Indent << "\t" << Doc->InstallName << "\n";
+ printDifferences(OS, std::move(Doc->DocValues), 2);
+ }
+ }
+ break;
+ }
+ }
+}
+
+bool DiffEngine::compareFiles(raw_ostream &OS) {
+ const auto *IFLHS = &(FileLHS->getInterfaceFile());
+ const auto *IFRHS = &(FileRHS->getInterfaceFile());
+ if (*IFLHS == *IFRHS)
+ return false;
+ OS << "< " << std::string(IFLHS->getPath().data()) << "\n> "
+ << std::string(IFRHS->getPath().data()) << "\n\n";
+ std::vector<DiffOutput> Diffs = findDifferences(IFLHS, IFRHS);
+ printDifferences(OS, Diffs, 0);
+ return true;
+}
diff --git a/llvm/tools/llvm-tapi-diff/DiffEngine.h b/llvm/tools/llvm-tapi-diff/DiffEngine.h
new file mode 100644
index 000000000000..252fbd87c637
--- /dev/null
+++ b/llvm/tools/llvm-tapi-diff/DiffEngine.h
@@ -0,0 +1,169 @@
+//===-- DiffEngine.h - File comparator --------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This header defines the interface to the llvm-tapi difference engine,
+// which structurally compares two tbd files.
+//
+//===----------------------------------------------------------------------===/
+#ifndef LLVM_TOOLS_LLVM_TAPI_DIFF_DIFFENGINE_H
+#define LLVM_TOOLS_LLVM_TAPI_DIFF_DIFFENGINE_H
+
+#include "llvm/ADT/Optional.h"
+#include "llvm/Object/TapiUniversal.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/TextAPI/Symbol.h"
+#include "llvm/TextAPI/Target.h"
+
+namespace llvm {
+
+/// InterfaceInputOrder determines from which file the diff attribute belongs
+/// to.
+enum InterfaceInputOrder { lhs, rhs };
+
+/// DiffAttrKind is the enum that holds the concrete bases for RTTI.
+enum DiffAttrKind {
+ AD_Diff_Scalar_PackedVersion,
+ AD_Diff_Scalar_Unsigned,
+ AD_Diff_Scalar_Bool,
+ AD_Diff_Scalar_Str,
+ AD_Str_Vec,
+ AD_Sym_Vec,
+ AD_Inline_Doc,
+};
+
+/// AttributeDiff is the abstract class for RTTI.
+class AttributeDiff {
+public:
+ AttributeDiff(DiffAttrKind Kind) : Kind(Kind){};
+ virtual ~AttributeDiff(){};
+ DiffAttrKind getKind() const { return Kind; }
+
+private:
+ DiffAttrKind Kind;
+};
+
+/// DiffOutput is the representation of a diff for a single attribute.
+struct DiffOutput {
+ /// The name of the attribute.
+ std::string Name;
+ /// The kind for RTTI
+ DiffAttrKind Kind;
+ /// Different values for the attribute
+ /// from each file where a diff is present.
+ std::vector<std::unique_ptr<AttributeDiff>> Values;
+ DiffOutput(std::string Name) : Name(Name){};
+};
+
+/// DiffScalarVal is a template class for the different types of scalar values.
+template <class T, DiffAttrKind U> class DiffScalarVal : public AttributeDiff {
+public:
+ DiffScalarVal(InterfaceInputOrder Order, T Val)
+ : AttributeDiff(U), Order(Order), Val(Val){};
+
+ static bool classof(const AttributeDiff *A) { return A->getKind() == U; }
+
+ void print(raw_ostream &, std::string);
+
+ T getVal() const { return Val; }
+ InterfaceInputOrder getOrder() const { return Order; }
+
+private:
+ /// The order is the file from which the diff is found.
+ InterfaceInputOrder Order;
+ T Val;
+};
+
+/// SymScalar is the diff symbol and the order.
+class SymScalar {
+public:
+ SymScalar(InterfaceInputOrder Order, const MachO::Symbol *Sym)
+ : Order(Order), Val(Sym){};
+
+ std::string getFlagString(MachO::SymbolFlags Flags) {
+ return Flags != MachO::SymbolFlags::None
+ ? " - " + stringifySymbolFlag(Flags)
+ : stringifySymbolFlag(Flags);
+ }
+
+ void print(raw_ostream &OS, std::string Indent, MachO::Target Targ);
+
+ const MachO::Symbol *getVal() const { return Val; }
+ InterfaceInputOrder getOrder() const { return Order; }
+
+private:
+ /// The order is the file from which the diff is found.
+ InterfaceInputOrder Order;
+ const MachO::Symbol *Val;
+ StringLiteral getSymbolNamePrefix(MachO::SymbolKind Kind);
+ std::string stringifySymbolFlag(MachO::SymbolFlags Flag);
+};
+
+class DiffStrVec : public AttributeDiff {
+public:
+ MachO::Target Targ;
+ /// Values is a vector of StringRef values associated with the target.
+ std::vector<DiffScalarVal<StringRef, AD_Diff_Scalar_Str>> TargValues;
+ DiffStrVec(MachO::Target Targ) : AttributeDiff(AD_Str_Vec), Targ(Targ){};
+
+ static bool classof(const AttributeDiff *A) {
+ return A->getKind() == AD_Str_Vec;
+ }
+};
+
+class DiffSymVec : public AttributeDiff {
+public:
+ MachO::Target Targ;
+ /// Values is a vector of symbol values associated with the target.
+ std::vector<SymScalar> TargValues;
+ DiffSymVec(MachO::Target Targ) : AttributeDiff(AD_Sym_Vec), Targ(Targ){};
+
+ static bool classof(const AttributeDiff *A) {
+ return A->getKind() == AD_Sym_Vec;
+ }
+};
+
+/// InlineDoc represents an inlined framework/library in a TBD File.
+class InlineDoc : public AttributeDiff {
+public:
+ /// Install name of the framework/library.
+ std::string InstallName;
+ /// Differences found from each file.
+ std::vector<DiffOutput> DocValues;
+ InlineDoc(StringRef InstName, std::vector<DiffOutput> Diff)
+ : AttributeDiff(AD_Inline_Doc), InstallName(InstName),
+ DocValues(std::move(Diff)){};
+
+ static bool classof(const AttributeDiff *A) {
+ return A->getKind() == AD_Inline_Doc;
+ }
+};
+
+/// DiffEngine contains the methods to compare the input files and print the
+/// output of the differences found in the files.
+class DiffEngine {
+public:
+ DiffEngine(object::TapiUniversal *InputFileNameLHS,
+ object::TapiUniversal *InputFileNameRHS)
+ : FileLHS(InputFileNameLHS), FileRHS(InputFileNameRHS){};
+ bool compareFiles(raw_ostream &);
+
+private:
+ object::TapiUniversal *FileLHS;
+ object::TapiUniversal *FileRHS;
+
+ /// Function that prints the differences found in the files.
+ void printDifferences(raw_ostream &, const std::vector<DiffOutput> &, int);
+ /// Function that does the comparison of the TBD files and returns the
+ /// differences.
+ std::vector<DiffOutput> findDifferences(const MachO::InterfaceFile *,
+ const MachO::InterfaceFile *);
+};
+
+} // namespace llvm
+
+#endif
diff --git a/llvm/tools/llvm-tapi-diff/llvm-tapi-diff.cpp b/llvm/tools/llvm-tapi-diff/llvm-tapi-diff.cpp
new file mode 100644
index 000000000000..40f1eec162be
--- /dev/null
+++ b/llvm/tools/llvm-tapi-diff/llvm-tapi-diff.cpp
@@ -0,0 +1,89 @@
+//===-- llvm-tapi-diff.cpp - tbd comparator command-line driver ---*-
+// C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the command-line driver for the llvm-tapi difference
+// engine.
+//
+//===----------------------------------------------------------------------===//
+#include "DiffEngine.h"
+#include "llvm/Object/TapiUniversal.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/WithColor.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cstdlib>
+
+using namespace llvm;
+using namespace MachO;
+using namespace object;
+
+namespace {
+cl::OptionCategory NMCat("llvm-tapi-diff Options");
+cl::opt<std::string> InputFileNameLHS(cl::Positional, cl::desc("<first file>"),
+ cl::cat(NMCat));
+cl::opt<std::string> InputFileNameRHS(cl::Positional, cl::desc("<second file>"),
+ cl::cat(NMCat));
+
+std::string ToolName;
+} // anonymous namespace
+
+ExitOnError ExitOnErr;
+
+void setErrorBanner(ExitOnError &ExitOnErr, std::string InputFile) {
+ ExitOnErr.setBanner(ToolName + ": error: " + InputFile + ": ");
+}
+
+Expected<std::unique_ptr<Binary>> convertFileToBinary(std::string &Filename) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
+ MemoryBuffer::getFileOrSTDIN(Filename);
+ if (BufferOrErr.getError())
+ return errorCodeToError(BufferOrErr.getError());
+ return createBinary(BufferOrErr.get()->getMemBufferRef());
+}
+
+int main(int Argc, char **Argv) {
+ InitLLVM X(Argc, Argv);
+ cl::HideUnrelatedOptions(NMCat);
+ cl::ParseCommandLineOptions(
+ Argc, Argv,
+ "This tool will compare two tbd files and return the "
+ "differences in those files.");
+ if (InputFileNameLHS.empty() || InputFileNameRHS.empty()) {
+ cl::PrintHelpMessage();
+ return EXIT_FAILURE;
+ }
+
+ ToolName = Argv[0];
+
+ setErrorBanner(ExitOnErr, InputFileNameLHS);
+ auto BinLHS = ExitOnErr(convertFileToBinary(InputFileNameLHS));
+
+ TapiUniversal *FileLHS = dyn_cast<TapiUniversal>(BinLHS.get());
+ if (!FileLHS) {
+ ExitOnErr(
+ createStringError(std::errc::executable_format_error,
+ "Error when parsing file, unsupported file format"));
+ }
+
+ setErrorBanner(ExitOnErr, InputFileNameRHS);
+ auto BinRHS = ExitOnErr(convertFileToBinary(InputFileNameRHS));
+
+ TapiUniversal *FileRHS = dyn_cast<TapiUniversal>(BinRHS.get());
+ if (!FileRHS) {
+ ExitOnErr(
+ createStringError(std::errc::executable_format_error,
+ "Error when parsing file, unsupported file format"));
+ }
+
+ raw_ostream &OS = outs();
+
+ return DiffEngine(FileLHS, FileRHS).compareFiles(OS);
+}
diff --git a/llvm/tools/llvm-xray/xray-account.cpp b/llvm/tools/llvm-xray/xray-account.cpp
index bde028a432fe..111704665c0b 100644
--- a/llvm/tools/llvm-xray/xray-account.cpp
+++ b/llvm/tools/llvm-xray/xray-account.cpp
@@ -459,7 +459,7 @@ static CommandRegistration Unused(&Account, []() -> Error {
}
std::error_code EC;
- raw_fd_ostream OS(AccountOutput, EC, sys::fs::OpenFlags::OF_Text);
+ raw_fd_ostream OS(AccountOutput, EC, sys::fs::OpenFlags::OF_TextWithCRLF);
if (EC)
return make_error<StringError>(
Twine("Cannot open file '") + AccountOutput + "' for writing.", EC);
diff --git a/llvm/tools/llvm-xray/xray-color-helper.cpp b/llvm/tools/llvm-xray/xray-color-helper.cpp
index ea7ff357826b..e2cae21e162b 100644
--- a/llvm/tools/llvm-xray/xray-color-helper.cpp
+++ b/llvm/tools/llvm-xray/xray-color-helper.cpp
@@ -13,6 +13,7 @@
#include "xray-color-helper.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/raw_ostream.h"
+#include <cmath>
using namespace llvm;
using namespace xray;
diff --git a/llvm/tools/llvm-xray/xray-color-helper.h b/llvm/tools/llvm-xray/xray-color-helper.h
index 0940fc211343..3141e90cc893 100644
--- a/llvm/tools/llvm-xray/xray-color-helper.h
+++ b/llvm/tools/llvm-xray/xray-color-helper.h
@@ -13,9 +13,8 @@
#ifndef XRAY_COLOR_HELPER_H
#define XRAY_COLOR_HELPER_H
-#include <tuple>
-
#include "llvm/ADT/ArrayRef.h"
+#include <tuple>
namespace llvm {
namespace xray {
diff --git a/llvm/tools/llvm-xray/xray-converter.cpp b/llvm/tools/llvm-xray/xray-converter.cpp
index c1a623f0f858..47cb645a5408 100644
--- a/llvm/tools/llvm-xray/xray-converter.cpp
+++ b/llvm/tools/llvm-xray/xray-converter.cpp
@@ -269,19 +269,14 @@ void TraceConverter::exportAsChromeTraceEventFormat(const Trace &Records,
auto CycleFreq = FH.CycleFrequency;
unsigned id_counter = 0;
+ int NumOutputRecords = 0;
- OS << "{\n \"traceEvents\": [";
+ OS << "{\n \"traceEvents\": [\n";
DenseMap<uint32_t, StackTrieNode *> StackCursorByThreadId{};
DenseMap<uint32_t, SmallVector<StackTrieNode *, 4>> StackRootsByThreadId{};
DenseMap<unsigned, StackTrieNode *> StacksByStackId{};
std::forward_list<StackTrieNode> NodeStore{};
- int loop_count = 0;
for (const auto &R : Records) {
- if (loop_count++ == 0)
- OS << "\n";
- else
- OS << ",\n";
-
// Chrome trace event format always wants data in micros.
// CyclesPerMicro = CycleHertz / 10^6
// TSC / CyclesPerMicro == TSC * 10^6 / CycleHertz == MicroTimestamp
@@ -306,6 +301,9 @@ void TraceConverter::exportAsChromeTraceEventFormat(const Trace &Records,
// type of B for begin or E for end, thread id, process id,
// timestamp in microseconds, and a stack frame id. The ids are logged
// in an id dictionary after the events.
+ if (NumOutputRecords++ > 0) {
+ OS << ",\n";
+ }
writeTraceViewerRecord(Version, OS, R.FuncId, R.TId, R.PId, Symbolize,
FuncIdHelper, EventTimestampUs, *StackCursor, "B");
break;
@@ -318,7 +316,7 @@ void TraceConverter::exportAsChromeTraceEventFormat(const Trace &Records,
// (And/Or in loop termination below)
StackTrieNode *PreviousCursor = nullptr;
do {
- if (PreviousCursor != nullptr) {
+ if (NumOutputRecords++ > 0) {
OS << ",\n";
}
writeTraceViewerRecord(Version, OS, StackCursor->FuncId, R.TId, R.PId,
@@ -383,7 +381,7 @@ static CommandRegistration Unused(&Convert, []() -> Error {
raw_fd_ostream OS(ConvertOutput, EC,
ConvertOutputFormat == ConvertFormats::BINARY
? sys::fs::OpenFlags::OF_None
- : sys::fs::OpenFlags::OF_Text);
+ : sys::fs::OpenFlags::OF_TextWithCRLF);
if (EC)
return make_error<StringError>(
Twine("Cannot open file '") + ConvertOutput + "' for writing.", EC);
diff --git a/llvm/tools/llvm-xray/xray-extract.cpp b/llvm/tools/llvm-xray/xray-extract.cpp
index 8304d2d27afa..a6ffacc6ab92 100644
--- a/llvm/tools/llvm-xray/xray-extract.cpp
+++ b/llvm/tools/llvm-xray/xray-extract.cpp
@@ -83,7 +83,7 @@ static CommandRegistration Unused(&Extract, []() -> Error {
InstrumentationMapOrError.takeError());
std::error_code EC;
- raw_fd_ostream OS(ExtractOutput, EC, sys::fs::OpenFlags::OF_Text);
+ raw_fd_ostream OS(ExtractOutput, EC, sys::fs::OpenFlags::OF_TextWithCRLF);
if (EC)
return make_error<StringError>(
Twine("Cannot open file '") + ExtractOutput + "' for writing.", EC);
diff --git a/llvm/tools/llvm-xray/xray-graph-diff.cpp b/llvm/tools/llvm-xray/xray-graph-diff.cpp
index 11210e2004a7..f22ea06e0537 100644
--- a/llvm/tools/llvm-xray/xray-graph-diff.cpp
+++ b/llvm/tools/llvm-xray/xray-graph-diff.cpp
@@ -294,10 +294,7 @@ static Twine truncateString(const StringRef &S, size_t n) {
}
template <typename T> static bool containsNullptr(const T &Collection) {
- for (const auto &E : Collection)
- if (E == nullptr)
- return true;
- return false;
+ return llvm::is_contained(Collection, nullptr);
}
static std::string getLabel(const GraphDiffRenderer::GraphT::EdgeValueType &E,
@@ -459,7 +456,7 @@ static CommandRegistration Unused(&GraphDiff, []() -> Error {
auto &GDR = *GDROrErr;
std::error_code EC;
- raw_fd_ostream OS(GraphDiffOutput, EC, sys::fs::OpenFlags::OF_Text);
+ raw_fd_ostream OS(GraphDiffOutput, EC, sys::fs::OpenFlags::OF_TextWithCRLF);
if (EC)
return make_error<StringError>(
Twine("Cannot open file '") + GraphDiffOutput + "' for writing.", EC);
diff --git a/llvm/tools/llvm-xray/xray-graph.cpp b/llvm/tools/llvm-xray/xray-graph.cpp
index 00a1807c07c9..39d2c5c153ef 100644
--- a/llvm/tools/llvm-xray/xray-graph.cpp
+++ b/llvm/tools/llvm-xray/xray-graph.cpp
@@ -523,7 +523,7 @@ static CommandRegistration Unused(&GraphC, []() -> Error {
auto &GR = *GROrError;
std::error_code EC;
- raw_fd_ostream OS(GraphOutput, EC, sys::fs::OpenFlags::OF_Text);
+ raw_fd_ostream OS(GraphOutput, EC, sys::fs::OpenFlags::OF_TextWithCRLF);
if (EC)
return make_error<StringError>(
Twine("Cannot open file '") + GraphOutput + "' for writing.", EC);
diff --git a/llvm/tools/opt/NewPMDriver.cpp b/llvm/tools/opt/NewPMDriver.cpp
index 401a58fc154a..8b1fbd09e40b 100644
--- a/llvm/tools/opt/NewPMDriver.cpp
+++ b/llvm/tools/opt/NewPMDriver.cpp
@@ -52,9 +52,19 @@ cl::opt<std::string>
cl::value_desc("filename"));
} // namespace llvm
-static cl::opt<bool>
- DebugPM("debug-pass-manager", cl::Hidden,
- cl::desc("Print pass management debugging information"));
+enum class DebugLogging { None, Normal, Verbose, Quiet };
+
+static cl::opt<DebugLogging> DebugPM(
+ "debug-pass-manager", cl::Hidden, cl::ValueOptional,
+ cl::desc("Print pass management debugging information"),
+ cl::init(DebugLogging::None),
+ cl::values(
+ clEnumValN(DebugLogging::Normal, "", ""),
+ clEnumValN(DebugLogging::Quiet, "quiet",
+ "Skip printing info about analyses"),
+ clEnumValN(
+ DebugLogging::Verbose, "verbose",
+ "Print extra information about adaptors and pass managers")));
static cl::list<std::string>
PassPlugins("load-pass-plugin",
@@ -121,11 +131,13 @@ static cl::opt<std::string> OptimizerLastEPPipeline(
// Individual pipeline tuning options.
extern cl::opt<bool> DisableLoopUnrolling;
+namespace llvm {
extern cl::opt<PGOKind> PGOKindFlag;
extern cl::opt<std::string> ProfileFile;
extern cl::opt<CSPGOKind> CSPGOKindFlag;
extern cl::opt<std::string> CSProfileGenFile;
extern cl::opt<bool> DisableBasicAA;
+} // namespace llvm
static cl::opt<std::string>
ProfileRemappingFile("profile-remapping-file",
@@ -137,10 +149,6 @@ static cl::opt<bool> DebugInfoForProfiling(
static cl::opt<bool> PseudoProbeForProfiling(
"new-pm-pseudo-probe-for-profiling", cl::init(false), cl::Hidden,
cl::desc("Emit pseudo probes to enable PGO profile generation."));
-static cl::opt<bool> UniqueInternalLinkageNames(
- "new-pm-unique-internal-linkage-names", cl::init(false), cl::Hidden,
- cl::desc("Uniqueify Internal Linkage Symbol Names by appending the MD5 "
- "hash of the module path."));
/// @}}
template <typename PassManagerT>
@@ -235,7 +243,7 @@ bool llvm::runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM,
bool ShouldPreserveAssemblyUseListOrder,
bool ShouldPreserveBitcodeUseListOrder,
bool EmitSummaryIndex, bool EmitModuleHash,
- bool EnableDebugify, bool Coroutines) {
+ bool EnableDebugify) {
bool VerifyEachPass = VK == VK_VerifyEachPass;
Optional<PGOOptions> P;
@@ -279,9 +287,18 @@ bool llvm::runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM,
P->CSAction = PGOOptions::CSIRUse;
}
}
+ LoopAnalysisManager LAM;
+ FunctionAnalysisManager FAM;
+ CGSCCAnalysisManager CGAM;
+ ModuleAnalysisManager MAM;
+
PassInstrumentationCallbacks PIC;
- StandardInstrumentations SI(DebugPM, VerifyEachPass);
- SI.registerCallbacks(PIC);
+ PrintPassOptions PrintPassOpts;
+ PrintPassOpts.Verbose = DebugPM == DebugLogging::Verbose;
+ PrintPassOpts.SkipAnalyses = DebugPM == DebugLogging::Quiet;
+ StandardInstrumentations SI(DebugPM != DebugLogging::None, VerifyEachPass,
+ PrintPassOpts);
+ SI.registerCallbacks(PIC, &FAM);
DebugifyEachInstrumentation Debugify;
if (DebugifyEach)
Debugify.registerCallbacks(PIC);
@@ -291,9 +308,7 @@ bool llvm::runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM,
// to false above so we shouldn't necessarily need to check whether or not the
// option has been enabled.
PTO.LoopUnrolling = !DisableLoopUnrolling;
- PTO.Coroutines = Coroutines;
- PTO.UniqueLinkageNames = UniqueInternalLinkageNames;
- PassBuilder PB(DebugPM, TM, PTO, P, &PIC);
+ PassBuilder PB(TM, PTO, P, &PIC);
registerEPCallbacks(PB);
// Load requested pass plugins and let them register pass builder callbacks
@@ -378,11 +393,6 @@ bool llvm::runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM,
}
}
- 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 our TargetLibraryInfoImpl.
@@ -395,7 +405,7 @@ bool llvm::runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM,
PB.registerLoopAnalyses(LAM);
PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);
- ModulePassManager MPM(DebugPM);
+ ModulePassManager MPM;
if (VK > VK_NoVerifier)
MPM.addPass(VerifierPass());
if (EnableDebugify)
@@ -463,3 +473,8 @@ bool llvm::runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM,
return true;
}
+
+void llvm::printPasses(raw_ostream &OS) {
+ PassBuilder PB;
+ PB.printPassNames(OS);
+}
diff --git a/llvm/tools/opt/NewPMDriver.h b/llvm/tools/opt/NewPMDriver.h
index 87a71cec4c53..056f7d6a9b80 100644
--- a/llvm/tools/opt/NewPMDriver.h
+++ b/llvm/tools/opt/NewPMDriver.h
@@ -54,6 +54,8 @@ enum PGOKind {
enum CSPGOKind { NoCSPGO, CSInstrGen, CSInstrUse };
}
+void printPasses(raw_ostream &OS);
+
/// Driver function to run the new pass manager over a module.
///
/// This function only exists factored away from opt.cpp in order to prevent
@@ -71,7 +73,7 @@ bool runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM,
bool ShouldPreserveAssemblyUseListOrder,
bool ShouldPreserveBitcodeUseListOrder,
bool EmitSummaryIndex, bool EmitModuleHash,
- bool EnableDebugify, bool Coroutines);
+ bool EnableDebugify);
} // namespace llvm
#endif
diff --git a/llvm/tools/opt/opt.cpp b/llvm/tools/opt/opt.cpp
index 5cb59f85ccf8..094f517fb703 100644
--- a/llvm/tools/opt/opt.cpp
+++ b/llvm/tools/opt/opt.cpp
@@ -66,14 +66,15 @@ static codegen::RegisterCodeGenFlags CFG;
// The OptimizationList is automatically populated with registered Passes by the
// PassNameParser.
-//
-static cl::list<const PassInfo*, bool, PassNameParser>
-PassList(cl::desc("Optimizations available:"));
+static cl::list<const PassInfo *, bool, PassNameParser> PassList(cl::desc(
+ "Optimizations available (use '-passes=' for the new pass manager)"));
-static cl::opt<bool>
- EnableNewPassManager("enable-new-pm",
- cl::desc("Enable the new pass manager"),
- cl::init(LLVM_ENABLE_NEW_PASS_MANAGER));
+static cl::opt<bool> EnableNewPassManager(
+ "enable-new-pm",
+ cl::desc("Enable the new pass manager, translating "
+ "'opt -foo' to 'opt -passes=foo'. This is strictly for the new PM "
+ "migration, use '-passes=' when possible."),
+ cl::init(LLVM_ENABLE_NEW_PASS_MANAGER));
// This flag specifies a textual description of the optimization pass pipeline
// to run over the module. This flag switches opt to use the new pass manager
@@ -81,11 +82,14 @@ static cl::opt<bool>
// pass management.
static cl::opt<std::string> PassPipeline(
"passes",
- cl::desc("A textual description of the pass pipeline for optimizing"),
- cl::Hidden);
+ cl::desc(
+ "A textual description of the pass pipeline. To have analysis passes "
+ "available before a certain pass, add 'require<foo-analysis>'."));
+
+static cl::opt<bool> PrintPasses("print-passes",
+ cl::desc("Print available passes that can be "
+ "specified in -passes=foo and exit"));
-// Other command line options...
-//
static cl::opt<std::string>
InputFilename(cl::Positional, cl::desc("<input bitcode file>"),
cl::init("-"), cl::value_desc("filename"));
@@ -142,44 +146,46 @@ static cl::opt<bool>
StripNamedMetadata("strip-named-metadata",
cl::desc("Strip module-level named metadata"));
-static cl::opt<bool> DisableInline("disable-inlining",
- cl::desc("Do not run the inliner pass"));
+static cl::opt<bool>
+ DisableInline("disable-inlining",
+ cl::desc("Do not run the inliner pass (legacy PM only)"));
static cl::opt<bool>
DisableOptimizations("disable-opt",
cl::desc("Do not run any optimization passes"));
-static cl::opt<bool>
-StandardLinkOpts("std-link-opts",
- cl::desc("Include the standard link time optimizations"));
+static cl::opt<bool> StandardLinkOpts(
+ "std-link-opts",
+ cl::desc("Include the standard link time optimizations (legacy PM only)"));
static cl::opt<bool>
-OptLevelO0("O0",
- cl::desc("Optimization level 0. Similar to clang -O0"));
+ OptLevelO0("O0", cl::desc("Optimization level 0. Similar to clang -O0. "
+ "Use -passes='default<O0>' for the new PM"));
static cl::opt<bool>
-OptLevelO1("O1",
- cl::desc("Optimization level 1. Similar to clang -O1"));
+ OptLevelO1("O1", cl::desc("Optimization level 1. Similar to clang -O1. "
+ "Use -passes='default<O1>' for the new PM"));
static cl::opt<bool>
-OptLevelO2("O2",
- cl::desc("Optimization level 2. Similar to clang -O2"));
+ OptLevelO2("O2", cl::desc("Optimization level 2. Similar to clang -O2. "
+ "Use -passes='default<O2>' for the new PM"));
static cl::opt<bool>
-OptLevelOs("Os",
- cl::desc("Like -O2 with extra optimizations for size. Similar to clang -Os"));
+ OptLevelOs("Os", cl::desc("Like -O2 but size-conscious. Similar to clang "
+ "-Os. Use -passes='default<Os>' for the new PM"));
-static cl::opt<bool>
-OptLevelOz("Oz",
- cl::desc("Like -Os but reduces code size further. Similar to clang -Oz"));
+static cl::opt<bool> OptLevelOz(
+ "Oz",
+ cl::desc("Like -O2 but optimize for code size above all else. Similar to "
+ "clang -Oz. Use -passes='default<Oz>' for the new PM"));
static cl::opt<bool>
-OptLevelO3("O3",
- cl::desc("Optimization level 3. Similar to clang -O3"));
+ OptLevelO3("O3", cl::desc("Optimization level 3. Similar to clang -O3. "
+ "Use -passes='default<O3>' for the new PM"));
-static cl::opt<unsigned>
-CodeGenOptLevel("codegen-opt-level",
- cl::desc("Override optimization level for codegen hooks"));
+static cl::opt<unsigned> CodeGenOptLevel(
+ "codegen-opt-level",
+ cl::desc("Override optimization level for codegen hooks, legacy PM only"));
static cl::opt<std::string>
TargetTriple("mtriple", cl::desc("Override target triple for module"));
@@ -205,13 +211,31 @@ DisableBuiltins("disable-builtin",
cl::ZeroOrMore);
static cl::opt<bool>
-AnalyzeOnly("analyze", cl::desc("Only perform analysis, no optimization"));
+ AnalyzeOnly("analyze", cl::desc("Only perform analysis, no optimization. "
+ "Legacy pass manager only."));
static cl::opt<bool> EnableDebugify(
"enable-debugify",
cl::desc(
"Start the pipeline with debugify and end it with check-debugify"));
+static cl::opt<bool> VerifyDebugInfoPreserve(
+ "verify-debuginfo-preserve",
+ cl::desc("Start the pipeline with collecting and end it with checking of "
+ "debug info preservation."));
+
+static cl::opt<bool> VerifyEachDebugInfoPreserve(
+ "verify-each-debuginfo-preserve",
+ cl::desc("Start each pass with collecting and end it with checking of "
+ "debug info preservation."));
+
+static cl::opt<std::string>
+ VerifyDIPreserveExport("verify-di-preserve-export",
+ cl::desc("Export debug info preservation failures into "
+ "specified (JSON) file (should be abs path as we use"
+ " append mode to insert new JSON objects)"),
+ cl::value_desc("filename"), cl::init(""));
+
static cl::opt<bool>
PrintBreakpoints("print-breakpoints-for-testing",
cl::desc("Print select breakpoints location for testing"));
@@ -231,10 +255,10 @@ static cl::opt<bool> PreserveAssemblyUseListOrder(
cl::desc("Preserve use-list order when writing LLVM assembly."),
cl::init(false), cl::Hidden);
-static cl::opt<bool>
- RunTwice("run-twice",
- cl::desc("Run all passes twice, re-using the same pass manager."),
- cl::init(false), cl::Hidden);
+static cl::opt<bool> RunTwice("run-twice",
+ cl::desc("Run all passes twice, re-using the "
+ "same pass manager (legacy PM only)."),
+ cl::init(false), cl::Hidden);
static cl::opt<bool> DiscardValueNames(
"discard-value-names",
@@ -289,6 +313,7 @@ static cl::opt<std::string> RemarksFormat(
cl::desc("The format used for serializing remarks (default: YAML)"),
cl::value_desc("format"), cl::init("yaml"));
+namespace llvm {
cl::opt<PGOKind>
PGOKindFlag("pgo-kind", cl::init(NoPGO), cl::Hidden,
cl::desc("The kind of profile guided optimization"),
@@ -317,6 +342,7 @@ cl::opt<std::string> CSProfileGenFile(
"cs-profilegen-file",
cl::desc("Path to the instrumented context sensitive profile."),
cl::Hidden);
+} // namespace llvm
static inline void addPass(legacy::PassManagerBase &PM, Pass *P) {
// Add the pass to the pass manager...
@@ -477,9 +503,8 @@ static bool shouldPinPassToLegacyPM(StringRef Pass) {
"amdgpu-unify-metadata",
"amdgpu-printf-runtime-binding",
"amdgpu-always-inline"};
- for (const auto &P : PassNameExactToIgnore)
- if (Pass == P)
- return false;
+ if (llvm::is_contained(PassNameExactToIgnore, Pass))
+ return false;
std::vector<StringRef> PassNamePrefix = {
"x86-", "xcore-", "wasm-", "systemz-", "ppc-", "nvvm-", "nvptx-",
@@ -490,24 +515,22 @@ static bool shouldPinPassToLegacyPM(StringRef Pass) {
"safe-stack", "cost-model",
"codegenprepare", "interleaved-load-combine",
"unreachableblockelim", "verify-safepoint-ir",
- "divergence", "atomic-expand",
+ "atomic-expand", "expandvp",
"hardware-loops", "type-promotion",
"mve-tail-predication", "interleaved-access",
"global-merge", "pre-isel-intrinsic-lowering",
"expand-reductions", "indirectbr-expand",
"generic-to-nvvm", "expandmemcmp",
"loop-reduce", "lower-amx-type",
- "polyhedral-info"};
+ "pre-amx-config", "lower-amx-intrinsics",
+ "polyhedral-info", "replace-with-veclib"};
for (const auto &P : PassNamePrefix)
if (Pass.startswith(P))
return true;
for (const auto &P : PassNameContain)
if (Pass.contains(P))
return true;
- for (const auto &P : PassNameExact)
- if (Pass == P)
- return true;
- return false;
+ return llvm::is_contained(PassNameExact, Pass);
}
// For use in NPM transition.
@@ -529,8 +552,6 @@ int main(int argc, char **argv) {
// Enable debug stream buffering.
EnableDebugBuffering = true;
- LLVMContext Context;
-
InitializeAllTargets();
InitializeAllTargetMCs();
InitializeAllAsmPrinters();
@@ -570,10 +591,12 @@ int main(int argc, char **argv) {
initializePostInlineEntryExitInstrumenterPass(Registry);
initializeUnreachableBlockElimLegacyPassPass(Registry);
initializeExpandReductionsPass(Registry);
+ initializeExpandVectorPredicationPass(Registry);
initializeWasmEHPreparePass(Registry);
initializeWriteBitcodePassPass(Registry);
initializeHardwareLoopsPass(Registry);
initializeTypePromotionPass(Registry);
+ initializeReplaceWithVeclibLegacyPass(Registry);
#ifdef BUILD_EXAMPLES
initializeExampleIRTransforms(Registry);
@@ -582,11 +605,21 @@ int main(int argc, char **argv) {
cl::ParseCommandLineOptions(argc, argv,
"llvm .bc -> .bc modular optimizer and analysis printer\n");
+ LLVMContext Context;
+
if (AnalyzeOnly && NoOutput) {
errs() << argv[0] << ": analyze mode conflicts with no-output mode.\n";
return 1;
}
+ // FIXME: once the legacy PM code is deleted, move runPassPipeline() here and
+ // construct the PassBuilder before parsing IR so we can reuse the same
+ // PassBuilder for print passes.
+ if (PrintPasses) {
+ printPasses(outs());
+ return 0;
+ }
+
TimeTracerRAII TimeTracer(argv[0]);
SMDiagnostic Err;
@@ -654,7 +687,8 @@ int main(int argc, char **argv) {
// specified by an internal option. This is normally done during LTO which is
// not performed via opt.
updateVCallVisibilityInModule(*M,
- /* WholeProgramVisibilityEnabledInLTO */ false);
+ /* WholeProgramVisibilityEnabledInLTO */ false,
+ /* DynamicExportSymbols */ {});
// Figure out what stream we are supposed to write to...
std::unique_ptr<ToolOutputFile> Out;
@@ -669,8 +703,8 @@ int main(int argc, char **argv) {
OutputFilename = "-";
std::error_code EC;
- sys::fs::OpenFlags Flags = OutputAssembly ? sys::fs::OF_Text
- : sys::fs::OF_None;
+ sys::fs::OpenFlags Flags =
+ OutputAssembly ? sys::fs::OF_TextWithCRLF : sys::fs::OF_None;
Out.reset(new ToolOutputFile(OutputFilename, EC, Flags));
if (EC) {
errs() << EC.message() << '\n';
@@ -746,7 +780,16 @@ int main(int argc, char **argv) {
if ((EnableNewPassManager && !shouldForceLegacyPM()) ||
PassPipeline.getNumOccurrences() > 0) {
if (AnalyzeOnly) {
- errs() << "Cannot specify -analyze under new pass manager\n";
+ errs() << "Cannot specify -analyze under new pass manager, either "
+ "specify '-enable-new-pm=0', or use the corresponding new pass "
+ "manager pass, e.g. '-passes=print<scalar-evolution>'. For a "
+ "full list of passes, see the '--print-passes' flag.\n";
+ return 1;
+ }
+ if (legacy::debugPassSpecified()) {
+ errs()
+ << "-debug-pass does not work with the new PM, either use "
+ "-debug-pass-manager, or use the legacy PM (-enable-new-pm=0)\n";
return 1;
}
if (PassPipeline.getNumOccurrences() > 0 && PassList.size() > 0) {
@@ -788,7 +831,7 @@ int main(int argc, char **argv) {
ThinLinkOut.get(), RemarksFile.get(), PassPipeline,
Passes, OK, VK, PreserveAssemblyUseListOrder,
PreserveBitcodeUseListOrder, EmitSummaryIndex,
- EmitModuleHash, EnableDebugify, Coroutines)
+ EmitModuleHash, EnableDebugify)
? 0
: 1;
}
@@ -797,10 +840,21 @@ int main(int argc, char **argv) {
// about to build. If the -debugify-each option is set, wrap each pass with
// the (-check)-debugify passes.
DebugifyCustomPassManager Passes;
- if (DebugifyEach)
- Passes.enableDebugifyEach();
+ DebugifyStatsMap DIStatsMap;
+ DebugInfoPerPassMap DIPreservationMap;
+ if (DebugifyEach) {
+ Passes.setDebugifyMode(DebugifyMode::SyntheticDebugInfo);
+ Passes.setDIStatsMap(DIStatsMap);
+ } else if (VerifyEachDebugInfoPreserve) {
+ Passes.setDebugifyMode(DebugifyMode::OriginalDebugInfo);
+ Passes.setDIPreservationMap(DIPreservationMap);
+ if (!VerifyDIPreserveExport.empty())
+ Passes.setOrigDIVerifyBugsReportFilePath(VerifyDIPreserveExport);
+ }
- bool AddOneTimeDebugifyPasses = EnableDebugify && !DebugifyEach;
+ bool AddOneTimeDebugifyPasses =
+ (EnableDebugify && !DebugifyEach) ||
+ (VerifyDebugInfoPreserve && !VerifyEachDebugInfoPreserve);
Passes.add(new TargetLibraryInfoWrapperPass(TLII));
@@ -808,8 +862,17 @@ int main(int argc, char **argv) {
Passes.add(createTargetTransformInfoWrapperPass(TM ? TM->getTargetIRAnalysis()
: TargetIRAnalysis()));
- if (AddOneTimeDebugifyPasses)
- Passes.add(createDebugifyModulePass());
+ if (AddOneTimeDebugifyPasses) {
+ if (EnableDebugify) {
+ Passes.setDIStatsMap(DIStatsMap);
+ Passes.add(createDebugifyModulePass());
+ } else if (VerifyDebugInfoPreserve) {
+ Passes.setDIPreservationMap(DIPreservationMap);
+ Passes.add(createDebugifyModulePass(
+ DebugifyMode::OriginalDebugInfo, "",
+ &(Passes.getDebugInfoPerPassMap())));
+ }
+ }
std::unique_ptr<legacy::FunctionPassManager> FPasses;
if (OptLevelO0 || OptLevelO1 || OptLevelO2 || OptLevelOs || OptLevelOz ||
@@ -953,8 +1016,17 @@ int main(int argc, char **argv) {
if (!NoVerify && !VerifyEach)
Passes.add(createVerifierPass());
- if (AddOneTimeDebugifyPasses)
- Passes.add(createCheckDebugifyModulePass(false));
+ if (AddOneTimeDebugifyPasses) {
+ if (EnableDebugify)
+ Passes.add(createCheckDebugifyModulePass(false));
+ else if (VerifyDebugInfoPreserve) {
+ if (!VerifyDIPreserveExport.empty())
+ Passes.setOrigDIVerifyBugsReportFilePath(VerifyDIPreserveExport);
+ Passes.add(createCheckDebugifyModulePass(
+ false, "", nullptr, DebugifyMode::OriginalDebugInfo,
+ &(Passes.getDebugInfoPerPassMap()), VerifyDIPreserveExport));
+ }
+ }
// In run twice mode, we want to make sure the output is bit-by-bit
// equivalent if we run the pass manager again, so setup two buffers and