mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 845907: Adds generic ReadAhead functions r=glandium
This commit is contained in:
parent
fd02ebad7e
commit
a66b11d089
@ -2,9 +2,27 @@
|
||||
* 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/. */
|
||||
#if defined(XP_UNIX)
|
||||
|
||||
#include "nscore.h"
|
||||
#include "nsStringGlue.h"
|
||||
#include "private/pprio.h"
|
||||
#include "mozilla/FileUtils.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)
|
||||
@ -14,9 +32,9 @@
|
||||
#include <os2.h>
|
||||
#endif
|
||||
|
||||
#include "nscore.h"
|
||||
#include "private/pprio.h"
|
||||
#include "mozilla/FileUtils.h"
|
||||
// Functions that are not to be used in standalone glue must be implemented
|
||||
// within this #if block
|
||||
#if !defined(XPCOM_GLUE)
|
||||
|
||||
bool
|
||||
mozilla::fallocate(PRFileDesc *aFD, int64_t aLength)
|
||||
@ -94,3 +112,334 @@ mozilla::fallocate(PRFileDesc *aFD, int64_t aLength)
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
#endif // !defined(XPCOM_GLUE)
|
||||
|
||||
#if defined(LINUX) && !defined(ANDROID)
|
||||
|
||||
static const unsigned int bufsize = 4096;
|
||||
|
||||
#ifdef HAVE_64BIT_OS
|
||||
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;
|
||||
#else
|
||||
#error Unsupported CPU type
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_64BIT_OS
|
||||
#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:
|
||||
ScopedMMap(const char *aFilePath)
|
||||
: buf(NULL)
|
||||
{
|
||||
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(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
}
|
||||
~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;
|
||||
}
|
||||
#if defined(XP_WIN)
|
||||
ReadAheadFile(aFilePath);
|
||||
#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) ||
|
||||
(elf.ehdr.e_phoff + 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)];
|
||||
off_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 (!aFilePath) {
|
||||
return;
|
||||
}
|
||||
#if defined(XP_WIN)
|
||||
HANDLE fd = CreateFileW(aFilePath, GENERIC_READ, FILE_SHARE_READ,
|
||||
NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
|
||||
if (fd == INVALID_HANDLE_VALUE) {
|
||||
return;
|
||||
}
|
||||
ReadAhead(fd, aOffset, aCount);
|
||||
if (aOutFd) {
|
||||
*aOutFd = fd;
|
||||
} else {
|
||||
CloseHandle(fd);
|
||||
}
|
||||
#elif defined(LINUX) && !defined(ANDROID) || defined(XP_MACOSX)
|
||||
int fd = open(aFilePath, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
return;
|
||||
}
|
||||
size_t count;
|
||||
if (aCount == SIZE_MAX) {
|
||||
struct stat st;
|
||||
if (fstat(fd, &st) < 0) {
|
||||
return;
|
||||
}
|
||||
count = st.st_size;
|
||||
} else {
|
||||
count = aCount;
|
||||
}
|
||||
ReadAhead(fd, aOffset, count);
|
||||
if (aOutFd) {
|
||||
*aOutFd = fd;
|
||||
} else {
|
||||
close(fd);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -16,26 +16,19 @@
|
||||
#include "prio.h"
|
||||
|
||||
#include "mozilla/Scoped.h"
|
||||
#include "nsIFile.h"
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/**
|
||||
* 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 NULL; }
|
||||
static void release(type fd) {
|
||||
if (fd != NULL) {
|
||||
PR_Close(fd);
|
||||
}
|
||||
}
|
||||
};
|
||||
typedef Scoped<ScopedClosePRFDTraits> AutoFDClose;
|
||||
#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
|
||||
@ -56,6 +49,25 @@ struct ScopedCloseFDTraits
|
||||
};
|
||||
typedef Scoped<ScopedCloseFDTraits> ScopedClose;
|
||||
|
||||
#if !defined(XPCOM_GLUE)
|
||||
|
||||
/**
|
||||
* 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 NULL; }
|
||||
static void release(type fd) {
|
||||
if (fd != NULL) {
|
||||
PR_Close(fd);
|
||||
}
|
||||
}
|
||||
};
|
||||
typedef Scoped<ScopedClosePRFDTraits> AutoFDClose;
|
||||
|
||||
/**
|
||||
* Fallocate efficiently and continuously allocates files via fallocate-type APIs.
|
||||
* This is useful for avoiding fragmentation.
|
||||
@ -67,5 +79,71 @@ typedef Scoped<ScopedCloseFDTraits> ScopedClose;
|
||||
*/
|
||||
NS_COM_GLUE 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
|
||||
*/
|
||||
NS_COM_GLUE 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.
|
||||
*/
|
||||
NS_COM_GLUE void ReadAheadFile(nsIFile* aFile, const size_t aOffset = 0,
|
||||
const size_t aCount = SIZE_MAX,
|
||||
filedesc_t* aOutFd = nullptr);
|
||||
|
||||
#endif // !defined(XPCOM_GLUE)
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
NS_COM_GLUE 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.
|
||||
*/
|
||||
NS_COM_GLUE 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)
|
||||
*/
|
||||
NS_COM_GLUE void ReadAhead(filedesc_t aFd, const size_t aOffset = 0,
|
||||
const size_t aCount = SIZE_MAX);
|
||||
|
||||
|
||||
} // namespace mozilla
|
||||
#endif
|
||||
|
@ -30,6 +30,7 @@ XPCOM_GLUE_SRC_LCPPSRCS = \
|
||||
nsCycleCollectorUtils.cpp \
|
||||
nsDeque.cpp \
|
||||
pldhash.cpp \
|
||||
FileUtils.cpp \
|
||||
$(NULL)
|
||||
|
||||
XPCOM_GLUE_SRC_CPPSRCS = $(addprefix $(topsrcdir)/xpcom/glue/, $(XPCOM_GLUE_SRC_LCPPSRCS))
|
||||
@ -42,7 +43,6 @@ XPCOM_GLUENS_SRC_LCPPSRCS = \
|
||||
nsProxyRelease.cpp \
|
||||
nsTextFormatter.cpp \
|
||||
GenericFactory.cpp \
|
||||
FileUtils.cpp \
|
||||
$(NULL)
|
||||
|
||||
ifneq (,$(filter arm%,$(TARGET_CPU)))
|
||||
|
@ -6,12 +6,12 @@
|
||||
#include "nsGlueLinking.h"
|
||||
#include "nsXPCOMGlue.h"
|
||||
#include "nscore.h"
|
||||
#include "mozilla/FileUtils.h"
|
||||
|
||||
#if defined(LINUX) && !defined(ANDROID)
|
||||
#define _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <elf.h>
|
||||
#include <limits.h>
|
||||
#endif
|
||||
|
||||
@ -125,68 +125,12 @@ AppendDependentLib(void *libHandle)
|
||||
sTop = d;
|
||||
}
|
||||
|
||||
#if defined(LINUX) && !defined(ANDROID)
|
||||
static const unsigned int bufsize = 4096;
|
||||
|
||||
#ifdef HAVE_64BIT_OS
|
||||
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
|
||||
|
||||
static void
|
||||
preload(const char *file)
|
||||
{
|
||||
union {
|
||||
char buf[bufsize];
|
||||
Elf_Ehdr ehdr;
|
||||
} elf;
|
||||
int fd = open(file, O_RDONLY);
|
||||
if (fd < 0)
|
||||
return;
|
||||
// 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) ||
|
||||
(elf.ehdr.e_phoff + 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);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
ReadDependentCB(const char *aDependentLib, bool do_preload)
|
||||
{
|
||||
#if defined(LINUX) && !defined(ANDROID)
|
||||
if (do_preload)
|
||||
preload(aDependentLib);
|
||||
mozilla::ReadAheadLib(aDependentLib);
|
||||
#endif
|
||||
void *libHandle = dlopen(aDependentLib, RTLD_GLOBAL | RTLD_LAZY);
|
||||
if (!libHandle)
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
#include "nsGlueLinking.h"
|
||||
#include "nsXPCOMGlue.h"
|
||||
#include "mozilla/FileUtils.h"
|
||||
|
||||
#include <mach-o/dyld.h>
|
||||
#include <sys/param.h>
|
||||
@ -14,129 +15,17 @@
|
||||
|
||||
#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>
|
||||
|
||||
#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;
|
||||
#else
|
||||
#error Unsupported CPU type
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_64BIT_OS
|
||||
#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:
|
||||
ScopedMMap(const char *file): buf(NULL) {
|
||||
fd = open(file, O_RDONLY);
|
||||
if (fd < 0)
|
||||
return;
|
||||
struct stat st;
|
||||
if (fstat(fd, &st) < 0)
|
||||
return;
|
||||
size = st.st_size;
|
||||
buf = (char *)mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
}
|
||||
~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;
|
||||
};
|
||||
|
||||
static void
|
||||
preload(const char *file)
|
||||
{
|
||||
ScopedMMap buf(file);
|
||||
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)];
|
||||
off_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. The F_RDADVISE fcntl is equivalent
|
||||
// to Linux' readahead() system call.
|
||||
if (end > 0) {
|
||||
struct radvisory ra;
|
||||
ra.ra_offset = (base - buf);
|
||||
ra.ra_count = end;
|
||||
fcntl(buf.getFd(), F_RDADVISE, &ra);
|
||||
}
|
||||
}
|
||||
|
||||
static const mach_header* sXULLibImage;
|
||||
|
||||
static void
|
||||
ReadDependentCB(const char *aDependentLib, bool do_preload)
|
||||
{
|
||||
if (do_preload)
|
||||
preload(aDependentLib);
|
||||
mozilla::ReadAheadLib(aDependentLib);
|
||||
(void) NSAddImage(aDependentLib,
|
||||
NSADDIMAGE_OPTION_RETURN_ON_ERROR |
|
||||
NSADDIMAGE_OPTION_MATCH_FILENAME_BY_INSTALLNAME);
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
#include "nsGlueLinking.h"
|
||||
#include "nsXPCOMGlue.h"
|
||||
#include "mozilla/FileUtils.h"
|
||||
|
||||
#include "nsStringAPI.h"
|
||||
#include <windows.h>
|
||||
@ -36,26 +37,6 @@ AppendDependentLib(HINSTANCE libHandle)
|
||||
sTop = d;
|
||||
}
|
||||
|
||||
static void
|
||||
preload(LPCWSTR dll)
|
||||
{
|
||||
HANDLE fd = CreateFileW(dll, GENERIC_READ, FILE_SHARE_READ,
|
||||
NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
|
||||
char buf[64 * 1024];
|
||||
|
||||
if (fd == INVALID_HANDLE_VALUE)
|
||||
return;
|
||||
|
||||
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 (ReadFile(fd, buf, sizeof(buf), &dwBytesRead, NULL) && dwBytesRead == sizeof(buf))
|
||||
/* Nothing */;
|
||||
|
||||
CloseHandle(fd);
|
||||
}
|
||||
|
||||
static void
|
||||
ReadDependentCB(const char *aDependentLib, bool do_preload)
|
||||
{
|
||||
@ -63,7 +44,7 @@ ReadDependentCB(const char *aDependentLib, bool do_preload)
|
||||
MultiByteToWideChar(CP_UTF8, 0, aDependentLib, -1, wideDependentLib, MAX_PATH);
|
||||
|
||||
if (do_preload)
|
||||
preload(wideDependentLib);
|
||||
mozilla::ReadAheadLib(wideDependentLib);
|
||||
|
||||
HINSTANCE h =
|
||||
LoadLibraryExW(wideDependentLib, NULL, MOZ_LOADLIBRARY_FLAGS);
|
||||
|
Loading…
Reference in New Issue
Block a user