Summary: This is the fifth patch to improve module loading in a series that started here (where I explain the motivation and solution): D62499 Reading strings with ReadMemory is really slow when reading the path of the shared library. This is because we don't know the length of the path so use PATH_MAX (4096) and these strings are actually super close to the boundary of an unreadable page. So even though we use process_vm_readv it will usually fail because the read size spans to the unreadable page and we then default to read the string word by word with ptrace. This new function is very similar to another ReadCStringFromMemory that already exists in lldb that makes sure it never reads cross page boundaries and checks if we already read the entire string by finding '\0'. I was able to reduce the GetLoadedSharedLibraries call from 30ms to 4ms (or something of that order). Reviewers: clayborg, xiaobai, labath Reviewed By: labath Subscribers: emaste, lldb-commits Tags: #lldb Differential Revision: https://reviews.llvm.org/D62503 llvm-svn: 363750
178 lines
6.3 KiB
C++
178 lines
6.3 KiB
C++
//===-- NativeProcessELF.cpp ---------------------------------- -*- 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 "NativeProcessELF.h"
|
|
|
|
#include "lldb/Utility/DataExtractor.h"
|
|
|
|
namespace lldb_private {
|
|
|
|
llvm::Optional<uint64_t>
|
|
NativeProcessELF::GetAuxValue(enum AuxVector::EntryType type) {
|
|
if (m_aux_vector == nullptr) {
|
|
auto buffer_or_error = GetAuxvData();
|
|
if (!buffer_or_error)
|
|
return llvm::None;
|
|
DataExtractor auxv_data(buffer_or_error.get()->getBufferStart(),
|
|
buffer_or_error.get()->getBufferSize(),
|
|
GetByteOrder(), GetAddressByteSize());
|
|
m_aux_vector = llvm::make_unique<AuxVector>(auxv_data);
|
|
}
|
|
|
|
return m_aux_vector->GetAuxValue(type);
|
|
}
|
|
|
|
lldb::addr_t NativeProcessELF::GetSharedLibraryInfoAddress() {
|
|
if (!m_shared_library_info_addr.hasValue()) {
|
|
if (GetAddressByteSize() == 8)
|
|
m_shared_library_info_addr =
|
|
GetELFImageInfoAddress<llvm::ELF::Elf64_Ehdr, llvm::ELF::Elf64_Phdr,
|
|
llvm::ELF::Elf64_Dyn>();
|
|
else
|
|
m_shared_library_info_addr =
|
|
GetELFImageInfoAddress<llvm::ELF::Elf32_Ehdr, llvm::ELF::Elf32_Phdr,
|
|
llvm::ELF::Elf32_Dyn>();
|
|
}
|
|
|
|
return m_shared_library_info_addr.getValue();
|
|
}
|
|
|
|
template <typename ELF_EHDR, typename ELF_PHDR, typename ELF_DYN>
|
|
lldb::addr_t NativeProcessELF::GetELFImageInfoAddress() {
|
|
llvm::Optional<uint64_t> maybe_phdr_addr =
|
|
GetAuxValue(AuxVector::AUXV_AT_PHDR);
|
|
llvm::Optional<uint64_t> maybe_phdr_entry_size =
|
|
GetAuxValue(AuxVector::AUXV_AT_PHENT);
|
|
llvm::Optional<uint64_t> maybe_phdr_num_entries =
|
|
GetAuxValue(AuxVector::AUXV_AT_PHNUM);
|
|
if (!maybe_phdr_addr || !maybe_phdr_entry_size || !maybe_phdr_num_entries)
|
|
return LLDB_INVALID_ADDRESS;
|
|
lldb::addr_t phdr_addr = *maybe_phdr_addr;
|
|
size_t phdr_entry_size = *maybe_phdr_entry_size;
|
|
size_t phdr_num_entries = *maybe_phdr_num_entries;
|
|
|
|
// Find the PT_DYNAMIC segment (.dynamic section) in the program header and
|
|
// what the load bias by calculating the difference of the program header
|
|
// load address and its virtual address.
|
|
lldb::offset_t load_bias;
|
|
bool found_load_bias = false;
|
|
lldb::addr_t dynamic_section_addr = 0;
|
|
uint64_t dynamic_section_size = 0;
|
|
bool found_dynamic_section = false;
|
|
ELF_PHDR phdr_entry;
|
|
for (size_t i = 0; i < phdr_num_entries; i++) {
|
|
size_t bytes_read;
|
|
auto error = ReadMemory(phdr_addr + i * phdr_entry_size, &phdr_entry,
|
|
sizeof(phdr_entry), bytes_read);
|
|
if (!error.Success())
|
|
return LLDB_INVALID_ADDRESS;
|
|
if (phdr_entry.p_type == llvm::ELF::PT_PHDR) {
|
|
load_bias = phdr_addr - phdr_entry.p_vaddr;
|
|
found_load_bias = true;
|
|
}
|
|
|
|
if (phdr_entry.p_type == llvm::ELF::PT_DYNAMIC) {
|
|
dynamic_section_addr = phdr_entry.p_vaddr;
|
|
dynamic_section_size = phdr_entry.p_memsz;
|
|
found_dynamic_section = true;
|
|
}
|
|
}
|
|
|
|
if (!found_load_bias || !found_dynamic_section)
|
|
return LLDB_INVALID_ADDRESS;
|
|
|
|
// Find the DT_DEBUG entry in the .dynamic section
|
|
dynamic_section_addr += load_bias;
|
|
ELF_DYN dynamic_entry;
|
|
size_t dynamic_num_entries = dynamic_section_size / sizeof(dynamic_entry);
|
|
for (size_t i = 0; i < dynamic_num_entries; i++) {
|
|
size_t bytes_read;
|
|
auto error = ReadMemory(dynamic_section_addr + i * sizeof(dynamic_entry),
|
|
&dynamic_entry, sizeof(dynamic_entry), bytes_read);
|
|
if (!error.Success())
|
|
return LLDB_INVALID_ADDRESS;
|
|
// Return the &DT_DEBUG->d_ptr which points to r_debug which contains the
|
|
// link_map.
|
|
if (dynamic_entry.d_tag == llvm::ELF::DT_DEBUG) {
|
|
return dynamic_section_addr + i * sizeof(dynamic_entry) +
|
|
sizeof(dynamic_entry.d_tag);
|
|
}
|
|
}
|
|
|
|
return LLDB_INVALID_ADDRESS;
|
|
}
|
|
|
|
template <typename T>
|
|
llvm::Expected<SVR4LibraryInfo>
|
|
NativeProcessELF::ReadSVR4LibraryInfo(lldb::addr_t link_map_addr) {
|
|
ELFLinkMap<T> link_map;
|
|
size_t bytes_read;
|
|
auto error =
|
|
ReadMemory(link_map_addr, &link_map, sizeof(link_map), bytes_read);
|
|
if (!error.Success())
|
|
return error.ToError();
|
|
|
|
char name_buffer[PATH_MAX];
|
|
llvm::Expected<llvm::StringRef> string_or_error = ReadCStringFromMemory(
|
|
link_map.l_name, &name_buffer[0], sizeof(name_buffer), bytes_read);
|
|
if (!string_or_error)
|
|
return string_or_error.takeError();
|
|
|
|
SVR4LibraryInfo info;
|
|
info.name = string_or_error->str();
|
|
info.link_map = link_map_addr;
|
|
info.base_addr = link_map.l_addr;
|
|
info.ld_addr = link_map.l_ld;
|
|
info.next = link_map.l_next;
|
|
|
|
return info;
|
|
}
|
|
|
|
llvm::Expected<std::vector<SVR4LibraryInfo>>
|
|
NativeProcessELF::GetLoadedSVR4Libraries() {
|
|
// Address of DT_DEBUG.d_ptr which points to r_debug
|
|
lldb::addr_t info_address = GetSharedLibraryInfoAddress();
|
|
if (info_address == LLDB_INVALID_ADDRESS)
|
|
return llvm::createStringError(llvm::inconvertibleErrorCode(),
|
|
"Invalid shared library info address");
|
|
// Address of r_debug
|
|
lldb::addr_t address = 0;
|
|
size_t bytes_read;
|
|
auto status =
|
|
ReadMemory(info_address, &address, GetAddressByteSize(), bytes_read);
|
|
if (!status.Success())
|
|
return status.ToError();
|
|
if (address == 0)
|
|
return llvm::createStringError(llvm::inconvertibleErrorCode(),
|
|
"Invalid r_debug address");
|
|
// Read r_debug.r_map
|
|
lldb::addr_t link_map = 0;
|
|
status = ReadMemory(address + GetAddressByteSize(), &link_map,
|
|
GetAddressByteSize(), bytes_read);
|
|
if (!status.Success())
|
|
return status.ToError();
|
|
if (address == 0)
|
|
return llvm::createStringError(llvm::inconvertibleErrorCode(),
|
|
"Invalid link_map address");
|
|
|
|
std::vector<SVR4LibraryInfo> library_list;
|
|
while (link_map) {
|
|
llvm::Expected<SVR4LibraryInfo> info =
|
|
GetAddressByteSize() == 8 ? ReadSVR4LibraryInfo<uint64_t>(link_map)
|
|
: ReadSVR4LibraryInfo<uint32_t>(link_map);
|
|
if (!info)
|
|
return info.takeError();
|
|
if (!info->name.empty() && info->base_addr != 0)
|
|
library_list.push_back(*info);
|
|
link_map = info->next;
|
|
}
|
|
|
|
return library_list;
|
|
}
|
|
|
|
} // namespace lldb_private
|