1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/nacl/renderer/plugin/plugin.h"
12 #include "components/nacl/renderer/plugin/nacl_subprocess.h"
13 #include "components/nacl/renderer/plugin/plugin_error.h"
14 #include "components/nacl/renderer/plugin/service_runtime.h"
15 #include "components/nacl/renderer/plugin/utility.h"
16 #include "components/nacl/renderer/ppb_nacl_private.h"
17 #include "native_client/src/include/nacl_base.h"
18 #include "native_client/src/include/nacl_macros.h"
19 #include "native_client/src/include/nacl_scoped_ptr.h"
20 #include "native_client/src/include/portability.h"
21 #include "native_client/src/include/portability_io.h"
22 #include "native_client/src/shared/platform/nacl_check.h"
23 #include "native_client/src/trusted/desc/nacl_desc_wrapper.h"
24 #include "ppapi/c/pp_errors.h"
25 #include "ppapi/cpp/module.h"
29 void Plugin::ShutDownSubprocesses() {
30 PLUGIN_PRINTF(("Plugin::ShutDownSubprocesses (this=%p)\n",
31 static_cast<void*>(this)));
33 // Shut down service runtime. This must be done before all other calls so
34 // they don't block forever when waiting for the upcall thread to exit.
35 main_subprocess_
.Shutdown();
37 PLUGIN_PRINTF(("Plugin::ShutDownSubprocess (this=%p, return)\n",
38 static_cast<void*>(this)));
41 bool Plugin::LoadHelperNaClModuleInternal(NaClSubprocess
* subprocess
,
42 const SelLdrStartParams
& params
) {
43 CHECK(!pp::Module::Get()->core()->IsMainThread());
44 ServiceRuntime
* service_runtime
=
45 new ServiceRuntime(this,
47 false, // No main_service_runtime.
48 false); // No non-SFI mode (i.e. in SFI-mode).
50 // Now start the SelLdr instance. This must be created on the main thread.
51 bool service_runtime_started
= false;
52 pp::CompletionCallback sel_ldr_callback
=
53 callback_factory_
.NewCallback(&Plugin::SignalStartSelLdrDone
,
54 &service_runtime_started
,
56 pp::CompletionCallback callback
=
57 callback_factory_
.NewCallback(&Plugin::StartSelLdrOnMainThread
,
58 service_runtime
, params
,
60 pp::Module::Get()->core()->CallOnMainThread(0, callback
, 0);
61 if (!service_runtime
->WaitForSelLdrStart()) {
62 PLUGIN_PRINTF(("Plugin::LoadHelperNaClModule "
63 "WaitForSelLdrStart timed out!\n"));
64 service_runtime
->Shutdown();
65 // Don't delete service_runtime here; it could still be used by the pending
66 // SignalStartSelLdrDone callback.
69 PLUGIN_PRINTF(("Plugin::LoadHelperNaClModule (service_runtime_started=%d)\n",
70 service_runtime_started
));
71 if (!service_runtime_started
) {
72 service_runtime
->Shutdown();
73 delete service_runtime
;
77 // Now actually start the nexe.
79 // We can't use pp::BlockUntilComplete() inside an in-process plugin, so we
80 // have to roll our own blocking logic, similar to WaitForSelLdrStart()
81 // above, except without timeout logic.
82 pp::Module::Get()->core()->CallOnMainThread(
84 callback_factory_
.NewCallback(&Plugin::StartNexe
, service_runtime
));
85 if (!service_runtime
->WaitForNexeStart()) {
86 service_runtime
->Shutdown();
87 delete service_runtime
;
90 subprocess
->set_service_runtime(service_runtime
);
94 void Plugin::StartSelLdrOnMainThread(int32_t pp_error
,
95 ServiceRuntime
* service_runtime
,
96 const SelLdrStartParams
& params
,
97 pp::CompletionCallback callback
) {
98 CHECK(pp_error
== PP_OK
);
99 service_runtime
->StartSelLdr(params
, callback
);
102 void Plugin::SignalStartSelLdrDone(int32_t pp_error
,
104 ServiceRuntime
* service_runtime
) {
105 if (service_runtime
->SelLdrWaitTimedOut()) {
106 delete service_runtime
;
108 *started
= (pp_error
== PP_OK
);
109 service_runtime
->SignalStartSelLdrDone();
113 void Plugin::LoadNaClModule(PP_NaClFileInfo file_info
,
114 bool uses_nonsfi_mode
,
115 PP_NaClAppProcessType process_type
) {
116 CHECK(pp::Module::Get()->core()->IsMainThread());
117 // Before forking a new sel_ldr process, ensure that we do not leak
118 // the ServiceRuntime object for an existing subprocess, and that any
119 // associated listener threads do not go unjoined because if they
120 // outlive the Plugin object, they will not be memory safe.
121 ShutDownSubprocesses();
122 pp::Var manifest_base_url
=
123 pp::Var(pp::PASS_REF
, nacl_interface_
->GetManifestBaseURL(pp_instance()));
124 std::string manifest_base_url_str
= manifest_base_url
.AsString();
126 SelLdrStartParams
params(manifest_base_url_str
,
129 ErrorInfo error_info
;
130 ServiceRuntime
* service_runtime
= new ServiceRuntime(
131 this, pp_instance(), true, uses_nonsfi_mode
);
132 main_subprocess_
.set_service_runtime(service_runtime
);
133 if (NULL
== service_runtime
) {
134 error_info
.SetReport(
135 PP_NACL_ERROR_SEL_LDR_INIT
,
136 "sel_ldr init failure " + main_subprocess_
.description());
137 ReportLoadError(error_info
);
141 // We don't take any action once nexe loading has completed, so pass an empty
142 // callback here for |callback|.
143 pp::CompletionCallback callback
= callback_factory_
.NewCallback(
144 &Plugin::StartNexe
, service_runtime
);
145 StartSelLdrOnMainThread(
146 static_cast<int32_t>(PP_OK
), service_runtime
, params
, callback
);
149 void Plugin::StartNexe(int32_t pp_error
, ServiceRuntime
* service_runtime
) {
150 CHECK(pp::Module::Get()->core()->IsMainThread());
151 if (pp_error
!= PP_OK
)
153 service_runtime
->StartNexe();
156 NaClSubprocess
* Plugin::LoadHelperNaClModule(const std::string
& helper_url
,
157 PP_NaClFileInfo file_info
,
158 ErrorInfo
* error_info
) {
159 nacl::scoped_ptr
<NaClSubprocess
> nacl_subprocess(
160 new NaClSubprocess("helper module", NULL
, NULL
));
161 if (NULL
== nacl_subprocess
.get()) {
162 error_info
->SetReport(PP_NACL_ERROR_SEL_LDR_INIT
,
163 "unable to allocate helper subprocess.");
167 // Do not report UMA stats for translator-related nexes.
168 // TODO(sehr): define new UMA stats for translator related nexe events.
169 // NOTE: The PNaCl translator nexes are not built to use the IRT. This is
170 // done to save on address space and swap space.
171 SelLdrStartParams
params(helper_url
,
173 PP_PNACL_TRANSLATOR_PROCESS_TYPE
);
175 // Helper NaCl modules always use the PNaCl manifest, as there is no
176 // corresponding NMF.
177 if (!LoadHelperNaClModuleInternal(nacl_subprocess
.get(), params
))
180 // We can block here in StartSrpcServices, since helper NaCl
181 // modules are spawned from a private thread.
183 // TODO(bsy): if helper module crashes, we should abort.
184 // crash_cb is not used here, so we are relying on crashes
185 // being detected in StartSrpcServices or later.
187 // NB: More refactoring might be needed, however, if helper
188 // NaCl modules have their own manifest. Currently the
189 // manifest is a per-plugin-instance object, not a per
190 // NaClSubprocess object.
191 if (!nacl_subprocess
->StartSrpcServices()) {
192 error_info
->SetReport(PP_NACL_ERROR_SRPC_CONNECTION_FAIL
,
193 "SRPC connection failure for " +
194 nacl_subprocess
->description());
198 PLUGIN_PRINTF(("Plugin::LoadHelperNaClModule (%s, %s)\n",
200 nacl_subprocess
.get()->detailed_description().c_str()));
202 return nacl_subprocess
.release();
205 // All failures of this function will show up as "Missing Plugin-in", so
206 // there is no need to log to JS console that there was an initialization
207 // failure. Note that module loading functions will log their own errors.
208 bool Plugin::Init(uint32_t argc
, const char* argn
[], const char* argv
[]) {
209 nacl_interface_
->InitializePlugin(pp_instance(), argc
, argn
, argv
);
210 wrapper_factory_
= new nacl::DescWrapperFactory();
211 pp::CompletionCallback open_cb
=
212 callback_factory_
.NewCallback(&Plugin::NaClManifestFileDidOpen
);
213 nacl_interface_
->RequestNaClManifest(pp_instance(),
214 open_cb
.pp_completion_callback());
218 Plugin::Plugin(PP_Instance pp_instance
)
219 : pp::Instance(pp_instance
),
220 main_subprocess_("main subprocess", NULL
, NULL
),
221 uses_nonsfi_mode_(false),
222 wrapper_factory_(NULL
),
223 nacl_interface_(NULL
),
224 uma_interface_(this) {
225 callback_factory_
.Initialize(this);
226 nacl_interface_
= GetNaClInterface();
227 CHECK(nacl_interface_
!= NULL
);
229 // Notify PPB_NaCl_Private that the instance is created before altering any
230 // state that it tracks.
231 nacl_interface_
->InstanceCreated(pp_instance
);
232 nexe_file_info_
= kInvalidNaClFileInfo
;
236 // Destroy the coordinator while the rest of the data is still there
237 pnacl_coordinator_
.reset(NULL
);
239 nacl_interface_
->InstanceDestroyed(pp_instance());
241 // ShutDownSubprocesses shuts down the main subprocess, which shuts
242 // down the main ServiceRuntime object, which kills the subprocess.
243 // As a side effect of the subprocess being killed, the reverse
244 // services thread(s) will get EOF on the reverse channel(s), and
245 // the thread(s) will exit. In ServiceRuntime::Shutdown, we invoke
246 // ReverseService::WaitForServiceThreadsToExit(), so that there will
247 // not be an extent thread(s) hanging around. This means that the
248 // ~Plugin will block until this happens. This is a requirement,
249 // since the renderer should be free to unload the plugin code, and
250 // we cannot have threads running code that gets unloaded before
253 // By waiting for the threads here, we also ensure that the Plugin
254 // object and the subprocess and ServiceRuntime objects is not
255 // (fully) destroyed while the threads are running, so resources
256 // that are destroyed after ShutDownSubprocesses (below) are
257 // guaranteed to be live and valid for access from the service
260 // The main_subprocess object, which wraps the main service_runtime
261 // object, is dtor'd implicitly after the explicit code below runs,
262 // so the main service runtime object will not have been dtor'd,
263 // though the Shutdown method may have been called, during the
264 // lifetime of the service threads.
265 ShutDownSubprocesses();
267 delete wrapper_factory_
;
270 bool Plugin::HandleDocumentLoad(const pp::URLLoader
& url_loader
) {
271 // We don't know if the plugin will handle the document load, but return
272 // true in order to give it a chance to respond once the proxy is started.
276 void Plugin::NexeFileDidOpen(int32_t pp_error
) {
277 if (pp_error
!= PP_OK
)
282 PP_NATIVE_NACL_PROCESS_TYPE
);
285 void Plugin::BitcodeDidTranslate(int32_t pp_error
) {
286 PLUGIN_PRINTF(("Plugin::BitcodeDidTranslate (pp_error=%" NACL_PRId32
")\n",
288 if (pp_error
!= PP_OK
) {
289 // Error should have been reported by pnacl. Just return.
293 // Inform JavaScript that we successfully translated the bitcode to a nexe.
294 PP_FileHandle handle
= pnacl_coordinator_
->TakeTranslatedFileHandle();
296 PP_NaClFileInfo info
;
297 info
.handle
= handle
;
302 false, /* uses_nonsfi_mode */
303 PP_PNACL_PROCESS_TYPE
);
306 void Plugin::NaClManifestFileDidOpen(int32_t pp_error
) {
307 PLUGIN_PRINTF(("Plugin::NaClManifestFileDidOpen (pp_error=%"
308 NACL_PRId32
")\n", pp_error
));
309 if (pp_error
!= PP_OK
)
312 PP_Var pp_program_url
;
313 PP_PNaClOptions pnacl_options
= {PP_FALSE
, PP_FALSE
, PP_FALSE
, 2};
314 PP_Bool uses_nonsfi_mode
;
315 if (nacl_interface_
->GetManifestProgramURL(
316 pp_instance(), &pp_program_url
, &pnacl_options
, &uses_nonsfi_mode
)) {
317 std::string program_url
= pp::Var(pp::PASS_REF
, pp_program_url
).AsString();
318 // TODO(teravest): Make ProcessNaClManifest take responsibility for more of
320 nacl_interface_
->ProcessNaClManifest(pp_instance(), program_url
.c_str());
321 uses_nonsfi_mode_
= PP_ToBool(uses_nonsfi_mode
);
322 if (pnacl_options
.translate
) {
323 pp::CompletionCallback translate_callback
=
324 callback_factory_
.NewCallback(&Plugin::BitcodeDidTranslate
);
325 pnacl_coordinator_
.reset(
326 PnaclCoordinator::BitcodeToNative(this,
329 translate_callback
));
332 pp::CompletionCallback open_callback
=
333 callback_factory_
.NewCallback(&Plugin::NexeFileDidOpen
);
334 // Will always call the callback on success or failure.
335 nacl_interface_
->DownloadNexe(pp_instance(),
338 open_callback
.pp_completion_callback());
344 void Plugin::ReportLoadError(const ErrorInfo
& error_info
) {
345 nacl_interface_
->ReportLoadError(pp_instance(),
346 error_info
.error_code(),
347 error_info
.message().c_str());
350 } // namespace plugin