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/Core/ValueObject.h"
13 #include "lldb/Host/HostInfo.h"
14 #include "lldb/Utility/LLDBLog.h"
15 #include "lldb/Utility/Log.h"
16 #include "lldb/Utility/Scalar.h"
17 #include "lldb/Utility/UriParser.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 static uint32_t g_initialize_count = 0;
33 static const unsigned int g_android_default_cache_size =
34 2048; // Fits inside 4k adb packet.
35
Initialize()36 void PlatformAndroid::Initialize() {
37 PlatformLinux::Initialize();
38
39 if (g_initialize_count++ == 0) {
40 #if defined(__ANDROID__)
41 PlatformSP default_platform_sp(new PlatformAndroid(true));
42 default_platform_sp->SetSystemArchitecture(HostInfo::GetArchitecture());
43 Platform::SetHostPlatform(default_platform_sp);
44 #endif
45 PluginManager::RegisterPlugin(
46 PlatformAndroid::GetPluginNameStatic(false),
47 PlatformAndroid::GetPluginDescriptionStatic(false),
48 PlatformAndroid::CreateInstance);
49 }
50 }
51
Terminate()52 void PlatformAndroid::Terminate() {
53 if (g_initialize_count > 0) {
54 if (--g_initialize_count == 0) {
55 PluginManager::UnregisterPlugin(PlatformAndroid::CreateInstance);
56 }
57 }
58
59 PlatformLinux::Terminate();
60 }
61
CreateInstance(bool force,const ArchSpec * arch)62 PlatformSP PlatformAndroid::CreateInstance(bool force, const ArchSpec *arch) {
63 Log *log = GetLog(LLDBLog::Platform);
64 if (log) {
65 const char *arch_name;
66 if (arch && arch->GetArchitectureName())
67 arch_name = arch->GetArchitectureName();
68 else
69 arch_name = "<null>";
70
71 const char *triple_cstr =
72 arch ? arch->GetTriple().getTriple().c_str() : "<null>";
73
74 LLDB_LOGF(log, "PlatformAndroid::%s(force=%s, arch={%s,%s})", __FUNCTION__,
75 force ? "true" : "false", arch_name, triple_cstr);
76 }
77
78 bool create = force;
79 if (!create && arch && arch->IsValid()) {
80 const llvm::Triple &triple = arch->GetTriple();
81 switch (triple.getVendor()) {
82 case llvm::Triple::PC:
83 create = true;
84 break;
85
86 #if defined(__ANDROID__)
87 // Only accept "unknown" for the vendor if the host is android and if
88 // "unknown" wasn't specified (it was just returned because it was NOT
89 // specified).
90 case llvm::Triple::VendorType::UnknownVendor:
91 create = !arch->TripleVendorWasSpecified();
92 break;
93 #endif
94 default:
95 break;
96 }
97
98 if (create) {
99 switch (triple.getEnvironment()) {
100 case llvm::Triple::Android:
101 break;
102
103 #if defined(__ANDROID__)
104 // Only accept "unknown" for the OS if the host is android and it
105 // "unknown" wasn't specified (it was just returned because it was NOT
106 // specified)
107 case llvm::Triple::EnvironmentType::UnknownEnvironment:
108 create = !arch->TripleEnvironmentWasSpecified();
109 break;
110 #endif
111 default:
112 create = false;
113 break;
114 }
115 }
116 }
117
118 if (create) {
119 LLDB_LOGF(log, "PlatformAndroid::%s() creating remote-android platform",
120 __FUNCTION__);
121 return PlatformSP(new PlatformAndroid(false));
122 }
123
124 LLDB_LOGF(
125 log, "PlatformAndroid::%s() aborting creation of remote-android platform",
126 __FUNCTION__);
127
128 return PlatformSP();
129 }
130
PlatformAndroid(bool is_host)131 PlatformAndroid::PlatformAndroid(bool is_host)
132 : PlatformLinux(is_host), m_sdk_version(0) {}
133
GetPluginDescriptionStatic(bool is_host)134 llvm::StringRef PlatformAndroid::GetPluginDescriptionStatic(bool is_host) {
135 if (is_host)
136 return "Local Android user platform plug-in.";
137 return "Remote Android user platform plug-in.";
138 }
139
ConnectRemote(Args & args)140 Status PlatformAndroid::ConnectRemote(Args &args) {
141 m_device_id.clear();
142
143 if (IsHost())
144 return Status("can't connect to the host platform, always connected");
145
146 if (!m_remote_platform_sp)
147 m_remote_platform_sp = PlatformSP(new PlatformAndroidRemoteGDBServer());
148
149 const char *url = args.GetArgumentAtIndex(0);
150 if (!url)
151 return Status("URL is null.");
152 std::optional<URI> parsed_url = URI::Parse(url);
153 if (!parsed_url)
154 return Status("Invalid URL: %s", url);
155 if (parsed_url->hostname != "localhost")
156 m_device_id = parsed_url->hostname.str();
157
158 auto error = PlatformLinux::ConnectRemote(args);
159 if (error.Success()) {
160 AdbClient adb;
161 error = AdbClient::CreateByDeviceID(m_device_id, adb);
162 if (error.Fail())
163 return error;
164
165 m_device_id = adb.GetDeviceID();
166 }
167 return error;
168 }
169
GetFile(const FileSpec & source,const FileSpec & destination)170 Status PlatformAndroid::GetFile(const FileSpec &source,
171 const FileSpec &destination) {
172 if (IsHost() || !m_remote_platform_sp)
173 return PlatformLinux::GetFile(source, destination);
174
175 FileSpec source_spec(source.GetPath(false), FileSpec::Style::posix);
176 if (source_spec.IsRelative())
177 source_spec = GetRemoteWorkingDirectory().CopyByAppendingPathComponent(
178 source_spec.GetPathAsConstString(false).GetStringRef());
179
180 Status error;
181 auto sync_service = GetSyncService(error);
182 if (error.Fail())
183 return error;
184
185 uint32_t mode = 0, size = 0, mtime = 0;
186 error = sync_service->Stat(source_spec, mode, size, mtime);
187 if (error.Fail())
188 return error;
189
190 if (mode != 0)
191 return sync_service->PullFile(source_spec, destination);
192
193 std::string source_file = source_spec.GetPath(false);
194
195 Log *log = GetLog(LLDBLog::Platform);
196 LLDB_LOGF(log, "Got mode == 0 on '%s': try to get file via 'shell cat'",
197 source_file.c_str());
198
199 if (strchr(source_file.c_str(), '\'') != nullptr)
200 return Status("Doesn't support single-quotes in filenames");
201
202 // mode == 0 can signify that adbd cannot access the file due security
203 // constraints - try "cat ..." as a fallback.
204 AdbClient adb(m_device_id);
205
206 char cmd[PATH_MAX];
207 snprintf(cmd, sizeof(cmd), "cat '%s'", source_file.c_str());
208
209 return adb.ShellToFile(cmd, minutes(1), destination);
210 }
211
PutFile(const FileSpec & source,const FileSpec & destination,uint32_t uid,uint32_t gid)212 Status PlatformAndroid::PutFile(const FileSpec &source,
213 const FileSpec &destination, uint32_t uid,
214 uint32_t gid) {
215 if (IsHost() || !m_remote_platform_sp)
216 return PlatformLinux::PutFile(source, destination, uid, gid);
217
218 FileSpec destination_spec(destination.GetPath(false), FileSpec::Style::posix);
219 if (destination_spec.IsRelative())
220 destination_spec = GetRemoteWorkingDirectory().CopyByAppendingPathComponent(
221 destination_spec.GetPath(false));
222
223 // TODO: Set correct uid and gid on remote file.
224 Status error;
225 auto sync_service = GetSyncService(error);
226 if (error.Fail())
227 return error;
228 return sync_service->PushFile(source, destination_spec);
229 }
230
GetCacheHostname()231 const char *PlatformAndroid::GetCacheHostname() { return m_device_id.c_str(); }
232
DownloadModuleSlice(const FileSpec & src_file_spec,const uint64_t src_offset,const uint64_t src_size,const FileSpec & dst_file_spec)233 Status PlatformAndroid::DownloadModuleSlice(const FileSpec &src_file_spec,
234 const uint64_t src_offset,
235 const uint64_t src_size,
236 const FileSpec &dst_file_spec) {
237 if (src_offset != 0)
238 return Status("Invalid offset - %" PRIu64, src_offset);
239
240 return GetFile(src_file_spec, dst_file_spec);
241 }
242
DisconnectRemote()243 Status PlatformAndroid::DisconnectRemote() {
244 Status error = PlatformLinux::DisconnectRemote();
245 if (error.Success()) {
246 m_device_id.clear();
247 m_sdk_version = 0;
248 }
249 return error;
250 }
251
GetDefaultMemoryCacheLineSize()252 uint32_t PlatformAndroid::GetDefaultMemoryCacheLineSize() {
253 return g_android_default_cache_size;
254 }
255
GetSdkVersion()256 uint32_t PlatformAndroid::GetSdkVersion() {
257 if (!IsConnected())
258 return 0;
259
260 if (m_sdk_version != 0)
261 return m_sdk_version;
262
263 std::string version_string;
264 AdbClient adb(m_device_id);
265 Status error =
266 adb.Shell("getprop ro.build.version.sdk", seconds(5), &version_string);
267 version_string = llvm::StringRef(version_string).trim().str();
268
269 if (error.Fail() || version_string.empty()) {
270 Log *log = GetLog(LLDBLog::Platform);
271 LLDB_LOGF(log, "Get SDK version failed. (error: %s, output: %s)",
272 error.AsCString(), version_string.c_str());
273 return 0;
274 }
275
276 // FIXME: improve error handling
277 llvm::to_integer(version_string, m_sdk_version);
278 return m_sdk_version;
279 }
280
DownloadSymbolFile(const lldb::ModuleSP & module_sp,const FileSpec & dst_file_spec)281 Status PlatformAndroid::DownloadSymbolFile(const lldb::ModuleSP &module_sp,
282 const FileSpec &dst_file_spec) {
283 // For oat file we can try to fetch additional debug info from the device
284 ConstString extension = module_sp->GetFileSpec().GetFileNameExtension();
285 if (extension != ".oat" && extension != ".odex")
286 return Status(
287 "Symbol file downloading only supported for oat and odex files");
288
289 // If we have no information about the platform file we can't execute oatdump
290 if (!module_sp->GetPlatformFileSpec())
291 return Status("No platform file specified");
292
293 // Symbolizer isn't available before SDK version 23
294 if (GetSdkVersion() < 23)
295 return Status("Symbol file generation only supported on SDK 23+");
296
297 // If we already have symtab then we don't have to try and generate one
298 if (module_sp->GetSectionList()->FindSectionByName(ConstString(".symtab")) !=
299 nullptr)
300 return Status("Symtab already available in the module");
301
302 AdbClient adb(m_device_id);
303 std::string tmpdir;
304 Status error = adb.Shell("mktemp --directory --tmpdir /data/local/tmp",
305 seconds(5), &tmpdir);
306 if (error.Fail() || tmpdir.empty())
307 return Status("Failed to generate temporary directory on the device (%s)",
308 error.AsCString());
309 tmpdir = llvm::StringRef(tmpdir).trim().str();
310
311 // Create file remover for the temporary directory created on the device
312 std::unique_ptr<std::string, std::function<void(std::string *)>>
313 tmpdir_remover(&tmpdir, [&adb](std::string *s) {
314 StreamString command;
315 command.Printf("rm -rf %s", s->c_str());
316 Status error = adb.Shell(command.GetData(), seconds(5), nullptr);
317
318 Log *log = GetLog(LLDBLog::Platform);
319 if (log && error.Fail())
320 LLDB_LOGF(log, "Failed to remove temp directory: %s", error.AsCString());
321 });
322
323 FileSpec symfile_platform_filespec(tmpdir);
324 symfile_platform_filespec.AppendPathComponent("symbolized.oat");
325
326 // Execute oatdump on the remote device to generate a file with symtab
327 StreamString command;
328 command.Printf("oatdump --symbolize=%s --output=%s",
329 module_sp->GetPlatformFileSpec().GetPath(false).c_str(),
330 symfile_platform_filespec.GetPath(false).c_str());
331 error = adb.Shell(command.GetData(), minutes(1), nullptr);
332 if (error.Fail())
333 return Status("Oatdump failed: %s", error.AsCString());
334
335 // Download the symbolfile from the remote device
336 return GetFile(symfile_platform_filespec, dst_file_spec);
337 }
338
GetRemoteOSVersion()339 bool PlatformAndroid::GetRemoteOSVersion() {
340 m_os_version = llvm::VersionTuple(GetSdkVersion());
341 return !m_os_version.empty();
342 }
343
344 llvm::StringRef
GetLibdlFunctionDeclarations(lldb_private::Process * process)345 PlatformAndroid::GetLibdlFunctionDeclarations(lldb_private::Process *process) {
346 SymbolContextList matching_symbols;
347 std::vector<const char *> dl_open_names = { "__dl_dlopen", "dlopen" };
348 const char *dl_open_name = nullptr;
349 Target &target = process->GetTarget();
350 for (auto name: dl_open_names) {
351 target.GetImages().FindFunctionSymbols(
352 ConstString(name), eFunctionNameTypeFull, matching_symbols);
353 if (matching_symbols.GetSize()) {
354 dl_open_name = name;
355 break;
356 }
357 }
358 // Older platform versions have the dl function symbols mangled
359 if (dl_open_name == dl_open_names[0])
360 return R"(
361 extern "C" void* dlopen(const char*, int) asm("__dl_dlopen");
362 extern "C" void* dlsym(void*, const char*) asm("__dl_dlsym");
363 extern "C" int dlclose(void*) asm("__dl_dlclose");
364 extern "C" char* dlerror(void) asm("__dl_dlerror");
365 )";
366
367 return PlatformPOSIX::GetLibdlFunctionDeclarations(process);
368 }
369
GetSyncService(Status & error)370 AdbClient::SyncService *PlatformAndroid::GetSyncService(Status &error) {
371 if (m_adb_sync_svc && m_adb_sync_svc->IsConnected())
372 return m_adb_sync_svc.get();
373
374 AdbClient adb(m_device_id);
375 m_adb_sync_svc = adb.GetSyncService(error);
376 return (error.Success()) ? m_adb_sync_svc.get() : nullptr;
377 }
378