| // Copyright 2020 The Fuchsia Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "src/developer/debug/debug_agent/elf_utils.h" | |
| // clang-format off | |
| // Included early because of conflicts. | |
| #include "src/lib/elflib/elflib.h" | |
| // clang-format on | |
| #include <link.h> | |
| #include <string> | |
| #include "src/developer/debug/debug_agent/process_handle.h" | |
| #include "src/developer/debug/ipc/records.h" | |
| namespace debug_agent { | |
| namespace { | |
| // Reads a null-terminated string from the given address of the given process. | |
| debug::Status ReadNullTerminatedString(const ProcessHandle& process, zx_vaddr_t vaddr, | |
| std::string* dest) { | |
| // Max size of string we'll load as a sanity check. | |
| constexpr size_t kMaxString = 32768; | |
| dest->clear(); | |
| constexpr size_t kBlockSize = 256; | |
| char block[kBlockSize]; | |
| while (dest->size() < kMaxString) { | |
| size_t num_read = 0; | |
| if (auto status = process.ReadMemory(vaddr, block, kBlockSize, &num_read); status.has_error()) | |
| return status; | |
| for (size_t i = 0; i < num_read; i++) { | |
| if (block[i] == 0) | |
| return debug::Status(); | |
| dest->push_back(block[i]); | |
| } | |
| if (num_read < kBlockSize) | |
| return debug::Status(); // Partial read: hit the mapped memory boundary. | |
| vaddr += kBlockSize; | |
| } | |
| return debug::Status(); | |
| } | |
| } // namespace | |
| debug::Status WalkElfModules(const ProcessHandle& process, uint64_t dl_debug_addr, | |
| std::function<bool(uint64_t base_addr, uint64_t lmap)> cb) { | |
| size_t num_read = 0; | |
| uint64_t lmap = 0; | |
| if (auto status = process.ReadMemory(dl_debug_addr + offsetof(r_debug, r_map), &lmap, | |
| sizeof(lmap), &num_read); | |
| status.has_error()) | |
| return status; | |
| size_t module_count = 0; | |
| // Walk the linked list. | |
| constexpr size_t kMaxObjects = 512; // Sanity threshold. | |
| while (lmap != 0) { | |
| if (module_count++ >= kMaxObjects) | |
| return debug::Status("Too many modules, memory likely corrupted."); | |
| uint64_t base; | |
| if (process.ReadMemory(lmap + offsetof(link_map, l_addr), &base, sizeof(base), &num_read) | |
| .has_error()) | |
| break; | |
| uint64_t next; | |
| if (process.ReadMemory(lmap + offsetof(link_map, l_next), &next, sizeof(next), &num_read) | |
| .has_error()) | |
| break; | |
| if (!cb(base, lmap)) | |
| break; | |
| lmap = next; | |
| } | |
| return debug::Status(); | |
| } | |
| std::vector<debug_ipc::Module> GetElfModulesForProcess(const ProcessHandle& process, | |
| uint64_t dl_debug_addr) { | |
| std::vector<debug_ipc::Module> modules; | |
| WalkElfModules(process, dl_debug_addr, [&process, &modules](uint64_t base, uint64_t lmap) { | |
| debug_ipc::Module module; | |
| module.base = base; | |
| module.debug_address = lmap; | |
| uint64_t str_addr; | |
| size_t num_read; | |
| if (process | |
| .ReadMemory(lmap + offsetof(link_map, l_name), &str_addr, sizeof(str_addr), &num_read) | |
| .has_error()) | |
| return false; | |
| if (ReadNullTerminatedString(process, str_addr, &module.name).has_error()) | |
| return false; | |
| auto elf = elflib::ElfLib::Create( | |
| [&process, base = module.base](uint64_t offset, std::vector<uint8_t>* buf) { | |
| size_t num_read = 0; | |
| if (process.ReadMemory(base + offset, buf->data(), buf->size(), &num_read).has_error()) | |
| return false; | |
| return num_read == buf->size(); | |
| }); | |
| if (elf) | |
| module.build_id = elf->GetGNUBuildID(); | |
| modules.push_back(std::move(module)); | |
| return true; | |
| }); | |
| return modules; | |
| } | |
| // The dynamic loader puts the address of the code it calls after changing the shared library | |
| // lists in r_debug.r_brk where the dl_debug_addr points to the r_debug structure. | |
| uint64_t GetLoaderBreakpointAddress(const ProcessHandle& process, uint64_t dl_debug_addr) { | |
| size_t num_read = 0; | |
| uint64_t rbrk = 0; | |
| if (process.ReadMemory(dl_debug_addr + offsetof(r_debug, r_brk), &rbrk, sizeof(rbrk), &num_read) | |
| .ok() && | |
| num_read == sizeof(rbrk)) | |
| return rbrk; | |
| return 0; | |
| } | |
| } // namespace debug_agent |