brintos

brintos / llvm-project-archived public Read only

0
0
Text · 20.3 KiB · 22b9711 Raw
617 lines · cpp
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-exception6//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/UriParser.h"16#include "lldb/ValueObject/ValueObject.h"17 18#include "llvm/ADT/DenseMap.h"19 20#include "AdbClient.h"21#include "PlatformAndroid.h"22#include "PlatformAndroidRemoteGDBServer.h"23#include "lldb/Target/Target.h"24#include <optional>25 26using namespace lldb;27using namespace lldb_private;28using namespace lldb_private::platform_android;29using namespace std::chrono;30 31LLDB_PLUGIN_DEFINE(PlatformAndroid)32 33namespace {34 35#define LLDB_PROPERTIES_android36#include "PlatformAndroidProperties.inc"37 38enum {39#define LLDB_PROPERTIES_android40#include "PlatformAndroidPropertiesEnum.inc"41};42 43class PluginProperties : public Properties {44public:45  PluginProperties() {46    m_collection_sp = std::make_shared<OptionValueProperties>(47        PlatformAndroid::GetPluginNameStatic(false));48    m_collection_sp->Initialize(g_android_properties);49  }50};51 52static PluginProperties &GetGlobalProperties() {53  static PluginProperties g_settings;54  return g_settings;55}56 57uint32_t g_initialize_count = 0;58const unsigned int g_android_default_cache_size =59    2048; // Fits inside 4k adb packet.60 61} // end of anonymous namespace62 63void PlatformAndroid::Initialize() {64  PlatformLinux::Initialize();65 66  if (g_initialize_count++ == 0) {67#if defined(__ANDROID__)68    PlatformSP default_platform_sp(new PlatformAndroid(true));69    default_platform_sp->SetSystemArchitecture(HostInfo::GetArchitecture());70    Platform::SetHostPlatform(default_platform_sp);71#endif72    PluginManager::RegisterPlugin(73        PlatformAndroid::GetPluginNameStatic(false),74        PlatformAndroid::GetPluginDescriptionStatic(false),75        PlatformAndroid::CreateInstance, PlatformAndroid::DebuggerInitialize);76  }77}78 79void PlatformAndroid::Terminate() {80  if (g_initialize_count > 0) {81    if (--g_initialize_count == 0) {82      PluginManager::UnregisterPlugin(PlatformAndroid::CreateInstance);83    }84  }85 86  PlatformLinux::Terminate();87}88 89PlatformSP PlatformAndroid::CreateInstance(bool force, const ArchSpec *arch) {90  Log *log = GetLog(LLDBLog::Platform);91  if (log) {92    const char *arch_name;93    if (arch && arch->GetArchitectureName())94      arch_name = arch->GetArchitectureName();95    else96      arch_name = "<null>";97 98    const char *triple_cstr =99        arch ? arch->GetTriple().getTriple().c_str() : "<null>";100 101    LLDB_LOGF(log, "PlatformAndroid::%s(force=%s, arch={%s,%s})", __FUNCTION__,102              force ? "true" : "false", arch_name, triple_cstr);103  }104 105  bool create = force;106  if (!create && arch && arch->IsValid()) {107    const llvm::Triple &triple = arch->GetTriple();108    switch (triple.getVendor()) {109    case llvm::Triple::PC:110      create = true;111      break;112 113#if defined(__ANDROID__)114    // Only accept "unknown" for the vendor if the host is android and if115    // "unknown" wasn't specified (it was just returned because it was NOT116    // specified).117    case llvm::Triple::VendorType::UnknownVendor:118      create = !arch->TripleVendorWasSpecified();119      break;120#endif121    default:122      break;123    }124 125    if (create) {126      switch (triple.getEnvironment()) {127      case llvm::Triple::Android:128        break;129 130#if defined(__ANDROID__)131      // Only accept "unknown" for the OS if the host is android and it132      // "unknown" wasn't specified (it was just returned because it was NOT133      // specified)134      case llvm::Triple::EnvironmentType::UnknownEnvironment:135        create = !arch->TripleEnvironmentWasSpecified();136        break;137#endif138      default:139        create = false;140        break;141      }142    }143  }144 145  if (create) {146    LLDB_LOGF(log, "PlatformAndroid::%s() creating remote-android platform",147              __FUNCTION__);148    return PlatformSP(new PlatformAndroid(false));149  }150 151  LLDB_LOGF(152      log, "PlatformAndroid::%s() aborting creation of remote-android platform",153      __FUNCTION__);154 155  return PlatformSP();156}157 158void PlatformAndroid::DebuggerInitialize(Debugger &debugger) {159  if (!PluginManager::GetSettingForPlatformPlugin(debugger,160                                                  GetPluginNameStatic(false))) {161    PluginManager::CreateSettingForPlatformPlugin(162        debugger, GetGlobalProperties().GetValueProperties(),163        "Properties for the Android platform plugin.",164        /*is_global_property=*/true);165  }166}167 168PlatformAndroid::PlatformAndroid(bool is_host)169    : PlatformLinux(is_host), m_sdk_version(0) {}170 171llvm::StringRef PlatformAndroid::GetPluginDescriptionStatic(bool is_host) {172  if (is_host)173    return "Local Android user platform plug-in.";174  return "Remote Android user platform plug-in.";175}176 177Status PlatformAndroid::ConnectRemote(Args &args) {178  m_device_id.clear();179 180  if (IsHost())181    return Status::FromErrorString(182        "can't connect to the host platform, always connected");183 184  if (!m_remote_platform_sp)185    m_remote_platform_sp = PlatformSP(new PlatformAndroidRemoteGDBServer());186 187  const char *url = args.GetArgumentAtIndex(0);188  if (!url)189    return Status::FromErrorString("URL is null.");190  std::optional<URI> parsed_url = URI::Parse(url);191  if (!parsed_url)192    return Status::FromErrorStringWithFormat("Invalid URL: %s", url);193  if (parsed_url->hostname != "localhost")194    m_device_id = parsed_url->hostname.str();195 196  auto error = PlatformLinux::ConnectRemote(args);197  if (error.Success()) {198    auto resolved_device_id_or_error = AdbClient::ResolveDeviceID(m_device_id);199    if (!resolved_device_id_or_error)200      return Status::FromError(resolved_device_id_or_error.takeError());201    m_device_id = *resolved_device_id_or_error;202  }203  return error;204}205 206Status 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(false), FileSpec::Style::posix);212  if (source_spec.IsRelative())213    source_spec = GetRemoteWorkingDirectory().CopyByAppendingPathComponent(214        source_spec.GetPathAsConstString(false).GetStringRef());215 216  Status error;217  auto sync_service = GetSyncService(error);218 219  // If sync service is available, try to use it220  if (error.Success() && sync_service) {221    uint32_t mode = 0, size = 0, mtime = 0;222    error = sync_service->Stat(source_spec, mode, size, mtime);223    if (error.Success()) {224      if (mode != 0)225        return sync_service->PullFile(source_spec, destination);226 227      // mode == 0 can signify that adbd cannot access the file due security228      // constraints - fall through to try "cat ..." as a fallback.229      Log *log = GetLog(LLDBLog::Platform);230      LLDB_LOGF(log, "Got mode == 0 on '%s': try to get file via 'shell cat'",231                source_spec.GetPath(false).c_str());232    }233  }234 235  // Fallback to shell cat command if sync service failed or returned mode == 0236  std::string source_file = source_spec.GetPath(false);237 238  Log *log = GetLog(LLDBLog::Platform);239  LLDB_LOGF(log, "Using shell cat fallback for '%s'", source_file.c_str());240 241  if (strchr(source_file.c_str(), '\'') != nullptr)242    return Status::FromErrorString(243        "Doesn't support single-quotes in filenames");244 245  AdbClientUP adb(GetAdbClient(error));246  if (error.Fail())247    return error;248 249  char cmd[PATH_MAX];250  snprintf(cmd, sizeof(cmd), "%scat '%s'", GetRunAs().c_str(),251           source_file.c_str());252 253  return adb->ShellToFile(cmd, minutes(1), destination);254}255 256Status PlatformAndroid::PutFile(const FileSpec &source,257                                const FileSpec &destination, uint32_t uid,258                                uint32_t gid) {259  if (IsHost() || !m_remote_platform_sp)260    return PlatformLinux::PutFile(source, destination, uid, gid);261 262  FileSpec destination_spec(destination.GetPath(false), FileSpec::Style::posix);263  if (destination_spec.IsRelative())264    destination_spec = GetRemoteWorkingDirectory().CopyByAppendingPathComponent(265        destination_spec.GetPath(false));266 267  // TODO: Set correct uid and gid on remote file.268  Status error;269  auto sync_service = GetSyncService(error);270  if (error.Fail())271    return error;272  return sync_service->PushFile(source, destination_spec);273}274 275const char *PlatformAndroid::GetCacheHostname() { return m_device_id.c_str(); }276 277Status PlatformAndroid::DownloadModuleSlice(const FileSpec &src_file_spec,278                                            const uint64_t src_offset,279                                            const uint64_t src_size,280                                            const FileSpec &dst_file_spec) {281  std::string source_file = src_file_spec.GetPath(false);282  if (source_file.empty())283    return Status::FromErrorString("Source file path cannot be empty");284 285  std::string destination_file = dst_file_spec.GetPath(false);286  if (destination_file.empty())287    return Status::FromErrorString("Destination file path cannot be empty");288 289  // In Android API level 23 and above, dynamic loader is able to load .so290  // file directly from APK. In that case, src_offset will be an non-zero.291  if (src_offset == 0) // Use GetFile for a normal file.292    return GetFile(src_file_spec, dst_file_spec);293 294  if (source_file.find('\'') != std::string::npos)295    return Status::FromErrorString(296        "Doesn't support single-quotes in filenames");297 298  // For zip .so file, src_file_spec will be "zip_path!/so_path".299  // Extract "zip_path" from the source_file.300  static constexpr llvm::StringLiteral k_zip_separator("!/");301  size_t pos = source_file.find(k_zip_separator);302  if (pos != std::string::npos)303    source_file.resize(pos);304 305  Status error;306  AdbClientUP adb(GetAdbClient(error));307  if (error.Fail())308    return error;309 310  // Use 'shell dd' to download the file slice with the offset and size.311  char cmd[PATH_MAX];312  snprintf(cmd, sizeof(cmd),313           "%sdd if='%s' iflag=skip_bytes,count_bytes "314           "skip=%" PRIu64 " count=%" PRIu64 " status=none",315           GetRunAs().c_str(), source_file.c_str(), src_offset, src_size);316 317  return adb->ShellToFile(cmd, minutes(1), dst_file_spec);318}319 320Status PlatformAndroid::DisconnectRemote() {321  Status error = PlatformLinux::DisconnectRemote();322  if (error.Success()) {323    m_device_id.clear();324    m_sdk_version = 0;325  }326  return error;327}328 329uint32_t PlatformAndroid::GetDefaultMemoryCacheLineSize() {330  return g_android_default_cache_size;331}332 333uint32_t PlatformAndroid::GetSdkVersion() {334  if (!IsConnected())335    return 0;336 337  if (m_sdk_version != 0)338    return m_sdk_version;339 340  std::string version_string;341  Status error;342  AdbClientUP adb(GetAdbClient(error));343  if (error.Fail())344    return 0;345  error =346      adb->Shell("getprop ro.build.version.sdk", seconds(5), &version_string);347  version_string = llvm::StringRef(version_string).trim().str();348 349  if (error.Fail() || version_string.empty()) {350    Log *log = GetLog(LLDBLog::Platform);351    LLDB_LOGF(log, "Get SDK version failed. (error: %s, output: %s)",352              error.AsCString(), version_string.c_str());353    return 0;354  }355 356  // FIXME: improve error handling357  llvm::to_integer(version_string, m_sdk_version);358  return m_sdk_version;359}360 361Status PlatformAndroid::DownloadSymbolFile(const lldb::ModuleSP &module_sp,362                                           const FileSpec &dst_file_spec) {363  // For oat file we can try to fetch additional debug info from the device364  llvm::StringRef extension = module_sp->GetFileSpec().GetFileNameExtension();365  if (extension != ".oat" && extension != ".odex")366    return Status::FromErrorString(367        "Symbol file downloading only supported for oat and odex files");368 369  // If we have no information about the platform file we can't execute oatdump370  if (!module_sp->GetPlatformFileSpec())371    return Status::FromErrorString("No platform file specified");372 373  // Symbolizer isn't available before SDK version 23374  if (GetSdkVersion() < 23)375    return Status::FromErrorString(376        "Symbol file generation only supported on SDK 23+");377 378  // If we already have symtab then we don't have to try and generate one379  if (module_sp->GetSectionList()->FindSectionByName(ConstString(".symtab")) !=380      nullptr)381    return Status::FromErrorString("Symtab already available in the module");382 383  Status error;384  AdbClientUP adb(GetAdbClient(error));385  if (error.Fail())386    return error;387  std::string tmpdir;388  error = adb->Shell("mktemp --directory --tmpdir /data/local/tmp", seconds(5),389                     &tmpdir);390  if (error.Fail() || tmpdir.empty())391    return Status::FromErrorStringWithFormat(392        "Failed to generate temporary directory on the device (%s)",393        error.AsCString());394  tmpdir = llvm::StringRef(tmpdir).trim().str();395 396  // Create file remover for the temporary directory created on the device397  std::unique_ptr<std::string, std::function<void(std::string *)>>398      tmpdir_remover(&tmpdir, [&adb](std::string *s) {399        StreamString command;400        command.Printf("rm -rf %s", s->c_str());401        Status error = adb->Shell(command.GetData(), seconds(5), nullptr);402 403        Log *log = GetLog(LLDBLog::Platform);404        if (log && error.Fail())405          LLDB_LOGF(log, "Failed to remove temp directory: %s",406                    error.AsCString());407      });408 409  FileSpec symfile_platform_filespec(tmpdir);410  symfile_platform_filespec.AppendPathComponent("symbolized.oat");411 412  // Execute oatdump on the remote device to generate a file with symtab413  StreamString command;414  command.Printf("oatdump --symbolize=%s --output=%s",415                 module_sp->GetPlatformFileSpec().GetPath(false).c_str(),416                 symfile_platform_filespec.GetPath(false).c_str());417  error = adb->Shell(command.GetData(), minutes(1), nullptr);418  if (error.Fail())419    return Status::FromErrorStringWithFormat("Oatdump failed: %s",420                                             error.AsCString());421 422  // Download the symbolfile from the remote device423  return GetFile(symfile_platform_filespec, dst_file_spec);424}425 426bool PlatformAndroid::GetRemoteOSVersion() {427  m_os_version = llvm::VersionTuple(GetSdkVersion());428  return !m_os_version.empty();429}430 431llvm::StringRef432PlatformAndroid::GetLibdlFunctionDeclarations(lldb_private::Process *process) {433  SymbolContextList matching_symbols;434  std::vector<const char *> dl_open_names = {"__dl_dlopen", "dlopen"};435  const char *dl_open_name = nullptr;436  Target &target = process->GetTarget();437  for (auto *name : dl_open_names) {438    target.GetImages().FindFunctionSymbols(439        ConstString(name), eFunctionNameTypeFull, matching_symbols);440    if (matching_symbols.GetSize()) {441      dl_open_name = name;442      break;443    }444  }445  // Older platform versions have the dl function symbols mangled446  if (dl_open_name == dl_open_names[0])447    return R"(448              extern "C" void* dlopen(const char*, int) asm("__dl_dlopen");449              extern "C" void* dlsym(void*, const char*) asm("__dl_dlsym");450              extern "C" int   dlclose(void*) asm("__dl_dlclose");451              extern "C" char* dlerror(void) asm("__dl_dlerror");452             )";453 454  return PlatformPOSIX::GetLibdlFunctionDeclarations(process);455}456 457PlatformAndroid::AdbClientUP PlatformAndroid::GetAdbClient(Status &error) {458  AdbClientUP adb = std::make_unique<AdbClient>(m_device_id);459  error = adb->Connect();460  return adb;461}462 463llvm::StringRef PlatformAndroid::GetPropertyPackageName() {464  return GetGlobalProperties().GetPropertyAtIndexAs<llvm::StringRef>(465      ePropertyPlatformPackageName, "");466}467 468std::string PlatformAndroid::GetRunAs() {469  llvm::StringRef run_as = GetPropertyPackageName();470  if (!run_as.empty()) {471    // When LLDB fails to pull file from a package directory due to security472    // constraint, user needs to set the package name to473    // 'platform.plugin.remote-android.package-name' property in order to run474    // shell commands as the package user using 'run-as' (e.g. to get file with475    // 'cat' and 'dd').476    // https://cs.android.com/android/platform/superproject/+/master:477    // system/core/run-as/run-as.cpp;l=39-61;478    // drc=4a77a84a55522a3b122f9c63ef0d0b8a6a131627479    return std::string("run-as '") + run_as.str() + "' ";480  }481  return run_as.str();482}483 484static bool NeedsCmdlineSupplement(const ProcessInstanceInfo &proc_info) {485  llvm::StringRef name =486      proc_info.GetExecutableFile().GetFilename().GetStringRef();487  return name.contains("app_process") || name.contains("zygote");488}489 490// Fetch /proc/PID/cmdline for processes to get actual package names.491// Android apps often show as "zygote" or "app_process" without this.492static void SupplementWithCmdlineInfo(ProcessInstanceInfoList &proc_infos,493                                      AdbClient *adb) {494  if (proc_infos.empty())495    return;496 497  llvm::DenseMap<lldb::pid_t, ProcessInstanceInfo *> pid_map;498  std::string pid_list;499  for (auto &proc_info : proc_infos) {500    if (NeedsCmdlineSupplement(proc_info)) {501      lldb::pid_t pid = proc_info.GetProcessID();502      pid_map[pid] = &proc_info;503      if (!pid_list.empty())504        pid_list += " ";505      pid_list += std::to_string(pid);506    }507  }508 509  if (pid_list.empty())510    return;511 512  Log *log = GetLog(LLDBLog::Platform);513 514  // Use xargs -P to parallelize cmdline fetching (up to 8 concurrent reads)515  StreamString cmd;516  cmd.Printf(517      "echo '%s' | xargs -n 1 -P 8 sh -c "518      "'echo \"$1:$(cat /proc/$1/cmdline 2>/dev/null | tr \"\\0\" \" \")\"' sh",519      pid_list.c_str());520 521  std::string cmdline_output;522  Status error = adb->Shell(cmd.GetData(), seconds(5), &cmdline_output);523 524  if (error.Fail() || cmdline_output.empty())525    return;526 527  llvm::SmallVector<llvm::StringRef, 256> lines;528  llvm::StringRef(cmdline_output).split(lines, '\n', -1, false);529 530  for (llvm::StringRef line : lines) {531    line = line.trim();532    auto [pid_str, cmdline] = line.split(':');533    if (pid_str.empty() || cmdline.empty())534      continue;535 536    cmdline = cmdline.trim();537 538    lldb::pid_t pid;539    if (!llvm::to_integer(pid_str, pid) || cmdline.empty())540      continue;541 542    auto it = pid_map.find(pid);543    if (it == pid_map.end())544      continue;545 546    ProcessInstanceInfo *proc_info = it->second;547    llvm::SmallVector<llvm::StringRef, 16> args;548    cmdline.split(args, ' ', -1, false);549 550    if (!args.empty()) {551      proc_info->GetExecutableFile().SetFile(args[0], FileSpec::Style::posix);552 553      if (args.size() > 1) {554        Args process_args;555        for (size_t i = 1; i < args.size(); ++i) {556          if (!args[i].empty())557            process_args.AppendArgument(args[i]);558        }559        proc_info->SetArguments(process_args, false);560      }561 562      LLDB_LOGF(log,563                "PlatformAndroid::%s supplemented PID %llu with cmdline: %s",564                __FUNCTION__, static_cast<unsigned long long>(pid),565                cmdline.str().c_str());566    }567  }568}569 570uint32_t571PlatformAndroid::FindProcesses(const ProcessInstanceInfoMatch &match_info,572                               ProcessInstanceInfoList &proc_infos) {573  proc_infos.clear();574 575  if (IsHost())576    return PlatformLinux::FindProcesses(match_info, proc_infos);577 578  if (!m_remote_platform_sp)579    return 0;580 581  // Android-specific process name handling:582  // Apps spawned from zygote initially appear as "app_process" or "zygote"583  // in the process list, but their actual package names (e.g.,584  // "com.example.app") are only available in /proc/PID/cmdline. To support585  // name-based matching, we must first fetch cmdline info for all processes,586  // then apply the original name filter.587  ProcessInstanceInfoMatch broad_match_info = match_info;588  broad_match_info.SetNameMatchType(NameMatch::Ignore);589 590  ProcessInstanceInfoList all_procs;591  uint32_t count =592      m_remote_platform_sp->FindProcesses(broad_match_info, all_procs);593 594  if (count > 0) {595    Status error;596    AdbClientUP adb(GetAdbClient(error));597    if (error.Success())598      SupplementWithCmdlineInfo(all_procs, adb.get());599 600    // Apply the original name matching against supplemented process info.601    for (auto &proc_info : all_procs) {602      if (match_info.Matches(proc_info))603        proc_infos.push_back(proc_info);604    }605  }606 607  return proc_infos.size();608}609 610std::unique_ptr<AdbSyncService> PlatformAndroid::GetSyncService(Status &error) {611  auto sync_service = std::make_unique<AdbSyncService>(m_device_id);612  error = sync_service->SetupSyncConnection();613  if (error.Fail())614    return nullptr;615  return sync_service;616}617