2 * Copyright 2008, Google Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
15 * * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 #include "native_client/npapi_plugin/srpc/plugin.h"
36 #include "native_client/npapi_plugin/srpc/shared_memory.h"
37 #include "native_client/npapi_plugin/srpc/utility.h"
38 #include "native_client/service_runtime/include/bits/mman.h"
39 #include "native_client/service_runtime/include/sys/stat.h"
40 #include "native_client/service_runtime/internal_errno.h"
44 int SharedMemory::number_alive
= 0;
46 // SharedMemory implements only the connect method.
47 bool SharedMemory::HasMethod(NPObject
*obj
, NPIdentifier name
) {
48 dprintf(("SharedMemory::HasMethod(%p, %s)\n", obj
, IdentToString(name
)));
50 return (Plugin::kReadIdent
== name
) || (Plugin::kWriteIdent
== name
);
53 bool SharedMemory::Invoke(NPObject
*obj
,
55 const NPVariant
*args
,
58 SharedMemory
* shared_memory
= reinterpret_cast<SharedMemory
*>(obj
);
59 Plugin
* plugin
= shared_memory
->plugin_
;
61 dprintf(("SharedMemory::Invoke(%p, %s, %d)\n",
62 obj
, IdentToString(name
), arg_count
));
64 if ((Plugin::kReadIdent
== name
) && (2 == arg_count
)) {
65 // read() takes an offset and a length, returning a string.
69 if (NPVARIANT_IS_INT32(args
[0])) {
70 offset
= NPVARIANT_TO_INT32(args
[0]);
71 } else if (NPVARIANT_IS_DOUBLE(args
[0])) {
72 offset
= static_cast<uint32_t>(NPVARIANT_TO_DOUBLE(args
[0]));
76 if (NPVARIANT_IS_INT32(args
[1])) {
77 len
= NPVARIANT_TO_INT32(args
[1]);
78 } else if (NPVARIANT_IS_DOUBLE(args
[1])) {
79 len
= static_cast<uint32_t>(NPVARIANT_TO_DOUBLE(args
[1]));
83 if (NULL
== shared_memory
->map_addr_
||
84 offset
+ len
> shared_memory
->size_
) {
85 NULL_TO_NPVARIANT(*result
);
88 // UTF-8 encoding may result in a 2x size increase at the most.
89 // TODO: should we do a pre-scan to get the real size?
90 char* ret_string
= reinterpret_cast<char*>(NPN_MemAlloc(2 * len
));
91 unsigned char* shm_addr
=
92 reinterpret_cast<unsigned char*>(shared_memory
->map_addr_
) + offset
;
93 unsigned char* out
= reinterpret_cast<unsigned char*>(ret_string
);
94 // NPAPI wants length to be the number of bytes, not UTF-8 characters.
95 unsigned int utf_bytes
= 0;
96 for (unsigned int i
= 0; i
< len
; ++i
) {
97 unsigned char c
= *shm_addr
;
99 // Code results in a one byte encoding
104 // Code results in a two byte encoding
105 out
[0] = 0xc0 | (c
>> 6);
106 out
[1] = 0x80 | (c
& 0x3f);
112 STRINGN_TO_NPVARIANT(ret_string
, utf_bytes
, *result
);
115 } else if ((Plugin::kWriteIdent
== name
) && (3 == arg_count
)) {
116 // write() takes an offset, a length, and a string.
120 if (NPVARIANT_IS_INT32(args
[0])) {
121 offset
= NPVARIANT_TO_INT32(args
[0]);
122 } else if (NPVARIANT_IS_DOUBLE(args
[0])) {
123 offset
= static_cast<uint32_t>(NPVARIANT_TO_DOUBLE(args
[0]));
127 if (NPVARIANT_IS_INT32(args
[1])) {
128 len
= NPVARIANT_TO_INT32(args
[1]);
129 } else if (NPVARIANT_IS_DOUBLE(args
[1])) {
130 len
= static_cast<uint32_t>(NPVARIANT_TO_DOUBLE(args
[1]));
134 NULL_TO_NPVARIANT(*result
);
135 if (NULL
== shared_memory
->map_addr_
||
136 offset
+ len
> shared_memory
->size_
) {
139 // The input is a JavaScript string, which must consist of UFT-8
140 // characters with character codes between 0 and 255 inclusive.
141 NPString string
= NPVARIANT_TO_STRING(args
[2]);
142 const unsigned char* str
=
143 reinterpret_cast<unsigned const char*>(string
.utf8characters
);
144 const unsigned int utf_bytes
= string
.utf8length
;
145 unsigned char* shm_addr
=
146 reinterpret_cast<unsigned char*>(shared_memory
->map_addr_
) + offset
;
148 for (unsigned int i
= 0; i
< len
; ++i
) {
149 unsigned char c1
= str
[0];
150 unsigned char c2
= 0;
152 // Check that we are still pointing into the JavaScript string we were
154 if (i
>= utf_bytes
) {
157 // Process the byte in the string as UTF-8 characters.
160 // Assert two byte encoding.
161 // The first character must contain 110xxxxxb and the
162 // second must contain 10xxxxxxb.
163 if (((c1
& 0xc3) != c1
) || ((c2
& 0xbf) != c2
)) {
166 *shm_addr
= (c1
<< 6) | (c2
& 0x3f);
169 // One-byte encoding.
181 // SharedMemory have no properties.
182 bool SharedMemory::HasProperty(NPObject
*obj
, NPIdentifier name
) {
183 dprintf(("SharedMemory::HasProperty(%p, %s)\n", obj
, IdentToString(name
)));
188 bool SharedMemory::GetProperty(NPObject
*obj
,
190 NPVariant
*variant
) {
191 dprintf(("SharedMemory::GetProperty(%p, %s)\n", obj
, IdentToString(name
)));
196 bool SharedMemory::SetProperty(NPObject
*obj
,
198 const NPVariant
*variant
) {
199 dprintf(("SharedMemory::SetProperty(%p, %s, %p)\n",
200 obj
, IdentToString(name
), variant
));
205 SharedMemory
* SharedMemory::New(Plugin
* plugin
, struct NaClDesc
* desc
) {
206 static NPClass sharedMemoryClass
= {
207 NP_CLASS_STRUCT_VERSION
,
213 0, // There is no InvokeDefault for SharedMemory.
218 struct nacl_abi_stat st
;
220 dprintf(("SharedMemory::New(%p, %p)\n", plugin
, desc
));
222 SharedMemory
* shared_memory
=
223 reinterpret_cast<SharedMemory
*>(NPN_CreateObject(plugin
->npp(),
224 &sharedMemoryClass
));
226 shared_memory
->plugin_
= plugin
;
227 shared_memory
->desc_
= desc
;
228 // Set size from stat call.
229 int rval
= desc
->vtbl
->Fstat(desc
, plugin
->effp_
, &st
);
231 void* map_addr
= NULL
;
232 size_t size
= st
.nacl_abi_st_size
;
233 // Set the object size.
234 shared_memory
->size_
= size
;
235 dprintf(("SharedMemory::New: size 0x%08x\n", (unsigned) size
));
236 // Find an address range to map the object into.
237 const int kMaxTries
= 10;
242 map_addr
= VirtualAlloc(NULL
, size
, MEM_RESERVE
, PAGE_READWRITE
);
243 if (NULL
== map_addr
||!VirtualFree(map_addr
, 0, MEM_RELEASE
)) {
247 map_addr
= mmap(NULL
,
249 PROT_READ
| PROT_WRITE
,
250 MAP_SHARED
| MAP_ANONYMOUS
,
253 dprintf(("SharedMemory::New: trying addr %p %d\n", map_addr
, errno
));
254 if (MAP_FAILED
== map_addr
|| munmap(map_addr
, size
)) {
259 dprintf(("SharedMemory::New: trying addr %p\n", map_addr
));
260 rval
= desc
->vtbl
->Map(desc
,
264 NACL_ABI_PROT_READ
| NACL_ABI_PROT_WRITE
,
267 dprintf(("SharedMemory::New: result 0x%08x\n", rval
));
268 if (!NaClIsNegErrno(rval
)) {
269 map_addr
= (void*) rval
;
272 } while (NULL
== map_addr
&& tries
< kMaxTries
);
273 dprintf(("SharedMemory::New: addr %p\n", map_addr
));
274 shared_memory
->map_addr_
= map_addr
;
276 dprintf(("SharedMemory::New: Fstat failed\n"));
277 shared_memory
->map_addr_
= NULL
;
280 return shared_memory
;
283 SharedMemory
* SharedMemory::New(Plugin
* plugin
, off_t length
) {
284 void* map_addr
= NULL
;
285 size_t size
= static_cast<size_t>(length
);
286 NaClHandle handle
= nacl::CreateMemoryObject(size
);
287 struct NaClDescImcShm
*imc_desc
=
288 reinterpret_cast<struct NaClDescImcShm
*>(malloc(sizeof(*imc_desc
)));
289 struct NaClDesc
* desc
= reinterpret_cast<struct NaClDesc
*>(imc_desc
);
291 dprintf(("SharedMemory::New(%p, 0x%08x)\n", plugin
, (unsigned) length
));
292 NaClDescImcShmCtor(imc_desc
, handle
, length
);
293 // Allocate the object through the canonical factory and return.
294 return New(plugin
, desc
);
297 NPObject
*SharedMemory::Allocate(NPP npp
, NPClass
*theClass
) {
298 dprintf(("SharedMemory::Allocate(%d)\n", ++number_alive
));
300 return new SharedMemory(npp
);
303 void SharedMemory::Deallocate(NPObject
*obj
) {
304 SharedMemory
* shared_memory
= reinterpret_cast<SharedMemory
*>(obj
);
306 dprintf(("SharedMemory::Deallocate(%p, %d)\n", obj
, --number_alive
));
308 // Free the memory that was mapped to the descriptor.
309 if (shared_memory
->desc_
&& shared_memory
->plugin_
) {
310 shared_memory
->desc_
->vtbl
->Unmap(shared_memory
->desc_
,
311 shared_memory
->plugin_
->effp_
,
312 shared_memory
->map_addr_
,
313 shared_memory
->size_
);
315 // TODO: is there a missing NaClDescUnref here?
316 delete reinterpret_cast<SharedMemory
*>(obj
);
319 void SharedMemory::Invalidate(NPObject
*obj
) {
320 SharedMemory
* shared_memory
= reinterpret_cast<SharedMemory
*>(obj
);
322 dprintf(("SharedMemory::Invalidate(%p)\n", shared_memory
));
324 // Invalidates are called by Firefox in abitrary order. Hence, the plugin
325 // could have been freed/trashed before we get invalidated. Therefore, we
326 // cannot use the effp_ member of the plugin object.
327 // TODO: fix the resulting address space leak.
328 // Free the memory that was mapped to the descriptor.
329 // shared_memory->desc_->vtbl->Unmap(shared_memory->desc_,
330 // shared_memory->plugin_->effp_,
331 // shared_memory->map_addr_,
332 // shared_memory->size_);
333 // After invalidation, the browser does not respect reference counting,
334 // so we shut down here what we can and prevent attempts to shut down
335 // other linked structures in Deallocate.
336 shared_memory
->plugin_
= NULL
;
337 shared_memory
->desc_
= NULL
;
340 SharedMemory::SharedMemory(NPP npp
) : UnknownHandle(npp
) {
341 dprintf(("SharedMemory::SharedMemory(%p)\n", this));
344 SharedMemory::~SharedMemory() {
345 dprintf(("SharedMemory::~SharedMemory(%p)\n", this));
348 } // namespace nacl_srpc