summaryrefslogtreecommitdiffstats
path: root/lldb/source
diff options
context:
space:
mode:
authorPavel Labath <pavel@labath.sk>2019-11-20 16:25:55 +0100
committerPavel Labath <pavel@labath.sk>2019-12-09 13:39:08 +0100
commit329008fdf1889c0554f7afbb426f829f98327c78 (patch)
tree99605a3f78ed2035e6d59f5b37d98add7e039b6d /lldb/source
parentdba420bc05aec26c2b6d060cfd186169da30b5f7 (diff)
downloadbcm5719-llvm-329008fdf1889c0554f7afbb426f829f98327c78.tar.gz
bcm5719-llvm-329008fdf1889c0554f7afbb426f829f98327c78.zip
[lldb] Improve/fix base address selection in location lists
Summary: Lldb support base address selection entries in location lists was broken for a long time. This wasn't noticed until llvm started producing these kinds of entries more frequently with r374600. In r374769, I made a quick patch which added sufficient support for them to get the test suite to pass. However, I did not fully understand how this code operates, and so the fix was not complete. Specifically, what was lacking was the ability to handle modules which were not loaded at their preferred load address (for instance, due to ASLR). Now that I better understand how this code works, I've come to the conclusion that the current setup does not provide enough information to correctly process these entries. In the current setup the location lists were parameterized by two addresses: - the distance of the function start from the start of the compile unit. The purpose of this was to make the location ranges relative to the start of the function. - the actual address where the function was loaded at. With this the function-start-relative ranges can be translated to actual memory locations. The reason for the two values, instead of just one (the load bias) is (I think) MachO, where the debug info in the object files will appear to be relative to the address zero, but the actual code it refers to can be moved and reordered by the linker. This means that the location lists need to be "linked" to reflect the locations in the actual linked file. These two bits of information were enough to correctly process location lists which do not contain base address selection entries (and so all entries are relative to the CU base). However, they don't work with them because, in theory two base address can be completely unrelated (as can happen for instace with hot/cold function splitting, where the linker can reorder the two pars arbitrarily). To fix that, I split the first parameter into two: - the compile unit base address - the function start address, as is known in the object file The new algorithm becomes: - the location lists are processed as they were meant to be processed. The CU base address is used as the initial base address value. Base address selection entries can set a new base. - the difference between the "file" and "load" function start addresses is used to compute the load bias. This value is added to the final ranges to get the actual memory location. This algorithm is correct for non-MachO debug info, as there the location lists correctly describe the code in the final executable, and the dynamic linker can just move the entire module, not pieces of it. It will also be correct for MachO if the static linker preserves relative positions of the various parts of the location lists -- I don't know whether it actually does that, but judging by the lack of base address selection support in dsymutil and lldb, this isn't something that has come up in the past. I add a test case which simulates the ASLR scenario and demonstrates that base address selection entries now work correctly here. Reviewers: JDevlieghere, aprantl, clayborg Subscribers: dblaikie, lldb-commits Tags: #lldb Differential Revision: https://reviews.llvm.org/D70532
Diffstat (limited to 'lldb/source')
-rw-r--r--lldb/source/Expression/DWARFExpression.cpp109
-rw-r--r--lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp6
-rw-r--r--lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp6
3 files changed, 64 insertions, 57 deletions
diff --git a/lldb/source/Expression/DWARFExpression.cpp b/lldb/source/Expression/DWARFExpression.cpp
index 1297255a38b..9193c17673e 100644
--- a/lldb/source/Expression/DWARFExpression.cpp
+++ b/lldb/source/Expression/DWARFExpression.cpp
@@ -56,13 +56,13 @@ ReadAddressFromDebugAddrSection(const DWARFUnit *dwarf_cu,
// DWARFExpression constructor
DWARFExpression::DWARFExpression()
: m_module_wp(), m_data(), m_dwarf_cu(nullptr),
- m_reg_kind(eRegisterKindDWARF), m_loclist_slide(LLDB_INVALID_ADDRESS) {}
+ m_reg_kind(eRegisterKindDWARF) {}
DWARFExpression::DWARFExpression(lldb::ModuleSP module_sp,
const DataExtractor &data,
const DWARFUnit *dwarf_cu)
: m_module_wp(), m_data(data), m_dwarf_cu(dwarf_cu),
- m_reg_kind(eRegisterKindDWARF), m_loclist_slide(LLDB_INVALID_ADDRESS) {
+ m_reg_kind(eRegisterKindDWARF) {
if (module_sp)
m_module_wp = module_sp;
}
@@ -94,8 +94,9 @@ void DWARFExpression::DumpLocation(Stream *s, lldb::offset_t offset,
nullptr);
}
-void DWARFExpression::SetLocationListSlide(addr_t slide) {
- m_loclist_slide = slide;
+void DWARFExpression::SetLocationListAddresses(addr_t cu_file_addr,
+ addr_t func_file_addr) {
+ m_loclist_addresses = LoclistAddresses{cu_file_addr, func_file_addr};
}
int DWARFExpression::GetRegisterKind() { return m_reg_kind; }
@@ -105,7 +106,7 @@ void DWARFExpression::SetRegisterKind(RegisterKind reg_kind) {
}
bool DWARFExpression::IsLocationList() const {
- return m_loclist_slide != LLDB_INVALID_ADDRESS;
+ return bool(m_loclist_addresses);
}
void DWARFExpression::GetDescription(Stream *s, lldb::DescriptionLevel level,
@@ -614,46 +615,43 @@ bool DWARFExpression::LinkThreadLocalStorage(
return true;
}
-bool DWARFExpression::LocationListContainsAddress(
- lldb::addr_t loclist_base_addr, lldb::addr_t addr) const {
- if (addr == LLDB_INVALID_ADDRESS)
+bool DWARFExpression::LocationListContainsAddress(addr_t func_load_addr,
+ lldb::addr_t addr) const {
+ if (func_load_addr == LLDB_INVALID_ADDRESS || addr == LLDB_INVALID_ADDRESS)
return false;
- if (IsLocationList()) {
- lldb::offset_t offset = 0;
-
- if (loclist_base_addr == LLDB_INVALID_ADDRESS)
- return false;
+ if (!IsLocationList())
+ return false;
- while (m_data.ValidOffset(offset)) {
- // We need to figure out what the value is for the location.
- addr_t lo_pc = LLDB_INVALID_ADDRESS;
- addr_t hi_pc = LLDB_INVALID_ADDRESS;
- if (!AddressRangeForLocationListEntry(m_dwarf_cu, m_data, &offset, lo_pc,
- hi_pc))
- break;
+ lldb::offset_t offset = 0;
+ lldb::addr_t base_address = m_loclist_addresses->cu_file_addr;
+ while (m_data.ValidOffset(offset)) {
+ // We need to figure out what the value is for the location.
+ addr_t lo_pc = LLDB_INVALID_ADDRESS;
+ addr_t hi_pc = LLDB_INVALID_ADDRESS;
+ if (!AddressRangeForLocationListEntry(m_dwarf_cu, m_data, &offset, lo_pc,
+ hi_pc))
+ break;
- if (lo_pc == 0 && hi_pc == 0)
- break;
+ if (lo_pc == 0 && hi_pc == 0)
+ break;
- if ((m_data.GetAddressByteSize() == 4 && (lo_pc == UINT32_MAX)) ||
- (m_data.GetAddressByteSize() == 8 && (lo_pc == UINT64_MAX))) {
- loclist_base_addr = hi_pc + m_loclist_slide;
- continue;
- }
- lo_pc += loclist_base_addr - m_loclist_slide;
- hi_pc += loclist_base_addr - m_loclist_slide;
+ if ((m_data.GetAddressByteSize() == 4 && (lo_pc == UINT32_MAX)) ||
+ (m_data.GetAddressByteSize() == 8 && (lo_pc == UINT64_MAX))) {
+ base_address = hi_pc;
+ continue;
+ }
+ RelocateLowHighPC(base_address, func_load_addr, lo_pc, hi_pc);
- if (lo_pc <= addr && addr < hi_pc)
- return true;
+ if (lo_pc <= addr && addr < hi_pc)
+ return true;
- offset += m_data.GetU16(&offset);
- }
+ offset += m_data.GetU16(&offset);
}
return false;
}
-bool DWARFExpression::GetLocation(addr_t base_addr, addr_t pc,
+bool DWARFExpression::GetLocation(addr_t func_load_addr, addr_t pc,
lldb::offset_t &offset,
lldb::offset_t &length) {
offset = 0;
@@ -662,9 +660,8 @@ bool DWARFExpression::GetLocation(addr_t base_addr, addr_t pc,
return true;
}
- if (base_addr != LLDB_INVALID_ADDRESS && pc != LLDB_INVALID_ADDRESS) {
- addr_t curr_base_addr = base_addr;
-
+ if (func_load_addr != LLDB_INVALID_ADDRESS && pc != LLDB_INVALID_ADDRESS) {
+ addr_t base_address = m_loclist_addresses->cu_file_addr;
while (m_data.ValidOffset(offset)) {
// We need to figure out what the value is for the location.
addr_t lo_pc = LLDB_INVALID_ADDRESS;
@@ -678,13 +675,11 @@ bool DWARFExpression::GetLocation(addr_t base_addr, addr_t pc,
if ((m_data.GetAddressByteSize() == 4 && (lo_pc == UINT32_MAX)) ||
(m_data.GetAddressByteSize() == 8 && (lo_pc == UINT64_MAX))) {
- curr_base_addr = hi_pc + m_loclist_slide;
+ base_address = hi_pc;
continue;
}
- lo_pc += curr_base_addr - m_loclist_slide;
- hi_pc += curr_base_addr - m_loclist_slide;
-
+ RelocateLowHighPC(base_address, func_load_addr, lo_pc, hi_pc);
length = m_data.GetU16(&offset);
if (length > 0 && lo_pc <= pc && pc < hi_pc)
@@ -700,12 +695,12 @@ bool DWARFExpression::GetLocation(addr_t base_addr, addr_t pc,
bool DWARFExpression::DumpLocationForAddress(Stream *s,
lldb::DescriptionLevel level,
- addr_t base_addr, addr_t address,
- ABI *abi) {
+ addr_t func_load_addr,
+ addr_t address, ABI *abi) {
lldb::offset_t offset = 0;
lldb::offset_t length = 0;
- if (GetLocation(base_addr, address, offset, length)) {
+ if (GetLocation(func_load_addr, address, offset, length)) {
if (length > 0) {
DumpLocation(s, offset, length, level, abi);
return true;
@@ -936,7 +931,7 @@ bool DWARFExpression::Evaluate(ExecutionContextScope *exe_scope,
bool DWARFExpression::Evaluate(ExecutionContext *exe_ctx,
RegisterContext *reg_ctx,
- lldb::addr_t loclist_base_load_addr,
+ lldb::addr_t func_load_addr,
const Value *initial_value_ptr,
const Value *object_address_ptr, Value &result,
Status *error_ptr) const {
@@ -958,15 +953,14 @@ bool DWARFExpression::Evaluate(ExecutionContext *exe_ctx,
pc = reg_ctx_sp->GetPC();
}
- if (loclist_base_load_addr != LLDB_INVALID_ADDRESS) {
+ if (func_load_addr != LLDB_INVALID_ADDRESS) {
if (pc == LLDB_INVALID_ADDRESS) {
if (error_ptr)
error_ptr->SetErrorString("Invalid PC in frame.");
return false;
}
- addr_t curr_loclist_base_load_addr = loclist_base_load_addr;
-
+ addr_t base_address = m_loclist_addresses->cu_file_addr;
while (m_data.ValidOffset(offset)) {
// We need to figure out what the value is for the location.
addr_t lo_pc = LLDB_INVALID_ADDRESS;
@@ -982,12 +976,11 @@ bool DWARFExpression::Evaluate(ExecutionContext *exe_ctx,
(lo_pc == UINT32_MAX)) ||
(m_data.GetAddressByteSize() == 8 &&
(lo_pc == UINT64_MAX))) {
- curr_loclist_base_load_addr = hi_pc + m_loclist_slide;
+ base_address = hi_pc;
continue;
}
- lo_pc += curr_loclist_base_load_addr - m_loclist_slide;
- hi_pc += curr_loclist_base_load_addr - m_loclist_slide;
+ RelocateLowHighPC(base_address, func_load_addr, lo_pc, hi_pc);
uint16_t length = m_data.GetU16(&offset);
if (length > 0 && lo_pc <= pc && pc < hi_pc) {
@@ -2970,6 +2963,20 @@ bool DWARFExpression::GetOpAndEndOffsets(StackFrame &frame,
return true;
}
+void DWARFExpression::RelocateLowHighPC(addr_t base_address,
+ addr_t func_load_addr, addr_t &low_pc,
+ addr_t &high_pc) const {
+ // How this works:
+ // base_address is the current base address, as known in the file. low_pc and
+ // high_pc are relative to that. First, we relocate the base address by
+ // applying the load bias (the difference between an address in the file and
+ // the actual address in memory). Then we relocate low_pc and high_pc based on
+ // that.
+ base_address += func_load_addr - m_loclist_addresses->func_file_addr;
+ low_pc += base_address;
+ high_pc += base_address;
+}
+
bool DWARFExpression::MatchesOperand(StackFrame &frame,
const Instruction::Operand &operand) {
using namespace OperandMatchers;
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp
index 1bab4e9db63..e9fdfe2e479 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp
@@ -350,8 +350,8 @@ bool DWARFDebugInfoEntry::GetDIENamesAndRanges(
*frame_base = DWARFExpression(module, data, cu);
if (lo_pc != LLDB_INVALID_ADDRESS) {
assert(lo_pc >= cu->GetBaseAddress());
- frame_base->SetLocationListSlide(lo_pc -
- cu->GetBaseAddress());
+ frame_base->SetLocationListAddresses(cu->GetBaseAddress(),
+ lo_pc);
} else {
set_frame_base_loclist_addr = true;
}
@@ -379,7 +379,7 @@ bool DWARFDebugInfoEntry::GetDIENamesAndRanges(
if (set_frame_base_loclist_addr) {
dw_addr_t lowest_range_pc = ranges.GetMinRangeBase(0);
assert(lowest_range_pc >= cu->GetBaseAddress());
- frame_base->SetLocationListSlide(lowest_range_pc - cu->GetBaseAddress());
+ frame_base->SetLocationListAddresses(cu->GetBaseAddress(), lowest_range_pc);
}
if (ranges.IsEmpty() || name == nullptr || mangled == nullptr) {
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
index 9b9077a450b..8178ccd272f 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
@@ -3362,9 +3362,9 @@ VariableSP SymbolFileDWARF::ParseVariableDIE(const SymbolContext &sc,
data = DataExtractor(data, offset, data.GetByteSize() - offset);
location = DWARFExpression(module, data, die.GetCU());
assert(func_low_pc != LLDB_INVALID_ADDRESS);
- location.SetLocationListSlide(
- func_low_pc -
- attributes.CompileUnitAtIndex(i)->GetBaseAddress());
+ location.SetLocationListAddresses(
+ attributes.CompileUnitAtIndex(i)->GetBaseAddress(),
+ func_low_pc);
}
}
} break;
OpenPOWER on IntegriCloud