Skip to content

[LLD][COFF] Add support for hybrid ARM64X entry points #123096

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

Merged
merged 2 commits into from
Jan 16, 2025
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
1 change: 0 additions & 1 deletion lld/COFF/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,6 @@ struct Configuration {
size_t wordsize;
bool verbose = false;
WindowsSubsystem subsystem = llvm::COFF::IMAGE_SUBSYSTEM_UNKNOWN;
Symbol *entry = nullptr;
bool noEntry = false;
std::string outputFile;
std::string importName;
Expand Down
39 changes: 21 additions & 18 deletions lld/COFF/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -491,8 +491,9 @@ void LinkerDriver::parseDirectives(InputFile *file) {
case OPT_entry:
if (!arg->getValue()[0])
Fatal(ctx) << "missing entry point symbol name";
ctx.config.entry =
file->symtab.addGCRoot(file->symtab.mangle(arg->getValue()), true);
ctx.forEachSymtab([&](SymbolTable &symtab) {
symtab.entry = symtab.addGCRoot(symtab.mangle(arg->getValue()), true);
});
break;
case OPT_failifmismatch:
checkFailIfMismatch(arg->getValue(), file);
Expand Down Expand Up @@ -1394,8 +1395,9 @@ void LinkerDriver::createECExportThunks() {
}
}

if (ctx.config.entry)
maybeCreateECExportThunk(ctx.config.entry->getName(), ctx.config.entry);
if (ctx.symtabEC->entry)
maybeCreateECExportThunk(ctx.symtabEC->entry->getName(),
ctx.symtabEC->entry);
for (Export &e : ctx.config.exports) {
if (!e.data)
maybeCreateECExportThunk(e.extName.empty() ? e.name : e.extName, e.sym);
Expand Down Expand Up @@ -2357,33 +2359,32 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
}

// Handle /entry and /dll
{
ctx.forEachSymtab([&](SymbolTable &symtab) {
llvm::TimeTraceScope timeScope("Entry point");
if (auto *arg = args.getLastArg(OPT_entry)) {
if (!arg->getValue()[0])
Fatal(ctx) << "missing entry point symbol name";
config->entry =
ctx.symtab.addGCRoot(ctx.symtab.mangle(arg->getValue()), true);
} else if (!config->entry && !config->noEntry) {
symtab.entry = symtab.addGCRoot(symtab.mangle(arg->getValue()), true);
} else if (!symtab.entry && !config->noEntry) {
if (args.hasArg(OPT_dll)) {
StringRef s = (config->machine == I386) ? "__DllMainCRTStartup@12"
: "_DllMainCRTStartup";
config->entry = ctx.symtab.addGCRoot(s, true);
symtab.entry = symtab.addGCRoot(s, true);
} else if (config->driverWdm) {
// /driver:wdm implies /entry:_NtProcessStartup
config->entry =
ctx.symtab.addGCRoot(ctx.symtab.mangle("_NtProcessStartup"), true);
symtab.entry =
symtab.addGCRoot(symtab.mangle("_NtProcessStartup"), true);
} else {
// Windows specific -- If entry point name is not given, we need to
// infer that from user-defined entry name.
StringRef s = ctx.symtab.findDefaultEntry();
StringRef s = symtab.findDefaultEntry();
if (s.empty())
Fatal(ctx) << "entry point must be defined";
config->entry = ctx.symtab.addGCRoot(s, true);
symtab.entry = symtab.addGCRoot(s, true);
Log(ctx) << "Entry name inferred: " << s;
}
}
}
});

// Handle /delayload
{
Expand Down Expand Up @@ -2522,10 +2523,12 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
{
llvm::TimeTraceScope timeScope("Add unresolved symbols");
do {
// Windows specific -- if entry point is not found,
// search for its mangled names.
if (config->entry)
ctx.symtab.mangleMaybe(config->entry);
ctx.forEachSymtab([&](SymbolTable &symtab) {
// Windows specific -- if entry point is not found,
// search for its mangled names.
if (symtab.entry)
symtab.mangleMaybe(symtab.entry);
});

// Windows specific -- Make sure we resolve all dllexported symbols.
for (Export &e : config->exports) {
Expand Down
2 changes: 1 addition & 1 deletion lld/COFF/MapFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ void lld::coff::writeMapFile(COFFLinkerContext &ctx) {
uint64_t entryAddress = 0;

if (!ctx.config.noEntry) {
Defined *entry = dyn_cast_or_null<Defined>(ctx.config.entry);
Defined *entry = dyn_cast_or_null<Defined>(ctx.symtab.entry);
if (entry) {
Chunk *chunk = entry->getChunk();
entrySecIndex = chunk->getOutputSectionIdx();
Expand Down
3 changes: 3 additions & 0 deletions lld/COFF/SymbolTable.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,9 @@ class SymbolTable {

bool isEC() const { return machine == ARM64EC; }

// An entry point symbol.
Symbol *entry = nullptr;

// A list of chunks which to be added to .rdata.
std::vector<Chunk *> localImportChunks;

Expand Down
14 changes: 11 additions & 3 deletions lld/COFF/Writer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1748,7 +1748,7 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
pe->SizeOfImage = sizeOfImage;
pe->SizeOfHeaders = sizeOfHeaders;
if (!config->noEntry) {
Defined *entry = cast<Defined>(config->entry);
Defined *entry = cast<Defined>(ctx.symtab.entry);
pe->AddressOfEntryPoint = entry->getRVA();
// Pointer to thumb code must have the LSB set, so adjust it.
if (config->machine == ARMNT)
Expand Down Expand Up @@ -2031,8 +2031,10 @@ void Writer::createGuardCFTables() {
}

// Mark the image entry as address-taken.
if (config->entry)
maybeAddAddressTakenFunction(addressTakenSyms, config->entry);
ctx.forEachSymtab([&](SymbolTable &symtab) {
if (symtab.entry)
maybeAddAddressTakenFunction(addressTakenSyms, symtab.entry);
});

// Mark exported symbols in executable sections as address-taken.
for (Export &e : config->exports)
Expand Down Expand Up @@ -2584,6 +2586,12 @@ void Writer::createDynamicRelocs() {
coffHeaderOffset + offsetof(coff_file_header, Machine),
AMD64);

if (ctx.symtab.entry != ctx.hybridSymtab->entry)
ctx.dynamicRelocs->add(IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE, sizeof(uint32_t),
peHeaderOffset +
offsetof(pe32plus_header, AddressOfEntryPoint),
cast_or_null<Defined>(ctx.hybridSymtab->entry));

// Set the hybrid load config to the EC load config.
ctx.dynamicRelocs->add(IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE, sizeof(uint32_t),
dataDirOffset64 +
Expand Down
92 changes: 92 additions & 0 deletions lld/test/COFF/arm64x-entry.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
REQUIRES: aarch64, x86
RUN: split-file %s %t.dir && cd %t.dir

RUN: llvm-mc -filetype=obj -triple=arm64ec-windows arm64ec-dllmain.s -o arm64ec-dllmain.obj
RUN: llvm-mc -filetype=obj -triple=aarch64-windows arm64-dllmain.s -o arm64-dllmain.obj
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows arm64ec-func.s -o arm64ec-func.obj
RUN: llvm-mc -filetype=obj -triple=aarch64-windows arm64-func.s -o arm64-func.obj
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows arm64-drectve.s -o arm64ec-drectve.obj
RUN: llvm-mc -filetype=obj -triple=aarch64-windows arm64-drectve.s -o arm64-drectve.obj
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows %S/Inputs/loadconfig-arm64ec.s -o loadconfig-arm64ec.obj
RUN: llvm-mc -filetype=obj -triple=aarch64-windows %S/Inputs/loadconfig-arm64.s -o loadconfig-arm64.obj

RUN: lld-link -machine:arm64x -dll -out:out.dll arm64ec-dllmain.obj arm64-dllmain.obj \
RUN: loadconfig-arm64.obj loadconfig-arm64ec.obj

RUN: llvm-objdump -d out.dll | FileCheck --check-prefix=DISASM %s
DISASM: Disassembly of section .text:
DISASM-EMPTY:
DISASM-NEXT: 0000000180001000 <.text>:
DISASM-NEXT: 180001000: 52800020 mov w0, #0x1 // =1
DISASM-NEXT: 180001004: d65f03c0 ret
DISASM-NEXT: ...
DISASM-NEXT: 180002000: 52800040 mov w0, #0x2 // =2
DISASM-NEXT: 180002004: d65f03c0 ret
DISASM-EMPTY:
DISASM-NEXT: Disassembly of section .hexpthk:
DISASM-EMPTY:
DISASM-NEXT: 0000000180003000 <.hexpthk>:
DISASM-NEXT: 180003000: 48 8b c4 movq %rsp, %rax
DISASM-NEXT: 180003003: 48 89 58 20 movq %rbx, 0x20(%rax)
DISASM-NEXT: 180003007: 55 pushq %rbp
DISASM-NEXT: 180003008: 5d popq %rbp
DISASM-NEXT: 180003009: e9 f2 ef ff ff jmp 0x180002000 <.text+0x1000>
DISASM-NEXT: 18000300e: cc int3
DISASM-NEXT: 18000300f: cc int3

RUN: llvm-readobj --headers out.dll | FileCheck --check-prefix=READOBJ %s
READOBJ: AddressOfEntryPoint: 0x1000
READOBJ: HybridObject {
READOBJ: AddressOfEntryPoint: 0x3000
READOBJ: }

RUN: lld-link -machine:arm64x -dll -out:out2.dll arm64ec-func.obj arm64-func.obj \
RUN: arm64ec-drectve.obj loadconfig-arm64.obj loadconfig-arm64ec.obj
RUN: llvm-objdump -d out2.dll | FileCheck --check-prefix=DISASM %s
RUN: llvm-readobj --headers --coff-load-config out2.dll | FileCheck --check-prefix=READOBJ %s

RUN: lld-link -machine:arm64x -dll -out:out3.dll arm64ec-func.obj arm64-func.obj \
RUN: arm64-drectve.obj loadconfig-arm64.obj loadconfig-arm64ec.obj
RUN: llvm-objdump -d out3.dll | FileCheck --check-prefix=DISASM %s
RUN: llvm-readobj --headers --coff-load-config out3.dll | FileCheck --check-prefix=READOBJ %s

RUN: lld-link -machine:arm64x -dll -out:out4.dll arm64ec-func.obj arm64-func.obj \
RUN: loadconfig-arm64.obj loadconfig-arm64ec.obj -entry:func
RUN: llvm-objdump -d out4.dll | FileCheck --check-prefix=DISASM %s
RUN: llvm-readobj --headers --coff-load-config out4.dll | FileCheck --check-prefix=READOBJ %s

#--- arm64-dllmain.s
.section .text,"xr",discard,_DllMainCRTStartup
.globl _DllMainCRTStartup
.p2align 2
_DllMainCRTStartup:
mov w0, #1
ret

#--- arm64ec-dllmain.s
.section .text,"xr",discard,_DllMainCRTStartup
.globl _DllMainCRTStartup
.p2align 2
_DllMainCRTStartup:
mov w0, #2
ret

#--- arm64-func.s
.section .text,"xr",discard,func
.globl func
.p2align 2
func:
mov w0, #1
ret

#--- arm64ec-func.s
.section .text,"xr",discard,func
.globl func
.p2align 2
func:
mov w0, #2
ret

#--- arm64-drectve.s
.section .drectve
.ascii "-entry:func"
Loading