summaryrefslogtreecommitdiff
path: root/llvm/tools
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2020-07-26 19:36:28 +0000
committerDimitry Andric <dim@FreeBSD.org>2020-07-26 19:36:28 +0000
commitcfca06d7963fa0909f90483b42a6d7d194d01e08 (patch)
tree209fb2a2d68f8f277793fc8df46c753d31bc853b /llvm/tools
parent706b4fc47bbc608932d3b491ae19a3b9cde9497b (diff)
Notes
Diffstat (limited to 'llvm/tools')
-rw-r--r--llvm/tools/bugpoint/CrashDebugger.cpp13
-rw-r--r--llvm/tools/bugpoint/ExecutionDriver.cpp4
-rw-r--r--llvm/tools/bugpoint/Miscompilation.cpp30
-rw-r--r--llvm/tools/bugpoint/OptimizerDriver.cpp2
-rw-r--r--llvm/tools/bugpoint/ToolRunner.cpp4
-rw-r--r--llvm/tools/bugpoint/ToolRunner.h2
-rw-r--r--llvm/tools/bugpoint/bugpoint.cpp4
-rw-r--r--llvm/tools/llc/llc.cpp192
-rw-r--r--llvm/tools/lli/lli.cpp255
-rw-r--r--llvm/tools/llvm-ar/llvm-ar.cpp187
-rw-r--r--llvm/tools/llvm-as/llvm-as.cpp25
-rw-r--r--llvm/tools/llvm-cov/CodeCoverage.cpp36
-rw-r--r--llvm/tools/llvm-cov/CoverageExporterJson.cpp19
-rw-r--r--llvm/tools/llvm-cov/CoverageExporterLcov.cpp14
-rw-r--r--llvm/tools/llvm-cov/CoverageFilters.cpp1
-rw-r--r--llvm/tools/llvm-cov/CoverageFilters.h10
-rw-r--r--llvm/tools/llvm-cov/CoverageReport.cpp16
-rw-r--r--llvm/tools/llvm-cov/CoverageSummaryInfo.cpp2
-rw-r--r--llvm/tools/llvm-cov/SourceCoverageView.cpp4
-rw-r--r--llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp11
-rw-r--r--llvm/tools/llvm-cov/gcov.cpp28
-rw-r--r--llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp4
-rw-r--r--llvm/tools/llvm-diff/DiffConsumer.cpp12
-rw-r--r--llvm/tools/llvm-diff/DifferenceEngine.cpp30
-rw-r--r--llvm/tools/llvm-dwarfdump/SectionSizes.cpp124
-rw-r--r--llvm/tools/llvm-dwarfdump/Statistics.cpp407
-rw-r--r--llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp136
-rw-r--r--llvm/tools/llvm-dwarfdump/llvm-dwarfdump.h43
-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.cpp795
-rw-r--r--llvm/tools/llvm-extract/llvm-extract.cpp56
-rw-r--r--llvm/tools/llvm-link/llvm-link.cpp83
-rw-r--r--llvm/tools/llvm-lto/llvm-lto.cpp65
-rw-r--r--llvm/tools/llvm-lto2/llvm-lto2.cpp53
-rw-r--r--llvm/tools/llvm-mc/Disassembler.cpp2
-rw-r--r--llvm/tools/llvm-mc/llvm-mc.cpp36
-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/llvm-mca.cpp10
-rw-r--r--llvm/tools/llvm-nm/llvm-nm.cpp156
-rw-r--r--llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp53
-rw-r--r--llvm/tools/llvm-objcopy/COFF/Reader.cpp19
-rw-r--r--llvm/tools/llvm-objcopy/COFF/Writer.cpp25
-rw-r--r--llvm/tools/llvm-objcopy/COFF/Writer.h1
-rw-r--r--llvm/tools/llvm-objcopy/CopyConfig.cpp114
-rw-r--r--llvm/tools/llvm-objcopy/CopyConfig.h11
-rw-r--r--llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp55
-rw-r--r--llvm/tools/llvm-objcopy/ELF/Object.cpp173
-rw-r--r--llvm/tools/llvm-objcopy/ELF/Object.h7
-rw-r--r--llvm/tools/llvm-objcopy/InstallNameToolOpts.td12
-rw-r--r--llvm/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp103
-rw-r--r--llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp248
-rw-r--r--llvm/tools/llvm-objcopy/MachO/MachOReader.cpp120
-rw-r--r--llvm/tools/llvm-objcopy/MachO/MachOReader.h3
-rw-r--r--llvm/tools/llvm-objcopy/MachO/MachOWriter.cpp100
-rw-r--r--llvm/tools/llvm-objcopy/MachO/MachOWriter.h2
-rw-r--r--llvm/tools/llvm-objcopy/MachO/Object.cpp94
-rw-r--r--llvm/tools/llvm-objcopy/MachO/Object.h56
-rw-r--r--llvm/tools/llvm-objcopy/StripOpts.td3
-rw-r--r--llvm/tools/llvm-objcopy/llvm-objcopy.cpp31
-rw-r--r--llvm/tools/llvm-objcopy/wasm/Object.cpp36
-rw-r--r--llvm/tools/llvm-objcopy/wasm/Object.h47
-rw-r--r--llvm/tools/llvm-objcopy/wasm/Reader.cpp33
-rw-r--r--llvm/tools/llvm-objcopy/wasm/Reader.h31
-rw-r--r--llvm/tools/llvm-objcopy/wasm/WasmObjcopy.cpp114
-rw-r--r--llvm/tools/llvm-objcopy/wasm/WasmObjcopy.h31
-rw-r--r--llvm/tools/llvm-objcopy/wasm/Writer.cpp78
-rw-r--r--llvm/tools/llvm-objcopy/wasm/Writer.h50
-rw-r--r--llvm/tools/llvm-objdump/COFFDump.cpp72
-rw-r--r--llvm/tools/llvm-objdump/COFFDump.h37
-rw-r--r--llvm/tools/llvm-objdump/ELFDump.cpp68
-rw-r--r--llvm/tools/llvm-objdump/ELFDump.h39
-rw-r--r--llvm/tools/llvm-objdump/MachODump.cpp234
-rw-r--r--llvm/tools/llvm-objdump/MachODump.h66
-rw-r--r--llvm/tools/llvm-objdump/WasmDump.cpp13
-rw-r--r--llvm/tools/llvm-objdump/WasmDump.h35
-rw-r--r--llvm/tools/llvm-objdump/XCOFFDump.cpp88
-rw-r--r--llvm/tools/llvm-objdump/XCOFFDump.h33
-rw-r--r--llvm/tools/llvm-objdump/llvm-objdump.cpp1505
-rw-r--r--llvm/tools/llvm-objdump/llvm-objdump.h78
-rw-r--r--llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp15
-rw-r--r--llvm/tools/llvm-pdbutil/FormatUtil.cpp12
-rw-r--r--llvm/tools/llvm-pdbutil/FormatUtil.h3
-rw-r--r--llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp4
-rw-r--r--llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp5
-rw-r--r--llvm/tools/llvm-pdbutil/StreamUtil.cpp6
-rw-r--r--llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp6
-rw-r--r--llvm/tools/llvm-pdbutil/llvm-pdbutil.h1
-rw-r--r--llvm/tools/llvm-profdata/llvm-profdata.cpp274
-rw-r--r--llvm/tools/llvm-readobj/COFFDumper.cpp265
-rw-r--r--llvm/tools/llvm-readobj/DwarfCFIEHPrinter.h171
-rw-r--r--llvm/tools/llvm-readobj/ELFDumper.cpp2020
-rw-r--r--llvm/tools/llvm-readobj/ObjDumper.cpp4
-rw-r--r--llvm/tools/llvm-readobj/ObjDumper.h4
-rw-r--r--llvm/tools/llvm-readobj/WasmDumper.cpp26
-rw-r--r--llvm/tools/llvm-readobj/XCOFFDumper.cpp13
-rw-r--r--llvm/tools/llvm-readobj/llvm-readobj.cpp38
-rw-r--r--llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp6
-rw-r--r--llvm/tools/llvm-size/llvm-size.cpp910
-rw-r--r--llvm/tools/llvm-stress/llvm-stress.cpp13
-rw-r--r--llvm/tools/llvm-strings/llvm-strings.cpp120
-rw-r--r--llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp72
-rw-r--r--llvm/tools/llvm-xray/trie-node.h2
-rw-r--r--llvm/tools/llvm-xray/xray-color-helper.cpp4
-rw-r--r--llvm/tools/llvm-xray/xray-extract.cpp16
-rw-r--r--llvm/tools/llvm-xray/xray-graph-diff.cpp12
-rw-r--r--llvm/tools/llvm-xray/xray-graph.cpp29
-rw-r--r--llvm/tools/llvm-xray/xray-stacks.cpp9
-rw-r--r--llvm/tools/opt/AnalysisWrappers.cpp9
-rw-r--r--llvm/tools/opt/NewPMDriver.cpp135
-rw-r--r--llvm/tools/opt/NewPMDriver.h8
-rw-r--r--llvm/tools/opt/PassPrinters.cpp82
-rw-r--r--llvm/tools/opt/PassPrinters.h17
-rw-r--r--llvm/tools/opt/PrintSCC.cpp11
-rw-r--r--llvm/tools/opt/opt.cpp170
117 files changed, 8503 insertions, 2874 deletions
diff --git a/llvm/tools/bugpoint/CrashDebugger.cpp b/llvm/tools/bugpoint/CrashDebugger.cpp
index aa88a06a6df06..1a39ff654f056 100644
--- a/llvm/tools/bugpoint/CrashDebugger.cpp
+++ b/llvm/tools/bugpoint/CrashDebugger.cpp
@@ -499,7 +499,8 @@ bool ReduceCrashingBlocks::TestBlocks(std::vector<const BasicBlock *> &BBs) {
std::vector<std::pair<std::string, std::string>> BlockInfo;
for (BasicBlock *BB : Blocks)
- BlockInfo.emplace_back(BB->getParent()->getName(), BB->getName());
+ BlockInfo.emplace_back(std::string(BB->getParent()->getName()),
+ std::string(BB->getName()));
SmallVector<BasicBlock *, 16> ToProcess;
for (auto &F : *M) {
@@ -606,7 +607,8 @@ bool ReduceCrashingConditionals::TestBlocks(
std::vector<std::pair<std::string, std::string>> BlockInfo;
for (const BasicBlock *BB : Blocks)
- BlockInfo.emplace_back(BB->getParent()->getName(), BB->getName());
+ BlockInfo.emplace_back(std::string(BB->getParent()->getName()),
+ std::string(BB->getName()));
SmallVector<BasicBlock *, 16> ToProcess;
for (auto &F : *M) {
@@ -696,7 +698,8 @@ bool ReduceSimplifyCFG::TestBlocks(std::vector<const BasicBlock *> &BBs) {
std::vector<std::pair<std::string, std::string>> BlockInfo;
for (const BasicBlock *BB : Blocks)
- BlockInfo.emplace_back(BB->getParent()->getName(), BB->getName());
+ BlockInfo.emplace_back(std::string(BB->getParent()->getName()),
+ std::string(BB->getName()));
// Loop over and delete any hack up any blocks that are not listed...
for (auto &F : *M)
@@ -861,7 +864,7 @@ bool ReduceCrashingMetadata::TestInsts(std::vector<Instruction *> &Insts) {
// selected in Instructions.
for (Function &F : *M)
for (Instruction &Inst : instructions(F)) {
- if (Instructions.find(&Inst) == Instructions.end()) {
+ if (!Instructions.count(&Inst)) {
Inst.dropUnknownNonDebugMetadata();
Inst.setDebugLoc({});
}
@@ -1216,7 +1219,7 @@ static Error DebugACrash(BugDriver &BD, BugTester TestFn) {
// For each remaining function, try to reduce that function's attributes.
std::vector<std::string> FunctionNames;
for (Function &F : BD.getProgram())
- FunctionNames.push_back(F.getName());
+ FunctionNames.push_back(std::string(F.getName()));
if (!FunctionNames.empty() && !BugpointIsInterrupted) {
outs() << "\n*** Attempting to reduce the number of function attributes"
diff --git a/llvm/tools/bugpoint/ExecutionDriver.cpp b/llvm/tools/bugpoint/ExecutionDriver.cpp
index 40f198b88d1a0..4c83a95989766 100644
--- a/llvm/tools/bugpoint/ExecutionDriver.cpp
+++ b/llvm/tools/bugpoint/ExecutionDriver.cpp
@@ -311,7 +311,7 @@ Expected<std::string> BugDriver::executeProgram(const Module &Program,
<< "!\n";
exit(1);
}
- BitcodeFile = UniqueFilename.str();
+ BitcodeFile = std::string(UniqueFilename.str());
if (writeProgramToFile(BitcodeFile, UniqueFD, Program)) {
errs() << ToolName << ": Error emitting bitcode to file '" << BitcodeFile
@@ -336,7 +336,7 @@ Expected<std::string> BugDriver::executeProgram(const Module &Program,
<< "\n";
exit(1);
}
- OutputFile = UniqueFile.str();
+ OutputFile = std::string(UniqueFile.str());
// Figure out which shared objects to run, if any.
std::vector<std::string> SharedObjs(AdditionalSOs);
diff --git a/llvm/tools/bugpoint/Miscompilation.cpp b/llvm/tools/bugpoint/Miscompilation.cpp
index 1621a51c91d6d..e69fe9ff6c15a 100644
--- a/llvm/tools/bugpoint/Miscompilation.cpp
+++ b/llvm/tools/bugpoint/Miscompilation.cpp
@@ -389,7 +389,8 @@ ExtractLoops(BugDriver &BD,
std::vector<std::pair<std::string, FunctionType *>> MisCompFunctions;
for (Function *F : MiscompiledFunctions) {
- MisCompFunctions.emplace_back(F->getName(), F->getFunctionType());
+ MisCompFunctions.emplace_back(std::string(F->getName()),
+ F->getFunctionType());
}
if (Linker::linkModules(*ToNotOptimize,
@@ -415,7 +416,8 @@ ExtractLoops(BugDriver &BD,
E = ToOptimizeLoopExtracted->end();
I != E; ++I)
if (!I->isDeclaration())
- MisCompFunctions.emplace_back(I->getName(), I->getFunctionType());
+ MisCompFunctions.emplace_back(std::string(I->getName()),
+ I->getFunctionType());
// Okay, great! Now we know that we extracted a loop and that loop
// extraction both didn't break the program, and didn't mask the problem.
@@ -586,7 +588,8 @@ ExtractBlocks(BugDriver &BD,
for (Module::iterator I = Extracted->begin(), E = Extracted->end(); I != E;
++I)
if (!I->isDeclaration())
- MisCompFunctions.emplace_back(I->getName(), I->getFunctionType());
+ MisCompFunctions.emplace_back(std::string(I->getName()),
+ I->getFunctionType());
if (Linker::linkModules(*ProgClone, std::move(Extracted)))
exit(1);
@@ -953,7 +956,8 @@ static Expected<bool> TestCodeGenerator(BugDriver &BD,
<< "Error making unique filename: " << EC.message() << "\n";
exit(1);
}
- if (BD.writeProgramToFile(TestModuleBC.str(), TestModuleFD, *Test)) {
+ if (BD.writeProgramToFile(std::string(TestModuleBC.str()), TestModuleFD,
+ *Test)) {
errs() << "Error writing bitcode to `" << TestModuleBC.str()
<< "'\nExiting.";
exit(1);
@@ -972,7 +976,8 @@ static Expected<bool> TestCodeGenerator(BugDriver &BD,
exit(1);
}
- if (BD.writeProgramToFile(SafeModuleBC.str(), SafeModuleFD, *Safe)) {
+ if (BD.writeProgramToFile(std::string(SafeModuleBC.str()), SafeModuleFD,
+ *Safe)) {
errs() << "Error writing bitcode to `" << SafeModuleBC << "'\nExiting.";
exit(1);
}
@@ -980,7 +985,7 @@ static Expected<bool> TestCodeGenerator(BugDriver &BD,
FileRemover SafeModuleBCRemover(SafeModuleBC.str(), !SaveTemps);
Expected<std::string> SharedObject =
- BD.compileSharedObject(SafeModuleBC.str());
+ BD.compileSharedObject(std::string(SafeModuleBC.str()));
if (Error E = SharedObject.takeError())
return std::move(E);
@@ -988,8 +993,8 @@ static Expected<bool> TestCodeGenerator(BugDriver &BD,
// Run the code generator on the `Test' code, loading the shared library.
// The function returns whether or not the new output differs from reference.
- Expected<bool> Result =
- BD.diffProgram(BD.getProgram(), TestModuleBC.str(), *SharedObject, false);
+ Expected<bool> Result = BD.diffProgram(
+ BD.getProgram(), std::string(TestModuleBC.str()), *SharedObject, false);
if (Error E = Result.takeError())
return std::move(E);
@@ -1046,7 +1051,8 @@ Error BugDriver::debugCodeGenerator() {
exit(1);
}
- if (writeProgramToFile(TestModuleBC.str(), TestModuleFD, *ToCodeGen)) {
+ if (writeProgramToFile(std::string(TestModuleBC.str()), TestModuleFD,
+ *ToCodeGen)) {
errs() << "Error writing bitcode to `" << TestModuleBC << "'\nExiting.";
exit(1);
}
@@ -1062,11 +1068,13 @@ Error BugDriver::debugCodeGenerator() {
exit(1);
}
- if (writeProgramToFile(SafeModuleBC.str(), SafeModuleFD, *ToNotCodeGen)) {
+ if (writeProgramToFile(std::string(SafeModuleBC.str()), SafeModuleFD,
+ *ToNotCodeGen)) {
errs() << "Error writing bitcode to `" << SafeModuleBC << "'\nExiting.";
exit(1);
}
- Expected<std::string> SharedObject = compileSharedObject(SafeModuleBC.str());
+ Expected<std::string> SharedObject =
+ compileSharedObject(std::string(SafeModuleBC.str()));
if (Error E = SharedObject.takeError())
return E;
diff --git a/llvm/tools/bugpoint/OptimizerDriver.cpp b/llvm/tools/bugpoint/OptimizerDriver.cpp
index 64af81fcc8a1a..25a970bd68785 100644
--- a/llvm/tools/bugpoint/OptimizerDriver.cpp
+++ b/llvm/tools/bugpoint/OptimizerDriver.cpp
@@ -141,7 +141,7 @@ bool BugDriver::runPasses(Module &Program,
<< ": Error making unique filename: " << EC.message() << "\n";
return 1;
}
- OutputFilename = UniqueFilename.str();
+ OutputFilename = std::string(UniqueFilename.str());
// set up the input file name
Expected<sys::fs::TempFile> Temp =
diff --git a/llvm/tools/bugpoint/ToolRunner.cpp b/llvm/tools/bugpoint/ToolRunner.cpp
index 19b2ea2c0181e..d880aca044d1b 100644
--- a/llvm/tools/bugpoint/ToolRunner.cpp
+++ b/llvm/tools/bugpoint/ToolRunner.cpp
@@ -442,7 +442,7 @@ Expected<CC::FileType> LLC::OutputCode(const std::string &Bitcode,
errs() << "Error making unique filename: " << EC.message() << "\n";
exit(1);
}
- OutputAsmFile = UniqueFile.str();
+ OutputAsmFile = std::string(UniqueFile.str());
std::vector<StringRef> LLCArgs;
LLCArgs.push_back(LLCPath);
@@ -772,7 +772,7 @@ Error CC::MakeSharedObject(const std::string &InputFile, FileType fileType,
errs() << "Error making unique filename: " << EC.message() << "\n";
exit(1);
}
- OutputFile = UniqueFilename.str();
+ OutputFile = std::string(UniqueFilename.str());
std::vector<StringRef> CCArgs;
diff --git a/llvm/tools/bugpoint/ToolRunner.h b/llvm/tools/bugpoint/ToolRunner.h
index dde4ec539cfb0..f6b5f26c7a668 100644
--- a/llvm/tools/bugpoint/ToolRunner.h
+++ b/llvm/tools/bugpoint/ToolRunner.h
@@ -40,7 +40,7 @@ class CC {
std::vector<std::string> ccArgs; // CC-specific arguments.
CC(StringRef ccPath, StringRef RemotePath,
const std::vector<std::string> *CCArgs)
- : CCPath(ccPath), RemoteClientPath(RemotePath) {
+ : CCPath(std::string(ccPath)), RemoteClientPath(std::string(RemotePath)) {
if (CCArgs)
ccArgs = *CCArgs;
}
diff --git a/llvm/tools/bugpoint/bugpoint.cpp b/llvm/tools/bugpoint/bugpoint.cpp
index d29a79ee3e13b..937ec23231b07 100644
--- a/llvm/tools/bugpoint/bugpoint.cpp
+++ b/llvm/tools/bugpoint/bugpoint.cpp
@@ -110,7 +110,7 @@ public:
void add(Pass *P) override {
const void *ID = P->getPassID();
const PassInfo *PI = PassRegistry::getPassRegistry()->getPassInfo(ID);
- D.addPass(PI->getPassArgument());
+ D.addPass(std::string(PI->getPassArgument()));
}
};
}
@@ -221,7 +221,7 @@ int main(int argc, char **argv) {
AddOptimizationPasses(PM, 2, 2);
for (const PassInfo *PI : PassList)
- D.addPass(PI->getPassArgument());
+ D.addPass(std::string(PI->getPassArgument()));
// Bugpoint has the ability of generating a plethora of core files, so to
// avoid filling up the disk, we prevent it
diff --git a/llvm/tools/llc/llc.cpp b/llvm/tools/llc/llc.cpp
index b35f8e853c307..95f2963ecbd61 100644
--- a/llvm/tools/llc/llc.cpp
+++ b/llvm/tools/llc/llc.cpp
@@ -15,7 +15,7 @@
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/Triple.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
-#include "llvm/CodeGen/CommandFlags.inc"
+#include "llvm/CodeGen/CommandFlags.h"
#include "llvm/CodeGen/LinkAllAsmWriterComponents.h"
#include "llvm/CodeGen/LinkAllCodegenComponents.h"
#include "llvm/CodeGen/MIRParser/MIRParser.h"
@@ -29,9 +29,9 @@
#include "llvm/IR/DiagnosticPrinter.h"
#include "llvm/IR/IRPrintingPasses.h"
#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/LLVMRemarkStreamer.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Module.h"
-#include "llvm/IR/RemarkStreamer.h"
#include "llvm/IR/Verifier.h"
#include "llvm/IRReader/IRReader.h"
#include "llvm/InitializePasses.h"
@@ -50,11 +50,14 @@
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/ToolOutputFile.h"
#include "llvm/Support/WithColor.h"
+#include "llvm/Target/TargetLoweringObjectFile.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Transforms/Utils/Cloning.h"
#include <memory>
using namespace llvm;
+static codegen::RegisterCodeGenFlags CGF;
+
// General options for llc. Other pass-specific options are specified
// within the corresponding llc passes, and target-specific options
// and back-end code generation options are specified with the target machine.
@@ -171,7 +174,7 @@ struct RunPassOption {
SmallVector<StringRef, 8> PassNames;
StringRef(Val).split(PassNames, ',', -1, false);
for (auto PassName : PassNames)
- RunPassNames->push_back(PassName);
+ RunPassNames->push_back(std::string(PassName));
}
};
}
@@ -196,13 +199,13 @@ static std::unique_ptr<ToolOutputFile> GetOutputStream(const char *TargetName,
// If InputFilename ends in .bc or .ll, remove it.
StringRef IFN = InputFilename;
if (IFN.endswith(".bc") || IFN.endswith(".ll"))
- OutputFilename = IFN.drop_back(3);
+ OutputFilename = std::string(IFN.drop_back(3));
else if (IFN.endswith(".mir"))
- OutputFilename = IFN.drop_back(4);
+ OutputFilename = std::string(IFN.drop_back(4));
else
- OutputFilename = IFN;
+ OutputFilename = std::string(IFN);
- switch (FileType) {
+ switch (codegen::getFileType()) {
case CGFT_AssemblyFile:
if (TargetName[0] == 'c') {
if (TargetName[1] == 0)
@@ -229,7 +232,7 @@ static std::unique_ptr<ToolOutputFile> GetOutputStream(const char *TargetName,
// Decide if we need "binary" output.
bool Binary = false;
- switch (FileType) {
+ switch (codegen::getFileType()) {
case CGFT_AssemblyFile:
break;
case CGFT_ObjectFile:
@@ -316,6 +319,7 @@ int main(int argc, char **argv) {
initializeScalarizeMaskedMemIntrinPass(*Registry);
initializeExpandReductionsPass(*Registry);
initializeHardwareLoopsPass(*Registry);
+ initializeTransformUtils(*Registry);
// Initialize debugging passes.
initializeScavengerTestPass(*Registry);
@@ -334,9 +338,9 @@ int main(int argc, char **argv) {
Context.setInlineAsmDiagnosticHandler(InlineAsmDiagHandler, &HasError);
Expected<std::unique_ptr<ToolOutputFile>> RemarksFileOrErr =
- setupOptimizationRemarks(Context, RemarksFilename, RemarksPasses,
- RemarksFormat, RemarksWithHotness,
- RemarksHotnessThreshold);
+ setupLLVMOptimizationRemarks(Context, RemarksFilename, RemarksPasses,
+ RemarksFormat, RemarksWithHotness,
+ RemarksHotnessThreshold);
if (Error E = RemarksFileOrErr.takeError()) {
WithColor::error(errs(), argv[0]) << toString(std::move(E)) << '\n';
return 1;
@@ -383,8 +387,9 @@ static bool addPass(PassManagerBase &PM, const char *argv0,
return true;
}
std::string Banner = std::string("After ") + std::string(P->getPassName());
+ TPC.addMachinePrePasses();
PM.add(P);
- TPC.printAndVerify(Banner);
+ TPC.addMachinePostPasses(Banner);
return false;
}
@@ -395,51 +400,18 @@ static int compileModule(char **argv, LLVMContext &Context) {
std::unique_ptr<Module> M;
std::unique_ptr<MIRParser> MIR;
Triple TheTriple;
- std::string CPUStr = getCPUStr(), FeaturesStr = getFeaturesStr();
+ std::string CPUStr = codegen::getCPUStr(),
+ FeaturesStr = codegen::getFeaturesStr();
// Set attributes on functions as loaded from MIR from command line arguments.
auto setMIRFunctionAttributes = [&CPUStr, &FeaturesStr](Function &F) {
- setFunctionAttributes(CPUStr, FeaturesStr, F);
+ codegen::setFunctionAttributes(CPUStr, FeaturesStr, F);
};
- bool SkipModule = MCPU == "help" ||
+ auto MAttrs = codegen::getMAttrs();
+ bool SkipModule = codegen::getMCPU() == "help" ||
(!MAttrs.empty() && MAttrs.front() == "help");
- // If user just wants to list available options, skip module loading
- if (!SkipModule) {
- if (InputLanguage == "mir" ||
- (InputLanguage == "" && StringRef(InputFilename).endswith(".mir"))) {
- MIR = createMIRParserFromFile(InputFilename, Err, Context,
- setMIRFunctionAttributes);
- if (MIR)
- M = MIR->parseIRModule();
- } else
- M = parseIRFile(InputFilename, Err, Context, false);
- if (!M) {
- Err.print(argv[0], WithColor::error(errs(), argv[0]));
- return 1;
- }
-
- // If we are supposed to override the target triple, do so now.
- if (!TargetTriple.empty())
- M->setTargetTriple(Triple::normalize(TargetTriple));
- TheTriple = Triple(M->getTargetTriple());
- } else {
- TheTriple = Triple(Triple::normalize(TargetTriple));
- }
-
- if (TheTriple.getTriple().empty())
- TheTriple.setTriple(sys::getDefaultTargetTriple());
-
- // Get the target specific parser.
- std::string Error;
- const Target *TheTarget = TargetRegistry::lookupTarget(MArch, TheTriple,
- Error);
- if (!TheTarget) {
- WithColor::error(errs(), argv[0]) << Error;
- return 1;
- }
-
CodeGenOpt::Level OLvl = CodeGenOpt::Default;
switch (OptLevel) {
default:
@@ -452,7 +424,7 @@ static int compileModule(char **argv, LLVMContext &Context) {
case '3': OLvl = CodeGenOpt::Aggressive; break;
}
- TargetOptions Options = InitTargetOptionsFromCodeGenFlags();
+ TargetOptions Options = codegen::InitTargetOptionsFromCodeGenFlags();
Options.DisableIntegratedAS = NoIntegratedAssembler;
Options.MCOptions.ShowMCEncoding = ShowMCEncoding;
Options.MCOptions.MCUseDwarfDirectory = EnableDwarfDirectory;
@@ -461,21 +433,97 @@ static int compileModule(char **argv, LLVMContext &Context) {
Options.MCOptions.IASSearchPaths = IncludeDirs;
Options.MCOptions.SplitDwarfFile = SplitDwarfFile;
- std::unique_ptr<TargetMachine> Target(TheTarget->createTargetMachine(
- TheTriple.getTriple(), CPUStr, FeaturesStr, Options, getRelocModel(),
- getCodeModel(), OLvl));
+ Optional<Reloc::Model> RM = codegen::getExplicitRelocModel();
+
+ const Target *TheTarget = nullptr;
+ std::unique_ptr<TargetMachine> Target;
+
+ // If user just wants to list available options, skip module loading
+ if (!SkipModule) {
+ auto SetDataLayout =
+ [&](StringRef DataLayoutTargetTriple) -> Optional<std::string> {
+ // If we are supposed to override the target triple, do so now.
+ std::string IRTargetTriple = DataLayoutTargetTriple.str();
+ if (!TargetTriple.empty())
+ IRTargetTriple = Triple::normalize(TargetTriple);
+ TheTriple = Triple(IRTargetTriple);
+ if (TheTriple.getTriple().empty())
+ TheTriple.setTriple(sys::getDefaultTargetTriple());
+
+ std::string Error;
+ TheTarget =
+ TargetRegistry::lookupTarget(codegen::getMArch(), TheTriple, Error);
+ if (!TheTarget) {
+ WithColor::error(errs(), argv[0]) << Error;
+ exit(1);
+ }
+
+ // On AIX, setting the relocation model to anything other than PIC is
+ // considered a user error.
+ if (TheTriple.isOSAIX() && RM.hasValue() && *RM != Reloc::PIC_) {
+ WithColor::error(errs(), argv[0])
+ << "invalid relocation model, AIX only supports PIC.\n";
+ exit(1);
+ }
- assert(Target && "Could not allocate target machine!");
+ Target = std::unique_ptr<TargetMachine>(TheTarget->createTargetMachine(
+ TheTriple.getTriple(), CPUStr, FeaturesStr, Options, RM,
+ codegen::getExplicitCodeModel(), OLvl));
+ assert(Target && "Could not allocate target machine!");
- // If we don't have a module then just exit now. We do this down
- // here since the CPU/Feature help is underneath the target machine
- // creation.
- if (SkipModule)
+ return Target->createDataLayout().getStringRepresentation();
+ };
+ if (InputLanguage == "mir" ||
+ (InputLanguage == "" && StringRef(InputFilename).endswith(".mir"))) {
+ MIR = createMIRParserFromFile(InputFilename, Err, Context,
+ setMIRFunctionAttributes);
+ if (MIR)
+ M = MIR->parseIRModule(SetDataLayout);
+ } else {
+ M = parseIRFile(InputFilename, Err, Context, SetDataLayout);
+ }
+ if (!M) {
+ Err.print(argv[0], WithColor::error(errs(), argv[0]));
+ return 1;
+ }
+ if (!TargetTriple.empty())
+ M->setTargetTriple(Triple::normalize(TargetTriple));
+ } else {
+ TheTriple = Triple(Triple::normalize(TargetTriple));
+ if (TheTriple.getTriple().empty())
+ TheTriple.setTriple(sys::getDefaultTargetTriple());
+
+ // Get the target specific parser.
+ std::string Error;
+ TheTarget =
+ TargetRegistry::lookupTarget(codegen::getMArch(), TheTriple, Error);
+ if (!TheTarget) {
+ WithColor::error(errs(), argv[0]) << Error;
+ return 1;
+ }
+
+ // On AIX, setting the relocation model to anything other than PIC is
+ // considered a user error.
+ if (TheTriple.isOSAIX() && RM.hasValue() && *RM != Reloc::PIC_) {
+ WithColor::error(errs(), argv[0])
+ << "invalid relocation model, AIX only supports PIC.\n";
+ return 1;
+ }
+
+ Target = std::unique_ptr<TargetMachine>(TheTarget->createTargetMachine(
+ TheTriple.getTriple(), CPUStr, FeaturesStr, Options, RM,
+ codegen::getExplicitCodeModel(), OLvl));
+ assert(Target && "Could not allocate target machine!");
+
+ // If we don't have a module then just exit now. We do this down
+ // here since the CPU/Feature help is underneath the target machine
+ // creation.
return 0;
+ }
assert(M && "Should have exited if we didn't have a module!");
- if (FloatABIForCalls != FloatABI::Default)
- Options.FloatABIType = FloatABIForCalls;
+ if (codegen::getFloatABIForCalls() != FloatABI::Default)
+ Options.FloatABIType = codegen::getFloatABIForCalls();
// Figure out where we are going to send the output.
std::unique_ptr<ToolOutputFile> Out =
@@ -504,13 +552,6 @@ static int compileModule(char **argv, LLVMContext &Context) {
TLII.disableAllFunctions();
PM.add(new TargetLibraryInfoWrapperPass(TLII));
- // Add the target data from the target machine, if it exists, or the module.
- M->setDataLayout(Target->createDataLayout());
-
- // This needs to be done after setting datalayout since it calls verifier
- // to check debug info whereas verifier relies on correct datalayout.
- UpgradeDebugInfo(*M);
-
// Verify module immediately to catch problems before doInitialization() is
// called on any passes.
if (!NoVerify && verifyModule(*M, &errs())) {
@@ -522,10 +563,9 @@ static int compileModule(char **argv, LLVMContext &Context) {
// Override function attributes based on CPUStr, FeaturesStr, and command line
// flags.
- setFunctionAttributes(CPUStr, FeaturesStr, *M);
+ codegen::setFunctionAttributes(CPUStr, FeaturesStr, *M);
- if (RelaxAll.getNumOccurrences() > 0 &&
- FileType != CGFT_ObjectFile)
+ if (mc::getExplicitRelaxAll() && codegen::getFileType() != CGFT_ObjectFile)
WithColor::warning(errs(), argv[0])
<< ": warning: ignoring -mc-relax-all because filetype != obj";
@@ -536,7 +576,7 @@ static int compileModule(char **argv, LLVMContext &Context) {
// so we can memcmp the contents in CompileTwice mode
SmallVector<char, 0> Buffer;
std::unique_ptr<raw_svector_ostream> BOS;
- if ((FileType != CGFT_AssemblyFile &&
+ if ((codegen::getFileType() != CGFT_AssemblyFile &&
!Out->os().supportsSeeking()) ||
CompileTwice) {
BOS = std::make_unique<raw_svector_ostream>(Buffer);
@@ -575,15 +615,17 @@ static int compileModule(char **argv, LLVMContext &Context) {
TPC.setInitialized();
PM.add(createPrintMIRPass(*OS));
PM.add(createFreeMachineFunctionPass());
- } else if (Target->addPassesToEmitFile(PM, *OS,
- DwoOut ? &DwoOut->os() : nullptr,
- FileType, NoVerify, MMIWP)) {
+ } else if (Target->addPassesToEmitFile(
+ PM, *OS, DwoOut ? &DwoOut->os() : nullptr,
+ codegen::getFileType(), NoVerify, MMIWP)) {
WithColor::warning(errs(), argv[0])
<< "target does not support generation of this"
<< " file type!\n";
return 1;
}
+ const_cast<TargetLoweringObjectFile *>(LLVMTM.getObjFileLowering())
+ ->Initialize(MMIWP->getMMI().getContext(), *Target);
if (MIR) {
assert(MMIWP && "Forgot to create MMIWP?");
if (MIR->parseMachineFunctions(*M, MMIWP->getMMI()))
diff --git a/llvm/tools/lli/lli.cpp b/llvm/tools/lli/lli.cpp
index bfe7e8f043034..981e0812d45ee 100644
--- a/llvm/tools/lli/lli.cpp
+++ b/llvm/tools/lli/lli.cpp
@@ -16,7 +16,7 @@
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/Triple.h"
#include "llvm/Bitcode/BitcodeReader.h"
-#include "llvm/CodeGen/CommandFlags.inc"
+#include "llvm/CodeGen/CommandFlags.h"
#include "llvm/CodeGen/LinkAllCodegenComponents.h"
#include "llvm/Config/llvm-config.h"
#include "llvm/ExecutionEngine/GenericValue.h"
@@ -24,10 +24,13 @@
#include "llvm/ExecutionEngine/JITEventListener.h"
#include "llvm/ExecutionEngine/MCJIT.h"
#include "llvm/ExecutionEngine/ObjectCache.h"
+#include "llvm/ExecutionEngine/Orc/DebugUtils.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/OrcMCJITReplacement.h"
#include "llvm/ExecutionEngine/SectionMemoryManager.h"
#include "llvm/IR/IRBuilder.h"
@@ -67,6 +70,8 @@
using namespace llvm;
+static codegen::RegisterCodeGenFlags CGF;
+
#define DEBUG_TYPE "lli"
namespace {
@@ -115,6 +120,10 @@ namespace {
cl::desc("Specifies the JITDylib to be used for any subsequent "
"-extra-module arguments."));
+ cl::list<std::string>
+ Dylibs("dlopen", cl::desc("Dynamic libraries to load before linking"),
+ cl::ZeroOrMore);
+
// The MCJIT supports building for a target address space separate from
// the JIT compilation process. Use a forked process and a copying
// memory manager with IPC to execute using this functionality.
@@ -197,6 +206,24 @@ namespace {
cl::desc("Generate software floating point library calls"),
cl::init(false));
+ cl::opt<bool> NoProcessSymbols(
+ "no-process-syms",
+ cl::desc("Do not resolve lli process symbols in JIT'd code"),
+ cl::init(false));
+
+ enum class LLJITPlatform { DetectHost, GenericIR, MachO };
+
+ cl::opt<LLJITPlatform>
+ Platform("lljit-platform", cl::desc("Platform to use with LLJIT"),
+ cl::init(LLJITPlatform::DetectHost),
+ cl::values(clEnumValN(LLJITPlatform::DetectHost, "DetectHost",
+ "Select based on JIT target triple"),
+ clEnumValN(LLJITPlatform::GenericIR, "GenericIR",
+ "Use LLJITGenericIRPlatform"),
+ clEnumValN(LLJITPlatform::MachO, "MachO",
+ "Use LLJITMachOPlatform")),
+ cl::Hidden);
+
enum class DumpKind {
NoDump,
DumpFuncsToStdOut,
@@ -250,6 +277,7 @@ public:
SmallString<128> dir(sys::path::parent_path(CacheName));
sys::fs::create_directories(Twine(dir));
}
+
std::error_code EC;
raw_fd_ostream outfile(CacheName, EC, sys::fs::OF_None);
outfile.write(Obj.getBufferStart(), Obj.getBufferSize());
@@ -282,14 +310,16 @@ private:
size_t PrefixLength = Prefix.length();
if (ModID.substr(0, PrefixLength) != Prefix)
return false;
- std::string CacheSubdir = ModID.substr(PrefixLength);
+
+ std::string CacheSubdir = ModID.substr(PrefixLength);
#if defined(_WIN32)
- // Transform "X:\foo" => "/X\foo" for convenience.
- if (isalpha(CacheSubdir[0]) && CacheSubdir[1] == ':') {
- CacheSubdir[1] = CacheSubdir[0];
- CacheSubdir[0] = '/';
- }
+ // Transform "X:\foo" => "/X\foo" for convenience.
+ if (isalpha(CacheSubdir[0]) && CacheSubdir[1] == ':') {
+ CacheSubdir[1] = CacheSubdir[0];
+ CacheSubdir[0] = '/';
+ }
#endif
+
CacheName = CacheDir + CacheSubdir;
size_t pos = CacheName.rfind('.');
CacheName.replace(pos, CacheName.length() - pos, ".o");
@@ -350,6 +380,7 @@ static void reportError(SMDiagnostic Err, const char *ProgName) {
exit(1);
}
+Error loadDylibs();
int runOrcLazyJIT(const char *ProgName);
void disallowOrcOptions();
@@ -375,6 +406,8 @@ int main(int argc, char **argv, char * const *envp) {
if (DisableCoreFiles)
sys::Process::PreventCoreFiles();
+ ExitOnErr(loadDylibs());
+
if (UseJITKind == JITKind::OrcLazy)
return runOrcLazyJIT(argv[0]);
else
@@ -405,13 +438,13 @@ int main(int argc, char **argv, char * const *envp) {
std::string ErrorMsg;
EngineBuilder builder(std::move(Owner));
- builder.setMArch(MArch);
- builder.setMCPU(getCPUStr());
- builder.setMAttrs(getFeatureList());
- if (RelocModel.getNumOccurrences())
- builder.setRelocationModel(RelocModel);
- if (CMModel.getNumOccurrences())
- builder.setCodeModel(CMModel);
+ builder.setMArch(codegen::getMArch());
+ builder.setMCPU(codegen::getCPUStr());
+ builder.setMAttrs(codegen::getFeatureList());
+ if (auto RM = codegen::getExplicitRelocModel())
+ builder.setRelocationModel(RM.getValue());
+ if (auto CM = codegen::getExplicitCodeModel())
+ builder.setCodeModel(CM.getValue());
builder.setErrorStr(&ErrorMsg);
builder.setEngineKind(ForceInterpreter
? EngineKind::Interpreter
@@ -443,9 +476,9 @@ int main(int argc, char **argv, char * const *envp) {
builder.setOptLevel(getOptLevel());
- TargetOptions Options = InitTargetOptionsFromCodeGenFlags();
- if (FloatABIForCalls != FloatABI::Default)
- Options.FloatABIType = FloatABIForCalls;
+ TargetOptions Options = codegen::InitTargetOptionsFromCodeGenFlags();
+ if (codegen::getFloatABIForCalls() != FloatABI::Default)
+ Options.FloatABIType = codegen::getFloatABIForCalls();
builder.setTargetOptions(Options);
@@ -709,7 +742,7 @@ static std::function<void(Module &)> createDebugDumper() {
continue;
if (F.hasName()) {
- std::string Name(F.getName());
+ std::string Name(std::string(F.getName()));
printf("%s ", Name.c_str());
} else
printf("<anon> ");
@@ -738,75 +771,164 @@ static std::function<void(Module &)> createDebugDumper() {
llvm_unreachable("Unknown DumpKind");
}
+Error loadDylibs() {
+ for (const auto &Dylib : Dylibs) {
+ std::string ErrMsg;
+ if (sys::DynamicLibrary::LoadLibraryPermanently(Dylib.c_str(), &ErrMsg))
+ return make_error<StringError>(ErrMsg, inconvertibleErrorCode());
+ }
+
+ return Error::success();
+}
+
static void exitOnLazyCallThroughFailure() { exit(1); }
+Expected<orc::ThreadSafeModule>
+loadModule(StringRef Path, orc::ThreadSafeContext TSCtx) {
+ SMDiagnostic Err;
+ auto M = parseIRFile(Path, Err, *TSCtx.getContext());
+ if (!M) {
+ std::string ErrMsg;
+ {
+ raw_string_ostream ErrMsgStream(ErrMsg);
+ Err.print("lli", ErrMsgStream);
+ }
+ return make_error<StringError>(std::move(ErrMsg), inconvertibleErrorCode());
+ }
+
+ if (EnableCacheManager)
+ M->setModuleIdentifier("file:" + M->getModuleIdentifier());
+
+ return orc::ThreadSafeModule(std::move(M), std::move(TSCtx));
+}
+
int runOrcLazyJIT(const char *ProgName) {
// Start setting up the JIT environment.
// Parse the main module.
orc::ThreadSafeContext TSCtx(std::make_unique<LLVMContext>());
- SMDiagnostic Err;
- auto MainModule = parseIRFile(InputFile, Err, *TSCtx.getContext());
- if (!MainModule)
- reportError(Err, ProgName);
+ auto MainModule = ExitOnErr(loadModule(InputFile, TSCtx));
+
+ // Get TargetTriple and DataLayout from the main module if they're explicitly
+ // set.
+ Optional<Triple> TT;
+ Optional<DataLayout> DL;
+ MainModule.withModuleDo([&](Module &M) {
+ if (!M.getTargetTriple().empty())
+ TT = Triple(M.getTargetTriple());
+ if (!M.getDataLayout().isDefault())
+ DL = M.getDataLayout();
+ });
- const auto &TT = MainModule->getTargetTriple();
orc::LLLazyJITBuilder Builder;
Builder.setJITTargetMachineBuilder(
- TT.empty() ? ExitOnErr(orc::JITTargetMachineBuilder::detectHost())
- : orc::JITTargetMachineBuilder(Triple(TT)));
+ TT ? orc::JITTargetMachineBuilder(*TT)
+ : ExitOnErr(orc::JITTargetMachineBuilder::detectHost()));
- if (!MArch.empty())
- Builder.getJITTargetMachineBuilder()->getTargetTriple().setArchName(MArch);
+ TT = Builder.getJITTargetMachineBuilder()->getTargetTriple();
+ if (DL)
+ Builder.setDataLayout(DL);
+
+ if (!codegen::getMArch().empty())
+ Builder.getJITTargetMachineBuilder()->getTargetTriple().setArchName(
+ codegen::getMArch());
Builder.getJITTargetMachineBuilder()
- ->setCPU(getCPUStr())
- .addFeatures(getFeatureList())
- .setRelocationModel(RelocModel.getNumOccurrences()
- ? Optional<Reloc::Model>(RelocModel)
- : None)
- .setCodeModel(CMModel.getNumOccurrences()
- ? Optional<CodeModel::Model>(CMModel)
- : None);
+ ->setCPU(codegen::getCPUStr())
+ .addFeatures(codegen::getFeatureList())
+ .setRelocationModel(codegen::getExplicitRelocModel())
+ .setCodeModel(codegen::getExplicitCodeModel());
Builder.setLazyCompileFailureAddr(
pointerToJITTargetAddress(exitOnLazyCallThroughFailure));
Builder.setNumCompileThreads(LazyJITCompileThreads);
+ // If the object cache is enabled then set a custom compile function
+ // creator to use the cache.
+ std::unique_ptr<LLIObjectCache> CacheManager;
+ if (EnableCacheManager) {
+
+ CacheManager = std::make_unique<LLIObjectCache>(ObjectCacheDir);
+
+ Builder.setCompileFunctionCreator(
+ [&](orc::JITTargetMachineBuilder JTMB)
+ -> Expected<std::unique_ptr<orc::IRCompileLayer::IRCompiler>> {
+ if (LazyJITCompileThreads > 0)
+ return std::make_unique<orc::ConcurrentIRCompiler>(std::move(JTMB),
+ CacheManager.get());
+
+ auto TM = JTMB.createTargetMachine();
+ if (!TM)
+ return TM.takeError();
+
+ return std::make_unique<orc::TMOwningSimpleCompiler>(std::move(*TM),
+ CacheManager.get());
+ });
+ }
+
+ // Set up LLJIT platform.
+ {
+ LLJITPlatform P = Platform;
+ if (P == LLJITPlatform::DetectHost) {
+ if (TT->isOSBinFormatMachO())
+ P = LLJITPlatform::MachO;
+ else
+ 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"));
+ break;
+ default:
+ llvm_unreachable("Unrecognized platform value");
+ }
+ }
+
auto J = ExitOnErr(Builder.create());
+ if (TT->isOSBinFormatELF())
+ static_cast<llvm::orc::RTDyldObjectLinkingLayer &>(J->getObjLinkingLayer())
+ .registerJITEventListener(
+ *JITEventListener::createGDBRegistrationListener());
+
if (PerModuleLazy)
J->setPartitionFunction(orc::CompileOnDemandLayer::compileWholeModule);
auto Dump = createDebugDumper();
- J->setLazyCompileTransform([&](orc::ThreadSafeModule TSM,
- const orc::MaterializationResponsibility &R) {
- TSM.withModuleDo([&](Module &M) {
- if (verifyModule(M, &dbgs())) {
- dbgs() << "Bad module: " << &M << "\n";
- exit(1);
- }
- Dump(M);
- });
- return TSM;
- });
+ J->getIRTransformLayer().setTransform(
+ [&](orc::ThreadSafeModule TSM,
+ const orc::MaterializationResponsibility &R) {
+ TSM.withModuleDo([&](Module &M) {
+ if (verifyModule(M, &dbgs())) {
+ dbgs() << "Bad module: " << &M << "\n";
+ exit(1);
+ }
+ Dump(M);
+ });
+ return TSM;
+ });
orc::MangleAndInterner Mangle(J->getExecutionSession(), J->getDataLayout());
- J->getMainJITDylib().addGenerator(
- ExitOnErr(orc::DynamicLibrarySearchGenerator::GetForCurrentProcess(
- J->getDataLayout().getGlobalPrefix(),
- [MainName = Mangle("main")](const orc::SymbolStringPtr &Name) {
- return Name != MainName;
- })));
- orc::LocalCXXRuntimeOverrides CXXRuntimeOverrides;
- ExitOnErr(CXXRuntimeOverrides.enable(J->getMainJITDylib(), Mangle));
+ // Unless they've been explicitly disabled, make process symbols available to
+ // JIT'd code.
+ if (!NoProcessSymbols)
+ J->getMainJITDylib().addGenerator(
+ ExitOnErr(orc::DynamicLibrarySearchGenerator::GetForCurrentProcess(
+ J->getDataLayout().getGlobalPrefix(),
+ [MainName = Mangle("main")](const orc::SymbolStringPtr &Name) {
+ return Name != MainName;
+ })));
// Add the main module.
- ExitOnErr(
- J->addLazyIRModule(orc::ThreadSafeModule(std::move(MainModule), TSCtx)));
+ ExitOnErr(J->addLazyIRModule(std::move(MainModule)));
// Create JITDylibs and add any extra modules.
{
@@ -818,23 +940,23 @@ int runOrcLazyJIT(const char *ProgName) {
for (auto JDItr = JITDylibs.begin(), JDEnd = JITDylibs.end();
JDItr != JDEnd; ++JDItr) {
orc::JITDylib *JD = J->getJITDylibByName(*JDItr);
- if (!JD)
- JD = &J->createJITDylib(*JDItr);
+ if (!JD) {
+ JD = &ExitOnErr(J->createJITDylib(*JDItr));
+ J->getMainJITDylib().addToLinkOrder(*JD);
+ JD->addToLinkOrder(J->getMainJITDylib());
+ }
IdxToDylib[JITDylibs.getPosition(JDItr - JITDylibs.begin())] = JD;
}
for (auto EMItr = ExtraModules.begin(), EMEnd = ExtraModules.end();
EMItr != EMEnd; ++EMItr) {
- auto M = parseIRFile(*EMItr, Err, *TSCtx.getContext());
- if (!M)
- reportError(Err, ProgName);
+ auto M = ExitOnErr(loadModule(*EMItr, TSCtx));
auto EMIdx = ExtraModules.getPosition(EMItr - ExtraModules.begin());
assert(EMIdx != 0 && "ExtraModule should have index > 0");
auto JDItr = std::prev(IdxToDylib.lower_bound(EMIdx));
auto &JD = *JDItr->second;
- ExitOnErr(
- J->addLazyIRModule(JD, orc::ThreadSafeModule(std::move(M), TSCtx)));
+ ExitOnErr(J->addLazyIRModule(JD, std::move(M)));
}
for (auto EAItr = ExtraArchives.begin(), EAEnd = ExtraArchives.end();
@@ -844,7 +966,7 @@ int runOrcLazyJIT(const char *ProgName) {
auto JDItr = std::prev(IdxToDylib.lower_bound(EAIdx));
auto &JD = *JDItr->second;
JD.addGenerator(ExitOnErr(orc::StaticLibraryDefinitionGenerator::Load(
- J->getObjLinkingLayer(), EAItr->c_str())));
+ J->getObjLinkingLayer(), EAItr->c_str(), *TT)));
}
}
@@ -855,7 +977,7 @@ int runOrcLazyJIT(const char *ProgName) {
}
// Run any static constructors.
- ExitOnErr(J->runConstructors());
+ ExitOnErr(J->initialize(J->getMainJITDylib()));
// Run any -thread-entry points.
std::vector<std::thread> AltEntryThreads;
@@ -880,8 +1002,7 @@ int runOrcLazyJIT(const char *ProgName) {
AltEntryThread.join();
// Run destructors.
- ExitOnErr(J->runDestructors());
- CXXRuntimeOverrides.runDestructors();
+ ExitOnErr(J->deinitialize(J->getMainJITDylib()));
return Result;
}
diff --git a/llvm/tools/llvm-ar/llvm-ar.cpp b/llvm/tools/llvm-ar/llvm-ar.cpp
index c339dfe1f33e3..d699d4323f0a1 100644
--- a/llvm/tools/llvm-ar/llvm-ar.cpp
+++ b/llvm/tools/llvm-ar/llvm-ar.cpp
@@ -14,17 +14,22 @@
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/ADT/Triple.h"
+#include "llvm/BinaryFormat/Magic.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/ArchiveWriter.h"
+#include "llvm/Object/IRObjectFile.h"
#include "llvm/Object/MachO.h"
#include "llvm/Object/ObjectFile.h"
+#include "llvm/Object/SymbolicFile.h"
#include "llvm/Support/Chrono.h"
#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/ConvertUTF.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/Host.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/LineIterator.h"
#include "llvm/Support/MemoryBuffer.h"
@@ -45,8 +50,7 @@
#endif
#ifdef _WIN32
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
+#include "llvm/Support/Windows/WindowsSupport.h"
#endif
using namespace llvm;
@@ -83,6 +87,9 @@ OPTIONS:
=bsd - bsd
--plugin=<string> - ignored for compatibility
-h --help - display this help and exit
+ --rsp-quoting - quoting style for response files
+ =posix - posix
+ =windows - windows
--version - print the version and exit
@<file> - read options from <file>
@@ -513,13 +520,13 @@ static std::string normalizePath(StringRef Path) {
static bool comparePaths(StringRef Path1, StringRef Path2) {
// When on Windows this function calls CompareStringOrdinal
-// as Windows file paths are case-insensitive.
+// as Windows file paths are case-insensitive.
// CompareStringOrdinal compares two Unicode strings for
// binary equivalence and allows for case insensitivity.
#ifdef _WIN32
SmallVector<wchar_t, 128> WPath1, WPath2;
- failIfError(sys::path::widenPath(normalizePath(Path1), WPath1));
- failIfError(sys::path::widenPath(normalizePath(Path2), WPath2));
+ failIfError(sys::windows::UTF8ToUTF16(normalizePath(Path1), WPath1));
+ failIfError(sys::windows::UTF8ToUTF16(normalizePath(Path2), WPath2));
return CompareStringOrdinal(WPath1.data(), WPath1.size(), WPath2.data(),
WPath2.size(), true) == CSTR_EQUAL;
@@ -650,7 +657,7 @@ static void addChildMember(std::vector<NewArchiveMember> &Members,
// the archive it's in, so the file resolves correctly.
if (Thin && FlattenArchive) {
StringSaver Saver(Alloc);
- Expected<std::string> FileNameOrErr = M.getName();
+ Expected<std::string> FileNameOrErr(M.getName());
failIfError(FileNameOrErr.takeError());
if (sys::path::is_absolute(*FileNameOrErr)) {
NMOrErr->MemberName = Saver.save(sys::path::convert_to_slash(*FileNameOrErr));
@@ -792,7 +799,7 @@ computeNewArchiveMembers(ArchiveOperation Operation,
int Pos = Ret.size();
Expected<StringRef> NameOrErr = Child.getName();
failIfError(NameOrErr.takeError());
- std::string Name = NameOrErr.get();
+ std::string Name = std::string(NameOrErr.get());
if (comparePaths(Name, RelPos)) {
assert(AddAfter || AddBefore);
if (AddBefore)
@@ -871,8 +878,9 @@ static object::Archive::Kind getDefaultForHost() {
}
static object::Archive::Kind getKindFromMember(const NewArchiveMember &Member) {
+ auto MemBufferRef = Member.Buf->getMemBufferRef();
Expected<std::unique_ptr<object::ObjectFile>> OptionalObject =
- object::ObjectFile::createObjectFile(Member.Buf->getMemBufferRef());
+ object::ObjectFile::createObjectFile(MemBufferRef);
if (OptionalObject)
return isa<object::MachOObjectFile>(**OptionalObject)
@@ -881,6 +889,23 @@ static object::Archive::Kind getKindFromMember(const NewArchiveMember &Member) {
// squelch the error in case we had a non-object file
consumeError(OptionalObject.takeError());
+
+ // If we're adding a bitcode file to the archive, detect the Archive kind
+ // based on the target triple.
+ LLVMContext Context;
+ if (identify_magic(MemBufferRef.getBuffer()) == file_magic::bitcode) {
+ if (auto ObjOrErr = object::SymbolicFile::createSymbolicFile(
+ MemBufferRef, file_magic::bitcode, &Context)) {
+ auto &IRObject = cast<object::IRObjectFile>(**ObjOrErr);
+ return Triple(IRObject.getTargetTriple()).isOSDarwin()
+ ? object::Archive::K_DARWIN
+ : object::Archive::K_GNU;
+ } else {
+ // Squelch the error in case this was not a SymbolicFile.
+ consumeError(ObjOrErr.takeError());
+ }
+ }
+
return getDefaultForHost();
}
@@ -974,7 +999,7 @@ static int performOperation(ArchiveOperation Operation,
MemoryBuffer::getFile(ArchiveName, -1, false);
std::error_code EC = Buf.getError();
if (EC && EC != errc::no_such_file_or_directory)
- fail("error opening '" + ArchiveName + "': " + EC.message());
+ fail("unable to open '" + ArchiveName + "': " + EC.message());
if (!EC) {
Error Err = Error::success();
@@ -989,7 +1014,7 @@ static int performOperation(ArchiveOperation Operation,
assert(EC == errc::no_such_file_or_directory);
if (!shouldCreateArchive(Operation)) {
- failIfError(EC, Twine("error loading '") + ArchiveName + "'");
+ failIfError(EC, Twine("unable to load '") + ArchiveName + "'");
} else {
if (!Create) {
// Produce a warning if we should and we're creating the archive
@@ -1058,7 +1083,7 @@ static void runMRIScript() {
fail("editing multiple archives not supported");
if (Saved)
fail("file already saved");
- ArchiveName = Rest;
+ ArchiveName = std::string(Rest);
break;
case MRICommand::Delete: {
llvm::erase_if(NewMembers, [=](NewArchiveMember &M) {
@@ -1075,9 +1100,9 @@ static void runMRIScript() {
fail("unknown command: " + CommandStr);
}
}
-
+
ParsingMRIScript = false;
-
+
// Nothing to do if not saved.
if (Saved)
performOperation(ReplaceOrInsert, &NewMembers);
@@ -1096,61 +1121,103 @@ static bool handleGenericOption(StringRef arg) {
return false;
}
+static const char *matchFlagWithArg(StringRef Expected,
+ ArrayRef<const char *>::iterator &ArgIt,
+ ArrayRef<const char *> Args) {
+ StringRef Arg = *ArgIt;
+
+ if (Arg.startswith("--"))
+ Arg = Arg.substr(2);
+ else if (Arg.startswith("-"))
+ Arg = Arg.substr(1);
+
+ size_t len = Expected.size();
+ if (Arg == Expected) {
+ if (++ArgIt == Args.end())
+ fail(std::string(Expected) + " requires an argument");
+
+ return *ArgIt;
+ }
+ if (Arg.startswith(Expected) && Arg.size() > len && Arg[len] == '=')
+ return Arg.data() + len + 1;
+
+ return nullptr;
+}
+
+static cl::TokenizerCallback getRspQuoting(ArrayRef<const char *> ArgsArr) {
+ cl::TokenizerCallback Ret =
+ Triple(sys::getProcessTriple()).getOS() == Triple::Win32
+ ? cl::TokenizeWindowsCommandLine
+ : cl::TokenizeGNUCommandLine;
+
+ for (ArrayRef<const char *>::iterator ArgIt = ArgsArr.begin();
+ ArgIt != ArgsArr.end(); ++ArgIt) {
+ if (const char *Match = matchFlagWithArg("rsp-quoting", ArgIt, ArgsArr)) {
+ StringRef MatchRef = Match;
+ if (MatchRef == "posix")
+ Ret = cl::TokenizeGNUCommandLine;
+ else if (MatchRef == "windows")
+ Ret = cl::TokenizeWindowsCommandLine;
+ else
+ fail(std::string("Invalid response file quoting style ") + Match);
+ }
+ }
+
+ return Ret;
+}
+
static int ar_main(int argc, char **argv) {
- SmallVector<const char *, 0> Argv(argv, argv + argc);
+ SmallVector<const char *, 0> Argv(argv + 1, argv + argc);
StringSaver Saver(Alloc);
- cl::ExpandResponseFiles(Saver, cl::TokenizeGNUCommandLine, Argv);
- for (size_t i = 1; i < Argv.size(); ++i) {
- StringRef Arg = Argv[i];
- const char *match = nullptr;
- auto MatchFlagWithArg = [&](const char *expected) {
- size_t len = strlen(expected);
- if (Arg == expected) {
- if (++i >= Argv.size())
- fail(std::string(expected) + " requires an argument");
- match = Argv[i];
- return true;
- }
- if (Arg.startswith(expected) && Arg.size() > len && Arg[len] == '=') {
- match = Arg.data() + len + 1;
- return true;
- }
- return false;
- };
- if (handleGenericOption(Argv[i]))
+
+ cl::ExpandResponseFiles(Saver, getRspQuoting(makeArrayRef(argv, argc)), Argv);
+
+ for (ArrayRef<const char *>::iterator ArgIt = Argv.begin();
+ ArgIt != Argv.end(); ++ArgIt) {
+ const char *Match = nullptr;
+
+ if (handleGenericOption(*ArgIt))
return 0;
- if (Arg == "--") {
- for (; i < Argv.size(); ++i)
- PositionalArgs.push_back(Argv[i]);
+ if (strcmp(*ArgIt, "--") == 0) {
+ ++ArgIt;
+ for (; ArgIt != Argv.end(); ++ArgIt)
+ PositionalArgs.push_back(*ArgIt);
break;
}
- if (Arg[0] == '-') {
- if (Arg.startswith("--"))
- Arg = Argv[i] + 2;
+
+ if (*ArgIt[0] != '-') {
+ if (Options.empty())
+ Options += *ArgIt;
else
- Arg = Argv[i] + 1;
- if (Arg == "M") {
- MRI = true;
- } else if (MatchFlagWithArg("format")) {
- FormatType = StringSwitch<Format>(match)
- .Case("default", Default)
- .Case("gnu", GNU)
- .Case("darwin", DARWIN)
- .Case("bsd", BSD)
- .Default(Unknown);
- if (FormatType == Unknown)
- fail(std::string("Invalid format ") + match);
- } else if (MatchFlagWithArg("plugin")) {
- // Ignored.
- } else {
- Options += Argv[i] + 1;
- }
- } else if (Options.empty()) {
- Options += Argv[i];
- } else {
- PositionalArgs.push_back(Argv[i]);
+ PositionalArgs.push_back(*ArgIt);
+ continue;
}
+
+ if (strcmp(*ArgIt, "-M") == 0) {
+ MRI = true;
+ continue;
+ }
+
+ Match = matchFlagWithArg("format", ArgIt, Argv);
+ if (Match) {
+ FormatType = StringSwitch<Format>(Match)
+ .Case("default", Default)
+ .Case("gnu", GNU)
+ .Case("darwin", DARWIN)
+ .Case("bsd", BSD)
+ .Default(Unknown);
+ if (FormatType == Unknown)
+ fail(std::string("Invalid format ") + Match);
+ continue;
+ }
+
+ if (matchFlagWithArg("plugin", ArgIt, Argv) ||
+ matchFlagWithArg("rsp-quoting", ArgIt, Argv))
+ continue;
+
+ Options += *ArgIt + 1;
}
+
ArchiveOperation Operation = parseCommandLine();
return performOperation(Operation, nullptr);
}
diff --git a/llvm/tools/llvm-as/llvm-as.cpp b/llvm/tools/llvm-as/llvm-as.cpp
index c9f50e38fc618..f2b52890a7f5c 100644
--- a/llvm/tools/llvm-as/llvm-as.cpp
+++ b/llvm/tools/llvm-as/llvm-as.cpp
@@ -88,11 +88,13 @@ static void WriteOutputFile(const Module *M, const ModuleSummaryIndex *Index) {
exit(1);
}
- if (Force || !CheckBitcodeOutputToConsole(Out->os(), true)) {
+ if (Force || !CheckBitcodeOutputToConsole(Out->os())) {
const ModuleSummaryIndex *IndexToWrite = nullptr;
- // Don't attempt to write a summary index unless it contains any entries.
- // Otherwise we get an empty summary section.
- if (Index && Index->begin() != Index->end())
+ // Don't attempt to write a summary index unless it contains any entries or
+ // has non-zero flags. The latter is used to assemble dummy index files for
+ // skipping modules by distributed ThinLTO backends. Otherwise we get an empty
+ // summary section.
+ if (Index && (Index->begin() != Index->end() || Index->getFlags()))
IndexToWrite = Index;
if (!IndexToWrite || (M && (!M->empty() || !M->global_empty())))
// If we have a non-empty Module, then we write the Module plus
@@ -119,8 +121,19 @@ int main(int argc, char **argv) {
// Parse the file now...
SMDiagnostic Err;
- auto ModuleAndIndex = parseAssemblyFileWithIndex(
- InputFilename, Err, Context, nullptr, !DisableVerify, ClDataLayout);
+ auto SetDataLayout = [](StringRef) -> Optional<std::string> {
+ if (ClDataLayout.empty())
+ return None;
+ return ClDataLayout;
+ };
+ ParsedModuleAndIndex ModuleAndIndex;
+ if (DisableVerify) {
+ ModuleAndIndex = parseAssemblyFileWithIndexNoUpgradeDebugInfo(
+ InputFilename, Err, Context, nullptr, SetDataLayout);
+ } else {
+ ModuleAndIndex = parseAssemblyFileWithIndex(InputFilename, Err, Context,
+ nullptr, SetDataLayout);
+ }
std::unique_ptr<Module> M = std::move(ModuleAndIndex.Mod);
if (!M.get()) {
Err.print(argv[0], errs());
diff --git a/llvm/tools/llvm-cov/CodeCoverage.cpp b/llvm/tools/llvm-cov/CodeCoverage.cpp
index 5f1e23f20d772..b3c895b44a6d6 100644
--- a/llvm/tools/llvm-cov/CodeCoverage.cpp
+++ b/llvm/tools/llvm-cov/CodeCoverage.cpp
@@ -33,6 +33,7 @@
#include "llvm/Support/Process.h"
#include "llvm/Support/Program.h"
#include "llvm/Support/ScopedPrinter.h"
+#include "llvm/Support/SpecialCaseList.h"
#include "llvm/Support/ThreadPool.h"
#include "llvm/Support/Threading.h"
#include "llvm/Support/ToolOutputFile.h"
@@ -245,7 +246,8 @@ CodeCoverageTool::getSourceFile(StringRef SourceFile) {
error(EC.message(), SourceFile);
return EC;
}
- LoadedSourceFiles.emplace_back(SourceFile, std::move(Buffer.get()));
+ LoadedSourceFiles.emplace_back(std::string(SourceFile),
+ std::move(Buffer.get()));
return *LoadedSourceFiles.back().second;
}
@@ -413,7 +415,8 @@ void CodeCoverageTool::remapPathNames(const CoverageMapping &Coverage) {
// Convert input files from local paths to coverage data file paths.
StringMap<std::string> InvRemappedFilenames;
for (const auto &RemappedFilename : RemappedFilenames)
- InvRemappedFilenames[RemappedFilename.getValue()] = RemappedFilename.getKey();
+ InvRemappedFilenames[RemappedFilename.getValue()] =
+ std::string(RemappedFilename.getKey());
for (std::string &Filename : SourceFiles) {
SmallString<128> NativeFilename;
@@ -510,7 +513,7 @@ void CodeCoverageTool::demangleSymbols(const CoverageMapping &Coverage) {
for (const auto &Function : Coverage.getCoveredFunctions())
// On Windows, lines in the demangler's output file end with "\r\n".
// Splitting by '\n' keeps '\r's, so cut them now.
- DC.DemangledNames[Function.Name] = Symbols[I++].rtrim();
+ DC.DemangledNames[Function.Name] = std::string(Symbols[I++].rtrim());
}
void CodeCoverageTool::writeSourceFileView(StringRef SourceFile,
@@ -688,7 +691,8 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
// PathRemapping.
auto EquivPair = StringRef(PathRemap).split(',');
if (!(EquivPair.first.empty() && EquivPair.second.empty()))
- PathRemapping = EquivPair;
+ PathRemapping = {std::string(EquivPair.first),
+ std::string(EquivPair.second)};
// If a demangler is supplied, check if it exists and register it.
if (!DemanglerOpts.empty()) {
@@ -864,8 +868,8 @@ int CodeCoverageTool::doShow(int argc, const char **argv,
}
sys::fs::file_status Status;
- if (sys::fs::status(PGOFilename, Status)) {
- error("profdata file error: can not get the file status. \n");
+ if (std::error_code EC = sys::fs::status(PGOFilename, Status)) {
+ error("Could not read profile data!", EC.message());
return 1;
}
@@ -886,7 +890,7 @@ int CodeCoverageTool::doShow(int argc, const char **argv,
// Get the source files from the function coverage mapping.
for (StringRef Filename : Coverage->getUniqueSourceFiles()) {
if (!IgnoreFilenameFilters.matchesFilename(Filename))
- SourceFiles.push_back(Filename);
+ SourceFiles.push_back(std::string(Filename));
}
// Create an index out of the source files.
@@ -940,21 +944,21 @@ int CodeCoverageTool::doShow(int argc, const char **argv,
(SourceFiles.size() != 1) || ViewOpts.hasOutputDirectory() ||
(ViewOpts.Format == CoverageViewOptions::OutputFormat::HTML);
- auto NumThreads = ViewOpts.NumThreads;
-
- // If NumThreads is not specified, auto-detect a good default.
- if (NumThreads == 0)
- NumThreads =
- std::max(1U, std::min(llvm::heavyweight_hardware_concurrency(),
- unsigned(SourceFiles.size())));
+ ThreadPoolStrategy S = hardware_concurrency(ViewOpts.NumThreads);
+ if (ViewOpts.NumThreads == 0) {
+ // If NumThreads is not specified, create one thread for each input, up to
+ // the number of hardware cores.
+ S = heavyweight_hardware_concurrency(SourceFiles.size());
+ S.Limit = true;
+ }
- if (!ViewOpts.hasOutputDirectory() || NumThreads == 1) {
+ if (!ViewOpts.hasOutputDirectory() || S.ThreadsRequested == 1) {
for (const std::string &SourceFile : SourceFiles)
writeSourceFileView(SourceFile, Coverage.get(), Printer.get(),
ShowFilenames);
} else {
// In -output-dir mode, it's safe to use multiple threads to print files.
- ThreadPool Pool(NumThreads);
+ ThreadPool Pool(S);
for (const std::string &SourceFile : SourceFiles)
Pool.async(&CodeCoverageTool::writeSourceFileView, this, SourceFile,
Coverage.get(), Printer.get(), ShowFilenames);
diff --git a/llvm/tools/llvm-cov/CoverageExporterJson.cpp b/llvm/tools/llvm-cov/CoverageExporterJson.cpp
index 216b5e3fd2263..c8bb1aa5b6ea6 100644
--- a/llvm/tools/llvm-cov/CoverageExporterJson.cpp
+++ b/llvm/tools/llvm-cov/CoverageExporterJson.cpp
@@ -53,7 +53,7 @@
#include <utility>
/// The semantic version combined as a string.
-#define LLVM_COVERAGE_EXPORT_JSON_STR "2.0.0"
+#define LLVM_COVERAGE_EXPORT_JSON_STR "2.0.1"
/// Unique type identifier for JSON coverage export.
#define LLVM_COVERAGE_EXPORT_JSON_TYPE_STR "llvm.coverage.json.export"
@@ -72,8 +72,9 @@ int64_t clamp_uint64_to_int64(uint64_t u) {
}
json::Array renderSegment(const coverage::CoverageSegment &Segment) {
- return json::Array({Segment.Line, Segment.Col, clamp_uint64_to_int64(Segment.Count),
- Segment.HasCount, Segment.IsRegionEntry});
+ return json::Array({Segment.Line, Segment.Col,
+ clamp_uint64_to_int64(Segment.Count), Segment.HasCount,
+ Segment.IsRegionEntry, Segment.IsGapRegion});
}
json::Array renderRegion(const coverage::CountedRegion &Region) {
@@ -162,12 +163,14 @@ json::Array renderFiles(const coverage::CoverageMapping &Coverage,
ArrayRef<std::string> SourceFiles,
ArrayRef<FileCoverageSummary> FileReports,
const CoverageViewOptions &Options) {
- auto NumThreads = Options.NumThreads;
- if (NumThreads == 0) {
- NumThreads = std::max(1U, std::min(llvm::heavyweight_hardware_concurrency(),
- unsigned(SourceFiles.size())));
+ ThreadPoolStrategy S = hardware_concurrency(Options.NumThreads);
+ if (Options.NumThreads == 0) {
+ // If NumThreads is not specified, create one thread for each input, up to
+ // the number of hardware cores.
+ S = heavyweight_hardware_concurrency(SourceFiles.size());
+ S.Limit = true;
}
- ThreadPool Pool(NumThreads);
+ ThreadPool Pool(S);
json::Array FileArray;
std::mutex FileArrayMutex;
diff --git a/llvm/tools/llvm-cov/CoverageExporterLcov.cpp b/llvm/tools/llvm-cov/CoverageExporterLcov.cpp
index d9b0c3b0d7a88..a6b3c66070304 100644
--- a/llvm/tools/llvm-cov/CoverageExporterLcov.cpp
+++ b/llvm/tools/llvm-cov/CoverageExporterLcov.cpp
@@ -78,10 +78,11 @@ void renderLineSummary(raw_ostream &OS, const FileCoverageSummary &Summary) {
void renderFile(raw_ostream &OS, const coverage::CoverageMapping &Coverage,
const std::string &Filename,
- const FileCoverageSummary &FileReport, bool ExportSummaryOnly) {
+ const FileCoverageSummary &FileReport, bool ExportSummaryOnly,
+ bool SkipFunctions) {
OS << "SF:" << Filename << '\n';
- if (!ExportSummaryOnly) {
+ if (!ExportSummaryOnly && !SkipFunctions) {
renderFunctions(OS, Coverage.getCoveredFunctions(Filename));
}
renderFunctionSummary(OS, FileReport);
@@ -99,9 +100,10 @@ void renderFile(raw_ostream &OS, const coverage::CoverageMapping &Coverage,
void renderFiles(raw_ostream &OS, const coverage::CoverageMapping &Coverage,
ArrayRef<std::string> SourceFiles,
ArrayRef<FileCoverageSummary> FileReports,
- bool ExportSummaryOnly) {
+ bool ExportSummaryOnly, bool SkipFunctions) {
for (unsigned I = 0, E = SourceFiles.size(); I < E; ++I)
- renderFile(OS, Coverage, SourceFiles[I], FileReports[I], ExportSummaryOnly);
+ renderFile(OS, Coverage, SourceFiles[I], FileReports[I], ExportSummaryOnly,
+ SkipFunctions);
}
} // end anonymous namespace
@@ -119,6 +121,6 @@ void CoverageExporterLcov::renderRoot(ArrayRef<std::string> SourceFiles) {
FileCoverageSummary Totals = FileCoverageSummary("Totals");
auto FileReports = CoverageReport::prepareFileReports(Coverage, Totals,
SourceFiles, Options);
- renderFiles(OS, Coverage, SourceFiles, FileReports,
- Options.ExportSummaryOnly);
+ renderFiles(OS, Coverage, SourceFiles, FileReports, Options.ExportSummaryOnly,
+ Options.SkipFunctions);
}
diff --git a/llvm/tools/llvm-cov/CoverageFilters.cpp b/llvm/tools/llvm-cov/CoverageFilters.cpp
index ca241e386e87e..da3b5214eec4c 100644
--- a/llvm/tools/llvm-cov/CoverageFilters.cpp
+++ b/llvm/tools/llvm-cov/CoverageFilters.cpp
@@ -13,6 +13,7 @@
#include "CoverageFilters.h"
#include "CoverageSummaryInfo.h"
#include "llvm/Support/Regex.h"
+#include "llvm/Support/SpecialCaseList.h"
using namespace llvm;
diff --git a/llvm/tools/llvm-cov/CoverageFilters.h b/llvm/tools/llvm-cov/CoverageFilters.h
index ce56e16071117..33fd9929c59a2 100644
--- a/llvm/tools/llvm-cov/CoverageFilters.h
+++ b/llvm/tools/llvm-cov/CoverageFilters.h
@@ -13,13 +13,17 @@
#ifndef LLVM_COV_COVERAGEFILTERS_H
#define LLVM_COV_COVERAGEFILTERS_H
-#include "CoverageSummaryInfo.h"
-#include "llvm/ProfileData/Coverage/CoverageMapping.h"
-#include "llvm/Support/SpecialCaseList.h"
+#include "llvm/ADT/StringRef.h"
#include <memory>
#include <vector>
namespace llvm {
+class SpecialCaseList;
+
+namespace coverage {
+class CoverageMapping;
+struct FunctionRecord;
+} // namespace coverage
/// Matches specific functions that pass the requirement of this filter.
class CoverageFilter {
diff --git a/llvm/tools/llvm-cov/CoverageReport.cpp b/llvm/tools/llvm-cov/CoverageReport.cpp
index 82259542c5970..8509710032d15 100644
--- a/llvm/tools/llvm-cov/CoverageReport.cpp
+++ b/llvm/tools/llvm-cov/CoverageReport.cpp
@@ -352,15 +352,15 @@ std::vector<FileCoverageSummary> CoverageReport::prepareFileReports(
ArrayRef<std::string> Files, const CoverageViewOptions &Options,
const CoverageFilter &Filters) {
unsigned LCP = getRedundantPrefixLen(Files);
- auto NumThreads = Options.NumThreads;
- // If NumThreads is not specified, auto-detect a good default.
- if (NumThreads == 0)
- NumThreads =
- std::max(1U, std::min(llvm::heavyweight_hardware_concurrency(),
- unsigned(Files.size())));
-
- ThreadPool Pool(NumThreads);
+ ThreadPoolStrategy S = hardware_concurrency(Options.NumThreads);
+ if (Options.NumThreads == 0) {
+ // If NumThreads is not specified, create one thread for each input, up to
+ // the number of hardware cores.
+ S = heavyweight_hardware_concurrency(Files.size());
+ S.Limit = true;
+ }
+ ThreadPool Pool(S);
std::vector<FileCoverageSummary> FileReports;
FileReports.reserve(Files.size());
diff --git a/llvm/tools/llvm-cov/CoverageSummaryInfo.cpp b/llvm/tools/llvm-cov/CoverageSummaryInfo.cpp
index 1029f7784040d..929529c27b6ea 100644
--- a/llvm/tools/llvm-cov/CoverageSummaryInfo.cpp
+++ b/llvm/tools/llvm-cov/CoverageSummaryInfo.cpp
@@ -51,7 +51,7 @@ FunctionCoverageSummary::get(const InstantiationGroup &Group,
ArrayRef<FunctionCoverageSummary> Summaries) {
std::string Name;
if (Group.hasName()) {
- Name = Group.getName();
+ Name = std::string(Group.getName());
} else {
llvm::raw_string_ostream OS(Name);
OS << "Definition at line " << Group.getLine() << ", column "
diff --git a/llvm/tools/llvm-cov/SourceCoverageView.cpp b/llvm/tools/llvm-cov/SourceCoverageView.cpp
index 0e20ea63cd6ff..cd7395a1a87d3 100644
--- a/llvm/tools/llvm-cov/SourceCoverageView.cpp
+++ b/llvm/tools/llvm-cov/SourceCoverageView.cpp
@@ -48,7 +48,7 @@ std::string CoveragePrinter::getOutputPath(StringRef Path, StringRef Extension,
sys::path::append(FullPath, PathFilename);
sys::path::native(FullPath);
- return FullPath.str();
+ return std::string(FullPath.str());
}
Expected<CoveragePrinter::OwnedStream>
@@ -158,7 +158,7 @@ std::string SourceCoverageView::getSourceName() const {
SmallString<128> SourceText(SourceName);
sys::path::remove_dots(SourceText, /*remove_dot_dots=*/true);
sys::path::native(SourceText);
- return SourceText.str();
+ return std::string(SourceText.str());
}
void SourceCoverageView::addExpansion(
diff --git a/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp b/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp
index e3332245f9c8e..9d10def0a211b 100644
--- a/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp
+++ b/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp
@@ -30,8 +30,7 @@ std::string escape(StringRef Str, const CoverageViewOptions &Opts) {
if (C == '\t') {
// Replace '\t' with up to TabSize spaces.
unsigned NumSpaces = Opts.TabSize - (ColNum % Opts.TabSize);
- for (unsigned I = 0; I < NumSpaces; ++I)
- TabExpandedResult += ' ';
+ TabExpandedResult.append(NumSpaces, ' ');
ColNum += NumSpaces;
} else {
TabExpandedResult += C;
@@ -250,7 +249,7 @@ const char *CreatedTimeTag = "h4";
std::string getPathToStyle(StringRef ViewPath) {
std::string PathToStyle = "";
- std::string PathSep = sys::path::get_separator();
+ std::string PathSep = std::string(sys::path::get_separator());
unsigned NumSeps = ViewPath.count(PathSep);
for (unsigned I = 0, E = NumSeps; I < E; ++I)
PathToStyle += ".." + PathSep;
@@ -359,7 +358,7 @@ void CoveragePrinterHTML::emitFileSummary(raw_ostream &OS, StringRef SF,
// Simplify the display file path, and wrap it in a link if requested.
std::string Filename;
if (IsTotals) {
- Filename = SF;
+ Filename = std::string(SF);
} else {
Filename = buildLinkToFile(SF, FCS);
}
@@ -507,7 +506,7 @@ void SourceCoverageViewHTML::renderLine(raw_ostream &OS, LineRef L,
unsigned LCol = 1;
auto Snip = [&](unsigned Start, unsigned Len) {
- Snippets.push_back(Line.substr(Start, Len));
+ Snippets.push_back(std::string(Line.substr(Start, Len)));
LCol += Len;
};
@@ -533,7 +532,7 @@ void SourceCoverageViewHTML::renderLine(raw_ostream &OS, LineRef L,
auto Highlight = [&](const std::string &Snippet, unsigned LC, unsigned RC) {
if (getOptions().Debug)
HighlightedRanges.emplace_back(LC, RC);
- return tag("span", Snippet, Color.getValue());
+ return tag("span", Snippet, std::string(Color.getValue()));
};
auto CheckIfUncovered = [&](const CoverageSegment *S) {
diff --git a/llvm/tools/llvm-cov/gcov.cpp b/llvm/tools/llvm-cov/gcov.cpp
index 8a00ff64711fb..7a1dbbfe9338b 100644
--- a/llvm/tools/llvm-cov/gcov.cpp
+++ b/llvm/tools/llvm-cov/gcov.cpp
@@ -65,11 +65,11 @@ static void reportCoverage(StringRef SourceFile, StringRef ObjectDir,
// Clear the filename to make it clear we didn't read anything.
GCDA = "-";
} else {
- GCOVBuffer GCDA_GB(GCDA_Buff.get().get());
- if (!GF.readGCDA(GCDA_GB)) {
+ GCOVBuffer gcda_buf(GCDA_Buff.get().get());
+ if (!gcda_buf.readGCDAFormat())
+ errs() << GCDA << ":not a gcov data file\n";
+ else if (!GF.readGCDA(gcda_buf))
errs() << "Invalid .gcda File!\n";
- return;
- }
}
if (DumpGCOV)
@@ -77,7 +77,7 @@ static void reportCoverage(StringRef SourceFile, StringRef ObjectDir,
FileInfo FI(Options);
GF.collectLineCounts(FI);
- FI.print(llvm::outs(), SourceFile, GCNO, GCDA);
+ FI.print(llvm::outs(), SourceFile, GCNO, GCDA, GF);
}
int gcovMain(int argc, const char *argv[]) {
@@ -105,6 +105,16 @@ int gcovMain(int argc, const char *argv[]) {
cl::desc("Show coverage for each function"));
cl::alias FuncSummaryA("function-summaries", cl::aliasopt(FuncSummary));
+ // Supported by gcov 4.9~8. gcov 9 (GCC r265587) removed --intermediate-format
+ // and -i was changed to mean --json-format. We consider this format still
+ // useful and support -i.
+ cl::opt<bool> Intermediate(
+ "intermediate-format", cl::init(false),
+ cl::desc("Output .gcov in intermediate text format"));
+ cl::alias IntermediateA("i", cl::desc("Alias for --intermediate-format"),
+ cl::Grouping, cl::NotHidden,
+ cl::aliasopt(Intermediate));
+
cl::opt<bool> NoOutput("n", cl::Grouping, cl::init(false),
cl::desc("Do not output any .gcov files"));
cl::alias NoOutputA("no-output", cl::aliasopt(NoOutput));
@@ -119,6 +129,10 @@ int gcovMain(int argc, const char *argv[]) {
cl::desc("Preserve path components"));
cl::alias PreservePathsA("preserve-paths", cl::aliasopt(PreservePaths));
+ cl::opt<bool> UseStdout("t", cl::Grouping, cl::init(false),
+ cl::desc("Print to stdout"));
+ cl::alias UseStdoutA("stdout", cl::aliasopt(UseStdout));
+
cl::opt<bool> UncondBranch("u", cl::Grouping, cl::init(false),
cl::desc("Display unconditional branch info "
"(requires -b)"));
@@ -140,8 +154,8 @@ int gcovMain(int argc, const char *argv[]) {
cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n");
GCOV::Options Options(AllBlocks, BranchProb, BranchCount, FuncSummary,
- PreservePaths, UncondBranch, LongNames, NoOutput,
- HashFilenames);
+ PreservePaths, UncondBranch, Intermediate, LongNames,
+ NoOutput, UseStdout, HashFilenames);
for (const auto &SourceFile : SourceFiles)
reportCoverage(SourceFile, ObjectDir, InputGCNO, InputGCDA, DumpGCOV,
diff --git a/llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp b/llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp
index 6de512fc18dc0..93d6322a167ee 100644
--- a/llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp
+++ b/llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp
@@ -145,9 +145,9 @@ static void demangleLine(llvm::raw_ostream &OS, StringRef Mangled, bool Split) {
SmallVector<std::pair<StringRef, StringRef>, 16> Words;
SplitStringDelims(Mangled, Words, IsLegalItaniumChar);
for (const auto &Word : Words)
- Result += ::demangle(Word.first) + Word.second.str();
+ Result += ::demangle(std::string(Word.first)) + Word.second.str();
} else
- Result = ::demangle(Mangled);
+ Result = ::demangle(std::string(Mangled));
OS << Result << '\n';
OS.flush();
}
diff --git a/llvm/tools/llvm-diff/DiffConsumer.cpp b/llvm/tools/llvm-diff/DiffConsumer.cpp
index b797143bde1b4..6228ff2bae983 100644
--- a/llvm/tools/llvm-diff/DiffConsumer.cpp
+++ b/llvm/tools/llvm-diff/DiffConsumer.cpp
@@ -50,15 +50,15 @@ void DiffConsumer::printValue(Value *V, bool isL) {
return;
}
if (V->getType()->isVoidTy()) {
- if (isa<StoreInst>(V)) {
+ if (auto *SI = dyn_cast<StoreInst>(V)) {
out << "store to ";
- printValue(cast<StoreInst>(V)->getPointerOperand(), isL);
- } else if (isa<CallInst>(V)) {
+ printValue(SI->getPointerOperand(), isL);
+ } else if (auto *CI = dyn_cast<CallInst>(V)) {
out << "call to ";
- printValue(cast<CallInst>(V)->getCalledValue(), isL);
- } else if (isa<InvokeInst>(V)) {
+ printValue(CI->getCalledOperand(), isL);
+ } else if (auto *II = dyn_cast<InvokeInst>(V)) {
out << "invoke to ";
- printValue(cast<InvokeInst>(V)->getCalledValue(), isL);
+ printValue(II->getCalledOperand(), isL);
} else {
out << *V;
}
diff --git a/llvm/tools/llvm-diff/DifferenceEngine.cpp b/llvm/tools/llvm-diff/DifferenceEngine.cpp
index 564ce78705927..2cf1afbc6af57 100644
--- a/llvm/tools/llvm-diff/DifferenceEngine.cpp
+++ b/llvm/tools/llvm-diff/DifferenceEngine.cpp
@@ -14,10 +14,10 @@
#include "DifferenceEngine.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/IR/CFG.h"
-#include "llvm/IR/CallSite.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Instructions.h"
@@ -222,9 +222,9 @@ class FunctionDifferenceEngine {
bool matchForBlockDiff(Instruction *L, Instruction *R);
void runBlockDiff(BasicBlock::iterator LI, BasicBlock::iterator RI);
- bool diffCallSites(CallSite L, CallSite R, bool Complain) {
+ bool diffCallSites(CallBase &L, CallBase &R, bool Complain) {
// FIXME: call attributes
- if (!equivalentAsOperands(L.getCalledValue(), R.getCalledValue())) {
+ if (!equivalentAsOperands(L.getCalledOperand(), R.getCalledOperand())) {
if (Complain) Engine.log("called functions differ");
return true;
}
@@ -233,10 +233,10 @@ class FunctionDifferenceEngine {
return true;
}
for (unsigned I = 0, E = L.arg_size(); I != E; ++I)
- if (!equivalentAsOperands(L.getArgument(I), R.getArgument(I))) {
+ if (!equivalentAsOperands(L.getArgOperand(I), R.getArgOperand(I))) {
if (Complain)
Engine.logf("arguments %l and %r differ")
- << L.getArgument(I) << R.getArgument(I);
+ << L.getArgOperand(I) << R.getArgOperand(I);
return true;
}
return false;
@@ -258,7 +258,7 @@ class FunctionDifferenceEngine {
return true;
}
} else if (isa<CallInst>(L)) {
- return diffCallSites(CallSite(L), CallSite(R), Complain);
+ return diffCallSites(cast<CallInst>(*L), cast<CallInst>(*R), Complain);
} else if (isa<PHINode>(L)) {
// FIXME: implement.
@@ -273,14 +273,14 @@ class FunctionDifferenceEngine {
// Terminators.
} else if (isa<InvokeInst>(L)) {
- InvokeInst *LI = cast<InvokeInst>(L);
- InvokeInst *RI = cast<InvokeInst>(R);
- if (diffCallSites(CallSite(LI), CallSite(RI), Complain))
+ InvokeInst &LI = cast<InvokeInst>(*L);
+ InvokeInst &RI = cast<InvokeInst>(*R);
+ if (diffCallSites(LI, RI, Complain))
return true;
if (TryUnify) {
- tryUnify(LI->getNormalDest(), RI->getNormalDest());
- tryUnify(LI->getUnwindDest(), RI->getUnwindDest());
+ tryUnify(LI.getNormalDest(), RI.getNormalDest());
+ tryUnify(LI.getUnwindDest(), RI.getUnwindDest());
}
return false;
@@ -577,7 +577,7 @@ void FunctionDifferenceEngine::runBlockDiff(BasicBlock::iterator LStart,
DiffLogBuilder Diff(Engine.getConsumer());
// Drop trailing matches.
- while (Path.back() == DC_match)
+ while (Path.size() && Path.back() == DC_match)
Path.pop_back();
// Skip leading matches.
@@ -638,7 +638,8 @@ void FunctionDifferenceEngine::runBlockDiff(BasicBlock::iterator LStart,
if (!isa<CallInst>(*I)) return;
CallInst *LCall = cast<CallInst>(&*I);
InvokeInst *RInvoke = cast<InvokeInst>(RTerm);
- if (!equivalentAsOperands(LCall->getCalledValue(), RInvoke->getCalledValue()))
+ if (!equivalentAsOperands(LCall->getCalledOperand(),
+ RInvoke->getCalledOperand()))
return;
if (!LCall->use_empty())
Values[LCall] = RInvoke;
@@ -651,7 +652,8 @@ void FunctionDifferenceEngine::runBlockDiff(BasicBlock::iterator LStart,
if (!isa<CallInst>(*I)) return;
CallInst *RCall = cast<CallInst>(I);
InvokeInst *LInvoke = cast<InvokeInst>(LTerm);
- if (!equivalentAsOperands(LInvoke->getCalledValue(), RCall->getCalledValue()))
+ if (!equivalentAsOperands(LInvoke->getCalledOperand(),
+ RCall->getCalledOperand()))
return;
if (!LInvoke->use_empty())
Values[LInvoke] = RCall;
diff --git a/llvm/tools/llvm-dwarfdump/SectionSizes.cpp b/llvm/tools/llvm-dwarfdump/SectionSizes.cpp
new file mode 100644
index 0000000000000..8c456d50baa7c
--- /dev/null
+++ b/llvm/tools/llvm-dwarfdump/SectionSizes.cpp
@@ -0,0 +1,124 @@
+//===-- SectionSizes.cpp - Debug section sizes ----------------------------===//
+//
+// 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 "llvm-dwarfdump.h"
+
+#define DEBUG_TYPE "dwarfdump"
+
+using namespace llvm;
+using namespace llvm::dwarfdump;
+using namespace llvm::object;
+
+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());
+ }
+ return Width;
+}
+
+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();
+ Width = std::max(Width, NumWidth);
+ }
+ return Width;
+}
+
+static void prettyPrintSectionSizes(const ObjectFile &Obj,
+ const SectionSizes &Sizes,
+ raw_ostream &OS) {
+ const StringRef SectionNameTitle = "SECTION";
+ const StringRef SectionSizeTitle = "SIZE (b)";
+
+ size_t NameColWidth = getNameColumnWidth(Sizes, SectionNameTitle);
+ size_t SizeColWidth = getSizeColumnWidth(Sizes, SectionSizeTitle);
+
+ OS << "----------------------------------------------------" << '\n';
+ OS << SectionNameTitle;
+ size_t SectionNameTitleWidth = SectionNameTitle.size();
+ for (unsigned i = 0; i < (NameColWidth - SectionNameTitleWidth) + 2; i++)
+ OS << " ";
+ OS << SectionSizeTitle << '\n';
+ for (unsigned i = 0; i < NameColWidth; i++)
+ OS << "-";
+ OS << " ";
+
+ for (unsigned i = 0; i < SizeColWidth; i++)
+ OS << "-";
+ OS << '\n';
+
+ for (const auto &DebugSec : Sizes.DebugSectionSizes) {
+ OS << left_justify(DebugSec.getKey(), NameColWidth) << " ";
+
+ auto NumBytes = std::to_string(DebugSec.getValue());
+ OS << right_justify(NumBytes, SizeColWidth) << " ("
+ << format("%0.2f", DebugSec.getValue() /
+ static_cast<double>(Sizes.TotalObjectSize) * 100)
+ << "%)\n";
+ }
+
+ OS << '\n';
+ OS << " Total Size: " << Sizes.TotalDebugSectionsSize << " ("
+ << format("%0.2f", Sizes.TotalDebugSectionsSize /
+ static_cast<double>(Sizes.TotalObjectSize) * 100)
+ << "%)\n";
+ OS << " Total File Size: " << Sizes.TotalObjectSize << '\n';
+ OS << "----------------------------------------------------" << '\n';
+}
+
+void dwarfdump::calculateSectionSizes(const ObjectFile &Obj,
+ SectionSizes &Sizes,
+ const Twine &Filename) {
+ // Get total size.
+ Sizes.TotalObjectSize = Obj.getData().size();
+
+ for (const SectionRef &Section : Obj.sections()) {
+ StringRef SectionName;
+ if (Expected<StringRef> NameOrErr = Section.getName())
+ SectionName = *NameOrErr;
+ else
+ WithColor::defaultWarningHandler(
+ createFileError(Filename, NameOrErr.takeError()));
+
+ LLVM_DEBUG(dbgs() << SectionName.str() << ": " << Section.getSize()
+ << '\n');
+
+ if (!Section.isDebugSection(SectionName))
+ continue;
+
+ Sizes.TotalDebugSectionsSize += Section.getSize();
+ Sizes.DebugSectionSizes[SectionName] += Section.getSize();
+ }
+}
+
+bool dwarfdump::collectObjectSectionSizes(ObjectFile &Obj,
+ DWARFContext & /*DICtx*/,
+ const Twine &Filename,
+ raw_ostream &OS) {
+ SectionSizes Sizes;
+
+ // Get the section sizes.
+ calculateSectionSizes(Obj, Sizes, Filename);
+
+ OS << "----------------------------------------------------\n";
+ OS << "file: " << Filename.str() << '\n';
+
+ prettyPrintSectionSizes(Obj, Sizes, OS);
+
+ // TODO: If the input file is an archive, print the cumulative summary of all
+ // files from the archive.
+
+ return true;
+}
diff --git a/llvm/tools/llvm-dwarfdump/Statistics.cpp b/llvm/tools/llvm-dwarfdump/Statistics.cpp
index 5bef4d5148ca3..18b4c40c4d751 100644
--- a/llvm/tools/llvm-dwarfdump/Statistics.cpp
+++ b/llvm/tools/llvm-dwarfdump/Statistics.cpp
@@ -1,3 +1,12 @@
+//===-- Statistics.cpp - Debug Info quality metrics -----------------------===//
+//
+// 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 "llvm-dwarfdump.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringSet.h"
@@ -9,7 +18,8 @@
#define DEBUG_TYPE "dwarfdump"
using namespace llvm;
-using namespace object;
+using namespace llvm::dwarfdump;
+using namespace llvm::object;
/// This represents the number of categories of debug location coverage being
/// calculated. The first category is the number of variables with 0% location
@@ -17,11 +27,14 @@ using namespace object;
/// location coverage.
constexpr int NumOfCoverageCategories = 12;
+namespace {
/// Holds statistics for one function (or other entity that has a PC range and
/// contains variables, such as a compile unit).
struct PerFunctionStats {
/// Number of inlined instances of this function.
unsigned NumFnInlined = 0;
+ /// Number of out-of-line instances of this function.
+ unsigned NumFnOutOfLine = 0;
/// Number of inlined instances that have abstract origins.
unsigned NumAbstractOrigins = 0;
/// Number of variables and parameters with location across all inlined
@@ -29,13 +42,12 @@ struct PerFunctionStats {
unsigned TotalVarWithLoc = 0;
/// Number of constants with location across all inlined instances.
unsigned ConstantMembers = 0;
+ /// Number of arificial variables, parameters or members across all instances.
+ unsigned NumArtificial = 0;
/// List of all Variables and parameters in this function.
StringSet<> VarsInFunction;
/// Compile units also cover a PC range, but have this flag set to false.
bool IsFunction = false;
- /// Verify function definition has PC addresses (for detecting when
- /// a function has been inlined everywhere).
- bool HasPCAddresses = false;
/// Function has source location information.
bool HasSourceLocation = false;
/// Number of function parameters.
@@ -46,14 +58,14 @@ struct PerFunctionStats {
unsigned NumParamTypes = 0;
/// Number of function parameters with a DW_AT_location.
unsigned NumParamLocations = 0;
- /// Number of variables.
- unsigned NumVars = 0;
- /// Number of variables with source location.
- unsigned NumVarSourceLocations = 0;
- /// Number of variables with type.
- unsigned NumVarTypes = 0;
- /// Number of variables with DW_AT_location.
- unsigned NumVarLocations = 0;
+ /// Number of local variables.
+ unsigned NumLocalVars = 0;
+ /// Number of local variables with source location.
+ unsigned NumLocalVarSourceLocations = 0;
+ /// Number of local variables with type.
+ unsigned NumLocalVarTypes = 0;
+ /// Number of local variables with DW_AT_location.
+ unsigned NumLocalVarLocations = 0;
};
/// Holds accumulated global statistics about DIEs.
@@ -68,21 +80,19 @@ struct GlobalStats {
/// Total number of PC range bytes covered by DW_AT_locations of
/// formal parameters.
unsigned ParamScopeBytesCovered = 0;
- /// Total number of PC range bytes in each variable's enclosing scope
- /// (only for parameters).
+ /// Total number of PC range bytes in each parameter's enclosing scope.
unsigned ParamScopeBytes = 0;
/// Total number of PC range bytes covered by DW_AT_locations with
/// the debug entry values (DW_OP_entry_value) (only for parameters).
unsigned ParamScopeEntryValueBytesCovered = 0;
/// Total number of PC range bytes covered by DW_AT_locations (only for local
/// variables).
- unsigned VarScopeBytesCovered = 0;
- /// Total number of PC range bytes in each variable's enclosing scope
- /// (only for local variables).
- unsigned VarScopeBytes = 0;
+ unsigned LocalVarScopeBytesCovered = 0;
+ /// Total number of PC range bytes in each local variable's enclosing scope.
+ unsigned LocalVarScopeBytes = 0;
/// Total number of PC range bytes covered by DW_AT_locations with
/// the debug entry values (DW_OP_entry_value) (only for local variables).
- unsigned VarScopeEntryValueBytesCovered = 0;
+ unsigned LocalVarScopeEntryValueBytesCovered = 0;
/// Total number of call site entries (DW_AT_call_file & DW_AT_call_line).
unsigned CallSiteEntries = 0;
/// Total number of call site DIEs (DW_TAG_call_site).
@@ -118,10 +128,10 @@ struct LocationStats {
std::vector<unsigned> ParamNonEntryValLocStats{
std::vector<unsigned>(NumOfCoverageCategories, 0)};
/// The debug location statistics for local variables.
- std::vector<unsigned> VarLocStats{
+ std::vector<unsigned> LocalVarLocStats{
std::vector<unsigned>(NumOfCoverageCategories, 0)};
/// Map non debug entry values coverage for local variables.
- std::vector<unsigned> VarNonEntryValLocStats{
+ std::vector<unsigned> LocalVarNonEntryValLocStats{
std::vector<unsigned>(NumOfCoverageCategories, 0)};
/// Total number of local variables and function parameters processed.
unsigned NumVarParam = 0;
@@ -130,13 +140,14 @@ struct LocationStats {
/// Total number of local variables processed.
unsigned NumVar = 0;
};
+} // namespace
/// Collect debug location statistics for one DIE.
static void collectLocStats(uint64_t BytesCovered, uint64_t BytesInScope,
std::vector<unsigned> &VarParamLocStats,
std::vector<unsigned> &ParamLocStats,
- std::vector<unsigned> &VarLocStats, bool IsParam,
- bool IsLocalVar) {
+ std::vector<unsigned> &LocalVarLocStats,
+ bool IsParam, bool IsLocalVar) {
auto getCoverageBucket = [BytesCovered, BytesInScope]() -> unsigned {
// No debug location at all for the variable.
if (BytesCovered == 0)
@@ -155,7 +166,36 @@ static void collectLocStats(uint64_t BytesCovered, uint64_t BytesInScope,
if (IsParam)
ParamLocStats[CoverageBucket]++;
else if (IsLocalVar)
- VarLocStats[CoverageBucket]++;
+ 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.
+static std::string constructDieID(DWARFDie Die,
+ StringRef Prefix = StringRef()) {
+ std::string IDStr;
+ llvm::raw_string_ostream ID(IDStr);
+ ID << Prefix
+ << Die.getName(DINameKind::LinkageName);
+
+ // Prefix + Name is enough for local variables and parameters.
+ if (!Prefix.empty() && !Prefix.equals("g"))
+ return ID.str();
+
+ auto DeclFile = Die.findRecursively(dwarf::DW_AT_decl_file);
+ std::string File;
+ if (DeclFile) {
+ DWARFUnit *U = Die.getDwarfUnit();
+ if (const auto *LT = U->getContext().getLineTableForUnit(U))
+ if (LT->getFileNameByIndex(
+ dwarf::toUnsigned(DeclFile, 0), U->getCompilationDir(),
+ DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, File))
+ File = std::string(sys::path::filename(File));
+ }
+ ID << ":" << (File.empty() ? "/" : File);
+ ID << ":"
+ << dwarf::toUnsigned(Die.findRecursively(dwarf::DW_AT_decl_line), 0);
+ return ID.str();
}
/// Collect debug info quality metrics for one DIE.
@@ -168,12 +208,13 @@ static void collectStatsForDie(DWARFDie Die, std::string FnPrefix,
bool HasLoc = false;
bool HasSrcLoc = false;
bool HasType = false;
- bool IsArtificial = false;
uint64_t BytesCovered = 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 &&
+ Die.find(dwarf::DW_AT_const_value);
if (Die.getTag() == dwarf::DW_TAG_call_site ||
Die.getTag() == dwarf::DW_TAG_GNU_call_site) {
@@ -187,11 +228,15 @@ static void collectStatsForDie(DWARFDie Die, std::string FnPrefix,
return;
}
- if (!IsParam && !IsLocalVar && Die.getTag() != dwarf::DW_TAG_member) {
+ if (!IsParam && !IsLocalVar && !IsConstantMember) {
// Not a variable or constant member.
return;
}
+ // Ignore declarations of global variables.
+ if (IsLocalVar && Die.find(dwarf::DW_AT_declaration))
+ return;
+
if (Die.findRecursively(dwarf::DW_AT_decl_file) &&
Die.findRecursively(dwarf::DW_AT_decl_line))
HasSrcLoc = true;
@@ -199,14 +244,12 @@ static void collectStatsForDie(DWARFDie Die, std::string FnPrefix,
if (Die.findRecursively(dwarf::DW_AT_type))
HasType = true;
- if (Die.find(dwarf::DW_AT_artificial))
- IsArtificial = true;
-
auto IsEntryValue = [&](ArrayRef<uint8_t> D) -> bool {
DWARFUnit *U = Die.getDwarfUnit();
DataExtractor Data(toStringRef(D),
Die.getDwarfUnit()->getContext().isLittleEndian(), 0);
- DWARFExpression Expression(Data, U->getVersion(), U->getAddressByteSize());
+ DWARFExpression Expression(Data, U->getAddressByteSize(),
+ U->getFormParams().Format);
// Consider the expression containing the DW_OP_entry_value as
// an entry value.
return llvm::any_of(Expression, [](DWARFExpression::Operation &Op) {
@@ -220,10 +263,6 @@ static void collectStatsForDie(DWARFDie Die, std::string FnPrefix,
HasLoc = true;
BytesCovered = BytesInScope;
} else {
- if (Die.getTag() == dwarf::DW_TAG_member) {
- // Non-const member.
- return;
- }
// Handle variables and function arguments.
Expected<std::vector<DWARFLocationExpression>> Loc =
Die.getLocations(dwarf::DW_AT_location);
@@ -257,25 +296,24 @@ static void collectStatsForDie(DWARFDie Die, std::string FnPrefix,
LocStats.NumVar++;
collectLocStats(BytesCovered, BytesInScope, LocStats.VarParamLocStats,
- LocStats.ParamLocStats, LocStats.VarLocStats, IsParam,
+ LocStats.ParamLocStats, LocStats.LocalVarLocStats, IsParam,
IsLocalVar);
// Non debug entry values coverage statistics.
collectLocStats(BytesCovered - BytesEntryValuesCovered, BytesInScope,
LocStats.VarParamNonEntryValLocStats,
LocStats.ParamNonEntryValLocStats,
- LocStats.VarNonEntryValLocStats, IsParam, IsLocalVar);
+ LocStats.LocalVarNonEntryValLocStats, IsParam, IsLocalVar);
}
// Collect PC range coverage data.
if (DWARFDie D =
Die.getAttributeValueAsReferencedDie(dwarf::DW_AT_abstract_origin))
Die = D;
- // By using the variable name + the path through the lexical block tree, the
- // keys are consistent across duplicate abstract origins in different CUs.
- std::string VarName = StringRef(Die.getName(DINameKind::ShortName));
- FnStats.VarsInFunction.insert(VarPrefix + VarName);
+
+ std::string VarID = constructDieID(Die, VarPrefix);
+ FnStats.VarsInFunction.insert(VarID);
+
if (BytesInScope) {
- FnStats.TotalVarWithLoc += (unsigned)HasLoc;
// Turns out we have a lot of ranges that extend past the lexical scope.
GlobalStats.ScopeBytesCovered += std::min(BytesInScope, BytesCovered);
GlobalStats.ScopeBytes += BytesInScope;
@@ -286,34 +324,43 @@ static void collectStatsForDie(DWARFDie Die, std::string FnPrefix,
GlobalStats.ParamScopeBytes += BytesInScope;
GlobalStats.ParamScopeEntryValueBytesCovered += BytesEntryValuesCovered;
} else if (IsLocalVar) {
- GlobalStats.VarScopeBytesCovered += std::min(BytesInScope, BytesCovered);
- GlobalStats.VarScopeBytes += BytesInScope;
- GlobalStats.VarScopeEntryValueBytesCovered += BytesEntryValuesCovered;
+ GlobalStats.LocalVarScopeBytesCovered +=
+ std::min(BytesInScope, BytesCovered);
+ GlobalStats.LocalVarScopeBytes += BytesInScope;
+ GlobalStats.LocalVarScopeEntryValueBytesCovered +=
+ BytesEntryValuesCovered;
}
assert(GlobalStats.ScopeBytesCovered <= GlobalStats.ScopeBytes);
- } else if (Die.getTag() == dwarf::DW_TAG_member) {
+ }
+
+ if (IsConstantMember) {
FnStats.ConstantMembers++;
- } else {
- FnStats.TotalVarWithLoc += (unsigned)HasLoc;
+ return;
}
- if (!IsArtificial) {
- if (IsParam) {
- FnStats.NumParams++;
- if (HasType)
- FnStats.NumParamTypes++;
- if (HasSrcLoc)
- FnStats.NumParamSourceLocations++;
- if (HasLoc)
- FnStats.NumParamLocations++;
- } else if (IsLocalVar) {
- FnStats.NumVars++;
- if (HasType)
- FnStats.NumVarTypes++;
- if (HasSrcLoc)
- FnStats.NumVarSourceLocations++;
- if (HasLoc)
- FnStats.NumVarLocations++;
- }
+
+ FnStats.TotalVarWithLoc += (unsigned)HasLoc;
+
+ if (Die.find(dwarf::DW_AT_artificial)) {
+ FnStats.NumArtificial++;
+ return;
+ }
+
+ if (IsParam) {
+ FnStats.NumParams++;
+ if (HasType)
+ FnStats.NumParamTypes++;
+ if (HasSrcLoc)
+ FnStats.NumParamSourceLocations++;
+ if (HasLoc)
+ FnStats.NumParamLocations++;
+ } else if (IsLocalVar) {
+ FnStats.NumLocalVars++;
+ if (HasType)
+ FnStats.NumLocalVarTypes++;
+ if (HasSrcLoc)
+ FnStats.NumLocalVarSourceLocations++;
+ if (HasLoc)
+ FnStats.NumLocalVarLocations++;
}
}
@@ -324,8 +371,12 @@ static void collectStatsRecursive(DWARFDie Die, std::string FnPrefix,
StringMap<PerFunctionStats> &FnStatMap,
GlobalStats &GlobalStats,
LocationStats &LocStats) {
- // Handle any kind of lexical scope.
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 IsFunction = Tag == dwarf::DW_TAG_subprogram;
const bool IsBlock = Tag == dwarf::DW_TAG_lexical_block;
const bool IsInlinedFunction = Tag == dwarf::DW_TAG_inlined_subroutine;
@@ -358,27 +409,25 @@ static void collectStatsRecursive(DWARFDie Die, std::string FnPrefix,
// Count the function.
if (!IsBlock) {
- StringRef Name = Die.getName(DINameKind::LinkageName);
- if (Name.empty())
- Name = Die.getName(DINameKind::ShortName);
- FnPrefix = Name;
// Skip over abstract origins.
if (Die.find(dwarf::DW_AT_inline))
return;
- // We've seen an (inlined) instance of this function.
- auto &FnStats = FnStatMap[Name];
+ std::string FnID = constructDieID(Die);
+ // We've seen an instance of this function.
+ auto &FnStats = FnStatMap[FnID];
+ FnStats.IsFunction = true;
if (IsInlinedFunction) {
FnStats.NumFnInlined++;
if (Die.findRecursively(dwarf::DW_AT_abstract_origin))
FnStats.NumAbstractOrigins++;
+ } else {
+ FnStats.NumFnOutOfLine++;
}
- FnStats.IsFunction = true;
- if (BytesInThisScope && !IsInlinedFunction)
- FnStats.HasPCAddresses = true;
- std::string FnName = StringRef(Die.getName(DINameKind::ShortName));
if (Die.findRecursively(dwarf::DW_AT_decl_file) &&
Die.findRecursively(dwarf::DW_AT_decl_line))
FnStats.HasSourceLocation = true;
+ // Update function prefix.
+ FnPrefix = FnID;
}
if (BytesInThisScope) {
@@ -402,11 +451,14 @@ static void collectStatsRecursive(DWARFDie Die, std::string FnPrefix,
// Traverse children.
unsigned LexicalBlockIndex = 0;
+ unsigned FormalParameterIndex = 0;
DWARFDie Child = Die.getFirstChild();
while (Child) {
std::string ChildVarPrefix = VarPrefix;
if (Child.getTag() == dwarf::DW_TAG_lexical_block)
ChildVarPrefix += toHex(LexicalBlockIndex++) + '.';
+ if (Child.getTag() == dwarf::DW_TAG_formal_parameter)
+ ChildVarPrefix += 'p' + toHex(FormalParameterIndex++) + '.';
collectStatsRecursive(Child, FnPrefix, ChildVarPrefix, BytesInScope,
InlineDepth, FnStatMap, GlobalStats, LocStats);
@@ -421,29 +473,44 @@ static void printDatum(raw_ostream &OS, const char *Key, json::Value Value) {
OS << ",\"" << Key << "\":" << Value;
LLVM_DEBUG(llvm::dbgs() << Key << ": " << Value << '\n');
}
-static void printLocationStats(raw_ostream &OS,
- const char *Key,
+
+static void printLocationStats(raw_ostream &OS, const char *Key,
std::vector<unsigned> &LocationStats) {
- OS << ",\"" << Key << " with 0% of its scope covered\":"
+ OS << ",\"" << Key << " with 0% of parent scope covered by DW_AT_location\":"
<< LocationStats[0];
- LLVM_DEBUG(llvm::dbgs() << Key << " with 0% of its scope covered: "
- << LocationStats[0] << '\n');
- OS << ",\"" << Key << " with (0%,10%) of its scope covered\":"
+ LLVM_DEBUG(
+ llvm::dbgs() << Key
+ << " with 0% of parent scope covered by DW_AT_location: \\"
+ << LocationStats[0] << '\n');
+ OS << ",\"" << Key
+ << " with (0%,10%) of parent scope covered by DW_AT_location\":"
<< LocationStats[1];
- LLVM_DEBUG(llvm::dbgs() << Key << " with (0%,10%) of its scope covered: "
- << LocationStats[1] << '\n');
+ LLVM_DEBUG(llvm::dbgs()
+ << Key
+ << " with (0%,10%) of parent scope covered by DW_AT_location: "
+ << LocationStats[1] << '\n');
for (unsigned i = 2; i < NumOfCoverageCategories - 1; ++i) {
OS << ",\"" << Key << " with [" << (i - 1) * 10 << "%," << i * 10
- << "%) of its scope covered\":" << LocationStats[i];
+ << "%) of parent scope covered by DW_AT_location\":" << LocationStats[i];
LLVM_DEBUG(llvm::dbgs()
<< Key << " with [" << (i - 1) * 10 << "%," << i * 10
- << "%) of its scope covered: " << LocationStats[i]);
+ << "%) of parent scope covered by DW_AT_location: "
+ << LocationStats[i]);
}
- OS << ",\"" << Key << " with 100% of its scope covered\":"
+ OS << ",\"" << Key
+ << " with 100% of parent scope covered by DW_AT_location\":"
<< LocationStats[NumOfCoverageCategories - 1];
- LLVM_DEBUG(llvm::dbgs() << Key << " with 100% of its scope covered: "
- << LocationStats[NumOfCoverageCategories - 1]);
+ LLVM_DEBUG(
+ llvm::dbgs() << Key
+ << " with 100% of parent scope covered by DW_AT_location: "
+ << LocationStats[NumOfCoverageCategories - 1]);
}
+
+static void printSectionSizes(raw_ostream &OS, const SectionSizes &Sizes) {
+ for (const auto &DebugSec : Sizes.DebugSectionSizes)
+ OS << ",\"#bytes in " << DebugSec.getKey() << "\":" << DebugSec.getValue();
+}
+
/// \}
/// Collect debug info quality metrics for an entire DIContext.
@@ -454,8 +521,9 @@ static void printLocationStats(raw_ostream &OS,
/// of particular optimizations. The raw numbers themselves are not particularly
/// useful, only the delta between compiling the same program with different
/// compilers is.
-bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
- Twine Filename, raw_ostream &OS) {
+bool dwarfdump::collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
+ const Twine &Filename,
+ raw_ostream &OS) {
StringRef FormatName = Obj.getFileFormatName();
GlobalStats GlobalStats;
LocationStats LocStats;
@@ -465,10 +533,14 @@ bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
collectStatsRecursive(CUDie, "/", "g", 0, 0, Statistics, GlobalStats,
LocStats);
+ /// Collect the sizes of debug sections.
+ SectionSizes Sizes;
+ calculateSectionSizes(Obj, Sizes, Filename);
+
/// 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 = 4;
+ unsigned Version = 5;
unsigned VarParamTotal = 0;
unsigned VarParamUnique = 0;
unsigned VarParamWithLoc = 0;
@@ -480,16 +552,18 @@ bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
unsigned ParamWithType = 0;
unsigned ParamWithLoc = 0;
unsigned ParamWithSrcLoc = 0;
- unsigned VarTotal = 0;
- unsigned VarWithType = 0;
- unsigned VarWithSrcLoc = 0;
- unsigned VarWithLoc = 0;
+ unsigned LocalVarTotal = 0;
+ unsigned LocalVarWithType = 0;
+ unsigned LocalVarWithSrcLoc = 0;
+ unsigned LocalVarWithLoc = 0;
for (auto &Entry : Statistics) {
PerFunctionStats &Stats = Entry.getValue();
- unsigned TotalVars = Stats.VarsInFunction.size() * Stats.NumFnInlined;
- // Count variables in concrete out-of-line functions and in global scope.
- if (Stats.HasPCAddresses || !Stats.IsFunction)
- TotalVars += Stats.VarsInFunction.size();
+ unsigned TotalVars = Stats.VarsInFunction.size() *
+ (Stats.NumFnInlined + Stats.NumFnOutOfLine);
+ // Count variables in global scope.
+ if (!Stats.IsFunction)
+ TotalVars =
+ Stats.NumLocalVars + Stats.ConstantMembers + Stats.NumArtificial;
unsigned Constants = Stats.ConstantMembers;
VarParamWithLoc += Stats.TotalVarWithLoc + Constants;
VarParamTotal += TotalVars;
@@ -505,10 +579,10 @@ bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
ParamWithType += Stats.NumParamTypes;
ParamWithLoc += Stats.NumParamLocations;
ParamWithSrcLoc += Stats.NumParamSourceLocations;
- VarTotal += Stats.NumVars;
- VarWithType += Stats.NumVarTypes;
- VarWithLoc += Stats.NumVarLocations;
- VarWithSrcLoc += Stats.NumVarSourceLocations;
+ LocalVarTotal += Stats.NumLocalVars;
+ LocalVarWithType += Stats.NumLocalVarTypes;
+ LocalVarWithLoc += Stats.NumLocalVarLocations;
+ LocalVarWithSrcLoc += Stats.NumLocalVarSourceLocations;
}
// Print summary.
@@ -516,56 +590,97 @@ bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
OS << "{\"version\":" << Version;
LLVM_DEBUG(llvm::dbgs() << "Variable location quality metrics\n";
llvm::dbgs() << "---------------------------------\n");
+
printDatum(OS, "file", Filename.str());
printDatum(OS, "format", FormatName);
- printDatum(OS, "source functions", NumFunctions);
- printDatum(OS, "source functions with location", NumFuncsWithSrcLoc);
- printDatum(OS, "inlined functions", NumInlinedFunctions);
- printDatum(OS, "inlined funcs with abstract origins", NumAbstractOrigins);
- printDatum(OS, "unique source variables", VarParamUnique);
- printDatum(OS, "source variables", VarParamTotal);
- printDatum(OS, "variables with location", VarParamWithLoc);
- printDatum(OS, "call site entries", GlobalStats.CallSiteEntries);
- printDatum(OS, "call site DIEs", GlobalStats.CallSiteDIEs);
- printDatum(OS, "call site parameter DIEs", GlobalStats.CallSiteParamDIEs);
- printDatum(OS, "scope bytes total", GlobalStats.ScopeBytes);
- printDatum(OS, "scope bytes covered", GlobalStats.ScopeBytesCovered);
- printDatum(OS, "entry value scope bytes covered",
+
+ printDatum(OS, "#functions", NumFunctions);
+ printDatum(OS, "#functions with location", NumFuncsWithSrcLoc);
+ printDatum(OS, "#inlined functions", NumInlinedFunctions);
+ printDatum(OS, "#inlined functions with abstract origins",
+ NumAbstractOrigins);
+
+ // This includes local variables and formal parameters.
+ printDatum(OS, "#unique source variables", VarParamUnique);
+ printDatum(OS, "#source variables", VarParamTotal);
+ printDatum(OS, "#source variables with location", VarParamWithLoc);
+
+ printDatum(OS, "#call site entries", GlobalStats.CallSiteEntries);
+ printDatum(OS, "#call site DIEs", GlobalStats.CallSiteDIEs);
+ printDatum(OS, "#call site parameter DIEs", GlobalStats.CallSiteParamDIEs);
+
+ printDatum(OS, "sum_all_variables(#bytes in parent scope)",
+ GlobalStats.ScopeBytes);
+ printDatum(OS,
+ "sum_all_variables(#bytes in parent scope covered by "
+ "DW_AT_location)",
+ GlobalStats.ScopeBytesCovered);
+ printDatum(OS,
+ "sum_all_variables(#bytes in parent scope covered by "
+ "DW_OP_entry_value)",
GlobalStats.ScopeEntryValueBytesCovered);
- printDatum(OS, "formal params scope bytes total",
+
+ printDatum(OS, "sum_all_params(#bytes in parent scope)",
GlobalStats.ParamScopeBytes);
- printDatum(OS, "formal params scope bytes covered",
- GlobalStats.ParamScopeBytesCovered);
- printDatum(OS, "formal params entry value scope bytes covered",
+ printDatum(
+ OS,
+ "sum_all_params(#bytes in parent scope covered by DW_AT_location)",
+ GlobalStats.ParamScopeBytesCovered);
+ printDatum(OS,
+ "sum_all_params(#bytes in parent scope covered by "
+ "DW_OP_entry_value)",
GlobalStats.ParamScopeEntryValueBytesCovered);
- printDatum(OS, "vars scope bytes total", GlobalStats.VarScopeBytes);
- printDatum(OS, "vars scope bytes covered", GlobalStats.VarScopeBytesCovered);
- printDatum(OS, "vars entry value scope bytes covered",
- GlobalStats.VarScopeEntryValueBytesCovered);
- printDatum(OS, "total function size", GlobalStats.FunctionSize);
- printDatum(OS, "total inlined function size", GlobalStats.InlineFunctionSize);
- printDatum(OS, "total formal params", ParamTotal);
- printDatum(OS, "formal params with source location", ParamWithSrcLoc);
- printDatum(OS, "formal params with type", ParamWithType);
- printDatum(OS, "formal params with binary location", ParamWithLoc);
- printDatum(OS, "total vars", VarTotal);
- printDatum(OS, "vars with source location", VarWithSrcLoc);
- printDatum(OS, "vars with type", VarWithType);
- printDatum(OS, "vars with binary location", VarWithLoc);
- printDatum(OS, "total variables procesed by location statistics",
+
+ printDatum(OS, "sum_all_local_vars(#bytes in parent scope)",
+ GlobalStats.LocalVarScopeBytes);
+ printDatum(OS,
+ "sum_all_local_vars(#bytes in parent scope covered by "
+ "DW_AT_location)",
+ GlobalStats.LocalVarScopeBytesCovered);
+ printDatum(OS,
+ "sum_all_local_vars(#bytes in parent scope covered by "
+ "DW_OP_entry_value)",
+ GlobalStats.LocalVarScopeEntryValueBytesCovered);
+
+ printDatum(OS, "#bytes witin functions", GlobalStats.FunctionSize);
+ printDatum(OS, "#bytes witin inlined functions",
+ GlobalStats.InlineFunctionSize);
+
+ // Print the summary for formal parameters.
+ printDatum(OS, "#params", ParamTotal);
+ printDatum(OS, "#params with source location", ParamWithSrcLoc);
+ printDatum(OS, "#params with type", ParamWithType);
+ printDatum(OS, "#params with binary location", ParamWithLoc);
+
+ // Print the summary for local variables.
+ printDatum(OS, "#local vars", LocalVarTotal);
+ printDatum(OS, "#local vars with source location", LocalVarWithSrcLoc);
+ printDatum(OS, "#local vars with type", LocalVarWithType);
+ printDatum(OS, "#local vars with binary location", LocalVarWithLoc);
+
+ // Print the debug section sizes.
+ printSectionSizes(OS, Sizes);
+
+ // Print the location statistics for variables (includes local variables
+ // and formal parameters).
+ printDatum(OS, "#variables processed by location statistics",
LocStats.NumVarParam);
- printLocationStats(OS, "variables", LocStats.VarParamLocStats);
- printLocationStats(OS, "variables (excluding the debug entry values)",
+ printLocationStats(OS, "#variables", LocStats.VarParamLocStats);
+ printLocationStats(OS, "#variables - entry values",
LocStats.VarParamNonEntryValLocStats);
- printDatum(OS, "total params procesed by location statistics",
- LocStats.NumParam);
- printLocationStats(OS, "params", LocStats.ParamLocStats);
- printLocationStats(OS, "params (excluding the debug entry values)",
+
+ // Print the location statistics for formal parameters.
+ printDatum(OS, "#params processed by location statistics", LocStats.NumParam);
+ printLocationStats(OS, "#params", LocStats.ParamLocStats);
+ printLocationStats(OS, "#params - entry values",
LocStats.ParamNonEntryValLocStats);
- printDatum(OS, "total vars procesed by location statistics", LocStats.NumVar);
- printLocationStats(OS, "vars", LocStats.VarLocStats);
- printLocationStats(OS, "vars (excluding the debug entry values)",
- LocStats.VarNonEntryValLocStats);
+
+ // Print the location statistics for local variables.
+ printDatum(OS, "#local vars processed by location statistics",
+ LocStats.NumVar);
+ printLocationStats(OS, "#local vars", LocStats.LocalVarLocStats);
+ printLocationStats(OS, "#local vars - entry values",
+ LocStats.LocalVarNonEntryValLocStats);
OS << "}\n";
LLVM_DEBUG(
llvm::dbgs() << "Total Availability: "
diff --git a/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp b/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp
index 374bdd482a8d6..d8fa4f9953dca 100644
--- a/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp
+++ b/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp
@@ -10,6 +10,7 @@
//
//===----------------------------------------------------------------------===//
+#include "llvm-dwarfdump.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/ADT/Triple.h"
@@ -29,10 +30,13 @@
#include "llvm/Support/ToolOutputFile.h"
#include "llvm/Support/WithColor.h"
#include "llvm/Support/raw_ostream.h"
+#include <cstdlib>
using namespace llvm;
-using namespace object;
+using namespace llvm::dwarfdump;
+using namespace llvm::object;
+namespace {
/// Parser for options that take an optional offest argument.
/// @{
struct OffsetOption {
@@ -40,6 +44,8 @@ struct OffsetOption {
bool HasValue = false;
bool IsRequested = false;
};
+struct BoolOption : public OffsetOption {};
+} // namespace
namespace llvm {
namespace cl {
@@ -57,7 +63,7 @@ public:
return false;
}
if (Arg.getAsInteger(0, Val.Val))
- return O.error("'" + Arg + "' value invalid for integer argument!");
+ return O.error("'" + Arg + "' value invalid for integer argument");
Val.HasValue = true;
Val.IsRequested = true;
return false;
@@ -67,22 +73,42 @@ public:
return ValueOptional;
}
- void printOptionInfo(const Option &O, size_t GlobalWidth) const {
- outs() << " -" << O.ArgStr;
- Option::printHelpStr(O.HelpStr, GlobalWidth, getOptionWidth(O));
- }
+ StringRef getValueName() const override { return StringRef("offset"); }
void printOptionDiff(const Option &O, OffsetOption V, OptVal Default,
size_t GlobalWidth) const {
printOptionName(O, GlobalWidth);
outs() << "[=offset]";
}
+};
+
+template <> class parser<BoolOption> final : public basic_parser<BoolOption> {
+public:
+ parser(Option &O) : basic_parser(O) {}
+
+ /// Return true on error.
+ bool parse(Option &O, StringRef ArgName, StringRef Arg, BoolOption &Val) {
+ if (Arg != "")
+ return O.error("this is a flag and does not take a value");
+ Val.Val = 0;
+ Val.HasValue = false;
+ Val.IsRequested = true;
+ return false;
+ }
+
+ enum ValueExpected getValueExpectedFlagDefault() const {
+ return ValueOptional;
+ }
- // An out-of-line virtual method to provide a 'home' for this class.
- void anchor() override {};
+ StringRef getValueName() const override { return StringRef(); }
+
+ void printOptionDiff(const Option &O, OffsetOption V, OptVal Default,
+ size_t GlobalWidth) const {
+ printOptionName(O, GlobalWidth);
+ }
};
-} // cl
-} // llvm
+} // namespace cl
+} // namespace llvm
/// @}
/// Command line options.
@@ -110,10 +136,10 @@ static alias DumpAllAlias("a", desc("Alias for -all"), aliasopt(DumpAll));
static unsigned DumpType = DIDT_Null;
static std::array<llvm::Optional<uint64_t>, (unsigned)DIDT_ID_Count>
DumpOffsets;
-#define HANDLE_DWARF_SECTION(ENUM_NAME, ELF_NAME, CMDLINE_NAME) \
- static opt<OffsetOption> Dump##ENUM_NAME( \
- CMDLINE_NAME, desc("Dump the " ELF_NAME " section"), \
- cat(SectionCategory));
+#define HANDLE_DWARF_SECTION(ENUM_NAME, ELF_NAME, CMDLINE_NAME, OPTION) \
+ static opt<OPTION> Dump##ENUM_NAME(CMDLINE_NAME, \
+ desc("Dump the " ELF_NAME " section"), \
+ cat(SectionCategory));
#include "llvm/BinaryFormat/Dwarf.def"
#undef HANDLE_DWARF_SECTION
@@ -208,6 +234,11 @@ static cl::opt<bool>
Statistics("statistics",
cl::desc("Emit JSON-formatted debug info quality metrics."),
cat(DwarfDumpCategory));
+static cl::opt<bool>
+ ShowSectionSizes("show-section-sizes",
+ cl::desc("Show the sizes of all debug sections, "
+ "expressed in bytes."),
+ cat(DwarfDumpCategory));
static opt<bool> Verify("verify", desc("Verify the DWARF debug info."),
cat(DwarfDumpCategory));
static opt<bool> Quiet("quiet", desc("Use with -verify to not emit to STDOUT."),
@@ -233,7 +264,7 @@ static void error(StringRef Prefix, std::error_code EC) {
exit(1);
}
-static DIDumpOptions getDumpOpts() {
+static DIDumpOptions getDumpOpts(DWARFContext &C) {
DIDumpOptions DumpOpts;
DumpOpts.DumpType = DumpType;
DumpOpts.ChildRecurseDepth = ChildRecurseDepth;
@@ -244,6 +275,7 @@ static DIDumpOptions getDumpOpts() {
DumpOpts.ShowForm = ShowForm;
DumpOpts.SummarizeTypes = SummarizeTypes;
DumpOpts.Verbose = Verbose;
+ DumpOpts.RecoverableErrorHandler = C.getRecoverableErrorHandler();
// In -verify mode, print DIEs without children in error messages.
if (Verify)
return DumpOpts.noImplicitRecursion();
@@ -278,12 +310,13 @@ static bool filterArch(ObjectFile &Obj) {
return false;
}
-using HandlerFn = std::function<bool(ObjectFile &, DWARFContext &DICtx, Twine,
- raw_ostream &)>;
+using HandlerFn = std::function<bool(ObjectFile &, DWARFContext &DICtx,
+ const Twine &, raw_ostream &)>;
/// Print only DIEs that have a certain name.
static bool filterByName(const StringSet<> &Names, DWARFDie Die,
StringRef NameRef, raw_ostream &OS) {
+ DIDumpOptions DumpOpts = getDumpOpts(Die.getDwarfUnit()->getContext());
std::string Name =
(IgnoreCase && !UseRegex) ? NameRef.lower() : NameRef.str();
if (UseRegex) {
@@ -296,13 +329,13 @@ static bool filterByName(const StringSet<> &Names, DWARFDie Die,
exit(1);
}
if (RE.match(Name)) {
- Die.dump(OS, 0, getDumpOpts());
+ Die.dump(OS, 0, DumpOpts);
return true;
}
}
} else if (Names.count(Name)) {
// Match full text.
- Die.dump(OS, 0, getDumpOpts());
+ Die.dump(OS, 0, DumpOpts);
return true;
}
return false;
@@ -375,8 +408,9 @@ static void filterByAccelName(ArrayRef<std::string> Names, DWARFContext &DICtx,
llvm::sort(Dies);
Dies.erase(std::unique(Dies.begin(), Dies.end()), Dies.end());
+ DIDumpOptions DumpOpts = getDumpOpts(DICtx);
for (DWARFDie Die : Dies)
- Die.dump(OS, 0, getDumpOpts());
+ Die.dump(OS, 0, DumpOpts);
}
/// Handle the --lookup option and dump the DIEs and line info for the given
@@ -392,7 +426,7 @@ static bool lookup(ObjectFile &Obj, DWARFContext &DICtx, uint64_t Address,
if (!DIEsForAddr)
return false;
- DIDumpOptions DumpOpts = getDumpOpts();
+ DIDumpOptions DumpOpts = getDumpOpts(DICtx);
DumpOpts.ChildRecurseDepth = 0;
DIEsForAddr.CompileUnit->dump(OS, DumpOpts);
if (DIEsForAddr.FunctionDIE) {
@@ -410,11 +444,8 @@ static bool lookup(ObjectFile &Obj, DWARFContext &DICtx, uint64_t Address,
return true;
}
-bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
- Twine Filename, raw_ostream &OS);
-
-static bool dumpObjectFile(ObjectFile &Obj, DWARFContext &DICtx, Twine Filename,
- raw_ostream &OS) {
+static bool dumpObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
+ const Twine &Filename, raw_ostream &OS) {
logAllUnhandledErrors(DICtx.loadRegisterInfo(Obj), errs(),
Filename.str() + ": ");
// The UUID dump already contains all the same information.
@@ -443,18 +474,18 @@ static bool dumpObjectFile(ObjectFile &Obj, DWARFContext &DICtx, Twine Filename,
}
// Dump the complete DWARF structure.
- DICtx.dump(OS, getDumpOpts(), DumpOffsets);
+ DICtx.dump(OS, getDumpOpts(DICtx), DumpOffsets);
return true;
}
static bool verifyObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
- Twine Filename, raw_ostream &OS) {
+ const Twine &Filename, raw_ostream &OS) {
// Verify the DWARF and exit with non-zero exit status if verification
// fails.
raw_ostream &stream = Quiet ? nulls() : OS;
stream << "Verifying " << Filename.str() << ":\tfile format "
<< Obj.getFileFormatName() << "\n";
- bool Result = DICtx.verify(stream, getDumpOpts());
+ bool Result = DICtx.verify(stream, getDumpOpts(DICtx));
if (Result)
stream << "No errors.\n";
else
@@ -488,10 +519,16 @@ static bool handleBuffer(StringRef Filename, MemoryBufferRef Buffer,
error(Filename, errorToErrorCode(BinOrErr.takeError()));
bool Result = true;
+ auto RecoverableErrorHandler = [&](Error E) {
+ Result = false;
+ WithColor::defaultErrorHandler(std::move(E));
+ };
if (auto *Obj = dyn_cast<ObjectFile>(BinOrErr->get())) {
if (filterArch(*Obj)) {
- std::unique_ptr<DWARFContext> DICtx = DWARFContext::create(*Obj);
- Result = HandleObj(*Obj, *DICtx, Filename, OS);
+ std::unique_ptr<DWARFContext> DICtx =
+ DWARFContext::create(*Obj, nullptr, "", RecoverableErrorHandler);
+ if (!HandleObj(*Obj, *DICtx, Filename, OS))
+ Result = false;
}
}
else if (auto *Fat = dyn_cast<MachOUniversalBinary>(BinOrErr->get()))
@@ -501,15 +538,18 @@ static bool handleBuffer(StringRef Filename, MemoryBufferRef Buffer,
if (auto MachOOrErr = ObjForArch.getAsObjectFile()) {
auto &Obj = **MachOOrErr;
if (filterArch(Obj)) {
- std::unique_ptr<DWARFContext> DICtx = DWARFContext::create(Obj);
- Result &= HandleObj(Obj, *DICtx, ObjName, OS);
+ std::unique_ptr<DWARFContext> DICtx =
+ DWARFContext::create(Obj, nullptr, "", RecoverableErrorHandler);
+ if (!HandleObj(Obj, *DICtx, ObjName, OS))
+ Result = false;
}
continue;
} else
consumeError(MachOOrErr.takeError());
if (auto ArchiveOrErr = ObjForArch.getAsArchive()) {
error(ObjName, errorToErrorCode(ArchiveOrErr.takeError()));
- Result &= handleArchive(ObjName, *ArchiveOrErr.get(), HandleObj, OS);
+ if (!handleArchive(ObjName, *ArchiveOrErr.get(), HandleObj, OS))
+ Result = false;
continue;
} else
consumeError(ArchiveOrErr.takeError());
@@ -566,6 +606,10 @@ static std::vector<std::string> expandBundle(const std::string &InputPath) {
int main(int argc, char **argv) {
InitLLVM X(argc, argv);
+ // Flush outs() when printing to errs(). This avoids interleaving output
+ // between the two.
+ errs().tie(&outs());
+
llvm::InitializeAllTargetInfos();
llvm::InitializeAllTargetMCs();
@@ -593,7 +637,7 @@ int main(int argc, char **argv) {
// Defaults to dumping all sections, unless brief mode is specified in which
// case only the .debug_info section in dumped.
-#define HANDLE_DWARF_SECTION(ENUM_NAME, ELF_NAME, CMDLINE_NAME) \
+#define HANDLE_DWARF_SECTION(ENUM_NAME, ELF_NAME, CMDLINE_NAME, OPTION) \
if (Dump##ENUM_NAME.IsRequested) { \
DumpType |= DIDT_##ENUM_NAME; \
if (Dump##ENUM_NAME.HasValue) { \
@@ -629,18 +673,20 @@ int main(int argc, char **argv) {
Objects.insert(Objects.end(), Objs.begin(), Objs.end());
}
+ bool Success = true;
if (Verify) {
- // If we encountered errors during verify, exit with a non-zero exit status.
- if (!all_of(Objects, [&](std::string Object) {
- return handleFile(Object, verifyObjectFile, OutputFile.os());
- }))
- return 1;
- } else if (Statistics)
for (auto Object : Objects)
- handleFile(Object, collectStatsForObjectFile, OutputFile.os());
- else
+ Success &= handleFile(Object, verifyObjectFile, OutputFile.os());
+ } else if (Statistics) {
for (auto Object : Objects)
- handleFile(Object, dumpObjectFile, OutputFile.os());
+ Success &= handleFile(Object, collectStatsForObjectFile, OutputFile.os());
+ } else if (ShowSectionSizes) {
+ for (auto Object : Objects)
+ Success &= handleFile(Object, collectObjectSectionSizes, OutputFile.os());
+ } else {
+ for (auto Object : Objects)
+ Success &= handleFile(Object, dumpObjectFile, OutputFile.os());
+ }
- return EXIT_SUCCESS;
+ return Success ? EXIT_SUCCESS : EXIT_FAILURE;
}
diff --git a/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.h b/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.h
new file mode 100644
index 0000000000000..dc41298265d2a
--- /dev/null
+++ b/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.h
@@ -0,0 +1,43 @@
+//===-- llvm-dwarfdump - Debug info dumping utility -------------*- 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_DWARFDUMP_LLVM_DWARFDUMP_H
+#define LLVM_TOOLS_LLVM_DWARFDUMP_LLVM_DWARFDUMP_H
+
+#include "llvm/ADT/Twine.h"
+#include "llvm/DebugInfo/DWARF/DWARFContext.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace llvm {
+namespace dwarfdump {
+
+/// Holds cumulative section sizes for an object file.
+struct SectionSizes {
+ /// Map of .debug section names and their sizes across all such-named
+ /// sections.
+ StringMap<uint64_t> DebugSectionSizes;
+ /// Total number of bytes of all sections.
+ uint64_t TotalObjectSize = 0;
+ /// Total number of bytes of all debug sections.
+ uint64_t TotalDebugSectionsSize = 0;
+};
+
+/// Calculate the section sizes.
+void calculateSectionSizes(const object::ObjectFile &Obj, SectionSizes &Sizes,
+ const Twine &Filename);
+
+bool collectStatsForObjectFile(object::ObjectFile &Obj, DWARFContext &DICtx,
+ const Twine &Filename, raw_ostream &OS);
+bool collectObjectSectionSizes(object::ObjectFile &Obj, DWARFContext &DICtx,
+ const Twine &Filename, raw_ostream &OS);
+
+} // namespace dwarfdump
+} // namespace llvm
+
+#endif
diff --git a/llvm/tools/llvm-dwp/DWPError.cpp b/llvm/tools/llvm-dwp/DWPError.cpp
new file mode 100644
index 0000000000000..21d53ed6d198c
--- /dev/null
+++ b/llvm/tools/llvm-dwp/DWPError.cpp
@@ -0,0 +1,3 @@
+#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
new file mode 100644
index 0000000000000..62025ed4caa55
--- /dev/null
+++ b/llvm/tools/llvm-dwp/DWPError.h
@@ -0,0 +1,23 @@
+#ifndef TOOLS_LLVM_DWP_DWPERROR
+#define TOOLS_LLVM_DWP_DWPERROR
+
+#include "llvm/Support/Error.h"
+#include "llvm/Support/ErrorHandling.h"
+#include <string>
+
+namespace llvm {
+class DWPError : public ErrorInfo<DWPError> {
+public:
+ DWPError(std::string Info) : Info(std::move(Info)) {}
+ void log(raw_ostream &OS) const override { OS << Info; }
+ std::error_code convertToErrorCode() const override {
+ llvm_unreachable("Not implemented");
+ }
+ static char ID;
+
+private:
+ std::string Info;
+};
+}
+
+#endif
diff --git a/llvm/tools/llvm-dwp/DWPStringPool.h b/llvm/tools/llvm-dwp/DWPStringPool.h
new file mode 100644
index 0000000000000..e423076f43333
--- /dev/null
+++ b/llvm/tools/llvm-dwp/DWPStringPool.h
@@ -0,0 +1,56 @@
+#ifndef TOOLS_LLVM_DWP_DWPSTRINGPOOL
+#define TOOLS_LLVM_DWP_DWPSTRINGPOOL
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/MC/MCSection.h"
+#include "llvm/MC/MCStreamer.h"
+#include <cassert>
+
+namespace llvm {
+class DWPStringPool {
+
+ struct CStrDenseMapInfo {
+ static inline const char *getEmptyKey() {
+ return reinterpret_cast<const char *>(~static_cast<uintptr_t>(0));
+ }
+ static inline const char *getTombstoneKey() {
+ return reinterpret_cast<const char *>(~static_cast<uintptr_t>(1));
+ }
+ static unsigned getHashValue(const char *Val) {
+ assert(Val != getEmptyKey() && "Cannot hash the empty key!");
+ assert(Val != getTombstoneKey() && "Cannot hash the tombstone key!");
+ return (unsigned)hash_value(StringRef(Val));
+ }
+ static bool isEqual(const char *LHS, const char *RHS) {
+ if (RHS == getEmptyKey())
+ return LHS == getEmptyKey();
+ if (RHS == getTombstoneKey())
+ return LHS == getTombstoneKey();
+ return strcmp(LHS, RHS) == 0;
+ }
+ };
+
+ MCStreamer &Out;
+ MCSection *Sec;
+ DenseMap<const char *, uint32_t, CStrDenseMapInfo> Pool;
+ uint32_t Offset = 0;
+
+public:
+ DWPStringPool(MCStreamer &Out, MCSection *Sec) : Out(Out), Sec(Sec) {}
+
+ uint32_t getOffset(const char *Str, unsigned Length) {
+ assert(strlen(Str) + 1 == Length && "Ensure length hint is correct");
+
+ auto Pair = Pool.insert(std::make_pair(Str, Offset));
+ if (Pair.second) {
+ Out.SwitchSection(Sec);
+ Out.emitBytes(StringRef(Str, Length));
+ Offset += Length;
+ }
+
+ return Pair.first->second;
+ }
+};
+}
+
+#endif
diff --git a/llvm/tools/llvm-dwp/llvm-dwp.cpp b/llvm/tools/llvm-dwp/llvm-dwp.cpp
new file mode 100644
index 0000000000000..d5ebe5ab0a57a
--- /dev/null
+++ b/llvm/tools/llvm-dwp/llvm-dwp.cpp
@@ -0,0 +1,795 @@
+//===-- llvm-dwp.cpp - Split DWARF merging tool for llvm ------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// A utility for merging DWARF 5 Split DWARF .dwo files into .dwp (DWARF
+// package files).
+//
+//===----------------------------------------------------------------------===//
+#include "DWPError.h"
+#include "DWPStringPool.h"
+#include "llvm/ADT/MapVector.h"
+#include "llvm/ADT/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/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;
+
+static mc::RegisterMCTargetOptionsFlags MCTargetOptionsFlags;
+
+cl::OptionCategory DwpCategory("Specific Options");
+static cl::list<std::string> InputFiles(cl::Positional, cl::ZeroOrMore,
+ cl::desc("<input files>"),
+ cl::cat(DwpCategory));
+
+static cl::list<std::string> ExecFilenames(
+ "e", cl::ZeroOrMore,
+ cl::desc("Specify the executable/library files to get the list of *.dwo from"),
+ cl::value_desc("filename"), cl::cat(DwpCategory));
+
+static cl::opt<std::string> OutputFilename(cl::Required, "o",
+ cl::desc("Specify the output file."),
+ 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);
+}
+
+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);
+ if (!ErrOrObj)
+ return ErrOrObj.takeError();
+
+ const ObjectFile &Obj = *ErrOrObj.get().getBinary();
+ std::unique_ptr<DWARFContext> DWARFCtx = DWARFContext::create(Obj);
+
+ SmallVector<std::string, 16> DWOPaths;
+ for (const auto &CU : DWARFCtx->compile_units()) {
+ const DWARFDie &Die = CU->getUnitDIE();
+ std::string DWOName = dwarf::toString(
+ Die.find({dwarf::DW_AT_dwo_name, dwarf::DW_AT_GNU_dwo_name}), "");
+ if (DWOName.empty())
+ continue;
+ std::string DWOCompDir =
+ dwarf::toString(Die.find(dwarf::DW_AT_comp_dir), "");
+ if (!DWOCompDir.empty()) {
+ SmallString<16> DWOPath;
+ sys::path::append(DWOPath, DWOCompDir, DWOName);
+ DWOPaths.emplace_back(DWOPath.data(), DWOPath.size());
+ } else {
+ DWOPaths.push_back(std::move(DWOName));
+ }
+ }
+ 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";
+ return 1;
+}
+
+int main(int argc, char **argv) {
+ InitLLVM X(argc, argv);
+
+ cl::ParseCommandLineOptions(argc, argv, "merge split dwarf (.dwo) files\n");
+
+ llvm::InitializeAllTargetInfos();
+ llvm::InitializeAllTargetMCs();
+ llvm::InitializeAllTargets();
+ llvm::InitializeAllAsmPrinters();
+
+ std::string ErrorStr;
+ StringRef Context = "dwarf streamer init";
+
+ Triple TheTriple("x86_64-linux-gnu");
+
+ // Get the target.
+ const Target *TheTarget =
+ TargetRegistry::lookupTarget("", TheTriple, ErrorStr);
+ if (!TheTarget)
+ return error(ErrorStr, Context);
+ std::string TripleName = TheTriple.getTriple();
+
+ // Create all the MC Objects.
+ std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TripleName));
+ if (!MRI)
+ return error(Twine("no register info for target ") + TripleName, Context);
+
+ MCTargetOptions MCOptions = llvm::mc::InitMCTargetOptionsFromFlags();
+ std::unique_ptr<MCAsmInfo> MAI(
+ TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions));
+ if (!MAI)
+ return error("no asm info for target " + TripleName, Context);
+
+ MCObjectFileInfo MOFI;
+ MCContext MC(MAI.get(), MRI.get(), &MOFI);
+ MOFI.InitMCObjectFileInfo(TheTriple, /*PIC*/ false, MC);
+
+ std::unique_ptr<MCSubtargetInfo> MSTI(
+ TheTarget->createMCSubtargetInfo(TripleName, "", ""));
+ if (!MSTI)
+ return error("no subtarget info for target " + TripleName, Context);
+
+ MCTargetOptions Options;
+ auto MAB = TheTarget->createMCAsmBackend(*MSTI, *MRI, Options);
+ if (!MAB)
+ return error("no asm backend for target " + TripleName, Context);
+
+ std::unique_ptr<MCInstrInfo> MII(TheTarget->createMCInstrInfo());
+ if (!MII)
+ return error("no instr info info for target " + TripleName, Context);
+
+ MCCodeEmitter *MCE = TheTarget->createMCCodeEmitter(*MII, *MRI, MC);
+ if (!MCE)
+ return error("no code emitter for target " + TripleName, Context);
+
+ // Create the output file.
+ std::error_code EC;
+ ToolOutputFile OutFile(OutputFilename, EC, sys::fs::OF_None);
+ Optional<buffer_ostream> BOS;
+ raw_pwrite_stream *OS;
+ if (EC)
+ return error(Twine(OutputFilename) + ": " + EC.message(), Context);
+ if (OutFile.os().supportsSeeking()) {
+ OS = &OutFile.os();
+ } else {
+ BOS.emplace(OutFile.os());
+ OS = BOS.getPointer();
+ }
+
+ std::unique_ptr<MCStreamer> MS(TheTarget->createMCObjectStreamer(
+ TheTriple, MC, std::unique_ptr<MCAsmBackend>(MAB),
+ MAB->createObjectWriter(*OS), std::unique_ptr<MCCodeEmitter>(MCE), *MSTI,
+ MCOptions.MCRelaxAll, MCOptions.MCIncrementalLinkerCompatible,
+ /*DWARFMustBeAtTheEnd*/ false));
+ if (!MS)
+ return error("no object streamer for target " + TripleName, Context);
+
+ std::vector<std::string> DWOFilenames = InputFiles;
+ for (const auto &ExecFilename : ExecFilenames) {
+ auto DWOs = getDWOFilenames(ExecFilename);
+ if (!DWOs) {
+ logAllUnhandledErrors(DWOs.takeError(), WithColor::error());
+ return 1;
+ }
+ DWOFilenames.insert(DWOFilenames.end(),
+ std::make_move_iterator(DWOs->begin()),
+ std::make_move_iterator(DWOs->end()));
+ }
+
+ if (auto Err = write(*MS, DWOFilenames)) {
+ logAllUnhandledErrors(std::move(Err), WithColor::error());
+ return 1;
+ }
+
+ MS->Finish();
+ OutFile.keep();
+ return 0;
+}
diff --git a/llvm/tools/llvm-extract/llvm-extract.cpp b/llvm/tools/llvm-extract/llvm-extract.cpp
index dddc0d9baa089..cb1c4116ff192 100644
--- a/llvm/tools/llvm-extract/llvm-extract.cpp
+++ b/llvm/tools/llvm-extract/llvm-extract.cpp
@@ -31,6 +31,7 @@
#include "llvm/Support/ToolOutputFile.h"
#include "llvm/Transforms/IPO.h"
#include <memory>
+#include <utility>
using namespace llvm;
cl::OptionCategory ExtractCat("llvm-extract Options");
@@ -53,6 +54,10 @@ static cl::opt<bool> DeleteFn("delete",
cl::desc("Delete specified Globals from Module"),
cl::cat(ExtractCat));
+static cl::opt<bool> KeepConstInit("keep-const-init",
+ cl::desc("Keep initializers of constants"),
+ cl::cat(ExtractCat));
+
static cl::opt<bool>
Recursive("recursive", cl::desc("Recursively extract all called functions"),
cl::cat(ExtractCat));
@@ -252,8 +257,9 @@ int main(int argc, char **argv) {
}
// Figure out which BasicBlocks we should extract.
- SmallVector<SmallVector<BasicBlock *, 16>, 4> GroupOfBBs;
+ SmallVector<std::pair<Function *, SmallVector<StringRef, 16>>, 2> BBMap;
for (StringRef StrPair : ExtractBlocks) {
+ SmallVector<StringRef, 16> BBNames;
auto BBInfo = StrPair.split(':');
// Get the function.
Function *F = M->getFunction(BBInfo.first);
@@ -262,26 +268,11 @@ int main(int argc, char **argv) {
<< BBInfo.first << "'!\n";
return 1;
}
- // Do not materialize this function.
+ // Add the function to the materialize list, and store the basic block names
+ // to check after materialization.
GVs.insert(F);
- // Get the basic blocks.
- SmallVector<BasicBlock *, 16> BBs;
- SmallVector<StringRef, 16> BBNames;
- BBInfo.second.split(BBNames, ';', /*MaxSplit=*/-1,
- /*KeepEmpty=*/false);
- for (StringRef BBName : BBNames) {
- auto Res = llvm::find_if(*F, [&](const BasicBlock &BB) {
- return BB.getName().equals(BBName);
- });
- if (Res == F->end()) {
- errs() << argv[0] << ": function " << F->getName()
- << " doesn't contain a basic block named '" << BBInfo.second
- << "'!\n";
- return 1;
- }
- BBs.push_back(&*Res);
- }
- GroupOfBBs.push_back(BBs);
+ BBInfo.second.split(BBNames, ';', /*MaxSplit=*/-1, /*KeepEmpty=*/false);
+ BBMap.push_back({F, std::move(BBNames)});
}
// Use *argv instead of argv[0] to work around a wrong GCC warning.
@@ -333,7 +324,7 @@ int main(int argc, char **argv) {
{
std::vector<GlobalValue *> Gvs(GVs.begin(), GVs.end());
legacy::PassManager Extract;
- Extract.add(createGVExtractionPass(Gvs, DeleteFn));
+ Extract.add(createGVExtractionPass(Gvs, DeleteFn, KeepConstInit));
Extract.run(*M);
// Now that we have all the GVs we want, mark the module as fully
@@ -345,6 +336,27 @@ int main(int argc, char **argv) {
// Extract the specified basic blocks from the module and erase the existing
// functions.
if (!ExtractBlocks.empty()) {
+ // Figure out which BasicBlocks we should extract.
+ SmallVector<SmallVector<BasicBlock *, 16>, 4> GroupOfBBs;
+ for (auto &P : BBMap) {
+ SmallVector<BasicBlock *, 16> BBs;
+ for (StringRef BBName : P.second) {
+ // The function has been materialized, so add its matching basic blocks
+ // to the block extractor list, or fail if a name is not found.
+ auto Res = llvm::find_if(*P.first, [&](const BasicBlock &BB) {
+ return BB.getName().equals(BBName);
+ });
+ if (Res == P.first->end()) {
+ errs() << argv[0] << ": function " << P.first->getName()
+ << " doesn't contain a basic block named '" << BBName
+ << "'!\n";
+ return 1;
+ }
+ BBs.push_back(&*Res);
+ }
+ GroupOfBBs.push_back(BBs);
+ }
+
legacy::PassManager PM;
PM.add(createBlockExtractorPass(GroupOfBBs, true));
PM.run(*M);
@@ -369,7 +381,7 @@ int main(int argc, char **argv) {
if (OutputAssembly)
Passes.add(
createPrintModulePass(Out.os(), "", PreserveAssemblyUseListOrder));
- else if (Force || !CheckBitcodeOutputToConsole(Out.os(), true))
+ else if (Force || !CheckBitcodeOutputToConsole(Out.os()))
Passes.add(createBitcodeWriterPass(Out.os(), PreserveBitcodeUseListOrder));
Passes.run(*M.get());
diff --git a/llvm/tools/llvm-link/llvm-link.cpp b/llvm/tools/llvm-link/llvm-link.cpp
index fa36e083b6f8c..7141bd1ca7a15 100644
--- a/llvm/tools/llvm-link/llvm-link.cpp
+++ b/llvm/tools/llvm-link/llvm-link.cpp
@@ -11,6 +11,7 @@
//
//===----------------------------------------------------------------------===//
+#include "llvm/Object/Archive.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Bitcode/BitcodeReader.h"
#include "llvm/Bitcode/BitcodeWriter.h"
@@ -139,6 +140,73 @@ static std::unique_ptr<Module> loadFile(const char *argv0,
return Result;
}
+static std::unique_ptr<Module> loadArFile(const char *Argv0,
+ const std::string &ArchiveName,
+ LLVMContext &Context, Linker &L,
+ unsigned OrigFlags,
+ unsigned ApplicableFlags) {
+ std::unique_ptr<Module> Result(new Module("ArchiveModule", Context));
+ if (Verbose)
+ errs() << "Reading library archive file '" << ArchiveName
+ << "' to memory\n";
+ ErrorOr<std::unique_ptr<MemoryBuffer>> Buf =
+ MemoryBuffer::getFile(ArchiveName, -1, false);
+ ExitOnErr(errorCodeToError(Buf.getError()));
+ Error Err = Error::success();
+ object::Archive Archive(Buf.get()->getMemBufferRef(), Err);
+ ExitOnErr(std::move(Err));
+ for (const object::Archive::Child &C : Archive.children(Err)) {
+ Expected<StringRef> Ename = C.getName();
+ if (Error E = Ename.takeError()) {
+ errs() << Argv0 << ": ";
+ WithColor::error()
+ << " failed to read name of archive member"
+ << ArchiveName << "'\n";
+ return nullptr;
+ };
+ std::string ChildName = Ename.get().str();
+ if (Verbose)
+ errs() << "Parsing member '" << ChildName
+ << "' of archive library to module.\n";
+ SMDiagnostic ParseErr;
+ Expected<MemoryBufferRef> MemBuf = C.getMemoryBufferRef();
+ if (Error E = MemBuf.takeError()) {
+ errs() << Argv0 << ": ";
+ WithColor::error() << " loading memory for member '" << ChildName
+ << "' of archive library failed'" << ArchiveName
+ << "'\n";
+ return nullptr;
+ };
+
+ 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";
+ return nullptr;
+ }
+
+ std::unique_ptr<Module> M = parseIR(MemBuf.get(), ParseErr, Context);
+
+ if (!M.get()) {
+ errs() << Argv0 << ": ";
+ WithColor::error() << " parsing member '" << ChildName
+ << "' of archive library failed'" << ArchiveName
+ << "'\n";
+ return nullptr;
+ }
+ if (Verbose)
+ errs() << "Linking member '" << ChildName << "' of archive library.\n";
+ if (L.linkModules(*Result, std::move(M), ApplicableFlags))
+ return nullptr;
+ ApplicableFlags = OrigFlags;
+ } // end for each child
+ ExitOnErr(std::move(Err));
+ return Result;
+}
+
namespace {
/// Helper to load on demand a Module from file and cache it for subsequent
@@ -264,9 +332,10 @@ static bool importFunctions(const char *argv0, Module &DestModule) {
Entry.insert(F->getGUID());
}
auto CachedModuleLoader = [&](StringRef Identifier) {
- return ModuleLoaderCache.takeModule(Identifier);
+ return ModuleLoaderCache.takeModule(std::string(Identifier));
};
- FunctionImporter Importer(*Index, CachedModuleLoader);
+ FunctionImporter Importer(*Index, CachedModuleLoader,
+ /*ClearDSOLocalOnDeclarations=*/false);
ExitOnErr(Importer.importFunctions(DestModule, ImportList));
return true;
@@ -280,7 +349,10 @@ static bool linkFiles(const char *argv0, LLVMContext &Context, Linker &L,
// Similar to some flags, internalization doesn't apply to the first file.
bool InternalizeLinkedSymbols = false;
for (const auto &File : Files) {
- std::unique_ptr<Module> M = loadFile(argv0, File, Context);
+ std::unique_ptr<Module> M =
+ (llvm::sys::path::extension(File) == ".a")
+ ? loadArFile(argv0, File, Context, L, Flags, ApplicableFlags)
+ : loadFile(argv0, File, Context);
if (!M.get()) {
errs() << argv0 << ": ";
WithColor::error() << " loading file '" << File << "'\n";
@@ -313,7 +385,8 @@ static bool linkFiles(const char *argv0, LLVMContext &Context, Linker &L,
}
// Promotion
- if (renameModuleForThinLTO(*M, *Index))
+ if (renameModuleForThinLTO(*M, *Index,
+ /*ClearDSOLocalOnDeclarations=*/false))
return true;
}
@@ -397,7 +470,7 @@ int main(int argc, char **argv) {
errs() << "Writing bitcode...\n";
if (OutputAssembly) {
Composite->print(Out.os(), nullptr, PreserveAssemblyUseListOrder);
- } else if (Force || !CheckBitcodeOutputToConsole(Out.os(), true))
+ } else if (Force || !CheckBitcodeOutputToConsole(Out.os()))
WriteBitcodeToFile(*Composite, Out.os(), PreserveBitcodeUseListOrder);
// Declare success.
diff --git a/llvm/tools/llvm-lto/llvm-lto.cpp b/llvm/tools/llvm-lto/llvm-lto.cpp
index b47e68e82850c..0bd9078f2d8ca 100644
--- a/llvm/tools/llvm-lto/llvm-lto.cpp
+++ b/llvm/tools/llvm-lto/llvm-lto.cpp
@@ -21,7 +21,7 @@
#include "llvm/ADT/Twine.h"
#include "llvm/Bitcode/BitcodeReader.h"
#include "llvm/Bitcode/BitcodeWriter.h"
-#include "llvm/CodeGen/CommandFlags.inc"
+#include "llvm/CodeGen/CommandFlags.h"
#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/IR/DiagnosticPrinter.h"
#include "llvm/IR/LLVMContext.h"
@@ -62,6 +62,8 @@
using namespace llvm;
+static codegen::RegisterCodeGenFlags CGF;
+
static cl::opt<char>
OptLevel("O", cl::desc("Optimization level. [-O0, -O1, -O2, or -O3] "
"(default = '-O2')"),
@@ -223,6 +225,10 @@ static cl::opt<bool> CheckHasObjC(
"check-for-objc", cl::init(false),
cl::desc("Only check if the module has objective-C defined in it"));
+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"));
+
namespace {
struct ModuleInfo {
@@ -404,6 +410,30 @@ static void listDependentLibraries() {
}
}
+static void printMachOCPUOnly() {
+ LLVMContext Context;
+ Context.setDiagnosticHandler(std::make_unique<LLVMLTODiagnosticHandler>(),
+ true);
+ TargetOptions Options = codegen::InitTargetOptionsFromCodeGenFlags();
+ for (auto &Filename : InputFilenames) {
+ ErrorOr<std::unique_ptr<LTOModule>> ModuleOrErr =
+ LTOModule::createFromFile(Context, Filename, Options);
+ if (!ModuleOrErr)
+ error(ModuleOrErr, "llvm-lto: ");
+
+ Expected<uint32_t> CPUType = (*ModuleOrErr)->getMachOCPUType();
+ Expected<uint32_t> CPUSubType = (*ModuleOrErr)->getMachOCPUSubType();
+ if (!CPUType)
+ error("Error while printing mach-o cputype: " +
+ toString(CPUType.takeError()));
+ if (!CPUSubType)
+ error("Error while printing mach-o cpusubtype: " +
+ toString(CPUSubType.takeError()));
+ outs() << llvm::format("%s:\ncputype: %u\ncpusubtype: %u\n",
+ Filename.c_str(), *CPUType, *CPUSubType);
+ }
+}
+
/// Create a combined index file from the input IR files and write it.
///
/// This is meant to enable testing of ThinLTO combined index generation,
@@ -454,7 +484,7 @@ static std::string getThinLTOOutputFile(const std::string &Path,
if (std::error_code EC = llvm::sys::fs::create_directories(ParentPath))
error(EC, "error creating the directory '" + ParentPath + "'");
}
- return NewPath.str();
+ return std::string(NewPath.str());
}
namespace thinlto {
@@ -521,7 +551,7 @@ public:
ThinLTOCodeGenerator ThinGenerator;
ThinLTOProcessing(const TargetOptions &Options) {
- ThinGenerator.setCodePICModel(getRelocModel());
+ ThinGenerator.setCodePICModel(codegen::getExplicitRelocModel());
ThinGenerator.setTargetOptions(Options);
ThinGenerator.setCacheDir(ThinLTOCacheDir);
ThinGenerator.setCachePruningInterval(ThinLTOCachePruningInterval);
@@ -873,7 +903,7 @@ int main(int argc, char **argv) {
InitializeAllAsmParsers();
// set up the TargetOptions for the machine
- TargetOptions Options = InitTargetOptionsFromCodeGenFlags();
+ TargetOptions Options = codegen::InitTargetOptionsFromCodeGenFlags();
if (ListSymbolsOnly) {
listSymbols(Options);
@@ -905,6 +935,11 @@ int main(int argc, char **argv) {
return 0;
}
+ if (PrintMachOCPUOnly) {
+ printMachOCPUOnly();
+ return 0;
+ }
+
if (ThinLTOMode.getNumOccurrences()) {
if (ThinLTOMode.getNumOccurrences() > 1)
report_fatal_error("You can't specify more than one -thinlto-action");
@@ -929,7 +964,7 @@ int main(int argc, char **argv) {
if (UseDiagnosticHandler)
CodeGen.setDiagnosticHandler(handleDiagnostics, nullptr);
- CodeGen.setCodePICModel(getRelocModel());
+ CodeGen.setCodePICModel(codegen::getExplicitRelocModel());
CodeGen.setFreestanding(EnableFreestanding);
CodeGen.setDebugInfo(LTO_DEBUG_MODEL_DWARF);
@@ -957,7 +992,7 @@ int main(int argc, char **argv) {
lto_symbol_attributes Attrs = Module->getSymbolAttributes(I);
unsigned Scope = Attrs & LTO_SYMBOL_SCOPE_MASK;
if (Scope != LTO_SYMBOL_SCOPE_DEFAULT_CAN_BE_HIDDEN)
- KeptDSOSyms.push_back(Name);
+ KeptDSOSyms.push_back(std::string(Name));
}
// We use the first input module as the destination module when
@@ -980,22 +1015,18 @@ int main(int argc, char **argv) {
CodeGen.addMustPreserveSymbol(KeptDSOSyms[i]);
// Set cpu and attrs strings for the default target/subtarget.
- CodeGen.setCpu(MCPU.c_str());
+ CodeGen.setCpu(codegen::getMCPU().c_str());
CodeGen.setOptLevel(OptLevel - '0');
- std::string attrs;
- for (unsigned i = 0; i < MAttrs.size(); ++i) {
- if (i > 0)
- attrs.append(",");
- attrs.append(MAttrs[i]);
- }
-
- if (!attrs.empty())
+ auto MAttrs = codegen::getMAttrs();
+ if (!MAttrs.empty()) {
+ std::string attrs = join(MAttrs, ",");
CodeGen.setAttr(attrs);
+ }
- if (FileType.getNumOccurrences())
- CodeGen.setFileType(FileType);
+ if (auto FT = codegen::getExplicitFileType())
+ CodeGen.setFileType(FT.getValue());
if (!OutputFilename.empty()) {
if (!CodeGen.optimize(DisableVerify, DisableInline, DisableGVNLoadPRE,
diff --git a/llvm/tools/llvm-lto2/llvm-lto2.cpp b/llvm/tools/llvm-lto2/llvm-lto2.cpp
index 67a677dd45fb9..9dd1f13bd3c39 100644
--- a/llvm/tools/llvm-lto2/llvm-lto2.cpp
+++ b/llvm/tools/llvm-lto2/llvm-lto2.cpp
@@ -16,19 +16,23 @@
//===----------------------------------------------------------------------===//
#include "llvm/Bitcode/BitcodeReader.h"
-#include "llvm/CodeGen/CommandFlags.inc"
+#include "llvm/CodeGen/CommandFlags.h"
#include "llvm/IR/DiagnosticPrinter.h"
#include "llvm/LTO/Caching.h"
#include "llvm/LTO/LTO.h"
+#include "llvm/Passes/PassPlugin.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/PluginLoader.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/Threading.h"
using namespace llvm;
using namespace lto;
+static codegen::RegisterCodeGenFlags CGF;
+
static cl::opt<char>
OptLevel("O", cl::desc("Optimization level. [-O0, -O1, -O2, or -O3] "
"(default = '-O2')"),
@@ -65,8 +69,11 @@ static cl::opt<bool>
"import files for the "
"distributed backend case"));
-static cl::opt<int> Threads("thinlto-threads",
- cl::init(llvm::heavyweight_hardware_concurrency()));
+// Default to using all available threads in the system, but using only one
+// thread per core (no SMT).
+// Use -thinlto-threads=all to use hardware_concurrency() instead, which means
+// to use all hardware threads or cores in the system.
+static cl::opt<std::string> Threads("thinlto-threads");
static cl::list<std::string> SymbolResolutions(
"r",
@@ -137,6 +144,10 @@ static cl::opt<bool>
static cl::opt<std::string>
StatsFile("stats-file", cl::desc("Filename to write statistics to"));
+static cl::list<std::string>
+ PassPlugins("load-pass-plugin",
+ cl::desc("Load passes from plugin library"));
+
static void check(Error E, std::string Msg) {
if (!E)
return;
@@ -203,7 +214,8 @@ static int run(int argc, char **argv) {
return 1;
}
}
- CommandLineResolutions[{FileName, SymbolName}].push_back(Res);
+ CommandLineResolutions[{std::string(FileName), std::string(SymbolName)}]
+ .push_back(Res);
}
std::vector<std::unique_ptr<MemoryBuffer>> MBs;
@@ -217,12 +229,12 @@ static int run(int argc, char **argv) {
exit(1);
};
- Conf.CPU = MCPU;
- Conf.Options = InitTargetOptionsFromCodeGenFlags();
- Conf.MAttrs = MAttrs;
- if (auto RM = getRelocModel())
- Conf.RelocModel = *RM;
- Conf.CodeModel = getCodeModel();
+ Conf.CPU = codegen::getMCPU();
+ Conf.Options = codegen::InitTargetOptionsFromCodeGenFlags();
+ Conf.MAttrs = codegen::getMAttrs();
+ if (auto RM = codegen::getExplicitRelocModel())
+ Conf.RelocModel = RM.getValue();
+ Conf.CodeModel = codegen::getExplicitCodeModel();
Conf.DebugPassManager = DebugPassManager;
@@ -246,6 +258,8 @@ static int run(int argc, char **argv) {
Conf.OptLevel = OptLevel - '0';
Conf.UseNewPM = UseNewPM;
+ for (auto &PluginFN : PassPlugins)
+ Conf.PassPlugins.push_back(PluginFN);
switch (CGOptLevel) {
case '0':
Conf.CGOptLevel = CodeGenOpt::None;
@@ -264,8 +278,8 @@ static int run(int argc, char **argv) {
return 1;
}
- if (FileType.getNumOccurrences())
- Conf.CGFileType = FileType;
+ if (auto FT = codegen::getExplicitFileType())
+ Conf.CGFileType = FT.getValue();
Conf.OverrideTriple = OverrideTriple;
Conf.DefaultTriple = DefaultTriple;
@@ -281,7 +295,8 @@ static int run(int argc, char **argv) {
/* LinkedObjectsFile */ nullptr,
/* OnWrite */ {});
else
- Backend = createInProcessThinBackend(Threads);
+ Backend = createInProcessThinBackend(
+ llvm::heavyweight_hardware_concurrency(Threads));
LTO Lto(std::move(Conf), std::move(Backend));
bool HasErrors = false;
@@ -292,14 +307,14 @@ static int run(int argc, char **argv) {
std::vector<SymbolResolution> Res;
for (const InputFile::Symbol &Sym : Input->symbols()) {
- auto I = CommandLineResolutions.find({F, Sym.getName()});
+ auto I = CommandLineResolutions.find({F, std::string(Sym.getName())});
// 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 "$".
if (I == CommandLineResolutions.end()) {
auto SplitName = Sym.getName().rsplit("$");
- I = CommandLineResolutions.find({F, SplitName.first});
+ I = CommandLineResolutions.find({F, std::string(SplitName.first)});
}
if (I == CommandLineResolutions.end()) {
llvm::errs() << argv[0] << ": missing symbol resolution for " << F
@@ -354,8 +369,10 @@ static int run(int argc, char **argv) {
static int dumpSymtab(int argc, char **argv) {
for (StringRef F : make_range(argv + 1, argv + argc)) {
- std::unique_ptr<MemoryBuffer> MB = check(MemoryBuffer::getFile(F), F);
- BitcodeFileContents BFC = check(getBitcodeFileContents(*MB), F);
+ std::unique_ptr<MemoryBuffer> MB =
+ check(MemoryBuffer::getFile(F), std::string(F));
+ BitcodeFileContents BFC =
+ check(getBitcodeFileContents(*MB), std::string(F));
if (BFC.Symtab.size() >= sizeof(irsymtab::storage::Header)) {
auto *Hdr = reinterpret_cast<const irsymtab::storage::Header *>(
@@ -367,7 +384,7 @@ static int dumpSymtab(int argc, char **argv) {
}
std::unique_ptr<InputFile> Input =
- check(InputFile::create(MB->getMemBufferRef()), F);
+ check(InputFile::create(MB->getMemBufferRef()), std::string(F));
outs() << "target triple: " << Input->getTargetTriple() << '\n';
Triple TT(Input->getTargetTriple());
diff --git a/llvm/tools/llvm-mc/Disassembler.cpp b/llvm/tools/llvm-mc/Disassembler.cpp
index e286c0fff6e15..16ab99548adfc 100644
--- a/llvm/tools/llvm-mc/Disassembler.cpp
+++ b/llvm/tools/llvm-mc/Disassembler.cpp
@@ -68,7 +68,7 @@ static bool PrintInsts(const MCDisassembler &DisAsm,
LLVM_FALLTHROUGH;
case MCDisassembler::Success:
- Streamer.EmitInstruction(Inst, STI);
+ Streamer.emitInstruction(Inst, STI);
break;
}
}
diff --git a/llvm/tools/llvm-mc/llvm-mc.cpp b/llvm/tools/llvm-mc/llvm-mc.cpp
index 6aa347d98be24..66b55abc48983 100644
--- a/llvm/tools/llvm-mc/llvm-mc.cpp
+++ b/llvm/tools/llvm-mc/llvm-mc.cpp
@@ -25,7 +25,7 @@
#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/MC/MCSubtargetInfo.h"
-#include "llvm/MC/MCTargetOptionsCommandFlags.inc"
+#include "llvm/MC/MCTargetOptionsCommandFlags.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Compression.h"
#include "llvm/Support/FileUtilities.h"
@@ -41,6 +41,8 @@
using namespace llvm;
+static mc::RegisterMCTargetOptionsFlags MOF;
+
static cl::opt<std::string>
InputFilename(cl::Positional, cl::desc("<input file>"), cl::init("-"));
@@ -317,7 +319,7 @@ int main(int argc, char **argv) {
cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion);
cl::ParseCommandLineOptions(argc, argv, "llvm machine code playground\n");
- const MCTargetOptions MCOptions = InitMCTargetOptionsFromFlags();
+ const MCTargetOptions MCOptions = mc::InitMCTargetOptionsFromFlags();
setDwarfDebugFlags(argc, argv);
setDwarfDebugProducer();
@@ -385,6 +387,31 @@ int main(int argc, char **argv) {
return 1;
}
Ctx.setDwarfVersion(DwarfVersion);
+ if (MCOptions.Dwarf64) {
+ // The 64-bit DWARF format was introduced in DWARFv3.
+ if (DwarfVersion < 3) {
+ errs() << ProgName
+ << ": the 64-bit DWARF format is not supported for DWARF versions "
+ "prior to 3\n";
+ return 1;
+ }
+ // 32-bit targets don't support DWARF64, which requires 64-bit relocations.
+ if (MAI->getCodePointerSize() < 8) {
+ errs() << ProgName
+ << ": the 64-bit DWARF format is only supported for 64-bit "
+ "targets\n";
+ return 1;
+ }
+ // If needsDwarfSectionOffsetDirective is true, we would eventually call
+ // MCStreamer::emitSymbolValue() with IsSectionRelative = true, but that
+ // is supported only for 4-byte long references.
+ if (MAI->needsDwarfSectionOffsetDirective()) {
+ errs() << ProgName << ": the 64-bit DWARF format is not supported for "
+ << TheTriple.normalize() << "\n";
+ return 1;
+ }
+ Ctx.setDwarfFormat(dwarf::DWARF64);
+ }
if (!DwarfDebugFlags.empty())
Ctx.setDwarfDebugFlags(StringRef(DwarfDebugFlags));
if (!DwarfDebugProducer.empty())
@@ -399,7 +426,7 @@ int main(int argc, char **argv) {
}
for (const auto &Arg : DebugPrefixMap) {
const auto &KV = StringRef(Arg).split('=');
- Ctx.addDebugPrefixMapEntry(KV.first, KV.second);
+ Ctx.addDebugPrefixMapEntry(std::string(KV.first), std::string(KV.second));
}
if (!MainFileName.empty())
Ctx.setMainFileName(MainFileName);
@@ -474,9 +501,6 @@ int main(int argc, char **argv) {
} else {
assert(FileType == OFT_ObjectFile && "Invalid file type!");
- // Don't waste memory on names of temp labels.
- Ctx.setUseNamesOnTempLabels(false);
-
if (!Out->os().supportsSeeking()) {
BOS = std::make_unique<buffer_ostream>(Out->os());
OS = BOS.get();
diff --git a/llvm/tools/llvm-mca/CodeRegion.h b/llvm/tools/llvm-mca/CodeRegion.h
index cabb4a5d44842..d2b05fa80c54e 100644
--- a/llvm/tools/llvm-mca/CodeRegion.h
+++ b/llvm/tools/llvm-mca/CodeRegion.h
@@ -35,8 +35,10 @@
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/MC/MCInst.h"
+#include "llvm/Support/Error.h"
#include "llvm/Support/SMLoc.h"
#include "llvm/Support/SourceMgr.h"
#include <vector>
diff --git a/llvm/tools/llvm-mca/CodeRegionGenerator.cpp b/llvm/tools/llvm-mca/CodeRegionGenerator.cpp
index 8ddcd2f4abe21..831b76ab80cf7 100644
--- a/llvm/tools/llvm-mca/CodeRegionGenerator.cpp
+++ b/llvm/tools/llvm-mca/CodeRegionGenerator.cpp
@@ -47,21 +47,21 @@ public:
: MCStreamer(Context), Regions(R) {}
// We only want to intercept the emission of new instructions.
- virtual void EmitInstruction(const MCInst &Inst,
- const MCSubtargetInfo &/* unused */) override {
+ virtual void emitInstruction(const MCInst &Inst,
+ const MCSubtargetInfo & /* unused */) override {
Regions.addInstruction(Inst);
}
- bool EmitSymbolAttribute(MCSymbol *Symbol, MCSymbolAttr Attribute) override {
+ bool emitSymbolAttribute(MCSymbol *Symbol, MCSymbolAttr Attribute) override {
return true;
}
- void EmitCommonSymbol(MCSymbol *Symbol, uint64_t Size,
+ void emitCommonSymbol(MCSymbol *Symbol, uint64_t Size,
unsigned ByteAlignment) override {}
- void EmitZerofill(MCSection *Section, MCSymbol *Symbol = nullptr,
+ void emitZerofill(MCSection *Section, MCSymbol *Symbol = nullptr,
uint64_t Size = 0, unsigned ByteAlignment = 0,
SMLoc Loc = SMLoc()) override {}
- void EmitGPRel32Value(const MCExpr *Value) override {}
+ void emitGPRel32Value(const MCExpr *Value) override {}
void BeginCOFFSymbolDef(const MCSymbol *Symbol) override {}
void EmitCOFFSymbolStorageClass(int StorageClass) override {}
void EmitCOFFSymbolType(int Type) override {}
diff --git a/llvm/tools/llvm-mca/llvm-mca.cpp b/llvm/tools/llvm-mca/llvm-mca.cpp
index fff5906bb59ba..9f3bf41ff3f8b 100644
--- a/llvm/tools/llvm-mca/llvm-mca.cpp
+++ b/llvm/tools/llvm-mca/llvm-mca.cpp
@@ -39,7 +39,7 @@
#include "llvm/MC/MCObjectFileInfo.h"
#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/MC/MCSubtargetInfo.h"
-#include "llvm/MC/MCTargetOptionsCommandFlags.inc"
+#include "llvm/MC/MCTargetOptionsCommandFlags.h"
#include "llvm/MCA/CodeEmitter.h"
#include "llvm/MCA/Context.h"
#include "llvm/MCA/InstrBuilder.h"
@@ -62,6 +62,8 @@
using namespace llvm;
+static mc::RegisterMCTargetOptionsFlags MOF;
+
static cl::OptionCategory ToolOptions("Tool Options");
static cl::OptionCategory ViewOptions("View Options");
@@ -324,7 +326,7 @@ int main(int argc, char **argv) {
processViewOptions();
if (!MCPU.compare("native"))
- MCPU = llvm::sys::getHostCPUName();
+ MCPU = std::string(llvm::sys::getHostCPUName());
std::unique_ptr<MCSubtargetInfo> STI(
TheTarget->createMCSubtargetInfo(TripleName, MCPU, MATTR));
@@ -353,7 +355,7 @@ int main(int argc, char **argv) {
std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TripleName));
assert(MRI && "Unable to create target register info!");
- MCTargetOptions MCOptions = InitMCTargetOptionsFromFlags();
+ MCTargetOptions MCOptions = mc::InitMCTargetOptionsFromFlags();
std::unique_ptr<MCAsmInfo> MAI(
TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions));
assert(MAI && "Unable to create target asm info!");
@@ -443,7 +445,7 @@ int main(int argc, char **argv) {
TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx));
std::unique_ptr<MCAsmBackend> MAB(TheTarget->createMCAsmBackend(
- *STI, *MRI, InitMCTargetOptionsFromFlags()));
+ *STI, *MRI, mc::InitMCTargetOptionsFromFlags()));
for (const std::unique_ptr<mca::CodeRegion> &Region : Regions) {
// Skip empty code regions.
diff --git a/llvm/tools/llvm-nm/llvm-nm.cpp b/llvm/tools/llvm-nm/llvm-nm.cpp
index 107d62b1f2b92..ecd1e21e15bfb 100644
--- a/llvm/tools/llvm-nm/llvm-nm.cpp
+++ b/llvm/tools/llvm-nm/llvm-nm.cpp
@@ -28,6 +28,8 @@
#include "llvm/Object/MachO.h"
#include "llvm/Object/MachOUniversal.h"
#include "llvm/Object/ObjectFile.h"
+#include "llvm/Object/TapiFile.h"
+#include "llvm/Object/TapiUniversal.h"
#include "llvm/Object/Wasm.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
@@ -180,8 +182,10 @@ cl::opt<bool> JustSymbolName("just-symbol-name",
cl::alias JustSymbolNames("j", cl::desc("Alias for --just-symbol-name"),
cl::aliasopt(JustSymbolName), cl::Grouping);
-cl::opt<bool> SpecialSyms("special-syms",
- cl::desc("No-op. Used for GNU compatibility only"));
+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,
@@ -210,6 +214,11 @@ 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;
@@ -306,13 +315,17 @@ struct NMSymbol {
static bool compareSymbolAddress(const NMSymbol &A, const NMSymbol &B) {
bool ADefined;
+ // Symbol flags have been checked in the caller.
+ uint32_t AFlags = cantFail(A.Sym.getFlags());
if (A.Sym.getRawDataRefImpl().p)
- ADefined = !(A.Sym.getFlags() & SymbolRef::SF_Undefined);
+ ADefined = !(AFlags & SymbolRef::SF_Undefined);
else
ADefined = A.TypeChar != 'U';
bool BDefined;
+ // Symbol flags have been checked in the caller.
+ uint32_t BFlags = cantFail(B.Sym.getFlags());
if (B.Sym.getRawDataRefImpl().p)
- BDefined = !(B.Sym.getFlags() & SymbolRef::SF_Undefined);
+ BDefined = !(BFlags & SymbolRef::SF_Undefined);
else
BDefined = B.TypeChar != 'U';
return std::make_tuple(ADefined, A.Address, A.Name, A.Size) <
@@ -336,6 +349,8 @@ static char isSymbolList64Bit(SymbolicFile &Obj) {
return false;
if (isa<WasmObjectFile>(Obj))
return false;
+ if (TapiFile *Tapi = dyn_cast<TapiFile>(&Obj))
+ return Tapi->is64Bit();
if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(&Obj))
return MachO->is64Bit();
return cast<ELFObjectFileBase>(Obj).getBytesInAddress() == 8;
@@ -366,7 +381,7 @@ static void darwinPrintSymbol(SymbolicFile &Obj, const NMSymbol &S,
uint64_t NValue = 0;
MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(&Obj);
if (Obj.isIR()) {
- uint32_t SymFlags = S.Sym.getFlags();
+ uint32_t SymFlags = cantFail(S.Sym.getFlags());
if (SymFlags & SymbolRef::SF_Global)
NType |= MachO::N_EXT;
if (SymFlags & SymbolRef::SF_Hidden)
@@ -707,9 +722,32 @@ static bool symbolIsDefined(const NMSymbol &Sym) {
return Sym.TypeChar != 'U' && Sym.TypeChar != 'w' && Sym.TypeChar != 'v';
}
+static void writeFileName(raw_ostream &S, StringRef ArchiveName,
+ StringRef ArchitectureName) {
+ if (!ArchitectureName.empty())
+ S << "(for architecture " << ArchitectureName << "):";
+ if (OutputFormat == posix && !ArchiveName.empty())
+ S << ArchiveName << "[" << CurrentFilename << "]: ";
+ else {
+ if (!ArchiveName.empty())
+ S << ArchiveName << ":";
+ S << CurrentFilename << ": ";
+ }
+}
+
+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,
- const std::string &ArchiveName,
- const std::string &ArchitectureName) {
+ StringRef ArchiveName,
+ StringRef ArchitectureName) {
if (!NoSort) {
using Comparator = bool (*)(const NMSymbol &, const NMSymbol &);
Comparator Cmp;
@@ -773,24 +811,6 @@ static void sortAndPrintSymbolList(SymbolicFile &Obj, bool printName,
}
}
- auto writeFileName = [&](raw_ostream &S) {
- if (!ArchitectureName.empty())
- S << "(for architecture " << ArchitectureName << "):";
- if (OutputFormat == posix && !ArchiveName.empty())
- S << ArchiveName << "[" << CurrentFilename << "]: ";
- else {
- if (!ArchiveName.empty())
- S << ArchiveName << ":";
- S << CurrentFilename << ": ";
- }
- };
-
- if (SymbolList.empty()) {
- if (PrintFileName)
- writeFileName(errs());
- errs() << "no symbols\n";
- }
-
for (const NMSymbol &S : SymbolList) {
uint32_t SymFlags;
std::string Name = S.Name.str();
@@ -799,19 +819,26 @@ static void sortAndPrintSymbolList(SymbolicFile &Obj, bool printName,
if (Optional<std::string> Opt = demangle(S.Name, MachO))
Name = *Opt;
}
- if (S.Sym.getRawDataRefImpl().p)
- SymFlags = S.Sym.getFlags();
- else
+ if (S.Sym.getRawDataRefImpl().p) {
+ Expected<uint32_t> SymFlagsOrErr = S.Sym.getFlags();
+ if (!SymFlagsOrErr) {
+ // TODO: Test this error.
+ error(SymFlagsOrErr.takeError(), Obj.getFileName());
+ return;
+ }
+ SymFlags = *SymFlagsOrErr;
+ } else
SymFlags = S.SymFlags;
bool Undefined = SymFlags & SymbolRef::SF_Undefined;
bool Global = SymFlags & SymbolRef::SF_Global;
bool Weak = SymFlags & SymbolRef::SF_Weak;
if ((!Undefined && UndefinedOnly) || (Undefined && DefinedOnly) ||
- (!Global && ExternalOnly) || (Weak && NoWeakSymbols))
+ (!Global && ExternalOnly) || (Weak && NoWeakSymbols) ||
+ (!SpecialSyms && isSpecialSym(Obj, Name)))
continue;
if (PrintFileName)
- writeFileName(outs());
+ writeFileName(outs(), ArchiveName, ArchitectureName);
if ((JustSymbolName ||
(UndefinedOnly && MachO && OutputFormat != darwin)) &&
OutputFormat != posix) {
@@ -1041,15 +1068,19 @@ static char getSymbolNMTypeChar(MachOObjectFile &Obj, basic_symbol_iterator I) {
return '?';
}
+static char getSymbolNMTypeChar(TapiFile &Obj, basic_symbol_iterator I) {
+ return 's';
+}
+
static char getSymbolNMTypeChar(WasmObjectFile &Obj, basic_symbol_iterator I) {
- uint32_t Flags = I->getFlags();
+ uint32_t Flags = cantFail(I->getFlags());
if (Flags & SymbolRef::SF_Executable)
return 't';
return 'd';
}
static char getSymbolNMTypeChar(IRObjectFile &Obj, basic_symbol_iterator I) {
- uint32_t Flags = I->getFlags();
+ uint32_t Flags = cantFail(I->getFlags());
// FIXME: should we print 'b'? At the IR level we cannot be sure if this
// will be in bss or not, but we could approximate.
if (Flags & SymbolRef::SF_Executable)
@@ -1081,7 +1112,8 @@ static StringRef getNMTypeName(SymbolicFile &Obj, basic_symbol_iterator I) {
// section and name, to be used in format=sysv output.
static char getNMSectionTagAndName(SymbolicFile &Obj, basic_symbol_iterator I,
StringRef &SecName) {
- uint32_t Symflags = I->getFlags();
+ // Symbol Flags have been checked in the caller.
+ uint32_t Symflags = cantFail(I->getFlags());
if (ELFObjectFileBase *ELFObj = dyn_cast<ELFObjectFileBase>(&Obj)) {
if (Symflags & object::SymbolRef::SF_Absolute)
SecName = "*ABS*";
@@ -1133,6 +1165,8 @@ static char getNMSectionTagAndName(SymbolicFile &Obj, basic_symbol_iterator I,
Ret = getSymbolNMTypeChar(*MachO, I);
else if (WasmObjectFile *Wasm = dyn_cast<WasmObjectFile>(&Obj))
Ret = getSymbolNMTypeChar(*Wasm, 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';
@@ -1184,10 +1218,9 @@ static unsigned getNsectInMachO(MachOObjectFile &Obj, BasicSymbolRef Sym) {
return (STE.n_type & MachO::N_TYPE) == MachO::N_SECT ? STE.n_sect : 0;
}
-static void
-dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName,
- const std::string &ArchiveName = std::string(),
- const std::string &ArchitectureName = std::string()) {
+static void dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName,
+ StringRef ArchiveName = {},
+ StringRef ArchitectureName = {}) {
auto Symbols = Obj.symbols();
if (DynamicSyms) {
const auto *E = dyn_cast<ELFObjectFileBase>(&Obj);
@@ -1209,12 +1242,16 @@ dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName,
if (Nsect == 0)
return;
}
- if (!MachO || !DyldInfoOnly) {
+ if (!(MachO && DyldInfoOnly)) {
for (BasicSymbolRef Sym : Symbols) {
- uint32_t SymFlags = Sym.getFlags();
- if (!DebugSyms && (SymFlags & SymbolRef::SF_FormatSpecific))
+ Expected<uint32_t> SymFlagsOrErr = Sym.getFlags();
+ if (!SymFlagsOrErr) {
+ error(SymFlagsOrErr.takeError(), Obj.getFileName());
+ return;
+ }
+ if (!DebugSyms && (*SymFlagsOrErr & SymbolRef::SF_FormatSpecific))
continue;
- if (WithoutAliases && (SymFlags & SymbolRef::SF_Indirect))
+ if (WithoutAliases && (*SymFlagsOrErr & SymbolRef::SF_Indirect))
continue;
// If a "-s segname sectname" option was specified and this is a Mach-O
// file and this section appears in this file, Nsect will be non-zero then
@@ -1739,6 +1776,12 @@ dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName,
}
CurrentFilename = Obj.getFileName();
+
+ if (Symbols.empty() && SymbolList.empty()) {
+ writeFileName(errs(), ArchiveName, ArchitectureName);
+ errs() << "no symbols\n";
+ }
+
sortAndPrintSymbolList(Obj, printName, ArchiveName, ArchitectureName);
}
@@ -1903,7 +1946,7 @@ static void dumpSymbolNamesFromFile(std::string &Filename) {
if (SymbolicFile *O =
dyn_cast<SymbolicFile>(&*ChildOrErr.get())) {
if (PrintFileName) {
- ArchiveName = A->getFileName();
+ ArchiveName = std::string(A->getFileName());
if (ArchFlags.size() > 1)
ArchitectureName = I->getArchFlagName();
} else {
@@ -1972,7 +2015,7 @@ static void dumpSymbolNamesFromFile(std::string &Filename) {
if (SymbolicFile *O =
dyn_cast<SymbolicFile>(&*ChildOrErr.get())) {
if (PrintFileName)
- ArchiveName = A->getFileName();
+ ArchiveName = std::string(A->getFileName());
else
outs() << "\n" << A->getFileName() << "(" << O->getFileName()
<< ")"
@@ -2037,7 +2080,7 @@ static void dumpSymbolNamesFromFile(std::string &Filename) {
}
if (SymbolicFile *F = dyn_cast<SymbolicFile>(&*ChildOrErr.get())) {
if (PrintFileName) {
- ArchiveName = A->getFileName();
+ ArchiveName = std::string(A->getFileName());
if (isa<MachOObjectFile>(F) && moreThanOneArch)
ArchitectureName = O.getArchFlagName();
} else {
@@ -2066,6 +2109,31 @@ static void dumpSymbolNamesFromFile(std::string &Filename) {
}
return;
}
+
+ if (TapiUniversal *TU = dyn_cast<TapiUniversal>(&Bin)) {
+ for (const TapiUniversal::ObjectForArch &I : TU->objects()) {
+ StringRef ArchName = I.getArchFlagName();
+ const bool ShowArch =
+ ArchFlags.empty() ||
+ any_of(ArchFlags, [&](StringRef Name) { return Name == ArchName; });
+ if (!ShowArch)
+ continue;
+ if (!AddInlinedInfo && !I.isTopLevelLib())
+ continue;
+ if (auto ObjOrErr = I.getAsObjectFile()) {
+ outs() << "\n"
+ << I.getInstallName() << " (for architecture " << ArchName << ")"
+ << ":\n";
+ dumpSymbolNamesFromObject(*ObjOrErr.get(), false, {}, ArchName);
+ } else if (Error E =
+ isNotObjectErrorInvalidFileType(ObjOrErr.takeError())) {
+ error(std::move(E), Filename, ArchName);
+ }
+ }
+
+ return;
+ }
+
if (SymbolicFile *O = dyn_cast<SymbolicFile>(&Bin)) {
if (!MachOPrintSizeWarning && PrintSize && isa<MachOObjectFile>(O)) {
WithColor::warning(errs(), ToolName)
diff --git a/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp b/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
index b172fae527eb5..43ec2b1fa82f2 100644
--- a/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
+++ b/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
@@ -89,6 +89,43 @@ static void addGnuDebugLink(Object &Obj, StringRef DebugLinkFile) {
IMAGE_SCN_MEM_DISCARDABLE);
}
+static void setSectionFlags(Section &Sec, SectionFlag AllFlags) {
+ // Need to preserve alignment flags.
+ const uint32_t PreserveMask =
+ IMAGE_SCN_ALIGN_1BYTES | IMAGE_SCN_ALIGN_2BYTES | IMAGE_SCN_ALIGN_4BYTES |
+ IMAGE_SCN_ALIGN_8BYTES | IMAGE_SCN_ALIGN_16BYTES |
+ IMAGE_SCN_ALIGN_32BYTES | IMAGE_SCN_ALIGN_64BYTES |
+ IMAGE_SCN_ALIGN_128BYTES | IMAGE_SCN_ALIGN_256BYTES |
+ IMAGE_SCN_ALIGN_512BYTES | IMAGE_SCN_ALIGN_1024BYTES |
+ IMAGE_SCN_ALIGN_2048BYTES | IMAGE_SCN_ALIGN_4096BYTES |
+ IMAGE_SCN_ALIGN_8192BYTES;
+
+ // Setup new section characteristics based on the flags provided in command
+ // line.
+ uint32_t NewCharacteristics =
+ (Sec.Header.Characteristics & PreserveMask) | IMAGE_SCN_MEM_READ;
+
+ if ((AllFlags & SectionFlag::SecAlloc) && !(AllFlags & SectionFlag::SecLoad))
+ NewCharacteristics |= IMAGE_SCN_CNT_UNINITIALIZED_DATA;
+ if (AllFlags & SectionFlag::SecNoload)
+ NewCharacteristics |= IMAGE_SCN_LNK_REMOVE;
+ if (!(AllFlags & SectionFlag::SecReadonly))
+ NewCharacteristics |= IMAGE_SCN_MEM_WRITE;
+ if (AllFlags & SectionFlag::SecDebug)
+ NewCharacteristics |=
+ IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_DISCARDABLE;
+ if (AllFlags & SectionFlag::SecCode)
+ NewCharacteristics |= IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE;
+ if (AllFlags & SectionFlag::SecData)
+ NewCharacteristics |= IMAGE_SCN_CNT_INITIALIZED_DATA;
+ if (AllFlags & SectionFlag::SecShare)
+ NewCharacteristics |= IMAGE_SCN_MEM_SHARED;
+ if (AllFlags & SectionFlag::SecExclude)
+ NewCharacteristics |= IMAGE_SCN_LNK_REMOVE;
+
+ Sec.Header.Characteristics = NewCharacteristics;
+}
+
static Error handleArgs(const CopyConfig &Config, Object &Obj) {
// Perform the actual section removals.
Obj.removeSections([&Config](const Section &Sec) {
@@ -178,6 +215,13 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj) {
return false;
});
+ if (!Config.SetSectionFlags.empty())
+ for (Section &Sec : Obj.getMutableSections()) {
+ const auto It = Config.SetSectionFlags.find(Sec.Name);
+ if (It != Config.SetSectionFlags.end())
+ setSectionFlags(Sec, It->second.NewFlags);
+ }
+
for (const auto &Flag : Config.AddSection) {
StringRef SecName, FileName;
std::tie(SecName, FileName) = Flag.split("=");
@@ -205,10 +249,11 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj) {
!Config.SymbolsToGlobalize.empty() || !Config.SymbolsToKeep.empty() ||
!Config.SymbolsToLocalize.empty() || !Config.SymbolsToWeaken.empty() ||
!Config.SymbolsToKeepGlobal.empty() || !Config.SectionsToRename.empty() ||
- !Config.SetSectionAlignment.empty() || !Config.SetSectionFlags.empty() ||
- Config.ExtractDWO || Config.KeepFileSymbols || Config.LocalizeHidden ||
- Config.PreserveDates || Config.StripDWO || Config.StripNonAlloc ||
- Config.StripSections || Config.Weaken || Config.DecompressDebugSections ||
+ !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,
diff --git a/llvm/tools/llvm-objcopy/COFF/Reader.cpp b/llvm/tools/llvm-objcopy/COFF/Reader.cpp
index 7be9cce2be3d9..d1beacb3bd67e 100644
--- a/llvm/tools/llvm-objcopy/COFF/Reader.cpp
+++ b/llvm/tools/llvm-objcopy/COFF/Reader.cpp
@@ -45,9 +45,9 @@ Error COFFReader::readExecutableHeaders(Object &Obj) const {
}
for (size_t I = 0; I < Obj.PeHeader.NumberOfRvaAndSize; I++) {
- const data_directory *Dir;
- if (auto EC = COFFObj.getDataDirectory(I, Dir))
- return errorCodeToError(EC);
+ const data_directory *Dir = COFFObj.getDataDirectory(I);
+ if (!Dir)
+ return errorCodeToError(object_error::parse_failed);
Obj.DataDirectories.emplace_back(*Dir);
}
return Error::success();
@@ -57,9 +57,10 @@ Error COFFReader::readSections(Object &Obj) const {
std::vector<Section> Sections;
// Section indexing starts from 1.
for (size_t I = 1, E = COFFObj.getNumberOfSections(); I <= E; I++) {
- const coff_section *Sec;
- if (auto EC = COFFObj.getSection(I, Sec))
- return errorCodeToError(EC);
+ Expected<const coff_section *> SecOrErr = COFFObj.getSection(I);
+ if (!SecOrErr)
+ return SecOrErr.takeError();
+ const coff_section *Sec = *SecOrErr;
Sections.push_back(Section());
Section &S = Sections.back();
S.Header = *Sec;
@@ -99,8 +100,10 @@ Error COFFReader::readSymbols(Object &Obj, bool IsBigObj) const {
else
copySymbol(Sym.Sym,
*reinterpret_cast<const coff_symbol16 *>(SymRef.getRawPtr()));
- if (auto EC = COFFObj.getSymbolName(SymRef, Sym.Name))
- return errorCodeToError(EC);
+ auto NameOrErr = COFFObj.getSymbolName(SymRef);
+ if (!NameOrErr)
+ return NameOrErr.takeError();
+ Sym.Name = *NameOrErr;
ArrayRef<uint8_t> AuxData = COFFObj.getSymbolAuxData(SymRef);
size_t SymSize = IsBigObj ? sizeof(coff_symbol32) : sizeof(coff_symbol16);
diff --git a/llvm/tools/llvm-objcopy/COFF/Writer.cpp b/llvm/tools/llvm-objcopy/COFF/Writer.cpp
index e35e0474a36d5..6b560890a4c16 100644
--- a/llvm/tools/llvm-objcopy/COFF/Writer.cpp
+++ b/llvm/tools/llvm-objcopy/COFF/Writer.cpp
@@ -383,6 +383,16 @@ Error COFFWriter::write(bool IsBigObj) {
return Buf.commit();
}
+Expected<uint32_t> COFFWriter::virtualAddressToFileAddress(uint32_t RVA) {
+ for (const auto &S : Obj.getSections()) {
+ if (RVA >= S.Header.VirtualAddress &&
+ RVA < S.Header.VirtualAddress + S.Header.SizeOfRawData)
+ return S.Header.PointerToRawData + RVA - S.Header.VirtualAddress;
+ }
+ return createStringError(object_error::parse_failed,
+ "debug directory payload not found");
+}
+
// Locate which sections contain the debug directories, iterate over all
// the debug_directory structs in there, and set the PointerToRawData field
// in all of them, according to their new physical location in the file.
@@ -406,10 +416,17 @@ Error COFFWriter::patchDebugDirectory() {
uint8_t *End = Ptr + Dir->Size;
while (Ptr < End) {
debug_directory *Debug = reinterpret_cast<debug_directory *>(Ptr);
- Debug->PointerToRawData =
- S.Header.PointerToRawData + Offset + sizeof(debug_directory);
- Ptr += sizeof(debug_directory) + Debug->SizeOfData;
- Offset += sizeof(debug_directory) + Debug->SizeOfData;
+ if (!Debug->AddressOfRawData)
+ return createStringError(object_error::parse_failed,
+ "debug directory payload outside of "
+ "mapped sections not supported");
+ if (Expected<uint32_t> FilePosOrErr =
+ virtualAddressToFileAddress(Debug->AddressOfRawData))
+ Debug->PointerToRawData = *FilePosOrErr;
+ else
+ return FilePosOrErr.takeError();
+ Ptr += sizeof(debug_directory);
+ Offset += sizeof(debug_directory);
}
// Debug directory found and patched, all done.
return Error::success();
diff --git a/llvm/tools/llvm-objcopy/COFF/Writer.h b/llvm/tools/llvm-objcopy/COFF/Writer.h
index 681a8d5e4a66c..3c0bdcbd5d6f6 100644
--- a/llvm/tools/llvm-objcopy/COFF/Writer.h
+++ b/llvm/tools/llvm-objcopy/COFF/Writer.h
@@ -45,6 +45,7 @@ class COFFWriter {
Error write(bool IsBigObj);
Error patchDebugDirectory();
+ Expected<uint32_t> virtualAddressToFileAddress(uint32_t RVA);
public:
virtual ~COFFWriter() {}
diff --git a/llvm/tools/llvm-objcopy/CopyConfig.cpp b/llvm/tools/llvm-objcopy/CopyConfig.cpp
index 73ed00b5cb2ab..1fde54dd290ae 100644
--- a/llvm/tools/llvm-objcopy/CopyConfig.cpp
+++ b/llvm/tools/llvm-objcopy/CopyConfig.cpp
@@ -146,6 +146,7 @@ static SectionFlag parseSectionRenameFlag(StringRef SectionName) {
.CaseLower("strings", SectionFlag::SecStrings)
.CaseLower("contents", SectionFlag::SecContents)
.CaseLower("share", SectionFlag::SecShare)
+ .CaseLower("exclude", SectionFlag::SecExclude)
.Default(SectionFlag::SecNone);
}
@@ -158,8 +159,8 @@ parseSectionFlagSet(ArrayRef<StringRef> SectionFlags) {
return createStringError(
errc::invalid_argument,
"unrecognized section flag '%s'. Flags supported for GNU "
- "compatibility: alloc, load, noload, readonly, debug, code, data, "
- "rom, share, contents, merge, strings",
+ "compatibility: alloc, load, noload, readonly, exclude, debug, "
+ "code, data, rom, share, contents, merge, strings",
Flag.str().c_str());
ParsedFlags |= ParsedFlag;
}
@@ -272,6 +273,7 @@ static const StringMap<MachineInfo> TargetMap{
// SPARC
{"elf32-sparc", {ELF::EM_SPARC, false, false}},
{"elf32-sparcel", {ELF::EM_SPARC, false, true}},
+ {"elf32-hexagon", {ELF::EM_HEXAGON, false, true}},
};
static Expected<TargetInfo>
@@ -391,9 +393,30 @@ template <class T> static ErrorOr<T> getAsInteger(StringRef Val) {
return Result;
}
+namespace {
+
+enum class ToolType { Objcopy, Strip, InstallNameTool };
+
+} // anonymous namespace
+
static void printHelp(const opt::OptTable &OptTable, raw_ostream &OS,
- StringRef ToolName) {
- OptTable.PrintHelp(OS, (ToolName + " input [output]").str().c_str(),
+ ToolType Tool) {
+ StringRef HelpText, ToolName;
+ switch (Tool) {
+ case ToolType::Objcopy:
+ ToolName = "llvm-objcopy";
+ HelpText = " [options] input [output]";
+ break;
+ case ToolType::Strip:
+ ToolName = "llvm-strip";
+ HelpText = " [options] inputs...";
+ break;
+ case ToolType::InstallNameTool:
+ ToolName = "llvm-install-name-tool";
+ HelpText = " [options] input";
+ break;
+ }
+ 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,
@@ -414,12 +437,12 @@ parseObjcopyOptions(ArrayRef<const char *> ArgsArr,
T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount);
if (InputArgs.size() == 0) {
- printHelp(T, errs(), "llvm-objcopy");
+ printHelp(T, errs(), ToolType::Objcopy);
exit(1);
}
if (InputArgs.hasArg(OBJCOPY_help)) {
- printHelp(T, outs(), "llvm-objcopy");
+ printHelp(T, outs(), ToolType::Objcopy);
exit(0);
}
@@ -665,8 +688,10 @@ parseObjcopyOptions(ArrayRef<const char *> ArgsArr,
Config.KeepFileSymbols = InputArgs.hasArg(OBJCOPY_keep_file_symbols);
Config.DecompressDebugSections =
InputArgs.hasArg(OBJCOPY_decompress_debug_sections);
- if (Config.DiscardMode == DiscardType::All)
+ if (Config.DiscardMode == DiscardType::All) {
Config.StripDebug = true;
+ Config.KeepFileSymbols = true;
+ }
for (auto Arg : InputArgs.filtered(OBJCOPY_localize_symbol))
if (Error E = Config.SymbolsToLocalize.addMatcher(NameOrPattern::create(
Arg->getValue(), SymbolMatchStyle, ErrorCallback)))
@@ -802,13 +827,20 @@ parseInstallNameToolOptions(ArrayRef<const char *> ArgsArr) {
llvm::opt::InputArgList InputArgs =
T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount);
+ if (MissingArgumentCount)
+ return createStringError(
+ errc::invalid_argument,
+ "missing argument to " +
+ StringRef(InputArgs.getArgString(MissingArgumentIndex)) +
+ " option");
+
if (InputArgs.size() == 0) {
- printHelp(T, errs(), "llvm-install-name-tool");
+ printHelp(T, errs(), ToolType::InstallNameTool);
exit(1);
}
if (InputArgs.hasArg(INSTALL_NAME_TOOL_help)) {
- printHelp(T, outs(), "llvm-install-name-tool");
+ printHelp(T, outs(), ToolType::InstallNameTool);
exit(0);
}
@@ -822,6 +854,61 @@ parseInstallNameToolOptions(ArrayRef<const char *> ArgsArr) {
for (auto Arg : InputArgs.filtered(INSTALL_NAME_TOOL_add_rpath))
Config.RPathToAdd.push_back(Arg->getValue());
+ for (auto Arg : InputArgs.filtered(INSTALL_NAME_TOOL_delete_rpath)) {
+ StringRef RPath = Arg->getValue();
+
+ // Cannot add and delete the same rpath at the same time.
+ if (is_contained(Config.RPathToAdd, RPath))
+ return createStringError(
+ errc::invalid_argument,
+ "cannot specify both -add_rpath %s and -delete_rpath %s",
+ RPath.str().c_str(), RPath.str().c_str());
+
+ Config.RPathsToRemove.insert(RPath);
+ }
+
+ for (auto *Arg : InputArgs.filtered(INSTALL_NAME_TOOL_rpath)) {
+ StringRef Old = Arg->getValue(0);
+ StringRef New = Arg->getValue(1);
+
+ auto Match = [=](StringRef RPath) { return RPath == Old || RPath == New; };
+
+ // Cannot specify duplicate -rpath entries
+ auto It1 = find_if(
+ Config.RPathsToUpdate,
+ [&Match](const DenseMap<StringRef, StringRef>::value_type &OldNew) {
+ return Match(OldNew.getFirst()) || Match(OldNew.getSecond());
+ });
+ if (It1 != Config.RPathsToUpdate.end())
+ return createStringError(errc::invalid_argument,
+ "cannot specify both -rpath " + It1->getFirst() +
+ " " + It1->getSecond() + " and -rpath " +
+ Old + " " + New);
+
+ // Cannot specify the same rpath under both -delete_rpath and -rpath
+ auto It2 = find_if(Config.RPathsToRemove, Match);
+ if (It2 != Config.RPathsToRemove.end())
+ return createStringError(errc::invalid_argument,
+ "cannot specify both -delete_rpath " + *It2 +
+ " and -rpath " + Old + " " + New);
+
+ // Cannot specify the same rpath under both -add_rpath and -rpath
+ auto It3 = find_if(Config.RPathToAdd, Match);
+ if (It3 != Config.RPathToAdd.end())
+ return createStringError(errc::invalid_argument,
+ "cannot specify both -add_rpath " + *It3 +
+ " and -rpath " + Old + " " + New);
+
+ Config.RPathsToUpdate.insert({Old, New});
+ }
+
+ if (auto *Arg = InputArgs.getLastArg(INSTALL_NAME_TOOL_id))
+ Config.SharedLibId = Arg->getValue();
+
+ for (auto *Arg : InputArgs.filtered(INSTALL_NAME_TOOL_change)) {
+ Config.InstallNamesToUpdate.insert({Arg->getValue(0), Arg->getValue(1)});
+ }
+
SmallVector<StringRef, 2> Positional;
for (auto Arg : InputArgs.filtered(INSTALL_NAME_TOOL_UNKNOWN))
return createStringError(errc::invalid_argument, "unknown argument '%s'",
@@ -853,12 +940,12 @@ parseStripOptions(ArrayRef<const char *> ArgsArr,
T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount);
if (InputArgs.size() == 0) {
- printHelp(T, errs(), "llvm-strip");
+ printHelp(T, errs(), ToolType::Strip);
exit(1);
}
if (InputArgs.hasArg(STRIP_help)) {
- printHelp(T, outs(), "llvm-strip");
+ printHelp(T, outs(), ToolType::Strip);
exit(0);
}
@@ -908,6 +995,7 @@ parseStripOptions(ArrayRef<const char *> ArgsArr,
if (auto Arg = InputArgs.getLastArg(STRIP_strip_all, STRIP_no_strip_all))
Config.StripAll = Arg->getOption().getID() == STRIP_strip_all;
Config.StripAllGNU = InputArgs.hasArg(STRIP_strip_all_gnu);
+ Config.StripSwiftSymbols = InputArgs.hasArg(STRIP_strip_swift_symbols);
Config.OnlyKeepDebug = InputArgs.hasArg(STRIP_only_keep_debug);
Config.KeepFileSymbols = InputArgs.hasArg(STRIP_keep_file_symbols);
@@ -936,8 +1024,10 @@ parseStripOptions(ArrayRef<const char *> ArgsArr,
!Config.StripAllGNU && Config.SymbolsToRemove.empty())
Config.StripAll = true;
- if (Config.DiscardMode == DiscardType::All)
+ if (Config.DiscardMode == DiscardType::All) {
Config.StripDebug = true;
+ Config.KeepFileSymbols = true;
+ }
Config.DeterministicArchives =
InputArgs.hasFlag(STRIP_enable_deterministic_archives,
diff --git a/llvm/tools/llvm-objcopy/CopyConfig.h b/llvm/tools/llvm-objcopy/CopyConfig.h
index c262934b4a419..1341dd674c7b7 100644
--- a/llvm/tools/llvm-objcopy/CopyConfig.h
+++ b/llvm/tools/llvm-objcopy/CopyConfig.h
@@ -12,6 +12,7 @@
#include "ELF/ELFConfig.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/BitmaskEnum.h"
+#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringMap.h"
@@ -69,7 +70,8 @@ enum SectionFlag {
SecStrings = 1 << 9,
SecContents = 1 << 10,
SecShare = 1 << 11,
- LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ SecShare)
+ SecExclude = 1 << 12,
+ LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/SecExclude)
};
struct SectionRename {
@@ -176,6 +178,12 @@ struct CopyConfig {
std::vector<StringRef> DumpSection;
std::vector<StringRef> SymbolsToAdd;
std::vector<StringRef> RPathToAdd;
+ DenseMap<StringRef, StringRef> RPathsToUpdate;
+ DenseMap<StringRef, StringRef> InstallNamesToUpdate;
+ DenseSet<StringRef> RPathsToRemove;
+
+ // install-name-tool's id option
+ Optional<StringRef> SharedLibId;
// Section matchers
NameMatcher KeepSection;
@@ -218,6 +226,7 @@ struct CopyConfig {
bool StripDebug = false;
bool StripNonAlloc = false;
bool StripSections = false;
+ bool StripSwiftSymbols = false;
bool StripUnneeded = false;
bool Weaken = false;
bool DecompressDebugSections = false;
diff --git a/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp b/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp
index a0cfd9a5ff868..66953f9ef0d56 100644
--- a/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp
+++ b/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp
@@ -11,7 +11,6 @@
#include "CopyConfig.h"
#include "Object.h"
#include "llvm-objcopy.h"
-
#include "llvm/ADT/BitmaskEnum.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/Optional.h"
@@ -32,6 +31,7 @@
#include "llvm/Support/Error.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/ErrorOr.h"
+#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Memory.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
@@ -83,6 +83,8 @@ uint64_t getNewShfFlags(SectionFlag AllFlags) {
NewFlags |= ELF::SHF_MERGE;
if (AllFlags & SectionFlag::SecStrings)
NewFlags |= ELF::SHF_STRINGS;
+ if (AllFlags & SectionFlag::SecExclude)
+ NewFlags |= ELF::SHF_EXCLUDE;
return NewFlags;
}
@@ -90,10 +92,11 @@ static uint64_t getSectionFlagsPreserveMask(uint64_t OldFlags,
uint64_t NewFlags) {
// Preserve some flags which should not be dropped when setting flags.
// Also, preserve anything OS/processor dependant.
- const uint64_t PreserveMask = ELF::SHF_COMPRESSED | ELF::SHF_EXCLUDE |
- ELF::SHF_GROUP | ELF::SHF_LINK_ORDER |
- ELF::SHF_MASKOS | ELF::SHF_MASKPROC |
- ELF::SHF_TLS | ELF::SHF_INFO_LINK;
+ const uint64_t PreserveMask =
+ (ELF::SHF_COMPRESSED | ELF::SHF_GROUP | ELF::SHF_LINK_ORDER |
+ ELF::SHF_MASKOS | ELF::SHF_MASKPROC | ELF::SHF_TLS |
+ ELF::SHF_INFO_LINK) &
+ ~ELF::SHF_EXCLUDE;
return (OldFlags & PreserveMask) | (NewFlags & ~PreserveMask);
}
@@ -267,7 +270,7 @@ static Error splitDWOToFile(const CopyConfig &Config, const Reader &Reader,
auto OnlyKeepDWOPred = [&DWOFile](const SectionBase &Sec) {
return onlyKeepDWOPred(*DWOFile, Sec);
};
- if (Error E = DWOFile->removeSections(Config.AllowBrokenLinks,
+ if (Error E = DWOFile->removeSections(Config.AllowBrokenLinks,
OnlyKeepDWOPred))
return E;
if (Config.OutputArch) {
@@ -285,7 +288,7 @@ static Error dumpSectionToFile(StringRef SecName, StringRef Filename,
Object &Obj) {
for (auto &Sec : Obj.sections()) {
if (Sec.Name == SecName) {
- if (Sec.OriginalData.empty())
+ if (Sec.Type == SHT_NOBITS)
return createStringError(object_error::parse_failed,
"cannot dump section '%s': it has no contents",
SecName.str().c_str());
@@ -387,7 +390,7 @@ static Error updateAndRemoveSymbols(const CopyConfig &Config, Object &Obj) {
const auto I = Config.SymbolsToRename.find(Sym.Name);
if (I != Config.SymbolsToRename.end())
- Sym.Name = I->getValue();
+ Sym.Name = std::string(I->getValue());
if (!Config.SymbolsPrefix.empty() && Sym.Type != STT_SECTION)
Sym.Name = (Config.SymbolsPrefix + Sym.Name).str();
@@ -417,6 +420,9 @@ static Error updateAndRemoveSymbols(const CopyConfig &Config, Object &Obj) {
if (Config.StripAll || Config.StripAllGNU)
return true;
+ if (Config.StripDebug && Sym.Type == STT_FILE)
+ return true;
+
if (Config.SymbolsToRemove.matches(Sym.Name))
return true;
@@ -572,11 +578,11 @@ static Error replaceAndRemoveSections(const CopyConfig &Config, Object &Obj) {
}
if (Config.CompressionType != DebugCompressionType::None)
- replaceDebugSections(Obj, RemovePred, isCompressable,
+ replaceDebugSections(Obj, RemovePred, isCompressable,
[&Config, &Obj](const SectionBase *S) {
return &Obj.addSection<CompressedSection>(
*S, Config.CompressionType);
- });
+ });
else if (Config.DecompressDebugSections)
replaceDebugSections(
Obj, RemovePred,
@@ -598,7 +604,9 @@ static Error replaceAndRemoveSections(const CopyConfig &Config, Object &Obj) {
// 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))
@@ -609,6 +617,15 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj,
Obj.OSABI = Config.OutputArch.getValue().OSABI;
}
+ // Dump sections before add/remove for compatibility with GNU objcopy.
+ for (StringRef Flag : Config.DumpSection) {
+ StringRef SectionName;
+ StringRef FileName;
+ std::tie(SectionName, FileName) = Flag.split('=');
+ if (Error E = dumpSectionToFile(SectionName, FileName, Obj))
+ return E;
+ }
+
// It is important to remove the sections first. For example, we want to
// remove the relocation sections before removing the symbols. That allows
// us to avoid reporting the inappropriate errors about removing symbols
@@ -624,7 +641,7 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj,
const auto Iter = Config.SectionsToRename.find(Sec.Name);
if (Iter != Config.SectionsToRename.end()) {
const SectionRename &SR = Iter->second;
- Sec.Name = SR.NewName;
+ Sec.Name = std::string(SR.NewName);
if (SR.NewFlags.hasValue())
setSectionFlagsAndType(Sec, SR.NewFlags.getValue());
}
@@ -717,18 +734,16 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj,
NewSection.Type = SHT_NOTE;
}
- for (const auto &Flag : Config.DumpSection) {
- std::pair<StringRef, StringRef> SecPair = Flag.split("=");
- StringRef SecName = SecPair.first;
- StringRef File = SecPair.second;
- if (Error E = dumpSectionToFile(SecName, File, Obj))
- return E;
- }
-
if (!Config.AddGnuDebugLink.empty())
Obj.addSection<GnuDebugLinkSection>(Config.AddGnuDebugLink,
Config.GnuDebugLinkCRC32);
+ // 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()) {
+ Obj.addNewSymbolTable();
+ }
+
for (const NewSymbolInfo &SI : Config.ELF->SymbolsToAdd) {
SectionBase *Sec = Obj.findSection(SI.SectionName);
uint64_t Value = Sec ? Sec->Addr + SI.Value : SI.Value;
diff --git a/llvm/tools/llvm-objcopy/ELF/Object.cpp b/llvm/tools/llvm-objcopy/ELF/Object.cpp
index ad53c75663ec7..e15fb24f4c425 100644
--- a/llvm/tools/llvm-objcopy/ELF/Object.cpp
+++ b/llvm/tools/llvm-objcopy/ELF/Object.cpp
@@ -65,6 +65,7 @@ void SectionBase::finalize() {}
void SectionBase::markSymbols() {}
void SectionBase::replaceSectionReferences(
const DenseMap<SectionBase *, SectionBase *> &) {}
+void SectionBase::onRemove() {}
template <class ELFT> void ELFWriter<ELFT>::writeShdr(const SectionBase &Sec) {
uint8_t *B = Buf.getBufferStart() + Sec.HeaderOffset;
@@ -111,7 +112,9 @@ void ELFSectionSizer<ELFT>::visit(RelocationSection &Sec) {
template <class ELFT>
void ELFSectionSizer<ELFT>::visit(GnuDebugLinkSection &Sec) {}
-template <class ELFT> void ELFSectionSizer<ELFT>::visit(GroupSection &Sec) {}
+template <class ELFT> void ELFSectionSizer<ELFT>::visit(GroupSection &Sec) {
+ Sec.Size = sizeof(Elf_Word) + Sec.GroupMembers.size() * sizeof(Elf_Word);
+}
template <class ELFT>
void ELFSectionSizer<ELFT>::visit(SectionIndexSection &Sec) {}
@@ -605,6 +608,7 @@ static bool isValidReservedSectionIndex(uint16_t Index, uint16_t Machine) {
if (Machine == EM_HEXAGON) {
switch (Index) {
case SHN_HEXAGON_SCOMMON:
+ case SHN_HEXAGON_SCOMMON_1:
case SHN_HEXAGON_SCOMMON_2:
case SHN_HEXAGON_SCOMMON_4:
case SHN_HEXAGON_SCOMMON_8:
@@ -741,7 +745,7 @@ void SymbolTableSection::prepareForLayout() {
// Reserve proper amount of space in section index table, so we can
// layout sections correctly. We will fill the table with correct
// indexes later in fillShdnxTable.
- if (SectionIndexTable)
+ if (SectionIndexTable)
SectionIndexTable->reserve(Symbols.size());
// Add all of our strings to SymbolNames so that SymbolNames has the right
@@ -963,8 +967,24 @@ Error Section::removeSectionReferences(
}
void GroupSection::finalize() {
- this->Info = Sym->Index;
- this->Link = SymTab->Index;
+ this->Info = Sym ? Sym->Index : 0;
+ this->Link = SymTab ? SymTab->Index : 0;
+}
+
+Error GroupSection::removeSectionReferences(
+ bool AllowBrokenLinks, function_ref<bool(const SectionBase *)> ToRemove) {
+ if (ToRemove(SymTab)) {
+ if (!AllowBrokenLinks)
+ return createStringError(
+ llvm::errc::invalid_argument,
+ "section '.symtab' cannot be removed because it is "
+ "referenced by the group section '%s'",
+ this->Name.data());
+ SymTab = nullptr;
+ Sym = nullptr;
+ }
+ llvm::erase_if(GroupMembers, ToRemove);
+ return Error::success();
}
Error GroupSection::removeSymbols(function_ref<bool(const Symbol &)> ToRemove) {
@@ -988,6 +1008,13 @@ void GroupSection::replaceSectionReferences(
Sec = To;
}
+void GroupSection::onRemove() {
+ // As the header section of the group is removed, drop the Group flag in its
+ // former members.
+ for (SectionBase *Sec : GroupMembers)
+ Sec->Flags &= ~SHF_GROUP;
+}
+
void Section::initialize(SectionTableRef SecTable) {
if (Link == ELF::SHN_UNDEF)
return;
@@ -1101,14 +1128,6 @@ static bool compareSegmentsByOffset(const Segment *A, const Segment *B) {
return A->Index < B->Index;
}
-static bool compareSegmentsByPAddr(const Segment *A, const Segment *B) {
- if (A->PAddr < B->PAddr)
- return true;
- if (A->PAddr > B->PAddr)
- return false;
- return A->Index < B->Index;
-}
-
void BasicELFBuilder::initFileHeader() {
Obj->Flags = 0x0;
Obj->Type = ET_REL;
@@ -1241,7 +1260,7 @@ std::unique_ptr<Object> IHexELFBuilder::build() {
template <class ELFT> void ELFBuilder<ELFT>::setParentSegment(Segment &Child) {
for (Segment &Parent : Obj.segments()) {
// Every segment will overlap with itself but we don't want a segment to
- // be it's own parent so we avoid that situation.
+ // be its own parent so we avoid that situation.
if (&Child != &Parent && segmentOverlapsSegment(Child, Parent)) {
// We want a canonical "most parental" segment but this requires
// inspecting the ParentSegment.
@@ -1330,18 +1349,20 @@ void ELFBuilder<ELFT>::initGroupSection(GroupSection *GroupSec) {
error("invalid alignment " + Twine(GroupSec->Align) + " of group section '" +
GroupSec->Name + "'");
SectionTableRef SecTable = Obj.sections();
- auto SymTab = SecTable.template getSectionOfType<SymbolTableSection>(
- GroupSec->Link,
- "link field value '" + Twine(GroupSec->Link) + "' in section '" +
- GroupSec->Name + "' is invalid",
- "link field value '" + Twine(GroupSec->Link) + "' in section '" +
- GroupSec->Name + "' is not a symbol table");
- Symbol *Sym = SymTab->getSymbolByIndex(GroupSec->Info);
- if (!Sym)
- error("info field value '" + Twine(GroupSec->Info) + "' in section '" +
- GroupSec->Name + "' is not a valid symbol index");
- GroupSec->setSymTab(SymTab);
- GroupSec->setSymbol(Sym);
+ if (GroupSec->Link != SHN_UNDEF) {
+ auto SymTab = SecTable.template getSectionOfType<SymbolTableSection>(
+ GroupSec->Link,
+ "link field value '" + Twine(GroupSec->Link) + "' in section '" +
+ GroupSec->Name + "' is invalid",
+ "link field value '" + Twine(GroupSec->Link) + "' in section '" +
+ GroupSec->Name + "' is not a symbol table");
+ Symbol *Sym = SymTab->getSymbolByIndex(GroupSec->Info);
+ if (!Sym)
+ error("info field value '" + Twine(GroupSec->Info) + "' in section '" +
+ GroupSec->Name + "' is not a valid symbol index");
+ GroupSec->setSymTab(SymTab);
+ GroupSec->setSymbol(Sym);
+ }
if (GroupSec->Contents.size() % sizeof(ELF::Elf32_Word) ||
GroupSec->Contents.empty())
error("the content of the section " + GroupSec->Name + " is malformed");
@@ -1520,7 +1541,7 @@ template <class ELFT> void ELFBuilder<ELFT>::readSectionHeaders() {
continue;
}
auto &Sec = makeSection(Shdr);
- Sec.Name = unwrapOrError(ElfFile.getSectionName(&Shdr));
+ Sec.Name = std::string(unwrapOrError(ElfFile.getSectionName(&Shdr)));
Sec.Type = Sec.OriginalType = Shdr.sh_type;
Sec.Flags = Sec.OriginalFlags = Shdr.sh_flags;
Sec.Addr = Shdr.sh_addr;
@@ -1567,27 +1588,7 @@ template <class ELFT> void ELFBuilder<ELFT>::readSections(bool EnsureSymtab) {
Obj.SymbolTable->initialize(Obj.sections());
initSymbolTable(Obj.SymbolTable);
} else if (EnsureSymtab) {
- // Reuse an existing SHT_STRTAB section if it exists.
- StringTableSection *StrTab = nullptr;
- for (auto &Sec : Obj.sections()) {
- if (Sec.Type == ELF::SHT_STRTAB && !(Sec.Flags & SHF_ALLOC)) {
- StrTab = static_cast<StringTableSection *>(&Sec);
-
- // Prefer a string table that is not the section header string table, if
- // such a table exists.
- if (Obj.SectionNames != &Sec)
- break;
- }
- }
- if (!StrTab)
- StrTab = &Obj.addSection<StringTableSection>();
-
- SymbolTableSection &SymTab = Obj.addSection<SymbolTableSection>();
- SymTab.Name = ".symtab";
- SymTab.Link = StrTab->Index;
- SymTab.initialize(Obj.sections());
- SymTab.addSymbol("", 0, 0, nullptr, 0, 0, 0, 0);
- Obj.SymbolTable = &SymTab;
+ Obj.addNewSymbolTable();
}
// Now that all sections and symbols have been added we can add
@@ -1846,6 +1847,7 @@ Error Object::removeSections(bool AllowBrokenLinks,
for (auto &RemoveSec : make_range(Iter, std::end(Sections))) {
for (auto &Segment : Segments)
Segment->removeSection(RemoveSec.get());
+ RemoveSec->onRemove();
RemoveSections.insert(RemoveSec.get());
}
@@ -1878,6 +1880,33 @@ Error Object::removeSymbols(function_ref<bool(const Symbol &)> ToRemove) {
return Error::success();
}
+void Object::addNewSymbolTable() {
+ assert(!SymbolTable && "Object must not has a SymbolTable.");
+
+ // Reuse an existing SHT_STRTAB section if it exists.
+ StringTableSection *StrTab = nullptr;
+ for (SectionBase &Sec : sections()) {
+ if (Sec.Type == ELF::SHT_STRTAB && !(Sec.Flags & SHF_ALLOC)) {
+ StrTab = static_cast<StringTableSection *>(&Sec);
+
+ // Prefer a string table that is not the section header string table, if
+ // such a table exists.
+ if (SectionNames != &Sec)
+ break;
+ }
+ }
+ if (!StrTab)
+ StrTab = &addSection<StringTableSection>();
+
+ SymbolTableSection &SymTab = addSection<SymbolTableSection>();
+ SymTab.Name = ".symtab";
+ SymTab.Link = StrTab->Index;
+ SymTab.initialize(sections());
+ SymTab.addSymbol("", 0, 0, nullptr, 0, 0, 0, 0);
+
+ SymbolTable = &SymTab;
+}
+
void Object::sortSections() {
// Use stable_sort to maintain the original ordering as closely as possible.
llvm::stable_sort(Sections, [](const SecPtr &A, const SecPtr &B) {
@@ -1902,8 +1931,7 @@ static void orderSegments(std::vector<Segment *> &Segments) {
// returns an Offset one past the end of the last segment.
static uint64_t layoutSegments(std::vector<Segment *> &Segments,
uint64_t Offset) {
- assert(std::is_sorted(std::begin(Segments), std::end(Segments),
- compareSegmentsByOffset));
+ assert(llvm::is_sorted(Segments, compareSegmentsByOffset));
// The only way a segment should move is if a section was between two
// segments and that section was removed. If that section isn't in a segment
// then it's acceptable, but not ideal, to simply move it to after the
@@ -2225,56 +2253,29 @@ Error BinaryWriter::write() {
}
Error BinaryWriter::finalize() {
- // We need a temporary list of segments that has a special order to it
- // so that we know that anytime ->ParentSegment is set that segment has
- // already had it's offset properly set. We only want to consider the segments
- // that will affect layout of allocated sections so we only add those.
- std::vector<Segment *> OrderedSegments;
- for (const SectionBase &Sec : Obj.allocSections())
- if (Sec.ParentSegment != nullptr)
- OrderedSegments.push_back(Sec.ParentSegment);
-
- // For binary output, we're going to use physical addresses instead of
- // virtual addresses, since a binary output is used for cases like ROM
- // loading and physical addresses are intended for ROM loading.
- // However, if no segment has a physical address, we'll fallback to using
- // virtual addresses for all.
- if (all_of(OrderedSegments,
- [](const Segment *Seg) { return Seg->PAddr == 0; }))
- for (Segment *Seg : OrderedSegments)
- Seg->PAddr = Seg->VAddr;
-
- llvm::stable_sort(OrderedSegments, compareSegmentsByPAddr);
-
- // Because we add a ParentSegment for each section we might have duplicate
- // segments in OrderedSegments. If there were duplicates then layoutSegments
- // would do very strange things.
- auto End =
- std::unique(std::begin(OrderedSegments), std::end(OrderedSegments));
- OrderedSegments.erase(End, std::end(OrderedSegments));
-
// Compute the section LMA based on its sh_offset and the containing segment's
- // p_offset and p_paddr. Also compute the minimum LMA of all sections as
- // MinAddr. In the output, the contents between address 0 and MinAddr will be
- // skipped.
+ // p_offset and p_paddr. Also compute the minimum LMA of all non-empty
+ // sections as MinAddr. In the output, the contents between address 0 and
+ // MinAddr will be skipped.
uint64_t MinAddr = UINT64_MAX;
for (SectionBase &Sec : Obj.allocSections()) {
if (Sec.ParentSegment != nullptr)
Sec.Addr =
Sec.Offset - Sec.ParentSegment->Offset + Sec.ParentSegment->PAddr;
- MinAddr = std::min(MinAddr, Sec.Addr);
+ if (Sec.Size > 0)
+ MinAddr = std::min(MinAddr, Sec.Addr);
}
// Now that every section has been laid out we just need to compute the total
// file size. This might not be the same as the offset returned by
// layoutSections, because we want to truncate the last segment to the end of
- // its last section, to match GNU objcopy's behaviour.
+ // its last non-empty section, to match GNU objcopy's behaviour.
TotalSize = 0;
- for (SectionBase &Sec : Obj.allocSections()) {
- Sec.Offset = Sec.Addr - MinAddr;
- if (Sec.Type != SHT_NOBITS)
+ for (SectionBase &Sec : Obj.allocSections())
+ if (Sec.Type != SHT_NOBITS && Sec.Size > 0) {
+ Sec.Offset = Sec.Addr - MinAddr;
TotalSize = std::max(TotalSize, Sec.Offset + Sec.Size);
- }
+ }
if (Error E = Buf.allocate(TotalSize))
return E;
diff --git a/llvm/tools/llvm-objcopy/ELF/Object.h b/llvm/tools/llvm-objcopy/ELF/Object.h
index 97702a66bc479..ed89e916b8385 100644
--- a/llvm/tools/llvm-objcopy/ELF/Object.h
+++ b/llvm/tools/llvm-objcopy/ELF/Object.h
@@ -424,6 +424,8 @@ public:
virtual void markSymbols();
virtual void
replaceSectionReferences(const DenseMap<SectionBase *, SectionBase *> &);
+ // Notify the section that it is subject to removal.
+ virtual void onRemove();
};
class Segment {
@@ -799,10 +801,14 @@ public:
void accept(SectionVisitor &) const override;
void accept(MutableSectionVisitor &Visitor) override;
void finalize() override;
+ Error removeSectionReferences(
+ bool AllowBrokenLinks,
+ function_ref<bool(const SectionBase *)> ToRemove) override;
Error removeSymbols(function_ref<bool(const Symbol &)> ToRemove) override;
void markSymbols() override;
void replaceSectionReferences(
const DenseMap<SectionBase *, SectionBase *> &FromTo) override;
+ void onRemove() override;
static bool classof(const SectionBase *S) {
return S->OriginalType == ELF::SHT_GROUP;
@@ -1066,6 +1072,7 @@ public:
Ptr->Index = Sections.size();
return *Ptr;
}
+ void addNewSymbolTable();
Segment &addSegment(ArrayRef<uint8_t> Data) {
Segments.emplace_back(std::make_unique<Segment>(Data));
return *Segments.back();
diff --git a/llvm/tools/llvm-objcopy/InstallNameToolOpts.td b/llvm/tools/llvm-objcopy/InstallNameToolOpts.td
index 35047a57994cb..04ffe62c42fca 100644
--- a/llvm/tools/llvm-objcopy/InstallNameToolOpts.td
+++ b/llvm/tools/llvm-objcopy/InstallNameToolOpts.td
@@ -18,5 +18,17 @@ def h : Flag<["-"], "h">, Alias<help>;
def add_rpath : Option<["-", "--"], "add_rpath", KIND_SEPARATE>,
HelpText<"Add new rpath">;
+def delete_rpath: Option<["-", "--"], "delete_rpath", KIND_SEPARATE>,
+ HelpText<"Delete specified rpath">;
+
+def rpath: MultiArg<["-", "--"], "rpath", 2>,
+ HelpText<"Change rpath path name">;
+
+def id : Option<["-","--"], "id", KIND_SEPARATE>,
+ HelpText<"Change dynamic shared library id">;
+
+def change: MultiArg<["-", "--"], "change", 2>,
+ HelpText<"Change dependent shared library install name">;
+
def version : Flag<["--"], "version">,
HelpText<"Print the version and exit.">;
diff --git a/llvm/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp b/llvm/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp
index 380f2e989fe4c..256c830a44a46 100644
--- a/llvm/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp
+++ b/llvm/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp
@@ -17,7 +17,7 @@ namespace macho {
uint32_t MachOLayoutBuilder::computeSizeOfCmds() const {
uint32_t Size = 0;
- for (const auto &LC : O.LoadCommands) {
+ for (const LoadCommand &LC : O.LoadCommands) {
const MachO::macho_load_command &MLC = LC.MachOLoadCommand;
auto cmd = MLC.load_command_data.cmd;
switch (cmd) {
@@ -61,15 +61,16 @@ void MachOLayoutBuilder::updateDySymTab(MachO::macho_load_command &MLC) {
assert(MLC.load_command_data.cmd == MachO::LC_DYSYMTAB);
// Make sure that nlist entries in the symbol table are sorted by the those
// types. The order is: local < defined external < undefined external.
- assert(std::is_sorted(O.SymTable.Symbols.begin(), O.SymTable.Symbols.end(),
- [](const std::unique_ptr<SymbolEntry> &A,
- const std::unique_ptr<SymbolEntry> &B) {
- bool AL = A->isLocalSymbol(), BL = B->isLocalSymbol();
- if (AL != BL)
- return AL;
- return !AL && !A->isUndefinedSymbol() &&
- B->isUndefinedSymbol();
- }) &&
+ assert(llvm::is_sorted(O.SymTable.Symbols,
+ [](const std::unique_ptr<SymbolEntry> &A,
+ const std::unique_ptr<SymbolEntry> &B) {
+ bool AL = A->isLocalSymbol(),
+ BL = B->isLocalSymbol();
+ if (AL != BL)
+ return AL;
+ return !AL && !A->isUndefinedSymbol() &&
+ B->isUndefinedSymbol();
+ }) &&
"Symbols are not sorted by their types.");
uint32_t NumLocalSymbols = 0;
@@ -107,7 +108,7 @@ uint64_t MachOLayoutBuilder::layoutSegments() {
const bool IsObjectFile =
O.Header.FileType == MachO::HeaderFileType::MH_OBJECT;
uint64_t Offset = IsObjectFile ? (HeaderSize + O.Header.SizeOfCmds) : 0;
- for (auto &LC : O.LoadCommands) {
+ for (LoadCommand &LC : O.LoadCommands) {
auto &MLC = LC.MachOLoadCommand;
StringRef Segname;
uint64_t SegmentVmAddr;
@@ -142,30 +143,30 @@ uint64_t MachOLayoutBuilder::layoutSegments() {
uint64_t SegOffset = Offset;
uint64_t SegFileSize = 0;
uint64_t VMSize = 0;
- for (auto &Sec : LC.Sections) {
+ for (std::unique_ptr<Section> &Sec : LC.Sections) {
+ assert(SegmentVmAddr <= Sec->Addr &&
+ "Section's address cannot be smaller than Segment's one");
+ uint32_t SectOffset = Sec->Addr - SegmentVmAddr;
if (IsObjectFile) {
- if (Sec.isVirtualSection()) {
- Sec.Offset = 0;
+ if (Sec->isVirtualSection()) {
+ Sec->Offset = 0;
} else {
uint64_t PaddingSize =
- offsetToAlignment(SegFileSize, Align(1ull << Sec.Align));
- Sec.Offset = SegOffset + SegFileSize + PaddingSize;
- Sec.Size = Sec.Content.size();
- SegFileSize += PaddingSize + Sec.Size;
+ offsetToAlignment(SegFileSize, Align(1ull << Sec->Align));
+ Sec->Offset = SegOffset + SegFileSize + PaddingSize;
+ Sec->Size = Sec->Content.size();
+ SegFileSize += PaddingSize + Sec->Size;
}
- VMSize = std::max(VMSize, Sec.Addr + Sec.Size);
} else {
- if (Sec.isVirtualSection()) {
- Sec.Offset = 0;
- VMSize += Sec.Size;
+ if (Sec->isVirtualSection()) {
+ Sec->Offset = 0;
} else {
- uint32_t SectOffset = Sec.Addr - SegmentVmAddr;
- Sec.Offset = SegOffset + SectOffset;
- Sec.Size = Sec.Content.size();
- SegFileSize = std::max(SegFileSize, SectOffset + Sec.Size);
- VMSize = std::max(VMSize, SegFileSize);
+ Sec->Offset = SegOffset + SectOffset;
+ Sec->Size = Sec->Content.size();
+ SegFileSize = std::max(SegFileSize, SectOffset + Sec->Size);
}
}
+ VMSize = std::max(VMSize, SectOffset + Sec->Size);
}
if (IsObjectFile) {
@@ -204,21 +205,33 @@ uint64_t MachOLayoutBuilder::layoutSegments() {
}
uint64_t MachOLayoutBuilder::layoutRelocations(uint64_t Offset) {
- for (auto &LC : O.LoadCommands)
- for (auto &Sec : LC.Sections) {
- Sec.RelOff = Sec.Relocations.empty() ? 0 : Offset;
- Sec.NReloc = Sec.Relocations.size();
- Offset += sizeof(MachO::any_relocation_info) * Sec.NReloc;
+ for (LoadCommand &LC : O.LoadCommands)
+ for (std::unique_ptr<Section> &Sec : LC.Sections) {
+ Sec->RelOff = Sec->Relocations.empty() ? 0 : Offset;
+ Sec->NReloc = Sec->Relocations.size();
+ Offset += sizeof(MachO::any_relocation_info) * Sec->NReloc;
}
return Offset;
}
Error MachOLayoutBuilder::layoutTail(uint64_t Offset) {
+ // If we are building the layout of an executable or dynamic library
+ // which does not have any segments other than __LINKEDIT,
+ // the Offset can be equal to zero by this time. It happens because of the
+ // convention that in such cases the file offsets specified by LC_SEGMENT
+ // start with zero (unlike the case of a relocatable object file).
+ const uint64_t HeaderSize =
+ Is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header);
+ assert((!(O.Header.FileType == MachO::HeaderFileType::MH_OBJECT) ||
+ Offset >= HeaderSize + O.Header.SizeOfCmds) &&
+ "Incorrect tail offset");
+ Offset = std::max(Offset, HeaderSize + O.Header.SizeOfCmds);
+
// The order of LINKEDIT elements is as follows:
// rebase info, binding info, weak binding info, lazy binding info, export
// trie, data-in-code, symbol table, indirect symbol table, symbol table
- // strings.
+ // strings, code signature.
uint64_t NListSize = Is64Bit ? sizeof(MachO::nlist_64) : sizeof(MachO::nlist);
uint64_t StartOfLinkEdit = Offset;
uint64_t StartOfRebaseInfo = StartOfLinkEdit;
@@ -237,8 +250,10 @@ Error MachOLayoutBuilder::layoutTail(uint64_t Offset) {
uint64_t StartOfSymbolStrings =
StartOfIndirectSymbols +
sizeof(uint32_t) * O.IndirectSymTable.Symbols.size();
+ uint64_t StartOfCodeSignature =
+ StartOfSymbolStrings + StrTableBuilder.getSize();
uint64_t LinkEditSize =
- (StartOfSymbolStrings + StrTableBuilder.getSize()) - StartOfLinkEdit;
+ (StartOfCodeSignature + O.CodeSignature.Data.size()) - StartOfLinkEdit;
// Now we have determined the layout of the contents of the __LINKEDIT
// segment. Update its load command.
@@ -260,10 +275,14 @@ Error MachOLayoutBuilder::layoutTail(uint64_t Offset) {
}
}
- for (auto &LC : O.LoadCommands) {
+ for (LoadCommand &LC : O.LoadCommands) {
auto &MLC = LC.MachOLoadCommand;
auto cmd = MLC.load_command_data.cmd;
switch (cmd) {
+ case MachO::LC_CODE_SIGNATURE:
+ MLC.linkedit_data_command_data.dataoff = StartOfCodeSignature;
+ MLC.linkedit_data_command_data.datasize = O.CodeSignature.Data.size();
+ break;
case MachO::LC_SYMTAB:
MLC.symtab_command_data.symoff = StartOfSymbols;
MLC.symtab_command_data.nsyms = O.SymTable.Symbols.size();
@@ -314,6 +333,19 @@ Error MachOLayoutBuilder::layoutTail(uint64_t Offset) {
O.Exports.Trie.empty() ? 0 : StartOfExportTrie;
MLC.dyld_info_command_data.export_size = O.Exports.Trie.size();
break;
+ // Note that LC_ENCRYPTION_INFO.cryptoff despite its name and the comment in
+ // <mach-o/loader.h> is not an offset in the binary file, instead, it is a
+ // relative virtual address. At the moment modification of the __TEXT
+ // segment of executables isn't supported anyway (e.g. data in code entries
+ // are not recalculated). Moreover, in general
+ // LC_ENCRYPT_INFO/LC_ENCRYPTION_INFO_64 are nontrivial to update because
+ // without making additional assumptions (e.g. that the entire __TEXT
+ // segment should be encrypted) we do not know how to recalculate the
+ // boundaries of the encrypted part. For now just copy over these load
+ // commands until we encounter a real world usecase where
+ // LC_ENCRYPT_INFO/LC_ENCRYPTION_INFO_64 need to be adjusted.
+ case MachO::LC_ENCRYPTION_INFO:
+ case MachO::LC_ENCRYPTION_INFO_64:
case MachO::LC_LOAD_DYLINKER:
case MachO::LC_MAIN:
case MachO::LC_RPATH:
@@ -326,6 +358,7 @@ Error MachOLayoutBuilder::layoutTail(uint64_t Offset) {
case MachO::LC_BUILD_VERSION:
case MachO::LC_ID_DYLIB:
case MachO::LC_LOAD_DYLIB:
+ case MachO::LC_LOAD_WEAK_DYLIB:
case MachO::LC_UUID:
case MachO::LC_SOURCE_VERSION:
// Nothing to update.
diff --git a/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp b/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp
index 4578d0bb75d4f..5ca5b133572b4 100644
--- a/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp
+++ b/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp
@@ -10,6 +10,7 @@
#include "../CopyConfig.h"
#include "MachOReader.h"
#include "MachOWriter.h"
+#include "llvm/ADT/DenseSet.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/Error.h"
@@ -18,21 +19,44 @@ namespace objcopy {
namespace macho {
using namespace object;
-using SectionPred = std::function<bool(const Section &Sec)>;
+using SectionPred = std::function<bool(const std::unique_ptr<Section> &Sec)>;
+using LoadCommandPred = std::function<bool(const LoadCommand &LC)>;
+
+#ifndef NDEBUG
+static bool isLoadCommandWithPayloadString(const LoadCommand &LC) {
+ // TODO: Add support for LC_REEXPORT_DYLIB, LC_LOAD_UPWARD_DYLIB and
+ // LC_LAZY_LOAD_DYLIB
+ return LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_RPATH ||
+ LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_ID_DYLIB ||
+ LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_LOAD_DYLIB ||
+ LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_LOAD_WEAK_DYLIB;
+}
+#endif
+
+static StringRef getPayloadString(const LoadCommand &LC) {
+ assert(isLoadCommandWithPayloadString(LC) &&
+ "unsupported load command encountered");
+
+ return StringRef(reinterpret_cast<const char *>(LC.Payload.data()),
+ LC.Payload.size())
+ .rtrim('\0');
+}
-static void removeSections(const CopyConfig &Config, Object &Obj) {
- SectionPred RemovePred = [](const Section &) { return false; };
+static Error removeSections(const CopyConfig &Config, Object &Obj) {
+ SectionPred RemovePred = [](const std::unique_ptr<Section> &) {
+ return false;
+ };
if (!Config.ToRemove.empty()) {
- RemovePred = [&Config, RemovePred](const Section &Sec) {
- return Config.ToRemove.matches(Sec.CanonicalName);
+ RemovePred = [&Config, RemovePred](const std::unique_ptr<Section> &Sec) {
+ return Config.ToRemove.matches(Sec->CanonicalName);
};
}
if (Config.StripAll || Config.StripDebug) {
// Remove all debug sections.
- RemovePred = [RemovePred](const Section &Sec) {
- if (Sec.Segname == "__DWARF")
+ RemovePred = [RemovePred](const std::unique_ptr<Section> &Sec) {
+ if (Sec->Segname == "__DWARF")
return true;
return RemovePred(Sec);
@@ -41,8 +65,8 @@ static void removeSections(const CopyConfig &Config, Object &Obj) {
if (!Config.OnlySection.empty()) {
// Overwrite RemovePred because --only-section takes priority.
- RemovePred = [&Config](const Section &Sec) {
- return !Config.OnlySection.matches(Sec.CanonicalName);
+ RemovePred = [&Config](const std::unique_ptr<Section> &Sec) {
+ return !Config.OnlySection.matches(Sec->CanonicalName);
};
}
@@ -60,41 +84,158 @@ static void updateAndRemoveSymbols(const CopyConfig &Config, Object &Obj) {
for (SymbolEntry &Sym : Obj.SymTable) {
auto I = Config.SymbolsToRename.find(Sym.Name);
if (I != Config.SymbolsToRename.end())
- Sym.Name = I->getValue();
+ Sym.Name = std::string(I->getValue());
}
- auto RemovePred = [Config](const std::unique_ptr<SymbolEntry> &N) {
+ auto RemovePred = [Config, &Obj](const std::unique_ptr<SymbolEntry> &N) {
if (N->Referenced)
return false;
- return Config.StripAll;
+ if (Config.StripAll)
+ return true;
+ if (Config.DiscardMode == DiscardType::All && !(N->n_type & MachO::N_EXT))
+ return true;
+ // This behavior is consistent with cctools' strip.
+ if (Config.StripSwiftSymbols && (Obj.Header.Flags & MachO::MH_DYLDLINK) &&
+ Obj.SwiftVersion && *Obj.SwiftVersion && N->isSwiftSymbol())
+ return true;
+ return false;
};
Obj.SymTable.removeSymbols(RemovePred);
}
+template <typename LCType>
+static void updateLoadCommandPayloadString(LoadCommand &LC, StringRef S) {
+ assert(isLoadCommandWithPayloadString(LC) &&
+ "unsupported load command encountered");
+
+ uint32_t NewCmdsize = alignTo(sizeof(LCType) + S.size() + 1, 8);
+
+ LC.MachOLoadCommand.load_command_data.cmdsize = NewCmdsize;
+ LC.Payload.assign(NewCmdsize - sizeof(LCType), 0);
+ std::copy(S.begin(), S.end(), LC.Payload.begin());
+}
+
static LoadCommand buildRPathLoadCommand(StringRef Path) {
LoadCommand LC;
MachO::rpath_command RPathLC;
RPathLC.cmd = MachO::LC_RPATH;
RPathLC.path = sizeof(MachO::rpath_command);
- RPathLC.cmdsize = alignTo(sizeof(MachO::rpath_command) + Path.size(), 8);
+ RPathLC.cmdsize = alignTo(sizeof(MachO::rpath_command) + Path.size() + 1, 8);
LC.MachOLoadCommand.rpath_command_data = RPathLC;
LC.Payload.assign(RPathLC.cmdsize - sizeof(MachO::rpath_command), 0);
std::copy(Path.begin(), Path.end(), LC.Payload.begin());
return LC;
}
+static Error processLoadCommands(const CopyConfig &Config, Object &Obj) {
+ // Remove RPaths.
+ DenseSet<StringRef> RPathsToRemove(Config.RPathsToRemove.begin(),
+ Config.RPathsToRemove.end());
+
+ LoadCommandPred RemovePred = [&RPathsToRemove](const LoadCommand &LC) {
+ if (LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_RPATH) {
+ StringRef RPath = getPayloadString(LC);
+ if (RPathsToRemove.count(RPath)) {
+ RPathsToRemove.erase(RPath);
+ return true;
+ }
+ }
+ return false;
+ };
+
+ if (Error E = Obj.removeLoadCommands(RemovePred))
+ return E;
+
+ // Emit an error if the Mach-O binary does not contain an rpath path name
+ // specified in -delete_rpath.
+ for (StringRef RPath : Config.RPathsToRemove) {
+ if (RPathsToRemove.count(RPath))
+ return createStringError(errc::invalid_argument,
+ "no LC_RPATH load command with path: %s",
+ RPath.str().c_str());
+ }
+
+ DenseSet<StringRef> RPaths;
+
+ // Get all existing RPaths.
+ for (LoadCommand &LC : Obj.LoadCommands) {
+ if (LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_RPATH)
+ RPaths.insert(getPayloadString(LC));
+ }
+
+ // Throw errors for invalid RPaths.
+ for (const auto &OldNew : Config.RPathsToUpdate) {
+ StringRef Old = OldNew.getFirst();
+ StringRef New = OldNew.getSecond();
+ if (RPaths.count(Old) == 0)
+ return createStringError(errc::invalid_argument,
+ "no LC_RPATH load command with path: " + Old);
+ if (RPaths.count(New) != 0)
+ return createStringError(errc::invalid_argument,
+ "rpath " + New +
+ " would create a duplicate load command");
+ }
+
+ // Update load commands.
+ for (LoadCommand &LC : Obj.LoadCommands) {
+ switch (LC.MachOLoadCommand.load_command_data.cmd) {
+ case MachO::LC_ID_DYLIB:
+ if (Config.SharedLibId) {
+ StringRef Id = Config.SharedLibId.getValue();
+ if (Id.empty())
+ return createStringError(errc::invalid_argument,
+ "cannot specify an empty id");
+ updateLoadCommandPayloadString<MachO::dylib_command>(LC, Id);
+ }
+ break;
+
+ case MachO::LC_RPATH: {
+ StringRef RPath = getPayloadString(LC);
+ StringRef NewRPath = Config.RPathsToUpdate.lookup(RPath);
+ if (!NewRPath.empty())
+ updateLoadCommandPayloadString<MachO::rpath_command>(LC, NewRPath);
+ break;
+ }
+
+ // TODO: Add LC_REEXPORT_DYLIB, LC_LAZY_LOAD_DYLIB, and LC_LOAD_UPWARD_DYLIB
+ // here once llvm-objcopy supports them.
+ case MachO::LC_LOAD_DYLIB:
+ case MachO::LC_LOAD_WEAK_DYLIB:
+ StringRef InstallName = getPayloadString(LC);
+ StringRef NewInstallName =
+ Config.InstallNamesToUpdate.lookup(InstallName);
+ if (!NewInstallName.empty())
+ updateLoadCommandPayloadString<MachO::dylib_command>(LC,
+ NewInstallName);
+ break;
+ }
+ }
+
+ // Add new RPaths.
+ for (StringRef RPath : Config.RPathToAdd) {
+ if (RPaths.count(RPath) != 0)
+ return createStringError(errc::invalid_argument,
+ "rpath " + RPath +
+ " would create a duplicate load command");
+ RPaths.insert(RPath);
+ Obj.addLoadCommand(buildRPathLoadCommand(RPath));
+ }
+
+ return Error::success();
+}
+
static Error dumpSectionToFile(StringRef SecName, StringRef Filename,
Object &Obj) {
for (LoadCommand &LC : Obj.LoadCommands)
- for (Section &Sec : LC.Sections) {
- if (Sec.CanonicalName == SecName) {
+ for (const std::unique_ptr<Section> &Sec : LC.Sections) {
+ if (Sec->CanonicalName == SecName) {
Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr =
- FileOutputBuffer::create(Filename, Sec.Content.size());
+ FileOutputBuffer::create(Filename, Sec->Content.size());
if (!BufferOrErr)
return BufferOrErr.takeError();
std::unique_ptr<FileOutputBuffer> Buf = std::move(*BufferOrErr);
- llvm::copy(Sec.Content, Buf->getBufferStart());
+ llvm::copy(Sec->Content, Buf->getBufferStart());
if (Error E = Buf->commit())
return E;
@@ -122,7 +263,7 @@ static Error addSection(StringRef SecName, StringRef Filename, Object &Obj) {
for (LoadCommand &LC : Obj.LoadCommands) {
Optional<StringRef> SegName = LC.getSegmentName();
if (SegName && SegName == TargetSegName) {
- LC.Sections.push_back(Sec);
+ LC.Sections.push_back(std::make_unique<Section>(Sec));
return Error::success();
}
}
@@ -130,7 +271,7 @@ static Error addSection(StringRef SecName, StringRef Filename, Object &Obj) {
// There's no segment named TargetSegName. Create a new load command and
// Insert a new section into it.
LoadCommand &NewSegment = Obj.addSegment(TargetSegName);
- NewSegment.Sections.push_back(Sec);
+ NewSegment.Sections.push_back(std::make_unique<Section>(Sec));
return Error::success();
}
@@ -167,17 +308,27 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj) {
!Config.SectionsToRename.empty() ||
!Config.UnneededSymbolsToRemove.empty() ||
!Config.SetSectionAlignment.empty() || !Config.SetSectionFlags.empty() ||
- Config.ExtractDWO || Config.KeepFileSymbols || Config.LocalizeHidden ||
- Config.PreserveDates || Config.StripAllGNU || Config.StripDWO ||
- Config.StripNonAlloc || Config.StripSections || Config.Weaken ||
- Config.DecompressDebugSections || Config.StripNonAlloc ||
- Config.StripSections || Config.StripUnneeded ||
- Config.DiscardMode != DiscardType::None || !Config.SymbolsToAdd.empty() ||
- Config.EntryExpr) {
+ Config.ExtractDWO || Config.LocalizeHidden || Config.PreserveDates ||
+ Config.StripAllGNU || Config.StripDWO || Config.StripNonAlloc ||
+ Config.StripSections || Config.Weaken || Config.DecompressDebugSections ||
+ Config.StripNonAlloc || Config.StripSections || 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");
}
- removeSections(Config, Obj);
+
+ // Dump sections before add/remove for compatibility with GNU objcopy.
+ for (StringRef Flag : Config.DumpSection) {
+ StringRef SectionName;
+ StringRef FileName;
+ std::tie(SectionName, FileName) = Flag.split('=');
+ if (Error E = dumpSectionToFile(SectionName, FileName, Obj))
+ return E;
+ }
+
+ if (Error E = removeSections(Config, Obj))
+ return E;
// Mark symbols to determine which symbols are still needed.
if (Config.StripAll)
@@ -187,16 +338,8 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj) {
if (Config.StripAll)
for (LoadCommand &LC : Obj.LoadCommands)
- for (Section &Sec : LC.Sections)
- Sec.Relocations.clear();
-
- for (const StringRef &Flag : Config.DumpSection) {
- std::pair<StringRef, StringRef> SecPair = Flag.split("=");
- StringRef SecName = SecPair.first;
- StringRef File = SecPair.second;
- if (Error E = dumpSectionToFile(SecName, File, Obj))
- return E;
- }
+ for (std::unique_ptr<Section> &Sec : LC.Sections)
+ Sec->Relocations.clear();
for (const auto &Flag : Config.AddSection) {
std::pair<StringRef, StringRef> SecPair = Flag.split("=");
@@ -208,19 +351,9 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj) {
return E;
}
- for (StringRef RPath : Config.RPathToAdd) {
- for (LoadCommand &LC : Obj.LoadCommands) {
- if (LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_RPATH &&
- RPath == StringRef(reinterpret_cast<char *>(LC.Payload.data()),
- LC.Payload.size())
- .trim(0)) {
- return createStringError(errc::invalid_argument,
- "rpath " + RPath +
- " would create a duplicate load command");
- }
- }
- Obj.addLoadCommand(buildRPathLoadCommand(RPath));
- }
+ if (Error E = processLoadCommands(Config, Obj))
+ return E;
+
return Error::success();
}
@@ -237,9 +370,18 @@ Error executeObjcopyOnBinary(const CopyConfig &Config,
if (Error E = handleArgs(Config, *O))
return createFileError(Config.InputFilename, std::move(E));
- // TODO: Support 16KB pages which are employed in iOS arm64 binaries:
- // https://github.com/llvm/llvm-project/commit/1bebb2832ee312d3b0316dacff457a7a29435edb
- const uint64_t PageSize = 4096;
+ // Page size used for alignment of segment sizes in Mach-O executables and
+ // dynamic libraries.
+ uint64_t PageSize;
+ switch (In.getArch()) {
+ case Triple::ArchType::arm:
+ case Triple::ArchType::aarch64:
+ case Triple::ArchType::aarch64_32:
+ PageSize = 16384;
+ break;
+ default:
+ PageSize = 4096;
+ }
MachOWriter Writer(*O, In.is64Bit(), In.isLittleEndian(), PageSize, Out);
if (auto E = Writer.finalize())
diff --git a/llvm/tools/llvm-objcopy/MachO/MachOReader.cpp b/llvm/tools/llvm-objcopy/MachO/MachOReader.cpp
index 46bb117273229..99bcec7f6b51e 100644
--- a/llvm/tools/llvm-objcopy/MachO/MachOReader.cpp
+++ b/llvm/tools/llvm-objcopy/MachO/MachOReader.cpp
@@ -28,10 +28,11 @@ void MachOReader::readHeader(Object &O) const {
}
template <typename SectionType>
-Section constructSectionCommon(SectionType Sec) {
+Section constructSectionCommon(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);
+ S.Index = Index;
S.Addr = Sec.addr;
S.Size = Sec.size;
S.Offset = Sec.offset;
@@ -45,39 +46,42 @@ Section constructSectionCommon(SectionType Sec) {
return S;
}
-template <typename SectionType> Section constructSection(SectionType Sec);
+template <typename SectionType>
+Section constructSection(SectionType Sec, uint32_t Index);
-template <> Section constructSection(MachO::section Sec) {
- return constructSectionCommon(Sec);
+template <> Section constructSection(MachO::section Sec, uint32_t Index) {
+ return constructSectionCommon(Sec, Index);
}
-template <> Section constructSection(MachO::section_64 Sec) {
- Section S = constructSectionCommon(Sec);
+template <> Section constructSection(MachO::section_64 Sec, uint32_t Index) {
+ Section S = constructSectionCommon(Sec, Index);
S.Reserved3 = Sec.reserved3;
return S;
}
// TODO: get rid of reportError and make MachOReader return Expected<> instead.
template <typename SectionType, typename SegmentType>
-std::vector<Section>
+std::vector<std::unique_ptr<Section>>
extractSections(const object::MachOObjectFile::LoadCommandInfo &LoadCmd,
const object::MachOObjectFile &MachOObj,
- size_t &NextSectionIndex) {
+ uint32_t &NextSectionIndex) {
auto End = LoadCmd.Ptr + LoadCmd.C.cmdsize;
const SectionType *Curr =
reinterpret_cast<const SectionType *>(LoadCmd.Ptr + sizeof(SegmentType));
- std::vector<Section> Sections;
+ 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));
MachO::swapStruct(Sec);
- Sections.push_back(constructSection(Sec));
+ Sections.push_back(
+ std::make_unique<Section>(constructSection(Sec, NextSectionIndex)));
} else {
- Sections.push_back(constructSection(*Curr));
+ Sections.push_back(
+ std::make_unique<Section>(constructSection(*Curr, NextSectionIndex)));
}
- Section &S = Sections.back();
+ Section &S = *Sections.back();
Expected<object::SectionRef> SecRef =
MachOObj.getSection(NextSectionIndex++);
@@ -99,6 +103,7 @@ 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);
+ R.Extern = !R.Scattered && MachOObj.getPlainRelocationExternal(R.Info);
S.Relocations.push_back(R);
}
@@ -110,10 +115,13 @@ extractSections(const object::MachOObjectFile::LoadCommandInfo &LoadCmd,
void MachOReader::readLoadCommands(Object &O) const {
// For MachO sections indices start from 1.
- size_t NextSectionIndex = 1;
+ uint32_t NextSectionIndex = 1;
for (auto LoadCmd : MachOObj.load_commands()) {
LoadCommand LC;
switch (LoadCmd.C.cmd) {
+ case MachO::LC_CODE_SIGNATURE:
+ O.CodeSignatureCommandIndex = O.LoadCommands.size();
+ break;
case MachO::LC_SEGMENT:
LC.Sections = extractSections<MachO::section, MachO::segment_command>(
LoadCmd, MachOObj, NextSectionIndex);
@@ -189,24 +197,36 @@ void MachOReader::readSymbolTable(Object &O) const {
for (auto Symbol : MachOObj.symbols()) {
SymbolEntry SE =
(MachOObj.is64Bit()
- ? constructSymbolEntry(
- StrTable,
- MachOObj.getSymbol64TableEntry(Symbol.getRawDataRefImpl()))
- : constructSymbolEntry(
- StrTable,
- MachOObj.getSymbolTableEntry(Symbol.getRawDataRefImpl())));
+ ? constructSymbolEntry(StrTable, MachOObj.getSymbol64TableEntry(
+ Symbol.getRawDataRefImpl()))
+ : constructSymbolEntry(StrTable, MachOObj.getSymbolTableEntry(
+ Symbol.getRawDataRefImpl())));
O.SymTable.Symbols.push_back(std::make_unique<SymbolEntry>(SE));
}
}
void MachOReader::setSymbolInRelocationInfo(Object &O) const {
+ std::vector<const Section *> Sections;
for (auto &LC : O.LoadCommands)
- for (auto &Sec : LC.Sections)
- for (auto &Reloc : Sec.Relocations)
+ for (std::unique_ptr<Section> &Sec : LC.Sections)
+ Sections.push_back(Sec.get());
+
+ for (LoadCommand &LC : O.LoadCommands)
+ for (std::unique_ptr<Section> &Sec : LC.Sections)
+ for (auto &Reloc : Sec->Relocations)
if (!Reloc.Scattered) {
- auto *Info = reinterpret_cast<MachO::relocation_info *>(&Reloc.Info);
- Reloc.Symbol = O.SymTable.getSymbolByIndex(Info->r_symbolnum);
+ const uint32_t SymbolNum =
+ Reloc.getPlainRelocationSymbolNum(MachOObj.isLittleEndian());
+ if (Reloc.Extern) {
+ Reloc.Symbol = O.SymTable.getSymbolByIndex(SymbolNum);
+ } else {
+ // FIXME: Refactor error handling in MachOReader and report an error
+ // if we encounter an invalid relocation.
+ assert(SymbolNum >= 1 && SymbolNum <= Sections.size() &&
+ "Invalid section index.");
+ Reloc.Sec = Sections[SymbolNum - 1];
+ }
}
}
@@ -230,26 +250,26 @@ void MachOReader::readExportInfo(Object &O) const {
O.Exports.Trie = MachOObj.getDyldInfoExportsTrie();
}
-void MachOReader::readDataInCodeData(Object &O) const {
- if (!O.DataInCodeCommandIndex)
+void MachOReader::readLinkData(Object &O, Optional<size_t> LCIndex,
+ LinkData &LD) const {
+ if (!LCIndex)
return;
- const MachO::linkedit_data_command &LDC =
- O.LoadCommands[*O.DataInCodeCommandIndex]
- .MachOLoadCommand.linkedit_data_command_data;
+ const MachO::linkedit_data_command &LC =
+ O.LoadCommands[*LCIndex].MachOLoadCommand.linkedit_data_command_data;
+ LD.Data =
+ arrayRefFromStringRef(MachOObj.getData().substr(LC.dataoff, LC.datasize));
+}
- O.DataInCode.Data = arrayRefFromStringRef(
- MachOObj.getData().substr(LDC.dataoff, LDC.datasize));
+void MachOReader::readCodeSignature(Object &O) const {
+ return readLinkData(O, O.CodeSignatureCommandIndex, O.CodeSignature);
}
-void MachOReader::readFunctionStartsData(Object &O) const {
- if (!O.FunctionStartsCommandIndex)
- return;
- const MachO::linkedit_data_command &LDC =
- O.LoadCommands[*O.FunctionStartsCommandIndex]
- .MachOLoadCommand.linkedit_data_command_data;
+void MachOReader::readDataInCodeData(Object &O) const {
+ return readLinkData(O, O.DataInCodeCommandIndex, O.DataInCode);
+}
- O.FunctionStarts.Data = arrayRefFromStringRef(
- MachOObj.getData().substr(LDC.dataoff, LDC.datasize));
+void MachOReader::readFunctionStartsData(Object &O) const {
+ return readLinkData(O, O.FunctionStartsCommandIndex, O.FunctionStarts);
}
void MachOReader::readIndirectSymbolTable(Object &O) const {
@@ -266,6 +286,28 @@ void MachOReader::readIndirectSymbolTable(Object &O) const {
}
}
+void MachOReader::readSwiftVersion(Object &O) const {
+ struct ObjCImageInfo {
+ uint32_t Version;
+ uint32_t Flags;
+ } ImageInfo;
+
+ for (const LoadCommand &LC : O.LoadCommands)
+ for (const std::unique_ptr<Section> &Sec : LC.Sections)
+ if (Sec->Sectname == "__objc_imageinfo" &&
+ (Sec->Segname == "__DATA" || Sec->Segname == "__DATA_CONST" ||
+ Sec->Segname == "__DATA_DIRTY") &&
+ Sec->Content.size() >= sizeof(ObjCImageInfo)) {
+ memcpy(&ImageInfo, Sec->Content.data(), sizeof(ObjCImageInfo));
+ if (MachOObj.isLittleEndian() != sys::IsLittleEndianHost) {
+ sys::swapByteOrder(ImageInfo.Version);
+ sys::swapByteOrder(ImageInfo.Flags);
+ }
+ O.SwiftVersion = (ImageInfo.Flags >> 8) & 0xff;
+ return;
+ }
+}
+
std::unique_ptr<Object> MachOReader::create() const {
auto Obj = std::make_unique<Object>();
readHeader(*Obj);
@@ -277,9 +319,11 @@ std::unique_ptr<Object> MachOReader::create() const {
readWeakBindInfo(*Obj);
readLazyBindInfo(*Obj);
readExportInfo(*Obj);
+ readCodeSignature(*Obj);
readDataInCodeData(*Obj);
readFunctionStartsData(*Obj);
readIndirectSymbolTable(*Obj);
+ readSwiftVersion(*Obj);
return Obj;
}
diff --git a/llvm/tools/llvm-objcopy/MachO/MachOReader.h b/llvm/tools/llvm-objcopy/MachO/MachOReader.h
index 00c8f0d55f61f..65824b6eb3893 100644
--- a/llvm/tools/llvm-objcopy/MachO/MachOReader.h
+++ b/llvm/tools/llvm-objcopy/MachO/MachOReader.h
@@ -36,9 +36,12 @@ class MachOReader : public Reader {
void readWeakBindInfo(Object &O) const;
void readLazyBindInfo(Object &O) const;
void readExportInfo(Object &O) const;
+ void readLinkData(Object &O, Optional<size_t> LCIndex, LinkData &LD) const;
+ void readCodeSignature(Object &O) const;
void readDataInCodeData(Object &O) const;
void readFunctionStartsData(Object &O) const;
void readIndirectSymbolTable(Object &O) const;
+ void readSwiftVersion(Object &O) const;
public:
explicit MachOReader(const object::MachOObjectFile &Obj) : MachOObj(Obj) {}
diff --git a/llvm/tools/llvm-objcopy/MachO/MachOWriter.cpp b/llvm/tools/llvm-objcopy/MachO/MachOWriter.cpp
index 0d9590612eca0..3c41e73b2b013 100644
--- a/llvm/tools/llvm-objcopy/MachO/MachOWriter.cpp
+++ b/llvm/tools/llvm-objcopy/MachO/MachOWriter.cpp
@@ -89,6 +89,15 @@ size_t MachOWriter::totalSize() const {
sizeof(uint32_t) * O.IndirectSymTable.Symbols.size());
}
+ if (O.CodeSignatureCommandIndex) {
+ const MachO::linkedit_data_command &LinkEditDataCommand =
+ O.LoadCommands[*O.CodeSignatureCommandIndex]
+ .MachOLoadCommand.linkedit_data_command_data;
+ if (LinkEditDataCommand.dataoff)
+ Ends.push_back(LinkEditDataCommand.dataoff +
+ LinkEditDataCommand.datasize);
+ }
+
if (O.DataInCodeCommandIndex) {
const MachO::linkedit_data_command &LinkEditDataCommand =
O.LoadCommands[*O.DataInCodeCommandIndex]
@@ -110,12 +119,12 @@ size_t MachOWriter::totalSize() const {
}
// Otherwise, use the last section / reloction.
- for (const auto &LC : O.LoadCommands)
- for (const auto &S : LC.Sections) {
- Ends.push_back(S.Offset + S.Size);
- if (S.RelOff)
- Ends.push_back(S.RelOff +
- S.NReloc * sizeof(MachO::any_relocation_info));
+ for (const LoadCommand &LC : O.LoadCommands)
+ for (const std::unique_ptr<Section> &S : LC.Sections) {
+ Ends.push_back(S->Offset + S->Size);
+ if (S->RelOff)
+ Ends.push_back(S->RelOff +
+ S->NReloc * sizeof(MachO::any_relocation_info));
}
if (!Ends.empty())
@@ -147,7 +156,7 @@ void MachOWriter::writeHeader() {
void MachOWriter::writeLoadCommands() {
uint8_t *Begin = B.getBufferStart() + headerSize();
- for (const auto &LC : O.LoadCommands) {
+ for (const LoadCommand &LC : O.LoadCommands) {
// Construct a load command.
MachO::macho_load_command MLC = LC.MachOLoadCommand;
switch (MLC.load_command_data.cmd) {
@@ -157,8 +166,8 @@ void MachOWriter::writeLoadCommands() {
memcpy(Begin, &MLC.segment_command_data, sizeof(MachO::segment_command));
Begin += sizeof(MachO::segment_command);
- for (const auto &Sec : LC.Sections)
- writeSectionInLoadCommand<MachO::section>(Sec, Begin);
+ for (const std::unique_ptr<Section> &Sec : LC.Sections)
+ writeSectionInLoadCommand<MachO::section>(*Sec, Begin);
continue;
case MachO::LC_SEGMENT_64:
if (IsLittleEndian != sys::IsLittleEndianHost)
@@ -167,8 +176,8 @@ void MachOWriter::writeLoadCommands() {
sizeof(MachO::segment_command_64));
Begin += sizeof(MachO::segment_command_64);
- for (const auto &Sec : LC.Sections)
- writeSectionInLoadCommand<MachO::section_64>(Sec, Begin);
+ for (const std::unique_ptr<Section> &Sec : LC.Sections)
+ writeSectionInLoadCommand<MachO::section_64>(*Sec, Begin);
continue;
}
@@ -229,27 +238,27 @@ void MachOWriter::writeSectionInLoadCommand(const Section &Sec, uint8_t *&Out) {
}
void MachOWriter::writeSections() {
- for (const auto &LC : O.LoadCommands)
- for (const auto &Sec : LC.Sections) {
- if (Sec.isVirtualSection())
+ for (const LoadCommand &LC : O.LoadCommands)
+ for (const std::unique_ptr<Section> &Sec : LC.Sections) {
+ if (Sec->isVirtualSection())
continue;
- 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(),
- Sec.Content.size());
- for (size_t Index = 0; Index < Sec.Relocations.size(); ++Index) {
- auto RelocInfo = Sec.Relocations[Index];
+ 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(),
+ Sec->Content.size());
+ for (size_t Index = 0; Index < Sec->Relocations.size(); ++Index) {
+ RelocationInfo RelocInfo = Sec->Relocations[Index];
if (!RelocInfo.Scattered) {
- auto *Info =
- reinterpret_cast<MachO::relocation_info *>(&RelocInfo.Info);
- Info->r_symbolnum = RelocInfo.Symbol->Index;
+ const uint32_t SymbolNum = RelocInfo.Extern
+ ? (*RelocInfo.Symbol)->Index
+ : (*RelocInfo.Sec)->Index;
+ RelocInfo.setPlainRelocationSymbolNum(SymbolNum, IsLittleEndian);
}
-
if (IsLittleEndian != sys::IsLittleEndianHost)
MachO::swapStruct(
reinterpret_cast<MachO::any_relocation_info &>(RelocInfo.Info));
- memcpy(B.getBufferStart() + Sec.RelOff +
+ memcpy(B.getBufferStart() + Sec->RelOff +
Index * sizeof(MachO::any_relocation_info),
&RelocInfo.Info, sizeof(RelocInfo.Info));
}
@@ -381,28 +390,27 @@ void MachOWriter::writeIndirectSymbolTable() {
}
}
-void MachOWriter::writeDataInCodeData() {
- if (!O.DataInCodeCommandIndex)
+void MachOWriter::writeLinkData(Optional<size_t> LCIndex, const LinkData &LD) {
+ if (!LCIndex)
return;
const MachO::linkedit_data_command &LinkEditDataCommand =
- O.LoadCommands[*O.DataInCodeCommandIndex]
- .MachOLoadCommand.linkedit_data_command_data;
+ O.LoadCommands[*LCIndex].MachOLoadCommand.linkedit_data_command_data;
char *Out = (char *)B.getBufferStart() + LinkEditDataCommand.dataoff;
- assert((LinkEditDataCommand.datasize == O.DataInCode.Data.size()) &&
- "Incorrect data in code data size");
- memcpy(Out, O.DataInCode.Data.data(), O.DataInCode.Data.size());
+ assert((LinkEditDataCommand.datasize == LD.Data.size()) &&
+ "Incorrect data size");
+ memcpy(Out, LD.Data.data(), LD.Data.size());
+}
+
+void MachOWriter::writeCodeSignatureData() {
+ return writeLinkData(O.CodeSignatureCommandIndex, O.CodeSignature);
+}
+
+void MachOWriter::writeDataInCodeData() {
+ return writeLinkData(O.DataInCodeCommandIndex, O.DataInCode);
}
void MachOWriter::writeFunctionStartsData() {
- if (!O.FunctionStartsCommandIndex)
- return;
- const MachO::linkedit_data_command &LinkEditDataCommand =
- O.LoadCommands[*O.FunctionStartsCommandIndex]
- .MachOLoadCommand.linkedit_data_command_data;
- char *Out = (char *)B.getBufferStart() + LinkEditDataCommand.dataoff;
- assert((LinkEditDataCommand.datasize == O.FunctionStarts.Data.size()) &&
- "Incorrect function starts data size");
- memcpy(Out, O.FunctionStarts.Data.data(), O.FunctionStarts.Data.size());
+ return writeLinkData(O.FunctionStartsCommandIndex, O.FunctionStarts);
}
void MachOWriter::writeTail() {
@@ -450,6 +458,16 @@ void MachOWriter::writeTail() {
&MachOWriter::writeIndirectSymbolTable);
}
+ if (O.CodeSignatureCommandIndex) {
+ const MachO::linkedit_data_command &LinkEditDataCommand =
+ O.LoadCommands[*O.CodeSignatureCommandIndex]
+ .MachOLoadCommand.linkedit_data_command_data;
+
+ if (LinkEditDataCommand.dataoff)
+ Queue.emplace_back(LinkEditDataCommand.dataoff,
+ &MachOWriter::writeCodeSignatureData);
+ }
+
if (O.DataInCodeCommandIndex) {
const MachO::linkedit_data_command &LinkEditDataCommand =
O.LoadCommands[*O.DataInCodeCommandIndex]
diff --git a/llvm/tools/llvm-objcopy/MachO/MachOWriter.h b/llvm/tools/llvm-objcopy/MachO/MachOWriter.h
index 22abbad56f41c..c2c6f5a55e9af 100644
--- a/llvm/tools/llvm-objcopy/MachO/MachOWriter.h
+++ b/llvm/tools/llvm-objcopy/MachO/MachOWriter.h
@@ -45,6 +45,8 @@ class MachOWriter {
void writeLazyBindInfo();
void writeExportInfo();
void writeIndirectSymbolTable();
+ void writeLinkData(Optional<size_t> LCIndex, const LinkData &LD);
+ void writeCodeSignatureData();
void writeDataInCodeData();
void writeFunctionStartsData();
void writeTail();
diff --git a/llvm/tools/llvm-objcopy/MachO/Object.cpp b/llvm/tools/llvm-objcopy/MachO/Object.cpp
index d3b4fdc2f6338..de8cb0af108d7 100644
--- a/llvm/tools/llvm-objcopy/MachO/Object.cpp
+++ b/llvm/tools/llvm-objcopy/MachO/Object.cpp
@@ -1,5 +1,15 @@
+//===- Object.cpp - Mach-O object file model --------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
#include "Object.h"
#include "../llvm-objcopy.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include <unordered_set>
namespace llvm {
namespace objcopy {
@@ -22,11 +32,83 @@ void SymbolTable::removeSymbols(
std::end(Symbols));
}
-void Object::removeSections(function_ref<bool(const Section &)> ToRemove) {
- for (LoadCommand &LC : LoadCommands)
- LC.Sections.erase(std::remove_if(std::begin(LC.Sections),
- std::end(LC.Sections), ToRemove),
- std::end(LC.Sections));
+void Object::updateLoadCommandIndexes() {
+ // Update indices of special load commands
+ for (size_t Index = 0, Size = LoadCommands.size(); Index < Size; ++Index) {
+ LoadCommand &LC = LoadCommands[Index];
+ switch (LC.MachOLoadCommand.load_command_data.cmd) {
+ case MachO::LC_SYMTAB:
+ SymTabCommandIndex = Index;
+ break;
+ case MachO::LC_DYSYMTAB:
+ DySymTabCommandIndex = Index;
+ break;
+ case MachO::LC_DYLD_INFO:
+ case MachO::LC_DYLD_INFO_ONLY:
+ DyLdInfoCommandIndex = Index;
+ break;
+ case MachO::LC_DATA_IN_CODE:
+ DataInCodeCommandIndex = Index;
+ break;
+ case MachO::LC_FUNCTION_STARTS:
+ FunctionStartsCommandIndex = Index;
+ break;
+ }
+ }
+}
+
+Error Object::removeLoadCommands(
+ function_ref<bool(const LoadCommand &)> ToRemove) {
+ auto It = std::stable_partition(
+ LoadCommands.begin(), LoadCommands.end(),
+ [&](const LoadCommand &LC) { return !ToRemove(LC); });
+ LoadCommands.erase(It, LoadCommands.end());
+
+ updateLoadCommandIndexes();
+ return Error::success();
+}
+
+Error Object::removeSections(
+ function_ref<bool(const std::unique_ptr<Section> &)> ToRemove) {
+ DenseMap<uint32_t, const Section *> OldIndexToSection;
+ uint32_t NextSectionIndex = 1;
+ for (LoadCommand &LC : LoadCommands) {
+ auto It = std::stable_partition(
+ std::begin(LC.Sections), std::end(LC.Sections),
+ [&](const std::unique_ptr<Section> &Sec) { return !ToRemove(Sec); });
+ for (auto I = LC.Sections.begin(), End = It; I != End; ++I) {
+ OldIndexToSection[(*I)->Index] = I->get();
+ (*I)->Index = NextSectionIndex++;
+ }
+ LC.Sections.erase(It, LC.Sections.end());
+ }
+
+ auto IsDead = [&](const std::unique_ptr<SymbolEntry> &S) -> bool {
+ Optional<uint32_t> Section = S->section();
+ return (Section && !OldIndexToSection.count(*Section));
+ };
+
+ SmallPtrSet<const SymbolEntry *, 2> DeadSymbols;
+ for (const std::unique_ptr<SymbolEntry> &Sym : SymTable.Symbols)
+ if (IsDead(Sym))
+ DeadSymbols.insert(Sym.get());
+
+ for (const LoadCommand &LC : LoadCommands)
+ for (const std::unique_ptr<Section> &Sec : LC.Sections)
+ for (const RelocationInfo &R : Sec->Relocations)
+ if (R.Symbol && *R.Symbol && DeadSymbols.count(*R.Symbol))
+ return createStringError(std::errc::invalid_argument,
+ "symbol '%s' defined in section with index "
+ "'%u' cannot be removed because it is "
+ "referenced by a relocation in section '%s'",
+ (*R.Symbol)->Name.c_str(),
+ *((*R.Symbol)->section()),
+ Sec->CanonicalName.c_str());
+ SymTable.removeSymbols(IsDead);
+ for (std::unique_ptr<SymbolEntry> &S : SymTable.Symbols)
+ if (S->section())
+ S->n_sect = OldIndexToSection[S->n_sect]->Index;
+ return Error::success();
}
void Object::addLoadCommand(LoadCommand LC) {
@@ -52,7 +134,7 @@ LoadCommand &Object::addSegment(StringRef SegName) {
constructSegment(LC.MachOLoadCommand.segment_command_data,
MachO::LC_SEGMENT, SegName);
- LoadCommands.push_back(LC);
+ LoadCommands.push_back(std::move(LC));
return LoadCommands.back();
}
diff --git a/llvm/tools/llvm-objcopy/MachO/Object.h b/llvm/tools/llvm-objcopy/MachO/Object.h
index dc2606eefa4a2..e825d1867b09a 100644
--- a/llvm/tools/llvm-objcopy/MachO/Object.h
+++ b/llvm/tools/llvm-objcopy/MachO/Object.h
@@ -37,6 +37,7 @@ struct MachHeader {
struct RelocationInfo;
struct Section {
+ uint32_t Index;
std::string Segname;
std::string Sectname;
// CanonicalName is a string formatted as “<Segname>,<Sectname>".
@@ -55,11 +56,11 @@ struct Section {
std::vector<RelocationInfo> Relocations;
Section(StringRef SegName, StringRef SectName)
- : Segname(SegName), Sectname(SectName),
+ : Segname(std::string(SegName)), Sectname(std::string(SectName)),
CanonicalName((Twine(SegName) + Twine(',') + SectName).str()) {}
Section(StringRef SegName, StringRef SectName, StringRef Content)
- : Segname(SegName), Sectname(SectName),
+ : Segname(std::string(SegName)), Sectname(std::string(SectName)),
CanonicalName((Twine(SegName) + Twine(',') + SectName).str()),
Content(Content) {}
@@ -83,13 +84,13 @@ struct LoadCommand {
// The raw content of the payload of the load command (located right after the
// corresponding struct). In some cases it is either empty or can be
// copied-over without digging into its structure.
- std::vector<uint8_t> Payload;
+ std::vector<uint8_t> Payload;
// Some load commands can contain (inside the payload) an array of sections,
// though the contents of the sections are stored separately. The struct
// Section describes only sections' metadata and where to find the
// corresponding content inside the binary.
- std::vector<Section> Sections;
+ std::vector<std::unique_ptr<Section>> Sections;
// Returns the segment name if the load command is a segment command.
Optional<StringRef> getSegmentName() const;
@@ -106,15 +107,22 @@ struct SymbolEntry {
uint16_t n_desc;
uint64_t n_value;
- bool isExternalSymbol() const {
- return n_type & ((MachO::N_EXT | MachO::N_PEXT));
- }
+ bool isExternalSymbol() const { return n_type & MachO::N_EXT; }
bool isLocalSymbol() const { return !isExternalSymbol(); }
bool isUndefinedSymbol() const {
return (n_type & MachO::N_TYPE) == MachO::N_UNDF;
}
+
+ bool isSwiftSymbol() const {
+ return StringRef(Name).startswith("_$s") ||
+ StringRef(Name).startswith("_$S");
+ }
+
+ Optional<uint32_t> section() const {
+ return n_sect == MachO::NO_SECT ? None : Optional<uint32_t>(n_sect);
+ }
};
/// The location of the symbol table inside the binary is described by LC_SYMTAB
@@ -157,10 +165,29 @@ struct StringTable {
};
struct RelocationInfo {
- const SymbolEntry *Symbol;
+ // The referenced symbol entry. Set if !Scattered && Extern.
+ Optional<const SymbolEntry *> Symbol;
+ // The referenced section. Set if !Scattered && !Extern.
+ Optional<const Section *> Sec;
// True if Info is a scattered_relocation_info.
bool Scattered;
+ // True if the r_symbolnum points to a section number (i.e. r_extern=0).
+ bool Extern;
MachO::any_relocation_info Info;
+
+ unsigned getPlainRelocationSymbolNum(bool IsLittleEndian) {
+ if (IsLittleEndian)
+ return Info.r_word1 & 0xffffff;
+ return Info.r_word1 >> 8;
+ }
+
+ void setPlainRelocationSymbolNum(unsigned SymbolNum, bool IsLittleEndian) {
+ assert(SymbolNum < (1 << 24) && "SymbolNum out of range");
+ if (IsLittleEndian)
+ Info.r_word1 = (Info.r_word1 & ~0x00ffffff) | SymbolNum;
+ else
+ Info.r_word1 = (Info.r_word1 & ~0xffffff00) | (SymbolNum << 8);
+ }
};
/// The location of the rebase info inside the binary is described by
@@ -275,7 +302,12 @@ struct Object {
IndirectSymbolTable IndirectSymTable;
LinkData DataInCode;
LinkData FunctionStarts;
+ LinkData CodeSignature;
+
+ Optional<uint32_t> SwiftVersion;
+ /// The index of LC_CODE_SIGNATURE load command if present.
+ Optional<size_t> CodeSignatureCommandIndex;
/// The index of LC_SYMTAB load command if present.
Optional<size_t> SymTabCommandIndex;
/// The index of LC_DYLD_INFO or LC_DYLD_INFO_ONLY load command if present.
@@ -292,7 +324,13 @@ struct Object {
Object() : NewSectionsContents(Alloc) {}
- void removeSections(function_ref<bool(const Section &)> ToRemove);
+ Error
+ removeSections(function_ref<bool(const std::unique_ptr<Section> &)> ToRemove);
+
+ Error removeLoadCommands(function_ref<bool(const LoadCommand &)> ToRemove);
+
+ void updateLoadCommandIndexes();
+
void addLoadCommand(LoadCommand LC);
/// Creates a new segment load command in the object and returns a reference
diff --git a/llvm/tools/llvm-objcopy/StripOpts.td b/llvm/tools/llvm-objcopy/StripOpts.td
index cd02cffae6732..001da23528d78 100644
--- a/llvm/tools/llvm-objcopy/StripOpts.td
+++ b/llvm/tools/llvm-objcopy/StripOpts.td
@@ -15,3 +15,6 @@ def d : Flag<["-"], "d">,
def S : Flag<["-"], "S">,
Alias<strip_debug>,
HelpText<"Alias for --strip-debug">;
+
+def strip_swift_symbols : Flag<["-"], "T">,
+ HelpText<"Remove Swift symbols">;
diff --git a/llvm/tools/llvm-objcopy/llvm-objcopy.cpp b/llvm/tools/llvm-objcopy/llvm-objcopy.cpp
index e662f35f4b084..69b23b6cf9756 100644
--- a/llvm/tools/llvm-objcopy/llvm-objcopy.cpp
+++ b/llvm/tools/llvm-objcopy/llvm-objcopy.cpp
@@ -8,10 +8,11 @@
#include "llvm-objcopy.h"
#include "Buffer.h"
+#include "COFF/COFFObjcopy.h"
#include "CopyConfig.h"
#include "ELF/ELFObjcopy.h"
-#include "COFF/COFFObjcopy.h"
#include "MachO/MachOObjcopy.h"
+#include "wasm/WasmObjcopy.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
@@ -25,6 +26,7 @@
#include "llvm/Object/ELFTypes.h"
#include "llvm/Object/Error.h"
#include "llvm/Object/MachO.h"
+#include "llvm/Object/Wasm.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Option/Option.h"
@@ -33,6 +35,7 @@
#include "llvm/Support/Error.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/ErrorOr.h"
+#include "llvm/Support/Host.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/Memory.h"
#include "llvm/Support/Path.h"
@@ -172,6 +175,8 @@ static Error executeObjcopyOnBinary(CopyConfig &Config, object::Binary &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 *WasmBinary = dyn_cast<object::WasmObjectFile>(&In))
+ return objcopy::wasm::executeObjcopyOnBinary(Config, *WasmBinary, Out);
else
return createStringError(object_error::invalid_file_type,
"unsupported object file format");
@@ -322,11 +327,25 @@ enum class ToolType { Objcopy, Strip, InstallNameTool };
int main(int argc, char **argv) {
InitLLVM X(argc, argv);
ToolName = argv[0];
- ToolType Tool = StringSwitch<ToolType>(sys::path::stem(ToolName))
- .EndsWith("strip", ToolType::Strip)
- .EndsWith("install-name-tool", ToolType::InstallNameTool)
- .EndsWith("install_name_tool", ToolType::InstallNameTool)
- .Default(ToolType::Objcopy);
+
+ StringRef Stem = sys::path::stem(ToolName);
+ auto Is = [=](StringRef Tool) {
+ // We need to recognize the following filenames:
+ //
+ // llvm-objcopy -> objcopy
+ // strip-10.exe -> strip
+ // powerpc64-unknown-freebsd13-objcopy -> objcopy
+ // llvm-install-name-tool -> install-name-tool
+ auto I = Stem.rfind_lower(Tool);
+ return I != StringRef::npos &&
+ (I + Tool.size() == Stem.size() || !isAlnum(Stem[I + Tool.size()]));
+ };
+ ToolType Tool = ToolType::Objcopy;
+ if (Is("strip"))
+ Tool = ToolType::Strip;
+ else if (Is("install-name-tool") || Is("install_name_tool"))
+ Tool = ToolType::InstallNameTool;
+
// Expand response files.
// TODO: Move these lines, which are copied from lib/Support/CommandLine.cpp,
// into a separate function in the CommandLine library and call that function
diff --git a/llvm/tools/llvm-objcopy/wasm/Object.cpp b/llvm/tools/llvm-objcopy/wasm/Object.cpp
new file mode 100644
index 0000000000000..0c416483663f2
--- /dev/null
+++ b/llvm/tools/llvm-objcopy/wasm/Object.cpp
@@ -0,0 +1,36 @@
+//===- Object.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 "Object.h"
+
+#include "llvm/Support/LEB128.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace llvm {
+namespace objcopy {
+namespace wasm {
+
+using namespace object;
+using namespace llvm::wasm;
+
+void Object::addSectionWithOwnedContents(
+ Section NewSection, std::unique_ptr<MemoryBuffer> &&Content) {
+ Sections.push_back(NewSection);
+ OwnedContents.emplace_back(std::move(Content));
+}
+
+void Object::removeSections(function_ref<bool(const Section &)> ToRemove) {
+ // TODO: remove reloc sections for the removed section, handle symbols, etc.
+ Sections.erase(
+ std::remove_if(std::begin(Sections), std::end(Sections), ToRemove),
+ std::end(Sections));
+}
+
+} // end namespace wasm
+} // end namespace objcopy
+} // end namespace llvm
diff --git a/llvm/tools/llvm-objcopy/wasm/Object.h b/llvm/tools/llvm-objcopy/wasm/Object.h
new file mode 100644
index 0000000000000..9db91c41e2e26
--- /dev/null
+++ b/llvm/tools/llvm-objcopy/wasm/Object.h
@@ -0,0 +1,47 @@
+//===- Object.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_OBJECT_H
+#define LLVM_TOOLS_LLVM_OBJCOPY_WASM_OBJECT_H
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Object/Wasm.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include <vector>
+
+namespace llvm {
+namespace objcopy {
+namespace wasm {
+
+struct Section {
+ // For now, each section is only an opaque binary blob with no distinction
+ // between custom and known sections.
+ uint8_t SectionType;
+ StringRef Name;
+ ArrayRef<uint8_t> Contents;
+};
+
+struct Object {
+ llvm::wasm::WasmObjectHeader Header;
+ // For now don't discriminate between kinds of sections.
+ std::vector<Section> Sections;
+
+ void addSectionWithOwnedContents(Section NewSection,
+ std::unique_ptr<MemoryBuffer> &&Content);
+ void removeSections(function_ref<bool(const Section &)> ToRemove);
+
+private:
+ std::vector<std::unique_ptr<MemoryBuffer>> OwnedContents;
+};
+
+} // end namespace wasm
+} // end namespace objcopy
+} // end namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_OBJCOPY_WASM_OBJECT_H
diff --git a/llvm/tools/llvm-objcopy/wasm/Reader.cpp b/llvm/tools/llvm-objcopy/wasm/Reader.cpp
new file mode 100644
index 0000000000000..13fa84ad80201
--- /dev/null
+++ b/llvm/tools/llvm-objcopy/wasm/Reader.cpp
@@ -0,0 +1,33 @@
+//===- Reader.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 "Reader.h"
+
+namespace llvm {
+namespace objcopy {
+namespace wasm {
+
+using namespace object;
+using namespace llvm::wasm;
+
+Expected<std::unique_ptr<Object>> Reader::create() const {
+ auto Obj = std::make_unique<Object>();
+ Obj->Header = WasmObj.getHeader();
+ std::vector<Section> Sections;
+ Obj->Sections.reserve(WasmObj.getNumSections());
+ for (const SectionRef &Sec : WasmObj.sections()) {
+ const WasmSection &WS = WasmObj.getWasmSection(Sec);
+ Obj->Sections.push_back(
+ {static_cast<uint8_t>(WS.Type), WS.Name, WS.Content});
+ }
+ return std::move(Obj);
+}
+
+} // end namespace wasm
+} // end namespace objcopy
+} // end namespace llvm
diff --git a/llvm/tools/llvm-objcopy/wasm/Reader.h b/llvm/tools/llvm-objcopy/wasm/Reader.h
new file mode 100644
index 0000000000000..2dcf7dde029a0
--- /dev/null
+++ b/llvm/tools/llvm-objcopy/wasm/Reader.h
@@ -0,0 +1,31 @@
+//===- Reader.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_READER_H
+#define LLVM_TOOLS_LLVM_OBJCOPY_WASM_READER_H
+
+#include "Object.h"
+
+namespace llvm {
+namespace objcopy {
+namespace wasm {
+
+class Reader {
+public:
+ explicit Reader(const object::WasmObjectFile &O) : WasmObj(O) {}
+ Expected<std::unique_ptr<Object>> create() const;
+
+private:
+ const object::WasmObjectFile &WasmObj;
+};
+
+} // end namespace wasm
+} // end namespace objcopy
+} // end namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_OBJCOPY_WASM_READER_H
diff --git a/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.cpp b/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.cpp
new file mode 100644
index 0000000000000..20781cef2d33a
--- /dev/null
+++ b/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.cpp
@@ -0,0 +1,114 @@
+//===- WasmObjcopy.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 "WasmObjcopy.h"
+#include "Buffer.h"
+#include "CopyConfig.h"
+#include "Object.h"
+#include "Reader.h"
+#include "Writer.h"
+#include "llvm-objcopy.h"
+#include "llvm/Support/Errc.h"
+
+namespace llvm {
+namespace objcopy {
+namespace wasm {
+
+using namespace object;
+
+static Error dumpSectionToFile(StringRef SecName, StringRef Filename,
+ Object &Obj) {
+ for (const Section &Sec : Obj.Sections) {
+ if (Sec.Name == SecName) {
+ ArrayRef<uint8_t> Contents = Sec.Contents;
+ Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr =
+ FileOutputBuffer::create(Filename, Contents.size());
+ if (!BufferOrErr)
+ return BufferOrErr.takeError();
+ std::unique_ptr<FileOutputBuffer> Buf = std::move(*BufferOrErr);
+ std::copy(Contents.begin(), Contents.end(), Buf->getBufferStart());
+ if (Error E = Buf->commit())
+ return E;
+ return Error::success();
+ }
+ }
+ return createStringError(errc::invalid_argument, "section '%s' not found",
+ SecName.str().c_str());
+}
+static Error handleArgs(const CopyConfig &Config, Object &Obj) {
+ // Only support AddSection, DumpSection, RemoveSection for now.
+ for (StringRef Flag : Config.DumpSection) {
+ StringRef SecName;
+ StringRef FileName;
+ std::tie(SecName, FileName) = Flag.split("=");
+ if (Error E = dumpSectionToFile(SecName, FileName, Obj))
+ return createFileError(FileName, std::move(E));
+ }
+
+ Obj.removeSections([&Config](const Section &Sec) {
+ if (Config.ToRemove.matches(Sec.Name))
+ return true;
+ return false;
+ });
+
+ for (StringRef Flag : Config.AddSection) {
+ StringRef SecName, FileName;
+ std::tie(SecName, FileName) = Flag.split("=");
+ ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
+ MemoryBuffer::getFile(FileName);
+ if (!BufOrErr)
+ return createFileError(FileName, errorCodeToError(BufOrErr.getError()));
+ Section Sec;
+ Sec.SectionType = llvm::wasm::WASM_SEC_CUSTOM;
+ Sec.Name = SecName;
+ std::unique_ptr<MemoryBuffer> Buf = std::move(*BufOrErr);
+ Sec.Contents = makeArrayRef<uint8_t>(
+ reinterpret_cast<const uint8_t *>(Buf->getBufferStart()),
+ Buf->getBufferSize());
+ 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) {
+ Reader TheReader(In);
+ Expected<std::unique_ptr<Object>> ObjOrErr = TheReader.create();
+ if (!ObjOrErr)
+ return createFileError(Config.InputFilename, ObjOrErr.takeError());
+ Object *Obj = ObjOrErr->get();
+ assert(Obj && "Unable to deserialize Wasm object");
+ if (Error E = handleArgs(Config, *Obj))
+ return E;
+ Writer TheWriter(*Obj, Out);
+ if (Error E = TheWriter.write())
+ return createFileError(Config.OutputFilename, std::move(E));
+ return Error::success();
+}
+
+} // end namespace wasm
+} // end namespace objcopy
+} // end namespace llvm
diff --git a/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.h b/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.h
new file mode 100644
index 0000000000000..3557d5c0a50df
--- /dev/null
+++ b/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.h
@@ -0,0 +1,31 @@
+//===- WasmObjcopy.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_WASMOBJCOPY_H
+#define LLVM_TOOLS_LLVM_OBJCOPY_WASM_WASMOBJCOPY_H
+
+namespace llvm {
+class Error;
+
+namespace object {
+class WasmObjectFile;
+} // end namespace object
+
+namespace objcopy {
+struct CopyConfig;
+class Buffer;
+
+namespace wasm {
+Error executeObjcopyOnBinary(const CopyConfig &Config,
+ object::WasmObjectFile &In, Buffer &Out);
+
+} // end namespace wasm
+} // end namespace objcopy
+} // end namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_OBJCOPY_WASM_WASMOBJCOPY_H
diff --git a/llvm/tools/llvm-objcopy/wasm/Writer.cpp b/llvm/tools/llvm-objcopy/wasm/Writer.cpp
new file mode 100644
index 0000000000000..50d26507b4983
--- /dev/null
+++ b/llvm/tools/llvm-objcopy/wasm/Writer.cpp
@@ -0,0 +1,78 @@
+//===- Writer.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 "Writer.h"
+#include "llvm/BinaryFormat/Wasm.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/LEB128.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace llvm {
+namespace objcopy {
+namespace wasm {
+
+using namespace object;
+using namespace llvm::wasm;
+
+Writer::SectionHeader Writer::createSectionHeader(const Section &S,
+ size_t &SectionSize) {
+ SectionHeader Header;
+ raw_svector_ostream OS(Header);
+ OS << S.SectionType;
+ bool HasName = S.SectionType == WASM_SEC_CUSTOM;
+ SectionSize = S.Contents.size();
+ if (HasName)
+ SectionSize += getULEB128Size(S.Name.size()) + S.Name.size();
+ // Pad the LEB value out to 5 bytes to make it a predictable size, and
+ // match the behavior of clang.
+ encodeULEB128(SectionSize, OS, 5);
+ if (HasName) {
+ encodeULEB128(S.Name.size(), OS);
+ OS << S.Name;
+ }
+ // Total section size is the content size plus 1 for the section type and
+ // 5 for the LEB-encoded size.
+ SectionSize = SectionSize + 1 + 5;
+ return Header;
+}
+
+size_t Writer::finalize() {
+ size_t ObjectSize = sizeof(WasmMagic) + sizeof(WasmVersion);
+ SectionHeaders.reserve(Obj.Sections.size());
+ // Finalize the headers of each section so we know the total size.
+ for (const Section &S : Obj.Sections) {
+ size_t SectionSize;
+ SectionHeaders.push_back(createSectionHeader(S, SectionSize));
+ ObjectSize += SectionSize;
+ }
+ return ObjectSize;
+}
+
+Error Writer::write() {
+ size_t FileSize = finalize();
+ if (Error E = Buf.allocate(FileSize))
+ return E;
+
+ // 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);
+
+ // 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);
+ }
+ return Buf.commit();
+}
+
+} // end namespace wasm
+} // end namespace objcopy
+} // end namespace llvm
diff --git a/llvm/tools/llvm-objcopy/wasm/Writer.h b/llvm/tools/llvm-objcopy/wasm/Writer.h
new file mode 100644
index 0000000000000..da48ee730c3ba
--- /dev/null
+++ b/llvm/tools/llvm-objcopy/wasm/Writer.h
@@ -0,0 +1,50 @@
+//===- Writer.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_WRITER_H
+#define LLVM_TOOLS_LLVM_OBJCOPY_WASM_WRITER_H
+
+#include "Buffer.h"
+#include "Object.h"
+#include <cstdint>
+#include <vector>
+
+namespace llvm {
+namespace objcopy {
+namespace wasm {
+
+class Writer {
+public:
+ Writer(Object &Obj, Buffer &Buf) : Obj(Obj), Buf(Buf) {}
+ Error write();
+
+private:
+ using SectionHeader = SmallVector<char, 8>;
+ Object &Obj;
+ Buffer &Buf;
+ std::vector<SectionHeader> SectionHeaders;
+
+ /// Generate a wasm section section header for S.
+ /// The header consists of
+ /// * A one-byte section ID (aka the section type).
+ /// * The size of the section contents, encoded as ULEB128.
+ /// * If the section is a custom section (type 0) it also has a name, which is
+ /// encoded as a length-prefixed string. The encoded section size *includes*
+ /// this string.
+ /// See https://webassembly.github.io/spec/core/binary/modules.html#sections
+ /// Return the header and store the total size in SectionSize.
+ static SectionHeader createSectionHeader(const Section &S,
+ size_t &SectionSize);
+ size_t finalize();
+};
+
+} // end namespace wasm
+} // end namespace objcopy
+} // end namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_OBJCOPY_WASM_WRITER_H
diff --git a/llvm/tools/llvm-objdump/COFFDump.cpp b/llvm/tools/llvm-objdump/COFFDump.cpp
index 60b0f5a3cbd15..b9d69d62e4e71 100644
--- a/llvm/tools/llvm-objdump/COFFDump.cpp
+++ b/llvm/tools/llvm-objdump/COFFDump.cpp
@@ -14,6 +14,8 @@
///
//===----------------------------------------------------------------------===//
+#include "COFFDump.h"
+
#include "llvm-objdump.h"
#include "llvm/Demangle/Demangle.h"
#include "llvm/Object/COFF.h"
@@ -24,10 +26,11 @@
#include "llvm/Support/WithColor.h"
#include "llvm/Support/raw_ostream.h"
+using namespace llvm;
+using namespace llvm::objdump;
using namespace llvm::object;
using namespace llvm::Win64EH;
-namespace llvm {
// Returns the name of the unwind code.
static StringRef getUnwindCodeTypeName(uint8_t Code) {
switch(Code) {
@@ -235,8 +238,8 @@ printSEHTable(const COFFObjectFile *Obj, uint32_t TableVA, int Count) {
return;
uintptr_t IntPtr = 0;
- if (std::error_code EC = Obj->getVaPtr(TableVA, IntPtr))
- reportError(errorCodeToError(EC), Obj->getFileName());
+ if (Error E = Obj->getVaPtr(TableVA, IntPtr))
+ reportError(std::move(E), Obj->getFileName());
const support::ulittle32_t *P = (const support::ulittle32_t *)IntPtr;
outs() << "SEH Table:";
@@ -274,17 +277,17 @@ static void printTLSDirectory(const COFFObjectFile *Obj) {
if (!PE32Header && !PE32PlusHeader)
return;
- const data_directory *DataDir;
- if (std::error_code EC = Obj->getDataDirectory(COFF::TLS_TABLE, DataDir))
- reportError(errorCodeToError(EC), Obj->getFileName());
+ const data_directory *DataDir = Obj->getDataDirectory(COFF::TLS_TABLE);
+ if (!DataDir)
+ reportError("missing data dir for TLS table", Obj->getFileName());
if (DataDir->RelativeVirtualAddress == 0)
return;
uintptr_t IntPtr = 0;
- if (std::error_code EC =
+ if (Error E =
Obj->getRvaPtr(DataDir->RelativeVirtualAddress, IntPtr))
- reportError(errorCodeToError(EC), Obj->getFileName());
+ reportError(std::move(E), Obj->getFileName());
if (PE32Header) {
auto *TLSDir = reinterpret_cast<const coff_tls_directory32 *>(IntPtr);
@@ -306,19 +309,17 @@ static void printLoadConfiguration(const COFFObjectFile *Obj) {
if (Obj->getMachine() != COFF::IMAGE_FILE_MACHINE_I386)
return;
- const data_directory *DataDir;
-
- if (std::error_code EC =
- Obj->getDataDirectory(COFF::LOAD_CONFIG_TABLE, DataDir))
- reportError(errorCodeToError(EC), Obj->getFileName());
+ const data_directory *DataDir = Obj->getDataDirectory(COFF::LOAD_CONFIG_TABLE);
+ if (!DataDir)
+ reportError("no load config data dir", Obj->getFileName());
uintptr_t IntPtr = 0;
if (DataDir->RelativeVirtualAddress == 0)
return;
- if (std::error_code EC =
+ if (Error E =
Obj->getRvaPtr(DataDir->RelativeVirtualAddress, IntPtr))
- reportError(errorCodeToError(EC), Obj->getFileName());
+ reportError(std::move(E), Obj->getFileName());
auto *LoadConf = reinterpret_cast<const coff_load_configuration32 *>(IntPtr);
outs() << "Load configuration:"
@@ -473,9 +474,9 @@ static bool getPDataSection(const COFFObjectFile *Obj,
return false;
}
-Error getCOFFRelocationValueString(const COFFObjectFile *Obj,
- const RelocationRef &Rel,
- SmallVectorImpl<char> &Result) {
+Error objdump::getCOFFRelocationValueString(const COFFObjectFile *Obj,
+ const RelocationRef &Rel,
+ SmallVectorImpl<char> &Result) {
symbol_iterator SymI = Rel.getSymbol();
Expected<StringRef> SymNameOrErr = SymI->getName();
if (!SymNameOrErr)
@@ -596,7 +597,7 @@ static void printRuntimeFunctionRels(const COFFObjectFile *Obj,
printWin64EHUnwindInfo(UI);
}
-void printCOFFUnwindInfo(const COFFObjectFile *Obj) {
+void objdump::printCOFFUnwindInfo(const COFFObjectFile *Obj) {
if (Obj->getMachine() != COFF::IMAGE_FILE_MACHINE_AMD64) {
WithColor::error(errs(), "llvm-objdump")
<< "unsupported image machine type "
@@ -625,7 +626,7 @@ void printCOFFUnwindInfo(const COFFObjectFile *Obj) {
}
}
-void printCOFFFileHeader(const object::ObjectFile *Obj) {
+void objdump::printCOFFFileHeader(const object::ObjectFile *Obj) {
const COFFObjectFile *file = dyn_cast<const COFFObjectFile>(Obj);
printTLSDirectory(file);
printLoadConfiguration(file);
@@ -633,7 +634,7 @@ void printCOFFFileHeader(const object::ObjectFile *Obj) {
printExportTable(file);
}
-void printCOFFSymbolTable(const object::COFFImportFile *i) {
+void objdump::printCOFFSymbolTable(const object::COFFImportFile *i) {
unsigned Index = 0;
bool IsCode = i->getCOFFImportHeader()->getType() == COFF::IMPORT_CODE;
@@ -656,15 +657,16 @@ void printCOFFSymbolTable(const object::COFFImportFile *i) {
}
}
-void printCOFFSymbolTable(const COFFObjectFile *coff) {
+void objdump::printCOFFSymbolTable(const COFFObjectFile *coff) {
for (unsigned SI = 0, SE = coff->getNumberOfSymbols(); SI != SE; ++SI) {
Expected<COFFSymbolRef> Symbol = coff->getSymbol(SI);
if (!Symbol)
reportError(Symbol.takeError(), coff->getFileName());
- StringRef Name;
- if (std::error_code EC = coff->getSymbolName(*Symbol, Name))
- reportError(errorCodeToError(EC), coff->getFileName());
+ Expected<StringRef> NameOrErr = coff->getSymbolName(*Symbol);
+ if (!NameOrErr)
+ reportError(NameOrErr.takeError(), coff->getFileName());
+ StringRef Name = *NameOrErr;
outs() << "[" << format("%2d", SI) << "]"
<< "(sec " << format("%2d", int(Symbol->getSectionNumber())) << ")"
@@ -676,11 +678,9 @@ void printCOFFSymbolTable(const COFFObjectFile *coff) {
<< "0x" << format("%08x", unsigned(Symbol->getValue())) << " "
<< Name;
if (Demangle && Name.startswith("?")) {
- char *DemangledSymbol = nullptr;
- size_t Size = 0;
int Status = -1;
- DemangledSymbol =
- microsoftDemangle(Name.data(), DemangledSymbol, &Size, &Status);
+ char *DemangledSymbol =
+ microsoftDemangle(Name.data(), nullptr, nullptr, nullptr, &Status);
if (Status == 0 && DemangledSymbol) {
outs() << " (" << StringRef(DemangledSymbol) << ")";
@@ -694,9 +694,9 @@ void printCOFFSymbolTable(const COFFObjectFile *coff) {
for (unsigned AI = 0, AE = Symbol->getNumberOfAuxSymbols(); AI < AE; ++AI, ++SI) {
if (Symbol->isSectionDefinition()) {
const coff_aux_section_definition *asd;
- if (std::error_code EC =
+ if (Error E =
coff->getAuxSymbol<coff_aux_section_definition>(SI + 1, asd))
- reportError(errorCodeToError(EC), coff->getFileName());
+ reportError(std::move(E), coff->getFileName());
int32_t AuxNumber = asd->getNumber(Symbol->isBigObj());
@@ -711,8 +711,8 @@ void printCOFFSymbolTable(const COFFObjectFile *coff) {
, unsigned(asd->Selection));
} else if (Symbol->isFileRecord()) {
const char *FileName;
- if (std::error_code EC = coff->getAuxSymbol<char>(SI + 1, FileName))
- reportError(errorCodeToError(EC), coff->getFileName());
+ if (Error E = coff->getAuxSymbol<char>(SI + 1, FileName))
+ reportError(std::move(E), coff->getFileName());
StringRef Name(FileName, Symbol->getNumberOfAuxSymbols() *
coff->getSymbolTableEntrySize());
@@ -722,9 +722,8 @@ void printCOFFSymbolTable(const COFFObjectFile *coff) {
break;
} else if (Symbol->isWeakExternal()) {
const coff_aux_weak_external *awe;
- if (std::error_code EC =
- coff->getAuxSymbol<coff_aux_weak_external>(SI + 1, awe))
- reportError(errorCodeToError(EC), coff->getFileName());
+ if (Error E = coff->getAuxSymbol<coff_aux_weak_external>(SI + 1, awe))
+ reportError(std::move(E), coff->getFileName());
outs() << "AUX " << format("indx %d srch %d\n",
static_cast<uint32_t>(awe->TagIndex),
@@ -735,4 +734,3 @@ void printCOFFSymbolTable(const COFFObjectFile *coff) {
}
}
}
-} // namespace llvm
diff --git a/llvm/tools/llvm-objdump/COFFDump.h b/llvm/tools/llvm-objdump/COFFDump.h
new file mode 100644
index 0000000000000..21f97bdeb83cd
--- /dev/null
+++ b/llvm/tools/llvm-objdump/COFFDump.h
@@ -0,0 +1,37 @@
+//===-- COFFDump.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_OBJDUMP_COFFDUMP_H
+#define LLVM_TOOLS_LLVM_OBJDUMP_COFFDUMP_H
+
+#include "llvm/ADT/SmallVector.h"
+
+namespace llvm {
+
+class Error;
+
+namespace object {
+class COFFObjectFile;
+class COFFImportFile;
+class ObjectFile;
+class RelocationRef;
+} // namespace object
+
+namespace objdump {
+Error getCOFFRelocationValueString(const object::COFFObjectFile *Obj,
+ const object::RelocationRef &Rel,
+ llvm::SmallVectorImpl<char> &Result);
+
+void printCOFFUnwindInfo(const object::COFFObjectFile *O);
+void printCOFFFileHeader(const object::ObjectFile *O);
+void printCOFFSymbolTable(const object::COFFImportFile *I);
+void printCOFFSymbolTable(const object::COFFObjectFile *O);
+} // namespace objdump
+} // namespace llvm
+
+#endif
diff --git a/llvm/tools/llvm-objdump/ELFDump.cpp b/llvm/tools/llvm-objdump/ELFDump.cpp
index abfe08346bbd7..602bc63882527 100644
--- a/llvm/tools/llvm-objdump/ELFDump.cpp
+++ b/llvm/tools/llvm-objdump/ELFDump.cpp
@@ -11,6 +11,8 @@
///
//===----------------------------------------------------------------------===//
+#include "ELFDump.h"
+
#include "llvm-objdump.h"
#include "llvm/Demangle/Demangle.h"
#include "llvm/Object/ELFObjectFile.h"
@@ -18,9 +20,10 @@
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/raw_ostream.h"
+using namespace llvm;
using namespace llvm::object;
+using namespace llvm::objdump;
-namespace llvm {
template <class ELFT>
static Expected<StringRef> getDynamicStrTab(const ELFFile<ELFT> *Elf) {
auto DynamicEntriesOrError = Elf->dynamicEntries();
@@ -98,7 +101,7 @@ static Error getRelocationValueString(const ELFObjectFile<ELFT> *Obj,
if (!SymName)
return SymName.takeError();
if (Demangle)
- Fmt << demangle(*SymName);
+ Fmt << demangle(std::string(*SymName));
else
Fmt << *SymName;
}
@@ -116,9 +119,9 @@ static Error getRelocationValueString(const ELFObjectFile<ELFT> *Obj,
return Error::success();
}
-Error getELFRelocationValueString(const ELFObjectFileBase *Obj,
- const RelocationRef &Rel,
- SmallVectorImpl<char> &Result) {
+Error objdump::getELFRelocationValueString(const ELFObjectFileBase *Obj,
+ const RelocationRef &Rel,
+ SmallVectorImpl<char> &Result) {
if (auto *ELF32LE = dyn_cast<ELF32LEObjectFile>(Obj))
return getRelocationValueString(ELF32LE, Rel, Result);
if (auto *ELF64LE = dyn_cast<ELF64LEObjectFile>(Obj))
@@ -147,7 +150,7 @@ static uint64_t getSectionLMA(const ELFFile<ELFT> *Obj,
return Sec.getAddress();
}
-uint64_t getELFSectionLMA(const object::ELFSectionRef &Sec) {
+uint64_t objdump::getELFSectionLMA(const object::ELFSectionRef &Sec) {
if (const auto *ELFObj = dyn_cast<ELF32LEObjectFile>(Sec.getObject()))
return getSectionLMA(ELFObj->getELFFile(), Sec);
else if (const auto *ELFObj = dyn_cast<ELF32BEObjectFile>(Sec.getObject()))
@@ -159,16 +162,23 @@ uint64_t getELFSectionLMA(const object::ELFSectionRef &Sec) {
}
template <class ELFT>
-void printDynamicSection(const ELFFile<ELFT> *Elf, StringRef Filename) {
+static void printDynamicSection(const ELFFile<ELFT> *Elf, StringRef Filename) {
ArrayRef<typename ELFT::Dyn> DynamicEntries =
unwrapOrError(Elf->dynamicEntries(), Filename);
+
+ // Find the maximum tag name length to format the value column properly.
+ size_t MaxLen = 0;
+ for (const typename ELFT::Dyn &Dyn : DynamicEntries)
+ MaxLen = std::max(MaxLen, Elf->getDynamicTagAsString(Dyn.d_tag).size());
+ std::string TagFmt = " %-" + std::to_string(MaxLen) + "s ";
+
outs() << "Dynamic Section:\n";
for (const typename ELFT::Dyn &Dyn : DynamicEntries) {
if (Dyn.d_tag == ELF::DT_NULL)
continue;
std::string Str = Elf->getDynamicTagAsString(Dyn.d_tag);
- outs() << format(" %-21s", Str.c_str());
+ outs() << format(TagFmt.c_str(), Str.c_str());
const char *Fmt =
ELFT::Is64Bits ? "0x%016" PRIx64 "\n" : "0x%08" PRIx64 "\n";
@@ -188,11 +198,17 @@ void printDynamicSection(const ELFFile<ELFT> *Elf, StringRef Filename) {
}
}
-template <class ELFT> void printProgramHeaders(const ELFFile<ELFT> *o) {
+template <class ELFT>
+static void printProgramHeaders(const ELFFile<ELFT> *Obj, StringRef FileName) {
outs() << "Program Header:\n";
- auto ProgramHeaderOrError = o->program_headers();
- if (!ProgramHeaderOrError)
- report_fatal_error(toString(ProgramHeaderOrError.takeError()));
+ auto ProgramHeaderOrError = Obj->program_headers();
+ if (!ProgramHeaderOrError) {
+ reportWarning("unable to read program headers: " +
+ toString(ProgramHeaderOrError.takeError()),
+ FileName);
+ return;
+ }
+
for (const typename ELFT::Phdr &Phdr : *ProgramHeaderOrError) {
switch (Phdr.p_type) {
case ELF::PT_DYNAMIC:
@@ -255,8 +271,8 @@ template <class ELFT> void printProgramHeaders(const ELFFile<ELFT> *o) {
}
template <class ELFT>
-void printSymbolVersionDependency(ArrayRef<uint8_t> Contents,
- StringRef StrTab) {
+static void printSymbolVersionDependency(ArrayRef<uint8_t> Contents,
+ StringRef StrTab) {
outs() << "Version References:\n";
const uint8_t *Buf = Contents.data();
@@ -280,9 +296,9 @@ void printSymbolVersionDependency(ArrayRef<uint8_t> Contents,
}
template <class ELFT>
-void printSymbolVersionDefinition(const typename ELFT::Shdr &Shdr,
- ArrayRef<uint8_t> Contents,
- StringRef StrTab) {
+static void printSymbolVersionDefinition(const typename ELFT::Shdr &Shdr,
+ ArrayRef<uint8_t> Contents,
+ StringRef StrTab) {
outs() << "Version definitions:\n";
const uint8_t *Buf = Contents.data();
@@ -312,7 +328,8 @@ void printSymbolVersionDefinition(const typename ELFT::Shdr &Shdr,
}
template <class ELFT>
-void printSymbolVersionInfo(const ELFFile<ELFT> *Elf, StringRef FileName) {
+static void printSymbolVersionInfo(const ELFFile<ELFT> *Elf,
+ StringRef FileName) {
ArrayRef<typename ELFT::Shdr> Sections =
unwrapOrError(Elf->sections(), FileName);
for (const typename ELFT::Shdr &Shdr : Sections) {
@@ -333,18 +350,18 @@ void printSymbolVersionInfo(const ELFFile<ELFT> *Elf, StringRef FileName) {
}
}
-void printELFFileHeader(const object::ObjectFile *Obj) {
+void objdump::printELFFileHeader(const object::ObjectFile *Obj) {
if (const auto *ELFObj = dyn_cast<ELF32LEObjectFile>(Obj))
- printProgramHeaders(ELFObj->getELFFile());
+ printProgramHeaders(ELFObj->getELFFile(), Obj->getFileName());
else if (const auto *ELFObj = dyn_cast<ELF32BEObjectFile>(Obj))
- printProgramHeaders(ELFObj->getELFFile());
+ printProgramHeaders(ELFObj->getELFFile(), Obj->getFileName());
else if (const auto *ELFObj = dyn_cast<ELF64LEObjectFile>(Obj))
- printProgramHeaders(ELFObj->getELFFile());
+ printProgramHeaders(ELFObj->getELFFile(), Obj->getFileName());
else if (const auto *ELFObj = dyn_cast<ELF64BEObjectFile>(Obj))
- printProgramHeaders(ELFObj->getELFFile());
+ printProgramHeaders(ELFObj->getELFFile(), Obj->getFileName());
}
-void printELFDynamicSection(const object::ObjectFile *Obj) {
+void objdump::printELFDynamicSection(const object::ObjectFile *Obj) {
if (const auto *ELFObj = dyn_cast<ELF32LEObjectFile>(Obj))
printDynamicSection(ELFObj->getELFFile(), Obj->getFileName());
else if (const auto *ELFObj = dyn_cast<ELF32BEObjectFile>(Obj))
@@ -355,7 +372,7 @@ void printELFDynamicSection(const object::ObjectFile *Obj) {
printDynamicSection(ELFObj->getELFFile(), Obj->getFileName());
}
-void printELFSymbolVersionInfo(const object::ObjectFile *Obj) {
+void objdump::printELFSymbolVersionInfo(const object::ObjectFile *Obj) {
if (const auto *ELFObj = dyn_cast<ELF32LEObjectFile>(Obj))
printSymbolVersionInfo(ELFObj->getELFFile(), Obj->getFileName());
else if (const auto *ELFObj = dyn_cast<ELF32BEObjectFile>(Obj))
@@ -365,4 +382,3 @@ void printELFSymbolVersionInfo(const object::ObjectFile *Obj) {
else if (const auto *ELFObj = dyn_cast<ELF64BEObjectFile>(Obj))
printSymbolVersionInfo(ELFObj->getELFFile(), Obj->getFileName());
}
-} // namespace llvm
diff --git a/llvm/tools/llvm-objdump/ELFDump.h b/llvm/tools/llvm-objdump/ELFDump.h
new file mode 100644
index 0000000000000..9b6b1f341cf3a
--- /dev/null
+++ b/llvm/tools/llvm-objdump/ELFDump.h
@@ -0,0 +1,39 @@
+//===-- ELFDump.h - ELF-specific dumper -------------------------*- 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_ELFDUMP_H
+#define LLVM_TOOLS_LLVM_OBJDUMP_ELFDUMP_H
+
+#include "llvm/ADT/SmallVector.h"
+
+namespace llvm {
+
+class Error;
+
+namespace object {
+class ELFObjectFileBase;
+class ELFSectionRef;
+class ObjectFile;
+class RelocationRef;
+} // namespace object
+
+namespace objdump {
+
+Error getELFRelocationValueString(const object::ELFObjectFileBase *Obj,
+ const object::RelocationRef &Rel,
+ llvm::SmallVectorImpl<char> &Result);
+uint64_t getELFSectionLMA(const object::ELFSectionRef &Sec);
+
+void printELFFileHeader(const object::ObjectFile *O);
+void printELFDynamicSection(const object::ObjectFile *Obj);
+void printELFSymbolVersionInfo(const object::ObjectFile *Obj);
+
+} // namespace objdump
+} // namespace llvm
+
+#endif
diff --git a/llvm/tools/llvm-objdump/MachODump.cpp b/llvm/tools/llvm-objdump/MachODump.cpp
index 87c7a92933f1d..6d46496ecd4ea 100644
--- a/llvm/tools/llvm-objdump/MachODump.cpp
+++ b/llvm/tools/llvm-objdump/MachODump.cpp
@@ -10,6 +10,8 @@
//
//===----------------------------------------------------------------------===//
+#include "MachODump.h"
+
#include "llvm-objdump.h"
#include "llvm-c/Disassembler.h"
#include "llvm/ADT/STLExtras.h"
@@ -56,53 +58,35 @@ extern "C" {
}
#endif
+using namespace llvm;
using namespace llvm::object;
+using namespace llvm::objdump;
-namespace llvm {
-
-cl::OptionCategory MachOCat("llvm-objdump MachO Specific Options");
-
-extern cl::opt<bool> ArchiveHeaders;
-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<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> SymbolTable;
-extern cl::opt<std::string> TripleName;
-extern cl::opt<bool> UnwindInfo;
+cl::OptionCategory objdump::MachOCat("llvm-objdump MachO Specific Options");
-cl::opt<bool>
- FirstPrivateHeader("private-header",
- cl::desc("Display only the first format specific file "
- "header"),
- cl::cat(MachOCat));
+cl::opt<bool> objdump::FirstPrivateHeader(
+ "private-header",
+ cl::desc("Display only the first format specific file header"),
+ cl::cat(MachOCat));
-cl::opt<bool> ExportsTrie("exports-trie",
- cl::desc("Display mach-o exported symbols"),
- cl::cat(MachOCat));
+cl::opt<bool> objdump::ExportsTrie("exports-trie",
+ cl::desc("Display mach-o exported symbols"),
+ cl::cat(MachOCat));
-cl::opt<bool> Rebase("rebase", cl::desc("Display mach-o rebasing info"),
- cl::cat(MachOCat));
+cl::opt<bool> objdump::Rebase("rebase",
+ cl::desc("Display mach-o rebasing info"),
+ cl::cat(MachOCat));
-cl::opt<bool> Bind("bind", cl::desc("Display mach-o binding info"),
- cl::cat(MachOCat));
+cl::opt<bool> objdump::Bind("bind", cl::desc("Display mach-o binding info"),
+ cl::cat(MachOCat));
-cl::opt<bool> LazyBind("lazy-bind",
- cl::desc("Display mach-o lazy 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> WeakBind("weak-bind",
- cl::desc("Display mach-o weak 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,
@@ -121,63 +105,65 @@ static cl::opt<bool> NoLeadingHeaders("no-leading-headers",
cl::desc("Print no leading headers"),
cl::cat(MachOCat));
-cl::opt<bool> UniversalHeaders("universal-headers",
- cl::desc("Print Mach-O universal headers "
- "(requires -macho)"),
- cl::cat(MachOCat));
-
-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> IndirectSymbols("indirect-symbols",
- cl::desc("Print indirect symbol table for Mach-O "
- "objects (requires -macho)"),
- cl::cat(MachOCat));
+cl::opt<bool> objdump::UniversalHeaders(
+ "universal-headers",
+ cl::desc("Print Mach-O universal headers (requires -macho)"),
+ cl::cat(MachOCat));
-cl::opt<bool>
- DataInCode("data-in-code",
- cl::desc("Print the data in code table for Mach-O objects "
- "(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> LinkOptHints("link-opt-hints",
- cl::desc("Print the linker optimization hints for "
- "Mach-O objects (requires -macho)"),
- 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> 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::DataInCode(
+ "data-in-code",
+ cl::desc(
+ "Print the data in code table for Mach-O objects (requires -macho)"),
+ cl::cat(MachOCat));
-cl::opt<bool> DylibsUsed("dylibs-used",
- cl::desc("Print the shared libraries used for linked "
- "Mach-O files (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>
- DylibId("dylib-id",
- cl::desc("Print the shared library's id for the dylib Mach-O "
- "file (requires -macho)"),
- cl::cat(MachOCat));
+ 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::desc("Print the info for Mach-O objects in non-verbose or "
+ "numeric form (requires -macho)"),
cl::cat(MachOCat));
cl::opt<bool>
- ObjcMetaData("objc-meta-data",
- cl::desc("Print the Objective-C runtime meta data for "
- "Mach-O files (requires -macho)"),
- cl::cat(MachOCat));
+ objdump::ObjcMetaData("objc-meta-data",
+ cl::desc("Print the Objective-C runtime meta data "
+ "for Mach-O files (requires -macho)"),
+ cl::cat(MachOCat));
-cl::opt<std::string> DisSymName(
+static cl::opt<std::string> DisSymName(
"dis-symname",
cl::desc("disassemble just this symbol's instructions (requires -macho)"),
cl::cat(MachOCat));
@@ -191,7 +177,7 @@ static cl::list<std::string>
ArchFlags("arch", cl::desc("architecture(s) from a Mach-O file to dump"),
cl::ZeroOrMore, cl::cat(MachOCat));
-bool ArchAll = false;
+static bool ArchAll = false;
static std::string ThumbTripleName;
@@ -233,6 +219,7 @@ static const Target *GetTarget(const MachOObjectFile *MachOObj,
return nullptr;
}
+namespace {
struct SymbolSorter {
bool operator()(const SymbolRef &A, const SymbolRef &B) {
Expected<SymbolRef::Type> ATypeOrErr = A.getType();
@@ -243,11 +230,14 @@ struct SymbolSorter {
if (!BTypeOrErr)
reportError(BTypeOrErr.takeError(), B.getObject()->getFileName());
SymbolRef::Type BType = *BTypeOrErr;
- uint64_t AAddr = (AType != SymbolRef::ST_Function) ? 0 : A.getValue();
- uint64_t BAddr = (BType != SymbolRef::ST_Function) ? 0 : B.getValue();
+ uint64_t AAddr =
+ (AType != SymbolRef::ST_Function) ? 0 : cantFail(A.getValue());
+ uint64_t BAddr =
+ (BType != SymbolRef::ST_Function) ? 0 : cantFail(B.getValue());
return AAddr < BAddr;
}
};
+} // namespace
// Types for the storted data in code table that is built before disassembly
// and the predicate function to sort them.
@@ -497,9 +487,9 @@ static void printRelocationTargetName(const MachOObjectFile *O,
Fmt << S;
}
-Error getMachORelocationValueString(const MachOObjectFile *Obj,
- const RelocationRef &RelRef,
- SmallVectorImpl<char> &Result) {
+Error objdump::getMachORelocationValueString(const MachOObjectFile *Obj,
+ const RelocationRef &RelRef,
+ SmallVectorImpl<char> &Result) {
DataRefImpl Rel = RelRef.getRawDataRefImpl();
MachO::any_relocation_info RE = Obj->getRelocation(Rel);
@@ -1279,7 +1269,7 @@ static void CreateSymbolAddressMap(MachOObjectFile *O,
SymbolRef::Type ST = unwrapOrError(Symbol.getType(), FileName);
if (ST == SymbolRef::ST_Function || ST == SymbolRef::ST_Data ||
ST == SymbolRef::ST_Other) {
- uint64_t Address = Symbol.getValue();
+ uint64_t Address = cantFail(Symbol.getValue());
StringRef SymName = unwrapOrError(Symbol.getName(), FileName);
if (!SymName.startswith(".objc"))
(*AddrMap)[Address] = SymName;
@@ -1754,6 +1744,9 @@ static void DumpSectionContents(StringRef Filename, MachOObjectFile *O,
else
consumeError(SecNameOrErr.takeError());
+ if (!DumpSection.empty())
+ FoundSectionSet.insert(DumpSection);
+
DataRefImpl Ref = Section.getRawDataRefImpl();
StringRef SegName = O->getSectionFinalSegmentName(Ref);
if ((DumpSegName.empty() || SegName == DumpSegName) &&
@@ -1776,8 +1769,9 @@ static void DumpSectionContents(StringRef Filename, MachOObjectFile *O,
uint32_t sect_size = BytesStr.size();
uint64_t sect_addr = Section.getAddress();
- outs() << "Contents of (" << SegName << "," << SectName
- << ") section\n";
+ if (!NoLeadingHeaders)
+ outs() << "Contents of (" << SegName << "," << SectName
+ << ") section\n";
if (verbose) {
if ((section_flags & MachO::S_ATTR_PURE_INSTRUCTIONS) ||
@@ -2318,7 +2312,7 @@ static bool ValidateArchFlags() {
// -arch flags selecting just those slices as specified by them and also parses
// archive files. Then for each individual Mach-O file ProcessMachO() is
// called to process the file based on the command line options.
-void parseInputMachO(StringRef Filename) {
+void objdump::parseInputMachO(StringRef Filename) {
if (!ValidateArchFlags())
return;
@@ -2376,7 +2370,7 @@ void parseInputMachO(StringRef Filename) {
llvm_unreachable("Input object can't be invalid at this point");
}
-void parseInputMachO(MachOUniversalBinary *UB) {
+void objdump::parseInputMachO(MachOUniversalBinary *UB) {
if (!ValidateArchFlags())
return;
@@ -2562,6 +2556,7 @@ void parseInputMachO(MachOUniversalBinary *UB) {
}
}
+namespace {
// The block of info used by the Symbolizer call backs.
struct DisassembleInfo {
DisassembleInfo(MachOObjectFile *O, SymbolAddressMap *AddrMap,
@@ -2581,6 +2576,7 @@ struct DisassembleInfo {
std::unique_ptr<SymbolAddressMap> bindtable;
uint32_t depth = 0;
};
+} // namespace
// SymbolizerGetOpInfo() is the operand information call back function.
// This is called to get the symbolic information for operand(s) of an
@@ -3358,7 +3354,7 @@ static const char *get_symbol_64(uint32_t sect_offset, SectionRef S,
// and return its name.
const char *SymbolName = nullptr;
if (reloc_found && isExtern) {
- n_value = Symbol.getValue();
+ n_value = cantFail(Symbol.getValue());
StringRef Name = unwrapOrError(Symbol.getName(), info->O->getFileName());
if (!Name.empty()) {
SymbolName = Name.data();
@@ -3389,6 +3385,8 @@ static const char *get_symbol_32(uint32_t sect_offset, SectionRef S,
return get_symbol_64(sect_offset, S, info, n_value64, ReferenceValue);
}
+namespace {
+
// These are structs in the Objective-C meta data and read to produce the
// comments for disassembly. While these are part of the ABI they are no
// public defintions. So the are here not in include/llvm/BinaryFormat/MachO.h
@@ -3974,6 +3972,8 @@ inline void swapStruct(struct objc_method_description_t &md) {
sys::swapByteOrder(md.types);
}
+} // namespace
+
static const char *get_dyld_bind_info_symbolname(uint64_t ReferenceValue,
struct DisassembleInfo *info);
@@ -6473,7 +6473,7 @@ static void DumpProtocolSection(MachOObjectFile *O, const char *sect,
}
#ifdef HAVE_LIBXAR
-inline void swapStruct(struct xar_header &xar) {
+static inline void swapStruct(struct xar_header &xar) {
sys::swapByteOrder(xar.magic);
sys::swapByteOrder(xar.size);
sys::swapByteOrder(xar.version);
@@ -6910,7 +6910,7 @@ static const char *GuessLiteralPointer(uint64_t ReferenceValue,
if (info->O->getAnyRelocationPCRel(RE)) {
unsigned Type = info->O->getAnyRelocationType(RE);
if (Type == MachO::X86_64_RELOC_SIGNED) {
- ReferenceValue = Symbol.getValue();
+ ReferenceValue = cantFail(Symbol.getValue());
}
}
}
@@ -7346,7 +7346,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
SmallString<1024> FullPath(DSYMPath);
llvm::sys::path::append(FullPath, "Contents", "Resources", "DWARF",
ShortName);
- DSYMPath = FullPath.str();
+ DSYMPath = std::string(FullPath.str());
}
// Load the file.
@@ -7451,7 +7451,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
unwrapOrError(Symbol.getType(), MachOOF->getFileName());
if (ST == SymbolRef::ST_Function || ST == SymbolRef::ST_Data ||
ST == SymbolRef::ST_Other) {
- uint64_t Address = Symbol.getValue();
+ uint64_t Address = cantFail(Symbol.getValue());
StringRef SymName =
unwrapOrError(Symbol.getName(), MachOOF->getFileName());
AddrMap[Address] = SymName;
@@ -7530,7 +7530,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
// Start at the address of the symbol relative to the section's address.
uint64_t SectSize = Sections[SectIdx].getSize();
- uint64_t Start = Symbols[SymIdx].getValue();
+ uint64_t Start = cantFail(Symbols[SymIdx].getValue());
uint64_t SectionAddress = Sections[SectIdx].getAddress();
Start -= SectionAddress;
@@ -7551,7 +7551,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
if (NextSymType == SymbolRef::ST_Function) {
containsNextSym =
Sections[SectIdx].containsSymbol(Symbols[NextSymIdx]);
- NextSym = Symbols[NextSymIdx].getValue();
+ NextSym = cantFail(Symbols[NextSymIdx].getValue());
NextSym -= SectionAddress;
break;
}
@@ -7564,7 +7564,8 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
symbolTableWorked = true;
DataRefImpl Symb = Symbols[SymIdx].getRawDataRefImpl();
- bool IsThumb = MachOOF->getSymbolFlags(Symb) & SymbolRef::SF_Thumb;
+ uint32_t SymbolFlags = cantFail(MachOOF->getSymbolFlags(Symb));
+ bool IsThumb = SymbolFlags & SymbolRef::SF_Thumb;
// We only need the dedicated Thumb target if there's a real choice
// (i.e. we're not targeting M-class) and the function is Thumb.
@@ -8194,7 +8195,7 @@ static void printMachOUnwindInfoSection(const MachOObjectFile *Obj,
}
}
-void printMachOUnwindInfo(const MachOObjectFile *Obj) {
+void objdump::printMachOUnwindInfo(const MachOObjectFile *Obj) {
std::map<uint64_t, SymbolRef> Symbols;
for (const SymbolRef &SymRef : Obj->symbols()) {
// Discard any undefined or absolute symbols. They're not going to take part
@@ -8209,7 +8210,7 @@ void printMachOUnwindInfo(const MachOObjectFile *Obj) {
if (Section == Obj->section_end())
continue;
- uint64_t Addr = SymRef.getValue();
+ uint64_t Addr = cantFail(SymRef.getValue());
Symbols.insert(std::make_pair(Addr, SymRef));
}
@@ -10202,12 +10203,12 @@ static void PrintMachHeader(const MachOObjectFile *Obj, bool verbose) {
}
}
-void printMachOFileHeader(const object::ObjectFile *Obj) {
+void objdump::printMachOFileHeader(const object::ObjectFile *Obj) {
const MachOObjectFile *file = dyn_cast<const MachOObjectFile>(Obj);
PrintMachHeader(file, !NonVerbose);
}
-void printMachOLoadCommands(const object::ObjectFile *Obj) {
+void objdump::printMachOLoadCommands(const object::ObjectFile *Obj) {
const MachOObjectFile *file = dyn_cast<const MachOObjectFile>(Obj);
uint32_t filetype = 0;
uint32_t cputype = 0;
@@ -10229,7 +10230,7 @@ void printMachOLoadCommands(const object::ObjectFile *Obj) {
// export trie dumping
//===----------------------------------------------------------------------===//
-void printMachOExportsTrie(const object::MachOObjectFile *Obj) {
+static void printMachOExportsTrie(const object::MachOObjectFile *Obj) {
uint64_t BaseSegmentAddress = 0;
for (const auto &Command : Obj->load_commands()) {
if (Command.C.cmd == MachO::LC_SEGMENT) {
@@ -10308,7 +10309,7 @@ void printMachOExportsTrie(const object::MachOObjectFile *Obj) {
// rebase table dumping
//===----------------------------------------------------------------------===//
-void printMachORebaseTable(object::MachOObjectFile *Obj) {
+static void printMachORebaseTable(object::MachOObjectFile *Obj) {
outs() << "segment section address type\n";
Error Err = Error::success();
for (const object::MachORebaseEntry &Entry : Obj->rebaseTable(Err)) {
@@ -10350,7 +10351,7 @@ static StringRef ordinalName(const object::MachOObjectFile *Obj, int Ordinal) {
// bind table dumping
//===----------------------------------------------------------------------===//
-void printMachOBindTable(object::MachOObjectFile *Obj) {
+static void printMachOBindTable(object::MachOObjectFile *Obj) {
// Build table of sections so names can used in final output.
outs() << "segment section address type "
"addend dylib symbol\n";
@@ -10381,7 +10382,7 @@ void printMachOBindTable(object::MachOObjectFile *Obj) {
// lazy bind table dumping
//===----------------------------------------------------------------------===//
-void printMachOLazyBindTable(object::MachOObjectFile *Obj) {
+static void printMachOLazyBindTable(object::MachOObjectFile *Obj) {
outs() << "segment section address "
"dylib symbol\n";
Error Err = Error::success();
@@ -10406,7 +10407,7 @@ void printMachOLazyBindTable(object::MachOObjectFile *Obj) {
// weak bind table dumping
//===----------------------------------------------------------------------===//
-void printMachOWeakBindTable(object::MachOObjectFile *Obj) {
+static void printMachOWeakBindTable(object::MachOObjectFile *Obj) {
outs() << "segment section address "
"type addend symbol\n";
Error Err = Error::success();
@@ -10456,7 +10457,7 @@ static const char *get_dyld_bind_info_symbolname(uint64_t ReferenceValue,
return !name.empty() ? name.data() : nullptr;
}
-void printLazyBindTable(ObjectFile *o) {
+void objdump::printLazyBindTable(ObjectFile *o) {
outs() << "Lazy bind table:\n";
if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o))
printMachOLazyBindTable(MachO);
@@ -10466,7 +10467,7 @@ void printLazyBindTable(ObjectFile *o) {
"for Mach-O executable files.\n";
}
-void printWeakBindTable(ObjectFile *o) {
+void objdump::printWeakBindTable(ObjectFile *o) {
outs() << "Weak bind table:\n";
if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o))
printMachOWeakBindTable(MachO);
@@ -10476,7 +10477,7 @@ void printWeakBindTable(ObjectFile *o) {
"for Mach-O executable files.\n";
}
-void printExportsTrie(const ObjectFile *o) {
+void objdump::printExportsTrie(const ObjectFile *o) {
outs() << "Exports trie:\n";
if (const MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o))
printMachOExportsTrie(MachO);
@@ -10486,7 +10487,7 @@ void printExportsTrie(const ObjectFile *o) {
"for Mach-O executable files.\n";
}
-void printRebaseTable(ObjectFile *o) {
+void objdump::printRebaseTable(ObjectFile *o) {
outs() << "Rebase table:\n";
if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o))
printMachORebaseTable(MachO);
@@ -10496,7 +10497,7 @@ void printRebaseTable(ObjectFile *o) {
"for Mach-O executable files.\n";
}
-void printBindTable(ObjectFile *o) {
+void objdump::printBindTable(ObjectFile *o) {
outs() << "Bind table:\n";
if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o))
printMachOBindTable(MachO);
@@ -10505,4 +10506,3 @@ void printBindTable(ObjectFile *o) {
<< "This operation is only currently supported "
"for Mach-O executable files.\n";
}
-} // namespace llvm
diff --git a/llvm/tools/llvm-objdump/MachODump.h b/llvm/tools/llvm-objdump/MachODump.h
new file mode 100644
index 0000000000000..adf6c3404f790
--- /dev/null
+++ b/llvm/tools/llvm-objdump/MachODump.h
@@ -0,0 +1,66 @@
+//===-- MachODump.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_OBJDUMP_MACHODUMP_H
+#define LLVM_TOOLS_LLVM_OBJDUMP_MACHODUMP_H
+
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/CommandLine.h"
+
+namespace llvm {
+
+class Error;
+class StringRef;
+
+namespace object {
+class MachOObjectFile;
+class MachOUniversalBinary;
+class ObjectFile;
+class RelocationRef;
+} // namespace object
+
+namespace objdump {
+
+// 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;
+
+Error getMachORelocationValueString(const object::MachOObjectFile *Obj,
+ const object::RelocationRef &RelRef,
+ llvm::SmallVectorImpl<char> &Result);
+
+void parseInputMachO(StringRef Filename);
+void parseInputMachO(object::MachOUniversalBinary *UB);
+
+void printMachOUnwindInfo(const object::MachOObjectFile *O);
+void printMachOFileHeader(const object::ObjectFile *O);
+void printMachOLoadCommands(const object::ObjectFile *O);
+
+void printExportsTrie(const object::ObjectFile *O);
+void printRebaseTable(object::ObjectFile *O);
+void printBindTable(object::ObjectFile *O);
+void printLazyBindTable(object::ObjectFile *O);
+void printWeakBindTable(object::ObjectFile *O);
+
+} // namespace objdump
+} // namespace llvm
+
+#endif
diff --git a/llvm/tools/llvm-objdump/WasmDump.cpp b/llvm/tools/llvm-objdump/WasmDump.cpp
index da27a4acbb5fb..28311361d97e9 100644
--- a/llvm/tools/llvm-objdump/WasmDump.cpp
+++ b/llvm/tools/llvm-objdump/WasmDump.cpp
@@ -11,13 +11,15 @@
///
//===----------------------------------------------------------------------===//
+#include "WasmDump.h"
+
#include "llvm-objdump.h"
#include "llvm/Object/Wasm.h"
+using namespace llvm;
using namespace llvm::object;
-namespace llvm {
-void printWasmFileHeader(const object::ObjectFile *Obj) {
+void objdump::printWasmFileHeader(const object::ObjectFile *Obj) {
const auto *File = dyn_cast<const WasmObjectFile>(Obj);
outs() << "Program Header:\n";
@@ -26,9 +28,9 @@ void printWasmFileHeader(const object::ObjectFile *Obj) {
outs() << "\n";
}
-Error getWasmRelocationValueString(const WasmObjectFile *Obj,
- const RelocationRef &RelRef,
- SmallVectorImpl<char> &Result) {
+Error objdump::getWasmRelocationValueString(const WasmObjectFile *Obj,
+ const RelocationRef &RelRef,
+ SmallVectorImpl<char> &Result) {
const wasm::WasmRelocation &Rel = Obj->getWasmRelocation(RelRef);
symbol_iterator SI = RelRef.getSymbol();
std::string FmtBuf;
@@ -49,4 +51,3 @@ Error getWasmRelocationValueString(const WasmObjectFile *Obj,
Result.append(FmtBuf.begin(), FmtBuf.end());
return Error::success();
}
-} // namespace llvm
diff --git a/llvm/tools/llvm-objdump/WasmDump.h b/llvm/tools/llvm-objdump/WasmDump.h
new file mode 100644
index 0000000000000..03ff9aed8e07b
--- /dev/null
+++ b/llvm/tools/llvm-objdump/WasmDump.h
@@ -0,0 +1,35 @@
+//===-- WasmDump.h - wasm-specific dumper -----------------------*- 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_WASMDUMP_H
+#define LLVM_TOOLS_LLVM_OBJDUMP_WASMDUMP_H
+
+#include "llvm/ADT/SmallVector.h"
+
+namespace llvm {
+
+class Error;
+
+namespace object {
+class WasmObjectFile;
+class ObjectFile;
+class RelocationRef;
+} // namespace object
+
+namespace objdump {
+
+Error getWasmRelocationValueString(const object::WasmObjectFile *Obj,
+ const object::RelocationRef &RelRef,
+ llvm::SmallVectorImpl<char> &Result);
+
+void printWasmFileHeader(const object::ObjectFile *O);
+
+} // namespace objdump
+} // namespace llvm
+
+#endif
diff --git a/llvm/tools/llvm-objdump/XCOFFDump.cpp b/llvm/tools/llvm-objdump/XCOFFDump.cpp
new file mode 100644
index 0000000000000..df37abbd38813
--- /dev/null
+++ b/llvm/tools/llvm-objdump/XCOFFDump.cpp
@@ -0,0 +1,88 @@
+//===-- XCOFFDump.cpp - XCOFF-specific dumper -----------------------------===//
+//
+// 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 the XCOFF-specific dumper for llvm-objdump.
+///
+//===----------------------------------------------------------------------===//
+
+#include "XCOFFDump.h"
+
+#include "llvm-objdump.h"
+#include "llvm/Demangle/Demangle.h"
+
+using namespace llvm;
+using namespace llvm::object;
+
+Error objdump::getXCOFFRelocationValueString(const XCOFFObjectFile *Obj,
+ const RelocationRef &Rel,
+ SmallVectorImpl<char> &Result) {
+ symbol_iterator SymI = Rel.getSymbol();
+ if (SymI == Obj->symbol_end())
+ return make_error<GenericBinaryError>(
+ "invalid symbol reference in relocation entry",
+ object_error::parse_failed);
+
+ Expected<StringRef> SymNameOrErr = SymI->getName();
+ if (!SymNameOrErr)
+ return SymNameOrErr.takeError();
+
+ std::string SymName = (*SymNameOrErr).str();
+ if (Demangle)
+ SymName = demangle(SymName);
+
+ if (SymbolDescription)
+ SymName = getXCOFFSymbolDescription(createSymbolInfo(Obj, *SymI), SymName);
+
+ Result.append(SymName.begin(), SymName.end());
+ return Error::success();
+}
+
+Optional<XCOFF::StorageMappingClass>
+objdump::getXCOFFSymbolCsectSMC(const XCOFFObjectFile *Obj,
+ const SymbolRef &Sym) {
+ XCOFFSymbolRef SymRef(Sym.getRawDataRefImpl(), Obj);
+
+ if (SymRef.hasCsectAuxEnt())
+ return SymRef.getXCOFFCsectAuxEnt32()->StorageMappingClass;
+
+ return None;
+}
+
+bool objdump::isLabel(const XCOFFObjectFile *Obj, const SymbolRef &Sym) {
+
+ XCOFFSymbolRef SymRef(Sym.getRawDataRefImpl(), Obj);
+
+ if (SymRef.hasCsectAuxEnt())
+ return SymRef.getXCOFFCsectAuxEnt32()->isLabel();
+
+ return false;
+}
+
+std::string objdump::getXCOFFSymbolDescription(const SymbolInfoTy &SymbolInfo,
+ StringRef SymbolName) {
+ assert(SymbolInfo.isXCOFF() && "Must be a XCOFFSymInfo.");
+
+ std::string Result;
+ // Dummy symbols have no symbol index.
+ if (SymbolInfo.XCOFFSymInfo.Index)
+ Result = ("(idx: " + Twine(SymbolInfo.XCOFFSymInfo.Index.getValue()) +
+ ") " + SymbolName)
+ .str();
+ else
+ Result.append(SymbolName.begin(), SymbolName.end());
+
+ if (SymbolInfo.XCOFFSymInfo.StorageMappingClass &&
+ !SymbolInfo.XCOFFSymInfo.IsLabel) {
+ const XCOFF::StorageMappingClass Smc =
+ SymbolInfo.XCOFFSymInfo.StorageMappingClass.getValue();
+ Result.append(("[" + XCOFF::getMappingClassString(Smc) + "]").str());
+ }
+
+ return Result;
+}
diff --git a/llvm/tools/llvm-objdump/XCOFFDump.h b/llvm/tools/llvm-objdump/XCOFFDump.h
new file mode 100644
index 0000000000000..dbf520021594b
--- /dev/null
+++ b/llvm/tools/llvm-objdump/XCOFFDump.h
@@ -0,0 +1,33 @@
+//===-- XCOFFDump.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_OBJDUMP_XCOFFDUMP_H
+#define LLVM_TOOLS_LLVM_OBJDUMP_XCOFFDUMP_H
+
+#include "llvm/Object/XCOFFObjectFile.h"
+
+namespace llvm {
+
+struct SymbolInfoTy;
+
+namespace objdump {
+Optional<XCOFF::StorageMappingClass>
+getXCOFFSymbolCsectSMC(const object::XCOFFObjectFile *Obj,
+ const object::SymbolRef &Sym);
+
+bool isLabel(const object::XCOFFObjectFile *Obj, const object::SymbolRef &Sym);
+
+std::string getXCOFFSymbolDescription(const SymbolInfoTy &SymbolInfo,
+ StringRef SymbolName);
+
+Error getXCOFFRelocationValueString(const object::XCOFFObjectFile *Obj,
+ const object::RelocationRef &RelRef,
+ llvm::SmallVectorImpl<char> &Result);
+} // namespace objdump
+} // namespace llvm
+#endif
diff --git a/llvm/tools/llvm-objdump/llvm-objdump.cpp b/llvm/tools/llvm-objdump/llvm-objdump.cpp
index 6bd37a1fb86c9..320bbb5d358b9 100644
--- a/llvm/tools/llvm-objdump/llvm-objdump.cpp
+++ b/llvm/tools/llvm-objdump/llvm-objdump.cpp
@@ -16,7 +16,14 @@
//===----------------------------------------------------------------------===//
#include "llvm-objdump.h"
+#include "COFFDump.h"
+#include "ELFDump.h"
+#include "MachODump.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/StringExtras.h"
@@ -70,28 +77,13 @@
#include <unordered_map>
#include <utility>
+using namespace llvm;
using namespace llvm::object;
+using namespace llvm::objdump;
-namespace llvm {
-
-cl::OptionCategory ObjdumpCat("llvm-objdump Options");
-
-// MachO specific
-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;
+#define DEBUG_TYPE "objdump"
+
+static cl::OptionCategory ObjdumpCat("llvm-objdump Options");
static cl::opt<uint64_t> AdjustVMA(
"adjust-vma",
@@ -112,21 +104,22 @@ static cl::opt<std::string>
"see -version for available targets"),
cl::cat(ObjdumpCat));
-cl::opt<bool> ArchiveHeaders("archive-headers",
- cl::desc("Display archive header information"),
- 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> Demangle("demangle", cl::desc("Demangle symbols names"),
- cl::init(false), cl::cat(ObjdumpCat));
+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> Disassemble(
+cl::opt<bool> objdump::Disassemble(
"disassemble",
cl::desc("Display assembler mnemonics for the machine instructions"),
cl::cat(ObjdumpCat));
@@ -134,7 +127,7 @@ static cl::alias DisassembleShort("d", cl::desc("Alias for --disassemble"),
cl::NotHidden, cl::Grouping,
cl::aliasopt(Disassemble));
-cl::opt<bool> DisassembleAll(
+cl::opt<bool> objdump::DisassembleAll(
"disassemble-all",
cl::desc("Display assembler mnemonics for the machine instructions"),
cl::cat(ObjdumpCat));
@@ -143,12 +136,18 @@ static cl::alias DisassembleAllShort("D",
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>
- DisassembleFunctions("disassemble-functions", cl::CommaSeparated,
- cl::desc("List of functions to disassemble. "
- "Accept demangled names when --demangle is "
- "specified, otherwise accept mangled names"),
- cl::cat(ObjdumpCat));
+ 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",
@@ -170,7 +169,7 @@ static cl::alias
cl::CommaSeparated,
cl::aliasopt(DisassemblerOptions));
-cl::opt<DIDumpType> DwarfDumpType(
+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));
@@ -197,9 +196,10 @@ static cl::alias FileHeadersShort("f", cl::desc("Alias for --file-headers"),
cl::NotHidden, cl::Grouping,
cl::aliasopt(FileHeaders));
-cl::opt<bool> SectionContents("full-contents",
- cl::desc("Display the content of each section"),
- cl::cat(ObjdumpCat));
+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,
@@ -225,24 +225,24 @@ static cl::opt<bool> MachOOpt("macho",
static cl::alias MachOm("m", cl::desc("Alias for --macho"), cl::NotHidden,
cl::Grouping, cl::aliasopt(MachOOpt));
-cl::opt<std::string>
- MCPU("mcpu",
- cl::desc("Target a specific cpu type (-mcpu=help for details)"),
- cl::value_desc("cpu-name"), cl::init(""), cl::cat(ObjdumpCat));
+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> MAttrs("mattr", cl::CommaSeparated,
- cl::desc("Target specific attributes"),
- cl::value_desc("a1,+a2,-a3,..."),
- cl::cat(ObjdumpCat));
+cl::list<std::string> objdump::MAttrs("mattr", cl::CommaSeparated,
+ cl::desc("Target specific attributes"),
+ cl::value_desc("a1,+a2,-a3,..."),
+ cl::cat(ObjdumpCat));
-cl::opt<bool> NoShowRawInsn("no-show-raw-insn",
- cl::desc("When disassembling "
- "instructions, do not print "
- "the instruction bytes."),
- cl::cat(ObjdumpCat));
-cl::opt<bool> NoLeadingAddr("no-leading-addr",
- cl::desc("Print no leading address"),
- 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",
@@ -250,37 +250,40 @@ static cl::opt<bool> RawClangAST(
cl::cat(ObjdumpCat));
cl::opt<bool>
- Relocations("reloc", cl::desc("Display the relocation entries in the file"),
- cl::cat(ObjdumpCat));
+ 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> PrintImmHex("print-imm-hex",
- cl::desc("Use hex format for immediate values"),
- cl::cat(ObjdumpCat));
+cl::opt<bool>
+ objdump::PrintImmHex("print-imm-hex",
+ cl::desc("Use hex format for immediate values"),
+ cl::cat(ObjdumpCat));
-cl::opt<bool> PrivateHeaders("private-headers",
- cl::desc("Display format specific file headers"),
- 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>
- FilterSections("section",
- cl::desc("Operate on the specified sections only. "
- "With -macho dump segment,section"),
- cl::cat(ObjdumpCat));
+ 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> SectionHeaders("section-headers",
- cl::desc("Display summaries of the "
- "headers for each section."),
- cl::cat(ObjdumpCat));
+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,
@@ -312,19 +315,30 @@ static cl::opt<uint64_t> StopAddress("stop-address",
cl::value_desc("address"),
cl::init(UINT64_MAX), cl::cat(ObjdumpCat));
-cl::opt<bool> SymbolTable("syms", cl::desc("Display the symbol table"),
- 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));
-cl::opt<std::string> TripleName("triple",
- cl::desc("Target triple to disassemble for, "
- "see -version for available targets"),
- 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<bool> UnwindInfo("unwind-info", cl::desc("Display unwind information"),
- cl::cat(ObjdumpCat));
+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));
@@ -334,15 +348,35 @@ static cl::opt<bool>
cl::cat(ObjdumpCat));
static cl::alias WideShort("w", cl::Grouping, cl::aliasopt(Wide));
+enum DebugVarsFormat {
+ DVDisabled,
+ DVUnicode,
+ DVASCII,
+};
+
+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");
-static StringSet<> DisasmFuncsSet;
-static StringSet<> FoundSectionSet;
+static StringSet<> DisasmSymbolSet;
+StringSet<> objdump::FoundSectionSet;
static StringRef ToolName;
-typedef std::vector<std::tuple<uint64_t, StringRef, uint8_t>> SectionSymbolsTy;
-
namespace {
struct FilterResult {
// True if the section should not be skipped.
@@ -378,7 +412,8 @@ static FilterResult checkSectionFilter(object::SectionRef S) {
/*IncrementIndex=*/true};
}
-SectionFilter ToolSectionFilter(object::ObjectFile const &O, uint64_t *Idx) {
+SectionFilter objdump::ToolSectionFilter(object::ObjectFile const &O,
+ uint64_t *Idx) {
// Start at UINT64_MAX so that the first index returned after an increment is
// zero (after the unsigned wrap).
if (Idx)
@@ -393,35 +428,37 @@ SectionFilter ToolSectionFilter(object::ObjectFile const &O, uint64_t *Idx) {
O);
}
-std::string getFileNameForError(const object::Archive::Child &C,
- unsigned Index) {
+std::string objdump::getFileNameForError(const object::Archive::Child &C,
+ unsigned Index) {
Expected<StringRef> NameOrErr = C.getName();
if (NameOrErr)
- return NameOrErr.get();
+ return std::string(NameOrErr.get());
// If we have an error getting the name then we print the index of the archive
// member. Since we are already in an error state, we just ignore this error.
consumeError(NameOrErr.takeError());
return "<file index: " + std::to_string(Index) + ">";
}
-void reportWarning(Twine Message, StringRef File) {
+void objdump::reportWarning(Twine Message, StringRef File) {
// Output order between errs() and outs() matters especially for archive
// files where the output is per member object.
outs().flush();
WithColor::warning(errs(), ToolName)
<< "'" << File << "': " << Message << "\n";
- errs().flush();
}
-LLVM_ATTRIBUTE_NORETURN void reportError(StringRef File, Twine Message) {
+LLVM_ATTRIBUTE_NORETURN void objdump::reportError(StringRef File,
+ Twine Message) {
+ outs().flush();
WithColor::error(errs(), ToolName) << "'" << File << "': " << Message << "\n";
exit(1);
}
-LLVM_ATTRIBUTE_NORETURN void reportError(Error E, StringRef FileName,
- StringRef ArchiveName,
- StringRef ArchitectureName) {
+LLVM_ATTRIBUTE_NORETURN void objdump::reportError(Error E, StringRef FileName,
+ StringRef ArchiveName,
+ StringRef ArchitectureName) {
assert(E);
+ outs().flush();
WithColor::error(errs(), ToolName);
if (ArchiveName != "")
errs() << ArchiveName << "(" << FileName << ")";
@@ -429,11 +466,8 @@ LLVM_ATTRIBUTE_NORETURN void reportError(Error E, StringRef FileName,
errs() << "'" << FileName << "'";
if (!ArchitectureName.empty())
errs() << " (for architecture " << ArchitectureName << ")";
- std::string Buf;
- raw_string_ostream OS(Buf);
- logAllUnhandledErrors(std::move(E), OS);
- OS.flush();
- errs() << ": " << Buf;
+ errs() << ": ";
+ logAllUnhandledErrors(std::move(E), errs());
exit(1);
}
@@ -487,7 +521,7 @@ static const Target *getTarget(const ObjectFile *Obj) {
return TheTarget;
}
-bool isRelocAddressLess(RelocationRef A, RelocationRef B) {
+bool objdump::isRelocAddressLess(RelocationRef A, RelocationRef B) {
return A.getOffset() < B.getOffset();
}
@@ -502,6 +536,8 @@ static Error getRelocationValueString(const RelocationRef &Rel,
return getWasmRelocationValueString(Wasm, Rel, Result);
if (auto *MachO = dyn_cast<MachOObjectFile>(Obj))
return getMachORelocationValueString(MachO, Rel, Result);
+ if (auto *XCOFF = dyn_cast<XCOFFObjectFile>(Obj))
+ return getXCOFFRelocationValueString(XCOFF, Rel, Result);
llvm_unreachable("unknown object file format");
}
@@ -538,6 +574,358 @@ static bool getHidden(RelocationRef RelRef) {
}
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 ? "^" : u8"\u2548";
+ case LineChar::RangeMid:
+ return IsASCII ? "|" : u8"\u2503";
+ case LineChar::RangeEnd:
+ return IsASCII ? "v" : u8"\u253b";
+ case LineChar::LabelVert:
+ return IsASCII ? "|" : u8"\u2502";
+ case LineChar::LabelCornerNew:
+ return IsASCII ? "/" : u8"\u250c";
+ case LineChar::LabelCornerActive:
+ return IsASCII ? "|" : u8"\u2520";
+ case LineChar::LabelHoriz:
+ return IsASCII ? "-" : 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;
@@ -555,20 +943,29 @@ protected:
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::None;
- SymbolizerOpts.Demangle = false;
- SymbolizerOpts.DefaultArch = DefaultArch;
+ 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(raw_ostream &OS,
+ virtual void printSourceLine(formatted_raw_ostream &OS,
object::SectionedAddress Address,
StringRef ObjectFilename,
+ LiveVariablePrinter &LVP,
StringRef Delimiter = "; ");
};
@@ -602,9 +999,10 @@ bool SourcePrinter::cacheSource(const DILineInfo &LineInfo) {
return true;
}
-void SourcePrinter::printSourceLine(raw_ostream &OS,
+void SourcePrinter::printSourceLine(formatted_raw_ostream &OS,
object::SectionedAddress Address,
StringRef ObjectFilename,
+ LiveVariablePrinter &LVP,
StringRef Delimiter) {
if (!Symbolizer)
return;
@@ -626,34 +1024,62 @@ void SourcePrinter::printSourceLine(raw_ostream &OS,
reportWarning(Warning, ObjectFilename);
WarnedNoDebugInfo = true;
}
- return;
}
- if (LineInfo.Line == 0 || ((OldLineInfo.Line == LineInfo.Line) &&
- (OldLineInfo.FileName == LineInfo.FileName)))
+ 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 (PrintLines)
- OS << Delimiter << LineInfo.FileName << ":" << LineInfo.Line << "\n";
- if (PrintSource) {
- 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] << '\n';
+ 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);
}
- OldLineInfo = LineInfo;
}
static bool isAArch64Elf(const ObjectFile *Obj) {
@@ -670,28 +1096,30 @@ static bool hasMappingSymbols(const ObjectFile *Obj) {
return isArmElf(Obj) || isAArch64Elf(Obj);
}
-static void printRelocation(StringRef FileName, const RelocationRef &Rel,
- uint64_t Address, bool Is64Bits) {
+static void printRelocation(formatted_raw_ostream &OS, StringRef FileName,
+ const RelocationRef &Rel, uint64_t Address,
+ bool Is64Bits) {
StringRef Fmt = Is64Bits ? "\t\t%016" PRIx64 ": " : "\t\t\t%08" PRIx64 ": ";
SmallString<16> Name;
SmallString<32> Val;
Rel.getTypeName(Name);
if (Error E = getRelocationValueString(Rel, Val))
reportError(std::move(E), FileName);
- outs() << format(Fmt.data(), Address) << Name << "\t" << Val << "\n";
+ OS << format(Fmt.data(), Address) << Name << "\t" << Val;
}
class PrettyPrinter {
public:
virtual ~PrettyPrinter() = default;
- virtual void printInst(MCInstPrinter &IP, const MCInst *MI,
- ArrayRef<uint8_t> Bytes,
- object::SectionedAddress Address, raw_ostream &OS,
- StringRef Annot, MCSubtargetInfo const &STI,
- SourcePrinter *SP, StringRef ObjectFilename,
- std::vector<RelocationRef> *Rels = nullptr) {
+ virtual void
+ printInst(MCInstPrinter &IP, const MCInst *MI, ArrayRef<uint8_t> Bytes,
+ object::SectionedAddress Address, formatted_raw_ostream &OS,
+ StringRef Annot, MCSubtargetInfo const &STI, SourcePrinter *SP,
+ StringRef ObjectFilename, std::vector<RelocationRef> *Rels,
+ LiveVariablePrinter &LVP) {
if (SP && (PrintSource || PrintLines))
- SP->printSourceLine(OS, Address, ObjectFilename);
+ SP->printSourceLine(OS, Address, ObjectFilename, LVP);
+ LVP.printBetweenInsts(OS, false);
size_t Start = OS.tell();
if (!NoLeadingAddr)
@@ -703,13 +1131,19 @@ public:
// The output of printInst starts with a tab. Print some spaces so that
// the tab has 1 column and advances to the target tab stop.
- unsigned TabStop = NoShowRawInsn ? 16 : 40;
+ unsigned TabStop = getInstStartColumn(STI);
unsigned Column = OS.tell() - Start;
OS.indent(Column < TabStop - 1 ? TabStop - 1 - Column : 7 - Column % 8);
- if (MI)
- IP.printInst(MI, Address.Address, "", STI, OS);
- else
+ if (MI) {
+ // See MCInstPrinter::printInst. On targets where a PC relative immediate
+ // is relative to the next instruction and the length of a MCInst is
+ // difficult to measure (x86), this is the address of the next
+ // instruction.
+ uint64_t Addr =
+ Address.Address + (STI.getTargetTriple().isX86() ? Bytes.size() : 0);
+ IP.printInst(MI, Addr, "", STI, OS);
+ } else
OS << "\t<unknown>";
}
};
@@ -718,7 +1152,7 @@ PrettyPrinter PrettyPrinterInst;
class HexagonPrettyPrinter : public PrettyPrinter {
public:
void printLead(ArrayRef<uint8_t> Bytes, uint64_t Address,
- raw_ostream &OS) {
+ formatted_raw_ostream &OS) {
uint32_t opcode =
(Bytes[3] << 24) | (Bytes[2] << 16) | (Bytes[1] << 8) | Bytes[0];
if (!NoLeadingAddr)
@@ -730,12 +1164,12 @@ public:
}
}
void printInst(MCInstPrinter &IP, const MCInst *MI, ArrayRef<uint8_t> Bytes,
- object::SectionedAddress Address, raw_ostream &OS,
+ object::SectionedAddress Address, formatted_raw_ostream &OS,
StringRef Annot, MCSubtargetInfo const &STI, SourcePrinter *SP,
- StringRef ObjectFilename,
- std::vector<RelocationRef> *Rels) override {
+ StringRef ObjectFilename, std::vector<RelocationRef> *Rels,
+ LiveVariablePrinter &LVP) override {
if (SP && (PrintSource || PrintLines))
- SP->printSourceLine(OS, Address, ObjectFilename, "");
+ SP->printSourceLine(OS, Address, ObjectFilename, LVP, "");
if (!MI) {
printLead(Bytes, Address.Address, OS);
OS << " <unknown>";
@@ -761,7 +1195,7 @@ public:
auto PrintReloc = [&]() -> void {
while ((RelCur != RelEnd) && (RelCur->getOffset() <= Address.Address)) {
if (RelCur->getOffset() == Address.Address) {
- printRelocation(ObjectFilename, *RelCur, Address.Address, false);
+ printRelocation(OS, ObjectFilename, *RelCur, Address.Address, false);
return;
}
++RelCur;
@@ -772,7 +1206,7 @@ public:
OS << Separator;
Separator = "\n";
if (SP && (PrintSource || PrintLines))
- SP->printSourceLine(OS, Address, ObjectFilename, "");
+ SP->printSourceLine(OS, Address, ObjectFilename, LVP, "");
printLead(Bytes, Address.Address, OS);
OS << Preamble;
Preamble = " ";
@@ -800,12 +1234,12 @@ HexagonPrettyPrinter HexagonPrettyPrinterInst;
class AMDGCNPrettyPrinter : public PrettyPrinter {
public:
void printInst(MCInstPrinter &IP, const MCInst *MI, ArrayRef<uint8_t> Bytes,
- object::SectionedAddress Address, raw_ostream &OS,
+ object::SectionedAddress Address, formatted_raw_ostream &OS,
StringRef Annot, MCSubtargetInfo const &STI, SourcePrinter *SP,
- StringRef ObjectFilename,
- std::vector<RelocationRef> *Rels) override {
+ StringRef ObjectFilename, std::vector<RelocationRef> *Rels,
+ LiveVariablePrinter &LVP) override {
if (SP && (PrintSource || PrintLines))
- SP->printSourceLine(OS, Address, ObjectFilename);
+ SP->printSourceLine(OS, Address, ObjectFilename, LVP);
if (MI) {
SmallString<40> InstStr;
@@ -852,12 +1286,12 @@ AMDGCNPrettyPrinter AMDGCNPrettyPrinterInst;
class BPFPrettyPrinter : public PrettyPrinter {
public:
void printInst(MCInstPrinter &IP, const MCInst *MI, ArrayRef<uint8_t> Bytes,
- object::SectionedAddress Address, raw_ostream &OS,
+ object::SectionedAddress Address, formatted_raw_ostream &OS,
StringRef Annot, MCSubtargetInfo const &STI, SourcePrinter *SP,
- StringRef ObjectFilename,
- std::vector<RelocationRef> *Rels) override {
+ StringRef ObjectFilename, std::vector<RelocationRef> *Rels,
+ LiveVariablePrinter &LVP) override {
if (SP && (PrintSource || PrintLines))
- SP->printSourceLine(OS, Address, ObjectFilename);
+ SP->printSourceLine(OS, Address, ObjectFilename, LVP);
if (!NoLeadingAddr)
OS << format("%8" PRId64 ":", Address.Address / 8);
if (!NoShowRawInsn) {
@@ -1023,7 +1457,7 @@ getRelocsMap(object::ObjectFile const &Obj) {
// TODO: implement for other file formats.
static bool shouldAdjustVA(const SectionRef &Section) {
const ObjectFile *Obj = Section.getObject();
- if (isa<object::ELFObjectFileBase>(Obj))
+ if (Obj->isELF())
return ELFSectionRef(Section).getFlags() & ELF::SHF_ALLOC;
return false;
}
@@ -1043,37 +1477,31 @@ static char getMappingSymbolKind(ArrayRef<MappingSymbolPair> MappingSymbols,
return (It - 1)->second;
}
-static uint64_t
-dumpARMELFData(uint64_t SectionAddr, uint64_t Index, uint64_t End,
- const ObjectFile *Obj, ArrayRef<uint8_t> Bytes,
- ArrayRef<MappingSymbolPair> MappingSymbols) {
+static uint64_t dumpARMELFData(uint64_t SectionAddr, uint64_t Index,
+ uint64_t End, const ObjectFile *Obj,
+ ArrayRef<uint8_t> Bytes,
+ ArrayRef<MappingSymbolPair> MappingSymbols,
+ raw_ostream &OS) {
support::endianness Endian =
Obj->isLittleEndian() ? support::little : support::big;
- while (Index < End) {
- outs() << format("%8" PRIx64 ":", SectionAddr + Index);
- outs() << "\t";
- if (Index + 4 <= End) {
- dumpBytes(Bytes.slice(Index, 4), outs());
- outs() << "\t.word\t"
- << format_hex(
- support::endian::read32(Bytes.data() + Index, Endian), 10);
- Index += 4;
- } else if (Index + 2 <= End) {
- dumpBytes(Bytes.slice(Index, 2), outs());
- outs() << "\t\t.short\t"
- << format_hex(
- support::endian::read16(Bytes.data() + Index, Endian), 6);
- Index += 2;
- } else {
- dumpBytes(Bytes.slice(Index, 1), outs());
- outs() << "\t\t.byte\t" << format_hex(Bytes[0], 4);
- ++Index;
- }
- outs() << "\n";
- if (getMappingSymbolKind(MappingSymbols, Index) != 'd')
- break;
+ OS << format("%8" PRIx64 ":\t", SectionAddr + Index);
+ if (Index + 4 <= End) {
+ dumpBytes(Bytes.slice(Index, 4), OS);
+ OS << "\t.word\t"
+ << format_hex(support::endian::read32(Bytes.data() + Index, Endian),
+ 10);
+ return 4;
+ }
+ if (Index + 2 <= End) {
+ dumpBytes(Bytes.slice(Index, 2), OS);
+ OS << "\t\t.short\t"
+ << format_hex(support::endian::read16(Bytes.data() + Index, Endian),
+ 6);
+ return 2;
}
- return Index;
+ dumpBytes(Bytes.slice(Index, 1), OS);
+ OS << "\t\t.byte\t" << format_hex(Bytes[0], 4);
+ return 1;
}
static void dumpELFData(uint64_t SectionAddr, uint64_t Index, uint64_t End,
@@ -1110,6 +1538,36 @@ static void dumpELFData(uint64_t SectionAddr, uint64_t Index, uint64_t End,
}
}
+SymbolInfoTy objdump::createSymbolInfo(const ObjectFile *Obj,
+ const SymbolRef &Symbol) {
+ const StringRef FileName = Obj->getFileName();
+ const uint64_t Addr = unwrapOrError(Symbol.getAddress(), FileName);
+ const StringRef Name = unwrapOrError(Symbol.getName(), FileName);
+
+ if (Obj->isXCOFF() && SymbolDescription) {
+ const auto *XCOFFObj = cast<XCOFFObjectFile>(Obj);
+ DataRefImpl SymbolDRI = Symbol.getRawDataRefImpl();
+
+ const uint32_t SymbolIndex = XCOFFObj->getSymbolIndex(SymbolDRI.p);
+ Optional<XCOFF::StorageMappingClass> Smc =
+ getXCOFFSymbolCsectSMC(XCOFFObj, Symbol);
+ return SymbolInfoTy(Addr, Name, Smc, SymbolIndex,
+ isLabel(XCOFFObj, Symbol));
+ } else
+ return SymbolInfoTy(Addr, Name,
+ Obj->isELF() ? getElfSymbolType(Obj, Symbol)
+ : (uint8_t)ELF::STT_NOTYPE);
+}
+
+static SymbolInfoTy createDummySymbolInfo(const ObjectFile *Obj,
+ const uint64_t Addr, StringRef &Name,
+ uint8_t Type) {
+ if (Obj->isXCOFF() && SymbolDescription)
+ return SymbolInfoTy(Addr, Name, None, None, false);
+ else
+ return SymbolInfoTy(Addr, Name, Type);
+}
+
static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj,
MCContext &Ctx, MCDisassembler *PrimaryDisAsm,
MCDisassembler *SecondaryDisAsm,
@@ -1136,20 +1594,14 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj,
const StringRef FileName = Obj->getFileName();
const MachOObjectFile *MachO = dyn_cast<const MachOObjectFile>(Obj);
for (const SymbolRef &Symbol : Obj->symbols()) {
- uint64_t Address = unwrapOrError(Symbol.getAddress(), FileName);
-
StringRef Name = unwrapOrError(Symbol.getName(), FileName);
- if (Name.empty())
+ if (Name.empty() && !(Obj->isXCOFF() && SymbolDescription))
continue;
- uint8_t SymbolType = ELF::STT_NOTYPE;
- if (Obj->isELF()) {
- SymbolType = getElfSymbolType(Obj, Symbol);
- if (SymbolType == ELF::STT_SECTION)
- continue;
- }
+ 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
+ // 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) {
@@ -1163,10 +1615,11 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj,
section_iterator SecI = unwrapOrError(Symbol.getSection(), FileName);
if (SecI != Obj->section_end())
- AllSymbols[*SecI].emplace_back(Address, Name, SymbolType);
+ AllSymbols[*SecI].push_back(createSymbolInfo(Obj, Symbol));
else
- AbsoluteSymbols.emplace_back(Address, Name, SymbolType);
+ AbsoluteSymbols.push_back(createSymbolInfo(Obj, Symbol));
}
+
if (AllSymbols.empty() && Obj->isELF())
addDynamicElfSymbols(Obj, AllSymbols);
@@ -1174,25 +1627,32 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj,
StringSaver Saver(A);
addPltEntries(Obj, AllSymbols, Saver);
- // Create a mapping from virtual address to section.
+ // Create a mapping from virtual address to section. An empty section can
+ // cause more than one section at the same address. Sort such sections to be
+ // before same-addressed non-empty sections so that symbol lookups prefer the
+ // non-empty section.
std::vector<std::pair<uint64_t, SectionRef>> SectionAddresses;
for (SectionRef Sec : Obj->sections())
SectionAddresses.emplace_back(Sec.getAddress(), Sec);
- array_pod_sort(SectionAddresses.begin(), SectionAddresses.end());
+ llvm::stable_sort(SectionAddresses, [](const auto &LHS, const auto &RHS) {
+ if (LHS.first != RHS.first)
+ return LHS.first < RHS.first;
+ return LHS.second.getSize() < RHS.second.getSize();
+ });
// Linked executables (.exe and .dll files) typically don't include a real
// symbol table but they might contain an export table.
if (const auto *COFFObj = dyn_cast<COFFObjectFile>(Obj)) {
for (const auto &ExportEntry : COFFObj->export_directories()) {
StringRef Name;
- if (std::error_code EC = ExportEntry.getSymbolName(Name))
- reportError(errorCodeToError(EC), Obj->getFileName());
+ if (Error E = ExportEntry.getSymbolName(Name))
+ reportError(std::move(E), Obj->getFileName());
if (Name.empty())
continue;
uint32_t RVA;
- if (std::error_code EC = ExportEntry.getExportRVA(RVA))
- reportError(errorCodeToError(EC), Obj->getFileName());
+ if (Error E = ExportEntry.getExportRVA(RVA))
+ reportError(std::move(E), Obj->getFileName());
uint64_t VA = COFFObj->getImageBase() + RVA;
auto Sec = partition_point(
@@ -1208,11 +1668,23 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj,
}
// Sort all the symbols, this allows us to use a simple binary search to find
- // a symbol near an address.
- StringSet<> FoundDisasmFuncsSet;
+ // Multiple symbols can have the same address. Use a stable sort to stabilize
+ // the output.
+ StringSet<> FoundDisasmSymbolSet;
for (std::pair<const SectionRef, SectionSymbolsTy> &SecSyms : AllSymbols)
- array_pod_sort(SecSyms.second.begin(), SecSyms.second.end());
- array_pod_sort(AbsoluteSymbols.begin(), AbsoluteSymbols.end());
+ stable_sort(SecSyms.second);
+ stable_sort(AbsoluteSymbols);
+
+ std::unique_ptr<DWARFContext> DICtx;
+ LiveVariablePrinter LVP(*Ctx.getRegisterInfo(), *STI);
+
+ if (DbgVariables != DVDisabled) {
+ DICtx = DWARFContext::create(*Obj);
+ for (const std::unique_ptr<DWARFUnit> &CU : DICtx->compile_units())
+ LVP.addCompileUnit(CU->getUnitDIE(false));
+ }
+
+ LLVM_DEBUG(LVP.dump());
for (const SectionRef &Section : ToolSectionFilter(*Obj)) {
if (FilterSections.empty() && !DisassembleAll &&
@@ -1229,8 +1701,8 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj,
std::vector<MappingSymbolPair> MappingSymbols;
if (hasMappingSymbols(Obj)) {
for (const auto &Symb : Symbols) {
- uint64_t Address = std::get<0>(Symb);
- StringRef Name = std::get<1>(Symb);
+ uint64_t Address = Symb.Addr;
+ StringRef Name = Symb.Name;
if (Name.startswith("$d"))
MappingSymbols.emplace_back(Address - SectionAddr, 'd');
if (Name.startswith("$x"))
@@ -1264,11 +1736,11 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj,
StringRef SectionName = unwrapOrError(Section.getName(), Obj->getFileName());
// If the section has no symbol at the start, just insert a dummy one.
- if (Symbols.empty() || std::get<0>(Symbols[0]) != 0) {
- Symbols.insert(
- Symbols.begin(),
- std::make_tuple(SectionAddr, SectionName,
- Section.isText() ? ELF::STT_FUNC : ELF::STT_OBJECT));
+ if (Symbols.empty() || Symbols[0].Addr != 0) {
+ Symbols.insert(Symbols.begin(),
+ createDummySymbolInfo(Obj, SectionAddr, SectionName,
+ Section.isText() ? ELF::STT_FUNC
+ : ELF::STT_OBJECT));
}
SmallString<40> Comments;
@@ -1289,26 +1761,26 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj,
std::vector<RelocationRef>::const_iterator RelEnd = Rels.end();
// Disassemble symbol by symbol.
for (unsigned SI = 0, SE = Symbols.size(); SI != SE; ++SI) {
- std::string SymbolName = std::get<1>(Symbols[SI]).str();
+ std::string SymbolName = Symbols[SI].Name.str();
if (Demangle)
SymbolName = demangle(SymbolName);
- // Skip if --disassemble-functions is not empty and the symbol is not in
+ // Skip if --disassemble-symbols is not empty and the symbol is not in
// the list.
- if (!DisasmFuncsSet.empty() && !DisasmFuncsSet.count(SymbolName))
+ if (!DisasmSymbolSet.empty() && !DisasmSymbolSet.count(SymbolName))
continue;
- uint64_t Start = std::get<0>(Symbols[SI]);
+ uint64_t Start = Symbols[SI].Addr;
if (Start < SectionAddr || StopAddress <= Start)
continue;
else
- FoundDisasmFuncsSet.insert(SymbolName);
+ FoundDisasmSymbolSet.insert(SymbolName);
// The end is the section end, the beginning of the next symbol, or
// --stop-address.
uint64_t End = std::min<uint64_t>(SectionAddr + SectSize, StopAddress);
if (SI + 1 < SE)
- End = std::min(End, std::get<0>(Symbols[SI + 1]));
+ End = std::min(End, Symbols[SI + 1].Addr);
if (Start >= End || End <= StartAddress)
continue;
Start -= SectionAddr;
@@ -1323,12 +1795,12 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj,
}
if (Obj->isELF() && Obj->getArch() == Triple::amdgcn) {
- if (std::get<2>(Symbols[SI]) == ELF::STT_AMDGPU_HSA_KERNEL) {
+ if (Symbols[SI].Type == ELF::STT_AMDGPU_HSA_KERNEL) {
// skip amd_kernel_code_t at the begining of kernel symbol (256 bytes)
Start += 256;
}
if (SI == SE - 1 ||
- std::get<2>(Symbols[SI + 1]) == ELF::STT_AMDGPU_HSA_KERNEL) {
+ Symbols[SI + 1].Type == ELF::STT_AMDGPU_HSA_KERNEL) {
// cut trailing zeroes at the end of kernel
// cut up to 256 bytes
const uint64_t EndAlign = 256;
@@ -1343,8 +1815,10 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj,
if (!NoLeadingAddr)
outs() << format(Is64Bits ? "%016" PRIx64 " " : "%08" PRIx64 " ",
SectionAddr + Start + VMAAdjustment);
-
- outs() << SymbolName << ":\n";
+ if (Obj->isXCOFF() && SymbolDescription) {
+ outs() << getXCOFFSymbolDescription(Symbols[SI], SymbolName) << ":\n";
+ } else
+ outs() << '<' << SymbolName << ">:\n";
// Don't print raw contents of a virtual section. A virtual section
// doesn't have any contents in the file.
@@ -1353,10 +1827,37 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj,
continue;
}
- // Some targets (like WebAssembly) have a special prelude at the start
- // of each symbol.
- DisAsm->onSymbolStart(SymbolName, Size, Bytes.slice(Start, End - Start),
- SectionAddr + Start, CommentStream);
+ auto Status = DisAsm->onSymbolStart(Symbols[SI], Size,
+ Bytes.slice(Start, End - Start),
+ SectionAddr + Start, CommentStream);
+ // To have round trippable disassembly, we fall back to decoding the
+ // remaining bytes as instructions.
+ //
+ // If there is a failure, we disassemble the failed region as bytes before
+ // falling back. The target is expected to print nothing in this case.
+ //
+ // If there is Success or SoftFail i.e no 'real' failure, we go ahead by
+ // Size bytes before falling back.
+ // So if the entire symbol is 'eaten' by the target:
+ // Start += Size // Now Start = End and we will never decode as
+ // // instructions
+ //
+ // Right now, most targets return None i.e ignore to treat a symbol
+ // separately. But WebAssembly decodes preludes for some symbols.
+ //
+ if (Status.hasValue()) {
+ if (Status.getValue() == MCDisassembler::Fail) {
+ outs() << "// Error in decoding " << SymbolName
+ << " : Decoding failed region as bytes.\n";
+ for (uint64_t I = 0; I < Size; ++I) {
+ outs() << "\t.byte\t " << format_hex(Bytes[I], 1, /*Upper=*/true)
+ << "\n";
+ }
+ }
+ } else {
+ Size = 0;
+ }
+
Start += Size;
Index = Start;
@@ -1367,7 +1868,7 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj,
// only disassembling text (applicable all architectures), we are in a
// situation where we must print the data and not disassemble it.
if (Obj->isELF() && !DisassembleAll && Section.isText()) {
- uint8_t SymTy = std::get<2>(Symbols[SI]);
+ uint8_t SymTy = Symbols[SI].Type;
if (SymTy == ELF::STT_OBJECT || SymTy == ELF::STT_COMMON) {
dumpELFData(SectionAddr, Index, End, Bytes);
Index = End;
@@ -1375,123 +1876,155 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj,
}
bool CheckARMELFData = hasMappingSymbols(Obj) &&
- std::get<2>(Symbols[SI]) != ELF::STT_OBJECT &&
+ Symbols[SI].Type != ELF::STT_OBJECT &&
!DisassembleAll;
+ bool DumpARMELFData = false;
+ formatted_raw_ostream FOS(outs());
while (Index < End) {
// ARM and AArch64 ELF binaries can interleave data and text in the
// same section. We rely on the markers introduced to understand what
// we need to dump. If the data marker is within a function, it is
// denoted as a word/short etc.
- if (CheckARMELFData &&
- getMappingSymbolKind(MappingSymbols, Index) == 'd') {
- Index = dumpARMELFData(SectionAddr, Index, End, Obj, Bytes,
- MappingSymbols);
- continue;
- }
-
- // When -z or --disassemble-zeroes are given we always dissasemble
- // them. Otherwise we might want to skip zero bytes we see.
- if (!DisassembleZeroes) {
- uint64_t MaxOffset = End - Index;
- // For -reloc: print zero blocks patched by relocations, so that
- // relocations can be shown in the dump.
- if (RelCur != RelEnd)
- MaxOffset = RelCur->getOffset() - Index;
-
- if (size_t N =
- countSkippableZeroBytes(Bytes.slice(Index, MaxOffset))) {
- outs() << "\t\t..." << '\n';
- Index += N;
- continue;
+ if (CheckARMELFData) {
+ char Kind = getMappingSymbolKind(MappingSymbols, Index);
+ DumpARMELFData = Kind == 'd';
+ if (SecondarySTI) {
+ if (Kind == 'a') {
+ STI = PrimaryIsThumb ? SecondarySTI : PrimarySTI;
+ DisAsm = PrimaryIsThumb ? SecondaryDisAsm : PrimaryDisAsm;
+ } else if (Kind == 't') {
+ STI = PrimaryIsThumb ? PrimarySTI : SecondarySTI;
+ DisAsm = PrimaryIsThumb ? PrimaryDisAsm : SecondaryDisAsm;
+ }
}
}
- if (SecondarySTI) {
- if (getMappingSymbolKind(MappingSymbols, Index) == 'a') {
- STI = PrimaryIsThumb ? SecondarySTI : PrimarySTI;
- DisAsm = PrimaryIsThumb ? SecondaryDisAsm : PrimaryDisAsm;
- } else if (getMappingSymbolKind(MappingSymbols, Index) == 't') {
- STI = PrimaryIsThumb ? PrimarySTI : SecondarySTI;
- DisAsm = PrimaryIsThumb ? PrimaryDisAsm : SecondaryDisAsm;
+ if (DumpARMELFData) {
+ Size = dumpARMELFData(SectionAddr, Index, End, Obj, Bytes,
+ MappingSymbols, FOS);
+ } else {
+ // When -z or --disassemble-zeroes are given we always dissasemble
+ // them. Otherwise we might want to skip zero bytes we see.
+ if (!DisassembleZeroes) {
+ uint64_t MaxOffset = End - Index;
+ // For --reloc: print zero blocks patched by relocations, so that
+ // relocations can be shown in the dump.
+ if (RelCur != RelEnd)
+ MaxOffset = RelCur->getOffset() - Index;
+
+ if (size_t N =
+ countSkippableZeroBytes(Bytes.slice(Index, MaxOffset))) {
+ FOS << "\t\t..." << '\n';
+ Index += N;
+ continue;
+ }
}
- }
- // Disassemble a real instruction or a data when disassemble all is
- // provided
- MCInst Inst;
- bool Disassembled = DisAsm->getInstruction(
- Inst, Size, Bytes.slice(Index), SectionAddr + Index, CommentStream);
- if (Size == 0)
- Size = 1;
-
- PIP.printInst(*IP, Disassembled ? &Inst : nullptr,
- Bytes.slice(Index, Size),
- {SectionAddr + Index + VMAAdjustment, Section.getIndex()},
- outs(), "", *STI, &SP, Obj->getFileName(), &Rels);
- outs() << CommentStream.str();
- Comments.clear();
-
- // Try to resolve the target of a call, tail call, etc. to a specific
- // symbol.
- if (MIA && (MIA->isCall(Inst) || MIA->isUnconditionalBranch(Inst) ||
- MIA->isConditionalBranch(Inst))) {
- uint64_t Target;
- if (MIA->evaluateBranch(Inst, SectionAddr + Index, Size, Target)) {
- // In a relocatable object, the target's section must reside in
- // the same section as the call instruction or it is accessed
- // through a relocation.
- //
- // In a non-relocatable object, the target may be in any section.
- //
- // N.B. We don't walk the relocations in the relocatable case yet.
- auto *TargetSectionSymbols = &Symbols;
- if (!Obj->isRelocatableObject()) {
- auto It = partition_point(
- SectionAddresses,
- [=](const std::pair<uint64_t, SectionRef> &O) {
- return O.first <= Target;
- });
- if (It != SectionAddresses.begin()) {
- --It;
- TargetSectionSymbols = &AllSymbols[It->second];
+ // Disassemble a real instruction or a data when disassemble all is
+ // provided
+ MCInst Inst;
+ bool Disassembled =
+ DisAsm->getInstruction(Inst, Size, Bytes.slice(Index),
+ SectionAddr + Index, CommentStream);
+ if (Size == 0)
+ Size = 1;
+
+ LVP.update({Index, Section.getIndex()},
+ {Index + Size, Section.getIndex()}, Index + Size != End);
+
+ 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();
+
+ // 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) {
+ uint64_t Target;
+ bool PrintTarget =
+ MIA->evaluateBranch(Inst, SectionAddr + Index, Size, Target);
+ if (!PrintTarget)
+ if (Optional<uint64_t> MaybeTarget =
+ MIA->evaluateMemoryOperandAddress(
+ Inst, SectionAddr + Index, Size)) {
+ Target = *MaybeTarget;
+ PrintTarget = true;
+ FOS << " # " << Twine::utohexstr(Target);
+ }
+ if (PrintTarget) {
+ // In a relocatable object, the target's section must reside in
+ // the same section as the call instruction or it is accessed
+ // through a relocation.
+ //
+ // In a non-relocatable object, the target may be in any section.
+ // In that case, locate the section(s) containing the target
+ // address and find the symbol in one of those, if possible.
+ //
+ // N.B. We don't walk the relocations in the relocatable case yet.
+ std::vector<const SectionSymbolsTy *> TargetSectionSymbols;
+ if (!Obj->isRelocatableObject()) {
+ auto It = llvm::partition_point(
+ SectionAddresses,
+ [=](const std::pair<uint64_t, SectionRef> &O) {
+ return O.first <= Target;
+ });
+ uint64_t TargetSecAddr = 0;
+ while (It != SectionAddresses.begin()) {
+ --It;
+ if (TargetSecAddr == 0)
+ TargetSecAddr = It->first;
+ if (It->first != TargetSecAddr)
+ break;
+ TargetSectionSymbols.push_back(&AllSymbols[It->second]);
+ }
} else {
- TargetSectionSymbols = &AbsoluteSymbols;
+ TargetSectionSymbols.push_back(&Symbols);
+ }
+ TargetSectionSymbols.push_back(&AbsoluteSymbols);
+
+ // Find the last symbol in the first candidate section whose
+ // offset is less than or equal to the target. If there are no
+ // such symbols, try in the next section and so on, before finally
+ // using the nearest preceding absolute symbol (if any), if there
+ // are no other valid symbols.
+ const SymbolInfoTy *TargetSym = nullptr;
+ for (const SectionSymbolsTy *TargetSymbols :
+ TargetSectionSymbols) {
+ auto It = llvm::partition_point(
+ *TargetSymbols,
+ [=](const SymbolInfoTy &O) { return O.Addr <= Target; });
+ if (It != TargetSymbols->begin()) {
+ TargetSym = &*(It - 1);
+ break;
+ }
}
- }
- // Find the last symbol in the section whose offset is less than
- // or equal to the target. If there isn't a section that contains
- // the target, find the nearest preceding absolute symbol.
- auto TargetSym = partition_point(
- *TargetSectionSymbols,
- [=](const std::tuple<uint64_t, StringRef, uint8_t> &O) {
- return std::get<0>(O) <= Target;
- });
- if (TargetSym == TargetSectionSymbols->begin()) {
- TargetSectionSymbols = &AbsoluteSymbols;
- TargetSym = partition_point(
- AbsoluteSymbols,
- [=](const std::tuple<uint64_t, StringRef, uint8_t> &O) {
- return std::get<0>(O) <= Target;
- });
- }
- if (TargetSym != TargetSectionSymbols->begin()) {
- --TargetSym;
- uint64_t TargetAddress = std::get<0>(*TargetSym);
- StringRef TargetName = std::get<1>(*TargetSym);
- outs() << " <" << TargetName;
- uint64_t Disp = Target - TargetAddress;
- if (Disp)
- outs() << "+0x" << Twine::utohexstr(Disp);
- outs() << '>';
+ if (TargetSym != nullptr) {
+ uint64_t TargetAddress = TargetSym->Addr;
+ std::string TargetName = TargetSym->Name.str();
+ if (Demangle)
+ TargetName = demangle(TargetName);
+
+ FOS << " <" << TargetName;
+ uint64_t Disp = Target - TargetAddress;
+ if (Disp)
+ FOS << "+0x" << Twine::utohexstr(Disp);
+ FOS << '>';
+ }
}
}
}
- outs() << "\n";
+
+ LVP.printAfterInst(FOS);
+ FOS << "\n";
// Hexagon does this in pretty printer
if (Obj->getArch() != Triple::hexagon) {
- // Print relocation for instruction.
+ // Print relocation for instruction and data.
while (RelCur != RelEnd) {
uint64_t Offset = RelCur->getOffset();
// If this relocation is hidden, skip it.
@@ -1500,7 +2033,11 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj,
continue;
}
- // Stop when RelCur's offset is past the current instruction.
+ // Stop when RelCur's offset is past the disassembled
+ // instruction/data. Note that it's possible the disassembled data
+ // is not the complete data: we might see the relocation printed in
+ // the middle of the data, but this matches the binutils objdump
+ // output.
if (Offset >= Index + Size)
break;
@@ -1513,8 +2050,9 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj,
Offset += AdjustVMA;
}
- printRelocation(Obj->getFileName(), *RelCur, SectionAddr + Offset,
- Is64Bits);
+ printRelocation(FOS, Obj->getFileName(), *RelCur,
+ SectionAddr + Offset, Is64Bits);
+ LVP.printAfterOtherLine(FOS, true);
++RelCur;
}
}
@@ -1523,11 +2061,10 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj,
}
}
}
- StringSet<> MissingDisasmFuncsSet =
- set_difference(DisasmFuncsSet, FoundDisasmFuncsSet);
- for (StringRef MissingDisasmFunc : MissingDisasmFuncsSet.keys())
- reportWarning("failed to disassemble missing function " + MissingDisasmFunc,
- FileName);
+ StringSet<> MissingDisasmSymbolSet =
+ set_difference(DisasmSymbolSet, FoundDisasmSymbolSet);
+ for (StringRef Sym : MissingDisasmSymbolSet.keys())
+ reportWarning("failed to disassemble missing symbol " + Sym, FileName);
}
static void disassembleObject(const ObjectFile *Obj, bool InlineRelocs) {
@@ -1597,6 +2134,7 @@ static void disassembleObject(const ObjectFile *Obj, bool InlineRelocs) {
reportError(Obj->getFileName(),
"no instruction printer for target " + TripleName);
IP->setPrintImmHex(PrintImmHex);
+ IP->setPrintBranchImmAsAddress(true);
PrettyPrinter &PIP = selectPrettyPrinter(Triple(TripleName));
SourcePrinter SP(Obj, TheTarget->getName());
@@ -1611,7 +2149,7 @@ static void disassembleObject(const ObjectFile *Obj, bool InlineRelocs) {
SP, InlineRelocs);
}
-void printRelocations(const ObjectFile *Obj) {
+void objdump::printRelocations(const ObjectFile *Obj) {
StringRef Fmt = Obj->getBytesInAddress() > 4 ? "%016" PRIx64 :
"%08" PRIx64;
// Regular objdump doesn't print relocations in non-relocatable object
@@ -1639,6 +2177,11 @@ void 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";
+ uint32_t OffsetPadding = (Obj->getBytesInAddress() > 4 ? 16 : 8);
+ uint32_t TypePadding = 24;
+ outs() << left_justify("OFFSET", OffsetPadding) << " "
+ << left_justify("TYPE", TypePadding) << " "
+ << "VALUE\n";
for (SectionRef Section : P.second) {
for (const RelocationRef &Reloc : Section.relocations()) {
@@ -1651,15 +2194,16 @@ void printRelocations(const ObjectFile *Obj) {
if (Error E = getRelocationValueString(Reloc, ValueStr))
reportError(std::move(E), Obj->getFileName());
- outs() << format(Fmt.data(), Address) << " " << RelocName << " "
- << ValueStr << "\n";
+ outs() << format(Fmt.data(), Address) << " "
+ << left_justify(RelocName, TypePadding) << " " << ValueStr
+ << "\n";
}
}
outs() << "\n";
}
}
-void printDynamicRelocations(const ObjectFile *Obj) {
+void objdump::printDynamicRelocations(const ObjectFile *Obj) {
// For the moment, this option is for ELF only
if (!Obj->isELF())
return;
@@ -1711,7 +2255,7 @@ static size_t getMaxSectionNameWidth(const ObjectFile *Obj) {
return MaxWidth;
}
-void printSectionHeaders(const ObjectFile *Obj) {
+void objdump::printSectionHeaders(const ObjectFile *Obj) {
size_t NameWidth = getMaxSectionNameWidth(Obj);
size_t AddressWidth = 2 * Obj->getBytesInAddress();
bool HasLMAColumn = shouldDisplayLMA(Obj);
@@ -1756,7 +2300,7 @@ void printSectionHeaders(const ObjectFile *Obj) {
outs() << "\n";
}
-void printSectionContents(const ObjectFile *Obj) {
+void objdump::printSectionContents(const ObjectFile *Obj) {
for (const SectionRef &Section : ToolSectionFilter(*Obj)) {
StringRef Name = unwrapOrError(Section.getName(), Obj->getFileName());
uint64_t BaseAddr = Section.getAddress();
@@ -1800,137 +2344,169 @@ void printSectionContents(const ObjectFile *Obj) {
}
}
-void printSymbolTable(const ObjectFile *O, StringRef ArchiveName,
- StringRef ArchitectureName) {
- outs() << "SYMBOL TABLE:\n";
-
- if (const COFFObjectFile *Coff = dyn_cast<const COFFObjectFile>(O)) {
- printCOFFSymbolTable(Coff);
+void objdump::printSymbolTable(const ObjectFile *O, StringRef ArchiveName,
+ StringRef ArchitectureName, bool DumpDynamic) {
+ if (O->isCOFF() && !DumpDynamic) {
+ outs() << "SYMBOL TABLE:\n";
+ printCOFFSymbolTable(cast<const COFFObjectFile>(O));
return;
}
const StringRef FileName = O->getFileName();
+
+ if (!DumpDynamic) {
+ outs() << "SYMBOL 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";
+ if (!O->isELF()) {
+ reportWarning(
+ "this operation is not currently supported for this file format",
+ FileName);
+ return;
+ }
+
+ const ELFObjectFileBase *ELF = cast<const ELFObjectFileBase>(O);
+ for (auto I = ELF->getDynamicSymbolIterators().begin();
+ I != ELF->getDynamicSymbolIterators().end(); ++I)
+ printSymbol(O, *I, FileName, ArchiveName, ArchitectureName, DumpDynamic);
+}
+
+void objdump::printSymbol(const ObjectFile *O, const SymbolRef &Symbol,
+ StringRef FileName, StringRef ArchiveName,
+ StringRef ArchitectureName, bool DumpDynamic) {
const MachOObjectFile *MachO = dyn_cast<const MachOObjectFile>(O);
- for (auto I = O->symbol_begin(), E = O->symbol_end(); I != E; ++I) {
- const SymbolRef &Symbol = *I;
- uint64_t Address = unwrapOrError(Symbol.getAddress(), FileName, ArchiveName,
- ArchitectureName);
- if ((Address < StartAddress) || (Address > StopAddress))
- continue;
- SymbolRef::Type Type = unwrapOrError(Symbol.getType(), FileName,
- ArchiveName, ArchitectureName);
- uint32_t Flags = Symbol.getFlags();
+ uint64_t Address = unwrapOrError(Symbol.getAddress(), FileName, ArchiveName,
+ ArchitectureName);
+ if ((Address < StartAddress) || (Address > StopAddress))
+ return;
+ SymbolRef::Type Type =
+ unwrapOrError(Symbol.getType(), FileName, ArchiveName, ArchitectureName);
+ uint32_t Flags =
+ unwrapOrError(Symbol.getFlags(), FileName, ArchiveName, ArchitectureName);
+
+ // 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.
+ bool IsSTAB = false;
+ if (MachO) {
+ DataRefImpl SymDRI = Symbol.getRawDataRefImpl();
+ uint8_t NType =
+ (MachO->is64Bit() ? MachO->getSymbol64TableEntry(SymDRI).n_type
+ : MachO->getSymbolTableEntry(SymDRI).n_type);
+ if (NType & MachO::N_STAB)
+ IsSTAB = true;
+ }
+ section_iterator Section = IsSTAB
+ ? O->section_end()
+ : unwrapOrError(Symbol.getSection(), FileName,
+ ArchiveName, ArchitectureName);
+
+ StringRef Name;
+ if (Type == SymbolRef::ST_Debug && Section != O->section_end()) {
+ if (Expected<StringRef> NameOrErr = Section->getName())
+ Name = *NameOrErr;
+ else
+ consumeError(NameOrErr.takeError());
- // 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.
- bool isSTAB = false;
- if (MachO) {
- DataRefImpl SymDRI = Symbol.getRawDataRefImpl();
- uint8_t NType = (MachO->is64Bit() ?
- MachO->getSymbol64TableEntry(SymDRI).n_type:
- MachO->getSymbolTableEntry(SymDRI).n_type);
- if (NType & MachO::N_STAB)
- isSTAB = true;
- }
- section_iterator Section = isSTAB ? O->section_end() :
- unwrapOrError(Symbol.getSection(), FileName,
- ArchiveName, ArchitectureName);
+ } else {
+ Name = unwrapOrError(Symbol.getName(), FileName, ArchiveName,
+ ArchitectureName);
+ }
- StringRef Name;
- if (Type == SymbolRef::ST_Debug && Section != O->section_end()) {
- if (Expected<StringRef> NameOrErr = Section->getName())
- Name = *NameOrErr;
- else
- consumeError(NameOrErr.takeError());
+ bool Global = Flags & SymbolRef::SF_Global;
+ bool Weak = Flags & SymbolRef::SF_Weak;
+ bool Absolute = Flags & SymbolRef::SF_Absolute;
+ bool Common = Flags & SymbolRef::SF_Common;
+ bool Hidden = Flags & SymbolRef::SF_Hidden;
- } else {
- Name = unwrapOrError(Symbol.getName(), FileName, ArchiveName,
- ArchitectureName);
- }
+ char GlobLoc = ' ';
+ if ((Section != O->section_end() || Absolute) && !Weak)
+ GlobLoc = Global ? 'g' : 'l';
+ char IFunc = ' ';
+ if (O->isELF()) {
+ if (ELFSymbolRef(Symbol).getELFType() == ELF::STT_GNU_IFUNC)
+ IFunc = 'i';
+ if (ELFSymbolRef(Symbol).getBinding() == ELF::STB_GNU_UNIQUE)
+ GlobLoc = 'u';
+ }
- bool Global = Flags & SymbolRef::SF_Global;
- bool Weak = Flags & SymbolRef::SF_Weak;
- bool Absolute = Flags & SymbolRef::SF_Absolute;
- bool Common = Flags & SymbolRef::SF_Common;
- bool Hidden = Flags & SymbolRef::SF_Hidden;
-
- char GlobLoc = ' ';
- if (Type != SymbolRef::ST_Unknown)
- GlobLoc = Global ? 'g' : 'l';
- char Debug = (Type == SymbolRef::ST_Debug || Type == SymbolRef::ST_File)
- ? 'd' : ' ';
- char FileFunc = ' ';
- if (Type == SymbolRef::ST_File)
- FileFunc = 'f';
- else if (Type == SymbolRef::ST_Function)
- FileFunc = 'F';
- else if (Type == SymbolRef::ST_Data)
- FileFunc = 'O';
-
- const char *Fmt = O->getBytesInAddress() > 4 ? "%016" PRIx64 :
- "%08" PRIx64;
-
- outs() << format(Fmt, Address) << " "
- << GlobLoc // Local -> 'l', Global -> 'g', Neither -> ' '
- << (Weak ? 'w' : ' ') // Weak?
- << ' ' // Constructor. Not supported yet.
- << ' ' // Warning. Not supported yet.
- << ' ' // Indirect reference to another symbol.
- << Debug // Debugging (d) or dynamic (D) symbol.
- << FileFunc // Name of function (F), file (f) or object (O).
- << ' ';
- if (Absolute) {
- outs() << "*ABS*";
- } else if (Common) {
- outs() << "*COM*";
- } else if (Section == O->section_end()) {
- outs() << "*UND*";
- } else {
- if (const MachOObjectFile *MachO =
- dyn_cast<const MachOObjectFile>(O)) {
- DataRefImpl DR = Section->getRawDataRefImpl();
- StringRef SegmentName = MachO->getSectionFinalSegmentName(DR);
- outs() << SegmentName << ",";
- }
- StringRef SectionName =
- unwrapOrError(Section->getName(), O->getFileName());
- outs() << SectionName;
+ char Debug = ' ';
+ if (DumpDynamic)
+ Debug = 'D';
+ else if (Type == SymbolRef::ST_Debug || Type == SymbolRef::ST_File)
+ Debug = 'd';
+
+ char FileFunc = ' ';
+ if (Type == SymbolRef::ST_File)
+ FileFunc = 'f';
+ else if (Type == SymbolRef::ST_Function)
+ FileFunc = 'F';
+ else if (Type == SymbolRef::ST_Data)
+ FileFunc = 'O';
+
+ const char *Fmt = O->getBytesInAddress() > 4 ? "%016" PRIx64 : "%08" PRIx64;
+
+ outs() << format(Fmt, Address) << " "
+ << GlobLoc // Local -> 'l', Global -> 'g', Neither -> ' '
+ << (Weak ? 'w' : ' ') // Weak?
+ << ' ' // Constructor. Not supported yet.
+ << ' ' // Warning. Not supported yet.
+ << IFunc // Indirect reference to another symbol.
+ << Debug // Debugging (d) or dynamic (D) symbol.
+ << FileFunc // Name of function (F), file (f) or object (O).
+ << ' ';
+ if (Absolute) {
+ outs() << "*ABS*";
+ } else if (Common) {
+ outs() << "*COM*";
+ } else if (Section == O->section_end()) {
+ outs() << "*UND*";
+ } else {
+ if (MachO) {
+ DataRefImpl DR = Section->getRawDataRefImpl();
+ StringRef SegmentName = MachO->getSectionFinalSegmentName(DR);
+ outs() << SegmentName << ",";
}
+ StringRef SectionName = unwrapOrError(Section->getName(), FileName);
+ outs() << SectionName;
+ }
- if (Common || isa<ELFObjectFileBase>(O)) {
- uint64_t Val =
- Common ? Symbol.getAlignment() : ELFSymbolRef(Symbol).getSize();
- outs() << format("\t%08" PRIx64, Val);
- }
+ if (Common || O->isELF()) {
+ uint64_t Val =
+ Common ? Symbol.getAlignment() : ELFSymbolRef(Symbol).getSize();
+ outs() << '\t' << format(Fmt, Val);
+ }
- if (isa<ELFObjectFileBase>(O)) {
- uint8_t Other = ELFSymbolRef(Symbol).getOther();
- switch (Other) {
- case ELF::STV_DEFAULT:
- break;
- case ELF::STV_INTERNAL:
- outs() << " .internal";
- break;
- case ELF::STV_HIDDEN:
- outs() << " .hidden";
- break;
- case ELF::STV_PROTECTED:
- outs() << " .protected";
- break;
- default:
- outs() << format(" 0x%02x", Other);
- break;
- }
- } else if (Hidden) {
+ if (O->isELF()) {
+ uint8_t Other = ELFSymbolRef(Symbol).getOther();
+ switch (Other) {
+ case ELF::STV_DEFAULT:
+ break;
+ case ELF::STV_INTERNAL:
+ outs() << " .internal";
+ break;
+ case ELF::STV_HIDDEN:
outs() << " .hidden";
+ break;
+ case ELF::STV_PROTECTED:
+ outs() << " .protected";
+ break;
+ default:
+ outs() << format(" 0x%02x", Other);
+ break;
}
-
- if (Demangle)
- outs() << ' ' << demangle(Name) << '\n';
- else
- outs() << ' ' << Name << '\n';
+ } else if (Hidden) {
+ outs() << " .hidden";
}
+
+ if (Demangle)
+ outs() << ' ' << demangle(std::string(Name)) << '\n';
+ else
+ outs() << ' ' << Name << '\n';
}
static void printUnwindInfo(const ObjectFile *O) {
@@ -1949,7 +2525,7 @@ static void printUnwindInfo(const ObjectFile *O) {
/// Dump the raw contents of the __clangast section so the output can be piped
/// into llvm-bcanalyzer.
-void printRawClangAST(const ObjectFile *Obj) {
+static void printRawClangAST(const ObjectFile *Obj) {
if (outs().is_displayed()) {
WithColor::error(errs(), ToolName)
<< "The -raw-clang-ast option will dump the raw binary contents of "
@@ -1960,7 +2536,7 @@ void printRawClangAST(const ObjectFile *Obj) {
}
StringRef ClangASTSectionName("__clangast");
- if (isa<COFFObjectFile>(Obj)) {
+ if (Obj->isCOFF()) {
ClangASTSectionName = "clangast";
}
@@ -1988,9 +2564,9 @@ void printRawClangAST(const ObjectFile *Obj) {
static void printFaultMaps(const ObjectFile *Obj) {
StringRef FaultMapSectionName;
- if (isa<ELFObjectFileBase>(Obj)) {
+ if (Obj->isELF()) {
FaultMapSectionName = ".llvm_faultmaps";
- } else if (isa<MachOObjectFile>(Obj)) {
+ } else if (Obj->isMachO()) {
FaultMapSectionName = "__llvm_faultmaps";
} else {
WithColor::error(errs(), ToolName)
@@ -2156,7 +2732,7 @@ static void dumpObject(ObjectFile *O, const Archive *A = nullptr,
outs() << A->getFileName() << "(" << O->getFileName() << ")";
else
outs() << O->getFileName();
- outs() << ":\tfile format " << O->getFileFormatName() << "\n\n";
+ outs() << ":\tfile format " << O->getFileFormatName().lower() << "\n\n";
}
if (StartAddress.getNumOccurrences() || StopAddress.getNumOccurrences())
@@ -2174,6 +2750,9 @@ static void dumpObject(ObjectFile *O, const Archive *A = nullptr,
printSectionHeaders(O);
if (SymbolTable)
printSymbolTable(O, ArchiveName);
+ if (DynamicSymbolTable)
+ printSymbolTable(O, ArchiveName, /*ArchitectureName=*/"",
+ /*DumpDynamic=*/true);
if (DwarfDumpType != DIDT_Null) {
std::unique_ptr<DIContext> DICtx = DWARFContext::create(*O);
// Dump the complete DWARF structure.
@@ -2275,7 +2854,6 @@ static void dumpInput(StringRef file) {
else
reportError(errorCodeToError(object_error::invalid_file_type), file);
}
-} // namespace llvm
int main(int argc, char **argv) {
using namespace llvm;
@@ -2291,7 +2869,9 @@ int main(int argc, char **argv) {
// Register the target printer for --version.
cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion);
- cl::ParseCommandLineOptions(argc, argv, "llvm object file dumper\n");
+ cl::ParseCommandLineOptions(argc, argv, "llvm object file dumper\n", nullptr,
+ /*EnvVar=*/nullptr,
+ /*LongOptionsUseDoubleDash=*/true);
if (StartAddress >= StopAddress)
reportCmdLineError("start address should be less than stop address");
@@ -2307,13 +2887,13 @@ int main(int argc, char **argv) {
SectionHeaders = SymbolTable = true;
if (DisassembleAll || PrintSource || PrintLines ||
- (!DisassembleFunctions.empty()))
+ !DisassembleSymbols.empty())
Disassemble = true;
if (!ArchiveHeaders && !Disassemble && DwarfDumpType == DIDT_Null &&
!DynamicRelocations && !FileHeaders && !PrivateHeaders && !RawClangAST &&
!Relocations && !SectionHeaders && !SectionContents && !SymbolTable &&
- !UnwindInfo && !FaultMapSection &&
+ !DynamicSymbolTable && !UnwindInfo && !FaultMapSection &&
!(MachOOpt &&
(Bind || DataInCode || DylibId || DylibsUsed || ExportsTrie ||
FirstPrivateHeader || IndirectSymbols || InfoPlist || LazyBind ||
@@ -2323,8 +2903,7 @@ int main(int argc, char **argv) {
return 2;
}
- DisasmFuncsSet.insert(DisassembleFunctions.begin(),
- DisassembleFunctions.end());
+ DisasmSymbolSet.insert(DisassembleSymbols.begin(), DisassembleSymbols.end());
llvm::for_each(InputFilenames, dumpInput);
diff --git a/llvm/tools/llvm-objdump/llvm-objdump.h b/llvm/tools/llvm-objdump/llvm-objdump.h
index 43ce02ae0bc27..390fc62d09f81 100644
--- a/llvm/tools/llvm-objdump/llvm-objdump.h
+++ b/llvm/tools/llvm-objdump/llvm-objdump.h
@@ -8,26 +8,48 @@
#ifndef LLVM_TOOLS_LLVM_OBJDUMP_LLVM_OBJDUMP_H
#define LLVM_TOOLS_LLVM_OBJDUMP_LLVM_OBJDUMP_H
+#include "llvm/ADT/StringSet.h"
#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"
-#include "llvm/Object/Archive.h"
namespace llvm {
class StringRef;
namespace object {
-class COFFObjectFile;
-class COFFImportFile;
class ELFObjectFileBase;
class ELFSectionRef;
class MachOObjectFile;
class MachOUniversalBinary;
class RelocationRef;
-}
+} // namespace object
+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<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;
+
+extern StringSet<> FoundSectionSet;
typedef std::function<bool(llvm::object::SectionRef const &)> FilterPredicate;
@@ -93,52 +115,17 @@ private:
SectionFilter ToolSectionFilter(llvm::object::ObjectFile const &O,
uint64_t *Idx = nullptr);
-Error getELFRelocationValueString(const object::ELFObjectFileBase *Obj,
- const object::RelocationRef &Rel,
- llvm::SmallVectorImpl<char> &Result);
-Error getCOFFRelocationValueString(const object::COFFObjectFile *Obj,
- const object::RelocationRef &Rel,
- llvm::SmallVectorImpl<char> &Result);
-Error getWasmRelocationValueString(const object::WasmObjectFile *Obj,
- const object::RelocationRef &RelRef,
- llvm::SmallVectorImpl<char> &Result);
-Error getMachORelocationValueString(const object::MachOObjectFile *Obj,
- const object::RelocationRef &RelRef,
- llvm::SmallVectorImpl<char> &Result);
-
-uint64_t getELFSectionLMA(const object::ELFSectionRef& Sec);
-
bool isRelocAddressLess(object::RelocationRef A, object::RelocationRef B);
-void parseInputMachO(StringRef Filename);
-void parseInputMachO(object::MachOUniversalBinary *UB);
-void printCOFFUnwindInfo(const object::COFFObjectFile *O);
-void printMachOUnwindInfo(const object::MachOObjectFile *O);
-void printMachOExportsTrie(const object::MachOObjectFile *O);
-void printMachORebaseTable(object::MachOObjectFile *O);
-void printMachOBindTable(object::MachOObjectFile *O);
-void printMachOLazyBindTable(object::MachOObjectFile *O);
-void printMachOWeakBindTable(object::MachOObjectFile *O);
-void printELFFileHeader(const object::ObjectFile *O);
-void printELFDynamicSection(const object::ObjectFile *Obj);
-void printELFSymbolVersionInfo(const object::ObjectFile *Obj);
-void printCOFFFileHeader(const object::ObjectFile *O);
-void printCOFFSymbolTable(const object::COFFImportFile *I);
-void printCOFFSymbolTable(const object::COFFObjectFile *O);
-void printMachOFileHeader(const object::ObjectFile *O);
-void printMachOLoadCommands(const object::ObjectFile *O);
-void printWasmFileHeader(const object::ObjectFile *O);
-void printExportsTrie(const object::ObjectFile *O);
-void printRebaseTable(object::ObjectFile *O);
-void printBindTable(object::ObjectFile *O);
-void printLazyBindTable(object::ObjectFile *O);
-void printWeakBindTable(object::ObjectFile *O);
-void printRawClangAST(const object::ObjectFile *O);
void printRelocations(const object::ObjectFile *O);
void printDynamicRelocations(const object::ObjectFile *O);
void printSectionHeaders(const object::ObjectFile *O);
void printSectionContents(const object::ObjectFile *O);
void printSymbolTable(const object::ObjectFile *O, StringRef ArchiveName,
- StringRef ArchitectureName = StringRef());
+ StringRef ArchitectureName = StringRef(),
+ bool DumpDynamic = false);
+void printSymbol(const object::ObjectFile *O, const object::SymbolRef &Symbol,
+ StringRef FileName, StringRef ArchiveName,
+ StringRef ArchitectureName, bool DumpDynamic);
LLVM_ATTRIBUTE_NORETURN void reportError(StringRef File, Twine Message);
LLVM_ATTRIBUTE_NORETURN void reportError(Error E, StringRef FileName,
StringRef ArchiveName = "",
@@ -154,7 +141,10 @@ T unwrapOrError(Expected<T> EO, Ts &&... Args) {
std::string getFileNameForError(const object::Archive::Child &C,
unsigned Index);
+SymbolInfoTy createSymbolInfo(const object::ObjectFile *Obj,
+ const object::SymbolRef &Symbol);
+} // namespace objdump
} // end namespace llvm
#endif
diff --git a/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp b/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp
index bf725ad8d6066..aa185e8a2f228 100644
--- a/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp
+++ b/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp
@@ -110,7 +110,7 @@ Error DumpOutputStyle::dump() {
P.NewLine();
}
- if (opts::dump::DumpTypeStats) {
+ if (opts::dump::DumpTypeStats || opts::dump::DumpIDStats) {
if (auto EC = dumpTypeStats())
return EC;
P.NewLine();
@@ -701,7 +701,8 @@ Error DumpOutputStyle::dumpTypeStats() {
// Iterate the types, categorize by kind, accumulate size stats.
StatCollection TypeStats;
- LazyRandomTypeCollection &Types = File.types();
+ LazyRandomTypeCollection &Types =
+ opts::dump::DumpTypeStats ? File.types() : File.ids();
for (Optional<TypeIndex> TI = Types.getFirst(); TI; TI = Types.getNext(*TI)) {
CVType Type = Types.getType(*TI);
TypeStats.update(uint32_t(Type.kind()), Type.length());
@@ -710,18 +711,16 @@ Error DumpOutputStyle::dumpTypeStats() {
P.NewLine();
P.formatLine(" Types");
AutoIndent Indent(P);
- P.formatLine("{0,14}: {1,7} entries ({2,12:N} bytes, {3,7} avg)", "Total",
+ P.formatLine("{0,16}: {1,7} entries ({2,12:N} bytes, {3,7} avg)", "Total",
TypeStats.Totals.Count, TypeStats.Totals.Size,
(double)TypeStats.Totals.Size / TypeStats.Totals.Count);
P.formatLine("{0}", fmt_repeat('-', 74));
for (const auto &K : TypeStats.getStatsSortedBySize()) {
- P.formatLine("{0,14}: {1,7} entries ({2,12:N} bytes, {3,7} avg)",
+ P.formatLine("{0,16}: {1,7} entries ({2,12:N} bytes, {3,7} avg)",
formatTypeLeafKind(TypeLeafKind(K.first)), K.second.Count,
K.second.Size, (double)K.second.Size / K.second.Count);
}
-
-
return Error::success();
}
@@ -896,7 +895,7 @@ Error DumpOutputStyle::dumpUdtStats() {
return L.Stat.Size > R.Stat.Size;
});
for (const auto &Stat : NamespacedStatsSorted) {
- std::string Label = formatv("namespace '{0}'", Stat.Key);
+ std::string Label = std::string(formatv("namespace '{0}'", Stat.Key));
P.formatLine("{0} | {1:N} {2:N}",
fmt_align(Label, AlignStyle::Right, FieldWidth),
fmt_align(Stat.Stat.Count, AlignStyle::Right, CD),
@@ -1039,7 +1038,7 @@ Error DumpOutputStyle::dumpXmi() {
}
std::vector<std::string> TIs;
for (const auto I : Xmi.Imports)
- TIs.push_back(formatv("{0,+10:X+}", fmtle(I)));
+ TIs.push_back(std::string(formatv("{0,+10:X+}", fmtle(I))));
std::string Result =
typesetItemList(TIs, P.getIndentLevel() + 35, 12, " ");
P.formatLine("{0,+32} | {1}", Module, Result);
diff --git a/llvm/tools/llvm-pdbutil/FormatUtil.cpp b/llvm/tools/llvm-pdbutil/FormatUtil.cpp
index 1a13f383e53c8..c9ef196094964 100644
--- a/llvm/tools/llvm-pdbutil/FormatUtil.cpp
+++ b/llvm/tools/llvm-pdbutil/FormatUtil.cpp
@@ -20,7 +20,7 @@ using namespace llvm::pdb;
std::string llvm::pdb::truncateStringBack(StringRef S, uint32_t MaxLen) {
if (MaxLen == 0 || S.size() <= MaxLen || S.size() <= 3)
- return S;
+ return std::string(S);
assert(MaxLen >= 3);
uint32_t FinalLen = std::min<size_t>(S.size(), MaxLen - 3);
@@ -30,7 +30,7 @@ std::string llvm::pdb::truncateStringBack(StringRef S, uint32_t MaxLen) {
std::string llvm::pdb::truncateStringMiddle(StringRef S, uint32_t MaxLen) {
if (MaxLen == 0 || S.size() <= MaxLen || S.size() <= 3)
- return S;
+ return std::string(S);
assert(MaxLen >= 3);
uint32_t FinalLen = std::min<size_t>(S.size(), MaxLen - 3);
@@ -41,7 +41,7 @@ std::string llvm::pdb::truncateStringMiddle(StringRef S, uint32_t MaxLen) {
std::string llvm::pdb::truncateStringFront(StringRef S, uint32_t MaxLen) {
if (MaxLen == 0 || S.size() <= MaxLen || S.size() <= 3)
- return S;
+ return std::string(S);
assert(MaxLen >= 3);
S = S.take_back(MaxLen - 3);
@@ -82,7 +82,7 @@ std::string llvm::pdb::typesetItemList(ArrayRef<std::string> Opts,
if (!Opts.empty()) {
Result += Sep;
Result += "\n";
- Result += formatv("{0}", fmt_repeat(' ', IndentLevel));
+ Result += std::string(formatv("{0}", fmt_repeat(' ', IndentLevel)));
}
}
return Result;
@@ -92,7 +92,7 @@ std::string llvm::pdb::typesetStringList(uint32_t IndentLevel,
ArrayRef<StringRef> Strings) {
std::string Result = "[";
for (const auto &S : Strings) {
- Result += formatv("\n{0}{1}", fmt_repeat(' ', IndentLevel), S);
+ Result += std::string(formatv("\n{0}{1}", fmt_repeat(' ', IndentLevel), S));
}
Result += "]";
return Result;
@@ -169,7 +169,7 @@ StringRef llvm::pdb::formatTypeLeafKind(TypeLeafKind K) {
}
std::string llvm::pdb::formatSegmentOffset(uint16_t Segment, uint32_t Offset) {
- return formatv("{0:4}:{1:4}", Segment, Offset);
+ return std::string(formatv("{0:4}:{1:4}", Segment, Offset));
}
#define PUSH_CHARACTERISTIC_FLAG(Enum, TheOpt, Value, Style, Descriptive) \
diff --git a/llvm/tools/llvm-pdbutil/FormatUtil.h b/llvm/tools/llvm-pdbutil/FormatUtil.h
index 19ce248f9a6f0..1a006844e011a 100644
--- a/llvm/tools/llvm-pdbutil/FormatUtil.h
+++ b/llvm/tools/llvm-pdbutil/FormatUtil.h
@@ -42,8 +42,7 @@ std::string truncateQuotedNameBack(StringRef Label, StringRef Name,
return Ret;
template <typename T> std::string formatUnknownEnum(T Value) {
- return formatv("unknown ({0})",
- static_cast<typename std::underlying_type<T>::type>(Value))
+ return formatv("unknown ({0})", static_cast<std::underlying_type_t<T>>(Value))
.str();
}
diff --git a/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp b/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp
index ebfa50625e767..7a06140855f80 100644
--- a/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp
+++ b/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp
@@ -371,9 +371,9 @@ std::string MinimalSymbolDumper::typeOrIdIndex(codeview::TypeIndex TI,
StringRef Name = Container.getTypeName(TI);
if (Name.size() > 32) {
Name = Name.take_front(32);
- return formatv("{0} ({1}...)", TI, Name);
+ return std::string(formatv("{0} ({1}...)", TI, Name));
} else
- return formatv("{0} ({1})", TI, Name);
+ return std::string(formatv("{0} ({1})", TI, Name));
}
std::string MinimalSymbolDumper::idIndex(codeview::TypeIndex TI) const {
diff --git a/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp b/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp
index 3fdef085f19e0..8e46a97272d58 100644
--- a/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp
+++ b/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp
@@ -201,8 +201,9 @@ static std::string formatPointerAttrs(const PointerRecord &Record) {
PointerMode Mode = Record.getMode();
PointerOptions Opts = Record.getOptions();
PointerKind Kind = Record.getPointerKind();
- return formatv("mode = {0}, opts = {1}, kind = {2}", formatPointerMode(Mode),
- pointerOptions(Opts), pointerKind(Kind));
+ return std::string(formatv("mode = {0}, opts = {1}, kind = {2}",
+ formatPointerMode(Mode), pointerOptions(Opts),
+ pointerKind(Kind)));
}
static std::string formatFunctionOptions(FunctionOptions Options) {
diff --git a/llvm/tools/llvm-pdbutil/StreamUtil.cpp b/llvm/tools/llvm-pdbutil/StreamUtil.cpp
index 7dfc2beefe78d..d0d0a9fbe9275 100644
--- a/llvm/tools/llvm-pdbutil/StreamUtil.cpp
+++ b/llvm/tools/llvm-pdbutil/StreamUtil.cpp
@@ -32,7 +32,7 @@ std::string StreamInfo::getLongName() const {
StreamInfo StreamInfo::createStream(StreamPurpose Purpose, StringRef Name,
uint32_t StreamIndex) {
StreamInfo Result;
- Result.Name = Name;
+ Result.Name = std::string(Name);
Result.StreamIndex = StreamIndex;
Result.Purpose = Purpose;
return Result;
@@ -41,7 +41,7 @@ StreamInfo StreamInfo::createStream(StreamPurpose Purpose, StringRef Name,
StreamInfo StreamInfo::createModuleStream(StringRef Module,
uint32_t StreamIndex, uint32_t Modi) {
StreamInfo Result;
- Result.Name = Module;
+ Result.Name = std::string(Module);
Result.StreamIndex = StreamIndex;
Result.ModuleIndex = Modi;
Result.Purpose = StreamPurpose::ModuleStream;
@@ -90,7 +90,7 @@ void llvm::pdb::discoverStreamPurposes(PDBFile &File,
if (Info) {
for (auto &NSE : Info->named_streams()) {
if (NSE.second != kInvalidStreamIndex)
- NamedStreams[NSE.second] = NSE.first();
+ NamedStreams[NSE.second] = std::string(NSE.first());
}
}
diff --git a/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp b/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp
index 9307300861d43..00092e71c6b49 100644
--- a/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp
+++ b/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp
@@ -466,6 +466,10 @@ cl::opt<bool> DumpTypeStats(
"type-stats",
cl::desc("Dump a detailed breakdown of type usage/size"),
cl::cat(MsfOptions), cl::sub(DumpSubcommand));
+cl::opt<bool> DumpIDStats(
+ "id-stats",
+ cl::desc("Dump a detailed breakdown of IPI types usage/size"),
+ cl::cat(MsfOptions), cl::sub(DumpSubcommand));
cl::opt<bool> DumpUdtStats(
"udt-stats",
cl::desc("Dump a detailed breakdown of S_UDT record usage / stats"),
@@ -1507,7 +1511,7 @@ int main(int Argc, const char **Argv) {
if (opts::yaml2pdb::YamlPdbOutputFile.empty()) {
SmallString<16> OutputFilename(opts::yaml2pdb::InputFilename.getValue());
sys::path::replace_extension(OutputFilename, ".pdb");
- opts::yaml2pdb::YamlPdbOutputFile = OutputFilename.str();
+ opts::yaml2pdb::YamlPdbOutputFile = std::string(OutputFilename.str());
}
yamlToPdb(opts::yaml2pdb::InputFilename);
} else if (opts::DiaDumpSubcommand) {
diff --git a/llvm/tools/llvm-pdbutil/llvm-pdbutil.h b/llvm/tools/llvm-pdbutil/llvm-pdbutil.h
index 321f41bba7f17..9fe92c2c9d75e 100644
--- a/llvm/tools/llvm-pdbutil/llvm-pdbutil.h
+++ b/llvm/tools/llvm-pdbutil/llvm-pdbutil.h
@@ -141,6 +141,7 @@ extern llvm::cl::opt<bool> DumpFpm;
extern llvm::cl::opt<bool> DumpStreams;
extern llvm::cl::opt<bool> DumpSymbolStats;
extern llvm::cl::opt<bool> DumpTypeStats;
+extern llvm::cl::opt<bool> DumpIDStats;
extern llvm::cl::opt<bool> DumpUdtStats;
extern llvm::cl::opt<bool> DumpStreamBlocks;
diff --git a/llvm/tools/llvm-profdata/llvm-profdata.cpp b/llvm/tools/llvm-profdata/llvm-profdata.cpp
index 41e9abb82b1fc..843f072a61c32 100644
--- a/llvm/tools/llvm-profdata/llvm-profdata.cpp
+++ b/llvm/tools/llvm-profdata/llvm-profdata.cpp
@@ -23,11 +23,12 @@
#include "llvm/Support/Errc.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Format.h"
+#include "llvm/Support/FormattedStream.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
-#include "llvm/Support/Threading.h"
#include "llvm/Support/ThreadPool.h"
+#include "llvm/Support/Threading.h"
#include "llvm/Support/WithColor.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
@@ -70,18 +71,18 @@ static void exitWithError(Error E, StringRef Whence = "") {
instrprof_error instrError = IPE.get();
StringRef Hint = "";
if (instrError == instrprof_error::unrecognized_format) {
- // Hint for common error of forgetting -sample for sample profiles.
- Hint = "Perhaps you forgot to use the -sample option?";
+ // Hint for common error of forgetting --sample for sample profiles.
+ Hint = "Perhaps you forgot to use the --sample option?";
}
- exitWithError(IPE.message(), Whence, Hint);
+ exitWithError(IPE.message(), std::string(Whence), std::string(Hint));
});
}
- exitWithError(toString(std::move(E)), Whence);
+ exitWithError(toString(std::move(E)), std::string(Whence));
}
static void exitWithErrorCode(std::error_code EC, StringRef Whence = "") {
- exitWithError(EC.message(), Whence);
+ exitWithError(EC.message(), std::string(Whence));
}
namespace {
@@ -94,7 +95,7 @@ static void warnOrExitGivenError(FailureMode FailMode, std::error_code EC,
if (FailMode == failIfAnyAreInvalid)
exitWithErrorCode(EC, Whence);
else
- warn(EC.message(), Whence);
+ warn(EC.message(), std::string(Whence));
}
static void handleMergeWriterError(Error E, StringRef WhenceFile = "",
@@ -290,6 +291,22 @@ static void mergeWriterContexts(WriterContext *Dst, WriterContext *Src) {
});
}
+static void writeInstrProfile(StringRef OutputFilename,
+ ProfileFormat OutputFormat,
+ InstrProfWriter &Writer) {
+ std::error_code EC;
+ raw_fd_ostream Output(OutputFilename.data(), EC, sys::fs::OF_None);
+ if (EC)
+ exitWithErrorCode(EC, OutputFilename);
+
+ if (OutputFormat == PF_Text) {
+ if (Error E = Writer.writeText(Output))
+ exitWithError(std::move(E));
+ } else {
+ Writer.write(Output);
+ }
+}
+
static void mergeInstrProfile(const WeightedFileVector &Inputs,
SymbolRemapper *Remapper,
StringRef OutputFilename,
@@ -307,8 +324,11 @@ static void mergeInstrProfile(const WeightedFileVector &Inputs,
// If NumThreads is not specified, auto-detect a good default.
if (NumThreads == 0)
- NumThreads =
- std::min(hardware_concurrency(), unsigned((Inputs.size() + 1) / 2));
+ NumThreads = std::min(hardware_concurrency().compute_thread_count(),
+ unsigned((Inputs.size() + 1) / 2));
+ // FIXME: There's a bug here, where setting NumThreads = Inputs.size() fails
+ // the merge_empty_profile.test because the InstrProfWriter.ProfileKind isn't
+ // merged, thus the emitted file ends up with a PF_Unknown kind.
// Initialize the writer contexts.
SmallVector<std::unique_ptr<WriterContext>, 4> Contexts;
@@ -320,7 +340,7 @@ static void mergeInstrProfile(const WeightedFileVector &Inputs,
for (const auto &Input : Inputs)
loadInput(Input, Remapper, Contexts[0].get());
} else {
- ThreadPool Pool(NumThreads);
+ ThreadPool Pool(hardware_concurrency(NumThreads));
// Load the inputs in parallel (N/NumThreads serial steps).
unsigned Ctx = 0;
@@ -362,18 +382,7 @@ static void mergeInstrProfile(const WeightedFileVector &Inputs,
(NumErrors > 0 && FailMode == failIfAnyAreInvalid))
exitWithError("No profiles could be merged.");
- std::error_code EC;
- raw_fd_ostream Output(OutputFilename.data(), EC, sys::fs::OF_None);
- if (EC)
- exitWithErrorCode(EC, OutputFilename);
-
- InstrProfWriter &Writer = Contexts[0]->Writer;
- if (OutputFormat == PF_Text) {
- if (Error E = Writer.writeText(Output))
- exitWithError(std::move(E));
- } else {
- Writer.write(Output);
- }
+ writeInstrProfile(OutputFilename, OutputFormat, Contexts[0]->Writer);
}
/// Make a copy of the given function samples with all symbol names remapped
@@ -401,7 +410,8 @@ remapSamples(const sampleprof::FunctionSamples &Samples,
for (const auto &Callsite : CallsiteSamples.second) {
sampleprof::FunctionSamples Remapped =
remapSamples(Callsite.second, Remapper, Error);
- MergeResult(Error, Target[Remapped.getName()].merge(Remapped));
+ MergeResult(Error,
+ Target[std::string(Remapped.getName())].merge(Remapped));
}
}
return Result;
@@ -444,7 +454,8 @@ static void handleExtBinaryWriter(sampleprof::SampleProfileWriter &Writer,
ProfileFormat OutputFormat,
MemoryBuffer *Buffer,
sampleprof::ProfileSymbolList &WriterList,
- bool CompressAllSections) {
+ bool CompressAllSections, bool UseMD5,
+ bool GenPartialProfile) {
populateProfileSymbolList(Buffer, WriterList);
if (WriterList.size() > 0 && OutputFormat != PF_Ext_Binary)
warn("Profile Symbol list is not empty but the output format is not "
@@ -453,22 +464,30 @@ static void handleExtBinaryWriter(sampleprof::SampleProfileWriter &Writer,
Writer.setProfileSymbolList(&WriterList);
if (CompressAllSections) {
- if (OutputFormat != PF_Ext_Binary) {
+ if (OutputFormat != PF_Ext_Binary)
warn("-compress-all-section is ignored. Specify -extbinary to enable it");
- } else {
- auto ExtBinaryWriter =
- static_cast<sampleprof::SampleProfileWriterExtBinary *>(&Writer);
- ExtBinaryWriter->setToCompressAllSections();
- }
+ else
+ Writer.setToCompressAllSections();
+ }
+ if (UseMD5) {
+ if (OutputFormat != PF_Ext_Binary)
+ warn("-use-md5 is ignored. Specify -extbinary to enable it");
+ else
+ Writer.setUseMD5();
+ }
+ if (GenPartialProfile) {
+ if (OutputFormat != PF_Ext_Binary)
+ warn("-gen-partial-profile is ignored. Specify -extbinary to enable it");
+ else
+ Writer.setPartialProfile();
}
}
-static void mergeSampleProfile(const WeightedFileVector &Inputs,
- SymbolRemapper *Remapper,
- StringRef OutputFilename,
- ProfileFormat OutputFormat,
- StringRef ProfileSymbolListFile,
- bool CompressAllSections, FailureMode FailMode) {
+static void
+mergeSampleProfile(const WeightedFileVector &Inputs, SymbolRemapper *Remapper,
+ StringRef OutputFilename, ProfileFormat OutputFormat,
+ StringRef ProfileSymbolListFile, bool CompressAllSections,
+ bool UseMD5, bool GenPartialProfile, FailureMode FailMode) {
using namespace sampleprof;
StringMap<FunctionSamples> ProfileMap;
SmallVector<std::unique_ptr<sampleprof::SampleProfileReader>, 5> Readers;
@@ -525,7 +544,7 @@ static void mergeSampleProfile(const WeightedFileVector &Inputs,
// Make sure Buffer lives as long as WriterList.
auto Buffer = getInputFileBuf(ProfileSymbolListFile);
handleExtBinaryWriter(*Writer, OutputFormat, Buffer.get(), WriterList,
- CompressAllSections);
+ CompressAllSections, UseMD5, GenPartialProfile);
Writer->write(ProfileMap);
}
@@ -537,7 +556,7 @@ static WeightedFile parseWeightedFile(const StringRef &WeightedFilename) {
if (WeightStr.getAsInteger(10, Weight) || Weight < 1)
exitWithError("Input weight must be a positive integer.");
- return {FileName, Weight};
+ return {std::string(FileName), Weight};
}
static void addWeightedInput(WeightedFileVector &WNI, const WeightedFile &WF) {
@@ -546,7 +565,7 @@ static void addWeightedInput(WeightedFileVector &WNI, const WeightedFile &WF) {
// If it's STDIN just pass it on.
if (Filename == "-") {
- WNI.push_back({Filename, Weight});
+ WNI.push_back({std::string(Filename), Weight});
return;
}
@@ -557,7 +576,7 @@ static void addWeightedInput(WeightedFileVector &WNI, const WeightedFile &WF) {
Filename);
// If it's a source file, collect it.
if (llvm::sys::fs::is_regular_file(Status)) {
- WNI.push_back({Filename, Weight});
+ WNI.push_back({std::string(Filename), Weight});
return;
}
@@ -589,7 +608,7 @@ static void parseInputFilenamesFile(MemoryBuffer *Buffer,
continue;
// If there's no comma, it's an unweighted profile.
else if (SanitizedEntry.find(',') == StringRef::npos)
- addWeightedInput(WFV, {SanitizedEntry, 1});
+ addWeightedInput(WFV, {std::string(SanitizedEntry), 1});
else
addWeightedInput(WFV, parseWeightedFile(SanitizedEntry));
}
@@ -653,12 +672,19 @@ static int merge_main(int argc, const char *argv[]) {
"compress-all-sections", cl::init(false), cl::Hidden,
cl::desc("Compress all sections when writing the profile (only "
"meaningful for -extbinary)"));
+ cl::opt<bool> UseMD5(
+ "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> GenPartialProfile(
+ "gen-partial-profile", cl::init(false), cl::Hidden,
+ cl::desc("Generate a partial profile (only meaningful for -extbinary)"));
cl::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n");
WeightedFileVector WeightedInputs;
for (StringRef Filename : InputFilenames)
- addWeightedInput(WeightedInputs, {Filename, 1});
+ addWeightedInput(WeightedInputs, {std::string(Filename), 1});
for (StringRef WeightedFilename : WeightedInputFilenames)
addWeightedInput(WeightedInputs, parseWeightedFile(WeightedFilename));
@@ -687,7 +713,7 @@ static int merge_main(int argc, const char *argv[]) {
else
mergeSampleProfile(WeightedInputs, Remapper.get(), OutputFilename,
OutputFormat, ProfileSymbolListFile, CompressAllSections,
- FailureMode);
+ UseMD5, GenPartialProfile, FailureMode);
return 0;
}
@@ -989,15 +1015,9 @@ static int showInstrProfile(const std::string &Filename, bool ShowCounts,
}
if (ShowDetailedSummary) {
- OS << "Detailed summary:\n";
OS << "Total number of blocks: " << PS->getNumCounts() << "\n";
OS << "Total count: " << PS->getTotalCount() << "\n";
- for (auto Entry : PS->getDetailedSummary()) {
- OS << Entry.NumCounts << " blocks with count >= " << Entry.MinCount
- << " account for "
- << format("%0.6g", (float)Entry.Cutoff / ProfileSummary::Scale * 100)
- << " percentage of the total counts.\n";
- }
+ PS->printDetailedSummary(OS);
}
return 0;
}
@@ -1012,11 +1032,144 @@ static void showSectionInfo(sampleprof::SampleProfileReader *Reader,
}
}
+namespace {
+struct HotFuncInfo {
+ StringRef FuncName;
+ uint64_t TotalCount;
+ double TotalCountPercent;
+ uint64_t MaxCount;
+ uint64_t EntryCount;
+
+ HotFuncInfo()
+ : FuncName(), TotalCount(0), TotalCountPercent(0.0f), MaxCount(0),
+ EntryCount(0) {}
+
+ HotFuncInfo(StringRef FN, uint64_t TS, double TSP, uint64_t MS, uint64_t ES)
+ : FuncName(FN), TotalCount(TS), TotalCountPercent(TSP), MaxCount(MS),
+ EntryCount(ES) {}
+};
+} // namespace
+
+// Print out detailed information about hot functions in PrintValues vector.
+// Users specify titles and offset of every columns through ColumnTitle and
+// ColumnOffset. The size of ColumnTitle and ColumnOffset need to be the same
+// and at least 4. Besides, users can optionally give a HotFuncMetric string to
+// print out or let it be an empty string.
+static void dumpHotFunctionList(const std::vector<std::string> &ColumnTitle,
+ const std::vector<int> &ColumnOffset,
+ const std::vector<HotFuncInfo> &PrintValues,
+ uint64_t HotFuncCount, uint64_t TotalFuncCount,
+ uint64_t HotProfCount, uint64_t TotalProfCount,
+ const std::string &HotFuncMetric,
+ raw_fd_ostream &OS) {
+ assert(ColumnOffset.size() == ColumnTitle.size());
+ assert(ColumnTitle.size() >= 4);
+ assert(TotalFuncCount > 0);
+ double TotalProfPercent = 0;
+ if (TotalProfCount > 0)
+ TotalProfPercent = ((double)HotProfCount) / TotalProfCount * 100;
+
+ formatted_raw_ostream FOS(OS);
+ FOS << HotFuncCount << " out of " << TotalFuncCount
+ << " functions with profile ("
+ << format("%.2f%%", (((double)HotFuncCount) / TotalFuncCount * 100))
+ << ") are considered hot functions";
+ if (!HotFuncMetric.empty())
+ FOS << " (" << HotFuncMetric << ")";
+ FOS << ".\n";
+ FOS << HotProfCount << " out of " << TotalProfCount << " profile counts ("
+ << format("%.2f%%", TotalProfPercent) << ") are from hot functions.\n";
+
+ for (size_t I = 0; I < ColumnTitle.size(); ++I) {
+ FOS.PadToColumn(ColumnOffset[I]);
+ FOS << ColumnTitle[I];
+ }
+ FOS << "\n";
+
+ for (const HotFuncInfo &R : PrintValues) {
+ FOS.PadToColumn(ColumnOffset[0]);
+ FOS << R.TotalCount << " (" << format("%.2f%%", R.TotalCountPercent) << ")";
+ FOS.PadToColumn(ColumnOffset[1]);
+ FOS << R.MaxCount;
+ FOS.PadToColumn(ColumnOffset[2]);
+ FOS << R.EntryCount;
+ FOS.PadToColumn(ColumnOffset[3]);
+ FOS << R.FuncName << "\n";
+ }
+ return;
+}
+
+static int
+showHotFunctionList(const StringMap<sampleprof::FunctionSamples> &Profiles,
+ ProfileSummary &PS, raw_fd_ostream &OS) {
+ using namespace sampleprof;
+
+ const uint32_t HotFuncCutoff = 990000;
+ auto &SummaryVector = PS.getDetailedSummary();
+ uint64_t MinCountThreshold = 0;
+ for (const ProfileSummaryEntry &SummaryEntry : SummaryVector) {
+ if (SummaryEntry.Cutoff == HotFuncCutoff) {
+ MinCountThreshold = SummaryEntry.MinCount;
+ break;
+ }
+ }
+ assert(MinCountThreshold != 0);
+
+ // Traverse all functions in the profile and keep only hot functions.
+ // The following loop also calculates the sum of total samples of all
+ // functions.
+ std::multimap<uint64_t, std::pair<const FunctionSamples *, const uint64_t>,
+ std::greater<uint64_t>>
+ HotFunc;
+ uint64_t ProfileTotalSample = 0;
+ uint64_t HotFuncSample = 0;
+ uint64_t HotFuncCount = 0;
+ uint64_t MaxCount = 0;
+ for (const auto &I : Profiles) {
+ const FunctionSamples &FuncProf = I.second;
+ ProfileTotalSample += FuncProf.getTotalSamples();
+ MaxCount = FuncProf.getMaxCountInside();
+
+ // MinCountThreshold is a block/line threshold computed for a given cutoff.
+ // We intentionally compare the maximum sample count in a function with this
+ // threshold to get an approximate threshold for hot functions.
+ if (MaxCount >= MinCountThreshold) {
+ HotFunc.emplace(FuncProf.getTotalSamples(),
+ std::make_pair(&(I.second), MaxCount));
+ HotFuncSample += FuncProf.getTotalSamples();
+ ++HotFuncCount;
+ }
+ }
+
+ std::vector<std::string> ColumnTitle{"Total sample (%)", "Max sample",
+ "Entry sample", "Function name"};
+ std::vector<int> ColumnOffset{0, 24, 42, 58};
+ std::string Metric =
+ std::string("max sample >= ") + std::to_string(MinCountThreshold);
+ std::vector<HotFuncInfo> PrintValues;
+ for (const auto &FuncPair : HotFunc) {
+ const FunctionSamples &Func = *FuncPair.second.first;
+ double TotalSamplePercent =
+ (ProfileTotalSample > 0)
+ ? (Func.getTotalSamples() * 100.0) / ProfileTotalSample
+ : 0;
+ PrintValues.emplace_back(HotFuncInfo(
+ Func.getFuncName(), Func.getTotalSamples(), TotalSamplePercent,
+ FuncPair.second.second, Func.getEntrySamples()));
+ }
+ dumpHotFunctionList(ColumnTitle, ColumnOffset, PrintValues, HotFuncCount,
+ Profiles.size(), HotFuncSample, ProfileTotalSample,
+ Metric, OS);
+
+ return 0;
+}
+
static int showSampleProfile(const std::string &Filename, bool ShowCounts,
- bool ShowAllFunctions,
+ bool ShowAllFunctions, bool ShowDetailedSummary,
const std::string &ShowFunction,
bool ShowProfileSymbolList,
- bool ShowSectionInfoOnly, raw_fd_ostream &OS) {
+ bool ShowSectionInfoOnly, bool ShowHotFuncList,
+ raw_fd_ostream &OS) {
using namespace sampleprof;
LLVMContext Context;
auto ReaderOrErr = SampleProfileReader::create(Filename, Context);
@@ -1044,6 +1197,15 @@ static int showSampleProfile(const std::string &Filename, bool ShowCounts,
ReaderList->dump(OS);
}
+ if (ShowDetailedSummary) {
+ auto &PS = Reader->getSummary();
+ PS.printSummary(OS);
+ PS.printDetailedSummary(OS);
+ }
+
+ if (ShowHotFuncList)
+ showHotFunctionList(Reader->getProfiles(), Reader->getSummary(), OS);
+
return 0;
}
@@ -1070,6 +1232,9 @@ static int show_main(int argc, const char *argv[]) {
cl::desc(
"Cutoff percentages (times 10000) for generating detailed summary"),
cl::value_desc("800000,901000,999999"));
+ cl::opt<bool> ShowHotFuncList(
+ "hot-func-list", cl::init(false),
+ cl::desc("Show profile summary of a list of hot functions"));
cl::opt<bool> ShowAllFunctions("all-functions", cl::init(false),
cl::desc("Details for every function"));
cl::opt<bool> ShowCS("showcs", cl::init(false),
@@ -1132,8 +1297,9 @@ static int show_main(int argc, const char *argv[]) {
OnlyListBelow, ShowFunction, TextFormat, OS);
else
return showSampleProfile(Filename, ShowCounts, ShowAllFunctions,
- ShowFunction, ShowProfileSymbolList,
- ShowSectionInfoOnly, OS);
+ ShowDetailedSummary, ShowFunction,
+ ShowProfileSymbolList, ShowSectionInfoOnly,
+ ShowHotFuncList, OS);
}
int main(int argc, const char *argv[]) {
diff --git a/llvm/tools/llvm-readobj/COFFDumper.cpp b/llvm/tools/llvm-readobj/COFFDumper.cpp
index 9b2c6adb9d930..89a904f53ae7d 100644
--- a/llvm/tools/llvm-readobj/COFFDumper.cpp
+++ b/llvm/tools/llvm-readobj/COFFDumper.cpp
@@ -104,7 +104,10 @@ public:
bool GHash) override;
void printStackMap() const override;
void printAddrsig() override;
+ void printCGProfile() override;
+
private:
+ StringRef getSymbolName(uint32_t Index);
void printSymbols() override;
void printDynamicSymbols() override;
void printSymbol(const SymbolRef &Sym);
@@ -409,6 +412,11 @@ static const EnumEntry<COFF::DLLCharacteristics> PEDLLCharacteristics[] = {
LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE),
};
+static const EnumEntry<COFF::ExtendedDLLCharacteristics>
+ PEExtendedDLLCharacteristics[] = {
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT),
+};
+
static const EnumEntry<COFF::SectionCharacteristics>
ImageSectionCharacteristics[] = {
LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_TYPE_NOLOAD ),
@@ -516,23 +524,25 @@ static const EnumEntry<COFF::COMDATType> ImageCOMDATSelect[] = {
};
static const EnumEntry<COFF::DebugType> ImageDebugType[] = {
- { "Unknown" , COFF::IMAGE_DEBUG_TYPE_UNKNOWN },
- { "COFF" , COFF::IMAGE_DEBUG_TYPE_COFF },
- { "CodeView" , COFF::IMAGE_DEBUG_TYPE_CODEVIEW },
- { "FPO" , COFF::IMAGE_DEBUG_TYPE_FPO },
- { "Misc" , COFF::IMAGE_DEBUG_TYPE_MISC },
- { "Exception" , COFF::IMAGE_DEBUG_TYPE_EXCEPTION },
- { "Fixup" , COFF::IMAGE_DEBUG_TYPE_FIXUP },
- { "OmapToSrc" , COFF::IMAGE_DEBUG_TYPE_OMAP_TO_SRC },
- { "OmapFromSrc", COFF::IMAGE_DEBUG_TYPE_OMAP_FROM_SRC },
- { "Borland" , COFF::IMAGE_DEBUG_TYPE_BORLAND },
- { "Reserved10" , COFF::IMAGE_DEBUG_TYPE_RESERVED10 },
- { "CLSID" , COFF::IMAGE_DEBUG_TYPE_CLSID },
- { "VCFeature" , COFF::IMAGE_DEBUG_TYPE_VC_FEATURE },
- { "POGO" , COFF::IMAGE_DEBUG_TYPE_POGO },
- { "ILTCG" , COFF::IMAGE_DEBUG_TYPE_ILTCG },
- { "MPX" , COFF::IMAGE_DEBUG_TYPE_MPX },
- { "Repro" , COFF::IMAGE_DEBUG_TYPE_REPRO },
+ {"Unknown", COFF::IMAGE_DEBUG_TYPE_UNKNOWN},
+ {"COFF", COFF::IMAGE_DEBUG_TYPE_COFF},
+ {"CodeView", COFF::IMAGE_DEBUG_TYPE_CODEVIEW},
+ {"FPO", COFF::IMAGE_DEBUG_TYPE_FPO},
+ {"Misc", COFF::IMAGE_DEBUG_TYPE_MISC},
+ {"Exception", COFF::IMAGE_DEBUG_TYPE_EXCEPTION},
+ {"Fixup", COFF::IMAGE_DEBUG_TYPE_FIXUP},
+ {"OmapToSrc", COFF::IMAGE_DEBUG_TYPE_OMAP_TO_SRC},
+ {"OmapFromSrc", COFF::IMAGE_DEBUG_TYPE_OMAP_FROM_SRC},
+ {"Borland", COFF::IMAGE_DEBUG_TYPE_BORLAND},
+ {"Reserved10", COFF::IMAGE_DEBUG_TYPE_RESERVED10},
+ {"CLSID", COFF::IMAGE_DEBUG_TYPE_CLSID},
+ {"VCFeature", COFF::IMAGE_DEBUG_TYPE_VC_FEATURE},
+ {"POGO", COFF::IMAGE_DEBUG_TYPE_POGO},
+ {"ILTCG", COFF::IMAGE_DEBUG_TYPE_ILTCG},
+ {"MPX", COFF::IMAGE_DEBUG_TYPE_MPX},
+ {"Repro", COFF::IMAGE_DEBUG_TYPE_REPRO},
+ {"ExtendedDLLCharacteristics",
+ COFF::IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS},
};
static const EnumEntry<COFF::WeakExternalCharacteristics>
@@ -601,8 +611,8 @@ void COFFDumper::cacheRelocations() {
void COFFDumper::printDataDirectory(uint32_t Index,
const std::string &FieldName) {
- const data_directory *Data;
- if (Obj->getDataDirectory(Index, Data))
+ const data_directory *Data = Obj->getDataDirectory(Index);
+ if (!Data)
return;
W.printHex(FieldName + "RVA", Data->RelativeVirtualAddress);
W.printHex(FieldName + "Size", Data->Size);
@@ -621,6 +631,7 @@ void COFFDumper::printFileHeaders() {
W.printHex ("TimeDateStamp", FormattedTime, Obj->getTimeDateStamp());
W.printHex ("PointerToSymbolTable", Obj->getPointerToSymbolTable());
W.printNumber("SymbolCount", Obj->getNumberOfSymbols());
+ W.printNumber("StringTableSize", Obj->getStringTableSize());
W.printNumber("OptionalHeaderSize", Obj->getSizeOfOptionalHeader());
W.printFlags ("Characteristics", Obj->getCharacteristics(),
makeArrayRef(ImageFileCharacteristics));
@@ -722,11 +733,15 @@ void COFFDumper::printCOFFDebugDirectory() {
W.printHex("SizeOfData", D.SizeOfData);
W.printHex("AddressOfRawData", D.AddressOfRawData);
W.printHex("PointerToRawData", D.PointerToRawData);
+ // Ideally, if D.AddressOfRawData == 0, we should try to load the payload
+ // using D.PointerToRawData instead.
+ if (D.AddressOfRawData == 0)
+ continue;
if (D.Type == COFF::IMAGE_DEBUG_TYPE_CODEVIEW) {
const codeview::DebugInfo *DebugInfo;
StringRef PDBFileName;
- if (std::error_code EC = Obj->getDebugPDBInfo(&D, DebugInfo, PDBFileName))
- reportError(errorCodeToError(EC), Obj->getFileName());
+ if (Error E = Obj->getDebugPDBInfo(&D, DebugInfo, PDBFileName))
+ reportError(std::move(E), Obj->getFileName());
DictScope PDBScope(W, "PDBInfo");
W.printHex("PDBSignature", DebugInfo->Signature.CVSignature);
@@ -736,12 +751,19 @@ void COFFDumper::printCOFFDebugDirectory() {
W.printString("PDBFileName", PDBFileName);
}
} else if (D.SizeOfData != 0) {
- // FIXME: Type values of 12 and 13 are commonly observed but are not in
- // the documented type enum. Figure out what they mean.
+ // FIXME: Data visualization for IMAGE_DEBUG_TYPE_VC_FEATURE and
+ // IMAGE_DEBUG_TYPE_POGO?
ArrayRef<uint8_t> RawData;
- if (std::error_code EC = Obj->getRvaAndSizeAsBytes(D.AddressOfRawData,
+ if (Error E = Obj->getRvaAndSizeAsBytes(D.AddressOfRawData,
D.SizeOfData, RawData))
- reportError(errorCodeToError(EC), Obj->getFileName());
+ reportError(std::move(E), Obj->getFileName());
+ if (D.Type == COFF::IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS) {
+ // FIXME right now the only possible value would fit in 8 bits,
+ // but that might change in the future
+ uint16_t Characteristics = RawData[0];
+ W.printFlags("ExtendedCharacteristics", Characteristics,
+ makeArrayRef(PEExtendedDLLCharacteristics));
+ }
W.printBinaryBlock("RawData", RawData);
}
}
@@ -750,11 +772,11 @@ void COFFDumper::printCOFFDebugDirectory() {
void COFFDumper::printRVATable(uint64_t TableVA, uint64_t Count,
uint64_t EntrySize, PrintExtraCB PrintExtra) {
uintptr_t TableStart, TableEnd;
- if (std::error_code EC = Obj->getVaPtr(TableVA, TableStart))
- reportError(errorCodeToError(EC), Obj->getFileName());
- if (std::error_code EC =
+ if (Error E = Obj->getVaPtr(TableVA, TableStart))
+ reportError(std::move(E), Obj->getFileName());
+ if (Error E =
Obj->getVaPtr(TableVA + Count * EntrySize - 1, TableEnd))
- reportError(errorCodeToError(EC), Obj->getFileName());
+ reportError(std::move(E), Obj->getFileName());
TableEnd++;
for (uintptr_t I = TableStart; I < TableEnd; I += EntrySize) {
uint32_t RVA = *reinterpret_cast<const ulittle32_t *>(I);
@@ -1135,7 +1157,7 @@ void COFFDumper::printCodeViewSymbolSection(StringRef SectionName,
return;
}
- std::string PC = formatv("+{0:X}", uint32_t(Line.Offset));
+ std::string PC = std::string(formatv("+{0:X}", uint32_t(Line.Offset)));
ListScope PCScope(W, PC);
codeview::LineInfo LI(Line.Flags);
@@ -1449,21 +1471,25 @@ void COFFDumper::printSymbol(const SymbolRef &Sym) {
DictScope D(W, "Symbol");
COFFSymbolRef Symbol = Obj->getCOFFSymbol(Sym);
- const coff_section *Section;
- if (std::error_code EC = Obj->getSection(Symbol.getSectionNumber(), Section)) {
- W.startLine() << "Invalid section number: " << EC.message() << "\n";
+ Expected<const coff_section *> SecOrErr =
+ Obj->getSection(Symbol.getSectionNumber());
+ if (!SecOrErr) {
+ W.startLine() << "Invalid section number: " << Symbol.getSectionNumber()
+ << "\n";
W.flush();
+ consumeError(SecOrErr.takeError());
return;
}
+ const coff_section *Section = *SecOrErr;
StringRef SymbolName;
- if (Obj->getSymbolName(Symbol, SymbolName))
- SymbolName = "";
+ if (Expected<StringRef> SymNameOrErr = Obj->getSymbolName(Symbol))
+ SymbolName = *SymNameOrErr;
StringRef SectionName;
- if (Expected<StringRef> NameOrErr =
+ if (Expected<StringRef> SecNameOrErr =
getSectionName(Obj, Symbol.getSectionNumber(), Section))
- SectionName = *NameOrErr;
+ SectionName = *SecNameOrErr;
W.printString("Name", SymbolName);
W.printNumber("Value", Symbol.getValue());
@@ -1492,16 +1518,8 @@ void COFFDumper::printSymbol(const SymbolRef &Sym) {
if (std::error_code EC = getSymbolAuxData(Obj, Symbol, I, Aux))
reportError(errorCodeToError(EC), Obj->getFileName());
- Expected<COFFSymbolRef> Linked = Obj->getSymbol(Aux->TagIndex);
- if (!Linked)
- reportError(Linked.takeError(), Obj->getFileName());
-
- StringRef LinkedName;
- if (std::error_code EC = Obj->getSymbolName(*Linked, LinkedName))
- reportError(errorCodeToError(EC), Obj->getFileName());
-
DictScope AS(W, "AuxWeakExternal");
- W.printNumber("Linked", LinkedName, Aux->TagIndex);
+ W.printNumber("Linked", getSymbolName(Aux->TagIndex), Aux->TagIndex);
W.printEnum ("Search", Aux->Characteristics,
makeArrayRef(WeakExternalCharacteristics));
@@ -1532,35 +1550,25 @@ void COFFDumper::printSymbol(const SymbolRef &Sym) {
if (Section && Section->Characteristics & COFF::IMAGE_SCN_LNK_COMDAT
&& Aux->Selection == COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE) {
- const coff_section *Assoc;
- StringRef AssocName = "";
- if (std::error_code EC = Obj->getSection(AuxNumber, Assoc))
- reportError(errorCodeToError(EC), Obj->getFileName());
- Expected<StringRef> Res = getSectionName(Obj, AuxNumber, Assoc);
- if (!Res)
- reportError(Res.takeError(), Obj->getFileName());
- AssocName = *Res;
-
- W.printNumber("AssocSection", AssocName, AuxNumber);
+ Expected<const coff_section *> Assoc = Obj->getSection(AuxNumber);
+ if (!Assoc)
+ reportError(Assoc.takeError(), Obj->getFileName());
+ Expected<StringRef> AssocName = getSectionName(Obj, AuxNumber, *Assoc);
+ if (!AssocName)
+ reportError(AssocName.takeError(), Obj->getFileName());
+
+ W.printNumber("AssocSection", *AssocName, AuxNumber);
}
} else if (Symbol.isCLRToken()) {
const coff_aux_clr_token *Aux;
if (std::error_code EC = getSymbolAuxData(Obj, Symbol, I, Aux))
reportError(errorCodeToError(EC), Obj->getFileName());
- Expected<COFFSymbolRef> ReferredSym =
- Obj->getSymbol(Aux->SymbolTableIndex);
- if (!ReferredSym)
- reportError(ReferredSym.takeError(), Obj->getFileName());
-
- StringRef ReferredName;
- if (std::error_code EC = Obj->getSymbolName(*ReferredSym, ReferredName))
- reportError(errorCodeToError(EC), Obj->getFileName());
-
DictScope AS(W, "AuxCLRToken");
W.printNumber("AuxType", Aux->AuxType);
W.printNumber("Reserved", Aux->Reserved);
- W.printNumber("SymbolTableIndex", ReferredName, Aux->SymbolTableIndex);
+ W.printNumber("SymbolTableIndex", getSymbolName(Aux->SymbolTableIndex),
+ Aux->SymbolTableIndex);
} else {
W.startLine() << "<unhandled auxiliary record>\n";
@@ -1621,11 +1629,11 @@ void COFFDumper::printImportedSymbols(
iterator_range<imported_symbol_iterator> Range) {
for (const ImportedSymbolRef &I : Range) {
StringRef Sym;
- if (std::error_code EC = I.getSymbolName(Sym))
- reportError(errorCodeToError(EC), Obj->getFileName());
+ if (Error E = I.getSymbolName(Sym))
+ reportError(std::move(E), Obj->getFileName());
uint16_t Ordinal;
- if (std::error_code EC = I.getOrdinal(Ordinal))
- reportError(errorCodeToError(EC), Obj->getFileName());
+ if (Error E = I.getOrdinal(Ordinal))
+ reportError(std::move(E), Obj->getFileName());
W.printNumber("Symbol", Sym, Ordinal);
}
}
@@ -1637,17 +1645,17 @@ void COFFDumper::printDelayImportedSymbols(
for (const ImportedSymbolRef &S : Range) {
DictScope Import(W, "Import");
StringRef Sym;
- if (std::error_code EC = S.getSymbolName(Sym))
- reportError(errorCodeToError(EC), Obj->getFileName());
+ if (Error E = S.getSymbolName(Sym))
+ reportError(std::move(E), Obj->getFileName());
uint16_t Ordinal;
- if (std::error_code EC = S.getOrdinal(Ordinal))
- reportError(errorCodeToError(EC), Obj->getFileName());
+ if (Error E = S.getOrdinal(Ordinal))
+ reportError(std::move(E), Obj->getFileName());
W.printNumber("Symbol", Sym, Ordinal);
uint64_t Addr;
- if (std::error_code EC = I.getImportAddress(Index++, Addr))
- reportError(errorCodeToError(EC), Obj->getFileName());
+ if (Error E = I.getImportAddress(Index++, Addr))
+ reportError(std::move(E), Obj->getFileName());
W.printHex("Address", Addr);
}
}
@@ -1657,16 +1665,16 @@ void COFFDumper::printCOFFImports() {
for (const ImportDirectoryEntryRef &I : Obj->import_directories()) {
DictScope Import(W, "Import");
StringRef Name;
- if (std::error_code EC = I.getName(Name))
- reportError(errorCodeToError(EC), Obj->getFileName());
+ if (Error E = I.getName(Name))
+ reportError(std::move(E), Obj->getFileName());
W.printString("Name", Name);
uint32_t ILTAddr;
- if (std::error_code EC = I.getImportLookupTableRVA(ILTAddr))
- reportError(errorCodeToError(EC), Obj->getFileName());
+ if (Error E = I.getImportLookupTableRVA(ILTAddr))
+ reportError(std::move(E), Obj->getFileName());
W.printHex("ImportLookupTableRVA", ILTAddr);
uint32_t IATAddr;
- if (std::error_code EC = I.getImportAddressTableRVA(IATAddr))
- reportError(errorCodeToError(EC), Obj->getFileName());
+ if (Error E = I.getImportAddressTableRVA(IATAddr))
+ reportError(std::move(E), Obj->getFileName());
W.printHex("ImportAddressTableRVA", IATAddr);
// The import lookup table can be missing with certain older linkers, so
// fall back to the import address table in that case.
@@ -1680,12 +1688,12 @@ void COFFDumper::printCOFFImports() {
for (const DelayImportDirectoryEntryRef &I : Obj->delay_import_directories()) {
DictScope Import(W, "DelayImport");
StringRef Name;
- if (std::error_code EC = I.getName(Name))
- reportError(errorCodeToError(EC), Obj->getFileName());
+ if (Error E = I.getName(Name))
+ reportError(std::move(E), Obj->getFileName());
W.printString("Name", Name);
const delay_import_directory_table_entry *Table;
- if (std::error_code EC = I.getDelayImportTable(Table))
- reportError(errorCodeToError(EC), Obj->getFileName());
+ if (Error E = I.getDelayImportTable(Table))
+ reportError(std::move(E), Obj->getFileName());
W.printHex("Attributes", Table->Attributes);
W.printHex("ModuleHandle", Table->ModuleHandle);
W.printHex("ImportAddressTable", Table->DelayImportAddressTable);
@@ -1697,18 +1705,18 @@ void COFFDumper::printCOFFImports() {
}
void COFFDumper::printCOFFExports() {
- for (const ExportDirectoryEntryRef &E : Obj->export_directories()) {
+ for (const ExportDirectoryEntryRef &Exp : Obj->export_directories()) {
DictScope Export(W, "Export");
StringRef Name;
uint32_t Ordinal, RVA;
- if (std::error_code EC = E.getSymbolName(Name))
- reportError(errorCodeToError(EC), Obj->getFileName());
- if (std::error_code EC = E.getOrdinal(Ordinal))
- reportError(errorCodeToError(EC), Obj->getFileName());
- if (std::error_code EC = E.getExportRVA(RVA))
- reportError(errorCodeToError(EC), Obj->getFileName());
+ if (Error E = Exp.getSymbolName(Name))
+ reportError(std::move(E), Obj->getFileName());
+ if (Error E = Exp.getOrdinal(Ordinal))
+ reportError(std::move(E), Obj->getFileName());
+ if (Error E = Exp.getExportRVA(RVA))
+ reportError(std::move(E), Obj->getFileName());
W.printNumber("Ordinal", Ordinal);
W.printString("Name", Name);
@@ -1746,10 +1754,10 @@ void COFFDumper::printCOFFBaseReloc() {
for (const BaseRelocRef &I : Obj->base_relocs()) {
uint8_t Type;
uint32_t RVA;
- if (std::error_code EC = I.getRVA(RVA))
- reportError(errorCodeToError(EC), Obj->getFileName());
- if (std::error_code EC = I.getType(Type))
- reportError(errorCodeToError(EC), Obj->getFileName());
+ if (Error E = I.getRVA(RVA))
+ reportError(std::move(E), Obj->getFileName());
+ if (Error E = I.getType(Type))
+ reportError(std::move(E), Obj->getFileName());
DictScope Import(W, "Entry");
W.printString("Type", getBaseRelocTypeName(Type));
W.printHex("Address", RVA);
@@ -1882,7 +1890,7 @@ void COFFDumper::printResourceDirectoryTable(
}
void COFFDumper::printStackMap() const {
- object::SectionRef StackMapSection;
+ SectionRef StackMapSection;
for (auto Sec : Obj->sections()) {
StringRef Name;
if (Expected<StringRef> NameOrErr = Sec.getName())
@@ -1896,7 +1904,7 @@ void COFFDumper::printStackMap() const {
}
}
- if (StackMapSection == object::SectionRef())
+ if (StackMapSection == SectionRef())
return;
StringRef StackMapContents =
@@ -1913,7 +1921,7 @@ void COFFDumper::printStackMap() const {
}
void COFFDumper::printAddrsig() {
- object::SectionRef AddrsigSection;
+ SectionRef AddrsigSection;
for (auto Sec : Obj->sections()) {
StringRef Name;
if (Expected<StringRef> NameOrErr = Sec.getName())
@@ -1927,7 +1935,7 @@ void COFFDumper::printAddrsig() {
}
}
- if (AddrsigSection == object::SectionRef())
+ if (AddrsigSection == SectionRef())
return;
StringRef AddrsigContents =
@@ -1945,19 +1953,58 @@ void COFFDumper::printAddrsig() {
if (Err)
reportError(createError(Err), Obj->getFileName());
- Expected<COFFSymbolRef> Sym = Obj->getSymbol(SymIndex);
- if (!Sym)
- reportError(Sym.takeError(), Obj->getFileName());
+ W.printNumber("Sym", getSymbolName(SymIndex), SymIndex);
+ Cur += Size;
+ }
+}
- StringRef SymName;
- if (std::error_code EC = Obj->getSymbolName(*Sym, SymName))
- reportError(errorCodeToError(EC), Obj->getFileName());
+void COFFDumper::printCGProfile() {
+ SectionRef CGProfileSection;
+ for (SectionRef Sec : Obj->sections()) {
+ StringRef Name = unwrapOrError(Obj->getFileName(), Sec.getName());
+ if (Name == ".llvm.call-graph-profile") {
+ CGProfileSection = Sec;
+ break;
+ }
+ }
- W.printNumber("Sym", SymName, SymIndex);
- Cur += Size;
+ if (CGProfileSection == SectionRef())
+ return;
+
+ StringRef CGProfileContents =
+ unwrapOrError(Obj->getFileName(), CGProfileSection.getContents());
+ BinaryStreamReader Reader(CGProfileContents, llvm::support::little);
+
+ ListScope L(W, "CGProfile");
+ while (!Reader.empty()) {
+ uint32_t FromIndex, ToIndex;
+ uint64_t Count;
+ if (Error Err = Reader.readInteger(FromIndex))
+ reportError(std::move(Err), Obj->getFileName());
+ if (Error Err = Reader.readInteger(ToIndex))
+ reportError(std::move(Err), Obj->getFileName());
+ if (Error Err = Reader.readInteger(Count))
+ reportError(std::move(Err), Obj->getFileName());
+
+ DictScope D(W, "CGProfileEntry");
+ W.printNumber("From", getSymbolName(FromIndex), FromIndex);
+ W.printNumber("To", getSymbolName(ToIndex), ToIndex);
+ W.printNumber("Weight", Count);
}
}
+StringRef COFFDumper::getSymbolName(uint32_t Index) {
+ Expected<COFFSymbolRef> Sym = Obj->getSymbol(Index);
+ if (!Sym)
+ reportError(Sym.takeError(), Obj->getFileName());
+
+ Expected<StringRef> SymName = Obj->getSymbolName(*Sym);
+ if (!SymName)
+ reportError(SymName.takeError(), Obj->getFileName());
+
+ return *SymName;
+}
+
void llvm::dumpCodeViewMergedTypes(ScopedPrinter &Writer,
ArrayRef<ArrayRef<uint8_t>> IpiRecords,
ArrayRef<ArrayRef<uint8_t>> TpiRecords) {
diff --git a/llvm/tools/llvm-readobj/DwarfCFIEHPrinter.h b/llvm/tools/llvm-readobj/DwarfCFIEHPrinter.h
index 0a365d4fe72a4..27942224053f1 100644
--- a/llvm/tools/llvm-readobj/DwarfCFIEHPrinter.h
+++ b/llvm/tools/llvm-readobj/DwarfCFIEHPrinter.h
@@ -30,12 +30,14 @@ namespace DwarfCFIEH {
template <typename ELFT>
class PrinterContext {
+ using Elf_Shdr = typename ELFT::Shdr;
+ using Elf_Phdr = typename ELFT::Phdr;
+
ScopedPrinter &W;
const object::ELFObjectFile<ELFT> *ObjF;
- void printEHFrameHdr(uint64_t Offset, uint64_t Address, uint64_t Size) const;
-
- void printEHFrame(const typename ELFT::Shdr *EHFrameShdr) const;
+ void printEHFrameHdr(const Elf_Phdr *EHFramePHdr) const;
+ void printEHFrame(const Elf_Shdr *EHFrameShdr) const;
public:
PrinterContext(ScopedPrinter &W, const object::ELFObjectFile<ELFT> *ObjF)
@@ -45,13 +47,14 @@ public:
};
template <class ELFT>
-static const typename object::ELFObjectFile<ELFT>::Elf_Shdr *
+static const typename ELFT::Shdr *
findSectionByAddress(const object::ELFObjectFile<ELFT> *ObjF, uint64_t Addr) {
- auto Sections = ObjF->getELFFile()->sections();
- if (Error E = Sections.takeError())
- reportError(std::move(E), ObjF->getFileName());
+ Expected<typename ELFT::ShdrRange> SectionsOrErr =
+ ObjF->getELFFile()->sections();
+ if (!SectionsOrErr)
+ reportError(SectionsOrErr.takeError(), ObjF->getFileName());
- for (const auto &Shdr : *Sections)
+ for (const typename ELFT::Shdr &Shdr : *SectionsOrErr)
if (Shdr.sh_addr == Addr)
return &Shdr;
return nullptr;
@@ -60,61 +63,59 @@ findSectionByAddress(const object::ELFObjectFile<ELFT> *ObjF, uint64_t Addr) {
template <typename ELFT>
void PrinterContext<ELFT>::printUnwindInformation() const {
const object::ELFFile<ELFT> *Obj = ObjF->getELFFile();
- const typename ELFT::Phdr *EHFramePhdr = nullptr;
- auto PHs = Obj->program_headers();
- if (Error E = PHs.takeError())
- reportError(std::move(E), ObjF->getFileName());
-
- for (const auto &Phdr : *PHs) {
- if (Phdr.p_type == ELF::PT_GNU_EH_FRAME) {
- EHFramePhdr = &Phdr;
- if (Phdr.p_memsz != Phdr.p_filesz)
- reportError(object::createError(
- "p_memsz does not match p_filesz for GNU_EH_FRAME"),
- ObjF->getFileName());
- break;
- }
- }
+ Expected<typename ELFT::PhdrRange> PhdrsOrErr = Obj->program_headers();
+ if (!PhdrsOrErr)
+ reportError(PhdrsOrErr.takeError(), ObjF->getFileName());
- if (EHFramePhdr)
- printEHFrameHdr(EHFramePhdr->p_offset, EHFramePhdr->p_vaddr,
- EHFramePhdr->p_memsz);
+ for (const Elf_Phdr &Phdr : *PhdrsOrErr) {
+ if (Phdr.p_type != ELF::PT_GNU_EH_FRAME)
+ continue;
- auto Sections = Obj->sections();
- if (Error E = Sections.takeError())
- reportError(std::move(E), ObjF->getFileName());
+ if (Phdr.p_memsz != Phdr.p_filesz)
+ reportError(object::createError(
+ "p_memsz does not match p_filesz for GNU_EH_FRAME"),
+ ObjF->getFileName());
+ printEHFrameHdr(&Phdr);
+ break;
+ }
- for (const auto &Shdr : *Sections) {
- auto SectionName = Obj->getSectionName(&Shdr);
- if (Error E = SectionName.takeError())
- reportError(std::move(E), ObjF->getFileName());
+ Expected<typename ELFT::ShdrRange> SectionsOrErr =
+ ObjF->getELFFile()->sections();
+ if (!SectionsOrErr)
+ reportError(SectionsOrErr.takeError(), ObjF->getFileName());
- if (*SectionName == ".eh_frame")
+ for (const Elf_Shdr &Shdr : *SectionsOrErr) {
+ Expected<StringRef> NameOrErr = Obj->getSectionName(&Shdr);
+ if (!NameOrErr)
+ reportError(NameOrErr.takeError(), ObjF->getFileName());
+ if (*NameOrErr == ".eh_frame")
printEHFrame(&Shdr);
}
}
template <typename ELFT>
-void PrinterContext<ELFT>::printEHFrameHdr(uint64_t EHFrameHdrOffset,
- uint64_t EHFrameHdrAddress,
- uint64_t EHFrameHdrSize) const {
+void PrinterContext<ELFT>::printEHFrameHdr(const Elf_Phdr *EHFramePHdr) const {
DictScope L(W, "EHFrameHeader");
+ uint64_t EHFrameHdrAddress = EHFramePHdr->p_vaddr;
W.startLine() << format("Address: 0x%" PRIx64 "\n", EHFrameHdrAddress);
- W.startLine() << format("Offset: 0x%" PRIx64 "\n", EHFrameHdrOffset);
- W.startLine() << format("Size: 0x%" PRIx64 "\n", EHFrameHdrSize);
+ W.startLine() << format("Offset: 0x%" PRIx64 "\n", (uint64_t)EHFramePHdr->p_offset);
+ W.startLine() << format("Size: 0x%" PRIx64 "\n", (uint64_t)EHFramePHdr->p_memsz);
const object::ELFFile<ELFT> *Obj = ObjF->getELFFile();
- const auto *EHFrameHdrShdr = findSectionByAddress(ObjF, EHFrameHdrAddress);
- if (EHFrameHdrShdr) {
- auto SectionName = Obj->getSectionName(EHFrameHdrShdr);
- if (Error E = SectionName.takeError())
- reportError(std::move(E), ObjF->getFileName());
-
- W.printString("Corresponding Section", *SectionName);
+ if (const Elf_Shdr *EHFrameHdr =
+ findSectionByAddress(ObjF, EHFramePHdr->p_vaddr)) {
+ Expected<StringRef> NameOrErr = Obj->getSectionName(EHFrameHdr);
+ if (!NameOrErr)
+ reportError(NameOrErr.takeError(), ObjF->getFileName());
+ W.printString("Corresponding Section", *NameOrErr);
}
- DataExtractor DE(makeArrayRef(Obj->base() + EHFrameHdrOffset, EHFrameHdrSize),
+ Expected<ArrayRef<uint8_t>> Content = Obj->getSegmentContents(EHFramePHdr);
+ if (!Content)
+ reportError(Content.takeError(), ObjF->getFileName());
+
+ DataExtractor DE(*Content,
ELFT::TargetEndianness == support::endianness::little,
ELFT::Is64Bits ? 8 : 4);
@@ -154,7 +155,7 @@ void PrinterContext<ELFT>::printEHFrameHdr(uint64_t EHFrameHdrOffset,
unsigned NumEntries = 0;
uint64_t PrevPC = 0;
- while (Offset + 8 <= EHFrameHdrSize && NumEntries < FDECount) {
+ while (Offset + 8 <= EHFramePHdr->p_memsz && NumEntries < FDECount) {
DictScope D(W, std::string("entry ") + std::to_string(NumEntries));
auto InitialPC = DE.getSigned(&Offset, 4) + EHFrameHdrAddress;
@@ -172,8 +173,7 @@ void PrinterContext<ELFT>::printEHFrameHdr(uint64_t EHFrameHdrOffset,
}
template <typename ELFT>
-void PrinterContext<ELFT>::printEHFrame(
- const typename ELFT::Shdr *EHFrameShdr) const {
+void PrinterContext<ELFT>::printEHFrame(const Elf_Shdr *EHFrameShdr) const {
uint64_t Address = EHFrameShdr->sh_addr;
uint64_t ShOffset = EHFrameShdr->sh_offset;
W.startLine() << format(".eh_frame section at offset 0x%" PRIx64
@@ -181,26 +181,23 @@ void PrinterContext<ELFT>::printEHFrame(
ShOffset, Address);
W.indent();
- const object::ELFFile<ELFT> *Obj = ObjF->getELFFile();
- auto Result = Obj->getSectionContents(EHFrameShdr);
- if (Error E = Result.takeError())
- reportError(std::move(E), ObjF->getFileName());
+ Expected<ArrayRef<uint8_t>> DataOrErr =
+ ObjF->getELFFile()->getSectionContents(EHFrameShdr);
+ if (!DataOrErr)
+ reportError(DataOrErr.takeError(), ObjF->getFileName());
- auto Contents = Result.get();
- DWARFDataExtractor DE(
- StringRef(reinterpret_cast<const char *>(Contents.data()),
- Contents.size()),
- ELFT::TargetEndianness == support::endianness::little,
- ELFT::Is64Bits ? 8 : 4);
+ DWARFDataExtractor DE(*DataOrErr,
+ ELFT::TargetEndianness == support::endianness::little,
+ ELFT::Is64Bits ? 8 : 4);
DWARFDebugFrame EHFrame(Triple::ArchType(ObjF->getArch()), /*IsEH=*/true,
/*EHFrameAddress=*/Address);
- EHFrame.parse(DE);
+ if (Error E = EHFrame.parse(DE))
+ reportError(std::move(E), ObjF->getFileName());
- for (const auto &Entry : EHFrame) {
- if (const auto *CIE = dyn_cast<dwarf::CIE>(&Entry)) {
+ for (const dwarf::FrameEntry &Entry : EHFrame) {
+ if (const dwarf::CIE *CIE = dyn_cast<dwarf::CIE>(&Entry)) {
W.startLine() << format("[0x%" PRIx64 "] CIE length=%" PRIu64 "\n",
- Address + CIE->getOffset(),
- CIE->getLength());
+ Address + CIE->getOffset(), CIE->getLength());
W.indent();
W.printNumber("version", CIE->getVersion());
@@ -208,47 +205,33 @@ void PrinterContext<ELFT>::printEHFrame(
W.printNumber("code_alignment_factor", CIE->getCodeAlignmentFactor());
W.printNumber("data_alignment_factor", CIE->getDataAlignmentFactor());
W.printNumber("return_address_register", CIE->getReturnAddressRegister());
-
- W.getOStream() << "\n";
- W.startLine() << "Program:\n";
- W.indent();
- CIE->cfis().dump(W.getOStream(), nullptr, W.getIndentLevel());
- W.unindent();
-
- W.unindent();
- W.getOStream() << "\n";
-
- } else if (const auto *FDE = dyn_cast<dwarf::FDE>(&Entry)) {
+ } else {
+ const dwarf::FDE *FDE = cast<dwarf::FDE>(&Entry);
W.startLine() << format("[0x%" PRIx64 "] FDE length=%" PRIu64
" cie=[0x%" PRIx64 "]\n",
- Address + FDE->getOffset(),
- FDE->getLength(),
+ Address + FDE->getOffset(), FDE->getLength(),
Address + FDE->getLinkedCIE()->getOffset());
W.indent();
W.startLine() << format("initial_location: 0x%" PRIx64 "\n",
FDE->getInitialLocation());
- W.startLine()
- << format("address_range: 0x%" PRIx64 " (end : 0x%" PRIx64 ")\n",
- FDE->getAddressRange(),
- FDE->getInitialLocation() + FDE->getAddressRange());
-
- W.getOStream() << "\n";
- W.startLine() << "Program:\n";
- W.indent();
- FDE->cfis().dump(W.getOStream(), nullptr, W.getIndentLevel());
- W.unindent();
-
- W.unindent();
- W.getOStream() << "\n";
- } else {
- llvm_unreachable("unexpected DWARF frame kind");
+ W.startLine() << format(
+ "address_range: 0x%" PRIx64 " (end : 0x%" PRIx64 ")\n",
+ FDE->getAddressRange(),
+ FDE->getInitialLocation() + FDE->getAddressRange());
}
+
+ W.getOStream() << "\n";
+ W.startLine() << "Program:\n";
+ W.indent();
+ Entry.cfis().dump(W.getOStream(), nullptr, W.getIndentLevel());
+ W.unindent();
+ W.unindent();
+ W.getOStream() << "\n";
}
W.unindent();
}
-
}
}
diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 8ffb68283405b..15076f1f89337 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -52,6 +52,8 @@
#include "llvm/Support/LEB128.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/MipsABIFlags.h"
+#include "llvm/Support/RISCVAttributeParser.h"
+#include "llvm/Support/RISCVAttributes.h"
#include "llvm/Support/ScopedPrinter.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
@@ -135,19 +137,34 @@ struct DynRegionInfo {
/// Name of the file. Used for error reporting.
StringRef FileName;
+ /// Error prefix. Used for error reporting to provide more information.
+ std::string Context;
+ /// Region size name. Used for error reporting.
+ StringRef SizePrintName = "size";
+ /// Entry size name. Used for error reporting. If this field is empty, errors
+ /// will not mention the entry size.
+ StringRef EntSizePrintName = "entry size";
template <typename Type> ArrayRef<Type> getAsArrayRef() const {
const Type *Start = reinterpret_cast<const Type *>(Addr);
if (!Start)
return {Start, Start};
- if (EntSize != sizeof(Type) || Size % EntSize) {
- // TODO: Add a section index to this warning.
- reportWarning(createError("invalid section size (" + Twine(Size) +
- ") or entity size (" + Twine(EntSize) + ")"),
- FileName);
- return {Start, Start};
- }
- return {Start, Start + (Size / EntSize)};
+ if (EntSize == sizeof(Type) && (Size % EntSize == 0))
+ return {Start, Start + (Size / EntSize)};
+
+ std::string Msg;
+ if (!Context.empty())
+ Msg += Context + " has ";
+
+ Msg += ("invalid " + SizePrintName + " (0x" + Twine::utohexstr(Size) + ")")
+ .str();
+ if (!EntSizePrintName.empty())
+ Msg +=
+ (" or " + EntSizePrintName + " (0x" + Twine::utohexstr(EntSize) + ")")
+ .str();
+
+ reportWarning(createError(Msg.c_str()), FileName);
+ return {Start, Start};
}
};
@@ -204,7 +221,7 @@ public:
void printProgramHeaders(bool PrintProgramHeaders,
cl::boolOrDefault PrintSectionMapping) override;
void printHashTable() override;
- void printGnuHashTable() override;
+ void printGnuHashTable(const object::ObjectFile *Obj) override;
void printLoadName() override;
void printVersionInfo() override;
void printGroupSections() override;
@@ -213,7 +230,7 @@ public:
void printStackMap() const override;
- void printHashHistogram() override;
+ void printHashHistograms() override;
void printCGProfile() override;
void printAddrsig() override;
@@ -268,10 +285,10 @@ private:
DynRegionInfo DynRelaRegion;
DynRegionInfo DynRelrRegion;
DynRegionInfo DynPLTRelRegion;
- DynRegionInfo DynSymRegion;
+ Optional<DynRegionInfo> DynSymRegion;
DynRegionInfo DynamicTable;
StringRef DynamicStringTable;
- std::string SOName = "<Not found>";
+ StringRef SOName = "<Not found>";
const Elf_Hash *HashTable = nullptr;
const Elf_GnuHash *GnuHashTable = nullptr;
const Elf_Shdr *DotSymtabSec = nullptr;
@@ -290,6 +307,8 @@ private:
};
mutable SmallVector<Optional<VersionEntry>, 16> VersionMap;
+ std::unordered_set<std::string> Warnings;
+
public:
Elf_Dyn_Range dynamic_table() const {
// A valid .dynamic section contains an array of entries terminated
@@ -306,26 +325,31 @@ public:
return Table.slice(0, Size);
}
+ Optional<DynRegionInfo> getDynSymRegion() const { return DynSymRegion; }
+
Elf_Sym_Range dynamic_symbols() const {
- return DynSymRegion.getAsArrayRef<Elf_Sym>();
+ if (!DynSymRegion)
+ return Elf_Sym_Range();
+ return DynSymRegion->getAsArrayRef<Elf_Sym>();
}
Elf_Rel_Range dyn_rels() const;
Elf_Rela_Range dyn_relas() const;
Elf_Relr_Range dyn_relrs() const;
- std::string getFullSymbolName(const Elf_Sym *Symbol, StringRef StrTable,
+ std::string getFullSymbolName(const Elf_Sym *Symbol,
+ Optional<StringRef> StrTable,
bool IsDynamic) const;
Expected<unsigned> getSymbolSectionIndex(const Elf_Sym *Symbol,
const Elf_Sym *FirstSym) const;
Expected<StringRef> getSymbolSectionName(const Elf_Sym *Symbol,
unsigned SectionIndex) const;
- Expected<std::string> getStaticSymbolName(uint32_t Index) const;
- std::string getDynamicString(uint64_t Value) const;
+ std::string getStaticSymbolName(uint32_t Index) const;
+ StringRef getDynamicString(uint64_t Value) const;
Expected<StringRef> getSymbolVersionByIndex(uint32_t VersionSymbolIndex,
bool &IsDefault) const;
void printSymbolsHelper(bool IsDynamic) const;
- void printDynamicEntry(raw_ostream &OS, uint64_t Type, uint64_t Value) const;
+ std::string getDynamicEntry(uint64_t Type, uint64_t Value) const;
const Elf_Shdr *getDotSymtabSec() const { return DotSymtabSec; }
const Elf_Shdr *getDotCGProfileSec() const { return DotCGProfileSec; }
@@ -347,6 +371,12 @@ public:
getVersionDefinitions(const Elf_Shdr *Sec) const;
Expected<std::vector<VerNeed>>
getVersionDependencies(const Elf_Shdr *Sec) const;
+
+ Expected<std::pair<const Elf_Sym *, std::string>>
+ getRelocationTarget(const Elf_Shdr *SymTab, const Elf_Rela &R) const;
+
+ std::function<Error(const Twine &Msg)> WarningHandler;
+ void reportUniqueWarning(Error Err) const;
};
template <class ELFT>
@@ -439,12 +469,12 @@ ELFDumper<ELFT>::getVersionTable(const Elf_Shdr *Sec, ArrayRef<Elf_Sym> *SymTab,
Expected<std::pair<ArrayRef<Elf_Sym>, StringRef>> SymTabOrErr =
getLinkAsSymtab(Obj, Sec, SecNdx, SHT_DYNSYM);
if (!SymTabOrErr) {
- ELFDumperStyle->reportUniqueWarning(SymTabOrErr.takeError());
+ reportUniqueWarning(SymTabOrErr.takeError());
return *VersionsOrErr;
}
if (SymTabOrErr->first.size() != VersionsOrErr->size())
- ELFDumperStyle->reportUniqueWarning(
+ reportUniqueWarning(
createError("SHT_GNU_versym section with index " + Twine(SecNdx) +
": the number of entries (" + Twine(VersionsOrErr->size()) +
") does not match the number of symbols (" +
@@ -490,7 +520,7 @@ ELFDumper<ELFT>::getVersionDefinitions(const Elf_Shdr *Sec) const {
VerdAux Aux;
Aux.Offset = VerdauxBuf - Start;
if (Verdaux->vda_name <= StrTabOrErr->size())
- Aux.Name = StrTabOrErr->drop_front(Verdaux->vda_name);
+ Aux.Name = std::string(StrTabOrErr->drop_front(Verdaux->vda_name));
else
Aux.Name = "<invalid vda_name: " + to_string(Verdaux->vda_name) + ">";
return Aux;
@@ -558,7 +588,7 @@ ELFDumper<ELFT>::getVersionDependencies(const Elf_Shdr *Sec) const {
StringRef StrTab;
Expected<StringRef> StrTabOrErr = getLinkAsStrtab(Obj, Sec, SecNdx);
if (!StrTabOrErr)
- ELFDumperStyle->reportUniqueWarning(StrTabOrErr.takeError());
+ reportUniqueWarning(StrTabOrErr.takeError());
else
StrTab = *StrTabOrErr;
@@ -600,7 +630,7 @@ ELFDumper<ELFT>::getVersionDependencies(const Elf_Shdr *Sec) const {
VN.Offset = VerneedBuf - Start;
if (Verneed->vn_file < StrTab.size())
- VN.File = StrTab.drop_front(Verneed->vn_file);
+ VN.File = std::string(StrTab.drop_front(Verneed->vn_file));
else
VN.File = "<corrupt vn_file: " + to_string(Verneed->vn_file) + ">";
@@ -630,7 +660,7 @@ ELFDumper<ELFT>::getVersionDependencies(const Elf_Shdr *Sec) const {
if (StrTab.size() <= Vernaux->vna_name)
Aux.Name = "<corrupt>";
else
- Aux.Name = StrTab.drop_front(Vernaux->vna_name);
+ Aux.Name = std::string(StrTab.drop_front(Vernaux->vna_name));
VernauxBuf += Vernaux->vna_next;
}
@@ -641,7 +671,8 @@ ELFDumper<ELFT>::getVersionDependencies(const Elf_Shdr *Sec) const {
template <class ELFT>
void ELFDumper<ELFT>::printSymbolsHelper(bool IsDynamic) const {
- StringRef StrTable, SymtabName;
+ Optional<StringRef> StrTable;
+ StringRef SymtabName;
size_t Entries = 0;
Elf_Sym_Range Syms(nullptr, nullptr);
const ELFFile<ELFT> *Obj = ObjF->getELFFile();
@@ -649,16 +680,36 @@ void ELFDumper<ELFT>::printSymbolsHelper(bool IsDynamic) const {
StrTable = DynamicStringTable;
Syms = dynamic_symbols();
SymtabName = DynSymtabName;
- if (DynSymRegion.Addr)
- Entries = DynSymRegion.Size / DynSymRegion.EntSize;
+ Entries = Syms.size();
} else {
if (!DotSymtabSec)
return;
- StrTable = unwrapOrError(ObjF->getFileName(),
- Obj->getStringTableForSymtab(*DotSymtabSec));
- Syms = unwrapOrError(ObjF->getFileName(), Obj->symbols(DotSymtabSec));
- SymtabName =
- unwrapOrError(ObjF->getFileName(), Obj->getSectionName(DotSymtabSec));
+
+ if (Expected<StringRef> StrTableOrErr =
+ Obj->getStringTableForSymtab(*DotSymtabSec))
+ StrTable = *StrTableOrErr;
+ else
+ reportUniqueWarning(createError(
+ "unable to get the string table for the SHT_SYMTAB section: " +
+ toString(StrTableOrErr.takeError())));
+
+ if (Expected<Elf_Sym_Range> SymsOrErr = Obj->symbols(DotSymtabSec))
+ Syms = *SymsOrErr;
+ else
+ reportUniqueWarning(
+ createError("unable to read symbols from the SHT_SYMTAB section: " +
+ toString(SymsOrErr.takeError())));
+
+ if (Expected<StringRef> SymtabNameOrErr =
+ Obj->getSectionName(DotSymtabSec)) {
+ SymtabName = *SymtabNameOrErr;
+ } else {
+ reportUniqueWarning(
+ createError("unable to get the name of the SHT_SYMTAB section: " +
+ toString(SymtabNameOrErr.takeError())));
+ SymtabName = "<?>";
+ }
+
Entries = DotSymtabSec->getEntityCount();
}
if (Syms.begin() == Syms.end())
@@ -687,14 +738,6 @@ public:
DumpStyle(ELFDumper<ELFT> *Dumper) : Dumper(Dumper) {
FileName = this->Dumper->getElfObject()->getFileName();
-
- // Dumper reports all non-critical errors as warnings.
- // It does not print the same warning more than once.
- WarningHandler = [this](const Twine &Msg) {
- if (Warnings.insert(Msg.str()).second)
- reportWarning(createError(Msg), FileName);
- return Error::success();
- };
}
virtual ~DumpStyle() = default;
@@ -712,8 +755,9 @@ public:
virtual void printSymtabMessage(const ELFFile<ELFT> *Obj, StringRef Name,
size_t Offset, bool NonVisibilityBitsUsed) {}
virtual void printSymbol(const ELFFile<ELFT> *Obj, const Elf_Sym *Symbol,
- const Elf_Sym *FirstSym, StringRef StrTable,
- bool IsDynamic, bool NonVisibilityBitsUsed) = 0;
+ const Elf_Sym *FirstSym,
+ Optional<StringRef> StrTable, bool IsDynamic,
+ bool NonVisibilityBitsUsed) = 0;
virtual void printProgramHeaders(const ELFFile<ELFT> *Obj,
bool PrintProgramHeaders,
cl::boolOrDefault PrintSectionMapping) = 0;
@@ -723,7 +767,7 @@ public:
const Elf_Shdr *Sec) = 0;
virtual void printVersionDependencySection(const ELFFile<ELFT> *Obj,
const Elf_Shdr *Sec) = 0;
- virtual void printHashHistogram(const ELFFile<ELFT> *Obj) = 0;
+ virtual void printHashHistograms(const ELFFile<ELFT> *Obj) = 0;
virtual void printCGProfile(const ELFFile<ELFT> *Obj) = 0;
virtual void printAddrsig(const ELFFile<ELFT> *Obj) = 0;
virtual void printNotes(const ELFFile<ELFT> *Obj) = 0;
@@ -734,7 +778,7 @@ public:
void printRelocatableStackSizes(const ELFObjectFile<ELFT> *Obj,
std::function<void()> PrintHeader);
void printFunctionStackSize(const ELFObjectFile<ELFT> *Obj, uint64_t SymValue,
- SectionRef FunctionSec,
+ Optional<SectionRef> FunctionSec,
const StringRef SectionName, DataExtractor Data,
uint64_t *Offset);
void printStackSize(const ELFObjectFile<ELFT> *Obj, RelocationRef Rel,
@@ -747,14 +791,16 @@ public:
virtual void printMipsABIFlags(const ELFObjectFile<ELFT> *Obj) = 0;
const ELFDumper<ELFT> *dumper() const { return Dumper; }
- void reportUniqueWarning(Error Err) const;
-
protected:
- std::function<Error(const Twine &Msg)> WarningHandler;
+ void printDependentLibsHelper(
+ const ELFFile<ELFT> *Obj,
+ function_ref<void(const Elf_Shdr &)> OnSectionStart,
+ function_ref<void(StringRef, uint64_t)> OnSectionEntry);
+
+ void reportUniqueWarning(Error Err) const;
StringRef FileName;
private:
- std::unordered_set<std::string> Warnings;
const ELFDumper<ELFT> *Dumper;
};
@@ -790,7 +836,7 @@ public:
const Elf_Shdr *Sec) override;
void printVersionDependencySection(const ELFFile<ELFT> *Obj,
const Elf_Shdr *Sec) override;
- void printHashHistogram(const ELFFile<ELFT> *Obj) override;
+ void printHashHistograms(const ELFFile<ELFT> *Obj) override;
void printCGProfile(const ELFFile<ELFT> *Obj) override;
void printAddrsig(const ELFFile<ELFT> *Obj) override;
void printNotes(const ELFFile<ELFT> *Obj) override;
@@ -802,11 +848,18 @@ public:
void printMipsABIFlags(const ELFObjectFile<ELFT> *Obj) override;
private:
+ void printHashHistogram(const Elf_Hash &HashTable);
+ void printGnuHashHistogram(const Elf_GnuHash &GnuHashTable);
+
+ void printHashTableSymbols(const ELFO *Obj, const Elf_Hash &HashTable);
+ void printGnuHashTableSymbols(const ELFO *Obj,
+ const Elf_GnuHash &GnuHashTable);
+
struct Field {
std::string Str;
unsigned Column;
- Field(StringRef S, unsigned Col) : Str(S), Column(Col) {}
+ Field(StringRef S, unsigned Col) : Str(std::string(S)), Column(Col) {}
Field(unsigned Col) : Column(Col) {}
};
@@ -814,7 +867,7 @@ private:
std::string printEnum(T Value, ArrayRef<EnumEntry<TEnum>> EnumValues) {
for (const auto &EnumItem : EnumValues)
if (EnumItem.Value == Value)
- return EnumItem.AltName;
+ return std::string(EnumItem.AltName);
return to_hexString(Value, false);
}
@@ -855,20 +908,17 @@ private:
void printHashedSymbol(const ELFO *Obj, const Elf_Sym *FirstSym, uint32_t Sym,
StringRef StrTable, uint32_t Bucket);
void printRelocHeader(unsigned SType);
- void printRelocation(const ELFO *Obj, const Elf_Shdr *SymTab,
- const Elf_Rela &R, bool IsRela);
+ void printRelocation(const ELFO *Obj, unsigned SecIndex,
+ const Elf_Shdr *SymTab, const Elf_Rela &R,
+ unsigned RelIndex, bool IsRela);
void printRelocation(const ELFO *Obj, const Elf_Sym *Sym,
StringRef SymbolName, const Elf_Rela &R, bool IsRela);
void printSymbol(const ELFO *Obj, const Elf_Sym *Symbol, const Elf_Sym *First,
- StringRef StrTable, bool IsDynamic,
+ Optional<StringRef> StrTable, bool IsDynamic,
bool NonVisibilityBitsUsed) override;
std::string getSymbolSectionNdx(const ELFO *Obj, const Elf_Sym *Symbol,
const Elf_Sym *FirstSym);
void printDynamicRelocation(const ELFO *Obj, Elf_Rela R, bool IsRela);
- bool checkTLSSections(const Elf_Phdr &Phdr, const Elf_Shdr &Sec);
- bool checkoffsets(const Elf_Phdr &Phdr, const Elf_Shdr &Sec);
- bool checkVMA(const Elf_Phdr &Phdr, const Elf_Shdr &Sec);
- bool checkPTDynamic(const Elf_Phdr &Phdr, const Elf_Shdr &Sec);
void printProgramHeaders(const ELFO *Obj);
void printSectionMapping(const ELFO *Obj);
void printGNUVersionSectionProlog(const ELFFile<ELFT> *Obj,
@@ -877,13 +927,18 @@ private:
};
template <class ELFT>
-void DumpStyle<ELFT>::reportUniqueWarning(Error Err) const {
+void ELFDumper<ELFT>::reportUniqueWarning(Error Err) const {
handleAllErrors(std::move(Err), [&](const ErrorInfoBase &EI) {
cantFail(WarningHandler(EI.message()),
"WarningHandler should always return ErrorSuccess");
});
}
+template <class ELFT>
+void DumpStyle<ELFT>::reportUniqueWarning(Error Err) const {
+ this->dumper()->reportUniqueWarning(std::move(Err));
+}
+
template <typename ELFT> class LLVMStyle : public DumpStyle<ELFT> {
public:
TYPEDEF_ELF_TYPES(ELFT)
@@ -909,7 +964,7 @@ public:
const Elf_Shdr *Sec) override;
void printVersionDependencySection(const ELFFile<ELFT> *Obj,
const Elf_Shdr *Sec) override;
- void printHashHistogram(const ELFFile<ELFT> *Obj) override;
+ void printHashHistograms(const ELFFile<ELFT> *Obj) override;
void printCGProfile(const ELFFile<ELFT> *Obj) override;
void printAddrsig(const ELFFile<ELFT> *Obj) override;
void printNotes(const ELFFile<ELFT> *Obj) override;
@@ -921,13 +976,14 @@ public:
void printMipsABIFlags(const ELFObjectFile<ELFT> *Obj) override;
private:
- void printRelocation(const ELFO *Obj, Elf_Rela Rel, const Elf_Shdr *SymTab);
+ void printRelocation(const ELFO *Obj, unsigned SecIndex, Elf_Rela Rel,
+ unsigned RelIndex, const Elf_Shdr *SymTab);
void printDynamicRelocation(const ELFO *Obj, Elf_Rela Rel);
void printSymbols(const ELFO *Obj);
void printDynamicSymbols(const ELFO *Obj);
void printSymbolSection(const Elf_Sym *Symbol, const Elf_Sym *First);
void printSymbol(const ELFO *Obj, const Elf_Sym *Symbol, const Elf_Sym *First,
- StringRef StrTable, bool IsDynamic,
+ Optional<StringRef> StrTable, bool IsDynamic,
bool /*NonVisibilityBitsUsed*/) override;
void printProgramHeaders(const ELFO *Obj);
void printSectionMapping(const ELFO *Obj) {}
@@ -973,7 +1029,7 @@ std::error_code createELFDumper(const object::ObjectFile *Obj,
template <class ELFT> Error ELFDumper<ELFT>::LoadVersionMap() const {
// If there is no dynamic symtab or version table, there is nothing to do.
- if (!DynSymRegion.Addr || !SymbolVersionSection)
+ if (!DynSymRegion || !SymbolVersionSection)
return Error::success();
// Has the VersionMap already been loaded?
@@ -988,7 +1044,7 @@ template <class ELFT> Error ELFDumper<ELFT>::LoadVersionMap() const {
auto InsertEntry = [this](unsigned N, StringRef Version, bool IsVerdef) {
if (N >= VersionMap.size())
VersionMap.resize(N + 1);
- VersionMap[N] = {Version, IsVerdef};
+ VersionMap[N] = {std::string(Version), IsVerdef};
};
if (SymbolVersionDefSection) {
@@ -1023,38 +1079,85 @@ Expected<StringRef> ELFDumper<ELFT>::getSymbolVersion(const Elf_Sym *Sym,
return "";
}
+ assert(DynSymRegion && "DynSymRegion has not been initialised");
// Determine the position in the symbol table of this entry.
size_t EntryIndex = (reinterpret_cast<uintptr_t>(Sym) -
- reinterpret_cast<uintptr_t>(DynSymRegion.Addr)) /
- sizeof(Elf_Sym);
+ reinterpret_cast<uintptr_t>(DynSymRegion->Addr)) /
+ sizeof(Elf_Sym);
// Get the corresponding version index entry.
- const Elf_Versym *Versym = unwrapOrError(
- ObjF->getFileName(), ObjF->getELFFile()->template getEntry<Elf_Versym>(
- SymbolVersionSection, EntryIndex));
- return this->getSymbolVersionByIndex(Versym->vs_index, IsDefault);
+ if (Expected<const Elf_Versym *> EntryOrErr =
+ ObjF->getELFFile()->template getEntry<Elf_Versym>(
+ SymbolVersionSection, EntryIndex))
+ return this->getSymbolVersionByIndex((*EntryOrErr)->vs_index, IsDefault);
+ else
+ return EntryOrErr.takeError();
+}
+
+template <typename ELFT>
+Expected<std::pair<const typename ELFT::Sym *, std::string>>
+ELFDumper<ELFT>::getRelocationTarget(const Elf_Shdr *SymTab,
+ const Elf_Rela &R) const {
+ const ELFFile<ELFT> *Obj = ObjF->getELFFile();
+ Expected<const Elf_Sym *> SymOrErr = Obj->getRelocationSymbol(&R, SymTab);
+ if (!SymOrErr)
+ return SymOrErr.takeError();
+ const Elf_Sym *Sym = *SymOrErr;
+ if (!Sym)
+ return std::make_pair(nullptr, "");
+
+ // The st_name field of a STT_SECTION is usually 0 (empty string).
+ // This code block returns the section name.
+ if (Sym->getType() == ELF::STT_SECTION) {
+ Expected<const Elf_Shdr *> SecOrErr =
+ Obj->getSection(Sym, SymTab, ShndxTable);
+ if (!SecOrErr)
+ return SecOrErr.takeError();
+ // A section symbol describes the section at index 0.
+ if (*SecOrErr == nullptr)
+ return std::make_pair(Sym, "");
+
+ Expected<StringRef> NameOrErr = Obj->getSectionName(*SecOrErr);
+ if (!NameOrErr)
+ return NameOrErr.takeError();
+ return std::make_pair(Sym, NameOrErr->str());
+ }
+
+ Expected<StringRef> StrTableOrErr = Obj->getStringTableForSymtab(*SymTab);
+ if (!StrTableOrErr)
+ return StrTableOrErr.takeError();
+
+ std::string SymbolName =
+ getFullSymbolName(Sym, *StrTableOrErr, SymTab->sh_type == SHT_DYNSYM);
+ return std::make_pair(Sym, SymbolName);
}
static std::string maybeDemangle(StringRef Name) {
- return opts::Demangle ? demangle(Name) : Name.str();
+ return opts::Demangle ? demangle(std::string(Name)) : Name.str();
}
template <typename ELFT>
-Expected<std::string>
-ELFDumper<ELFT>::getStaticSymbolName(uint32_t Index) const {
+std::string ELFDumper<ELFT>::getStaticSymbolName(uint32_t Index) const {
+ auto Warn = [&](Error E) -> std::string {
+ this->reportUniqueWarning(
+ createError("unable to read the name of symbol with index " +
+ Twine(Index) + ": " + toString(std::move(E))));
+ return "<?>";
+ };
+
const ELFFile<ELFT> *Obj = ObjF->getELFFile();
Expected<const typename ELFT::Sym *> SymOrErr =
Obj->getSymbol(DotSymtabSec, Index);
if (!SymOrErr)
- return SymOrErr.takeError();
+ return Warn(SymOrErr.takeError());
Expected<StringRef> StrTabOrErr = Obj->getStringTableForSymtab(*DotSymtabSec);
if (!StrTabOrErr)
- return StrTabOrErr.takeError();
+ return Warn(StrTabOrErr.takeError());
Expected<StringRef> NameOrErr = (*SymOrErr)->getName(*StrTabOrErr);
if (!NameOrErr)
- return NameOrErr.takeError();
+ return Warn(NameOrErr.takeError());
return maybeDemangle(*NameOrErr);
}
@@ -1087,10 +1190,18 @@ ELFDumper<ELFT>::getSymbolVersionByIndex(uint32_t SymbolVersionIndex,
template <typename ELFT>
std::string ELFDumper<ELFT>::getFullSymbolName(const Elf_Sym *Symbol,
- StringRef StrTable,
+ Optional<StringRef> StrTable,
bool IsDynamic) const {
- std::string SymbolName = maybeDemangle(
- unwrapOrError(ObjF->getFileName(), Symbol->getName(StrTable)));
+ if (!StrTable)
+ return "<?>";
+
+ std::string SymbolName;
+ if (Expected<StringRef> NameOrErr = Symbol->getName(*StrTable)) {
+ SymbolName = maybeDemangle(*NameOrErr);
+ } else {
+ reportUniqueWarning(NameOrErr.takeError());
+ return "<?>";
+ }
if (SymbolName.empty() && Symbol->getType() == ELF::STT_SECTION) {
Elf_Sym_Range Syms = unwrapOrError(
@@ -1098,15 +1209,15 @@ std::string ELFDumper<ELFT>::getFullSymbolName(const Elf_Sym *Symbol,
Expected<unsigned> SectionIndex =
getSymbolSectionIndex(Symbol, Syms.begin());
if (!SectionIndex) {
- ELFDumperStyle->reportUniqueWarning(SectionIndex.takeError());
+ reportUniqueWarning(SectionIndex.takeError());
return "<?>";
}
Expected<StringRef> NameOrErr = getSymbolSectionName(Symbol, *SectionIndex);
if (!NameOrErr) {
- ELFDumperStyle->reportUniqueWarning(NameOrErr.takeError());
+ reportUniqueWarning(NameOrErr.takeError());
return ("<section " + Twine(*SectionIndex) + ">").str();
}
- return *NameOrErr;
+ return std::string(*NameOrErr);
}
if (!IsDynamic)
@@ -1115,7 +1226,7 @@ std::string ELFDumper<ELFT>::getFullSymbolName(const Elf_Sym *Symbol,
bool IsDefault;
Expected<StringRef> VersionOrErr = getSymbolVersion(&*Symbol, IsDefault);
if (!VersionOrErr) {
- ELFDumperStyle->reportUniqueWarning(VersionOrErr.takeError());
+ reportUniqueWarning(VersionOrErr.takeError());
return SymbolName + "@<corrupt>";
}
@@ -1170,7 +1281,7 @@ template <class ELFO>
static const typename ELFO::Elf_Shdr *
findNotEmptySectionByAddress(const ELFO *Obj, StringRef FileName,
uint64_t Addr) {
- for (const auto &Shdr : unwrapOrError(FileName, Obj->sections()))
+ for (const typename ELFO::Elf_Shdr &Shdr : cantFail(Obj->sections()))
if (Shdr.sh_addr == Addr && Shdr.sh_size > 0)
return &Shdr;
return nullptr;
@@ -1179,7 +1290,7 @@ findNotEmptySectionByAddress(const ELFO *Obj, StringRef FileName,
template <class ELFO>
static const typename ELFO::Elf_Shdr *
findSectionByName(const ELFO &Obj, StringRef FileName, StringRef Name) {
- for (const auto &Shdr : unwrapOrError(FileName, Obj.sections()))
+ for (const typename ELFO::Elf_Shdr &Shdr : cantFail(Obj.sections()))
if (Name == unwrapOrError(FileName, Obj.getSectionName(&Shdr)))
return &Shdr;
return nullptr;
@@ -1372,6 +1483,8 @@ static const EnumEntry<unsigned> ElfMachineType[] = {
ENUM_ENT(EM_STXP7X, "STMicroelectronics STxP7x family"),
ENUM_ENT(EM_NDS32, "Andes Technology compact code size embedded RISC processor family"),
ENUM_ENT(EM_ECOG1, "Cyan Technology eCOG1 microprocessor"),
+ // FIXME: Following EM_ECOG1X definitions is dead code since EM_ECOG1X has
+ // an identical number to EM_ECOG1.
ENUM_ENT(EM_ECOG1X, "Cyan Technology eCOG1X family"),
ENUM_ENT(EM_MAXQ30, "Dallas Semiconductor MAXQ30 Core microcontrollers"),
ENUM_ENT(EM_XIMO16, "New Japan Radio (NJR) 16-bit DSP Processor"),
@@ -1406,6 +1519,7 @@ static const EnumEntry<unsigned> ElfMachineType[] = {
ENUM_ENT(EM_RISCV, "RISC-V"),
ENUM_ENT(EM_LANAI, "EM_LANAI"),
ENUM_ENT(EM_BPF, "EM_BPF"),
+ ENUM_ENT(EM_VE, "NEC SX-Aurora Vector Engine"),
};
static const EnumEntry<unsigned> ElfSymbolBindings[] = {
@@ -1731,6 +1845,7 @@ static const EnumEntry<unsigned> ElfHeaderAMDGPUFlags[] = {
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_GFX1030),
LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_XNACK),
LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_SRAM_ECC)
};
@@ -1786,18 +1901,22 @@ std::pair<const typename ELFT::Phdr *, const typename ELFT::Shdr *>
ELFDumper<ELFT>::findDynamic(const ELFFile<ELFT> *Obj) {
// Try to locate the PT_DYNAMIC header.
const Elf_Phdr *DynamicPhdr = nullptr;
- for (const Elf_Phdr &Phdr :
- unwrapOrError(ObjF->getFileName(), Obj->program_headers())) {
- if (Phdr.p_type != ELF::PT_DYNAMIC)
- continue;
- DynamicPhdr = &Phdr;
- break;
+ if (Expected<ArrayRef<Elf_Phdr>> PhdrsOrErr = Obj->program_headers()) {
+ for (const Elf_Phdr &Phdr : *PhdrsOrErr) {
+ if (Phdr.p_type != ELF::PT_DYNAMIC)
+ continue;
+ DynamicPhdr = &Phdr;
+ break;
+ }
+ } else {
+ this->reportUniqueWarning(createError(
+ "unable to read program headers to locate the PT_DYNAMIC segment: " +
+ toString(PhdrsOrErr.takeError())));
}
// Try to locate the .dynamic section in the sections header table.
const Elf_Shdr *DynamicSec = nullptr;
- for (const Elf_Shdr &Sec :
- unwrapOrError(ObjF->getFileName(), Obj->sections())) {
+ for (const Elf_Shdr &Sec : cantFail(Obj->sections())) {
if (Sec.sh_type != ELF::SHT_DYNAMIC)
continue;
DynamicSec = &Sec;
@@ -1847,6 +1966,9 @@ void ELFDumper<ELFT>::loadDynamicTable(const ELFFile<ELFT> *Obj) {
bool IsPhdrTableValid = false;
if (DynamicPhdr) {
FromPhdr = createDRIFrom(DynamicPhdr, sizeof(Elf_Dyn));
+ FromPhdr.SizePrintName = "PT_DYNAMIC size";
+ FromPhdr.EntSizePrintName = "";
+
IsPhdrTableValid = !FromPhdr.getAsArrayRef<Elf_Dyn>().empty();
}
@@ -1860,6 +1982,11 @@ void ELFDumper<ELFT>::loadDynamicTable(const ELFFile<ELFT> *Obj) {
FromSec =
checkDRI({ObjF->getELFFile()->base() + DynamicSec->sh_offset,
DynamicSec->sh_size, sizeof(Elf_Dyn), ObjF->getFileName()});
+ FromSec.Context = ("section with index " +
+ Twine(DynamicSec - &cantFail(Obj->sections()).front()))
+ .str();
+ FromSec.EntSizePrintName = "";
+
IsSecTableValid = !FromSec.getAsArrayRef<Elf_Dyn>().empty();
}
@@ -1917,19 +2044,33 @@ ELFDumper<ELFT>::ELFDumper(const object::ELFObjectFile<ELFT> *ObjF,
ScopedPrinter &Writer)
: ObjDumper(Writer), ObjF(ObjF), DynRelRegion(ObjF->getFileName()),
DynRelaRegion(ObjF->getFileName()), DynRelrRegion(ObjF->getFileName()),
- DynPLTRelRegion(ObjF->getFileName()), DynSymRegion(ObjF->getFileName()),
- DynamicTable(ObjF->getFileName()) {
+ DynPLTRelRegion(ObjF->getFileName()), DynamicTable(ObjF->getFileName()) {
+ // Dumper reports all non-critical errors as warnings.
+ // It does not print the same warning more than once.
+ WarningHandler = [this](const Twine &Msg) {
+ if (Warnings.insert(Msg.str()).second)
+ reportWarning(createError(Msg), this->ObjF->getFileName());
+ return Error::success();
+ };
+
+ if (opts::Output == opts::GNU)
+ ELFDumperStyle.reset(new GNUStyle<ELFT>(Writer, this));
+ else
+ ELFDumperStyle.reset(new LLVMStyle<ELFT>(Writer, this));
+
const ELFFile<ELFT> *Obj = ObjF->getELFFile();
- for (const Elf_Shdr &Sec :
- unwrapOrError(ObjF->getFileName(), Obj->sections())) {
+ typename ELFT::ShdrRange Sections = cantFail(Obj->sections());
+ for (const Elf_Shdr &Sec : Sections) {
switch (Sec.sh_type) {
case ELF::SHT_SYMTAB:
if (!DotSymtabSec)
DotSymtabSec = &Sec;
break;
case ELF::SHT_DYNSYM:
- if (!DynSymRegion.Size) {
+ if (!DynSymRegion) {
DynSymRegion = createDRIFrom(&Sec);
+ DynSymRegion->Context =
+ ("section with index " + Twine(&Sec - &Sections.front())).str();
// This is only used (if Elf_Shdr present)for naming section in GNU
// style
DynSymtabName =
@@ -1968,11 +2109,6 @@ ELFDumper<ELFT>::ELFDumper(const object::ELFObjectFile<ELFT> *ObjF,
}
loadDynamicTable(Obj);
-
- if (opts::Output == opts::GNU)
- ELFDumperStyle.reset(new GNUStyle<ELFT>(Writer, this));
- else
- ELFDumperStyle.reset(new LLVMStyle<ELFT>(Writer, this));
}
template <typename ELFT>
@@ -1993,6 +2129,7 @@ void ELFDumper<ELFT>::parseDynamicTable(const ELFFile<ELFT> *Obj) {
uint64_t SONameOffset = 0;
const char *StringTableBegin = nullptr;
uint64_t StringTableSize = 0;
+ Optional<DynRegionInfo> DynSymFromTable;
for (const Elf_Dyn &Dyn : dynamic_table()) {
switch (Dyn.d_tag) {
case ELF::DT_HASH:
@@ -2011,36 +2148,36 @@ void ELFDumper<ELFT>::parseDynamicTable(const ELFFile<ELFT> *Obj) {
StringTableSize = Dyn.getVal();
break;
case ELF::DT_SYMTAB: {
- // Often we find the information about the dynamic symbol table
- // location in the SHT_DYNSYM section header. However, the value in
- // DT_SYMTAB has priority, because it is used by dynamic loaders to
- // locate .dynsym at runtime. The location we find in the section header
- // and the location we find here should match. If we can't map the
- // DT_SYMTAB value to an address (e.g. when there are no program headers), we
- // ignore its value.
+ // If we can't map the DT_SYMTAB value to an address (e.g. when there are
+ // no program headers), we ignore its value.
if (const uint8_t *VA = toMappedAddr(Dyn.getTag(), Dyn.getPtr())) {
- // EntSize is non-zero if the dynamic symbol table has been found via a
- // section header.
- if (DynSymRegion.EntSize && VA != DynSymRegion.Addr)
- reportWarning(
- createError(
- "SHT_DYNSYM section header and DT_SYMTAB disagree about "
- "the location of the dynamic symbol table"),
- ObjF->getFileName());
-
- DynSymRegion.Addr = VA;
- DynSymRegion.EntSize = sizeof(Elf_Sym);
+ DynSymFromTable.emplace(ObjF->getFileName());
+ DynSymFromTable->Addr = VA;
+ DynSymFromTable->EntSize = sizeof(Elf_Sym);
+ DynSymFromTable->EntSizePrintName = "";
}
break;
}
+ case ELF::DT_SYMENT: {
+ uint64_t Val = Dyn.getVal();
+ if (Val != sizeof(Elf_Sym))
+ reportWarning(createError("DT_SYMENT value of 0x" +
+ Twine::utohexstr(Val) +
+ " is not the size of a symbol (0x" +
+ Twine::utohexstr(sizeof(Elf_Sym)) + ")"),
+ ObjF->getFileName());
+ break;
+ }
case ELF::DT_RELA:
DynRelaRegion.Addr = toMappedAddr(Dyn.getTag(), Dyn.getPtr());
break;
case ELF::DT_RELASZ:
DynRelaRegion.Size = Dyn.getVal();
+ DynRelaRegion.SizePrintName = "DT_RELASZ value";
break;
case ELF::DT_RELAENT:
DynRelaRegion.EntSize = Dyn.getVal();
+ DynRelaRegion.EntSizePrintName = "DT_RELAENT value";
break;
case ELF::DT_SONAME:
SONameOffset = Dyn.getVal();
@@ -2050,9 +2187,11 @@ void ELFDumper<ELFT>::parseDynamicTable(const ELFFile<ELFT> *Obj) {
break;
case ELF::DT_RELSZ:
DynRelRegion.Size = Dyn.getVal();
+ DynRelRegion.SizePrintName = "DT_RELSZ value";
break;
case ELF::DT_RELENT:
DynRelRegion.EntSize = Dyn.getVal();
+ DynRelRegion.EntSizePrintName = "DT_RELENT value";
break;
case ELF::DT_RELR:
case ELF::DT_ANDROID_RELR:
@@ -2061,10 +2200,16 @@ void ELFDumper<ELFT>::parseDynamicTable(const ELFFile<ELFT> *Obj) {
case ELF::DT_RELRSZ:
case ELF::DT_ANDROID_RELRSZ:
DynRelrRegion.Size = Dyn.getVal();
+ DynRelrRegion.SizePrintName = Dyn.d_tag == ELF::DT_RELRSZ
+ ? "DT_RELRSZ value"
+ : "DT_ANDROID_RELRSZ value";
break;
case ELF::DT_RELRENT:
case ELF::DT_ANDROID_RELRENT:
DynRelrRegion.EntSize = Dyn.getVal();
+ DynRelrRegion.EntSizePrintName = Dyn.d_tag == ELF::DT_RELRENT
+ ? "DT_RELRENT value"
+ : "DT_ANDROID_RELRENT value";
break;
case ELF::DT_PLTREL:
if (Dyn.getVal() == DT_REL)
@@ -2075,18 +2220,78 @@ void ELFDumper<ELFT>::parseDynamicTable(const ELFFile<ELFT> *Obj) {
reportError(createError(Twine("unknown DT_PLTREL value of ") +
Twine((uint64_t)Dyn.getVal())),
ObjF->getFileName());
+ DynPLTRelRegion.EntSizePrintName = "";
break;
case ELF::DT_JMPREL:
DynPLTRelRegion.Addr = toMappedAddr(Dyn.getTag(), Dyn.getPtr());
break;
case ELF::DT_PLTRELSZ:
DynPLTRelRegion.Size = Dyn.getVal();
+ DynPLTRelRegion.SizePrintName = "DT_PLTRELSZ value";
break;
}
}
- if (StringTableBegin)
- DynamicStringTable = StringRef(StringTableBegin, StringTableSize);
+
+ if (StringTableBegin) {
+ const uint64_t FileSize = ObjF->getELFFile()->getBufSize();
+ const uint64_t Offset =
+ (const uint8_t *)StringTableBegin - ObjF->getELFFile()->base();
+ if (StringTableSize > FileSize - Offset)
+ reportUniqueWarning(createError(
+ "the dynamic string table at 0x" + Twine::utohexstr(Offset) +
+ " goes past the end of the file (0x" + Twine::utohexstr(FileSize) +
+ ") with DT_STRSZ = 0x" + Twine::utohexstr(StringTableSize)));
+ else
+ DynamicStringTable = StringRef(StringTableBegin, StringTableSize);
+ }
+
SOName = getDynamicString(SONameOffset);
+
+ if (DynSymRegion) {
+ // Often we find the information about the dynamic symbol table
+ // location in the SHT_DYNSYM section header. However, the value in
+ // DT_SYMTAB has priority, because it is used by dynamic loaders to
+ // locate .dynsym at runtime. The location we find in the section header
+ // and the location we find here should match.
+ if (DynSymFromTable && DynSymFromTable->Addr != DynSymRegion->Addr)
+ reportUniqueWarning(
+ createError("SHT_DYNSYM section header and DT_SYMTAB disagree about "
+ "the location of the dynamic symbol table"));
+
+ // According to the ELF gABI: "The number of symbol table entries should
+ // equal nchain". Check to see if the DT_HASH hash table nchain value
+ // conflicts with the number of symbols in the dynamic symbol table
+ // according to the section header.
+ if (HashTable) {
+ if (DynSymRegion->EntSize == 0)
+ reportUniqueWarning(
+ createError("SHT_DYNSYM section has sh_entsize == 0"));
+ else if (HashTable->nchain != DynSymRegion->Size / DynSymRegion->EntSize)
+ reportUniqueWarning(createError(
+ "hash table nchain (" + Twine(HashTable->nchain) +
+ ") differs from symbol count derived from SHT_DYNSYM section "
+ "header (" +
+ Twine(DynSymRegion->Size / DynSymRegion->EntSize) + ")"));
+ }
+ }
+
+ // Delay the creation of the actual dynamic symbol table until now, so that
+ // checks can always be made against the section header-based properties,
+ // without worrying about tag order.
+ if (DynSymFromTable) {
+ if (!DynSymRegion) {
+ DynSymRegion = DynSymFromTable;
+ } else {
+ DynSymRegion->Addr = DynSymFromTable->Addr;
+ DynSymRegion->EntSize = DynSymFromTable->EntSize;
+ DynSymRegion->EntSizePrintName = DynSymFromTable->EntSizePrintName;
+ }
+ }
+
+ // Derive the dynamic symbol table size from the DT_HASH hash table, if
+ // present.
+ if (HashTable && DynSymRegion)
+ DynSymRegion->Size = HashTable->nchain * DynSymRegion->EntSize;
}
template <typename ELFT>
@@ -2156,8 +2361,8 @@ template <class ELFT> void ELFDumper<ELFT>::printHashSymbols() {
ELFDumperStyle->printHashSymbols(ObjF->getELFFile());
}
-template <class ELFT> void ELFDumper<ELFT>::printHashHistogram() {
- ELFDumperStyle->printHashHistogram(ObjF->getELFFile());
+template <class ELFT> void ELFDumper<ELFT>::printHashHistograms() {
+ ELFDumperStyle->printHashHistograms(ObjF->getELFFile());
}
template <class ELFT> void ELFDumper<ELFT>::printCGProfile() {
@@ -2213,7 +2418,8 @@ static const EnumEntry<unsigned> ElfDynamicDTFlags1[] = {
LLVM_READOBJ_DT_FLAG_ENT(DF_1, NORELOC),
LLVM_READOBJ_DT_FLAG_ENT(DF_1, SYMINTPOSE),
LLVM_READOBJ_DT_FLAG_ENT(DF_1, GLOBAUDIT),
- LLVM_READOBJ_DT_FLAG_ENT(DF_1, SINGLETON)
+ LLVM_READOBJ_DT_FLAG_ENT(DF_1, SINGLETON),
+ LLVM_READOBJ_DT_FLAG_ENT(DF_1, PIE),
};
static const EnumEntry<unsigned> ElfDynamicDTMipsFlags[] = {
@@ -2257,10 +2463,24 @@ void printFlags(T Value, ArrayRef<EnumEntry<TFlag>> Flags, raw_ostream &OS) {
}
template <class ELFT>
-void ELFDumper<ELFT>::printDynamicEntry(raw_ostream &OS, uint64_t Type,
- uint64_t Value) const {
- const char *ConvChar =
- (opts::Output == opts::GNU) ? "0x%" PRIx64 : "0x%" PRIX64;
+std::string ELFDumper<ELFT>::getDynamicEntry(uint64_t Type,
+ uint64_t Value) const {
+ auto FormatHexValue = [](uint64_t V) {
+ std::string Str;
+ raw_string_ostream OS(Str);
+ const char *ConvChar =
+ (opts::Output == opts::GNU) ? "0x%" PRIx64 : "0x%" PRIX64;
+ OS << format(ConvChar, V);
+ return OS.str();
+ };
+
+ auto FormatFlags = [](uint64_t V,
+ llvm::ArrayRef<llvm::EnumEntry<unsigned int>> Array) {
+ std::string Str;
+ raw_string_ostream OS(Str);
+ printFlags(V, Array, OS);
+ return OS.str();
+ };
// Handle custom printing of architecture specific tags
switch (ObjF->getELFFile()->getHeader()->e_machine) {
@@ -2268,8 +2488,7 @@ void ELFDumper<ELFT>::printDynamicEntry(raw_ostream &OS, uint64_t Type,
switch (Type) {
case DT_AARCH64_BTI_PLT:
case DT_AARCH64_PAC_PLT:
- OS << Value;
- return;
+ return std::to_string(Value);
default:
break;
}
@@ -2277,12 +2496,10 @@ void ELFDumper<ELFT>::printDynamicEntry(raw_ostream &OS, uint64_t Type,
case EM_HEXAGON:
switch (Type) {
case DT_HEXAGON_VER:
- OS << Value;
- return;
+ return std::to_string(Value);
case DT_HEXAGON_SYMSZ:
case DT_HEXAGON_PLT:
- OS << format(ConvChar, Value);
- return;
+ return FormatHexValue(Value);
default:
break;
}
@@ -2293,8 +2510,7 @@ void ELFDumper<ELFT>::printDynamicEntry(raw_ostream &OS, uint64_t Type,
case DT_MIPS_LOCAL_GOTNO:
case DT_MIPS_SYMTABNO:
case DT_MIPS_UNREFEXTNO:
- OS << Value;
- return;
+ return std::to_string(Value);
case DT_MIPS_TIME_STAMP:
case DT_MIPS_ICHECKSUM:
case DT_MIPS_IVERSION:
@@ -2335,11 +2551,9 @@ void ELFDumper<ELFT>::printDynamicEntry(raw_ostream &OS, uint64_t Type,
case DT_MIPS_PLTGOT:
case DT_MIPS_RWPLT:
case DT_MIPS_RLD_MAP_REL:
- OS << format(ConvChar, Value);
- return;
+ return FormatHexValue(Value);
case DT_MIPS_FLAGS:
- printFlags(Value, makeArrayRef(ElfDynamicDTMipsFlags), OS);
- return;
+ return FormatFlags(Value, makeArrayRef(ElfDynamicDTMipsFlags));
default:
break;
}
@@ -2350,13 +2564,10 @@ void ELFDumper<ELFT>::printDynamicEntry(raw_ostream &OS, uint64_t Type,
switch (Type) {
case DT_PLTREL:
- if (Value == DT_REL) {
- OS << "REL";
- break;
- } else if (Value == DT_RELA) {
- OS << "RELA";
- break;
- }
+ if (Value == DT_REL)
+ return "REL";
+ if (Value == DT_RELA)
+ return "RELA";
LLVM_FALLTHROUGH;
case DT_PLTGOT:
case DT_HASH:
@@ -2376,14 +2587,12 @@ void ELFDumper<ELFT>::printDynamicEntry(raw_ostream &OS, uint64_t Type,
case DT_VERSYM:
case DT_GNU_HASH:
case DT_NULL:
- OS << format(ConvChar, Value);
- break;
+ return FormatHexValue(Value);
case DT_RELACOUNT:
case DT_RELCOUNT:
case DT_VERDEFNUM:
case DT_VERNEEDNUM:
- OS << Value;
- break;
+ return std::to_string(Value);
case DT_PLTRELSZ:
case DT_RELASZ:
case DT_RELAENT:
@@ -2396,8 +2605,7 @@ void ELFDumper<ELFT>::printDynamicEntry(raw_ostream &OS, uint64_t Type,
case DT_PREINIT_ARRAYSZ:
case DT_ANDROID_RELSZ:
case DT_ANDROID_RELASZ:
- OS << Value << " (bytes)";
- break;
+ return std::to_string(Value) + " (bytes)";
case DT_NEEDED:
case DT_SONAME:
case DT_AUXILIARY:
@@ -2405,37 +2613,62 @@ void ELFDumper<ELFT>::printDynamicEntry(raw_ostream &OS, uint64_t Type,
case DT_FILTER:
case DT_RPATH:
case DT_RUNPATH: {
- const std::map<uint64_t, const char*> TagNames = {
- {DT_NEEDED, "Shared library"},
- {DT_SONAME, "Library soname"},
- {DT_AUXILIARY, "Auxiliary library"},
- {DT_USED, "Not needed object"},
- {DT_FILTER, "Filter library"},
- {DT_RPATH, "Library rpath"},
- {DT_RUNPATH, "Library runpath"},
+ const std::map<uint64_t, const char *> TagNames = {
+ {DT_NEEDED, "Shared library"}, {DT_SONAME, "Library soname"},
+ {DT_AUXILIARY, "Auxiliary library"}, {DT_USED, "Not needed object"},
+ {DT_FILTER, "Filter library"}, {DT_RPATH, "Library rpath"},
+ {DT_RUNPATH, "Library runpath"},
};
- OS << TagNames.at(Type) << ": [" << getDynamicString(Value) << "]";
- break;
+
+ return (Twine(TagNames.at(Type)) + ": [" + getDynamicString(Value) + "]")
+ .str();
}
case DT_FLAGS:
- printFlags(Value, makeArrayRef(ElfDynamicDTFlags), OS);
- break;
+ return FormatFlags(Value, makeArrayRef(ElfDynamicDTFlags));
case DT_FLAGS_1:
- printFlags(Value, makeArrayRef(ElfDynamicDTFlags1), OS);
- break;
+ return FormatFlags(Value, makeArrayRef(ElfDynamicDTFlags1));
default:
- OS << format(ConvChar, Value);
- break;
+ return FormatHexValue(Value);
}
}
template <class ELFT>
-std::string ELFDumper<ELFT>::getDynamicString(uint64_t Value) const {
- if (DynamicStringTable.empty())
- return "<String table is empty or was not found>";
- if (Value < DynamicStringTable.size())
- return DynamicStringTable.data() + Value;
- return Twine("<Invalid offset 0x" + utohexstr(Value) + ">").str();
+StringRef ELFDumper<ELFT>::getDynamicString(uint64_t Value) const {
+ if (DynamicStringTable.empty() && !DynamicStringTable.data()) {
+ reportUniqueWarning(createError("string table was not found"));
+ return "<?>";
+ }
+
+ auto WarnAndReturn = [this](const Twine &Msg, uint64_t Offset) {
+ reportUniqueWarning(createError("string table at offset 0x" +
+ Twine::utohexstr(Offset) + Msg));
+ return "<?>";
+ };
+
+ const uint64_t FileSize = ObjF->getELFFile()->getBufSize();
+ const uint64_t Offset =
+ (const uint8_t *)DynamicStringTable.data() - ObjF->getELFFile()->base();
+ if (DynamicStringTable.size() > FileSize - Offset)
+ return WarnAndReturn(" with size 0x" +
+ Twine::utohexstr(DynamicStringTable.size()) +
+ " goes past the end of the file (0x" +
+ Twine::utohexstr(FileSize) + ")",
+ Offset);
+
+ if (Value >= DynamicStringTable.size())
+ return WarnAndReturn(
+ ": unable to read the string at 0x" + Twine::utohexstr(Offset + Value) +
+ ": it goes past the end of the table (0x" +
+ Twine::utohexstr(Offset + DynamicStringTable.size()) + ")",
+ Offset);
+
+ if (DynamicStringTable.back() != '\0')
+ return WarnAndReturn(": unable to read the string at 0x" +
+ Twine::utohexstr(Offset + Value) +
+ ": the string table is not null-terminated",
+ Offset);
+
+ return DynamicStringTable.data() + Value;
}
template <class ELFT> void ELFDumper<ELFT>::printUnwindInfo() {
@@ -2466,42 +2699,159 @@ template <class ELFT> void ELFDumper<ELFT>::printDynamicTable() {
template <class ELFT> void ELFDumper<ELFT>::printNeededLibraries() {
ListScope D(W, "NeededLibraries");
- std::vector<std::string> Libs;
+ std::vector<StringRef> Libs;
for (const auto &Entry : dynamic_table())
if (Entry.d_tag == ELF::DT_NEEDED)
Libs.push_back(getDynamicString(Entry.d_un.d_val));
- llvm::stable_sort(Libs);
+ llvm::sort(Libs);
- for (const auto &L : Libs)
+ for (StringRef L : Libs)
W.startLine() << L << "\n";
}
+template <class ELFT>
+static Error checkHashTable(const ELFFile<ELFT> *Obj,
+ const typename ELFT::Hash *H,
+ bool *IsHeaderValid = nullptr) {
+ auto MakeError = [&](uint64_t Off, const Twine &Msg = "") {
+ return createError("the hash table at offset 0x" + Twine::utohexstr(Off) +
+ " goes past the end of the file (0x" +
+ Twine::utohexstr(Obj->getBufSize()) + ")" + Msg);
+ };
+
+ // Each SHT_HASH section starts from two 32-bit fields: nbucket and nchain.
+ const unsigned HeaderSize = 2 * sizeof(typename ELFT::Word);
+ const uint64_t SecOffset = (const uint8_t *)H - Obj->base();
+
+ if (IsHeaderValid)
+ *IsHeaderValid = Obj->getBufSize() - SecOffset >= HeaderSize;
+
+ if (Obj->getBufSize() - SecOffset < HeaderSize)
+ return MakeError(SecOffset);
+
+ if (Obj->getBufSize() - SecOffset - HeaderSize <
+ ((uint64_t)H->nbucket + H->nchain) * sizeof(typename ELFT::Word))
+ return MakeError(SecOffset, ", nbucket = " + Twine(H->nbucket) +
+ ", nchain = " + Twine(H->nchain));
+ return Error::success();
+}
+
+template <class ELFT>
+static Error checkGNUHashTable(const ELFFile<ELFT> *Obj,
+ const typename ELFT::GnuHash *GnuHashTable,
+ bool *IsHeaderValid = nullptr) {
+ const uint8_t *TableData = reinterpret_cast<const uint8_t *>(GnuHashTable);
+ assert(TableData >= Obj->base() &&
+ TableData < Obj->base() + Obj->getBufSize() &&
+ "GnuHashTable must always point to a location inside the file");
+
+ uint64_t TableOffset = TableData - Obj->base();
+ if (IsHeaderValid)
+ *IsHeaderValid = TableOffset + /*Header size:*/ 16 < Obj->getBufSize();
+ if (TableOffset + 16 + (uint64_t)GnuHashTable->nbuckets * 4 +
+ (uint64_t)GnuHashTable->maskwords * sizeof(typename ELFT::Off) >=
+ Obj->getBufSize())
+ return createError("unable to dump the SHT_GNU_HASH "
+ "section at 0x" +
+ Twine::utohexstr(TableOffset) +
+ ": it goes past the end of the file");
+ return Error::success();
+}
+
template <typename ELFT> void ELFDumper<ELFT>::printHashTable() {
DictScope D(W, "HashTable");
if (!HashTable)
return;
- W.printNumber("Num Buckets", HashTable->nbucket);
- W.printNumber("Num Chains", HashTable->nchain);
+
+ bool IsHeaderValid;
+ Error Err = checkHashTable(ObjF->getELFFile(), HashTable, &IsHeaderValid);
+ if (IsHeaderValid) {
+ W.printNumber("Num Buckets", HashTable->nbucket);
+ W.printNumber("Num Chains", HashTable->nchain);
+ }
+
+ if (Err) {
+ reportUniqueWarning(std::move(Err));
+ return;
+ }
+
W.printList("Buckets", HashTable->buckets());
W.printList("Chains", HashTable->chains());
}
-template <typename ELFT> void ELFDumper<ELFT>::printGnuHashTable() {
+template <class ELFT>
+static Expected<ArrayRef<typename ELFT::Word>>
+getGnuHashTableChains(Optional<DynRegionInfo> DynSymRegion,
+ const typename ELFT::GnuHash *GnuHashTable) {
+ if (!DynSymRegion)
+ return createError("no dynamic symbol table found");
+
+ ArrayRef<typename ELFT::Sym> DynSymTable =
+ DynSymRegion->getAsArrayRef<typename ELFT::Sym>();
+ size_t NumSyms = DynSymTable.size();
+ if (!NumSyms)
+ return createError("the dynamic symbol table is empty");
+
+ if (GnuHashTable->symndx < NumSyms)
+ return GnuHashTable->values(NumSyms);
+
+ // A normal empty GNU hash table section produced by linker might have
+ // symndx set to the number of dynamic symbols + 1 (for the zero symbol)
+ // and have dummy null values in the Bloom filter and in the buckets
+ // vector (or no values at all). It happens because the value of symndx is not
+ // important for dynamic loaders when the GNU hash table is empty. They just
+ // skip the whole object during symbol lookup. In such cases, the symndx value
+ // is irrelevant and we should not report a warning.
+ ArrayRef<typename ELFT::Word> Buckets = GnuHashTable->buckets();
+ if (!llvm::all_of(Buckets, [](typename ELFT::Word V) { return V == 0; }))
+ return createError("the first hashed symbol index (" +
+ Twine(GnuHashTable->symndx) +
+ ") is larger than the number of dynamic symbols (" +
+ Twine(NumSyms) + ")");
+ // There is no way to represent an array of (dynamic symbols count - symndx)
+ // length.
+ return ArrayRef<typename ELFT::Word>();
+}
+
+template <typename ELFT>
+void ELFDumper<ELFT>::printGnuHashTable(const object::ObjectFile *Obj) {
DictScope D(W, "GnuHashTable");
if (!GnuHashTable)
return;
- W.printNumber("Num Buckets", GnuHashTable->nbuckets);
- W.printNumber("First Hashed Symbol Index", GnuHashTable->symndx);
- W.printNumber("Num Mask Words", GnuHashTable->maskwords);
- W.printNumber("Shift Count", GnuHashTable->shift2);
- W.printHexList("Bloom Filter", GnuHashTable->filter());
- W.printList("Buckets", GnuHashTable->buckets());
- Elf_Sym_Range Syms = dynamic_symbols();
- unsigned NumSyms = std::distance(Syms.begin(), Syms.end());
- if (!NumSyms)
- reportError(createError("No dynamic symbol section"), ObjF->getFileName());
- W.printHexList("Values", GnuHashTable->values(NumSyms));
+
+ bool IsHeaderValid;
+ Error Err =
+ checkGNUHashTable<ELFT>(ObjF->getELFFile(), GnuHashTable, &IsHeaderValid);
+ if (IsHeaderValid) {
+ W.printNumber("Num Buckets", GnuHashTable->nbuckets);
+ W.printNumber("First Hashed Symbol Index", GnuHashTable->symndx);
+ W.printNumber("Num Mask Words", GnuHashTable->maskwords);
+ W.printNumber("Shift Count", GnuHashTable->shift2);
+ }
+
+ if (Err) {
+ reportUniqueWarning(std::move(Err));
+ return;
+ }
+
+ ArrayRef<typename ELFT::Off> BloomFilter = GnuHashTable->filter();
+ W.printHexList("Bloom Filter", BloomFilter);
+
+ ArrayRef<Elf_Word> Buckets = GnuHashTable->buckets();
+ W.printList("Buckets", Buckets);
+
+ Expected<ArrayRef<Elf_Word>> Chains =
+ getGnuHashTableChains<ELFT>(DynSymRegion, GnuHashTable);
+ if (!Chains) {
+ reportUniqueWarning(
+ createError("unable to dump 'Values' for the SHT_GNU_HASH "
+ "section: " +
+ toString(Chains.takeError())));
+ return;
+ }
+
+ W.printHexList("Values", *Chains);
}
template <typename ELFT> void ELFDumper<ELFT>::printLoadName() {
@@ -2512,6 +2862,7 @@ template <class ELFT> void ELFDumper<ELFT>::printArchSpecificInfo() {
const ELFFile<ELFT> *Obj = ObjF->getELFFile();
switch (Obj->getHeader()->e_machine) {
case EM_ARM:
+ case EM_RISCV:
printAttributes();
break;
case EM_MIPS: {
@@ -2521,9 +2872,14 @@ template <class ELFT> void ELFDumper<ELFT>::printArchSpecificInfo() {
MipsGOTParser<ELFT> Parser(Obj, ObjF->getFileName(), dynamic_table(),
dynamic_symbols());
- if (Parser.hasGot())
+ if (Error E = Parser.findGOT(dynamic_table(), dynamic_symbols()))
+ reportError(std::move(E), ObjF->getFileName());
+ else if (!Parser.isGotEmpty())
ELFDumperStyle->printMipsGOT(Parser);
- if (Parser.hasPlt())
+
+ if (Error E = Parser.findPLT(dynamic_table()))
+ reportError(std::move(E), ObjF->getFileName());
+ else if (!Parser.isPltEmpty())
ELFDumperStyle->printMipsPLT(Parser);
break;
}
@@ -2532,38 +2888,45 @@ template <class ELFT> void ELFDumper<ELFT>::printArchSpecificInfo() {
}
}
-template <class ELFT> void ELFDumper<ELFT>::printAttributes() {
- W.startLine() << "Attributes not implemented.\n";
-}
-
namespace {
-template <> void ELFDumper<ELF32LE>::printAttributes() {
- const ELFFile<ELF32LE> *Obj = ObjF->getELFFile();
- if (Obj->getHeader()->e_machine != EM_ARM) {
+template <class ELFT> void ELFDumper<ELFT>::printAttributes() {
+ const ELFFile<ELFT> *Obj = ObjF->getELFFile();
+ if (!Obj->isLE()) {
W.startLine() << "Attributes not implemented.\n";
return;
}
+ const unsigned Machine = Obj->getHeader()->e_machine;
+ assert((Machine == EM_ARM || Machine == EM_RISCV) &&
+ "Attributes not implemented.");
+
DictScope BA(W, "BuildAttributes");
- for (const ELFO::Elf_Shdr &Sec :
- unwrapOrError(ObjF->getFileName(), Obj->sections())) {
- if (Sec.sh_type != ELF::SHT_ARM_ATTRIBUTES)
+ for (const Elf_Shdr &Sec : cantFail(Obj->sections())) {
+ if (Sec.sh_type != ELF::SHT_ARM_ATTRIBUTES &&
+ Sec.sh_type != ELF::SHT_RISCV_ATTRIBUTES)
continue;
ArrayRef<uint8_t> Contents =
unwrapOrError(ObjF->getFileName(), Obj->getSectionContents(&Sec));
- if (Contents[0] != ARMBuildAttrs::Format_Version) {
- errs() << "unrecognised FormatVersion: 0x"
- << Twine::utohexstr(Contents[0]) << '\n';
+ if (Contents[0] != ELFAttrs::Format_Version) {
+ reportWarning(createError(Twine("unrecognised FormatVersion: 0x") +
+ Twine::utohexstr(Contents[0])),
+ ObjF->getFileName());
continue;
}
-
W.printHex("FormatVersion", Contents[0]);
if (Contents.size() == 1)
continue;
- ARMAttributeParser(&W).Parse(Contents, true);
+ // TODO: Delete the redundant FormatVersion check above.
+ if (Machine == EM_ARM) {
+ if (Error E = ARMAttributeParser(&W).parse(Contents, support::little))
+ reportWarning(std::move(E), ObjF->getFileName());
+ } else if (Machine == EM_RISCV) {
+ if (Error E = RISCVAttributeParser(&W).parse(Contents, support::little))
+ reportWarning(std::move(E), ObjF->getFileName());
+ }
}
}
@@ -2578,9 +2941,11 @@ public:
MipsGOTParser(const ELFO *Obj, StringRef FileName, Elf_Dyn_Range DynTable,
Elf_Sym_Range DynSyms);
+ Error findGOT(Elf_Dyn_Range DynTable, Elf_Sym_Range DynSyms);
+ Error findPLT(Elf_Dyn_Range DynTable);
- bool hasGot() const { return !GotEntries.empty(); }
- bool hasPlt() const { return !PltEntries.empty(); }
+ bool isGotEmpty() const { return GotEntries.empty(); }
+ bool isPltEmpty() const { return PltEntries.empty(); }
uint64_t getGp() const;
@@ -2628,7 +2993,11 @@ MipsGOTParser<ELFT>::MipsGOTParser(const ELFO *Obj, StringRef FileName,
Elf_Sym_Range DynSyms)
: IsStatic(DynTable.empty()), Obj(Obj), GotSec(nullptr), LocalNum(0),
GlobalNum(0), PltSec(nullptr), PltRelSec(nullptr), PltSymTable(nullptr),
- FileName(FileName) {
+ FileName(FileName) {}
+
+template <class ELFT>
+Error MipsGOTParser<ELFT>::findGOT(Elf_Dyn_Range DynTable,
+ Elf_Sym_Range DynSyms) {
// See "Global Offset Table" in Chapter 5 in the following document
// for detailed GOT description.
// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
@@ -2637,22 +3006,20 @@ MipsGOTParser<ELFT>::MipsGOTParser(const ELFO *Obj, StringRef FileName,
if (IsStatic) {
GotSec = findSectionByName(*Obj, FileName, ".got");
if (!GotSec)
- return;
+ return Error::success();
ArrayRef<uint8_t> Content =
unwrapOrError(FileName, Obj->getSectionContents(GotSec));
GotEntries = Entries(reinterpret_cast<const Entry *>(Content.data()),
Content.size() / sizeof(Entry));
LocalNum = GotEntries.size();
- return;
+ return Error::success();
}
- // Lookup dynamic table tags which define GOT/PLT layouts.
+ // Lookup dynamic table tags which define the GOT layout.
Optional<uint64_t> DtPltGot;
Optional<uint64_t> DtLocalGotNum;
Optional<uint64_t> DtGotSym;
- Optional<uint64_t> DtMipsPltGot;
- Optional<uint64_t> DtJmpRel;
for (const auto &Entry : DynTable) {
switch (Entry.getTag()) {
case ELF::DT_PLTGOT:
@@ -2664,6 +3031,49 @@ MipsGOTParser<ELFT>::MipsGOTParser(const ELFO *Obj, StringRef FileName,
case ELF::DT_MIPS_GOTSYM:
DtGotSym = Entry.getVal();
break;
+ }
+ }
+
+ if (!DtPltGot && !DtLocalGotNum && !DtGotSym)
+ return Error::success();
+
+ if (!DtPltGot)
+ return createError("cannot find PLTGOT dynamic tag");
+ if (!DtLocalGotNum)
+ return createError("cannot find MIPS_LOCAL_GOTNO dynamic tag");
+ if (!DtGotSym)
+ return createError("cannot find MIPS_GOTSYM dynamic tag");
+
+ size_t DynSymTotal = DynSyms.size();
+ if (*DtGotSym > DynSymTotal)
+ return createError("DT_MIPS_GOTSYM value (" + Twine(*DtGotSym) +
+ ") exceeds the number of dynamic symbols (" +
+ Twine(DynSymTotal) + ")");
+
+ GotSec = findNotEmptySectionByAddress(Obj, FileName, *DtPltGot);
+ if (!GotSec)
+ return createError("there is no non-empty GOT section at 0x" +
+ Twine::utohexstr(*DtPltGot));
+
+ LocalNum = *DtLocalGotNum;
+ GlobalNum = DynSymTotal - *DtGotSym;
+
+ ArrayRef<uint8_t> Content =
+ unwrapOrError(FileName, Obj->getSectionContents(GotSec));
+ GotEntries = Entries(reinterpret_cast<const Entry *>(Content.data()),
+ Content.size() / sizeof(Entry));
+ GotDynSyms = DynSyms.drop_front(*DtGotSym);
+
+ return Error::success();
+}
+
+template <class ELFT>
+Error MipsGOTParser<ELFT>::findPLT(Elf_Dyn_Range DynTable) {
+ // Lookup dynamic table tags which define the PLT layout.
+ Optional<uint64_t> DtMipsPltGot;
+ Optional<uint64_t> DtJmpRel;
+ for (const auto &Entry : DynTable) {
+ switch (Entry.getTag()) {
case ELF::DT_MIPS_PLTGOT:
DtMipsPltGot = Entry.getVal();
break;
@@ -2673,63 +3083,56 @@ MipsGOTParser<ELFT>::MipsGOTParser(const ELFO *Obj, StringRef FileName,
}
}
- // Find dynamic GOT section.
- if (DtPltGot || DtLocalGotNum || DtGotSym) {
- if (!DtPltGot)
- report_fatal_error("Cannot find PLTGOT dynamic table tag.");
- if (!DtLocalGotNum)
- report_fatal_error("Cannot find MIPS_LOCAL_GOTNO dynamic table tag.");
- if (!DtGotSym)
- report_fatal_error("Cannot find MIPS_GOTSYM dynamic table tag.");
-
- size_t DynSymTotal = DynSyms.size();
- if (*DtGotSym > DynSymTotal)
- reportError(
- createError("MIPS_GOTSYM exceeds a number of dynamic symbols"),
- FileName);
-
- GotSec = findNotEmptySectionByAddress(Obj, FileName, *DtPltGot);
- if (!GotSec)
- reportError(createError("There is no not empty GOT section at 0x" +
- Twine::utohexstr(*DtPltGot)),
- FileName);
-
- LocalNum = *DtLocalGotNum;
- GlobalNum = DynSymTotal - *DtGotSym;
-
- ArrayRef<uint8_t> Content =
- unwrapOrError(FileName, Obj->getSectionContents(GotSec));
- GotEntries = Entries(reinterpret_cast<const Entry *>(Content.data()),
- Content.size() / sizeof(Entry));
- GotDynSyms = DynSyms.drop_front(*DtGotSym);
- }
+ if (!DtMipsPltGot && !DtJmpRel)
+ return Error::success();
// Find PLT section.
- if (DtMipsPltGot || DtJmpRel) {
- if (!DtMipsPltGot)
- report_fatal_error("Cannot find MIPS_PLTGOT dynamic table tag.");
- if (!DtJmpRel)
- report_fatal_error("Cannot find JMPREL dynamic table tag.");
-
- PltSec = findNotEmptySectionByAddress(Obj, FileName, * DtMipsPltGot);
- if (!PltSec)
- report_fatal_error("There is no not empty PLTGOT section at 0x " +
- Twine::utohexstr(*DtMipsPltGot));
-
- PltRelSec = findNotEmptySectionByAddress(Obj, FileName, * DtJmpRel);
- if (!PltRelSec)
- report_fatal_error("There is no not empty RELPLT section at 0x" +
- Twine::utohexstr(*DtJmpRel));
+ if (!DtMipsPltGot)
+ return createError("cannot find MIPS_PLTGOT dynamic tag");
+ if (!DtJmpRel)
+ return createError("cannot find JMPREL dynamic tag");
+
+ PltSec = findNotEmptySectionByAddress(Obj, FileName, *DtMipsPltGot);
+ if (!PltSec)
+ return createError("there is no non-empty PLTGOT section at 0x" +
+ Twine::utohexstr(*DtMipsPltGot));
+
+ PltRelSec = findNotEmptySectionByAddress(Obj, FileName, *DtJmpRel);
+ if (!PltRelSec)
+ return createError("there is no non-empty RELPLT section at 0x" +
+ Twine::utohexstr(*DtJmpRel));
+
+ if (Expected<ArrayRef<uint8_t>> PltContentOrErr =
+ Obj->getSectionContents(PltSec))
+ PltEntries =
+ Entries(reinterpret_cast<const Entry *>(PltContentOrErr->data()),
+ PltContentOrErr->size() / sizeof(Entry));
+ else
+ return createError("unable to read PLTGOT section content: " +
+ toString(PltContentOrErr.takeError()));
- ArrayRef<uint8_t> PltContent =
- unwrapOrError(FileName, Obj->getSectionContents(PltSec));
- PltEntries = Entries(reinterpret_cast<const Entry *>(PltContent.data()),
- PltContent.size() / sizeof(Entry));
+ if (Expected<const Elf_Shdr *> PltSymTableOrErr =
+ Obj->getSection(PltRelSec->sh_link)) {
+ PltSymTable = *PltSymTableOrErr;
+ } else {
+ unsigned SecNdx = PltRelSec - &cantFail(Obj->sections()).front();
+ return createError("unable to get a symbol table linked to the RELPLT "
+ "section with index " +
+ Twine(SecNdx) + ": " +
+ toString(PltSymTableOrErr.takeError()));
+ }
- PltSymTable = unwrapOrError(FileName, Obj->getSection(PltRelSec->sh_link));
- PltStrTable =
- unwrapOrError(FileName, Obj->getStringTableForSymtab(*PltSymTable));
+ if (Expected<StringRef> StrTabOrErr =
+ Obj->getStringTableForSymtab(*PltSymTable)) {
+ PltStrTable = *StrTabOrErr;
+ } else {
+ unsigned SecNdx = PltSymTable - &cantFail(Obj->sections()).front();
+ return createError(
+ "unable to get a string table for the symbol table with index " +
+ Twine(SecNdx) + ": " + toString(StrTabOrErr.takeError()));
}
+
+ return Error::success();
}
template <class ELFT> uint64_t MipsGOTParser<ELFT>::getGp() const {
@@ -2977,7 +3380,7 @@ template <class ELFT> void ELFDumper<ELFT>::printMipsOptions() {
template <class ELFT> void ELFDumper<ELFT>::printStackMap() const {
const ELFFile<ELFT> *Obj = ObjF->getELFFile();
const Elf_Shdr *StackMapSection = nullptr;
- for (const auto &Sec : unwrapOrError(ObjF->getFileName(), Obj->sections())) {
+ for (const Elf_Shdr &Sec : cantFail(Obj->sections())) {
StringRef Name =
unwrapOrError(ObjF->getFileName(), Obj->getSectionName(&Sec));
if (Name == ".llvm_stackmaps") {
@@ -3020,7 +3423,7 @@ static std::string getSectionHeadersNumString(const ELFFile<ELFT> *Obj,
if (ElfHeader->e_shnum != 0)
return to_string(ElfHeader->e_shnum);
- ArrayRef<typename ELFT::Shdr> Arr = unwrapOrError(FileName, Obj->sections());
+ ArrayRef<typename ELFT::Shdr> Arr = cantFail(Obj->sections());
if (Arr.empty())
return "0";
return "0 (" + to_string(Arr[0].sh_size) + ")";
@@ -3033,7 +3436,7 @@ static std::string getSectionHeaderTableIndexString(const ELFFile<ELFT> *Obj,
if (ElfHeader->e_shstrndx != SHN_XINDEX)
return to_string(ElfHeader->e_shstrndx);
- ArrayRef<typename ELFT::Shdr> Arr = unwrapOrError(FileName, Obj->sections());
+ ArrayRef<typename ELFT::Shdr> Arr = cantFail(Obj->sections());
if (Arr.empty())
return "65535 (corrupt: out of range)";
return to_string(ElfHeader->e_shstrndx) + " (" + to_string(Arr[0].sh_link) +
@@ -3127,7 +3530,7 @@ std::vector<GroupSection> getGroups(const ELFFile<ELFT> *Obj,
std::vector<GroupSection> Ret;
uint64_t I = 0;
- for (const Elf_Shdr &Sec : unwrapOrError(FileName, Obj->sections())) {
+ for (const Elf_Shdr &Sec : cantFail(Obj->sections())) {
++I;
if (Sec.sh_type != ELF::SHT_GROUP)
continue;
@@ -3202,23 +3605,18 @@ template <class ELFT> void GNUStyle<ELFT>::printGroupSections(const ELFO *Obj) {
}
template <class ELFT>
-void GNUStyle<ELFT>::printRelocation(const ELFO *Obj, const Elf_Shdr *SymTab,
- const Elf_Rela &R, bool IsRela) {
- const Elf_Sym *Sym =
- unwrapOrError(this->FileName, Obj->getRelocationSymbol(&R, SymTab));
- std::string TargetName;
- if (Sym && Sym->getType() == ELF::STT_SECTION) {
- const Elf_Shdr *Sec = unwrapOrError(
- this->FileName,
- Obj->getSection(Sym, SymTab, this->dumper()->getShndxTable()));
- TargetName = unwrapOrError(this->FileName, Obj->getSectionName(Sec));
- } else if (Sym) {
- StringRef StrTable =
- unwrapOrError(this->FileName, Obj->getStringTableForSymtab(*SymTab));
- TargetName = this->dumper()->getFullSymbolName(
- Sym, StrTable, SymTab->sh_type == SHT_DYNSYM /* IsDynamic */);
- }
- printRelocation(Obj, Sym, TargetName, R, IsRela);
+void GNUStyle<ELFT>::printRelocation(const ELFO *Obj, unsigned SecIndex,
+ const Elf_Shdr *SymTab, const Elf_Rela &R,
+ unsigned RelIndex, bool IsRela) {
+ Expected<std::pair<const typename ELFT::Sym *, std::string>> Target =
+ this->dumper()->getRelocationTarget(SymTab, R);
+ if (!Target)
+ this->reportUniqueWarning(createError(
+ "unable to print relocation " + Twine(RelIndex) + " in section " +
+ Twine(SecIndex) + ": " + toString(Target.takeError())));
+ else
+ printRelocation(Obj, /*Sym=*/Target->first, /*Name=*/Target->second, R,
+ IsRela);
}
template <class ELFT>
@@ -3237,10 +3635,10 @@ void GNUStyle<ELFT>::printRelocation(const ELFO *Obj, const Elf_Sym *Sym,
Obj->getRelocationTypeName(R.getType(Obj->isMips64EL()), RelocName);
Fields[2].Str = RelocName.c_str();
- if (Sym && (!SymbolName.empty() || Sym->getValue() != 0))
+ if (Sym)
Fields[3].Str = to_string(format_hex_no_prefix(Sym->getValue(), Width));
- Fields[4].Str = SymbolName;
+ Fields[4].Str = std::string(SymbolName);
for (const Field &F : Fields)
printField(F);
@@ -3283,7 +3681,7 @@ template <class ELFT> void GNUStyle<ELFT>::printRelocHeader(unsigned SType) {
template <class ELFT> void GNUStyle<ELFT>::printRelocations(const ELFO *Obj) {
bool HasRelocSections = false;
- for (const Elf_Shdr &Sec : unwrapOrError(this->FileName, Obj->sections())) {
+ for (const Elf_Shdr &Sec : cantFail(Obj->sections())) {
if (Sec.sh_type != ELF::SHT_REL && Sec.sh_type != ELF::SHT_RELA &&
Sec.sh_type != ELF::SHT_RELR && Sec.sh_type != ELF::SHT_ANDROID_REL &&
Sec.sh_type != ELF::SHT_ANDROID_RELA &&
@@ -3316,6 +3714,9 @@ template <class ELFT> void GNUStyle<ELFT>::printRelocations(const ELFO *Obj) {
printRelocHeader(Sec.sh_type);
const Elf_Shdr *SymTab =
unwrapOrError(this->FileName, Obj->getSection(Sec.sh_link));
+ unsigned SecNdx = &Sec - &cantFail(Obj->sections()).front();
+ unsigned RelNdx = 0;
+
switch (Sec.sh_type) {
case ELF::SHT_REL:
for (const auto &R : unwrapOrError(this->FileName, Obj->rels(&Sec))) {
@@ -3323,12 +3724,12 @@ template <class ELFT> void GNUStyle<ELFT>::printRelocations(const ELFO *Obj) {
Rela.r_offset = R.r_offset;
Rela.r_info = R.r_info;
Rela.r_addend = 0;
- printRelocation(Obj, SymTab, Rela, false);
+ printRelocation(Obj, SecNdx, SymTab, Rela, ++RelNdx, false);
}
break;
case ELF::SHT_RELA:
for (const auto &R : unwrapOrError(this->FileName, Obj->relas(&Sec)))
- printRelocation(Obj, SymTab, R, true);
+ printRelocation(Obj, SecNdx, SymTab, R, ++RelNdx, true);
break;
case ELF::SHT_RELR:
case ELF::SHT_ANDROID_RELR:
@@ -3338,12 +3739,13 @@ template <class ELFT> void GNUStyle<ELFT>::printRelocations(const ELFO *Obj) {
<< "\n";
else
for (const auto &R : RelrRelas)
- printRelocation(Obj, SymTab, R, false);
+ printRelocation(Obj, SecNdx, SymTab, R, ++RelNdx, false);
break;
case ELF::SHT_ANDROID_REL:
case ELF::SHT_ANDROID_RELA:
for (const auto &R : AndroidRelas)
- printRelocation(Obj, SymTab, R, Sec.sh_type == ELF::SHT_ANDROID_RELA);
+ printRelocation(Obj, SecNdx, SymTab, R, ++RelNdx,
+ Sec.sh_type == ELF::SHT_ANDROID_RELA);
break;
}
}
@@ -3402,6 +3804,11 @@ static std::string getSectionTypeString(unsigned Arch, unsigned Type) {
return "MIPS_ABIFLAGS";
}
break;
+ case EM_RISCV:
+ switch (Type) {
+ case SHT_RISCV_ATTRIBUTES:
+ return "RISCV_ATTRIBUTES";
+ }
}
switch (Type) {
case SHT_NULL:
@@ -3500,7 +3907,7 @@ static void printSectionDescription(formatted_raw_ostream &OS,
template <class ELFT>
void GNUStyle<ELFT>::printSectionHeaders(const ELFO *Obj) {
unsigned Bias = ELFT::Is64Bits ? 0 : 8;
- ArrayRef<Elf_Shdr> Sections = unwrapOrError(this->FileName, Obj->sections());
+ ArrayRef<Elf_Shdr> Sections = cantFail(Obj->sections());
OS << "There are " << to_string(Sections.size())
<< " section headers, starting at offset "
<< "0x" << to_hexString(Obj->getHeader()->e_shoff, false) << ":\n\n";
@@ -3514,12 +3921,21 @@ void GNUStyle<ELFT>::printSectionHeaders(const ELFO *Obj) {
printField(F);
OS << "\n";
- const ELFObjectFile<ELFT> *ElfObj = this->dumper()->getElfObject();
+ StringRef SecStrTable;
+ if (Expected<StringRef> SecStrTableOrErr =
+ Obj->getSectionStringTable(Sections, this->dumper()->WarningHandler))
+ SecStrTable = *SecStrTableOrErr;
+ else
+ this->reportUniqueWarning(SecStrTableOrErr.takeError());
+
size_t SectionIndex = 0;
for (const Elf_Shdr &Sec : Sections) {
Fields[0].Str = to_string(SectionIndex);
- Fields[1].Str = unwrapOrError<StringRef>(
- ElfObj->getFileName(), Obj->getSectionName(&Sec, this->WarningHandler));
+ if (SecStrTable.empty())
+ Fields[1].Str = "<no-strings>";
+ else
+ Fields[1].Str = std::string(unwrapOrError<StringRef>(
+ this->FileName, Obj->getSectionName(&Sec, SecStrTable)));
Fields[2].Str =
getSectionTypeString(Obj->getHeader()->e_machine, Sec.sh_type);
Fields[3].Str =
@@ -3555,10 +3971,10 @@ void GNUStyle<ELFT>::printSymtabMessage(const ELFO *Obj, StringRef Name,
size_t Entries,
bool NonVisibilityBitsUsed) {
if (!Name.empty())
- OS << "\nSymbol table '" << Name << "' contains " << Entries
- << " entries:\n";
+ OS << "\nSymbol table '" << Name << "'";
else
- OS << "\n Symbol table for image:\n";
+ OS << "\nSymbol table for image";
+ OS << " contains " << Entries << " entries:\n";
if (ELFT::Is64Bits)
OS << " Num: Value Size Type Bind Vis";
@@ -3616,8 +4032,9 @@ std::string GNUStyle<ELFT>::getSymbolSectionNdx(const ELFO *Obj,
template <class ELFT>
void GNUStyle<ELFT>::printSymbol(const ELFO *Obj, const Elf_Sym *Symbol,
- const Elf_Sym *FirstSym, StringRef StrTable,
- bool IsDynamic, bool NonVisibilityBitsUsed) {
+ const Elf_Sym *FirstSym,
+ Optional<StringRef> StrTable, bool IsDynamic,
+ bool NonVisibilityBitsUsed) {
static int Idx = 0;
static bool Dynamic = true;
@@ -3707,67 +4124,110 @@ void GNUStyle<ELFT>::printSymbols(const ELFO *Obj, bool PrintSymbols,
this->dumper()->printSymbolsHelper(false);
}
-template <class ELFT> void GNUStyle<ELFT>::printHashSymbols(const ELFO *Obj) {
- if (this->dumper()->getDynamicStringTable().empty())
+template <class ELFT>
+void GNUStyle<ELFT>::printHashTableSymbols(const ELFO *Obj,
+ const Elf_Hash &SysVHash) {
+ StringRef StringTable = this->dumper()->getDynamicStringTable();
+ if (StringTable.empty())
return;
- auto StringTable = this->dumper()->getDynamicStringTable();
- auto DynSyms = this->dumper()->dynamic_symbols();
- // Try printing .hash
- if (auto SysVHash = this->dumper()->getHashTable()) {
- OS << "\n Symbol table of .hash for image:\n";
- if (ELFT::Is64Bits)
- OS << " Num Buc: Value Size Type Bind Vis Ndx Name";
- else
- OS << " Num Buc: Value Size Type Bind Vis Ndx Name";
- OS << "\n";
+ if (ELFT::Is64Bits)
+ OS << " Num Buc: Value Size Type Bind Vis Ndx Name";
+ else
+ OS << " Num Buc: Value Size Type Bind Vis Ndx Name";
+ OS << "\n";
- auto Buckets = SysVHash->buckets();
- auto Chains = SysVHash->chains();
- for (uint32_t Buc = 0; Buc < SysVHash->nbucket; Buc++) {
- if (Buckets[Buc] == ELF::STN_UNDEF)
- continue;
- std::vector<bool> Visited(SysVHash->nchain);
- for (uint32_t Ch = Buckets[Buc]; Ch < SysVHash->nchain; Ch = Chains[Ch]) {
- if (Ch == ELF::STN_UNDEF)
- break;
+ Elf_Sym_Range DynSyms = this->dumper()->dynamic_symbols();
+ const Elf_Sym *FirstSym = DynSyms.empty() ? nullptr : &DynSyms[0];
+ if (!FirstSym) {
+ Optional<DynRegionInfo> DynSymRegion = this->dumper()->getDynSymRegion();
+ this->reportUniqueWarning(
+ createError(Twine("unable to print symbols for the .hash table: the "
+ "dynamic symbol table ") +
+ (DynSymRegion ? "is empty" : "was not found")));
+ return;
+ }
- if (Visited[Ch]) {
- reportWarning(
- createError(".hash section is invalid: bucket " + Twine(Ch) +
- ": a cycle was detected in the linked chain"),
- this->FileName);
- break;
- }
+ auto Buckets = SysVHash.buckets();
+ auto Chains = SysVHash.chains();
+ for (uint32_t Buc = 0; Buc < SysVHash.nbucket; Buc++) {
+ if (Buckets[Buc] == ELF::STN_UNDEF)
+ continue;
+ std::vector<bool> Visited(SysVHash.nchain);
+ for (uint32_t Ch = Buckets[Buc]; Ch < SysVHash.nchain; Ch = Chains[Ch]) {
+ if (Ch == ELF::STN_UNDEF)
+ break;
- printHashedSymbol(Obj, &DynSyms[0], Ch, StringTable, Buc);
- Visited[Ch] = true;
+ if (Visited[Ch]) {
+ reportWarning(createError(".hash section is invalid: bucket " +
+ Twine(Ch) +
+ ": a cycle was detected in the linked chain"),
+ this->FileName);
+ break;
}
+
+ printHashedSymbol(Obj, FirstSym, Ch, StringTable, Buc);
+ Visited[Ch] = true;
+ }
+ }
+}
+
+template <class ELFT>
+void GNUStyle<ELFT>::printGnuHashTableSymbols(const ELFO *Obj,
+ const Elf_GnuHash &GnuHash) {
+ StringRef StringTable = this->dumper()->getDynamicStringTable();
+ if (StringTable.empty())
+ return;
+
+ Elf_Sym_Range DynSyms = this->dumper()->dynamic_symbols();
+ const Elf_Sym *FirstSym = DynSyms.empty() ? nullptr : &DynSyms[0];
+ if (!FirstSym) {
+ Optional<DynRegionInfo> DynSymRegion = this->dumper()->getDynSymRegion();
+ this->reportUniqueWarning(createError(
+ Twine("unable to print symbols for the .gnu.hash table: the "
+ "dynamic symbol table ") +
+ (DynSymRegion ? "is empty" : "was not found")));
+ return;
+ }
+
+ ArrayRef<Elf_Word> Buckets = GnuHash.buckets();
+ for (uint32_t Buc = 0; Buc < GnuHash.nbuckets; Buc++) {
+ if (Buckets[Buc] == ELF::STN_UNDEF)
+ continue;
+ uint32_t Index = Buckets[Buc];
+ uint32_t GnuHashable = Index - GnuHash.symndx;
+ // Print whole chain
+ while (true) {
+ printHashedSymbol(Obj, FirstSym, Index++, StringTable, Buc);
+ // Chain ends at symbol with stopper bit
+ if ((GnuHash.values(DynSyms.size())[GnuHashable++] & 1) == 1)
+ break;
}
}
+}
+
+template <class ELFT> void GNUStyle<ELFT>::printHashSymbols(const ELFO *Obj) {
+ if (const Elf_Hash *SysVHash = this->dumper()->getHashTable()) {
+ OS << "\n Symbol table of .hash for image:\n";
+ if (Error E = checkHashTable<ELFT>(Obj, SysVHash))
+ this->reportUniqueWarning(std::move(E));
+ else
+ printHashTableSymbols(Obj, *SysVHash);
+ }
- // Try printing .gnu.hash
- if (auto GnuHash = this->dumper()->getGnuHashTable()) {
+ // Try printing the .gnu.hash table.
+ if (const Elf_GnuHash *GnuHash = this->dumper()->getGnuHashTable()) {
OS << "\n Symbol table of .gnu.hash for image:\n";
if (ELFT::Is64Bits)
OS << " Num Buc: Value Size Type Bind Vis Ndx Name";
else
OS << " Num Buc: Value Size Type Bind Vis Ndx Name";
OS << "\n";
- auto Buckets = GnuHash->buckets();
- for (uint32_t Buc = 0; Buc < GnuHash->nbuckets; Buc++) {
- if (Buckets[Buc] == ELF::STN_UNDEF)
- continue;
- uint32_t Index = Buckets[Buc];
- uint32_t GnuHashable = Index - GnuHash->symndx;
- // Print whole chain
- while (true) {
- printHashedSymbol(Obj, &DynSyms[0], Index++, StringTable, Buc);
- // Chain ends at symbol with stopper bit
- if ((GnuHash->values(DynSyms.size())[GnuHashable++] & 1) == 1)
- break;
- }
- }
+
+ if (Error E = checkGNUHashTable<ELFT>(Obj, GnuHash))
+ this->reportUniqueWarning(std::move(E));
+ else
+ printGnuHashTableSymbols(Obj, *GnuHash);
}
}
@@ -3779,63 +4239,76 @@ static inline std::string printPhdrFlags(unsigned Flag) {
return Str;
}
-// SHF_TLS sections are only in PT_TLS, PT_LOAD or PT_GNU_RELRO
-// PT_TLS must only have SHF_TLS sections
template <class ELFT>
-bool GNUStyle<ELFT>::checkTLSSections(const Elf_Phdr &Phdr,
- const Elf_Shdr &Sec) {
- return (((Sec.sh_flags & ELF::SHF_TLS) &&
- ((Phdr.p_type == ELF::PT_TLS) || (Phdr.p_type == ELF::PT_LOAD) ||
- (Phdr.p_type == ELF::PT_GNU_RELRO))) ||
- (!(Sec.sh_flags & ELF::SHF_TLS) && Phdr.p_type != ELF::PT_TLS));
+static bool checkTLSSections(const typename ELFT::Phdr &Phdr,
+ const typename ELFT::Shdr &Sec) {
+ if (Sec.sh_flags & ELF::SHF_TLS) {
+ // .tbss must only be shown in the PT_TLS segment.
+ if (Sec.sh_type == ELF::SHT_NOBITS)
+ return Phdr.p_type == ELF::PT_TLS;
+
+ // SHF_TLS sections are only shown in PT_TLS, PT_LOAD or PT_GNU_RELRO
+ // segments.
+ return (Phdr.p_type == ELF::PT_TLS) || (Phdr.p_type == ELF::PT_LOAD) ||
+ (Phdr.p_type == ELF::PT_GNU_RELRO);
+ }
+
+ // PT_TLS must only have SHF_TLS sections.
+ return Phdr.p_type != ELF::PT_TLS;
}
-// Non-SHT_NOBITS must have its offset inside the segment
-// Only non-zero section can be at end of segment
template <class ELFT>
-bool GNUStyle<ELFT>::checkoffsets(const Elf_Phdr &Phdr, const Elf_Shdr &Sec) {
+static bool checkOffsets(const typename ELFT::Phdr &Phdr,
+ const typename ELFT::Shdr &Sec) {
+ // SHT_NOBITS sections don't need to have an offset inside the segment.
if (Sec.sh_type == ELF::SHT_NOBITS)
return true;
- bool IsSpecial =
- (Sec.sh_type == ELF::SHT_NOBITS) && ((Sec.sh_flags & ELF::SHF_TLS) != 0);
- // .tbss is special, it only has memory in PT_TLS and has NOBITS properties
- auto SectionSize =
- (IsSpecial && Phdr.p_type != ELF::PT_TLS) ? 0 : Sec.sh_size;
- if (Sec.sh_offset >= Phdr.p_offset)
- return ((Sec.sh_offset + SectionSize <= Phdr.p_filesz + Phdr.p_offset)
- /*only non-zero sized sections at end*/
- && (Sec.sh_offset + 1 <= Phdr.p_offset + Phdr.p_filesz));
- return false;
-}
-
-// SHF_ALLOC must have VMA inside segment
-// Only non-zero section can be at end of segment
+
+ if (Sec.sh_offset < Phdr.p_offset)
+ return false;
+
+ // Only non-empty sections can be at the end of a segment.
+ if (Sec.sh_size == 0)
+ return (Sec.sh_offset + 1 <= Phdr.p_offset + Phdr.p_filesz);
+ return Sec.sh_offset + Sec.sh_size <= Phdr.p_offset + Phdr.p_filesz;
+}
+
+// Check that an allocatable section belongs to a virtual address
+// space of a segment.
template <class ELFT>
-bool GNUStyle<ELFT>::checkVMA(const Elf_Phdr &Phdr, const Elf_Shdr &Sec) {
+static bool checkVMA(const typename ELFT::Phdr &Phdr,
+ const typename ELFT::Shdr &Sec) {
if (!(Sec.sh_flags & ELF::SHF_ALLOC))
return true;
- bool IsSpecial =
+
+ if (Sec.sh_addr < Phdr.p_vaddr)
+ return false;
+
+ bool IsTbss =
(Sec.sh_type == ELF::SHT_NOBITS) && ((Sec.sh_flags & ELF::SHF_TLS) != 0);
- // .tbss is special, it only has memory in PT_TLS and has NOBITS properties
- auto SectionSize =
- (IsSpecial && Phdr.p_type != ELF::PT_TLS) ? 0 : Sec.sh_size;
- if (Sec.sh_addr >= Phdr.p_vaddr)
- return ((Sec.sh_addr + SectionSize <= Phdr.p_vaddr + Phdr.p_memsz) &&
- (Sec.sh_addr + 1 <= Phdr.p_vaddr + Phdr.p_memsz));
- return false;
+ // .tbss is special, it only has memory in PT_TLS and has NOBITS properties.
+ bool IsTbssInNonTLS = IsTbss && Phdr.p_type != ELF::PT_TLS;
+ // Only non-empty sections can be at the end of a segment.
+ if (Sec.sh_size == 0 || IsTbssInNonTLS)
+ return Sec.sh_addr + 1 <= Phdr.p_vaddr + Phdr.p_memsz;
+ return Sec.sh_addr + Sec.sh_size <= Phdr.p_vaddr + Phdr.p_memsz;
}
-// No section with zero size must be at start or end of PT_DYNAMIC
template <class ELFT>
-bool GNUStyle<ELFT>::checkPTDynamic(const Elf_Phdr &Phdr, const Elf_Shdr &Sec) {
- if (Phdr.p_type != ELF::PT_DYNAMIC || Sec.sh_size != 0 || Phdr.p_memsz == 0)
+static bool checkPTDynamic(const typename ELFT::Phdr &Phdr,
+ const typename ELFT::Shdr &Sec) {
+ if (Phdr.p_type != ELF::PT_DYNAMIC || Phdr.p_memsz == 0 || Sec.sh_size != 0)
return true;
- // Is section within the phdr both based on offset and VMA ?
- return ((Sec.sh_type == ELF::SHT_NOBITS) ||
- (Sec.sh_offset > Phdr.p_offset &&
- Sec.sh_offset < Phdr.p_offset + Phdr.p_filesz)) &&
- (!(Sec.sh_flags & ELF::SHF_ALLOC) ||
- (Sec.sh_addr > Phdr.p_vaddr && Sec.sh_addr < Phdr.p_memsz));
+
+ // We get here when we have an empty section. Only non-empty sections can be
+ // at the start or at the end of PT_DYNAMIC.
+ // Is section within the phdr both based on offset and VMA?
+ bool CheckOffset = (Sec.sh_type == ELF::SHT_NOBITS) ||
+ (Sec.sh_offset > Phdr.p_offset &&
+ Sec.sh_offset < Phdr.p_offset + Phdr.p_filesz);
+ bool CheckVA = !(Sec.sh_flags & ELF::SHF_ALLOC) ||
+ (Sec.sh_addr > Phdr.p_vaddr && Sec.sh_addr < Phdr.p_memsz);
+ return CheckOffset && CheckVA;
}
template <class ELFT>
@@ -3872,8 +4345,15 @@ void GNUStyle<ELFT>::printProgramHeaders(const ELFO *Obj) {
unsigned Width = ELFT::Is64Bits ? 18 : 10;
unsigned SizeWidth = ELFT::Is64Bits ? 8 : 7;
- for (const auto &Phdr :
- unwrapOrError(this->FileName, Obj->program_headers())) {
+
+ Expected<ArrayRef<Elf_Phdr>> PhdrsOrErr = Obj->program_headers();
+ if (!PhdrsOrErr) {
+ this->reportUniqueWarning(createError("unable to dump program headers: " +
+ toString(PhdrsOrErr.takeError())));
+ return;
+ }
+
+ for (const Elf_Phdr &Phdr : *PhdrsOrErr) {
Fields[0].Str = getElfPtType(Header->e_machine, Phdr.p_type);
Fields[1].Str = to_string(format_hex(Phdr.p_offset, 8));
Fields[2].Str = to_string(format_hex(Phdr.p_vaddr, Width));
@@ -3885,8 +4365,31 @@ void GNUStyle<ELFT>::printProgramHeaders(const ELFO *Obj) {
for (auto Field : Fields)
printField(Field);
if (Phdr.p_type == ELF::PT_INTERP) {
- OS << "\n [Requesting program interpreter: ";
- OS << reinterpret_cast<const char *>(Obj->base()) + Phdr.p_offset << "]";
+ OS << "\n";
+ auto ReportBadInterp = [&](const Twine &Msg) {
+ reportWarning(
+ createError("unable to read program interpreter name at offset 0x" +
+ Twine::utohexstr(Phdr.p_offset) + ": " + Msg),
+ this->FileName);
+ };
+
+ if (Phdr.p_offset >= Obj->getBufSize()) {
+ ReportBadInterp("it goes past the end of the file (0x" +
+ Twine::utohexstr(Obj->getBufSize()) + ")");
+ continue;
+ }
+
+ const char *Data =
+ reinterpret_cast<const char *>(Obj->base()) + Phdr.p_offset;
+ size_t MaxSize = Obj->getBufSize() - Phdr.p_offset;
+ size_t Len = strnlen(Data, MaxSize);
+ if (Len == MaxSize) {
+ ReportBadInterp("it is not null-terminated");
+ continue;
+ }
+
+ OS << " [Requesting program interpreter: ";
+ OS << StringRef(Data, Len) << "]";
}
OS << "\n";
}
@@ -3897,21 +4400,28 @@ void GNUStyle<ELFT>::printSectionMapping(const ELFO *Obj) {
OS << "\n Section to Segment mapping:\n Segment Sections...\n";
DenseSet<const Elf_Shdr *> BelongsToSegment;
int Phnum = 0;
- for (const Elf_Phdr &Phdr :
- unwrapOrError(this->FileName, Obj->program_headers())) {
+
+ Expected<ArrayRef<Elf_Phdr>> PhdrsOrErr = Obj->program_headers();
+ if (!PhdrsOrErr) {
+ this->reportUniqueWarning(createError(
+ "can't read program headers to build section to segment mapping: " +
+ toString(PhdrsOrErr.takeError())));
+ return;
+ }
+
+ for (const Elf_Phdr &Phdr : *PhdrsOrErr) {
std::string Sections;
OS << format(" %2.2d ", Phnum++);
- for (const Elf_Shdr &Sec : unwrapOrError(this->FileName, Obj->sections())) {
- // Check if each section is in a segment and then print mapping.
+ // Check if each section is in a segment and then print mapping.
+ for (const Elf_Shdr &Sec : cantFail(Obj->sections())) {
+ if (Sec.sh_type == ELF::SHT_NULL)
+ continue;
+
// readelf additionally makes sure it does not print zero sized sections
// at end of segments and for PT_DYNAMIC both start and end of section
// .tbss must only be shown in PT_TLS section.
- bool TbssInNonTLS = (Sec.sh_type == ELF::SHT_NOBITS) &&
- ((Sec.sh_flags & ELF::SHF_TLS) != 0) &&
- Phdr.p_type != ELF::PT_TLS;
- if (!TbssInNonTLS && checkTLSSections(Phdr, Sec) &&
- checkoffsets(Phdr, Sec) && checkVMA(Phdr, Sec) &&
- checkPTDynamic(Phdr, Sec) && (Sec.sh_type != ELF::SHT_NULL)) {
+ if (checkTLSSections<ELFT>(Phdr, Sec) && checkOffsets<ELFT>(Phdr, Sec) &&
+ checkVMA<ELFT>(Phdr, Sec) && checkPTDynamic<ELFT>(Phdr, Sec)) {
Sections +=
unwrapOrError(this->FileName, Obj->getSectionName(&Sec)).str() +
" ";
@@ -3924,7 +4434,7 @@ void GNUStyle<ELFT>::printSectionMapping(const ELFO *Obj) {
// Display sections that do not belong to a segment.
std::string Sections;
- for (const Elf_Shdr &Sec : unwrapOrError(this->FileName, Obj->sections())) {
+ for (const Elf_Shdr &Sec : cantFail(Obj->sections())) {
if (BelongsToSegment.find(&Sec) == BelongsToSegment.end())
Sections +=
unwrapOrError(this->FileName, Obj->getSectionName(&Sec)).str() + ' ';
@@ -3946,21 +4456,35 @@ RelSymbol<ELFT> getSymbolForReloc(const ELFFile<ELFT> *Obj, StringRef FileName,
const ELFDumper<ELFT> *Dumper,
const typename ELFT::Rela &Reloc) {
uint32_t SymIndex = Reloc.getSymbol(Obj->isMips64EL());
- const typename ELFT::Sym *Sym = Dumper->dynamic_symbols().begin() + SymIndex;
- Expected<StringRef> ErrOrName = Sym->getName(Dumper->getDynamicStringTable());
-
- std::string Name;
- if (ErrOrName) {
- Name = maybeDemangle(*ErrOrName);
- } else {
+ auto WarnAndReturn = [&](const typename ELFT::Sym *Sym,
+ const Twine &Reason) -> RelSymbol<ELFT> {
reportWarning(
createError("unable to get name of the dynamic symbol with index " +
- Twine(SymIndex) + ": " + toString(ErrOrName.takeError())),
+ Twine(SymIndex) + ": " + Reason),
FileName);
- Name = "<corrupt>";
- }
+ return {Sym, "<corrupt>"};
+ };
- return {Sym, std::move(Name)};
+ ArrayRef<typename ELFT::Sym> Symbols = Dumper->dynamic_symbols();
+ const typename ELFT::Sym *FirstSym = Symbols.begin();
+ if (!FirstSym)
+ return WarnAndReturn(nullptr, "no dynamic symbol table found");
+
+ // We might have an object without a section header. In this case the size of
+ // Symbols is zero, because there is no way to know the size of the dynamic
+ // table. We should allow this case and not print a warning.
+ if (!Symbols.empty() && SymIndex >= Symbols.size())
+ return WarnAndReturn(
+ nullptr,
+ "index is greater than or equal to the number of dynamic symbols (" +
+ Twine(Symbols.size()) + ")");
+
+ const typename ELFT::Sym *Sym = FirstSym + SymIndex;
+ Expected<StringRef> ErrOrName = Sym->getName(Dumper->getDynamicStringTable());
+ if (!ErrOrName)
+ return WarnAndReturn(Sym, toString(ErrOrName.takeError()));
+
+ return {Sym == FirstSym ? nullptr : Sym, maybeDemangle(*ErrOrName)};
}
} // namespace
@@ -3971,6 +4495,15 @@ void GNUStyle<ELFT>::printDynamicRelocation(const ELFO *Obj, Elf_Rela R,
printRelocation(Obj, S.Sym, S.Name, R, IsRela);
}
+template <class ELFT>
+static size_t getMaxDynamicTagSize(const ELFFile<ELFT> *Obj,
+ typename ELFT::DynRange Tags) {
+ size_t Max = 0;
+ for (const typename ELFT::Dyn &Dyn : Tags)
+ Max = std::max(Max, Obj->getDynamicTagAsString(Dyn.d_tag).size());
+ return Max;
+}
+
template <class ELFT> void GNUStyle<ELFT>::printDynamic(const ELFO *Obj) {
Elf_Dyn_Range Table = this->dumper()->dynamic_table();
if (Table.empty())
@@ -3985,19 +4518,22 @@ template <class ELFT> void GNUStyle<ELFT>::printDynamic(const ELFO *Obj) {
1)
<< " contains " << Table.size() << " entries:\n";
- bool Is64 = ELFT::Is64Bits;
- if (Is64)
- OS << " Tag Type Name/Value\n";
- else
- OS << " Tag Type Name/Value\n";
+ // The type name is surrounded with round brackets, hence add 2.
+ size_t MaxTagSize = getMaxDynamicTagSize(Obj, Table) + 2;
+ // The "Name/Value" column should be indented from the "Type" column by N
+ // spaces, where N = MaxTagSize - length of "Type" (4) + trailing
+ // space (1) = 3.
+ OS << " Tag" + std::string(ELFT::Is64Bits ? 16 : 8, ' ') + "Type"
+ << std::string(MaxTagSize - 3, ' ') << "Name/Value\n";
+
+ std::string ValueFmt = " %-" + std::to_string(MaxTagSize) + "s ";
for (auto Entry : Table) {
uintX_t Tag = Entry.getTag();
- std::string TypeString =
+ std::string Type =
std::string("(") + Obj->getDynamicTagAsString(Tag).c_str() + ")";
- OS << " " << format_hex(Tag, Is64 ? 18 : 10)
- << format(" %-20s ", TypeString.c_str());
- this->dumper()->printDynamicEntry(OS, Tag, Entry.getVal());
- OS << "\n";
+ std::string Value = this->dumper()->getDynamicEntry(Tag, Entry.getVal());
+ OS << " " << format_hex(Tag, ELFT::Is64Bits ? 18 : 10)
+ << format(ValueFmt.c_str(), Type.c_str()) << Value << "\n";
}
}
@@ -4052,19 +4588,20 @@ void GNUStyle<ELFT>::printDynamicRelocations(const ELFO *Obj) {
Obj->base(),
1)
<< " contains " << DynPLTRelRegion.Size << " bytes:\n";
- }
- if (DynPLTRelRegion.EntSize == sizeof(Elf_Rela)) {
- printRelocHeader(ELF::SHT_RELA);
- for (const Elf_Rela &Rela : DynPLTRelRegion.getAsArrayRef<Elf_Rela>())
- printDynamicRelocation(Obj, Rela, true);
- } else {
- printRelocHeader(ELF::SHT_REL);
- for (const Elf_Rel &Rel : DynPLTRelRegion.getAsArrayRef<Elf_Rel>()) {
- Elf_Rela Rela;
- Rela.r_offset = Rel.r_offset;
- Rela.r_info = Rel.r_info;
- Rela.r_addend = 0;
- printDynamicRelocation(Obj, Rela, false);
+
+ if (DynPLTRelRegion.EntSize == sizeof(Elf_Rela)) {
+ printRelocHeader(ELF::SHT_RELA);
+ for (const Elf_Rela &Rela : DynPLTRelRegion.getAsArrayRef<Elf_Rela>())
+ printDynamicRelocation(Obj, Rela, true);
+ } else {
+ printRelocHeader(ELF::SHT_REL);
+ for (const Elf_Rel &Rel : DynPLTRelRegion.getAsArrayRef<Elf_Rel>()) {
+ Elf_Rela Rela;
+ Rela.r_offset = Rel.r_offset;
+ Rela.r_info = Rel.r_info;
+ Rela.r_addend = 0;
+ printDynamicRelocation(Obj, Rela, false);
+ }
}
}
}
@@ -4231,116 +4768,137 @@ void GNUStyle<ELFT>::printVersionDependencySection(const ELFFile<ELFT> *Obj,
OS << '\n';
}
-// Hash histogram shows statistics of how efficient the hash was for the
-// dynamic symbol table. The table shows number of hash buckets for different
-// lengths of chains as absolute number and percentage of the total buckets.
-// Additionally cumulative coverage of symbols for each set of buckets.
template <class ELFT>
-void GNUStyle<ELFT>::printHashHistogram(const ELFFile<ELFT> *Obj) {
- // Print histogram for .hash section
- if (const Elf_Hash *HashTable = this->dumper()->getHashTable()) {
- size_t NBucket = HashTable->nbucket;
- size_t NChain = HashTable->nchain;
- ArrayRef<Elf_Word> Buckets = HashTable->buckets();
- ArrayRef<Elf_Word> Chains = HashTable->chains();
- size_t TotalSyms = 0;
- // If hash table is correct, we have at least chains with 0 length
- size_t MaxChain = 1;
- size_t CumulativeNonZero = 0;
-
- if (NChain == 0 || NBucket == 0)
- return;
+void GNUStyle<ELFT>::printHashHistogram(const Elf_Hash &HashTable) {
+ size_t NBucket = HashTable.nbucket;
+ size_t NChain = HashTable.nchain;
+ ArrayRef<Elf_Word> Buckets = HashTable.buckets();
+ ArrayRef<Elf_Word> Chains = HashTable.chains();
+ size_t TotalSyms = 0;
+ // If hash table is correct, we have at least chains with 0 length
+ size_t MaxChain = 1;
+ size_t CumulativeNonZero = 0;
+
+ if (NChain == 0 || NBucket == 0)
+ return;
- std::vector<size_t> ChainLen(NBucket, 0);
- // Go over all buckets and and note chain lengths of each bucket (total
- // unique chain lengths).
- for (size_t B = 0; B < NBucket; B++) {
- std::vector<bool> Visited(NChain);
- for (size_t C = Buckets[B]; C < NChain; C = Chains[C]) {
- if (C == ELF::STN_UNDEF)
- break;
- if (Visited[C]) {
- reportWarning(
- createError(".hash section is invalid: bucket " + Twine(C) +
- ": a cycle was detected in the linked chain"),
- this->FileName);
- break;
- }
- Visited[C] = true;
- if (MaxChain <= ++ChainLen[B])
- MaxChain++;
+ std::vector<size_t> ChainLen(NBucket, 0);
+ // Go over all buckets and and note chain lengths of each bucket (total
+ // unique chain lengths).
+ for (size_t B = 0; B < NBucket; B++) {
+ std::vector<bool> Visited(NChain);
+ for (size_t C = Buckets[B]; C < NChain; C = Chains[C]) {
+ if (C == ELF::STN_UNDEF)
+ break;
+ if (Visited[C]) {
+ reportWarning(createError(".hash section is invalid: bucket " +
+ Twine(C) +
+ ": a cycle was detected in the linked chain"),
+ this->FileName);
+ break;
}
- TotalSyms += ChainLen[B];
+ Visited[C] = true;
+ if (MaxChain <= ++ChainLen[B])
+ MaxChain++;
}
+ TotalSyms += ChainLen[B];
+ }
- if (!TotalSyms)
- return;
+ if (!TotalSyms)
+ return;
- std::vector<size_t> Count(MaxChain, 0) ;
- // Count how long is the chain for each bucket
- for (size_t B = 0; B < NBucket; B++)
- ++Count[ChainLen[B]];
- // Print Number of buckets with each chain lengths and their cumulative
- // coverage of the symbols
- OS << "Histogram for bucket list length (total of " << NBucket
- << " buckets)\n"
- << " Length Number % of total Coverage\n";
- for (size_t I = 0; I < MaxChain; I++) {
- CumulativeNonZero += Count[I] * I;
- OS << format("%7lu %-10lu (%5.1f%%) %5.1f%%\n", I, Count[I],
- (Count[I] * 100.0) / NBucket,
- (CumulativeNonZero * 100.0) / TotalSyms);
- }
+ std::vector<size_t> Count(MaxChain, 0);
+ // Count how long is the chain for each bucket
+ for (size_t B = 0; B < NBucket; B++)
+ ++Count[ChainLen[B]];
+ // Print Number of buckets with each chain lengths and their cumulative
+ // coverage of the symbols
+ OS << "Histogram for bucket list length (total of " << NBucket
+ << " buckets)\n"
+ << " Length Number % of total Coverage\n";
+ for (size_t I = 0; I < MaxChain; I++) {
+ CumulativeNonZero += Count[I] * I;
+ OS << format("%7lu %-10lu (%5.1f%%) %5.1f%%\n", I, Count[I],
+ (Count[I] * 100.0) / NBucket,
+ (CumulativeNonZero * 100.0) / TotalSyms);
}
+}
- // Print histogram for .gnu.hash section
- if (const Elf_GnuHash *GnuHashTable = this->dumper()->getGnuHashTable()) {
- size_t NBucket = GnuHashTable->nbuckets;
- ArrayRef<Elf_Word> Buckets = GnuHashTable->buckets();
- unsigned NumSyms = this->dumper()->dynamic_symbols().size();
- if (!NumSyms)
- return;
- ArrayRef<Elf_Word> Chains = GnuHashTable->values(NumSyms);
- size_t Symndx = GnuHashTable->symndx;
- size_t TotalSyms = 0;
- size_t MaxChain = 1;
- size_t CumulativeNonZero = 0;
+template <class ELFT>
+void GNUStyle<ELFT>::printGnuHashHistogram(const Elf_GnuHash &GnuHashTable) {
+ Expected<ArrayRef<Elf_Word>> ChainsOrErr = getGnuHashTableChains<ELFT>(
+ this->dumper()->getDynSymRegion(), &GnuHashTable);
+ if (!ChainsOrErr) {
+ this->reportUniqueWarning(
+ createError("unable to print the GNU hash table histogram: " +
+ toString(ChainsOrErr.takeError())));
+ return;
+ }
- if (Chains.empty() || NBucket == 0)
- return;
+ ArrayRef<Elf_Word> Chains = *ChainsOrErr;
+ size_t Symndx = GnuHashTable.symndx;
+ size_t TotalSyms = 0;
+ size_t MaxChain = 1;
+ size_t CumulativeNonZero = 0;
- std::vector<size_t> ChainLen(NBucket, 0);
+ size_t NBucket = GnuHashTable.nbuckets;
+ if (Chains.empty() || NBucket == 0)
+ return;
- for (size_t B = 0; B < NBucket; B++) {
- if (!Buckets[B])
- continue;
- size_t Len = 1;
- for (size_t C = Buckets[B] - Symndx;
- C < Chains.size() && (Chains[C] & 1) == 0; C++)
- if (MaxChain < ++Len)
- MaxChain++;
- ChainLen[B] = Len;
- TotalSyms += Len;
- }
- MaxChain++;
+ ArrayRef<Elf_Word> Buckets = GnuHashTable.buckets();
+ std::vector<size_t> ChainLen(NBucket, 0);
+ for (size_t B = 0; B < NBucket; B++) {
+ if (!Buckets[B])
+ continue;
+ size_t Len = 1;
+ for (size_t C = Buckets[B] - Symndx;
+ C < Chains.size() && (Chains[C] & 1) == 0; C++)
+ if (MaxChain < ++Len)
+ MaxChain++;
+ ChainLen[B] = Len;
+ TotalSyms += Len;
+ }
+ MaxChain++;
- if (!TotalSyms)
- return;
+ if (!TotalSyms)
+ return;
- std::vector<size_t> Count(MaxChain, 0) ;
- for (size_t B = 0; B < NBucket; B++)
- ++Count[ChainLen[B]];
- // Print Number of buckets with each chain lengths and their cumulative
- // coverage of the symbols
- OS << "Histogram for `.gnu.hash' bucket list length (total of " << NBucket
- << " buckets)\n"
- << " Length Number % of total Coverage\n";
- for (size_t I = 0; I <MaxChain; I++) {
- CumulativeNonZero += Count[I] * I;
- OS << format("%7lu %-10lu (%5.1f%%) %5.1f%%\n", I, Count[I],
- (Count[I] * 100.0) / NBucket,
- (CumulativeNonZero * 100.0) / TotalSyms);
- }
+ std::vector<size_t> Count(MaxChain, 0);
+ for (size_t B = 0; B < NBucket; B++)
+ ++Count[ChainLen[B]];
+ // Print Number of buckets with each chain lengths and their cumulative
+ // coverage of the symbols
+ OS << "Histogram for `.gnu.hash' bucket list length (total of " << NBucket
+ << " buckets)\n"
+ << " Length Number % of total Coverage\n";
+ for (size_t I = 0; I < MaxChain; I++) {
+ CumulativeNonZero += Count[I] * I;
+ OS << format("%7lu %-10lu (%5.1f%%) %5.1f%%\n", I, Count[I],
+ (Count[I] * 100.0) / NBucket,
+ (CumulativeNonZero * 100.0) / TotalSyms);
+ }
+}
+
+// Hash histogram shows statistics of how efficient the hash was for the
+// dynamic symbol table. The table shows the number of hash buckets for
+// different lengths of chains as an absolute number and percentage of the total
+// buckets, and the cumulative coverage of symbols for each set of buckets.
+template <class ELFT>
+void GNUStyle<ELFT>::printHashHistograms(const ELFFile<ELFT> *Obj) {
+ // Print histogram for the .hash section.
+ if (const Elf_Hash *HashTable = this->dumper()->getHashTable()) {
+ if (Error E = checkHashTable<ELFT>(Obj, HashTable))
+ this->reportUniqueWarning(std::move(E));
+ else
+ printHashHistogram(*HashTable);
+ }
+
+ // Print histogram for the .gnu.hash section.
+ if (const Elf_GnuHash *GnuHashTable = this->dumper()->getGnuHashTable()) {
+ if (Error E = checkGNUHashTable<ELFT>(Obj, GnuHashTable))
+ this->reportUniqueWarning(std::move(E));
+ else
+ printGnuHashHistogram(*GnuHashTable);
}
}
@@ -4713,7 +5271,7 @@ template <typename ELFT> static GNUAbiTag getGNUAbiTag(ArrayRef<uint8_t> Desc) {
std::string str;
raw_string_ostream ABI(str);
ABI << Major << "." << Minor << "." << Patch;
- return {OSName, ABI.str(), /*IsValid=*/true};
+ return {std::string(OSName), ABI.str(), /*IsValid=*/true};
}
static std::string getGNUBuildId(ArrayRef<uint8_t> Desc) {
@@ -4883,11 +5441,18 @@ static void printCoreNote(raw_ostream &OS, const CoreNote &Note) {
template <class ELFT>
void GNUStyle<ELFT>::printNotes(const ELFFile<ELFT> *Obj) {
- auto PrintHeader = [&](const typename ELFT::Off Offset,
+ auto PrintHeader = [&](Optional<StringRef> SecName,
+ const typename ELFT::Off Offset,
const typename ELFT::Addr Size) {
- OS << "Displaying notes found at file offset " << format_hex(Offset, 10)
- << " with length " << format_hex(Size, 10) << ":\n"
- << " Owner Data size \tDescription\n";
+ OS << "Displaying notes found ";
+
+ if (SecName)
+ OS << "in: " << *SecName << "\n";
+ else
+ OS << "at file offset " << format_hex(Offset, 10) << " with length "
+ << format_hex(Size, 10) << ":\n";
+
+ OS << " Owner Data size \tDescription\n";
};
auto ProcessNote = [&](const Elf_Note &Note) {
@@ -4947,12 +5512,13 @@ void GNUStyle<ELFT>::printNotes(const ELFFile<ELFT> *Obj) {
}
};
- ArrayRef<Elf_Shdr> Sections = unwrapOrError(this->FileName, Obj->sections());
+ ArrayRef<Elf_Shdr> Sections = cantFail(Obj->sections());
if (Obj->getHeader()->e_type != ELF::ET_CORE && !Sections.empty()) {
for (const auto &S : Sections) {
if (S.sh_type != SHT_NOTE)
continue;
- PrintHeader(S.sh_offset, S.sh_size);
+ PrintHeader(expectedToOptional(Obj->getSectionName(&S)), S.sh_offset,
+ S.sh_size);
Error Err = Error::success();
for (auto Note : Obj->notes(S, Err))
ProcessNote(Note);
@@ -4960,11 +5526,18 @@ void GNUStyle<ELFT>::printNotes(const ELFFile<ELFT> *Obj) {
reportError(std::move(Err), this->FileName);
}
} else {
- for (const auto &P :
- unwrapOrError(this->FileName, Obj->program_headers())) {
+ Expected<ArrayRef<Elf_Phdr>> PhdrsOrErr = Obj->program_headers();
+ if (!PhdrsOrErr) {
+ this->reportUniqueWarning(createError(
+ "unable to read program headers to locate the PT_NOTE segment: " +
+ toString(PhdrsOrErr.takeError())));
+ return;
+ }
+
+ for (const Elf_Phdr &P : *PhdrsOrErr) {
if (P.p_type != PT_NOTE)
continue;
- PrintHeader(P.p_offset, P.p_filesz);
+ PrintHeader(/*SecName=*/None, P.p_offset, P.p_filesz);
Error Err = Error::success();
for (auto Note : Obj->notes(P, Err))
ProcessNote(Note);
@@ -4980,8 +5553,87 @@ void GNUStyle<ELFT>::printELFLinkerOptions(const ELFFile<ELFT> *Obj) {
}
template <class ELFT>
+void DumpStyle<ELFT>::printDependentLibsHelper(
+ const ELFFile<ELFT> *Obj,
+ function_ref<void(const Elf_Shdr &)> OnSectionStart,
+ function_ref<void(StringRef, uint64_t)> OnLibEntry) {
+ auto Warn = [this](unsigned SecNdx, StringRef Msg) {
+ this->reportUniqueWarning(
+ createError("SHT_LLVM_DEPENDENT_LIBRARIES section at index " +
+ Twine(SecNdx) + " is broken: " + Msg));
+ };
+
+ unsigned I = -1;
+ for (const Elf_Shdr &Shdr : cantFail(Obj->sections())) {
+ ++I;
+ if (Shdr.sh_type != ELF::SHT_LLVM_DEPENDENT_LIBRARIES)
+ continue;
+
+ OnSectionStart(Shdr);
+
+ Expected<ArrayRef<uint8_t>> ContentsOrErr = Obj->getSectionContents(&Shdr);
+ if (!ContentsOrErr) {
+ Warn(I, toString(ContentsOrErr.takeError()));
+ continue;
+ }
+
+ ArrayRef<uint8_t> Contents = *ContentsOrErr;
+ if (!Contents.empty() && Contents.back() != 0) {
+ Warn(I, "the content is not null-terminated");
+ continue;
+ }
+
+ for (const uint8_t *I = Contents.begin(), *E = Contents.end(); I < E;) {
+ StringRef Lib((const char *)I);
+ OnLibEntry(Lib, I - Contents.begin());
+ I += Lib.size() + 1;
+ }
+ }
+}
+
+template <class ELFT>
void GNUStyle<ELFT>::printDependentLibs(const ELFFile<ELFT> *Obj) {
- OS << "printDependentLibs not implemented!\n";
+ bool SectionStarted = false;
+ struct NameOffset {
+ StringRef Name;
+ uint64_t Offset;
+ };
+ std::vector<NameOffset> SecEntries;
+ NameOffset Current;
+ auto PrintSection = [&]() {
+ OS << "Dependent libraries section " << Current.Name << " at offset "
+ << format_hex(Current.Offset, 1) << " contains " << SecEntries.size()
+ << " entries:\n";
+ for (NameOffset Entry : SecEntries)
+ OS << " [" << format("%6tx", Entry.Offset) << "] " << Entry.Name
+ << "\n";
+ OS << "\n";
+ SecEntries.clear();
+ };
+
+ auto OnSectionStart = [&](const Elf_Shdr &Shdr) {
+ if (SectionStarted)
+ PrintSection();
+ SectionStarted = true;
+ Current.Offset = Shdr.sh_offset;
+ Expected<StringRef> Name = Obj->getSectionName(&Shdr);
+ if (!Name) {
+ Current.Name = "<?>";
+ this->reportUniqueWarning(
+ createError("cannot get section name of "
+ "SHT_LLVM_DEPENDENT_LIBRARIES section: " +
+ toString(Name.takeError())));
+ } else {
+ Current.Name = *Name;
+ }
+ };
+ auto OnLibEntry = [&](StringRef Lib, uint64_t Offset) {
+ SecEntries.push_back(NameOffset{Lib, Offset});
+ };
+
+ this->printDependentLibsHelper(Obj, OnSectionStart, OnLibEntry);
+ if (SectionStarted)
+ PrintSection();
}
// Used for printing section names in places where possible errors can be
@@ -5005,9 +5657,12 @@ static std::string getSymbolName(const ELFSymbolRef &Sym) {
}
template <class ELFT>
-void DumpStyle<ELFT>::printFunctionStackSize(
- const ELFObjectFile<ELFT> *Obj, uint64_t SymValue, SectionRef FunctionSec,
- const StringRef SectionName, DataExtractor Data, uint64_t *Offset) {
+void DumpStyle<ELFT>::printFunctionStackSize(const ELFObjectFile<ELFT> *Obj,
+ uint64_t SymValue,
+ Optional<SectionRef> FunctionSec,
+ const StringRef SectionName,
+ DataExtractor Data,
+ uint64_t *Offset) {
// This function ignores potentially erroneous input, unless it is directly
// related to stack size reporting.
SymbolRef FuncSym;
@@ -5017,9 +5672,15 @@ void DumpStyle<ELFT>::printFunctionStackSize(
consumeError(SymAddrOrErr.takeError());
continue;
}
+ if (Expected<uint32_t> SymFlags = Symbol.getFlags()) {
+ if (*SymFlags & SymbolRef::SF_Undefined)
+ continue;
+ } else
+ consumeError(SymFlags.takeError());
if (Symbol.getELFType() == ELF::STT_FUNC && *SymAddrOrErr == SymValue) {
- // Check if the symbol is in the right section.
- if (FunctionSec.containsSymbol(Symbol)) {
+ // Check if the symbol is in the right section. FunctionSec == None means
+ // "any section".
+ if (!FunctionSec || FunctionSec->containsSymbol(Symbol)) {
FuncSym = Symbol;
break;
}
@@ -5130,11 +5791,6 @@ void DumpStyle<ELFT>::printNonRelocatableStackSizes(
ArrayRef<uint8_t> Contents =
unwrapOrError(this->FileName, EF->getSectionContents(ElfSec));
DataExtractor Data(Contents, Obj->isLittleEndian(), sizeof(Elf_Addr));
- // A .stack_sizes section header's sh_link field is supposed to point
- // to the section that contains the functions whose stack sizes are
- // described in it.
- const Elf_Shdr *FunctionELFSec =
- unwrapOrError(this->FileName, EF->getSection(ElfSec->sh_link));
uint64_t Offset = 0;
while (Offset < Contents.size()) {
// The function address is followed by a ULEB representing the stack
@@ -5148,8 +5804,8 @@ void DumpStyle<ELFT>::printNonRelocatableStackSizes(
FileStr);
}
uint64_t SymValue = Data.getAddress(&Offset);
- printFunctionStackSize(Obj, SymValue, Obj->toSectionRef(FunctionELFSec),
- SectionName, Data, &Offset);
+ printFunctionStackSize(Obj, SymValue, /*FunctionSec=*/None, SectionName,
+ Data, &Offset);
}
}
}
@@ -5532,7 +6188,7 @@ template <class ELFT> void LLVMStyle<ELFT>::printRelocations(const ELFO *Obj) {
ListScope D(W, "Relocations");
int SectionNumber = -1;
- for (const Elf_Shdr &Sec : unwrapOrError(this->FileName, Obj->sections())) {
+ for (const Elf_Shdr &Sec : cantFail(Obj->sections())) {
++SectionNumber;
if (Sec.sh_type != ELF::SHT_REL && Sec.sh_type != ELF::SHT_RELA &&
@@ -5557,6 +6213,8 @@ template <class ELFT>
void LLVMStyle<ELFT>::printRelocations(const Elf_Shdr *Sec, const ELFO *Obj) {
const Elf_Shdr *SymTab =
unwrapOrError(this->FileName, Obj->getSection(Sec->sh_link));
+ unsigned SecNdx = Sec - &cantFail(Obj->sections()).front();
+ unsigned RelNdx = 0;
switch (Sec->sh_type) {
case ELF::SHT_REL:
@@ -5565,12 +6223,12 @@ void LLVMStyle<ELFT>::printRelocations(const Elf_Shdr *Sec, const ELFO *Obj) {
Rela.r_offset = R.r_offset;
Rela.r_info = R.r_info;
Rela.r_addend = 0;
- printRelocation(Obj, Rela, SymTab);
+ printRelocation(Obj, SecNdx, Rela, ++RelNdx, SymTab);
}
break;
case ELF::SHT_RELA:
for (const Elf_Rela &R : unwrapOrError(this->FileName, Obj->relas(Sec)))
- printRelocation(Obj, R, SymTab);
+ printRelocation(Obj, SecNdx, R, ++RelNdx, SymTab);
break;
case ELF::SHT_RELR:
case ELF::SHT_ANDROID_RELR: {
@@ -5582,7 +6240,7 @@ void LLVMStyle<ELFT>::printRelocations(const Elf_Shdr *Sec, const ELFO *Obj) {
std::vector<Elf_Rela> RelrRelas =
unwrapOrError(this->FileName, Obj->decode_relrs(Relrs));
for (const Elf_Rela &R : RelrRelas)
- printRelocation(Obj, R, SymTab);
+ printRelocation(Obj, SecNdx, R, ++RelNdx, SymTab);
}
break;
}
@@ -5590,30 +6248,27 @@ void LLVMStyle<ELFT>::printRelocations(const Elf_Shdr *Sec, const ELFO *Obj) {
case ELF::SHT_ANDROID_RELA:
for (const Elf_Rela &R :
unwrapOrError(this->FileName, Obj->android_relas(Sec)))
- printRelocation(Obj, R, SymTab);
+ printRelocation(Obj, SecNdx, R, ++RelNdx, SymTab);
break;
}
}
template <class ELFT>
-void LLVMStyle<ELFT>::printRelocation(const ELFO *Obj, Elf_Rela Rel,
+void LLVMStyle<ELFT>::printRelocation(const ELFO *Obj, unsigned SecIndex,
+ Elf_Rela Rel, unsigned RelIndex,
const Elf_Shdr *SymTab) {
+ Expected<std::pair<const typename ELFT::Sym *, std::string>> Target =
+ this->dumper()->getRelocationTarget(SymTab, Rel);
+ if (!Target) {
+ this->reportUniqueWarning(createError(
+ "unable to print relocation " + Twine(RelIndex) + " in section " +
+ Twine(SecIndex) + ": " + toString(Target.takeError())));
+ return;
+ }
+
+ std::string TargetName = Target->second;
SmallString<32> RelocName;
Obj->getRelocationTypeName(Rel.getType(Obj->isMips64EL()), RelocName);
- std::string TargetName;
- const Elf_Sym *Sym =
- unwrapOrError(this->FileName, Obj->getRelocationSymbol(&Rel, SymTab));
- if (Sym && Sym->getType() == ELF::STT_SECTION) {
- const Elf_Shdr *Sec = unwrapOrError(
- this->FileName,
- Obj->getSection(Sym, SymTab, this->dumper()->getShndxTable()));
- TargetName = unwrapOrError(this->FileName, Obj->getSectionName(Sec));
- } else if (Sym) {
- StringRef StrTable =
- unwrapOrError(this->FileName, Obj->getStringTableForSymtab(*SymTab));
- TargetName = this->dumper()->getFullSymbolName(
- Sym, StrTable, SymTab->sh_type == SHT_DYNSYM /* IsDynamic */);
- }
if (opts::ExpandRelocs) {
DictScope Group(W, "Relocation");
@@ -5635,13 +6290,16 @@ void LLVMStyle<ELFT>::printSectionHeaders(const ELFO *Obj) {
ListScope SectionsD(W, "Sections");
int SectionIndex = -1;
- ArrayRef<Elf_Shdr> Sections = unwrapOrError(this->FileName, Obj->sections());
- const ELFObjectFile<ELFT> *ElfObj = this->dumper()->getElfObject();
std::vector<EnumEntry<unsigned>> FlagsList =
getSectionFlagsForTarget(Obj->getHeader()->e_machine);
- for (const Elf_Shdr &Sec : Sections) {
- StringRef Name = unwrapOrError(
- ElfObj->getFileName(), Obj->getSectionName(&Sec, this->WarningHandler));
+ for (const Elf_Shdr &Sec : cantFail(Obj->sections())) {
+ StringRef Name = "<?>";
+ if (Expected<StringRef> SecNameOrErr =
+ Obj->getSectionName(&Sec, this->dumper()->WarningHandler))
+ Name = *SecNameOrErr;
+ else
+ this->reportUniqueWarning(SecNameOrErr.takeError());
+
DictScope SectionD(W, "Section");
W.printNumber("Index", ++SectionIndex);
W.printNumber("Name", Name, Sec.sh_name);
@@ -5709,7 +6367,12 @@ void LLVMStyle<ELFT>::printSymbolSection(const Elf_Sym *Symbol,
Expected<StringRef> SectionName =
this->dumper()->getSymbolSectionName(Symbol, *SectionIndex);
if (!SectionName) {
- this->reportUniqueWarning(SectionName.takeError());
+ // Don't report an invalid section name if the section headers are missing.
+ // In such situations, all sections will be "invalid".
+ if (!this->dumper()->getElfObject()->sections().empty())
+ this->reportUniqueWarning(SectionName.takeError());
+ else
+ consumeError(SectionName.takeError());
W.printHex("Section", "<?>", *SectionIndex);
} else {
W.printHex("Section", *SectionName, *SectionIndex);
@@ -5718,8 +6381,8 @@ void LLVMStyle<ELFT>::printSymbolSection(const Elf_Sym *Symbol,
template <class ELFT>
void LLVMStyle<ELFT>::printSymbol(const ELFO *Obj, const Elf_Sym *Symbol,
- const Elf_Sym *First, StringRef StrTable,
- bool IsDynamic,
+ const Elf_Sym *First,
+ Optional<StringRef> StrTable, bool IsDynamic,
bool /*NonVisibilityBitsUsed*/) {
std::string FullSymbolName =
this->dumper()->getFullSymbolName(Symbol, StrTable, IsDynamic);
@@ -5785,20 +6448,24 @@ template <class ELFT> void LLVMStyle<ELFT>::printDynamic(const ELFFile<ELFT> *Ob
if (Table.empty())
return;
- raw_ostream &OS = W.getOStream();
W.startLine() << "DynamicSection [ (" << Table.size() << " entries)\n";
- bool Is64 = ELFT::Is64Bits;
- if (Is64)
- W.startLine() << " Tag Type Name/Value\n";
- else
- W.startLine() << " Tag Type Name/Value\n";
+ size_t MaxTagSize = getMaxDynamicTagSize(Obj, Table);
+ // The "Name/Value" column should be indented from the "Type" column by N
+ // spaces, where N = MaxTagSize - length of "Type" (4) + trailing
+ // space (1) = -3.
+ W.startLine() << " Tag" << std::string(ELFT::Is64Bits ? 16 : 8, ' ')
+ << "Type" << std::string(MaxTagSize - 3, ' ') << "Name/Value\n";
+
+ std::string ValueFmt = "%-" + std::to_string(MaxTagSize) + "s ";
for (auto Entry : Table) {
uintX_t Tag = Entry.getTag();
- W.startLine() << " " << format_hex(Tag, Is64 ? 18 : 10, true) << " "
- << format("%-21s", Obj->getDynamicTagAsString(Tag).c_str());
- this->dumper()->printDynamicEntry(OS, Tag, Entry.getVal());
- OS << "\n";
+ std::string Value = this->dumper()->getDynamicEntry(Tag, Entry.getVal());
+ W.startLine() << " " << format_hex(Tag, ELFT::Is64Bits ? 18 : 10, true)
+ << " "
+ << format(ValueFmt.c_str(),
+ Obj->getDynamicTagAsString(Tag).c_str())
+ << Value << "\n";
}
W.startLine() << "]\n";
}
@@ -5809,14 +6476,14 @@ void LLVMStyle<ELFT>::printDynamicRelocations(const ELFO *Obj) {
const DynRegionInfo &DynRelaRegion = this->dumper()->getDynRelaRegion();
const DynRegionInfo &DynRelrRegion = this->dumper()->getDynRelrRegion();
const DynRegionInfo &DynPLTRelRegion = this->dumper()->getDynPLTRelRegion();
- if (DynRelRegion.Size && DynRelaRegion.Size)
- report_fatal_error("There are both REL and RELA dynamic relocations");
+
W.startLine() << "Dynamic Relocations {\n";
W.indent();
- if (DynRelaRegion.Size > 0)
+ if (DynRelaRegion.Size > 0) {
for (const Elf_Rela &Rela : this->dumper()->dyn_relas())
printDynamicRelocation(Obj, Rela);
- else
+ }
+ if (DynRelRegion.Size > 0) {
for (const Elf_Rel &Rel : this->dumper()->dyn_rels()) {
Elf_Rela Rela;
Rela.r_offset = Rel.r_offset;
@@ -5824,6 +6491,8 @@ void LLVMStyle<ELFT>::printDynamicRelocations(const ELFO *Obj) {
Rela.r_addend = 0;
printDynamicRelocation(Obj, Rela);
}
+ }
+
if (DynRelrRegion.Size > 0) {
Elf_Relr_Range Relrs = this->dumper()->dyn_relrs();
std::vector<Elf_Rela> RelrRelas =
@@ -5881,8 +6550,14 @@ template <class ELFT>
void LLVMStyle<ELFT>::printProgramHeaders(const ELFO *Obj) {
ListScope L(W, "ProgramHeaders");
- for (const Elf_Phdr &Phdr :
- unwrapOrError(this->FileName, Obj->program_headers())) {
+ Expected<ArrayRef<Elf_Phdr>> PhdrsOrErr = Obj->program_headers();
+ if (!PhdrsOrErr) {
+ this->reportUniqueWarning(createError("unable to dump program headers: " +
+ toString(PhdrsOrErr.takeError())));
+ return;
+ }
+
+ for (const Elf_Phdr &Phdr : *PhdrsOrErr) {
DictScope P(W, "ProgramHeader");
W.printHex("Type",
getElfSegmentType(Obj->getHeader()->e_machine, Phdr.p_type),
@@ -5982,7 +6657,7 @@ void LLVMStyle<ELFT>::printVersionDependencySection(const ELFFile<ELFT> *Obj,
}
template <class ELFT>
-void LLVMStyle<ELFT>::printHashHistogram(const ELFFile<ELFT> *Obj) {
+void LLVMStyle<ELFT>::printHashHistograms(const ELFFile<ELFT> *Obj) {
W.startLine() << "Hash Histogram not implemented!\n";
}
@@ -5991,21 +6666,23 @@ void LLVMStyle<ELFT>::printCGProfile(const ELFFile<ELFT> *Obj) {
ListScope L(W, "CGProfile");
if (!this->dumper()->getDotCGProfileSec())
return;
- auto CGProfile = unwrapOrError(
- this->FileName, Obj->template getSectionContentsAsArray<Elf_CGProfile>(
- this->dumper()->getDotCGProfileSec()));
- for (const Elf_CGProfile &CGPE : CGProfile) {
+
+ Expected<ArrayRef<Elf_CGProfile>> CGProfileOrErr =
+ Obj->template getSectionContentsAsArray<Elf_CGProfile>(
+ this->dumper()->getDotCGProfileSec());
+ if (!CGProfileOrErr) {
+ this->reportUniqueWarning(
+ createError("unable to dump the SHT_LLVM_CALL_GRAPH_PROFILE section: " +
+ toString(CGProfileOrErr.takeError())));
+ return;
+ }
+
+ for (const Elf_CGProfile &CGPE : *CGProfileOrErr) {
DictScope D(W, "CGProfileEntry");
- W.printNumber(
- "From",
- unwrapOrError(this->FileName,
- this->dumper()->getStaticSymbolName(CGPE.cgp_from)),
- CGPE.cgp_from);
- W.printNumber(
- "To",
- unwrapOrError(this->FileName,
- this->dumper()->getStaticSymbolName(CGPE.cgp_to)),
- CGPE.cgp_to);
+ W.printNumber("From", this->dumper()->getStaticSymbolName(CGPE.cgp_from),
+ CGPE.cgp_from);
+ W.printNumber("To", this->dumper()->getStaticSymbolName(CGPE.cgp_to),
+ CGPE.cgp_to);
W.printNumber("Weight", CGPE.cgp_weight);
}
}
@@ -6096,8 +6773,10 @@ template <class ELFT>
void LLVMStyle<ELFT>::printNotes(const ELFFile<ELFT> *Obj) {
ListScope L(W, "Notes");
- auto PrintHeader = [&](const typename ELFT::Off Offset,
+ auto PrintHeader = [&](Optional<StringRef> SecName,
+ const typename ELFT::Off Offset,
const typename ELFT::Addr Size) {
+ W.printString("Name", SecName ? *SecName : "<?>");
W.printHex("Offset", Offset);
W.printHex("Size", Size);
};
@@ -6158,13 +6837,14 @@ void LLVMStyle<ELFT>::printNotes(const ELFFile<ELFT> *Obj) {
}
};
- ArrayRef<Elf_Shdr> Sections = unwrapOrError(this->FileName, Obj->sections());
+ ArrayRef<Elf_Shdr> Sections = cantFail(Obj->sections());
if (Obj->getHeader()->e_type != ELF::ET_CORE && !Sections.empty()) {
for (const auto &S : Sections) {
if (S.sh_type != SHT_NOTE)
continue;
DictScope D(W, "NoteSection");
- PrintHeader(S.sh_offset, S.sh_size);
+ PrintHeader(expectedToOptional(Obj->getSectionName(&S)), S.sh_offset,
+ S.sh_size);
Error Err = Error::success();
for (auto Note : Obj->notes(S, Err))
ProcessNote(Note);
@@ -6172,12 +6852,19 @@ void LLVMStyle<ELFT>::printNotes(const ELFFile<ELFT> *Obj) {
reportError(std::move(Err), this->FileName);
}
} else {
- for (const auto &P :
- unwrapOrError(this->FileName, Obj->program_headers())) {
+ Expected<ArrayRef<Elf_Phdr>> PhdrsOrErr = Obj->program_headers();
+ if (!PhdrsOrErr) {
+ this->reportUniqueWarning(createError(
+ "unable to read program headers to locate the PT_NOTE segment: " +
+ toString(PhdrsOrErr.takeError())));
+ return;
+ }
+
+ for (const Elf_Phdr &P : *PhdrsOrErr) {
if (P.p_type != PT_NOTE)
continue;
DictScope D(W, "NoteSection");
- PrintHeader(P.p_offset, P.p_filesz);
+ PrintHeader(/*SecName=*/None, P.p_offset, P.p_filesz);
Error Err = Error::success();
for (auto Note : Obj->notes(P, Err))
ProcessNote(Note);
@@ -6192,35 +6879,38 @@ void LLVMStyle<ELFT>::printELFLinkerOptions(const ELFFile<ELFT> *Obj) {
ListScope L(W, "LinkerOptions");
unsigned I = -1;
- for (const Elf_Shdr &Shdr : unwrapOrError(this->FileName, Obj->sections())) {
+ for (const Elf_Shdr &Shdr : cantFail(Obj->sections())) {
++I;
if (Shdr.sh_type != ELF::SHT_LLVM_LINKER_OPTIONS)
continue;
- ArrayRef<uint8_t> Contents =
- unwrapOrError(this->FileName, Obj->getSectionContents(&Shdr));
- if (Contents.empty())
+ Expected<ArrayRef<uint8_t>> ContentsOrErr = Obj->getSectionContents(&Shdr);
+ if (!ContentsOrErr) {
+ this->reportUniqueWarning(
+ createError("unable to read the content of the "
+ "SHT_LLVM_LINKER_OPTIONS section: " +
+ toString(ContentsOrErr.takeError())));
+ continue;
+ }
+ if (ContentsOrErr->empty())
continue;
- if (Contents.back() != 0) {
- reportWarning(createError("SHT_LLVM_LINKER_OPTIONS section at index " +
- Twine(I) +
- " is broken: the "
- "content is not null-terminated"),
- this->FileName);
+ if (ContentsOrErr->back() != 0) {
+ this->reportUniqueWarning(
+ createError("SHT_LLVM_LINKER_OPTIONS section at index " + Twine(I) +
+ " is broken: the "
+ "content is not null-terminated"));
continue;
}
SmallVector<StringRef, 16> Strings;
- toStringRef(Contents.drop_back()).split(Strings, '\0');
+ toStringRef(ContentsOrErr->drop_back()).split(Strings, '\0');
if (Strings.size() % 2 != 0) {
- reportWarning(
- createError(
- "SHT_LLVM_LINKER_OPTIONS section at index " + Twine(I) +
- " is broken: an incomplete "
- "key-value pair was found. The last possible key was: \"" +
- Strings.back() + "\""),
- this->FileName);
+ this->reportUniqueWarning(createError(
+ "SHT_LLVM_LINKER_OPTIONS section at index " + Twine(I) +
+ " is broken: an incomplete "
+ "key-value pair was found. The last possible key was: \"" +
+ Strings.back() + "\""));
continue;
}
@@ -6232,37 +6922,9 @@ void LLVMStyle<ELFT>::printELFLinkerOptions(const ELFFile<ELFT> *Obj) {
template <class ELFT>
void LLVMStyle<ELFT>::printDependentLibs(const ELFFile<ELFT> *Obj) {
ListScope L(W, "DependentLibs");
-
- auto Warn = [this](unsigned SecNdx, StringRef Msg) {
- this->reportUniqueWarning(
- createError("SHT_LLVM_DEPENDENT_LIBRARIES section at index " +
- Twine(SecNdx) + " is broken: " + Msg));
- };
-
- unsigned I = -1;
- for (const Elf_Shdr &Shdr : unwrapOrError(this->FileName, Obj->sections())) {
- ++I;
- if (Shdr.sh_type != ELF::SHT_LLVM_DEPENDENT_LIBRARIES)
- continue;
-
- Expected<ArrayRef<uint8_t>> ContentsOrErr = Obj->getSectionContents(&Shdr);
- if (!ContentsOrErr) {
- Warn(I, toString(ContentsOrErr.takeError()));
- continue;
- }
-
- ArrayRef<uint8_t> Contents = *ContentsOrErr;
- if (!Contents.empty() && Contents.back() != 0) {
- Warn(I, "the content is not null-terminated");
- continue;
- }
-
- for (const uint8_t *I = Contents.begin(), *E = Contents.end(); I < E;) {
- StringRef Lib((const char *)I);
- W.printString(Lib);
- I += Lib.size() + 1;
- }
- }
+ this->printDependentLibsHelper(
+ Obj, [](const Elf_Shdr &) {},
+ [this](StringRef Lib, uint64_t) { W.printString(Lib); });
}
template <class ELFT>
diff --git a/llvm/tools/llvm-readobj/ObjDumper.cpp b/llvm/tools/llvm-readobj/ObjDumper.cpp
index 6229b52693d87..ce61f1c53a4dd 100644
--- a/llvm/tools/llvm-readobj/ObjDumper.cpp
+++ b/llvm/tools/llvm-readobj/ObjDumper.cpp
@@ -48,13 +48,13 @@ getSectionRefsByNameOrIndex(const object::ObjectFile *Obj,
if (!Section.getAsInteger(0, SecIndex))
SecIndices.emplace(SecIndex, false);
else
- SecNames.emplace(Section, false);
+ SecNames.emplace(std::string(Section), false);
}
SecIndex = Obj->isELF() ? 0 : 1;
for (object::SectionRef SecRef : Obj->sections()) {
StringRef SecName = unwrapOrError(Obj->getFileName(), SecRef.getName());
- auto NameIt = SecNames.find(SecName);
+ auto NameIt = SecNames.find(std::string(SecName));
if (NameIt != SecNames.end())
NameIt->second = true;
auto IndexIt = SecIndices.find(SecIndex);
diff --git a/llvm/tools/llvm-readobj/ObjDumper.h b/llvm/tools/llvm-readobj/ObjDumper.h
index 3fc8d3e79ac1c..57477606d6e8e 100644
--- a/llvm/tools/llvm-readobj/ObjDumper.h
+++ b/llvm/tools/llvm-readobj/ObjDumper.h
@@ -59,12 +59,12 @@ public:
virtual void printNeededLibraries() { }
virtual void printSectionAsHex(StringRef SectionName) {}
virtual void printHashTable() { }
- virtual void printGnuHashTable() { }
+ virtual void printGnuHashTable(const object::ObjectFile *Obj) {}
virtual void printHashSymbols() {}
virtual void printLoadName() {}
virtual void printVersionInfo() {}
virtual void printGroupSections() {}
- virtual void printHashHistogram() {}
+ virtual void printHashHistograms() {}
virtual void printCGProfile() {}
virtual void printAddrsig() {}
virtual void printNotes() {}
diff --git a/llvm/tools/llvm-readobj/WasmDumper.cpp b/llvm/tools/llvm-readobj/WasmDumper.cpp
index dfab9f40d71b4..a02dbb9998267 100644
--- a/llvm/tools/llvm-readobj/WasmDumper.cpp
+++ b/llvm/tools/llvm-readobj/WasmDumper.cpp
@@ -93,18 +93,8 @@ void WasmDumper::printRelocation(const SectionRef &Section,
if (SI != Obj->symbol_end())
SymName = unwrapOrError(Obj->getFileName(), SI->getName());
- bool HasAddend = false;
- switch (RelocType) {
- case wasm::R_WASM_MEMORY_ADDR_LEB:
- case wasm::R_WASM_MEMORY_ADDR_SLEB:
- case wasm::R_WASM_MEMORY_ADDR_I32:
- case wasm::R_WASM_FUNCTION_OFFSET_I32:
- case wasm::R_WASM_SECTION_OFFSET_I32:
- HasAddend = true;
- break;
- default:
- break;
- }
+ bool HasAddend = wasm::relocTypeHasAddend(static_cast<uint32_t>(RelocType));
+
if (opts::ExpandRelocs) {
DictScope Group(W, "Relocation");
W.printNumber("Type", RelocTypeName, RelocType);
@@ -192,6 +182,10 @@ void WasmDumper::printSectionHeaders() {
W.printNumber("Size", static_cast<uint64_t>(Seg.Content.size()));
if (Seg.Offset.Opcode == wasm::WASM_OPCODE_I32_CONST)
W.printNumber("Offset", Seg.Offset.Value.Int32);
+ else if (Seg.Offset.Opcode == wasm::WASM_OPCODE_I64_CONST)
+ W.printNumber("Offset", Seg.Offset.Value.Int64);
+ else
+ llvm_unreachable("unknown init expr opcode");
}
break;
}
@@ -227,8 +221,12 @@ void WasmDumper::printSymbol(const SymbolRef &Sym) {
W.printFlags("Flags", Symbol.Info.Flags, makeArrayRef(WasmSymbolFlags));
if (Symbol.Info.Flags & wasm::WASM_SYMBOL_UNDEFINED) {
- W.printString("ImportName", Symbol.Info.ImportName);
- W.printString("ImportModule", Symbol.Info.ImportModule);
+ if (Symbol.Info.ImportName) {
+ W.printString("ImportName", *Symbol.Info.ImportName);
+ }
+ if (Symbol.Info.ImportModule) {
+ W.printString("ImportModule", *Symbol.Info.ImportModule);
+ }
}
if (Symbol.Info.Kind != wasm::WASM_SYMBOL_TYPE_DATA) {
W.printHex("ElementIndex", Symbol.Info.ElementIndex);
diff --git a/llvm/tools/llvm-readobj/XCOFFDumper.cpp b/llvm/tools/llvm-readobj/XCOFFDumper.cpp
index 1f94036655943..dd62f98d95957 100644
--- a/llvm/tools/llvm-readobj/XCOFFDumper.cpp
+++ b/llvm/tools/llvm-readobj/XCOFFDumper.cpp
@@ -22,11 +22,6 @@ using namespace object;
namespace {
class XCOFFDumper : public ObjDumper {
- enum {
- SymbolTypeMask = 0x07,
- SymbolAlignmentMask = 0xF8,
- SymbolAlignmentBitOffset = 3
- };
public:
XCOFFDumper(const XCOFFObjectFile &Obj, ScopedPrinter &Writer)
@@ -211,17 +206,15 @@ void XCOFFDumper::printCsectAuxEnt32(const XCOFFCsectAuxEnt32 *AuxEntPtr) {
DictScope SymDs(W, "CSECT Auxiliary Entry");
W.printNumber("Index",
Obj.getSymbolIndex(reinterpret_cast<uintptr_t>(AuxEntPtr)));
- if ((AuxEntPtr->SymbolAlignmentAndType & SymbolTypeMask) == XCOFF::XTY_LD)
+ if (AuxEntPtr->isLabel())
W.printNumber("ContainingCsectSymbolIndex", AuxEntPtr->SectionOrLength);
else
W.printNumber("SectionLen", AuxEntPtr->SectionOrLength);
W.printHex("ParameterHashIndex", AuxEntPtr->ParameterHashIndex);
W.printHex("TypeChkSectNum", AuxEntPtr->TypeChkSectNum);
// Print out symbol alignment and type.
- W.printNumber("SymbolAlignmentLog2",
- (AuxEntPtr->SymbolAlignmentAndType & SymbolAlignmentMask) >>
- SymbolAlignmentBitOffset);
- W.printEnum("SymbolType", AuxEntPtr->SymbolAlignmentAndType & SymbolTypeMask,
+ W.printNumber("SymbolAlignmentLog2", AuxEntPtr->getAlignmentLog2());
+ W.printEnum("SymbolType", AuxEntPtr->getSymbolType(),
makeArrayRef(CsectSymbolTypeClass));
W.printEnum("StorageMappingClass",
static_cast<uint8_t>(AuxEntPtr->StorageMappingClass),
diff --git a/llvm/tools/llvm-readobj/llvm-readobj.cpp b/llvm/tools/llvm-readobj/llvm-readobj.cpp
index fadeec1072d67..b9c6ad2256ae2 100644
--- a/llvm/tools/llvm-readobj/llvm-readobj.cpp
+++ b/llvm/tools/llvm-readobj/llvm-readobj.cpp
@@ -345,8 +345,11 @@ namespace opts {
cl::desc("Alias for --elf-hash-histogram"),
cl::aliasopt(HashHistogram));
- // --elf-cg-profile
- cl::opt<bool> CGProfile("elf-cg-profile", cl::desc("Display callgraph profile section"));
+ // --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",
@@ -456,8 +459,9 @@ static void dumpObject(const ObjectFile *Obj, ScopedPrinter &Writer,
Writer.printString("Format", Obj->getFileFormatName());
Writer.printString("Arch", Triple::getArchTypeName(
(llvm::Triple::ArchType)Obj->getArch()));
- Writer.printString("AddressSize",
- formatv("{0}bit", 8 * Obj->getBytesInAddress()));
+ Writer.printString(
+ "AddressSize",
+ std::string(formatv("{0}bit", 8 * Obj->getBytesInAddress())));
Dumper->printLoadName();
}
@@ -465,22 +469,22 @@ static void dumpObject(const ObjectFile *Obj, ScopedPrinter &Writer,
Dumper->printFileHeaders();
if (opts::SectionHeaders)
Dumper->printSectionHeaders();
- if (opts::Relocations)
- Dumper->printRelocations();
- if (opts::DynRelocs)
- Dumper->printDynamicRelocations();
- if (opts::Symbols || opts::DynamicSymbols)
- Dumper->printSymbols(opts::Symbols, opts::DynamicSymbols);
if (opts::HashSymbols)
Dumper->printHashSymbols();
- if (opts::UnwindInfo)
- Dumper->printUnwindInfo();
+ if (opts::ProgramHeaders || opts::SectionMapping == cl::BOU_TRUE)
+ Dumper->printProgramHeaders(opts::ProgramHeaders, opts::SectionMapping);
if (opts::DynamicTable)
Dumper->printDynamicTable();
if (opts::NeededLibraries)
Dumper->printNeededLibraries();
- if (opts::ProgramHeaders || opts::SectionMapping == cl::BOU_TRUE)
- Dumper->printProgramHeaders(opts::ProgramHeaders, opts::SectionMapping);
+ if (opts::Relocations)
+ Dumper->printRelocations();
+ if (opts::DynRelocs)
+ Dumper->printDynamicRelocations();
+ if (opts::UnwindInfo)
+ Dumper->printUnwindInfo();
+ if (opts::Symbols || opts::DynamicSymbols)
+ Dumper->printSymbols(opts::Symbols, opts::DynamicSymbols);
if (!opts::StringDump.empty())
Dumper->printSectionsAsString(Obj, opts::StringDump);
if (!opts::HexDump.empty())
@@ -488,7 +492,7 @@ static void dumpObject(const ObjectFile *Obj, ScopedPrinter &Writer,
if (opts::HashTable)
Dumper->printHashTable();
if (opts::GnuHashTable)
- Dumper->printGnuHashTable();
+ Dumper->printGnuHashTable(Obj);
if (opts::VersionInfo)
Dumper->printVersionInfo();
if (Obj->isELF()) {
@@ -501,7 +505,7 @@ static void dumpObject(const ObjectFile *Obj, ScopedPrinter &Writer,
if (opts::SectionGroups)
Dumper->printGroupSections();
if (opts::HashHistogram)
- Dumper->printHashHistogram();
+ Dumper->printHashHistograms();
if (opts::CGProfile)
Dumper->printCGProfile();
if (opts::Addrsig)
@@ -524,6 +528,8 @@ static void dumpObject(const ObjectFile *Obj, ScopedPrinter &Writer,
Dumper->printCOFFResources();
if (opts::COFFLoadConfig)
Dumper->printCOFFLoadConfig();
+ if (opts::CGProfile)
+ Dumper->printCGProfile();
if (opts::Addrsig)
Dumper->printAddrsig();
if (opts::CodeView)
diff --git a/llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp b/llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp
index 9b84c46d39010..be5dbdd1c5597 100644
--- a/llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp
+++ b/llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp
@@ -189,7 +189,7 @@ class TrivialMemoryManager : public RTDyldMemoryManager {
public:
struct SectionInfo {
SectionInfo(StringRef Name, sys::MemoryBlock MB, unsigned SectionID)
- : Name(Name), MB(std::move(MB)), SectionID(SectionID) {}
+ : Name(std::string(Name)), MB(std::move(MB)), SectionID(SectionID) {}
std::string Name;
sys::MemoryBlock MB;
unsigned SectionID = ~0U;
@@ -599,7 +599,7 @@ void applySpecificSectionMappings(RuntimeDyld &Dyld,
for (StringRef Mapping : SpecificSectionMappings) {
size_t EqualsIdx = Mapping.find_first_of("=");
- std::string SectionIDStr = Mapping.substr(0, EqualsIdx);
+ std::string SectionIDStr = std::string(Mapping.substr(0, EqualsIdx));
size_t ComaIdx = Mapping.find_first_of(",");
if (ComaIdx == StringRef::npos)
@@ -612,7 +612,7 @@ void applySpecificSectionMappings(RuntimeDyld &Dyld,
ExitOnErr(getSectionId(FileToSecIDMap, FileName, SectionName));
auto* OldAddr = Dyld.getSectionContent(SectionID).data();
- std::string NewAddrStr = Mapping.substr(EqualsIdx + 1);
+ std::string NewAddrStr = std::string(Mapping.substr(EqualsIdx + 1));
uint64_t NewAddr;
if (StringRef(NewAddrStr).getAsInteger(0, NewAddr))
diff --git a/llvm/tools/llvm-size/llvm-size.cpp b/llvm/tools/llvm-size/llvm-size.cpp
new file mode 100644
index 0000000000000..987270e98c483
--- /dev/null
+++ b/llvm/tools/llvm-size/llvm-size.cpp
@@ -0,0 +1,910 @@
+//===-- llvm-size.cpp - Print the size of each object section ---*- 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 is a utility that works like traditional Unix "size",
+// that is, it prints out the size of each section, and the total size of all
+// sections.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/APInt.h"
+#include "llvm/Object/Archive.h"
+#include "llvm/Object/ELFObjectFile.h"
+#include "llvm/Object/MachO.h"
+#include "llvm/Object/MachOUniversal.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/WithColor.h"
+#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+#include <string>
+#include <system_error>
+
+using namespace llvm;
+using namespace object;
+
+cl::OptionCategory SizeCat("llvm-size Options");
+
+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));
+
+static bool BerkeleyHeaderPrinted = false;
+static bool MoreThanOneFile = false;
+static uint64_t TotalObjectText = 0;
+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) {
+ HadError = true;
+ WithColor::error(errs(), ToolName) << "'" << File << "': " << Message << "\n";
+}
+
+// This version of error() prints the archive name and member name, for example:
+// "libx.a(foo.o)" after the ToolName before the error message. It sets
+// HadError but returns allowing the code to move on to other archive members.
+static void error(llvm::Error E, StringRef FileName, const Archive::Child &C,
+ StringRef ArchitectureName = StringRef()) {
+ HadError = true;
+ WithColor::error(errs(), ToolName) << "'" << FileName << "'";
+
+ Expected<StringRef> NameOrErr = C.getName();
+ // TODO: if we have a error getting the name then it would be nice to print
+ // the index of which archive member this is and or its offset in the
+ // archive instead of "???" as the name.
+ if (!NameOrErr) {
+ consumeError(NameOrErr.takeError());
+ errs() << "(" << "???" << ")";
+ } else
+ errs() << "(" << NameOrErr.get() << ")";
+
+ if (!ArchitectureName.empty())
+ errs() << " (for architecture " << ArchitectureName << ") ";
+
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(std::move(E), OS);
+ OS.flush();
+ errs() << ": " << Buf << "\n";
+}
+
+// This version of error() prints the file name and which architecture slice it // is from, for example: "foo.o (for architecture i386)" after the ToolName
+// before the error message. It sets HadError but returns allowing the code to
+// move on to other architecture slices.
+static void error(llvm::Error E, StringRef FileName,
+ StringRef ArchitectureName = StringRef()) {
+ HadError = true;
+ WithColor::error(errs(), ToolName) << "'" << FileName << "'";
+
+ if (!ArchitectureName.empty())
+ errs() << " (for architecture " << ArchitectureName << ") ";
+
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(std::move(E), OS);
+ OS.flush();
+ errs() << ": " << Buf << "\n";
+}
+
+/// Get the length of the string that represents @p num in Radix including the
+/// leading 0x or 0 for hexadecimal and octal respectively.
+static size_t getNumLengthAsString(uint64_t num) {
+ APInt conv(64, num);
+ SmallString<32> result;
+ conv.toString(result, Radix, false, true);
+ return result.size();
+}
+
+/// Return the printing format for the Radix.
+static const char *getRadixFmt() {
+ switch (Radix) {
+ case octal:
+ return PRIo64;
+ case decimal:
+ return PRIu64;
+ case hexadecimal:
+ return PRIx64;
+ }
+ return nullptr;
+}
+
+/// Remove unneeded ELF sections from calculation
+static bool considerForSize(ObjectFile *Obj, SectionRef Section) {
+ if (!Obj->isELF())
+ return true;
+ switch (static_cast<ELFSectionRef>(Section).getType()) {
+ case ELF::SHT_NULL:
+ case ELF::SHT_SYMTAB:
+ return false;
+ case ELF::SHT_STRTAB:
+ case ELF::SHT_REL:
+ case ELF::SHT_RELA:
+ return static_cast<ELFSectionRef>(Section).getFlags() & ELF::SHF_ALLOC;
+ }
+ return true;
+}
+
+/// Total size of all ELF common symbols
+static Expected<uint64_t> getCommonSize(ObjectFile *Obj) {
+ uint64_t TotalCommons = 0;
+ for (auto &Sym : Obj->symbols()) {
+ Expected<uint32_t> SymFlagsOrErr =
+ Obj->getSymbolFlags(Sym.getRawDataRefImpl());
+ if (!SymFlagsOrErr)
+ return SymFlagsOrErr.takeError();
+ if (*SymFlagsOrErr & SymbolRef::SF_Common)
+ TotalCommons += Obj->getCommonSymbolSize(Sym.getRawDataRefImpl());
+ }
+ return TotalCommons;
+}
+
+/// Print the size of each Mach-O segment and section in @p MachO.
+///
+/// This is when used when @c OutputFormat is darwin and produces the same
+/// output as darwin's size(1) -m output.
+static void printDarwinSectionSizes(MachOObjectFile *MachO) {
+ std::string fmtbuf;
+ raw_string_ostream fmt(fmtbuf);
+ const char *radix_fmt = getRadixFmt();
+ if (Radix == hexadecimal)
+ fmt << "0x";
+ fmt << "%" << radix_fmt;
+
+ uint32_t Filetype = MachO->getHeader().filetype;
+
+ uint64_t total = 0;
+ for (const auto &Load : MachO->load_commands()) {
+ if (Load.C.cmd == MachO::LC_SEGMENT_64) {
+ MachO::segment_command_64 Seg = MachO->getSegment64LoadCommand(Load);
+ outs() << "Segment " << Seg.segname << ": "
+ << format(fmt.str().c_str(), Seg.vmsize);
+ if (DarwinLongFormat)
+ outs() << " (vmaddr 0x" << format("%" PRIx64, Seg.vmaddr) << " fileoff "
+ << Seg.fileoff << ")";
+ outs() << "\n";
+ total += Seg.vmsize;
+ uint64_t sec_total = 0;
+ for (unsigned J = 0; J < Seg.nsects; ++J) {
+ MachO::section_64 Sec = MachO->getSection64(Load, J);
+ if (Filetype == MachO::MH_OBJECT)
+ outs() << "\tSection (" << format("%.16s", &Sec.segname) << ", "
+ << format("%.16s", &Sec.sectname) << "): ";
+ else
+ outs() << "\tSection " << format("%.16s", &Sec.sectname) << ": ";
+ outs() << format(fmt.str().c_str(), Sec.size);
+ if (DarwinLongFormat)
+ outs() << " (addr 0x" << format("%" PRIx64, Sec.addr) << " offset "
+ << Sec.offset << ")";
+ outs() << "\n";
+ sec_total += Sec.size;
+ }
+ if (Seg.nsects != 0)
+ outs() << "\ttotal " << format(fmt.str().c_str(), sec_total) << "\n";
+ } else if (Load.C.cmd == MachO::LC_SEGMENT) {
+ MachO::segment_command Seg = MachO->getSegmentLoadCommand(Load);
+ uint64_t Seg_vmsize = Seg.vmsize;
+ outs() << "Segment " << Seg.segname << ": "
+ << format(fmt.str().c_str(), Seg_vmsize);
+ if (DarwinLongFormat)
+ outs() << " (vmaddr 0x" << format("%" PRIx32, Seg.vmaddr) << " fileoff "
+ << Seg.fileoff << ")";
+ outs() << "\n";
+ total += Seg.vmsize;
+ uint64_t sec_total = 0;
+ for (unsigned J = 0; J < Seg.nsects; ++J) {
+ MachO::section Sec = MachO->getSection(Load, J);
+ if (Filetype == MachO::MH_OBJECT)
+ outs() << "\tSection (" << format("%.16s", &Sec.segname) << ", "
+ << format("%.16s", &Sec.sectname) << "): ";
+ else
+ outs() << "\tSection " << format("%.16s", &Sec.sectname) << ": ";
+ uint64_t Sec_size = Sec.size;
+ outs() << format(fmt.str().c_str(), Sec_size);
+ if (DarwinLongFormat)
+ outs() << " (addr 0x" << format("%" PRIx32, Sec.addr) << " offset "
+ << Sec.offset << ")";
+ outs() << "\n";
+ sec_total += Sec.size;
+ }
+ if (Seg.nsects != 0)
+ outs() << "\ttotal " << format(fmt.str().c_str(), sec_total) << "\n";
+ }
+ }
+ outs() << "total " << format(fmt.str().c_str(), total) << "\n";
+}
+
+/// Print the summary sizes of the standard Mach-O segments in @p MachO.
+///
+/// This is when used when @c OutputFormat is berkeley with a Mach-O file and
+/// produces the same output as darwin's size(1) default output.
+static void printDarwinSegmentSizes(MachOObjectFile *MachO) {
+ uint64_t total_text = 0;
+ uint64_t total_data = 0;
+ uint64_t total_objc = 0;
+ uint64_t total_others = 0;
+ for (const auto &Load : MachO->load_commands()) {
+ if (Load.C.cmd == MachO::LC_SEGMENT_64) {
+ MachO::segment_command_64 Seg = MachO->getSegment64LoadCommand(Load);
+ if (MachO->getHeader().filetype == MachO::MH_OBJECT) {
+ for (unsigned J = 0; J < Seg.nsects; ++J) {
+ MachO::section_64 Sec = MachO->getSection64(Load, J);
+ StringRef SegmentName = StringRef(Sec.segname);
+ if (SegmentName == "__TEXT")
+ total_text += Sec.size;
+ else if (SegmentName == "__DATA")
+ total_data += Sec.size;
+ else if (SegmentName == "__OBJC")
+ total_objc += Sec.size;
+ else
+ total_others += Sec.size;
+ }
+ } else {
+ StringRef SegmentName = StringRef(Seg.segname);
+ if (SegmentName == "__TEXT")
+ total_text += Seg.vmsize;
+ else if (SegmentName == "__DATA")
+ total_data += Seg.vmsize;
+ else if (SegmentName == "__OBJC")
+ total_objc += Seg.vmsize;
+ else
+ total_others += Seg.vmsize;
+ }
+ } else if (Load.C.cmd == MachO::LC_SEGMENT) {
+ MachO::segment_command Seg = MachO->getSegmentLoadCommand(Load);
+ if (MachO->getHeader().filetype == MachO::MH_OBJECT) {
+ for (unsigned J = 0; J < Seg.nsects; ++J) {
+ MachO::section Sec = MachO->getSection(Load, J);
+ StringRef SegmentName = StringRef(Sec.segname);
+ if (SegmentName == "__TEXT")
+ total_text += Sec.size;
+ else if (SegmentName == "__DATA")
+ total_data += Sec.size;
+ else if (SegmentName == "__OBJC")
+ total_objc += Sec.size;
+ else
+ total_others += Sec.size;
+ }
+ } else {
+ StringRef SegmentName = StringRef(Seg.segname);
+ if (SegmentName == "__TEXT")
+ total_text += Seg.vmsize;
+ else if (SegmentName == "__DATA")
+ total_data += Seg.vmsize;
+ else if (SegmentName == "__OBJC")
+ total_objc += Seg.vmsize;
+ else
+ total_others += Seg.vmsize;
+ }
+ }
+ }
+ uint64_t total = total_text + total_data + total_objc + total_others;
+
+ if (!BerkeleyHeaderPrinted) {
+ outs() << "__TEXT\t__DATA\t__OBJC\tothers\tdec\thex\n";
+ BerkeleyHeaderPrinted = true;
+ }
+ outs() << total_text << "\t" << total_data << "\t" << total_objc << "\t"
+ << total_others << "\t" << total << "\t" << format("%" PRIx64, total)
+ << "\t";
+}
+
+/// Print the size of each section in @p Obj.
+///
+/// The format used is determined by @c OutputFormat and @c Radix.
+static void printObjectSectionSizes(ObjectFile *Obj) {
+ uint64_t total = 0;
+ std::string fmtbuf;
+ raw_string_ostream fmt(fmtbuf);
+ const char *radix_fmt = getRadixFmt();
+
+ // If OutputFormat is darwin and we have a MachOObjectFile print as darwin's
+ // size(1) -m output, else if OutputFormat is darwin and not a Mach-O object
+ // let it fall through to OutputFormat berkeley.
+ MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(Obj);
+ if (OutputFormat == darwin && MachO)
+ printDarwinSectionSizes(MachO);
+ // If we have a MachOObjectFile and the OutputFormat is berkeley print as
+ // darwin's default berkeley format for Mach-O files.
+ else if (MachO && OutputFormat == berkeley)
+ printDarwinSegmentSizes(MachO);
+ else if (OutputFormat == sysv) {
+ // Run two passes over all sections. The first gets the lengths needed for
+ // formatting the output. The second actually does the output.
+ std::size_t max_name_len = strlen("section");
+ std::size_t max_size_len = strlen("size");
+ std::size_t max_addr_len = strlen("addr");
+ for (const SectionRef &Section : Obj->sections()) {
+ if (!considerForSize(Obj, Section))
+ continue;
+ uint64_t size = Section.getSize();
+ total += size;
+
+ Expected<StringRef> name_or_err = Section.getName();
+ if (!name_or_err) {
+ error(name_or_err.takeError(), Obj->getFileName());
+ return;
+ }
+
+ uint64_t addr = Section.getAddress();
+ max_name_len = std::max(max_name_len, name_or_err->size());
+ max_size_len = std::max(max_size_len, getNumLengthAsString(size));
+ max_addr_len = std::max(max_addr_len, getNumLengthAsString(addr));
+ }
+
+ // Add extra padding.
+ max_name_len += 2;
+ max_size_len += 2;
+ max_addr_len += 2;
+
+ // Setup header format.
+ fmt << "%-" << max_name_len << "s "
+ << "%" << max_size_len << "s "
+ << "%" << max_addr_len << "s\n";
+
+ // Print header
+ outs() << format(fmt.str().c_str(), static_cast<const char *>("section"),
+ static_cast<const char *>("size"),
+ static_cast<const char *>("addr"));
+ fmtbuf.clear();
+
+ // Setup per section format.
+ fmt << "%-" << max_name_len << "s "
+ << "%#" << max_size_len << radix_fmt << " "
+ << "%#" << max_addr_len << radix_fmt << "\n";
+
+ // Print each section.
+ for (const SectionRef &Section : Obj->sections()) {
+ if (!considerForSize(Obj, Section))
+ continue;
+
+ Expected<StringRef> name_or_err = Section.getName();
+ if (!name_or_err) {
+ error(name_or_err.takeError(), Obj->getFileName());
+ return;
+ }
+
+ uint64_t size = Section.getSize();
+ uint64_t addr = Section.getAddress();
+ outs() << format(fmt.str().c_str(), name_or_err->str().c_str(), size, addr);
+ }
+
+ if (ELFCommons) {
+ if (Expected<uint64_t> CommonSizeOrErr = getCommonSize(Obj)) {
+ total += *CommonSizeOrErr;
+ outs() << format(fmt.str().c_str(), std::string("*COM*").c_str(),
+ *CommonSizeOrErr, static_cast<uint64_t>(0));
+ } else {
+ error(CommonSizeOrErr.takeError(), Obj->getFileName());
+ return;
+ }
+ }
+
+ // Print total.
+ fmtbuf.clear();
+ fmt << "%-" << max_name_len << "s "
+ << "%#" << max_size_len << radix_fmt << "\n";
+ outs() << format(fmt.str().c_str(), static_cast<const char *>("Total"),
+ total)
+ << "\n\n";
+ } else {
+ // The Berkeley format does not display individual section sizes. It
+ // displays the cumulative size for each section type.
+ uint64_t total_text = 0;
+ uint64_t total_data = 0;
+ uint64_t total_bss = 0;
+
+ // Make one pass over the section table to calculate sizes.
+ for (const SectionRef &Section : Obj->sections()) {
+ uint64_t size = Section.getSize();
+ bool isText = Section.isBerkeleyText();
+ bool isData = Section.isBerkeleyData();
+ bool isBSS = Section.isBSS();
+ if (isText)
+ total_text += size;
+ else if (isData)
+ total_data += size;
+ else if (isBSS)
+ total_bss += size;
+ }
+
+ if (ELFCommons) {
+ if (Expected<uint64_t> CommonSizeOrErr = getCommonSize(Obj))
+ total_bss += *CommonSizeOrErr;
+ else {
+ error(CommonSizeOrErr.takeError(), Obj->getFileName());
+ return;
+ }
+ }
+
+ total = total_text + total_data + total_bss;
+
+ if (TotalSizes) {
+ TotalObjectText += total_text;
+ TotalObjectData += total_data;
+ TotalObjectBss += total_bss;
+ TotalObjectTotal += total;
+ }
+
+ if (!BerkeleyHeaderPrinted) {
+ outs() << " text\t"
+ " data\t"
+ " bss\t"
+ " "
+ << (Radix == octal ? "oct" : "dec")
+ << "\t"
+ " hex\t"
+ "filename\n";
+ BerkeleyHeaderPrinted = true;
+ }
+
+ // Print result.
+ fmt << "%#7" << radix_fmt << "\t"
+ << "%#7" << radix_fmt << "\t"
+ << "%#7" << radix_fmt << "\t";
+ outs() << format(fmt.str().c_str(), total_text, total_data, total_bss);
+ fmtbuf.clear();
+ fmt << "%7" << (Radix == octal ? PRIo64 : PRIu64) << "\t"
+ << "%7" PRIx64 "\t";
+ outs() << format(fmt.str().c_str(), total, total);
+ }
+}
+
+/// Checks to see if the @p O ObjectFile is a Mach-O file and if it is and there
+/// is a list of architecture flags specified then check to make sure this
+/// Mach-O file is one of those architectures or all architectures was
+/// specificed. If not then an error is generated and this routine returns
+/// false. Else it returns true.
+static bool checkMachOAndArchFlags(ObjectFile *O, StringRef Filename) {
+ auto *MachO = dyn_cast<MachOObjectFile>(O);
+
+ if (!MachO || ArchAll || ArchFlags.empty())
+ return true;
+
+ MachO::mach_header H;
+ MachO::mach_header_64 H_64;
+ Triple T;
+ if (MachO->is64Bit()) {
+ H_64 = MachO->MachOObjectFile::getHeader64();
+ T = MachOObjectFile::getArchTriple(H_64.cputype, H_64.cpusubtype);
+ } else {
+ H = MachO->MachOObjectFile::getHeader();
+ T = MachOObjectFile::getArchTriple(H.cputype, H.cpusubtype);
+ }
+ if (none_of(ArchFlags, [&](const std::string &Name) {
+ return Name == T.getArchName();
+ })) {
+ error("no architecture specified", Filename);
+ return false;
+ }
+ return true;
+}
+
+/// Print the section sizes for @p file. If @p file is an archive, print the
+/// section sizes for each archive member.
+static void printFileSectionSizes(StringRef file) {
+
+ // Attempt to open the binary.
+ Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(file);
+ if (!BinaryOrErr) {
+ error(BinaryOrErr.takeError(), file);
+ return;
+ }
+ Binary &Bin = *BinaryOrErr.get().getBinary();
+
+ if (Archive *a = dyn_cast<Archive>(&Bin)) {
+ // This is an archive. Iterate over each member and display its sizes.
+ Error Err = Error::success();
+ for (auto &C : a->children(Err)) {
+ Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
+ if (!ChildOrErr) {
+ if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError()))
+ error(std::move(E), a->getFileName(), C);
+ continue;
+ }
+ if (ObjectFile *o = dyn_cast<ObjectFile>(&*ChildOrErr.get())) {
+ MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
+ if (!checkMachOAndArchFlags(o, file))
+ return;
+ if (OutputFormat == sysv)
+ outs() << o->getFileName() << " (ex " << a->getFileName() << "):\n";
+ else if (MachO && OutputFormat == darwin)
+ outs() << a->getFileName() << "(" << o->getFileName() << "):\n";
+ printObjectSectionSizes(o);
+ if (OutputFormat == berkeley) {
+ if (MachO)
+ outs() << a->getFileName() << "(" << o->getFileName() << ")\n";
+ else
+ outs() << o->getFileName() << " (ex " << a->getFileName() << ")\n";
+ }
+ }
+ }
+ if (Err)
+ error(std::move(Err), a->getFileName());
+ } else if (MachOUniversalBinary *UB =
+ dyn_cast<MachOUniversalBinary>(&Bin)) {
+ // If we have a list of architecture flags specified dump only those.
+ if (!ArchAll && !ArchFlags.empty()) {
+ // Look for a slice in the universal binary that matches each ArchFlag.
+ bool ArchFound;
+ for (unsigned i = 0; i < ArchFlags.size(); ++i) {
+ ArchFound = false;
+ for (MachOUniversalBinary::object_iterator I = UB->begin_objects(),
+ E = UB->end_objects();
+ I != E; ++I) {
+ if (ArchFlags[i] == I->getArchFlagName()) {
+ ArchFound = true;
+ Expected<std::unique_ptr<ObjectFile>> UO = I->getAsObjectFile();
+ if (UO) {
+ if (ObjectFile *o = dyn_cast<ObjectFile>(&*UO.get())) {
+ MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
+ if (OutputFormat == sysv)
+ outs() << o->getFileName() << " :\n";
+ else if (MachO && OutputFormat == darwin) {
+ if (MoreThanOneFile || ArchFlags.size() > 1)
+ outs() << o->getFileName() << " (for architecture "
+ << I->getArchFlagName() << "): \n";
+ }
+ printObjectSectionSizes(o);
+ if (OutputFormat == berkeley) {
+ if (!MachO || MoreThanOneFile || ArchFlags.size() > 1)
+ outs() << o->getFileName() << " (for architecture "
+ << I->getArchFlagName() << ")";
+ outs() << "\n";
+ }
+ }
+ } else if (auto E = isNotObjectErrorInvalidFileType(
+ UO.takeError())) {
+ error(std::move(E), file, ArchFlags.size() > 1 ?
+ StringRef(I->getArchFlagName()) : StringRef());
+ return;
+ } else if (Expected<std::unique_ptr<Archive>> AOrErr =
+ I->getAsArchive()) {
+ std::unique_ptr<Archive> &UA = *AOrErr;
+ // This is an archive. Iterate over each member and display its
+ // sizes.
+ Error Err = Error::success();
+ for (auto &C : UA->children(Err)) {
+ Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
+ if (!ChildOrErr) {
+ if (auto E = isNotObjectErrorInvalidFileType(
+ ChildOrErr.takeError()))
+ error(std::move(E), UA->getFileName(), C,
+ ArchFlags.size() > 1 ?
+ StringRef(I->getArchFlagName()) : StringRef());
+ continue;
+ }
+ if (ObjectFile *o = dyn_cast<ObjectFile>(&*ChildOrErr.get())) {
+ MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
+ if (OutputFormat == sysv)
+ outs() << o->getFileName() << " (ex " << UA->getFileName()
+ << "):\n";
+ else if (MachO && OutputFormat == darwin)
+ outs() << UA->getFileName() << "(" << o->getFileName()
+ << ")"
+ << " (for architecture " << I->getArchFlagName()
+ << "):\n";
+ printObjectSectionSizes(o);
+ if (OutputFormat == berkeley) {
+ if (MachO) {
+ outs() << UA->getFileName() << "(" << o->getFileName()
+ << ")";
+ if (ArchFlags.size() > 1)
+ outs() << " (for architecture " << I->getArchFlagName()
+ << ")";
+ outs() << "\n";
+ } else
+ outs() << o->getFileName() << " (ex " << UA->getFileName()
+ << ")\n";
+ }
+ }
+ }
+ if (Err)
+ error(std::move(Err), UA->getFileName());
+ } else {
+ consumeError(AOrErr.takeError());
+ error("mach-o universal file for architecture " +
+ StringRef(I->getArchFlagName()) +
+ " is not a mach-o file or an archive file",
+ file);
+ }
+ }
+ }
+ if (!ArchFound) {
+ error("file does not contain architecture " + ArchFlags[i], file);
+ return;
+ }
+ }
+ return;
+ }
+ // No architecture flags were specified so if this contains a slice that
+ // matches the host architecture dump only that.
+ if (!ArchAll) {
+ StringRef HostArchName = MachOObjectFile::getHostArch().getArchName();
+ for (MachOUniversalBinary::object_iterator I = UB->begin_objects(),
+ E = UB->end_objects();
+ I != E; ++I) {
+ if (HostArchName == I->getArchFlagName()) {
+ Expected<std::unique_ptr<ObjectFile>> UO = I->getAsObjectFile();
+ if (UO) {
+ if (ObjectFile *o = dyn_cast<ObjectFile>(&*UO.get())) {
+ MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
+ if (OutputFormat == sysv)
+ outs() << o->getFileName() << " :\n";
+ else if (MachO && OutputFormat == darwin) {
+ if (MoreThanOneFile)
+ outs() << o->getFileName() << " (for architecture "
+ << I->getArchFlagName() << "):\n";
+ }
+ printObjectSectionSizes(o);
+ if (OutputFormat == berkeley) {
+ if (!MachO || MoreThanOneFile)
+ outs() << o->getFileName() << " (for architecture "
+ << I->getArchFlagName() << ")";
+ outs() << "\n";
+ }
+ }
+ } else if (auto E = isNotObjectErrorInvalidFileType(UO.takeError())) {
+ error(std::move(E), file);
+ return;
+ } else if (Expected<std::unique_ptr<Archive>> AOrErr =
+ I->getAsArchive()) {
+ std::unique_ptr<Archive> &UA = *AOrErr;
+ // This is an archive. Iterate over each member and display its
+ // sizes.
+ Error Err = Error::success();
+ for (auto &C : UA->children(Err)) {
+ Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
+ if (!ChildOrErr) {
+ if (auto E = isNotObjectErrorInvalidFileType(
+ ChildOrErr.takeError()))
+ error(std::move(E), UA->getFileName(), C);
+ continue;
+ }
+ if (ObjectFile *o = dyn_cast<ObjectFile>(&*ChildOrErr.get())) {
+ MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
+ if (OutputFormat == sysv)
+ outs() << o->getFileName() << " (ex " << UA->getFileName()
+ << "):\n";
+ else if (MachO && OutputFormat == darwin)
+ outs() << UA->getFileName() << "(" << o->getFileName() << ")"
+ << " (for architecture " << I->getArchFlagName()
+ << "):\n";
+ printObjectSectionSizes(o);
+ if (OutputFormat == berkeley) {
+ if (MachO)
+ outs() << UA->getFileName() << "(" << o->getFileName()
+ << ")\n";
+ else
+ outs() << o->getFileName() << " (ex " << UA->getFileName()
+ << ")\n";
+ }
+ }
+ }
+ if (Err)
+ error(std::move(Err), UA->getFileName());
+ } else {
+ consumeError(AOrErr.takeError());
+ error("mach-o universal file for architecture " +
+ StringRef(I->getArchFlagName()) +
+ " is not a mach-o file or an archive file",
+ file);
+ }
+ return;
+ }
+ }
+ }
+ // Either all architectures have been specified or none have been specified
+ // and this does not contain the host architecture so dump all the slices.
+ bool MoreThanOneArch = UB->getNumberOfObjects() > 1;
+ for (MachOUniversalBinary::object_iterator I = UB->begin_objects(),
+ E = UB->end_objects();
+ I != E; ++I) {
+ Expected<std::unique_ptr<ObjectFile>> UO = I->getAsObjectFile();
+ if (UO) {
+ if (ObjectFile *o = dyn_cast<ObjectFile>(&*UO.get())) {
+ MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
+ if (OutputFormat == sysv)
+ outs() << o->getFileName() << " :\n";
+ else if (MachO && OutputFormat == darwin) {
+ if (MoreThanOneFile || MoreThanOneArch)
+ outs() << o->getFileName() << " (for architecture "
+ << I->getArchFlagName() << "):";
+ outs() << "\n";
+ }
+ printObjectSectionSizes(o);
+ if (OutputFormat == berkeley) {
+ if (!MachO || MoreThanOneFile || MoreThanOneArch)
+ outs() << o->getFileName() << " (for architecture "
+ << I->getArchFlagName() << ")";
+ outs() << "\n";
+ }
+ }
+ } else if (auto E = isNotObjectErrorInvalidFileType(UO.takeError())) {
+ error(std::move(E), file, MoreThanOneArch ?
+ StringRef(I->getArchFlagName()) : StringRef());
+ return;
+ } else if (Expected<std::unique_ptr<Archive>> AOrErr =
+ I->getAsArchive()) {
+ std::unique_ptr<Archive> &UA = *AOrErr;
+ // This is an archive. Iterate over each member and display its sizes.
+ Error Err = Error::success();
+ for (auto &C : UA->children(Err)) {
+ Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
+ if (!ChildOrErr) {
+ if (auto E = isNotObjectErrorInvalidFileType(
+ ChildOrErr.takeError()))
+ error(std::move(E), UA->getFileName(), C, MoreThanOneArch ?
+ StringRef(I->getArchFlagName()) : StringRef());
+ continue;
+ }
+ if (ObjectFile *o = dyn_cast<ObjectFile>(&*ChildOrErr.get())) {
+ MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
+ if (OutputFormat == sysv)
+ outs() << o->getFileName() << " (ex " << UA->getFileName()
+ << "):\n";
+ else if (MachO && OutputFormat == darwin)
+ outs() << UA->getFileName() << "(" << o->getFileName() << ")"
+ << " (for architecture " << I->getArchFlagName() << "):\n";
+ printObjectSectionSizes(o);
+ if (OutputFormat == berkeley) {
+ if (MachO)
+ outs() << UA->getFileName() << "(" << o->getFileName() << ")"
+ << " (for architecture " << I->getArchFlagName()
+ << ")\n";
+ else
+ outs() << o->getFileName() << " (ex " << UA->getFileName()
+ << ")\n";
+ }
+ }
+ }
+ if (Err)
+ error(std::move(Err), UA->getFileName());
+ } else {
+ consumeError(AOrErr.takeError());
+ error("mach-o universal file for architecture " +
+ StringRef(I->getArchFlagName()) +
+ " is not a mach-o file or an archive file",
+ file);
+ }
+ }
+ } else if (ObjectFile *o = dyn_cast<ObjectFile>(&Bin)) {
+ if (!checkMachOAndArchFlags(o, file))
+ return;
+ MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
+ if (OutputFormat == sysv)
+ outs() << o->getFileName() << " :\n";
+ else if (MachO && OutputFormat == darwin && MoreThanOneFile)
+ outs() << o->getFileName() << ":\n";
+ printObjectSectionSizes(o);
+ if (OutputFormat == berkeley) {
+ if (!MachO || MoreThanOneFile)
+ outs() << o->getFileName();
+ outs() << "\n";
+ }
+ } else {
+ error("unsupported file type", file);
+ }
+}
+
+static void printBerkeleyTotals() {
+ std::string fmtbuf;
+ raw_string_ostream fmt(fmtbuf);
+ const char *radix_fmt = getRadixFmt();
+ fmt << "%#7" << radix_fmt << "\t"
+ << "%#7" << radix_fmt << "\t"
+ << "%#7" << radix_fmt << "\t";
+ outs() << format(fmt.str().c_str(), TotalObjectText, TotalObjectData,
+ TotalObjectBss);
+ fmtbuf.clear();
+ fmt << "%7" << (Radix == octal ? PRIo64 : PRIu64) << "\t"
+ << "%7" PRIx64 "\t";
+ outs() << format(fmt.str().c_str(), TotalObjectTotal, TotalObjectTotal)
+ << "(TOTALS)\n";
+}
+
+int main(int argc, char **argv) {
+ InitLLVM X(argc, argv);
+ cl::HideUnrelatedOptions(SizeCat);
+ cl::ParseCommandLineOptions(argc, argv, "llvm object size dumper\n");
+
+ 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)) {
+ outs() << ToolName << ": for the -arch option: Unknown architecture "
+ << "named '" << Arch << "'";
+ return 1;
+ }
+ }
+ }
+
+ if (InputFilenames.empty())
+ InputFilenames.push_back("a.out");
+
+ MoreThanOneFile = InputFilenames.size() > 1;
+ llvm::for_each(InputFilenames, printFileSectionSizes);
+ if (OutputFormat == berkeley && TotalSizes)
+ printBerkeleyTotals();
+
+ if (HadError)
+ return 1;
+}
diff --git a/llvm/tools/llvm-stress/llvm-stress.cpp b/llvm/tools/llvm-stress/llvm-stress.cpp
index 5f36a785332b5..22f530dde1670 100644
--- a/llvm/tools/llvm-stress/llvm-stress.cpp
+++ b/llvm/tools/llvm-stress/llvm-stress.cpp
@@ -300,7 +300,7 @@ protected:
if (len != (unsigned)-1)
width = len;
- return VectorType::get(Ty, width);
+ return FixedVectorType::get(Ty, width);
}
/// Pick a random scalar type.
@@ -343,7 +343,9 @@ struct LoadModifier: public Modifier {
void Act() override {
// Try to use predefined pointers. If non-exist, use undef pointer value;
Value *Ptr = getRandomPointerValue();
- Value *V = new LoadInst(Ptr, "L", BB->getTerminator());
+ PointerType *Tp = cast<PointerType>(Ptr->getType());
+ Value *V = new LoadInst(Tp->getElementType(), Ptr, "L",
+ BB->getTerminator());
PT->push_back(V);
}
};
@@ -626,9 +628,10 @@ 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 (Val0->getType()->isVectorTy() && (getRandom() % 1)) {
- unsigned NumElem = cast<VectorType>(Val0->getType())->getNumElements();
- CondTy = VectorType::get(CondTy, NumElem);
+ if (isa<FixedVectorType>(Val0->getType()) && (getRandom() % 1)) {
+ unsigned NumElem =
+ cast<FixedVectorType>(Val0->getType())->getNumElements();
+ CondTy = FixedVectorType::get(CondTy, NumElem);
}
Value *Cond = getRandomValue(CondTy);
diff --git a/llvm/tools/llvm-strings/llvm-strings.cpp b/llvm/tools/llvm-strings/llvm-strings.cpp
new file mode 100644
index 0000000000000..51313d73401e2
--- /dev/null
+++ b/llvm/tools/llvm-strings/llvm-strings.cpp
@@ -0,0 +1,120 @@
+//===-- llvm-strings.cpp - Printable String dumping utility ---------------===//
+//
+// 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 is a utility that works like binutils "strings", that is, it
+// prints out printable strings in a binary, objdump, or archive file.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Object/Binary.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 <cctype>
+#include <string>
+
+using namespace llvm;
+using namespace llvm::object;
+
+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));
+
+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 cl::extrahelp
+ HelpResponse("\nPass @FILE as argument to read options from FILE.\n");
+
+static void strings(raw_ostream &OS, StringRef FileName, StringRef Contents) {
+ auto print = [&OS, FileName](unsigned Offset, StringRef L) {
+ if (L.size() < static_cast<size_t>(MinLength))
+ return;
+ if (PrintFileName)
+ OS << FileName << ": ";
+ switch (Radix) {
+ case none:
+ break;
+ case octal:
+ OS << format("%7o ", Offset);
+ break;
+ case hexadecimal:
+ OS << format("%7x ", Offset);
+ break;
+ case decimal:
+ OS << format("%7u ", Offset);
+ break;
+ }
+ OS << L << '\n';
+ };
+
+ const char *B = Contents.begin();
+ const char *P = nullptr, *E = nullptr, *S = nullptr;
+ for (P = Contents.begin(), E = Contents.end(); P < E; ++P) {
+ if (isPrint(*P) || *P == '\t') {
+ if (S == nullptr)
+ S = P;
+ } else if (S) {
+ print(S - B, StringRef(S, P - S));
+ S = nullptr;
+ }
+ }
+ if (S)
+ print(S - B, StringRef(S, E - S));
+}
+
+int main(int argc, char **argv) {
+ InitLLVM X(argc, argv);
+
+ cl::ParseCommandLineOptions(argc, argv, "llvm string dumper\n");
+ if (MinLength == 0) {
+ errs() << "invalid minimum string length 0\n";
+ return EXIT_FAILURE;
+ }
+
+ if (InputFileNames.empty())
+ InputFileNames.push_back("-");
+
+ for (const auto &File : InputFileNames) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer =
+ MemoryBuffer::getFileOrSTDIN(File);
+ if (std::error_code EC = Buffer.getError())
+ errs() << File << ": " << EC.message() << '\n';
+ else
+ strings(llvm::outs(), File == "-" ? "{standard input}" : File,
+ Buffer.get()->getMemBufferRef().getBuffer());
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp b/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp
index 96b2b72d8ba11..6a702c64a1053 100644
--- a/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp
+++ b/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp
@@ -71,13 +71,15 @@ static cl::alias
ClPrintInliningAliasInlines("inlines", cl::desc("Alias for -inlining"),
cl::NotHidden, cl::aliasopt(ClPrintInlining));
-// -basenames, -s
static cl::opt<bool> ClBasenames("basenames", cl::init(false),
cl::desc("Strip directory names from paths"));
static cl::alias ClBasenamesShort("s", cl::desc("Alias for -basenames"),
cl::NotHidden, cl::aliasopt(ClBasenames));
-// -demangle, -C, -no-demangle
+static cl::opt<bool>
+ ClRelativenames("relativenames", cl::init(false),
+ cl::desc("Strip the compilation directory from paths"));
+
static cl::opt<bool>
ClDemangle("demangle", cl::init(true), cl::desc("Demangle function names"));
static cl::alias
@@ -91,7 +93,6 @@ static cl::opt<std::string> ClDefaultArch("default-arch", cl::init(""),
cl::desc("Default architecture "
"(for multi-arch objects)"));
-// -obj, -exe, -e
static cl::opt<std::string>
ClBinaryName("obj", cl::init(""),
cl::desc("Path to object file to be symbolized (if not provided, "
@@ -112,7 +113,6 @@ ClDsymHint("dsym-hint", cl::ZeroOrMore,
cl::desc("Path to .dSYM bundles to search for debug info for the "
"object files"));
-// -print-address, -addresses, -a
static cl::opt<bool>
ClPrintAddress("print-address", cl::init(false),
cl::desc("Show address before line information"));
@@ -123,7 +123,6 @@ static cl::alias
ClPrintAddressAliasA("a", cl::desc("Alias for -print-address"),
cl::NotHidden, cl::aliasopt(ClPrintAddress), cl::Grouping);
-// -pretty-print, -p
static cl::opt<bool>
ClPrettyPrint("pretty-print", cl::init(false),
cl::desc("Make the output more human friendly"));
@@ -138,7 +137,6 @@ static cl::opt<int> ClPrintSourceContextLines(
static cl::opt<bool> ClVerbose("verbose", cl::init(false),
cl::desc("Print verbose line info"));
-// -adjust-vma
static cl::opt<uint64_t>
ClAdjustVMA("adjust-vma", cl::init(0), cl::value_desc("offset"),
cl::desc("Add specified offset to object file addresses"));
@@ -165,6 +163,10 @@ static cl::opt<DIPrinter::OutputStyle>
clEnumValN(DIPrinter::OutputStyle::GNU, "GNU",
"GNU addr2line style")));
+static cl::opt<bool>
+ ClUseNativePDBReader("use-native-pdb-reader", cl::init(0),
+ cl::desc("Use native PDB functionality"));
+
static cl::extrahelp
HelpResponse("\nPass @FILE as argument to read options from FILE.\n");
@@ -183,7 +185,7 @@ enum class Command {
Frame,
};
-static bool parseCommand(StringRef InputString, Command &Cmd,
+static bool parseCommand(bool IsAddr2Line, StringRef InputString, Command &Cmd,
std::string &ModuleName, uint64_t &ModuleOffset) {
const char kDelimiters[] = " \n\r";
ModuleName = "";
@@ -197,38 +199,44 @@ static bool parseCommand(StringRef InputString, Command &Cmd,
// If no cmd, assume it's CODE.
Cmd = Command::Code;
}
- const char *pos = InputString.data();
+ const char *Pos = InputString.data();
// Skip delimiters and parse input filename (if needed).
if (ClBinaryName.empty()) {
- pos += strspn(pos, kDelimiters);
- if (*pos == '"' || *pos == '\'') {
- char quote = *pos;
- pos++;
- const char *end = strchr(pos, quote);
- if (!end)
+ Pos += strspn(Pos, kDelimiters);
+ if (*Pos == '"' || *Pos == '\'') {
+ char Quote = *Pos;
+ Pos++;
+ const char *End = strchr(Pos, Quote);
+ if (!End)
return false;
- ModuleName = std::string(pos, end - pos);
- pos = end + 1;
+ ModuleName = std::string(Pos, End - Pos);
+ Pos = End + 1;
} else {
- int name_length = strcspn(pos, kDelimiters);
- ModuleName = std::string(pos, name_length);
- pos += name_length;
+ int NameLength = strcspn(Pos, kDelimiters);
+ ModuleName = std::string(Pos, NameLength);
+ Pos += NameLength;
}
} else {
ModuleName = ClBinaryName;
}
// Skip delimiters and parse module offset.
- pos += strspn(pos, kDelimiters);
- int offset_length = strcspn(pos, kDelimiters);
- return !StringRef(pos, offset_length).getAsInteger(0, ModuleOffset);
+ Pos += strspn(Pos, kDelimiters);
+ int OffsetLength = strcspn(Pos, kDelimiters);
+ StringRef Offset(Pos, OffsetLength);
+ // GNU addr2line assumes the offset is hexadecimal and allows a redundant
+ // "0x" or "0X" prefix; do the same for compatibility.
+ if (IsAddr2Line)
+ Offset.consume_front("0x") || Offset.consume_front("0X");
+ return !Offset.getAsInteger(IsAddr2Line ? 16 : 0, ModuleOffset);
}
-static void symbolizeInput(StringRef InputString, LLVMSymbolizer &Symbolizer,
- DIPrinter &Printer) {
+static void symbolizeInput(bool IsAddr2Line, StringRef InputString,
+ LLVMSymbolizer &Symbolizer, DIPrinter &Printer) {
Command Cmd;
std::string ModuleName;
uint64_t Offset = 0;
- if (!parseCommand(StringRef(InputString), Cmd, ModuleName, Offset)) {
+ if (!parseCommand(IsAddr2Line, StringRef(InputString), Cmd, ModuleName,
+ Offset)) {
outs() << InputString << "\n";
return;
}
@@ -309,6 +317,14 @@ int main(int argc, char **argv) {
Opts.FallbackDebugPath = ClFallbackDebugPath;
Opts.DWPName = ClDwpName;
Opts.DebugFileDirectory = ClDebugFileDirectory;
+ Opts.UseNativePDBReader = ClUseNativePDBReader;
+ Opts.PathStyle = DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath;
+ // If both --basenames and --relativenames are specified then pick the last
+ // one.
+ if (ClBasenames.getPosition() > ClRelativenames.getPosition())
+ Opts.PathStyle = DILineInfoSpecifier::FileLineInfoKind::BaseNameOnly;
+ else if (ClRelativenames)
+ Opts.PathStyle = DILineInfoSpecifier::FileLineInfoKind::RelativeFilePath;
for (const auto &hint : ClDsymHint) {
if (sys::path::extension(hint) == ".dSYM") {
@@ -322,7 +338,7 @@ int main(int argc, char **argv) {
DIPrinter Printer(outs(), ClPrintFunctions != FunctionNameKind::None,
ClPrettyPrint, ClPrintSourceContextLines, ClVerbose,
- ClBasenames, ClOutputStyle);
+ ClOutputStyle);
if (ClInputAddresses.empty()) {
const int kMaxInputStringLength = 1024;
@@ -335,12 +351,12 @@ int main(int argc, char **argv) {
std::remove_if(StrippedInputString.begin(), StrippedInputString.end(),
[](char c) { return c == '\r' || c == '\n'; }),
StrippedInputString.end());
- symbolizeInput(StrippedInputString, Symbolizer, Printer);
+ symbolizeInput(IsAddr2Line, StrippedInputString, Symbolizer, Printer);
outs().flush();
}
} else {
for (StringRef Address : ClInputAddresses)
- symbolizeInput(Address, Symbolizer, Printer);
+ symbolizeInput(IsAddr2Line, Address, Symbolizer, Printer);
}
return 0;
diff --git a/llvm/tools/llvm-xray/trie-node.h b/llvm/tools/llvm-xray/trie-node.h
index 47d4b8f1e78c8..7bff81473b5df 100644
--- a/llvm/tools/llvm-xray/trie-node.h
+++ b/llvm/tools/llvm-xray/trie-node.h
@@ -48,7 +48,7 @@ template <typename T, typename Callable>
TrieNode<T> *
mergeTrieNodes(const TrieNode<T> &Left, const TrieNode<T> &Right,
/*Non-deduced pointer type for nullptr compatibility*/
- typename std::remove_reference<TrieNode<T> *>::type NewParent,
+ std::remove_reference_t<TrieNode<T> *> NewParent,
std::forward_list<TrieNode<T>> &NodeStore,
Callable &&MergeCallable) {
llvm::function_ref<T(const T &, const T &)> MergeFn(
diff --git a/llvm/tools/llvm-xray/xray-color-helper.cpp b/llvm/tools/llvm-xray/xray-color-helper.cpp
index c09cad3ba7d24..ea7ff357826bf 100644
--- a/llvm/tools/llvm-xray/xray-color-helper.cpp
+++ b/llvm/tools/llvm-xray/xray-color-helper.cpp
@@ -208,8 +208,8 @@ ColorHelper::getColorTuple(double Point) const {
// string.
std::string
ColorHelper::getColorString(std::tuple<uint8_t, uint8_t, uint8_t> t) {
- return llvm::formatv("#{0:X-2}{1:X-2}{2:X-2}", std::get<0>(t), std::get<1>(t),
- std::get<2>(t));
+ return std::string(llvm::formatv("#{0:X-2}{1:X-2}{2:X-2}", std::get<0>(t),
+ std::get<1>(t), std::get<2>(t)));
}
// Gets a color in a gradient given a number in the interval [0,1], it does this
diff --git a/llvm/tools/llvm-xray/xray-extract.cpp b/llvm/tools/llvm-xray/xray-extract.cpp
index af9255af21c35..8304d2d27afa3 100644
--- a/llvm/tools/llvm-xray/xray-extract.cpp
+++ b/llvm/tools/llvm-xray/xray-extract.cpp
@@ -45,6 +45,11 @@ static cl::opt<bool> ExtractSymbolize("symbolize", cl::value_desc("symbolize"),
cl::sub(Extract));
static cl::alias ExtractSymbolize2("s", cl::aliasopt(ExtractSymbolize),
cl::desc("alias for -symbolize"));
+static cl::opt<bool> ExtractNoDemangle("no-demangle",
+ cl::value_desc("no-demangle"),
+ cl::init(false),
+ cl::desc("don't demangle symbols"),
+ cl::sub(Extract));
namespace {
@@ -58,9 +63,9 @@ void exportAsYAML(const InstrumentationMap &Map, raw_ostream &OS,
auto FuncId = Map.getFunctionId(Sled.Function);
if (!FuncId)
return;
- YAMLSleds.push_back({*FuncId, Sled.Address, Sled.Function, Sled.Kind,
- Sled.AlwaysInstrument,
- ExtractSymbolize ? FH.SymbolOrNumber(*FuncId) : ""});
+ YAMLSleds.push_back(
+ {*FuncId, Sled.Address, Sled.Function, Sled.Kind, Sled.AlwaysInstrument,
+ ExtractSymbolize ? FH.SymbolOrNumber(*FuncId) : "", Sled.Version});
}
Output Out(OS, nullptr, 0);
Out << YAMLSleds;
@@ -84,7 +89,10 @@ static CommandRegistration Unused(&Extract, []() -> Error {
Twine("Cannot open file '") + ExtractOutput + "' for writing.", EC);
const auto &FunctionAddresses =
InstrumentationMapOrError->getFunctionAddresses();
- symbolize::LLVMSymbolizer Symbolizer;
+ symbolize::LLVMSymbolizer::Options opts;
+ if (ExtractNoDemangle)
+ opts.Demangle = false;
+ symbolize::LLVMSymbolizer Symbolizer(opts);
llvm::xray::FuncIdConversionHelper FuncIdHelper(ExtractInput, Symbolizer,
FunctionAddresses);
exportAsYAML(*InstrumentationMapOrError, OS, FuncIdHelper);
diff --git a/llvm/tools/llvm-xray/xray-graph-diff.cpp b/llvm/tools/llvm-xray/xray-graph-diff.cpp
index a1bca326930e5..11210e2004a7a 100644
--- a/llvm/tools/llvm-xray/xray-graph-diff.cpp
+++ b/llvm/tools/llvm-xray/xray-graph-diff.cpp
@@ -314,7 +314,7 @@ static std::string getLabel(const GraphDiffRenderer::GraphT::EdgeValueType &E,
const auto &RightStat = EdgeAttr.CorrEdgePtr[1]->second.S;
double RelDiff = statRelDiff(LeftStat, RightStat, EL);
- return formatv(R"({0:P})", RelDiff);
+ return std::string(formatv(R"({0:P})", RelDiff));
}
}
@@ -324,17 +324,19 @@ static std::string getLabel(const GraphDiffRenderer::GraphT::VertexValueType &V,
const auto &VertexAttr = V.second;
switch (VL) {
case GraphDiffRenderer::StatType::NONE:
- return formatv(R"({0})", truncateString(VertexId, TrunLen).str());
+ return std::string(
+ formatv(R"({0})", truncateString(VertexId, TrunLen).str()));
default:
if (containsNullptr(VertexAttr.CorrVertexPtr))
- return formatv(R"({0})", truncateString(VertexId, TrunLen).str());
+ return std::string(
+ formatv(R"({0})", truncateString(VertexId, TrunLen).str()));
const auto &LeftStat = VertexAttr.CorrVertexPtr[0]->second.S;
const auto &RightStat = VertexAttr.CorrVertexPtr[1]->second.S;
double RelDiff = statRelDiff(LeftStat, RightStat, VL);
- return formatv(R"({{{0}|{1:P}})", truncateString(VertexId, TrunLen).str(),
- RelDiff);
+ return std::string(formatv(
+ R"({{{0}|{1:P}})", truncateString(VertexId, TrunLen).str(), RelDiff));
}
}
diff --git a/llvm/tools/llvm-xray/xray-graph.cpp b/llvm/tools/llvm-xray/xray-graph.cpp
index f836f9ba54fce..522609b938f2d 100644
--- a/llvm/tools/llvm-xray/xray-graph.cpp
+++ b/llvm/tools/llvm-xray/xray-graph.cpp
@@ -163,6 +163,30 @@ static void updateStat(GraphRenderer::TimeStat &S, int64_t L) {
S.Sum += L;
}
+// Labels in a DOT graph must be legal XML strings so it's necessary to escape
+// certain characters.
+static std::string escapeString(StringRef Label) {
+ std::string Str;
+ Str.reserve(Label.size());
+ for (const auto C : Label) {
+ switch (C) {
+ case '&':
+ Str.append("&amp;");
+ break;
+ case '<':
+ Str.append("&lt;");
+ break;
+ case '>':
+ Str.append("&gt;");
+ break;
+ default:
+ Str.push_back(C);
+ break;
+ }
+ }
+ return Str;
+}
+
// Evaluates an XRay record and performs accounting on it.
//
// If the record is an ENTER record it pushes the FuncID and TSC onto a
@@ -398,8 +422,9 @@ void GraphRenderer::exportGraphAsDOT(raw_ostream &OS, StatType ET, StatType EC,
if (V.first == 0)
continue;
OS << "F" << V.first << " [label=\"" << (VT != StatType::NONE ? "{" : "")
- << (VA.SymbolName.size() > 40 ? VA.SymbolName.substr(0, 40) + "..."
- : VA.SymbolName);
+ << escapeString(VA.SymbolName.size() > 40
+ ? VA.SymbolName.substr(0, 40) + "..."
+ : VA.SymbolName);
if (VT != StatType::NONE)
OS << "|" << VA.S.getString(VT) << "}\"";
else
diff --git a/llvm/tools/llvm-xray/xray-stacks.cpp b/llvm/tools/llvm-xray/xray-stacks.cpp
index cf292887b6b8a..1e4490289534d 100644
--- a/llvm/tools/llvm-xray/xray-stacks.cpp
+++ b/llvm/tools/llvm-xray/xray-stacks.cpp
@@ -674,11 +674,12 @@ std::string CreateErrorMessage(StackTrie::AccountRecordStatus Error,
const FuncIdConversionHelper &Converter) {
switch (Error) {
case StackTrie::AccountRecordStatus::ENTRY_NOT_FOUND:
- return formatv("Found record {0} with no matching function entry\n",
- format_xray_record(Record, Converter));
+ return std::string(
+ formatv("Found record {0} with no matching function entry\n",
+ format_xray_record(Record, Converter)));
default:
- return formatv("Unknown error type for record {0}\n",
- format_xray_record(Record, Converter));
+ return std::string(formatv("Unknown error type for record {0}\n",
+ format_xray_record(Record, Converter)));
}
}
diff --git a/llvm/tools/opt/AnalysisWrappers.cpp b/llvm/tools/opt/AnalysisWrappers.cpp
index b888605a516c7..2ae1da84a9a0f 100644
--- a/llvm/tools/opt/AnalysisWrappers.cpp
+++ b/llvm/tools/opt/AnalysisWrappers.cpp
@@ -17,7 +17,6 @@
//===----------------------------------------------------------------------===//
#include "llvm/Analysis/CallGraph.h"
-#include "llvm/IR/CallSite.h"
#include "llvm/IR/Module.h"
#include "llvm/Pass.h"
#include "llvm/Support/raw_ostream.h"
@@ -40,11 +39,11 @@ namespace {
Instruction *UI = dyn_cast<Instruction>(U);
if (!UI) continue;
- CallSite CS(cast<Value>(UI));
- if (!CS) continue;
+ CallBase *CB = dyn_cast<CallBase>(UI);
+ if (!CB)
+ continue;
- for (CallSite::arg_iterator AI = CS.arg_begin(),
- E = CS.arg_end(); AI != E; ++AI) {
+ for (auto AI = CB->arg_begin(), E = CB->arg_end(); AI != E; ++AI) {
if (!isa<Constant>(*AI)) continue;
if (!PrintedFn) {
diff --git a/llvm/tools/opt/NewPMDriver.cpp b/llvm/tools/opt/NewPMDriver.cpp
index ac04a32d93fd5..b94c58decdda2 100644
--- a/llvm/tools/opt/NewPMDriver.cpp
+++ b/llvm/tools/opt/NewPMDriver.cpp
@@ -14,6 +14,7 @@
#include "NewPMDriver.h"
#include "PassPrinters.h"
+#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Analysis/CGSCCPassManager.h"
@@ -100,6 +101,9 @@ static cl::opt<std::string> OptimizerLastEPPipeline(
"the OptimizerLast extension point into default pipelines"),
cl::Hidden);
+// Individual pipeline tuning options.
+extern cl::opt<bool> DisableLoopUnrolling;
+
extern cl::opt<PGOKind> PGOKindFlag;
extern cl::opt<std::string> ProfileFile;
extern cl::opt<CSPGOKind> CSPGOKindFlag;
@@ -194,7 +198,7 @@ static void registerEPCallbacks(PassBuilder &PB, bool VerifyEachPass,
});
if (tryParsePipelineText<FunctionPassManager>(PB, OptimizerLastEPPipeline))
PB.registerOptimizerLastEPCallback(
- [&PB, VerifyEachPass, DebugLogging](FunctionPassManager &PM,
+ [&PB, VerifyEachPass, DebugLogging](ModulePassManager &PM,
PassBuilder::OptimizationLevel) {
ExitOnError Err("Unable to parse OptimizerLastEP pipeline: ");
Err(PB.parsePassPipeline(PM, OptimizerLastEPPipeline, VerifyEachPass,
@@ -209,57 +213,63 @@ static void registerEPCallbacks(PassBuilder &PB, bool VerifyEachPass,
bool llvm::runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM,
ToolOutputFile *Out, ToolOutputFile *ThinLTOLinkOut,
ToolOutputFile *OptRemarkFile,
- StringRef PassPipeline, OutputKind OK,
- VerifierKind VK,
+ StringRef PassPipeline, ArrayRef<StringRef> Passes,
+ OutputKind OK, VerifierKind VK,
bool ShouldPreserveAssemblyUseListOrder,
bool ShouldPreserveBitcodeUseListOrder,
bool EmitSummaryIndex, bool EmitModuleHash,
- bool EnableDebugify) {
+ bool EnableDebugify, bool Coroutines) {
bool VerifyEachPass = VK == VK_VerifyEachPass;
Optional<PGOOptions> P;
switch (PGOKindFlag) {
- case InstrGen:
- P = PGOOptions(ProfileFile, "", "", PGOOptions::IRInstr);
- break;
- case InstrUse:
- P = PGOOptions(ProfileFile, "", ProfileRemappingFile, PGOOptions::IRUse);
- break;
- case SampleUse:
- P = PGOOptions(ProfileFile, "", ProfileRemappingFile,
- PGOOptions::SampleUse);
- break;
- case NoPGO:
- if (DebugInfoForProfiling)
- P = PGOOptions("", "", "", PGOOptions::NoAction, PGOOptions::NoCSAction,
- true);
- else
- P = None;
- }
- if (CSPGOKindFlag != NoCSPGO) {
- if (P && (P->Action == PGOOptions::IRInstr ||
- P->Action == PGOOptions::SampleUse))
- errs() << "CSPGOKind cannot be used with IRInstr or SampleUse";
- if (CSPGOKindFlag == CSInstrGen) {
- if (CSProfileGenFile.empty())
- errs() << "CSInstrGen needs to specify CSProfileGenFile";
- if (P) {
- P->CSAction = PGOOptions::CSIRInstr;
- P->CSProfileGenFile = CSProfileGenFile;
- } else
- P = PGOOptions("", CSProfileGenFile, ProfileRemappingFile,
- PGOOptions::NoAction, PGOOptions::CSIRInstr);
- } else /* CSPGOKindFlag == CSInstrUse */ {
- if (!P)
- errs() << "CSInstrUse needs to be together with InstrUse";
- P->CSAction = PGOOptions::CSIRUse;
- }
+ case InstrGen:
+ P = PGOOptions(ProfileFile, "", "", PGOOptions::IRInstr);
+ break;
+ case InstrUse:
+ P = PGOOptions(ProfileFile, "", ProfileRemappingFile, PGOOptions::IRUse);
+ break;
+ case SampleUse:
+ P = PGOOptions(ProfileFile, "", ProfileRemappingFile,
+ PGOOptions::SampleUse);
+ break;
+ case NoPGO:
+ if (DebugInfoForProfiling)
+ P = PGOOptions("", "", "", PGOOptions::NoAction, PGOOptions::NoCSAction,
+ true);
+ else
+ P = None;
+ }
+ if (CSPGOKindFlag != NoCSPGO) {
+ if (P && (P->Action == PGOOptions::IRInstr ||
+ P->Action == PGOOptions::SampleUse))
+ errs() << "CSPGOKind cannot be used with IRInstr or SampleUse";
+ if (CSPGOKindFlag == CSInstrGen) {
+ if (CSProfileGenFile.empty())
+ errs() << "CSInstrGen needs to specify CSProfileGenFile";
+ if (P) {
+ P->CSAction = PGOOptions::CSIRInstr;
+ P->CSProfileGenFile = CSProfileGenFile;
+ } else
+ P = PGOOptions("", CSProfileGenFile, ProfileRemappingFile,
+ PGOOptions::NoAction, PGOOptions::CSIRInstr);
+ } else /* CSPGOKindFlag == CSInstrUse */ {
+ if (!P)
+ errs() << "CSInstrUse needs to be together with InstrUse";
+ P->CSAction = PGOOptions::CSIRUse;
}
+ }
PassInstrumentationCallbacks PIC;
StandardInstrumentations SI;
SI.registerCallbacks(PIC);
- PassBuilder PB(TM, PipelineTuningOptions(), P, &PIC);
+ PipelineTuningOptions PTO;
+ // LoopUnrolling defaults on to true and DisableLoopUnrolling is initialized
+ // 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;
+ PassBuilder PB(TM, PTO, P, &PIC);
registerEPCallbacks(PB, VerifyEachPass, DebugPM);
// Load requested pass plugins and let them register pass builder callbacks
@@ -295,9 +305,26 @@ bool llvm::runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM,
// Specially handle the alias analysis manager so that we can register
// a custom pipeline of AA passes with it.
AAManager AA;
- if (auto Err = PB.parseAAPipeline(AA, AAPipeline)) {
- errs() << Arg0 << ": " << toString(std::move(Err)) << "\n";
- return false;
+ if (!AAPipeline.empty()) {
+ assert(Passes.empty() &&
+ "--aa-pipeline and -foo-pass should not both be specified");
+ if (auto Err = PB.parseAAPipeline(AA, AAPipeline)) {
+ errs() << Arg0 << ": " << toString(std::move(Err)) << "\n";
+ return false;
+ }
+ }
+ // For compatibility with legacy pass manager.
+ // Alias analyses are not specially specified when using the legacy PM.
+ SmallVector<StringRef, 4> NonAAPasses;
+ for (auto PassName : Passes) {
+ if (PB.isAAPassName(PassName)) {
+ if (auto Err = PB.parseAAPipeline(AA, PassName)) {
+ errs() << Arg0 << ": " << toString(std::move(Err)) << "\n";
+ return false;
+ }
+ } else {
+ NonAAPasses.push_back(PassName);
+ }
}
LoopAnalysisManager LAM(DebugPM);
@@ -321,10 +348,24 @@ bool llvm::runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM,
if (EnableDebugify)
MPM.addPass(NewPMDebugifyPass());
- if (auto Err =
- PB.parsePassPipeline(MPM, PassPipeline, VerifyEachPass, DebugPM)) {
- errs() << Arg0 << ": " << toString(std::move(Err)) << "\n";
- return false;
+ if (!PassPipeline.empty()) {
+ assert(Passes.empty() &&
+ "PassPipeline and Passes should not both contain passes");
+ if (auto Err =
+ PB.parsePassPipeline(MPM, PassPipeline, VerifyEachPass, DebugPM)) {
+ errs() << Arg0 << ": " << toString(std::move(Err)) << "\n";
+ return false;
+ }
+ }
+ for (auto PassName : NonAAPasses) {
+ std::string ModifiedPassName(PassName.begin(), PassName.end());
+ if (PB.isAnalysisPassName(PassName))
+ ModifiedPassName = "require<" + ModifiedPassName + ">";
+ if (auto Err = PB.parsePassPipeline(MPM, ModifiedPassName, VerifyEachPass,
+ DebugPM)) {
+ errs() << Arg0 << ": " << toString(std::move(Err)) << "\n";
+ return false;
+ }
}
if (VK > VK_NoVerifier)
diff --git a/llvm/tools/opt/NewPMDriver.h b/llvm/tools/opt/NewPMDriver.h
index b672c97c9aa35..7ae273a2c1f46 100644
--- a/llvm/tools/opt/NewPMDriver.h
+++ b/llvm/tools/opt/NewPMDriver.h
@@ -20,9 +20,10 @@
#ifndef LLVM_TOOLS_OPT_NEWPMDRIVER_H
#define LLVM_TOOLS_OPT_NEWPMDRIVER_H
+#include "llvm/ADT/ArrayRef.h"
+
namespace llvm {
class StringRef;
-class LLVMContext;
class Module;
class TargetMachine;
class ToolOutputFile;
@@ -60,11 +61,12 @@ enum CSPGOKind { NoCSPGO, CSInstrGen, CSInstrUse };
bool runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM,
ToolOutputFile *Out, ToolOutputFile *ThinLinkOut,
ToolOutputFile *OptRemarkFile, StringRef PassPipeline,
- opt_tool::OutputKind OK, opt_tool::VerifierKind VK,
+ ArrayRef<StringRef> PassInfos, opt_tool::OutputKind OK,
+ opt_tool::VerifierKind VK,
bool ShouldPreserveAssemblyUseListOrder,
bool ShouldPreserveBitcodeUseListOrder,
bool EmitSummaryIndex, bool EmitModuleHash,
- bool EnableDebugify);
+ bool EnableDebugify, bool Coroutines);
} // namespace llvm
#endif
diff --git a/llvm/tools/opt/PassPrinters.cpp b/llvm/tools/opt/PassPrinters.cpp
index a877d9dc90f48..4e81b5d29c4d6 100644
--- a/llvm/tools/opt/PassPrinters.cpp
+++ b/llvm/tools/opt/PassPrinters.cpp
@@ -33,18 +33,16 @@ struct FunctionPassPrinter : public FunctionPass {
raw_ostream &Out;
static char ID;
std::string PassName;
- bool QuietPass;
- FunctionPassPrinter(const PassInfo *PI, raw_ostream &out, bool Quiet)
- : FunctionPass(ID), PassToPrint(PI), Out(out), QuietPass(Quiet) {
- std::string PassToPrintName = PassToPrint->getPassName();
+ FunctionPassPrinter(const PassInfo *PI, raw_ostream &out)
+ : FunctionPass(ID), PassToPrint(PI), Out(out) {
+ std::string PassToPrintName = std::string(PassToPrint->getPassName());
PassName = "FunctionPass Printer: " + PassToPrintName;
}
bool runOnFunction(Function &F) override {
- if (!QuietPass)
- Out << "Printing analysis '" << PassToPrint->getPassName()
- << "' for function '" << F.getName() << "':\n";
+ Out << "Printing analysis '" << PassToPrint->getPassName()
+ << "' for function '" << F.getName() << "':\n";
// Get and print pass...
getAnalysisID<Pass>(PassToPrint->getTypeInfo()).print(Out, F.getParent());
@@ -66,17 +64,15 @@ struct CallGraphSCCPassPrinter : public CallGraphSCCPass {
const PassInfo *PassToPrint;
raw_ostream &Out;
std::string PassName;
- bool QuietPass;
- CallGraphSCCPassPrinter(const PassInfo *PI, raw_ostream &out, bool Quiet)
- : CallGraphSCCPass(ID), PassToPrint(PI), Out(out), QuietPass(Quiet) {
- std::string PassToPrintName = PassToPrint->getPassName();
+ CallGraphSCCPassPrinter(const PassInfo *PI, raw_ostream &out)
+ : CallGraphSCCPass(ID), PassToPrint(PI), Out(out) {
+ std::string PassToPrintName = std::string(PassToPrint->getPassName());
PassName = "CallGraphSCCPass Printer: " + PassToPrintName;
}
bool runOnSCC(CallGraphSCC &SCC) override {
- if (!QuietPass)
- Out << "Printing analysis '" << PassToPrint->getPassName() << "':\n";
+ Out << "Printing analysis '" << PassToPrint->getPassName() << "':\n";
// Get and print pass...
for (CallGraphSCC::iterator I = SCC.begin(), E = SCC.end(); I != E; ++I) {
@@ -103,17 +99,15 @@ struct ModulePassPrinter : public ModulePass {
const PassInfo *PassToPrint;
raw_ostream &Out;
std::string PassName;
- bool QuietPass;
- ModulePassPrinter(const PassInfo *PI, raw_ostream &out, bool Quiet)
- : ModulePass(ID), PassToPrint(PI), Out(out), QuietPass(Quiet) {
- std::string PassToPrintName = PassToPrint->getPassName();
+ ModulePassPrinter(const PassInfo *PI, raw_ostream &out)
+ : ModulePass(ID), PassToPrint(PI), Out(out) {
+ std::string PassToPrintName = std::string(PassToPrint->getPassName());
PassName = "ModulePass Printer: " + PassToPrintName;
}
bool runOnModule(Module &M) override {
- if (!QuietPass)
- Out << "Printing analysis '" << PassToPrint->getPassName() << "':\n";
+ Out << "Printing analysis '" << PassToPrint->getPassName() << "':\n";
// Get and print pass...
getAnalysisID<Pass>(PassToPrint->getTypeInfo()).print(Out, &M);
@@ -135,17 +129,15 @@ struct LoopPassPrinter : public LoopPass {
const PassInfo *PassToPrint;
raw_ostream &Out;
std::string PassName;
- bool QuietPass;
- LoopPassPrinter(const PassInfo *PI, raw_ostream &out, bool Quiet)
- : LoopPass(ID), PassToPrint(PI), Out(out), QuietPass(Quiet) {
- std::string PassToPrintName = PassToPrint->getPassName();
+ LoopPassPrinter(const PassInfo *PI, raw_ostream &out)
+ : LoopPass(ID), PassToPrint(PI), Out(out) {
+ std::string PassToPrintName = std::string(PassToPrint->getPassName());
PassName = "LoopPass Printer: " + PassToPrintName;
}
bool runOnLoop(Loop *L, LPPassManager &LPM) override {
- if (!QuietPass)
- Out << "Printing analysis '" << PassToPrint->getPassName() << "':\n";
+ Out << "Printing analysis '" << PassToPrint->getPassName() << "':\n";
// Get and print pass...
getAnalysisID<Pass>(PassToPrint->getTypeInfo())
@@ -168,20 +160,17 @@ struct RegionPassPrinter : public RegionPass {
const PassInfo *PassToPrint;
raw_ostream &Out;
std::string PassName;
- bool QuietPass;
- RegionPassPrinter(const PassInfo *PI, raw_ostream &out, bool Quiet)
- : RegionPass(ID), PassToPrint(PI), Out(out), QuietPass(Quiet) {
- std::string PassToPrintName = PassToPrint->getPassName();
+ RegionPassPrinter(const PassInfo *PI, raw_ostream &out)
+ : RegionPass(ID), PassToPrint(PI), Out(out) {
+ std::string PassToPrintName = std::string(PassToPrint->getPassName());
PassName = "RegionPass Printer: " + PassToPrintName;
}
bool runOnRegion(Region *R, RGPassManager &RGM) override {
- if (!QuietPass) {
- Out << "Printing analysis '" << PassToPrint->getPassName() << "' for "
- << "region: '" << R->getNameStr() << "' in function '"
- << R->getEntry()->getParent()->getName() << "':\n";
- }
+ Out << "Printing analysis '" << PassToPrint->getPassName() << "' for "
+ << "region: '" << R->getNameStr() << "' in function '"
+ << R->getEntry()->getParent()->getName() << "':\n";
// Get and print pass...
getAnalysisID<Pass>(PassToPrint->getTypeInfo())
.print(Out, R->getEntry()->getParent()->getParent());
@@ -201,28 +190,23 @@ char RegionPassPrinter::ID = 0;
} // end anonymous namespace
FunctionPass *llvm::createFunctionPassPrinter(const PassInfo *PI,
- raw_ostream &OS, bool Quiet) {
- return new FunctionPassPrinter(PI, OS, Quiet);
+ raw_ostream &OS) {
+ return new FunctionPassPrinter(PI, OS);
}
CallGraphSCCPass *llvm::createCallGraphPassPrinter(const PassInfo *PI,
- raw_ostream &OS,
- bool Quiet) {
- return new CallGraphSCCPassPrinter(PI, OS, Quiet);
+ raw_ostream &OS) {
+ return new CallGraphSCCPassPrinter(PI, OS);
}
-ModulePass *llvm::createModulePassPrinter(const PassInfo *PI, raw_ostream &OS,
- bool Quiet) {
- return new ModulePassPrinter(PI, OS, Quiet);
+ModulePass *llvm::createModulePassPrinter(const PassInfo *PI, raw_ostream &OS) {
+ return new ModulePassPrinter(PI, OS);
}
-LoopPass *llvm::createLoopPassPrinter(const PassInfo *PI, raw_ostream &OS,
- bool Quiet) {
- return new LoopPassPrinter(PI, OS, Quiet);
+LoopPass *llvm::createLoopPassPrinter(const PassInfo *PI, raw_ostream &OS) {
+ return new LoopPassPrinter(PI, OS);
}
-RegionPass *llvm::createRegionPassPrinter(const PassInfo *PI, raw_ostream &OS,
- bool Quiet) {
- return new RegionPassPrinter(PI, OS, Quiet);
+RegionPass *llvm::createRegionPassPrinter(const PassInfo *PI, raw_ostream &OS) {
+ return new RegionPassPrinter(PI, OS);
}
-
diff --git a/llvm/tools/opt/PassPrinters.h b/llvm/tools/opt/PassPrinters.h
index 692befbdae758..a4e1921399fc6 100644
--- a/llvm/tools/opt/PassPrinters.h
+++ b/llvm/tools/opt/PassPrinters.h
@@ -14,8 +14,6 @@
#ifndef LLVM_TOOLS_OPT_PASSPRINTERS_H
#define LLVM_TOOLS_OPT_PASSPRINTERS_H
-#include "llvm/IR/PassManager.h"
-
namespace llvm {
class CallGraphSCCPass;
@@ -25,22 +23,17 @@ class LoopPass;
class PassInfo;
class raw_ostream;
class RegionPass;
-class Module;
-FunctionPass *createFunctionPassPrinter(const PassInfo *PI, raw_ostream &out,
- bool Quiet);
+FunctionPass *createFunctionPassPrinter(const PassInfo *PI, raw_ostream &out);
CallGraphSCCPass *createCallGraphPassPrinter(const PassInfo *PI,
- raw_ostream &out, bool Quiet);
+ raw_ostream &out);
-ModulePass *createModulePassPrinter(const PassInfo *PI, raw_ostream &out,
- bool Quiet);
+ModulePass *createModulePassPrinter(const PassInfo *PI, raw_ostream &out);
-LoopPass *createLoopPassPrinter(const PassInfo *PI, raw_ostream &out,
- bool Quiet);
+LoopPass *createLoopPassPrinter(const PassInfo *PI, raw_ostream &out);
-RegionPass *createRegionPassPrinter(const PassInfo *PI, raw_ostream &out,
- bool Quiet);
+RegionPass *createRegionPassPrinter(const PassInfo *PI, raw_ostream &out);
} // end namespace llvm
diff --git a/llvm/tools/opt/PrintSCC.cpp b/llvm/tools/opt/PrintSCC.cpp
index 419886d6cc60a..1ca52745ff400 100644
--- a/llvm/tools/opt/PrintSCC.cpp
+++ b/llvm/tools/opt/PrintSCC.cpp
@@ -76,10 +76,11 @@ bool CFGSCC::runOnFunction(Function &F) {
for (scc_iterator<Function*> SCCI = scc_begin(&F); !SCCI.isAtEnd(); ++SCCI) {
const std::vector<BasicBlock *> &nextSCC = *SCCI;
errs() << "\nSCC #" << ++sccNum << " : ";
- for (std::vector<BasicBlock*>::const_iterator I = nextSCC.begin(),
- E = nextSCC.end(); I != E; ++I)
- errs() << (*I)->getName() << ", ";
- if (nextSCC.size() == 1 && SCCI.hasLoop())
+ for (BasicBlock *BB : nextSCC) {
+ BB->printAsOperand(errs(), false);
+ errs() << ", ";
+ }
+ if (nextSCC.size() == 1 && SCCI.hasCycle())
errs() << " (Has self-loop).";
}
errs() << "\n";
@@ -101,7 +102,7 @@ bool CallGraphSCC::runOnModule(Module &M) {
E = nextSCC.end(); I != E; ++I)
errs() << ((*I)->getFunction() ? (*I)->getFunction()->getName()
: "external node") << ", ";
- if (nextSCC.size() == 1 && SCCI.hasLoop())
+ if (nextSCC.size() == 1 && SCCI.hasCycle())
errs() << " (Has self-loop).";
}
errs() << "\n";
diff --git a/llvm/tools/opt/opt.cpp b/llvm/tools/opt/opt.cpp
index 75a6cdc3892b3..c250eefb8c430 100644
--- a/llvm/tools/opt/opt.cpp
+++ b/llvm/tools/opt/opt.cpp
@@ -21,18 +21,19 @@
#include "llvm/Analysis/RegionPass.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/TargetTransformInfo.h"
+#include "llvm/AsmParser/Parser.h"
#include "llvm/Bitcode/BitcodeWriterPass.h"
-#include "llvm/CodeGen/CommandFlags.inc"
+#include "llvm/CodeGen/CommandFlags.h"
#include "llvm/CodeGen/TargetPassConfig.h"
#include "llvm/Config/llvm-config.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DebugInfo.h"
#include "llvm/IR/IRPrintingPasses.h"
#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/LLVMRemarkStreamer.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/LegacyPassNameParser.h"
#include "llvm/IR/Module.h"
-#include "llvm/IR/RemarkStreamer.h"
#include "llvm/IR/Verifier.h"
#include "llvm/IRReader/IRReader.h"
#include "llvm/InitializePasses.h"
@@ -54,6 +55,7 @@
#include "llvm/Transforms/Coroutines.h"
#include "llvm/Transforms/IPO/AlwaysInliner.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
+#include "llvm/Transforms/IPO/WholeProgramDevirt.h"
#include "llvm/Transforms/Utils/Cloning.h"
#include "llvm/Transforms/Utils/Debugify.h"
#include <algorithm>
@@ -61,12 +63,17 @@
using namespace llvm;
using namespace opt_tool;
+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::opt<bool> EnableNewPassManager(
+ "enable-new-pm", cl::desc("Enable the new pass manager"), cl::init(false));
+
// 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
// infrastructure, completely disabling all of the flags specific to the old
@@ -115,8 +122,12 @@ static cl::opt<std::string> ThinLinkBitcodeFile(
static cl::opt<bool>
NoVerify("disable-verify", cl::desc("Do not run the verifier"), cl::Hidden);
-static cl::opt<bool>
-VerifyEach("verify-each", cl::desc("Verify after each transform"));
+static cl::opt<bool> NoUpgradeDebugInfo("disable-upgrade-debug-info",
+ cl::desc("Generate invalid output"),
+ cl::ReallyHidden);
+
+static cl::opt<bool> VerifyEach("verify-each",
+ cl::desc("Verify after each transform"));
static cl::opt<bool>
DisableDITypeMap("disable-debug-info-type-map",
@@ -172,15 +183,9 @@ CodeGenOptLevel("codegen-opt-level",
static cl::opt<std::string>
TargetTriple("mtriple", cl::desc("Override target triple for module"));
-static cl::opt<bool>
-DisableLoopUnrolling("disable-loop-unrolling",
- cl::desc("Disable loop unrolling in all relevant passes"),
- cl::init(false));
-
-static cl::opt<bool>
-DisableSLPVectorization("disable-slp-vectorization",
- cl::desc("Disable the slp vectorization pass"),
- cl::init(false));
+cl::opt<bool> DisableLoopUnrolling(
+ "disable-loop-unrolling",
+ cl::desc("Disable loop unrolling in all relevant passes"), cl::init(false));
static cl::opt<bool> EmitSummaryIndex("module-summary",
cl::desc("Emit module summary index"),
@@ -198,13 +203,6 @@ DisableBuiltins("disable-builtin",
cl::desc("Disable specific target library builtin function"),
cl::ZeroOrMore);
-
-static cl::opt<bool>
-Quiet("q", cl::desc("Obsolete option"), cl::Hidden);
-
-static cl::alias
-QuietA("quiet", cl::desc("Alias for -q"), cl::aliasopt(Quiet));
-
static cl::opt<bool>
AnalyzeOnly("analyze", cl::desc("Only perform analysis, no optimization"));
@@ -257,6 +255,20 @@ static cl::opt<bool> Coroutines(
cl::desc("Enable coroutine passes."),
cl::init(false), cl::Hidden);
+static cl::opt<bool> TimeTrace(
+ "time-trace",
+ cl::desc("Record time trace"));
+
+static cl::opt<unsigned> TimeTraceGranularity(
+ "time-trace-granularity",
+ cl::desc("Minimum time granularity (in microseconds) traced by time profiler"),
+ cl::init(500), cl::Hidden);
+
+static cl::opt<std::string>
+ TimeTraceFile("time-trace-file",
+ cl::desc("Specify time trace file destination"),
+ cl::value_desc("filename"));
+
static cl::opt<bool> RemarksWithHotness(
"pass-remarks-with-hotness",
cl::desc("With PGO, include profile count in optimization remarks"),
@@ -389,18 +401,9 @@ static void AddOptimizationPasses(legacy::PassManagerBase &MPM,
Builder.DisableUnrollLoops = (DisableLoopUnrolling.getNumOccurrences() > 0) ?
DisableLoopUnrolling : OptLevel == 0;
- // Check if vectorization is explicitly disabled via -vectorize-loops=false.
- // The flag enables vectorization in the LoopVectorize pass, it is on by
- // default, and if it was disabled, leave it disabled here.
- // Another flag that exists: -loop-vectorize, controls adding the pass to the
- // pass manager. If set, the pass is added, and there is no additional check
- // here for it.
- if (Builder.LoopVectorize)
- Builder.LoopVectorize = OptLevel > 1 && SizeLevel < 2;
+ Builder.LoopVectorize = OptLevel > 1 && SizeLevel < 2;
- // When #pragma vectorize is on for SLP, do the same as above
- Builder.SLPVectorize =
- DisableSLPVectorization ? false : OptLevel > 1 && SizeLevel < 2;
+ Builder.SLPVectorize = OptLevel > 1 && SizeLevel < 2;
if (TM)
TM->adjustPassManager(Builder);
@@ -470,16 +473,17 @@ static TargetMachine* GetTargetMachine(Triple TheTriple, StringRef CPUStr,
StringRef FeaturesStr,
const TargetOptions &Options) {
std::string Error;
- const Target *TheTarget = TargetRegistry::lookupTarget(MArch, TheTriple,
- Error);
+ const Target *TheTarget =
+ TargetRegistry::lookupTarget(codegen::getMArch(), TheTriple, Error);
// Some modules don't specify a triple, and this is okay.
if (!TheTarget) {
return nullptr;
}
- return TheTarget->createTargetMachine(TheTriple.getTriple(), CPUStr,
- FeaturesStr, Options, getRelocModel(),
- getCodeModel(), GetCodeGenOptLevel());
+ return TheTarget->createTargetMachine(
+ TheTriple.getTriple(), codegen::getCPUStr(), codegen::getFeaturesStr(),
+ Options, codegen::getExplicitRelocModel(),
+ codegen::getExplicitCodeModel(), GetCodeGenOptLevel());
}
#ifdef BUILD_EXAMPLES
@@ -508,6 +512,24 @@ void exportDebugifyStats(llvm::StringRef Path, const DebugifyStatsMap &Map) {
}
}
+struct TimeTracerRAII {
+ TimeTracerRAII(StringRef ProgramName) {
+ if (TimeTrace)
+ timeTraceProfilerInitialize(TimeTraceGranularity, ProgramName);
+ }
+ ~TimeTracerRAII() {
+ if (TimeTrace) {
+ if (auto E = timeTraceProfilerWrite(TimeTraceFile, OutputFilename)) {
+ handleAllErrors(std::move(E), [&](const StringError &SE) {
+ errs() << SE.getMessage() << "\n";
+ });
+ return;
+ }
+ timeTraceProfilerCleanup();
+ }
+ }
+};
+
//===----------------------------------------------------------------------===//
// main for opt
//
@@ -575,6 +597,8 @@ int main(int argc, char **argv) {
return 1;
}
+ TimeTracerRAII TimeTracer(argv[0]);
+
SMDiagnostic Err;
Context.setDiscardValueNames(DiscardValueNames);
@@ -582,9 +606,9 @@ int main(int argc, char **argv) {
Context.enableDebugTypeODRUniquing();
Expected<std::unique_ptr<ToolOutputFile>> RemarksFileOrErr =
- setupOptimizationRemarks(Context, RemarksFilename, RemarksPasses,
- RemarksFormat, RemarksWithHotness,
- RemarksHotnessThreshold);
+ setupLLVMOptimizationRemarks(Context, RemarksFilename, RemarksPasses,
+ RemarksFormat, RemarksWithHotness,
+ RemarksHotnessThreshold);
if (Error E = RemarksFileOrErr.takeError()) {
errs() << toString(std::move(E)) << '\n';
return 1;
@@ -592,8 +616,18 @@ int main(int argc, char **argv) {
std::unique_ptr<ToolOutputFile> RemarksFile = std::move(*RemarksFileOrErr);
// Load the input module...
- std::unique_ptr<Module> M =
- parseIRFile(InputFilename, Err, Context, !NoVerify, ClDataLayout);
+ auto SetDataLayout = [](StringRef) -> Optional<std::string> {
+ if (ClDataLayout.empty())
+ return None;
+ return ClDataLayout;
+ };
+ std::unique_ptr<Module> M;
+ if (NoUpgradeDebugInfo)
+ M = parseAssemblyFileWithIndexNoUpgradeDebugInfo(
+ InputFilename, Err, Context, nullptr, SetDataLayout)
+ .Mod;
+ else
+ M = parseIRFile(InputFilename, Err, Context, SetDataLayout);
if (!M) {
Err.print(argv[0], errs());
@@ -625,6 +659,13 @@ int main(int argc, char **argv) {
return 1;
}
+ // Enable testing of whole program devirtualization on this module by invoking
+ // the facility for updating public visibility to linkage unit visibility when
+ // specified by an internal option. This is normally done during LTO which is
+ // not performed via opt.
+ updateVCallVisibilityInModule(*M,
+ /* WholeProgramVisibilityEnabledInLTO */ false);
+
// Figure out what stream we are supposed to write to...
std::unique_ptr<ToolOutputFile> Out;
std::unique_ptr<ToolOutputFile> ThinLinkOut;
@@ -659,11 +700,11 @@ int main(int argc, char **argv) {
Triple ModuleTriple(M->getTargetTriple());
std::string CPUStr, FeaturesStr;
TargetMachine *Machine = nullptr;
- const TargetOptions Options = InitTargetOptionsFromCodeGenFlags();
+ const TargetOptions Options = codegen::InitTargetOptionsFromCodeGenFlags();
if (ModuleTriple.getArch()) {
- CPUStr = getCPUStr();
- FeaturesStr = getFeaturesStr();
+ CPUStr = codegen::getCPUStr();
+ FeaturesStr = codegen::getFeaturesStr();
Machine = GetTargetMachine(ModuleTriple, CPUStr, FeaturesStr, Options);
} else if (ModuleTriple.getArchName() != "unknown" &&
ModuleTriple.getArchName() != "") {
@@ -676,19 +717,40 @@ int main(int argc, char **argv) {
// Override function attributes based on CPUStr, FeaturesStr, and command line
// flags.
- setFunctionAttributes(CPUStr, FeaturesStr, *M);
+ codegen::setFunctionAttributes(CPUStr, FeaturesStr, *M);
// If the output is set to be emitted to standard out, and standard out is a
// console, print out a warning message and refuse to do it. We don't
// impress anyone by spewing tons of binary goo to a terminal.
if (!Force && !NoOutput && !AnalyzeOnly && !OutputAssembly)
- if (CheckBitcodeOutputToConsole(Out->os(), !Quiet))
+ if (CheckBitcodeOutputToConsole(Out->os()))
NoOutput = true;
if (OutputThinLTOBC)
M->addModuleFlag(Module::Error, "EnableSplitLTOUnit", SplitLTOUnit);
- if (PassPipeline.getNumOccurrences() > 0) {
+ if (EnableNewPassManager || PassPipeline.getNumOccurrences() > 0) {
+ if (PassPipeline.getNumOccurrences() > 0 && PassList.size() > 0) {
+ errs()
+ << "Cannot specify passes via both -foo-pass and --passes=foo-pass";
+ return 1;
+ }
+ SmallVector<StringRef, 4> Passes;
+ for (const auto &P : PassList) {
+ Passes.push_back(P->getPassArgument());
+ }
+ if (OptLevelO0)
+ Passes.push_back("default<O0>");
+ if (OptLevelO1)
+ Passes.push_back("default<O1>");
+ if (OptLevelO2)
+ Passes.push_back("default<O2>");
+ if (OptLevelO3)
+ Passes.push_back("default<O3>");
+ if (OptLevelOs)
+ Passes.push_back("default<Os>");
+ if (OptLevelOz)
+ Passes.push_back("default<Oz>");
OutputKind OK = OK_NoOutput;
if (!NoOutput)
OK = OutputAssembly
@@ -705,10 +767,10 @@ int main(int argc, char **argv) {
// string. Hand off the rest of the functionality to the new code for that
// layer.
return runPassPipeline(argv[0], *M, TM.get(), Out.get(), ThinLinkOut.get(),
- RemarksFile.get(), PassPipeline, OK, VK,
+ RemarksFile.get(), PassPipeline, Passes, OK, VK,
PreserveAssemblyUseListOrder,
PreserveBitcodeUseListOrder, EmitSummaryIndex,
- EmitModuleHash, EnableDebugify)
+ EmitModuleHash, EnableDebugify, Coroutines)
? 0
: 1;
}
@@ -831,19 +893,19 @@ int main(int argc, char **argv) {
if (AnalyzeOnly) {
switch (Kind) {
case PT_Region:
- Passes.add(createRegionPassPrinter(PassInf, Out->os(), Quiet));
+ Passes.add(createRegionPassPrinter(PassInf, Out->os()));
break;
case PT_Loop:
- Passes.add(createLoopPassPrinter(PassInf, Out->os(), Quiet));
+ Passes.add(createLoopPassPrinter(PassInf, Out->os()));
break;
case PT_Function:
- Passes.add(createFunctionPassPrinter(PassInf, Out->os(), Quiet));
+ Passes.add(createFunctionPassPrinter(PassInf, Out->os()));
break;
case PT_CallGraphSCC:
- Passes.add(createCallGraphPassPrinter(PassInf, Out->os(), Quiet));
+ Passes.add(createCallGraphPassPrinter(PassInf, Out->os()));
break;
default:
- Passes.add(createModulePassPrinter(PassInf, Out->os(), Quiet));
+ Passes.add(createModulePassPrinter(PassInf, Out->os()));
break;
}
}