chore: checkpoint before Python removal

This commit is contained in:
2026-03-26 22:33:59 +00:00
parent 683cec9307
commit e568ddf82a
29972 changed files with 11269302 additions and 2 deletions

View File

@@ -0,0 +1,327 @@
//===-- llvm/Support/Alignment.h - Useful alignment functions ---*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file contains types to represent alignments.
// They are instrumented to guarantee some invariants are preserved and prevent
// invalid manipulations.
//
// - Align represents an alignment in bytes, it is always set and always a valid
// power of two, its minimum value is 1 which means no alignment requirements.
//
// - MaybeAlign is an optional type, it may be undefined or set. When it's set
// you can get the underlying Align type by using the value() method.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_SUPPORT_ALIGNMENT_H_
#define LLVM_SUPPORT_ALIGNMENT_H_
#include "llvm/Support/MathExtras.h"
#include <cassert>
#include <optional>
#ifndef NDEBUG
#include <string>
#endif // NDEBUG
namespace llvm {
#define ALIGN_CHECK_ISPOSITIVE(decl) \
assert(decl > 0 && (#decl " should be defined"))
/// This struct is a compact representation of a valid (non-zero power of two)
/// alignment.
/// It is suitable for use as static global constants.
struct Align {
private:
uint8_t ShiftValue = 0; /// The log2 of the required alignment.
/// ShiftValue is less than 64 by construction.
friend struct MaybeAlign;
friend unsigned Log2(Align);
friend bool operator==(Align Lhs, Align Rhs);
friend bool operator!=(Align Lhs, Align Rhs);
friend bool operator<=(Align Lhs, Align Rhs);
friend bool operator>=(Align Lhs, Align Rhs);
friend bool operator<(Align Lhs, Align Rhs);
friend bool operator>(Align Lhs, Align Rhs);
friend unsigned encode(struct MaybeAlign A);
friend struct MaybeAlign decodeMaybeAlign(unsigned Value);
/// A trivial type to allow construction of constexpr Align.
/// This is currently needed to workaround a bug in GCC 5.3 which prevents
/// definition of constexpr assign operators.
/// https://stackoverflow.com/questions/46756288/explicitly-defaulted-function-cannot-be-declared-as-constexpr-because-the-implic
/// FIXME: Remove this, make all assign operators constexpr and introduce user
/// defined literals when we don't have to support GCC 5.3 anymore.
/// https://llvm.org/docs/GettingStarted.html#getting-a-modern-host-c-toolchain
struct LogValue {
uint8_t Log;
};
public:
/// Default is byte-aligned.
constexpr Align() = default;
/// Do not perform checks in case of copy/move construct/assign, because the
/// checks have been performed when building `Other`.
constexpr Align(const Align &Other) = default;
constexpr Align(Align &&Other) = default;
Align &operator=(const Align &Other) = default;
Align &operator=(Align &&Other) = default;
explicit Align(uint64_t Value) {
assert(Value > 0 && "Value must not be 0");
assert(llvm::isPowerOf2_64(Value) && "Alignment is not a power of 2");
ShiftValue = Log2_64(Value);
assert(ShiftValue < 64 && "Broken invariant");
}
/// This is a hole in the type system and should not be abused.
/// Needed to interact with C for instance.
uint64_t value() const { return uint64_t(1) << ShiftValue; }
// Returns the previous alignment.
Align previous() const {
assert(ShiftValue != 0 && "Undefined operation");
Align Out;
Out.ShiftValue = ShiftValue - 1;
return Out;
}
/// Allow constructions of constexpr Align.
template <size_t kValue> constexpr static Align Constant() {
return LogValue{static_cast<uint8_t>(CTLog2<kValue>())};
}
/// Allow constructions of constexpr Align from types.
/// Compile time equivalent to Align(alignof(T)).
template <typename T> constexpr static Align Of() {
return Constant<std::alignment_of_v<T>>();
}
/// Constexpr constructor from LogValue type.
constexpr Align(LogValue CA) : ShiftValue(CA.Log) {}
};
/// Treats the value 0 as a 1, so Align is always at least 1.
inline Align assumeAligned(uint64_t Value) {
return Value ? Align(Value) : Align();
}
/// This struct is a compact representation of a valid (power of two) or
/// undefined (0) alignment.
struct MaybeAlign : public std::optional<Align> {
private:
using UP = std::optional<Align>;
public:
/// Default is undefined.
MaybeAlign() = default;
/// Do not perform checks in case of copy/move construct/assign, because the
/// checks have been performed when building `Other`.
MaybeAlign(const MaybeAlign &Other) = default;
MaybeAlign &operator=(const MaybeAlign &Other) = default;
MaybeAlign(MaybeAlign &&Other) = default;
MaybeAlign &operator=(MaybeAlign &&Other) = default;
constexpr MaybeAlign(std::nullopt_t None) : UP(None) {}
constexpr MaybeAlign(Align Value) : UP(Value) {}
explicit MaybeAlign(uint64_t Value) {
assert((Value == 0 || llvm::isPowerOf2_64(Value)) &&
"Alignment is neither 0 nor a power of 2");
if (Value)
emplace(Value);
}
/// For convenience, returns a valid alignment or 1 if undefined.
Align valueOrOne() const { return value_or(Align()); }
};
/// Checks that SizeInBytes is a multiple of the alignment.
inline bool isAligned(Align Lhs, uint64_t SizeInBytes) {
return SizeInBytes % Lhs.value() == 0;
}
/// Checks that Addr is a multiple of the alignment.
inline bool isAddrAligned(Align Lhs, const void *Addr) {
return isAligned(Lhs, reinterpret_cast<uintptr_t>(Addr));
}
/// Returns a multiple of A needed to store `Size` bytes.
inline uint64_t alignTo(uint64_t Size, Align A) {
const uint64_t Value = A.value();
// The following line is equivalent to `(Size + Value - 1) / Value * Value`.
// The division followed by a multiplication can be thought of as a right
// shift followed by a left shift which zeros out the extra bits produced in
// the bump; `~(Value - 1)` is a mask where all those bits being zeroed out
// are just zero.
// Most compilers can generate this code but the pattern may be missed when
// multiple functions gets inlined.
return (Size + Value - 1) & ~(Value - 1U);
}
/// If non-zero \p Skew is specified, the return value will be a minimal integer
/// that is greater than or equal to \p Size and equal to \p A * N + \p Skew for
/// some integer N. If \p Skew is larger than \p A, its value is adjusted to '\p
/// Skew mod \p A'.
///
/// Examples:
/// \code
/// alignTo(5, Align(8), 7) = 7
/// alignTo(17, Align(8), 1) = 17
/// alignTo(~0LL, Align(8), 3) = 3
/// \endcode
inline uint64_t alignTo(uint64_t Size, Align A, uint64_t Skew) {
const uint64_t Value = A.value();
Skew %= Value;
return alignTo(Size - Skew, A) + Skew;
}
/// Aligns `Addr` to `Alignment` bytes, rounding up.
inline uintptr_t alignAddr(const void *Addr, Align Alignment) {
uintptr_t ArithAddr = reinterpret_cast<uintptr_t>(Addr);
assert(static_cast<uintptr_t>(ArithAddr + Alignment.value() - 1) >=
ArithAddr &&
"Overflow");
return alignTo(ArithAddr, Alignment);
}
/// Returns the offset to the next integer (mod 2**64) that is greater than
/// or equal to \p Value and is a multiple of \p Align.
inline uint64_t offsetToAlignment(uint64_t Value, Align Alignment) {
return alignTo(Value, Alignment) - Value;
}
/// Returns the necessary adjustment for aligning `Addr` to `Alignment`
/// bytes, rounding up.
inline uint64_t offsetToAlignedAddr(const void *Addr, Align Alignment) {
return offsetToAlignment(reinterpret_cast<uintptr_t>(Addr), Alignment);
}
/// Returns the log2 of the alignment.
inline unsigned Log2(Align A) { return A.ShiftValue; }
/// Returns the alignment that satisfies both alignments.
/// Same semantic as MinAlign.
inline Align commonAlignment(Align A, uint64_t Offset) {
return Align(MinAlign(A.value(), Offset));
}
/// Returns a representation of the alignment that encodes undefined as 0.
inline unsigned encode(MaybeAlign A) { return A ? A->ShiftValue + 1 : 0; }
/// Dual operation of the encode function above.
inline MaybeAlign decodeMaybeAlign(unsigned Value) {
if (Value == 0)
return MaybeAlign();
Align Out;
Out.ShiftValue = Value - 1;
return Out;
}
/// Returns a representation of the alignment, the encoded value is positive by
/// definition.
inline unsigned encode(Align A) { return encode(MaybeAlign(A)); }
/// Comparisons between Align and scalars. Rhs must be positive.
inline bool operator==(Align Lhs, uint64_t Rhs) {
ALIGN_CHECK_ISPOSITIVE(Rhs);
return Lhs.value() == Rhs;
}
inline bool operator!=(Align Lhs, uint64_t Rhs) {
ALIGN_CHECK_ISPOSITIVE(Rhs);
return Lhs.value() != Rhs;
}
inline bool operator<=(Align Lhs, uint64_t Rhs) {
ALIGN_CHECK_ISPOSITIVE(Rhs);
return Lhs.value() <= Rhs;
}
inline bool operator>=(Align Lhs, uint64_t Rhs) {
ALIGN_CHECK_ISPOSITIVE(Rhs);
return Lhs.value() >= Rhs;
}
inline bool operator<(Align Lhs, uint64_t Rhs) {
ALIGN_CHECK_ISPOSITIVE(Rhs);
return Lhs.value() < Rhs;
}
inline bool operator>(Align Lhs, uint64_t Rhs) {
ALIGN_CHECK_ISPOSITIVE(Rhs);
return Lhs.value() > Rhs;
}
/// Comparisons operators between Align.
inline bool operator==(Align Lhs, Align Rhs) {
return Lhs.ShiftValue == Rhs.ShiftValue;
}
inline bool operator!=(Align Lhs, Align Rhs) {
return Lhs.ShiftValue != Rhs.ShiftValue;
}
inline bool operator<=(Align Lhs, Align Rhs) {
return Lhs.ShiftValue <= Rhs.ShiftValue;
}
inline bool operator>=(Align Lhs, Align Rhs) {
return Lhs.ShiftValue >= Rhs.ShiftValue;
}
inline bool operator<(Align Lhs, Align Rhs) {
return Lhs.ShiftValue < Rhs.ShiftValue;
}
inline bool operator>(Align Lhs, Align Rhs) {
return Lhs.ShiftValue > Rhs.ShiftValue;
}
// Don't allow relational comparisons with MaybeAlign.
bool operator<=(Align Lhs, MaybeAlign Rhs) = delete;
bool operator>=(Align Lhs, MaybeAlign Rhs) = delete;
bool operator<(Align Lhs, MaybeAlign Rhs) = delete;
bool operator>(Align Lhs, MaybeAlign Rhs) = delete;
bool operator<=(MaybeAlign Lhs, Align Rhs) = delete;
bool operator>=(MaybeAlign Lhs, Align Rhs) = delete;
bool operator<(MaybeAlign Lhs, Align Rhs) = delete;
bool operator>(MaybeAlign Lhs, Align Rhs) = delete;
bool operator<=(MaybeAlign Lhs, MaybeAlign Rhs) = delete;
bool operator>=(MaybeAlign Lhs, MaybeAlign Rhs) = delete;
bool operator<(MaybeAlign Lhs, MaybeAlign Rhs) = delete;
bool operator>(MaybeAlign Lhs, MaybeAlign Rhs) = delete;
// Allow equality comparisons between Align and MaybeAlign.
inline bool operator==(MaybeAlign Lhs, Align Rhs) { return Lhs && *Lhs == Rhs; }
inline bool operator!=(MaybeAlign Lhs, Align Rhs) { return !(Lhs == Rhs); }
inline bool operator==(Align Lhs, MaybeAlign Rhs) { return Rhs == Lhs; }
inline bool operator!=(Align Lhs, MaybeAlign Rhs) { return !(Rhs == Lhs); }
// Allow equality comparisons with MaybeAlign.
inline bool operator==(MaybeAlign Lhs, MaybeAlign Rhs) {
return (Lhs && Rhs && (*Lhs == *Rhs)) || (!Lhs && !Rhs);
}
inline bool operator!=(MaybeAlign Lhs, MaybeAlign Rhs) { return !(Lhs == Rhs); }
// Allow equality comparisons with std::nullopt.
inline bool operator==(MaybeAlign Lhs, std::nullopt_t) { return !bool(Lhs); }
inline bool operator!=(MaybeAlign Lhs, std::nullopt_t) { return bool(Lhs); }
inline bool operator==(std::nullopt_t, MaybeAlign Rhs) { return !bool(Rhs); }
inline bool operator!=(std::nullopt_t, MaybeAlign Rhs) { return bool(Rhs); }
#ifndef NDEBUG
// For usage in LLVM_DEBUG macros.
inline std::string DebugStr(const Align &A) {
return std::to_string(A.value());
}
// For usage in LLVM_DEBUG macros.
inline std::string DebugStr(const MaybeAlign &MA) {
if (MA)
return std::to_string(MA->value());
return "None";
}
#endif // NDEBUG
#undef ALIGN_CHECK_ISPOSITIVE
} // namespace llvm
#endif // LLVM_SUPPORT_ALIGNMENT_H_

View File

@@ -0,0 +1,431 @@
//===- Archive.h - ar archive file format -----------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file declares the ar archive file format class.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_OBJECT_ARCHIVE_H
#define LLVM_OBJECT_ARCHIVE_H
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/fallible_iterator.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/Object/Binary.h"
#include "llvm/Support/Chrono.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/MemoryBuffer.h"
#include <cassert>
#include <cstdint>
#include <memory>
#include <string>
#include <vector>
namespace llvm {
namespace object {
const char ArchiveMagic[] = "!<arch>\n";
const char ThinArchiveMagic[] = "!<thin>\n";
const char BigArchiveMagic[] = "<bigaf>\n";
class Archive;
class AbstractArchiveMemberHeader {
protected:
AbstractArchiveMemberHeader(const Archive *Parent) : Parent(Parent){};
public:
friend class Archive;
virtual std::unique_ptr<AbstractArchiveMemberHeader> clone() const = 0;
virtual ~AbstractArchiveMemberHeader() = default;
/// Get the name without looking up long names.
virtual Expected<StringRef> getRawName() const = 0;
virtual StringRef getRawAccessMode() const = 0;
virtual StringRef getRawLastModified() const = 0;
virtual StringRef getRawUID() const = 0;
virtual StringRef getRawGID() const = 0;
/// Get the name looking up long names.
virtual Expected<StringRef> getName(uint64_t Size) const = 0;
virtual Expected<uint64_t> getSize() const = 0;
virtual uint64_t getOffset() const = 0;
/// Get next file member location.
virtual Expected<const char *> getNextChildLoc() const = 0;
virtual Expected<bool> isThin() const = 0;
Expected<sys::fs::perms> getAccessMode() const;
Expected<sys::TimePoint<std::chrono::seconds>> getLastModified() const;
Expected<unsigned> getUID() const;
Expected<unsigned> getGID() const;
/// Returns the size in bytes of the format-defined member header of the
/// concrete archive type.
virtual uint64_t getSizeOf() const = 0;
const Archive *Parent;
};
template <typename T>
class CommonArchiveMemberHeader : public AbstractArchiveMemberHeader {
public:
CommonArchiveMemberHeader(const Archive *Parent, const T *RawHeaderPtr)
: AbstractArchiveMemberHeader(Parent), ArMemHdr(RawHeaderPtr){};
StringRef getRawAccessMode() const override;
StringRef getRawLastModified() const override;
StringRef getRawUID() const override;
StringRef getRawGID() const override;
uint64_t getOffset() const override;
uint64_t getSizeOf() const override { return sizeof(T); }
T const *ArMemHdr;
};
struct UnixArMemHdrType {
char Name[16];
char LastModified[12];
char UID[6];
char GID[6];
char AccessMode[8];
char Size[10]; ///< Size of data, not including header or padding.
char Terminator[2];
};
class ArchiveMemberHeader : public CommonArchiveMemberHeader<UnixArMemHdrType> {
public:
ArchiveMemberHeader(const Archive *Parent, const char *RawHeaderPtr,
uint64_t Size, Error *Err);
std::unique_ptr<AbstractArchiveMemberHeader> clone() const override {
return std::make_unique<ArchiveMemberHeader>(*this);
}
Expected<StringRef> getRawName() const override;
Expected<StringRef> getName(uint64_t Size) const override;
Expected<uint64_t> getSize() const override;
Expected<const char *> getNextChildLoc() const override;
Expected<bool> isThin() const override;
};
// File Member Header
struct BigArMemHdrType {
char Size[20]; // File member size in decimal
char NextOffset[20]; // Next member offset in decimal
char PrevOffset[20]; // Previous member offset in decimal
char LastModified[12];
char UID[12];
char GID[12];
char AccessMode[12];
char NameLen[4]; // File member name length in decimal
union {
char Name[2]; // Start of member name
char Terminator[2];
};
};
// Define file member header of AIX big archive.
class BigArchiveMemberHeader
: public CommonArchiveMemberHeader<BigArMemHdrType> {
public:
BigArchiveMemberHeader(Archive const *Parent, const char *RawHeaderPtr,
uint64_t Size, Error *Err);
std::unique_ptr<AbstractArchiveMemberHeader> clone() const override {
return std::make_unique<BigArchiveMemberHeader>(*this);
}
Expected<StringRef> getRawName() const override;
Expected<uint64_t> getRawNameSize() const;
Expected<StringRef> getName(uint64_t Size) const override;
Expected<uint64_t> getSize() const override;
Expected<const char *> getNextChildLoc() const override;
Expected<uint64_t> getNextOffset() const;
Expected<bool> isThin() const override { return false; }
};
class Archive : public Binary {
virtual void anchor();
public:
class Child {
friend Archive;
friend AbstractArchiveMemberHeader;
const Archive *Parent;
std::unique_ptr<AbstractArchiveMemberHeader> Header;
/// Includes header but not padding byte.
StringRef Data;
/// Offset from Data to the start of the file.
uint16_t StartOfFile;
Expected<bool> isThinMember() const;
public:
Child(const Archive *Parent, const char *Start, Error *Err);
Child(const Archive *Parent, StringRef Data, uint16_t StartOfFile);
Child(const Child &C)
: Parent(C.Parent), Data(C.Data), StartOfFile(C.StartOfFile) {
if (C.Header)
Header = C.Header->clone();
}
Child(Child &&C) {
Parent = std::move(C.Parent);
Header = std::move(C.Header);
Data = C.Data;
StartOfFile = C.StartOfFile;
}
Child &operator=(Child &&C) noexcept {
if (&C == this)
return *this;
Parent = std::move(C.Parent);
Header = std::move(C.Header);
Data = C.Data;
StartOfFile = C.StartOfFile;
return *this;
}
Child &operator=(const Child &C) {
if (&C == this)
return *this;
Parent = C.Parent;
if (C.Header)
Header = C.Header->clone();
Data = C.Data;
StartOfFile = C.StartOfFile;
return *this;
}
bool operator==(const Child &other) const {
assert(!Parent || !other.Parent || Parent == other.Parent);
return Data.begin() == other.Data.begin();
}
const Archive *getParent() const { return Parent; }
Expected<Child> getNext() const;
Expected<StringRef> getName() const;
Expected<std::string> getFullName() const;
Expected<StringRef> getRawName() const { return Header->getRawName(); }
Expected<sys::TimePoint<std::chrono::seconds>> getLastModified() const {
return Header->getLastModified();
}
StringRef getRawLastModified() const {
return Header->getRawLastModified();
}
Expected<unsigned> getUID() const { return Header->getUID(); }
Expected<unsigned> getGID() const { return Header->getGID(); }
Expected<sys::fs::perms> getAccessMode() const {
return Header->getAccessMode();
}
/// \return the size of the archive member without the header or padding.
Expected<uint64_t> getSize() const;
/// \return the size in the archive header for this member.
Expected<uint64_t> getRawSize() const;
Expected<StringRef> getBuffer() const;
uint64_t getChildOffset() const;
uint64_t getDataOffset() const { return getChildOffset() + StartOfFile; }
Expected<MemoryBufferRef> getMemoryBufferRef() const;
Expected<std::unique_ptr<Binary>>
getAsBinary(LLVMContext *Context = nullptr) const;
};
class ChildFallibleIterator {
Child C;
public:
ChildFallibleIterator() : C(Child(nullptr, nullptr, nullptr)) {}
ChildFallibleIterator(const Child &C) : C(C) {}
const Child *operator->() const { return &C; }
const Child &operator*() const { return C; }
bool operator==(const ChildFallibleIterator &other) const {
// Ignore errors here: If an error occurred during increment then getNext
// will have been set to child_end(), and the following comparison should
// do the right thing.
return C == other.C;
}
bool operator!=(const ChildFallibleIterator &other) const {
return !(*this == other);
}
Error inc() {
auto NextChild = C.getNext();
if (!NextChild)
return NextChild.takeError();
C = std::move(*NextChild);
return Error::success();
}
};
using child_iterator = fallible_iterator<ChildFallibleIterator>;
class Symbol {
const Archive *Parent;
uint32_t SymbolIndex;
uint32_t StringIndex; // Extra index to the string.
public:
Symbol(const Archive *p, uint32_t symi, uint32_t stri)
: Parent(p), SymbolIndex(symi), StringIndex(stri) {}
bool operator==(const Symbol &other) const {
return (Parent == other.Parent) && (SymbolIndex == other.SymbolIndex);
}
StringRef getName() const;
Expected<Child> getMember() const;
Symbol getNext() const;
bool isECSymbol() const;
};
class symbol_iterator {
Symbol symbol;
public:
symbol_iterator(const Symbol &s) : symbol(s) {}
const Symbol *operator->() const { return &symbol; }
const Symbol &operator*() const { return symbol; }
bool operator==(const symbol_iterator &other) const {
return symbol == other.symbol;
}
bool operator!=(const symbol_iterator &other) const {
return !(*this == other);
}
symbol_iterator &operator++() { // Preincrement
symbol = symbol.getNext();
return *this;
}
};
Archive(MemoryBufferRef Source, Error &Err);
static Expected<std::unique_ptr<Archive>> create(MemoryBufferRef Source);
/// Size field is 10 decimal digits long
static const uint64_t MaxMemberSize = 9999999999;
enum Kind { K_GNU, K_GNU64, K_BSD, K_DARWIN, K_DARWIN64, K_COFF, K_AIXBIG };
Kind kind() const { return (Kind)Format; }
bool isThin() const { return IsThin; }
static object::Archive::Kind getDefaultKind();
static object::Archive::Kind getDefaultKindForTriple(const Triple &T);
child_iterator child_begin(Error &Err, bool SkipInternal = true) const;
child_iterator child_end() const;
iterator_range<child_iterator> children(Error &Err,
bool SkipInternal = true) const {
return make_range(child_begin(Err, SkipInternal), child_end());
}
symbol_iterator symbol_begin() const;
symbol_iterator symbol_end() const;
iterator_range<symbol_iterator> symbols() const {
return make_range(symbol_begin(), symbol_end());
}
Expected<iterator_range<symbol_iterator>> ec_symbols() const;
static bool classof(Binary const *v) { return v->isArchive(); }
// check if a symbol is in the archive
Expected<std::optional<Child>> findSym(StringRef name) const;
virtual bool isEmpty() const;
bool hasSymbolTable() const;
StringRef getSymbolTable() const { return SymbolTable; }
StringRef getStringTable() const { return StringTable; }
uint32_t getNumberOfSymbols() const;
uint32_t getNumberOfECSymbols() const;
virtual uint64_t getFirstChildOffset() const { return getArchiveMagicLen(); }
std::vector<std::unique_ptr<MemoryBuffer>> takeThinBuffers() {
return std::move(ThinBuffers);
}
std::unique_ptr<AbstractArchiveMemberHeader>
createArchiveMemberHeader(const char *RawHeaderPtr, uint64_t Size,
Error *Err) const;
protected:
uint64_t getArchiveMagicLen() const;
void setFirstRegular(const Child &C);
StringRef SymbolTable;
StringRef ECSymbolTable;
StringRef StringTable;
private:
StringRef FirstRegularData;
uint16_t FirstRegularStartOfFile = -1;
unsigned Format : 3;
unsigned IsThin : 1;
mutable std::vector<std::unique_ptr<MemoryBuffer>> ThinBuffers;
};
class BigArchive : public Archive {
public:
/// Fixed-Length Header.
struct FixLenHdr {
char Magic[sizeof(BigArchiveMagic) - 1]; ///< Big archive magic string.
char MemOffset[20]; ///< Offset to member table.
char GlobSymOffset[20]; ///< Offset to global symbol table.
char
GlobSym64Offset[20]; ///< Offset global symbol table for 64-bit objects.
char FirstChildOffset[20]; ///< Offset to first archive member.
char LastChildOffset[20]; ///< Offset to last archive member.
char FreeOffset[20]; ///< Offset to first mem on free list.
};
const FixLenHdr *ArFixLenHdr;
uint64_t FirstChildOffset = 0;
uint64_t LastChildOffset = 0;
std::string MergedGlobalSymtabBuf;
bool Has32BitGlobalSymtab = false;
bool Has64BitGlobalSymtab = false;
public:
BigArchive(MemoryBufferRef Source, Error &Err);
uint64_t getFirstChildOffset() const override { return FirstChildOffset; }
uint64_t getLastChildOffset() const { return LastChildOffset; }
bool isEmpty() const override { return getFirstChildOffset() == 0; }
bool has32BitGlobalSymtab() { return Has32BitGlobalSymtab; }
bool has64BitGlobalSymtab() { return Has64BitGlobalSymtab; }
};
} // end namespace object
} // end namespace llvm
#endif // LLVM_OBJECT_ARCHIVE_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,77 @@
//===- ArchiveWriter.h - ar archive file format writer ----------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Declares the writeArchive function for writing an archive file.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_OBJECT_ARCHIVEWRITER_H
#define LLVM_OBJECT_ARCHIVEWRITER_H
#include "llvm/Object/Archive.h"
namespace llvm {
struct NewArchiveMember {
std::unique_ptr<MemoryBuffer> Buf;
StringRef MemberName;
sys::TimePoint<std::chrono::seconds> ModTime;
unsigned UID = 0, GID = 0, Perms = 0644;
NewArchiveMember() = default;
NewArchiveMember(MemoryBufferRef BufRef);
// Detect the archive format from the object or bitcode file. This helps
// assume the archive format when creating or editing archives in the case
// one isn't explicitly set.
object::Archive::Kind detectKindFromObject() const;
static Expected<NewArchiveMember>
getOldMember(const object::Archive::Child &OldMember, bool Deterministic);
static Expected<NewArchiveMember> getFile(StringRef FileName,
bool Deterministic);
};
Expected<std::string> computeArchiveRelativePath(StringRef From, StringRef To);
enum class SymtabWritingMode {
NoSymtab, // Do not write symbol table.
NormalSymtab, // Write symbol table. For the Big Archive format, write both
// 32-bit and 64-bit symbol tables.
BigArchive32, // Only write the 32-bit symbol table.
BigArchive64 // Only write the 64-bit symbol table.
};
void warnToStderr(Error Err);
// Write an archive directly to an output stream.
Error writeArchiveToStream(raw_ostream &Out,
ArrayRef<NewArchiveMember> NewMembers,
SymtabWritingMode WriteSymtab,
object::Archive::Kind Kind, bool Deterministic,
bool Thin, std::optional<bool> IsEC = std::nullopt,
function_ref<void(Error)> Warn = warnToStderr);
Error writeArchive(StringRef ArcName, ArrayRef<NewArchiveMember> NewMembers,
SymtabWritingMode WriteSymtab, object::Archive::Kind Kind,
bool Deterministic, bool Thin,
std::unique_ptr<MemoryBuffer> OldArchiveBuf = nullptr,
std::optional<bool> IsEC = std::nullopt,
function_ref<void(Error)> Warn = warnToStderr);
// writeArchiveToBuffer is similar to writeArchive but returns the Archive in a
// buffer instead of writing it out to a file.
Expected<std::unique_ptr<MemoryBuffer>>
writeArchiveToBuffer(ArrayRef<NewArchiveMember> NewMembers,
SymtabWritingMode WriteSymtab, object::Archive::Kind Kind,
bool Deterministic, bool Thin,
function_ref<void(Error)> Warn = warnToStderr);
}
#endif

View File

@@ -0,0 +1,859 @@
//===-- llvm/BinaryFormat/COFF.h --------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file contains an definitions used in Windows COFF Files.
//
// Structures and enums defined within this file where created using
// information from Microsoft's publicly available PE/COFF format document:
//
// Microsoft Portable Executable and Common Object File Format Specification
// Revision 8.1 - February 15, 2008
//
// As of 5/2/2010, hosted by Microsoft at:
// http://www.microsoft.com/whdc/system/platform/firmware/pecoff.mspx
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_BINARYFORMAT_COFF_H
#define LLVM_BINARYFORMAT_COFF_H
#include "llvm/Support/DataTypes.h"
#include <cassert>
namespace llvm {
namespace COFF {
// The maximum number of sections that a COFF object can have (inclusive).
const int32_t MaxNumberOfSections16 = 65279;
// The PE signature bytes that follows the DOS stub header.
static const char PEMagic[] = {'P', 'E', '\0', '\0'};
static const char BigObjMagic[] = {
'\xc7', '\xa1', '\xba', '\xd1', '\xee', '\xba', '\xa9', '\x4b',
'\xaf', '\x20', '\xfa', '\xf6', '\x6a', '\xa4', '\xdc', '\xb8',
};
static const char ClGlObjMagic[] = {
'\x38', '\xfe', '\xb3', '\x0c', '\xa5', '\xd9', '\xab', '\x4d',
'\xac', '\x9b', '\xd6', '\xb6', '\x22', '\x26', '\x53', '\xc2',
};
// The signature bytes that start a .res file.
static const char WinResMagic[] = {
'\x00', '\x00', '\x00', '\x00', '\x20', '\x00', '\x00', '\x00',
'\xff', '\xff', '\x00', '\x00', '\xff', '\xff', '\x00', '\x00',
};
// Sizes in bytes of various things in the COFF format.
enum {
Header16Size = 20,
Header32Size = 56,
NameSize = 8,
Symbol16Size = 18,
Symbol32Size = 20,
SectionSize = 40,
RelocationSize = 10
};
struct header {
uint16_t Machine;
int32_t NumberOfSections;
uint32_t TimeDateStamp;
uint32_t PointerToSymbolTable;
uint32_t NumberOfSymbols;
uint16_t SizeOfOptionalHeader;
uint16_t Characteristics;
};
struct BigObjHeader {
enum : uint16_t { MinBigObjectVersion = 2 };
uint16_t Sig1; ///< Must be IMAGE_FILE_MACHINE_UNKNOWN (0).
uint16_t Sig2; ///< Must be 0xFFFF.
uint16_t Version;
uint16_t Machine;
uint32_t TimeDateStamp;
uint8_t UUID[16];
uint32_t unused1;
uint32_t unused2;
uint32_t unused3;
uint32_t unused4;
uint32_t NumberOfSections;
uint32_t PointerToSymbolTable;
uint32_t NumberOfSymbols;
};
enum MachineTypes : unsigned {
MT_Invalid = 0xffff,
IMAGE_FILE_MACHINE_UNKNOWN = 0x0,
IMAGE_FILE_MACHINE_AM33 = 0x1D3,
IMAGE_FILE_MACHINE_AMD64 = 0x8664,
IMAGE_FILE_MACHINE_ARM = 0x1C0,
IMAGE_FILE_MACHINE_ARMNT = 0x1C4,
IMAGE_FILE_MACHINE_ARM64 = 0xAA64,
IMAGE_FILE_MACHINE_ARM64EC = 0xA641,
IMAGE_FILE_MACHINE_ARM64X = 0xA64E,
IMAGE_FILE_MACHINE_EBC = 0xEBC,
IMAGE_FILE_MACHINE_I386 = 0x14C,
IMAGE_FILE_MACHINE_IA64 = 0x200,
IMAGE_FILE_MACHINE_M32R = 0x9041,
IMAGE_FILE_MACHINE_MIPS16 = 0x266,
IMAGE_FILE_MACHINE_MIPSFPU = 0x366,
IMAGE_FILE_MACHINE_MIPSFPU16 = 0x466,
IMAGE_FILE_MACHINE_POWERPC = 0x1F0,
IMAGE_FILE_MACHINE_POWERPCFP = 0x1F1,
IMAGE_FILE_MACHINE_R4000 = 0x166,
IMAGE_FILE_MACHINE_RISCV32 = 0x5032,
IMAGE_FILE_MACHINE_RISCV64 = 0x5064,
IMAGE_FILE_MACHINE_RISCV128 = 0x5128,
IMAGE_FILE_MACHINE_SH3 = 0x1A2,
IMAGE_FILE_MACHINE_SH3DSP = 0x1A3,
IMAGE_FILE_MACHINE_SH4 = 0x1A6,
IMAGE_FILE_MACHINE_SH5 = 0x1A8,
IMAGE_FILE_MACHINE_THUMB = 0x1C2,
IMAGE_FILE_MACHINE_WCEMIPSV2 = 0x169
};
template <typename T> bool isArm64EC(T Machine) {
return Machine == IMAGE_FILE_MACHINE_ARM64EC ||
Machine == IMAGE_FILE_MACHINE_ARM64X;
}
template <typename T> bool isAnyArm64(T Machine) {
return Machine == IMAGE_FILE_MACHINE_ARM64 || isArm64EC(Machine);
}
template <typename T> bool is64Bit(T Machine) {
return Machine == IMAGE_FILE_MACHINE_AMD64 || isAnyArm64(Machine);
}
enum Characteristics : unsigned {
C_Invalid = 0,
/// The file does not contain base relocations and must be loaded at its
/// preferred base. If this cannot be done, the loader will error.
IMAGE_FILE_RELOCS_STRIPPED = 0x0001,
/// The file is valid and can be run.
IMAGE_FILE_EXECUTABLE_IMAGE = 0x0002,
/// COFF line numbers have been stripped. This is deprecated and should be
/// 0.
IMAGE_FILE_LINE_NUMS_STRIPPED = 0x0004,
/// COFF symbol table entries for local symbols have been removed. This is
/// deprecated and should be 0.
IMAGE_FILE_LOCAL_SYMS_STRIPPED = 0x0008,
/// Aggressively trim working set. This is deprecated and must be 0.
IMAGE_FILE_AGGRESSIVE_WS_TRIM = 0x0010,
/// Image can handle > 2GiB addresses.
IMAGE_FILE_LARGE_ADDRESS_AWARE = 0x0020,
/// Little endian: the LSB precedes the MSB in memory. This is deprecated
/// and should be 0.
IMAGE_FILE_BYTES_REVERSED_LO = 0x0080,
/// Machine is based on a 32bit word architecture.
IMAGE_FILE_32BIT_MACHINE = 0x0100,
/// Debugging info has been removed.
IMAGE_FILE_DEBUG_STRIPPED = 0x0200,
/// If the image is on removable media, fully load it and copy it to swap.
IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP = 0x0400,
/// If the image is on network media, fully load it and copy it to swap.
IMAGE_FILE_NET_RUN_FROM_SWAP = 0x0800,
/// The image file is a system file, not a user program.
IMAGE_FILE_SYSTEM = 0x1000,
/// The image file is a DLL.
IMAGE_FILE_DLL = 0x2000,
/// This file should only be run on a uniprocessor machine.
IMAGE_FILE_UP_SYSTEM_ONLY = 0x4000,
/// Big endian: the MSB precedes the LSB in memory. This is deprecated
/// and should be 0.
IMAGE_FILE_BYTES_REVERSED_HI = 0x8000
};
enum ResourceTypeID : unsigned {
RID_Cursor = 1,
RID_Bitmap = 2,
RID_Icon = 3,
RID_Menu = 4,
RID_Dialog = 5,
RID_String = 6,
RID_FontDir = 7,
RID_Font = 8,
RID_Accelerator = 9,
RID_RCData = 10,
RID_MessageTable = 11,
RID_Group_Cursor = 12,
RID_Group_Icon = 14,
RID_Version = 16,
RID_DLGInclude = 17,
RID_PlugPlay = 19,
RID_VXD = 20,
RID_AniCursor = 21,
RID_AniIcon = 22,
RID_HTML = 23,
RID_Manifest = 24,
};
struct symbol {
char Name[NameSize];
uint32_t Value;
int32_t SectionNumber;
uint16_t Type;
uint8_t StorageClass;
uint8_t NumberOfAuxSymbols;
};
enum SymbolSectionNumber : int32_t {
IMAGE_SYM_DEBUG = -2,
IMAGE_SYM_ABSOLUTE = -1,
IMAGE_SYM_UNDEFINED = 0
};
/// Storage class tells where and what the symbol represents
enum SymbolStorageClass {
SSC_Invalid = 0xff,
IMAGE_SYM_CLASS_END_OF_FUNCTION = -1, ///< Physical end of function
IMAGE_SYM_CLASS_NULL = 0, ///< No symbol
IMAGE_SYM_CLASS_AUTOMATIC = 1, ///< Stack variable
IMAGE_SYM_CLASS_EXTERNAL = 2, ///< External symbol
IMAGE_SYM_CLASS_STATIC = 3, ///< Static
IMAGE_SYM_CLASS_REGISTER = 4, ///< Register variable
IMAGE_SYM_CLASS_EXTERNAL_DEF = 5, ///< External definition
IMAGE_SYM_CLASS_LABEL = 6, ///< Label
IMAGE_SYM_CLASS_UNDEFINED_LABEL = 7, ///< Undefined label
IMAGE_SYM_CLASS_MEMBER_OF_STRUCT = 8, ///< Member of structure
IMAGE_SYM_CLASS_ARGUMENT = 9, ///< Function argument
IMAGE_SYM_CLASS_STRUCT_TAG = 10, ///< Structure tag
IMAGE_SYM_CLASS_MEMBER_OF_UNION = 11, ///< Member of union
IMAGE_SYM_CLASS_UNION_TAG = 12, ///< Union tag
IMAGE_SYM_CLASS_TYPE_DEFINITION = 13, ///< Type definition
IMAGE_SYM_CLASS_UNDEFINED_STATIC = 14, ///< Undefined static
IMAGE_SYM_CLASS_ENUM_TAG = 15, ///< Enumeration tag
IMAGE_SYM_CLASS_MEMBER_OF_ENUM = 16, ///< Member of enumeration
IMAGE_SYM_CLASS_REGISTER_PARAM = 17, ///< Register parameter
IMAGE_SYM_CLASS_BIT_FIELD = 18, ///< Bit field
/// ".bb" or ".eb" - beginning or end of block
IMAGE_SYM_CLASS_BLOCK = 100,
/// ".bf" or ".ef" - beginning or end of function
IMAGE_SYM_CLASS_FUNCTION = 101,
IMAGE_SYM_CLASS_END_OF_STRUCT = 102, ///< End of structure
IMAGE_SYM_CLASS_FILE = 103, ///< File name
/// Line number, reformatted as symbol
IMAGE_SYM_CLASS_SECTION = 104,
IMAGE_SYM_CLASS_WEAK_EXTERNAL = 105, ///< Duplicate tag
/// External symbol in dmert public lib
IMAGE_SYM_CLASS_CLR_TOKEN = 107
};
enum SymbolBaseType : unsigned {
IMAGE_SYM_TYPE_NULL = 0, ///< No type information or unknown base type.
IMAGE_SYM_TYPE_VOID = 1, ///< Used with void pointers and functions.
IMAGE_SYM_TYPE_CHAR = 2, ///< A character (signed byte).
IMAGE_SYM_TYPE_SHORT = 3, ///< A 2-byte signed integer.
IMAGE_SYM_TYPE_INT = 4, ///< A natural integer type on the target.
IMAGE_SYM_TYPE_LONG = 5, ///< A 4-byte signed integer.
IMAGE_SYM_TYPE_FLOAT = 6, ///< A 4-byte floating-point number.
IMAGE_SYM_TYPE_DOUBLE = 7, ///< An 8-byte floating-point number.
IMAGE_SYM_TYPE_STRUCT = 8, ///< A structure.
IMAGE_SYM_TYPE_UNION = 9, ///< An union.
IMAGE_SYM_TYPE_ENUM = 10, ///< An enumerated type.
IMAGE_SYM_TYPE_MOE = 11, ///< A member of enumeration (a specific value).
IMAGE_SYM_TYPE_BYTE = 12, ///< A byte; unsigned 1-byte integer.
IMAGE_SYM_TYPE_WORD = 13, ///< A word; unsigned 2-byte integer.
IMAGE_SYM_TYPE_UINT = 14, ///< An unsigned integer of natural size.
IMAGE_SYM_TYPE_DWORD = 15 ///< An unsigned 4-byte integer.
};
enum SymbolComplexType : unsigned {
IMAGE_SYM_DTYPE_NULL = 0, ///< No complex type; simple scalar variable.
IMAGE_SYM_DTYPE_POINTER = 1, ///< A pointer to base type.
IMAGE_SYM_DTYPE_FUNCTION = 2, ///< A function that returns a base type.
IMAGE_SYM_DTYPE_ARRAY = 3, ///< An array of base type.
/// Type is formed as (base + (derived << SCT_COMPLEX_TYPE_SHIFT))
SCT_COMPLEX_TYPE_SHIFT = 4
};
enum AuxSymbolType { IMAGE_AUX_SYMBOL_TYPE_TOKEN_DEF = 1 };
struct section {
char Name[NameSize];
uint32_t VirtualSize;
uint32_t VirtualAddress;
uint32_t SizeOfRawData;
uint32_t PointerToRawData;
uint32_t PointerToRelocations;
uint32_t PointerToLineNumbers;
uint16_t NumberOfRelocations;
uint16_t NumberOfLineNumbers;
uint32_t Characteristics;
};
enum SectionCharacteristics : uint32_t {
SC_Invalid = 0xffffffff,
IMAGE_SCN_TYPE_NOLOAD = 0x00000002,
IMAGE_SCN_TYPE_NO_PAD = 0x00000008,
IMAGE_SCN_CNT_CODE = 0x00000020,
IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040,
IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080,
IMAGE_SCN_LNK_OTHER = 0x00000100,
IMAGE_SCN_LNK_INFO = 0x00000200,
IMAGE_SCN_LNK_REMOVE = 0x00000800,
IMAGE_SCN_LNK_COMDAT = 0x00001000,
IMAGE_SCN_GPREL = 0x00008000,
IMAGE_SCN_MEM_PURGEABLE = 0x00020000,
IMAGE_SCN_MEM_16BIT = 0x00020000,
IMAGE_SCN_MEM_LOCKED = 0x00040000,
IMAGE_SCN_MEM_PRELOAD = 0x00080000,
IMAGE_SCN_ALIGN_1BYTES = 0x00100000,
IMAGE_SCN_ALIGN_2BYTES = 0x00200000,
IMAGE_SCN_ALIGN_4BYTES = 0x00300000,
IMAGE_SCN_ALIGN_8BYTES = 0x00400000,
IMAGE_SCN_ALIGN_16BYTES = 0x00500000,
IMAGE_SCN_ALIGN_32BYTES = 0x00600000,
IMAGE_SCN_ALIGN_64BYTES = 0x00700000,
IMAGE_SCN_ALIGN_128BYTES = 0x00800000,
IMAGE_SCN_ALIGN_256BYTES = 0x00900000,
IMAGE_SCN_ALIGN_512BYTES = 0x00A00000,
IMAGE_SCN_ALIGN_1024BYTES = 0x00B00000,
IMAGE_SCN_ALIGN_2048BYTES = 0x00C00000,
IMAGE_SCN_ALIGN_4096BYTES = 0x00D00000,
IMAGE_SCN_ALIGN_8192BYTES = 0x00E00000,
IMAGE_SCN_ALIGN_MASK = 0x00F00000,
IMAGE_SCN_LNK_NRELOC_OVFL = 0x01000000,
IMAGE_SCN_MEM_DISCARDABLE = 0x02000000,
IMAGE_SCN_MEM_NOT_CACHED = 0x04000000,
IMAGE_SCN_MEM_NOT_PAGED = 0x08000000,
IMAGE_SCN_MEM_SHARED = 0x10000000,
IMAGE_SCN_MEM_EXECUTE = 0x20000000,
IMAGE_SCN_MEM_READ = 0x40000000,
IMAGE_SCN_MEM_WRITE = 0x80000000
};
struct relocation {
uint32_t VirtualAddress;
uint32_t SymbolTableIndex;
uint16_t Type;
};
enum RelocationTypeI386 : unsigned {
IMAGE_REL_I386_ABSOLUTE = 0x0000,
IMAGE_REL_I386_DIR16 = 0x0001,
IMAGE_REL_I386_REL16 = 0x0002,
IMAGE_REL_I386_DIR32 = 0x0006,
IMAGE_REL_I386_DIR32NB = 0x0007,
IMAGE_REL_I386_SEG12 = 0x0009,
IMAGE_REL_I386_SECTION = 0x000A,
IMAGE_REL_I386_SECREL = 0x000B,
IMAGE_REL_I386_TOKEN = 0x000C,
IMAGE_REL_I386_SECREL7 = 0x000D,
IMAGE_REL_I386_REL32 = 0x0014
};
enum RelocationTypeAMD64 : unsigned {
IMAGE_REL_AMD64_ABSOLUTE = 0x0000,
IMAGE_REL_AMD64_ADDR64 = 0x0001,
IMAGE_REL_AMD64_ADDR32 = 0x0002,
IMAGE_REL_AMD64_ADDR32NB = 0x0003,
IMAGE_REL_AMD64_REL32 = 0x0004,
IMAGE_REL_AMD64_REL32_1 = 0x0005,
IMAGE_REL_AMD64_REL32_2 = 0x0006,
IMAGE_REL_AMD64_REL32_3 = 0x0007,
IMAGE_REL_AMD64_REL32_4 = 0x0008,
IMAGE_REL_AMD64_REL32_5 = 0x0009,
IMAGE_REL_AMD64_SECTION = 0x000A,
IMAGE_REL_AMD64_SECREL = 0x000B,
IMAGE_REL_AMD64_SECREL7 = 0x000C,
IMAGE_REL_AMD64_TOKEN = 0x000D,
IMAGE_REL_AMD64_SREL32 = 0x000E,
IMAGE_REL_AMD64_PAIR = 0x000F,
IMAGE_REL_AMD64_SSPAN32 = 0x0010
};
enum RelocationTypesARM : unsigned {
IMAGE_REL_ARM_ABSOLUTE = 0x0000,
IMAGE_REL_ARM_ADDR32 = 0x0001,
IMAGE_REL_ARM_ADDR32NB = 0x0002,
IMAGE_REL_ARM_BRANCH24 = 0x0003,
IMAGE_REL_ARM_BRANCH11 = 0x0004,
IMAGE_REL_ARM_TOKEN = 0x0005,
IMAGE_REL_ARM_BLX24 = 0x0008,
IMAGE_REL_ARM_BLX11 = 0x0009,
IMAGE_REL_ARM_REL32 = 0x000A,
IMAGE_REL_ARM_SECTION = 0x000E,
IMAGE_REL_ARM_SECREL = 0x000F,
IMAGE_REL_ARM_MOV32A = 0x0010,
IMAGE_REL_ARM_MOV32T = 0x0011,
IMAGE_REL_ARM_BRANCH20T = 0x0012,
IMAGE_REL_ARM_BRANCH24T = 0x0014,
IMAGE_REL_ARM_BLX23T = 0x0015,
IMAGE_REL_ARM_PAIR = 0x0016,
};
enum RelocationTypesARM64 : unsigned {
IMAGE_REL_ARM64_ABSOLUTE = 0x0000,
IMAGE_REL_ARM64_ADDR32 = 0x0001,
IMAGE_REL_ARM64_ADDR32NB = 0x0002,
IMAGE_REL_ARM64_BRANCH26 = 0x0003,
IMAGE_REL_ARM64_PAGEBASE_REL21 = 0x0004,
IMAGE_REL_ARM64_REL21 = 0x0005,
IMAGE_REL_ARM64_PAGEOFFSET_12A = 0x0006,
IMAGE_REL_ARM64_PAGEOFFSET_12L = 0x0007,
IMAGE_REL_ARM64_SECREL = 0x0008,
IMAGE_REL_ARM64_SECREL_LOW12A = 0x0009,
IMAGE_REL_ARM64_SECREL_HIGH12A = 0x000A,
IMAGE_REL_ARM64_SECREL_LOW12L = 0x000B,
IMAGE_REL_ARM64_TOKEN = 0x000C,
IMAGE_REL_ARM64_SECTION = 0x000D,
IMAGE_REL_ARM64_ADDR64 = 0x000E,
IMAGE_REL_ARM64_BRANCH19 = 0x000F,
IMAGE_REL_ARM64_BRANCH14 = 0x0010,
IMAGE_REL_ARM64_REL32 = 0x0011,
};
enum RelocationTypesMips : unsigned {
IMAGE_REL_MIPS_ABSOLUTE = 0x0000,
IMAGE_REL_MIPS_REFHALF = 0x0001,
IMAGE_REL_MIPS_REFWORD = 0x0002,
IMAGE_REL_MIPS_JMPADDR = 0x0003,
IMAGE_REL_MIPS_REFHI = 0x0004,
IMAGE_REL_MIPS_REFLO = 0x0005,
IMAGE_REL_MIPS_GPREL = 0x0006,
IMAGE_REL_MIPS_LITERAL = 0x0007,
IMAGE_REL_MIPS_SECTION = 0x000A,
IMAGE_REL_MIPS_SECREL = 0x000B,
IMAGE_REL_MIPS_SECRELLO = 0x000C,
IMAGE_REL_MIPS_SECRELHI = 0x000D,
IMAGE_REL_MIPS_JMPADDR16 = 0x0010,
IMAGE_REL_MIPS_REFWORDNB = 0x0022,
IMAGE_REL_MIPS_PAIR = 0x0025,
};
enum DynamicRelocationType : unsigned {
IMAGE_DYNAMIC_RELOCATION_GUARD_RF_PROLOGUE = 1,
IMAGE_DYNAMIC_RELOCATION_GUARD_RF_EPILOGUE = 2,
IMAGE_DYNAMIC_RELOCATION_GUARD_IMPORT_CONTROL_TRANSFER = 3,
IMAGE_DYNAMIC_RELOCATION_GUARD_INDIR_CONTROL_TRANSFER = 4,
IMAGE_DYNAMIC_RELOCATION_GUARD_SWITCHTABLE_BRANCH = 5,
IMAGE_DYNAMIC_RELOCATION_ARM64X = 6,
};
enum Arm64XFixupType : uint8_t {
IMAGE_DVRT_ARM64X_FIXUP_TYPE_ZEROFILL = 0,
IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE = 1,
IMAGE_DVRT_ARM64X_FIXUP_TYPE_DELTA = 2,
};
enum COMDATType : uint8_t {
IMAGE_COMDAT_SELECT_NODUPLICATES = 1,
IMAGE_COMDAT_SELECT_ANY,
IMAGE_COMDAT_SELECT_SAME_SIZE,
IMAGE_COMDAT_SELECT_EXACT_MATCH,
IMAGE_COMDAT_SELECT_ASSOCIATIVE,
IMAGE_COMDAT_SELECT_LARGEST,
IMAGE_COMDAT_SELECT_NEWEST
};
// Auxiliary Symbol Formats
struct AuxiliaryFunctionDefinition {
uint32_t TagIndex;
uint32_t TotalSize;
uint32_t PointerToLinenumber;
uint32_t PointerToNextFunction;
char unused[2];
};
struct AuxiliarybfAndefSymbol {
uint8_t unused1[4];
uint16_t Linenumber;
uint8_t unused2[6];
uint32_t PointerToNextFunction;
uint8_t unused3[2];
};
struct AuxiliaryWeakExternal {
uint32_t TagIndex;
uint32_t Characteristics;
uint8_t unused[10];
};
enum WeakExternalCharacteristics : unsigned {
IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY = 1,
IMAGE_WEAK_EXTERN_SEARCH_LIBRARY = 2,
IMAGE_WEAK_EXTERN_SEARCH_ALIAS = 3,
IMAGE_WEAK_EXTERN_ANTI_DEPENDENCY = 4
};
struct AuxiliarySectionDefinition {
uint32_t Length;
uint16_t NumberOfRelocations;
uint16_t NumberOfLinenumbers;
uint32_t CheckSum;
uint32_t Number;
uint8_t Selection;
char unused;
};
struct AuxiliaryCLRToken {
uint8_t AuxType;
uint8_t unused1;
uint32_t SymbolTableIndex;
char unused2[12];
};
union Auxiliary {
AuxiliaryFunctionDefinition FunctionDefinition;
AuxiliarybfAndefSymbol bfAndefSymbol;
AuxiliaryWeakExternal WeakExternal;
AuxiliarySectionDefinition SectionDefinition;
};
/// The Import Directory Table.
///
/// There is a single array of these and one entry per imported DLL.
struct ImportDirectoryTableEntry {
uint32_t ImportLookupTableRVA;
uint32_t TimeDateStamp;
uint32_t ForwarderChain;
uint32_t NameRVA;
uint32_t ImportAddressTableRVA;
};
/// The PE32 Import Lookup Table.
///
/// There is an array of these for each imported DLL. It represents either
/// the ordinal to import from the target DLL, or a name to lookup and import
/// from the target DLL.
///
/// This also happens to be the same format used by the Import Address Table
/// when it is initially written out to the image.
struct ImportLookupTableEntry32 {
uint32_t data;
/// Is this entry specified by ordinal, or name?
bool isOrdinal() const { return data & 0x80000000; }
/// Get the ordinal value of this entry. isOrdinal must be true.
uint16_t getOrdinal() const {
assert(isOrdinal() && "ILT entry is not an ordinal!");
return data & 0xFFFF;
}
/// Set the ordinal value and set isOrdinal to true.
void setOrdinal(uint16_t o) {
data = o;
data |= 0x80000000;
}
/// Get the Hint/Name entry RVA. isOrdinal must be false.
uint32_t getHintNameRVA() const {
assert(!isOrdinal() && "ILT entry is not a Hint/Name RVA!");
return data;
}
/// Set the Hint/Name entry RVA and set isOrdinal to false.
void setHintNameRVA(uint32_t rva) { data = rva; }
};
/// The DOS compatible header at the front of all PEs.
struct DOSHeader {
uint16_t Magic;
uint16_t UsedBytesInTheLastPage;
uint16_t FileSizeInPages;
uint16_t NumberOfRelocationItems;
uint16_t HeaderSizeInParagraphs;
uint16_t MinimumExtraParagraphs;
uint16_t MaximumExtraParagraphs;
uint16_t InitialRelativeSS;
uint16_t InitialSP;
uint16_t Checksum;
uint16_t InitialIP;
uint16_t InitialRelativeCS;
uint16_t AddressOfRelocationTable;
uint16_t OverlayNumber;
uint16_t Reserved[4];
uint16_t OEMid;
uint16_t OEMinfo;
uint16_t Reserved2[10];
uint32_t AddressOfNewExeHeader;
};
struct PE32Header {
enum { PE32 = 0x10b, PE32_PLUS = 0x20b };
uint16_t Magic;
uint8_t MajorLinkerVersion;
uint8_t MinorLinkerVersion;
uint32_t SizeOfCode;
uint32_t SizeOfInitializedData;
uint32_t SizeOfUninitializedData;
uint32_t AddressOfEntryPoint; // RVA
uint32_t BaseOfCode; // RVA
uint32_t BaseOfData; // RVA
uint64_t ImageBase;
uint32_t SectionAlignment;
uint32_t FileAlignment;
uint16_t MajorOperatingSystemVersion;
uint16_t MinorOperatingSystemVersion;
uint16_t MajorImageVersion;
uint16_t MinorImageVersion;
uint16_t MajorSubsystemVersion;
uint16_t MinorSubsystemVersion;
uint32_t Win32VersionValue;
uint32_t SizeOfImage;
uint32_t SizeOfHeaders;
uint32_t CheckSum;
uint16_t Subsystem;
// FIXME: This should be DllCharacteristics to match the COFF spec.
uint16_t DLLCharacteristics;
uint64_t SizeOfStackReserve;
uint64_t SizeOfStackCommit;
uint64_t SizeOfHeapReserve;
uint64_t SizeOfHeapCommit;
uint32_t LoaderFlags;
// FIXME: This should be NumberOfRvaAndSizes to match the COFF spec.
uint32_t NumberOfRvaAndSize;
};
struct DataDirectory {
uint32_t RelativeVirtualAddress;
uint32_t Size;
};
enum DataDirectoryIndex : unsigned {
EXPORT_TABLE = 0,
IMPORT_TABLE,
RESOURCE_TABLE,
EXCEPTION_TABLE,
CERTIFICATE_TABLE,
BASE_RELOCATION_TABLE,
DEBUG_DIRECTORY,
ARCHITECTURE,
GLOBAL_PTR,
TLS_TABLE,
LOAD_CONFIG_TABLE,
BOUND_IMPORT,
IAT,
DELAY_IMPORT_DESCRIPTOR,
CLR_RUNTIME_HEADER,
NUM_DATA_DIRECTORIES
};
enum WindowsSubsystem : unsigned {
IMAGE_SUBSYSTEM_UNKNOWN = 0, ///< An unknown subsystem.
IMAGE_SUBSYSTEM_NATIVE = 1, ///< Device drivers and native Windows processes
IMAGE_SUBSYSTEM_WINDOWS_GUI = 2, ///< The Windows GUI subsystem.
IMAGE_SUBSYSTEM_WINDOWS_CUI = 3, ///< The Windows character subsystem.
IMAGE_SUBSYSTEM_OS2_CUI = 5, ///< The OS/2 character subsystem.
IMAGE_SUBSYSTEM_POSIX_CUI = 7, ///< The POSIX character subsystem.
IMAGE_SUBSYSTEM_NATIVE_WINDOWS = 8, ///< Native Windows 9x driver.
IMAGE_SUBSYSTEM_WINDOWS_CE_GUI = 9, ///< Windows CE.
IMAGE_SUBSYSTEM_EFI_APPLICATION = 10, ///< An EFI application.
IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER = 11, ///< An EFI driver with boot
/// services.
IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER = 12, ///< An EFI driver with run-time
/// services.
IMAGE_SUBSYSTEM_EFI_ROM = 13, ///< An EFI ROM image.
IMAGE_SUBSYSTEM_XBOX = 14, ///< XBOX.
IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION = 16 ///< A BCD application.
};
enum DLLCharacteristics : unsigned {
/// ASLR with 64 bit address space.
IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA = 0x0020,
/// DLL can be relocated at load time.
IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE = 0x0040,
/// Code integrity checks are enforced.
IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY = 0x0080,
///< Image is NX compatible.
IMAGE_DLL_CHARACTERISTICS_NX_COMPAT = 0x0100,
/// Isolation aware, but do not isolate the image.
IMAGE_DLL_CHARACTERISTICS_NO_ISOLATION = 0x0200,
/// Does not use structured exception handling (SEH). No SEH handler may be
/// called in this image.
IMAGE_DLL_CHARACTERISTICS_NO_SEH = 0x0400,
/// Do not bind the image.
IMAGE_DLL_CHARACTERISTICS_NO_BIND = 0x0800,
///< Image should execute in an AppContainer.
IMAGE_DLL_CHARACTERISTICS_APPCONTAINER = 0x1000,
///< A WDM driver.
IMAGE_DLL_CHARACTERISTICS_WDM_DRIVER = 0x2000,
///< Image supports Control Flow Guard.
IMAGE_DLL_CHARACTERISTICS_GUARD_CF = 0x4000,
/// Terminal Server aware.
IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE = 0x8000
};
enum ExtendedDLLCharacteristics : unsigned {
/// Image is CET compatible
IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT = 0x0001
};
enum DebugType : unsigned {
IMAGE_DEBUG_TYPE_UNKNOWN = 0,
IMAGE_DEBUG_TYPE_COFF = 1,
IMAGE_DEBUG_TYPE_CODEVIEW = 2,
IMAGE_DEBUG_TYPE_FPO = 3,
IMAGE_DEBUG_TYPE_MISC = 4,
IMAGE_DEBUG_TYPE_EXCEPTION = 5,
IMAGE_DEBUG_TYPE_FIXUP = 6,
IMAGE_DEBUG_TYPE_OMAP_TO_SRC = 7,
IMAGE_DEBUG_TYPE_OMAP_FROM_SRC = 8,
IMAGE_DEBUG_TYPE_BORLAND = 9,
IMAGE_DEBUG_TYPE_RESERVED10 = 10,
IMAGE_DEBUG_TYPE_CLSID = 11,
IMAGE_DEBUG_TYPE_VC_FEATURE = 12,
IMAGE_DEBUG_TYPE_POGO = 13,
IMAGE_DEBUG_TYPE_ILTCG = 14,
IMAGE_DEBUG_TYPE_MPX = 15,
IMAGE_DEBUG_TYPE_REPRO = 16,
IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS = 20,
};
enum BaseRelocationType : unsigned {
IMAGE_REL_BASED_ABSOLUTE = 0,
IMAGE_REL_BASED_HIGH = 1,
IMAGE_REL_BASED_LOW = 2,
IMAGE_REL_BASED_HIGHLOW = 3,
IMAGE_REL_BASED_HIGHADJ = 4,
IMAGE_REL_BASED_MIPS_JMPADDR = 5,
IMAGE_REL_BASED_ARM_MOV32A = 5,
IMAGE_REL_BASED_ARM_MOV32T = 7,
IMAGE_REL_BASED_MIPS_JMPADDR16 = 9,
IMAGE_REL_BASED_DIR64 = 10
};
enum ImportType : unsigned {
IMPORT_CODE = 0,
IMPORT_DATA = 1,
IMPORT_CONST = 2
};
enum ImportNameType : unsigned {
/// Import is by ordinal. This indicates that the value in the Ordinal/Hint
/// field of the import header is the import's ordinal. If this constant is
/// not specified, then the Ordinal/Hint field should always be interpreted
/// as the import's hint.
IMPORT_ORDINAL = 0,
/// The import name is identical to the public symbol name
IMPORT_NAME = 1,
/// The import name is the public symbol name, but skipping the leading ?,
/// @, or optionally _.
IMPORT_NAME_NOPREFIX = 2,
/// The import name is the public symbol name, but skipping the leading ?,
/// @, or optionally _, and truncating at the first @.
IMPORT_NAME_UNDECORATE = 3,
/// The import name is specified as a separate string in the import library
/// object file.
IMPORT_NAME_EXPORTAS = 4
};
enum class GuardFlags : uint32_t {
/// Module performs control flow integrity checks using system-supplied
/// support.
CF_INSTRUMENTED = 0x100,
/// Module performs control flow and write integrity checks.
CFW_INSTRUMENTED = 0x200,
/// Module contains valid control flow target metadata.
CF_FUNCTION_TABLE_PRESENT = 0x400,
/// Module does not make use of the /GS security cookie.
SECURITY_COOKIE_UNUSED = 0x800,
/// Module supports read only delay load IAT.
PROTECT_DELAYLOAD_IAT = 0x1000,
/// Delayload import table in its own .didat section (with nothing else in it)
/// that can be freely reprotected.
DELAYLOAD_IAT_IN_ITS_OWN_SECTION = 0x2000,
/// Module contains suppressed export information. This also infers that the
/// address taken IAT table is also present in the load config.
CF_EXPORT_SUPPRESSION_INFO_PRESENT = 0x4000,
/// Module enables suppression of exports.
CF_ENABLE_EXPORT_SUPPRESSION = 0x8000,
/// Module contains longjmp target information.
CF_LONGJUMP_TABLE_PRESENT = 0x10000,
/// Module contains EH continuation target information.
EH_CONTINUATION_TABLE_PRESENT = 0x400000,
/// Mask for the subfield that contains the stride of Control Flow Guard
/// function table entries (that is, the additional count of bytes per table
/// entry).
CF_FUNCTION_TABLE_SIZE_MASK = 0xF0000000,
CF_FUNCTION_TABLE_SIZE_5BYTES = 0x10000000,
CF_FUNCTION_TABLE_SIZE_6BYTES = 0x20000000,
CF_FUNCTION_TABLE_SIZE_7BYTES = 0x30000000,
CF_FUNCTION_TABLE_SIZE_8BYTES = 0x40000000,
CF_FUNCTION_TABLE_SIZE_9BYTES = 0x50000000,
CF_FUNCTION_TABLE_SIZE_10BYTES = 0x60000000,
CF_FUNCTION_TABLE_SIZE_11BYTES = 0x70000000,
CF_FUNCTION_TABLE_SIZE_12BYTES = 0x80000000,
CF_FUNCTION_TABLE_SIZE_13BYTES = 0x90000000,
CF_FUNCTION_TABLE_SIZE_14BYTES = 0xA0000000,
CF_FUNCTION_TABLE_SIZE_15BYTES = 0xB0000000,
CF_FUNCTION_TABLE_SIZE_16BYTES = 0xC0000000,
CF_FUNCTION_TABLE_SIZE_17BYTES = 0xD0000000,
CF_FUNCTION_TABLE_SIZE_18BYTES = 0xE0000000,
CF_FUNCTION_TABLE_SIZE_19BYTES = 0xF0000000,
};
struct ImportHeader {
uint16_t Sig1; ///< Must be IMAGE_FILE_MACHINE_UNKNOWN (0).
uint16_t Sig2; ///< Must be 0xFFFF.
uint16_t Version;
uint16_t Machine;
uint32_t TimeDateStamp;
uint32_t SizeOfData;
uint16_t OrdinalHint;
uint16_t TypeInfo;
ImportType getType() const { return static_cast<ImportType>(TypeInfo & 0x3); }
ImportNameType getNameType() const {
return static_cast<ImportNameType>((TypeInfo & 0x1C) >> 2);
}
};
enum CodeViewIdentifiers {
DEBUG_SECTION_MAGIC = 0x4,
DEBUG_HASHES_SECTION_MAGIC = 0x133C9C5
};
// These flags show up in the @feat.00 symbol. They appear to be some kind of
// compiler features bitfield read by link.exe.
enum Feat00Flags : uint32_t {
// Object is compatible with /safeseh.
SafeSEH = 0x1,
// Object was compiled with /GS.
GuardStack = 0x100,
// Object was compiled with /sdl.
SDL = 0x200,
// Object was compiled with /guard:cf.
GuardCF = 0x800,
// Object was compiled with /guard:ehcont.
GuardEHCont = 0x4000,
// Object was compiled with /kernel.
Kernel = 0x40000000,
};
enum Arm64ECThunkType : uint8_t {
GuestExit = 0,
Entry = 1,
Exit = 4,
};
inline bool isReservedSectionNumber(int32_t SectionNumber) {
return SectionNumber <= 0;
}
/// Encode section name based on string table offset.
/// The size of Out must be at least COFF::NameSize.
bool encodeSectionName(char *Out, uint64_t Offset);
} // End namespace COFF.
} // End namespace llvm.
#endif

View File

@@ -0,0 +1,807 @@
//===- COFFImportFile.cpp - COFF short import file implementation ---------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file defines the writeImportLibrary function.
//
//===----------------------------------------------------------------------===//
#include "llvm/Object/COFFImportFile.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/ArchiveWriter.h"
#include "llvm/Object/COFF.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/Path.h"
#include <cstdint>
#include <string>
#include <vector>
using namespace llvm::COFF;
using namespace llvm::object;
using namespace llvm;
namespace llvm {
namespace object {
StringRef COFFImportFile::getFileFormatName() const {
switch (getMachine()) {
case COFF::IMAGE_FILE_MACHINE_I386:
return "COFF-import-file-i386";
case COFF::IMAGE_FILE_MACHINE_AMD64:
return "COFF-import-file-x86-64";
case COFF::IMAGE_FILE_MACHINE_ARMNT:
return "COFF-import-file-ARM";
case COFF::IMAGE_FILE_MACHINE_ARM64:
return "COFF-import-file-ARM64";
case COFF::IMAGE_FILE_MACHINE_ARM64EC:
return "COFF-import-file-ARM64EC";
case COFF::IMAGE_FILE_MACHINE_ARM64X:
return "COFF-import-file-ARM64X";
default:
return "COFF-import-file-<unknown arch>";
}
}
static StringRef applyNameType(ImportNameType Type, StringRef name) {
auto ltrim1 = [](StringRef s, StringRef chars) {
return !s.empty() && chars.contains(s[0]) ? s.substr(1) : s;
};
switch (Type) {
case IMPORT_NAME_NOPREFIX:
name = ltrim1(name, "?@_");
break;
case IMPORT_NAME_UNDECORATE:
name = ltrim1(name, "?@_");
name = name.substr(0, name.find('@'));
break;
default:
break;
}
return name;
}
StringRef COFFImportFile::getExportName() const {
const coff_import_header *hdr = getCOFFImportHeader();
StringRef name = Data.getBuffer().substr(sizeof(*hdr)).split('\0').first;
switch (hdr->getNameType()) {
case IMPORT_ORDINAL:
name = "";
break;
case IMPORT_NAME_NOPREFIX:
case IMPORT_NAME_UNDECORATE:
name = applyNameType(static_cast<ImportNameType>(hdr->getNameType()), name);
break;
case IMPORT_NAME_EXPORTAS: {
// Skip DLL name
name = Data.getBuffer().substr(sizeof(*hdr) + name.size() + 1);
name = name.split('\0').second.split('\0').first;
break;
}
default:
break;
}
return name;
}
Error COFFImportFile::printSymbolName(raw_ostream &OS, DataRefImpl Symb) const {
switch (Symb.p) {
case ImpSymbol:
OS << "__imp_";
break;
case ECAuxSymbol:
OS << "__imp_aux_";
break;
}
const char *Name = Data.getBufferStart() + sizeof(coff_import_header);
if (Symb.p != ECThunkSymbol && COFF::isArm64EC(getMachine())) {
if (std::optional<std::string> DemangledName =
getArm64ECDemangledFunctionName(Name)) {
OS << StringRef(*DemangledName);
return Error::success();
}
}
OS << StringRef(Name);
return Error::success();
}
static uint16_t getImgRelRelocation(MachineTypes Machine) {
switch (Machine) {
default:
llvm_unreachable("unsupported machine");
case IMAGE_FILE_MACHINE_AMD64:
return IMAGE_REL_AMD64_ADDR32NB;
case IMAGE_FILE_MACHINE_ARMNT:
return IMAGE_REL_ARM_ADDR32NB;
case IMAGE_FILE_MACHINE_ARM64:
case IMAGE_FILE_MACHINE_ARM64EC:
case IMAGE_FILE_MACHINE_ARM64X:
return IMAGE_REL_ARM64_ADDR32NB;
case IMAGE_FILE_MACHINE_I386:
return IMAGE_REL_I386_DIR32NB;
case IMAGE_FILE_MACHINE_R4000:
return IMAGE_REL_MIPS_REFWORDNB;
}
}
template <class T> static void append(std::vector<uint8_t> &B, const T &Data) {
size_t S = B.size();
B.resize(S + sizeof(T));
memcpy(&B[S], &Data, sizeof(T));
}
static void writeStringTable(std::vector<uint8_t> &B,
ArrayRef<const std::string_view> Strings) {
// The COFF string table consists of a 4-byte value which is the size of the
// table, including the length field itself. This value is followed by the
// string content itself, which is an array of null-terminated C-style
// strings. The termination is important as they are referenced to by offset
// by the symbol entity in the file format.
size_t Pos = B.size();
size_t Offset = B.size();
// Skip over the length field, we will fill it in later as we will have
// computed the length while emitting the string content itself.
Pos += sizeof(uint32_t);
for (const auto &S : Strings) {
B.resize(Pos + S.length() + 1);
std::copy(S.begin(), S.end(), std::next(B.begin(), Pos));
B[Pos + S.length()] = 0;
Pos += S.length() + 1;
}
// Backfill the length of the table now that it has been computed.
support::ulittle32_t Length(B.size() - Offset);
support::endian::write32le(&B[Offset], Length);
}
static ImportNameType getNameType(StringRef Sym, StringRef ExtName,
MachineTypes Machine, bool MinGW) {
// A decorated stdcall function in MSVC is exported with the
// type IMPORT_NAME, and the exported function name includes the
// the leading underscore. In MinGW on the other hand, a decorated
// stdcall function still omits the underscore (IMPORT_NAME_NOPREFIX).
// See the comment in isDecorated in COFFModuleDefinition.cpp for more
// details.
if (ExtName.starts_with("_") && ExtName.contains('@') && !MinGW)
return IMPORT_NAME;
if (Sym != ExtName)
return IMPORT_NAME_UNDECORATE;
if (Machine == IMAGE_FILE_MACHINE_I386 && Sym.starts_with("_"))
return IMPORT_NAME_NOPREFIX;
return IMPORT_NAME;
}
static Expected<std::string> replace(StringRef S, StringRef From,
StringRef To) {
size_t Pos = S.find(From);
// From and To may be mangled, but substrings in S may not.
if (Pos == StringRef::npos && From.starts_with("_") && To.starts_with("_")) {
From = From.substr(1);
To = To.substr(1);
Pos = S.find(From);
}
if (Pos == StringRef::npos) {
return make_error<StringError>(
StringRef(Twine(S + ": replacing '" + From +
"' with '" + To + "' failed").str()), object_error::parse_failed);
}
return (Twine(S.substr(0, Pos)) + To + S.substr(Pos + From.size())).str();
}
namespace {
// This class constructs various small object files necessary to support linking
// symbols imported from a DLL. The contents are pretty strictly defined and
// nearly entirely static. The details of the structures files are defined in
// WINNT.h and the PE/COFF specification.
class ObjectFactory {
using u16 = support::ulittle16_t;
using u32 = support::ulittle32_t;
MachineTypes NativeMachine;
BumpPtrAllocator Alloc;
StringRef ImportName;
StringRef Library;
std::string ImportDescriptorSymbolName;
std::string NullThunkSymbolName;
public:
ObjectFactory(StringRef S, MachineTypes M)
: NativeMachine(M), ImportName(S), Library(llvm::sys::path::stem(S)),
ImportDescriptorSymbolName((ImportDescriptorPrefix + Library).str()),
NullThunkSymbolName(
(NullThunkDataPrefix + Library + NullThunkDataSuffix).str()) {}
// Creates an Import Descriptor. This is a small object file which contains a
// reference to the terminators and contains the library name (entry) for the
// import name table. It will force the linker to construct the necessary
// structure to import symbols from the DLL.
NewArchiveMember createImportDescriptor(std::vector<uint8_t> &Buffer);
// Creates a NULL import descriptor. This is a small object file whcih
// contains a NULL import descriptor. It is used to terminate the imports
// from a specific DLL.
NewArchiveMember createNullImportDescriptor(std::vector<uint8_t> &Buffer);
// Create a NULL Thunk Entry. This is a small object file which contains a
// NULL Import Address Table entry and a NULL Import Lookup Table Entry. It
// is used to terminate the IAT and ILT.
NewArchiveMember createNullThunk(std::vector<uint8_t> &Buffer);
// Create a short import file which is described in PE/COFF spec 7. Import
// Library Format.
NewArchiveMember createShortImport(StringRef Sym, uint16_t Ordinal,
ImportType Type, ImportNameType NameType,
StringRef ExportName,
MachineTypes Machine);
// Create a weak external file which is described in PE/COFF Aux Format 3.
NewArchiveMember createWeakExternal(StringRef Sym, StringRef Weak, bool Imp,
MachineTypes Machine);
bool is64Bit() const { return COFF::is64Bit(NativeMachine); }
};
} // namespace
NewArchiveMember
ObjectFactory::createImportDescriptor(std::vector<uint8_t> &Buffer) {
const uint32_t NumberOfSections = 2;
const uint32_t NumberOfSymbols = 7;
const uint32_t NumberOfRelocations = 3;
// COFF Header
coff_file_header Header{
u16(NativeMachine),
u16(NumberOfSections),
u32(0),
u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section)) +
// .idata$2
sizeof(coff_import_directory_table_entry) +
NumberOfRelocations * sizeof(coff_relocation) +
// .idata$4
(ImportName.size() + 1)),
u32(NumberOfSymbols),
u16(0),
u16(is64Bit() ? C_Invalid : IMAGE_FILE_32BIT_MACHINE),
};
append(Buffer, Header);
// Section Header Table
const coff_section SectionTable[NumberOfSections] = {
{{'.', 'i', 'd', 'a', 't', 'a', '$', '2'},
u32(0),
u32(0),
u32(sizeof(coff_import_directory_table_entry)),
u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section)),
u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section) +
sizeof(coff_import_directory_table_entry)),
u32(0),
u16(NumberOfRelocations),
u16(0),
u32(IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA |
IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)},
{{'.', 'i', 'd', 'a', 't', 'a', '$', '6'},
u32(0),
u32(0),
u32(ImportName.size() + 1),
u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section) +
sizeof(coff_import_directory_table_entry) +
NumberOfRelocations * sizeof(coff_relocation)),
u32(0),
u32(0),
u16(0),
u16(0),
u32(IMAGE_SCN_ALIGN_2BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA |
IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)},
};
append(Buffer, SectionTable);
// .idata$2
const coff_import_directory_table_entry ImportDescriptor{
u32(0), u32(0), u32(0), u32(0), u32(0),
};
append(Buffer, ImportDescriptor);
const coff_relocation RelocationTable[NumberOfRelocations] = {
{u32(offsetof(coff_import_directory_table_entry, NameRVA)), u32(2),
u16(getImgRelRelocation(NativeMachine))},
{u32(offsetof(coff_import_directory_table_entry, ImportLookupTableRVA)),
u32(3), u16(getImgRelRelocation(NativeMachine))},
{u32(offsetof(coff_import_directory_table_entry, ImportAddressTableRVA)),
u32(4), u16(getImgRelRelocation(NativeMachine))},
};
append(Buffer, RelocationTable);
// .idata$6
auto S = Buffer.size();
Buffer.resize(S + ImportName.size() + 1);
memcpy(&Buffer[S], ImportName.data(), ImportName.size());
Buffer[S + ImportName.size()] = '\0';
// Symbol Table
coff_symbol16 SymbolTable[NumberOfSymbols] = {
{{{0, 0, 0, 0, 0, 0, 0, 0}},
u32(0),
u16(1),
u16(0),
IMAGE_SYM_CLASS_EXTERNAL,
0},
{{{'.', 'i', 'd', 'a', 't', 'a', '$', '2'}},
u32(0),
u16(1),
u16(0),
IMAGE_SYM_CLASS_SECTION,
0},
{{{'.', 'i', 'd', 'a', 't', 'a', '$', '6'}},
u32(0),
u16(2),
u16(0),
IMAGE_SYM_CLASS_STATIC,
0},
{{{'.', 'i', 'd', 'a', 't', 'a', '$', '4'}},
u32(0),
u16(0),
u16(0),
IMAGE_SYM_CLASS_SECTION,
0},
{{{'.', 'i', 'd', 'a', 't', 'a', '$', '5'}},
u32(0),
u16(0),
u16(0),
IMAGE_SYM_CLASS_SECTION,
0},
{{{0, 0, 0, 0, 0, 0, 0, 0}},
u32(0),
u16(0),
u16(0),
IMAGE_SYM_CLASS_EXTERNAL,
0},
{{{0, 0, 0, 0, 0, 0, 0, 0}},
u32(0),
u16(0),
u16(0),
IMAGE_SYM_CLASS_EXTERNAL,
0},
};
// TODO: Name.Offset.Offset here and in the all similar places below
// suggests a names refactoring. Maybe StringTableOffset.Value?
SymbolTable[0].Name.Offset.Offset =
sizeof(uint32_t);
SymbolTable[5].Name.Offset.Offset =
sizeof(uint32_t) + ImportDescriptorSymbolName.length() + 1;
SymbolTable[6].Name.Offset.Offset =
sizeof(uint32_t) + ImportDescriptorSymbolName.length() + 1 +
NullImportDescriptorSymbolName.length() + 1;
append(Buffer, SymbolTable);
// String Table
writeStringTable(Buffer,
{ImportDescriptorSymbolName, NullImportDescriptorSymbolName,
NullThunkSymbolName});
StringRef F{reinterpret_cast<const char *>(Buffer.data()), Buffer.size()};
return {MemoryBufferRef(F, ImportName)};
}
NewArchiveMember
ObjectFactory::createNullImportDescriptor(std::vector<uint8_t> &Buffer) {
const uint32_t NumberOfSections = 1;
const uint32_t NumberOfSymbols = 1;
// COFF Header
coff_file_header Header{
u16(NativeMachine),
u16(NumberOfSections),
u32(0),
u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section)) +
// .idata$3
sizeof(coff_import_directory_table_entry)),
u32(NumberOfSymbols),
u16(0),
u16(is64Bit() ? C_Invalid : IMAGE_FILE_32BIT_MACHINE),
};
append(Buffer, Header);
// Section Header Table
const coff_section SectionTable[NumberOfSections] = {
{{'.', 'i', 'd', 'a', 't', 'a', '$', '3'},
u32(0),
u32(0),
u32(sizeof(coff_import_directory_table_entry)),
u32(sizeof(coff_file_header) +
(NumberOfSections * sizeof(coff_section))),
u32(0),
u32(0),
u16(0),
u16(0),
u32(IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA |
IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)},
};
append(Buffer, SectionTable);
// .idata$3
const coff_import_directory_table_entry ImportDescriptor{
u32(0), u32(0), u32(0), u32(0), u32(0),
};
append(Buffer, ImportDescriptor);
// Symbol Table
coff_symbol16 SymbolTable[NumberOfSymbols] = {
{{{0, 0, 0, 0, 0, 0, 0, 0}},
u32(0),
u16(1),
u16(0),
IMAGE_SYM_CLASS_EXTERNAL,
0},
};
SymbolTable[0].Name.Offset.Offset = sizeof(uint32_t);
append(Buffer, SymbolTable);
// String Table
writeStringTable(Buffer, {NullImportDescriptorSymbolName});
StringRef F{reinterpret_cast<const char *>(Buffer.data()), Buffer.size()};
return {MemoryBufferRef(F, ImportName)};
}
NewArchiveMember ObjectFactory::createNullThunk(std::vector<uint8_t> &Buffer) {
const uint32_t NumberOfSections = 2;
const uint32_t NumberOfSymbols = 1;
uint32_t VASize = is64Bit() ? 8 : 4;
// COFF Header
coff_file_header Header{
u16(NativeMachine),
u16(NumberOfSections),
u32(0),
u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section)) +
// .idata$5
VASize +
// .idata$4
VASize),
u32(NumberOfSymbols),
u16(0),
u16(is64Bit() ? C_Invalid : IMAGE_FILE_32BIT_MACHINE),
};
append(Buffer, Header);
// Section Header Table
const coff_section SectionTable[NumberOfSections] = {
{{'.', 'i', 'd', 'a', 't', 'a', '$', '5'},
u32(0),
u32(0),
u32(VASize),
u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section)),
u32(0),
u32(0),
u16(0),
u16(0),
u32((is64Bit() ? IMAGE_SCN_ALIGN_8BYTES : IMAGE_SCN_ALIGN_4BYTES) |
IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ |
IMAGE_SCN_MEM_WRITE)},
{{'.', 'i', 'd', 'a', 't', 'a', '$', '4'},
u32(0),
u32(0),
u32(VASize),
u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section) +
VASize),
u32(0),
u32(0),
u16(0),
u16(0),
u32((is64Bit() ? IMAGE_SCN_ALIGN_8BYTES : IMAGE_SCN_ALIGN_4BYTES) |
IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ |
IMAGE_SCN_MEM_WRITE)},
};
append(Buffer, SectionTable);
// .idata$5, ILT
append(Buffer, u32(0));
if (is64Bit())
append(Buffer, u32(0));
// .idata$4, IAT
append(Buffer, u32(0));
if (is64Bit())
append(Buffer, u32(0));
// Symbol Table
coff_symbol16 SymbolTable[NumberOfSymbols] = {
{{{0, 0, 0, 0, 0, 0, 0, 0}},
u32(0),
u16(1),
u16(0),
IMAGE_SYM_CLASS_EXTERNAL,
0},
};
SymbolTable[0].Name.Offset.Offset = sizeof(uint32_t);
append(Buffer, SymbolTable);
// String Table
writeStringTable(Buffer, {NullThunkSymbolName});
StringRef F{reinterpret_cast<const char *>(Buffer.data()), Buffer.size()};
return {MemoryBufferRef{F, ImportName}};
}
NewArchiveMember
ObjectFactory::createShortImport(StringRef Sym, uint16_t Ordinal,
ImportType ImportType, ImportNameType NameType,
StringRef ExportName, MachineTypes Machine) {
size_t ImpSize = ImportName.size() + Sym.size() + 2; // +2 for NULs
if (!ExportName.empty())
ImpSize += ExportName.size() + 1;
size_t Size = sizeof(coff_import_header) + ImpSize;
char *Buf = Alloc.Allocate<char>(Size);
memset(Buf, 0, Size);
char *P = Buf;
// Write short import library.
auto *Imp = reinterpret_cast<coff_import_header *>(P);
P += sizeof(*Imp);
Imp->Sig2 = 0xFFFF;
Imp->Machine = Machine;
Imp->SizeOfData = ImpSize;
if (Ordinal > 0)
Imp->OrdinalHint = Ordinal;
Imp->TypeInfo = (NameType << 2) | ImportType;
// Write symbol name and DLL name.
memcpy(P, Sym.data(), Sym.size());
P += Sym.size() + 1;
memcpy(P, ImportName.data(), ImportName.size());
if (!ExportName.empty()) {
P += ImportName.size() + 1;
memcpy(P, ExportName.data(), ExportName.size());
}
return {MemoryBufferRef(StringRef(Buf, Size), ImportName)};
}
NewArchiveMember ObjectFactory::createWeakExternal(StringRef Sym,
StringRef Weak, bool Imp,
MachineTypes Machine) {
std::vector<uint8_t> Buffer;
const uint32_t NumberOfSections = 1;
const uint32_t NumberOfSymbols = 5;
// COFF Header
coff_file_header Header{
u16(Machine),
u16(NumberOfSections),
u32(0),
u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section))),
u32(NumberOfSymbols),
u16(0),
u16(0),
};
append(Buffer, Header);
// Section Header Table
const coff_section SectionTable[NumberOfSections] = {
{{'.', 'd', 'r', 'e', 'c', 't', 'v', 'e'},
u32(0),
u32(0),
u32(0),
u32(0),
u32(0),
u32(0),
u16(0),
u16(0),
u32(IMAGE_SCN_LNK_INFO | IMAGE_SCN_LNK_REMOVE)}};
append(Buffer, SectionTable);
// Symbol Table
coff_symbol16 SymbolTable[NumberOfSymbols] = {
{{{'@', 'c', 'o', 'm', 'p', '.', 'i', 'd'}},
u32(0),
u16(0xFFFF),
u16(0),
IMAGE_SYM_CLASS_STATIC,
0},
{{{'@', 'f', 'e', 'a', 't', '.', '0', '0'}},
u32(0),
u16(0xFFFF),
u16(0),
IMAGE_SYM_CLASS_STATIC,
0},
{{{0, 0, 0, 0, 0, 0, 0, 0}},
u32(0),
u16(0),
u16(0),
IMAGE_SYM_CLASS_EXTERNAL,
0},
{{{0, 0, 0, 0, 0, 0, 0, 0}},
u32(0),
u16(0),
u16(0),
IMAGE_SYM_CLASS_WEAK_EXTERNAL,
1},
{{{2, 0, 0, 0, IMAGE_WEAK_EXTERN_SEARCH_ALIAS, 0, 0, 0}},
u32(0),
u16(0),
u16(0),
IMAGE_SYM_CLASS_NULL,
0},
};
SymbolTable[2].Name.Offset.Offset = sizeof(uint32_t);
//__imp_ String Table
StringRef Prefix = Imp ? "__imp_" : "";
SymbolTable[3].Name.Offset.Offset =
sizeof(uint32_t) + Sym.size() + Prefix.size() + 1;
append(Buffer, SymbolTable);
writeStringTable(Buffer, {(Prefix + Sym).str(),
(Prefix + Weak).str()});
// Copied here so we can still use writeStringTable
char *Buf = Alloc.Allocate<char>(Buffer.size());
memcpy(Buf, Buffer.data(), Buffer.size());
return {MemoryBufferRef(StringRef(Buf, Buffer.size()), ImportName)};
}
Error writeImportLibrary(StringRef ImportName, StringRef Path,
ArrayRef<COFFShortExport> Exports,
MachineTypes Machine, bool MinGW,
ArrayRef<COFFShortExport> NativeExports) {
MachineTypes NativeMachine = Machine;
if (isArm64EC(Machine)) {
NativeMachine = IMAGE_FILE_MACHINE_ARM64;
Machine = IMAGE_FILE_MACHINE_ARM64EC;
}
std::vector<NewArchiveMember> Members;
ObjectFactory OF(llvm::sys::path::filename(ImportName), NativeMachine);
std::vector<uint8_t> ImportDescriptor;
Members.push_back(OF.createImportDescriptor(ImportDescriptor));
std::vector<uint8_t> NullImportDescriptor;
Members.push_back(OF.createNullImportDescriptor(NullImportDescriptor));
std::vector<uint8_t> NullThunk;
Members.push_back(OF.createNullThunk(NullThunk));
auto addExports = [&](ArrayRef<COFFShortExport> Exp,
MachineTypes M) -> Error {
StringMap<std::string> RegularImports;
struct Deferred {
std::string Name;
ImportType ImpType;
const COFFShortExport *Export;
};
SmallVector<Deferred, 0> Renames;
for (const COFFShortExport &E : Exp) {
if (E.Private)
continue;
ImportType ImportType = IMPORT_CODE;
if (E.Data)
ImportType = IMPORT_DATA;
if (E.Constant)
ImportType = IMPORT_CONST;
StringRef SymbolName = E.SymbolName.empty() ? E.Name : E.SymbolName;
std::string Name;
if (E.ExtName.empty()) {
Name = std::string(SymbolName);
} else {
Expected<std::string> ReplacedName =
object::replace(SymbolName, E.Name, E.ExtName);
if (!ReplacedName)
return ReplacedName.takeError();
Name.swap(*ReplacedName);
}
ImportNameType NameType;
std::string ExportName;
if (E.Noname) {
NameType = IMPORT_ORDINAL;
} else if (!E.ExportAs.empty()) {
NameType = IMPORT_NAME_EXPORTAS;
ExportName = E.ExportAs;
} else if (!E.ImportName.empty()) {
// If we need to import from a specific ImportName, we may need to use
// a weak alias (which needs another import to point at). But if we can
// express ImportName based on the symbol name and a specific NameType,
// prefer that over an alias.
if (Machine == IMAGE_FILE_MACHINE_I386 &&
applyNameType(IMPORT_NAME_UNDECORATE, Name) == E.ImportName)
NameType = IMPORT_NAME_UNDECORATE;
else if (Machine == IMAGE_FILE_MACHINE_I386 &&
applyNameType(IMPORT_NAME_NOPREFIX, Name) == E.ImportName)
NameType = IMPORT_NAME_NOPREFIX;
else if (isArm64EC(M)) {
NameType = IMPORT_NAME_EXPORTAS;
ExportName = E.ImportName;
} else if (Name == E.ImportName)
NameType = IMPORT_NAME;
else {
Deferred D;
D.Name = Name;
D.ImpType = ImportType;
D.Export = &E;
Renames.push_back(D);
continue;
}
} else {
NameType = getNameType(SymbolName, E.Name, M, MinGW);
}
// On ARM64EC, use EXPORTAS to import demangled name for mangled symbols.
if (ImportType == IMPORT_CODE && isArm64EC(M)) {
if (std::optional<std::string> MangledName =
getArm64ECMangledFunctionName(Name)) {
if (!E.Noname && ExportName.empty()) {
NameType = IMPORT_NAME_EXPORTAS;
ExportName.swap(Name);
}
Name = std::move(*MangledName);
} else if (!E.Noname && ExportName.empty()) {
std::optional<std::string> DemangledName =
getArm64ECDemangledFunctionName(Name);
if (!DemangledName)
return make_error<StringError>(
StringRef(Twine("Invalid ARM64EC function name '" + Name + "'")
.str()),
object_error::parse_failed);
NameType = IMPORT_NAME_EXPORTAS;
ExportName = std::move(*DemangledName);
}
}
RegularImports[applyNameType(NameType, Name)] = Name;
Members.push_back(OF.createShortImport(Name, E.Ordinal, ImportType,
NameType, ExportName, M));
}
for (const auto &D : Renames) {
auto It = RegularImports.find(D.Export->ImportName);
if (It != RegularImports.end()) {
// We have a regular import entry for a symbol with the name we
// want to reference; produce an alias pointing at that.
StringRef Symbol = It->second;
if (D.ImpType == IMPORT_CODE)
Members.push_back(OF.createWeakExternal(Symbol, D.Name, false, M));
Members.push_back(OF.createWeakExternal(Symbol, D.Name, true, M));
} else {
Members.push_back(OF.createShortImport(D.Name, D.Export->Ordinal,
D.ImpType, IMPORT_NAME_EXPORTAS,
D.Export->ImportName, M));
}
}
return Error::success();
};
if (Error e = addExports(Exports, Machine))
return e;
if (Error e = addExports(NativeExports, NativeMachine))
return e;
return writeArchive(Path, Members, SymtabWritingMode::NormalSymtab,
object::Archive::K_COFF,
/*Deterministic*/ true, /*Thin*/ false,
/*OldArchiveBuf*/ nullptr, isArm64EC(Machine));
}
} // namespace object
} // namespace llvm

View File

@@ -0,0 +1,146 @@
//===- COFFImportFile.h - COFF short import file implementation -*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// COFF short import file is a special kind of file which contains
// only symbol names for DLL-exported symbols. This class implements
// exporting of Symbols to create libraries and a SymbolicFile
// interface for the file type.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_OBJECT_COFFIMPORTFILE_H
#define LLVM_OBJECT_COFFIMPORTFILE_H
#include "llvm/ADT/ArrayRef.h"
#include "llvm/IR/Mangler.h"
#include "llvm/Object/COFF.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Object/SymbolicFile.h"
#include "llvm/Support/MemoryBufferRef.h"
#include "llvm/Support/raw_ostream.h"
namespace llvm {
namespace object {
constexpr std::string_view ImportDescriptorPrefix = "__IMPORT_DESCRIPTOR_";
constexpr std::string_view NullImportDescriptorSymbolName =
"__NULL_IMPORT_DESCRIPTOR";
constexpr std::string_view NullThunkDataPrefix = "\x7f";
constexpr std::string_view NullThunkDataSuffix = "_NULL_THUNK_DATA";
class COFFImportFile : public SymbolicFile {
private:
enum SymbolIndex { ImpSymbol, ThunkSymbol, ECAuxSymbol, ECThunkSymbol };
public:
COFFImportFile(MemoryBufferRef Source)
: SymbolicFile(ID_COFFImportFile, Source) {}
static bool classof(Binary const *V) { return V->isCOFFImportFile(); }
void moveSymbolNext(DataRefImpl &Symb) const override { ++Symb.p; }
Error printSymbolName(raw_ostream &OS, DataRefImpl Symb) const override;
Expected<uint32_t> getSymbolFlags(DataRefImpl Symb) const override {
return SymbolRef::SF_Global;
}
basic_symbol_iterator symbol_begin() const override {
return BasicSymbolRef(DataRefImpl(), this);
}
basic_symbol_iterator symbol_end() const override {
DataRefImpl Symb;
if (isData())
Symb.p = ImpSymbol + 1;
else if (COFF::isArm64EC(getMachine()))
Symb.p = ECThunkSymbol + 1;
else
Symb.p = ThunkSymbol + 1;
return BasicSymbolRef(Symb, this);
}
bool is64Bit() const override { return false; }
const coff_import_header *getCOFFImportHeader() const {
return reinterpret_cast<const object::coff_import_header *>(
Data.getBufferStart());
}
uint16_t getMachine() const { return getCOFFImportHeader()->Machine; }
StringRef getFileFormatName() const;
StringRef getExportName() const;
private:
bool isData() const {
return getCOFFImportHeader()->getType() == COFF::IMPORT_DATA;
}
};
struct COFFShortExport {
/// The name of the export as specified in the .def file or on the command
/// line, i.e. "foo" in "/EXPORT:foo", and "bar" in "/EXPORT:foo=bar". This
/// may lack mangling, such as underscore prefixing and stdcall suffixing.
std::string Name;
/// The external, exported name. Only non-empty when export renaming is in
/// effect, i.e. "foo" in "/EXPORT:foo=bar".
std::string ExtName;
/// The real, mangled symbol name from the object file. Given
/// "/export:foo=bar", this could be "_bar@8" if bar is stdcall.
std::string SymbolName;
/// Creates an import library entry that imports from a DLL export with a
/// different name. This is the name of the DLL export that should be
/// referenced when linking against this import library entry. In a .def
/// file, this is "baz" in "EXPORTS\nfoo = bar == baz".
std::string ImportName;
/// Specifies EXPORTAS name. In a .def file, this is "bar" in
/// "EXPORTS\nfoo EXPORTAS bar".
std::string ExportAs;
uint16_t Ordinal = 0;
bool Noname = false;
bool Data = false;
bool Private = false;
bool Constant = false;
friend bool operator==(const COFFShortExport &L, const COFFShortExport &R) {
return L.Name == R.Name && L.ExtName == R.ExtName &&
L.Ordinal == R.Ordinal && L.Noname == R.Noname &&
L.Data == R.Data && L.Private == R.Private;
}
friend bool operator!=(const COFFShortExport &L, const COFFShortExport &R) {
return !(L == R);
}
};
/// Writes a COFF import library containing entries described by the Exports
/// array.
///
/// For hybrid targets such as ARM64EC, additional native entry points can be
/// exposed using the NativeExports parameter. When NativeExports is used, the
/// output import library will expose these native ARM64 imports alongside the
/// entries described in the Exports array. Such a library can be used for
/// linking both ARM64EC and pure ARM64 objects, and the linker will pick only
/// the exports relevant to the target platform. For non-hybrid targets,
/// the NativeExports parameter should not be used.
Error writeImportLibrary(StringRef ImportName, StringRef Path,
ArrayRef<COFFShortExport> Exports,
COFF::MachineTypes Machine, bool MinGW,
ArrayRef<COFFShortExport> NativeExports = {});
} // namespace object
} // namespace llvm
#endif

View File

@@ -0,0 +1,329 @@
//===-- Mangler.cpp - Self-contained c/asm llvm name mangler --------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Unified name mangler for assembly backends.
//
//===----------------------------------------------------------------------===//
#include "llvm/IR/Mangler.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Demangle/Demangle.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/TargetParser/Triple.h"
using namespace llvm;
namespace {
enum ManglerPrefixTy {
Default, ///< Emit default string before each symbol.
Private, ///< Emit "private" prefix before each symbol.
LinkerPrivate ///< Emit "linker private" prefix before each symbol.
};
}
static void getNameWithPrefixImpl(raw_ostream &OS, const Twine &GVName,
ManglerPrefixTy PrefixTy,
const DataLayout &DL, char Prefix) {
SmallString<256> TmpData;
StringRef Name = GVName.toStringRef(TmpData);
assert(!Name.empty() && "getNameWithPrefix requires non-empty name");
// No need to do anything special if the global has the special "do not
// mangle" flag in the name.
if (Name[0] == '\1') {
OS << Name.substr(1);
return;
}
if (DL.doNotMangleLeadingQuestionMark() && Name[0] == '?')
Prefix = '\0';
if (PrefixTy == Private)
OS << DL.getPrivateGlobalPrefix();
else if (PrefixTy == LinkerPrivate)
OS << DL.getLinkerPrivateGlobalPrefix();
if (Prefix != '\0')
OS << Prefix;
// If this is a simple string that doesn't need escaping, just append it.
OS << Name;
}
static void getNameWithPrefixImpl(raw_ostream &OS, const Twine &GVName,
const DataLayout &DL,
ManglerPrefixTy PrefixTy) {
char Prefix = DL.getGlobalPrefix();
return getNameWithPrefixImpl(OS, GVName, PrefixTy, DL, Prefix);
}
void Mangler::getNameWithPrefix(raw_ostream &OS, const Twine &GVName,
const DataLayout &DL) {
return getNameWithPrefixImpl(OS, GVName, DL, Default);
}
void Mangler::getNameWithPrefix(SmallVectorImpl<char> &OutName,
const Twine &GVName, const DataLayout &DL) {
raw_svector_ostream OS(OutName);
char Prefix = DL.getGlobalPrefix();
return getNameWithPrefixImpl(OS, GVName, Default, DL, Prefix);
}
static bool hasByteCountSuffix(CallingConv::ID CC) {
switch (CC) {
case CallingConv::X86_FastCall:
case CallingConv::X86_StdCall:
case CallingConv::X86_VectorCall:
return true;
default:
return false;
}
}
/// Microsoft fastcall and stdcall functions require a suffix on their name
/// indicating the number of words of arguments they take.
static void addByteCountSuffix(raw_ostream &OS, const Function *F,
const DataLayout &DL) {
// Calculate arguments size total.
unsigned ArgWords = 0;
const unsigned PtrSize = DL.getPointerSize();
for (const Argument &A : F->args()) {
// For the purposes of the byte count suffix, structs returned by pointer
// do not count as function arguments.
if (A.hasStructRetAttr())
continue;
// 'Dereference' type in case of byval or inalloca parameter attribute.
uint64_t AllocSize = A.hasPassPointeeByValueCopyAttr() ?
A.getPassPointeeByValueCopySize(DL) :
DL.getTypeAllocSize(A.getType());
// Size should be aligned to pointer size.
ArgWords += alignTo(AllocSize, PtrSize);
}
OS << '@' << ArgWords;
}
void Mangler::getNameWithPrefix(raw_ostream &OS, const GlobalValue *GV,
bool CannotUsePrivateLabel) const {
ManglerPrefixTy PrefixTy = Default;
assert(GV != nullptr && "Invalid Global Value");
if (GV->hasPrivateLinkage()) {
if (CannotUsePrivateLabel)
PrefixTy = LinkerPrivate;
else
PrefixTy = Private;
}
const DataLayout &DL = GV->getDataLayout();
if (!GV->hasName()) {
// Get the ID for the global, assigning a new one if we haven't got one
// already.
unsigned &ID = AnonGlobalIDs[GV];
if (ID == 0)
ID = AnonGlobalIDs.size();
// Must mangle the global into a unique ID.
getNameWithPrefixImpl(OS, "__unnamed_" + Twine(ID), DL, PrefixTy);
return;
}
StringRef Name = GV->getName();
char Prefix = DL.getGlobalPrefix();
// Mangle functions with Microsoft calling conventions specially. Only do
// this mangling for x86_64 vectorcall and 32-bit x86.
const Function *MSFunc = dyn_cast_or_null<Function>(GV->getAliaseeObject());
// Don't add byte count suffixes when '\01' or '?' are in the first
// character.
if (Name.starts_with("\01") ||
(DL.doNotMangleLeadingQuestionMark() && Name.starts_with("?")))
MSFunc = nullptr;
CallingConv::ID CC =
MSFunc ? MSFunc->getCallingConv() : (unsigned)CallingConv::C;
if (!DL.hasMicrosoftFastStdCallMangling() &&
CC != CallingConv::X86_VectorCall)
MSFunc = nullptr;
if (MSFunc) {
if (CC == CallingConv::X86_FastCall)
Prefix = '@'; // fastcall functions have an @ prefix instead of _.
else if (CC == CallingConv::X86_VectorCall)
Prefix = '\0'; // vectorcall functions have no prefix.
}
getNameWithPrefixImpl(OS, Name, PrefixTy, DL, Prefix);
if (!MSFunc)
return;
// If we are supposed to add a microsoft-style suffix for stdcall, fastcall,
// or vectorcall, add it. These functions have a suffix of @N where N is the
// cumulative byte size of all of the parameters to the function in decimal.
if (CC == CallingConv::X86_VectorCall)
OS << '@'; // vectorcall functions use a double @ suffix.
FunctionType *FT = MSFunc->getFunctionType();
if (hasByteCountSuffix(CC) &&
// "Pure" variadic functions do not receive @0 suffix.
(!FT->isVarArg() || FT->getNumParams() == 0 ||
(FT->getNumParams() == 1 && MSFunc->hasStructRetAttr())))
addByteCountSuffix(OS, MSFunc, DL);
}
void Mangler::getNameWithPrefix(SmallVectorImpl<char> &OutName,
const GlobalValue *GV,
bool CannotUsePrivateLabel) const {
raw_svector_ostream OS(OutName);
getNameWithPrefix(OS, GV, CannotUsePrivateLabel);
}
// Check if the name needs quotes to be safe for the linker to interpret.
static bool canBeUnquotedInDirective(char C) {
return isAlnum(C) || C == '_' || C == '@' || C == '#';
}
static bool canBeUnquotedInDirective(StringRef Name) {
if (Name.empty())
return false;
// If any of the characters in the string is an unacceptable character, force
// quotes.
for (char C : Name) {
if (!canBeUnquotedInDirective(C))
return false;
}
return true;
}
void llvm::emitLinkerFlagsForGlobalCOFF(raw_ostream &OS, const GlobalValue *GV,
const Triple &TT, Mangler &Mangler) {
if (GV->hasDLLExportStorageClass() && !GV->isDeclaration()) {
if (TT.isWindowsMSVCEnvironment())
OS << " /EXPORT:";
else
OS << " -export:";
bool NeedQuotes = GV->hasName() && !canBeUnquotedInDirective(GV->getName());
if (NeedQuotes)
OS << "\"";
if (TT.isWindowsGNUEnvironment() || TT.isWindowsCygwinEnvironment()) {
std::string Flag;
raw_string_ostream FlagOS(Flag);
Mangler.getNameWithPrefix(FlagOS, GV, false);
FlagOS.flush();
if (Flag[0] == GV->getDataLayout().getGlobalPrefix())
OS << Flag.substr(1);
else
OS << Flag;
} else {
Mangler.getNameWithPrefix(OS, GV, false);
}
if (TT.isWindowsArm64EC()) {
// Use EXPORTAS for mangled ARM64EC symbols.
// FIXME: During LTO, we're invoked prior to the EC lowering pass,
// so symbols are not yet mangled. Emitting the unmangled name
// typically functions correctly; the linker can resolve the export
// with the demangled alias.
if (std::optional<std::string> demangledName =
getArm64ECDemangledFunctionName(GV->getName()))
OS << ",EXPORTAS," << *demangledName;
}
if (NeedQuotes)
OS << "\"";
if (!GV->getValueType()->isFunctionTy()) {
if (TT.isWindowsMSVCEnvironment())
OS << ",DATA";
else
OS << ",data";
}
}
if (GV->hasHiddenVisibility() && !GV->isDeclaration() && TT.isOSCygMing()) {
OS << " -exclude-symbols:";
bool NeedQuotes = GV->hasName() && !canBeUnquotedInDirective(GV->getName());
if (NeedQuotes)
OS << "\"";
std::string Flag;
raw_string_ostream FlagOS(Flag);
Mangler.getNameWithPrefix(FlagOS, GV, false);
FlagOS.flush();
if (Flag[0] == GV->getDataLayout().getGlobalPrefix())
OS << Flag.substr(1);
else
OS << Flag;
if (NeedQuotes)
OS << "\"";
}
}
void llvm::emitLinkerFlagsForUsedCOFF(raw_ostream &OS, const GlobalValue *GV,
const Triple &T, Mangler &M) {
if (!T.isWindowsMSVCEnvironment())
return;
OS << " /INCLUDE:";
bool NeedQuotes = GV->hasName() && !canBeUnquotedInDirective(GV->getName());
if (NeedQuotes)
OS << "\"";
M.getNameWithPrefix(OS, GV, false);
if (NeedQuotes)
OS << "\"";
}
std::optional<std::string> llvm::getArm64ECMangledFunctionName(StringRef Name) {
if (Name[0] != '?') {
// For non-C++ symbols, prefix the name with "#" unless it's already
// mangled.
if (Name[0] == '#')
return std::nullopt;
return std::optional<std::string>(("#" + Name).str());
}
// If the name contains $$h, then it is already mangled.
if (Name.contains("$$h"))
return std::nullopt;
// Ask the demangler where we should insert "$$h".
auto InsertIdx = getArm64ECInsertionPointInMangledName(Name);
if (!InsertIdx)
return std::nullopt;
return std::optional<std::string>(
(Name.substr(0, *InsertIdx) + "$$h" + Name.substr(*InsertIdx)).str());
}
std::optional<std::string>
llvm::getArm64ECDemangledFunctionName(StringRef Name) {
// For non-C++ names, drop the "#" prefix.
if (Name[0] == '#')
return std::optional<std::string>(Name.substr(1));
if (Name[0] != '?')
return std::nullopt;
// Drop the ARM64EC "$$h" tag.
std::pair<StringRef, StringRef> Pair = Name.split("$$h");
if (Pair.second.empty())
return std::nullopt;
return std::optional<std::string>((Pair.first + Pair.second).str());
}

View File

@@ -0,0 +1,273 @@
//===- llvm/unittest/IR/ManglerTest.cpp - Mangler unit tests --------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "llvm/IR/Mangler.h"
#include "llvm/IR/CallingConv.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/GlobalValue.h"
#include "llvm/IR/Module.h"
#include "gtest/gtest.h"
using namespace llvm;
static std::string mangleStr(StringRef IRName, Mangler &Mang,
const DataLayout &DL) {
std::string Mangled;
raw_string_ostream SS(Mangled);
Mang.getNameWithPrefix(SS, IRName, DL);
return Mangled;
}
static std::string mangleFunc(StringRef IRName,
GlobalValue::LinkageTypes Linkage,
llvm::CallingConv::ID CC, Module &Mod,
Mangler &Mang) {
Type *VoidTy = Type::getVoidTy(Mod.getContext());
Type *I32Ty = Type::getInt32Ty(Mod.getContext());
FunctionType *FTy =
FunctionType::get(VoidTy, {I32Ty, I32Ty, I32Ty}, /*isVarArg=*/false);
Function *F = Function::Create(FTy, Linkage, IRName, &Mod);
F->setCallingConv(CC);
std::string Mangled;
raw_string_ostream SS(Mangled);
Mang.getNameWithPrefix(SS, F, false);
F->eraseFromParent();
return Mangled;
}
namespace {
TEST(ManglerTest, MachO) {
LLVMContext Ctx;
DataLayout DL("m:o"); // macho
Module Mod("test", Ctx);
Mod.setDataLayout(DL);
Mangler Mang;
EXPECT_EQ(mangleStr("foo", Mang, DL), "_foo");
EXPECT_EQ(mangleStr("\01foo", Mang, DL), "foo");
EXPECT_EQ(mangleStr("?foo", Mang, DL), "_?foo");
EXPECT_EQ(mangleFunc("foo", llvm::GlobalValue::ExternalLinkage,
llvm::CallingConv::C, Mod, Mang),
"_foo");
EXPECT_EQ(mangleFunc("?foo", llvm::GlobalValue::ExternalLinkage,
llvm::CallingConv::C, Mod, Mang),
"_?foo");
EXPECT_EQ(mangleFunc("foo", llvm::GlobalValue::PrivateLinkage,
llvm::CallingConv::C, Mod, Mang),
"L_foo");
}
TEST(ManglerTest, WindowsX86) {
LLVMContext Ctx;
DataLayout DL("m:x-p:32:32"); // 32-bit windows
Module Mod("test", Ctx);
Mod.setDataLayout(DL);
Mangler Mang;
EXPECT_EQ(mangleStr("foo", Mang, DL), "_foo");
EXPECT_EQ(mangleStr("\01foo", Mang, DL), "foo");
EXPECT_EQ(mangleStr("?foo", Mang, DL), "?foo");
EXPECT_EQ(mangleFunc("foo", llvm::GlobalValue::ExternalLinkage,
llvm::CallingConv::C, Mod, Mang),
"_foo");
EXPECT_EQ(mangleFunc("?foo", llvm::GlobalValue::ExternalLinkage,
llvm::CallingConv::C, Mod, Mang),
"?foo");
EXPECT_EQ(mangleFunc("foo", llvm::GlobalValue::PrivateLinkage,
llvm::CallingConv::C, Mod, Mang),
"L_foo");
// Test calling conv mangling.
EXPECT_EQ(mangleFunc("stdcall", llvm::GlobalValue::ExternalLinkage,
llvm::CallingConv::X86_StdCall, Mod, Mang),
"_stdcall@12");
EXPECT_EQ(mangleFunc("fastcall", llvm::GlobalValue::ExternalLinkage,
llvm::CallingConv::X86_FastCall, Mod, Mang),
"@fastcall@12");
EXPECT_EQ(mangleFunc("vectorcall", llvm::GlobalValue::ExternalLinkage,
llvm::CallingConv::X86_VectorCall, Mod, Mang),
"vectorcall@@12");
// Adding a '?' prefix blocks calling convention mangling.
EXPECT_EQ(mangleFunc("?fastcall", llvm::GlobalValue::ExternalLinkage,
llvm::CallingConv::X86_FastCall, Mod, Mang),
"?fastcall");
}
TEST(ManglerTest, WindowsX64) {
LLVMContext Ctx;
DataLayout DL("m:w-p:64:64"); // windows
Module Mod("test", Ctx);
Mod.setDataLayout(DL);
Mangler Mang;
EXPECT_EQ(mangleStr("foo", Mang, DL), "foo");
EXPECT_EQ(mangleStr("\01foo", Mang, DL), "foo");
EXPECT_EQ(mangleStr("?foo", Mang, DL), "?foo");
EXPECT_EQ(mangleFunc("foo", llvm::GlobalValue::ExternalLinkage,
llvm::CallingConv::C, Mod, Mang),
"foo");
EXPECT_EQ(mangleFunc("?foo", llvm::GlobalValue::ExternalLinkage,
llvm::CallingConv::C, Mod, Mang),
"?foo");
EXPECT_EQ(mangleFunc("foo", llvm::GlobalValue::PrivateLinkage,
llvm::CallingConv::C, Mod, Mang),
".Lfoo");
// Test calling conv mangling.
EXPECT_EQ(mangleFunc("stdcall", llvm::GlobalValue::ExternalLinkage,
llvm::CallingConv::X86_StdCall, Mod, Mang),
"stdcall");
EXPECT_EQ(mangleFunc("fastcall", llvm::GlobalValue::ExternalLinkage,
llvm::CallingConv::X86_FastCall, Mod, Mang),
"fastcall");
EXPECT_EQ(mangleFunc("vectorcall", llvm::GlobalValue::ExternalLinkage,
llvm::CallingConv::X86_VectorCall, Mod, Mang),
"vectorcall@@24");
// Adding a '?' prefix blocks calling convention mangling.
EXPECT_EQ(mangleFunc("?vectorcall", llvm::GlobalValue::ExternalLinkage,
llvm::CallingConv::X86_VectorCall, Mod, Mang),
"?vectorcall");
}
TEST(ManglerTest, UEFIX64) {
LLVMContext Ctx;
DataLayout DL("e-m:w-p270:32:32-p271:32:32-p272:64:64-"
"i64:64-i128:128-f80:128-n8:16:32:64-S128"); // uefi X86_64
Module Mod("test", Ctx);
Mod.setDataLayout(DL);
Mangler Mang;
EXPECT_EQ(mangleStr("foo", Mang, DL), "foo");
EXPECT_EQ(mangleStr("\01foo", Mang, DL), "foo");
EXPECT_EQ(mangleStr("?foo", Mang, DL), "?foo");
EXPECT_EQ(mangleFunc("foo", llvm::GlobalValue::ExternalLinkage,
llvm::CallingConv::C, Mod, Mang),
"foo");
EXPECT_EQ(mangleFunc("?foo", llvm::GlobalValue::ExternalLinkage,
llvm::CallingConv::C, Mod, Mang),
"?foo");
EXPECT_EQ(mangleFunc("foo", llvm::GlobalValue::PrivateLinkage,
llvm::CallingConv::C, Mod, Mang),
".Lfoo");
}
TEST(ManglerTest, XCOFF) {
LLVMContext Ctx;
DataLayout DL("m:a"); // XCOFF/AIX
Module Mod("test", Ctx);
Mod.setDataLayout(DL);
Mangler Mang;
EXPECT_EQ(mangleStr("foo", Mang, DL), "foo");
EXPECT_EQ(mangleStr("\01foo", Mang, DL), "foo");
EXPECT_EQ(mangleStr("?foo", Mang, DL), "?foo");
EXPECT_EQ(mangleFunc("foo", llvm::GlobalValue::ExternalLinkage,
llvm::CallingConv::C, Mod, Mang),
"foo");
EXPECT_EQ(mangleFunc("?foo", llvm::GlobalValue::ExternalLinkage,
llvm::CallingConv::C, Mod, Mang),
"?foo");
EXPECT_EQ(mangleFunc("foo", llvm::GlobalValue::PrivateLinkage,
llvm::CallingConv::C, Mod, Mang),
"L..foo");
}
TEST(ManglerTest, GOFF) {
LLVMContext Ctx;
DataLayout DL("m:l"); // GOFF
Module Mod("test", Ctx);
Mod.setDataLayout(DL);
Mangler Mang;
EXPECT_EQ(mangleStr("foo", Mang, DL), "foo");
EXPECT_EQ(mangleStr("\01foo", Mang, DL), "foo");
EXPECT_EQ(mangleStr("?foo", Mang, DL), "?foo");
EXPECT_EQ(mangleFunc("foo", llvm::GlobalValue::ExternalLinkage,
llvm::CallingConv::C, Mod, Mang),
"foo");
EXPECT_EQ(mangleFunc("foo", llvm::GlobalValue::PrivateLinkage,
llvm::CallingConv::C, Mod, Mang),
"L#foo");
}
TEST(ManglerTest, Arm64EC) {
constexpr std::string_view Arm64ECNames[] = {
// Basic C name.
"#Foo",
// Basic C++ name.
"?foo@@$$hYAHXZ",
// Regression test: https://github.com/llvm/llvm-project/issues/115231
"?GetValue@?$Wrapper@UA@@@@$$hQEBAHXZ",
// Symbols from:
// ```
// namespace A::B::C::D {
// struct Base {
// virtual int f() { return 0; }
// };
// }
// struct Derived : public A::B::C::D::Base {
// virtual int f() override { return 1; }
// };
// A::B::C::D::Base* MakeObj() { return new Derived(); }
// ```
// void * __cdecl operator new(unsigned __int64)
"??2@$$hYAPEAX_K@Z",
// public: virtual int __cdecl A::B::C::D::Base::f(void)
"?f@Base@D@C@B@A@@$$hUEAAHXZ",
// public: __cdecl A::B::C::D::Base::Base(void)
"??0Base@D@C@B@A@@$$hQEAA@XZ",
// public: virtual int __cdecl Derived::f(void)
"?f@Derived@@$$hUEAAHXZ",
// public: __cdecl Derived::Derived(void)
"??0Derived@@$$hQEAA@XZ",
// struct A::B::C::D::Base * __cdecl MakeObj(void)
"?MakeObj@@$$hYAPEAUBase@D@C@B@A@@XZ",
// Symbols from:
// ```
// template <typename T> struct WW { struct Z{}; };
// template <typename X> struct Wrapper {
// int GetValue(typename WW<X>::Z) const;
// };
// struct A { };
// template <typename X> int Wrapper<X>::GetValue(typename WW<X>::Z) const
// { return 3; }
// template class Wrapper<A>;
// ```
// public: int __cdecl Wrapper<struct A>::GetValue(struct WW<struct
// A>::Z)const
"?GetValue@?$Wrapper@UA@@@@$$hQEBAHUZ@?$WW@UA@@@@@Z",
};
for (const auto &Arm64ECName : Arm64ECNames) {
// Check that this is a mangled name.
EXPECT_TRUE(isArm64ECMangledFunctionName(Arm64ECName))
<< "Test case: " << Arm64ECName;
// Refuse to mangle it again.
EXPECT_FALSE(getArm64ECMangledFunctionName(Arm64ECName).has_value())
<< "Test case: " << Arm64ECName;
// Demangle.
auto Arm64Name = getArm64ECDemangledFunctionName(Arm64ECName);
EXPECT_TRUE(Arm64Name.has_value()) << "Test case: " << Arm64ECName;
// Check that it is not mangled.
EXPECT_FALSE(isArm64ECMangledFunctionName(Arm64Name.value()))
<< "Test case: " << Arm64ECName;
// Refuse to demangle it again.
EXPECT_FALSE(getArm64ECDemangledFunctionName(Arm64Name.value()).has_value())
<< "Test case: " << Arm64ECName;
// Round-trip.
auto RoundTripArm64ECName =
getArm64ECMangledFunctionName(Arm64Name.value());
EXPECT_EQ(RoundTripArm64ECName, Arm64ECName);
}
}
} // end anonymous namespace

View File

@@ -0,0 +1,792 @@
//===-- llvm/Support/MathExtras.h - Useful math functions -------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file contains some functions that are useful for math stuff.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_SUPPORT_MATHEXTRAS_H
#define LLVM_SUPPORT_MATHEXTRAS_H
#include "llvm/ADT/bit.h"
#include "llvm/Support/Compiler.h"
#include <cassert>
#include <climits>
#include <cstdint>
#include <cstring>
#include <limits>
#include <type_traits>
namespace llvm {
/// Some template parameter helpers to optimize for bitwidth, for functions that
/// take multiple arguments.
// We can't verify signedness, since callers rely on implicit coercions to
// signed/unsigned.
template <typename T, typename U>
using enableif_int =
std::enable_if_t<std::is_integral_v<T> && std::is_integral_v<U>>;
// Use std::common_type_t to widen only up to the widest argument.
template <typename T, typename U, typename = enableif_int<T, U>>
using common_uint =
std::common_type_t<std::make_unsigned_t<T>, std::make_unsigned_t<U>>;
template <typename T, typename U, typename = enableif_int<T, U>>
using common_sint =
std::common_type_t<std::make_signed_t<T>, std::make_signed_t<U>>;
/// Mathematical constants.
namespace numbers {
// TODO: Track C++20 std::numbers.
// clang-format off
constexpr double e = 0x1.5bf0a8b145769P+1, // (2.7182818284590452354) https://oeis.org/A001113
egamma = 0x1.2788cfc6fb619P-1, // (.57721566490153286061) https://oeis.org/A001620
ln2 = 0x1.62e42fefa39efP-1, // (.69314718055994530942) https://oeis.org/A002162
ln10 = 0x1.26bb1bbb55516P+1, // (2.3025850929940456840) https://oeis.org/A002392
log2e = 0x1.71547652b82feP+0, // (1.4426950408889634074)
log10e = 0x1.bcb7b1526e50eP-2, // (.43429448190325182765)
pi = 0x1.921fb54442d18P+1, // (3.1415926535897932385) https://oeis.org/A000796
inv_pi = 0x1.45f306dc9c883P-2, // (.31830988618379067154) https://oeis.org/A049541
sqrtpi = 0x1.c5bf891b4ef6bP+0, // (1.7724538509055160273) https://oeis.org/A002161
inv_sqrtpi = 0x1.20dd750429b6dP-1, // (.56418958354775628695) https://oeis.org/A087197
sqrt2 = 0x1.6a09e667f3bcdP+0, // (1.4142135623730950488) https://oeis.org/A00219
inv_sqrt2 = 0x1.6a09e667f3bcdP-1, // (.70710678118654752440)
sqrt3 = 0x1.bb67ae8584caaP+0, // (1.7320508075688772935) https://oeis.org/A002194
inv_sqrt3 = 0x1.279a74590331cP-1, // (.57735026918962576451)
phi = 0x1.9e3779b97f4a8P+0; // (1.6180339887498948482) https://oeis.org/A001622
constexpr float ef = 0x1.5bf0a8P+1F, // (2.71828183) https://oeis.org/A001113
egammaf = 0x1.2788d0P-1F, // (.577215665) https://oeis.org/A001620
ln2f = 0x1.62e430P-1F, // (.693147181) https://oeis.org/A002162
ln10f = 0x1.26bb1cP+1F, // (2.30258509) https://oeis.org/A002392
log2ef = 0x1.715476P+0F, // (1.44269504)
log10ef = 0x1.bcb7b2P-2F, // (.434294482)
pif = 0x1.921fb6P+1F, // (3.14159265) https://oeis.org/A000796
inv_pif = 0x1.45f306P-2F, // (.318309886) https://oeis.org/A049541
sqrtpif = 0x1.c5bf8aP+0F, // (1.77245385) https://oeis.org/A002161
inv_sqrtpif = 0x1.20dd76P-1F, // (.564189584) https://oeis.org/A087197
sqrt2f = 0x1.6a09e6P+0F, // (1.41421356) https://oeis.org/A002193
inv_sqrt2f = 0x1.6a09e6P-1F, // (.707106781)
sqrt3f = 0x1.bb67aeP+0F, // (1.73205081) https://oeis.org/A002194
inv_sqrt3f = 0x1.279a74P-1F, // (.577350269)
phif = 0x1.9e377aP+0F; // (1.61803399) https://oeis.org/A001622
// clang-format on
} // namespace numbers
/// Create a bitmask with the N right-most bits set to 1, and all other
/// bits set to 0. Only unsigned types are allowed.
template <typename T> T maskTrailingOnes(unsigned N) {
static_assert(std::is_unsigned_v<T>, "Invalid type!");
const unsigned Bits = CHAR_BIT * sizeof(T);
assert(N <= Bits && "Invalid bit index");
if (N == 0)
return 0;
return T(-1) >> (Bits - N);
}
/// Create a bitmask with the N left-most bits set to 1, and all other
/// bits set to 0. Only unsigned types are allowed.
template <typename T> T maskLeadingOnes(unsigned N) {
return ~maskTrailingOnes<T>(CHAR_BIT * sizeof(T) - N);
}
/// Create a bitmask with the N right-most bits set to 0, and all other
/// bits set to 1. Only unsigned types are allowed.
template <typename T> T maskTrailingZeros(unsigned N) {
return maskLeadingOnes<T>(CHAR_BIT * sizeof(T) - N);
}
/// Create a bitmask with the N left-most bits set to 0, and all other
/// bits set to 1. Only unsigned types are allowed.
template <typename T> T maskLeadingZeros(unsigned N) {
return maskTrailingOnes<T>(CHAR_BIT * sizeof(T) - N);
}
/// Macro compressed bit reversal table for 256 bits.
///
/// http://graphics.stanford.edu/~seander/bithacks.html#BitReverseTable
static const unsigned char BitReverseTable256[256] = {
#define R2(n) n, n + 2 * 64, n + 1 * 64, n + 3 * 64
#define R4(n) R2(n), R2(n + 2 * 16), R2(n + 1 * 16), R2(n + 3 * 16)
#define R6(n) R4(n), R4(n + 2 * 4), R4(n + 1 * 4), R4(n + 3 * 4)
R6(0), R6(2), R6(1), R6(3)
#undef R2
#undef R4
#undef R6
};
/// Reverse the bits in \p Val.
template <typename T> T reverseBits(T Val) {
#if __has_builtin(__builtin_bitreverse8)
if constexpr (std::is_same_v<T, uint8_t>)
return __builtin_bitreverse8(Val);
#endif
#if __has_builtin(__builtin_bitreverse16)
if constexpr (std::is_same_v<T, uint16_t>)
return __builtin_bitreverse16(Val);
#endif
#if __has_builtin(__builtin_bitreverse32)
if constexpr (std::is_same_v<T, uint32_t>)
return __builtin_bitreverse32(Val);
#endif
#if __has_builtin(__builtin_bitreverse64)
if constexpr (std::is_same_v<T, uint64_t>)
return __builtin_bitreverse64(Val);
#endif
unsigned char in[sizeof(Val)];
unsigned char out[sizeof(Val)];
std::memcpy(in, &Val, sizeof(Val));
for (unsigned i = 0; i < sizeof(Val); ++i)
out[(sizeof(Val) - i) - 1] = BitReverseTable256[in[i]];
std::memcpy(&Val, out, sizeof(Val));
return Val;
}
// NOTE: The following support functions use the _32/_64 extensions instead of
// type overloading so that signed and unsigned integers can be used without
// ambiguity.
/// Return the high 32 bits of a 64 bit value.
constexpr uint32_t Hi_32(uint64_t Value) {
return static_cast<uint32_t>(Value >> 32);
}
/// Return the low 32 bits of a 64 bit value.
constexpr uint32_t Lo_32(uint64_t Value) {
return static_cast<uint32_t>(Value);
}
/// Make a 64-bit integer from a high / low pair of 32-bit integers.
constexpr uint64_t Make_64(uint32_t High, uint32_t Low) {
return ((uint64_t)High << 32) | (uint64_t)Low;
}
/// Checks if an integer fits into the given bit width.
template <unsigned N> constexpr bool isInt(int64_t x) {
if constexpr (N == 0)
return 0 == x;
if constexpr (N == 8)
return static_cast<int8_t>(x) == x;
if constexpr (N == 16)
return static_cast<int16_t>(x) == x;
if constexpr (N == 32)
return static_cast<int32_t>(x) == x;
if constexpr (N < 64)
return -(INT64_C(1) << (N - 1)) <= x && x < (INT64_C(1) << (N - 1));
(void)x; // MSVC v19.25 warns that x is unused.
return true;
}
/// Checks if a signed integer is an N bit number shifted left by S.
template <unsigned N, unsigned S>
constexpr bool isShiftedInt(int64_t x) {
static_assert(S < 64, "isShiftedInt<N, S> with S >= 64 is too much.");
static_assert(N + S <= 64, "isShiftedInt<N, S> with N + S > 64 is too wide.");
return isInt<N + S>(x) && (x % (UINT64_C(1) << S) == 0);
}
/// Checks if an unsigned integer fits into the given bit width.
template <unsigned N> constexpr bool isUInt(uint64_t x) {
if constexpr (N == 0)
return 0 == x;
if constexpr (N == 8)
return static_cast<uint8_t>(x) == x;
if constexpr (N == 16)
return static_cast<uint16_t>(x) == x;
if constexpr (N == 32)
return static_cast<uint32_t>(x) == x;
if constexpr (N < 64)
return x < (UINT64_C(1) << (N));
(void)x; // MSVC v19.25 warns that x is unused.
return true;
}
/// Checks if a unsigned integer is an N bit number shifted left by S.
template <unsigned N, unsigned S>
constexpr bool isShiftedUInt(uint64_t x) {
static_assert(S < 64, "isShiftedUInt<N, S> with S >= 64 is too much.");
static_assert(N + S <= 64,
"isShiftedUInt<N, S> with N + S > 64 is too wide.");
// S must be strictly less than 64. So 1 << S is not undefined behavior.
return isUInt<N + S>(x) && (x % (UINT64_C(1) << S) == 0);
}
/// Gets the maximum value for a N-bit unsigned integer.
inline uint64_t maxUIntN(uint64_t N) {
assert(N <= 64 && "integer width out of range");
// uint64_t(1) << 64 is undefined behavior, so we can't do
// (uint64_t(1) << N) - 1
// without checking first that N != 64. But this works and doesn't have a
// branch for N != 0.
// Unfortunately, shifting a uint64_t right by 64 bit is undefined
// behavior, so the condition on N == 0 is necessary. Fortunately, most
// optimizers do not emit branches for this check.
if (N == 0)
return 0;
return UINT64_MAX >> (64 - N);
}
/// Gets the minimum value for a N-bit signed integer.
inline int64_t minIntN(int64_t N) {
assert(N <= 64 && "integer width out of range");
if (N == 0)
return 0;
return UINT64_C(1) + ~(UINT64_C(1) << (N - 1));
}
/// Gets the maximum value for a N-bit signed integer.
inline int64_t maxIntN(int64_t N) {
assert(N <= 64 && "integer width out of range");
// This relies on two's complement wraparound when N == 64, so we convert to
// int64_t only at the very end to avoid UB.
if (N == 0)
return 0;
return (UINT64_C(1) << (N - 1)) - 1;
}
/// Checks if an unsigned integer fits into the given (dynamic) bit width.
inline bool isUIntN(unsigned N, uint64_t x) {
return N >= 64 || x <= maxUIntN(N);
}
/// Checks if an signed integer fits into the given (dynamic) bit width.
inline bool isIntN(unsigned N, int64_t x) {
return N >= 64 || (minIntN(N) <= x && x <= maxIntN(N));
}
/// Return true if the argument is a non-empty sequence of ones starting at the
/// least significant bit with the remainder zero (32 bit version).
/// Ex. isMask_32(0x0000FFFFU) == true.
constexpr bool isMask_32(uint32_t Value) {
return Value && ((Value + 1) & Value) == 0;
}
/// Return true if the argument is a non-empty sequence of ones starting at the
/// least significant bit with the remainder zero (64 bit version).
constexpr bool isMask_64(uint64_t Value) {
return Value && ((Value + 1) & Value) == 0;
}
/// Return true if the argument contains a non-empty sequence of ones with the
/// remainder zero (32 bit version.) Ex. isShiftedMask_32(0x0000FF00U) == true.
constexpr bool isShiftedMask_32(uint32_t Value) {
return Value && isMask_32((Value - 1) | Value);
}
/// Return true if the argument contains a non-empty sequence of ones with the
/// remainder zero (64 bit version.)
constexpr bool isShiftedMask_64(uint64_t Value) {
return Value && isMask_64((Value - 1) | Value);
}
/// Return true if the argument is a power of two > 0.
/// Ex. isPowerOf2_32(0x00100000U) == true (32 bit edition.)
constexpr bool isPowerOf2_32(uint32_t Value) {
return llvm::has_single_bit(Value);
}
/// Return true if the argument is a power of two > 0 (64 bit edition.)
constexpr bool isPowerOf2_64(uint64_t Value) {
return llvm::has_single_bit(Value);
}
/// Return true if the argument contains a non-empty sequence of ones with the
/// remainder zero (32 bit version.) Ex. isShiftedMask_32(0x0000FF00U) == true.
/// If true, \p MaskIdx will specify the index of the lowest set bit and \p
/// MaskLen is updated to specify the length of the mask, else neither are
/// updated.
inline bool isShiftedMask_32(uint32_t Value, unsigned &MaskIdx,
unsigned &MaskLen) {
if (!isShiftedMask_32(Value))
return false;
MaskIdx = llvm::countr_zero(Value);
MaskLen = llvm::popcount(Value);
return true;
}
/// Return true if the argument contains a non-empty sequence of ones with the
/// remainder zero (64 bit version.) If true, \p MaskIdx will specify the index
/// of the lowest set bit and \p MaskLen is updated to specify the length of the
/// mask, else neither are updated.
inline bool isShiftedMask_64(uint64_t Value, unsigned &MaskIdx,
unsigned &MaskLen) {
if (!isShiftedMask_64(Value))
return false;
MaskIdx = llvm::countr_zero(Value);
MaskLen = llvm::popcount(Value);
return true;
}
/// Compile time Log2.
/// Valid only for positive powers of two.
template <size_t kValue> constexpr size_t CTLog2() {
static_assert(kValue > 0 && llvm::isPowerOf2_64(kValue),
"Value is not a valid power of 2");
return 1 + CTLog2<kValue / 2>();
}
template <> constexpr size_t CTLog2<1>() { return 0; }
/// Return the floor log base 2 of the specified value, -1 if the value is zero.
/// (32 bit edition.)
/// Ex. Log2_32(32) == 5, Log2_32(1) == 0, Log2_32(0) == -1, Log2_32(6) == 2
inline unsigned Log2_32(uint32_t Value) {
return 31 - llvm::countl_zero(Value);
}
/// Return the floor log base 2 of the specified value, -1 if the value is zero.
/// (64 bit edition.)
inline unsigned Log2_64(uint64_t Value) {
return 63 - llvm::countl_zero(Value);
}
/// Return the ceil log base 2 of the specified value, 32 if the value is zero.
/// (32 bit edition).
/// Ex. Log2_32_Ceil(32) == 5, Log2_32_Ceil(1) == 0, Log2_32_Ceil(6) == 3
inline unsigned Log2_32_Ceil(uint32_t Value) {
return 32 - llvm::countl_zero(Value - 1);
}
/// Return the ceil log base 2 of the specified value, 64 if the value is zero.
/// (64 bit edition.)
inline unsigned Log2_64_Ceil(uint64_t Value) {
return 64 - llvm::countl_zero(Value - 1);
}
/// A and B are either alignments or offsets. Return the minimum alignment that
/// may be assumed after adding the two together.
template <typename U, typename V, typename T = common_uint<U, V>>
constexpr T MinAlign(U A, V B) {
// The largest power of 2 that divides both A and B.
//
// Replace "-Value" by "1+~Value" in the following commented code to avoid
// MSVC warning C4146
// return (A | B) & -(A | B);
return (A | B) & (1 + ~(A | B));
}
/// Fallback when arguments aren't integral.
constexpr uint64_t MinAlign(uint64_t A, uint64_t B) {
return (A | B) & (1 + ~(A | B));
}
/// Returns the next power of two (in 64-bits) that is strictly greater than A.
/// Returns zero on overflow.
constexpr uint64_t NextPowerOf2(uint64_t A) {
A |= (A >> 1);
A |= (A >> 2);
A |= (A >> 4);
A |= (A >> 8);
A |= (A >> 16);
A |= (A >> 32);
return A + 1;
}
/// Returns the power of two which is greater than or equal to the given value.
/// Essentially, it is a ceil operation across the domain of powers of two.
inline uint64_t PowerOf2Ceil(uint64_t A) {
if (!A || A > UINT64_MAX / 2)
return 0;
return UINT64_C(1) << Log2_64_Ceil(A);
}
/// Returns the integer ceil(Numerator / Denominator). Unsigned version.
/// Guaranteed to never overflow.
template <typename U, typename V, typename T = common_uint<U, V>>
constexpr T divideCeil(U Numerator, V Denominator) {
assert(Denominator && "Division by zero");
T Bias = (Numerator != 0);
return (Numerator - Bias) / Denominator + Bias;
}
/// Fallback when arguments aren't integral.
constexpr uint64_t divideCeil(uint64_t Numerator, uint64_t Denominator) {
assert(Denominator && "Division by zero");
uint64_t Bias = (Numerator != 0);
return (Numerator - Bias) / Denominator + Bias;
}
// Check whether divideCeilSigned or divideFloorSigned would overflow. This
// happens only when Numerator = INT_MIN and Denominator = -1.
template <typename U, typename V>
constexpr bool divideSignedWouldOverflow(U Numerator, V Denominator) {
return Numerator == std::numeric_limits<U>::min() && Denominator == -1;
}
/// Returns the integer ceil(Numerator / Denominator). Signed version.
/// Overflow is explicitly forbidden with an assert.
template <typename U, typename V, typename T = common_sint<U, V>>
constexpr T divideCeilSigned(U Numerator, V Denominator) {
assert(Denominator && "Division by zero");
assert(!divideSignedWouldOverflow(Numerator, Denominator) &&
"Divide would overflow");
if (!Numerator)
return 0;
// C's integer division rounds towards 0.
T Bias = Denominator >= 0 ? 1 : -1;
bool SameSign = (Numerator >= 0) == (Denominator >= 0);
return SameSign ? (Numerator - Bias) / Denominator + 1
: Numerator / Denominator;
}
/// Returns the integer floor(Numerator / Denominator). Signed version.
/// Overflow is explicitly forbidden with an assert.
template <typename U, typename V, typename T = common_sint<U, V>>
constexpr T divideFloorSigned(U Numerator, V Denominator) {
assert(Denominator && "Division by zero");
assert(!divideSignedWouldOverflow(Numerator, Denominator) &&
"Divide would overflow");
if (!Numerator)
return 0;
// C's integer division rounds towards 0.
T Bias = Denominator >= 0 ? -1 : 1;
bool SameSign = (Numerator >= 0) == (Denominator >= 0);
return SameSign ? Numerator / Denominator
: (Numerator - Bias) / Denominator - 1;
}
/// Returns the remainder of the Euclidean division of LHS by RHS. Result is
/// always non-negative.
template <typename U, typename V, typename T = common_sint<U, V>>
constexpr T mod(U Numerator, V Denominator) {
assert(Denominator >= 1 && "Mod by non-positive number");
T Mod = Numerator % Denominator;
return Mod < 0 ? Mod + Denominator : Mod;
}
/// Returns (Numerator / Denominator) rounded by round-half-up. Guaranteed to
/// never overflow.
template <typename U, typename V, typename T = common_uint<U, V>>
constexpr T divideNearest(U Numerator, V Denominator) {
assert(Denominator && "Division by zero");
T Mod = Numerator % Denominator;
return (Numerator / Denominator) +
(Mod > (static_cast<T>(Denominator) - 1) / 2);
}
/// Returns the next integer (mod 2**nbits) that is greater than or equal to
/// \p Value and is a multiple of \p Align. \p Align must be non-zero.
///
/// Examples:
/// \code
/// alignTo(5, 8) = 8
/// alignTo(17, 8) = 24
/// alignTo(~0LL, 8) = 0
/// alignTo(321, 255) = 510
/// \endcode
///
/// Will overflow only if result is not representable in T.
template <typename U, typename V, typename T = common_uint<U, V>>
constexpr T alignTo(U Value, V Align) {
assert(Align != 0u && "Align can't be 0.");
T CeilDiv = divideCeil(Value, Align);
return CeilDiv * Align;
}
/// Fallback when arguments aren't integral.
constexpr uint64_t alignTo(uint64_t Value, uint64_t Align) {
assert(Align != 0u && "Align can't be 0.");
uint64_t CeilDiv = divideCeil(Value, Align);
return CeilDiv * Align;
}
/// Will overflow only if result is not representable in T.
template <typename U, typename V, typename T = common_uint<U, V>>
constexpr T alignToPowerOf2(U Value, V Align) {
assert(Align != 0 && (Align & (Align - 1)) == 0 &&
"Align must be a power of 2");
T NegAlign = static_cast<T>(0) - Align;
return (Value + (Align - 1)) & NegAlign;
}
/// Fallback when arguments aren't integral.
constexpr uint64_t alignToPowerOf2(uint64_t Value, uint64_t Align) {
assert(Align != 0 && (Align & (Align - 1)) == 0 &&
"Align must be a power of 2");
uint64_t NegAlign = 0 - Align;
return (Value + (Align - 1)) & NegAlign;
}
/// If non-zero \p Skew is specified, the return value will be a minimal integer
/// that is greater than or equal to \p Size and equal to \p A * N + \p Skew for
/// some integer N. If \p Skew is larger than \p A, its value is adjusted to '\p
/// Skew mod \p A'. \p Align must be non-zero.
///
/// Examples:
/// \code
/// alignTo(5, 8, 7) = 7
/// alignTo(17, 8, 1) = 17
/// alignTo(~0LL, 8, 3) = 3
/// alignTo(321, 255, 42) = 552
/// \endcode
///
/// May overflow.
template <typename U, typename V, typename W,
typename T = common_uint<common_uint<U, V>, W>>
constexpr T alignTo(U Value, V Align, W Skew) {
assert(Align != 0u && "Align can't be 0.");
Skew %= Align;
return alignTo(Value - Skew, Align) + Skew;
}
/// Returns the next integer (mod 2**nbits) that is greater than or equal to
/// \p Value and is a multiple of \c Align. \c Align must be non-zero.
///
/// Will overflow only if result is not representable in T.
template <auto Align, typename V, typename T = common_uint<decltype(Align), V>>
constexpr T alignTo(V Value) {
static_assert(Align != 0u, "Align must be non-zero");
T CeilDiv = divideCeil(Value, Align);
return CeilDiv * Align;
}
/// Returns the largest unsigned integer less than or equal to \p Value and is
/// \p Skew mod \p Align. \p Align must be non-zero. Guaranteed to never
/// overflow.
template <typename U, typename V, typename W = uint8_t,
typename T = common_uint<common_uint<U, V>, W>>
constexpr T alignDown(U Value, V Align, W Skew = 0) {
assert(Align != 0u && "Align can't be 0.");
Skew %= Align;
return (Value - Skew) / Align * Align + Skew;
}
/// Sign-extend the number in the bottom B bits of X to a 32-bit integer.
/// Requires B <= 32.
template <unsigned B> constexpr int32_t SignExtend32(uint32_t X) {
static_assert(B <= 32, "Bit width out of range.");
if constexpr (B == 0)
return 0;
return int32_t(X << (32 - B)) >> (32 - B);
}
/// Sign-extend the number in the bottom B bits of X to a 32-bit integer.
/// Requires B <= 32.
inline int32_t SignExtend32(uint32_t X, unsigned B) {
assert(B <= 32 && "Bit width out of range.");
if (B == 0)
return 0;
return int32_t(X << (32 - B)) >> (32 - B);
}
/// Sign-extend the number in the bottom B bits of X to a 64-bit integer.
/// Requires B <= 64.
template <unsigned B> constexpr int64_t SignExtend64(uint64_t x) {
static_assert(B <= 64, "Bit width out of range.");
if constexpr (B == 0)
return 0;
return int64_t(x << (64 - B)) >> (64 - B);
}
/// Sign-extend the number in the bottom B bits of X to a 64-bit integer.
/// Requires B <= 64.
inline int64_t SignExtend64(uint64_t X, unsigned B) {
assert(B <= 64 && "Bit width out of range.");
if (B == 0)
return 0;
return int64_t(X << (64 - B)) >> (64 - B);
}
/// Subtract two unsigned integers, X and Y, of type T and return the absolute
/// value of the result.
template <typename U, typename V, typename T = common_uint<U, V>>
constexpr T AbsoluteDifference(U X, V Y) {
return X > Y ? (X - Y) : (Y - X);
}
/// Add two unsigned integers, X and Y, of type T. Clamp the result to the
/// maximum representable value of T on overflow. ResultOverflowed indicates if
/// the result is larger than the maximum representable value of type T.
template <typename T>
std::enable_if_t<std::is_unsigned_v<T>, T>
SaturatingAdd(T X, T Y, bool *ResultOverflowed = nullptr) {
bool Dummy;
bool &Overflowed = ResultOverflowed ? *ResultOverflowed : Dummy;
// Hacker's Delight, p. 29
T Z = X + Y;
Overflowed = (Z < X || Z < Y);
if (Overflowed)
return std::numeric_limits<T>::max();
else
return Z;
}
/// Add multiple unsigned integers of type T. Clamp the result to the
/// maximum representable value of T on overflow.
template <class T, class... Ts>
std::enable_if_t<std::is_unsigned_v<T>, T> SaturatingAdd(T X, T Y, T Z,
Ts... Args) {
bool Overflowed = false;
T XY = SaturatingAdd(X, Y, &Overflowed);
if (Overflowed)
return SaturatingAdd(std::numeric_limits<T>::max(), T(1), Args...);
return SaturatingAdd(XY, Z, Args...);
}
/// Multiply two unsigned integers, X and Y, of type T. Clamp the result to the
/// maximum representable value of T on overflow. ResultOverflowed indicates if
/// the result is larger than the maximum representable value of type T.
template <typename T>
std::enable_if_t<std::is_unsigned_v<T>, T>
SaturatingMultiply(T X, T Y, bool *ResultOverflowed = nullptr) {
bool Dummy;
bool &Overflowed = ResultOverflowed ? *ResultOverflowed : Dummy;
// Hacker's Delight, p. 30 has a different algorithm, but we don't use that
// because it fails for uint16_t (where multiplication can have undefined
// behavior due to promotion to int), and requires a division in addition
// to the multiplication.
Overflowed = false;
// Log2(Z) would be either Log2Z or Log2Z + 1.
// Special case: if X or Y is 0, Log2_64 gives -1, and Log2Z
// will necessarily be less than Log2Max as desired.
int Log2Z = Log2_64(X) + Log2_64(Y);
const T Max = std::numeric_limits<T>::max();
int Log2Max = Log2_64(Max);
if (Log2Z < Log2Max) {
return X * Y;
}
if (Log2Z > Log2Max) {
Overflowed = true;
return Max;
}
// We're going to use the top bit, and maybe overflow one
// bit past it. Multiply all but the bottom bit then add
// that on at the end.
T Z = (X >> 1) * Y;
if (Z & ~(Max >> 1)) {
Overflowed = true;
return Max;
}
Z <<= 1;
if (X & 1)
return SaturatingAdd(Z, Y, ResultOverflowed);
return Z;
}
/// Multiply two unsigned integers, X and Y, and add the unsigned integer, A to
/// the product. Clamp the result to the maximum representable value of T on
/// overflow. ResultOverflowed indicates if the result is larger than the
/// maximum representable value of type T.
template <typename T>
std::enable_if_t<std::is_unsigned_v<T>, T>
SaturatingMultiplyAdd(T X, T Y, T A, bool *ResultOverflowed = nullptr) {
bool Dummy;
bool &Overflowed = ResultOverflowed ? *ResultOverflowed : Dummy;
T Product = SaturatingMultiply(X, Y, &Overflowed);
if (Overflowed)
return Product;
return SaturatingAdd(A, Product, &Overflowed);
}
/// Use this rather than HUGE_VALF; the latter causes warnings on MSVC.
extern const float huge_valf;
/// Add two signed integers, computing the two's complement truncated result,
/// returning true if overflow occurred.
template <typename T>
std::enable_if_t<std::is_signed_v<T>, T> AddOverflow(T X, T Y, T &Result) {
#if __has_builtin(__builtin_add_overflow)
return __builtin_add_overflow(X, Y, &Result);
#else
// Perform the unsigned addition.
using U = std::make_unsigned_t<T>;
const U UX = static_cast<U>(X);
const U UY = static_cast<U>(Y);
const U UResult = UX + UY;
// Convert to signed.
Result = static_cast<T>(UResult);
// Adding two positive numbers should result in a positive number.
if (X > 0 && Y > 0)
return Result <= 0;
// Adding two negatives should result in a negative number.
if (X < 0 && Y < 0)
return Result >= 0;
return false;
#endif
}
/// Subtract two signed integers, computing the two's complement truncated
/// result, returning true if an overflow ocurred.
template <typename T>
std::enable_if_t<std::is_signed_v<T>, T> SubOverflow(T X, T Y, T &Result) {
#if __has_builtin(__builtin_sub_overflow)
return __builtin_sub_overflow(X, Y, &Result);
#else
// Perform the unsigned addition.
using U = std::make_unsigned_t<T>;
const U UX = static_cast<U>(X);
const U UY = static_cast<U>(Y);
const U UResult = UX - UY;
// Convert to signed.
Result = static_cast<T>(UResult);
// Subtracting a positive number from a negative results in a negative number.
if (X <= 0 && Y > 0)
return Result >= 0;
// Subtracting a negative number from a positive results in a positive number.
if (X >= 0 && Y < 0)
return Result <= 0;
return false;
#endif
}
/// Multiply two signed integers, computing the two's complement truncated
/// result, returning true if an overflow ocurred.
template <typename T>
std::enable_if_t<std::is_signed_v<T>, T> MulOverflow(T X, T Y, T &Result) {
#if __has_builtin(__builtin_mul_overflow)
return __builtin_mul_overflow(X, Y, &Result);
#else
// Perform the unsigned multiplication on absolute values.
using U = std::make_unsigned_t<T>;
const U UX = X < 0 ? (0 - static_cast<U>(X)) : static_cast<U>(X);
const U UY = Y < 0 ? (0 - static_cast<U>(Y)) : static_cast<U>(Y);
const U UResult = UX * UY;
// Convert to signed.
const bool IsNegative = (X < 0) ^ (Y < 0);
Result = IsNegative ? (0 - UResult) : UResult;
// If any of the args was 0, result is 0 and no overflow occurs.
if (UX == 0 || UY == 0)
return false;
// UX and UY are in [1, 2^n], where n is the number of digits.
// Check how the max allowed absolute value (2^n for negative, 2^(n-1) for
// positive) divided by an argument compares to the other.
if (IsNegative)
return UX > (static_cast<U>(std::numeric_limits<T>::max()) + U(1)) / UY;
else
return UX > (static_cast<U>(std::numeric_limits<T>::max())) / UY;
#endif
}
/// Type to force float point values onto the stack, so that x86 doesn't add
/// hidden precision, avoiding rounding differences on various platforms.
#if defined(__i386__) || defined(_M_IX86)
using stack_float_t = volatile float;
#else
using stack_float_t = float;
#endif
} // namespace llvm
#endif

View File

@@ -0,0 +1,24 @@
# LLVM Reference Files
These are a copy of the relevant LLVM files that were ported to Rust from the
last time that this project was "synced" with LLVM.
Currently that sync point is 20.1.8, commit [87f0227](https://github.com/llvm/llvm-project/tree/87f0227cb60147a26a1eeb4fb06e3b505e9c7261).
These files were originally located at:
* `llvm/include/llvm/BinaryFormat/COFF.h`
* `llvm/include/llvm/Object/Archive.h`
* `llvm/include/llvm/Object/ArchiveWriter.h`
* `llvm/include/llvm/Object/COFFImportFile.h`
* `llvm/include/llvm/Support/Alignment.h`
* `llvm/include/llvm/Support/MathExtras.h`
* `llvm/lib/IR/Mangler.cpp`
* `llvm/lib/Object/ArchiveWriter.cpp`
* `llvm/lib/Object/COFFImportFile.cpp`
* `llvm/unittests/IR/ManglerTest.cpp`
When syncing, make sure to update these files and the commit above.
Additionally, `ar_archive_writer` has removed some options, so you can assume:
* `deterministic` is always `true`.
* `write_symtab` is always `true`.