83 changes: 73 additions & 10 deletions llvm/lib/Object/ArchiveWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@
using namespace llvm;

struct SymMap {
bool UseECMap;
std::map<std::string, uint16_t> Map;
std::map<std::string, uint16_t> ECMap;
};

NewArchiveMember::NewArchiveMember(MemoryBufferRef BufRef)
Expand Down Expand Up @@ -414,6 +416,20 @@ static uint64_t computeSymbolMapSize(uint64_t NumObj, SymMap &SymMap,
return Size;
}

static uint64_t computeECSymbolsSize(SymMap &SymMap,
uint32_t *Padding = nullptr) {
uint64_t Size = sizeof(uint32_t); // Number of symbols

for (auto S : SymMap.ECMap)
Size += sizeof(uint16_t) + S.first.length() + 1;

uint32_t Pad = offsetToAlignment(Size, Align(2));
Size += Pad;
if (Padding)
*Padding = Pad;
return Size;
}

static void writeSymbolTableHeader(raw_ostream &Out, object::Archive::Kind Kind,
bool Deterministic, uint64_t Size,
uint64_t PrevMemberOffset = 0) {
Expand Down Expand Up @@ -446,8 +462,11 @@ static uint64_t computeHeadersSize(object::Archive::Kind Kind,
uint32_t HeaderSize = computeSymbolTableHeaderSize();
uint64_t Size = strlen("!<arch>\n") + HeaderSize + SymtabSize;

if (SymMap)
if (SymMap) {
Size += HeaderSize + computeSymbolMapSize(NumMembers, *SymMap);
if (SymMap->ECMap.size())
Size += HeaderSize + computeECSymbolsSize(*SymMap);
}

return Size + StringMemberSize;
}
Expand Down Expand Up @@ -521,6 +540,41 @@ static void writeSymbolMap(raw_ostream &Out, object::Archive::Kind Kind,
Out.write(uint8_t(0));
}

static void writeECSymbols(raw_ostream &Out, object::Archive::Kind Kind,
bool Deterministic, ArrayRef<MemberData> Members,
SymMap &SymMap) {
uint32_t Pad;
uint64_t Size = computeECSymbolsSize(SymMap, &Pad);
printGNUSmallMemberHeader(Out, "/<ECSYMBOLS>", now(Deterministic), 0, 0, 0,
Size);

printLE<uint32_t>(Out, SymMap.ECMap.size());

for (auto S : SymMap.ECMap)
printLE(Out, S.second);
for (auto S : SymMap.ECMap)
Out << S.first << '\0';
while (Pad--)
Out.write(uint8_t(0));
}

static bool isECObject(object::SymbolicFile &Obj) {
if (Obj.isCOFF())
return cast<llvm::object::COFFObjectFile>(&Obj)->getMachine() !=
COFF::IMAGE_FILE_MACHINE_ARM64;

if (Obj.isIR()) {
Expected<std::string> TripleStr =
getBitcodeTargetTriple(Obj.getMemoryBufferRef());
if (!TripleStr)
return false;
Triple T(*TripleStr);
return T.isWindowsArm64EC() || T.getArch() == Triple::x86_64;
}

return false;
}

static Expected<std::vector<unsigned>>
getSymbols(MemoryBufferRef Buf, uint16_t Index, raw_ostream &SymNames,
SymMap *SymMap, bool &HasObject) {
Expand Down Expand Up @@ -548,20 +602,25 @@ getSymbols(MemoryBufferRef Buf, uint16_t Index, raw_ostream &SymNames,
Obj = std::move(*ObjOrErr);
}

std::map<std::string, uint16_t> *Map = nullptr;
if (SymMap)
Map = SymMap->UseECMap && isECObject(*Obj) ? &SymMap->ECMap : &SymMap->Map;
HasObject = true;
for (const object::BasicSymbolRef &S : Obj->symbols()) {
if (!isArchiveSymbol(S))
continue;
if (SymMap) {
if (Map) {
std::string Name;
raw_string_ostream NameStream(Name);
if (Error E = S.printName(NameStream))
return std::move(E);
if (SymMap->Map.find(Name) != SymMap->Map.end())
if (Map->find(Name) != Map->end())
continue; // ignore duplicated symbol
SymMap->Map[Name] = Index;
Ret.push_back(SymNames.tell());
SymNames << Name << '\0';
(*Map)[Name] = Index;
if (Map == &SymMap->Map) {
Ret.push_back(SymNames.tell());
SymNames << Name << '\0';
}
} else {
Ret.push_back(SymNames.tell());
if (Error E = S.printName(SymNames))
Expand Down Expand Up @@ -755,7 +814,7 @@ Expected<std::string> computeArchiveRelativePath(StringRef From, StringRef To) {
static Error writeArchiveToStream(raw_ostream &Out,
ArrayRef<NewArchiveMember> NewMembers,
bool WriteSymtab, object::Archive::Kind Kind,
bool Deterministic, bool Thin) {
bool Deterministic, bool Thin, bool IsEC) {
assert((!Thin || !isBSDLike(Kind)) && "Only the gnu format has a thin mode");

SmallString<0> SymNamesBuf;
Expand All @@ -769,6 +828,7 @@ static Error writeArchiveToStream(raw_ostream &Out,
if (isCOFFArchive(Kind) && NewMembers.size() > 0xfffe)
Kind = object::Archive::K_GNU;

SymMap.UseECMap = IsEC;
Expected<std::vector<MemberData>> DataOrErr = computeMemberData(
StringTable, SymNames, Kind, Thin, Deterministic, WriteSymtab,
isCOFFArchive(Kind) ? &SymMap : nullptr, NewMembers);
Expand Down Expand Up @@ -855,6 +915,9 @@ static Error writeArchiveToStream(raw_ostream &Out,
Out << StringTableMember.Header << StringTableMember.Data
<< StringTableMember.Padding;

if (WriteSymtab && SymMap.ECMap.size())
writeECSymbols(Out, Kind, Deterministic, Data, SymMap);

for (const MemberData &M : Data)
Out << M.Header << M.Data << M.Padding;
} else {
Expand Down Expand Up @@ -944,15 +1007,15 @@ static Error writeArchiveToStream(raw_ostream &Out,
Error writeArchive(StringRef ArcName, ArrayRef<NewArchiveMember> NewMembers,
bool WriteSymtab, object::Archive::Kind Kind,
bool Deterministic, bool Thin,
std::unique_ptr<MemoryBuffer> OldArchiveBuf) {
std::unique_ptr<MemoryBuffer> OldArchiveBuf, bool IsEC) {
Expected<sys::fs::TempFile> Temp =
sys::fs::TempFile::create(ArcName + ".temp-archive-%%%%%%%.a");
if (!Temp)
return Temp.takeError();
raw_fd_ostream Out(Temp->FD, false);

if (Error E = writeArchiveToStream(Out, NewMembers, WriteSymtab, Kind,
Deterministic, Thin)) {
Deterministic, Thin, IsEC)) {
if (Error DiscardError = Temp->discard())
return joinErrors(std::move(E), std::move(DiscardError));
return E;
Expand Down Expand Up @@ -981,7 +1044,7 @@ writeArchiveToBuffer(ArrayRef<NewArchiveMember> NewMembers, bool WriteSymtab,
raw_svector_ostream ArchiveStream(ArchiveBufferVector);

if (Error E = writeArchiveToStream(ArchiveStream, NewMembers, WriteSymtab,
Kind, Deterministic, Thin))
Kind, Deterministic, Thin, false))
return std::move(E);

return std::make_unique<SmallVectorMemoryBuffer>(
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/Object/COFFImportFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ static bool is32bit(MachineTypes Machine) {
llvm_unreachable("unsupported machine");
case IMAGE_FILE_MACHINE_ARM64:
case IMAGE_FILE_MACHINE_ARM64EC:
case IMAGE_FILE_MACHINE_ARM64X:
case IMAGE_FILE_MACHINE_AMD64:
return false;
case IMAGE_FILE_MACHINE_ARMNT:
Expand All @@ -57,6 +58,7 @@ static uint16_t getImgRelRelocation(MachineTypes Machine) {
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;
Expand Down
5 changes: 5 additions & 0 deletions llvm/lib/Object/COFFObjectFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1016,6 +1016,8 @@ StringRef COFFObjectFile::getFileFormatName() const {
return "COFF-ARM64";
case COFF::IMAGE_FILE_MACHINE_ARM64EC:
return "COFF-ARM64EC";
case COFF::IMAGE_FILE_MACHINE_ARM64X:
return "COFF-ARM64X";
default:
return "COFF-<unknown arch>";
}
Expand All @@ -1031,6 +1033,7 @@ Triple::ArchType COFFObjectFile::getArch() const {
return Triple::thumb;
case COFF::IMAGE_FILE_MACHINE_ARM64:
case COFF::IMAGE_FILE_MACHINE_ARM64EC:
case COFF::IMAGE_FILE_MACHINE_ARM64X:
return Triple::aarch64;
default:
return Triple::UnknownArch;
Expand Down Expand Up @@ -1318,6 +1321,7 @@ StringRef COFFObjectFile::getRelocationTypeName(uint16_t Type) const {
break;
case COFF::IMAGE_FILE_MACHINE_ARM64:
case COFF::IMAGE_FILE_MACHINE_ARM64EC:
case COFF::IMAGE_FILE_MACHINE_ARM64X:
switch (Type) {
LLVM_COFF_SWITCH_RELOC_TYPE_NAME(IMAGE_REL_ARM64_ABSOLUTE);
LLVM_COFF_SWITCH_RELOC_TYPE_NAME(IMAGE_REL_ARM64_ADDR32);
Expand Down Expand Up @@ -1901,6 +1905,7 @@ ResourceSectionRef::getContents(const coff_resource_data_entry &Entry) {
break;
case COFF::IMAGE_FILE_MACHINE_ARM64:
case COFF::IMAGE_FILE_MACHINE_ARM64EC:
case COFF::IMAGE_FILE_MACHINE_ARM64X:
RVAReloc = COFF::IMAGE_REL_ARM64_ADDR32NB;
break;
default:
Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/Object/WindowsMachineFlag.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ COFF::MachineTypes llvm::getMachineType(StringRef S) {
.Case("arm", COFF::IMAGE_FILE_MACHINE_ARMNT)
.Case("arm64", COFF::IMAGE_FILE_MACHINE_ARM64)
.Case("arm64ec", COFF::IMAGE_FILE_MACHINE_ARM64EC)
.Case("arm64x", COFF::IMAGE_FILE_MACHINE_ARM64X)
.Default(COFF::IMAGE_FILE_MACHINE_UNKNOWN);
}

Expand All @@ -38,6 +39,8 @@ StringRef llvm::machineToStr(COFF::MachineTypes MT) {
return "arm64";
case COFF::IMAGE_FILE_MACHINE_ARM64EC:
return "arm64ec";
case COFF::IMAGE_FILE_MACHINE_ARM64X:
return "arm64x";
case COFF::IMAGE_FILE_MACHINE_AMD64:
return "x64";
case COFF::IMAGE_FILE_MACHINE_I386:
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Object/WindowsResource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -990,6 +990,7 @@ void WindowsResourceCOFFWriter::writeFirstSectionRelocations() {
break;
case COFF::IMAGE_FILE_MACHINE_ARM64:
case COFF::IMAGE_FILE_MACHINE_ARM64EC:
case COFF::IMAGE_FILE_MACHINE_ARM64X:
Reloc->Type = COFF::IMAGE_REL_ARM64_ADDR32NB;
break;
default:
Expand Down
3 changes: 1 addition & 2 deletions llvm/lib/ObjectYAML/COFFEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,7 @@ struct COFFParser {
bool isPE() const { return Obj.OptionalHeader.has_value(); }
bool is64Bit() const {
return Obj.Header.Machine == COFF::IMAGE_FILE_MACHINE_AMD64 ||
Obj.Header.Machine == COFF::IMAGE_FILE_MACHINE_ARM64 ||
Obj.Header.Machine == COFF::IMAGE_FILE_MACHINE_ARM64EC;
COFF::isAnyArm64(Obj.Header.Machine);
}

uint32_t getFileAlignment() const {
Expand Down
4 changes: 2 additions & 2 deletions llvm/lib/ObjectYAML/COFFYAML.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ void ScalarEnumerationTraits<COFF::MachineTypes>::enumeration(
ECase(IMAGE_FILE_MACHINE_ARMNT);
ECase(IMAGE_FILE_MACHINE_ARM64);
ECase(IMAGE_FILE_MACHINE_ARM64EC);
ECase(IMAGE_FILE_MACHINE_ARM64X);
ECase(IMAGE_FILE_MACHINE_EBC);
ECase(IMAGE_FILE_MACHINE_I386);
ECase(IMAGE_FILE_MACHINE_IA64);
Expand Down Expand Up @@ -430,8 +431,7 @@ void MappingTraits<COFFYAML::Relocation>::mapping(IO &IO,
MappingNormalization<NType<COFF::RelocationTypesARM>, uint16_t> NT(
IO, Rel.Type);
IO.mapRequired("Type", NT->Type);
} else if (H.Machine == COFF::IMAGE_FILE_MACHINE_ARM64 ||
H.Machine == COFF::IMAGE_FILE_MACHINE_ARM64EC) {
} else if (COFF::isAnyArm64(H.Machine)) {
MappingNormalization<NType<COFF::RelocationTypesARM64>, uint16_t> NT(
IO, Rel.Type);
IO.mapRequired("Type", NT->Type);
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/ObjectYAML/CodeViewYAMLSymbols.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ void ScalarEnumerationTraits<RegisterId>::enumeration(IO &io, RegisterId &Reg) {
break;
case COFF::IMAGE_FILE_MACHINE_ARM64:
case COFF::IMAGE_FILE_MACHINE_ARM64EC:
case COFF::IMAGE_FILE_MACHINE_ARM64X:
CpuType = CPUType::ARM64;
break;
}
Expand Down
39 changes: 33 additions & 6 deletions llvm/lib/ToolDrivers/llvm-lib/LibDriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,7 @@ static Expected<COFF::MachineTypes> getCOFFFileMachine(MemoryBufferRef MB) {
uint16_t Machine = (*Obj)->getMachine();
if (Machine != COFF::IMAGE_FILE_MACHINE_I386 &&
Machine != COFF::IMAGE_FILE_MACHINE_AMD64 &&
Machine != COFF::IMAGE_FILE_MACHINE_ARMNT &&
Machine != COFF::IMAGE_FILE_MACHINE_ARM64) {
Machine != COFF::IMAGE_FILE_MACHINE_ARMNT && !COFF::isAnyArm64(Machine)) {
return createStringError(inconvertibleErrorCode(),
"unknown machine: " + std::to_string(Machine));
}
Expand All @@ -181,21 +180,41 @@ static Expected<COFF::MachineTypes> getBitcodeFileMachine(MemoryBufferRef MB) {
if (!TripleStr)
return TripleStr.takeError();

switch (Triple(*TripleStr).getArch()) {
Triple T(*TripleStr);
switch (T.getArch()) {
case Triple::x86:
return COFF::IMAGE_FILE_MACHINE_I386;
case Triple::x86_64:
return COFF::IMAGE_FILE_MACHINE_AMD64;
case Triple::arm:
return COFF::IMAGE_FILE_MACHINE_ARMNT;
case Triple::aarch64:
return COFF::IMAGE_FILE_MACHINE_ARM64;
return T.isWindowsArm64EC() ? COFF::IMAGE_FILE_MACHINE_ARM64EC
: COFF::IMAGE_FILE_MACHINE_ARM64;
default:
return createStringError(inconvertibleErrorCode(),
"unknown arch in target triple: " + *TripleStr);
}
}

static bool machineMatches(COFF::MachineTypes LibMachine,
COFF::MachineTypes FileMachine) {
if (LibMachine == FileMachine)
return true;
// ARM64EC mode allows both pure ARM64, ARM64EC and X64 objects to be mixed in
// the archive.
switch (LibMachine) {
case COFF::IMAGE_FILE_MACHINE_ARM64:
return FileMachine == COFF::IMAGE_FILE_MACHINE_ARM64X;
case COFF::IMAGE_FILE_MACHINE_ARM64EC:
case COFF::IMAGE_FILE_MACHINE_ARM64X:
return COFF::isAnyArm64(FileMachine) ||
FileMachine == COFF::IMAGE_FILE_MACHINE_AMD64;
default:
return false;
}
}

static void appendFile(std::vector<NewArchiveMember> &Members,
COFF::MachineTypes &LibMachine,
std::string &LibMachineSource, MemoryBufferRef MB) {
Expand Down Expand Up @@ -263,11 +282,18 @@ static void appendFile(std::vector<NewArchiveMember> &Members,
// this check. See PR42180.
if (FileMachine != COFF::IMAGE_FILE_MACHINE_UNKNOWN) {
if (LibMachine == COFF::IMAGE_FILE_MACHINE_UNKNOWN) {
if (FileMachine == COFF::IMAGE_FILE_MACHINE_ARM64EC) {
llvm::errs() << MB.getBufferIdentifier() << ": file machine type "
<< machineToStr(FileMachine)
<< " conflicts with inferred library machine type,"
<< " use /machine:arm64ec or /machine:arm64x\n";
exit(1);
}
LibMachine = FileMachine;
LibMachineSource =
(" (inferred from earlier file '" + MB.getBufferIdentifier() + "')")
.str();
} else if (LibMachine != FileMachine) {
} else if (!machineMatches(LibMachine, FileMachine)) {
llvm::errs() << MB.getBufferIdentifier() << ": file machine type "
<< machineToStr(FileMachine)
<< " conflicts with library machine type "
Expand Down Expand Up @@ -460,7 +486,8 @@ int llvm::libDriverMain(ArrayRef<const char *> ArgsArr) {
writeArchive(OutputPath, Members,
/*WriteSymtab=*/true,
Thin ? object::Archive::K_GNU : object::Archive::K_COFF,
/*Deterministic*/ true, Thin)) {
/*Deterministic*/ true, Thin, nullptr,
COFF::isArm64EC(LibMachine))) {
handleAllErrors(std::move(E), [&](const ErrorInfoBase &EI) {
llvm::errs() << OutputPath << ": " << EI.message() << "\n";
});
Expand Down
30 changes: 30 additions & 0 deletions llvm/test/tools/llvm-cvtres/machine.test
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ RUN: llvm-readobj -h -r %t | FileCheck %s -check-prefix=ARM
RUN: llvm-cvtres /machine:ARM64 /out:%t %p/Inputs/test_resource.res
RUN: llvm-readobj -h -r %t | FileCheck %s -check-prefix=ARM64

RUN: llvm-cvtres /machine:ARM64EC /out:%t %p/Inputs/test_resource.res
RUN: llvm-readobj -h -r %t | FileCheck %s -check-prefix=ARM64EC

RUN: llvm-cvtres /machine:ARM64X /out:%t %p/Inputs/test_resource.res
RUN: llvm-readobj -h -r %t | FileCheck %s -check-prefix=ARM64X

X86: Machine: IMAGE_FILE_MACHINE_I386 (0x14C)
X86-DAG: Relocations [
X86-DAG: .rsrc$01 {
Expand Down Expand Up @@ -72,3 +78,27 @@ ARM64-NEXT: 0x1D8 IMAGE_REL_ARM64_ADDR32NB $R000004
ARM64-NEXT: 0x1F8 IMAGE_REL_ARM64_ADDR32NB $R000005
ARM64-NEXT: 0x1B8 IMAGE_REL_ARM64_ADDR32NB $R000006
ARM64-NEXT: 0x188 IMAGE_REL_ARM64_ADDR32NB $R000007

ARM64EC: Machine: IMAGE_FILE_MACHINE_ARM64EC (0xA641)
ARM64EC-DAG: Relocations [
ARM64EC-DAG: .rsrc$01 {
ARM64EC-NEXT: 0x1E8 IMAGE_REL_ARM64_ADDR32NB $R000000
ARM64EC-NEXT: 0x198 IMAGE_REL_ARM64_ADDR32NB $R000001
ARM64EC-NEXT: 0x1A8 IMAGE_REL_ARM64_ADDR32NB $R000002
ARM64EC-NEXT: 0x1C8 IMAGE_REL_ARM64_ADDR32NB $R000003
ARM64EC-NEXT: 0x1D8 IMAGE_REL_ARM64_ADDR32NB $R000004
ARM64EC-NEXT: 0x1F8 IMAGE_REL_ARM64_ADDR32NB $R000005
ARM64EC-NEXT: 0x1B8 IMAGE_REL_ARM64_ADDR32NB $R000006
ARM64EC-NEXT: 0x188 IMAGE_REL_ARM64_ADDR32NB $R000007

ARM64X: Machine: IMAGE_FILE_MACHINE_ARM64X (0xA64E)
ARM64X-DAG: Relocations [
ARM64X-DAG: .rsrc$01 {
ARM64X-NEXT: 0x1E8 IMAGE_REL_ARM64_ADDR32NB $R000000
ARM64X-NEXT: 0x198 IMAGE_REL_ARM64_ADDR32NB $R000001
ARM64X-NEXT: 0x1A8 IMAGE_REL_ARM64_ADDR32NB $R000002
ARM64X-NEXT: 0x1C8 IMAGE_REL_ARM64_ADDR32NB $R000003
ARM64X-NEXT: 0x1D8 IMAGE_REL_ARM64_ADDR32NB $R000004
ARM64X-NEXT: 0x1F8 IMAGE_REL_ARM64_ADDR32NB $R000005
ARM64X-NEXT: 0x1B8 IMAGE_REL_ARM64_ADDR32NB $R000006
ARM64X-NEXT: 0x188 IMAGE_REL_ARM64_ADDR32NB $R000007
23 changes: 23 additions & 0 deletions llvm/test/tools/llvm-lib/Inputs/arm64x.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
--- !COFF
header:
Machine: IMAGE_FILE_MACHINE_ARM64X
Characteristics: [ ]
sections:
- Name: .text
Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
Alignment: 4
SectionData: ''
symbols:
- Name: .text
Value: 0
SectionNumber: 1
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
SectionDefinition:
Length: 0
NumberOfRelocations: 0
NumberOfLinenumbers: 0
CheckSum: 0
Number: 1
...
24 changes: 20 additions & 4 deletions llvm/test/tools/llvm-lib/ecsymbols.test
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
# Check that llvm-lib can list the members of an archive which contains the
# Check that llvm-lib doesn't list the members of an archive which contains the
# special member /<ECSYMBOLS>/.

# RUN: yaml2obj %s -o %t.lib
# RUN: llvm-lib /list %t.lib | FileCheck %s

# CHECK: /<ECSYMBOLS>/
# RUN: llvm-lib /list %t.lib | FileCheck --check-prefix=NOEC --allow-empty %s
# NOEC-NOT: ECSYMBOLS

--- !Arch
Members:
Expand All @@ -15,3 +14,20 @@ Members:
- Name: '/<ECSYMBOLS>/'
Size: '0'
...

# Check that llvm-lib produces /<ECSYMBOLS>/ members for ARM64EC libraries.
# RUN: rm -rf %t && mkdir -p %t && cd %t
# RUN: llvm-mc -triple=arm64ec-pc-windows-msvc -filetype=obj -o arm64ec-foo.o %S/Inputs/a.s
# RUN: llvm-mc -triple=aarch64-pc-windows-msvc -filetype=obj -o arm64-foo.o %S/Inputs/a.s
# RUN: llvm-mc -triple=x86_64-pc-windows-msvc -filetype=obj -o x64-foo.o %S/Inputs/b.s
# RUN: llvm-lib -machine:arm64ec -out:foo.lib arm64-foo.o arm64ec-foo.o x64-foo.o
# RUN: grep -q '/<ECSYMBOLS>/' foo.lib

# RUN: llvm-nm --print-armap foo.lib | FileCheck %s
# CHECK: Archive map
# CHECK-NEXT: a in arm64-foo.o
# CHECK-EMPTY:
# CHECK-NEXT: Archive EC map
# CHECK-NEXT: a in arm64ec-foo.o
# CHECK-NEXT: b in x64-foo.o
# CHECK-EMPTY:
30 changes: 30 additions & 0 deletions llvm/test/tools/llvm-lib/machine-mismatch.test
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ Prepare inputs:
RUN: rm -rf %t && mkdir -p %t
RUN: llvm-mc -triple=i386-pc-windows-msvc -filetype=obj -o %t/i386.obj %S/Inputs/a.s
RUN: llvm-mc -triple=x86_64-pc-windows-msvc -filetype=obj -o %t/x86_64.obj %S/Inputs/a.s
RUN: llvm-mc -triple=aarch64-pc-windows-msvc -filetype=obj -o %t/arm64.obj %S/Inputs/a.s
RUN: llvm-mc -triple=arm64ec-pc-windows-msvc -filetype=obj -o %t/arm64ec.obj %S/Inputs/a.s
RUN: llvm-as -o %t/i386.bc %S/Inputs/i386.ll
RUN: llvm-as -o %t/x86_64.bc %S/Inputs/x86_64.ll
RUN: llvm-as -o %t/arm64.bc %S/Inputs/arm64.ll
RUN: yaml2obj -o %t/arm64x.obj %S/Inputs/arm64x.yaml


Mixing bitcode and normal object files with the same machine type is ok:
Expand Down Expand Up @@ -43,3 +46,30 @@ If /machine: is passed, its value is authoritative.
RUN: not llvm-lib /machine:X86 %t/x86_64.obj %t/i386.obj 2>&1 | \
RUN: FileCheck --check-prefix=OBJ64 %s
OBJ64: x86_64.obj: file machine type x64 conflicts with library machine type x86 (from '/machine:X86' flag)


Mixing arm64 and x86_64 is possible using arm64ec:

RUN: llvm-lib -machine:arm64ec %t/arm64.bc %t/x86_64.bc %t/arm64.obj %t/x86_64.obj %t/arm64ec.obj %t/arm64x.obj
RUN: llvm-lib -machine:arm64x %t/arm64.bc %t/x86_64.bc %t/arm64.obj %t/x86_64.obj %t/arm64ec.obj %t/arm64x.obj
RUN: llvm-lib -machine:arm64 %t/arm64.bc %t/arm64.obj %t/arm64x.obj

RUN: not llvm-lib %t/arm64ec.obj 2>&1 | FileCheck --check-prefix=NOEC %s
NOEC: arm64ec.obj: file machine type arm64ec conflicts with inferred library machine type, use /machine:arm64ec or /machine:arm64x

RUN: not llvm-lib -machine:arm64ec %t/arm64ec.obj %t/i386.obj 2>&1 | \
RUN: FileCheck --check-prefix=OBJEC %s
RUN: not llvm-lib -machine:arm64x %t/arm64ec.obj %t/i386.obj 2>&1 | \
RUN: FileCheck --check-prefix=OBJX %s
RUN: not llvm-lib -machine:x64 %t/x86_64.obj %t/arm64x.obj 2>&1 | \
RUN: FileCheck --check-prefix=OBJX2 %s
OBJEC: i386.obj: file machine type x86 conflicts with library machine type arm64ec (from '/machine:arm64ec' flag)
OBJX: i386.obj: file machine type x86 conflicts with library machine type arm64x (from '/machine:arm64x' flag)
OBJX2: arm64x.obj: file machine type arm64x conflicts with library machine type x64 (from '/machine:x64' flag)

RUN: not llvm-lib -machine:arm64ec %t/arm64.bc %t/x86_64.bc %t/i386.bc 2>&1 | \
RUN: FileCheck --check-prefix=BCEC %s
RUN: not llvm-lib -machine:arm64x %t/arm64.bc %t/x86_64.bc %t/i386.bc 2>&1 | \
RUN: FileCheck --check-prefix=BCX %s
BCEC: i386.bc: file machine type x86 conflicts with library machine type arm64ec (from '/machine:arm64ec' flag)
BCX: i386.bc: file machine type x86 conflicts with library machine type arm64x (from '/machine:arm64x' flag)
90 changes: 90 additions & 0 deletions llvm/test/tools/llvm-nm/ecsymbols.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# Check that llvm-lib doesn't list the members of an archive which contains the
# special member /<ECSYMBOLS>/.

# RUN: yaml2obj --docnum=1 %s -o %t.lib
# RUN: llvm-nm --print-armap %t.lib 2>&1 | FileCheck --check-prefix=NM1 %s
# NM1: truncated or malformed archive (invalid EC symbols size (3))

--- !Arch
Members:
- Name: '/'
Size: '0'
- Name: '/'
Size: '0'
- Name: '/<ECSYMBOLS>/'
Size: '3'
Content: 010203
PaddingByte: 0
...

# RUN: yaml2obj --docnum=2 %s -o %t.lib
# RUN: llvm-nm --print-armap %t.lib 2>&1 | FileCheck --check-prefix=NM2 %s
# NM2: truncated or malformed archive (invalid EC symbols size. Size was 7, but expected 8)

--- !Arch
Members:
- Name: '/'
Size: '0'
- Name: '/'
Size: '12'
Content: 010000001000000000000000
- Name: '/<ECSYMBOLS>/'
Size: '7'
Content: 02000000010001
PaddingByte: 0
...

# RUN: yaml2obj --docnum=3 %s -o %t.lib
# RUN: llvm-nm --print-armap %t.lib 2>&1 | FileCheck --check-prefix=NM3 %s
# NM3: truncated or malformed archive (invalid EC symbol index 2 is larger than member count 1)

--- !Arch
Members:
- Name: '/'
Size: '0'
- Name: '/'
Size: '12'
Content: 010000001000000000000000
- Name: '/<ECSYMBOLS>/'
Size: '8'
Content: 0100000002006100
- Name: 'a.obj'
Size: '0'
...


# RUN: yaml2obj --docnum=4 %s -o %t.lib
# RUN: llvm-nm --print-armap %t.lib 2>&1 | FileCheck --check-prefix=NM4 %s
# NM4: truncated or malformed archive (invalid EC symbol index 0)

--- !Arch
Members:
- Name: '/'
Size: '0'
- Name: '/'
Size: '12'
Content: 010000001000000000000000
- Name: '/<ECSYMBOLS>/'
Size: '8'
Content: 0100000000006100
- Name: 'a.obj'
Size: '0'
...

# RUN: yaml2obj --docnum=5 %s -o %t.lib
# RUN: llvm-nm --print-armap %t.lib 2>&1 | FileCheck --check-prefix=NM5 %s
# NM5: truncated or malformed archive (malformed EC symbol names: not null-terminated)

--- !Arch
Members:
- Name: '/'
Size: '0'
- Name: '/'
Size: '12'
Content: 010000001000000000000000
- Name: '/<ECSYMBOLS>/'
Size: '8'
Content: 0100000001006161
- Name: 'a.obj'
Size: '0'
...
27 changes: 27 additions & 0 deletions llvm/test/tools/llvm-readobj/COFF/arm64x.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# RUN: yaml2obj < %s | llvm-readobj - --file-headers | FileCheck %s
# Check we can process a simple arm64x file.
# CHECK: Format: COFF-ARM64X
# CHECK: Machine: IMAGE_FILE_MACHINE_ARM64X (0xA64E)
--- !COFF
header:
Machine: IMAGE_FILE_MACHINE_ARM64X
Characteristics: [ ]
sections:
- Name: .text
Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
Alignment: 4
SectionData: ''
symbols:
- Name: .text
Value: 0
SectionNumber: 1
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
SectionDefinition:
Length: 0
NumberOfRelocations: 0
NumberOfLinenumbers: 0
CheckSum: 0
Number: 1
...
49 changes: 31 additions & 18 deletions llvm/tools/llvm-nm/llvm-nm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1954,26 +1954,39 @@ static bool checkMachOAndArchFlags(SymbolicFile *O, StringRef Filename) {
return true;
}

static void printArchiveMap(iterator_range<Archive::symbol_iterator> &map,
StringRef Filename) {
for (auto I : map) {
Expected<Archive::Child> C = I.getMember();
if (!C) {
error(C.takeError(), Filename);
break;
}
Expected<StringRef> FileNameOrErr = C->getName();
if (!FileNameOrErr) {
error(FileNameOrErr.takeError(), Filename);
break;
}
StringRef SymName = I.getName();
outs() << SymName << " in " << FileNameOrErr.get() << "\n";
}

outs() << "\n";
}

static void dumpArchiveMap(Archive *A, StringRef Filename) {
Archive::symbol_iterator I = A->symbol_begin();
Archive::symbol_iterator E = A->symbol_end();
if (I != E) {
auto Map = A->symbols();
if (!Map.empty()) {
outs() << "Archive map\n";
for (; I != E; ++I) {
Expected<Archive::Child> C = I->getMember();
if (!C) {
error(C.takeError(), Filename);
break;
}
Expected<StringRef> FileNameOrErr = C->getName();
if (!FileNameOrErr) {
error(FileNameOrErr.takeError(), Filename);
break;
}
StringRef SymName = I->getName();
outs() << SymName << " in " << FileNameOrErr.get() << "\n";
}
outs() << "\n";
printArchiveMap(Map, Filename);
}

auto ECMap = A->ec_symbols();
if (!ECMap) {
warn(ECMap.takeError(), Filename);
} else if (!ECMap->empty()) {
outs() << "Archive EC map\n";
printArchiveMap(*ECMap, Filename);
}
}

Expand Down
2 changes: 2 additions & 0 deletions llvm/tools/llvm-readobj/COFFDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,7 @@ const EnumEntry<COFF::MachineTypes> ImageFileMachineType[] = {
LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_ARM ),
LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_ARM64 ),
LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_ARM64EC ),
LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_ARM64X ),
LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_ARMNT ),
LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_EBC ),
LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_I386 ),
Expand Down Expand Up @@ -1671,6 +1672,7 @@ void COFFDumper::printUnwindInfo() {
}
case COFF::IMAGE_FILE_MACHINE_ARM64:
case COFF::IMAGE_FILE_MACHINE_ARM64EC:
case COFF::IMAGE_FILE_MACHINE_ARM64X:
case COFF::IMAGE_FILE_MACHINE_ARMNT: {
ARM::WinEH::Decoder Decoder(W, Obj->getMachine() !=
COFF::IMAGE_FILE_MACHINE_ARMNT);
Expand Down