1 | //===-- PlatformAndroid.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 "lldb/Core/Module.h" |
10 | #include "lldb/Core/PluginManager.h" |
11 | #include "lldb/Core/Section.h" |
12 | #include "lldb/Core/ValueObject.h" |
13 | #include "lldb/Host/HostInfo.h" |
14 | #include "lldb/Utility/LLDBLog.h" |
15 | #include "lldb/Utility/Log.h" |
16 | #include "lldb/Utility/Scalar.h" |
17 | #include "lldb/Utility/UriParser.h" |
18 | |
19 | #include "AdbClient.h" |
20 | #include "PlatformAndroid.h" |
21 | #include "PlatformAndroidRemoteGDBServer.h" |
22 | #include "lldb/Target/Target.h" |
23 | #include <optional> |
24 | |
25 | using namespace lldb; |
26 | using namespace lldb_private; |
27 | using namespace lldb_private::platform_android; |
28 | using namespace std::chrono; |
29 | |
30 | LLDB_PLUGIN_DEFINE(PlatformAndroid) |
31 | |
32 | namespace { |
33 | |
34 | #define LLDB_PROPERTIES_android |
35 | #include "PlatformAndroidProperties.inc" |
36 | |
37 | enum { |
38 | #define LLDB_PROPERTIES_android |
39 | #include "PlatformAndroidPropertiesEnum.inc" |
40 | }; |
41 | |
42 | class PluginProperties : public Properties { |
43 | public: |
44 | PluginProperties() { |
45 | m_collection_sp = std::make_shared<OptionValueProperties>( |
46 | PlatformAndroid::GetPluginNameStatic(is_host: false)); |
47 | m_collection_sp->Initialize(g_android_properties); |
48 | } |
49 | }; |
50 | |
51 | static PluginProperties &GetGlobalProperties() { |
52 | static PluginProperties g_settings; |
53 | return g_settings; |
54 | } |
55 | |
56 | uint32_t g_initialize_count = 0; |
57 | const unsigned int g_android_default_cache_size = |
58 | 2048; // Fits inside 4k adb packet. |
59 | |
60 | } // end of anonymous namespace |
61 | |
62 | void PlatformAndroid::Initialize() { |
63 | PlatformLinux::Initialize(); |
64 | |
65 | if (g_initialize_count++ == 0) { |
66 | #if defined(__ANDROID__) |
67 | PlatformSP default_platform_sp(new PlatformAndroid(true)); |
68 | default_platform_sp->SetSystemArchitecture(HostInfo::GetArchitecture()); |
69 | Platform::SetHostPlatform(default_platform_sp); |
70 | #endif |
71 | PluginManager::RegisterPlugin( |
72 | name: PlatformAndroid::GetPluginNameStatic(is_host: false), |
73 | description: PlatformAndroid::GetPluginDescriptionStatic(is_host: false), |
74 | create_callback: PlatformAndroid::CreateInstance, debugger_init_callback: PlatformAndroid::DebuggerInitialize); |
75 | } |
76 | } |
77 | |
78 | void PlatformAndroid::Terminate() { |
79 | if (g_initialize_count > 0) { |
80 | if (--g_initialize_count == 0) { |
81 | PluginManager::UnregisterPlugin(create_callback: PlatformAndroid::CreateInstance); |
82 | } |
83 | } |
84 | |
85 | PlatformLinux::Terminate(); |
86 | } |
87 | |
88 | PlatformSP PlatformAndroid::CreateInstance(bool force, const ArchSpec *arch) { |
89 | Log *log = GetLog(mask: LLDBLog::Platform); |
90 | if (log) { |
91 | const char *arch_name; |
92 | if (arch && arch->GetArchitectureName()) |
93 | arch_name = arch->GetArchitectureName(); |
94 | else |
95 | arch_name = "<null>" ; |
96 | |
97 | const char *triple_cstr = |
98 | arch ? arch->GetTriple().getTriple().c_str() : "<null>" ; |
99 | |
100 | LLDB_LOGF(log, "PlatformAndroid::%s(force=%s, arch={%s,%s})" , __FUNCTION__, |
101 | force ? "true" : "false" , arch_name, triple_cstr); |
102 | } |
103 | |
104 | bool create = force; |
105 | if (!create && arch && arch->IsValid()) { |
106 | const llvm::Triple &triple = arch->GetTriple(); |
107 | switch (triple.getVendor()) { |
108 | case llvm::Triple::PC: |
109 | create = true; |
110 | break; |
111 | |
112 | #if defined(__ANDROID__) |
113 | // Only accept "unknown" for the vendor if the host is android and if |
114 | // "unknown" wasn't specified (it was just returned because it was NOT |
115 | // specified). |
116 | case llvm::Triple::VendorType::UnknownVendor: |
117 | create = !arch->TripleVendorWasSpecified(); |
118 | break; |
119 | #endif |
120 | default: |
121 | break; |
122 | } |
123 | |
124 | if (create) { |
125 | switch (triple.getEnvironment()) { |
126 | case llvm::Triple::Android: |
127 | break; |
128 | |
129 | #if defined(__ANDROID__) |
130 | // Only accept "unknown" for the OS if the host is android and it |
131 | // "unknown" wasn't specified (it was just returned because it was NOT |
132 | // specified) |
133 | case llvm::Triple::EnvironmentType::UnknownEnvironment: |
134 | create = !arch->TripleEnvironmentWasSpecified(); |
135 | break; |
136 | #endif |
137 | default: |
138 | create = false; |
139 | break; |
140 | } |
141 | } |
142 | } |
143 | |
144 | if (create) { |
145 | LLDB_LOGF(log, "PlatformAndroid::%s() creating remote-android platform" , |
146 | __FUNCTION__); |
147 | return PlatformSP(new PlatformAndroid(false)); |
148 | } |
149 | |
150 | LLDB_LOGF( |
151 | log, "PlatformAndroid::%s() aborting creation of remote-android platform" , |
152 | __FUNCTION__); |
153 | |
154 | return PlatformSP(); |
155 | } |
156 | |
157 | void PlatformAndroid::DebuggerInitialize(Debugger &debugger) { |
158 | if (!PluginManager::GetSettingForPlatformPlugin(debugger, |
159 | setting_name: GetPluginNameStatic(is_host: false))) { |
160 | PluginManager::CreateSettingForPlatformPlugin( |
161 | debugger, properties_sp: GetGlobalProperties().GetValueProperties(), |
162 | description: "Properties for the Android platform plugin." , |
163 | /*is_global_property=*/true); |
164 | } |
165 | } |
166 | |
167 | PlatformAndroid::PlatformAndroid(bool is_host) |
168 | : PlatformLinux(is_host), m_sdk_version(0) {} |
169 | |
170 | llvm::StringRef PlatformAndroid::GetPluginDescriptionStatic(bool is_host) { |
171 | if (is_host) |
172 | return "Local Android user platform plug-in." ; |
173 | return "Remote Android user platform plug-in." ; |
174 | } |
175 | |
176 | Status PlatformAndroid::ConnectRemote(Args &args) { |
177 | m_device_id.clear(); |
178 | |
179 | if (IsHost()) |
180 | return Status("can't connect to the host platform, always connected" ); |
181 | |
182 | if (!m_remote_platform_sp) |
183 | m_remote_platform_sp = PlatformSP(new PlatformAndroidRemoteGDBServer()); |
184 | |
185 | const char *url = args.GetArgumentAtIndex(idx: 0); |
186 | if (!url) |
187 | return Status("URL is null." ); |
188 | std::optional<URI> parsed_url = URI::Parse(uri: url); |
189 | if (!parsed_url) |
190 | return Status("Invalid URL: %s" , url); |
191 | if (parsed_url->hostname != "localhost" ) |
192 | m_device_id = parsed_url->hostname.str(); |
193 | |
194 | auto error = PlatformLinux::ConnectRemote(args); |
195 | if (error.Success()) { |
196 | AdbClient adb; |
197 | error = AdbClient::CreateByDeviceID(device_id: m_device_id, adb); |
198 | if (error.Fail()) |
199 | return error; |
200 | |
201 | m_device_id = adb.GetDeviceID(); |
202 | } |
203 | return error; |
204 | } |
205 | |
206 | Status PlatformAndroid::GetFile(const FileSpec &source, |
207 | const FileSpec &destination) { |
208 | if (IsHost() || !m_remote_platform_sp) |
209 | return PlatformLinux::GetFile(source, destination); |
210 | |
211 | FileSpec source_spec(source.GetPath(denormalize: false), FileSpec::Style::posix); |
212 | if (source_spec.IsRelative()) |
213 | source_spec = GetRemoteWorkingDirectory().CopyByAppendingPathComponent( |
214 | component: source_spec.GetPathAsConstString(denormalize: false).GetStringRef()); |
215 | |
216 | Status error; |
217 | auto sync_service = GetSyncService(error); |
218 | if (error.Fail()) |
219 | return error; |
220 | |
221 | uint32_t mode = 0, size = 0, mtime = 0; |
222 | error = sync_service->Stat(remote_file: source_spec, mode, size, mtime); |
223 | if (error.Fail()) |
224 | return error; |
225 | |
226 | if (mode != 0) |
227 | return sync_service->PullFile(remote_file: source_spec, local_file: destination); |
228 | |
229 | std::string source_file = source_spec.GetPath(denormalize: false); |
230 | |
231 | Log *log = GetLog(mask: LLDBLog::Platform); |
232 | LLDB_LOGF(log, "Got mode == 0 on '%s': try to get file via 'shell cat'" , |
233 | source_file.c_str()); |
234 | |
235 | if (strchr(s: source_file.c_str(), c: '\'') != nullptr) |
236 | return Status("Doesn't support single-quotes in filenames" ); |
237 | |
238 | // mode == 0 can signify that adbd cannot access the file due security |
239 | // constraints - try "cat ..." as a fallback. |
240 | AdbClientUP adb(GetAdbClient(error)); |
241 | if (error.Fail()) |
242 | return error; |
243 | |
244 | char cmd[PATH_MAX]; |
245 | snprintf(s: cmd, maxlen: sizeof(cmd), format: "%scat '%s'" , GetRunAs().c_str(), |
246 | source_file.c_str()); |
247 | |
248 | return adb->ShellToFile(command: cmd, timeout: minutes(1), output_file_spec: destination); |
249 | } |
250 | |
251 | Status PlatformAndroid::PutFile(const FileSpec &source, |
252 | const FileSpec &destination, uint32_t uid, |
253 | uint32_t gid) { |
254 | if (IsHost() || !m_remote_platform_sp) |
255 | return PlatformLinux::PutFile(source, destination, uid, gid); |
256 | |
257 | FileSpec destination_spec(destination.GetPath(denormalize: false), FileSpec::Style::posix); |
258 | if (destination_spec.IsRelative()) |
259 | destination_spec = GetRemoteWorkingDirectory().CopyByAppendingPathComponent( |
260 | component: destination_spec.GetPath(denormalize: false)); |
261 | |
262 | // TODO: Set correct uid and gid on remote file. |
263 | Status error; |
264 | auto sync_service = GetSyncService(error); |
265 | if (error.Fail()) |
266 | return error; |
267 | return sync_service->PushFile(local_file: source, remote_file: destination_spec); |
268 | } |
269 | |
270 | const char *PlatformAndroid::GetCacheHostname() { return m_device_id.c_str(); } |
271 | |
272 | Status PlatformAndroid::DownloadModuleSlice(const FileSpec &src_file_spec, |
273 | const uint64_t src_offset, |
274 | const uint64_t src_size, |
275 | const FileSpec &dst_file_spec) { |
276 | // In Android API level 23 and above, dynamic loader is able to load .so |
277 | // file directly from APK. In that case, src_offset will be an non-zero. |
278 | if (src_offset == 0) // Use GetFile for a normal file. |
279 | return GetFile(source: src_file_spec, destination: dst_file_spec); |
280 | |
281 | std::string source_file = src_file_spec.GetPath(denormalize: false); |
282 | if (source_file.find(c: '\'') != std::string::npos) |
283 | return Status("Doesn't support single-quotes in filenames" ); |
284 | |
285 | // For zip .so file, src_file_spec will be "zip_path!/so_path". |
286 | // Extract "zip_path" from the source_file. |
287 | static constexpr llvm::StringLiteral k_zip_separator("!/" ); |
288 | size_t pos = source_file.find(svt: k_zip_separator); |
289 | if (pos != std::string::npos) |
290 | source_file = source_file.substr(pos: 0, n: pos); |
291 | |
292 | Status error; |
293 | AdbClientUP adb(GetAdbClient(error)); |
294 | if (error.Fail()) |
295 | return error; |
296 | |
297 | // Use 'shell dd' to download the file slice with the offset and size. |
298 | char cmd[PATH_MAX]; |
299 | snprintf(s: cmd, maxlen: sizeof(cmd), |
300 | format: "%sdd if='%s' iflag=skip_bytes,count_bytes " |
301 | "skip=%" PRIu64 " count=%" PRIu64 " status=none" , |
302 | GetRunAs().c_str(), source_file.c_str(), src_offset, src_size); |
303 | |
304 | return adb->ShellToFile(command: cmd, timeout: minutes(1), output_file_spec: dst_file_spec); |
305 | } |
306 | |
307 | Status PlatformAndroid::DisconnectRemote() { |
308 | Status error = PlatformLinux::DisconnectRemote(); |
309 | if (error.Success()) { |
310 | m_device_id.clear(); |
311 | m_sdk_version = 0; |
312 | } |
313 | return error; |
314 | } |
315 | |
316 | uint32_t PlatformAndroid::GetDefaultMemoryCacheLineSize() { |
317 | return g_android_default_cache_size; |
318 | } |
319 | |
320 | uint32_t PlatformAndroid::GetSdkVersion() { |
321 | if (!IsConnected()) |
322 | return 0; |
323 | |
324 | if (m_sdk_version != 0) |
325 | return m_sdk_version; |
326 | |
327 | std::string version_string; |
328 | Status error; |
329 | AdbClientUP adb(GetAdbClient(error)); |
330 | if (error.Fail()) |
331 | return 0; |
332 | error = |
333 | adb->Shell(command: "getprop ro.build.version.sdk" , timeout: seconds(5), output: &version_string); |
334 | version_string = llvm::StringRef(version_string).trim().str(); |
335 | |
336 | if (error.Fail() || version_string.empty()) { |
337 | Log *log = GetLog(mask: LLDBLog::Platform); |
338 | LLDB_LOGF(log, "Get SDK version failed. (error: %s, output: %s)" , |
339 | error.AsCString(), version_string.c_str()); |
340 | return 0; |
341 | } |
342 | |
343 | // FIXME: improve error handling |
344 | llvm::to_integer(S: version_string, Num&: m_sdk_version); |
345 | return m_sdk_version; |
346 | } |
347 | |
348 | Status PlatformAndroid::DownloadSymbolFile(const lldb::ModuleSP &module_sp, |
349 | const FileSpec &dst_file_spec) { |
350 | // For oat file we can try to fetch additional debug info from the device |
351 | llvm::StringRef extension = module_sp->GetFileSpec().GetFileNameExtension(); |
352 | if (extension != ".oat" && extension != ".odex" ) |
353 | return Status( |
354 | "Symbol file downloading only supported for oat and odex files" ); |
355 | |
356 | // If we have no information about the platform file we can't execute oatdump |
357 | if (!module_sp->GetPlatformFileSpec()) |
358 | return Status("No platform file specified" ); |
359 | |
360 | // Symbolizer isn't available before SDK version 23 |
361 | if (GetSdkVersion() < 23) |
362 | return Status("Symbol file generation only supported on SDK 23+" ); |
363 | |
364 | // If we already have symtab then we don't have to try and generate one |
365 | if (module_sp->GetSectionList()->FindSectionByName(section_dstr: ConstString(".symtab" )) != |
366 | nullptr) |
367 | return Status("Symtab already available in the module" ); |
368 | |
369 | Status error; |
370 | AdbClientUP adb(GetAdbClient(error)); |
371 | if (error.Fail()) |
372 | return error; |
373 | std::string tmpdir; |
374 | error = adb->Shell(command: "mktemp --directory --tmpdir /data/local/tmp" , timeout: seconds(5), |
375 | output: &tmpdir); |
376 | if (error.Fail() || tmpdir.empty()) |
377 | return Status("Failed to generate temporary directory on the device (%s)" , |
378 | error.AsCString()); |
379 | tmpdir = llvm::StringRef(tmpdir).trim().str(); |
380 | |
381 | // Create file remover for the temporary directory created on the device |
382 | std::unique_ptr<std::string, std::function<void(std::string *)>> |
383 | tmpdir_remover(&tmpdir, [&adb](std::string *s) { |
384 | StreamString command; |
385 | command.Printf(format: "rm -rf %s" , s->c_str()); |
386 | Status error = adb->Shell(command: command.GetData(), timeout: seconds(5), output: nullptr); |
387 | |
388 | Log *log = GetLog(mask: LLDBLog::Platform); |
389 | if (log && error.Fail()) |
390 | LLDB_LOGF(log, "Failed to remove temp directory: %s" , |
391 | error.AsCString()); |
392 | }); |
393 | |
394 | FileSpec symfile_platform_filespec(tmpdir); |
395 | symfile_platform_filespec.AppendPathComponent(component: "symbolized.oat" ); |
396 | |
397 | // Execute oatdump on the remote device to generate a file with symtab |
398 | StreamString command; |
399 | command.Printf(format: "oatdump --symbolize=%s --output=%s" , |
400 | module_sp->GetPlatformFileSpec().GetPath(denormalize: false).c_str(), |
401 | symfile_platform_filespec.GetPath(denormalize: false).c_str()); |
402 | error = adb->Shell(command: command.GetData(), timeout: minutes(1), output: nullptr); |
403 | if (error.Fail()) |
404 | return Status("Oatdump failed: %s" , error.AsCString()); |
405 | |
406 | // Download the symbolfile from the remote device |
407 | return GetFile(source: symfile_platform_filespec, destination: dst_file_spec); |
408 | } |
409 | |
410 | bool PlatformAndroid::GetRemoteOSVersion() { |
411 | m_os_version = llvm::VersionTuple(GetSdkVersion()); |
412 | return !m_os_version.empty(); |
413 | } |
414 | |
415 | llvm::StringRef |
416 | PlatformAndroid::GetLibdlFunctionDeclarations(lldb_private::Process *process) { |
417 | SymbolContextList matching_symbols; |
418 | std::vector<const char *> dl_open_names = {"__dl_dlopen" , "dlopen" }; |
419 | const char *dl_open_name = nullptr; |
420 | Target &target = process->GetTarget(); |
421 | for (auto name : dl_open_names) { |
422 | target.GetImages().FindFunctionSymbols( |
423 | name: ConstString(name), name_type_mask: eFunctionNameTypeFull, sc_list&: matching_symbols); |
424 | if (matching_symbols.GetSize()) { |
425 | dl_open_name = name; |
426 | break; |
427 | } |
428 | } |
429 | // Older platform versions have the dl function symbols mangled |
430 | if (dl_open_name == dl_open_names[0]) |
431 | return R"( |
432 | extern "C" void* dlopen(const char*, int) asm("__dl_dlopen"); |
433 | extern "C" void* dlsym(void*, const char*) asm("__dl_dlsym"); |
434 | extern "C" int dlclose(void*) asm("__dl_dlclose"); |
435 | extern "C" char* dlerror(void) asm("__dl_dlerror"); |
436 | )" ; |
437 | |
438 | return PlatformPOSIX::GetLibdlFunctionDeclarations(process); |
439 | } |
440 | |
441 | PlatformAndroid::AdbClientUP PlatformAndroid::GetAdbClient(Status &error) { |
442 | AdbClientUP adb(std::make_unique<AdbClient>(args&: m_device_id)); |
443 | if (adb) |
444 | error.Clear(); |
445 | else |
446 | error = Status("Failed to create AdbClient" ); |
447 | return adb; |
448 | } |
449 | |
450 | llvm::StringRef PlatformAndroid::GetPropertyPackageName() { |
451 | return GetGlobalProperties().GetPropertyAtIndexAs<llvm::StringRef>( |
452 | ePropertyPlatformPackageName, "" ); |
453 | } |
454 | |
455 | std::string PlatformAndroid::GetRunAs() { |
456 | llvm::StringRef run_as = GetPropertyPackageName(); |
457 | if (!run_as.empty()) { |
458 | // When LLDB fails to pull file from a package directory due to security |
459 | // constraint, user needs to set the package name to |
460 | // 'platform.plugin.remote-android.package-name' property in order to run |
461 | // shell commands as the package user using 'run-as' (e.g. to get file with |
462 | // 'cat' and 'dd'). |
463 | // https://cs.android.com/android/platform/superproject/+/master: |
464 | // system/core/run-as/run-as.cpp;l=39-61; |
465 | // drc=4a77a84a55522a3b122f9c63ef0d0b8a6a131627 |
466 | return std::string("run-as '" ) + run_as.str() + "' " ; |
467 | } |
468 | return run_as.str(); |
469 | } |
470 | |
471 | AdbClient::SyncService *PlatformAndroid::GetSyncService(Status &error) { |
472 | if (m_adb_sync_svc && m_adb_sync_svc->IsConnected()) |
473 | return m_adb_sync_svc.get(); |
474 | |
475 | AdbClientUP adb(GetAdbClient(error)); |
476 | if (error.Fail()) |
477 | return nullptr; |
478 | m_adb_sync_svc = adb->GetSyncService(error); |
479 | return (error.Success()) ? m_adb_sync_svc.get() : nullptr; |
480 | } |
481 | |