/Users/buildslave/jenkins/workspace/coverage/llvm-project/lldb/source/Plugins/Platform/Android/PlatformAndroid.cpp
Line | Count | Source (jump to first uncovered line) |
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 | 3.89k | PluginProperties() { |
45 | 3.89k | m_collection_sp = std::make_shared<OptionValueProperties>( |
46 | 3.89k | PlatformAndroid::GetPluginNameStatic(false)); |
47 | 3.89k | m_collection_sp->Initialize(g_android_properties); |
48 | 3.89k | } |
49 | | }; |
50 | | |
51 | 3.89k | static PluginProperties &GetGlobalProperties() { |
52 | 3.89k | static PluginProperties g_settings; |
53 | 3.89k | return g_settings; |
54 | 3.89k | } |
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 | 3.95k | void PlatformAndroid::Initialize() { |
63 | 3.95k | PlatformLinux::Initialize(); |
64 | | |
65 | 3.95k | 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 | 3.95k | PluginManager::RegisterPlugin( |
72 | 3.95k | PlatformAndroid::GetPluginNameStatic(false), |
73 | 3.95k | PlatformAndroid::GetPluginDescriptionStatic(false), |
74 | 3.95k | PlatformAndroid::CreateInstance, PlatformAndroid::DebuggerInitialize); |
75 | 3.95k | } |
76 | 3.95k | } |
77 | | |
78 | 3.94k | void PlatformAndroid::Terminate() { |
79 | 3.94k | if (g_initialize_count > 0) { |
80 | 3.94k | if (--g_initialize_count == 0) { |
81 | 3.94k | PluginManager::UnregisterPlugin(PlatformAndroid::CreateInstance); |
82 | 3.94k | } |
83 | 3.94k | } |
84 | | |
85 | 3.94k | PlatformLinux::Terminate(); |
86 | 3.94k | } |
87 | | |
88 | 260 | PlatformSP PlatformAndroid::CreateInstance(bool force, const ArchSpec *arch) { |
89 | 260 | Log *log = GetLog(LLDBLog::Platform); |
90 | 260 | if (log) { |
91 | 0 | const char *arch_name; |
92 | 0 | if (arch && arch->GetArchitectureName()) |
93 | 0 | arch_name = arch->GetArchitectureName(); |
94 | 0 | else |
95 | 0 | arch_name = "<null>"; |
96 | |
|
97 | 0 | const char *triple_cstr = |
98 | 0 | arch ? arch->GetTriple().getTriple().c_str() : "<null>"; |
99 | |
|
100 | 0 | LLDB_LOGF(log, "PlatformAndroid::%s(force=%s, arch={%s,%s})", __FUNCTION__, |
101 | 0 | force ? "true" : "false", arch_name, triple_cstr); |
102 | 0 | } |
103 | | |
104 | 260 | bool create = force; |
105 | 260 | if (!create && arch233 && arch->IsValid()233 ) { |
106 | 233 | const llvm::Triple &triple = arch->GetTriple(); |
107 | 233 | switch (triple.getVendor()) { |
108 | 90 | case llvm::Triple::PC: |
109 | 90 | create = true; |
110 | 90 | 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 | 143 | default: |
121 | 143 | break; |
122 | 233 | } |
123 | | |
124 | 233 | if (create) { |
125 | 90 | switch (triple.getEnvironment()) { |
126 | 0 | case llvm::Triple::Android: |
127 | 0 | 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 | 90 | default: |
138 | 90 | create = false; |
139 | 90 | break; |
140 | 90 | } |
141 | 90 | } |
142 | 233 | } |
143 | | |
144 | 260 | if (create) { |
145 | 27 | LLDB_LOGF(log, "PlatformAndroid::%s() creating remote-android platform", |
146 | 27 | __FUNCTION__); |
147 | 27 | return PlatformSP(new PlatformAndroid(false)); |
148 | 27 | } |
149 | | |
150 | 233 | LLDB_LOGF( |
151 | 233 | log, "PlatformAndroid::%s() aborting creation of remote-android platform", |
152 | 233 | __FUNCTION__); |
153 | | |
154 | 233 | return PlatformSP(); |
155 | 260 | } |
156 | | |
157 | 6.06k | void PlatformAndroid::DebuggerInitialize(Debugger &debugger) { |
158 | 6.06k | if (!PluginManager::GetSettingForPlatformPlugin(debugger, |
159 | 6.06k | GetPluginNameStatic(false))) { |
160 | 3.89k | PluginManager::CreateSettingForPlatformPlugin( |
161 | 3.89k | debugger, GetGlobalProperties().GetValueProperties(), |
162 | 3.89k | "Properties for the Android platform plugin.", |
163 | 3.89k | /*is_global_property=*/true); |
164 | 3.89k | } |
165 | 6.06k | } |
166 | | |
167 | | PlatformAndroid::PlatformAndroid(bool is_host) |
168 | 34 | : PlatformLinux(is_host), m_sdk_version(0) {} |
169 | | |
170 | 3.95k | llvm::StringRef PlatformAndroid::GetPluginDescriptionStatic(bool is_host) { |
171 | 3.95k | if (is_host) |
172 | 0 | return "Local Android user platform plug-in."; |
173 | 3.95k | return "Remote Android user platform plug-in."; |
174 | 3.95k | } |
175 | | |
176 | 0 | Status PlatformAndroid::ConnectRemote(Args &args) { |
177 | 0 | m_device_id.clear(); |
178 | |
|
179 | 0 | if (IsHost()) |
180 | 0 | return Status("can't connect to the host platform, always connected"); |
181 | | |
182 | 0 | if (!m_remote_platform_sp) |
183 | 0 | m_remote_platform_sp = PlatformSP(new PlatformAndroidRemoteGDBServer()); |
184 | |
|
185 | 0 | const char *url = args.GetArgumentAtIndex(0); |
186 | 0 | if (!url) |
187 | 0 | return Status("URL is null."); |
188 | 0 | std::optional<URI> parsed_url = URI::Parse(url); |
189 | 0 | if (!parsed_url) |
190 | 0 | return Status("Invalid URL: %s", url); |
191 | 0 | if (parsed_url->hostname != "localhost") |
192 | 0 | m_device_id = parsed_url->hostname.str(); |
193 | |
|
194 | 0 | auto error = PlatformLinux::ConnectRemote(args); |
195 | 0 | if (error.Success()) { |
196 | 0 | AdbClient adb; |
197 | 0 | error = AdbClient::CreateByDeviceID(m_device_id, adb); |
198 | 0 | if (error.Fail()) |
199 | 0 | return error; |
200 | | |
201 | 0 | m_device_id = adb.GetDeviceID(); |
202 | 0 | } |
203 | 0 | return error; |
204 | 0 | } |
205 | | |
206 | | Status PlatformAndroid::GetFile(const FileSpec &source, |
207 | 8 | const FileSpec &destination) { |
208 | 8 | if (IsHost() || !m_remote_platform_sp) |
209 | 4 | return PlatformLinux::GetFile(source, destination); |
210 | | |
211 | 4 | FileSpec source_spec(source.GetPath(false), FileSpec::Style::posix); |
212 | 4 | if (source_spec.IsRelative()) |
213 | 0 | source_spec = GetRemoteWorkingDirectory().CopyByAppendingPathComponent( |
214 | 0 | source_spec.GetPathAsConstString(false).GetStringRef()); |
215 | | |
216 | 4 | Status error; |
217 | 4 | auto sync_service = GetSyncService(error); |
218 | 4 | if (error.Fail()) |
219 | 0 | return error; |
220 | | |
221 | 4 | uint32_t mode = 0, size = 0, mtime = 0; |
222 | 4 | error = sync_service->Stat(source_spec, mode, size, mtime); |
223 | 4 | if (error.Fail()) |
224 | 0 | return error; |
225 | | |
226 | 4 | if (mode != 0) |
227 | 2 | return sync_service->PullFile(source_spec, destination); |
228 | | |
229 | 2 | std::string source_file = source_spec.GetPath(false); |
230 | | |
231 | 2 | Log *log = GetLog(LLDBLog::Platform); |
232 | 2 | LLDB_LOGF(log, "Got mode == 0 on '%s': try to get file via 'shell cat'", |
233 | 2 | source_file.c_str()); |
234 | | |
235 | 2 | if (strchr(source_file.c_str(), '\'') != nullptr) |
236 | 0 | 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 | 2 | AdbClientUP adb(GetAdbClient(error)); |
241 | 2 | if (error.Fail()) |
242 | 0 | return error; |
243 | | |
244 | 2 | char cmd[PATH_MAX]; |
245 | 2 | snprintf(cmd, sizeof(cmd), "%scat '%s'", GetRunAs().c_str(), |
246 | 2 | source_file.c_str()); |
247 | | |
248 | 2 | return adb->ShellToFile(cmd, minutes(1), destination); |
249 | 2 | } |
250 | | |
251 | | Status PlatformAndroid::PutFile(const FileSpec &source, |
252 | | const FileSpec &destination, uint32_t uid, |
253 | 0 | uint32_t gid) { |
254 | 0 | if (IsHost() || !m_remote_platform_sp) |
255 | 0 | return PlatformLinux::PutFile(source, destination, uid, gid); |
256 | | |
257 | 0 | FileSpec destination_spec(destination.GetPath(false), FileSpec::Style::posix); |
258 | 0 | if (destination_spec.IsRelative()) |
259 | 0 | destination_spec = GetRemoteWorkingDirectory().CopyByAppendingPathComponent( |
260 | 0 | destination_spec.GetPath(false)); |
261 | | |
262 | | // TODO: Set correct uid and gid on remote file. |
263 | 0 | Status error; |
264 | 0 | auto sync_service = GetSyncService(error); |
265 | 0 | if (error.Fail()) |
266 | 0 | return error; |
267 | 0 | return sync_service->PushFile(source, destination_spec); |
268 | 0 | } |
269 | | |
270 | 15 | 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 | 8 | 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 | 8 | if (src_offset == 0) // Use GetFile for a normal file. |
279 | 5 | return GetFile(src_file_spec, dst_file_spec); |
280 | | |
281 | 3 | std::string source_file = src_file_spec.GetPath(false); |
282 | 3 | if (source_file.find('\'') != std::string::npos) |
283 | 0 | 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 | 3 | static constexpr llvm::StringLiteral k_zip_separator("!/"); |
288 | 3 | size_t pos = source_file.find(k_zip_separator); |
289 | 3 | if (pos != std::string::npos) |
290 | 3 | source_file = source_file.substr(0, pos); |
291 | | |
292 | 3 | Status error; |
293 | 3 | AdbClientUP adb(GetAdbClient(error)); |
294 | 3 | if (error.Fail()) |
295 | 1 | return error; |
296 | | |
297 | | // Use 'shell dd' to download the file slice with the offset and size. |
298 | 2 | char cmd[PATH_MAX]; |
299 | 2 | snprintf(cmd, sizeof(cmd), |
300 | 2 | "%sdd if='%s' iflag=skip_bytes,count_bytes " |
301 | 2 | "skip=%" PRIu64 " count=%" PRIu64 " status=none", |
302 | 2 | GetRunAs().c_str(), source_file.c_str(), src_offset, src_size); |
303 | | |
304 | 2 | return adb->ShellToFile(cmd, minutes(1), dst_file_spec); |
305 | 3 | } |
306 | | |
307 | 0 | Status PlatformAndroid::DisconnectRemote() { |
308 | 0 | Status error = PlatformLinux::DisconnectRemote(); |
309 | 0 | if (error.Success()) { |
310 | 0 | m_device_id.clear(); |
311 | 0 | m_sdk_version = 0; |
312 | 0 | } |
313 | 0 | return error; |
314 | 0 | } |
315 | | |
316 | 23 | uint32_t PlatformAndroid::GetDefaultMemoryCacheLineSize() { |
317 | 23 | return g_android_default_cache_size; |
318 | 23 | } |
319 | | |
320 | 0 | uint32_t PlatformAndroid::GetSdkVersion() { |
321 | 0 | if (!IsConnected()) |
322 | 0 | return 0; |
323 | | |
324 | 0 | if (m_sdk_version != 0) |
325 | 0 | return m_sdk_version; |
326 | | |
327 | 0 | std::string version_string; |
328 | 0 | Status error; |
329 | 0 | AdbClientUP adb(GetAdbClient(error)); |
330 | 0 | if (error.Fail()) |
331 | 0 | return 0; |
332 | 0 | error = |
333 | 0 | adb->Shell("getprop ro.build.version.sdk", seconds(5), &version_string); |
334 | 0 | version_string = llvm::StringRef(version_string).trim().str(); |
335 | |
|
336 | 0 | if (error.Fail() || version_string.empty()) { |
337 | 0 | Log *log = GetLog(LLDBLog::Platform); |
338 | 0 | LLDB_LOGF(log, "Get SDK version failed. (error: %s, output: %s)", |
339 | 0 | error.AsCString(), version_string.c_str()); |
340 | 0 | return 0; |
341 | 0 | } |
342 | | |
343 | | // FIXME: improve error handling |
344 | 0 | llvm::to_integer(version_string, m_sdk_version); |
345 | 0 | return m_sdk_version; |
346 | 0 | } |
347 | | |
348 | | Status PlatformAndroid::DownloadSymbolFile(const lldb::ModuleSP &module_sp, |
349 | 0 | const FileSpec &dst_file_spec) { |
350 | | // For oat file we can try to fetch additional debug info from the device |
351 | 0 | llvm::StringRef extension = module_sp->GetFileSpec().GetFileNameExtension(); |
352 | 0 | if (extension != ".oat" && extension != ".odex") |
353 | 0 | return Status( |
354 | 0 | "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 | 0 | if (!module_sp->GetPlatformFileSpec()) |
358 | 0 | return Status("No platform file specified"); |
359 | | |
360 | | // Symbolizer isn't available before SDK version 23 |
361 | 0 | if (GetSdkVersion() < 23) |
362 | 0 | 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 | 0 | if (module_sp->GetSectionList()->FindSectionByName(ConstString(".symtab")) != |
366 | 0 | nullptr) |
367 | 0 | return Status("Symtab already available in the module"); |
368 | | |
369 | 0 | Status error; |
370 | 0 | AdbClientUP adb(GetAdbClient(error)); |
371 | 0 | if (error.Fail()) |
372 | 0 | return error; |
373 | 0 | std::string tmpdir; |
374 | 0 | error = adb->Shell("mktemp --directory --tmpdir /data/local/tmp", seconds(5), |
375 | 0 | &tmpdir); |
376 | 0 | if (error.Fail() || tmpdir.empty()) |
377 | 0 | return Status("Failed to generate temporary directory on the device (%s)", |
378 | 0 | error.AsCString()); |
379 | 0 | tmpdir = llvm::StringRef(tmpdir).trim().str(); |
380 | | |
381 | | // Create file remover for the temporary directory created on the device |
382 | 0 | std::unique_ptr<std::string, std::function<void(std::string *)>> |
383 | 0 | tmpdir_remover(&tmpdir, [&adb](std::string *s) { |
384 | 0 | StreamString command; |
385 | 0 | command.Printf("rm -rf %s", s->c_str()); |
386 | 0 | Status error = adb->Shell(command.GetData(), seconds(5), nullptr); |
387 | |
|
388 | 0 | Log *log = GetLog(LLDBLog::Platform); |
389 | 0 | if (log && error.Fail()) |
390 | 0 | LLDB_LOGF(log, "Failed to remove temp directory: %s", |
391 | 0 | error.AsCString()); |
392 | 0 | }); |
393 | |
|
394 | 0 | FileSpec symfile_platform_filespec(tmpdir); |
395 | 0 | symfile_platform_filespec.AppendPathComponent("symbolized.oat"); |
396 | | |
397 | | // Execute oatdump on the remote device to generate a file with symtab |
398 | 0 | StreamString command; |
399 | 0 | command.Printf("oatdump --symbolize=%s --output=%s", |
400 | 0 | module_sp->GetPlatformFileSpec().GetPath(false).c_str(), |
401 | 0 | symfile_platform_filespec.GetPath(false).c_str()); |
402 | 0 | error = adb->Shell(command.GetData(), minutes(1), nullptr); |
403 | 0 | if (error.Fail()) |
404 | 0 | return Status("Oatdump failed: %s", error.AsCString()); |
405 | | |
406 | | // Download the symbolfile from the remote device |
407 | 0 | return GetFile(symfile_platform_filespec, dst_file_spec); |
408 | 0 | } |
409 | | |
410 | 0 | bool PlatformAndroid::GetRemoteOSVersion() { |
411 | 0 | m_os_version = llvm::VersionTuple(GetSdkVersion()); |
412 | 0 | return !m_os_version.empty(); |
413 | 0 | } |
414 | | |
415 | | llvm::StringRef |
416 | 0 | PlatformAndroid::GetLibdlFunctionDeclarations(lldb_private::Process *process) { |
417 | 0 | SymbolContextList matching_symbols; |
418 | 0 | std::vector<const char *> dl_open_names = {"__dl_dlopen", "dlopen"}; |
419 | 0 | const char *dl_open_name = nullptr; |
420 | 0 | Target &target = process->GetTarget(); |
421 | 0 | for (auto name : dl_open_names) { |
422 | 0 | target.GetImages().FindFunctionSymbols( |
423 | 0 | ConstString(name), eFunctionNameTypeFull, matching_symbols); |
424 | 0 | if (matching_symbols.GetSize()) { |
425 | 0 | dl_open_name = name; |
426 | 0 | break; |
427 | 0 | } |
428 | 0 | } |
429 | | // Older platform versions have the dl function symbols mangled |
430 | 0 | if (dl_open_name == dl_open_names[0]) |
431 | 0 | return R"( |
432 | 0 | extern "C" void* dlopen(const char*, int) asm("__dl_dlopen"); |
433 | 0 | extern "C" void* dlsym(void*, const char*) asm("__dl_dlsym"); |
434 | 0 | extern "C" int dlclose(void*) asm("__dl_dlclose"); |
435 | 0 | extern "C" char* dlerror(void) asm("__dl_dlerror"); |
436 | 0 | )"; |
437 | | |
438 | 0 | return PlatformPOSIX::GetLibdlFunctionDeclarations(process); |
439 | 0 | } |
440 | | |
441 | 0 | PlatformAndroid::AdbClientUP PlatformAndroid::GetAdbClient(Status &error) { |
442 | 0 | AdbClientUP adb(std::make_unique<AdbClient>(m_device_id)); |
443 | 0 | if (adb) |
444 | 0 | error.Clear(); |
445 | 0 | else |
446 | 0 | error = Status("Failed to create AdbClient"); |
447 | 0 | return adb; |
448 | 0 | } |
449 | | |
450 | 0 | llvm::StringRef PlatformAndroid::GetPropertyPackageName() { |
451 | 0 | return GetGlobalProperties().GetPropertyAtIndexAs<llvm::StringRef>( |
452 | 0 | ePropertyPlatformPackageName, ""); |
453 | 0 | } |
454 | | |
455 | 4 | std::string PlatformAndroid::GetRunAs() { |
456 | 4 | llvm::StringRef run_as = GetPropertyPackageName(); |
457 | 4 | 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 | 2 | return std::string("run-as '") + run_as.str() + "' "; |
467 | 2 | } |
468 | 2 | return run_as.str(); |
469 | 4 | } |
470 | | |
471 | 4 | AdbClient::SyncService *PlatformAndroid::GetSyncService(Status &error) { |
472 | 4 | if (m_adb_sync_svc && m_adb_sync_svc->IsConnected()0 ) |
473 | 0 | return m_adb_sync_svc.get(); |
474 | | |
475 | 4 | AdbClientUP adb(GetAdbClient(error)); |
476 | 4 | if (error.Fail()) |
477 | 0 | return nullptr; |
478 | 4 | m_adb_sync_svc = adb->GetSyncService(error); |
479 | 4 | return (error.Success()) ? m_adb_sync_svc.get() : nullptr0 ; |
480 | 4 | } |