mirror of
https://github.com/rui314/mold.git
synced 2024-09-11 21:17:28 +03:00
222 lines
4.9 KiB
C++
222 lines
4.9 KiB
C++
// This file defines integral types for file input/output. We need to use
|
|
// these types instead of the plain integers (such as uint32_t or int32_t)
|
|
// when reading from/writing to an mmap'ed file area for the following
|
|
// reasons:
|
|
//
|
|
// 1. mold is always a cross linker and should not depend on what host it
|
|
// is running on. Users should be able to run mold on a big-endian
|
|
// SPARC machine to create a little-endian RV64 binary, for example.
|
|
//
|
|
// 2. Even though data members in all ELF data strucutres are naturally
|
|
// aligned, they are not guaranteed to be aligned on memory. Because
|
|
// archive file (.a file) aligns each member only to a 2 byte boundary,
|
|
// anything larger than 2 bytes may be unaligned in an mmap'ed memory.
|
|
// Unaligned access is an undefined behavior in C/C++, so we shouldn't
|
|
// cast an arbitrary pointer to a uint32_t, for example, to read a
|
|
// 32-bits value.
|
|
//
|
|
// The data types defined in this file don't depend on host byte order and
|
|
// don't do unaligned access.
|
|
|
|
#pragma once
|
|
|
|
#include <bit>
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
|
|
#if !defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__)
|
|
# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
|
# define __LITTLE_ENDIAN__ 1
|
|
# elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
|
# define __BIG_ENDIAN__ 1
|
|
# else
|
|
# error "unknown host byte order"
|
|
# endif
|
|
#endif
|
|
|
|
namespace mold {
|
|
|
|
typedef uint8_t u8;
|
|
typedef uint16_t u16;
|
|
typedef uint32_t u32;
|
|
typedef uint64_t u64;
|
|
|
|
typedef int8_t i8;
|
|
typedef int16_t i16;
|
|
typedef int32_t i32;
|
|
typedef int64_t i64;
|
|
|
|
template <typename T>
|
|
static inline T bswap(T val) {
|
|
switch (sizeof(T)) {
|
|
case 2: return __builtin_bswap16(val);
|
|
case 4: return __builtin_bswap32(val);
|
|
case 8: return __builtin_bswap64(val);
|
|
default: __builtin_unreachable();
|
|
}
|
|
}
|
|
|
|
template <typename T, int SIZE = sizeof(T)>
|
|
class LittleEndian {
|
|
public:
|
|
LittleEndian() = default;
|
|
LittleEndian(T x) { *this = x; }
|
|
|
|
operator T() const {
|
|
if constexpr (sizeof(T) == SIZE) {
|
|
T x;
|
|
memcpy(&x, val, sizeof(T));
|
|
if constexpr (std::endian::native == std::endian::big)
|
|
x = bswap(x);
|
|
return x;
|
|
} else {
|
|
static_assert(SIZE == 3);
|
|
return (val[2] << 16) | (val[1] << 8) | val[0];
|
|
}
|
|
}
|
|
|
|
LittleEndian &operator=(T x) {
|
|
if constexpr (sizeof(T) == SIZE) {
|
|
if constexpr (std::endian::native == std::endian::big)
|
|
x = bswap(x);
|
|
memcpy(val, &x, sizeof(T));
|
|
} else {
|
|
static_assert(SIZE == 3);
|
|
val[2] = x >> 16;
|
|
val[1] = x >> 8;
|
|
val[0] = x;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
LittleEndian &operator++() {
|
|
return *this = *this + 1;
|
|
}
|
|
|
|
LittleEndian operator++(int) {
|
|
T ret = *this;
|
|
*this = *this + 1;
|
|
return ret;
|
|
}
|
|
|
|
LittleEndian &operator--() {
|
|
return *this = *this - 1;
|
|
}
|
|
|
|
LittleEndian operator--(int) {
|
|
T ret = *this;
|
|
*this = *this - 1;
|
|
return ret;
|
|
}
|
|
|
|
LittleEndian &operator+=(T x) {
|
|
return *this = *this + x;
|
|
}
|
|
|
|
LittleEndian &operator-=(T x) {
|
|
return *this = *this - x;
|
|
}
|
|
|
|
LittleEndian &operator&=(T x) {
|
|
return *this = *this & x;
|
|
}
|
|
|
|
LittleEndian &operator|=(T x) {
|
|
return *this = *this | x;
|
|
}
|
|
|
|
private:
|
|
u8 val[SIZE];
|
|
};
|
|
|
|
using il16 = LittleEndian<i16>;
|
|
using il32 = LittleEndian<i32>;
|
|
using il64 = LittleEndian<i64>;
|
|
using ul16 = LittleEndian<u16>;
|
|
using ul24 = LittleEndian<u32, 3>;
|
|
using ul32 = LittleEndian<u32>;
|
|
using ul64 = LittleEndian<u64>;
|
|
|
|
template <typename T, int SIZE = sizeof(T)>
|
|
class BigEndian {
|
|
public:
|
|
BigEndian() = default;
|
|
BigEndian(T x) { *this = x; }
|
|
|
|
operator T() const {
|
|
if constexpr (sizeof(T) == SIZE) {
|
|
T x;
|
|
memcpy(&x, val, sizeof(T));
|
|
if constexpr (std::endian::native == std::endian::little)
|
|
x = bswap(x);
|
|
return x;
|
|
} else {
|
|
static_assert(SIZE == 3);
|
|
return (val[0] << 16) | (val[1] << 8) | val[2];
|
|
}
|
|
}
|
|
|
|
BigEndian &operator=(T x) {
|
|
if constexpr (sizeof(T) == SIZE) {
|
|
if constexpr (std::endian::native == std::endian::little)
|
|
x = bswap(x);
|
|
memcpy(val, &x, sizeof(T));
|
|
} else {
|
|
static_assert(SIZE == 3);
|
|
val[0] = x >> 16;
|
|
val[1] = x >> 8;
|
|
val[2] = x;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
BigEndian &operator++() {
|
|
return *this = *this + 1;
|
|
}
|
|
|
|
BigEndian operator++(int) {
|
|
T ret = *this;
|
|
*this = *this + 1;
|
|
return ret;
|
|
}
|
|
|
|
BigEndian &operator--() {
|
|
return *this = *this - 1;
|
|
}
|
|
|
|
BigEndian operator--(int) {
|
|
T ret = *this;
|
|
*this = *this - 1;
|
|
return ret;
|
|
}
|
|
|
|
BigEndian &operator+=(T x) {
|
|
return *this = *this + x;
|
|
}
|
|
|
|
BigEndian &operator-=(T x) {
|
|
return *this = *this - x;
|
|
}
|
|
|
|
BigEndian &operator&=(T x) {
|
|
return *this = *this & x;
|
|
}
|
|
|
|
BigEndian &operator|=(T x) {
|
|
return *this = *this | x;
|
|
}
|
|
|
|
private:
|
|
u8 val[SIZE];
|
|
};
|
|
|
|
using ib16 = BigEndian<i16>;
|
|
using ib32 = BigEndian<i32>;
|
|
using ib64 = BigEndian<i64>;
|
|
using ub16 = BigEndian<u16>;
|
|
using ub24 = BigEndian<u32, 3>;
|
|
using ub32 = BigEndian<u32>;
|
|
using ub64 = BigEndian<u64>;
|
|
|
|
} // namespace mold
|