Skip to content

Commit 536abf8

Browse files
authored
Read and store gnu build id from loaded core file (#92078)
As we have debuginfod as symbol locator available in lldb now, we want to make full use of it. In case of post mortem debugging, we don't always have the main executable available. However, the .note.gnu.build-id of the main executable(some other modules too), should be available in the core file, as those binaries are loaded in memory and dumped in the core file. We try to iterate through the NT_FILE entries, read and store the gnu build id if possible. This will be very useful as this id is the unique key which is needed for querying the debuginfod server. Test: Build and run lldb. Breakpoint set to https://github.com/llvm/llvm-project/blob/main/lldb/source/Plugins/SymbolLocator/Debuginfod/SymbolLocatorDebuginfod.cpp#L147 Verified after this commit, module_uuid is the correct gnu build id of the main executable which caused the crash(first in the NT_FILE entry)
1 parent 67beebf commit 536abf8

File tree

5 files changed

+140
-59
lines changed

5 files changed

+140
-59
lines changed

lldb/include/lldb/Target/Process.h

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,36 @@ class Process : public std::enable_shared_from_this<Process>,
406406
lldb::StateType state);
407407
} Notifications;
408408

409+
class ProcessMemoryIterator {
410+
public:
411+
ProcessMemoryIterator(lldb::ProcessSP process_sp, lldb::addr_t base)
412+
: m_process_sp(process_sp), m_base_addr(base) {
413+
lldbassert(process_sp.get() != nullptr);
414+
}
415+
416+
bool IsValid() { return m_is_valid; }
417+
418+
uint8_t operator[](lldb::addr_t offset) {
419+
if (!IsValid())
420+
return 0;
421+
422+
uint8_t retval = 0;
423+
Status error;
424+
if (0 ==
425+
m_process_sp->ReadMemory(m_base_addr + offset, &retval, 1, error)) {
426+
m_is_valid = false;
427+
return 0;
428+
}
429+
430+
return retval;
431+
}
432+
433+
private:
434+
lldb::ProcessSP m_process_sp;
435+
lldb::addr_t m_base_addr;
436+
bool m_is_valid = true;
437+
};
438+
409439
class ProcessEventData : public EventData {
410440
friend class Process;
411441

@@ -1649,6 +1679,26 @@ class Process : public std::enable_shared_from_this<Process>,
16491679

16501680
lldb::addr_t ReadPointerFromMemory(lldb::addr_t vm_addr, Status &error);
16511681

1682+
/// Find a string within a memory region.
1683+
///
1684+
/// This function searches for the string represented by the provided buffer
1685+
/// within the memory range specified by the low and high addresses. It uses
1686+
/// a bad character heuristic to optimize the search process.
1687+
///
1688+
/// \param[in] low The starting address of the memory region to be searched.
1689+
///
1690+
/// \param[in] high The ending address of the memory region to be searched.
1691+
///
1692+
/// \param[in] buffer A pointer to the buffer containing the string to be
1693+
/// searched.
1694+
///
1695+
/// \param[in] buffer_size The size of the buffer in bytes.
1696+
///
1697+
/// \return The address where the string was found or LLDB_INVALID_ADDRESS if
1698+
/// not found.
1699+
lldb::addr_t FindInMemory(lldb::addr_t low, lldb::addr_t high,
1700+
uint8_t *buffer, size_t buffer_size);
1701+
16521702
bool WritePointerToMemory(lldb::addr_t vm_addr, lldb::addr_t ptr_value,
16531703
Status &error);
16541704

lldb/source/Commands/CommandObjectMemory.cpp

Lines changed: 2 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -977,35 +977,6 @@ class CommandObjectMemoryFind : public CommandObjectParsed {
977977
Options *GetOptions() override { return &m_option_group; }
978978

979979
protected:
980-
class ProcessMemoryIterator {
981-
public:
982-
ProcessMemoryIterator(ProcessSP process_sp, lldb::addr_t base)
983-
: m_process_sp(process_sp), m_base_addr(base) {
984-
lldbassert(process_sp.get() != nullptr);
985-
}
986-
987-
bool IsValid() { return m_is_valid; }
988-
989-
uint8_t operator[](lldb::addr_t offset) {
990-
if (!IsValid())
991-
return 0;
992-
993-
uint8_t retval = 0;
994-
Status error;
995-
if (0 ==
996-
m_process_sp->ReadMemory(m_base_addr + offset, &retval, 1, error)) {
997-
m_is_valid = false;
998-
return 0;
999-
}
1000-
1001-
return retval;
1002-
}
1003-
1004-
private:
1005-
ProcessSP m_process_sp;
1006-
lldb::addr_t m_base_addr;
1007-
bool m_is_valid = true;
1008-
};
1009980
void DoExecute(Args &command, CommandReturnObject &result) override {
1010981
// No need to check "process" for validity as eCommandRequiresProcess
1011982
// ensures it is valid
@@ -1106,8 +1077,8 @@ class CommandObjectMemoryFind : public CommandObjectParsed {
11061077
found_location = low_addr;
11071078
bool ever_found = false;
11081079
while (count) {
1109-
found_location = FastSearch(found_location, high_addr, buffer.GetBytes(),
1110-
buffer.GetByteSize());
1080+
found_location = process->FindInMemory(
1081+
found_location, high_addr, buffer.GetBytes(), buffer.GetByteSize());
11111082
if (found_location == LLDB_INVALID_ADDRESS) {
11121083
if (!ever_found) {
11131084
result.AppendMessage("data not found within the range.\n");
@@ -1144,34 +1115,6 @@ class CommandObjectMemoryFind : public CommandObjectParsed {
11441115
result.SetStatus(lldb::eReturnStatusSuccessFinishResult);
11451116
}
11461117

1147-
lldb::addr_t FastSearch(lldb::addr_t low, lldb::addr_t high, uint8_t *buffer,
1148-
size_t buffer_size) {
1149-
const size_t region_size = high - low;
1150-
1151-
if (region_size < buffer_size)
1152-
return LLDB_INVALID_ADDRESS;
1153-
1154-
std::vector<size_t> bad_char_heuristic(256, buffer_size);
1155-
ProcessSP process_sp = m_exe_ctx.GetProcessSP();
1156-
ProcessMemoryIterator iterator(process_sp, low);
1157-
1158-
for (size_t idx = 0; idx < buffer_size - 1; idx++) {
1159-
decltype(bad_char_heuristic)::size_type bcu_idx = buffer[idx];
1160-
bad_char_heuristic[bcu_idx] = buffer_size - idx - 1;
1161-
}
1162-
for (size_t s = 0; s <= (region_size - buffer_size);) {
1163-
int64_t j = buffer_size - 1;
1164-
while (j >= 0 && buffer[j] == iterator[s + j])
1165-
j--;
1166-
if (j < 0)
1167-
return low + s;
1168-
else
1169-
s += bad_char_heuristic[iterator[s + buffer_size - 1]];
1170-
}
1171-
1172-
return LLDB_INVALID_ADDRESS;
1173-
}
1174-
11751118
OptionGroupOptions m_option_group;
11761119
OptionGroupFindMemory m_memory_options;
11771120
OptionGroupMemoryTag m_memory_tag_options;

lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@
66
//
77
//===----------------------------------------------------------------------===//
88

9+
#include <cstddef>
910
#include <cstdlib>
1011

1112
#include <memory>
1213
#include <mutex>
14+
#include <tuple>
1315

1416
#include "lldb/Core/Module.h"
1517
#include "lldb/Core/ModuleSpec.h"
@@ -210,6 +212,9 @@ Status ProcessElfCore::DoLoadCore() {
210212
}
211213
}
212214

215+
// We need to update uuid after address range is populated.
216+
UpdateBuildIdForNTFileEntries();
217+
213218
if (!ranges_are_sorted) {
214219
m_core_aranges.Sort();
215220
m_core_range_infos.Sort();
@@ -258,6 +263,7 @@ Status ProcessElfCore::DoLoadCore() {
258263
if (!m_nt_file_entries.empty()) {
259264
ModuleSpec exe_module_spec;
260265
exe_module_spec.GetArchitecture() = arch;
266+
exe_module_spec.GetUUID() = m_nt_file_entries[0].uuid;
261267
exe_module_spec.GetFileSpec().SetFile(m_nt_file_entries[0].path,
262268
FileSpec::Style::native);
263269
if (exe_module_spec.GetFileSpec()) {
@@ -271,6 +277,16 @@ Status ProcessElfCore::DoLoadCore() {
271277
return error;
272278
}
273279

280+
void ProcessElfCore::UpdateBuildIdForNTFileEntries() {
281+
if (!m_nt_file_entries.empty()) {
282+
for (NT_FILE_Entry &entry : m_nt_file_entries) {
283+
std::optional<UUID> uuid = FindBuildId(entry);
284+
if (uuid)
285+
entry.uuid = uuid.value();
286+
}
287+
}
288+
}
289+
274290
lldb_private::DynamicLoader *ProcessElfCore::GetDynamicLoader() {
275291
if (m_dyld_up.get() == nullptr)
276292
m_dyld_up.reset(DynamicLoader::FindPlugin(
@@ -983,6 +999,40 @@ llvm::Error ProcessElfCore::ParseThreadContextsFromNoteSegment(
983999
}
9841000
}
9851001

1002+
bool ProcessElfCore::IsElf(const NT_FILE_Entry entry) {
1003+
size_t size = strlen(llvm::ELF::ElfMagic);
1004+
uint8_t buf[size];
1005+
Status error;
1006+
size_t byte_read = ReadMemory(entry.start, buf, size, error);
1007+
if (byte_read == size)
1008+
return memcmp(llvm::ELF::ElfMagic, buf, size) == 0;
1009+
else
1010+
return false;
1011+
}
1012+
1013+
std::optional<UUID> ProcessElfCore::FindBuildId(const NT_FILE_Entry entry) {
1014+
if (!IsElf(entry))
1015+
return std::nullopt;
1016+
// Build ID is stored in the ELF file as a section named ".note.gnu.build-id"
1017+
uint8_t gnu_build_id_bytes[8] = {0x03, 0x00, 0x00, 0x00,
1018+
0x47, 0x4e, 0x55, 0x00};
1019+
lldb::addr_t gnu_build_id_addr =
1020+
FindInMemory(entry.start, entry.end, gnu_build_id_bytes, 8);
1021+
if (gnu_build_id_addr == LLDB_INVALID_ADDRESS)
1022+
return std::nullopt;
1023+
uint8_t buf[36];
1024+
Status error;
1025+
size_t byte_read = ReadMemory(gnu_build_id_addr - 8, buf, 36, error);
1026+
// .note.gnu.build-id starts with 04 00 00 00 {id_byte_size} 00 00 00 03 00 00
1027+
// 00 47 4e 55 00
1028+
if (byte_read == 36) {
1029+
if (buf[0] == 0x04) {
1030+
return UUID(llvm::ArrayRef<uint8_t>(buf + 16, buf[4] /*byte size*/));
1031+
}
1032+
}
1033+
return std::nullopt;
1034+
}
1035+
9861036
uint32_t ProcessElfCore::GetNumThreadContexts() {
9871037
if (!m_thread_data_valid)
9881038
DoLoadCore();

lldb/source/Plugins/Process/elf-core/ProcessElfCore.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ class ProcessElfCore : public lldb_private::PostMortemProcess {
117117
lldb::addr_t end;
118118
lldb::addr_t file_ofs;
119119
std::string path;
120+
lldb_private::UUID
121+
uuid; // extracted from .note.gnu.build-id section from core file
120122
};
121123

122124
// For ProcessElfCore only
@@ -158,6 +160,15 @@ class ProcessElfCore : public lldb_private::PostMortemProcess {
158160
// Returns number of thread contexts stored in the core file
159161
uint32_t GetNumThreadContexts();
160162

163+
// Populate gnu uuid for each NT_FILE entry
164+
void UpdateBuildIdForNTFileEntries();
165+
166+
// Returns the UUID of a given NT_FILE entry
167+
std::optional<lldb_private::UUID> FindBuildId(const NT_FILE_Entry entry);
168+
169+
// Returns true if the given NT_FILE entry is an ELF file
170+
bool IsElf(const NT_FILE_Entry entry);
171+
161172
// Parse a contiguous address range of the process from LOAD segment
162173
lldb::addr_t
163174
AddAddressRangeFromLoadSegment(const elf::ELFProgramHeader &header);

lldb/source/Target/Process.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3191,6 +3191,33 @@ Status Process::Halt(bool clear_thread_plans, bool use_run_lock) {
31913191
return Status();
31923192
}
31933193

3194+
lldb::addr_t Process::FindInMemory(lldb::addr_t low, lldb::addr_t high,
3195+
uint8_t *buffer, size_t buffer_size) {
3196+
const size_t region_size = high - low;
3197+
3198+
if (region_size < buffer_size)
3199+
return LLDB_INVALID_ADDRESS;
3200+
3201+
std::vector<size_t> bad_char_heuristic(256, buffer_size);
3202+
ProcessMemoryIterator iterator(shared_from_this(), low);
3203+
3204+
for (size_t idx = 0; idx < buffer_size - 1; idx++) {
3205+
decltype(bad_char_heuristic)::size_type bcu_idx = buffer[idx];
3206+
bad_char_heuristic[bcu_idx] = buffer_size - idx - 1;
3207+
}
3208+
for (size_t s = 0; s <= (region_size - buffer_size);) {
3209+
int64_t j = buffer_size - 1;
3210+
while (j >= 0 && buffer[j] == iterator[s + j])
3211+
j--;
3212+
if (j < 0)
3213+
return low + s;
3214+
else
3215+
s += bad_char_heuristic[iterator[s + buffer_size - 1]];
3216+
}
3217+
3218+
return LLDB_INVALID_ADDRESS;
3219+
}
3220+
31943221
Status Process::StopForDestroyOrDetach(lldb::EventSP &exit_event_sp) {
31953222
Status error;
31963223

0 commit comments

Comments
 (0)