1 //===-- PlatformAndroidRemoteGDBServer.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/Host/ConnectionFileDescriptor.h"
10 #include "lldb/Host/common/TCPSocket.h"
11 #include "lldb/Utility/LLDBLog.h"
12 #include "lldb/Utility/Log.h"
13 #include "lldb/Utility/Status.h"
14 #include "lldb/Utility/UriParser.h"
15 
16 #include "PlatformAndroidRemoteGDBServer.h"
17 
18 #include <sstream>
19 
20 using namespace lldb;
21 using namespace lldb_private;
22 using namespace platform_android;
23 
24 static const lldb::pid_t g_remote_platform_pid =
25     0; // Alias for the process id of lldb-platform
26 
ForwardPortWithAdb(const uint16_t local_port,const uint16_t remote_port,llvm::StringRef remote_socket_name,const llvm::Optional<AdbClient::UnixSocketNamespace> & socket_namespace,std::string & device_id)27 static Status ForwardPortWithAdb(
28     const uint16_t local_port, const uint16_t remote_port,
29     llvm::StringRef remote_socket_name,
30     const llvm::Optional<AdbClient::UnixSocketNamespace> &socket_namespace,
31     std::string &device_id) {
32   Log *log = GetLog(LLDBLog::Platform);
33 
34   AdbClient adb;
35   auto error = AdbClient::CreateByDeviceID(device_id, adb);
36   if (error.Fail())
37     return error;
38 
39   device_id = adb.GetDeviceID();
40   LLDB_LOGF(log, "Connected to Android device \"%s\"", device_id.c_str());
41 
42   if (remote_port != 0) {
43     LLDB_LOGF(log, "Forwarding remote TCP port %d to local TCP port %d",
44               remote_port, local_port);
45     return adb.SetPortForwarding(local_port, remote_port);
46   }
47 
48   LLDB_LOGF(log, "Forwarding remote socket \"%s\" to local TCP port %d",
49             remote_socket_name.str().c_str(), local_port);
50 
51   if (!socket_namespace)
52     return Status("Invalid socket namespace");
53 
54   return adb.SetPortForwarding(local_port, remote_socket_name,
55                                *socket_namespace);
56 }
57 
DeleteForwardPortWithAdb(uint16_t local_port,const std::string & device_id)58 static Status DeleteForwardPortWithAdb(uint16_t local_port,
59                                        const std::string &device_id) {
60   AdbClient adb(device_id);
61   return adb.DeletePortForwarding(local_port);
62 }
63 
FindUnusedPort(uint16_t & port)64 static Status FindUnusedPort(uint16_t &port) {
65   Status error;
66   std::unique_ptr<TCPSocket> tcp_socket(new TCPSocket(true, false));
67   if (error.Fail())
68     return error;
69 
70   error = tcp_socket->Listen("127.0.0.1:0", 1);
71   if (error.Success())
72     port = tcp_socket->GetLocalPortNumber();
73 
74   return error;
75 }
76 
~PlatformAndroidRemoteGDBServer()77 PlatformAndroidRemoteGDBServer::~PlatformAndroidRemoteGDBServer() {
78   for (const auto &it : m_port_forwards)
79     DeleteForwardPortWithAdb(it.second, m_device_id);
80 }
81 
LaunchGDBServer(lldb::pid_t & pid,std::string & connect_url)82 bool PlatformAndroidRemoteGDBServer::LaunchGDBServer(lldb::pid_t &pid,
83                                                      std::string &connect_url) {
84   assert(IsConnected());
85   uint16_t remote_port = 0;
86   std::string socket_name;
87   if (!m_gdb_client_up->LaunchGDBServer("127.0.0.1", pid, remote_port,
88                                         socket_name))
89     return false;
90 
91   Log *log = GetLog(LLDBLog::Platform);
92 
93   auto error =
94       MakeConnectURL(pid, remote_port, socket_name.c_str(), connect_url);
95   if (error.Success() && log)
96     LLDB_LOGF(log, "gdbserver connect URL: %s", connect_url.c_str());
97 
98   return error.Success();
99 }
100 
KillSpawnedProcess(lldb::pid_t pid)101 bool PlatformAndroidRemoteGDBServer::KillSpawnedProcess(lldb::pid_t pid) {
102   assert(IsConnected());
103   DeleteForwardPort(pid);
104   return m_gdb_client_up->KillSpawnedProcess(pid);
105 }
106 
ConnectRemote(Args & args)107 Status PlatformAndroidRemoteGDBServer::ConnectRemote(Args &args) {
108   m_device_id.clear();
109 
110   if (args.GetArgumentCount() != 1)
111     return Status(
112         "\"platform connect\" takes a single argument: <connect-url>");
113 
114   const char *url = args.GetArgumentAtIndex(0);
115   if (!url)
116     return Status("URL is null.");
117   llvm::Optional<URI> parsed_url = URI::Parse(url);
118   if (!parsed_url)
119     return Status("Invalid URL: %s", url);
120   if (parsed_url->hostname != "localhost")
121     m_device_id = parsed_url->hostname.str();
122 
123   m_socket_namespace.reset();
124   if (parsed_url->scheme == "unix-connect")
125     m_socket_namespace = AdbClient::UnixSocketNamespaceFileSystem;
126   else if (parsed_url->scheme == "unix-abstract-connect")
127     m_socket_namespace = AdbClient::UnixSocketNamespaceAbstract;
128 
129   std::string connect_url;
130   auto error =
131       MakeConnectURL(g_remote_platform_pid, parsed_url->port.value_or(0),
132                      parsed_url->path, connect_url);
133 
134   if (error.Fail())
135     return error;
136 
137   args.ReplaceArgumentAtIndex(0, connect_url);
138 
139   Log *log = GetLog(LLDBLog::Platform);
140   LLDB_LOGF(log, "Rewritten platform connect URL: %s", connect_url.c_str());
141 
142   error = PlatformRemoteGDBServer::ConnectRemote(args);
143   if (error.Fail())
144     DeleteForwardPort(g_remote_platform_pid);
145 
146   return error;
147 }
148 
DisconnectRemote()149 Status PlatformAndroidRemoteGDBServer::DisconnectRemote() {
150   DeleteForwardPort(g_remote_platform_pid);
151   return PlatformRemoteGDBServer::DisconnectRemote();
152 }
153 
DeleteForwardPort(lldb::pid_t pid)154 void PlatformAndroidRemoteGDBServer::DeleteForwardPort(lldb::pid_t pid) {
155   Log *log = GetLog(LLDBLog::Platform);
156 
157   auto it = m_port_forwards.find(pid);
158   if (it == m_port_forwards.end())
159     return;
160 
161   const auto port = it->second;
162   const auto error = DeleteForwardPortWithAdb(port, m_device_id);
163   if (error.Fail()) {
164     LLDB_LOGF(log,
165               "Failed to delete port forwarding (pid=%" PRIu64
166               ", port=%d, device=%s): %s",
167               pid, port, m_device_id.c_str(), error.AsCString());
168   }
169   m_port_forwards.erase(it);
170 }
171 
MakeConnectURL(const lldb::pid_t pid,const uint16_t remote_port,llvm::StringRef remote_socket_name,std::string & connect_url)172 Status PlatformAndroidRemoteGDBServer::MakeConnectURL(
173     const lldb::pid_t pid, const uint16_t remote_port,
174     llvm::StringRef remote_socket_name, std::string &connect_url) {
175   static const int kAttempsNum = 5;
176 
177   Status error;
178   // There is a race possibility that somebody will occupy a port while we're
179   // in between FindUnusedPort and ForwardPortWithAdb - adding the loop to
180   // mitigate such problem.
181   for (auto i = 0; i < kAttempsNum; ++i) {
182     uint16_t local_port = 0;
183     error = FindUnusedPort(local_port);
184     if (error.Fail())
185       return error;
186 
187     error = ForwardPortWithAdb(local_port, remote_port, remote_socket_name,
188                                m_socket_namespace, m_device_id);
189     if (error.Success()) {
190       m_port_forwards[pid] = local_port;
191       std::ostringstream url_str;
192       url_str << "connect://127.0.0.1:" << local_port;
193       connect_url = url_str.str();
194       break;
195     }
196   }
197 
198   return error;
199 }
200 
ConnectProcess(llvm::StringRef connect_url,llvm::StringRef plugin_name,lldb_private::Debugger & debugger,lldb_private::Target * target,lldb_private::Status & error)201 lldb::ProcessSP PlatformAndroidRemoteGDBServer::ConnectProcess(
202     llvm::StringRef connect_url, llvm::StringRef plugin_name,
203     lldb_private::Debugger &debugger, lldb_private::Target *target,
204     lldb_private::Status &error) {
205   // We don't have the pid of the remote gdbserver when it isn't started by us
206   // but we still want to store the list of port forwards we set up in our port
207   // forward map. Generate a fake pid for these cases what won't collide with
208   // any other valid pid on android.
209   static lldb::pid_t s_remote_gdbserver_fake_pid = 0xffffffffffffffffULL;
210 
211   llvm::Optional<URI> parsed_url = URI::Parse(connect_url);
212   if (!parsed_url) {
213     error.SetErrorStringWithFormat("Invalid URL: %s",
214                                    connect_url.str().c_str());
215     return nullptr;
216   }
217 
218   std::string new_connect_url;
219   error = MakeConnectURL(s_remote_gdbserver_fake_pid--,
220                          parsed_url->port.value_or(0), parsed_url->path,
221                          new_connect_url);
222   if (error.Fail())
223     return nullptr;
224 
225   return PlatformRemoteGDBServer::ConnectProcess(new_connect_url, plugin_name,
226                                                  debugger, target, error);
227 }
228