Skip to content

Commit

Permalink
[EVM] Add libraries support
Browse files Browse the repository at this point in the history
  • Loading branch information
PavelKopyl committed Jan 23, 2025
1 parent 1441da9 commit 09fc13b
Show file tree
Hide file tree
Showing 23 changed files with 676 additions and 86 deletions.
23 changes: 17 additions & 6 deletions lld/include/lld-c/LLDAsLibraryC.h
Original file line number Diff line number Diff line change
Expand Up @@ -131,10 +131,16 @@ void LLVMGetUndefinedReferencesEraVM(LLVMMemoryBufferRef inBuffer,
char ***factoryDepSymbols,
uint64_t *numFactoryDepSymbols);

/** Disposes an array with linker symbols returned by the
* LLVMGetUndefinedReferencesEraVM(). */
void LLVMDisposeUndefinedReferencesEraVM(char *linkerSymbolNames[],
uint64_t numLinkerSymbols);
/** Returns true if the \p inBuffer contains an ELF object file. */
LLVMBool LLVMIsELFEVM(LLVMMemoryBufferRef inBuffer);

void LLVMGetUndefinedReferencesEVM(LLVMMemoryBufferRef inBuffer,
char ***linkerSymbols,
uint64_t *numLinkerSymbols);

/** Disposes an array with symbols returned by the
* LLVMGetUndefinedReferences* functions. */
void LLVMDisposeUndefinedReferences(char *symbolNames[], uint64_t numSymbols);

/** Links the deploy and runtime ELF object files using the information about
* dependencies.
Expand All @@ -154,13 +160,18 @@ void LLVMDisposeUndefinedReferencesEraVM(char *linkerSymbolNames[],
* to the object names in the YUL layout.
* On success, outBuffers[0] will contain the deploy bytecode and outBuffers[1]
* the runtime bytecode.
* \p linkerSymbolNames has the same meaning as for LLVMLinkEraVM.
* If at least one resulting binary contains unresolved linker symbols,
* output binaries will be returned as ELF object files. See LLVMLinkEraVM
* description.
* In case of an error the function returns 'true' and the error message is
* passes in \p errorMessage. The message should be disposed by
* 'LLVMDisposeMessage'. */
LLVMBool LLVMLinkEVM(LLVMMemoryBufferRef *inBuffers, const char *inBuffersIDs[],
uint64_t numInBuffers, LLVMMemoryBufferRef outBuffers[2],
char **errorMessage);

const char *const *linkerSymbolNames,
const char linkerSymbolValues[][LINKER_SYMBOL_SIZE],
uint64_t numLinkerSymbols, char **errorMessage);
LLVM_C_EXTERN_C_END

#endif // LLD_C_LLDASLIBRARYC_H
149 changes: 139 additions & 10 deletions lld/lld-c/LLDAsLibraryC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -543,13 +543,12 @@ void LLVMGetUndefinedReferencesEraVM(LLVMMemoryBufferRef inBuffer,
ReferenceSymbolType::Factory);
}

/// Disposes an array with linker symbols returned by the
/// LLVMGetUndefinedReferencesEraVM().
void LLVMDisposeUndefinedReferencesEraVM(char *linkerSymbolNames[],
uint64_t numLinkerSymbols) {
for (unsigned idx = 0; idx < numLinkerSymbols; ++idx)
std::free(linkerSymbolNames[idx]);
std::free(linkerSymbolNames);
/// Disposes an array with symbols returned by the
/// LLVMGetUndefinedReferences* functions.
void LLVMDisposeUndefinedReferences(char *symbolNames[], uint64_t numSymbols) {
for (unsigned idx = 0; idx < numSymbols; ++idx)
std::free(symbolNames[idx]);
std::free(symbolNames);
}

//----------------------------------------------------------------------------//
Expand Down Expand Up @@ -681,9 +680,111 @@ SECTIONS {\n\
return script;
}

/// Create linker script as described in the createEraVMRelLinkerScript.
static std::string createEVMLinkerSymbolsDefinitions(
const char *const *linkerSymbolNames,
const char linkerSymbolValues[][LINKER_SYMBOL_SIZE],
uint64_t numLinkerSymbols) {
return createSymbolDefinitions(linkerSymbolNames, linkerSymbolValues,
numLinkerSymbols, nullptr, nullptr, 0);
}

/// Resolve undefined linker symbols in the ELF objec fille \inByffer and
/// and return obtained ELF object file.
static bool
resolveEVMLinkerSymbols(MemoryBufferRef inBuffer,
LLVMMemoryBufferRef *outBuffer,
const char *const *linkerSymbolNames,
const char linkerSymbolValues[][LINKER_SYMBOL_SIZE],
uint64_t numLinkerSymbols, char **errorMessage) {
SmallVector<MemoryBufferRef> localInMemBufRefs(2);
localInMemBufRefs[0] = inBuffer;
std::string linkerScript = createEVMLinkerSymbolsDefinitions(
linkerSymbolNames, linkerSymbolValues, numLinkerSymbols);

std::unique_ptr<MemoryBuffer> linkerScriptBuf =
MemoryBuffer::getMemBuffer(linkerScript, "1");

localInMemBufRefs[1] = linkerScriptBuf->getMemBufferRef();

SmallVector<const char *, 16> lldArgs;
lldArgs.push_back("ld.lld");
lldArgs.push_back("-T");
lldArgs.push_back("1");

lldArgs.push_back("--relocatable");

// Push the name of the input object file - '0'.
lldArgs.push_back("0");

SmallString<0> codeString;
raw_svector_ostream ostream(codeString);
SmallString<0> errorString;
raw_svector_ostream errorOstream(errorString);

// Lld-as-a-library is not thread safe, as it has a global state,
// so we need to protect lld from simultaneous access from different threads.
std::unique_lock<std::mutex> lock(lldMutex);
const lld::Result s =
lld::lldMainMemBuf(localInMemBufRefs, &ostream, lldArgs, outs(),
errorOstream, {{lld::Gnu, &lld::elf::linkMemBuf}});
lock.unlock();

bool Ret = !s.retCode && s.canRunAgain;
// For unification with other LLVM C-API functions, return 'true' in case of
// an error.
if (!Ret) {
*errorMessage = strdup(errorString.c_str());
return true;
}

StringRef data = ostream.str();
*outBuffer = LLVMCreateMemoryBufferWithMemoryRangeCopy(data.data(),
data.size(), "result");
return false;
}

/// Returns true if the \p inBuffer contains an ELF object file.
LLVMBool LLVMIsELFEVM(LLVMMemoryBufferRef inBuffer) {
Expected<ELFObjectFile<ELF32BE>> inBinaryOrErr =
ELFObjectFile<ELF32BE>::create(unwrap(inBuffer)->getMemBufferRef());
if (!inBinaryOrErr) {
handleAllErrors(inBinaryOrErr.takeError(), [](const ErrorInfoBase &EI) {});
return false;
}
return inBinaryOrErr.get().getArch() == Triple::evm;
}

/// Returns an array of undefined linker symbol names
/// of the ELF object passed in \p inBuffer.
void LLVMGetUndefinedReferencesEVM(LLVMMemoryBufferRef inBuffer,
char ***linkerSymbols,
uint64_t *numLinkerSymbols) {
if (linkerSymbols) {
*numLinkerSymbols = 0;
*linkerSymbols = nullptr;
}

if (!LLVMIsELFEVM(inBuffer))
return;

std::unique_ptr<Binary> inBinary =
cantFail(createBinary(unwrap(inBuffer)->getMemBufferRef()));
const auto *oFile = static_cast<const ObjectFile *>(inBinary.get());

if (linkerSymbols)
*linkerSymbols = LLVMGetUndefinedSymbols(oFile, numLinkerSymbols,
ReferenceSymbolType::Linker);
}

/// Links the deploy and runtime ELF object files using the information about
// dependencies.
LLVMBool LLVMLinkEVM(LLVMMemoryBufferRef inBuffers[],
const char *inBuffersIDs[], uint64_t numInBuffers,
LLVMMemoryBufferRef outBuffers[2], char **errorMessage) {
LLVMMemoryBufferRef outBuffers[2],
const char *const *linkerSymbolNames,
const char linkerSymbolValues[][LINKER_SYMBOL_SIZE],
uint64_t numLinkerSymbols, char **errorMessage) {
assert(numInBuffers > 1);
SmallVector<MemoryBufferRef> localInMemBufRefs(3);
SmallVector<std::unique_ptr<MemoryBuffer>> localInMemBufs(3);
Expand All @@ -699,8 +800,36 @@ LLVMBool LLVMLinkEVM(LLVMMemoryBufferRef inBuffers[],
localInMemBufRefs[idx] = localInMemBufs[idx]->getMemBufferRef();
}

std::string linkerScript = creteEVMLinkerScript(
ArrayRef(inBuffers, numInBuffers), ArrayRef(inBuffersIDs, numInBuffers));
bool shouldEmitRelocatable = std::any_of(
localInMemBufRefs.begin(), std::prev(localInMemBufRefs.end()),
[linkerSymbolNames, numLinkerSymbols](MemoryBufferRef bufRef) {
std::unique_ptr<Binary> InBinary = cantFail(createBinary(bufRef));
assert(InBinary->isObject());
return hasUndefinedReferenceSymbols(
*static_cast<ObjectFile *>(InBinary.get()), linkerSymbolNames,
numLinkerSymbols, nullptr, 0);
});

if (shouldEmitRelocatable) {
for (unsigned idx = 0; idx < 2; ++idx) {
LLVMMemoryBufferRef outBuf = nullptr;
char *errMsg = nullptr;
if (resolveEVMLinkerSymbols(localInMemBufRefs[idx], &outBuf,
linkerSymbolNames, linkerSymbolValues,
numLinkerSymbols, &errMsg)) {
*errorMessage = errMsg;
return true;
}
outBuffers[idx] = outBuf;
}
return false;
}

std::string linkerScript = createEVMLinkerSymbolsDefinitions(
linkerSymbolNames, linkerSymbolValues, numLinkerSymbols);

linkerScript += creteEVMLinkerScript(ArrayRef(inBuffers, numInBuffers),
ArrayRef(inBuffersIDs, numInBuffers));

std::unique_ptr<MemoryBuffer> scriptBuf =
MemoryBuffer::getMemBuffer(linkerScript, "script.x");
Expand Down
3 changes: 3 additions & 0 deletions lld/unittests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@ add_subdirectory(AsLibELF)
# EraVM local begin
add_subdirectory(EraVM)
# EraVM local End
# EVM local begin
add_subdirectory(EVM)
# EVM local End
12 changes: 12 additions & 0 deletions lld/unittests/EVM/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
add_lld_unittests(EVMLLDTests
LLDTest.cpp
)

target_link_libraries(EVMLLDTests
PRIVATE
lldCommon
lldC
lldELF
LLVMObjCopy
LLVMObjCopyC
)
Loading

0 comments on commit 09fc13b

Please sign in to comment.