1 | //===-- NativeProcessAIX.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 "NativeProcessAIX.h" |
10 | #include "Plugins/Process/POSIX/ProcessPOSIXLog.h" |
11 | #include "lldb/Host/Host.h" |
12 | #include "lldb/Host/HostInfo.h" |
13 | #include "lldb/Host/HostProcess.h" |
14 | #include "lldb/Host/ProcessLaunchInfo.h" |
15 | #include "lldb/Host/posix/ProcessLauncherPosixFork.h" |
16 | #include "lldb/Symbol/ObjectFile.h" |
17 | #include "lldb/Utility/Log.h" |
18 | #include "lldb/Utility/State.h" |
19 | #include "lldb/Utility/Status.h" |
20 | #include "llvm/Support/Errno.h" |
21 | #include "llvm/Support/Error.h" |
22 | #include <cerrno> |
23 | #include <cstdint> |
24 | #include <cstring> |
25 | #include <sstream> |
26 | #include <string> |
27 | #include <sys/ptrace.h> |
28 | #include <unistd.h> |
29 | |
30 | using namespace lldb; |
31 | using namespace lldb_private; |
32 | using namespace lldb_private::process_aix; |
33 | using namespace llvm; |
34 | |
35 | static constexpr unsigned k_ptrace_word_size = sizeof(void *); |
36 | static_assert(sizeof(long) >= k_ptrace_word_size, |
37 | "Size of long must be larger than ptrace word size" ); |
38 | |
39 | // Simple helper function to ensure flags are enabled on the given file |
40 | // descriptor. |
41 | static llvm::Error SetFDFlags(int fd, int flags) { |
42 | int status = fcntl(fd: fd, F_GETFL); |
43 | if (status == -1) |
44 | return errorCodeToError(EC: errnoAsErrorCode()); |
45 | if (fcntl(fd: fd, F_SETFL, status | flags) == -1) |
46 | return errorCodeToError(EC: errnoAsErrorCode()); |
47 | return Error::success(); |
48 | } |
49 | |
50 | NativeProcessAIX::Manager::Manager(MainLoop &mainloop) |
51 | : NativeProcessProtocol::Manager(mainloop) { |
52 | Status status; |
53 | m_sigchld_handle = mainloop.RegisterSignal( |
54 | SIGCHLD, callback: [this](MainLoopBase &) { SigchldHandler(); }, error&: status); |
55 | assert(m_sigchld_handle && status.Success()); |
56 | } |
57 | |
58 | // Public Static Methods |
59 | |
60 | llvm::Expected<std::unique_ptr<NativeProcessProtocol>> |
61 | NativeProcessAIX::Manager::Launch(ProcessLaunchInfo &launch_info, |
62 | NativeDelegate &native_delegate) { |
63 | Log *log = GetLog(mask: POSIXLog::Process); |
64 | |
65 | Status status; |
66 | ::pid_t pid = ProcessLauncherPosixFork() |
67 | .LaunchProcess(launch_info, error&: status) |
68 | .GetProcessId(); |
69 | LLDB_LOG(log, "pid = {0:x}" , pid); |
70 | if (status.Fail()) { |
71 | LLDB_LOG(log, "failed to launch process: {0}" , status); |
72 | return status.ToError(); |
73 | } |
74 | |
75 | // Wait for the child process to trap on its call to execve. |
76 | int wstatus = 0; |
77 | ::pid_t wpid = llvm::sys::RetryAfterSignal(-1, ::waitpid, pid, &wstatus, 0); |
78 | assert(wpid == pid); |
79 | UNUSED_IF_ASSERT_DISABLED(wpid); |
80 | if (!WIFSTOPPED(wstatus)) { |
81 | LLDB_LOG(log, "Could not sync with inferior process: wstatus={1}" , |
82 | WaitStatus::Decode(wstatus)); |
83 | return llvm::make_error<StringError>(Args: "Could not sync with inferior process" , |
84 | Args: llvm::inconvertibleErrorCode()); |
85 | } |
86 | LLDB_LOG(log, "inferior started, now in stopped state" ); |
87 | |
88 | return std::unique_ptr<NativeProcessAIX>(new NativeProcessAIX( |
89 | pid, launch_info.GetPTY().ReleasePrimaryFileDescriptor(), native_delegate, |
90 | HostInfo::GetArchitecture(arch_kind: HostInfo::eArchKind64), *this, {pid})); |
91 | } |
92 | |
93 | llvm::Expected<std::unique_ptr<NativeProcessProtocol>> |
94 | NativeProcessAIX::Manager::Attach( |
95 | lldb::pid_t pid, NativeProcessProtocol::NativeDelegate &native_delegate) { |
96 | Log *log = GetLog(mask: POSIXLog::Process); |
97 | LLDB_LOG(log, "pid = {0:x}" , pid); |
98 | |
99 | auto tids_or = NativeProcessAIX::Attach(pid); |
100 | if (!tids_or) |
101 | return tids_or.takeError(); |
102 | |
103 | return std::unique_ptr<NativeProcessAIX>(new NativeProcessAIX( |
104 | pid, -1, native_delegate, |
105 | HostInfo::GetArchitecture(arch_kind: HostInfo::eArchKind64), *this, *tids_or)); |
106 | } |
107 | |
108 | lldb::addr_t NativeProcessAIX::GetSharedLibraryInfoAddress() { |
109 | return LLDB_INVALID_ADDRESS; |
110 | } |
111 | |
112 | static std::optional<std::pair<lldb::pid_t, WaitStatus>> WaitPid() { |
113 | Log *log = GetLog(mask: POSIXLog::Process); |
114 | |
115 | int status; |
116 | ::pid_t wait_pid = |
117 | llvm::sys::RetryAfterSignal(-1, ::waitpid, -1, &status, WNOHANG); |
118 | |
119 | if (wait_pid == 0) |
120 | return std::nullopt; |
121 | |
122 | if (wait_pid == -1) { |
123 | Status error(errno, eErrorTypePOSIX); |
124 | LLDB_LOG(log, "waitpid(-1, &status, _) failed: {0}" , error); |
125 | return std::nullopt; |
126 | } |
127 | |
128 | WaitStatus wait_status = WaitStatus::Decode(wstatus: status); |
129 | |
130 | LLDB_LOG(log, "waitpid(-1, &status, _) = {0}, status = {1}" , wait_pid, |
131 | wait_status); |
132 | return std::make_pair(x&: wait_pid, y&: wait_status); |
133 | } |
134 | |
135 | void NativeProcessAIX::Manager::SigchldHandler() { |
136 | while (true) { |
137 | auto wait_result = WaitPid(); |
138 | if (!wait_result) |
139 | return; |
140 | } |
141 | } |
142 | |
143 | void NativeProcessAIX::Manager::CollectThread(::pid_t tid) {} |
144 | |
145 | // Public Instance Methods |
146 | |
147 | NativeProcessAIX::NativeProcessAIX(::pid_t pid, int terminal_fd, |
148 | NativeDelegate &delegate, |
149 | const ArchSpec &arch, Manager &manager, |
150 | llvm::ArrayRef<::pid_t> tids) |
151 | : NativeProcessProtocol(pid, terminal_fd, delegate), m_manager(manager), |
152 | m_arch(arch) { |
153 | manager.AddProcess(process&: *this); |
154 | if (m_terminal_fd != -1) |
155 | cantFail(Err: SetFDFlags(fd: m_terminal_fd, O_NONBLOCK)); |
156 | |
157 | // Let our process instance know the thread has stopped. |
158 | SetCurrentThreadID(tids[0]); |
159 | SetState(state: StateType::eStateStopped, notify_delegates: false); |
160 | } |
161 | |
162 | llvm::Expected<std::vector<::pid_t>> NativeProcessAIX::Attach(::pid_t pid) { |
163 | Log *log = GetLog(mask: POSIXLog::Process); |
164 | Status status; |
165 | if (llvm::Error err = PtraceWrapper(PT_ATTACH, pid).takeError()) |
166 | return err; |
167 | |
168 | int wpid = llvm::sys::RetryAfterSignal(-1, ::waitpid, pid, nullptr, WNOHANG); |
169 | if (wpid <= 0) |
170 | return llvm::errorCodeToError(EC: errnoAsErrorCode()); |
171 | LLDB_LOG(log, "adding pid = {0}" , pid); |
172 | |
173 | return std::vector<::pid_t>{pid}; |
174 | } |
175 | |
176 | bool NativeProcessAIX::SupportHardwareSingleStepping() const { return false; } |
177 | |
178 | Status NativeProcessAIX::Resume(const ResumeActionList &resume_actions) { |
179 | return Status("unsupported" ); |
180 | } |
181 | |
182 | Status NativeProcessAIX::Halt() { return Status("unsupported" ); } |
183 | |
184 | Status NativeProcessAIX::Detach() { return Status("unsupported" ); } |
185 | |
186 | Status NativeProcessAIX::Signal(int signo) { return Status("unsupported" ); } |
187 | |
188 | Status NativeProcessAIX::Interrupt() { return Status("unsupported" ); } |
189 | |
190 | Status NativeProcessAIX::Kill() { return Status("unsupported" ); } |
191 | |
192 | Status NativeProcessAIX::ReadMemory(lldb::addr_t addr, void *buf, size_t size, |
193 | size_t &bytes_read) { |
194 | return Status("unsupported" ); |
195 | } |
196 | |
197 | Status NativeProcessAIX::WriteMemory(lldb::addr_t addr, const void *buf, |
198 | size_t size, size_t &bytes_written) { |
199 | return Status("unsupported" ); |
200 | } |
201 | |
202 | size_t NativeProcessAIX::UpdateThreads() { |
203 | // The NativeProcessAIX monitoring threads are always up to date with |
204 | // respect to thread state and they keep the thread list populated properly. |
205 | // All this method needs to do is return the thread count. |
206 | return m_threads.size(); |
207 | } |
208 | |
209 | Status NativeProcessAIX::GetLoadedModuleFileSpec(const char *module_path, |
210 | FileSpec &file_spec) { |
211 | return Status("unsupported" ); |
212 | } |
213 | |
214 | Status NativeProcessAIX::SetBreakpoint(lldb::addr_t addr, uint32_t size, |
215 | bool hardware) { |
216 | if (hardware) |
217 | return SetHardwareBreakpoint(addr, size); |
218 | return SetSoftwareBreakpoint(addr, size_hint: size); |
219 | } |
220 | |
221 | Status NativeProcessAIX::RemoveBreakpoint(lldb::addr_t addr, bool hardware) { |
222 | if (hardware) |
223 | return RemoveHardwareBreakpoint(addr); |
224 | return NativeProcessProtocol::RemoveBreakpoint(addr); |
225 | } |
226 | |
227 | llvm::Error NativeProcessAIX::Detach(lldb::tid_t tid) { |
228 | return PtraceWrapper(PT_DETACH, pid: tid).takeError(); |
229 | } |
230 | |
231 | llvm::Expected<int> NativeProcessAIX::PtraceWrapper(int req, lldb::pid_t pid, |
232 | void *addr, void *data, |
233 | size_t data_size) { |
234 | int ret; |
235 | |
236 | Log *log = GetLog(mask: POSIXLog::Ptrace); |
237 | switch (req) { |
238 | case PT_ATTACH: |
239 | case PT_DETACH: |
240 | ret = ptrace64(req, pid, 0, 0, nullptr); |
241 | break; |
242 | default: |
243 | llvm_unreachable("PT_ request not supported yet." ); |
244 | } |
245 | |
246 | LLDB_LOG(log, "ptrace({0}, {1}, {2}, {3}, {4})={5:x}" , req, pid, addr, data, |
247 | data_size, ret); |
248 | |
249 | if (ret == -1) { |
250 | LLDB_LOG(log, "ptrace() failed" ); |
251 | return llvm::errorCodeToError(EC: errnoAsErrorCode()); |
252 | } |
253 | return ret; |
254 | } |
255 | |