Make ncval-annotate and ncval-stubout executable
[nativeclient.git] / npapi_plugin / srpc / shared_memory.cc
blob3bd6e3c2160cb7a9ecad66f30695356c70ca7120
1 /*
2 * Copyright 2008, Google Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
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
14 * distribution.
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.
33 #include <signal.h>
34 #include <string.h>
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"
42 namespace nacl_srpc {
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,
54 NPIdentifier name,
55 const NPVariant *args,
56 uint32_t arg_count,
57 NPVariant *result) {
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.
66 uint32_t offset;
67 uint32_t len;
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]));
73 } else {
74 return false;
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]));
80 } else {
81 return false;
83 if (NULL == shared_memory->map_addr_ ||
84 offset + len > shared_memory->size_) {
85 NULL_TO_NPVARIANT(*result);
86 return false;
87 } else {
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;
98 if (c < 128) {
99 // Code results in a one byte encoding
100 *out = c;
101 ++out;
102 ++utf_bytes;
103 } else {
104 // Code results in a two byte encoding
105 out[0] = 0xc0 | (c >> 6);
106 out[1] = 0x80 | (c & 0x3f);
107 out += 2;
108 utf_bytes += 2;
110 ++shm_addr;
112 STRINGN_TO_NPVARIANT(ret_string, utf_bytes, *result);
113 return true;
115 } else if ((Plugin::kWriteIdent == name) && (3 == arg_count)) {
116 // write() takes an offset, a length, and a string.
117 uint32_t offset;
118 uint32_t len;
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]));
124 } else {
125 return false;
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]));
131 } else {
132 return false;
134 NULL_TO_NPVARIANT(*result);
135 if (NULL == shared_memory->map_addr_ ||
136 offset + len > shared_memory->size_) {
137 return false;
138 } else {
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
153 // passed.
154 if (i >= utf_bytes) {
155 return false;
157 // Process the byte in the string as UTF-8 characters.
158 if (c1 & 0x80) {
159 c2 = str[1];
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)) {
164 return false;
166 *shm_addr = (c1 << 6) | (c2 & 0x3f);
167 str += 2;
168 } else {
169 // One-byte encoding.
170 *shm_addr = c1;
171 ++str;
173 ++shm_addr;
175 return true;
178 return false;
181 // SharedMemory have no properties.
182 bool SharedMemory::HasProperty(NPObject *obj, NPIdentifier name) {
183 dprintf(("SharedMemory::HasProperty(%p, %s)\n", obj, IdentToString(name)));
185 return false;
188 bool SharedMemory::GetProperty(NPObject *obj,
189 NPIdentifier name,
190 NPVariant *variant) {
191 dprintf(("SharedMemory::GetProperty(%p, %s)\n", obj, IdentToString(name)));
193 return false;
196 bool SharedMemory::SetProperty(NPObject *obj,
197 NPIdentifier name,
198 const NPVariant *variant) {
199 dprintf(("SharedMemory::SetProperty(%p, %s, %p)\n",
200 obj, IdentToString(name), variant));
202 return false;
205 SharedMemory* SharedMemory::New(Plugin* plugin, struct NaClDesc* desc) {
206 static NPClass sharedMemoryClass = {
207 NP_CLASS_STRUCT_VERSION,
208 Allocate,
209 Deallocate,
210 Invalidate,
211 HasMethod,
212 Invoke,
213 0, // There is no InvokeDefault for SharedMemory.
214 HasProperty,
215 GetProperty,
216 SetProperty,
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);
230 if (0 == rval) {
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;
238 int tries = 0;
239 do {
240 ++tries;
241 #if NACL_WINDOWS
242 map_addr = VirtualAlloc(NULL, size, MEM_RESERVE, PAGE_READWRITE);
243 if (NULL == map_addr ||!VirtualFree(map_addr, 0, MEM_RELEASE)) {
244 continue;
246 #else
247 map_addr = mmap(NULL,
248 size,
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)) {
255 map_addr = NULL;
256 continue;
258 #endif
259 dprintf(("SharedMemory::New: trying addr %p\n", map_addr));
260 rval = desc->vtbl->Map(desc,
261 plugin->effp_,
262 map_addr,
263 size,
264 NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE,
265 NACL_ABI_MAP_SHARED,
267 dprintf(("SharedMemory::New: result 0x%08x\n", rval));
268 if (!NaClIsNegErrno(rval)) {
269 map_addr = (void*) rval;
270 break;
272 } while (NULL == map_addr && tries < kMaxTries);
273 dprintf(("SharedMemory::New: addr %p\n", map_addr));
274 shared_memory->map_addr_ = map_addr;
275 } else {
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