diff options
author | binji@chromium.org <binji@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-02-15 22:51:03 +0000 |
---|---|---|
committer | binji@chromium.org <binji@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-02-15 22:51:03 +0000 |
commit | 5b0cd647c7afc0f9f99efcfc80b6e888c460f760 (patch) | |
tree | d6d39125de0a1b866e156d91158b3e0f3fdaeb1b /native_client_sdk | |
parent | f8a5aec01f3489433363c2b34c9f8f6bcd8b79cb (diff) | |
download | chromium_src-5b0cd647c7afc0f9f99efcfc80b6e888c460f760.zip chromium_src-5b0cd647c7afc0f9f99efcfc80b6e888c460f760.tar.gz chromium_src-5b0cd647c7afc0f9f99efcfc80b6e888c460f760.tar.bz2 |
[NaCl SDK] dlopen works from non-NMF source.
* Modify dlopen example to show this new behavior.
* Add new MountPassthrough to nacl_io
* Add nacl_io wrapping for mmap and open_resource functions.
* Add more _real_* functions for passing through to the IRT.
BUG=155086
TBR=noelallen@chromium.org
NOTRY=true
Review URL: https://chromiumcodereview.appspot.com/12253041
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@182852 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'native_client_sdk')
34 files changed, 981 insertions, 91 deletions
diff --git a/native_client_sdk/src/examples/dlopen/Makefile b/native_client_sdk/src/examples/dlopen/Makefile index 29a08a8..8ddaf2c6 100644 --- a/native_client_sdk/src/examples/dlopen/Makefile +++ b/native_client_sdk/src/examples/dlopen/Makefile @@ -42,6 +42,7 @@ TARGET=dlopen # DLOPEN_SRCS=dlopen.cc EIGHTBALL_SRCS=eightball.cc +REVERSE_SRCS=reverse.cc # @@ -57,7 +58,7 @@ EIGHTBALL_SRCS=eightball.cc # and the set we do not. This example does not havea any additional library # dependencies. # -DEPS= +DEPS=nacl_io LIBS=$(DEPS) dl ppapi_cpp ppapi pthread @@ -71,6 +72,14 @@ $(foreach dep,$(DEPS),$(eval $(call DEPEND_RULE,$(dep)))) # $(foreach src,$(DLOPEN_SRCS),$(eval $(call COMPILE_RULE,$(src)))) $(foreach src,$(EIGHTBALL_SRCS),$(eval $(call COMPILE_RULE,$(src),-fPIC))) +$(foreach src,$(REVERSE_SRCS),$(eval $(call COMPILE_RULE,$(src),-fPIC))) + +# NOTE: The 1 argument here builds libeightball, but prevents it from being +# automatically included in the .nmf file (and therefore loaded at startup). +# This is done to demonstrate that the shared library can be successfully +# loaded from any location (in this example, the HTTP mount). +$(eval $(call SO_RULE,libreverse,$(REVERSE_SRCS),,,1)) + # # Use the link macro for this target on the list of sources. @@ -78,7 +87,6 @@ $(foreach src,$(EIGHTBALL_SRCS),$(eval $(call COMPILE_RULE,$(src),-fPIC))) $(eval $(call SO_RULE,libeightball,$(EIGHTBALL_SRCS))) $(eval $(call LINK_RULE,$(TARGET),$(DLOPEN_SRCS),$(LIBS),$(DEPS))) - # # Specify the NMF to be created with no additional arugments. # diff --git a/native_client_sdk/src/examples/dlopen/dlopen.cc b/native_client_sdk/src/examples/dlopen/dlopen.cc index 5a5487e..f77930a 100644 --- a/native_client_sdk/src/examples/dlopen/dlopen.cc +++ b/native_client_sdk/src/examples/dlopen/dlopen.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2012 The Native Client Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -16,35 +16,32 @@ /// a blocking call, which is not alowed on the main thread. #include <dlfcn.h> +#include <pthread.h> #include <stdio.h> #include <stdlib.h> -#include <pthread.h> +#include <string.h> -#include <ppapi/cpp/module.h> #include <ppapi/cpp/completion_callback.h> -#include <ppapi/cpp/var.h> #include <ppapi/cpp/instance.h> +#include <ppapi/cpp/module.h> +#include <ppapi/cpp/var.h> #include "eightball.h" +#include "nacl_io/nacl_io.h" +#include "reverse.h" -/// The Instance class. One of these exists for each instance of your NaCl -/// module on the web page. The browser will ask the Module object to create -/// a new Instance for each occurrence of the <embed> tag that has these -/// attributes: -/// <pre> -/// type="application/x-nacl" -/// nacl="dlopen.nmf" -/// </pre> -class dlOpenInstance : public pp::Instance { + +class DlopenInstance : public pp::Instance { public: - explicit dlOpenInstance(pp::Core *core, PP_Instance instance): - pp::Instance(instance) { - _dlhandle = NULL; - _eightball = NULL; - _core = core; - _tid = 0; - }; - virtual ~dlOpenInstance(){}; + explicit DlopenInstance(PP_Instance instance) + : pp::Instance(instance), + eightball_so_(NULL), + reverse_so_(NULL), + eightball_(NULL), + reverse_(NULL), + tid_(NULL) {} + + virtual ~DlopenInstance(){}; // Helper function to post a message back to the JS and stdout functions. void logmsg(const char* pStr){ @@ -54,8 +51,14 @@ class dlOpenInstance : public pp::Instance { // Initialize the module, staring a worker thread to load the shared object. virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]){ + nacl_io_init_ppapi(pp_instance(), + pp::Module::Get()->get_browser_interface()); + // Mount a HTTP mount at /http. All reads from /http/* will read from the + // server. + mount("", "/http", "httpfs", 0, ""); + logmsg("Spawning thread to cache .so files..."); - if (pthread_create(&_tid, NULL, LoadLibrariesOnWorker, this)) { + if (pthread_create(&tid_, NULL, LoadLibrariesOnWorker, this)) { logmsg("ERROR; pthread_create() failed.\n"); return false; } @@ -65,57 +68,86 @@ class dlOpenInstance : public pp::Instance { // This function is called on a worker thread, and will call dlopen to load // the shared object. In addition, note that this function does NOT call // dlclose, which would close the shared object and unload it from memory. - void LoadLibrary() - { + void LoadLibrary() { const int32_t IMMEDIATELY = 0; - _dlhandle = dlopen("libeightball.so", RTLD_LAZY); + eightball_so_ = dlopen("libeightball.so", RTLD_LAZY); + reverse_so_ = dlopen("/http/glibc/Debug/libreverse_x86_64.so", RTLD_LAZY); pp::CompletionCallback cc(LoadDoneCB, this); - _core->CallOnMainThread(IMMEDIATELY, cc , 0); + pp::Module::Get()->core()->CallOnMainThread(IMMEDIATELY, cc , 0); } // This function will run on the main thread and use the handle it stored by // the worker thread, assuming it successfully loaded, to get a pointer to the // message function in the shared object. void UseLibrary() { - _dlhandle = dlopen("libeightball.so", RTLD_LAZY); - if(_dlhandle == NULL) { - logmsg("libeightball.so did not load"); - } else { - intptr_t offset = (intptr_t) dlsym(this->_dlhandle, "Magic8Ball"); - _eightball = (TYPE_eightball) offset; - if (NULL == _eightball) { - std::string ballmessage = "dlsym() returned NULL: "; - ballmessage += dlerror(); - ballmessage += "\n"; - logmsg(ballmessage.c_str()); + if (eightball_so_ != NULL) { + intptr_t offset = (intptr_t) dlsym(eightball_so_, "Magic8Ball"); + eightball_ = (TYPE_eightball) offset; + if (NULL == eightball_) { + std::string message = "dlsym() returned NULL: "; + message += dlerror(); + message += "\n"; + logmsg(message.c_str()); + return; } - else{ - logmsg("Eightball loaded!"); + + logmsg("Loaded libeightball.so"); + } else { + logmsg("libeightball.so did not load"); + } + + + if (reverse_so_ != NULL) { + intptr_t offset = (intptr_t) dlsym(reverse_so_, "Reverse"); + reverse_ = (TYPE_reverse) offset; + if (NULL == reverse_) { + std::string message = "dlsym() returned NULL: "; + message += dlerror(); + message += "\n"; + logmsg(message.c_str()); + return; } + logmsg("Loaded libreverse.so"); + } else { + logmsg("libreverse.so did not load"); } } // Called by the browser to handle the postMessage() call in Javascript. virtual void HandleMessage(const pp::Var& var_message) { - if(NULL == _eightball){ - logmsg("Eightball library not loaded"); - return; - } - if (!var_message.is_string()) { logmsg("Message is not a string."); return; } std::string message = var_message.AsString(); - if (message == "query") { - fprintf(stdout, "%s(%d) Got this far.\n", __FILE__, __LINE__); + if (message == "eightball") { + if (NULL == eightball_){ + logmsg("Eightball library not loaded"); + return; + } + std::string ballmessage = "The Magic 8-Ball says: "; - ballmessage += this->_eightball(); + ballmessage += eightball_(); ballmessage += "!"; logmsg(ballmessage.c_str()); - fprintf(stdout, "%s(%d) Got this far.\n", __FILE__, __LINE__); + } else if (message.find("reverse:") == 0) { + if (NULL == reverse_) { + logmsg("Reverse library not loaded"); + return; + } + + std::string s = message.substr(strlen("reverse:")); + char* result = reverse_(s.c_str()); + + std::string message = "Your string reversed: \""; + message += result; + message += "\""; + + free(result); + + logmsg(message.c_str()); } else { std::string errormsg = "Unexpected message: "; errormsg += message + "\n"; @@ -124,22 +156,22 @@ class dlOpenInstance : public pp::Instance { } static void* LoadLibrariesOnWorker(void *pInst) { - dlOpenInstance *inst = static_cast<dlOpenInstance *>(pInst); + DlopenInstance *inst = static_cast<DlopenInstance *>(pInst); inst->LoadLibrary(); return NULL; } static void LoadDoneCB(void *pInst, int32_t result) { - dlOpenInstance *inst = static_cast<dlOpenInstance *>(pInst); + DlopenInstance *inst = static_cast<DlopenInstance *>(pInst); inst->UseLibrary(); } private: - void *_dlhandle; - TYPE_eightball _eightball; - pp::Core *_core; - pthread_t _tid; - + void* eightball_so_; + void* reverse_so_; + TYPE_eightball eightball_; + TYPE_reverse reverse_; + pthread_t tid_; }; // The Module class. The browser calls the CreateInstance() method to create @@ -150,9 +182,9 @@ class dlOpenModule : public pp::Module { dlOpenModule() : pp::Module() {} virtual ~dlOpenModule() {} - // Create and return a dlOpenInstance object. + // Create and return a DlopenInstance object. virtual pp::Instance* CreateInstance(PP_Instance instance) { - return new dlOpenInstance(core(), instance); + return new DlopenInstance(instance); } }; diff --git a/native_client_sdk/src/examples/dlopen/eightball.cc b/native_client_sdk/src/examples/dlopen/eightball.cc index df2be2a..531f8a61 100644 --- a/native_client_sdk/src/examples/dlopen/eightball.cc +++ b/native_client_sdk/src/examples/dlopen/eightball.cc @@ -1,3 +1,7 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + #include <stdlib.h> #include <stdio.h> diff --git a/native_client_sdk/src/examples/dlopen/eightball.h b/native_client_sdk/src/examples/dlopen/eightball.h index d0924d3..df3d997 100644 --- a/native_client_sdk/src/examples/dlopen/eightball.h +++ b/native_client_sdk/src/examples/dlopen/eightball.h @@ -1,9 +1,13 @@ -#ifndef __EIGHTBALL_H__ -#define __EIGHTBALL_H__ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef EIGHTBALL_H_ +#define EIGHTBALL_H_ /* Return an answer. Question not required */ typedef char* (*TYPE_eightball)(void); extern "C" const char* Magic8Ball(); -#endif /* __EIGHTBALL_H__ */ +#endif /* EIGHTBALL_H_ */ diff --git a/native_client_sdk/src/examples/dlopen/example.dsc b/native_client_sdk/src/examples/dlopen/example.dsc index cd66e0c..09f0434 100644 --- a/native_client_sdk/src/examples/dlopen/example.dsc +++ b/native_client_sdk/src/examples/dlopen/example.dsc @@ -13,6 +13,13 @@ 'SOURCES' : ['eightball.cc', 'eightball.h'], 'CXXFLAGS': ['-fPIC'], 'LIBS' : ['ppapi_cpp', 'ppapi', 'pthread'] + }, + { + 'NAME' : 'libreverse', + 'TYPE' : 'so', + 'SOURCES' : ['reverse.cc', 'reverse.h'], + 'CXXFLAGS': ['-fPIC'], + 'LIBS' : ['ppapi_cpp', 'ppapi', 'pthread'] } ], 'DATA': [ diff --git a/native_client_sdk/src/examples/dlopen/example.js b/native_client_sdk/src/examples/dlopen/example.js index 580fbe8..bb658cd 100644 --- a/native_client_sdk/src/examples/dlopen/example.js +++ b/native_client_sdk/src/examples/dlopen/example.js @@ -5,6 +5,7 @@ // Called by the common.js module. function attachListeners() { document.querySelector('form').addEventListener('submit', askBall); + document.getElementById('reverse').addEventListener('click', reverseString); } // Called by the common.js module. @@ -19,6 +20,15 @@ function askBall(event) { var query = questionEl.value; questionEl.value = ''; document.getElementById('log').innerHTML += 'You asked:' + query + '<br>'; - common.naclModule.postMessage('query'); + common.naclModule.postMessage('eightball'); event.preventDefault(); } + +function reverseString(event) { + var questionEl = document.getElementById('question'); + var query = questionEl.value; + questionEl.value = ''; + + document.getElementById('log').innerHTML += 'Reversing:' + query + '<br>'; + common.naclModule.postMessage('reverse:' + query); +} diff --git a/native_client_sdk/src/examples/dlopen/index.html b/native_client_sdk/src/examples/dlopen/index.html index 52be080..4ad4f54 100644 --- a/native_client_sdk/src/examples/dlopen/index.html +++ b/native_client_sdk/src/examples/dlopen/index.html @@ -21,6 +21,8 @@ found in the LICENSE file. <input type="text" id="question" value=""> <input type="submit" value="ASK!"> </form> + <p>... or click this button to reverse the string.</p> + <button id="reverse">Reverse</button> <!-- The NaCl plugin will be embedded inside the element with id "listener". See common.js.--> <div id="listener"></div> diff --git a/native_client_sdk/src/examples/dlopen/reverse.cc b/native_client_sdk/src/examples/dlopen/reverse.cc new file mode 100644 index 0000000..94eaaef --- /dev/null +++ b/native_client_sdk/src/examples/dlopen/reverse.cc @@ -0,0 +1,16 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "reverse.h" +#include <stdlib.h> +#include <string.h> + +extern "C" char* Reverse(const char* s) { + size_t len = strlen(s); + char* reversed = static_cast<char*>(malloc(len + 1)); + for (int i = len - 1; i >= 0; --i) + reversed[len - i - 1] = s[i]; + reversed[len] = 0; + return reversed; +} diff --git a/native_client_sdk/src/examples/dlopen/reverse.h b/native_client_sdk/src/examples/dlopen/reverse.h new file mode 100644 index 0000000..f0414ab --- /dev/null +++ b/native_client_sdk/src/examples/dlopen/reverse.h @@ -0,0 +1,12 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef REVERSE_H_ +#define REVERSE_H_ + +/* Allocate a new string that is the reverse of the given string. */ +typedef char* (*TYPE_reverse)(const char*); +extern "C" char* Reverse(const char *); + +#endif /* REVERSE_H_ */ diff --git a/native_client_sdk/src/libraries/nacl_io/Makefile b/native_client_sdk/src/libraries/nacl_io/Makefile index e8c9bc9..7c5e8b4 100644 --- a/native_client_sdk/src/libraries/nacl_io/Makefile +++ b/native_client_sdk/src/libraries/nacl_io/Makefile @@ -43,8 +43,9 @@ TARGET=nacl_io SOURCES:=kernel_handle.cc kernel_intercept.cc kernel_object.cc kernel_proxy.cc SOURCES+=kernel_wrap_glibc.cc kernel_wrap_newlib.cc kernel_wrap_win.cc SOURCES+=mount.cc mount_dev.cc mount_html5fs.cc mount_http.cc mount_mem.cc -SOURCES+=mount_node.cc mount_node_dir.cc mount_node_html5fs.cc mount_node_mem.cc -SOURCES+=nacl_io.cc path.cc pepper_interface.cc real_pepper_interface.cc +SOURCES+=mount_node.cc mount_node_dir.cc mount_node_html5fs.cc +SOURCES+=mount_node_mem.cc mount_passthrough.cc nacl_io.cc path.cc +SOURCES+=pepper_interface.cc real_pepper_interface.cc # # Use the compile macro for each source. diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_intercept.cc b/native_client_sdk/src/libraries/nacl_io/kernel_intercept.cc index 9af208d..d42f118 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_intercept.cc +++ b/native_client_sdk/src/libraries/nacl_io/kernel_intercept.cc @@ -155,3 +155,16 @@ int ki_link(const char* oldpath, const char* newpath) { int ki_symlink(const char* oldpath, const char* newpath) { return s_kp->symlink(oldpath, newpath); } + +void* ki_mmap(void* addr, size_t length, int prot, int flags, int fd, + off_t offset) { + return s_kp->mmap(addr, length, prot, flags, fd, offset); +} + +int ki_munmap(void* addr, size_t length) { + return s_kp->munmap(addr, length); +} + +int ki_open_resource(const char* file) { + return s_kp->open_resource(file); +} diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_intercept.h b/native_client_sdk/src/libraries/nacl_io/kernel_intercept.h index 8cec8ae..6de064d 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_intercept.h +++ b/native_client_sdk/src/libraries/nacl_io/kernel_intercept.h @@ -51,6 +51,10 @@ int ki_unlink(const char* path); int ki_access(const char* path, int amode); int ki_link(const char* oldpath, const char* newpath); int ki_symlink(const char* oldpath, const char* newpath); +void* ki_mmap(void* addr, size_t length, int prot, int flags, int fd, + off_t offset); +int ki_munmap(void* addr, size_t length); +int ki_open_resource(const char* file); EXTERN_C_END diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_object.cc b/native_client_sdk/src/libraries/nacl_io/kernel_object.cc index fca4041..95223f6 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_object.cc +++ b/native_client_sdk/src/libraries/nacl_io/kernel_object.cc @@ -19,6 +19,19 @@ #include "nacl_io/mount_node.h" #include "utils/auto_lock.h" +KernelObject::MMapInfo::MMapInfo() + : addr(NULL), + length(0), + handle(NULL) { +} + +KernelObject::MMapInfo::MMapInfo(void* addr, size_t length, + KernelHandle* handle) + : addr(addr), + length(length), + handle(handle) { +} + KernelObject::KernelObject() { pthread_mutex_init(&kernel_lock_, NULL); pthread_mutex_init(&process_lock_, NULL); @@ -161,8 +174,8 @@ void KernelObject::FreeFD(int fd) { // Release the mount and handle since we no longer // track them with this FD. KernelHandle* handle = handle_map_[fd]; - handle->mount_->Release(); handle->Release(); + handle->mount_->Release(); handle_map_[fd] = NULL; free_fds_.push_back(fd); diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_object.h b/native_client_sdk/src/libraries/nacl_io/kernel_object.h index f4c1b9e..d50aabc 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_object.h +++ b/native_client_sdk/src/libraries/nacl_io/kernel_object.h @@ -21,8 +21,18 @@ class Mount; // path resolution. class KernelObject { public: + struct MMapInfo { + MMapInfo(); + MMapInfo(void* addr, size_t length, KernelHandle* handle); + + void* addr; + size_t length; + KernelHandle* handle; + }; + typedef std::vector<KernelHandle*> HandleMap_t; typedef std::map<std::string, Mount*> MountMap_t; + typedef std::vector<MMapInfo> MMapInfoList_t; KernelObject(); virtual ~KernelObject(); @@ -49,6 +59,7 @@ class KernelObject { HandleMap_t handle_map_; MountMap_t mounts_; + MMapInfoList_t mmap_info_list_; // Kernel lock protects kernel wide resources such as the mount table... pthread_mutex_t kernel_lock_; diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_proxy.cc b/native_client_sdk/src/libraries/nacl_io/kernel_proxy.cc index 71a0e65..745a680 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_proxy.cc +++ b/native_client_sdk/src/libraries/nacl_io/kernel_proxy.cc @@ -9,15 +9,19 @@ #include <fcntl.h> #include <pthread.h> #include <string.h> +#include <iterator> #include <string> #include "nacl_io/kernel_handle.h" +#include "nacl_io/kernel_wrap_real.h" #include "nacl_io/mount.h" #include "nacl_io/mount_dev.h" #include "nacl_io/mount_html5fs.h" #include "nacl_io/mount_http.h" #include "nacl_io/mount_mem.h" #include "nacl_io/mount_node.h" +#include "nacl_io/mount_passthrough.h" +#include "nacl_io/osmman.h" #include "nacl_io/osstat.h" #include "nacl_io/path.h" #include "nacl_io/pepper_interface.h" @@ -33,6 +37,7 @@ #define GRP_ID 1003 + KernelProxy::KernelProxy() : dev_(0), ppapi_(NULL) { @@ -51,10 +56,12 @@ void KernelProxy::Init(PepperInterface* ppapi) { factories_["dev"] = MountDev::Create<MountDev>; factories_["html5fs"] = MountHtml5Fs::Create<MountHtml5Fs>; factories_["httpfs"] = MountHttp::Create<MountHttp>; + factories_["passthroughfs"] = MountPassthrough::Create<MountPassthrough>; - // Create memory mount at root + // Create passthrough mount at root StringMap_t smap; - mounts_["/"] = MountMem::Create<MountMem>(dev_++, smap, ppapi_); + mounts_["/"] = MountPassthrough::Create<MountPassthrough>( + dev_++, smap, ppapi_); mounts_["/dev"] = MountDev::Create<MountDev>(dev_++, smap, ppapi_); // Open the first three in order to get STDIN, STDOUT, STDERR @@ -428,3 +435,108 @@ int KernelProxy::symlink(const char* oldpath, const char* newpath) { errno = EINVAL; return -1; } + +void* KernelProxy::mmap(void* addr, size_t length, int prot, int flags, int fd, + size_t offset) { + // We shouldn't be getting anonymous mmaps here. + assert((flags & MAP_ANONYMOUS) == 0); + assert(fd != -1); + + KernelHandle* handle = AcquireHandle(fd); + + if (NULL == handle) + return MAP_FAILED; + + void* new_addr; + { + AutoLock lock(&handle->lock_); + new_addr = handle->node_->MMap(addr, length, prot, flags, offset); + if (new_addr == MAP_FAILED) { + ReleaseHandle(handle); + return MAP_FAILED; + } + } + + // Don't release the KernelHandle, it is now owned by the MMapInfo. + AutoLock lock(&process_lock_); + mmap_info_list_.push_back(MMapInfo(new_addr, length, handle)); + + return new_addr; +} + +int KernelProxy::munmap(void* addr, size_t length) { + if (addr == NULL || length == 0) { + errno = EINVAL; + return -1; + } + + MMapInfoList_t unmap_list; + { + AutoLock lock(&process_lock_); + int mmap_list_end = mmap_info_list_.size(); + void* addr_end = static_cast<char*>(addr) + length; + + for (int i = 0; i < mmap_list_end;) { + const MMapInfo& mmap_info = mmap_info_list_[i]; + if (addr < static_cast<char*>(mmap_info.addr) + mmap_info.length && + mmap_info.addr < addr_end) + // This memory area should be unmapped; swap it with the last entry in + // our list. + std::swap(mmap_info_list_[i], mmap_info_list_[--mmap_list_end]); + else + ++i; + } + + int num_to_unmap =- mmap_info_list_.size() - mmap_list_end; + if (!num_to_unmap) { + // From the Linux mmap man page: "It is not an error if the indicated + // range does not contain any mapped pages." + return 0; + } + + std::copy(mmap_info_list_.begin() + mmap_list_end, mmap_info_list_.end(), + std::back_inserter(unmap_list)); + + mmap_info_list_.resize(mmap_list_end); + } + + // Unmap everything past the new end of the list. + for (int i = 0; i < unmap_list.size(); ++i) { + const MMapInfo& mmap_info = unmap_list[i]; + KernelHandle* handle = mmap_info.handle; + assert(handle != NULL); + + // Ignore the results from individual munmaps. + handle->node_->Munmap(mmap_info.addr, mmap_info.length); + ReleaseHandle(handle); + } + + return 0; +} + +int KernelProxy::open_resource(const char* path) { + Path rel; + + Mount* mnt = AcquireMountAndPath(path, &rel); + if (mnt == NULL) return -1; + + MountNode* node = mnt->OpenResource(rel); + if (node == NULL) { + node = mnt->Open(rel, O_RDONLY); + if (node == NULL) { + ReleaseMount(mnt); + return -1; + } + } + + // OpenResource failed, try Open(). + + KernelHandle* handle = new KernelHandle(mnt, node, O_RDONLY); + int fd = AllocateFD(handle); + mnt->AcquireNode(node); + + ReleaseHandle(handle); + ReleaseMount(mnt); + + return fd; +} diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_proxy.h b/native_client_sdk/src/libraries/nacl_io/kernel_proxy.h index 904ef52..5ac7b9347 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_proxy.h +++ b/native_client_sdk/src/libraries/nacl_io/kernel_proxy.h @@ -89,6 +89,13 @@ class KernelProxy : protected KernelObject { virtual int link(const char* oldpath, const char* newpath); virtual int symlink(const char* oldpath, const char* newpath); + virtual void* mmap(void* addr, size_t length, int prot, int flags, int fd, + size_t offset); + virtual int munmap(void* addr, size_t length); + + // NaCl-only function to read resources specified in the NMF file. + virtual int open_resource(const char* file); + protected: MountFactoryMap_t factories_; int dev_; diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_wrap.h b/native_client_sdk/src/libraries/nacl_io/kernel_wrap.h index 7d88cfd..fa67be3 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_wrap.h +++ b/native_client_sdk/src/libraries/nacl_io/kernel_wrap.h @@ -61,8 +61,11 @@ int _mkdir(const char* path); #else int mkdir(const char* path, mode_t mode) NOTHROW; #endif +void* mmap(void* addr, size_t length, int prot, int flags, int fd, + off_t offset) NOTHROW; int mount(const char* source, const char* target, const char* filesystemtype, unsigned long mountflags, const void* data) NOTHROW; +int munmap(void* addr, size_t length) NOTHROW; int NAME(open)(const char* path, int oflag, ...); read_ssize_t NAME(read)(int fd, void* buf, size_t nbyte); int remove(const char* path) NOTHROW; diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_wrap_glibc.cc b/native_client_sdk/src/libraries/nacl_io/kernel_wrap_glibc.cc index 67f1fc2..3c81474 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_wrap_glibc.cc +++ b/native_client_sdk/src/libraries/nacl_io/kernel_wrap_glibc.cc @@ -17,6 +17,7 @@ #include <irt_syscalls.h> #include <nacl_stat.h> #include <string.h> +#include <sys/mman.h> #include <sys/stat.h> #include "nacl_io/kernel_intercept.h" @@ -117,6 +118,9 @@ DECLARE(rmdir) DECLARE(seek) DECLARE(stat) DECLARE(write) +DECLARE(mmap) +DECLARE(munmap) +DECLARE(open_resource) int access(const char* path, int amode) NOTHROW { return ki_access(path, amode); @@ -223,16 +227,37 @@ int WRAP(mkdir) (const char* pathname, mode_t mode) { return (ki_mkdir(pathname, mode)) ? errno : 0; } +int WRAP(mmap)(void** addr, size_t length, int prot, int flags, int fd, + off_t offset) { + if (flags & MAP_ANONYMOUS) + return REAL(mmap)(addr, length, prot, flags, fd, offset); + + *addr = ki_mmap(*addr, length, prot, flags, fd, offset); + return *addr == (void*)-1 ? errno : 0; +} + int mount(const char* source, const char* target, const char* filesystemtype, unsigned long mountflags, const void* data) NOTHROW { return ki_mount(source, target, filesystemtype, mountflags, data); } +int WRAP(munmap)(void* addr, size_t length) { + // Always let the real munmap run on the address range. It is not an error if + // there are no mapped pages in that range. + int result = ki_munmap(addr, length); + return REAL(munmap)(addr, length); +} + int WRAP(open)(const char* pathname, int oflag, mode_t cmode, int* newfd) { *newfd = ki_open(pathname, oflag); return (*newfd < 0) ? errno : 0; } +int WRAP(open_resource)(const char* file, int* fd) { + *fd = ki_open_resource(file); + return (*fd < 0) ? errno : 0; +} + int WRAP(read)(int fd, void *buf, size_t count, size_t *nread) { if (!ki_is_initialized()) return REAL(read)(fd, buf, count, nread); @@ -290,20 +315,92 @@ int WRAP(write)(int fd, const void *buf, size_t count, size_t *nwrote) { return (signed_nwrote < 0) ? errno : 0; } -int _real_write(int fd, const void *buf, size_t count, size_t *nwrote) { - return REAL(write)(fd, buf, count, nwrote); + +// "real" functions, i.e. the unwrapped original functions. + +int _real_close(int fd) { + return REAL(close)(fd); +} + +int _real_fstat(int fd, struct stat *buf) { + struct nacl_abi_stat st; + int err = REAL(fstat)(fd, &st); + if (err) { + errno = err; + return -1; + } + + nacl_stat_to_stat(&st, buf); + return 0; +} + +int _real_getdents(int fd, void* buf, size_t count, size_t *nread) { + // "buf" contains dirent(s); "nacl_buf" contains nacl_abi_dirent(s). + // See WRAP(getdents) above. + char* nacl_buf = (char*)alloca(count); + size_t offset = 0; + size_t nacl_offset = 0; + size_t nacl_nread; + int err = REAL(getdents)(fd, (dirent*)nacl_buf, count, &nacl_nread); + if (err) + return err; + + while (nacl_offset < nacl_nread) { + dirent* d = (dirent*)((char*)buf + offset); + nacl_abi_dirent* nacl_d = (nacl_abi_dirent*)(nacl_buf + nacl_offset); + d->d_ino = nacl_d->nacl_abi_d_ino; + d->d_off = nacl_d->nacl_abi_d_off; + d->d_reclen = nacl_d->nacl_abi_d_reclen + d_name_shift; + size_t d_name_len = nacl_d->nacl_abi_d_reclen - + offsetof(nacl_abi_dirent, nacl_abi_d_name); + memcpy(d->d_name, nacl_d->nacl_abi_d_name, d_name_len); + + offset += d->d_reclen; + offset += nacl_d->nacl_abi_d_reclen; + } + + *nread = offset; + return 0; +} + +int _real_lseek(int fd, off_t offset, int whence, off_t* new_offset) { + return REAL(seek)(fd, offset, whence, new_offset); +} + +int _real_mkdir(const char* pathname, mode_t mode) { + return REAL(mkdir)(pathname, mode); +} + +int _real_mmap(void** addr, size_t length, int prot, int flags, int fd, + off_t offset) { + return REAL(mmap)(addr, length, prot, flags, fd, offset); +} + +int _real_munmap(void* addr, size_t length) { + return REAL(munmap)(addr, length); +} + +int _real_open(const char* pathname, int oflag, mode_t cmode, int* newfd) { + return REAL(open)(pathname, oflag, cmode, newfd); +} + +int _real_open_resource(const char* file, int* fd) { + return REAL(open_resource)(file, fd); } int _real_read(int fd, void *buf, size_t count, size_t *nread) { return REAL(read)(fd, buf, count, nread); } -int _real_fstat(int fd, struct stat *buf) { - struct nacl_abi_stat st; - int ret = REAL(fstat)(fd, &st); +int _real_rmdir(const char* pathname) { + return REAL(rmdir)(pathname); +} +int _real_write(int fd, const void *buf, size_t count, size_t *nwrote) { + return REAL(write)(fd, buf, count, nwrote); } + void kernel_wrap_init() { static bool wrapped = false; @@ -323,6 +420,9 @@ void kernel_wrap_init() { DO_WRAP(seek); DO_WRAP(stat); DO_WRAP(write); + DO_WRAP(mmap); + DO_WRAP(munmap); + DO_WRAP(open_resource); } } diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_wrap_newlib.cc b/native_client_sdk/src/libraries/nacl_io/kernel_wrap_newlib.cc index f0c410e..37a58e5 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_wrap_newlib.cc +++ b/native_client_sdk/src/libraries/nacl_io/kernel_wrap_newlib.cc @@ -13,6 +13,7 @@ #include <dirent.h> #include <errno.h> #include <irt.h> +#include <sys/mman.h> #include <sys/stat.h> #include "nacl_io/kernel_intercept.h" @@ -32,6 +33,7 @@ EXTERN_C_BEGIN DECLARE_STRUCT(fdio) DECLARE_STRUCT(filename) +DECLARE_STRUCT(memory) DECLARE(fdio, close) DECLARE(fdio, dup) @@ -43,6 +45,8 @@ DECLARE(fdio, seek) DECLARE(fdio, write) DECLARE(filename, open) DECLARE(filename, stat) +DECLARE(memory, mmap) +DECLARE(memory, munmap) int access(const char* path, int amode) { @@ -107,11 +111,27 @@ int mkdir(const char* path, mode_t mode) { return ki_mkdir(path, mode); } +int WRAP(mmap)(void** addr, size_t length, int prot, int flags, int fd, + off_t offset) { + if (flags & MAP_ANONYMOUS) + return REAL(mmap)(addr, length, prot, flags, fd, offset); + + *addr = ki_mmap(*addr, length, prot, flags, fd, offset); + return *addr == (void*)-1 ? errno : 0; +} + int mount(const char* source, const char* target, const char* filesystemtype, unsigned long mountflags, const void* data) { return ki_mount(source, target, filesystemtype, mountflags, data); } +int WRAP(munmap)(void* addr, size_t length) { + // Always let the real munmap run on the address range. It is not an error if + // there are no mapped pages in that range. + ki_munmap(addr, length); + return REAL(munmap)(addr, length); +} + int WRAP(open)(const char* pathname, int oflag, mode_t cmode, int* newfd) { *newfd = ki_open(pathname, oflag); return (*newfd < 0) ? errno : 0; @@ -164,18 +184,59 @@ int WRAP(write)(int fd, const void *buf, size_t count, size_t *nwrote) { return (signed_nwrote < 0) ? errno : 0; } -int _real_write(int fd, const void *buf, size_t count, size_t *nwrote) { - return REAL(write)(fd, buf, count, nwrote); + +// "real" functions, i.e. the unwrapped original functions. + +int _real_close(int fd) { + return REAL(close)(fd); +} + +int _real_fstat(int fd, struct stat *buf) { + return REAL(fstat)(fd, buf); +} + +int _real_getdents(int fd, dirent* nacl_buf, size_t nacl_count, size_t *nread) { + return REAL(getdents)(fd, nacl_buf, nacl_count, nread); +} + +int _real_lseek(int fd, off_t offset, int whence, off_t* new_offset) { + return REAL(seek)(fd, offset, whence, new_offset); +} + +int _real_mkdir(const char* pathname, mode_t mode) { + return ENOSYS; +} + +int _real_mmap(void** addr, size_t length, int prot, int flags, int fd, + off_t offset) { + return REAL(mmap)(addr, length, prot, flags, fd, offset); +} + +int _real_munmap(void* addr, size_t length) { + return REAL(munmap)(addr, length); +} + +int _real_open(const char* pathname, int oflag, mode_t cmode, int* newfd) { + return REAL(open)(pathname, oflag, cmode, newfd); +} + +int _real_open_resource(const char* file, int* fd) { + return ENOSYS; } int _real_read(int fd, void *buf, size_t count, size_t *nread) { return REAL(read)(fd, buf, count, nread); } -int _real_fstat(int fd, struct stat *buf) { - return REAL(fstat)(fd, buf); +int _real_rmdir(const char* pathname) { + return ENOSYS; +} + +int _real_write(int fd, const void *buf, size_t count, size_t *nwrote) { + return REAL(write)(fd, buf, count, nwrote); } + void kernel_wrap_init() { static bool wrapped = false; @@ -191,6 +252,8 @@ void kernel_wrap_init() { DO_WRAP(fdio, write); DO_WRAP(filename, open); DO_WRAP(filename, stat); + DO_WRAP(memory, mmap); + DO_WRAP(memory, munmap); } } diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_wrap_real.h b/native_client_sdk/src/libraries/nacl_io/kernel_wrap_real.h new file mode 100644 index 0000000..7d7dd41 --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/kernel_wrap_real.h @@ -0,0 +1,29 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#ifndef LIBRARIES_NACL_IO_KERNEL_WRAP_REAL_H_ +#define LIBRARIES_NACL_IO_KERNEL_WRAP_REAL_H_ + +#include "nacl_io/ostypes.h" +#include "utils/macros.h" + +EXTERN_C_BEGIN + +int _real_close(int fd); +int _real_fstat(int fd, struct stat *buf); +int _real_getdents(int fd, void* nacl_buf, size_t nacl_count, size_t *nread); +int _real_lseek(int fd, off_t offset, int whence, off_t* new_offset); +int _real_mkdir(const char* pathname, mode_t mode); +int _real_mmap(void** addr, size_t length, int prot, int flags, int fd, + off_t offset); +int _real_munmap(void* addr, size_t length); +int _real_open(const char* pathname, int oflag, mode_t cmode, int* newfd); +int _real_open_resource(const char* file, int* fd); +int _real_read(int fd, void *buf, size_t count, size_t *nread); +int _real_rmdir(const char* pathname); +int _real_write(int fd, const void *buf, size_t count, size_t *nwrote); + +EXTERN_C_END + +#endif // LIBRARIES_NACL_IO_KERNEL_WRAP_REAL_H_ diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_wrap_win.cc b/native_client_sdk/src/libraries/nacl_io/kernel_wrap_win.cc index dd8f1ed..356bab7 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_wrap_win.cc +++ b/native_client_sdk/src/libraries/nacl_io/kernel_wrap_win.cc @@ -229,17 +229,58 @@ int _write(int fd, const void* buf, size_t nbyte) { return ki_write(fd, buf, nbyte); } -int _real_write(int fd, const void *buf, size_t count, size_t *nwrote) { - *nwrote = count; + +// "real" functions, i.e. the unwrapped original functions. On Windows we don't +// wrap, so the real functions aren't accessible. In most cases, we just fail. + +int _real_close(int fd) { + return ENOSYS; +} + +int _real_fstat(int fd, struct stat *buf) { return 0; } +int _real_getdents(int fd, void* nacl_buf, size_t nacl_count, size_t *nread) { + return ENOSYS; +} + +int _real_lseek(int fd, off_t offset, int whence, off_t* new_offset) { + return ENOSYS; +} + +int _real_mkdir(const char* pathname, mode_t mode) { + return ENOSYS; +} + +int _real_mmap(void** addr, size_t length, int prot, int flags, int fd, + off_t offset) { + return ENOSYS; +} + +int _real_munmap(void* addr, size_t length) { + return ENOSYS; +} + +int _real_open(const char* pathname, int oflag, mode_t cmode, int* newfd) { + return ENOSYS; +} + +int _real_open_resource(const char* file, int* fd) { + return ENOSYS; +} + int _real_read(int fd, void *buf, size_t count, size_t *nread) { *nread = count; return 0; } -int _real_fstat(int fd, struct stat *buf) { +int _real_rmdir(const char* pathname) { + return ENOSYS; +} + +int _real_write(int fd, const void *buf, size_t count, size_t *nwrote) { + *nwrote = count; return 0; } diff --git a/native_client_sdk/src/libraries/nacl_io/library.dsc b/native_client_sdk/src/libraries/nacl_io/library.dsc index 6c7015b..d4749b2 100644 --- a/native_client_sdk/src/libraries/nacl_io/library.dsc +++ b/native_client_sdk/src/libraries/nacl_io/library.dsc @@ -28,6 +28,7 @@ "mount_node_dir.cc", "mount_node_html5fs.cc", "mount_node_mem.cc", + "mount_passthrough.cc", "nacl_io.cc", "path.cc", "pepper_interface.cc", @@ -44,6 +45,7 @@ "kernel_object.h", "kernel_proxy.h", "kernel_wrap.h", + "kernel_wrap_real.h", "mount.h", "mount_dev.h", "mount_html5fs.h", @@ -53,9 +55,11 @@ "mount_node.h", "mount_node_html5fs.h", "mount_node_mem.h", + "mount_passthrough.h", "nacl_io.h", "osdirent.h", "osinttypes.h", + "osmman.h", "osstat.h", "ostypes.h", "path.h", diff --git a/native_client_sdk/src/libraries/nacl_io/mount.h b/native_client_sdk/src/libraries/nacl_io/mount.h index dfc2e72..622dae0 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount.h +++ b/native_client_sdk/src/libraries/nacl_io/mount.h @@ -47,6 +47,10 @@ class Mount : public RefObject { // MountNode is created with a ref count of 1. virtual MountNode *Open(const Path& path, int o_flags) = 0; + // OpenResource is only used to read files from the NaCl NMF file. No mount + // except MountPassthrough should implement it. + virtual MountNode *OpenResource(const Path& path) { return NULL; } + // Unlink, Mkdir, Rmdir will affect the both the RefCount // and the nlink number in the stat object. virtual int Unlink(const Path& path) = 0; diff --git a/native_client_sdk/src/libraries/nacl_io/mount_dev.cc b/native_client_sdk/src/libraries/nacl_io/mount_dev.cc index 4fd3dba..83d2e55 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_dev.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_dev.cc @@ -9,6 +9,7 @@ #include <errno.h> #include <fcntl.h> #include <string.h> +#include "nacl_io/kernel_wrap_real.h" #include "nacl_io/mount_dev.h" #include "nacl_io/mount_node.h" #include "nacl_io/mount_node_dir.h" @@ -22,13 +23,6 @@ #endif -extern "C" { -int _real_write(int fd, const void *buf, size_t count, size_t *nwrote); -int _real_read(int fd, void *buf, size_t count, size_t *nread); -int _real_fstat(int fd, struct stat *buf); -}; - - namespace { void ReleaseAndNullNode(MountNode** node) { diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node.cc b/native_client_sdk/src/libraries/nacl_io/mount_node.cc index 47c142c..d31b99f 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_node.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_node.cc @@ -10,7 +10,9 @@ #include <sys/stat.h> #include <string> +#include "nacl_io/kernel_wrap_real.h" #include "nacl_io/mount.h" +#include "nacl_io/osmman.h" #include "utils/auto_lock.h" static const int USR_ID = 1001; @@ -76,6 +78,33 @@ int MountNode::Write(size_t offs, const void* buf, size_t count) { return -1; } +void* MountNode::MMap(void* addr, size_t length, int prot, int flags, + size_t offset) { + // This default mmap support is just enough to make dlopen work. + // This implementation just reads from the mount into the mmap'd memory area. + void* new_addr = addr; + int err = _real_mmap(&new_addr, length, prot | PROT_WRITE, flags | + MAP_ANONYMOUS, -1, 0); + if (addr == MAP_FAILED) { + _real_munmap(addr, length); + errno = err; + return MAP_FAILED; + } + + ssize_t cnt = Read(offset, addr, length); + if (cnt == -1) { + _real_munmap(addr, length); + errno = ENOSYS; + return MAP_FAILED; + } + + return new_addr; +} + +int MountNode::Munmap(void* addr, size_t length) { + return _real_munmap(addr, length); +} + int MountNode::GetLinks() { return stat_.st_nlink; } diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node.h b/native_client_sdk/src/libraries/nacl_io/mount_node.h index aedc093..ced95b2 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_node.h +++ b/native_client_sdk/src/libraries/nacl_io/mount_node.h @@ -35,6 +35,9 @@ class MountNode : public RefObject { virtual int Read(size_t offs, void* buf, size_t count); virtual int Truncate(size_t size); virtual int Write(size_t offs, const void* buf, size_t count); + virtual void* MMap(void* addr, size_t length, int prot, int flags, + size_t offset); + virtual int Munmap(void* addr, size_t length); virtual int GetLinks(); virtual int GetMode(); diff --git a/native_client_sdk/src/libraries/nacl_io/mount_passthrough.cc b/native_client_sdk/src/libraries/nacl_io/mount_passthrough.cc new file mode 100644 index 0000000..7cb75b1 --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/mount_passthrough.cc @@ -0,0 +1,174 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include "nacl_io/mount_passthrough.h" +#include <errno.h> +#include "nacl_io/kernel_wrap_real.h" + +class MountNodePassthrough : public MountNode { + public: + explicit MountNodePassthrough(Mount* mount, int real_fd) + : MountNode(mount), + real_fd_(real_fd) { + } + + protected: + virtual bool Init(int flags) { + return true; + } + + virtual void Destroy() { + if (real_fd_) + _real_close(real_fd_); + real_fd_ = 0; + } + + public: + // Normal read/write operations on a file + virtual int Read(size_t offs, void* buf, size_t count) { + off_t new_offset; + int err = _real_lseek(real_fd_, offs, 0, &new_offset); + if (err) { + errno = err; + return -1; + } + + size_t nread; + err = _real_read(real_fd_, buf, count, &nread); + if (err) { + errno = err; + return -1; + } + + return static_cast<int>(nread); + } + + virtual int Write(size_t offs, const void* buf, size_t count) { + off_t new_offset; + int err = _real_lseek(real_fd_, offs, 0, &new_offset); + if (err) { + errno = err; + return -1; + } + + size_t nwrote; + err = _real_write(real_fd_, buf, count, &nwrote); + if (err) { + errno = err; + return -1; + } + + return static_cast<int>(nwrote); + } + + virtual int Truncate(size_t size) { + // TODO(binji): what to do here? + return -1; + } + + virtual int GetDents(size_t offs, struct dirent* pdir, size_t count) { + size_t nread; + int err = _real_getdents(real_fd_, pdir, count, &nread); + if (err) { + errno = err; + return -1; + } + + return nread; + } + + virtual int GetStat(struct stat* stat) { + int err = _real_fstat(real_fd_, stat); + if (err) { + errno = err; + return -1; + } + + return 0; + } + + void* MMap(void* addr, size_t length, int prot, int flags, size_t offset) { + void* new_addr = addr; + int err = _real_mmap(&new_addr, length, prot, flags, real_fd_, offset); + if (err) { + errno = err; + return (void*)-1; + } + + return new_addr; + } + + private: + friend class MountPassthrough; + + int real_fd_; +}; + +MountPassthrough::MountPassthrough() { +} + +bool MountPassthrough::Init(int dev, StringMap_t& args, + PepperInterface* ppapi) { + Mount::Init(dev, args, ppapi); + return true; +} + +void MountPassthrough::Destroy() { +} + +MountNode *MountPassthrough::Open(const Path& path, int mode) { + int real_fd; + int err = _real_open(path.Join().c_str(), mode, 0666, &real_fd); + if (err) { + errno = err; + return NULL; + } + + MountNodePassthrough* node = new MountNodePassthrough(this, real_fd); + return node; +} + +MountNode *MountPassthrough::OpenResource(const Path& path) { + int real_fd; + int err = _real_open_resource(path.Join().c_str(), &real_fd); + if (err) { + errno = err; + return NULL; + } + + MountNodePassthrough* node = new MountNodePassthrough(this, real_fd); + return node; +} + +int MountPassthrough::Unlink(const Path& path) { + // Not implemented by NaCl. + errno = ENOSYS; + return -1; +} + +int MountPassthrough::Mkdir(const Path& path, int perm) { + int err = _real_mkdir(path.Join().c_str(), perm); + if (err) { + errno = err; + return -1; + } + + return 0; +} + +int MountPassthrough::Rmdir(const Path& path) { + int err = _real_rmdir(path.Join().c_str()); + if (err) { + errno = err; + return -1; + } + + return 0; +} + +int MountPassthrough::Remove(const Path& path) { + // Not implemented by NaCl. + errno = ENOSYS; + return -1; +} diff --git a/native_client_sdk/src/libraries/nacl_io/mount_passthrough.h b/native_client_sdk/src/libraries/nacl_io/mount_passthrough.h new file mode 100644 index 0000000..00a0eec --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/mount_passthrough.h @@ -0,0 +1,30 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#ifndef LIBRARIES_NACL_IO_MOUNT_PASSTHROUGH_H_ +#define LIBRARIES_NACL_IO_MOUNT_PASSTHROUGH_H_ + +#include "nacl_io/mount.h" + +class MountPassthrough : public Mount { + protected: + MountPassthrough(); + + virtual bool Init(int dev, StringMap_t& args, PepperInterface* ppapi); + virtual void Destroy(); + + public: + virtual MountNode *Open(const Path& path, int mode); + virtual MountNode *OpenResource(const Path& path); + virtual int Unlink(const Path& path); + virtual int Mkdir(const Path& path, int perm); + virtual int Rmdir(const Path& path); + virtual int Remove(const Path& path); + +private: + friend class Mount; + DISALLOW_COPY_AND_ASSIGN(MountPassthrough); +}; + +#endif // LIBRARIES_NACL_IO_MOUNT_PASSTHROUGH_H_ diff --git a/native_client_sdk/src/libraries/nacl_io/nacl_io.h b/native_client_sdk/src/libraries/nacl_io/nacl_io.h index 3a94057..f934353 100644 --- a/native_client_sdk/src/libraries/nacl_io/nacl_io.h +++ b/native_client_sdk/src/libraries/nacl_io/nacl_io.h @@ -39,7 +39,7 @@ void nacl_io_init(); * pp::Module::Get()->get_browser_interface() */ void nacl_io_init_ppapi(PP_Instance instance, - PPB_GetInterface get_interface); + PPB_GetInterface get_interface); /** Mount a new filesystem type. @@ -94,6 +94,12 @@ void nacl_io_init_ppapi(PP_Instance instance, * All other key/value pairs are assumed to be headers to use with * HTTP requests. * + * "passthroughfs": A filesystem that passes all requests through to the + * underlying NaCL calls. The primary use of this filesystem + * is to allow reading NMF resources. + * source: Unused. + * data: Unused. + * * * @param[in] source Depends on the filesystem type. See above. * @param[in] target The absolute path to mount the filesystem. diff --git a/native_client_sdk/src/libraries/nacl_io/osmman.h b/native_client_sdk/src/libraries/nacl_io/osmman.h new file mode 100644 index 0000000..a652a09 --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/osmman.h @@ -0,0 +1,27 @@ +/* Copyright (c) 2013 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#ifndef LIBRARIES_NACL_IO_OSMMAN_H +#define LIBRARIES_NACL_IO_OSMMAN_H + +#if defined(WIN32) + +#define PROT_READ 0x1 +#define PROT_WRITE 0x2 +#define PROT_EXEC 0x4 +#define PROT_NONE 0x0 + +#define MAP_SHARED 0x01 +#define MAP_PRIVATE 0x02 +#define MAP_FIXED 0x10 +#define MAP_ANONYMOUS 0x20 +#define MAP_FAILED (void*)-1 + +#else + +#include <sys/mman.h> + +#endif + +#endif // LIBRARIES_NACL_IO_OSMMAN_H diff --git a/native_client_sdk/src/libraries/nacl_io_test/kernel_proxy_mock.h b/native_client_sdk/src/libraries/nacl_io_test/kernel_proxy_mock.h index c2f7dc3..45caa4c 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/kernel_proxy_mock.h +++ b/native_client_sdk/src/libraries/nacl_io_test/kernel_proxy_mock.h @@ -43,6 +43,8 @@ class KernelProxyMock : public KernelProxy { MOCK_METHOD3(write, ssize_t(int, const void*, size_t)); MOCK_METHOD2(link, int(const char*, const char*)); MOCK_METHOD2(symlink, int(const char*, const char*)); + MOCK_METHOD6(mmap, void*(void*, size_t, int, int, int, size_t)); + MOCK_METHOD1(open_resource, int(const char*)); }; #endif // LIBRARIES_NACL_IO_TEST_KERNEL_PROXY_MOCK_H_ diff --git a/native_client_sdk/src/libraries/nacl_io_test/kernel_proxy_test.cc b/native_client_sdk/src/libraries/nacl_io_test/kernel_proxy_test.cc index f1e1e81..3c70ec7 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/kernel_proxy_test.cc +++ b/native_client_sdk/src/libraries/nacl_io_test/kernel_proxy_test.cc @@ -16,6 +16,7 @@ #include "nacl_io/kernel_proxy.h" #include "nacl_io/mount.h" #include "nacl_io/mount_mem.h" +#include "nacl_io/osmman.h" #include "nacl_io/path.h" #include "gtest/gtest.h" @@ -26,6 +27,9 @@ class KernelProxyTest : public ::testing::Test { KernelProxyTest() : kp_(new KernelProxy) { ki_init(kp_); + // Unmount the passthrough FS and mount a memfs. + EXPECT_EQ(0, kp_->umount("/")); + EXPECT_EQ(0, kp_->mount("", "/", "memfs", 0, NULL)); } ~KernelProxyTest() { @@ -229,8 +233,6 @@ class KernelProxyMountTest : public ::testing::Test { KernelProxy* kp_; }; - - TEST_F(KernelProxyMountTest, MountInit) { int res1 = ki_mount("/", "/mnt1", "initfs", 0, "false,foo=bar"); @@ -242,3 +244,112 @@ TEST_F(KernelProxyMountTest, MountInit) { EXPECT_NE(-1, res2); EXPECT_EQ("y", g_StringMap["x"]); } + + +namespace { + +int g_MMapCount = 0; + +class MountNodeMockMMap : public MountNode { + public: + MountNodeMockMMap(Mount* mount) + : MountNode(mount), + node_mmap_count_(0) { + Init(0); + } + + virtual void* MMap(void* addr, size_t length, int prot, int flags, + size_t offset) { + node_mmap_count_++; + switch (g_MMapCount++) { + case 0: return reinterpret_cast<void*>(0x1000); + case 1: return reinterpret_cast<void*>(0x2000); + case 2: return reinterpret_cast<void*>(0x3000); + default: return MAP_FAILED; + } + } + + virtual int Munmap(void* addr, size_t length) { + EXPECT_GT(node_mmap_count_, 0); + --node_mmap_count_; + --g_MMapCount; + return 0; + } + + private: + int node_mmap_count_; +}; + +class MountMockMMap : public Mount { + public: + virtual MountNode* Open(const Path& path, int mode) { + MountNodeMockMMap* node = new MountNodeMockMMap(this); + return node; + } + + virtual MountNode* OpenResource(const Path& path) { return NULL; } + virtual int Unlink(const Path& path) { return -1; } + virtual int Mkdir(const Path& path, int permissions) { return -1; } + virtual int Rmdir(const Path& path) { return -1; } + virtual int Remove(const Path& path) { return -1; } +}; + +class KernelProxyMockMMap : public KernelProxy { + virtual void Init(PepperInterface* ppapi) { + KernelProxy::Init(NULL); + factories_["mmapfs"] = MountMockInit::Create<MountMockMMap>; + } +}; + +class KernelProxyMMapTest : public ::testing::Test { + public: + KernelProxyMMapTest() + : kp_(new KernelProxyMockMMap) { + ki_init(kp_); + } + + ~KernelProxyMMapTest() { + ki_uninit(); + delete kp_; + } + + private: + KernelProxy* kp_; +}; + +} // namespace + +TEST_F(KernelProxyMMapTest, MMap) { + EXPECT_EQ(0, ki_umount("/")); + EXPECT_EQ(0, ki_mount("", "/", "mmapfs", 0, NULL)); + int fd = ki_open("/file", O_RDWR | O_CREAT); + EXPECT_NE(-1, fd); + + void* addr1 = ki_mmap(NULL, 0x800, PROT_READ, MAP_PRIVATE, fd, 0); + EXPECT_EQ(reinterpret_cast<void*>(0x1000), addr1); + EXPECT_EQ(1, g_MMapCount); + + void* addr2 = ki_mmap(NULL, 0x800, PROT_READ, MAP_PRIVATE, fd, 0); + EXPECT_EQ(reinterpret_cast<void*>(0x2000), addr2); + EXPECT_EQ(2, g_MMapCount); + + void* addr3 = ki_mmap(NULL, 0x800, PROT_READ, MAP_PRIVATE, fd, 0); + EXPECT_EQ(reinterpret_cast<void*>(0x3000), addr3); + EXPECT_EQ(3, g_MMapCount); + + // Unmap 0x2000 and 0x3000. + EXPECT_EQ(0, ki_munmap(reinterpret_cast<void*>(0x2400), 0x1000)); + EXPECT_EQ(1, g_MMapCount); + + ki_close(fd); + EXPECT_EQ(1, g_MMapCount); // Closing the file doesn't unmap it. + + // Open a new file, should have the same fd. + int fd2 = ki_open("/foo", O_RDWR | O_CREAT); + EXPECT_EQ(fd, fd2); + + // Unmap 0x1000. This should unmap the old file, not the new file with the + // same fd. + EXPECT_EQ(0, ki_munmap(reinterpret_cast<void*>(0x1000), 0x800)); + EXPECT_EQ(0, g_MMapCount); +} diff --git a/native_client_sdk/src/libraries/nacl_io_test/kernel_wrap_test.cc b/native_client_sdk/src/libraries/nacl_io_test/kernel_wrap_test.cc index df7a784..0cf3bcb 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/kernel_wrap_test.cc +++ b/native_client_sdk/src/libraries/nacl_io_test/kernel_wrap_test.cc @@ -11,8 +11,9 @@ #include "nacl_io/kernel_wrap.h" #include "kernel_proxy_mock.h" -using ::testing::StrEq; using ::testing::_; +using ::testing::Return; +using ::testing::StrEq; namespace { @@ -75,6 +76,12 @@ void MakeDummyStatbuf(struct stat* statbuf) { class KernelWrapTest : public ::testing::Test { public: KernelWrapTest() { + // Initializing the KernelProxy opens stdin/stdout/stderr. + EXPECT_CALL(mock, open(_, _)) + .WillOnce(Return(0)) + .WillOnce(Return(1)) + .WillOnce(Return(2)); + ki_init(&mock); } diff --git a/native_client_sdk/src/tools/nacl_gcc.mk b/native_client_sdk/src/tools/nacl_gcc.mk index bde9115..59f2002 100644 --- a/native_client_sdk/src/tools/nacl_gcc.mk +++ b/native_client_sdk/src/tools/nacl_gcc.mk @@ -92,6 +92,9 @@ endef # # $1 = Target Name # $2 = List of Sources +# $3 = List of LIBS +# $4 = List of DEPS +# $5 = 1 => Don't add to NMF. # # GLIBC_REMAP:= @@ -104,9 +107,13 @@ NMF_TARGETS+=$(OUTDIR)/$(1)_x86_64.so $(OUTDIR)/$(1)_x86_64.so : $(foreach src,$(2),$$(OUTDIR)/$(basename $(src))_x86_64.o) $(4) $(call LOG,LINK,$$@,$(X86_32_LINK) -o $$@ $$(filter-out $(4),$$^) -shared -m64 $(LD_X86_64) $$(LD_FLAGS) $(foreach lib,$(3),-l$(lib))) +ifneq (1,$(5)) GLIBC_SO_LIST+=$(OUTDIR)/$(1)_x86_32.so $(OUTDIR)/$(1)_x86_64.so GLIBC_REMAP+=-n $(1)_x86_32.so,$(1).so GLIBC_REMAP+=-n $(1)_x86_64.so,$(1).so +else +all: $(OUTDIR)/$(1)_x86_32.so $(OUTDIR)/$(1)_x86_64.so +endif endef |