Loading mozglue/linker/CustomElf.h +1 −0 Original line number Diff line number Diff line Loading @@ -326,6 +326,7 @@ private: void (*func)(void); } f; f.ptr = ptr; debug("%s: Calling function @%p", GetPath(), ptr); f.func(); } Loading mozglue/linker/ElfLoader.cpp +12 −10 Original line number Diff line number Diff line Loading @@ -226,7 +226,6 @@ ElfLoader::Load(const char *path, int flags, LibHandle *parent) zip = zips.GetZip(zip_path); Zip::Stream s; if (zip && zip->GetStream(subpath, &s)) { if (s.GetType() == Zip::Stream::DEFLATE) { /* When the MOZ_LINKER_EXTRACT environment variable is set to "1", * compressed libraries are going to be (temporarily) extracted as * files, in the directory pointed by the MOZ_LINKER_CACHE Loading @@ -234,9 +233,12 @@ ElfLoader::Load(const char *path, int flags, LibHandle *parent) const char *extract = getenv("MOZ_LINKER_EXTRACT"); if (extract && !strncmp(extract, "1", 2 /* Including '\0' */)) mappable = MappableExtractFile::Create(name, &s); /* The above may fail in some cases. */ if (!mappable) if (!mappable) { if (s.GetType() == Zip::Stream::DEFLATE) { mappable = MappableDeflate::Create(name, zip, &s); } else if (s.GetType() == Zip::Stream::STORE) { mappable = MappableSeekableZStream::Create(name, zip, &s); } } } } Loading mozglue/linker/Makefile.in +1 −0 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ CPPSRCS += \ ElfLoader.cpp \ CustomElf.cpp \ Mappable.cpp \ SeekableZStream.cpp \ $(NULL) endif Loading mozglue/linker/Mappable.cpp +230 −28 Original line number Diff line number Diff line Loading @@ -13,6 +13,7 @@ #include <linux/ashmem.h> #endif #include "ElfLoader.h" #include "SeekableZStream.h" #include "Logging.h" #ifndef PAGE_SIZE Loading Loading @@ -80,6 +81,7 @@ MappableExtractFile::Create(const char *name, Zip::Stream *stream) return NULL; } AutoUnlinkFile file = path.forget(); if (stream->GetType() == Zip::Stream::DEFLATE) { if (ftruncate(fd, stream->GetUncompressedSize()) == -1) { log("Couldn't ftruncate %s to decompress library", file.get()); return NULL; Loading @@ -91,6 +93,7 @@ MappableExtractFile::Create(const char *name, Zip::Stream *stream) log("Couldn't map %s to decompress library", file.get()); return NULL; } z_stream zStream = stream->GetZStream(buffer); /* Decompress */ Loading @@ -111,6 +114,30 @@ MappableExtractFile::Create(const char *name, Zip::Stream *stream) static_cast<unsigned int>(stream->GetUncompressedSize())); return NULL; } } else if (stream->GetType() == Zip::Stream::STORE) { SeekableZStream zStream; if (!zStream.Init(stream->GetBuffer())) { log("Couldn't initialize SeekableZStream for %s", name); return NULL; } if (ftruncate(fd, zStream.GetUncompressedSize()) == -1) { log("Couldn't ftruncate %s to decompress library", file.get()); return NULL; } MappedPtr buffer(::mmap(NULL, zStream.GetUncompressedSize(), PROT_WRITE, MAP_SHARED, fd, 0), zStream.GetUncompressedSize()); if (buffer == MAP_FAILED) { log("Couldn't map %s to decompress library", file.get()); return NULL; } if (!zStream.Decompress(buffer, 0, zStream.GetUncompressedSize())) { log("%s: failed to decompress", name); return NULL; } } else { return NULL; } return new MappableExtractFile(fd.forget(), file.forget()); } Loading Loading @@ -296,3 +323,178 @@ MappableDeflate::finalize() /* Remove reference to Zip archive */ zip = NULL; } MappableSeekableZStream * MappableSeekableZStream::Create(const char *name, Zip *zip, Zip::Stream *stream) { MOZ_ASSERT(stream->GetType() == Zip::Stream::STORE); AutoDeletePtr<MappableSeekableZStream> mappable = new MappableSeekableZStream(zip); if (pthread_mutex_init(&mappable->mutex, NULL)) return NULL; if (!mappable->zStream.Init(stream->GetBuffer())) return NULL; mappable->buffer = _MappableBuffer::Create(name, mappable->zStream.GetUncompressedSize()); if (!mappable->buffer) return NULL; mappable->chunkAvail = new unsigned char[mappable->zStream.GetChunksNum()]; memset(mappable->chunkAvail, 0, mappable->zStream.GetChunksNum()); return mappable.forget(); } MappableSeekableZStream::MappableSeekableZStream(Zip *zip) : zip(zip) { } MappableSeekableZStream::~MappableSeekableZStream() { pthread_mutex_destroy(&mutex); } void * MappableSeekableZStream::mmap(const void *addr, size_t length, int prot, int flags, off_t offset) { /* Map with PROT_NONE so that accessing the mapping would segfault, and * bring us to ensure() */ void *res = buffer->mmap(addr, length, PROT_NONE, flags, offset); if (res == MAP_FAILED) return MAP_FAILED; /* Store the mapping, ordered by offset and length */ std::vector<LazyMap>::reverse_iterator it; for (it = lazyMaps.rbegin(); it < lazyMaps.rend(); ++it) { if ((it->offset < offset) || ((it->offset == offset) && (it->length < length))) break; } LazyMap map = { res, length, prot, offset }; lazyMaps.insert(it.base(), map); return res; } void MappableSeekableZStream::munmap(void *addr, size_t length) { std::vector<LazyMap>::iterator it; for (it = lazyMaps.begin(); it < lazyMaps.end(); ++it) if ((it->addr = addr) && (it->length == length)) { lazyMaps.erase(it); ::munmap(addr, length); return; } MOZ_NOT_REACHED("munmap called with unknown mapping"); } void MappableSeekableZStream::finalize() { } class AutoLock { public: AutoLock(pthread_mutex_t *mutex): mutex(mutex) { if (pthread_mutex_lock(mutex)) MOZ_NOT_REACHED("pthread_mutex_lock failed"); } ~AutoLock() { if (pthread_mutex_unlock(mutex)) MOZ_NOT_REACHED("pthread_mutex_unlock failed"); } private: pthread_mutex_t *mutex; }; bool MappableSeekableZStream::ensure(const void *addr) { debug("ensure @%p", addr); void *addrPage = reinterpret_cast<void *> (reinterpret_cast<uintptr_t>(addr) & PAGE_MASK); /* Find the mapping corresponding to the given page */ std::vector<LazyMap>::iterator map; for (map = lazyMaps.begin(); map < lazyMaps.end(); ++map) { if (map->Contains(addrPage)) break; } if (map == lazyMaps.end()) return false; /* Find corresponding chunk */ off_t mapOffset = map->offsetOf(addrPage); size_t chunk = mapOffset / zStream.GetChunkSize(); /* In the typical case, we just need to decompress the chunk entirely. But * when the current mapping ends in the middle of the chunk, we want to * stop there. However, if another mapping needs the last part of the * chunk, we still need to continue. As mappings are ordered by offset * and length, we don't need to scan the entire list of mappings. * It is safe to run through lazyMaps here because the linker is never * going to call mmap (which adds lazyMaps) while this function is * called. */ size_t length = zStream.GetChunkSize(chunk); size_t chunkStart = chunk * zStream.GetChunkSize(); size_t chunkEnd = chunkStart + length; std::vector<LazyMap>::iterator it; for (it = map; it < lazyMaps.end(); ++it) { if (chunkEnd <= it->endOffset()) break; } if ((it == lazyMaps.end()) || (chunkEnd > it->endOffset())) { /* The mapping "it" points at now is past the interesting one */ --it; length = it->endOffset() - chunkStart; } AutoLock lock(&mutex); /* The very first page is mapped and accessed separately of the rest, and * as such, only the first page of the first chunk is decompressed this way. * When we fault in the remaining pages of that chunk, we want to decompress * the complete chunk again. Short of doing that, we would end up with * no data between PAGE_SIZE and chunkSize, which would effectively corrupt * symbol resolution in the underlying library. */ if (chunkAvail[chunk] < (length + PAGE_SIZE - 1) / PAGE_SIZE) { if (!zStream.DecompressChunk(*buffer + chunkStart, chunk, length)) return false; #if defined(ANDROID) && defined(__arm__) if (map->prot & PROT_EXEC) { /* We just extracted data that may be executed in the future. * We thus need to ensure Instruction and Data cache coherency. */ debug("cacheflush(%p, %p)", *buffer + chunkStart, *buffer + (chunkStart + length)); cacheflush(reinterpret_cast<uintptr_t>(*buffer + chunkStart), reinterpret_cast<uintptr_t>(*buffer + (chunkStart + length)), 0); } #endif chunkAvail[chunk] = (length + PAGE_SIZE - 1) / PAGE_SIZE; } /* Flip the chunk mapping protection to the recorded flags. We could * also flip the protection for other mappings of the same chunk, * but it's easier to skip that and let further segfaults call * ensure again. */ const void *chunkAddr = reinterpret_cast<const void *> (reinterpret_cast<uintptr_t>(addrPage) - mapOffset % zStream.GetChunkSize()); const void *chunkEndAddr = reinterpret_cast<const void *> (reinterpret_cast<uintptr_t>(chunkAddr) + length); const void *start = std::max(map->addr, chunkAddr); const void *end = std::min(map->end(), chunkEndAddr); length = reinterpret_cast<uintptr_t>(end) - reinterpret_cast<uintptr_t>(start); debug("mprotect @%p, 0x%x, 0x%x", start, length, map->prot); if (mprotect(const_cast<void *>(start), length, map->prot) == 0) return true; log("mprotect failed"); return false; } mozglue/linker/Mappable.h +82 −0 Original line number Diff line number Diff line Loading @@ -6,7 +6,9 @@ #define Mappable_h #include <sys/types.h> #include <pthread.h> #include "Zip.h" #include "SeekableZStream.h" #include "mozilla/RefPtr.h" #include "zlib.h" Loading Loading @@ -155,4 +157,84 @@ private: z_stream zStream; }; /** * Mappable implementation for seekable zStreams. * Inflates the mapped bits in a temporary buffer, on demand. */ class MappableSeekableZStream: public Mappable { public: ~MappableSeekableZStream(); /** * Create a MappableSeekableZStream instance for the given Zip stream. The * name argument is used for an appropriately named temporary file, and the * Zip instance is given for the MappableSeekableZStream to keep a reference * of it. */ static MappableSeekableZStream *Create(const char *name, Zip *zip, Zip::Stream *stream); /* Inherited from Mappable */ virtual void *mmap(const void *addr, size_t length, int prot, int flags, off_t offset); virtual void munmap(void *addr, size_t length); virtual void finalize(); virtual bool ensure(const void *addr); private: MappableSeekableZStream(Zip *zip); /* Zip reference */ mozilla::RefPtr<Zip> zip; /* Decompression buffer */ AutoDeletePtr<_MappableBuffer> buffer; /* Seekable ZStream */ SeekableZStream zStream; /* Keep track of mappings performed with MappableSeekableZStream::mmap so * that they can be realized by MappableSeekableZStream::ensure. * Values stored in the struct are those passed to mmap */ struct LazyMap { const void *addr; size_t length; int prot; off_t offset; /* Returns addr + length, as a pointer */ const void *end() const { return reinterpret_cast<const void *> (reinterpret_cast<const unsigned char *>(addr) + length); } /* Returns offset + length */ const off_t endOffset() const { return offset + length; } /* Returns the offset corresponding to the given address */ const off_t offsetOf(const void *ptr) const { return reinterpret_cast<uintptr_t>(ptr) - reinterpret_cast<uintptr_t>(addr) + offset; } /* Returns whether the given address is in the LazyMap range */ const bool Contains(const void *ptr) const { return (ptr >= addr) && (ptr < end()); } }; /* List of all mappings */ std::vector<LazyMap> lazyMaps; /* Array keeping track of which chunks have already been decompressed. * Each value is the number of pages decompressed for the given chunk. */ AutoDeleteArray<unsigned char> chunkAvail; /* Mutex protecting decompression */ pthread_mutex_t mutex; }; #endif /* Mappable_h */ Loading
mozglue/linker/CustomElf.h +1 −0 Original line number Diff line number Diff line Loading @@ -326,6 +326,7 @@ private: void (*func)(void); } f; f.ptr = ptr; debug("%s: Calling function @%p", GetPath(), ptr); f.func(); } Loading
mozglue/linker/ElfLoader.cpp +12 −10 Original line number Diff line number Diff line Loading @@ -226,7 +226,6 @@ ElfLoader::Load(const char *path, int flags, LibHandle *parent) zip = zips.GetZip(zip_path); Zip::Stream s; if (zip && zip->GetStream(subpath, &s)) { if (s.GetType() == Zip::Stream::DEFLATE) { /* When the MOZ_LINKER_EXTRACT environment variable is set to "1", * compressed libraries are going to be (temporarily) extracted as * files, in the directory pointed by the MOZ_LINKER_CACHE Loading @@ -234,9 +233,12 @@ ElfLoader::Load(const char *path, int flags, LibHandle *parent) const char *extract = getenv("MOZ_LINKER_EXTRACT"); if (extract && !strncmp(extract, "1", 2 /* Including '\0' */)) mappable = MappableExtractFile::Create(name, &s); /* The above may fail in some cases. */ if (!mappable) if (!mappable) { if (s.GetType() == Zip::Stream::DEFLATE) { mappable = MappableDeflate::Create(name, zip, &s); } else if (s.GetType() == Zip::Stream::STORE) { mappable = MappableSeekableZStream::Create(name, zip, &s); } } } } Loading
mozglue/linker/Makefile.in +1 −0 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ CPPSRCS += \ ElfLoader.cpp \ CustomElf.cpp \ Mappable.cpp \ SeekableZStream.cpp \ $(NULL) endif Loading
mozglue/linker/Mappable.cpp +230 −28 Original line number Diff line number Diff line Loading @@ -13,6 +13,7 @@ #include <linux/ashmem.h> #endif #include "ElfLoader.h" #include "SeekableZStream.h" #include "Logging.h" #ifndef PAGE_SIZE Loading Loading @@ -80,6 +81,7 @@ MappableExtractFile::Create(const char *name, Zip::Stream *stream) return NULL; } AutoUnlinkFile file = path.forget(); if (stream->GetType() == Zip::Stream::DEFLATE) { if (ftruncate(fd, stream->GetUncompressedSize()) == -1) { log("Couldn't ftruncate %s to decompress library", file.get()); return NULL; Loading @@ -91,6 +93,7 @@ MappableExtractFile::Create(const char *name, Zip::Stream *stream) log("Couldn't map %s to decompress library", file.get()); return NULL; } z_stream zStream = stream->GetZStream(buffer); /* Decompress */ Loading @@ -111,6 +114,30 @@ MappableExtractFile::Create(const char *name, Zip::Stream *stream) static_cast<unsigned int>(stream->GetUncompressedSize())); return NULL; } } else if (stream->GetType() == Zip::Stream::STORE) { SeekableZStream zStream; if (!zStream.Init(stream->GetBuffer())) { log("Couldn't initialize SeekableZStream for %s", name); return NULL; } if (ftruncate(fd, zStream.GetUncompressedSize()) == -1) { log("Couldn't ftruncate %s to decompress library", file.get()); return NULL; } MappedPtr buffer(::mmap(NULL, zStream.GetUncompressedSize(), PROT_WRITE, MAP_SHARED, fd, 0), zStream.GetUncompressedSize()); if (buffer == MAP_FAILED) { log("Couldn't map %s to decompress library", file.get()); return NULL; } if (!zStream.Decompress(buffer, 0, zStream.GetUncompressedSize())) { log("%s: failed to decompress", name); return NULL; } } else { return NULL; } return new MappableExtractFile(fd.forget(), file.forget()); } Loading Loading @@ -296,3 +323,178 @@ MappableDeflate::finalize() /* Remove reference to Zip archive */ zip = NULL; } MappableSeekableZStream * MappableSeekableZStream::Create(const char *name, Zip *zip, Zip::Stream *stream) { MOZ_ASSERT(stream->GetType() == Zip::Stream::STORE); AutoDeletePtr<MappableSeekableZStream> mappable = new MappableSeekableZStream(zip); if (pthread_mutex_init(&mappable->mutex, NULL)) return NULL; if (!mappable->zStream.Init(stream->GetBuffer())) return NULL; mappable->buffer = _MappableBuffer::Create(name, mappable->zStream.GetUncompressedSize()); if (!mappable->buffer) return NULL; mappable->chunkAvail = new unsigned char[mappable->zStream.GetChunksNum()]; memset(mappable->chunkAvail, 0, mappable->zStream.GetChunksNum()); return mappable.forget(); } MappableSeekableZStream::MappableSeekableZStream(Zip *zip) : zip(zip) { } MappableSeekableZStream::~MappableSeekableZStream() { pthread_mutex_destroy(&mutex); } void * MappableSeekableZStream::mmap(const void *addr, size_t length, int prot, int flags, off_t offset) { /* Map with PROT_NONE so that accessing the mapping would segfault, and * bring us to ensure() */ void *res = buffer->mmap(addr, length, PROT_NONE, flags, offset); if (res == MAP_FAILED) return MAP_FAILED; /* Store the mapping, ordered by offset and length */ std::vector<LazyMap>::reverse_iterator it; for (it = lazyMaps.rbegin(); it < lazyMaps.rend(); ++it) { if ((it->offset < offset) || ((it->offset == offset) && (it->length < length))) break; } LazyMap map = { res, length, prot, offset }; lazyMaps.insert(it.base(), map); return res; } void MappableSeekableZStream::munmap(void *addr, size_t length) { std::vector<LazyMap>::iterator it; for (it = lazyMaps.begin(); it < lazyMaps.end(); ++it) if ((it->addr = addr) && (it->length == length)) { lazyMaps.erase(it); ::munmap(addr, length); return; } MOZ_NOT_REACHED("munmap called with unknown mapping"); } void MappableSeekableZStream::finalize() { } class AutoLock { public: AutoLock(pthread_mutex_t *mutex): mutex(mutex) { if (pthread_mutex_lock(mutex)) MOZ_NOT_REACHED("pthread_mutex_lock failed"); } ~AutoLock() { if (pthread_mutex_unlock(mutex)) MOZ_NOT_REACHED("pthread_mutex_unlock failed"); } private: pthread_mutex_t *mutex; }; bool MappableSeekableZStream::ensure(const void *addr) { debug("ensure @%p", addr); void *addrPage = reinterpret_cast<void *> (reinterpret_cast<uintptr_t>(addr) & PAGE_MASK); /* Find the mapping corresponding to the given page */ std::vector<LazyMap>::iterator map; for (map = lazyMaps.begin(); map < lazyMaps.end(); ++map) { if (map->Contains(addrPage)) break; } if (map == lazyMaps.end()) return false; /* Find corresponding chunk */ off_t mapOffset = map->offsetOf(addrPage); size_t chunk = mapOffset / zStream.GetChunkSize(); /* In the typical case, we just need to decompress the chunk entirely. But * when the current mapping ends in the middle of the chunk, we want to * stop there. However, if another mapping needs the last part of the * chunk, we still need to continue. As mappings are ordered by offset * and length, we don't need to scan the entire list of mappings. * It is safe to run through lazyMaps here because the linker is never * going to call mmap (which adds lazyMaps) while this function is * called. */ size_t length = zStream.GetChunkSize(chunk); size_t chunkStart = chunk * zStream.GetChunkSize(); size_t chunkEnd = chunkStart + length; std::vector<LazyMap>::iterator it; for (it = map; it < lazyMaps.end(); ++it) { if (chunkEnd <= it->endOffset()) break; } if ((it == lazyMaps.end()) || (chunkEnd > it->endOffset())) { /* The mapping "it" points at now is past the interesting one */ --it; length = it->endOffset() - chunkStart; } AutoLock lock(&mutex); /* The very first page is mapped and accessed separately of the rest, and * as such, only the first page of the first chunk is decompressed this way. * When we fault in the remaining pages of that chunk, we want to decompress * the complete chunk again. Short of doing that, we would end up with * no data between PAGE_SIZE and chunkSize, which would effectively corrupt * symbol resolution in the underlying library. */ if (chunkAvail[chunk] < (length + PAGE_SIZE - 1) / PAGE_SIZE) { if (!zStream.DecompressChunk(*buffer + chunkStart, chunk, length)) return false; #if defined(ANDROID) && defined(__arm__) if (map->prot & PROT_EXEC) { /* We just extracted data that may be executed in the future. * We thus need to ensure Instruction and Data cache coherency. */ debug("cacheflush(%p, %p)", *buffer + chunkStart, *buffer + (chunkStart + length)); cacheflush(reinterpret_cast<uintptr_t>(*buffer + chunkStart), reinterpret_cast<uintptr_t>(*buffer + (chunkStart + length)), 0); } #endif chunkAvail[chunk] = (length + PAGE_SIZE - 1) / PAGE_SIZE; } /* Flip the chunk mapping protection to the recorded flags. We could * also flip the protection for other mappings of the same chunk, * but it's easier to skip that and let further segfaults call * ensure again. */ const void *chunkAddr = reinterpret_cast<const void *> (reinterpret_cast<uintptr_t>(addrPage) - mapOffset % zStream.GetChunkSize()); const void *chunkEndAddr = reinterpret_cast<const void *> (reinterpret_cast<uintptr_t>(chunkAddr) + length); const void *start = std::max(map->addr, chunkAddr); const void *end = std::min(map->end(), chunkEndAddr); length = reinterpret_cast<uintptr_t>(end) - reinterpret_cast<uintptr_t>(start); debug("mprotect @%p, 0x%x, 0x%x", start, length, map->prot); if (mprotect(const_cast<void *>(start), length, map->prot) == 0) return true; log("mprotect failed"); return false; }
mozglue/linker/Mappable.h +82 −0 Original line number Diff line number Diff line Loading @@ -6,7 +6,9 @@ #define Mappable_h #include <sys/types.h> #include <pthread.h> #include "Zip.h" #include "SeekableZStream.h" #include "mozilla/RefPtr.h" #include "zlib.h" Loading Loading @@ -155,4 +157,84 @@ private: z_stream zStream; }; /** * Mappable implementation for seekable zStreams. * Inflates the mapped bits in a temporary buffer, on demand. */ class MappableSeekableZStream: public Mappable { public: ~MappableSeekableZStream(); /** * Create a MappableSeekableZStream instance for the given Zip stream. The * name argument is used for an appropriately named temporary file, and the * Zip instance is given for the MappableSeekableZStream to keep a reference * of it. */ static MappableSeekableZStream *Create(const char *name, Zip *zip, Zip::Stream *stream); /* Inherited from Mappable */ virtual void *mmap(const void *addr, size_t length, int prot, int flags, off_t offset); virtual void munmap(void *addr, size_t length); virtual void finalize(); virtual bool ensure(const void *addr); private: MappableSeekableZStream(Zip *zip); /* Zip reference */ mozilla::RefPtr<Zip> zip; /* Decompression buffer */ AutoDeletePtr<_MappableBuffer> buffer; /* Seekable ZStream */ SeekableZStream zStream; /* Keep track of mappings performed with MappableSeekableZStream::mmap so * that they can be realized by MappableSeekableZStream::ensure. * Values stored in the struct are those passed to mmap */ struct LazyMap { const void *addr; size_t length; int prot; off_t offset; /* Returns addr + length, as a pointer */ const void *end() const { return reinterpret_cast<const void *> (reinterpret_cast<const unsigned char *>(addr) + length); } /* Returns offset + length */ const off_t endOffset() const { return offset + length; } /* Returns the offset corresponding to the given address */ const off_t offsetOf(const void *ptr) const { return reinterpret_cast<uintptr_t>(ptr) - reinterpret_cast<uintptr_t>(addr) + offset; } /* Returns whether the given address is in the LazyMap range */ const bool Contains(const void *ptr) const { return (ptr >= addr) && (ptr < end()); } }; /* List of all mappings */ std::vector<LazyMap> lazyMaps; /* Array keeping track of which chunks have already been decompressed. * Each value is the number of pages decompressed for the given chunk. */ AutoDeleteArray<unsigned char> chunkAvail; /* Mutex protecting decompression */ pthread_mutex_t mutex; }; #endif /* Mappable_h */