1 //===-- PlatformAndroid.cpp -------------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 // C Includes
11 // C++ Includes
12 // Other libraries and framework includes
13 #include "lldb/Core/Log.h"
14 #include "lldb/Core/Module.h"
15 #include "lldb/Core/PluginManager.h"
16 #include "lldb/Core/Scalar.h"
17 #include "lldb/Core/Section.h"
18 #include "lldb/Core/ValueObject.h"
19 #include "lldb/Host/HostInfo.h"
20 #include "lldb/Host/StringConvert.h"
21 #include "Utility/UriParser.h"
22 
23 // Project includes
24 #include "AdbClient.h"
25 #include "PlatformAndroid.h"
26 #include "PlatformAndroidRemoteGDBServer.h"
27 
28 using namespace lldb;
29 using namespace lldb_private;
30 using namespace lldb_private::platform_android;
31 
32 static uint32_t g_initialize_count = 0;
33 static const unsigned int g_android_default_cache_size = 2048; // Fits inside 4k adb packet.
34 
35 void
36 PlatformAndroid::Initialize ()
37 {
38     PlatformLinux::Initialize ();
39 
40     if (g_initialize_count++ == 0)
41     {
42 #if defined(__ANDROID__)
43         PlatformSP default_platform_sp (new PlatformAndroid(true));
44         default_platform_sp->SetSystemArchitecture(HostInfo::GetArchitecture());
45         Platform::SetHostPlatform (default_platform_sp);
46 #endif
47         PluginManager::RegisterPlugin (PlatformAndroid::GetPluginNameStatic(false),
48                                        PlatformAndroid::GetPluginDescriptionStatic(false),
49                                        PlatformAndroid::CreateInstance);
50     }
51 }
52 
53 void
54 PlatformAndroid::Terminate ()
55 {
56     if (g_initialize_count > 0)
57     {
58         if (--g_initialize_count == 0)
59         {
60             PluginManager::UnregisterPlugin (PlatformAndroid::CreateInstance);
61         }
62     }
63 
64     PlatformLinux::Terminate ();
65 }
66 
67 PlatformSP
68 PlatformAndroid::CreateInstance (bool force, const ArchSpec *arch)
69 {
70     Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM));
71     if (log)
72     {
73         const char *arch_name;
74         if (arch && arch->GetArchitectureName ())
75             arch_name = arch->GetArchitectureName ();
76         else
77             arch_name = "<null>";
78 
79         const char *triple_cstr = arch ? arch->GetTriple ().getTriple ().c_str() : "<null>";
80 
81         log->Printf ("PlatformAndroid::%s(force=%s, arch={%s,%s})", __FUNCTION__, force ? "true" : "false", arch_name, triple_cstr);
82     }
83 
84     bool create = force;
85     if (create == false && arch && arch->IsValid())
86     {
87         const llvm::Triple &triple = arch->GetTriple();
88         switch (triple.getVendor())
89         {
90             case llvm::Triple::PC:
91                 create = true;
92                 break;
93 
94 #if defined(__ANDROID__)
95             // Only accept "unknown" for the vendor if the host is android and
96             // it "unknown" wasn't specified (it was just returned because it
97             // was NOT specified_
98             case llvm::Triple::VendorType::UnknownVendor:
99                 create = !arch->TripleVendorWasSpecified();
100                 break;
101 #endif
102             default:
103                 break;
104         }
105 
106         if (create)
107         {
108             switch (triple.getOS())
109             {
110                 case llvm::Triple::Android:
111                     break;
112 
113 #if defined(__ANDROID__)
114                 // Only accept "unknown" for the OS if the host is android and
115                 // it "unknown" wasn't specified (it was just returned because it
116                 // was NOT specified)
117                 case llvm::Triple::OSType::UnknownOS:
118                     create = !arch->TripleOSWasSpecified();
119                     break;
120 #endif
121                 default:
122                     create = false;
123                     break;
124             }
125         }
126     }
127 
128     if (create)
129     {
130         if (log)
131             log->Printf ("PlatformAndroid::%s() creating remote-android platform", __FUNCTION__);
132         return PlatformSP(new PlatformAndroid(false));
133     }
134 
135     if (log)
136         log->Printf ("PlatformAndroid::%s() aborting creation of remote-android platform", __FUNCTION__);
137 
138     return PlatformSP();
139 }
140 
141 PlatformAndroid::PlatformAndroid (bool is_host) :
142     PlatformLinux(is_host),
143     m_sdk_version(0)
144 {
145 }
146 
147 PlatformAndroid::~PlatformAndroid()
148 {
149 }
150 
151 ConstString
152 PlatformAndroid::GetPluginNameStatic (bool is_host)
153 {
154     if (is_host)
155     {
156         static ConstString g_host_name(Platform::GetHostPlatformName ());
157         return g_host_name;
158     }
159     else
160     {
161         static ConstString g_remote_name("remote-android");
162         return g_remote_name;
163     }
164 }
165 
166 const char *
167 PlatformAndroid::GetPluginDescriptionStatic (bool is_host)
168 {
169     if (is_host)
170         return "Local Android user platform plug-in.";
171     else
172         return "Remote Android user platform plug-in.";
173 }
174 
175 ConstString
176 PlatformAndroid::GetPluginName()
177 {
178     return GetPluginNameStatic(IsHost());
179 }
180 
181 Error
182 PlatformAndroid::ConnectRemote(Args& args)
183 {
184     m_device_id.clear();
185 
186     if (IsHost())
187     {
188         return Error ("can't connect to the host platform '%s', always connected", GetPluginName().GetCString());
189     }
190 
191     if (!m_remote_platform_sp)
192         m_remote_platform_sp = PlatformSP(new PlatformAndroidRemoteGDBServer());
193 
194     int port;
195     std::string scheme, host, path;
196     const char *url = args.GetArgumentAtIndex(0);
197     if (!url)
198         return Error("URL is null.");
199     if (!UriParser::Parse(url, scheme, host, port, path))
200         return Error("Invalid URL: %s", url);
201     if (host != "localhost")
202         m_device_id = host;
203 
204     auto error = PlatformLinux::ConnectRemote(args);
205     if (error.Success())
206     {
207         AdbClient adb;
208         error = AdbClient::CreateByDeviceID(m_device_id, adb);
209         if (error.Fail())
210             return error;
211 
212         m_device_id = adb.GetDeviceID();
213     }
214     return error;
215 }
216 
217 Error
218 PlatformAndroid::GetFile (const FileSpec& source,
219                           const FileSpec& destination)
220 {
221     if (IsHost() || !m_remote_platform_sp)
222         return PlatformLinux::GetFile(source, destination);
223 
224     FileSpec source_spec (source.GetPath (false), false, FileSpec::ePathSyntaxPosix);
225     if (source_spec.IsRelative())
226         source_spec = GetRemoteWorkingDirectory ().CopyByAppendingPathComponent (source_spec.GetCString (false));
227 
228     Error error;
229     auto sync_service = GetSyncService (error);
230     if (error.Fail ())
231         return error;
232 
233     uint32_t mode = 0, size = 0, mtime = 0;
234     error = sync_service->Stat(source_spec, mode, size, mtime);
235     if (error.Fail())
236         return error;
237 
238     if (mode != 0)
239         return sync_service->PullFile(source_spec, destination);
240 
241     auto source_file = source_spec.GetCString(false);
242 
243     Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM));
244     if (log)
245         log->Printf("Got mode == 0 on '%s': try to get file via 'shell cat'", source_file);
246 
247     if (strchr(source_file, '\'') != nullptr)
248         return Error("Doesn't support single-quotes in filenames");
249 
250     // mode == 0 can signify that adbd cannot access the file
251     // due security constraints - try "cat ..." as a fallback.
252     AdbClient adb(m_device_id);
253 
254     char cmd[PATH_MAX];
255     snprintf(cmd, sizeof(cmd), "cat '%s'", source_file);
256 
257     return adb.ShellToFile(cmd, 60000 /* ms */, destination);
258 }
259 
260 Error
261 PlatformAndroid::PutFile (const FileSpec& source,
262                           const FileSpec& destination,
263                           uint32_t uid,
264                           uint32_t gid)
265 {
266     if (IsHost() || !m_remote_platform_sp)
267         return PlatformLinux::PutFile (source, destination, uid, gid);
268 
269     FileSpec destination_spec (destination.GetPath (false), false, FileSpec::ePathSyntaxPosix);
270     if (destination_spec.IsRelative())
271         destination_spec = GetRemoteWorkingDirectory ().CopyByAppendingPathComponent (destination_spec.GetCString (false));
272 
273     // TODO: Set correct uid and gid on remote file.
274     Error error;
275     auto sync_service = GetSyncService (error);
276     if (error.Fail ())
277         return error;
278     return sync_service->PushFile(source, destination_spec);
279 }
280 
281 const char *
282 PlatformAndroid::GetCacheHostname ()
283 {
284     return m_device_id.c_str ();
285 }
286 
287 Error
288 PlatformAndroid::DownloadModuleSlice (const FileSpec &src_file_spec,
289                                       const uint64_t src_offset,
290                                       const uint64_t src_size,
291                                       const FileSpec &dst_file_spec)
292 {
293     if (src_offset != 0)
294         return Error ("Invalid offset - %" PRIu64, src_offset);
295 
296     return GetFile (src_file_spec, dst_file_spec);
297 }
298 
299 Error
300 PlatformAndroid::DisconnectRemote()
301 {
302     Error error = PlatformLinux::DisconnectRemote();
303     if (error.Success())
304     {
305         m_device_id.clear();
306         m_sdk_version = 0;
307     }
308     return error;
309 }
310 
311 uint32_t
312 PlatformAndroid::GetDefaultMemoryCacheLineSize()
313 {
314     return g_android_default_cache_size;
315 }
316 
317 uint32_t
318 PlatformAndroid::GetSdkVersion()
319 {
320     if (!IsConnected())
321         return 0;
322 
323     if (m_sdk_version != 0)
324         return m_sdk_version;
325 
326     std::string version_string;
327     AdbClient adb(m_device_id);
328     Error error = adb.Shell("getprop ro.build.version.sdk", 5000 /* ms */, &version_string);
329     version_string = llvm::StringRef(version_string).trim().str();
330 
331     if (error.Fail() || version_string.empty())
332     {
333         Log* log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM);
334         if (log)
335             log->Printf("Get SDK version failed. (error: %s, output: %s)",
336                         error.AsCString(), version_string.c_str());
337         return 0;
338     }
339 
340     m_sdk_version = StringConvert::ToUInt32(version_string.c_str());
341     return m_sdk_version;
342 }
343 
344 Error
345 PlatformAndroid::DownloadSymbolFile (const lldb::ModuleSP& module_sp,
346                                      const FileSpec& dst_file_spec)
347 {
348     // For oat file we can try to fetch additional debug info from the device
349     ConstString extension = module_sp->GetFileSpec().GetFileNameExtension();
350     if (extension != ConstString("oat") && extension != ConstString("odex"))
351         return Error("Symbol file downloading only supported for oat and odex files");
352 
353     // If we have no information about the platform file we can't execute oatdump
354     if (!module_sp->GetPlatformFileSpec())
355         return Error("No platform file specified");
356 
357     // Symbolizer isn't available before SDK version 23
358     if (GetSdkVersion() < 23)
359         return Error("Symbol file generation only supported on SDK 23+");
360 
361     // If we already have symtab then we don't have to try and generate one
362     if (module_sp->GetSectionList()->FindSectionByName(ConstString(".symtab")) != nullptr)
363         return Error("Symtab already available in the module");
364 
365     AdbClient adb(m_device_id);
366     std::string tmpdir;
367     Error error = adb.Shell("mktemp --directory --tmpdir /data/local/tmp", 5000 /* ms */, &tmpdir);
368     if (error.Fail() || tmpdir.empty())
369         return Error("Failed to generate temporary directory on the device (%s)", error.AsCString());
370     tmpdir = llvm::StringRef(tmpdir).trim().str();
371 
372     // Create file remover for the temporary directory created on the device
373     std::unique_ptr<std::string, std::function<void(std::string*)>> tmpdir_remover(
374         &tmpdir,
375         [this, &adb](std::string* s) {
376             StreamString command;
377             command.Printf("rm -rf %s", s->c_str());
378             Error error = adb.Shell(command.GetData(), 5000 /* ms */, nullptr);
379 
380             Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM));
381             if (error.Fail())
382                 log->Printf("Failed to remove temp directory: %s", error.AsCString());
383         }
384     );
385 
386     FileSpec symfile_platform_filespec(tmpdir.c_str(), false);
387     symfile_platform_filespec.AppendPathComponent("symbolized.oat");
388 
389     // Execute oatdump on the remote device to generate a file with symtab
390     StreamString command;
391     command.Printf("oatdump --symbolize=%s --output=%s",
392                    module_sp->GetPlatformFileSpec().GetCString(false),
393                    symfile_platform_filespec.GetCString(false));
394     error = adb.Shell(command.GetData(), 60000 /* ms */, nullptr);
395     if (error.Fail())
396         return Error("Oatdump failed: %s", error.AsCString());
397 
398     // Download the symbolfile from the remote device
399     return GetFile(symfile_platform_filespec, dst_file_spec);
400 }
401 
402 bool
403 PlatformAndroid::GetRemoteOSVersion ()
404 {
405     m_major_os_version = GetSdkVersion();
406     m_minor_os_version = 0;
407     m_update_os_version = 0;
408     return m_major_os_version != 0;
409 }
410 
411 const char*
412 PlatformAndroid::GetLibdlFunctionDeclarations() const
413 {
414     return R"(
415               extern "C" void* dlopen(const char*, int) asm("__dl_dlopen");
416               extern "C" void* dlsym(void*, const char*) asm("__dl_dlsym");
417               extern "C" int   dlclose(void*) asm("__dl_dlclose");
418               extern "C" char* dlerror(void) asm("__dl_dlerror");
419              )";
420 }
421 
422 AdbClient::SyncService*
423 PlatformAndroid::GetSyncService (Error &error)
424 {
425     if (m_adb_sync_svc && m_adb_sync_svc->IsConnected ())
426         return m_adb_sync_svc.get ();
427 
428     AdbClient adb (m_device_id);
429     m_adb_sync_svc = adb.GetSyncService (error);
430     return (error.Success ()) ? m_adb_sync_svc.get () : nullptr;
431 }
432 
433