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/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" 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(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 PlatformAndroid::GetPluginNameStatic(false), 73 PlatformAndroid::GetPluginDescriptionStatic(false), 74 PlatformAndroid::CreateInstance, PlatformAndroid::DebuggerInitialize); 75 } 76 } 77 78 void PlatformAndroid::Terminate() { 79 if (g_initialize_count > 0) { 80 if (--g_initialize_count == 0) { 81 PluginManager::UnregisterPlugin(PlatformAndroid::CreateInstance); 82 } 83 } 84 85 PlatformLinux::Terminate(); 86 } 87 88 PlatformSP PlatformAndroid::CreateInstance(bool force, const ArchSpec *arch) { 89 Log *log = GetLog(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 GetPluginNameStatic(false))) { 160 PluginManager::CreateSettingForPlatformPlugin( 161 debugger, GetGlobalProperties().GetValueProperties(), 162 "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::FromErrorString( 181 "can't connect to the host platform, always connected"); 182 183 if (!m_remote_platform_sp) 184 m_remote_platform_sp = PlatformSP(new PlatformAndroidRemoteGDBServer()); 185 186 const char *url = args.GetArgumentAtIndex(0); 187 if (!url) 188 return Status::FromErrorString("URL is null."); 189 std::optional<URI> parsed_url = URI::Parse(url); 190 if (!parsed_url) 191 return Status::FromErrorStringWithFormat("Invalid URL: %s", url); 192 if (parsed_url->hostname != "localhost") 193 m_device_id = parsed_url->hostname.str(); 194 195 auto error = PlatformLinux::ConnectRemote(args); 196 if (error.Success()) { 197 AdbClient adb; 198 error = AdbClient::CreateByDeviceID(m_device_id, adb); 199 if (error.Fail()) 200 return error; 201 202 m_device_id = adb.GetDeviceID(); 203 } 204 return error; 205 } 206 207 Status PlatformAndroid::GetFile(const FileSpec &source, 208 const FileSpec &destination) { 209 if (IsHost() || !m_remote_platform_sp) 210 return PlatformLinux::GetFile(source, destination); 211 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()); 216 217 Status error; 218 auto sync_service = GetSyncService(error); 219 if (error.Fail()) 220 return error; 221 222 uint32_t mode = 0, size = 0, mtime = 0; 223 error = sync_service->Stat(source_spec, mode, size, mtime); 224 if (error.Fail()) 225 return error; 226 227 if (mode != 0) 228 return sync_service->PullFile(source_spec, destination); 229 230 std::string source_file = source_spec.GetPath(false); 231 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()); 235 236 if (strchr(source_file.c_str(), '\'') != nullptr) 237 return Status::FromErrorString( 238 "Doesn't support single-quotes in filenames"); 239 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)); 243 if (error.Fail()) 244 return error; 245 246 char cmd[PATH_MAX]; 247 snprintf(cmd, sizeof(cmd), "%scat '%s'", GetRunAs().c_str(), 248 source_file.c_str()); 249 250 return adb->ShellToFile(cmd, minutes(1), destination); 251 } 252 253 Status PlatformAndroid::PutFile(const FileSpec &source, 254 const FileSpec &destination, uint32_t uid, 255 uint32_t gid) { 256 if (IsHost() || !m_remote_platform_sp) 257 return PlatformLinux::PutFile(source, destination, uid, gid); 258 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)); 263 264 // TODO: Set correct uid and gid on remote file. 265 Status error; 266 auto sync_service = GetSyncService(error); 267 if (error.Fail()) 268 return error; 269 return sync_service->PushFile(source, destination_spec); 270 } 271 272 const char *PlatformAndroid::GetCacheHostname() { return m_device_id.c_str(); } 273 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); 282 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"); 287 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); 294 295 Status error; 296 AdbClientUP adb(GetAdbClient(error)); 297 if (error.Fail()) 298 return error; 299 300 // Use 'shell dd' to download the file slice with the offset and size. 301 char cmd[PATH_MAX]; 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); 306 307 return adb->ShellToFile(cmd, minutes(1), dst_file_spec); 308 } 309 310 Status PlatformAndroid::DisconnectRemote() { 311 Status error = PlatformLinux::DisconnectRemote(); 312 if (error.Success()) { 313 m_device_id.clear(); 314 m_sdk_version = 0; 315 } 316 return error; 317 } 318 319 uint32_t PlatformAndroid::GetDefaultMemoryCacheLineSize() { 320 return g_android_default_cache_size; 321 } 322 323 uint32_t PlatformAndroid::GetSdkVersion() { 324 if (!IsConnected()) 325 return 0; 326 327 if (m_sdk_version != 0) 328 return m_sdk_version; 329 330 std::string version_string; 331 Status error; 332 AdbClientUP adb(GetAdbClient(error)); 333 if (error.Fail()) 334 return 0; 335 error = 336 adb->Shell("getprop ro.build.version.sdk", seconds(5), &version_string); 337 version_string = llvm::StringRef(version_string).trim().str(); 338 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()); 343 return 0; 344 } 345 346 // FIXME: improve error handling 347 llvm::to_integer(version_string, m_sdk_version); 348 return m_sdk_version; 349 } 350 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"); 358 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"); 362 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+"); 367 368 // If we already have symtab then we don't have to try and generate one 369 if (module_sp->GetSectionList()->FindSectionByName(ConstString(".symtab")) != 370 nullptr) 371 return Status::FromErrorString("Symtab already available in the module"); 372 373 Status error; 374 AdbClientUP adb(GetAdbClient(error)); 375 if (error.Fail()) 376 return error; 377 std::string tmpdir; 378 error = adb->Shell("mktemp --directory --tmpdir /data/local/tmp", seconds(5), 379 &tmpdir); 380 if (error.Fail() || tmpdir.empty()) 381 return Status::FromErrorStringWithFormat( 382 "Failed to generate temporary directory on the device (%s)", 383 error.AsCString()); 384 tmpdir = llvm::StringRef(tmpdir).trim().str(); 385 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); 392 393 Log *log = GetLog(LLDBLog::Platform); 394 if (log && error.Fail()) 395 LLDB_LOGF(log, "Failed to remove temp directory: %s", 396 error.AsCString()); 397 }); 398 399 FileSpec symfile_platform_filespec(tmpdir); 400 symfile_platform_filespec.AppendPathComponent("symbolized.oat"); 401 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); 408 if (error.Fail()) 409 return Status::FromErrorStringWithFormat("Oatdump failed: %s", 410 error.AsCString()); 411 412 // Download the symbolfile from the remote device 413 return GetFile(symfile_platform_filespec, dst_file_spec); 414 } 415 416 bool PlatformAndroid::GetRemoteOSVersion() { 417 m_os_version = llvm::VersionTuple(GetSdkVersion()); 418 return !m_os_version.empty(); 419 } 420 421 llvm::StringRef 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()) { 431 dl_open_name = name; 432 break; 433 } 434 } 435 // Older platform versions have the dl function symbols mangled 436 if (dl_open_name == dl_open_names[0]) 437 return R"( 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"); 442 )"; 443 444 return PlatformPOSIX::GetLibdlFunctionDeclarations(process); 445 } 446 447 PlatformAndroid::AdbClientUP PlatformAndroid::GetAdbClient(Status &error) { 448 AdbClientUP adb(std::make_unique<AdbClient>(m_device_id)); 449 if (adb) 450 error.Clear(); 451 else 452 error = Status::FromErrorString("Failed to create AdbClient"); 453 return adb; 454 } 455 456 llvm::StringRef PlatformAndroid::GetPropertyPackageName() { 457 return GetGlobalProperties().GetPropertyAtIndexAs<llvm::StringRef>( 458 ePropertyPlatformPackageName, ""); 459 } 460 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 468 // 'cat' and 'dd'). 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() + "' "; 473 } 474 return run_as.str(); 475 } 476 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(); 480 481 AdbClientUP adb(GetAdbClient(error)); 482 if (error.Fail()) 483 return nullptr; 484 m_adb_sync_svc = adb->GetSyncService(error); 485 return (error.Success()) ? m_adb_sync_svc.get() : nullptr; 486 } 487