Add BAT testing framework

This patch refactors BAT to be testable as a library, so we
can have open-source tests on it. This further fixes an issue with
basic blocks that lack a valid input offset, making BAT omit those
when writing translation tables.

Test Plan: new testcases added, new testing tool added (llvm-bat-dump)

Differential Revision: https://reviews.llvm.org/D129382
This commit is contained in:
Rafael Auler 2022-07-06 15:53:27 -07:00
parent d4940c0f3d
commit fc0ced73dc
12 changed files with 456 additions and 30 deletions

View File

@ -76,26 +76,29 @@ public:
/// output binary
static const char *SECTION_NAME;
BoltAddressTranslation(BinaryContext &BC) : BC(BC) {}
BoltAddressTranslation() {}
/// Write the serialized address translation tables for each reordered
/// function
void write(raw_ostream &OS);
void write(const BinaryContext &BC, raw_ostream &OS);
/// Read the serialized address translation tables and load them internally
/// in memory. Return a parse error if failed.
std::error_code parse(StringRef Buf);
/// Dump the parsed address translation tables
void dump(raw_ostream &OS);
/// If the maps are loaded in memory, perform the lookup to translate LBR
/// addresses in \p Func.
uint64_t translate(const BinaryFunction &Func, uint64_t Offset,
/// addresses in function located at \p FuncAddress.
uint64_t translate(uint64_t FuncAddress, uint64_t Offset,
bool IsBranchSrc) const;
/// Use the map keys containing basic block addresses to infer fall-throughs
/// taken in the path started at FirstLBR.To and ending at SecondLBR.From.
/// Return NoneType if trace is invalid or the list of fall-throughs
/// otherwise.
Optional<FallthroughListTy> getFallthroughsInTrace(const BinaryFunction &Func,
Optional<FallthroughListTy> getFallthroughsInTrace(uint64_t FuncAddress,
uint64_t From,
uint64_t To) const;
@ -115,8 +118,6 @@ private:
void writeEntriesForBB(MapTy &Map, const BinaryBasicBlock &BB,
uint64_t FuncAddress);
BinaryContext &BC;
std::map<uint64_t, MapTy> Maps;
/// Links outlined cold bocks to their original function

View File

@ -117,7 +117,6 @@ void ValidateInternalCalls::fixCFGForPIC(BinaryFunction &Function) const {
if (!MovedInsts.empty()) {
// Split this block at the call instruction.
std::unique_ptr<BinaryBasicBlock> NewBB = Function.createBasicBlock();
NewBB->setOffset(0);
NewBB->addInstructions(MovedInsts.begin(), MovedInsts.end());
BB.moveAllSuccessorsTo(NewBB.get());

View File

@ -25,9 +25,14 @@ void BoltAddressTranslation::writeEntriesForBB(MapTy &Map,
BB.getOutputAddressRange().first - FuncAddress;
const uint32_t BBInputOffset = BB.getInputOffset();
assert(BBInputOffset != BinaryBasicBlock::INVALID_OFFSET &&
"Every output BB must track back to an input BB for profile "
"collection in bolted binaries");
// Every output BB must track back to an input BB for profile collection
// in bolted binaries. If we are missing an offset, it means this block was
// created by a pass. We will skip writing any entries for it, and this means
// any traffic happening in this block will map to the previous block in the
// layout. This covers the case where an input basic block is split into two,
// and the second one lacks any offset.
if (BBInputOffset == BinaryBasicBlock::INVALID_OFFSET)
return;
LLVM_DEBUG(dbgs() << "BB " << BB.getName() << "\n");
LLVM_DEBUG(dbgs() << " Key: " << Twine::utohexstr(BBOutputOffset)
@ -56,13 +61,13 @@ void BoltAddressTranslation::writeEntriesForBB(MapTy &Map,
}
}
void BoltAddressTranslation::write(raw_ostream &OS) {
void BoltAddressTranslation::write(const BinaryContext &BC, raw_ostream &OS) {
LLVM_DEBUG(dbgs() << "BOLT-DEBUG: Writing BOLT Address Translation Tables\n");
for (auto &BFI : BC.getBinaryFunctions()) {
BinaryFunction &Function = BFI.second;
const BinaryFunction &Function = BFI.second;
// We don't need a translation table if the body of the function hasn't
// changed
if (!BC.HasRelocations && !Function.isSimple())
if (Function.isIgnored() || (!BC.HasRelocations && !Function.isSimple()))
continue;
LLVM_DEBUG(dbgs() << "Function name: " << Function.getPrintName() << "\n");
@ -70,7 +75,7 @@ void BoltAddressTranslation::write(raw_ostream &OS) {
<< Twine::utohexstr(Function.getOutputAddress()) << "\n");
MapTy Map;
const bool IsSplit = Function.isSplit();
for (const BinaryBasicBlock *BB : Function.getLayout().blocks()) {
for (const BinaryBasicBlock *const BB : Function.getLayout().blocks()) {
if (IsSplit && BB->isCold())
break;
writeEntriesForBB(Map, *BB, Function.getOutputAddress());
@ -83,7 +88,7 @@ void BoltAddressTranslation::write(raw_ostream &OS) {
// Cold map
Map.clear();
LLVM_DEBUG(dbgs() << " Cold part\n");
for (const BinaryBasicBlock *BB : Function.getLayout().blocks()) {
for (const BinaryBasicBlock *const BB : Function.getLayout().blocks()) {
if (!BB->isCold())
continue;
writeEntriesForBB(Map, *BB, Function.cold().getAddress());
@ -193,10 +198,39 @@ std::error_code BoltAddressTranslation::parse(StringRef Buf) {
return std::error_code();
}
uint64_t BoltAddressTranslation::translate(const BinaryFunction &Func,
void BoltAddressTranslation::dump(raw_ostream &OS) {
const size_t NumTables = Maps.size();
OS << "BAT tables for " << NumTables << " functions:\n";
for (const auto &MapEntry : Maps) {
OS << "Function Address: 0x" << Twine::utohexstr(MapEntry.first) << "\n";
OS << "BB mappings:\n";
for (const auto &Entry : MapEntry.second) {
const bool IsBranch = Entry.second & BRANCHENTRY;
const uint32_t Val = Entry.second & ~BRANCHENTRY;
OS << "0x" << Twine::utohexstr(Entry.first) << " -> "
<< "0x" << Twine::utohexstr(Val);
if (IsBranch)
OS << " (branch)";
OS << "\n";
}
OS << "\n";
}
const size_t NumColdParts = ColdPartSource.size();
if (!NumColdParts)
return;
OS << NumColdParts << " cold mappings:\n";
for (const auto &Entry : ColdPartSource) {
OS << "0x" << Twine::utohexstr(Entry.first) << " -> "
<< Twine::utohexstr(Entry.second) << "\n";
}
OS << "\n";
}
uint64_t BoltAddressTranslation::translate(uint64_t FuncAddress,
uint64_t Offset,
bool IsBranchSrc) const {
auto Iter = Maps.find(Func.getAddress());
auto Iter = Maps.find(FuncAddress);
if (Iter == Maps.end())
return Offset;
@ -217,7 +251,7 @@ uint64_t BoltAddressTranslation::translate(const BinaryFunction &Func,
}
Optional<BoltAddressTranslation::FallthroughListTy>
BoltAddressTranslation::getFallthroughsInTrace(const BinaryFunction &Func,
BoltAddressTranslation::getFallthroughsInTrace(uint64_t FuncAddress,
uint64_t From,
uint64_t To) const {
SmallVector<std::pair<uint64_t, uint64_t>, 16> Res;
@ -226,10 +260,10 @@ BoltAddressTranslation::getFallthroughsInTrace(const BinaryFunction &Func,
if (From >= To)
return Res;
From -= Func.getAddress();
To -= Func.getAddress();
From -= FuncAddress;
To -= FuncAddress;
auto Iter = Maps.find(Func.getAddress());
auto Iter = Maps.find(FuncAddress);
if (Iter == Maps.end())
return NoneType();

View File

@ -699,7 +699,7 @@ bool DataAggregator::doSample(BinaryFunction &Func, uint64_t Address,
Address -= Func.getAddress();
if (BAT)
Address = BAT->translate(Func, Address, /*IsBranchSrc=*/false);
Address = BAT->translate(Func.getAddress(), Address, /*IsBranchSrc=*/false);
I->second.bumpCount(Address, Count);
return true;
@ -722,8 +722,8 @@ bool DataAggregator::doIntraBranch(BinaryFunction &Func, uint64_t From,
<< Func.getPrintName() << " @ " << Twine::utohexstr(To)
<< '\n');
if (BAT) {
From = BAT->translate(Func, From, /*IsBranchSrc=*/true);
To = BAT->translate(Func, To, /*IsBranchSrc=*/false);
From = BAT->translate(Func.getAddress(), From, /*IsBranchSrc=*/true);
To = BAT->translate(Func.getAddress(), To, /*IsBranchSrc=*/false);
LLVM_DEBUG(dbgs() << "BOLT-DEBUG: BAT translation on bumpBranchCount: "
<< Func.getPrintName() << " @ " << Twine::utohexstr(From)
<< " -> " << Func.getPrintName() << " @ "
@ -752,7 +752,7 @@ bool DataAggregator::doInterBranch(BinaryFunction *FromFunc,
}
From -= FromFunc->getAddress();
if (BAT)
From = BAT->translate(*FromFunc, From, /*IsBranchSrc=*/true);
From = BAT->translate(FromFunc->getAddress(), From, /*IsBranchSrc=*/true);
recordExit(*FromFunc, From, Mispreds, Count);
}
@ -766,7 +766,7 @@ bool DataAggregator::doInterBranch(BinaryFunction *FromFunc,
}
To -= ToFunc->getAddress();
if (BAT)
To = BAT->translate(*ToFunc, To, /*IsBranchSrc=*/false);
To = BAT->translate(ToFunc->getAddress(), To, /*IsBranchSrc=*/false);
recordEntry(*ToFunc, To, Mispreds, Count);
}
@ -822,7 +822,8 @@ bool DataAggregator::doTrace(const LBREntry &First, const LBREntry &Second,
}
Optional<BoltAddressTranslation::FallthroughListTy> FTs =
BAT ? BAT->getFallthroughsInTrace(*FromFunc, First.To, Second.From)
BAT ? BAT->getFallthroughsInTrace(FromFunc->getAddress(), First.To,
Second.From)
: getFallthroughsInTrace(*FromFunc, First, Second, Count);
if (!FTs) {
LLVM_DEBUG(

View File

@ -356,7 +356,7 @@ RewriteInstance::RewriteInstance(ELFObjectFileBase *File, const int Argc,
BC->initializeTarget(std::unique_ptr<MCPlusBuilder>(createMCPlusBuilder(
BC->TheTriple->getArch(), BC->MIA.get(), BC->MII.get(), BC->MRI.get())));
BAT = std::make_unique<BoltAddressTranslation>(*BC);
BAT = std::make_unique<BoltAddressTranslation>();
if (opts::UpdateDebugSections)
DebugInfoRewriter = std::make_unique<DWARFRewriter>(*BC);
@ -4163,7 +4163,7 @@ void RewriteInstance::encodeBATSection() {
std::string DescStr;
raw_string_ostream DescOS(DescStr);
BAT->write(DescOS);
BAT->write(*BC, DescOS);
DescOS.flush();
const std::string BoltInfo =

View File

@ -39,6 +39,7 @@ list(APPEND BOLT_TEST_DEPS
llvm-bolt
llvm-boltdiff
llvm-bolt-heatmap
llvm-bat-dump
llvm-dwarfdump
llvm-dwp
llvm-mc

View File

@ -0,0 +1,52 @@
# This checks for an issue with internal calls and BAT (BOLT address
# translation). BAT needs to map every output block back to an input
# block, but passes that introduce new blocks (such as validate
# internal calls) might create new blocks without a mapping to an
# input block.
# REQUIRES: system-linux,bolt-runtime
# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %s -o %t.o
# Delete our BB symbols so BOLT doesn't mark them as entry points
# RUN: llvm-strip --strip-unneeded %t.o
# RUN: %clang %t.o -o %t.exe -Wl,-q
# RUN: llvm-bolt --enable-bat %t.exe --relocs -o %t.out | FileCheck %s
# CHECK: BOLT-INFO: Wrote {{.*}} BAT maps
# RUN: llvm-bat-dump %t.out --dump-all | \
# RUN: FileCheck %s --check-prefix=CHECK-BAT-DUMP
# CHECK-BAT-DUMP: BAT tables for {{.*}} functions
.text
.globl main
.type main, %function
.p2align 4
main:
push %rbp
mov %rsp,%rbp
push %r12
push %rbx
sub $0x120,%rsp
mov $0x3,%rbx
.J1:
cmp $0x0,%rbx
je .J2
callq .J3
nopl (%rax,%rax,1)
movabs $0xdeadbeef,%rax
retq
.J2:
add $0x120,%rsp
pop %rbx
pop %r12
jmp .J4
.J3:
pop %rax
add $0x4,%rax
dec %rbx
jmp .J1
.J4:
pop %rbp
retq
.size main, .-main

View File

@ -0,0 +1,146 @@
# Check a common case for BOLT address translation tables. These tables are used
# to translate profile activity happening in a bolted binary back to the
# original binary, so you can run BOLT again, with updated profile collected
# in a production environment that only runs bolted binaries. As BOLT only
# takes no-bolt binaries as inputs, this translation is necessary to cover
# this scenario.
#
# RUN: yaml2obj %p/Inputs/blarge.yaml &> %t.exe
# RUN: llvm-bolt %t.exe -o %t.out --data %p/Inputs/blarge.fdata \
# RUN: --reorder-blocks=normal --split-functions --enable-bat 2>&1 | FileCheck %s
# RUN: llvm-bat-dump %t.out --dump-all \
# RUN: --translate=0x401180 | FileCheck %s --check-prefix=CHECK-BAT-DUMP
#
# In this test we focus on function usqrt at address 0x401170. This is a
# non-reloc binary case, so we don't expect this address to change, that's
# why we hardcode its address here. This address also comes hardcoded in the
# blarge.yaml input file.
#
# This is the layout of the function before BOLT reorder blocks:
#
# BB Layout : .LBB02, .Ltmp39, .LFT1, .Ltmp38, .LFT2
#
# This is the layout of the function after BOLT reorder blocks:
#
# BB Layout : .LBB02, .Ltmp38, .Ltmp39, .LFT2, .LFT3
#
# .Ltmp38 is originally at offset 0x39 but gets moved to 0xc (see full dump
# below).
#
# We check that BAT is able to translate references happening in .Ltmp38 to
# its original offset.
#
# This binary has 3 functions with profile, all of them are split, so 6 maps.
# BAT creates one map per function fragment.
#
# CHECK: BOLT: 3 out of 7 functions were overwritten.
# CHECK: BOLT-INFO: Wrote 6 BAT maps
# CHECK: BOLT-INFO: Wrote 3 BAT cold-to-hot entries
#
# usqrt mappings (hot part). We match against any key (left side containing
# the bolted binary offsets) because BOLT may change where it puts instructions
# depending on whether it is relaxing a branch or not. But the original input
# binary offsets (right side) should be the same because these addresses are
# hardcoded in the blarge.yaml file.
#
# CHECK-BAT-DUMP: Function Address: 0x401170
# CHECK-BAT-DUMP-NEXT: BB mappings:
# CHECK-BAT-DUMP-NEXT: 0x0 -> 0x0
# CHECK-BAT-DUMP-NEXT: 0x8 -> 0x8 (branch)
# CHECK-BAT-DUMP-NEXT: 0x{{.*}} -> 0x39
# CHECK-BAT-DUMP-NEXT: 0x{{.*}} -> 0x3d (branch)
# CHECK-BAT-DUMP-NEXT: 0x{{.*}} -> 0x10
# CHECK-BAT-DUMP-NEXT: 0x{{.*}} -> 0x30 (branch)
#
# CHECK-BAT-DUMP: 3 cold mappings
#
# Now check that the translation 0x401180 maps back to its correct
# input offset (offset 3d in the usqrt input function).
#
# COM: CHECK-BAT-DUMP: Translating addresses according to parsed BAT tables:
# CHECK-BAT-DUMP: 0x401180 -> usqrt + 0x3d
# -------------------------
# Full dump for reference (this is not checked):
# -------------------------
Binary Function "usqrt" after finalize-functions
Number : 7
State : CFG finalized
Address : 0x401170
Size : 0x43
MaxSize : 0x43
Offset : 0xcb0
Section : .text
Orc Section : .local.text.usqrt
LSDA : 0x0
IsSimple : 1
IsMultiEntry: 0
IsSplit : 1
BB Count : 5
Hash : a6468f132ec176ca
BB Layout : .LBB02, .Ltmp38, .Ltmp39, .LFT2, .LFT3
Exec Count : 199
Profile Acc : 100.0%
.LBB02 (4 instructions, align : 1)
Entry Point
Exec Count : 199
CFI State : 0
Input offset: 0
00000000: movl $0x20, %r8d
00000006: xorl %eax, %eax
00000008: xorl %edx, %edx # Offset: 8
0000000a: jmp .Ltmp39
Successors: .Ltmp39 (mispreds: 0, count: 0)
.Ltmp38 (2 instructions, align : 1)
Exec Count : 4711
CFI State : 0
Input offset: 39
Predecessors: .Ltmp39, .LFT2
0000000c: subl $0x1, %r8d
00000010: je .LFT3 # Offset: 61
Successors: .LFT3 (mispreds: 0, count: 0), .Ltmp39 (mispreds: 33, count: 4711)
.Ltmp39 (10 instructions, align : 1)
Exec Count : 4711
CFI State : 0
Input offset: 10
Predecessors: .Ltmp38, .LBB02
00000012: movq %rdi, %rcx
00000015: addq %rax, %rax
00000018: shlq $0x2, %rdi
0000001c: andl $0xc0000000, %ecx
00000022: shrq $0x1e, %rcx
00000026: leaq (%rcx,%rdx,4), %rdx
0000002a: leaq 0x1(%rax,%rax), %rcx
0000002f: cmpq %rcx, %rdx
00000032: jb .Ltmp38 # Offset: 48
00000034: jmp .LFT2
Successors: .Ltmp38 (mispreds: 171, count: 2886), .LFT2 (mispreds: 0, count: 0)
------- HOT-COLD SPLIT POINT -------
.LFT2 (3 instructions, align : 1)
Exec Count : 0
CFI State : 0
Input offset: 32
Predecessors: .Ltmp39
00000036: subq %rcx, %rdx
00000039: addq $0x1, %rax # Offset: 53
0000003d: jmp .Ltmp38
Successors: .Ltmp38 (mispreds: 0, count: 0)
.LFT3 (2 instructions, align : 1)
Exec Count : 0
CFI State : 0
Input offset: 3f
Predecessors: .Ltmp38
0000003f: movq %rax, (%rsi)
00000042: retq # Offset: 66
DWARF CFI Instructions:
<empty>
End of Function "usqrt"

View File

@ -80,6 +80,7 @@ tools = [
ToolSubst('llvm-bolt', unresolved='fatal'),
ToolSubst('llvm-boltdiff', unresolved='fatal'),
ToolSubst('llvm-bolt-heatmap', unresolved='fatal'),
ToolSubst('llvm-bat-dump', unresolved='fatal'),
ToolSubst('perf2bolt', unresolved='fatal'),
ToolSubst('yaml2obj', unresolved='fatal'),
ToolSubst('llvm-mc', unresolved='fatal'),

View File

@ -14,5 +14,6 @@ endmacro()
add_subdirectory(driver)
add_subdirectory(llvm-bolt-fuzzer)
add_subdirectory(bat-dump)
add_subdirectory(merge-fdata)
add_subdirectory(heatmap)

View File

@ -0,0 +1,15 @@
set(LLVM_LINK_COMPONENTS
Object
Support
)
add_llvm_tool(llvm-bat-dump
bat-dump.cpp
)
target_link_libraries(llvm-bat-dump
PRIVATE
LLVMBOLTProfile
)
set_target_properties(llvm-bat-dump PROPERTIES FOLDER "BOLT")

View File

@ -0,0 +1,175 @@
#include "bolt/Profile/BoltAddressTranslation.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/Object/Binary.h"
#include "llvm/Object/ELFObjectFile.h"
#include "llvm/Object/Error.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Object/SymbolicFile.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Program.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <assert.h>
#include <cstdint>
#include <map>
#include <stdlib.h>
#include <string>
#include <system_error>
#include <type_traits>
#include <utility>
#include <vector>
using namespace llvm;
using namespace bolt;
namespace opts {
cl::OptionCategory BatDumpCategory("BAT dump options");
static cl::OptionCategory *BatDumpCategories[] = {&BatDumpCategory};
static cl::opt<std::string> InputFilename(cl::Positional,
cl::desc("<executable>"),
cl::Required,
cl::cat(BatDumpCategory));
static cl::list<uint64_t> Translate("translate",
cl::desc("translate addresses using BAT"),
cl::value_desc("addr"),
cl::cat(BatDumpCategory));
static cl::opt<bool> DumpAll("dump-all", cl::desc("dump all BAT tables"),
cl::cat(BatDumpCategory));
} // namespace opts
static StringRef ToolName;
static void report_error(StringRef Message, std::error_code EC) {
assert(EC);
errs() << ToolName << ": '" << Message << "': " << EC.message() << ".\n";
exit(1);
}
static void report_error(StringRef Message, Error E) {
assert(E);
errs() << ToolName << ": '" << Message << "': " << toString(std::move(E))
<< ".\n";
exit(1);
}
static std::string GetExecutablePath(const char *Argv0) {
SmallString<256> ExecutablePath(Argv0);
// Do a PATH lookup if Argv0 isn't a valid path.
if (!llvm::sys::fs::exists(ExecutablePath))
if (llvm::ErrorOr<std::string> P =
llvm::sys::findProgramByName(ExecutablePath))
ExecutablePath = *P;
return std::string(ExecutablePath.str());
}
void dumpBATFor(llvm::object::ELFObjectFileBase *InputFile) {
BoltAddressTranslation BAT;
if (!BAT.enabledFor(InputFile)) {
errs() << "error: no BAT table found.\n";
exit(1);
}
// Look for BAT section
bool Found = false;
StringRef SectionContents;
for (const llvm::object::SectionRef &Section : InputFile->sections()) {
Expected<StringRef> SectionNameOrErr = Section.getName();
if (Error E = SectionNameOrErr.takeError())
continue;
if (SectionNameOrErr.get() != BoltAddressTranslation::SECTION_NAME)
continue;
Found = true;
Expected<StringRef> ContentsOrErr = Section.getContents();
if (Error E = ContentsOrErr.takeError())
continue;
SectionContents = ContentsOrErr.get();
}
if (!Found) {
errs() << "BOLT-ERROR: failed to parse BOLT address translation "
"table. No BAT section found\n";
exit(1);
}
if (std::error_code EC = BAT.parse(SectionContents)) {
errs() << "BOLT-ERROR: failed to parse BOLT address translation "
"table. Malformed BAT section\n";
exit(1);
}
if (opts::DumpAll)
BAT.dump(outs());
if (!opts::Translate.empty()) {
// Build map of <Address, SymbolName> for InputFile
std::map<uint64_t, StringRef> FunctionsMap;
for (const llvm::object::ELFSymbolRef &Symbol : InputFile->symbols()) {
Expected<StringRef> NameOrError = Symbol.getName();
if (NameOrError.takeError())
continue;
if (cantFail(Symbol.getType()) != llvm::object::SymbolRef::ST_Function)
continue;
const StringRef Name = *NameOrError;
const uint64_t Address = cantFail(Symbol.getAddress());
FunctionsMap[Address] = Name;
}
outs() << "Translating addresses according to parsed BAT tables:\n";
for (uint64_t Address : opts::Translate) {
auto FI = FunctionsMap.upper_bound(Address);
if (FI == FunctionsMap.begin()) {
outs() << "No function symbol found for 0x" << Twine::utohexstr(Address)
<< "\n";
continue;
}
--FI;
outs() << "0x" << Twine::utohexstr(Address) << " -> " << FI->second
<< " + 0x"
<< Twine::utohexstr(
BAT.translate(FI->first, Address - FI->first, false))
<< "\n";
}
}
}
int main(int argc, char **argv) {
cl::HideUnrelatedOptions(makeArrayRef(opts::BatDumpCategories));
cl::ParseCommandLineOptions(argc, argv, "");
if (!sys::fs::exists(opts::InputFilename))
report_error(opts::InputFilename, errc::no_such_file_or_directory);
ToolName = argv[0];
std::string ToolPath = GetExecutablePath(argv[0]);
Expected<llvm::object::OwningBinary<llvm::object::Binary>> BinaryOrErr =
llvm::object::createBinary(opts::InputFilename);
if (Error E = BinaryOrErr.takeError())
report_error(opts::InputFilename, std::move(E));
llvm::object::Binary &Binary = *BinaryOrErr.get().getBinary();
if (auto *InputFile = dyn_cast<llvm::object::ELFObjectFileBase>(&Binary))
dumpBATFor(InputFile);
else
report_error(opts::InputFilename,
llvm::object::object_error::invalid_file_type);
return EXIT_SUCCESS;
}