Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[LLD][COFF] Add support for CHPE redirection metadata. #105739

Merged
merged 1 commit into from
Aug 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions lld/COFF/Chunks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1078,4 +1078,17 @@ void ECExportThunkChunk::writeTo(uint8_t *buf) const {
write32le(buf + 10, target->getRVA() - rva - 14);
}

size_t CHPERedirectionChunk::getSize() const {
return exportThunks.size() * sizeof(chpe_redirection_entry);
}

void CHPERedirectionChunk::writeTo(uint8_t *buf) const {
auto entries = reinterpret_cast<chpe_redirection_entry *>(buf);

for (uint32_t i = 0; i < exportThunks.size(); i++) {
entries[i].Source = exportThunks[i].first->getRVA();
entries[i].Destination = exportThunks[i].second->getRVA();
}
}

} // namespace lld::coff
11 changes: 11 additions & 0 deletions lld/COFF/Chunks.h
Original file line number Diff line number Diff line change
Expand Up @@ -749,6 +749,17 @@ class ECCodeMapChunk : public NonSectionChunk {
std::vector<ECCodeMapEntry> &map;
};

class CHPERedirectionChunk : public NonSectionChunk {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this documented somewhere as being named with CHPE, as opposed to ARM64EC in some form?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not aware of this being explicitly documented. It's used in CHPE metadata, which is a somewhat public name; that's how it's referred to in the IMAGE_LOAD_CONFIG_DIRECTORY64 structure. The __chpe_metadata is somewhat public too - it’s explicitly referenced by the linker in the case of hybrid ARM64X images. These images have two separate load configurations but a single CHPE metadata, so the native load config can't directly reference the ARM64EC __chpe_metadata symbol, and the linker needs to look it up and use it to modify the load config instead.

Another place where this is named is in the __arm64x_redirection_metadata symbol from this PR. Note that it uses "arm64x," not "arm64ec." I think there is some ambiguity about what "arm64x" means. I usually use it to refer to hybrid files (as implied by -machine:arm64x) as ARM64X, while PE files containing only ARM64EC code (as implied by -machine:arm64ec) are referred to as ARM64EC. However, I'm no longer sure if this is the intended nomenclature. I’ve seen the ARM64X name used for both image types, so I guess ARM64X would refer to PE files containing ARM64EC code, while ARM64EC is the name of the ABI. As such, using ARM64X here would also be correct.

That said, I can see arguments for using any of the CHPE, ARM64EC, or ARM64X variants. I chose CHPE to emphasize that it's a part of CHPE metadata, but I wouldn't mind renaming it if you feel differently.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, if this is referred to in the load configs with a CHPE prefix, using these names is totally fine with me.

(I guess historically, the naming may stem from which parts were used in CHPEv1 and which are new for ARM64EC etc.)

public:
CHPERedirectionChunk(std::vector<std::pair<Chunk *, Defined *>> &exportThunks)
: exportThunks(exportThunks) {}
size_t getSize() const override;
void writeTo(uint8_t *buf) const override;

private:
std::vector<std::pair<Chunk *, Defined *>> &exportThunks;
};

static const uint8_t ECExportThunkCode[] = {
0x48, 0x8b, 0xc4, // movq %rsp, %rax
0x48, 0x89, 0x58, 0x20, // movq %rbx, 0x20(%rax)
Expand Down
2 changes: 2 additions & 0 deletions lld/COFF/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2440,6 +2440,8 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
if (isArm64EC(config->machine)) {
ctx.symtab.addAbsolute("__arm64x_extra_rfe_table", 0);
ctx.symtab.addAbsolute("__arm64x_extra_rfe_table_size", 0);
ctx.symtab.addAbsolute("__arm64x_redirection_metadata", 0);
ctx.symtab.addAbsolute("__arm64x_redirection_metadata_count", 0);
ctx.symtab.addAbsolute("__hybrid_code_map", 0);
ctx.symtab.addAbsolute("__hybrid_code_map_count", 0);
}
Expand Down
26 changes: 25 additions & 1 deletion lld/COFF/Writer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,9 @@ class Writer {
CVDebugRecordChunk *buildId = nullptr;
ArrayRef<uint8_t> sectionTable;

// List of Arm64EC export thunks.
std::vector<std::pair<Chunk *, Defined *>> exportThunks;

uint64_t fileSize;
uint32_t pointerToSymbolTable = 0;
uint64_t sizeOfImage;
Expand All @@ -312,6 +315,7 @@ class Writer {
OutputSection *idataSec;
OutputSection *edataSec;
OutputSection *didatSec;
OutputSection *a64xrmSec;
OutputSection *rsrcSec;
OutputSection *relocSec;
OutputSection *ctorsSec;
Expand Down Expand Up @@ -995,6 +999,8 @@ void Writer::createSections() {
idataSec = createSection(".idata", data | r);
edataSec = createSection(".edata", data | r);
didatSec = createSection(".didat", data | r);
if (isArm64EC(ctx.config.machine))
a64xrmSec = createSection(".a64xrm", data | r);
rsrcSec = createSection(".rsrc", data | r);
relocSec = createSection(".reloc", data | discardable | r);
ctorsSec = createSection(".ctors", data | r | w);
Expand Down Expand Up @@ -2053,15 +2059,24 @@ void Writer::createECChunks() {
auto sym = dyn_cast<Defined>(s);
if (!sym || !sym->getChunk())
continue;
if (auto thunk = dyn_cast<ECExportThunkChunk>(sym->getChunk()))
if (auto thunk = dyn_cast<ECExportThunkChunk>(sym->getChunk())) {
hexpthkSec->addChunk(thunk);
exportThunks.push_back({thunk, thunk->target});
}
}

auto codeMapChunk = make<ECCodeMapChunk>(codeMap);
rdataSec->addChunk(codeMapChunk);
Symbol *codeMapSym = ctx.symtab.findUnderscore("__hybrid_code_map");
replaceSymbol<DefinedSynthetic>(codeMapSym, codeMapSym->getName(),
codeMapChunk);

CHPERedirectionChunk *entryPoints = make<CHPERedirectionChunk>(exportThunks);
a64xrmSec->addChunk(entryPoints);
Symbol *entryPointsSym =
ctx.symtab.findUnderscore("__arm64x_redirection_metadata");
replaceSymbol<DefinedSynthetic>(entryPointsSym, entryPointsSym->getName(),
entryPoints);
}

// MinGW specific. Gather all relocations that are imported from a DLL even
Expand Down Expand Up @@ -2154,6 +2169,11 @@ void Writer::setECSymbols() {
if (!isArm64EC(ctx.config.machine))
return;

llvm::stable_sort(exportThunks, [](const std::pair<Chunk *, Defined *> &a,
const std::pair<Chunk *, Defined *> &b) {
return a.first->getRVA() < b.first->getRVA();
});

Symbol *rfeTableSym = ctx.symtab.findUnderscore("__arm64x_extra_rfe_table");
replaceSymbol<DefinedSynthetic>(rfeTableSym, "__arm64x_extra_rfe_table",
pdata.first);
Expand All @@ -2165,6 +2185,10 @@ void Writer::setECSymbols() {
->setVA(pdata.last->getRVA() + pdata.last->getSize() -
pdata.first->getRVA());
}

Symbol *entryPointCountSym =
ctx.symtab.findUnderscore("__arm64x_redirection_metadata_count");
cast<DefinedAbsolute>(entryPointCountSym)->setVA(exportThunks.size());
}

// Write section contents to a mmap'ed file.
Expand Down
4 changes: 2 additions & 2 deletions lld/test/COFF/Inputs/loadconfig-arm64ec.s
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ __chpe_metadata:
.rva __hybrid_code_map
.word __hybrid_code_map_count
.word 0 // __x64_code_ranges_to_entry_points
.word 0 //__arm64x_redirection_metadata
.rva __arm64x_redirection_metadata
.rva __os_arm64x_dispatch_call_no_redirect
.rva __os_arm64x_dispatch_ret
.rva __os_arm64x_check_call
Expand All @@ -76,7 +76,7 @@ __chpe_metadata:
.word 0 // __arm64x_native_entrypoint
.word 0 // __hybrid_auxiliary_iat
.word 0 // __x64_code_ranges_to_entry_points_count
.word 0 // __arm64x_redirection_metadata_count
.word __arm64x_redirection_metadata_count
.rva __os_arm64x_get_x64_information
.rva __os_arm64x_set_x64_information
.rva __arm64x_extra_rfe_table
Expand Down
29 changes: 27 additions & 2 deletions lld/test/COFF/arm64ec-export-thunks.test
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ EXP-DISASM-NEXT: 18000301f: cc int3
RUN: llvm-objdump -p exports.dll | FileCheck -check-prefix=EXP-EXPORT %s
EXP-EXPORT: Ordinal RVA Name
EXP-EXPORT-NEXT: 1 0x3010 arm64ec_func
EXP-EXPORT-NEXT: 2 0x6000 data_sym
EXP-EXPORT-NEXT: 2 0x7000 data_sym
EXP-EXPORT-NEXT: 3 0x3000 func
EXP-EXPORT-NEXT: 4 0x2000 x86_64_func

Expand All @@ -58,9 +58,30 @@ EXP-CHPE: CodeMap [
EXP-CHPE-NEXT: 0x1000 - 0x100C ARM64EC
EXP-CHPE-NEXT: 0x2000 - 0x3020 X64
EXP-CHPE-NEXT: ]
EXP-CHPE-NEXT: CodeRangesToEntryPoints: 0
EXP-CHPE-NEXT: RedirectionMetadata [
EXP-CHPE-NEXT: 0x3000 -> 0x1000
EXP-CHPE-NEXT: 0x3010 -> 0x1000
EXP-CHPE-NEXT: ]

RUN: llvm-readobj --sections exports.dll | FileCheck --check-prefix=A64XRM %s

A64XRM: Name: .a64xrm (2E 61 36 34 78 72 6D 00)
A64XRM-NEXT: VirtualSize: 0x10
A64XRM-NEXT: VirtualAddress: 0x6000
A64XRM-NEXT: RawDataSize: 512
A64XRM-NEXT: PointerToRawData:
A64XRM-NEXT: PointerToRelocations: 0x0
A64XRM-NEXT: PointerToLineNumbers: 0x0
A64XRM-NEXT: RelocationCount: 0
A64XRM-NEXT: LineNumberCount: 0
A64XRM-NEXT: Characteristics [ (0x40000040)
A64XRM-NEXT: IMAGE_SCN_CNT_INITIALIZED_DATA (0x40)
A64XRM-NEXT: IMAGE_SCN_MEM_READ (0x40000000)
A64XRM-NEXT: ]

RUN: llvm-objdump -s --section=.test exports.dll | FileCheck --check-prefix=EXP-DATA %s
EXP-DATA: 180006000 00300000 10300000
EXP-DATA: 180007000 00300000 10300000

RUN: lld-link -out:exports2.dll -machine:arm64ec antidep-func.obj x86_64-func.obj loadconfig-arm64ec.obj \
RUN: arm64ec-data.obj -dll -noentry -export:arm64ec_func -export:func=arm64ec_func \
Expand Down Expand Up @@ -100,6 +121,10 @@ ENTRY-CHPE: CodeMap [
ENTRY-CHPE-NEXT: 0x1000 - 0x100C ARM64EC
ENTRY-CHPE-NEXT: 0x2000 - 0x2010 X64
ENTRY-CHPE-NEXT: ]
ENTRY-CHPE-NEXT: CodeRangesToEntryPoints: 0
ENTRY-CHPE-NEXT: RedirectionMetadata [
ENTRY-CHPE-NEXT: 0x2000 -> 0x1000
ENTRY-CHPE-NEXT: ]


Test exporting data symbol as a function:
Expand Down
6 changes: 5 additions & 1 deletion lld/test/COFF/arm64ec-patchable-thunks.test
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,17 @@ PATCH-DISASM-NEXT: 18000200e: cc int3
PATCH-DISASM-NEXT: 18000200f: cc int3

RUN: llvm-readobj --hex-dump=.test test.dll | FileCheck -check-prefix=RVA %s
RVA: 0x180005000 00200000
RVA: 0x180006000 00200000

RUN: llvm-readobj --coff-load-config test.dll | FileCheck -check-prefix=PATCH-CHPE %s
PATCH-CHPE: CodeMap [
PATCH-CHPE-NEXT: 0x1000 - 0x1008 ARM64EC
PATCH-CHPE-NEXT: 0x2000 - 0x2010 X64
PATCH-CHPE-NEXT: ]
PATCH-CHPE-NEXT: CodeRangesToEntryPoints: 0
PATCH-CHPE-NEXT: RedirectionMetadata [
PATCH-CHPE-NEXT: 0x2000 -> 0x1000
PATCH-CHPE-NEXT: ]


RUN: lld-link -out:test2.dll -machine:arm64ec arm64ec-alias.obj test-sec.obj loadconfig-arm64ec.obj -dll -noentry
Expand Down
Loading