Skip to content

Commit

Permalink
[LLD][COFF] Add support for hybrid ARM64X entry points (llvm#123096)
Browse files Browse the repository at this point in the history
Store the entry symbol in SymbolTable instead of Configuration, as it
differs between symbol tables.
  • Loading branch information
cjacek authored Jan 16, 2025
1 parent b7e2014 commit d004947
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 23 deletions.
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"

0 comments on commit d004947

Please sign in to comment.