parent
980688cdaf
commit
17cb7c0a2a
|
@ -714,7 +714,7 @@ template <class ELFT> void LinkerScript<ELFT>::placeOrphanSections() {
|
|||
}
|
||||
|
||||
template <class ELFT>
|
||||
void LinkerScript<ELFT>::assignAddresses(std::vector<PhdrEntry<ELFT>> &Phdrs) {
|
||||
void LinkerScript<ELFT>::assignAddresses(std::vector<PhdrEntry> &Phdrs) {
|
||||
// Assign addresses as instructed by linker script SECTIONS sub-commands.
|
||||
Dot = 0;
|
||||
|
||||
|
@ -749,9 +749,8 @@ void LinkerScript<ELFT>::assignAddresses(std::vector<PhdrEntry<ELFT>> &Phdrs) {
|
|||
|
||||
uintX_t HeaderSize = getHeaderSize();
|
||||
auto FirstPTLoad =
|
||||
std::find_if(Phdrs.begin(), Phdrs.end(), [](const PhdrEntry<ELFT> &E) {
|
||||
return E.H.p_type == PT_LOAD;
|
||||
});
|
||||
std::find_if(Phdrs.begin(), Phdrs.end(),
|
||||
[](const PhdrEntry &E) { return E.p_type == PT_LOAD; });
|
||||
if (FirstPTLoad == Phdrs.end())
|
||||
return;
|
||||
|
||||
|
@ -771,15 +770,14 @@ void LinkerScript<ELFT>::assignAddresses(std::vector<PhdrEntry<ELFT>> &Phdrs) {
|
|||
}
|
||||
|
||||
// Creates program headers as instructed by PHDRS linker script command.
|
||||
template <class ELFT>
|
||||
std::vector<PhdrEntry<ELFT>> LinkerScript<ELFT>::createPhdrs() {
|
||||
std::vector<PhdrEntry<ELFT>> Ret;
|
||||
template <class ELFT> std::vector<PhdrEntry> LinkerScript<ELFT>::createPhdrs() {
|
||||
std::vector<PhdrEntry> Ret;
|
||||
|
||||
// Process PHDRS and FILEHDR keywords because they are not
|
||||
// real output sections and cannot be added in the following loop.
|
||||
for (const PhdrsCommand &Cmd : Opt.PhdrsCommands) {
|
||||
Ret.emplace_back(Cmd.Type, Cmd.Flags == UINT_MAX ? PF_R : Cmd.Flags);
|
||||
PhdrEntry<ELFT> &Phdr = Ret.back();
|
||||
PhdrEntry &Phdr = Ret.back();
|
||||
|
||||
if (Cmd.HasFilehdr)
|
||||
Phdr.add(Out<ELFT>::ElfHeader);
|
||||
|
@ -787,7 +785,7 @@ std::vector<PhdrEntry<ELFT>> LinkerScript<ELFT>::createPhdrs() {
|
|||
Phdr.add(Out<ELFT>::ProgramHeaders);
|
||||
|
||||
if (Cmd.LMAExpr) {
|
||||
Phdr.H.p_paddr = Cmd.LMAExpr(0);
|
||||
Phdr.p_paddr = Cmd.LMAExpr(0);
|
||||
Phdr.HasLMA = true;
|
||||
}
|
||||
}
|
||||
|
@ -801,7 +799,7 @@ std::vector<PhdrEntry<ELFT>> LinkerScript<ELFT>::createPhdrs() {
|
|||
for (size_t Id : getPhdrIndices(Sec->getName())) {
|
||||
Ret[Id].add(Sec);
|
||||
if (Opt.PhdrsCommands[Id].Flags == UINT_MAX)
|
||||
Ret[Id].H.p_flags |= Sec->getPhdrFlags();
|
||||
Ret[Id].p_flags |= Sec->getPhdrFlags();
|
||||
}
|
||||
}
|
||||
return Ret;
|
||||
|
|
|
@ -233,7 +233,7 @@ public:
|
|||
void adjustSectionsBeforeSorting();
|
||||
void adjustSectionsAfterSorting();
|
||||
|
||||
std::vector<PhdrEntry<ELFT>> createPhdrs();
|
||||
std::vector<PhdrEntry> createPhdrs();
|
||||
bool ignoreInterpSection();
|
||||
|
||||
uint32_t getFiller(StringRef Name);
|
||||
|
@ -242,7 +242,7 @@ public:
|
|||
bool shouldKeep(InputSectionBase<ELFT> *S);
|
||||
void assignOffsets(OutputSectionCommand *Cmd);
|
||||
void placeOrphanSections();
|
||||
void assignAddresses(std::vector<PhdrEntry<ELFT>> &Phdrs);
|
||||
void assignAddresses(std::vector<PhdrEntry> &Phdrs);
|
||||
bool hasPhdrsCommands();
|
||||
uint64_t getHeaderSize() override;
|
||||
uint64_t getSymbolValue(StringRef S) override;
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
struct PhdrEntry;
|
||||
class SymbolBody;
|
||||
struct EhSectionPiece;
|
||||
template <class ELFT> class EhInputSection;
|
||||
|
@ -207,7 +208,7 @@ template <class ELFT> struct Out {
|
|||
static OutputSection<ELFT> *Bss;
|
||||
static OutputSectionBase *Opd;
|
||||
static uint8_t *OpdBuf;
|
||||
static Elf_Phdr *TlsPhdr;
|
||||
static PhdrEntry *TlsPhdr;
|
||||
static OutputSectionBase *DebugInfo;
|
||||
static OutputSectionBase *ElfHeader;
|
||||
static OutputSectionBase *ProgramHeaders;
|
||||
|
@ -254,7 +255,7 @@ template <class ELFT> EhOutputSection<ELFT> *Out<ELFT>::EhFrame;
|
|||
template <class ELFT> OutputSection<ELFT> *Out<ELFT>::Bss;
|
||||
template <class ELFT> OutputSectionBase *Out<ELFT>::Opd;
|
||||
template <class ELFT> uint8_t *Out<ELFT>::OpdBuf;
|
||||
template <class ELFT> typename ELFT::Phdr *Out<ELFT>::TlsPhdr;
|
||||
template <class ELFT> PhdrEntry *Out<ELFT>::TlsPhdr;
|
||||
template <class ELFT> OutputSectionBase *Out<ELFT>::DebugInfo;
|
||||
template <class ELFT> OutputSectionBase *Out<ELFT>::ElfHeader;
|
||||
template <class ELFT> OutputSectionBase *Out<ELFT>::ProgramHeaders;
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "Strings.h"
|
||||
#include "SyntheticSections.h"
|
||||
#include "Target.h"
|
||||
#include "Writer.h"
|
||||
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
|
|
|
@ -48,8 +48,6 @@ public:
|
|||
void run();
|
||||
|
||||
private:
|
||||
typedef PhdrEntry<ELFT> Phdr;
|
||||
|
||||
void createSyntheticSections();
|
||||
void copyLocalSymbols();
|
||||
void addReservedSymbols();
|
||||
|
@ -60,9 +58,9 @@ private:
|
|||
void finalizeSections();
|
||||
void addPredefinedSections();
|
||||
|
||||
std::vector<Phdr> createPhdrs();
|
||||
std::vector<PhdrEntry> createPhdrs();
|
||||
void removeEmptyPTLoad();
|
||||
void addPtArmExid(std::vector<Phdr> &Phdrs);
|
||||
void addPtArmExid(std::vector<PhdrEntry> &Phdrs);
|
||||
void assignAddresses();
|
||||
void assignFileOffsets();
|
||||
void assignFileOffsetsBinary();
|
||||
|
@ -87,7 +85,7 @@ private:
|
|||
uintX_t getEntryAddr();
|
||||
OutputSectionBase *findSection(StringRef Name);
|
||||
|
||||
std::vector<Phdr> Phdrs;
|
||||
std::vector<PhdrEntry> Phdrs;
|
||||
|
||||
uintX_t FileSize;
|
||||
uintX_t SectionHeaderOff;
|
||||
|
@ -135,8 +133,8 @@ template <class ELFT> static bool needsInterpSection() {
|
|||
template <class ELFT> void elf::writeResult() { Writer<ELFT>().run(); }
|
||||
|
||||
template <class ELFT> void Writer<ELFT>::removeEmptyPTLoad() {
|
||||
auto I = std::remove_if(Phdrs.begin(), Phdrs.end(), [&](const Phdr &P) {
|
||||
if (P.H.p_type != PT_LOAD)
|
||||
auto I = std::remove_if(Phdrs.begin(), Phdrs.end(), [&](const PhdrEntry &P) {
|
||||
if (P.p_type != PT_LOAD)
|
||||
return false;
|
||||
if (!P.First)
|
||||
return true;
|
||||
|
@ -603,18 +601,17 @@ static bool compareSections(const OutputSectionBase *A,
|
|||
}
|
||||
|
||||
// Program header entry
|
||||
template <class ELFT>
|
||||
PhdrEntry<ELFT>::PhdrEntry(unsigned Type, unsigned Flags) {
|
||||
H.p_type = Type;
|
||||
H.p_flags = Flags;
|
||||
PhdrEntry::PhdrEntry(unsigned Type, unsigned Flags) {
|
||||
p_type = Type;
|
||||
p_flags = Flags;
|
||||
}
|
||||
|
||||
template <class ELFT> void PhdrEntry<ELFT>::add(OutputSectionBase *Sec) {
|
||||
void PhdrEntry::add(OutputSectionBase *Sec) {
|
||||
Last = Sec;
|
||||
if (!First)
|
||||
First = Sec;
|
||||
H.p_align = std::max<typename ELFT::uint>(H.p_align, Sec->Addralign);
|
||||
if (H.p_type == PT_LOAD)
|
||||
p_align = std::max(p_align, Sec->Addralign);
|
||||
if (p_type == PT_LOAD)
|
||||
Sec->FirstInPtLoad = First;
|
||||
}
|
||||
|
||||
|
@ -1136,34 +1133,34 @@ static typename ELFT::uint computeFlags(typename ELFT::uint F) {
|
|||
|
||||
// Decide which program headers to create and which sections to include in each
|
||||
// one.
|
||||
template <class ELFT> std::vector<PhdrEntry<ELFT>> Writer<ELFT>::createPhdrs() {
|
||||
std::vector<Phdr> Ret;
|
||||
auto AddHdr = [&](unsigned Type, unsigned Flags) -> Phdr * {
|
||||
template <class ELFT> std::vector<PhdrEntry> Writer<ELFT>::createPhdrs() {
|
||||
std::vector<PhdrEntry> Ret;
|
||||
auto AddHdr = [&](unsigned Type, unsigned Flags) -> PhdrEntry * {
|
||||
Ret.emplace_back(Type, Flags);
|
||||
return &Ret.back();
|
||||
};
|
||||
|
||||
// The first phdr entry is PT_PHDR which describes the program header itself.
|
||||
Phdr &Hdr = *AddHdr(PT_PHDR, PF_R);
|
||||
PhdrEntry &Hdr = *AddHdr(PT_PHDR, PF_R);
|
||||
Hdr.add(Out<ELFT>::ProgramHeaders);
|
||||
|
||||
// PT_INTERP must be the second entry if exists.
|
||||
if (OutputSectionBase *Sec = findSection(".interp")) {
|
||||
Phdr &Hdr = *AddHdr(PT_INTERP, Sec->getPhdrFlags());
|
||||
PhdrEntry &Hdr = *AddHdr(PT_INTERP, Sec->getPhdrFlags());
|
||||
Hdr.add(Sec);
|
||||
}
|
||||
|
||||
// Add the first PT_LOAD segment for regular output sections.
|
||||
uintX_t Flags = computeFlags<ELFT>(PF_R);
|
||||
Phdr *Load = AddHdr(PT_LOAD, Flags);
|
||||
PhdrEntry *Load = AddHdr(PT_LOAD, Flags);
|
||||
if (!ScriptConfig->HasSections) {
|
||||
Load->add(Out<ELFT>::ElfHeader);
|
||||
Load->add(Out<ELFT>::ProgramHeaders);
|
||||
}
|
||||
|
||||
Phdr TlsHdr(PT_TLS, PF_R);
|
||||
Phdr RelRo(PT_GNU_RELRO, PF_R);
|
||||
Phdr Note(PT_NOTE, PF_R);
|
||||
PhdrEntry TlsHdr(PT_TLS, PF_R);
|
||||
PhdrEntry RelRo(PT_GNU_RELRO, PF_R);
|
||||
PhdrEntry Note(PT_NOTE, PF_R);
|
||||
for (OutputSectionBase *Sec : OutputSections) {
|
||||
if (!(Sec->Flags & SHF_ALLOC))
|
||||
break;
|
||||
|
@ -1202,7 +1199,8 @@ template <class ELFT> std::vector<PhdrEntry<ELFT>> Writer<ELFT>::createPhdrs() {
|
|||
|
||||
// Add an entry for .dynamic.
|
||||
if (In<ELFT>::DynSymTab) {
|
||||
Phdr &H = *AddHdr(PT_DYNAMIC, In<ELFT>::Dynamic->OutSec->getPhdrFlags());
|
||||
PhdrEntry &H =
|
||||
*AddHdr(PT_DYNAMIC, In<ELFT>::Dynamic->OutSec->getPhdrFlags());
|
||||
H.add(In<ELFT>::Dynamic->OutSec);
|
||||
}
|
||||
|
||||
|
@ -1213,7 +1211,7 @@ template <class ELFT> std::vector<PhdrEntry<ELFT>> Writer<ELFT>::createPhdrs() {
|
|||
|
||||
// PT_GNU_EH_FRAME is a special section pointing on .eh_frame_hdr.
|
||||
if (!Out<ELFT>::EhFrame->empty() && In<ELFT>::EhFrameHdr) {
|
||||
Phdr &Hdr =
|
||||
PhdrEntry &Hdr =
|
||||
*AddHdr(PT_GNU_EH_FRAME, In<ELFT>::EhFrameHdr->OutSec->getPhdrFlags());
|
||||
Hdr.add(In<ELFT>::EhFrameHdr->OutSec);
|
||||
}
|
||||
|
@ -1222,16 +1220,16 @@ template <class ELFT> std::vector<PhdrEntry<ELFT>> Writer<ELFT>::createPhdrs() {
|
|||
// memory image of the program that must be filled with random data before any
|
||||
// code in the object is executed.
|
||||
if (OutputSectionBase *Sec = findSection(".openbsd.randomdata")) {
|
||||
Phdr &Hdr = *AddHdr(PT_OPENBSD_RANDOMIZE, Sec->getPhdrFlags());
|
||||
PhdrEntry &Hdr = *AddHdr(PT_OPENBSD_RANDOMIZE, Sec->getPhdrFlags());
|
||||
Hdr.add(Sec);
|
||||
}
|
||||
|
||||
// PT_GNU_STACK is a special section to tell the loader to make the
|
||||
// pages for the stack non-executable.
|
||||
if (!Config->ZExecstack) {
|
||||
Phdr &Hdr = *AddHdr(PT_GNU_STACK, PF_R | PF_W);
|
||||
PhdrEntry &Hdr = *AddHdr(PT_GNU_STACK, PF_R | PF_W);
|
||||
if (Config->ZStackSize != uint64_t(-1))
|
||||
Hdr.H.p_memsz = Config->ZStackSize;
|
||||
Hdr.p_memsz = Config->ZStackSize;
|
||||
}
|
||||
|
||||
// PT_OPENBSD_WXNEEDED is a OpenBSD-specific header to mark the executable
|
||||
|
@ -1247,7 +1245,7 @@ template <class ELFT> std::vector<PhdrEntry<ELFT>> Writer<ELFT>::createPhdrs() {
|
|||
}
|
||||
|
||||
template <class ELFT>
|
||||
void Writer<ELFT>::addPtArmExid(std::vector<PhdrEntry<ELFT>> &Phdrs) {
|
||||
void Writer<ELFT>::addPtArmExid(std::vector<PhdrEntry> &Phdrs) {
|
||||
if (Config->EMachine != EM_ARM)
|
||||
return;
|
||||
auto I = std::find_if(
|
||||
|
@ -1257,7 +1255,7 @@ void Writer<ELFT>::addPtArmExid(std::vector<PhdrEntry<ELFT>> &Phdrs) {
|
|||
return;
|
||||
|
||||
// PT_ARM_EXIDX is the ARM EHABI equivalent of PT_GNU_EH_FRAME
|
||||
Phdr ARMExidx(PT_ARM_EXIDX, PF_R);
|
||||
PhdrEntry ARMExidx(PT_ARM_EXIDX, PF_R);
|
||||
ARMExidx.add(*I);
|
||||
Phdrs.push_back(ARMExidx);
|
||||
}
|
||||
|
@ -1265,12 +1263,12 @@ void Writer<ELFT>::addPtArmExid(std::vector<PhdrEntry<ELFT>> &Phdrs) {
|
|||
// The first section of each PT_LOAD and the first section after PT_GNU_RELRO
|
||||
// have to be page aligned so that the dynamic linker can set the permissions.
|
||||
template <class ELFT> void Writer<ELFT>::fixSectionAlignments() {
|
||||
for (const Phdr &P : Phdrs)
|
||||
if (P.H.p_type == PT_LOAD)
|
||||
for (const PhdrEntry &P : Phdrs)
|
||||
if (P.p_type == PT_LOAD)
|
||||
P.First->PageAlign = true;
|
||||
|
||||
for (const Phdr &P : Phdrs) {
|
||||
if (P.H.p_type != PT_GNU_RELRO)
|
||||
for (const PhdrEntry &P : Phdrs) {
|
||||
if (P.p_type != PT_GNU_RELRO)
|
||||
continue;
|
||||
// Find the first section after PT_GNU_RELRO. If it is in a PT_LOAD we
|
||||
// have to align it to a page.
|
||||
|
@ -1381,31 +1379,30 @@ template <class ELFT> void Writer<ELFT>::assignFileOffsets() {
|
|||
// Finalize the program headers. We call this function after we assign
|
||||
// file offsets and VAs to all sections.
|
||||
template <class ELFT> void Writer<ELFT>::setPhdrs() {
|
||||
for (Phdr &P : Phdrs) {
|
||||
Elf_Phdr &H = P.H;
|
||||
for (PhdrEntry &P : Phdrs) {
|
||||
OutputSectionBase *First = P.First;
|
||||
OutputSectionBase *Last = P.Last;
|
||||
if (First) {
|
||||
H.p_filesz = Last->Offset - First->Offset;
|
||||
P.p_filesz = Last->Offset - First->Offset;
|
||||
if (Last->Type != SHT_NOBITS)
|
||||
H.p_filesz += Last->Size;
|
||||
H.p_memsz = Last->Addr + Last->Size - First->Addr;
|
||||
H.p_offset = First->Offset;
|
||||
H.p_vaddr = First->Addr;
|
||||
P.p_filesz += Last->Size;
|
||||
P.p_memsz = Last->Addr + Last->Size - First->Addr;
|
||||
P.p_offset = First->Offset;
|
||||
P.p_vaddr = First->Addr;
|
||||
if (!P.HasLMA)
|
||||
H.p_paddr = First->getLMA();
|
||||
P.p_paddr = First->getLMA();
|
||||
}
|
||||
if (H.p_type == PT_LOAD)
|
||||
H.p_align = Config->MaxPageSize;
|
||||
else if (H.p_type == PT_GNU_RELRO)
|
||||
H.p_align = 1;
|
||||
if (P.p_type == PT_LOAD)
|
||||
P.p_align = Config->MaxPageSize;
|
||||
else if (P.p_type == PT_GNU_RELRO)
|
||||
P.p_align = 1;
|
||||
|
||||
// The TLS pointer goes after PT_TLS. At least glibc will align it,
|
||||
// so round up the size to make sure the offsets are correct.
|
||||
if (H.p_type == PT_TLS) {
|
||||
Out<ELFT>::TlsPhdr = &H;
|
||||
if (H.p_memsz)
|
||||
H.p_memsz = alignTo(H.p_memsz, H.p_align);
|
||||
if (P.p_type == PT_TLS) {
|
||||
Out<ELFT>::TlsPhdr = &P;
|
||||
if (P.p_memsz)
|
||||
P.p_memsz = alignTo(P.p_memsz, P.p_align);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1473,14 +1470,13 @@ template <class ELFT> void Writer<ELFT>::fixAbsoluteSymbols() {
|
|||
// _etext is the first location after the last read-only loadable segment.
|
||||
// _edata is the first location after the last read-write loadable segment.
|
||||
// _end is the first location after the uninitialized data region.
|
||||
for (Phdr &P : Phdrs) {
|
||||
Elf_Phdr &H = P.H;
|
||||
if (H.p_type != PT_LOAD)
|
||||
for (PhdrEntry &P : Phdrs) {
|
||||
if (P.p_type != PT_LOAD)
|
||||
continue;
|
||||
Set(ElfSym<ELFT>::End, ElfSym<ELFT>::End2, H.p_vaddr + H.p_memsz);
|
||||
Set(ElfSym<ELFT>::End, ElfSym<ELFT>::End2, P.p_vaddr + P.p_memsz);
|
||||
|
||||
uintX_t Val = H.p_vaddr + H.p_filesz;
|
||||
if (H.p_flags & PF_W)
|
||||
uintX_t Val = P.p_vaddr + P.p_filesz;
|
||||
if (P.p_flags & PF_W)
|
||||
Set(ElfSym<ELFT>::Edata, ElfSym<ELFT>::Edata2, Val);
|
||||
else
|
||||
Set(ElfSym<ELFT>::Etext, ElfSym<ELFT>::Etext2, Val);
|
||||
|
@ -1542,8 +1538,17 @@ template <class ELFT> void Writer<ELFT>::writeHeader() {
|
|||
|
||||
// Write the program header table.
|
||||
auto *HBuf = reinterpret_cast<Elf_Phdr *>(Buf + EHdr->e_phoff);
|
||||
for (Phdr &P : Phdrs)
|
||||
*HBuf++ = P.H;
|
||||
for (PhdrEntry &P : Phdrs) {
|
||||
HBuf->p_type = P.p_type;
|
||||
HBuf->p_flags = P.p_flags;
|
||||
HBuf->p_offset = P.p_offset;
|
||||
HBuf->p_vaddr = P.p_vaddr;
|
||||
HBuf->p_paddr = P.p_paddr;
|
||||
HBuf->p_filesz = P.p_filesz;
|
||||
HBuf->p_memsz = P.p_memsz;
|
||||
HBuf->p_align = P.p_align;
|
||||
++HBuf;
|
||||
}
|
||||
|
||||
// Write the section header table. Note that the first table entry is null.
|
||||
auto *SHdrs = reinterpret_cast<Elf_Shdr *>(Buf + EHdr->e_shoff);
|
||||
|
@ -1671,11 +1676,6 @@ template void elf::writeResult<ELF32BE>();
|
|||
template void elf::writeResult<ELF64LE>();
|
||||
template void elf::writeResult<ELF64BE>();
|
||||
|
||||
template struct elf::PhdrEntry<ELF32LE>;
|
||||
template struct elf::PhdrEntry<ELF32BE>;
|
||||
template struct elf::PhdrEntry<ELF64LE>;
|
||||
template struct elf::PhdrEntry<ELF64BE>;
|
||||
|
||||
template bool elf::isRelroSection<ELF32LE>(const OutputSectionBase *);
|
||||
template bool elf::isRelroSection<ELF32BE>(const OutputSectionBase *);
|
||||
template bool elf::isRelroSection<ELF64LE>(const OutputSectionBase *);
|
||||
|
|
|
@ -28,11 +28,19 @@ template <class ELFT> bool isRelroSection(const OutputSectionBase *Sec);
|
|||
// This describes a program header entry.
|
||||
// Each contains type, access flags and range of output sections that will be
|
||||
// placed in it.
|
||||
template <class ELFT> struct PhdrEntry {
|
||||
struct PhdrEntry {
|
||||
PhdrEntry(unsigned Type, unsigned Flags);
|
||||
void add(OutputSectionBase *Sec);
|
||||
|
||||
typename ELFT::Phdr H = {};
|
||||
uint64_t p_paddr = 0;
|
||||
uint64_t p_vaddr = 0;
|
||||
uint64_t p_align = 0;
|
||||
uint64_t p_memsz = 0;
|
||||
uint64_t p_filesz = 0;
|
||||
uint64_t p_offset = 0;
|
||||
uint32_t p_type = 0;
|
||||
uint32_t p_flags = 0;
|
||||
|
||||
OutputSectionBase *First = nullptr;
|
||||
OutputSectionBase *Last = nullptr;
|
||||
bool HasLMA = false;
|
||||
|
|
Loading…
Reference in New Issue