100e305d2STamas Berghammer //===-- PlatformAndroid.cpp -------------------------------------*- C++ -*-===// 200e305d2STamas Berghammer // 300e305d2STamas Berghammer // The LLVM Compiler Infrastructure 400e305d2STamas Berghammer // 500e305d2STamas Berghammer // This file is distributed under the University of Illinois Open Source 600e305d2STamas Berghammer // License. See LICENSE.TXT for details. 700e305d2STamas Berghammer // 800e305d2STamas Berghammer //===----------------------------------------------------------------------===// 900e305d2STamas Berghammer 1000e305d2STamas Berghammer // C Includes 1100e305d2STamas Berghammer // C++ Includes 1200e305d2STamas Berghammer // Other libraries and framework includes 1300e305d2STamas Berghammer #include "lldb/Core/Log.h" 14ec3f92a5STamas Berghammer #include "lldb/Core/Module.h" 1500e305d2STamas Berghammer #include "lldb/Core/PluginManager.h" 163cb132a0STamas Berghammer #include "lldb/Core/Scalar.h" 17ec3f92a5STamas Berghammer #include "lldb/Core/Section.h" 183cb132a0STamas Berghammer #include "lldb/Core/ValueObject.h" 191c6a1ea9STamas Berghammer #include "lldb/Host/HostInfo.h" 201d0d90b9STamas Berghammer #include "lldb/Host/StringConvert.h" 213ea689b3SChaoren Lin #include "Utility/UriParser.h" 2200e305d2STamas Berghammer 2300e305d2STamas Berghammer // Project includes 246f001068SOleksiy Vyalov #include "AdbClient.h" 2500e305d2STamas Berghammer #include "PlatformAndroid.h" 2600e305d2STamas Berghammer #include "PlatformAndroidRemoteGDBServer.h" 2700e305d2STamas Berghammer 2800e305d2STamas Berghammer using namespace lldb; 2900e305d2STamas Berghammer using namespace lldb_private; 30db264a6dSTamas Berghammer using namespace lldb_private::platform_android; 3100e305d2STamas Berghammer 3200e305d2STamas Berghammer static uint32_t g_initialize_count = 0; 33f2991488SPavel Labath static const unsigned int g_android_default_cache_size = 2048; // Fits inside 4k adb packet. 3400e305d2STamas Berghammer 3500e305d2STamas Berghammer void 3600e305d2STamas Berghammer PlatformAndroid::Initialize () 3700e305d2STamas Berghammer { 383c4f89d7STamas Berghammer PlatformLinux::Initialize (); 393c4f89d7STamas Berghammer 4000e305d2STamas Berghammer if (g_initialize_count++ == 0) 4100e305d2STamas Berghammer { 421c6a1ea9STamas Berghammer #if defined(__ANDROID__) 431c6a1ea9STamas Berghammer PlatformSP default_platform_sp (new PlatformAndroid(true)); 441c6a1ea9STamas Berghammer default_platform_sp->SetSystemArchitecture(HostInfo::GetArchitecture()); 451c6a1ea9STamas Berghammer Platform::SetHostPlatform (default_platform_sp); 461c6a1ea9STamas Berghammer #endif 471c6a1ea9STamas Berghammer PluginManager::RegisterPlugin (PlatformAndroid::GetPluginNameStatic(false), 481c6a1ea9STamas Berghammer PlatformAndroid::GetPluginDescriptionStatic(false), 4900e305d2STamas Berghammer PlatformAndroid::CreateInstance); 5000e305d2STamas Berghammer } 5100e305d2STamas Berghammer } 5200e305d2STamas Berghammer 5300e305d2STamas Berghammer void 5400e305d2STamas Berghammer PlatformAndroid::Terminate () 5500e305d2STamas Berghammer { 5600e305d2STamas Berghammer if (g_initialize_count > 0) 5700e305d2STamas Berghammer { 5800e305d2STamas Berghammer if (--g_initialize_count == 0) 5900e305d2STamas Berghammer { 6000e305d2STamas Berghammer PluginManager::UnregisterPlugin (PlatformAndroid::CreateInstance); 6100e305d2STamas Berghammer } 6200e305d2STamas Berghammer } 633c4f89d7STamas Berghammer 643c4f89d7STamas Berghammer PlatformLinux::Terminate (); 6500e305d2STamas Berghammer } 6600e305d2STamas Berghammer 6700e305d2STamas Berghammer PlatformSP 6800e305d2STamas Berghammer PlatformAndroid::CreateInstance (bool force, const ArchSpec *arch) 6900e305d2STamas Berghammer { 70db264a6dSTamas Berghammer Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM)); 7100e305d2STamas Berghammer if (log) 7200e305d2STamas Berghammer { 7300e305d2STamas Berghammer const char *arch_name; 7400e305d2STamas Berghammer if (arch && arch->GetArchitectureName ()) 7500e305d2STamas Berghammer arch_name = arch->GetArchitectureName (); 7600e305d2STamas Berghammer else 7700e305d2STamas Berghammer arch_name = "<null>"; 7800e305d2STamas Berghammer 7900e305d2STamas Berghammer const char *triple_cstr = arch ? arch->GetTriple ().getTriple ().c_str() : "<null>"; 8000e305d2STamas Berghammer 8100e305d2STamas Berghammer log->Printf ("PlatformAndroid::%s(force=%s, arch={%s,%s})", __FUNCTION__, force ? "true" : "false", arch_name, triple_cstr); 8200e305d2STamas Berghammer } 8300e305d2STamas Berghammer 8400e305d2STamas Berghammer bool create = force; 8500e305d2STamas Berghammer if (create == false && arch && arch->IsValid()) 8600e305d2STamas Berghammer { 8700e305d2STamas Berghammer const llvm::Triple &triple = arch->GetTriple(); 8800e305d2STamas Berghammer switch (triple.getVendor()) 8900e305d2STamas Berghammer { 9000e305d2STamas Berghammer case llvm::Triple::PC: 9100e305d2STamas Berghammer create = true; 9200e305d2STamas Berghammer break; 9300e305d2STamas Berghammer 9400e305d2STamas Berghammer #if defined(__ANDROID__) 9500e305d2STamas Berghammer // Only accept "unknown" for the vendor if the host is android and 9600e305d2STamas Berghammer // it "unknown" wasn't specified (it was just returned because it 9700e305d2STamas Berghammer // was NOT specified_ 9800e305d2STamas Berghammer case llvm::Triple::VendorType::UnknownVendor: 9900e305d2STamas Berghammer create = !arch->TripleVendorWasSpecified(); 10000e305d2STamas Berghammer break; 10100e305d2STamas Berghammer #endif 10200e305d2STamas Berghammer default: 10300e305d2STamas Berghammer break; 10400e305d2STamas Berghammer } 10500e305d2STamas Berghammer 10600e305d2STamas Berghammer if (create) 10700e305d2STamas Berghammer { 10800e305d2STamas Berghammer switch (triple.getOS()) 10900e305d2STamas Berghammer { 1101c6a1ea9STamas Berghammer case llvm::Triple::Android: 11100e305d2STamas Berghammer break; 11200e305d2STamas Berghammer 11300e305d2STamas Berghammer #if defined(__ANDROID__) 11400e305d2STamas Berghammer // Only accept "unknown" for the OS if the host is android and 11500e305d2STamas Berghammer // it "unknown" wasn't specified (it was just returned because it 11600e305d2STamas Berghammer // was NOT specified) 11700e305d2STamas Berghammer case llvm::Triple::OSType::UnknownOS: 11800e305d2STamas Berghammer create = !arch->TripleOSWasSpecified(); 11900e305d2STamas Berghammer break; 12000e305d2STamas Berghammer #endif 12100e305d2STamas Berghammer default: 12200e305d2STamas Berghammer create = false; 12300e305d2STamas Berghammer break; 12400e305d2STamas Berghammer } 12500e305d2STamas Berghammer } 12600e305d2STamas Berghammer } 12700e305d2STamas Berghammer 12800e305d2STamas Berghammer if (create) 12900e305d2STamas Berghammer { 13000e305d2STamas Berghammer if (log) 13100e305d2STamas Berghammer log->Printf ("PlatformAndroid::%s() creating remote-android platform", __FUNCTION__); 1321c6a1ea9STamas Berghammer return PlatformSP(new PlatformAndroid(false)); 13300e305d2STamas Berghammer } 13400e305d2STamas Berghammer 13500e305d2STamas Berghammer if (log) 13600e305d2STamas Berghammer log->Printf ("PlatformAndroid::%s() aborting creation of remote-android platform", __FUNCTION__); 13700e305d2STamas Berghammer 13800e305d2STamas Berghammer return PlatformSP(); 13900e305d2STamas Berghammer } 14000e305d2STamas Berghammer 1411c6a1ea9STamas Berghammer PlatformAndroid::PlatformAndroid (bool is_host) : 1421d0d90b9STamas Berghammer PlatformLinux(is_host), 1431d0d90b9STamas Berghammer m_sdk_version(0) 14400e305d2STamas Berghammer { 14500e305d2STamas Berghammer } 14600e305d2STamas Berghammer 14700e305d2STamas Berghammer PlatformAndroid::~PlatformAndroid() 14800e305d2STamas Berghammer { 14900e305d2STamas Berghammer } 15000e305d2STamas Berghammer 151db264a6dSTamas Berghammer ConstString 1521c6a1ea9STamas Berghammer PlatformAndroid::GetPluginNameStatic (bool is_host) 1531c6a1ea9STamas Berghammer { 1541c6a1ea9STamas Berghammer if (is_host) 1551c6a1ea9STamas Berghammer { 1561c6a1ea9STamas Berghammer static ConstString g_host_name(Platform::GetHostPlatformName ()); 1571c6a1ea9STamas Berghammer return g_host_name; 1581c6a1ea9STamas Berghammer } 1591c6a1ea9STamas Berghammer else 16000e305d2STamas Berghammer { 16100e305d2STamas Berghammer static ConstString g_remote_name("remote-android"); 16200e305d2STamas Berghammer return g_remote_name; 16300e305d2STamas Berghammer } 1641c6a1ea9STamas Berghammer } 16500e305d2STamas Berghammer 16600e305d2STamas Berghammer const char * 1671c6a1ea9STamas Berghammer PlatformAndroid::GetPluginDescriptionStatic (bool is_host) 16800e305d2STamas Berghammer { 1691c6a1ea9STamas Berghammer if (is_host) 1701c6a1ea9STamas Berghammer return "Local Android user platform plug-in."; 1711c6a1ea9STamas Berghammer else 17200e305d2STamas Berghammer return "Remote Android user platform plug-in."; 17300e305d2STamas Berghammer } 17400e305d2STamas Berghammer 175db264a6dSTamas Berghammer ConstString 17600e305d2STamas Berghammer PlatformAndroid::GetPluginName() 17700e305d2STamas Berghammer { 1781c6a1ea9STamas Berghammer return GetPluginNameStatic(IsHost()); 17900e305d2STamas Berghammer } 18000e305d2STamas Berghammer 18100e305d2STamas Berghammer Error 18200e305d2STamas Berghammer PlatformAndroid::ConnectRemote(Args& args) 18300e305d2STamas Berghammer { 1846f001068SOleksiy Vyalov m_device_id.clear(); 1856f001068SOleksiy Vyalov 1861c6a1ea9STamas Berghammer if (IsHost()) 1871c6a1ea9STamas Berghammer { 1881c6a1ea9STamas Berghammer return Error ("can't connect to the host platform '%s', always connected", GetPluginName().GetCString()); 1891c6a1ea9STamas Berghammer } 1901c6a1ea9STamas Berghammer 19100e305d2STamas Berghammer if (!m_remote_platform_sp) 19200e305d2STamas Berghammer m_remote_platform_sp = PlatformSP(new PlatformAndroidRemoteGDBServer()); 1936f001068SOleksiy Vyalov 1943ea689b3SChaoren Lin int port; 1953ea689b3SChaoren Lin std::string scheme, host, path; 1963ea689b3SChaoren Lin const char *url = args.GetArgumentAtIndex(0); 1973ea689b3SChaoren Lin if (!url) 1983ea689b3SChaoren Lin return Error("URL is null."); 1993ea689b3SChaoren Lin if (!UriParser::Parse(url, scheme, host, port, path)) 2003ea689b3SChaoren Lin return Error("Invalid URL: %s", url); 201a29d6475SOleksiy Vyalov if (host != "localhost") 2023ea689b3SChaoren Lin m_device_id = host; 2033ea689b3SChaoren Lin 2046f001068SOleksiy Vyalov auto error = PlatformLinux::ConnectRemote(args); 2056f001068SOleksiy Vyalov if (error.Success()) 2066f001068SOleksiy Vyalov { 2076f001068SOleksiy Vyalov AdbClient adb; 2083ea689b3SChaoren Lin error = AdbClient::CreateByDeviceID(m_device_id, adb); 2096f001068SOleksiy Vyalov if (error.Fail()) 2106f001068SOleksiy Vyalov return error; 2116f001068SOleksiy Vyalov 2126f001068SOleksiy Vyalov m_device_id = adb.GetDeviceID(); 2136f001068SOleksiy Vyalov } 2146f001068SOleksiy Vyalov return error; 2156f001068SOleksiy Vyalov } 2166f001068SOleksiy Vyalov 2170b5ebef7SOleksiy Vyalov Error 2180b5ebef7SOleksiy Vyalov PlatformAndroid::GetFile (const FileSpec& source, 2190b5ebef7SOleksiy Vyalov const FileSpec& destination) 2200b5ebef7SOleksiy Vyalov { 221291f59c5SOleksiy Vyalov if (IsHost() || !m_remote_platform_sp) 2220b5ebef7SOleksiy Vyalov return PlatformLinux::GetFile(source, destination); 223291f59c5SOleksiy Vyalov 22432a9da56SOleksiy Vyalov FileSpec source_spec (source.GetPath (false), false, FileSpec::ePathSyntaxPosix); 225372e9067SChaoren Lin if (source_spec.IsRelative()) 22632a9da56SOleksiy Vyalov source_spec = GetRemoteWorkingDirectory ().CopyByAppendingPathComponent (source_spec.GetCString (false)); 227291f59c5SOleksiy Vyalov 228291f59c5SOleksiy Vyalov AdbClient adb (m_device_id); 229291f59c5SOleksiy Vyalov return adb.PullFile (source_spec, destination); 2300b5ebef7SOleksiy Vyalov } 2310b5ebef7SOleksiy Vyalov 2320b5ebef7SOleksiy Vyalov Error 2330b5ebef7SOleksiy Vyalov PlatformAndroid::PutFile (const FileSpec& source, 2340b5ebef7SOleksiy Vyalov const FileSpec& destination, 23562efb1f6SRobert Flack uint32_t uid, 23662efb1f6SRobert Flack uint32_t gid) 23762efb1f6SRobert Flack { 23817701b59SOleksiy Vyalov if (IsHost() || !m_remote_platform_sp) 23917701b59SOleksiy Vyalov return PlatformLinux::PutFile (source, destination, uid, gid); 24017701b59SOleksiy Vyalov 24117701b59SOleksiy Vyalov FileSpec destination_spec (destination.GetPath (false), false, FileSpec::ePathSyntaxPosix); 242372e9067SChaoren Lin if (destination_spec.IsRelative()) 24317701b59SOleksiy Vyalov destination_spec = GetRemoteWorkingDirectory ().CopyByAppendingPathComponent (destination_spec.GetCString (false)); 24417701b59SOleksiy Vyalov 24562efb1f6SRobert Flack AdbClient adb (m_device_id); 24662efb1f6SRobert Flack // TODO: Set correct uid and gid on remote file. 24717701b59SOleksiy Vyalov return adb.PushFile(source, destination_spec); 24862efb1f6SRobert Flack } 24962efb1f6SRobert Flack 2506f001068SOleksiy Vyalov const char * 2516f001068SOleksiy Vyalov PlatformAndroid::GetCacheHostname () 2526f001068SOleksiy Vyalov { 2536f001068SOleksiy Vyalov return m_device_id.c_str (); 25400e305d2STamas Berghammer } 25509e9079dSOleksiy Vyalov 25609e9079dSOleksiy Vyalov Error 25709e9079dSOleksiy Vyalov PlatformAndroid::DownloadModuleSlice (const FileSpec &src_file_spec, 25809e9079dSOleksiy Vyalov const uint64_t src_offset, 25909e9079dSOleksiy Vyalov const uint64_t src_size, 26009e9079dSOleksiy Vyalov const FileSpec &dst_file_spec) 26109e9079dSOleksiy Vyalov { 26209e9079dSOleksiy Vyalov if (src_offset != 0) 26309e9079dSOleksiy Vyalov return Error ("Invalid offset - %" PRIu64, src_offset); 26409e9079dSOleksiy Vyalov 2650b5ebef7SOleksiy Vyalov return GetFile (src_file_spec, dst_file_spec); 26609e9079dSOleksiy Vyalov } 2671d0d90b9STamas Berghammer 2681d0d90b9STamas Berghammer Error 2691d0d90b9STamas Berghammer PlatformAndroid::DisconnectRemote() 2701d0d90b9STamas Berghammer { 2711d0d90b9STamas Berghammer Error error = PlatformLinux::DisconnectRemote(); 2721d0d90b9STamas Berghammer if (error.Success()) 2731d0d90b9STamas Berghammer { 2741d0d90b9STamas Berghammer m_device_id.clear(); 2751d0d90b9STamas Berghammer m_sdk_version = 0; 2761d0d90b9STamas Berghammer } 2771d0d90b9STamas Berghammer return error; 2781d0d90b9STamas Berghammer } 2791d0d90b9STamas Berghammer 2801d0d90b9STamas Berghammer uint32_t 281f2991488SPavel Labath PlatformAndroid::GetDefaultMemoryCacheLineSize() 282f2991488SPavel Labath { 283f2991488SPavel Labath return g_android_default_cache_size; 284f2991488SPavel Labath } 285f2991488SPavel Labath 286f2991488SPavel Labath uint32_t 2871d0d90b9STamas Berghammer PlatformAndroid::GetSdkVersion() 2881d0d90b9STamas Berghammer { 2891d0d90b9STamas Berghammer if (!IsConnected()) 2901d0d90b9STamas Berghammer return 0; 2911d0d90b9STamas Berghammer 2921d0d90b9STamas Berghammer if (m_sdk_version != 0) 2931d0d90b9STamas Berghammer return m_sdk_version; 2941d0d90b9STamas Berghammer 2951d0d90b9STamas Berghammer std::string version_string; 2963e8947beSTamas Berghammer AdbClient adb(m_device_id); 2973e8947beSTamas Berghammer Error error = adb.Shell("getprop ro.build.version.sdk", 5000 /* ms */, &version_string); 2983e8947beSTamas Berghammer version_string = llvm::StringRef(version_string).trim().str(); 2993e8947beSTamas Berghammer 3003e8947beSTamas Berghammer if (error.Fail() || version_string.empty()) 3011d0d90b9STamas Berghammer { 3021d0d90b9STamas Berghammer Log* log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM); 3031d0d90b9STamas Berghammer if (log) 3043e8947beSTamas Berghammer log->Printf("Get SDK version failed. (error: %s, output: %s)", 3053e8947beSTamas Berghammer error.AsCString(), version_string.c_str()); 3061d0d90b9STamas Berghammer return 0; 3071d0d90b9STamas Berghammer } 3081d0d90b9STamas Berghammer 3091d0d90b9STamas Berghammer m_sdk_version = StringConvert::ToUInt32(version_string.c_str()); 3101d0d90b9STamas Berghammer return m_sdk_version; 3111d0d90b9STamas Berghammer } 312ec3f92a5STamas Berghammer 313ec3f92a5STamas Berghammer Error 314ec3f92a5STamas Berghammer PlatformAndroid::DownloadSymbolFile (const lldb::ModuleSP& module_sp, 315ec3f92a5STamas Berghammer const FileSpec& dst_file_spec) 316ec3f92a5STamas Berghammer { 317ec3f92a5STamas Berghammer // For oat file we can try to fetch additional debug info from the device 318ec3f92a5STamas Berghammer if (module_sp->GetFileSpec().GetFileNameExtension() != ConstString("oat")) 319ec3f92a5STamas Berghammer return Error("Symbol file downloading only supported for oat files"); 320ec3f92a5STamas Berghammer 321ec3f92a5STamas Berghammer // If we have no information about the platform file we can't execute oatdump 322ec3f92a5STamas Berghammer if (!module_sp->GetPlatformFileSpec()) 323ec3f92a5STamas Berghammer return Error("No platform file specified"); 324ec3f92a5STamas Berghammer 325ec3f92a5STamas Berghammer // Symbolizer isn't available before SDK version 23 326ec3f92a5STamas Berghammer if (GetSdkVersion() < 23) 327ec3f92a5STamas Berghammer return Error("Symbol file generation only supported on SDK 23+"); 328ec3f92a5STamas Berghammer 329ec3f92a5STamas Berghammer // If we already have symtab then we don't have to try and generate one 330ec3f92a5STamas Berghammer if (module_sp->GetSectionList()->FindSectionByName(ConstString(".symtab")) != nullptr) 331ec3f92a5STamas Berghammer return Error("Symtab already available in the module"); 332ec3f92a5STamas Berghammer 3339d8dde8cSTamas Berghammer AdbClient adb(m_device_id); 334ec3f92a5STamas Berghammer 3359d8dde8cSTamas Berghammer std::string tmpdir; 3369d8dde8cSTamas Berghammer Error error = adb.Shell("mktemp --directory --tmpdir /data/local/tmp", 5000 /* ms */, &tmpdir); 3379d8dde8cSTamas Berghammer if (error.Fail() || tmpdir.empty()) 338ec3f92a5STamas Berghammer return Error("Failed to generate temporary directory on the device (%s)", error.AsCString()); 339539cd118STamas Berghammer tmpdir = llvm::StringRef(tmpdir).trim().str(); 340ec3f92a5STamas Berghammer 341ec3f92a5STamas Berghammer // Create file remover for the temporary directory created on the device 342ec3f92a5STamas Berghammer std::unique_ptr<std::string, std::function<void(std::string*)>> tmpdir_remover( 343ec3f92a5STamas Berghammer &tmpdir, 3449d8dde8cSTamas Berghammer [this, &adb](std::string* s) { 345ec3f92a5STamas Berghammer StreamString command; 346ec3f92a5STamas Berghammer command.Printf("rm -rf %s", s->c_str()); 3479d8dde8cSTamas Berghammer Error error = adb.Shell(command.GetData(), 5000 /* ms */, nullptr); 348ec3f92a5STamas Berghammer 349ec3f92a5STamas Berghammer Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM)); 350ec3f92a5STamas Berghammer if (error.Fail()) 351ec3f92a5STamas Berghammer log->Printf("Failed to remove temp directory: %s", error.AsCString()); 352ec3f92a5STamas Berghammer } 353ec3f92a5STamas Berghammer ); 354ec3f92a5STamas Berghammer 355ec3f92a5STamas Berghammer FileSpec symfile_platform_filespec(tmpdir.c_str(), false); 356ec3f92a5STamas Berghammer symfile_platform_filespec.AppendPathComponent("symbolized.oat"); 357ec3f92a5STamas Berghammer 358ec3f92a5STamas Berghammer // Execute oatdump on the remote device to generate a file with symtab 3599d8dde8cSTamas Berghammer StreamString command; 360ec3f92a5STamas Berghammer command.Printf("oatdump --symbolize=%s --output=%s", 361ec3f92a5STamas Berghammer module_sp->GetPlatformFileSpec().GetCString(false), 362ec3f92a5STamas Berghammer symfile_platform_filespec.GetCString(false)); 3639d8dde8cSTamas Berghammer error = adb.Shell(command.GetData(), 60000 /* ms */, nullptr); 3649d8dde8cSTamas Berghammer if (error.Fail()) 365ec3f92a5STamas Berghammer return Error("Oatdump failed: %s", error.AsCString()); 366ec3f92a5STamas Berghammer 367ec3f92a5STamas Berghammer // Download the symbolfile from the remote device 368ec3f92a5STamas Berghammer return GetFile(symfile_platform_filespec, dst_file_spec); 369ec3f92a5STamas Berghammer } 3703c47151dSTamas Berghammer 3713c47151dSTamas Berghammer bool 3723c47151dSTamas Berghammer PlatformAndroid::GetRemoteOSVersion () 3733c47151dSTamas Berghammer { 3743c47151dSTamas Berghammer m_major_os_version = GetSdkVersion(); 3753c47151dSTamas Berghammer m_minor_os_version = 0; 3763c47151dSTamas Berghammer m_update_os_version = 0; 3773c47151dSTamas Berghammer return m_major_os_version != 0; 3783c47151dSTamas Berghammer } 3793cb132a0STamas Berghammer 380*fcdde63fSTamas Berghammer const char* 381*fcdde63fSTamas Berghammer PlatformAndroid::GetLibdlFunctionDeclarations() const 3823cb132a0STamas Berghammer { 383*fcdde63fSTamas Berghammer return R"( 384*fcdde63fSTamas Berghammer extern "C" void* dlopen(const char*, int) asm("__dl_dlopen"); 385*fcdde63fSTamas Berghammer extern "C" void* dlsym(void*, const char*) asm("__dl_dlsym"); 386*fcdde63fSTamas Berghammer extern "C" int dlclose(void*) asm("__dl_dlclose"); 387*fcdde63fSTamas Berghammer extern "C" char* dlerror(void) asm("__dl_dlerror"); 3883cb132a0STamas Berghammer )"; 3893cb132a0STamas Berghammer } 390