[JITLink][COFF] Implement include/alternatename linker directive.

Implements include/alternatename linker directive. Alternatename is used by static msvc runtime library. Alias symbol is technically incorrect (we have to search for external definition) but we don't have a way to represent this in jitlink/orc yet, this is solved in the following up patch.

Inlcude linker directive is used in ucrt to forcelly lookup the static initializer symbols so that they will be emitted. It's implemented as extenral symbols with live flag on that cause the lookup of these symbols.

Reviewed By: lhames

Differential Revision: https://reviews.llvm.org/D130276
This commit is contained in:
Sunho Kim 2022-07-31 07:49:37 +09:00
parent a8f2e24e48
commit 88181375a3
9 changed files with 351 additions and 34 deletions

View File

@ -1,3 +1,7 @@
set(LLVM_TARGET_DEFINITIONS COFFOptions.td)
tablegen(LLVM COFFOptions.inc -gen-opt-parser-defs)
add_public_tablegen_target(JITLinkTableGen)
add_llvm_component_library(LLVMJITLink
DWARFRecordSectionSplitter.cpp
EHFrameSupport.cpp
@ -23,6 +27,7 @@ add_llvm_component_library(LLVMJITLink
# COFF
COFF.cpp
COFFDirectiveParser.cpp
COFFLinkGraphBuilder.cpp
COFF_x86_64.cpp
@ -36,10 +41,12 @@ add_llvm_component_library(LLVMJITLink
DEPENDS
intrinsics_gen
JITLinkTableGen
LINK_COMPONENTS
BinaryFormat
Object
Option
OrcTargetProcess
Support
)

View File

@ -0,0 +1,77 @@
//===-- COFFDirectiveParser.cpp - JITLink coff directive parser --*- C++ -*===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// MSVC COFF directive parser
//
//===----------------------------------------------------------------------===//
#include "COFFDirectiveParser.h"
using namespace llvm;
using namespace jitlink;
#define DEBUG_TYPE "jitlink"
// Create prefix string literals used in Options.td
#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
#include "COFFOptions.inc"
#undef PREFIX
// Create table mapping all options defined in COFFOptions.td
static const opt::OptTable::Info infoTable[] = {
#define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X7, X8, X9, X10, X11, X12) \
{X1, \
X2, \
X10, \
X11, \
COFF_OPT_##ID, \
opt::Option::KIND##Class, \
X9, \
X8, \
COFF_OPT_##GROUP, \
COFF_OPT_##ALIAS, \
X7, \
X12},
#include "COFFOptions.inc"
#undef OPTION
};
class COFFOptTable : public opt::OptTable {
public:
COFFOptTable() : OptTable(infoTable, true) {}
};
static COFFOptTable optTable;
Expected<std::unique_ptr<opt::InputArgList>>
COFFDirectiveParser::parse(StringRef Str) {
SmallVector<StringRef, 16> Tokens;
SmallVector<const char *, 16> Buffer;
cl::TokenizeWindowsCommandLineNoCopy(Str, saver, Tokens);
for (StringRef Tok : Tokens) {
bool HasNul = Tok.end() != Str.end() && Tok.data()[Tok.size()] == '\0';
Buffer.push_back(HasNul ? Tok.data() : saver.save(Tok).data());
}
unsigned missingIndex;
unsigned missingCount;
auto Result = std::make_unique<opt::InputArgList>(
optTable.ParseArgs(Buffer, missingIndex, missingCount));
if (missingCount)
return make_error<JITLinkError>(Twine("COFF directive parsing failed: ") +
Result->getArgString(missingIndex) +
" missing argument");
LLVM_DEBUG({
for (auto *arg : Result->filtered(COFF_OPT_UNKNOWN))
dbgs() << "Unknown coff option argument: " << arg->getAsString(*Result)
<< "\n";
});
return Result;
}

View File

@ -0,0 +1,48 @@
//===--- COFFDirectiveParser.h - JITLink coff directive parser --*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// MSVC COFF directive parser
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_EXECUTIONENGINE_JITLINK_COFFDIRECTIVEPARSER_H
#define LLVM_EXECUTIONENGINE_JITLINK_COFFDIRECTIVEPARSER_H
#include "llvm/ADT/Triple.h"
#include "llvm/ExecutionEngine/JITLink/JITLink.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/StringSaver.h"
namespace llvm {
namespace jitlink {
enum {
COFF_OPT_INVALID = 0,
#define OPTION(_1, _2, ID, _4, _5, _6, _7, _8, _9, _10, _11, _12) COFF_OPT_##ID,
#include "COFFOptions.inc"
#undef OPTION
};
/// Parser for the MSVC specific preprocessor directives.
/// https://docs.microsoft.com/en-us/cpp/preprocessor/comment-c-cpp?view=msvc-160
class COFFDirectiveParser {
public:
Expected<std::unique_ptr<opt::InputArgList>> parse(StringRef Str);
private:
llvm::BumpPtrAllocator bAlloc;
llvm::StringSaver saver{bAlloc};
};
} // end namespace jitlink
} // end namespace llvm
#endif // LLVM_EXECUTIONENGINE_JITLINK_COFFDIRECTIVEPARSER_H

View File

@ -170,11 +170,16 @@ Error COFFLinkGraphBuilder::graphifySections() {
if (auto Err = Obj.getSectionContents(*Sec, Data))
return Err;
auto CharData = ArrayRef<char>(
reinterpret_cast<const char *>(Data.data()), Data.size());
if (SectionName == getDirectiveSectionName())
if (auto Err = handleDirectiveSection(
StringRef(CharData.data(), CharData.size())))
return Err;
B = &G->createContentBlock(
*GraphSec,
ArrayRef<char>(reinterpret_cast<const char *>(Data.data()),
Data.size()),
orc::ExecutorAddr(getSectionAddress(Obj, *Sec)),
*GraphSec, CharData, orc::ExecutorAddr(getSectionAddress(Obj, *Sec)),
(*Sec)->getAlignment(), 0);
}
@ -224,30 +229,17 @@ Error COFFLinkGraphBuilder::graphifySymbols() {
<< " (index: " << SectionIndex << ") \n";
});
else if (Sym->isUndefined()) {
auto CreateExternalSymbol = [&](StringRef SymbolName) {
if (!ExternalSymbols.count(SymbolName))
ExternalSymbols[SymbolName] = &G->addExternalSymbol(
SymbolName, Sym->getValue(), Linkage::Strong);
LLVM_DEBUG({
dbgs() << " " << SymIndex
<< ": Creating external graph symbol for COFF symbol \""
<< SymbolName << "\" in "
<< getCOFFSectionName(SectionIndex, Sec, *Sym)
<< " (index: " << SectionIndex << ") \n";
});
return ExternalSymbols[SymbolName];
};
if (SymbolName.startswith(getDLLImportStubPrefix())) {
if (Sym->getValue() != 0)
return make_error<JITLinkError>(
"DLL import symbol has non-zero offset");
auto ExternalSym = CreateExternalSymbol(
SymbolName.drop_front(getDLLImportStubPrefix().size()));
auto ExternalSym = createExternalSymbol(
SymIndex, SymbolName.drop_front(getDLLImportStubPrefix().size()),
*Sym, Sec);
GSym = &createDLLImportEntry(SymbolName, *ExternalSym);
} else
GSym = CreateExternalSymbol(SymbolName);
GSym = createExternalSymbol(SymIndex, SymbolName, *Sym, Sec);
} else if (Sym->isWeakExternal()) {
auto *WeakExternal = Sym->getAux<object::coff_aux_weak_external>();
COFFSymbolIndex TagIndex = WeakExternal->TagIndex;
@ -281,12 +273,52 @@ Error COFFLinkGraphBuilder::graphifySymbols() {
if (auto Err = flushWeakAliasRequests())
return Err;
if (auto Err = handleAlternateNames())
return Err;
if (auto Err = calculateImplicitSizeOfSymbols())
return Err;
return Error::success();
}
Error COFFLinkGraphBuilder::handleDirectiveSection(StringRef Str) {
auto Parsed = DirectiveParser.parse(Str);
if (!Parsed)
return Parsed.takeError();
for (auto *Arg : **Parsed) {
StringRef S = Arg->getValue();
switch (Arg->getOption().getID()) {
case COFF_OPT_alternatename: {
StringRef From, To;
std::tie(From, To) = S.split('=');
if (From.empty() || To.empty())
return make_error<JITLinkError>(
"Invalid COFF /alternatename directive");
AlternateNames[From] = To;
break;
}
case COFF_OPT_incl: {
auto DataCopy = G->allocateString(S);
StringRef StrCopy(DataCopy.data(), DataCopy.size());
ExternalSymbols[StrCopy] =
&G->addExternalSymbol(StrCopy, 0, Linkage::Strong);
ExternalSymbols[StrCopy]->setLive(true);
break;
}
case COFF_OPT_export:
break;
default: {
LLVM_DEBUG({
dbgs() << "Unknown coff directive: " << Arg->getSpelling() << "\n";
});
break;
}
}
}
return Error::success();
}
Error COFFLinkGraphBuilder::flushWeakAliasRequests() {
// Export the weak external symbols and alias it
for (auto &WeakExternal : WeakExternalRequests) {
@ -303,22 +335,18 @@ Error COFFLinkGraphBuilder::flushWeakAliasRequests() {
? Scope::Default
: Scope::Local;
// FIXME: Support this when there's a way to handle this.
if (!Target->isDefined())
return make_error<JITLinkError>("Weak external symbol with external "
"symbol as alternative not supported.");
jitlink::Symbol *NewSymbol = &G->addDefinedSymbol(
Target->getBlock(), Target->getOffset(), WeakExternal.SymbolName,
Target->getSize(), Linkage::Weak, S, Target->isCallable(), false);
auto NewSymbol =
createAliasSymbol(WeakExternal.SymbolName, Linkage::Weak, S, *Target);
if (!NewSymbol)
return NewSymbol.takeError();
setGraphSymbol(AliasSymbol->getSectionNumber(), WeakExternal.Alias,
*NewSymbol);
**NewSymbol);
LLVM_DEBUG({
dbgs() << " " << WeakExternal.Alias
<< ": Creating weak external symbol for COFF symbol \""
<< WeakExternal.SymbolName << "\" in section "
<< AliasSymbol->getSectionNumber() << "\n";
dbgs() << " " << *NewSymbol << "\n";
dbgs() << " " << **NewSymbol << "\n";
});
} else
return make_error<JITLinkError>("Weak symbol alias requested but actual "
@ -328,6 +356,49 @@ Error COFFLinkGraphBuilder::flushWeakAliasRequests() {
return Error::success();
}
Error COFFLinkGraphBuilder::handleAlternateNames() {
// FIXME: Use proper alias
for (auto &KeyValue : AlternateNames)
if (DefinedSymbols.count(KeyValue.second) &&
ExternalSymbols.count(KeyValue.first)) {
auto *Target = DefinedSymbols[KeyValue.second];
auto *Alias = ExternalSymbols[KeyValue.first];
G->makeDefined(*Alias, Target->getBlock(), Target->getOffset(),
Target->getSize(), Linkage::Weak, Scope::Local, false);
}
return Error::success();
}
Symbol *COFFLinkGraphBuilder::createExternalSymbol(
COFFSymbolIndex SymIndex, StringRef SymbolName,
object::COFFSymbolRef Symbol, const object::coff_section *Section) {
if (!ExternalSymbols.count(SymbolName))
ExternalSymbols[SymbolName] =
&G->addExternalSymbol(SymbolName, Symbol.getValue(), Linkage::Strong);
LLVM_DEBUG({
dbgs() << " " << SymIndex
<< ": Creating external graph symbol for COFF symbol \""
<< SymbolName << "\" in "
<< getCOFFSectionName(Symbol.getSectionNumber(), Section, Symbol)
<< " (index: " << Symbol.getSectionNumber() << ") \n";
});
return ExternalSymbols[SymbolName];
}
Expected<Symbol *> COFFLinkGraphBuilder::createAliasSymbol(StringRef SymbolName,
Linkage L, Scope S,
Symbol &Target) {
if (!Target.isDefined()) {
// FIXME: Support this when there's a way to handle this.
return make_error<JITLinkError>("Weak external symbol with external "
"symbol as alternative not supported.");
}
return &G->addDefinedSymbol(Target.getBlock(), Target.getOffset(), SymbolName,
Target.getSize(), L, S, Target.isCallable(),
false);
}
// In COFF, most of the defined symbols don't contain the size information.
// Hence, we calculate the "implicit" size of symbol by taking the delta of
// offsets of consecutive symbols within a block. We maintain a balanced tree
@ -426,10 +497,11 @@ Expected<Symbol *> COFFLinkGraphBuilder::createDefinedSymbol(
if (Symbol.isExternal()) {
// This is not a comdat sequence, export the symbol as it is
if (!isComdatSection(Section)) {
return &G->addDefinedSymbol(
auto GSym = &G->addDefinedSymbol(
*B, Symbol.getValue(), SymbolName, 0, Linkage::Strong, Scope::Default,
Symbol.getComplexType() == COFF::IMAGE_SYM_DTYPE_FUNCTION, false);
DefinedSymbols[SymbolName] = GSym;
return GSym;
} else {
if (!PendingComdatExports[Symbol.getSectionNumber()])
return make_error<JITLinkError>("No pending COMDAT export for symbol " +
@ -557,8 +629,9 @@ COFFLinkGraphBuilder::exportCOMDATSymbol(COFFSymbolIndex SymIndex,
dbgs() << " " << *Target << "\n";
});
PendingComdatExport = None;
DefinedSymbols[SymbolName] = Target;
return Target;
}
} // namespace jitlink
} // namespace llvm
} // namespace llvm

View File

@ -18,6 +18,7 @@
#include "llvm/ExecutionEngine/JITLink/JITLink.h"
#include "llvm/Object/COFF.h"
#include "COFFDirectiveParser.h"
#include "EHFrameSupportImpl.h"
#include "JITLinkGeneric.h"
@ -133,6 +134,11 @@ private:
Section &getCommonSection();
Symbol *createExternalSymbol(COFFSymbolIndex SymIndex, StringRef SymbolName,
object::COFFSymbolRef Symbol,
const object::coff_section *Section);
Expected<Symbol *> createAliasSymbol(StringRef SymbolName, Linkage L, Scope S,
Symbol &Target);
Expected<Symbol *> createDefinedSymbol(COFFSymbolIndex SymIndex,
StringRef SymbolName,
object::COFFSymbolRef Symbol,
@ -143,7 +149,10 @@ private:
Expected<Symbol *> exportCOMDATSymbol(COFFSymbolIndex SymIndex,
StringRef SymbolName,
object::COFFSymbolRef Symbol);
Error handleDirectiveSection(StringRef Str);
Error flushWeakAliasRequests();
Error handleAlternateNames();
Error calculateImplicitSizeOfSymbols();
static uint64_t getSectionAddress(const object::COFFObjectFile &Obj,
@ -154,18 +163,22 @@ private:
static unsigned getPointerSize(const object::COFFObjectFile &Obj);
static support::endianness getEndianness(const object::COFFObjectFile &Obj);
static StringRef getDLLImportStubPrefix() { return "__imp_"; }
static StringRef getDirectiveSectionName() { return ".drectve"; }
StringRef getCOFFSectionName(COFFSectionIndex SectionIndex,
const object::coff_section *Sec,
object::COFFSymbolRef Sym);
const object::COFFObjectFile &Obj;
std::unique_ptr<LinkGraph> G;
COFFDirectiveParser DirectiveParser;
Section *CommonSection = nullptr;
std::vector<Block *> GraphBlocks;
std::vector<Symbol *> GraphSymbols;
DenseMap<StringRef, StringRef> AlternateNames;
DenseMap<StringRef, Symbol *> ExternalSymbols;
DenseMap<StringRef, Symbol *> DefinedSymbols;
};
template <typename RelocHandlerFunction>

View File

@ -0,0 +1,21 @@
include "llvm/Option/OptParser.td"
// link.exe accepts options starting with either a dash or a slash.
// Flag that takes no arguments.
class F<string name> : Flag<["/", "-", "/?", "-?"], name>;
// Flag that takes one argument after ":".
class P<string name> :
Joined<["/", "-", "/?", "-?"], name#":">;
// Boolean flag which can be suffixed by ":no". Using it unsuffixed turns the
// flag on and using it suffixed by ":no" turns it off.
multiclass B_priv<string name> {
def "" : F<name>;
def _no : F<name#":no">;
}
def export : P<"export">;
def alternatename : P<"alternatename">;
def incl : Joined<["/", "-", "/?", "-?"], "include:">;

View File

@ -0,0 +1,30 @@
# RUN: llvm-mc -filetype=obj -triple=x86_64-windows-msvc %s -o %t
# RUN: llvm-jitlink -noexec %t
#
# Check object with alternatename directive does not fail, because
# foo external symbol was resolved to foo_def.
#
.text
.def foo_def;
.scl 2;
.type 32;
.endef
.globl foo_def
.p2align 4, 0x90
foo_def:
retq
.def main;
.scl 2;
.type 32;
.endef
.globl main
.p2align 4, 0x90
main:
callq foo
retq
.section .drectve,"yn"
.ascii "/alternatename:foo=foo_def"

View File

@ -0,0 +1,27 @@
# RUN: llvm-mc -filetype=obj -triple=x86_64-windows-msvc %s -o %t
# RUN: not llvm-jitlink -noexec %t 2>&1 | FileCheck %s
#
# Check object without alternatename directive fails because of
# external symbol not found error.
#
# CHECK: error: Symbols not found: [ foo ]
.text
.def foo_def;
.scl 2;
.type 32;
.endef
.globl foo_def
.p2align 4, 0x90
foo_def:
retq
.def main;
.scl 2;
.type 32;
.endef
.globl main
.p2align 4, 0x90
main:
callq foo
retq

View File

@ -0,0 +1,21 @@
# RUN: llvm-mc -filetype=obj -triple=x86_64-windows-msvc %s -o %t
# RUN: not llvm-jitlink -noexec %t 2>&1 | FileCheck %s
#
# Check an external symbol "foo" is generated and not dead-stripped
# because of include directive which turned into symbol not found error.
#
# CHECK: error: Symbols not found: [ foo ]
.text
.def main;
.scl 2;
.type 32;
.endef
.globl main
.p2align 4, 0x90
main:
retq
.section .drectve,"yn"
.ascii "/include:foo"