1 : /*
2 : * Copyright (c) 2011 The Native Client Authors. All rights reserved.
3 : * Use of this source code is governed by a BSD-style license that can be
4 : * found in the LICENSE file.
5 : */
6 :
7 :
8 : #include <vector>
9 : #include <map>
10 :
11 : /*
12 : * NaCl Functions for intereacting with debuggers
13 : */
14 :
15 : #include "native_client/src/trusted/gdb_rsp/session.h"
16 : #include "native_client/src/trusted/gdb_rsp/target.h"
17 : #include "native_client/src/trusted/port/platform.h"
18 : #include "native_client/src/trusted/port/thread.h"
19 :
20 : #include "native_client/src/include/nacl_string.h"
21 : #include "native_client/src/shared/platform/nacl_check.h"
22 : #include "native_client/src/shared/platform/nacl_log.h"
23 : #include "native_client/src/shared/platform/nacl_exit.h"
24 : #include "native_client/src/shared/platform/nacl_threads.h"
25 : #include "native_client/src/trusted/debug_stub/debug_stub.h"
26 : #include "native_client/src/trusted/debug_stub/nacl_debug.h"
27 : #include "native_client/src/trusted/service_runtime/nacl_app_thread.h"
28 : #include "native_client/src/trusted/service_runtime/nacl_debug_init.h"
29 : #include "native_client/src/trusted/service_runtime/sel_ldr.h"
30 :
31 : /* To enable debugging */
32 : // #define NACL_DEBUG_STUB 1
33 :
34 : using port::IPlatform;
35 : using port::IThread;
36 : using port::ITransport;
37 :
38 : using gdb_rsp::Session;
39 : using gdb_rsp::Target;
40 :
41 : #ifdef WIN32
42 : /* Disable warning for unwind disabled when exceptions used */
43 : #pragma warning(disable:4530)
44 : #endif
45 :
46 : /*
47 : * These macro wraps all debugging stub calls to prevent C++ code called
48 : * by the debugging stub to throw and exception past the C API. We use
49 : * this technique to allow the use of STL templates. We catch bad_alloc
50 : * separately purely to provide information for debugging purposes.
51 : */
52 : #define DBG_CATCH_ALL \
53 : catch(std::bad_alloc) { \
54 : NaClLog(LOG_FATAL, "nacl_debug(%d) : Failed to allocate.\n", __LINE__); \
55 : NaClExit(-1); \
56 : } \
57 : catch(std::exception e) { \
58 : NaClLog(LOG_FATAL, "nacl_debug(%d) : Caught exception: %s.\n", \
59 : __LINE__ , e.what()); \
60 : NaClExit(-1); \
61 : } \
62 : catch(...) { \
63 : NaClLog(LOG_FATAL, "nacl_debug(%d) : Unexpected exception.\n", __LINE__);\
64 : NaClExit(-1); \
65 : }
66 :
67 :
68 : enum NaClDebugStatus {
69 : NDS_DISABLED = 0,
70 : NDS_ENABLED = 1,
71 : NDS_STOPPED = 2
72 : };
73 :
74 : struct NaClDebugState {
75 0 : NaClDebugState() : target_(NULL), app_(NULL),
76 0 : errCode_(0), status_(NDS_DISABLED) {}
77 :
78 0 : bool Init() {
79 0 : NaClDebugStubInit();
80 0 : target_ = new Target();
81 :
82 0 : CHECK((NULL != target_) && (target_->Init()));
83 :
84 0 : status_ = NDS_ENABLED;
85 0 : return true;
86 : }
87 :
88 : ~NaClDebugState() {
89 : /*
90 : * TODO(mlinck): It is not safe to call this destructor, since there is an
91 : * unjoinable thread potentially accessing target_.
92 : */
93 : delete target_;
94 : }
95 :
96 : Target* target_;
97 : struct NaClApp *app_;
98 : volatile int errCode_;
99 : NaClDebugStatus status_;
100 : std::vector<const char *> arg_;
101 : std::vector<const char *> env_;
102 : };
103 :
104 : static struct NaClDebugCallbacks debug_callbacks = {
105 : NaClDebugThreadPrepDebugging,
106 : NaClDebugThreadStopDebugging,
107 : NaClDebugStop,
108 : };
109 :
110 : static NaClDebugState *g_nacl_debug_state = NULL;
111 :
112 0 : bool NaClDebugIsEnabled(void) throw() {
113 0 : if (NULL != g_nacl_debug_state &&
114 : NDS_ENABLED == g_nacl_debug_state->status_) {
115 0 : return true;
116 : }
117 0 : return false;
118 : }
119 :
120 0 : void WINAPI NaClStubThread(void *ptr) {
121 0 : Target *targ = reinterpret_cast<Target*>(ptr);
122 0 : while (1) {
123 0 : ITransport* trans = NULL;
124 0 : Session* ses = NULL;
125 :
126 : try {
127 : // Wait for a connection.
128 0 : trans = ITransport::Accept("127.0.0.1:4014");
129 0 : if (NULL == trans) continue;
130 :
131 : // Create a new session for this connection
132 0 : ses = new Session();
133 0 : ses->Init(trans);
134 0 : ses->SetFlags(Session::DEBUG_MASK);
135 :
136 : // Run this session for as long as it lasts
137 0 : targ->Run(ses);
138 : }
139 0 : catch(...) {
140 0 : delete ses;
141 0 : ITransport::Free(trans);
142 : }
143 : }
144 : }
145 :
146 0 : void NaClExceptionCatcher(uint32_t id, int8_t sig, void *cookie) {
147 0 : Target* targ = static_cast<Target*>(cookie);
148 :
149 : /* Signal the target that we caught something */
150 0 : IPlatform::LogWarning("Caught signal %d on thread %Xh.\n", sig, id);
151 0 : targ->Signal(id, sig, true);
152 0 : }
153 :
154 :
155 0 : void NaClDebugSetAppInfo(struct NaClApp *app) throw() {
156 0 : if (NaClDebugIsEnabled()) {
157 0 : g_nacl_debug_state->app_ = app;
158 : }
159 0 : }
160 :
161 :
162 : void NaClDebugSetAppEnvironment(int argc, char const * const argv[],
163 0 : int envc, char const * const envv[]) throw() {
164 0 : if (NaClDebugIsEnabled()) {
165 : int a;
166 : try {
167 : /*
168 : * Copy the pointer arrays. We use ptrs instead of strings
169 : * since the data persits and it prevents an extra copy.
170 : */
171 0 : g_nacl_debug_state->arg_.resize(argc);
172 0 : for (a = 0; a < argc; a++) g_nacl_debug_state->arg_[a] = argv[a];
173 0 : g_nacl_debug_state->env_.resize(envc);
174 0 : for (a = 0; a < envc; a++) g_nacl_debug_state->env_[a] = envv[a];
175 0 : } DBG_CATCH_ALL
176 : }
177 0 : }
178 :
179 0 : void NaClDebugThreadPrepDebugging(struct NaClAppThread *natp) throw() {
180 : UNREFERENCED_PARAMETER(natp);
181 :
182 0 : if (NaClDebugIsEnabled()) {
183 0 : uint32_t id = IPlatform::GetCurrentThread();
184 0 : IThread* thread = IThread::Acquire(id, true);
185 0 : g_nacl_debug_state->target_->SetMemoryBase(natp->nap->mem_start);
186 0 : g_nacl_debug_state->target_->TrackThread(thread);
187 :
188 : /*
189 : * TODO(noelallen) We need to associate the natp with this thread
190 : * so we can get to the untrusted context preserved on a syscall.
191 : */
192 : }
193 0 : }
194 :
195 0 : void NaClDebugThreadStopDebugging(struct NaClAppThread *natp) throw() {
196 : UNREFERENCED_PARAMETER(natp);
197 :
198 0 : if (NaClDebugIsEnabled()) {
199 0 : uint32_t id = IPlatform::GetCurrentThread();
200 0 : IThread* thread = IThread::Acquire(id, false);
201 0 : g_nacl_debug_state->target_->IgnoreThread(thread);
202 0 : IThread::Release(thread);
203 :
204 : /*
205 : * TODO(noelallen) We need to associate the natp with this thread
206 : * so we can get to the thread once we support freeing a thread
207 : * from a different thread than the executing one.
208 : */
209 : }
210 0 : }
211 :
212 0 : int NaClDebugStart(void) throw() {
213 0 : if (NaClDebugIsEnabled()) {
214 0 : NaClThread *thread = new NaClThread;
215 :
216 0 : if (NULL == thread) return false;
217 :
218 : /* Add a temp breakpoint. */
219 0 : struct NaClApp* app = g_nacl_debug_state->app_;
220 0 : if (0 != app->user_entry_pt) {
221 : g_nacl_debug_state->target_->AddTemporaryBreakpoint(app->user_entry_pt +
222 0 : app->mem_start);
223 : }
224 : g_nacl_debug_state->target_->AddTemporaryBreakpoint(app->initial_entry_pt +
225 0 : app->mem_start);
226 :
227 0 : NaClLog(LOG_WARNING, "nacl_debug(%d) : Debugging started.\n", __LINE__);
228 : IThread::SetExceptionCatch(NaClExceptionCatcher,
229 0 : g_nacl_debug_state->target_);
230 : return NaClThreadCtor(thread, NaClStubThread, g_nacl_debug_state->target_,
231 0 : NACL_KERN_STACK_SIZE);
232 : }
233 0 : return 0;
234 : }
235 :
236 0 : void NaClDebugStop(int ErrCode) throw() {
237 : /*
238 : * We check if debugging is enabled since this check is the only
239 : * mechanism for allocating the state object. We free the
240 : * resources but not the object itself. Instead we mark it as
241 : * STOPPED to prevent it from getting recreated.
242 : */
243 0 : if (NaClDebugIsEnabled()) {
244 0 : g_nacl_debug_state->status_ = NDS_STOPPED;
245 0 : g_nacl_debug_state->errCode_ = ErrCode;
246 : try {
247 0 : NaClDebugStubFini();
248 0 : } DBG_CATCH_ALL
249 : }
250 0 : }
251 :
252 : /*
253 : * This function is implemented for the service runtime. The service runtime
254 : * declares the function so it does not need to be declared in our header.
255 : */
256 : int NaClDebugInit(struct NaClApp *nap,
257 : int argc, char const *const argv[],
258 0 : int envc, char const *const envv[]) {
259 : static bool initialised = 0;
260 0 : CHECK(!initialised && NULL == g_nacl_debug_state);
261 0 : initialised = 1;
262 0 : nap->debug_stub_callbacks = &debug_callbacks;
263 0 : NaClDebugStubInit();
264 0 : g_nacl_debug_state = new NaClDebugState();
265 0 : CHECK(g_nacl_debug_state->Init());
266 0 : NaClDebugSetAppInfo(nap);
267 0 : NaClDebugSetAppEnvironment(argc, argv, envc, envv);
268 0 : NaClDebugStart();
269 0 : return 1;
270 : }
|