summaryrefslogtreecommitdiffstats
path: root/mozglue/linker/Utils.h
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /mozglue/linker/Utils.h
parentInitial commit. (diff)
downloadfirefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz
firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'mozglue/linker/Utils.h')
-rw-r--r--mozglue/linker/Utils.h532
1 files changed, 532 insertions, 0 deletions
diff --git a/mozglue/linker/Utils.h b/mozglue/linker/Utils.h
new file mode 100644
index 0000000000..d3827f1f41
--- /dev/null
+++ b/mozglue/linker/Utils.h
@@ -0,0 +1,532 @@
+/* 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 Utils_h
+#define Utils_h
+
+#include <pthread.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include "mozilla/Assertions.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/Scoped.h"
+
+/**
+ * On architectures that are little endian and that support unaligned reads,
+ * we can use direct type, but on others, we want to have a special class
+ * to handle conversion and alignment issues.
+ */
+#if !defined(DEBUG) && (defined(__i386__) || defined(__x86_64__))
+typedef uint16_t le_uint16;
+typedef uint32_t le_uint32;
+#else
+
+/**
+ * Template that allows to find an unsigned int type from a (computed) bit size
+ */
+template <int s>
+struct UInt {};
+template <>
+struct UInt<16> {
+ typedef uint16_t Type;
+};
+template <>
+struct UInt<32> {
+ typedef uint32_t Type;
+};
+
+/**
+ * Template to access 2 n-bit sized words as a 2*n-bit sized word, doing
+ * conversion from little endian and avoiding alignment issues.
+ */
+template <typename T>
+class le_to_cpu {
+ public:
+ typedef typename UInt<16 * sizeof(T)>::Type Type;
+
+ operator Type() const { return (b << (sizeof(T) * 8)) | a; }
+
+ const le_to_cpu& operator=(const Type& v) {
+ a = v & ((1 << (sizeof(T) * 8)) - 1);
+ b = v >> (sizeof(T) * 8);
+ return *this;
+ }
+
+ le_to_cpu() {}
+ explicit le_to_cpu(const Type& v) { operator=(v); }
+
+ const le_to_cpu& operator+=(const Type& v) {
+ return operator=(operator Type() + v);
+ }
+
+ const le_to_cpu& operator++(int) { return operator=(operator Type() + 1); }
+
+ private:
+ T a, b;
+};
+
+/**
+ * Type definitions
+ */
+typedef le_to_cpu<unsigned char> le_uint16;
+typedef le_to_cpu<le_uint16> le_uint32;
+#endif
+
+/**
+ * AutoCloseFD is a RAII wrapper for POSIX file descriptors
+ */
+struct AutoCloseFDTraits {
+ typedef int type;
+ static int empty() { return -1; }
+ static void release(int fd) {
+ if (fd != -1) close(fd);
+ }
+};
+typedef mozilla::Scoped<AutoCloseFDTraits> AutoCloseFD;
+
+/**
+ * AutoCloseFILE is a RAII wrapper for POSIX streams
+ */
+struct AutoCloseFILETraits {
+ typedef FILE* type;
+ static FILE* empty() { return nullptr; }
+ static void release(FILE* f) {
+ if (f) fclose(f);
+ }
+};
+typedef mozilla::Scoped<AutoCloseFILETraits> AutoCloseFILE;
+
+extern mozilla::Atomic<size_t, mozilla::ReleaseAcquire> gPageSize;
+
+/**
+ * Page alignment helpers
+ */
+static size_t PageSize() {
+ if (!gPageSize) {
+ gPageSize = sysconf(_SC_PAGESIZE);
+ }
+
+ return gPageSize;
+}
+
+static inline uintptr_t AlignedPtr(uintptr_t ptr, size_t alignment) {
+ return ptr & ~(alignment - 1);
+}
+
+template <typename T>
+static inline T* AlignedPtr(T* ptr, size_t alignment) {
+ return reinterpret_cast<T*>(
+ AlignedPtr(reinterpret_cast<uintptr_t>(ptr), alignment));
+}
+
+template <typename T>
+static inline T PageAlignedPtr(T ptr) {
+ return AlignedPtr(ptr, PageSize());
+}
+
+static inline uintptr_t AlignedEndPtr(uintptr_t ptr, size_t alignment) {
+ return AlignedPtr(ptr + alignment - 1, alignment);
+}
+
+template <typename T>
+static inline T* AlignedEndPtr(T* ptr, size_t alignment) {
+ return reinterpret_cast<T*>(
+ AlignedEndPtr(reinterpret_cast<uintptr_t>(ptr), alignment));
+}
+
+template <typename T>
+static inline T PageAlignedEndPtr(T ptr) {
+ return AlignedEndPtr(ptr, PageSize());
+}
+
+static inline size_t AlignedSize(size_t size, size_t alignment) {
+ return (size + alignment - 1) & ~(alignment - 1);
+}
+
+static inline size_t PageAlignedSize(size_t size) {
+ return AlignedSize(size, PageSize());
+}
+
+static inline bool IsAlignedPtr(uintptr_t ptr, size_t alignment) {
+ return ptr % alignment == 0;
+}
+
+template <typename T>
+static inline bool IsAlignedPtr(T* ptr, size_t alignment) {
+ return IsAlignedPtr(reinterpret_cast<uintptr_t>(ptr), alignment);
+}
+
+template <typename T>
+static inline bool IsPageAlignedPtr(T ptr) {
+ return IsAlignedPtr(ptr, PageSize());
+}
+
+static inline bool IsAlignedSize(size_t size, size_t alignment) {
+ return size % alignment == 0;
+}
+
+static inline bool IsPageAlignedSize(size_t size) {
+ return IsAlignedSize(size, PageSize());
+}
+
+static inline size_t PageNumber(size_t size) {
+ return (size + PageSize() - 1) / PageSize();
+}
+
+/**
+ * MemoryRange stores a pointer, size pair.
+ */
+class MemoryRange {
+ public:
+ MemoryRange(void* buf, size_t length) : buf(buf), length(length) {}
+
+ void Assign(void* b, size_t len) {
+ buf = b;
+ length = len;
+ }
+
+ void Assign(const MemoryRange& other) {
+ buf = other.buf;
+ length = other.length;
+ }
+
+ void* get() const { return buf; }
+
+ operator void*() const { return buf; }
+
+ operator unsigned char*() const {
+ return reinterpret_cast<unsigned char*>(buf);
+ }
+
+ bool operator==(void* ptr) const { return buf == ptr; }
+
+ bool operator==(unsigned char* ptr) const { return buf == ptr; }
+
+ void* operator+(off_t offset) const {
+ return reinterpret_cast<char*>(buf) + offset;
+ }
+
+ /**
+ * Returns whether the given address is within the mapped range
+ */
+ bool Contains(void* ptr) const {
+ return (ptr >= buf) && (ptr < reinterpret_cast<char*>(buf) + length);
+ }
+
+ /**
+ * Returns the length of the mapped range
+ */
+ size_t GetLength() const { return length; }
+
+ static MemoryRange mmap(void* addr, size_t length, int prot, int flags,
+ int fd, off_t offset) {
+ return MemoryRange(::mmap(addr, length, prot, flags, fd, offset), length);
+ }
+
+ private:
+ void* buf;
+ size_t length;
+};
+
+/**
+ * MappedPtr is a RAII wrapper for mmap()ed memory. It can be used as
+ * a simple void * or unsigned char *.
+ *
+ * It is defined as a derivative of a template that allows to use a
+ * different unmapping strategy.
+ */
+template <typename T>
+class GenericMappedPtr : public MemoryRange {
+ public:
+ GenericMappedPtr(void* buf, size_t length) : MemoryRange(buf, length) {}
+ explicit GenericMappedPtr(const MemoryRange& other) : MemoryRange(other) {}
+ GenericMappedPtr() : MemoryRange(MAP_FAILED, 0) {}
+
+ void Assign(void* b, size_t len) {
+ if (get() != MAP_FAILED) static_cast<T*>(this)->munmap(get(), GetLength());
+ MemoryRange::Assign(b, len);
+ }
+
+ void Assign(const MemoryRange& other) {
+ Assign(other.get(), other.GetLength());
+ }
+
+ ~GenericMappedPtr() {
+ if (get() != MAP_FAILED) static_cast<T*>(this)->munmap(get(), GetLength());
+ }
+
+ void release() { MemoryRange::Assign(MAP_FAILED, 0); }
+};
+
+struct MappedPtr : public GenericMappedPtr<MappedPtr> {
+ MappedPtr(void* buf, size_t length)
+ : GenericMappedPtr<MappedPtr>(buf, length) {}
+ MOZ_IMPLICIT MappedPtr(const MemoryRange& other)
+ : GenericMappedPtr<MappedPtr>(other) {}
+ MappedPtr() : GenericMappedPtr<MappedPtr>() {}
+
+ private:
+ friend class GenericMappedPtr<MappedPtr>;
+ void munmap(void* buf, size_t length) { ::munmap(buf, length); }
+};
+
+/**
+ * UnsizedArray is a way to access raw arrays of data in memory.
+ *
+ * struct S { ... };
+ * UnsizedArray<S> a(buf);
+ * UnsizedArray<S> b; b.Init(buf);
+ *
+ * This is roughly equivalent to
+ * const S *a = reinterpret_cast<const S *>(buf);
+ * const S *b = nullptr; b = reinterpret_cast<const S *>(buf);
+ *
+ * An UnsizedArray has no known length, and it's up to the caller to make
+ * sure the accessed memory is mapped and makes sense.
+ */
+template <typename T>
+class UnsizedArray {
+ public:
+ typedef size_t idx_t;
+
+ /**
+ * Constructors and Initializers
+ */
+ UnsizedArray() : contents(nullptr) {}
+ explicit UnsizedArray(const void* buf)
+ : contents(reinterpret_cast<const T*>(buf)) {}
+
+ void Init(const void* buf) {
+ MOZ_ASSERT(contents == nullptr);
+ contents = reinterpret_cast<const T*>(buf);
+ }
+
+ /**
+ * Returns the nth element of the array
+ */
+ const T& operator[](const idx_t index) const {
+ MOZ_ASSERT(contents);
+ return contents[index];
+ }
+
+ operator const T*() const { return contents; }
+ /**
+ * Returns whether the array points somewhere
+ */
+ explicit operator bool() const { return contents != nullptr; }
+
+ private:
+ const T* contents;
+};
+
+/**
+ * Array, like UnsizedArray, is a way to access raw arrays of data in memory.
+ * Unlike UnsizedArray, it has a known length, and is enumerable with an
+ * iterator.
+ *
+ * struct S { ... };
+ * Array<S> a(buf, len);
+ * UnsizedArray<S> b; b.Init(buf, len);
+ *
+ * In the above examples, len is the number of elements in the array. It is
+ * also possible to initialize an Array with the buffer size:
+ *
+ * Array<S> c; c.InitSize(buf, size);
+ *
+ * It is also possible to initialize an Array in two steps, only providing
+ * one data at a time:
+ *
+ * Array<S> d;
+ * d.Init(buf);
+ * d.Init(len); // or d.InitSize(size);
+ *
+ */
+template <typename T>
+class Array : public UnsizedArray<T> {
+ public:
+ typedef typename UnsizedArray<T>::idx_t idx_t;
+
+ /**
+ * Constructors and Initializers
+ */
+ Array() : UnsizedArray<T>(), length(0) {}
+ Array(const void* buf, const idx_t length)
+ : UnsizedArray<T>(buf), length(length) {}
+
+ void Init(const void* buf) { UnsizedArray<T>::Init(buf); }
+
+ void Init(const idx_t len) {
+ MOZ_ASSERT(length == 0);
+ length = len;
+ }
+
+ void InitSize(const idx_t size) { Init(size / sizeof(T)); }
+
+ void Init(const void* buf, const idx_t len) {
+ UnsizedArray<T>::Init(buf);
+ Init(len);
+ }
+
+ void InitSize(const void* buf, const idx_t size) {
+ UnsizedArray<T>::Init(buf);
+ InitSize(size);
+ }
+
+ /**
+ * Returns the nth element of the array
+ */
+ const T& operator[](const idx_t index) const {
+ MOZ_ASSERT(index < length);
+ MOZ_ASSERT(operator bool());
+ return UnsizedArray<T>::operator[](index);
+ }
+
+ /**
+ * Returns the number of elements in the array
+ */
+ idx_t numElements() const { return length; }
+
+ /**
+ * Returns whether the array points somewhere and has at least one element.
+ */
+ explicit operator bool() const {
+ return (length > 0) && UnsizedArray<T>::operator bool();
+ }
+
+ /**
+ * Iterator for an Array. Use is similar to that of STL const_iterators:
+ *
+ * struct S { ... };
+ * Array<S> a(buf, len);
+ * for (Array<S>::iterator it = a.begin(); it < a.end(); ++it) {
+ * // Do something with *it.
+ * }
+ */
+ class iterator {
+ public:
+ iterator() : item(nullptr) {}
+
+ const T& operator*() const { return *item; }
+
+ const T* operator->() const { return item; }
+
+ iterator& operator++() {
+ ++item;
+ return *this;
+ }
+
+ bool operator<(const iterator& other) const { return item < other.item; }
+
+ protected:
+ friend class Array<T>;
+ explicit iterator(const T& item) : item(&item) {}
+
+ private:
+ const T* item;
+ };
+
+ /**
+ * Returns an iterator pointing at the beginning of the Array
+ */
+ iterator begin() const {
+ if (length) return iterator(UnsizedArray<T>::operator[](0));
+ return iterator();
+ }
+
+ /**
+ * Returns an iterator pointing past the end of the Array
+ */
+ iterator end() const {
+ if (length) return iterator(UnsizedArray<T>::operator[](length));
+ return iterator();
+ }
+
+ /**
+ * Reverse iterator for an Array. Use is similar to that of STL
+ * const_reverse_iterators:
+ *
+ * struct S { ... };
+ * Array<S> a(buf, len);
+ * for (Array<S>::reverse_iterator it = a.rbegin(); it < a.rend(); ++it) {
+ * // Do something with *it.
+ * }
+ */
+ class reverse_iterator {
+ public:
+ reverse_iterator() : item(nullptr) {}
+
+ const T& operator*() const {
+ const T* tmp = item;
+ return *--tmp;
+ }
+
+ const T* operator->() const { return &operator*(); }
+
+ reverse_iterator& operator++() {
+ --item;
+ return *this;
+ }
+
+ bool operator<(const reverse_iterator& other) const {
+ return item > other.item;
+ }
+
+ protected:
+ friend class Array<T>;
+ explicit reverse_iterator(const T& item) : item(&item) {}
+
+ private:
+ const T* item;
+ };
+
+ /**
+ * Returns a reverse iterator pointing at the end of the Array
+ */
+ reverse_iterator rbegin() const {
+ if (length) return reverse_iterator(UnsizedArray<T>::operator[](length));
+ return reverse_iterator();
+ }
+
+ /**
+ * Returns a reverse iterator pointing past the beginning of the Array
+ */
+ reverse_iterator rend() const {
+ if (length) return reverse_iterator(UnsizedArray<T>::operator[](0));
+ return reverse_iterator();
+ }
+
+ private:
+ idx_t length;
+};
+
+/**
+ * Transforms a pointer-to-function to a pointer-to-object pointing at the
+ * same address.
+ */
+template <typename T>
+void* FunctionPtr(T func) {
+ union {
+ void* ptr;
+ T func;
+ } f;
+ f.func = func;
+ return f.ptr;
+}
+
+class AutoLock {
+ public:
+ explicit AutoLock(pthread_mutex_t* mutex) : mutex(mutex) {
+ if (pthread_mutex_lock(mutex)) MOZ_CRASH("pthread_mutex_lock failed");
+ }
+ ~AutoLock() {
+ if (pthread_mutex_unlock(mutex)) MOZ_CRASH("pthread_mutex_unlock failed");
+ }
+
+ private:
+ pthread_mutex_t* mutex;
+};
+
+#endif /* Utils_h */