1 | //===-- NativeProcessELF.cpp ----------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "NativeProcessELF.h" |
10 | |
11 | #include "lldb/Utility/DataExtractor.h" |
12 | #include <optional> |
13 | |
14 | namespace lldb_private { |
15 | |
16 | std::optional<uint64_t> |
17 | NativeProcessELF::GetAuxValue(enum AuxVector::EntryType type) { |
18 | if (m_aux_vector == nullptr) { |
19 | auto buffer_or_error = GetAuxvData(); |
20 | if (!buffer_or_error) |
21 | return std::nullopt; |
22 | DataExtractor auxv_data(buffer_or_error.get()->getBufferStart(), |
23 | buffer_or_error.get()->getBufferSize(), |
24 | GetByteOrder(), GetAddressByteSize()); |
25 | m_aux_vector = std::make_unique<AuxVector>(args&: auxv_data); |
26 | } |
27 | |
28 | return m_aux_vector->GetAuxValue(entry_type: type); |
29 | } |
30 | |
31 | lldb::addr_t NativeProcessELF::GetSharedLibraryInfoAddress() { |
32 | if (!m_shared_library_info_addr) { |
33 | if (GetAddressByteSize() == 8) |
34 | m_shared_library_info_addr = |
35 | GetELFImageInfoAddress<llvm::ELF::Elf64_Ehdr, llvm::ELF::Elf64_Phdr, |
36 | llvm::ELF::Elf64_Dyn>(); |
37 | else |
38 | m_shared_library_info_addr = |
39 | GetELFImageInfoAddress<llvm::ELF::Elf32_Ehdr, llvm::ELF::Elf32_Phdr, |
40 | llvm::ELF::Elf32_Dyn>(); |
41 | } |
42 | |
43 | return *m_shared_library_info_addr; |
44 | } |
45 | |
46 | template <typename ELF_EHDR, typename ELF_PHDR, typename ELF_DYN> |
47 | lldb::addr_t NativeProcessELF::GetELFImageInfoAddress() { |
48 | std::optional<uint64_t> maybe_phdr_addr = |
49 | GetAuxValue(type: AuxVector::AUXV_AT_PHDR); |
50 | std::optional<uint64_t> maybe_phdr_entry_size = |
51 | GetAuxValue(type: AuxVector::AUXV_AT_PHENT); |
52 | std::optional<uint64_t> maybe_phdr_num_entries = |
53 | GetAuxValue(type: AuxVector::AUXV_AT_PHNUM); |
54 | if (!maybe_phdr_addr || !maybe_phdr_entry_size || !maybe_phdr_num_entries) |
55 | return LLDB_INVALID_ADDRESS; |
56 | lldb::addr_t phdr_addr = *maybe_phdr_addr; |
57 | size_t phdr_entry_size = *maybe_phdr_entry_size; |
58 | size_t phdr_num_entries = *maybe_phdr_num_entries; |
59 | |
60 | // Find the PT_DYNAMIC segment (.dynamic section) in the program header and |
61 | // what the load bias by calculating the difference of the program header |
62 | // load address and its virtual address. |
63 | lldb::offset_t load_bias; |
64 | bool found_load_bias = false; |
65 | lldb::addr_t dynamic_section_addr = 0; |
66 | uint64_t dynamic_section_size = 0; |
67 | bool found_dynamic_section = false; |
68 | ELF_PHDR phdr_entry; |
69 | for (size_t i = 0; i < phdr_num_entries; i++) { |
70 | size_t bytes_read; |
71 | auto error = ReadMemory(addr: phdr_addr + i * phdr_entry_size, buf: &phdr_entry, |
72 | size: sizeof(phdr_entry), bytes_read); |
73 | if (!error.Success()) |
74 | return LLDB_INVALID_ADDRESS; |
75 | if (phdr_entry.p_type == llvm::ELF::PT_PHDR) { |
76 | load_bias = phdr_addr - phdr_entry.p_vaddr; |
77 | found_load_bias = true; |
78 | } |
79 | |
80 | if (phdr_entry.p_type == llvm::ELF::PT_DYNAMIC) { |
81 | dynamic_section_addr = phdr_entry.p_vaddr; |
82 | dynamic_section_size = phdr_entry.p_memsz; |
83 | found_dynamic_section = true; |
84 | } |
85 | } |
86 | |
87 | if (!found_load_bias || !found_dynamic_section) |
88 | return LLDB_INVALID_ADDRESS; |
89 | |
90 | // Find the DT_DEBUG entry in the .dynamic section |
91 | dynamic_section_addr += load_bias; |
92 | ELF_DYN dynamic_entry; |
93 | size_t dynamic_num_entries = dynamic_section_size / sizeof(dynamic_entry); |
94 | for (size_t i = 0; i < dynamic_num_entries; i++) { |
95 | size_t bytes_read; |
96 | auto error = ReadMemory(addr: dynamic_section_addr + i * sizeof(dynamic_entry), |
97 | buf: &dynamic_entry, size: sizeof(dynamic_entry), bytes_read); |
98 | if (!error.Success()) |
99 | return LLDB_INVALID_ADDRESS; |
100 | // Return the &DT_DEBUG->d_ptr which points to r_debug which contains the |
101 | // link_map. |
102 | if (dynamic_entry.d_tag == llvm::ELF::DT_DEBUG) { |
103 | return dynamic_section_addr + i * sizeof(dynamic_entry) + |
104 | sizeof(dynamic_entry.d_tag); |
105 | } |
106 | } |
107 | |
108 | return LLDB_INVALID_ADDRESS; |
109 | } |
110 | |
111 | template lldb::addr_t NativeProcessELF::GetELFImageInfoAddress< |
112 | llvm::ELF::Elf32_Ehdr, llvm::ELF::Elf32_Phdr, llvm::ELF::Elf32_Dyn>(); |
113 | template lldb::addr_t NativeProcessELF::GetELFImageInfoAddress< |
114 | llvm::ELF::Elf64_Ehdr, llvm::ELF::Elf64_Phdr, llvm::ELF::Elf64_Dyn>(); |
115 | |
116 | template <typename T> |
117 | llvm::Expected<SVR4LibraryInfo> |
118 | NativeProcessELF::ReadSVR4LibraryInfo(lldb::addr_t link_map_addr) { |
119 | ELFLinkMap<T> link_map; |
120 | size_t bytes_read; |
121 | auto error = |
122 | ReadMemory(addr: link_map_addr, buf: &link_map, size: sizeof(link_map), bytes_read); |
123 | if (!error.Success()) |
124 | return error.ToError(); |
125 | |
126 | char name_buffer[PATH_MAX]; |
127 | llvm::Expected<llvm::StringRef> string_or_error = ReadCStringFromMemory( |
128 | addr: link_map.l_name, buffer: &name_buffer[0], max_size: sizeof(name_buffer), total_bytes_read&: bytes_read); |
129 | if (!string_or_error) |
130 | return string_or_error.takeError(); |
131 | |
132 | SVR4LibraryInfo info; |
133 | info.name = string_or_error->str(); |
134 | info.link_map = link_map_addr; |
135 | info.base_addr = link_map.l_addr; |
136 | info.ld_addr = link_map.l_ld; |
137 | info.next = link_map.l_next; |
138 | |
139 | return info; |
140 | } |
141 | |
142 | llvm::Expected<std::vector<SVR4LibraryInfo>> |
143 | NativeProcessELF::GetLoadedSVR4Libraries() { |
144 | // Address of DT_DEBUG.d_ptr which points to r_debug |
145 | lldb::addr_t info_address = GetSharedLibraryInfoAddress(); |
146 | if (info_address == LLDB_INVALID_ADDRESS) |
147 | return llvm::createStringError(EC: llvm::inconvertibleErrorCode(), |
148 | Msg: "Invalid shared library info address" ); |
149 | // Address of r_debug |
150 | lldb::addr_t address = 0; |
151 | size_t bytes_read; |
152 | auto status = |
153 | ReadMemory(addr: info_address, buf: &address, size: GetAddressByteSize(), bytes_read); |
154 | if (!status.Success()) |
155 | return status.ToError(); |
156 | if (address == 0) |
157 | return llvm::createStringError(EC: llvm::inconvertibleErrorCode(), |
158 | Msg: "Invalid r_debug address" ); |
159 | // Read r_debug.r_map |
160 | lldb::addr_t link_map = 0; |
161 | status = ReadMemory(addr: address + GetAddressByteSize(), buf: &link_map, |
162 | size: GetAddressByteSize(), bytes_read); |
163 | if (!status.Success()) |
164 | return status.ToError(); |
165 | if (link_map == 0) |
166 | return llvm::createStringError(EC: llvm::inconvertibleErrorCode(), |
167 | Msg: "Invalid link_map address" ); |
168 | |
169 | std::vector<SVR4LibraryInfo> library_list; |
170 | while (link_map) { |
171 | llvm::Expected<SVR4LibraryInfo> info = |
172 | GetAddressByteSize() == 8 ? ReadSVR4LibraryInfo<uint64_t>(link_map_addr: link_map) |
173 | : ReadSVR4LibraryInfo<uint32_t>(link_map_addr: link_map); |
174 | if (!info) |
175 | return info.takeError(); |
176 | if (!info->name.empty() && info->base_addr != 0) |
177 | library_list.push_back(x: *info); |
178 | link_map = info->next; |
179 | } |
180 | |
181 | return library_list; |
182 | } |
183 | |
184 | void NativeProcessELF::NotifyDidExec() { |
185 | NativeProcessProtocol::NotifyDidExec(); |
186 | m_shared_library_info_addr.reset(); |
187 | } |
188 | |
189 | } // namespace lldb_private |
190 | |