diff options
Diffstat (limited to '')
-rw-r--r-- | xpcom/glue/FileUtils.cpp | 579 | ||||
-rw-r--r-- | xpcom/glue/FileUtils.h | 165 | ||||
-rw-r--r-- | xpcom/glue/MemUtils.cpp | 67 | ||||
-rw-r--r-- | xpcom/glue/MemUtils.h | 20 | ||||
-rw-r--r-- | xpcom/glue/XREAppData.cpp | 55 | ||||
-rw-r--r-- | xpcom/glue/moz.build | 12 | ||||
-rw-r--r-- | xpcom/glue/objs.mozbuild | 22 | ||||
-rw-r--r-- | xpcom/glue/standalone/moz.build | 36 | ||||
-rw-r--r-- | xpcom/glue/standalone/nsXPCOMGlue.cpp | 419 |
9 files changed, 1375 insertions, 0 deletions
diff --git a/xpcom/glue/FileUtils.cpp b/xpcom/glue/FileUtils.cpp new file mode 100644 index 0000000000..537c060bf7 --- /dev/null +++ b/xpcom/glue/FileUtils.cpp @@ -0,0 +1,579 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/FileUtils.h" + +#include "nscore.h" +#include "private/pprio.h" +#include "prmem.h" +#include "mozilla/BaseProfilerMarkers.h" +#include "mozilla/MemUtils.h" + +#if defined(XP_MACOSX) +# include <fcntl.h> +# include <unistd.h> +# include <mach/machine.h> +# include <mach-o/fat.h> +# include <mach-o/loader.h> +# include <sys/mman.h> +# include <sys/stat.h> +# include <limits.h> +#elif defined(XP_UNIX) +# include <fcntl.h> +# include <unistd.h> +# if defined(LINUX) +# include <elf.h> +# endif +# include <sys/types.h> +# include <sys/stat.h> +#elif defined(XP_WIN) +# include <nsWindowsHelpers.h> +# include <mozilla/NativeNt.h> +# include <mozilla/ScopeExit.h> +#endif + +// Functions that are not to be used in standalone glue must be implemented +// within this #if block +#if defined(MOZILLA_INTERNAL_API) + +# include "nsString.h" + +bool mozilla::fallocate(PRFileDesc* aFD, int64_t aLength) { +# if defined(HAVE_POSIX_FALLOCATE) + return posix_fallocate(PR_FileDesc2NativeHandle(aFD), 0, aLength) == 0; +# elif defined(XP_WIN) + int64_t oldpos = PR_Seek64(aFD, 0, PR_SEEK_CUR); + if (oldpos == -1) { + return false; + } + + if (PR_Seek64(aFD, aLength, PR_SEEK_SET) != aLength) { + return false; + } + + bool retval = (0 != SetEndOfFile((HANDLE)PR_FileDesc2NativeHandle(aFD))); + + PR_Seek64(aFD, oldpos, PR_SEEK_SET); + return retval; +# elif defined(XP_MACOSX) + int fd = PR_FileDesc2NativeHandle(aFD); + fstore_t store = {F_ALLOCATECONTIG, F_PEOFPOSMODE, 0, aLength}; + // Try to get a continous chunk of disk space + int ret = fcntl(fd, F_PREALLOCATE, &store); + if (ret == -1) { + // OK, perhaps we are too fragmented, allocate non-continuous + store.fst_flags = F_ALLOCATEALL; + ret = fcntl(fd, F_PREALLOCATE, &store); + if (ret == -1) { + return false; + } + } + return ftruncate(fd, aLength) == 0; +# elif defined(XP_UNIX) + // The following is copied from fcntlSizeHint in sqlite + /* If the OS does not have posix_fallocate(), fake it. First use + ** ftruncate() to set the file size, then write a single byte to + ** the last byte in each block within the extended region. This + ** is the same technique used by glibc to implement posix_fallocate() + ** on systems that do not have a real fallocate() system call. + */ + int64_t oldpos = PR_Seek64(aFD, 0, PR_SEEK_CUR); + if (oldpos == -1) { + return false; + } + + struct stat buf; + int fd = PR_FileDesc2NativeHandle(aFD); + if (fstat(fd, &buf)) { + return false; + } + + if (buf.st_size >= aLength) { + return false; + } + + const int nBlk = buf.st_blksize; + + if (!nBlk) { + return false; + } + + if (ftruncate(fd, aLength)) { + return false; + } + + int nWrite; // Return value from write() + int64_t iWrite = ((buf.st_size + 2 * nBlk - 1) / nBlk) * nBlk - + 1; // Next offset to write to + while (iWrite < aLength) { + nWrite = 0; + if (PR_Seek64(aFD, iWrite, PR_SEEK_SET) == iWrite) { + nWrite = PR_Write(aFD, "", 1); + } + if (nWrite != 1) { + break; + } + iWrite += nBlk; + } + + PR_Seek64(aFD, oldpos, PR_SEEK_SET); + return nWrite == 1; +# else + return false; +# endif +} + +void mozilla::ReadAheadLib(nsIFile* aFile) { +# if defined(XP_WIN) + nsAutoString path; + if (!aFile || NS_FAILED(aFile->GetPath(path))) { + return; + } + ReadAheadLib(path.get()); +# elif defined(LINUX) && !defined(ANDROID) || defined(XP_MACOSX) + nsAutoCString nativePath; + if (!aFile || NS_FAILED(aFile->GetNativePath(nativePath))) { + return; + } + ReadAheadLib(nativePath.get()); +# endif +} + +void mozilla::ReadAheadFile(nsIFile* aFile, const size_t aOffset, + const size_t aCount, mozilla::filedesc_t* aOutFd) { +# if defined(XP_WIN) + nsAutoString path; + if (!aFile || NS_FAILED(aFile->GetPath(path))) { + return; + } + ReadAheadFile(path.get(), aOffset, aCount, aOutFd); +# elif defined(LINUX) && !defined(ANDROID) || defined(XP_MACOSX) + nsAutoCString nativePath; + if (!aFile || NS_FAILED(aFile->GetNativePath(nativePath))) { + return; + } + ReadAheadFile(nativePath.get(), aOffset, aCount, aOutFd); +# endif +} + +mozilla::PathString mozilla::GetLibraryName(mozilla::pathstr_t aDirectory, + const char* aLib) { +# ifdef XP_WIN + nsAutoString fullName; + if (aDirectory) { + fullName.Assign(aDirectory); + fullName.Append('\\'); + } + AppendUTF8toUTF16(MakeStringSpan(aLib), fullName); + if (!strstr(aLib, ".dll")) { + fullName.AppendLiteral(".dll"); + } + return std::move(fullName); +# else + char* temp = PR_GetLibraryName(aDirectory, aLib); + if (!temp) { + return ""_ns; + } + nsAutoCString libname(temp); + PR_FreeLibraryName(temp); + return std::move(libname); +# endif +} + +mozilla::PathString mozilla::GetLibraryFilePathname(mozilla::pathstr_t aName, + PRFuncPtr aAddr) { +# ifdef XP_WIN + HMODULE handle = GetModuleHandleW(char16ptr_t(aName)); + if (!handle) { + return u""_ns; + } + + nsAutoString path; + path.SetLength(MAX_PATH); + DWORD len = GetModuleFileNameW(handle, char16ptr_t(path.BeginWriting()), + path.Length()); + if (!len) { + return u""_ns; + } + + path.SetLength(len); + return std::move(path); +# else + char* temp = PR_GetLibraryFilePathname(aName, aAddr); + if (!temp) { + return ""_ns; + } + nsAutoCString path(temp); + PR_Free(temp); // PR_GetLibraryFilePathname() uses PR_Malloc(). + return std::move(path); +# endif +} + +#endif // defined(MOZILLA_INTERNAL_API) + +#if defined(LINUX) && !defined(ANDROID) + +static const unsigned int bufsize = 4096; + +# ifdef __LP64__ +typedef Elf64_Ehdr Elf_Ehdr; +typedef Elf64_Phdr Elf_Phdr; +static const unsigned char ELFCLASS = ELFCLASS64; +typedef Elf64_Off Elf_Off; +# else +typedef Elf32_Ehdr Elf_Ehdr; +typedef Elf32_Phdr Elf_Phdr; +static const unsigned char ELFCLASS = ELFCLASS32; +typedef Elf32_Off Elf_Off; +# endif + +#elif defined(XP_MACOSX) + +# if defined(__i386__) +static const uint32_t CPU_TYPE = CPU_TYPE_X86; +# elif defined(__x86_64__) +static const uint32_t CPU_TYPE = CPU_TYPE_X86_64; +# elif defined(__ppc__) +static const uint32_t CPU_TYPE = CPU_TYPE_POWERPC; +# elif defined(__ppc64__) +static const uint32_t CPU_TYPE = CPU_TYPE_POWERPC64; +# elif defined(__aarch64__) +static const uint32_t CPU_TYPE = CPU_TYPE_ARM64; +# else +# error Unsupported CPU type +# endif + +# ifdef __LP64__ +# undef LC_SEGMENT +# define LC_SEGMENT LC_SEGMENT_64 +# undef MH_MAGIC +# define MH_MAGIC MH_MAGIC_64 +# define cpu_mach_header mach_header_64 +# define segment_command segment_command_64 +# else +# define cpu_mach_header mach_header +# endif + +class ScopedMMap { + public: + explicit ScopedMMap(const char* aFilePath) : buf(nullptr) { + fd = open(aFilePath, O_RDONLY); + if (fd < 0) { + return; + } + struct stat st; + if (fstat(fd, &st) < 0) { + return; + } + size = st.st_size; + buf = (char*)mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0); + if (buf == MAP_FAILED) { + buf = nullptr; + } + } + ~ScopedMMap() { + if (buf) { + munmap(buf, size); + } + if (fd >= 0) { + close(fd); + } + } + operator char*() { return buf; } + int getFd() { return fd; } + + private: + int fd; + char* buf; + size_t size; +}; +#endif + +void mozilla::ReadAhead(mozilla::filedesc_t aFd, const size_t aOffset, + const size_t aCount) { +#if defined(XP_WIN) + + LARGE_INTEGER fpOriginal; + LARGE_INTEGER fpOffset; +# if defined(HAVE_LONG_LONG) + fpOffset.QuadPart = 0; +# else + fpOffset.u.LowPart = 0; + fpOffset.u.HighPart = 0; +# endif + + // Get the current file pointer so that we can restore it. This isn't + // really necessary other than to provide the same semantics regarding the + // file pointer that other platforms do + if (!SetFilePointerEx(aFd, fpOffset, &fpOriginal, FILE_CURRENT)) { + return; + } + + if (aOffset) { +# if defined(HAVE_LONG_LONG) + fpOffset.QuadPart = static_cast<LONGLONG>(aOffset); +# else + fpOffset.u.LowPart = aOffset; + fpOffset.u.HighPart = 0; +# endif + + if (!SetFilePointerEx(aFd, fpOffset, nullptr, FILE_BEGIN)) { + return; + } + } + + char buf[64 * 1024]; + size_t totalBytesRead = 0; + DWORD dwBytesRead; + // Do dummy reads to trigger kernel-side readhead via + // FILE_FLAG_SEQUENTIAL_SCAN. Abort when underfilling because during testing + // the buffers are read fully A buffer that's not keeping up would imply that + // readahead isn't working right + while (totalBytesRead < aCount && + ReadFile(aFd, buf, sizeof(buf), &dwBytesRead, nullptr) && + dwBytesRead == sizeof(buf)) { + totalBytesRead += dwBytesRead; + } + + // Restore the file pointer + SetFilePointerEx(aFd, fpOriginal, nullptr, FILE_BEGIN); + +#elif defined(LINUX) && !defined(ANDROID) + + readahead(aFd, aOffset, aCount); + +#elif defined(XP_MACOSX) + + struct radvisory ra; + ra.ra_offset = aOffset; + ra.ra_count = aCount; + // The F_RDADVISE fcntl is equivalent to Linux' readahead() system call. + fcntl(aFd, F_RDADVISE, &ra); + +#endif +} + +void mozilla::ReadAheadLib(mozilla::pathstr_t aFilePath) { + if (!aFilePath) { + return; + } + +#ifdef XP_WIN + auto WideToUTF8 = [](const wchar_t* aStr) -> std::string { + std::string s; + // Determine the number of output bytes (including null terminator). + const int numConv = ::WideCharToMultiByte(CP_UTF8, 0, aStr, -1, nullptr, 0, + nullptr, nullptr); + if (numConv == 0) { + return s; + } + s.resize(numConv); + const int numConvd = ::WideCharToMultiByte(CP_UTF8, 0, aStr, -1, s.data(), + numConv, nullptr, nullptr); + if (numConvd != numConv) { + // Error during conversion, remove any temporary data. + s.clear(); + } + return s; + }; +#endif + + AUTO_BASE_PROFILER_MARKER_TEXT("ReadAheadLib", OTHER, {}, +#ifdef XP_WIN + WideToUTF8(aFilePath) +#else + aFilePath +#endif + ); + +#if defined(XP_WIN) + if (!CanPrefetchMemory()) { + ReadAheadFile(aFilePath); + return; + } + nsAutoHandle fd(CreateFileW(aFilePath, GENERIC_READ | GENERIC_EXECUTE, + FILE_SHARE_READ, nullptr, OPEN_EXISTING, + FILE_FLAG_SEQUENTIAL_SCAN, nullptr)); + if (!fd) { + return; + } + + nsAutoHandle mapping(CreateFileMapping( + fd, nullptr, SEC_IMAGE | PAGE_EXECUTE_READ, 0, 0, nullptr)); + if (!mapping) { + return; + } + + PVOID data = MapViewOfFile( + mapping, FILE_MAP_READ | FILE_MAP_EXECUTE | SEC_IMAGE, 0, 0, 0); + if (!data) { + return; + } + auto guard = MakeScopeExit([=]() { UnmapViewOfFile(data); }); + mozilla::nt::PEHeaders headers(data); + Maybe<Span<const uint8_t>> bounds = headers.GetBounds(); + if (!bounds) { + return; + } + + PrefetchMemory((uint8_t*)data, bounds->Length()); + +#elif defined(LINUX) && !defined(ANDROID) + int fd = open(aFilePath, O_RDONLY); + if (fd < 0) { + return; + } + + union { + char buf[bufsize]; + Elf_Ehdr ehdr; + } elf; + // Read ELF header (ehdr) and program header table (phdr). + // We check that the ELF magic is found, that the ELF class matches + // our own, and that the program header table as defined in the ELF + // headers fits in the buffer we read. + if ((read(fd, elf.buf, bufsize) <= 0) || (memcmp(elf.buf, ELFMAG, 4)) || + (elf.ehdr.e_ident[EI_CLASS] != ELFCLASS) || + // Upcast e_phentsize so the multiplication is done in the same precision + // as the subsequent addition, to satisfy static analyzers and avoid + // issues with abnormally large program header tables. + (elf.ehdr.e_phoff + + (static_cast<Elf_Off>(elf.ehdr.e_phentsize) * elf.ehdr.e_phnum) >= + bufsize)) { + close(fd); + return; + } + // The program header table contains segment definitions. One such + // segment type is PT_LOAD, which describes how the dynamic loader + // is going to map the file in memory. We use that information to + // find the biggest offset from the library that will be mapped in + // memory. + Elf_Phdr* phdr = (Elf_Phdr*)&elf.buf[elf.ehdr.e_phoff]; + Elf_Off end = 0; + for (int phnum = elf.ehdr.e_phnum; phnum; phdr++, phnum--) { + if ((phdr->p_type == PT_LOAD) && (end < phdr->p_offset + phdr->p_filesz)) { + end = phdr->p_offset + phdr->p_filesz; + } + } + // Let the kernel read ahead what the dynamic loader is going to + // map in memory soon after. + if (end > 0) { + ReadAhead(fd, 0, end); + } + close(fd); +#elif defined(XP_MACOSX) + ScopedMMap buf(aFilePath); + char* base = buf; + if (!base) { + return; + } + + // An OSX binary might either be a fat (universal) binary or a + // Mach-O binary. A fat binary actually embeds several Mach-O + // binaries. If we have a fat binary, find the offset where the + // Mach-O binary for our CPU type can be found. + struct fat_header* fh = (struct fat_header*)base; + + if (OSSwapBigToHostInt32(fh->magic) == FAT_MAGIC) { + uint32_t nfat_arch = OSSwapBigToHostInt32(fh->nfat_arch); + struct fat_arch* arch = (struct fat_arch*)&buf[sizeof(struct fat_header)]; + for (; nfat_arch; arch++, nfat_arch--) { + if (OSSwapBigToHostInt32(arch->cputype) == CPU_TYPE) { + base += OSSwapBigToHostInt32(arch->offset); + break; + } + } + if (base == buf) { + return; + } + } + + // Check Mach-O magic in the Mach header + struct cpu_mach_header* mh = (struct cpu_mach_header*)base; + if (mh->magic != MH_MAGIC) { + return; + } + + // The Mach header is followed by a sequence of load commands. + // Each command has a header containing the command type and the + // command size. LD_SEGMENT commands describes how the dynamic + // loader is going to map the file in memory. We use that + // information to find the biggest offset from the library that + // will be mapped in memory. + char* cmd = &base[sizeof(struct cpu_mach_header)]; + uint32_t end = 0; + for (uint32_t ncmds = mh->ncmds; ncmds; ncmds--) { + struct segment_command* sh = (struct segment_command*)cmd; + if (sh->cmd != LC_SEGMENT) { + continue; + } + if (end < sh->fileoff + sh->filesize) { + end = sh->fileoff + sh->filesize; + } + cmd += sh->cmdsize; + } + // Let the kernel read ahead what the dynamic loader is going to + // map in memory soon after. + if (end > 0) { + ReadAhead(buf.getFd(), base - buf, end); + } +#endif +} + +void mozilla::ReadAheadFile(mozilla::pathstr_t aFilePath, const size_t aOffset, + const size_t aCount, mozilla::filedesc_t* aOutFd) { +#if defined(XP_WIN) + if (!aFilePath) { + if (aOutFd) { + *aOutFd = INVALID_HANDLE_VALUE; + } + return; + } + HANDLE fd = CreateFileW(aFilePath, GENERIC_READ, FILE_SHARE_READ, nullptr, + OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, nullptr); + if (aOutFd) { + *aOutFd = fd; + } + if (fd == INVALID_HANDLE_VALUE) { + return; + } + ReadAhead(fd, aOffset, aCount); + if (!aOutFd) { + CloseHandle(fd); + } +#elif defined(LINUX) && !defined(ANDROID) || defined(XP_MACOSX) + if (!aFilePath) { + if (aOutFd) { + *aOutFd = -1; + } + return; + } + int fd = open(aFilePath, O_RDONLY); + if (aOutFd) { + *aOutFd = fd; + } + if (fd < 0) { + return; + } + size_t count; + if (aCount == SIZE_MAX) { + struct stat st; + if (fstat(fd, &st) < 0) { + if (!aOutFd) { + close(fd); + } + return; + } + count = st.st_size; + } else { + count = aCount; + } + ReadAhead(fd, aOffset, count); + if (!aOutFd) { + close(fd); + } +#endif +} diff --git a/xpcom/glue/FileUtils.h b/xpcom/glue/FileUtils.h new file mode 100644 index 0000000000..6b322fb839 --- /dev/null +++ b/xpcom/glue/FileUtils.h @@ -0,0 +1,165 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_FileUtils_h +#define mozilla_FileUtils_h + +#include "nscore.h" // nullptr + +#if defined(XP_UNIX) +# include <unistd.h> +#elif defined(XP_WIN) +# include <io.h> +#endif +#include "prio.h" +#include "prlink.h" + +#include "mozilla/Scoped.h" +#include "nsIFile.h" +#include <errno.h> +#include <limits.h> + +namespace mozilla { + +#if defined(XP_WIN) +typedef void* filedesc_t; +typedef const wchar_t* pathstr_t; +#else +typedef int filedesc_t; +typedef const char* pathstr_t; +#endif + +/** + * ScopedCloseFD is a RAII wrapper for POSIX file descriptors + * + * Instances |close()| their fds when they go out of scope. + */ +struct ScopedCloseFDTraits { + typedef int type; + static type empty() { return -1; } + static void release(type aFd) { + if (aFd != -1) { + close(aFd); + } + } +}; +typedef Scoped<ScopedCloseFDTraits> ScopedClose; + +#if defined(MOZILLA_INTERNAL_API) + +/** + * AutoFDClose is a RAII wrapper for PRFileDesc. + * + * Instances |PR_Close| their fds when they go out of scope. + **/ +struct ScopedClosePRFDTraits { + typedef PRFileDesc* type; + static type empty() { return nullptr; } + static void release(type aFd) { + if (aFd) { + PR_Close(aFd); + } + } +}; +typedef Scoped<ScopedClosePRFDTraits> AutoFDClose; + +/* RAII wrapper for FILE descriptors */ +struct ScopedCloseFileTraits { + typedef FILE* type; + static type empty() { return nullptr; } + static void release(type aFile) { + if (aFile) { + fclose(aFile); + } + } +}; +typedef Scoped<ScopedCloseFileTraits> ScopedCloseFile; + +/** + * Fallocate efficiently and continuously allocates files via fallocate-type + * APIs. This is useful for avoiding fragmentation. On sucess the file be padded + * with zeros to grow to aLength. + * + * @param aFD file descriptor. + * @param aLength length of file to grow to. + * @return true on success. + */ +bool fallocate(PRFileDesc* aFD, int64_t aLength); + +/** + * Use readahead to preload shared libraries into the file cache before loading. + * WARNING: This function should not be used without a telemetry field trial + * demonstrating a clear performance improvement! + * + * @param aFile nsIFile representing path to shared library + */ +void ReadAheadLib(nsIFile* aFile); + +/** + * Use readahead to preload a file into the file cache before reading. + * WARNING: This function should not be used without a telemetry field trial + * demonstrating a clear performance improvement! + * + * @param aFile nsIFile representing path to shared library + * @param aOffset Offset into the file to begin preloading + * @param aCount Number of bytes to preload (SIZE_MAX implies file size) + * @param aOutFd Pointer to file descriptor. If specified, ReadAheadFile will + * return its internal, opened file descriptor instead of closing it. + */ +void ReadAheadFile(nsIFile* aFile, const size_t aOffset = 0, + const size_t aCount = SIZE_MAX, + filedesc_t* aOutFd = nullptr); + +/* + * Wrappers for PR_GetLibraryName and PR_GetLibraryFilePathname. + */ +PathString GetLibraryName(pathstr_t aDirectory, const char* aLib); +PathString GetLibraryFilePathname(pathstr_t aName, PRFuncPtr aAddr); + +#endif // MOZILLA_INTERNAL_API + +/** + * Use readahead to preload shared libraries into the file cache before loading. + * WARNING: This function should not be used without a telemetry field trial + * demonstrating a clear performance improvement! + * + * @param aFilePath path to shared library + */ +void ReadAheadLib(pathstr_t aFilePath); + +/** + * Use readahead to preload a file into the file cache before loading. + * WARNING: This function should not be used without a telemetry field trial + * demonstrating a clear performance improvement! + * + * @param aFilePath path to shared library + * @param aOffset Offset into the file to begin preloading + * @param aCount Number of bytes to preload (SIZE_MAX implies file size) + * @param aOutFd Pointer to file descriptor. If specified, ReadAheadFile will + * return its internal, opened file descriptor instead of closing it. + */ +void ReadAheadFile(pathstr_t aFilePath, const size_t aOffset = 0, + const size_t aCount = SIZE_MAX, + filedesc_t* aOutFd = nullptr); + +/** + * Use readahead to preload a file into the file cache before reading. + * When this function exits, the file pointer is guaranteed to be in the same + * position it was in before this function was called. + * WARNING: This function should not be used without a telemetry field trial + * demonstrating a clear performance improvement! + * + * @param aFd file descriptor opened for read access + * (on Windows, file must be opened with FILE_FLAG_SEQUENTIAL_SCAN) + * @param aOffset Offset into the file to begin preloading + * @param aCount Number of bytes to preload (SIZE_MAX implies file size) + */ +void ReadAhead(filedesc_t aFd, const size_t aOffset = 0, + const size_t aCount = SIZE_MAX); + +} // namespace mozilla + +#endif diff --git a/xpcom/glue/MemUtils.cpp b/xpcom/glue/MemUtils.cpp new file mode 100644 index 0000000000..531a5ea2f5 --- /dev/null +++ b/xpcom/glue/MemUtils.cpp @@ -0,0 +1,67 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/MemUtils.h" + +#if defined(XP_WIN) +# include <windows.h> +# include "mozilla/Maybe.h" +#else +# include <sys/mman.h> +#endif + +#if defined(XP_WIN) +typedef BOOL(WINAPI* PrefetchVirtualMemoryFn)(HANDLE, ULONG_PTR, PVOID, ULONG); + +static mozilla::Maybe<PrefetchVirtualMemoryFn> sPrefetchVirtualMemory; + +void MaybeInitPrefetchVirtualMemory() { + if (sPrefetchVirtualMemory.isNothing()) { + sPrefetchVirtualMemory.emplace( + reinterpret_cast<PrefetchVirtualMemoryFn>(GetProcAddress( + GetModuleHandleW(L"kernel32.dll"), "PrefetchVirtualMemory"))); + } +} +#endif + +bool mozilla::CanPrefetchMemory() { +#if defined(XP_SOLARIS) || defined(XP_UNIX) + return true; +#elif defined(XP_WIN) + MaybeInitPrefetchVirtualMemory(); + return *sPrefetchVirtualMemory; +#else + return false; +#endif +} + +void mozilla::PrefetchMemory(uint8_t* aStart, size_t aNumBytes) { + if (aNumBytes == 0) { + return; + } + +#if defined(XP_SOLARIS) + posix_madvise(aStart, aNumBytes, POSIX_MADV_WILLNEED); +#elif defined(XP_UNIX) + madvise(aStart, aNumBytes, MADV_WILLNEED); +#elif defined(XP_WIN) + MaybeInitPrefetchVirtualMemory(); + if (*sPrefetchVirtualMemory) { + // Normally, we'd use WIN32_MEMORY_RANGE_ENTRY, but that requires + // a different _WIN32_WINNT value before including windows.h, but + // that causes complications with unified sources. It's a simple + // enough struct anyways. + struct { + PVOID VirtualAddress; + SIZE_T NumberOfBytes; + } entry; + entry.VirtualAddress = aStart; + entry.NumberOfBytes = aNumBytes; + (*sPrefetchVirtualMemory)(GetCurrentProcess(), 1, &entry, 0); + return; + } +#endif +} diff --git a/xpcom/glue/MemUtils.h b/xpcom/glue/MemUtils.h new file mode 100644 index 0000000000..606df35d09 --- /dev/null +++ b/xpcom/glue/MemUtils.h @@ -0,0 +1,20 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_MemUtils_h +#define mozilla_MemUtils_h + +#include <stddef.h> +#include <stdint.h> + +namespace mozilla { + +bool CanPrefetchMemory(); +void PrefetchMemory(uint8_t* aStart, size_t aNumBytes); + +} // namespace mozilla + +#endif diff --git a/xpcom/glue/XREAppData.cpp b/xpcom/glue/XREAppData.cpp new file mode 100644 index 0000000000..d8bb6b9580 --- /dev/null +++ b/xpcom/glue/XREAppData.cpp @@ -0,0 +1,55 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/XREAppData.h" +#include "nsCRTGlue.h" + +namespace mozilla { + +XREAppData& XREAppData::operator=(const StaticXREAppData& aOther) { + vendor = aOther.vendor; + name = aOther.name; + remotingName = aOther.remotingName; + version = aOther.version; + buildID = aOther.buildID; + ID = aOther.ID; + copyright = aOther.copyright; + flags = aOther.flags; + minVersion = aOther.minVersion; + maxVersion = aOther.maxVersion; + crashReporterURL = aOther.crashReporterURL; + profile = aOther.profile; + UAName = aOther.UAName; + sourceURL = aOther.sourceURL; + updateURL = aOther.updateURL; + + return *this; +} + +XREAppData& XREAppData::operator=(const XREAppData& aOther) = default; + +void XREAppData::SanitizeNameForDBus(nsACString& aName) { + auto IsValidDBusNameChar = [](char aChar) { + return IsAsciiAlpha(aChar) || IsAsciiDigit(aChar) || aChar == '_'; + }; + + // D-Bus names can contain only [a-z][A-Z][0-9]_, so we replace all characters + // that aren't in that range with underscores. + char* cur = aName.BeginWriting(); + char* end = aName.EndWriting(); + for (; cur != end; cur++) { + if (!IsValidDBusNameChar(*cur)) { + *cur = '_'; + } + } +} + +void XREAppData::GetDBusAppName(nsACString& aName) const { + aName.Assign(remotingName); + SanitizeNameForDBus(aName); +} + +} // namespace mozilla diff --git a/xpcom/glue/moz.build b/xpcom/glue/moz.build new file mode 100644 index 0000000000..29c75e7241 --- /dev/null +++ b/xpcom/glue/moz.build @@ -0,0 +1,12 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +DIRS += ["standalone"] + +EXPORTS.mozilla += [ + "FileUtils.h", + "MemUtils.h", +] diff --git a/xpcom/glue/objs.mozbuild b/xpcom/glue/objs.mozbuild new file mode 100644 index 0000000000..cae066e0c9 --- /dev/null +++ b/xpcom/glue/objs.mozbuild @@ -0,0 +1,22 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +xpcom_glue_src_lcppsrcs = [ + 'FileUtils.cpp', + 'MemUtils.cpp', + 'XREAppData.cpp', +] + +xpcom_glue_src_cppsrcs = [ + '/xpcom/glue/%s' % s for s in xpcom_glue_src_lcppsrcs +] + +xpcom_gluens_src_lcppsrcs = [ +] + +xpcom_gluens_src_cppsrcs = [ + '/xpcom/glue/%s' % s for s in xpcom_gluens_src_lcppsrcs +] diff --git a/xpcom/glue/standalone/moz.build b/xpcom/glue/standalone/moz.build new file mode 100644 index 0000000000..bf4cce5daf --- /dev/null +++ b/xpcom/glue/standalone/moz.build @@ -0,0 +1,36 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +SOURCES += [ + "../FileUtils.cpp", + "../MemUtils.cpp", + "nsXPCOMGlue.cpp", +] + +Library("xpcomglue") + +FORCE_STATIC_LIB = True + +if CONFIG["CC_TYPE"] == "clang-cl": + DEFINES["_USE_ANSI_CPP"] = True + # Don't include directives about which CRT to use + CFLAGS += ["-Zl"] + CXXFLAGS += ["-Zl"] + +DEFINES["XPCOM_GLUE"] = True + +LOCAL_INCLUDES += [ + "../../build", + "../../threads", +] + +# Don't use STL wrappers here (i.e. wrapped <new>); they require mozalloc +DisableStlWrapping() + +DIST_INSTALL = True + +if CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk": + CXXFLAGS += CONFIG["GLIB_CFLAGS"] diff --git a/xpcom/glue/standalone/nsXPCOMGlue.cpp b/xpcom/glue/standalone/nsXPCOMGlue.cpp new file mode 100644 index 0000000000..205f2aed05 --- /dev/null +++ b/xpcom/glue/standalone/nsXPCOMGlue.cpp @@ -0,0 +1,419 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/Bootstrap.h" + +#include "nsXPCOMPrivate.h" +#include <stdlib.h> +#include <stdio.h> + +#include "mozilla/FileUtils.h" +#include "mozilla/Result.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/UniquePtrExtensions.h" + +using namespace mozilla; + +#define XPCOM_DEPENDENT_LIBS_LIST "dependentlibs.list" + +#if defined(XP_WIN) +# define READ_TEXTMODE L"rt" +#else +# define READ_TEXTMODE "r" +#endif + +typedef void (*NSFuncPtr)(); + +#if defined(XP_WIN) +# include <windows.h> +using LibHandleType = HMODULE; +#else +using LibHandleType = void*; +#endif + +using LibHandleResult = ::mozilla::Result<LibHandleType, DLErrorType>; + +#if defined(XP_WIN) +# include <mbstring.h> +# include "mozilla/WindowsVersion.h" +# include "mozilla/PreXULSkeletonUI.h" + +static LibHandleResult GetLibHandle(pathstr_t aDependentLib) { + LibHandleType libHandle = + LoadLibraryExW(aDependentLib, nullptr, LOAD_WITH_ALTERED_SEARCH_PATH); + + if (!libHandle) { + DWORD err = GetLastError(); +# if defined(DEBUG) + LPWSTR lpMsgBuf; + FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPWSTR)&lpMsgBuf, 0, nullptr); + wprintf(L"Error loading %ls: %s\n", aDependentLib, lpMsgBuf); + LocalFree(lpMsgBuf); +# endif // defined(DEBUG) + return Err(err); + } + + return libHandle; +} + +static NSFuncPtr GetSymbol(LibHandleType aLibHandle, const char* aSymbol) { + return (NSFuncPtr)GetProcAddress(aLibHandle, aSymbol); +} + +static void CloseLibHandle(LibHandleType aLibHandle) { + FreeLibrary(aLibHandle); +} + +#else +# include <dlfcn.h> + +# if defined(MOZ_LINKER) +extern "C" { +NS_HIDDEN __typeof(dlopen) __wrap_dlopen; +NS_HIDDEN __typeof(dlsym) __wrap_dlsym; +NS_HIDDEN __typeof(dlclose) __wrap_dlclose; +} + +# define dlopen __wrap_dlopen +# define dlsym __wrap_dlsym +# define dlclose __wrap_dlclose +# endif + +static LibHandleResult GetLibHandle(pathstr_t aDependentLib) { + LibHandleType libHandle = dlopen(aDependentLib, RTLD_GLOBAL | RTLD_LAZY +# ifdef XP_MACOSX + | RTLD_FIRST +# endif + ); + if (!libHandle) { + UniqueFreePtr<char> errMsg(strdup(dlerror())); + fprintf(stderr, "XPCOMGlueLoad error for file %s:\n%s\n", aDependentLib, + errMsg.get()); + return Err(std::move(errMsg)); + } + return libHandle; +} + +static NSFuncPtr GetSymbol(LibHandleType aLibHandle, const char* aSymbol) { + return (NSFuncPtr)dlsym(aLibHandle, aSymbol); +} + +# if !defined(MOZ_LINKER) && !defined(__ANDROID__) +static void CloseLibHandle(LibHandleType aLibHandle) { dlclose(aLibHandle); } +# endif +#endif + +struct DependentLib { + LibHandleType libHandle; + DependentLib* next; +}; + +static DependentLib* sTop; + +static void AppendDependentLib(LibHandleType aLibHandle) { + auto* d = new DependentLib; + if (!d) { + return; + } + + d->next = sTop; + d->libHandle = aLibHandle; + + sTop = d; +} + +using ReadDependentCBResult = ::mozilla::Result<::mozilla::Ok, DLErrorType>; + +static ReadDependentCBResult ReadDependentCB( + pathstr_t aDependentLib, LibLoadingStrategy aLibLoadingStrategy) { +#if !defined(MOZ_LINKER) && !defined(__ANDROID__) + // Don't bother doing a ReadAhead if we're not in the parent process. + // What we need from the library should already be in the system file + // cache. + if (aLibLoadingStrategy == LibLoadingStrategy::ReadAhead) { + ReadAheadLib(aDependentLib); + } +#endif + LibHandleType libHandle; + MOZ_TRY_VAR(libHandle, GetLibHandle(aDependentLib)); + + AppendDependentLib(libHandle); + return Ok(); +} + +#ifdef XP_WIN +static ReadDependentCBResult ReadDependentCB( + const char* aDependentLib, LibLoadingStrategy aLibLoadingStrategy) { + wchar_t wideDependentLib[MAX_PATH]; + MultiByteToWideChar(CP_UTF8, 0, aDependentLib, -1, wideDependentLib, + MAX_PATH); + return ReadDependentCB(wideDependentLib, aLibLoadingStrategy); +} + +inline FILE* TS_tfopen(const char* path, const wchar_t* mode) { + wchar_t wPath[MAX_PATH]; + MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH); + return _wfopen(wPath, mode); +} +#else +inline FILE* TS_tfopen(const char* aPath, const char* aMode) { + return fopen(aPath, aMode); +} +#endif + +/* RAII wrapper for FILE descriptors */ +struct ScopedCloseFileTraits { + typedef FILE* type; + static type empty() { return nullptr; } + static void release(type aFile) { + if (aFile) { + fclose(aFile); + } + } +}; +typedef Scoped<ScopedCloseFileTraits> ScopedCloseFile; + +#if !defined(MOZ_LINKER) && !defined(__ANDROID__) +static void XPCOMGlueUnload() { + while (sTop) { + CloseLibHandle(sTop->libHandle); + + DependentLib* temp = sTop; + sTop = sTop->next; + + delete temp; + } +} +#endif + +#if defined(XP_WIN) +// like strpbrk but finds the *last* char, not the first +static const char* ns_strrpbrk(const char* string, const char* strCharSet) { + const char* found = nullptr; + for (; *string; ++string) { + for (const char* search = strCharSet; *search; ++search) { + if (*search == *string) { + found = string; + // Since we're looking for the last char, we save "found" + // until we're at the end of the string. + } + } + } + + return found; +} +#endif + +using XPCOMGlueLoadError = BootstrapError; +using XPCOMGlueLoadResult = + ::mozilla::Result<::mozilla::Ok, XPCOMGlueLoadError>; + +static XPCOMGlueLoadResult XPCOMGlueLoad( + const char* aXPCOMFile, LibLoadingStrategy aLibLoadingStrategy) { +#if defined(MOZ_LINKER) || defined(__ANDROID__) + ReadDependentCBResult readDependentCBResult = + ReadDependentCB(aXPCOMFile, aLibLoadingStrategy); + if (readDependentCBResult.isErr()) { + return Err(AsVariant(readDependentCBResult.unwrapErr())); + } +#else + char xpcomDir[MAXPATHLEN]; +# ifdef XP_WIN + const char* lastSlash = ns_strrpbrk(aXPCOMFile, "/\\"); +# elif XP_MACOSX + // On OSX, the dependentlibs.list file lives under Contents/Resources. + // However, the actual libraries listed in dependentlibs.list live under + // Contents/MacOS. We want to read the list from Contents/Resources, then + // load the libraries from Contents/MacOS. + const char* tempSlash = strrchr(aXPCOMFile, '/'); + size_t tempLen = size_t(tempSlash - aXPCOMFile); + if (tempLen > MAXPATHLEN) { + return Err(AsVariant(NS_ERROR_FAILURE)); + } + char tempBuffer[MAXPATHLEN]; + memcpy(tempBuffer, aXPCOMFile, tempLen); + tempBuffer[tempLen] = '\0'; + const char* slash = strrchr(tempBuffer, '/'); + tempLen = size_t(slash - tempBuffer); + const char* lastSlash = aXPCOMFile + tempLen; +# else + const char* lastSlash = strrchr(aXPCOMFile, '/'); +# endif + char* cursor; + if (lastSlash) { + size_t len = size_t(lastSlash - aXPCOMFile); + + if (len > MAXPATHLEN - sizeof(XPCOM_FILE_PATH_SEPARATOR +# ifdef XP_MACOSX + "Resources" XPCOM_FILE_PATH_SEPARATOR +# endif + XPCOM_DEPENDENT_LIBS_LIST)) { + return Err(AsVariant(NS_ERROR_FAILURE)); + } + memcpy(xpcomDir, aXPCOMFile, len); + strcpy(xpcomDir + len, XPCOM_FILE_PATH_SEPARATOR +# ifdef XP_MACOSX + "Resources" XPCOM_FILE_PATH_SEPARATOR +# endif + XPCOM_DEPENDENT_LIBS_LIST); + cursor = xpcomDir + len + 1; + } else { + strcpy(xpcomDir, XPCOM_DEPENDENT_LIBS_LIST); + cursor = xpcomDir; + } + + if (getenv("MOZ_RUN_GTEST") +# ifdef FUZZING + || getenv("FUZZER") +# endif + ) { + strcat(xpcomDir, ".gtest"); + } + + ScopedCloseFile flist; + flist = TS_tfopen(xpcomDir, READ_TEXTMODE); + if (!flist) { + return Err(AsVariant(NS_ERROR_FAILURE)); + } + +# ifdef XP_MACOSX + tempLen = size_t(cursor - xpcomDir); + if (tempLen > MAXPATHLEN - sizeof("MacOS" XPCOM_FILE_PATH_SEPARATOR) - 1) { + return Err(AsVariant(NS_ERROR_FAILURE)); + } + strcpy(cursor, "MacOS" XPCOM_FILE_PATH_SEPARATOR); + cursor += strlen(cursor); +# endif + *cursor = '\0'; + + char buffer[MAXPATHLEN]; + + while (fgets(buffer, sizeof(buffer), flist)) { + int l = strlen(buffer); + + // ignore empty lines and comments + if (l == 0 || *buffer == '#') { + continue; + } +# ifdef XP_WIN + // There is no point in reading Universal CRT forwarder DLLs ahead on + // Windows 10 because they will not be touched later. + if (IsWin10OrLater() && !strncmp(buffer, "api-", 4)) { + continue; + } +# endif + + // cut the trailing newline, if present + if (buffer[l - 1] == '\n') { + buffer[l - 1] = '\0'; + } + + if (l + size_t(cursor - xpcomDir) > MAXPATHLEN) { + return Err(AsVariant(NS_ERROR_FAILURE)); + } + + strcpy(cursor, buffer); + ReadDependentCBResult readDependentCBResult = + ReadDependentCB(xpcomDir, aLibLoadingStrategy); + if (readDependentCBResult.isErr()) { + XPCOMGlueUnload(); + return Err(AsVariant(readDependentCBResult.unwrapErr())); + } + +# ifdef XP_WIN + // We call PollPreXULSkeletonUIEvents here in order to not get flagged by + // Windows as nonresponsive. In order to not be flagged as such, we seem to + // simply need to respond to *a* message every few seconds. The halfway + // point on slow systems between process start and nsWindow taking over the + // skeleton UI window seems to be XUL being loaded. Accordingly, placing + // this call here covers the most ground (as we will call this after + // prefetching and loading all of the dlls in dependentlibs.list, which + // includes xul.dll.) + PollPreXULSkeletonUIEvents(); +# endif + } +#endif + return Ok(); +} + +#if defined(MOZ_WIDGET_GTK) && \ + (defined(MOZ_MEMORY) || defined(__FreeBSD__) || defined(__NetBSD__)) +# define MOZ_GSLICE_INIT +#endif + +#ifdef MOZ_GSLICE_INIT +# include <glib.h> + +class GSliceInit { + public: + GSliceInit() { + mHadGSlice = bool(getenv("G_SLICE")); + if (!mHadGSlice) { + // Disable the slice allocator, since jemalloc already uses similar layout + // algorithms, and using a sub-allocator tends to increase fragmentation. + // This must be done before g_thread_init() is called. + // glib >= 2.36 initializes g_slice as a side effect of its various static + // initializers, so this needs to happen before glib is loaded, which is + // this is hooked in XPCOMGlueStartup before libxul is loaded. This + // relies on the main executable not depending on glib. + setenv("G_SLICE", "always-malloc", 1); + } + } + + ~GSliceInit() { + if (!mHadGSlice) { + unsetenv("G_SLICE"); + } + } + + private: + bool mHadGSlice; +}; +#endif + +namespace mozilla { + +BootstrapResult GetBootstrap(const char* aXPCOMFile, + LibLoadingStrategy aLibLoadingStrategy) { +#ifdef MOZ_GSLICE_INIT + GSliceInit gSliceInit; +#endif + + if (!aXPCOMFile) { + return Err(AsVariant(NS_ERROR_INVALID_ARG)); + } + + char* lastSlash = + strrchr(const_cast<char*>(aXPCOMFile), XPCOM_FILE_PATH_SEPARATOR[0]); + if (!lastSlash) { + return Err(AsVariant(NS_ERROR_FILE_INVALID_PATH)); + } + + size_t base_len = size_t(lastSlash - aXPCOMFile) + 1; + + UniqueFreePtr<char> file( + reinterpret_cast<char*>(malloc(base_len + sizeof(XPCOM_DLL)))); + memcpy(file.get(), aXPCOMFile, base_len); + memcpy(file.get() + base_len, XPCOM_DLL, sizeof(XPCOM_DLL)); + + MOZ_TRY(XPCOMGlueLoad(file.get(), aLibLoadingStrategy)); + + GetBootstrapType func = + (GetBootstrapType)GetSymbol(sTop->libHandle, "XRE_GetBootstrap"); + if (!func) { + return Err(AsVariant(NS_ERROR_NOT_AVAILABLE)); + } + + Bootstrap::UniquePtr b; + (*func)(b); + + return b; +} + +} // namespace mozilla |