1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/FileUtils.h"
14 #include "private/pprio.h"
16 #include "mozilla/Assertions.h"
17 #include "mozilla/MemUtils.h"
18 #include "mozilla/BaseProfilerMarkers.h"
20 #if defined(XP_MACOSX)
23 # include <mach/machine.h>
24 # include <mach-o/fat.h>
25 # include <mach-o/loader.h>
26 # include <sys/mman.h>
27 # include <sys/stat.h>
29 #elif defined(XP_UNIX)
35 # include <sys/types.h>
36 # include <sys/stat.h>
38 # include <nsWindowsHelpers.h>
39 # include <mozilla/NativeNt.h>
40 # include <mozilla/ScopeExit.h>
43 // Functions that are not to be used in standalone glue must be implemented
44 // within this #if block
45 #if defined(MOZILLA_INTERNAL_API)
47 # include "nsString.h"
49 bool mozilla::fallocate(PRFileDesc
* aFD
, int64_t aLength
) {
50 # if defined(HAVE_POSIX_FALLOCATE)
51 return posix_fallocate(PR_FileDesc2NativeHandle(aFD
), 0, aLength
) == 0;
52 # elif defined(XP_WIN)
53 int64_t oldpos
= PR_Seek64(aFD
, 0, PR_SEEK_CUR
);
58 if (PR_Seek64(aFD
, aLength
, PR_SEEK_SET
) != aLength
) {
62 bool retval
= (0 != SetEndOfFile((HANDLE
)PR_FileDesc2NativeHandle(aFD
)));
64 PR_Seek64(aFD
, oldpos
, PR_SEEK_SET
);
66 # elif defined(XP_MACOSX)
67 int fd
= PR_FileDesc2NativeHandle(aFD
);
68 fstore_t store
= {F_ALLOCATECONTIG
, F_PEOFPOSMODE
, 0, aLength
};
69 // Try to get a continous chunk of disk space
70 int ret
= fcntl(fd
, F_PREALLOCATE
, &store
);
72 // OK, perhaps we are too fragmented, allocate non-continuous
73 store
.fst_flags
= F_ALLOCATEALL
;
74 ret
= fcntl(fd
, F_PREALLOCATE
, &store
);
79 return ftruncate(fd
, aLength
) == 0;
80 # elif defined(XP_UNIX)
81 // The following is copied from fcntlSizeHint in sqlite
82 /* If the OS does not have posix_fallocate(), fake it. First use
83 ** ftruncate() to set the file size, then write a single byte to
84 ** the last byte in each block within the extended region. This
85 ** is the same technique used by glibc to implement posix_fallocate()
86 ** on systems that do not have a real fallocate() system call.
88 int64_t oldpos
= PR_Seek64(aFD
, 0, PR_SEEK_CUR
);
94 int fd
= PR_FileDesc2NativeHandle(aFD
);
95 if (fstat(fd
, &buf
)) {
99 if (buf
.st_size
>= aLength
) {
103 const int nBlk
= buf
.st_blksize
;
109 if (ftruncate(fd
, aLength
)) {
113 int nWrite
; // Return value from write()
114 int64_t iWrite
= ((buf
.st_size
+ 2 * nBlk
- 1) / nBlk
) * nBlk
-
115 1; // Next offset to write to
116 while (iWrite
< aLength
) {
118 if (PR_Seek64(aFD
, iWrite
, PR_SEEK_SET
) == iWrite
) {
119 nWrite
= PR_Write(aFD
, "", 1);
127 PR_Seek64(aFD
, oldpos
, PR_SEEK_SET
);
134 void mozilla::ReadAheadLib(nsIFile
* aFile
) {
137 if (!aFile
|| NS_FAILED(aFile
->GetPath(path
))) {
140 ReadAheadLib(path
.get());
141 # elif defined(LINUX) && !defined(ANDROID) || defined(XP_MACOSX)
142 nsAutoCString nativePath
;
143 if (!aFile
|| NS_FAILED(aFile
->GetNativePath(nativePath
))) {
146 ReadAheadLib(nativePath
.get());
150 void mozilla::ReadAheadFile(nsIFile
* aFile
, const size_t aOffset
,
151 const size_t aCount
, mozilla::filedesc_t
* aOutFd
) {
154 if (!aFile
|| NS_FAILED(aFile
->GetPath(path
))) {
157 ReadAheadFile(path
.get(), aOffset
, aCount
, aOutFd
);
158 # elif defined(LINUX) && !defined(ANDROID) || defined(XP_MACOSX)
159 nsAutoCString nativePath
;
160 if (!aFile
|| NS_FAILED(aFile
->GetNativePath(nativePath
))) {
163 ReadAheadFile(nativePath
.get(), aOffset
, aCount
, aOutFd
);
167 mozilla::PathString
mozilla::GetLibraryName(mozilla::pathstr_t aDirectory
,
170 nsAutoString fullName
;
172 fullName
.Assign(aDirectory
);
173 fullName
.Append('\\');
175 AppendUTF8toUTF16(MakeStringSpan(aLib
), fullName
);
176 if (!strstr(aLib
, ".dll")) {
177 fullName
.AppendLiteral(".dll");
179 return std::move(fullName
);
181 char* temp
= PR_GetLibraryName(aDirectory
, aLib
);
185 nsAutoCString
libname(temp
);
186 PR_FreeLibraryName(temp
);
187 return std::move(libname
);
191 mozilla::PathString
mozilla::GetLibraryFilePathname(mozilla::pathstr_t aName
,
194 HMODULE handle
= GetModuleHandleW(char16ptr_t(aName
));
200 path
.SetLength(MAX_PATH
);
201 DWORD len
= GetModuleFileNameW(handle
, char16ptr_t(path
.BeginWriting()),
208 return std::move(path
);
210 char* temp
= PR_GetLibraryFilePathname(aName
, aAddr
);
214 nsAutoCString
path(temp
);
215 PR_Free(temp
); // PR_GetLibraryFilePathname() uses PR_Malloc().
216 return std::move(path
);
220 #endif // defined(MOZILLA_INTERNAL_API)
222 #if defined(LINUX) && !defined(ANDROID)
224 static const unsigned int bufsize
= 4096;
227 typedef Elf64_Ehdr Elf_Ehdr
;
228 typedef Elf64_Phdr Elf_Phdr
;
229 static const unsigned char ELFCLASS
= ELFCLASS64
;
230 typedef Elf64_Off Elf_Off
;
232 typedef Elf32_Ehdr Elf_Ehdr
;
233 typedef Elf32_Phdr Elf_Phdr
;
234 static const unsigned char ELFCLASS
= ELFCLASS32
;
235 typedef Elf32_Off Elf_Off
;
238 #elif defined(XP_MACOSX)
240 # if defined(__i386__)
241 static const uint32_t CPU_TYPE
= CPU_TYPE_X86
;
242 # elif defined(__x86_64__)
243 static const uint32_t CPU_TYPE
= CPU_TYPE_X86_64
;
244 # elif defined(__ppc__)
245 static const uint32_t CPU_TYPE
= CPU_TYPE_POWERPC
;
246 # elif defined(__ppc64__)
247 static const uint32_t CPU_TYPE
= CPU_TYPE_POWERPC64
;
248 # elif defined(__aarch64__)
249 static const uint32_t CPU_TYPE
= CPU_TYPE_ARM64
;
251 # error Unsupported CPU type
256 # define LC_SEGMENT LC_SEGMENT_64
258 # define MH_MAGIC MH_MAGIC_64
259 # define cpu_mach_header mach_header_64
260 # define segment_command segment_command_64
262 # define cpu_mach_header mach_header
267 explicit ScopedMMap(const char* aFilePath
) : buf(nullptr) {
268 fd
= open(aFilePath
, O_RDONLY
);
273 if (fstat(fd
, &st
) < 0) {
277 buf
= (char*)mmap(nullptr, size
, PROT_READ
, MAP_PRIVATE
, fd
, 0);
278 if (buf
== MAP_FAILED
) {
290 operator char*() { return buf
; }
291 int getFd() { return fd
; }
300 void mozilla::ReadAhead(mozilla::filedesc_t aFd
, const size_t aOffset
,
301 const size_t aCount
) {
304 LARGE_INTEGER fpOriginal
;
305 LARGE_INTEGER fpOffset
;
306 # if defined(HAVE_LONG_LONG)
307 fpOffset
.QuadPart
= 0;
309 fpOffset
.u
.LowPart
= 0;
310 fpOffset
.u
.HighPart
= 0;
313 // Get the current file pointer so that we can restore it. This isn't
314 // really necessary other than to provide the same semantics regarding the
315 // file pointer that other platforms do
316 if (!SetFilePointerEx(aFd
, fpOffset
, &fpOriginal
, FILE_CURRENT
)) {
321 # if defined(HAVE_LONG_LONG)
322 fpOffset
.QuadPart
= static_cast<LONGLONG
>(aOffset
);
324 fpOffset
.u
.LowPart
= aOffset
;
325 fpOffset
.u
.HighPart
= 0;
328 if (!SetFilePointerEx(aFd
, fpOffset
, nullptr, FILE_BEGIN
)) {
334 size_t totalBytesRead
= 0;
336 // Do dummy reads to trigger kernel-side readhead via
337 // FILE_FLAG_SEQUENTIAL_SCAN. Abort when underfilling because during testing
338 // the buffers are read fully A buffer that's not keeping up would imply that
339 // readahead isn't working right
340 while (totalBytesRead
< aCount
&&
341 ReadFile(aFd
, buf
, sizeof(buf
), &dwBytesRead
, nullptr) &&
342 dwBytesRead
== sizeof(buf
)) {
343 totalBytesRead
+= dwBytesRead
;
346 // Restore the file pointer
347 SetFilePointerEx(aFd
, fpOriginal
, nullptr, FILE_BEGIN
);
349 #elif defined(LINUX) && !defined(ANDROID)
351 readahead(aFd
, aOffset
, aCount
);
353 #elif defined(XP_MACOSX)
356 ra
.ra_offset
= aOffset
;
357 ra
.ra_count
= aCount
;
358 // The F_RDADVISE fcntl is equivalent to Linux' readahead() system call.
359 fcntl(aFd
, F_RDADVISE
, &ra
);
364 void mozilla::ReadAheadLib(mozilla::pathstr_t aFilePath
) {
370 auto WideToUTF8
= [](const wchar_t* aStr
) -> std::string
{
372 // Determine the number of output bytes (including null terminator).
373 const int numConv
= ::WideCharToMultiByte(CP_UTF8
, 0, aStr
, -1, nullptr, 0,
379 const int numConvd
= ::WideCharToMultiByte(CP_UTF8
, 0, aStr
, -1, s
.data(),
380 numConv
, nullptr, nullptr);
381 if (numConvd
!= numConv
) {
382 // Error during conversion, remove any temporary data.
389 AUTO_BASE_PROFILER_MARKER_TEXT("ReadAheadLib", OTHER
, {},
391 WideToUTF8(aFilePath
)
398 if (!CanPrefetchMemory()) {
399 ReadAheadFile(aFilePath
);
402 nsAutoHandle
fd(CreateFileW(aFilePath
, GENERIC_READ
| GENERIC_EXECUTE
,
403 FILE_SHARE_READ
, nullptr, OPEN_EXISTING
,
404 FILE_FLAG_SEQUENTIAL_SCAN
, nullptr));
409 nsAutoHandle
mapping(CreateFileMapping(
410 fd
, nullptr, SEC_IMAGE
| PAGE_EXECUTE_READ
, 0, 0, nullptr));
415 PVOID data
= MapViewOfFile(
416 mapping
, FILE_MAP_READ
| FILE_MAP_EXECUTE
| SEC_IMAGE
, 0, 0, 0);
420 auto guard
= MakeScopeExit([=]() { UnmapViewOfFile(data
); });
421 mozilla::nt::PEHeaders
headers(data
);
422 Maybe
<Span
<const uint8_t>> bounds
= headers
.GetBounds();
427 PrefetchMemory((uint8_t*)data
, bounds
->Length());
429 #elif defined(LINUX) && !defined(ANDROID)
430 int fd
= open(aFilePath
, O_RDONLY
);
439 // Read ELF header (ehdr) and program header table (phdr).
440 // We check that the ELF magic is found, that the ELF class matches
441 // our own, and that the program header table as defined in the ELF
442 // headers fits in the buffer we read.
443 if ((read(fd
, elf
.buf
, bufsize
) <= 0) || (memcmp(elf
.buf
, ELFMAG
, 4)) ||
444 (elf
.ehdr
.e_ident
[EI_CLASS
] != ELFCLASS
) ||
445 // Upcast e_phentsize so the multiplication is done in the same precision
446 // as the subsequent addition, to satisfy static analyzers and avoid
447 // issues with abnormally large program header tables.
449 (static_cast<Elf_Off
>(elf
.ehdr
.e_phentsize
) * elf
.ehdr
.e_phnum
) >=
454 // The program header table contains segment definitions. One such
455 // segment type is PT_LOAD, which describes how the dynamic loader
456 // is going to map the file in memory. We use that information to
457 // find the biggest offset from the library that will be mapped in
459 Elf_Phdr
* phdr
= (Elf_Phdr
*)&elf
.buf
[elf
.ehdr
.e_phoff
];
461 for (int phnum
= elf
.ehdr
.e_phnum
; phnum
; phdr
++, phnum
--) {
462 if ((phdr
->p_type
== PT_LOAD
) && (end
< phdr
->p_offset
+ phdr
->p_filesz
)) {
463 end
= phdr
->p_offset
+ phdr
->p_filesz
;
466 // Let the kernel read ahead what the dynamic loader is going to
467 // map in memory soon after.
469 ReadAhead(fd
, 0, end
);
472 #elif defined(XP_MACOSX)
473 ScopedMMap
buf(aFilePath
);
479 // An OSX binary might either be a fat (universal) binary or a
480 // Mach-O binary. A fat binary actually embeds several Mach-O
481 // binaries. If we have a fat binary, find the offset where the
482 // Mach-O binary for our CPU type can be found.
483 struct fat_header
* fh
= (struct fat_header
*)base
;
485 if (OSSwapBigToHostInt32(fh
->magic
) == FAT_MAGIC
) {
486 uint32_t nfat_arch
= OSSwapBigToHostInt32(fh
->nfat_arch
);
487 struct fat_arch
* arch
= (struct fat_arch
*)&buf
[sizeof(struct fat_header
)];
488 for (; nfat_arch
; arch
++, nfat_arch
--) {
489 if (OSSwapBigToHostInt32(arch
->cputype
) == CPU_TYPE
) {
490 base
+= OSSwapBigToHostInt32(arch
->offset
);
499 // Check Mach-O magic in the Mach header
500 struct cpu_mach_header
* mh
= (struct cpu_mach_header
*)base
;
501 if (mh
->magic
!= MH_MAGIC
) {
505 // The Mach header is followed by a sequence of load commands.
506 // Each command has a header containing the command type and the
507 // command size. LD_SEGMENT commands describes how the dynamic
508 // loader is going to map the file in memory. We use that
509 // information to find the biggest offset from the library that
510 // will be mapped in memory.
511 char* cmd
= &base
[sizeof(struct cpu_mach_header
)];
513 for (uint32_t ncmds
= mh
->ncmds
; ncmds
; ncmds
--) {
514 struct segment_command
* sh
= (struct segment_command
*)cmd
;
515 if (sh
->cmd
!= LC_SEGMENT
) {
518 if (end
< sh
->fileoff
+ sh
->filesize
) {
519 end
= sh
->fileoff
+ sh
->filesize
;
523 // Let the kernel read ahead what the dynamic loader is going to
524 // map in memory soon after.
526 ReadAhead(buf
.getFd(), base
- buf
, end
);
531 void mozilla::ReadAheadFile(mozilla::pathstr_t aFilePath
, const size_t aOffset
,
532 const size_t aCount
, mozilla::filedesc_t
* aOutFd
) {
536 *aOutFd
= INVALID_HANDLE_VALUE
;
540 HANDLE fd
= CreateFileW(aFilePath
, GENERIC_READ
, FILE_SHARE_READ
, nullptr,
541 OPEN_EXISTING
, FILE_FLAG_SEQUENTIAL_SCAN
, nullptr);
545 if (fd
== INVALID_HANDLE_VALUE
) {
548 ReadAhead(fd
, aOffset
, aCount
);
552 #elif defined(LINUX) && !defined(ANDROID) || defined(XP_MACOSX)
559 int fd
= open(aFilePath
, O_RDONLY
);
567 if (aCount
== SIZE_MAX
) {
569 if (fstat(fd
, &st
) < 0) {
579 ReadAhead(fd
, aOffset
, count
);