
Fixes MSVC CRT thread-local constructors support on hybrid ARM64X targets. `-arm64xsameaddress` is an undocumented option that ensures the specified function has the same address in both native and EC views of hybrid images. To achieve this, the linker emits additional thunks and replaces the symbols of those functions with the thunk symbol (the same thunk is used in both views). The thunk code jumps to the native function (similar to range extension thunks), but additional ARM64X relocations are emitted to replace the target with the EC function in the EC view. MSVC appears to generate thunks even for non-hybrid ARM64EC images. As a side effect, the native symbol is pulled in. Since this is used in the CRT for thread-local constructors, it results in the image containing unnecessary native code. Because these thunks do not appear to be useful in that context, we limit this behavior to actual hybrid targets. This may change if compatibility requires it. The tricky part is that thunks should be skipped if the symbol is not live in either view, and symbol replacement must be reflected in weak aliases. This requires thunk generation to happen before resolving weak aliases but after the GC pass. To enable this, the `markLive` call was moved earlier, and the final weak alias resolution was postponed until afterward. This requires more code to be aware of weak aliases, which previously could assume they were already resolved.
89 lines
2.7 KiB
C++
89 lines
2.7 KiB
C++
//===- MarkLive.cpp -------------------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "COFFLinkerContext.h"
|
|
#include "Chunks.h"
|
|
#include "Symbols.h"
|
|
#include "lld/Common/Timer.h"
|
|
#include "llvm/Support/TimeProfiler.h"
|
|
|
|
namespace lld::coff {
|
|
|
|
// Set live bit on for each reachable chunk. Unmarked (unreachable)
|
|
// COMDAT chunks will be ignored by Writer, so they will be excluded
|
|
// from the final output.
|
|
void markLive(COFFLinkerContext &ctx) {
|
|
llvm::TimeTraceScope timeScope("Mark live");
|
|
ScopedTimer t(ctx.gcTimer);
|
|
|
|
// We build up a worklist of sections which have been marked as live. We only
|
|
// push into the worklist when we discover an unmarked section, and we mark
|
|
// as we push, so sections never appear twice in the list.
|
|
SmallVector<SectionChunk *, 256> worklist;
|
|
|
|
// COMDAT section chunks are dead by default. Add non-COMDAT chunks. Do not
|
|
// traverse DWARF sections. They are live, but they should not keep other
|
|
// sections alive.
|
|
for (Chunk *c : ctx.driver.getChunks())
|
|
if (auto *sc = dyn_cast<SectionChunk>(c))
|
|
if (sc->live && !sc->isDWARF())
|
|
worklist.push_back(sc);
|
|
|
|
auto enqueue = [&](SectionChunk *c) {
|
|
if (c->live)
|
|
return;
|
|
c->live = true;
|
|
worklist.push_back(c);
|
|
};
|
|
|
|
std::function<void(Symbol *)> addSym;
|
|
|
|
auto addImportFile = [&](ImportFile *file) {
|
|
file->live = true;
|
|
if (file->impchkThunk && file->impchkThunk->exitThunk)
|
|
addSym(file->impchkThunk->exitThunk);
|
|
};
|
|
|
|
addSym = [&](Symbol *s) {
|
|
Defined *b = s->getDefined();
|
|
if (!b)
|
|
return;
|
|
if (auto *sym = dyn_cast<DefinedRegular>(b)) {
|
|
enqueue(sym->getChunk());
|
|
} else if (auto *sym = dyn_cast<DefinedImportData>(b)) {
|
|
addImportFile(sym->file);
|
|
} else if (auto *sym = dyn_cast<DefinedImportThunk>(b)) {
|
|
addImportFile(sym->wrappedSym->file);
|
|
sym->getChunk()->live = true;
|
|
}
|
|
};
|
|
|
|
// Add GC root chunks.
|
|
for (Symbol *b : ctx.config.gcroot)
|
|
addSym(b);
|
|
|
|
while (!worklist.empty()) {
|
|
SectionChunk *sc = worklist.pop_back_val();
|
|
assert(sc->live && "We mark as live when pushing onto the worklist!");
|
|
|
|
// Mark all symbols listed in the relocation table for this section.
|
|
for (Symbol *b : sc->symbols())
|
|
if (b)
|
|
addSym(b);
|
|
|
|
// Mark associative sections if any.
|
|
for (SectionChunk &c : sc->children())
|
|
enqueue(&c);
|
|
|
|
// Mark EC entry thunks.
|
|
if (Defined *entryThunk = sc->getEntryThunk())
|
|
addSym(entryThunk);
|
|
}
|
|
}
|
|
}
|