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 AdbClientSP; virtual AdbClientSP GetAdbClient(); + virtual llvm::StringRef GetPropertyRunAs(); + + std::string GetRunAs(); + private: AdbClient::SyncService *GetSyncService(Status &error); @@ -81,7 +87,7 @@ uint32_t m_sdk_version; }; -} // namespace platofor_android +} // namespace platform_android } // namespace lldb_private #endif // LLDB_SOURCE_PLUGINS_PLATFORM_ANDROID_PLATFORMANDROID_H 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) {} @@ -204,7 +240,8 @@ AdbClientSP adb(GetAdbClient()); 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); } @@ -255,9 +292,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); } @@ -324,7 +361,7 @@ AdbClientSP adb(GetAdbClient()); std::string tmpdir; Status error = adb->Shell("mktemp --directory --tmpdir /data/local/tmp", - seconds(5), &tmpdir); + seconds(5), &tmpdir); if (error.Fail() || tmpdir.empty()) return Status("Failed to generate temporary directory on the device (%s)", error.AsCString()); @@ -394,6 +431,26 @@ return std::make_unique(m_device_id); } +llvm::StringRef PlatformAndroid::GetPropertyRunAs() { + return GetGlobalProperties().GetPropertyAtIndexAs( + ePropertyPlatformRunAs, ""); +} + +std::string PlatformAndroid::GetRunAs() { + llvm::StringRef run_as = GetPropertyRunAs(); + 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.run-as' property in order to run shell + // commands as the package user. (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 PlatformRunAs: Property<"run-as", "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 @@ -52,6 +52,7 @@ } MOCK_METHOD0(GetAdbClient, AdbClientSP()); + MOCK_METHOD0(GetPropertyRunAs, llvm::StringRef()); }; } // namespace @@ -100,6 +101,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, GetPropertyRunAs()) + .Times(1) + .WillOnce(Return(llvm::StringRef("com.example.test"))); + + EXPECT_CALL(*this, GetAdbClient()) + .Times(1) + .WillOnce(Return(ByMove(std::move(AdbClientSP(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"), _, _, _)) @@ -152,3 +179,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(std::move(SyncServiceSP(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, GetPropertyRunAs()) + .Times(1) + .WillOnce(Return(llvm::StringRef("com.example.app"))); + + EXPECT_CALL(*this, GetAdbClient()) + .Times(2) + .WillOnce(Return(ByMove(std::move(AdbClientSP(adb_client0))))) + .WillOnce(Return(ByMove(std::move(AdbClientSP(adb_client1))))); + + EXPECT_TRUE( + GetFile(FileSpec("/data/data/com.example.app/lib-main/libtest.so"), + FileSpec()) + .Success()); +}