1 //===-- PlatformAndroid.cpp -----------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "lldb/Core/Module.h"
10 #include "lldb/Core/PluginManager.h"
11 #include "lldb/Core/Section.h"
12 #include "lldb/Host/HostInfo.h"
13 #include "lldb/Utility/LLDBLog.h"
14 #include "lldb/Utility/Log.h"
15 #include "lldb/Utility/Scalar.h"
16 #include "lldb/Utility/UriParser.h"
17 #include "lldb/ValueObject/ValueObject.h"
19 #include "AdbClient.h"
20 #include "PlatformAndroid.h"
21 #include "PlatformAndroidRemoteGDBServer.h"
22 #include "lldb/Target/Target.h"
26 using namespace lldb_private
;
27 using namespace lldb_private::platform_android
;
28 using namespace std::chrono
;
30 LLDB_PLUGIN_DEFINE(PlatformAndroid
)
34 #define LLDB_PROPERTIES_android
35 #include "PlatformAndroidProperties.inc"
38 #define LLDB_PROPERTIES_android
39 #include "PlatformAndroidPropertiesEnum.inc"
42 class PluginProperties
: public Properties
{
45 m_collection_sp
= std::make_shared
<OptionValueProperties
>(
46 PlatformAndroid::GetPluginNameStatic(false));
47 m_collection_sp
->Initialize(g_android_properties
);
51 static PluginProperties
&GetGlobalProperties() {
52 static PluginProperties g_settings
;
56 uint32_t g_initialize_count
= 0;
57 const unsigned int g_android_default_cache_size
=
58 2048; // Fits inside 4k adb packet.
60 } // end of anonymous namespace
62 void PlatformAndroid::Initialize() {
63 PlatformLinux::Initialize();
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
);
71 PluginManager::RegisterPlugin(
72 PlatformAndroid::GetPluginNameStatic(false),
73 PlatformAndroid::GetPluginDescriptionStatic(false),
74 PlatformAndroid::CreateInstance
, PlatformAndroid::DebuggerInitialize
);
78 void PlatformAndroid::Terminate() {
79 if (g_initialize_count
> 0) {
80 if (--g_initialize_count
== 0) {
81 PluginManager::UnregisterPlugin(PlatformAndroid::CreateInstance
);
85 PlatformLinux::Terminate();
88 PlatformSP
PlatformAndroid::CreateInstance(bool force
, const ArchSpec
*arch
) {
89 Log
*log
= GetLog(LLDBLog::Platform
);
91 const char *arch_name
;
92 if (arch
&& arch
->GetArchitectureName())
93 arch_name
= arch
->GetArchitectureName();
97 const char *triple_cstr
=
98 arch
? arch
->GetTriple().getTriple().c_str() : "<null>";
100 LLDB_LOGF(log
, "PlatformAndroid::%s(force=%s, arch={%s,%s})", __FUNCTION__
,
101 force
? "true" : "false", arch_name
, triple_cstr
);
105 if (!create
&& arch
&& arch
->IsValid()) {
106 const llvm::Triple
&triple
= arch
->GetTriple();
107 switch (triple
.getVendor()) {
108 case llvm::Triple::PC
:
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
116 case llvm::Triple::VendorType::UnknownVendor
:
117 create
= !arch
->TripleVendorWasSpecified();
125 switch (triple
.getEnvironment()) {
126 case llvm::Triple::Android
:
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
133 case llvm::Triple::EnvironmentType::UnknownEnvironment
:
134 create
= !arch
->TripleEnvironmentWasSpecified();
145 LLDB_LOGF(log
, "PlatformAndroid::%s() creating remote-android platform",
147 return PlatformSP(new PlatformAndroid(false));
151 log
, "PlatformAndroid::%s() aborting creation of remote-android platform",
157 void PlatformAndroid::DebuggerInitialize(Debugger
&debugger
) {
158 if (!PluginManager::GetSettingForPlatformPlugin(debugger
,
159 GetPluginNameStatic(false))) {
160 PluginManager::CreateSettingForPlatformPlugin(
161 debugger
, GetGlobalProperties().GetValueProperties(),
162 "Properties for the Android platform plugin.",
163 /*is_global_property=*/true);
167 PlatformAndroid::PlatformAndroid(bool is_host
)
168 : PlatformLinux(is_host
), m_sdk_version(0) {}
170 llvm::StringRef
PlatformAndroid::GetPluginDescriptionStatic(bool is_host
) {
172 return "Local Android user platform plug-in.";
173 return "Remote Android user platform plug-in.";
176 Status
PlatformAndroid::ConnectRemote(Args
&args
) {
180 return Status::FromErrorString(
181 "can't connect to the host platform, always connected");
183 if (!m_remote_platform_sp
)
184 m_remote_platform_sp
= PlatformSP(new PlatformAndroidRemoteGDBServer());
186 const char *url
= args
.GetArgumentAtIndex(0);
188 return Status::FromErrorString("URL is null.");
189 std::optional
<URI
> parsed_url
= URI::Parse(url
);
191 return Status::FromErrorStringWithFormat("Invalid URL: %s", url
);
192 if (parsed_url
->hostname
!= "localhost")
193 m_device_id
= parsed_url
->hostname
.str();
195 auto error
= PlatformLinux::ConnectRemote(args
);
196 if (error
.Success()) {
198 error
= AdbClient::CreateByDeviceID(m_device_id
, adb
);
202 m_device_id
= adb
.GetDeviceID();
207 Status
PlatformAndroid::GetFile(const FileSpec
&source
,
208 const FileSpec
&destination
) {
209 if (IsHost() || !m_remote_platform_sp
)
210 return PlatformLinux::GetFile(source
, destination
);
212 FileSpec
source_spec(source
.GetPath(false), FileSpec::Style::posix
);
213 if (source_spec
.IsRelative())
214 source_spec
= GetRemoteWorkingDirectory().CopyByAppendingPathComponent(
215 source_spec
.GetPathAsConstString(false).GetStringRef());
218 auto sync_service
= GetSyncService(error
);
222 uint32_t mode
= 0, size
= 0, mtime
= 0;
223 error
= sync_service
->Stat(source_spec
, mode
, size
, mtime
);
228 return sync_service
->PullFile(source_spec
, destination
);
230 std::string source_file
= source_spec
.GetPath(false);
232 Log
*log
= GetLog(LLDBLog::Platform
);
233 LLDB_LOGF(log
, "Got mode == 0 on '%s': try to get file via 'shell cat'",
234 source_file
.c_str());
236 if (strchr(source_file
.c_str(), '\'') != nullptr)
237 return Status::FromErrorString(
238 "Doesn't support single-quotes in filenames");
240 // mode == 0 can signify that adbd cannot access the file due security
241 // constraints - try "cat ..." as a fallback.
242 AdbClientUP
adb(GetAdbClient(error
));
247 snprintf(cmd
, sizeof(cmd
), "%scat '%s'", GetRunAs().c_str(),
248 source_file
.c_str());
250 return adb
->ShellToFile(cmd
, minutes(1), destination
);
253 Status
PlatformAndroid::PutFile(const FileSpec
&source
,
254 const FileSpec
&destination
, uint32_t uid
,
256 if (IsHost() || !m_remote_platform_sp
)
257 return PlatformLinux::PutFile(source
, destination
, uid
, gid
);
259 FileSpec
destination_spec(destination
.GetPath(false), FileSpec::Style::posix
);
260 if (destination_spec
.IsRelative())
261 destination_spec
= GetRemoteWorkingDirectory().CopyByAppendingPathComponent(
262 destination_spec
.GetPath(false));
264 // TODO: Set correct uid and gid on remote file.
266 auto sync_service
= GetSyncService(error
);
269 return sync_service
->PushFile(source
, destination_spec
);
272 const char *PlatformAndroid::GetCacheHostname() { return m_device_id
.c_str(); }
274 Status
PlatformAndroid::DownloadModuleSlice(const FileSpec
&src_file_spec
,
275 const uint64_t src_offset
,
276 const uint64_t src_size
,
277 const FileSpec
&dst_file_spec
) {
278 // In Android API level 23 and above, dynamic loader is able to load .so
279 // file directly from APK. In that case, src_offset will be an non-zero.
280 if (src_offset
== 0) // Use GetFile for a normal file.
281 return GetFile(src_file_spec
, dst_file_spec
);
283 std::string source_file
= src_file_spec
.GetPath(false);
284 if (source_file
.find('\'') != std::string::npos
)
285 return Status::FromErrorString(
286 "Doesn't support single-quotes in filenames");
288 // For zip .so file, src_file_spec will be "zip_path!/so_path".
289 // Extract "zip_path" from the source_file.
290 static constexpr llvm::StringLiteral
k_zip_separator("!/");
291 size_t pos
= source_file
.find(k_zip_separator
);
292 if (pos
!= std::string::npos
)
293 source_file
.resize(pos
);
296 AdbClientUP
adb(GetAdbClient(error
));
300 // Use 'shell dd' to download the file slice with the offset and size.
302 snprintf(cmd
, sizeof(cmd
),
303 "%sdd if='%s' iflag=skip_bytes,count_bytes "
304 "skip=%" PRIu64
" count=%" PRIu64
" status=none",
305 GetRunAs().c_str(), source_file
.c_str(), src_offset
, src_size
);
307 return adb
->ShellToFile(cmd
, minutes(1), dst_file_spec
);
310 Status
PlatformAndroid::DisconnectRemote() {
311 Status error
= PlatformLinux::DisconnectRemote();
312 if (error
.Success()) {
319 uint32_t PlatformAndroid::GetDefaultMemoryCacheLineSize() {
320 return g_android_default_cache_size
;
323 uint32_t PlatformAndroid::GetSdkVersion() {
327 if (m_sdk_version
!= 0)
328 return m_sdk_version
;
330 std::string version_string
;
332 AdbClientUP
adb(GetAdbClient(error
));
336 adb
->Shell("getprop ro.build.version.sdk", seconds(5), &version_string
);
337 version_string
= llvm::StringRef(version_string
).trim().str();
339 if (error
.Fail() || version_string
.empty()) {
340 Log
*log
= GetLog(LLDBLog::Platform
);
341 LLDB_LOGF(log
, "Get SDK version failed. (error: %s, output: %s)",
342 error
.AsCString(), version_string
.c_str());
346 // FIXME: improve error handling
347 llvm::to_integer(version_string
, m_sdk_version
);
348 return m_sdk_version
;
351 Status
PlatformAndroid::DownloadSymbolFile(const lldb::ModuleSP
&module_sp
,
352 const FileSpec
&dst_file_spec
) {
353 // For oat file we can try to fetch additional debug info from the device
354 llvm::StringRef extension
= module_sp
->GetFileSpec().GetFileNameExtension();
355 if (extension
!= ".oat" && extension
!= ".odex")
356 return Status::FromErrorString(
357 "Symbol file downloading only supported for oat and odex files");
359 // If we have no information about the platform file we can't execute oatdump
360 if (!module_sp
->GetPlatformFileSpec())
361 return Status::FromErrorString("No platform file specified");
363 // Symbolizer isn't available before SDK version 23
364 if (GetSdkVersion() < 23)
365 return Status::FromErrorString(
366 "Symbol file generation only supported on SDK 23+");
368 // If we already have symtab then we don't have to try and generate one
369 if (module_sp
->GetSectionList()->FindSectionByName(ConstString(".symtab")) !=
371 return Status::FromErrorString("Symtab already available in the module");
374 AdbClientUP
adb(GetAdbClient(error
));
378 error
= adb
->Shell("mktemp --directory --tmpdir /data/local/tmp", seconds(5),
380 if (error
.Fail() || tmpdir
.empty())
381 return Status::FromErrorStringWithFormat(
382 "Failed to generate temporary directory on the device (%s)",
384 tmpdir
= llvm::StringRef(tmpdir
).trim().str();
386 // Create file remover for the temporary directory created on the device
387 std::unique_ptr
<std::string
, std::function
<void(std::string
*)>>
388 tmpdir_remover(&tmpdir
, [&adb
](std::string
*s
) {
389 StreamString command
;
390 command
.Printf("rm -rf %s", s
->c_str());
391 Status error
= adb
->Shell(command
.GetData(), seconds(5), nullptr);
393 Log
*log
= GetLog(LLDBLog::Platform
);
394 if (log
&& error
.Fail())
395 LLDB_LOGF(log
, "Failed to remove temp directory: %s",
399 FileSpec
symfile_platform_filespec(tmpdir
);
400 symfile_platform_filespec
.AppendPathComponent("symbolized.oat");
402 // Execute oatdump on the remote device to generate a file with symtab
403 StreamString command
;
404 command
.Printf("oatdump --symbolize=%s --output=%s",
405 module_sp
->GetPlatformFileSpec().GetPath(false).c_str(),
406 symfile_platform_filespec
.GetPath(false).c_str());
407 error
= adb
->Shell(command
.GetData(), minutes(1), nullptr);
409 return Status::FromErrorStringWithFormat("Oatdump failed: %s",
412 // Download the symbolfile from the remote device
413 return GetFile(symfile_platform_filespec
, dst_file_spec
);
416 bool PlatformAndroid::GetRemoteOSVersion() {
417 m_os_version
= llvm::VersionTuple(GetSdkVersion());
418 return !m_os_version
.empty();
422 PlatformAndroid::GetLibdlFunctionDeclarations(lldb_private::Process
*process
) {
423 SymbolContextList matching_symbols
;
424 std::vector
<const char *> dl_open_names
= {"__dl_dlopen", "dlopen"};
425 const char *dl_open_name
= nullptr;
426 Target
&target
= process
->GetTarget();
427 for (auto name
: dl_open_names
) {
428 target
.GetImages().FindFunctionSymbols(
429 ConstString(name
), eFunctionNameTypeFull
, matching_symbols
);
430 if (matching_symbols
.GetSize()) {
435 // Older platform versions have the dl function symbols mangled
436 if (dl_open_name
== dl_open_names
[0])
438 extern "C
" void* dlopen(const char*, int) asm("__dl_dlopen
");
439 extern "C
" void* dlsym(void*, const char*) asm("__dl_dlsym
");
440 extern "C
" int dlclose(void*) asm("__dl_dlclose
");
441 extern "C
" char* dlerror(void) asm("__dl_dlerror
");
444 return PlatformPOSIX::GetLibdlFunctionDeclarations(process
);
447 PlatformAndroid::AdbClientUP
PlatformAndroid::GetAdbClient(Status
&error
) {
448 AdbClientUP
adb(std::make_unique
<AdbClient
>(m_device_id
));
452 error
= Status::FromErrorString("Failed to create AdbClient");
456 llvm::StringRef
PlatformAndroid::GetPropertyPackageName() {
457 return GetGlobalProperties().GetPropertyAtIndexAs
<llvm::StringRef
>(
458 ePropertyPlatformPackageName
, "");
461 std::string
PlatformAndroid::GetRunAs() {
462 llvm::StringRef run_as
= GetPropertyPackageName();
463 if (!run_as
.empty()) {
464 // When LLDB fails to pull file from a package directory due to security
465 // constraint, user needs to set the package name to
466 // 'platform.plugin.remote-android.package-name' property in order to run
467 // shell commands as the package user using 'run-as' (e.g. to get file with
469 // https://cs.android.com/android/platform/superproject/+/master:
470 // system/core/run-as/run-as.cpp;l=39-61;
471 // drc=4a77a84a55522a3b122f9c63ef0d0b8a6a131627
472 return std::string("run-as '") + run_as
.str() + "' ";
477 AdbClient::SyncService
*PlatformAndroid::GetSyncService(Status
&error
) {
478 if (m_adb_sync_svc
&& m_adb_sync_svc
->IsConnected())
479 return m_adb_sync_svc
.get();
481 AdbClientUP
adb(GetAdbClient(error
));
484 m_adb_sync_svc
= adb
->GetSyncService(error
);
485 return (error
.Success()) ? m_adb_sync_svc
.get() : nullptr;