2015-10-14 15:35:14 -07:00
|
|
|
//===--- BinaryContext.cpp - Interface for machine-level context ---------===//
|
|
|
|
//
|
|
|
|
// The LLVM Compiler Infrastructure
|
|
|
|
//
|
|
|
|
// This file is distributed under the University of Illinois Open Source
|
|
|
|
// License. See LICENSE.TXT for details.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "BinaryContext.h"
|
Update subroutine address ranges in binary.
Summary:
[WIP] Update DWARF info for function address ranges.
This diff currently does not work for unknown reasons,
but I'm describing here what's the current state.
According to both llvm-dwarf and readelf our output seems correct,
but GDB does not interpret it as expected. All details go below in
hope I missed something.
I couldn't actually track the whole change that introduced support for
what we need in gdb yet, but I think I can get to it
(2007-12-04: Support
lexical bocks and function bodies that occupy non-contiguous address ranges). I have reasons to believe gdb at least at some
nges).
The set of introduced changes was basically this:
- After disassembly, iterate over the DIEs in .debug_info and find the
ones that correspond to each BinaryFunction.
- Refactor DebugArangesWriter to also write addresses of functions to
.debug_ranges and track the offsets of function address ranges there
- Add some infrastructure to facilitate patching the binary in
simple ways (BinaryPatcher.h)
- In RewriteInstance, after writing .debug_ranges already with
function address ranges, for each function do:
-- Find the abbreviation corresponding to the function
-- Patch .debug_abbrev to replace DW_AT_low_pc with DW_AT_ranges and
DW_AT_high_pc with DW_AT_producer (I'll explain this hack below).
Also patch the corresponding forms to DW_FORM_sec_offset and
DW_FORM_string (null-terminated in-place string).
-- Patch debug_info with the .debug_ranges offset in place of
the first 4 bytes of DW_AT_low_pc (DW_AT_ranges only occupies 4
bytes whereas low_pc occupies 8), and write an arbitrary string
in-place in the other 12 bytes that were the 4 MSB of low_pc
and the 8 bytes of high_pc before the patch. This depends on
low_pc and high_pc being put consecutively by the compiler, but
it serves to validate the idea. I tried another way of doing it
that does not rely on this but it didn't work either and I believe
the reason for either not working is the same (and still unknown,
but unrelated to them. I might be wrong though, and if I find yet
another way of doing it I may try it). The other way was to
use a form of DW_FORM_data8 for the section offset. This is
disallowed by the specification, but I doubt gdb validates this,
as it's just easier to store it as 64-bit anyway as this is even
necessary to support 64-bit DWARF (which is not what gcc generates
by default apparently).
I still need to make changes to the diff to make it production-ready,
but first I want to figure out why it doesn't work as expected.
By looking at the output of llvm-dwarfdump or readelf, all of
.debug_ranges, .debug_abbrev and .debug_info seem to have been
correctly updated. However, gdb seems to have serious problems with
what we write.
(In fact, readelf --debug-dump=Ranges shows some funny warning messages
of the form ("Warning: There is a hole [0x100 - 0x120] in .debug_ranges"),
but I played around with this and it seems it's just because no
compile unit was using these ranges. Changing .debug_info apparently
changes these warnings, so they seem to be unrelated to the section
itself. Also looking at the hex dump of the section doesn't help,
as everything seems fine. llvm-dwarfdump doesn't say anything.
So I think .debug_ranges is fine.)
The result is that gdb not only doesn't show the function name as we
wanted, but it also stops showing line number information.
Apparently it's not reading/interpreting the address ranges at all,
and so the functions now have no associated address ranges, only the
symbol value which allows one to put a breakpoint in the function,
but not to show source code.
As this left me without more ideas of what to try to feed gdb with,
I believe the most promising next trial is to try to debug gdb itself,
unless someone spots anything I missed.
I found where the interesting part of the code lies for this
case (gdb/dwarf2read.c and some other related files, but mainly that one).
It seems in some parts gdb uses DW_AT_ranges for only getting
its lowest and highest addresses and setting that as low_pc and
high_pc (see dwarf2_get_pc_bounds in gdb's code and where it's called).
I really hope this is not actually the case for
function address ranges. I'll investigate this further. Otherwise
I don't think any changes we make will make it work as initially
intended, as we'll simply need gdb to support it and in that case it
doesn't.
(cherry picked from FBD3073641)
2016-03-16 18:08:29 -07:00
|
|
|
#include "BinaryFunction.h"
|
2015-10-14 15:35:14 -07:00
|
|
|
#include "llvm/ADT/Twine.h"
|
2017-05-16 09:27:34 -07:00
|
|
|
#include "llvm/DebugInfo/DWARF/DWARFFormValue.h"
|
2016-03-28 17:45:22 -07:00
|
|
|
#include "llvm/DebugInfo/DWARF/DWARFUnit.h"
|
2015-10-14 15:35:14 -07:00
|
|
|
#include "llvm/MC/MCContext.h"
|
2017-02-21 16:15:15 -08:00
|
|
|
#include "llvm/MC/MCStreamer.h"
|
2015-10-14 15:35:14 -07:00
|
|
|
#include "llvm/MC/MCSymbol.h"
|
2016-07-23 08:01:53 -07:00
|
|
|
#include "llvm/Support/CommandLine.h"
|
2015-10-14 15:35:14 -07:00
|
|
|
|
2016-12-21 17:13:56 -08:00
|
|
|
using namespace llvm;
|
|
|
|
using namespace bolt;
|
2015-10-14 15:35:14 -07:00
|
|
|
|
2016-07-23 08:01:53 -07:00
|
|
|
namespace opts {
|
|
|
|
|
2017-03-28 14:40:20 -07:00
|
|
|
extern cl::OptionCategory BoltCategory;
|
|
|
|
|
2016-07-23 08:01:53 -07:00
|
|
|
static cl::opt<bool>
|
|
|
|
PrintDebugInfo("print-debug-info",
|
2017-03-28 14:40:20 -07:00
|
|
|
cl::desc("print debug info when printing functions"),
|
|
|
|
cl::Hidden,
|
|
|
|
cl::cat(BoltCategory));
|
2016-07-23 08:01:53 -07:00
|
|
|
|
2017-10-20 12:11:34 -07:00
|
|
|
static cl::opt<bool>
|
|
|
|
PrintRelocations("print-relocations",
|
|
|
|
cl::desc("print relocations when printing functions"),
|
|
|
|
cl::Hidden,
|
|
|
|
cl::cat(BoltCategory));
|
|
|
|
|
|
|
|
static cl::opt<bool>
|
|
|
|
PrintMemData("print-mem-data",
|
|
|
|
cl::desc("print memory data annotations when printing functions"),
|
|
|
|
cl::Hidden,
|
|
|
|
cl::cat(BoltCategory));
|
|
|
|
|
2016-07-23 08:01:53 -07:00
|
|
|
} // namespace opts
|
|
|
|
|
2016-03-28 17:45:22 -07:00
|
|
|
BinaryContext::~BinaryContext() { }
|
|
|
|
|
2017-05-16 09:27:34 -07:00
|
|
|
MCObjectWriter *BinaryContext::createObjectWriter(raw_pwrite_stream &OS) {
|
|
|
|
if (!MAB) {
|
|
|
|
MAB = std::unique_ptr<MCAsmBackend>(
|
|
|
|
TheTarget->createMCAsmBackend(*MRI, TripleName, ""));
|
|
|
|
}
|
|
|
|
|
|
|
|
return MAB->createObjectWriter(OS);
|
|
|
|
}
|
|
|
|
|
2015-10-14 15:35:14 -07:00
|
|
|
MCSymbol *BinaryContext::getOrCreateGlobalSymbol(uint64_t Address,
|
|
|
|
Twine Prefix) {
|
|
|
|
MCSymbol *Symbol{nullptr};
|
|
|
|
std::string Name;
|
|
|
|
auto NI = GlobalAddresses.find(Address);
|
|
|
|
if (NI != GlobalAddresses.end()) {
|
|
|
|
// Even though there could be multiple names registered at the address,
|
|
|
|
// we only use the first one.
|
|
|
|
Name = NI->second;
|
|
|
|
} else {
|
|
|
|
Name = (Prefix + "0x" + Twine::utohexstr(Address)).str();
|
|
|
|
assert(GlobalSymbols.find(Name) == GlobalSymbols.end() &&
|
|
|
|
"created name is not unique");
|
|
|
|
GlobalAddresses.emplace(std::make_pair(Address, Name));
|
|
|
|
}
|
|
|
|
|
|
|
|
Symbol = Ctx->lookupSymbol(Name);
|
|
|
|
if (Symbol)
|
|
|
|
return Symbol;
|
|
|
|
|
|
|
|
Symbol = Ctx->getOrCreateSymbol(Name);
|
|
|
|
GlobalSymbols[Name] = Address;
|
|
|
|
|
|
|
|
return Symbol;
|
|
|
|
}
|
|
|
|
|
2016-09-29 11:19:06 -07:00
|
|
|
MCSymbol *BinaryContext::getGlobalSymbolAtAddress(uint64_t Address) const {
|
|
|
|
auto NI = GlobalAddresses.find(Address);
|
|
|
|
if (NI == GlobalAddresses.end())
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
auto *Symbol = Ctx->lookupSymbol(NI->second);
|
|
|
|
assert(Symbol && "symbol cannot be NULL at this point");
|
|
|
|
|
|
|
|
return Symbol;
|
|
|
|
}
|
|
|
|
|
2017-06-09 13:17:36 -07:00
|
|
|
MCSymbol *BinaryContext::getGlobalSymbolByName(const std::string &Name) const {
|
|
|
|
auto Itr = GlobalSymbols.find(Name);
|
|
|
|
return Itr == GlobalSymbols.end()
|
|
|
|
? nullptr : getGlobalSymbolAtAddress(Itr->second);
|
|
|
|
}
|
|
|
|
|
2016-12-21 17:13:56 -08:00
|
|
|
void BinaryContext::foldFunction(BinaryFunction &ChildBF,
|
|
|
|
BinaryFunction &ParentBF,
|
|
|
|
std::map<uint64_t, BinaryFunction> &BFs) {
|
|
|
|
// Copy name list.
|
|
|
|
ParentBF.addNewNames(ChildBF.getNames());
|
|
|
|
|
|
|
|
// Update internal bookkeeping info.
|
|
|
|
for (auto &Name : ChildBF.getNames()) {
|
|
|
|
// Calls to functions are handled via symbols, and we keep the lookup table
|
|
|
|
// that we need to update.
|
|
|
|
auto *Symbol = Ctx->lookupSymbol(Name);
|
|
|
|
assert(Symbol && "symbol cannot be NULL at this point");
|
|
|
|
SymbolToFunctionMap[Symbol] = &ParentBF;
|
|
|
|
|
|
|
|
// NB: there's no need to update GlobalAddresses and GlobalSymbols.
|
|
|
|
}
|
|
|
|
|
|
|
|
// Merge execution counts of ChildBF into those of ParentBF.
|
|
|
|
ChildBF.mergeProfileDataInto(ParentBF);
|
|
|
|
|
2017-12-09 21:40:39 -08:00
|
|
|
if (HasRelocations) {
|
2016-12-21 17:13:56 -08:00
|
|
|
// Remove ChildBF from the global set of functions in relocs mode.
|
|
|
|
auto FI = BFs.find(ChildBF.getAddress());
|
|
|
|
assert(FI != BFs.end() && "function not found");
|
|
|
|
assert(&ChildBF == &FI->second && "function mismatch");
|
|
|
|
FI = BFs.erase(FI);
|
|
|
|
} else {
|
|
|
|
// In non-relocation mode we keep the function, but rename it.
|
|
|
|
std::string NewName = "__ICF_" + ChildBF.Names.back();
|
|
|
|
ChildBF.Names.clear();
|
|
|
|
ChildBF.Names.push_back(NewName);
|
|
|
|
ChildBF.OutputSymbol = Ctx->getOrCreateSymbol(NewName);
|
2017-01-10 11:20:56 -08:00
|
|
|
ChildBF.setFolded();
|
2016-12-21 17:13:56 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-23 08:01:53 -07:00
|
|
|
void BinaryContext::printGlobalSymbols(raw_ostream& OS) const {
|
2018-01-24 05:42:11 -08:00
|
|
|
for (auto &Entry : GlobalSymbols) {
|
|
|
|
OS << "(" << Entry.first << " -> 0x"
|
|
|
|
<< Twine::utohexstr(Entry.second) << ")\n";
|
2016-07-23 08:01:53 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-28 17:45:22 -07:00
|
|
|
namespace {
|
|
|
|
|
2016-05-23 19:36:38 -07:00
|
|
|
/// Recursively finds DWARF DW_TAG_subprogram DIEs and match them with
|
|
|
|
/// BinaryFunctions. Record DIEs for unknown subprograms (mostly functions that
|
|
|
|
/// are never called and removed from the binary) in Unknown.
|
2016-05-27 20:19:19 -07:00
|
|
|
void findSubprograms(DWARFCompileUnit *Unit,
|
2016-04-08 16:24:38 -07:00
|
|
|
const DWARFDebugInfoEntryMinimal *DIE,
|
2017-05-16 09:27:34 -07:00
|
|
|
std::map<uint64_t, BinaryFunction> &BinaryFunctions) {
|
2016-04-08 16:24:38 -07:00
|
|
|
if (DIE->isSubprogramDIE()) {
|
2016-05-23 19:36:38 -07:00
|
|
|
// TODO: handle DW_AT_ranges.
|
2016-04-08 16:24:38 -07:00
|
|
|
uint64_t LowPC, HighPC;
|
|
|
|
if (DIE->getLowAndHighPC(Unit, LowPC, HighPC)) {
|
|
|
|
auto It = BinaryFunctions.find(LowPC);
|
|
|
|
if (It != BinaryFunctions.end()) {
|
2016-05-27 20:19:19 -07:00
|
|
|
It->second.addSubprogramDIE(Unit, DIE);
|
2016-04-08 16:24:38 -07:00
|
|
|
} else {
|
2017-05-16 09:27:34 -07:00
|
|
|
// The function must have been optimized away by GC.
|
2016-04-08 16:24:38 -07:00
|
|
|
}
|
2017-05-08 22:51:36 -07:00
|
|
|
} else {
|
|
|
|
const auto RangesVector = DIE->getAddressRanges(Unit);
|
|
|
|
if (!RangesVector.empty()) {
|
|
|
|
errs() << "BOLT-ERROR: split function detected in .debug_info. "
|
|
|
|
"Split functions are not supported.\n";
|
|
|
|
exit(1);
|
|
|
|
}
|
2016-04-08 16:24:38 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto ChildDIE = DIE->getFirstChild();
|
|
|
|
ChildDIE != nullptr && !ChildDIE->isNULL();
|
|
|
|
ChildDIE = ChildDIE->getSibling()) {
|
2017-05-16 09:27:34 -07:00
|
|
|
findSubprograms(Unit, ChildDIE, BinaryFunctions);
|
2016-04-08 16:24:38 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-28 17:45:22 -07:00
|
|
|
} // namespace
|
|
|
|
|
2016-09-02 11:58:53 -07:00
|
|
|
unsigned BinaryContext::addDebugFilenameToUnit(const uint32_t DestCUID,
|
|
|
|
const uint32_t SrcCUID,
|
|
|
|
unsigned FileIndex) {
|
|
|
|
auto SrcUnit = DwCtx->getCompileUnitForOffset(SrcCUID);
|
|
|
|
auto LineTable = DwCtx->getLineTableForUnit(SrcUnit);
|
|
|
|
const auto &FileNames = LineTable->Prologue.FileNames;
|
|
|
|
// Dir indexes start at 1, as DWARF file numbers, and a dir index 0
|
|
|
|
// means empty dir.
|
|
|
|
assert(FileIndex > 0 && FileIndex <= FileNames.size() &&
|
|
|
|
"FileIndex out of range for the compilation unit.");
|
|
|
|
const char *Dir = FileNames[FileIndex - 1].DirIdx ?
|
|
|
|
LineTable->Prologue.IncludeDirectories[FileNames[FileIndex - 1].DirIdx - 1] :
|
|
|
|
"";
|
|
|
|
return Ctx->getDwarfFile(Dir, FileNames[FileIndex - 1].Name, 0, DestCUID);
|
|
|
|
}
|
|
|
|
|
2017-08-31 11:45:37 -07:00
|
|
|
std::vector<BinaryFunction *> BinaryContext::getSortedFunctions(
|
|
|
|
std::map<uint64_t, BinaryFunction> &BinaryFunctions) {
|
|
|
|
std::vector<BinaryFunction *> SortedFunctions(BinaryFunctions.size());
|
|
|
|
std::transform(BinaryFunctions.begin(), BinaryFunctions.end(),
|
|
|
|
SortedFunctions.begin(),
|
|
|
|
[](std::pair<const uint64_t, BinaryFunction> &BFI) {
|
|
|
|
return &BFI.second;
|
|
|
|
});
|
|
|
|
|
2017-11-28 09:57:21 -08:00
|
|
|
std::stable_sort(SortedFunctions.begin(), SortedFunctions.end(),
|
|
|
|
[](const BinaryFunction *A, const BinaryFunction *B) {
|
|
|
|
if (A->hasValidIndex() && B->hasValidIndex()) {
|
|
|
|
return A->getIndex() < B->getIndex();
|
|
|
|
} else {
|
|
|
|
return A->hasValidIndex();
|
|
|
|
}
|
|
|
|
});
|
2017-08-31 11:45:37 -07:00
|
|
|
return SortedFunctions;
|
|
|
|
}
|
|
|
|
|
2016-05-27 20:19:19 -07:00
|
|
|
void BinaryContext::preprocessDebugInfo(
|
|
|
|
std::map<uint64_t, BinaryFunction> &BinaryFunctions) {
|
2016-03-14 18:48:05 -07:00
|
|
|
// Populate MCContext with DWARF files.
|
|
|
|
for (const auto &CU : DwCtx->compile_units()) {
|
|
|
|
const auto CUID = CU->getOffset();
|
|
|
|
auto LineTable = DwCtx->getLineTableForUnit(CU.get());
|
|
|
|
const auto &FileNames = LineTable->Prologue.FileNames;
|
|
|
|
for (size_t I = 0, Size = FileNames.size(); I != Size; ++I) {
|
|
|
|
// Dir indexes start at 1, as DWARF file numbers, and a dir index 0
|
|
|
|
// means empty dir.
|
|
|
|
const char *Dir = FileNames[I].DirIdx ?
|
|
|
|
LineTable->Prologue.IncludeDirectories[FileNames[I].DirIdx - 1] :
|
|
|
|
"";
|
2016-09-02 11:58:53 -07:00
|
|
|
Ctx->getDwarfFile(Dir, FileNames[I].Name, 0, CUID);
|
2016-03-14 18:48:05 -07:00
|
|
|
}
|
|
|
|
}
|
2016-02-25 16:57:07 -08:00
|
|
|
|
2016-04-08 16:24:38 -07:00
|
|
|
// For each CU, iterate over its children DIEs and match subprogram DIEs to
|
Update subroutine address ranges in binary.
Summary:
[WIP] Update DWARF info for function address ranges.
This diff currently does not work for unknown reasons,
but I'm describing here what's the current state.
According to both llvm-dwarf and readelf our output seems correct,
but GDB does not interpret it as expected. All details go below in
hope I missed something.
I couldn't actually track the whole change that introduced support for
what we need in gdb yet, but I think I can get to it
(2007-12-04: Support
lexical bocks and function bodies that occupy non-contiguous address ranges). I have reasons to believe gdb at least at some
nges).
The set of introduced changes was basically this:
- After disassembly, iterate over the DIEs in .debug_info and find the
ones that correspond to each BinaryFunction.
- Refactor DebugArangesWriter to also write addresses of functions to
.debug_ranges and track the offsets of function address ranges there
- Add some infrastructure to facilitate patching the binary in
simple ways (BinaryPatcher.h)
- In RewriteInstance, after writing .debug_ranges already with
function address ranges, for each function do:
-- Find the abbreviation corresponding to the function
-- Patch .debug_abbrev to replace DW_AT_low_pc with DW_AT_ranges and
DW_AT_high_pc with DW_AT_producer (I'll explain this hack below).
Also patch the corresponding forms to DW_FORM_sec_offset and
DW_FORM_string (null-terminated in-place string).
-- Patch debug_info with the .debug_ranges offset in place of
the first 4 bytes of DW_AT_low_pc (DW_AT_ranges only occupies 4
bytes whereas low_pc occupies 8), and write an arbitrary string
in-place in the other 12 bytes that were the 4 MSB of low_pc
and the 8 bytes of high_pc before the patch. This depends on
low_pc and high_pc being put consecutively by the compiler, but
it serves to validate the idea. I tried another way of doing it
that does not rely on this but it didn't work either and I believe
the reason for either not working is the same (and still unknown,
but unrelated to them. I might be wrong though, and if I find yet
another way of doing it I may try it). The other way was to
use a form of DW_FORM_data8 for the section offset. This is
disallowed by the specification, but I doubt gdb validates this,
as it's just easier to store it as 64-bit anyway as this is even
necessary to support 64-bit DWARF (which is not what gcc generates
by default apparently).
I still need to make changes to the diff to make it production-ready,
but first I want to figure out why it doesn't work as expected.
By looking at the output of llvm-dwarfdump or readelf, all of
.debug_ranges, .debug_abbrev and .debug_info seem to have been
correctly updated. However, gdb seems to have serious problems with
what we write.
(In fact, readelf --debug-dump=Ranges shows some funny warning messages
of the form ("Warning: There is a hole [0x100 - 0x120] in .debug_ranges"),
but I played around with this and it seems it's just because no
compile unit was using these ranges. Changing .debug_info apparently
changes these warnings, so they seem to be unrelated to the section
itself. Also looking at the hex dump of the section doesn't help,
as everything seems fine. llvm-dwarfdump doesn't say anything.
So I think .debug_ranges is fine.)
The result is that gdb not only doesn't show the function name as we
wanted, but it also stops showing line number information.
Apparently it's not reading/interpreting the address ranges at all,
and so the functions now have no associated address ranges, only the
symbol value which allows one to put a breakpoint in the function,
but not to show source code.
As this left me without more ideas of what to try to feed gdb with,
I believe the most promising next trial is to try to debug gdb itself,
unless someone spots anything I missed.
I found where the interesting part of the code lies for this
case (gdb/dwarf2read.c and some other related files, but mainly that one).
It seems in some parts gdb uses DW_AT_ranges for only getting
its lowest and highest addresses and setting that as low_pc and
high_pc (see dwarf2_get_pc_bounds in gdb's code and where it's called).
I really hope this is not actually the case for
function address ranges. I'll investigate this further. Otherwise
I don't think any changes we make will make it work as initially
intended, as we'll simply need gdb to support it and in that case it
doesn't.
(cherry picked from FBD3073641)
2016-03-16 18:08:29 -07:00
|
|
|
// BinaryFunctions.
|
2016-05-27 20:19:19 -07:00
|
|
|
for (auto &CU : DwCtx->compile_units()) {
|
2017-05-16 09:27:34 -07:00
|
|
|
findSubprograms(CU.get(), CU->getUnitDIE(false), BinaryFunctions);
|
Update subroutine address ranges in binary.
Summary:
[WIP] Update DWARF info for function address ranges.
This diff currently does not work for unknown reasons,
but I'm describing here what's the current state.
According to both llvm-dwarf and readelf our output seems correct,
but GDB does not interpret it as expected. All details go below in
hope I missed something.
I couldn't actually track the whole change that introduced support for
what we need in gdb yet, but I think I can get to it
(2007-12-04: Support
lexical bocks and function bodies that occupy non-contiguous address ranges). I have reasons to believe gdb at least at some
nges).
The set of introduced changes was basically this:
- After disassembly, iterate over the DIEs in .debug_info and find the
ones that correspond to each BinaryFunction.
- Refactor DebugArangesWriter to also write addresses of functions to
.debug_ranges and track the offsets of function address ranges there
- Add some infrastructure to facilitate patching the binary in
simple ways (BinaryPatcher.h)
- In RewriteInstance, after writing .debug_ranges already with
function address ranges, for each function do:
-- Find the abbreviation corresponding to the function
-- Patch .debug_abbrev to replace DW_AT_low_pc with DW_AT_ranges and
DW_AT_high_pc with DW_AT_producer (I'll explain this hack below).
Also patch the corresponding forms to DW_FORM_sec_offset and
DW_FORM_string (null-terminated in-place string).
-- Patch debug_info with the .debug_ranges offset in place of
the first 4 bytes of DW_AT_low_pc (DW_AT_ranges only occupies 4
bytes whereas low_pc occupies 8), and write an arbitrary string
in-place in the other 12 bytes that were the 4 MSB of low_pc
and the 8 bytes of high_pc before the patch. This depends on
low_pc and high_pc being put consecutively by the compiler, but
it serves to validate the idea. I tried another way of doing it
that does not rely on this but it didn't work either and I believe
the reason for either not working is the same (and still unknown,
but unrelated to them. I might be wrong though, and if I find yet
another way of doing it I may try it). The other way was to
use a form of DW_FORM_data8 for the section offset. This is
disallowed by the specification, but I doubt gdb validates this,
as it's just easier to store it as 64-bit anyway as this is even
necessary to support 64-bit DWARF (which is not what gcc generates
by default apparently).
I still need to make changes to the diff to make it production-ready,
but first I want to figure out why it doesn't work as expected.
By looking at the output of llvm-dwarfdump or readelf, all of
.debug_ranges, .debug_abbrev and .debug_info seem to have been
correctly updated. However, gdb seems to have serious problems with
what we write.
(In fact, readelf --debug-dump=Ranges shows some funny warning messages
of the form ("Warning: There is a hole [0x100 - 0x120] in .debug_ranges"),
but I played around with this and it seems it's just because no
compile unit was using these ranges. Changing .debug_info apparently
changes these warnings, so they seem to be unrelated to the section
itself. Also looking at the hex dump of the section doesn't help,
as everything seems fine. llvm-dwarfdump doesn't say anything.
So I think .debug_ranges is fine.)
The result is that gdb not only doesn't show the function name as we
wanted, but it also stops showing line number information.
Apparently it's not reading/interpreting the address ranges at all,
and so the functions now have no associated address ranges, only the
symbol value which allows one to put a breakpoint in the function,
but not to show source code.
As this left me without more ideas of what to try to feed gdb with,
I believe the most promising next trial is to try to debug gdb itself,
unless someone spots anything I missed.
I found where the interesting part of the code lies for this
case (gdb/dwarf2read.c and some other related files, but mainly that one).
It seems in some parts gdb uses DW_AT_ranges for only getting
its lowest and highest addresses and setting that as low_pc and
high_pc (see dwarf2_get_pc_bounds in gdb's code and where it's called).
I really hope this is not actually the case for
function address ranges. I'll investigate this further. Otherwise
I don't think any changes we make will make it work as initially
intended, as we'll simply need gdb to support it and in that case it
doesn't.
(cherry picked from FBD3073641)
2016-03-16 18:08:29 -07:00
|
|
|
}
|
2016-03-28 17:45:22 -07:00
|
|
|
|
2016-05-27 20:19:19 -07:00
|
|
|
// Some functions may not have a corresponding subprogram DIE
|
|
|
|
// yet they will be included in some CU and will have line number information.
|
|
|
|
// Hence we need to associate them with the CU and include in CU ranges.
|
|
|
|
for (auto &AddrFunctionPair : BinaryFunctions) {
|
|
|
|
auto FunctionAddress = AddrFunctionPair.first;
|
|
|
|
auto &Function = AddrFunctionPair.second;
|
|
|
|
if (!Function.getSubprogramDIEs().empty())
|
|
|
|
continue;
|
|
|
|
if (auto DebugAranges = DwCtx->getDebugAranges()) {
|
|
|
|
auto CUOffset = DebugAranges->findAddress(FunctionAddress);
|
|
|
|
if (CUOffset != -1U) {
|
|
|
|
Function.addSubprogramDIE(DwCtx->getCompileUnitForOffset(CUOffset),
|
|
|
|
nullptr);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DWARF_LOOKUP_ALL_RANGES
|
|
|
|
// Last resort - iterate over all compile units. This should not happen
|
|
|
|
// very often. If it does, we need to create a separate lookup table
|
|
|
|
// similar to .debug_aranges internally. This slows down processing
|
|
|
|
// considerably.
|
|
|
|
for (const auto &CU : DwCtx->compile_units()) {
|
|
|
|
const auto *CUDie = CU->getUnitDIE();
|
|
|
|
for (const auto &Range : CUDie->getAddressRanges(CU.get())) {
|
|
|
|
if (FunctionAddress >= Range.first &&
|
|
|
|
FunctionAddress < Range.second) {
|
|
|
|
Function.addSubprogramDIE(CU.get(), nullptr);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-01 16:52:54 -07:00
|
|
|
void BinaryContext::printCFI(raw_ostream &OS, const MCCFIInstruction &Inst) {
|
|
|
|
uint32_t Operation = Inst.getOperation();
|
|
|
|
switch (Operation) {
|
|
|
|
case MCCFIInstruction::OpSameValue:
|
|
|
|
OS << "OpSameValue Reg" << Inst.getRegister();
|
|
|
|
break;
|
|
|
|
case MCCFIInstruction::OpRememberState:
|
|
|
|
OS << "OpRememberState";
|
|
|
|
break;
|
|
|
|
case MCCFIInstruction::OpRestoreState:
|
|
|
|
OS << "OpRestoreState";
|
|
|
|
break;
|
|
|
|
case MCCFIInstruction::OpOffset:
|
|
|
|
OS << "OpOffset Reg" << Inst.getRegister() << " " << Inst.getOffset();
|
|
|
|
break;
|
|
|
|
case MCCFIInstruction::OpDefCfaRegister:
|
|
|
|
OS << "OpDefCfaRegister Reg" << Inst.getRegister();
|
|
|
|
break;
|
|
|
|
case MCCFIInstruction::OpDefCfaOffset:
|
|
|
|
OS << "OpDefCfaOffset " << Inst.getOffset();
|
|
|
|
break;
|
|
|
|
case MCCFIInstruction::OpDefCfa:
|
|
|
|
OS << "OpDefCfa Reg" << Inst.getRegister() << " " << Inst.getOffset();
|
|
|
|
break;
|
|
|
|
case MCCFIInstruction::OpRelOffset:
|
|
|
|
OS << "OpRelOffset";
|
|
|
|
break;
|
|
|
|
case MCCFIInstruction::OpAdjustCfaOffset:
|
|
|
|
OS << "OfAdjustCfaOffset";
|
|
|
|
break;
|
|
|
|
case MCCFIInstruction::OpEscape:
|
|
|
|
OS << "OpEscape";
|
|
|
|
break;
|
|
|
|
case MCCFIInstruction::OpRestore:
|
|
|
|
OS << "OpRestore";
|
|
|
|
break;
|
|
|
|
case MCCFIInstruction::OpUndefined:
|
|
|
|
OS << "OpUndefined";
|
|
|
|
break;
|
|
|
|
case MCCFIInstruction::OpRegister:
|
|
|
|
OS << "OpRegister";
|
|
|
|
break;
|
|
|
|
case MCCFIInstruction::OpWindowSave:
|
|
|
|
OS << "OpWindowSave";
|
|
|
|
break;
|
|
|
|
case MCCFIInstruction::OpGnuArgsSize:
|
|
|
|
OS << "OpGnuArgsSize";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
OS << "Op#" << Operation;
|
|
|
|
break;
|
2016-07-23 08:01:53 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void BinaryContext::printInstruction(raw_ostream &OS,
|
|
|
|
const MCInst &Instruction,
|
|
|
|
uint64_t Offset,
|
|
|
|
const BinaryFunction* Function,
|
2017-10-20 12:11:34 -07:00
|
|
|
bool PrintMCInst,
|
|
|
|
bool PrintMemData,
|
|
|
|
bool PrintRelocations) const {
|
2016-07-23 08:01:53 -07:00
|
|
|
if (MIA->isEHLabel(Instruction)) {
|
2016-09-08 14:52:26 -07:00
|
|
|
OS << " EH_LABEL: " << *MIA->getTargetSymbol(Instruction) << '\n';
|
2016-07-23 08:01:53 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
OS << format(" %08" PRIx64 ": ", Offset);
|
2016-08-22 14:24:09 -07:00
|
|
|
if (MIA->isCFI(Instruction)) {
|
2016-07-23 08:01:53 -07:00
|
|
|
uint32_t Offset = Instruction.getOperand(0).getImm();
|
|
|
|
OS << "\t!CFI\t$" << Offset << "\t; ";
|
2016-08-22 14:24:09 -07:00
|
|
|
if (Function)
|
2017-05-01 16:52:54 -07:00
|
|
|
printCFI(OS, *Function->getCFIFor(Instruction));
|
2016-07-23 08:01:53 -07:00
|
|
|
OS << "\n";
|
|
|
|
return;
|
|
|
|
}
|
2016-07-28 18:49:48 -07:00
|
|
|
InstPrinter->printInst(&Instruction, OS, "", *STI);
|
2016-07-23 08:01:53 -07:00
|
|
|
if (MIA->isCall(Instruction)) {
|
|
|
|
if (MIA->isTailCall(Instruction))
|
|
|
|
OS << " # TAILCALL ";
|
|
|
|
if (MIA->isInvoke(Instruction)) {
|
|
|
|
const MCSymbol *LP;
|
|
|
|
uint64_t Action;
|
|
|
|
std::tie(LP, Action) = MIA->getEHInfo(Instruction);
|
|
|
|
OS << " # handler: ";
|
|
|
|
if (LP)
|
|
|
|
OS << *LP;
|
|
|
|
else
|
|
|
|
OS << '0';
|
|
|
|
OS << "; action: " << Action;
|
|
|
|
auto GnuArgsSize = MIA->getGnuArgsSize(Instruction);
|
|
|
|
if (GnuArgsSize >= 0)
|
|
|
|
OS << "; GNU_args_size = " << GnuArgsSize;
|
|
|
|
}
|
|
|
|
}
|
2016-09-14 16:45:40 -07:00
|
|
|
if (MIA->isIndirectBranch(Instruction)) {
|
2016-09-16 15:54:32 -07:00
|
|
|
if (auto JTAddress = MIA->getJumpTable(Instruction)) {
|
|
|
|
OS << " # JUMPTABLE @0x" << Twine::utohexstr(JTAddress);
|
2016-09-14 16:45:40 -07:00
|
|
|
}
|
|
|
|
}
|
2016-07-23 08:01:53 -07:00
|
|
|
|
Indirect call promotion optimization.
Summary:
Perform indirect call promotion optimization in BOLT.
The code scans the instructions during CFG creation for all
indirect calls. Right now indirect tail calls are not handled
since the functions are marked not simple. The offsets of the
indirect calls are stored for later use by the ICP pass.
The indirect call promotion pass visits each indirect call and
examines the BranchData for each. If the most frequent targets
from that callsite exceed the specified threshold (default 90%),
the call is promoted. Otherwise, it is ignored. By default,
only one target is considered at each callsite.
When an candiate callsite is processed, we modify the callsite
to test for the most common call targets before calling through
the original generic call mechanism.
The CFG and layout are modified by ICP.
A few new command line options have been added:
-indirect-call-promotion
-indirect-call-promotion-threshold=<percentage>
-indirect-call-promotion-topn=<int>
The threshold is the minimum frequency of a call target needed
before ICP is triggered.
The topn option controls the number of targets to consider for
each callsite, e.g. ICP is triggered if topn=2 and the total
requency of the top two call targets exceeds the threshold.
Example of ICP:
C++ code:
int B_count = 0;
int C_count = 0;
struct A { virtual void foo() = 0; }
struct B : public A { virtual void foo() { ++B_count; }; };
struct C : public A { virtual void foo() { ++C_count; }; };
A* a = ...
a->foo();
...
original:
400863: 49 8b 07 mov (%r15),%rax
400866: 4c 89 ff mov %r15,%rdi
400869: ff 10 callq *(%rax)
40086b: 41 83 e6 01 and $0x1,%r14d
40086f: 4d 89 e6 mov %r12,%r14
400872: 4c 0f 44 f5 cmove %rbp,%r14
400876: 4c 89 f7 mov %r14,%rdi
...
after ICP:
40085e: 49 8b 07 mov (%r15),%rax
400861: 4c 89 ff mov %r15,%rdi
400864: 49 ba e0 0b 40 00 00 movabs $0x400be0,%r10
40086b: 00 00 00
40086e: 4c 3b 10 cmp (%rax),%r10
400871: 75 29 jne 40089c <main+0x9c>
400873: 41 ff d2 callq *%r10
400876: 41 83 e6 01 and $0x1,%r14d
40087a: 4d 89 e6 mov %r12,%r14
40087d: 4c 0f 44 f5 cmove %rbp,%r14
400881: 4c 89 f7 mov %r14,%rdi
...
40089c: ff 10 callq *(%rax)
40089e: eb d6 jmp 400876 <main+0x76>
(cherry picked from FBD3612218)
2016-09-07 18:59:23 -07:00
|
|
|
MIA->forEachAnnotation(
|
|
|
|
Instruction,
|
|
|
|
[&OS](const MCAnnotation *Annotation) {
|
|
|
|
OS << " # " << Annotation->getName() << ": ";
|
|
|
|
Annotation->print(OS);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
2016-07-23 08:01:53 -07:00
|
|
|
const DWARFDebugLine::LineTable *LineTable =
|
|
|
|
Function && opts::PrintDebugInfo ? Function->getDWARFUnitLineTable().second
|
|
|
|
: nullptr;
|
|
|
|
|
|
|
|
if (LineTable) {
|
|
|
|
auto RowRef = DebugLineTableRowRef::fromSMLoc(Instruction.getLoc());
|
|
|
|
|
|
|
|
if (RowRef != DebugLineTableRowRef::NULL_ROW) {
|
|
|
|
const auto &Row = LineTable->Rows[RowRef.RowIndex - 1];
|
|
|
|
OS << " # debug line "
|
|
|
|
<< LineTable->Prologue.FileNames[Row.File - 1].Name
|
|
|
|
<< ":" << Row.Line;
|
|
|
|
|
|
|
|
if (Row.Column) {
|
|
|
|
OS << ":" << Row.Column;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-20 12:11:34 -07:00
|
|
|
if ((opts::PrintMemData || PrintMemData) && Function) {
|
|
|
|
const auto *MD = Function->getMemData();
|
|
|
|
const auto MemDataOffset =
|
|
|
|
MIA->tryGetAnnotationAs<uint64_t>(Instruction, "MemDataOffset");
|
|
|
|
if (MD && MemDataOffset) {
|
|
|
|
bool DidPrint = false;
|
|
|
|
for (auto &MI : MD->getMemInfoRange(MemDataOffset.get())) {
|
|
|
|
OS << (DidPrint ? ", " : " # Loads: ");
|
|
|
|
OS << MI.Addr << "/" << MI.Count;
|
|
|
|
DidPrint = true;
|
|
|
|
}
|
2017-10-16 13:09:43 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-20 12:11:34 -07:00
|
|
|
if ((opts::PrintRelocations || PrintRelocations) && Function) {
|
|
|
|
const auto Size = computeCodeSize(&Instruction, &Instruction + 1);
|
|
|
|
Function->printRelocations(OS, Offset, Size);
|
|
|
|
}
|
|
|
|
|
2016-07-23 08:01:53 -07:00
|
|
|
OS << "\n";
|
|
|
|
|
2017-10-20 12:11:34 -07:00
|
|
|
if (PrintMCInst) {
|
2016-07-23 08:01:53 -07:00
|
|
|
Instruction.dump_pretty(OS, InstPrinter.get());
|
|
|
|
OS << "\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-20 12:11:34 -07:00
|
|
|
ErrorOr<ArrayRef<uint8_t>>
|
|
|
|
BinaryContext::getFunctionData(const BinaryFunction &Function) const {
|
2018-01-23 15:10:24 -08:00
|
|
|
auto &Section = Function.getSection();
|
|
|
|
assert(Section.containsRange(Function.getAddress(), Function.getSize()) &&
|
2017-10-20 12:11:34 -07:00
|
|
|
"wrong section for function");
|
|
|
|
|
|
|
|
if (!Section.isText() || Section.isVirtual() || !Section.getSize()) {
|
|
|
|
return std::make_error_code(std::errc::bad_address);
|
|
|
|
}
|
|
|
|
|
2018-01-23 15:10:24 -08:00
|
|
|
StringRef SectionContents = Section.getContents();
|
2017-10-20 12:11:34 -07:00
|
|
|
|
|
|
|
assert(SectionContents.size() == Section.getSize() &&
|
|
|
|
"section size mismatch");
|
|
|
|
|
|
|
|
// Function offset from the section start.
|
|
|
|
auto FunctionOffset = Function.getAddress() - Section.getAddress();
|
|
|
|
auto *Bytes = reinterpret_cast<const uint8_t *>(SectionContents.data());
|
|
|
|
return ArrayRef<uint8_t>(Bytes + FunctionOffset, Function.getSize());
|
|
|
|
}
|
|
|
|
|
2018-01-23 15:10:24 -08:00
|
|
|
ErrorOr<BinarySection&> BinaryContext::getSectionForAddress(uint64_t Address) {
|
2018-01-31 12:12:59 -08:00
|
|
|
auto SI = AddressToSection.upper_bound(Address);
|
|
|
|
if (SI != AddressToSection.begin()) {
|
2018-01-23 15:10:24 -08:00
|
|
|
--SI;
|
2018-01-31 12:12:59 -08:00
|
|
|
if (SI->first + SI->second->getSize() > Address)
|
|
|
|
return *SI->second;
|
2018-01-23 15:10:24 -08:00
|
|
|
}
|
|
|
|
return std::make_error_code(std::errc::bad_address);
|
|
|
|
}
|
|
|
|
|
|
|
|
ErrorOr<const BinarySection &>
|
|
|
|
BinaryContext::getSectionForAddress(uint64_t Address) const {
|
2018-01-31 12:12:59 -08:00
|
|
|
auto SI = AddressToSection.upper_bound(Address);
|
|
|
|
if (SI != AddressToSection.begin()) {
|
2016-07-21 12:45:35 -07:00
|
|
|
--SI;
|
2018-01-31 12:12:59 -08:00
|
|
|
if (SI->first + SI->second->getSize() > Address)
|
|
|
|
return *SI->second;
|
2016-07-21 12:45:35 -07:00
|
|
|
}
|
|
|
|
return std::make_error_code(std::errc::bad_address);
|
|
|
|
}
|
|
|
|
|
2018-01-31 12:12:59 -08:00
|
|
|
BinarySection &BinaryContext::registerSection(SectionRef Section) {
|
|
|
|
StringRef Name;
|
|
|
|
Section.getName(Name);
|
|
|
|
auto Res = Sections.insert(BinarySection(Section));
|
|
|
|
assert(Res.second && "can't register the same section twice.");
|
|
|
|
// Cast away const here because std::set always stores values by
|
|
|
|
// const. It's ok to do this because we can never change the
|
|
|
|
// BinarySection properties that affect set ordering.
|
|
|
|
auto *BS = const_cast<BinarySection *>(&*Res.first);
|
|
|
|
// Only register sections with addresses in the AddressToSection map.
|
|
|
|
if (Section.getAddress())
|
|
|
|
AddressToSection.insert(std::make_pair(Section.getAddress(), BS));
|
|
|
|
NameToSection.insert(std::make_pair(Name, BS));
|
|
|
|
return *BS;
|
|
|
|
}
|
|
|
|
|
2017-08-27 17:04:06 -07:00
|
|
|
ErrorOr<uint64_t>
|
|
|
|
BinaryContext::extractPointerAtAddress(uint64_t Address) const {
|
|
|
|
auto Section = getSectionForAddress(Address);
|
|
|
|
if (!Section)
|
2018-01-23 15:10:24 -08:00
|
|
|
return std::make_error_code(std::errc::bad_address);
|
2017-08-27 17:04:06 -07:00
|
|
|
|
2018-01-23 15:10:24 -08:00
|
|
|
StringRef SectionContents = Section->getContents();
|
2017-08-27 17:04:06 -07:00
|
|
|
DataExtractor DE(SectionContents,
|
|
|
|
AsmInfo->isLittleEndian(),
|
|
|
|
AsmInfo->getPointerSize());
|
|
|
|
uint32_t SectionOffset = Address - Section->getAddress();
|
|
|
|
return DE.getAddress(&SectionOffset);
|
|
|
|
}
|
|
|
|
|
2018-01-23 15:10:24 -08:00
|
|
|
void BinaryContext::addSectionRelocation(BinarySection &Section,
|
|
|
|
uint64_t Offset,
|
|
|
|
MCSymbol *Symbol,
|
|
|
|
uint64_t Type,
|
2016-09-27 19:09:38 -07:00
|
|
|
uint64_t Addend) {
|
2018-01-23 15:10:24 -08:00
|
|
|
Section.addRelocation(Offset, Symbol, Type, Addend);
|
2017-02-21 16:15:15 -08:00
|
|
|
}
|
|
|
|
|
2018-01-23 15:10:24 -08:00
|
|
|
void BinaryContext::addRelocation(uint64_t Address,
|
|
|
|
MCSymbol *Symbol,
|
|
|
|
uint64_t Type,
|
|
|
|
uint64_t Addend) {
|
|
|
|
auto Section = getSectionForAddress(Address);
|
|
|
|
assert(Section && "cannot find section for address");
|
|
|
|
Section->addRelocation(Address - Section->getAddress(), Symbol, Type, Addend);
|
2017-02-21 16:15:15 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
void BinaryContext::removeRelocationAt(uint64_t Address) {
|
2018-01-23 15:10:24 -08:00
|
|
|
auto Section = getSectionForAddress(Address);
|
|
|
|
assert(Section && "cannot find section for address");
|
|
|
|
Section->removeRelocationAt(Address - Section->getAddress());
|
2017-02-21 16:15:15 -08:00
|
|
|
}
|
|
|
|
|
2017-12-11 17:07:56 -08:00
|
|
|
const Relocation *BinaryContext::getRelocationAt(uint64_t Address) {
|
2018-01-23 15:10:24 -08:00
|
|
|
auto Section = getSectionForAddress(Address);
|
|
|
|
assert(Section && "cannot find section for address");
|
|
|
|
return Section->getRelocationAt(Address - Section->getAddress());
|
2017-10-20 12:11:34 -07:00
|
|
|
}
|