diff --git a/lldb/source/Plugins/Platform/Android/CMakeLists.txt b/lldb/source/Plugins/Platform/Android/CMakeLists.txt --- a/lldb/source/Plugins/Platform/Android/CMakeLists.txt +++ b/lldb/source/Plugins/Platform/Android/CMakeLists.txt @@ -1,3 +1,11 @@ +lldb_tablegen(PlatformAndroidProperties.inc -gen-lldb-property-defs + SOURCE PlatformAndroidProperties.td + TARGET LLDBPluginPlatformAndroidPropertiesGen) + +lldb_tablegen(PlatformAndroidPropertiesEnum.inc -gen-lldb-property-enum-defs + SOURCE PlatformAndroidProperties.td + TARGET LLDBPluginPlatformAndroidPropertiesEnumGen) + add_lldb_library(lldbPluginPlatformAndroid PLUGIN AdbClient.cpp PlatformAndroid.cpp @@ -11,3 +19,7 @@ LINK_COMPONENTS Support ) + +add_dependencies(lldbPluginPlatformAndroid + LLDBPluginPlatformAndroidPropertiesGen + LLDBPluginPlatformAndroidPropertiesEnumGen) diff --git a/lldb/source/Plugins/Platform/Android/PlatformAndroid.h b/lldb/source/Plugins/Platform/Android/PlatformAndroid.h --- a/lldb/source/Plugins/Platform/Android/PlatformAndroid.h +++ b/lldb/source/Plugins/Platform/Android/PlatformAndroid.h @@ -30,6 +30,8 @@ // lldb_private::PluginInterface functions static lldb::PlatformSP CreateInstance(bool force, const ArchSpec *arch); + static void DebuggerInitialize(lldb_private::Debugger &debugger); + static llvm::StringRef GetPluginNameStatic(bool is_host) { return is_host ? Platform::GetHostPlatformName() : "remote-android"; } @@ -73,6 +75,10 @@ typedef std::unique_ptr AdbClientUP; virtual AdbClientUP GetAdbClient(Status &error); + virtual llvm::StringRef GetPropertyPackageName(); + + std::string GetRunAs(); + private: AdbClient::SyncService *GetSyncService(Status &error); diff --git a/lldb/source/Plugins/Platform/Android/PlatformAndroid.cpp b/lldb/source/Plugins/Platform/Android/PlatformAndroid.cpp --- a/lldb/source/Plugins/Platform/Android/PlatformAndroid.cpp +++ b/lldb/source/Plugins/Platform/Android/PlatformAndroid.cpp @@ -29,10 +29,36 @@ LLDB_PLUGIN_DEFINE(PlatformAndroid) -static uint32_t g_initialize_count = 0; -static const unsigned int g_android_default_cache_size = +namespace { + +#define LLDB_PROPERTIES_android +#include "PlatformAndroidProperties.inc" + +enum { +#define LLDB_PROPERTIES_android +#include "PlatformAndroidPropertiesEnum.inc" +}; + +class PluginProperties : public Properties { +public: + PluginProperties() { + m_collection_sp = std::make_shared( + ConstString(PlatformAndroid::GetPluginNameStatic(false))); + m_collection_sp->Initialize(g_android_properties); + } +}; + +static PluginProperties &GetGlobalProperties() { + static PluginProperties g_settings; + return g_settings; +} + +uint32_t g_initialize_count = 0; +const unsigned int g_android_default_cache_size = 2048; // Fits inside 4k adb packet. +} // end of anonymous namespace + void PlatformAndroid::Initialize() { PlatformLinux::Initialize(); @@ -45,7 +71,7 @@ PluginManager::RegisterPlugin( PlatformAndroid::GetPluginNameStatic(false), PlatformAndroid::GetPluginDescriptionStatic(false), - PlatformAndroid::CreateInstance); + PlatformAndroid::CreateInstance, PlatformAndroid::DebuggerInitialize); } } @@ -128,6 +154,16 @@ return PlatformSP(); } +void PlatformAndroid::DebuggerInitialize(Debugger &debugger) { + if (!PluginManager::GetSettingForPlatformPlugin( + debugger, ConstString(GetPluginNameStatic(false)))) { + PluginManager::CreateSettingForPlatformPlugin( + debugger, GetGlobalProperties().GetValueProperties(), + "Properties for the Android platform plugin.", + /*is_global_property=*/true); + } +} + PlatformAndroid::PlatformAndroid(bool is_host) : PlatformLinux(is_host), m_sdk_version(0) {} @@ -206,7 +242,8 @@ return error; char cmd[PATH_MAX]; - snprintf(cmd, sizeof(cmd), "cat '%s'", source_file.c_str()); + snprintf(cmd, sizeof(cmd), "%scat '%s'", GetRunAs().c_str(), + source_file.c_str()); return adb->ShellToFile(cmd, minutes(1), destination); } @@ -260,9 +297,9 @@ // Use 'shell dd' to download the file slice with the offset and size. char cmd[PATH_MAX]; snprintf(cmd, sizeof(cmd), - "dd if='%s' iflag=skip_bytes,count_bytes " + "%sdd if='%s' iflag=skip_bytes,count_bytes " "skip=%" PRIu64 " count=%" PRIu64 " status=none", - source_file.c_str(), src_offset, src_size); + GetRunAs().c_str(), source_file.c_str(), src_offset, src_size); return adb->ShellToFile(cmd, minutes(1), dst_file_spec); } @@ -410,6 +447,27 @@ return adb; } +llvm::StringRef PlatformAndroid::GetPropertyPackageName() { + return GetGlobalProperties().GetPropertyAtIndexAs( + ePropertyPlatformPackageName, ""); +} + +std::string PlatformAndroid::GetRunAs() { + llvm::StringRef run_as = GetPropertyPackageName(); + if (!run_as.empty()) { + // When LLDB fails to pull file from a package directory due to security + // constraint, user needs to set the package name to + // 'platform.plugin.remote-android.package-name' property in order to run + // shell commands as the package user using 'run-as' (e.g. to get file with + // 'cat' and 'dd'). + // https://cs.android.com/android/platform/superproject/+/master: + // system/core/run-as/run-as.cpp;l=39-61; + // drc=4a77a84a55522a3b122f9c63ef0d0b8a6a131627 + return std::string("run-as '") + run_as.str() + "' "; + } + return run_as.str(); +} + AdbClient::SyncService *PlatformAndroid::GetSyncService(Status &error) { if (m_adb_sync_svc && m_adb_sync_svc->IsConnected()) return m_adb_sync_svc.get(); diff --git a/lldb/source/Plugins/Platform/Android/PlatformAndroidProperties.td b/lldb/source/Plugins/Platform/Android/PlatformAndroidProperties.td new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/Platform/Android/PlatformAndroidProperties.td @@ -0,0 +1,9 @@ +include "../../../../include/lldb/Core/PropertiesBase.td" + +let Definition = "android" in { + def PlatformPackageName: Property<"package-name", "String">, + Global, + DefaultStringValue<"">, + Desc<"Specify package name to run adb shell command with 'run-as' as the " + "package user when necessary (e.g. to get file with 'cat' and 'dd').">; +} diff --git a/lldb/unittests/Platform/Android/PlatformAndroidTest.cpp b/lldb/unittests/Platform/Android/PlatformAndroidTest.cpp --- a/lldb/unittests/Platform/Android/PlatformAndroidTest.cpp +++ b/lldb/unittests/Platform/Android/PlatformAndroidTest.cpp @@ -49,6 +49,7 @@ } MOCK_METHOD1(GetAdbClient, AdbClientUP(Status &error)); + MOCK_METHOD0(GetPropertyPackageName, llvm::StringRef()); }; } // namespace @@ -112,6 +113,32 @@ .Success()); } +TEST_F(PlatformAndroidTest, DownloadModuleSliceWithZipFileAndRunAs) { + auto adb_client = new MockAdbClient(); + EXPECT_CALL(*adb_client, + ShellToFile(StrEq("run-as 'com.example.test' " + "dd if='/system/app/Test/Test.apk' " + "iflag=skip_bytes,count_bytes " + "skip=4096 count=3600 status=none"), + _, _)) + .Times(1) + .WillOnce(Return(Status())); + + EXPECT_CALL(*this, GetPropertyPackageName()) + .Times(1) + .WillOnce(Return(llvm::StringRef("com.example.test"))); + + EXPECT_CALL(*this, GetAdbClient(_)) + .Times(1) + .WillOnce(Return(ByMove(AdbClientUP(adb_client)))); + + EXPECT_TRUE( + DownloadModuleSlice( + FileSpec("/system/app/Test/Test.apk!/lib/arm64-v8a/libtest.so"), 4096, + 3600, FileSpec()) + .Success()); +} + TEST_F(PlatformAndroidTest, GetFileWithNormalFile) { auto sync_service = new MockSyncService(); EXPECT_CALL(*sync_service, Stat(FileSpec("/data/local/tmp/test"), _, _, _)) @@ -164,3 +191,40 @@ FileSpec()) .Success()); } + +TEST_F(PlatformAndroidTest, GetFileWithCatFallbackAndRunAs) { + auto sync_service = new MockSyncService(); + EXPECT_CALL( + *sync_service, + Stat(FileSpec("/data/data/com.example.app/lib-main/libtest.so"), _, _, _)) + .Times(1) + .WillOnce(DoAll(SetArgReferee<1>(0), Return(Status()))); + + auto adb_client0 = new MockAdbClient(); + EXPECT_CALL(*adb_client0, GetSyncService(_)) + .Times(1) + .WillOnce(Return(ByMove(SyncServiceUP(sync_service)))); + + auto adb_client1 = new MockAdbClient(); + EXPECT_CALL( + *adb_client1, + ShellToFile(StrEq("run-as 'com.example.app' " + "cat '/data/data/com.example.app/lib-main/libtest.so'"), + _, _)) + .Times(1) + .WillOnce(Return(Status())); + + EXPECT_CALL(*this, GetPropertyPackageName()) + .Times(1) + .WillOnce(Return(llvm::StringRef("com.example.app"))); + + EXPECT_CALL(*this, GetAdbClient(_)) + .Times(2) + .WillOnce(Return(ByMove(AdbClientUP(adb_client0)))) + .WillOnce(Return(ByMove(AdbClientUP(adb_client1)))); + + EXPECT_TRUE( + GetFile(FileSpec("/data/data/com.example.app/lib-main/libtest.so"), + FileSpec()) + .Success()); +}