1 | //===-- DynamicLoaderWindowsDYLD.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 "DynamicLoaderWindowsDYLD.h" |
10 | |
11 | #include "lldb/Core/Module.h" |
12 | #include "lldb/Core/PluginManager.h" |
13 | #include "lldb/Target/ExecutionContext.h" |
14 | #include "lldb/Target/Platform.h" |
15 | #include "lldb/Target/Process.h" |
16 | #include "lldb/Target/RegisterContext.h" |
17 | #include "lldb/Target/Target.h" |
18 | #include "lldb/Target/ThreadPlanStepInstruction.h" |
19 | #include "lldb/Utility/LLDBLog.h" |
20 | #include "lldb/Utility/Log.h" |
21 | |
22 | #include "llvm/TargetParser/Triple.h" |
23 | |
24 | using namespace lldb; |
25 | using namespace lldb_private; |
26 | |
27 | LLDB_PLUGIN_DEFINE(DynamicLoaderWindowsDYLD) |
28 | |
29 | DynamicLoaderWindowsDYLD::DynamicLoaderWindowsDYLD(Process *process) |
30 | : DynamicLoader(process) {} |
31 | |
32 | DynamicLoaderWindowsDYLD::~DynamicLoaderWindowsDYLD() = default; |
33 | |
34 | void DynamicLoaderWindowsDYLD::Initialize() { |
35 | PluginManager::RegisterPlugin(name: GetPluginNameStatic(), |
36 | description: GetPluginDescriptionStatic(), create_callback: CreateInstance); |
37 | } |
38 | |
39 | void DynamicLoaderWindowsDYLD::Terminate() {} |
40 | |
41 | llvm::StringRef DynamicLoaderWindowsDYLD::GetPluginDescriptionStatic() { |
42 | return "Dynamic loader plug-in that watches for shared library " |
43 | "loads/unloads in Windows processes." ; |
44 | } |
45 | |
46 | DynamicLoader *DynamicLoaderWindowsDYLD::CreateInstance(Process *process, |
47 | bool force) { |
48 | bool should_create = force; |
49 | if (!should_create) { |
50 | const llvm::Triple &triple_ref = |
51 | process->GetTarget().GetArchitecture().GetTriple(); |
52 | if (triple_ref.getOS() == llvm::Triple::Win32) |
53 | should_create = true; |
54 | } |
55 | |
56 | if (should_create) |
57 | return new DynamicLoaderWindowsDYLD(process); |
58 | |
59 | return nullptr; |
60 | } |
61 | |
62 | void DynamicLoaderWindowsDYLD::OnLoadModule(lldb::ModuleSP module_sp, |
63 | const ModuleSpec module_spec, |
64 | lldb::addr_t module_addr) { |
65 | |
66 | // Resolve the module unless we already have one. |
67 | if (!module_sp) { |
68 | Status error; |
69 | module_sp = m_process->GetTarget().GetOrCreateModule(module_spec, |
70 | notify: true /* notify */, error_ptr: &error); |
71 | if (error.Fail()) |
72 | return; |
73 | } |
74 | |
75 | m_loaded_modules[module_sp] = module_addr; |
76 | UpdateLoadedSectionsCommon(module: module_sp, base_addr: module_addr, base_addr_is_offset: false); |
77 | ModuleList module_list; |
78 | module_list.Append(module_sp); |
79 | m_process->GetTarget().ModulesDidLoad(module_list); |
80 | } |
81 | |
82 | void DynamicLoaderWindowsDYLD::OnUnloadModule(lldb::addr_t module_addr) { |
83 | Address resolved_addr; |
84 | if (!m_process->GetTarget().ResolveLoadAddress(load_addr: module_addr, so_addr&: resolved_addr)) |
85 | return; |
86 | |
87 | ModuleSP module_sp = resolved_addr.GetModule(); |
88 | if (module_sp) { |
89 | m_loaded_modules.erase(x: module_sp); |
90 | UnloadSectionsCommon(module: module_sp); |
91 | ModuleList module_list; |
92 | module_list.Append(module_sp); |
93 | m_process->GetTarget().ModulesDidUnload(module_list, delete_locations: false); |
94 | } |
95 | } |
96 | |
97 | lldb::addr_t DynamicLoaderWindowsDYLD::GetLoadAddress(ModuleSP executable) { |
98 | // First, see if the load address is already cached. |
99 | auto it = m_loaded_modules.find(x: executable); |
100 | if (it != m_loaded_modules.end() && it->second != LLDB_INVALID_ADDRESS) |
101 | return it->second; |
102 | |
103 | lldb::addr_t load_addr = LLDB_INVALID_ADDRESS; |
104 | |
105 | // Second, try to get it through the process plugins. For a remote process, |
106 | // the remote platform will be responsible for providing it. |
107 | FileSpec file_spec(executable->GetPlatformFileSpec()); |
108 | bool is_loaded = false; |
109 | Status status = |
110 | m_process->GetFileLoadAddress(file: file_spec, is_loaded, load_addr); |
111 | // Servers other than lldb server could respond with a bogus address. |
112 | if (status.Success() && is_loaded && load_addr != LLDB_INVALID_ADDRESS) { |
113 | m_loaded_modules[executable] = load_addr; |
114 | return load_addr; |
115 | } |
116 | |
117 | return LLDB_INVALID_ADDRESS; |
118 | } |
119 | |
120 | void DynamicLoaderWindowsDYLD::DidAttach() { |
121 | Log *log = GetLog(mask: LLDBLog::DynamicLoader); |
122 | LLDB_LOGF(log, "DynamicLoaderWindowsDYLD::%s()" , __FUNCTION__); |
123 | |
124 | ModuleSP executable = GetTargetExecutable(); |
125 | |
126 | if (!executable.get()) |
127 | return; |
128 | |
129 | // Try to fetch the load address of the file from the process, since there |
130 | // could be randomization of the load address. |
131 | lldb::addr_t load_addr = GetLoadAddress(executable); |
132 | if (load_addr == LLDB_INVALID_ADDRESS) |
133 | return; |
134 | |
135 | // Request the process base address. |
136 | lldb::addr_t image_base = m_process->GetImageInfoAddress(); |
137 | if (image_base == load_addr) |
138 | return; |
139 | |
140 | // Rebase the process's modules if there is a mismatch. |
141 | UpdateLoadedSections(module: executable, LLDB_INVALID_ADDRESS, base_addr: load_addr, base_addr_is_offset: false); |
142 | |
143 | ModuleList module_list; |
144 | module_list.Append(module_sp: executable); |
145 | m_process->GetTarget().ModulesDidLoad(module_list); |
146 | auto error = m_process->LoadModules(); |
147 | LLDB_LOG_ERROR(log, std::move(error), "failed to load modules: {0}" ); |
148 | } |
149 | |
150 | void DynamicLoaderWindowsDYLD::DidLaunch() { |
151 | Log *log = GetLog(mask: LLDBLog::DynamicLoader); |
152 | LLDB_LOGF(log, "DynamicLoaderWindowsDYLD::%s()" , __FUNCTION__); |
153 | |
154 | ModuleSP executable = GetTargetExecutable(); |
155 | if (!executable.get()) |
156 | return; |
157 | |
158 | lldb::addr_t load_addr = GetLoadAddress(executable); |
159 | if (load_addr != LLDB_INVALID_ADDRESS) { |
160 | // Update the loaded sections so that the breakpoints can be resolved. |
161 | UpdateLoadedSections(module: executable, LLDB_INVALID_ADDRESS, base_addr: load_addr, base_addr_is_offset: false); |
162 | |
163 | ModuleList module_list; |
164 | module_list.Append(module_sp: executable); |
165 | m_process->GetTarget().ModulesDidLoad(module_list); |
166 | auto error = m_process->LoadModules(); |
167 | LLDB_LOG_ERROR(log, std::move(error), "failed to load modules: {0}" ); |
168 | } |
169 | } |
170 | |
171 | Status DynamicLoaderWindowsDYLD::CanLoadImage() { return Status(); } |
172 | |
173 | ThreadPlanSP |
174 | DynamicLoaderWindowsDYLD::GetStepThroughTrampolinePlan(Thread &thread, |
175 | bool stop) { |
176 | auto arch = m_process->GetTarget().GetArchitecture(); |
177 | if (arch.GetMachine() != llvm::Triple::x86) { |
178 | return ThreadPlanSP(); |
179 | } |
180 | |
181 | uint64_t pc = thread.GetRegisterContext()->GetPC(); |
182 | // Max size of an instruction in x86 is 15 bytes. |
183 | AddressRange range(pc, 2 * 15); |
184 | |
185 | DisassemblerSP disassembler_sp = Disassembler::DisassembleRange( |
186 | arch, plugin_name: nullptr, flavor: nullptr, target&: m_process->GetTarget(), disasm_range: range); |
187 | if (!disassembler_sp) { |
188 | return ThreadPlanSP(); |
189 | } |
190 | |
191 | InstructionList *insn_list = &disassembler_sp->GetInstructionList(); |
192 | if (insn_list == nullptr) { |
193 | return ThreadPlanSP(); |
194 | } |
195 | |
196 | // First instruction in a x86 Windows trampoline is going to be an indirect |
197 | // jump through the IAT and the next one will be a nop (usually there for |
198 | // alignment purposes). e.g.: |
199 | // 0x70ff4cfc <+956>: jmpl *0x7100c2a8 |
200 | // 0x70ff4d02 <+962>: nop |
201 | |
202 | auto first_insn = insn_list->GetInstructionAtIndex(idx: 0); |
203 | auto second_insn = insn_list->GetInstructionAtIndex(idx: 1); |
204 | |
205 | ExecutionContext exe_ctx(m_process->GetTarget()); |
206 | if (first_insn == nullptr || second_insn == nullptr || |
207 | strcmp(s1: first_insn->GetMnemonic(exe_ctx: &exe_ctx), s2: "jmpl" ) != 0 || |
208 | strcmp(s1: second_insn->GetMnemonic(exe_ctx: &exe_ctx), s2: "nop" ) != 0) { |
209 | return ThreadPlanSP(); |
210 | } |
211 | |
212 | assert(first_insn->DoesBranch() && !second_insn->DoesBranch()); |
213 | |
214 | return ThreadPlanSP(new ThreadPlanStepInstruction( |
215 | thread, false, false, eVoteNoOpinion, eVoteNoOpinion)); |
216 | } |
217 | |