michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=8 sts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include "mozilla/Assertions.h" michael@0: #include "mozilla/NullPtr.h" michael@0: michael@0: #include "PlatformMacros.h" michael@0: #include "AutoObjectMapper.h" michael@0: michael@0: #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK) michael@0: # include michael@0: # include "mozilla/Types.h" michael@0: // FIXME move these out of mozglue/linker/ElfLoader.h into their michael@0: // own header, so as to avoid conflicts arising from two definitions michael@0: // of Array michael@0: extern "C" { michael@0: MFBT_API size_t michael@0: __dl_get_mappable_length(void *handle); michael@0: MFBT_API void * michael@0: __dl_mmap(void *handle, void *addr, size_t length, off_t offset); michael@0: MFBT_API void michael@0: __dl_munmap(void *handle, void *addr, size_t length); michael@0: } michael@0: // The following are for get_installation_lib_dir() michael@0: # include "nsString.h" michael@0: # include "nsDirectoryServiceUtils.h" michael@0: # include "nsDirectoryServiceDefs.h" michael@0: #endif michael@0: michael@0: michael@0: // A helper function for creating failure error messages in michael@0: // AutoObjectMapper*::Map. michael@0: static void michael@0: failedToMessage(void(*aLog)(const char*), michael@0: const char* aHowFailed, std::string aFileName) michael@0: { michael@0: char buf[300]; michael@0: snprintf(buf, sizeof(buf), "AutoObjectMapper::Map: Failed to %s \'%s\'", michael@0: aHowFailed, aFileName.c_str()); michael@0: buf[sizeof(buf)-1] = 0; michael@0: aLog(buf); michael@0: } michael@0: michael@0: michael@0: AutoObjectMapperPOSIX::AutoObjectMapperPOSIX(void(*aLog)(const char*)) michael@0: : mImage(nullptr) michael@0: , mSize(0) michael@0: , mLog(aLog) michael@0: , mIsMapped(false) michael@0: {} michael@0: michael@0: AutoObjectMapperPOSIX::~AutoObjectMapperPOSIX() { michael@0: if (!mIsMapped) { michael@0: // There's nothing to do. michael@0: MOZ_ASSERT(!mImage); michael@0: MOZ_ASSERT(mSize == 0); michael@0: return; michael@0: } michael@0: MOZ_ASSERT(mSize > 0); michael@0: // The following assertion doesn't necessarily have to be true, michael@0: // but we assume (reasonably enough) that no mmap facility would michael@0: // be crazy enough to map anything at page zero. michael@0: MOZ_ASSERT(mImage); michael@0: munmap(mImage, mSize); michael@0: } michael@0: michael@0: bool AutoObjectMapperPOSIX::Map(/*OUT*/void** start, /*OUT*/size_t* length, michael@0: std::string fileName) michael@0: { michael@0: MOZ_ASSERT(!mIsMapped); michael@0: michael@0: int fd = open(fileName.c_str(), O_RDONLY); michael@0: if (fd == -1) { michael@0: failedToMessage(mLog, "open", fileName); michael@0: return false; michael@0: } michael@0: michael@0: struct stat st; michael@0: int err = fstat(fd, &st); michael@0: size_t sz = (err == 0) ? st.st_size : 0; michael@0: if (err != 0 || sz == 0) { michael@0: failedToMessage(mLog, "fstat", fileName); michael@0: close(fd); michael@0: return false; michael@0: } michael@0: michael@0: void* image = mmap(nullptr, sz, PROT_READ, MAP_SHARED, fd, 0); michael@0: if (image == MAP_FAILED) { michael@0: failedToMessage(mLog, "mmap", fileName); michael@0: close(fd); michael@0: return false; michael@0: } michael@0: michael@0: close(fd); michael@0: mIsMapped = true; michael@0: mImage = *start = image; michael@0: mSize = *length = sz; michael@0: return true; michael@0: } michael@0: michael@0: michael@0: #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK) michael@0: // A helper function for AutoObjectMapperFaultyLib::Map. Finds out michael@0: // where the installation's lib directory is, since we'll have to look michael@0: // in there to get hold of libmozglue.so. Returned C string is heap michael@0: // allocated and the caller must deallocate it. michael@0: static char* michael@0: get_installation_lib_dir() michael@0: { michael@0: nsCOMPtr michael@0: directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID)); michael@0: if (!directoryService) { michael@0: return nullptr; michael@0: } michael@0: nsCOMPtr greDir; michael@0: nsresult rv = directoryService->Get(NS_GRE_DIR, NS_GET_IID(nsIFile), michael@0: getter_AddRefs(greDir)); michael@0: if (NS_FAILED(rv)) return nullptr; michael@0: nsCString path; michael@0: rv = greDir->GetNativePath(path); michael@0: if (NS_FAILED(rv)) { michael@0: return nullptr; michael@0: } michael@0: return strdup(path.get()); michael@0: } michael@0: michael@0: AutoObjectMapperFaultyLib::AutoObjectMapperFaultyLib(void(*aLog)(const char*)) michael@0: : AutoObjectMapperPOSIX(aLog) michael@0: , mHdl(nullptr) michael@0: {} michael@0: michael@0: AutoObjectMapperFaultyLib::~AutoObjectMapperFaultyLib() { michael@0: if (mHdl) { michael@0: // We've got an object mapped by faulty.lib. Unmap it via faulty.lib. michael@0: MOZ_ASSERT(mSize > 0); michael@0: // Assert on the basis that no valid mapping would start at page zero. michael@0: MOZ_ASSERT(mImage); michael@0: __dl_munmap(mHdl, mImage, mSize); michael@0: dlclose(mHdl); michael@0: // Stop assertions in ~AutoObjectMapperPOSIX from failing. michael@0: mImage = nullptr; michael@0: mSize = 0; michael@0: } michael@0: // At this point the parent class destructor, ~AutoObjectMapperPOSIX, michael@0: // gets called. If that has something mapped in the normal way, it michael@0: // will unmap it in the normal way. Unfortunately there's no michael@0: // obvious way to enforce the requirement that the object is mapped michael@0: // either by faulty.lib or by the parent class, but not by both. michael@0: } michael@0: michael@0: bool AutoObjectMapperFaultyLib::Map(/*OUT*/void** start, /*OUT*/size_t* length, michael@0: std::string fileName) michael@0: { michael@0: MOZ_ASSERT(!mHdl); michael@0: michael@0: if (fileName == "libmozglue.so") { michael@0: michael@0: // Do (2) in the comment above. michael@0: char* libdir = get_installation_lib_dir(); michael@0: if (libdir) { michael@0: fileName = std::string(libdir) + "/lib/" + fileName; michael@0: free(libdir); michael@0: } michael@0: // Hand the problem off to the standard mapper. michael@0: return AutoObjectMapperPOSIX::Map(start, length, fileName); michael@0: michael@0: } else { michael@0: michael@0: // Do cases (1) and (3) in the comment above. We have to michael@0: // grapple with faulty.lib directly. michael@0: void* hdl = dlopen(fileName.c_str(), RTLD_GLOBAL | RTLD_LAZY); michael@0: if (!hdl) { michael@0: failedToMessage(mLog, "get handle for ELF file", fileName); michael@0: return false; michael@0: } michael@0: michael@0: size_t sz = __dl_get_mappable_length(hdl); michael@0: if (sz == 0) { michael@0: dlclose(hdl); michael@0: failedToMessage(mLog, "get size for ELF file", fileName); michael@0: return false; michael@0: } michael@0: michael@0: void* image = __dl_mmap(hdl, nullptr, sz, 0); michael@0: if (image == MAP_FAILED) { michael@0: dlclose(hdl); michael@0: failedToMessage(mLog, "mmap ELF file", fileName); michael@0: return false; michael@0: } michael@0: michael@0: mHdl = hdl; michael@0: mImage = *start = image; michael@0: mSize = *length = sz; michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: #endif // defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)