//===-- 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 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 = std::make_unique(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(); else m_shared_library_info_addr = GetELFImageInfoAddress(); } return m_shared_library_info_addr.getValue(); } template lldb::addr_t NativeProcessELF::GetELFImageInfoAddress() { llvm::Optional maybe_phdr_addr = GetAuxValue(AuxVector::AUXV_AT_PHDR); llvm::Optional maybe_phdr_entry_size = GetAuxValue(AuxVector::AUXV_AT_PHENT); llvm::Optional 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 llvm::Expected NativeProcessELF::ReadSVR4LibraryInfo(lldb::addr_t link_map_addr) { ELFLinkMap 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 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> 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 library_list; while (link_map) { llvm::Expected info = GetAddressByteSize() == 8 ? ReadSVR4LibraryInfo(link_map) : ReadSVR4LibraryInfo(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