mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
317 lines
10 KiB
C++
317 lines
10 KiB
C++
/// \file LinkForTranslation.cpp
|
|
|
|
//
|
|
// This file is distributed under the MIT License. See LICENSE.md for details.
|
|
//
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "llvm/ADT/ArrayRef.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/Object/ELFObjectFile.h"
|
|
#include "llvm/Object/ObjectFile.h"
|
|
#include "llvm/Support/Debug.h"
|
|
#include "llvm/Support/Process.h"
|
|
#include "llvm/Support/Program.h"
|
|
#include "llvm/Support/Regex.h"
|
|
#include "llvm/Support/StringSaver.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
|
|
#include "revng/Model/Importer/Binary/Options.h"
|
|
#include "revng/Model/LoadModelPass.h"
|
|
#include "revng/Model/RawBinaryView.h"
|
|
#include "revng/Recompile/LinkForTranslation.h"
|
|
#include "revng/Support/Assert.h"
|
|
#include "revng/Support/PathList.h"
|
|
#include "revng/Support/ProgramRunner.h"
|
|
#include "revng/Support/ResourceFinder.h"
|
|
#include "revng/Support/TemporaryFile.h"
|
|
|
|
using namespace llvm;
|
|
using namespace llvm::sys;
|
|
|
|
static std::string linkFunctionArgument(llvm::StringRef Lib) {
|
|
auto LastSlash = Lib.rfind('/');
|
|
if (LastSlash != llvm::StringRef::npos)
|
|
Lib = Lib.drop_front(LastSlash + 1);
|
|
|
|
llvm::Regex Reg("^lib(.*).so(\\.[0-9]+)*$");
|
|
if (not Reg.match(Lib))
|
|
return "-l:" + Lib.str();
|
|
|
|
Lib = Lib.drop_front(3);
|
|
auto LastDot = Lib.find('.');
|
|
revng_assert(LastDot != llvm::StringRef::npos);
|
|
Lib = Lib.substr(0, LastDot);
|
|
|
|
return "-l" + Lib.str();
|
|
}
|
|
|
|
static std::vector<std::string>
|
|
defineProgramHeadersSymbols(RawBinaryView &BinaryView,
|
|
const MemoryBuffer &Data) {
|
|
using namespace llvm::object;
|
|
|
|
auto Process = [&BinaryView](auto &ELF) -> std::vector<std::string> {
|
|
auto Header = ELF->getELFFile().getHeader();
|
|
|
|
auto Offset = Header.e_phoff;
|
|
MetaAddress ProgramHeadersAddress = BinaryView.offsetToAddress(Offset);
|
|
revng_assert(ProgramHeadersAddress.isValid());
|
|
|
|
auto Address = Twine::utohexstr(ProgramHeadersAddress.address()).str();
|
|
return { ("--defsym=e_phnum=0x" + Twine(Header.e_phnum)).str(),
|
|
("--defsym=e_phentsize=0x" + Twine(Header.e_phentsize)).str(),
|
|
"--defsym=phdr_address=0x" + Address };
|
|
};
|
|
|
|
auto MaybeObject = ObjectFile::createELFObjectFile(Data);
|
|
revng_check(MaybeObject);
|
|
if (auto *ELF = dyn_cast<ELFObjectFile<ELF32LE>>(&**MaybeObject))
|
|
return Process(ELF);
|
|
else if (auto *ELF = dyn_cast<ELFObjectFile<ELF64LE>>(&**MaybeObject))
|
|
return Process(ELF);
|
|
else if (auto *ELF = dyn_cast<ELFObjectFile<ELF32BE>>(&**MaybeObject))
|
|
return Process(ELF);
|
|
else if (auto *ELF = dyn_cast<ELFObjectFile<ELF64BE>>(&**MaybeObject))
|
|
return Process(ELF);
|
|
else
|
|
revng_abort();
|
|
}
|
|
|
|
class Command {
|
|
public:
|
|
std::string CommandName;
|
|
std::vector<std::string> Arguments;
|
|
|
|
public:
|
|
Command(std::string CommandName) : CommandName(CommandName) {}
|
|
};
|
|
|
|
class CommandList {
|
|
private:
|
|
std::vector<Command> Commands;
|
|
std::vector<std::unique_ptr<TemporaryFile>> Temporaries;
|
|
|
|
public:
|
|
void run() const {
|
|
for (const Command &C : Commands) {
|
|
auto ExitCode = ::Runner.run(C.CommandName, C.Arguments);
|
|
revng_check(ExitCode == 0);
|
|
}
|
|
}
|
|
|
|
public:
|
|
void enqueueCommand(Command C) { Commands.push_back(std::move(C)); }
|
|
|
|
TemporaryFile &createTemporary(std::string Prefix, std::string Suffix) {
|
|
Temporaries.emplace_back(std::make_unique<TemporaryFile>(Prefix, Suffix));
|
|
return *Temporaries.back();
|
|
}
|
|
|
|
public:
|
|
static std::string shellEscape(StringRef String) {
|
|
std::string Result = "\"";
|
|
|
|
for (char C : String) {
|
|
if (C == '"')
|
|
Result += "'\"'\"'";
|
|
else
|
|
Result += C;
|
|
}
|
|
|
|
Result += "\"";
|
|
|
|
return Result;
|
|
}
|
|
};
|
|
|
|
template<typename T = std::string,
|
|
typename Container = std::initializer_list<T>>
|
|
void appendTo(Container &&Range, auto &Destination) {
|
|
for (auto &&Element : Range)
|
|
Destination.push_back(std::forward<decltype(Element)>(Element));
|
|
}
|
|
|
|
static CommandList linkingArgs(const model::Binary &Model,
|
|
llvm::StringRef InputBinary,
|
|
llvm::StringRef ObjectFile,
|
|
llvm::StringRef OutputBinary) {
|
|
CommandList Result;
|
|
|
|
auto UToHexStr = Twine::utohexstr;
|
|
|
|
const uint64_t PageSize = 4096;
|
|
|
|
TemporaryFile &LinkerOutput = Result.createTemporary("revng-link-for-"
|
|
"translation",
|
|
"");
|
|
|
|
auto MaybeBuffer = llvm::MemoryBuffer::getFileOrSTDIN(InputBinary);
|
|
revng_assert(MaybeBuffer);
|
|
llvm::MemoryBuffer &Buffer = **MaybeBuffer;
|
|
RawBinaryView BinaryView(Model, Buffer.getBuffer());
|
|
|
|
Command Linker("ld.bfd");
|
|
|
|
// Parse options from environment variable.
|
|
if (auto EnvValue = sys::Process::GetEnv("REVNG_TRANSLATE_LDFLAGS")) {
|
|
SmallVector<const char *, 20> ExtraArguments;
|
|
BumpPtrAllocator A;
|
|
StringSaver Saver(A);
|
|
cl::TokenizeGNUCommandLine(*EnvValue, Saver, ExtraArguments);
|
|
|
|
appendTo(ExtraArguments, Linker.Arguments);
|
|
}
|
|
|
|
// Disable PIE
|
|
appendTo({ "-no-pie" }, Linker.Arguments);
|
|
|
|
// Disable huge pages
|
|
appendTo({ "-z", ("max-page-size=" + Twine(PageSize)).str() },
|
|
Linker.Arguments);
|
|
|
|
// Output file
|
|
appendTo({ "-o", LinkerOutput.path().str() }, Linker.Arguments);
|
|
|
|
revng_assert(Model.Segments().size() > 0);
|
|
uint64_t Min = Model.Segments().begin()->StartAddress().address();
|
|
uint64_t Max = Model.Segments().begin()->endAddress().address();
|
|
|
|
// Link opening crt files
|
|
appendTo({ "-l:crt1.o", "-l:crti.o", "-l:crtbegin.o" }, Linker.Arguments);
|
|
|
|
Linker.Arguments.push_back(ObjectFile.str());
|
|
|
|
for (auto &[Segment, Data] : BinaryView.segments()) {
|
|
// Compute section name
|
|
std::string SectionName;
|
|
{
|
|
llvm::raw_string_ostream NameStream(SectionName);
|
|
NameStream << "segment-" << Segment.StartAddress().toString() << "-"
|
|
<< Segment.endAddress().toString();
|
|
}
|
|
|
|
TemporaryFile &RawSegment = Result.createTemporary("revng-link-for-"
|
|
"translation",
|
|
"raw");
|
|
|
|
Command DD("dd");
|
|
DD.Arguments = { { "status=none",
|
|
"bs=1",
|
|
("skip=" + Twine(Segment.StartOffset())).str(),
|
|
("if=" + InputBinary).str(),
|
|
("count=" + Twine(Segment.FileSize())).str(),
|
|
("of=" + RawSegment.path()).str() } };
|
|
Result.enqueueCommand(std::move(DD));
|
|
|
|
Command Truncate("truncate");
|
|
Truncate.Arguments = { { ("--size=" + Twine(Segment.VirtualSize())).str(),
|
|
RawSegment.path().str() } };
|
|
Result.enqueueCommand(std::move(Truncate));
|
|
|
|
Command ObjCopy("objcopy");
|
|
|
|
// Create an object file we can later link
|
|
TemporaryFile &SegmentELF = Result.createTemporary("revng-link-for-"
|
|
"translation",
|
|
"o");
|
|
|
|
std::string SectionFlags = "alloc";
|
|
if (not Segment.IsWriteable())
|
|
SectionFlags += ",readonly";
|
|
|
|
ObjCopy.Arguments = { "-Ibinary",
|
|
"-Oelf64-x86-64",
|
|
"--add-section=.note.GNU-stack=/dev/null",
|
|
"--rename-section=.data=." + SectionName,
|
|
"--set-section-flags=.data=" + SectionFlags,
|
|
RawSegment.path().str(),
|
|
SegmentELF.path().str() };
|
|
|
|
// Register the objcopy invocation
|
|
Result.enqueueCommand(ObjCopy);
|
|
|
|
Min = std::min(Min, Segment.StartAddress().address());
|
|
Max = std::max(Max, Segment.endAddress().address());
|
|
|
|
// Add to linker command line
|
|
Linker.Arguments.push_back(SegmentELF.path().str());
|
|
|
|
// Force section address at link-time
|
|
const auto &StartAddr = Segment.StartAddress().address();
|
|
Linker.Arguments.push_back((Twine("--section-start=.") + SectionName
|
|
+ Twine("=0x") + UToHexStr(StartAddr))
|
|
.str());
|
|
}
|
|
|
|
// Link C standard library
|
|
appendTo({ "-l:ld-linux-x86-64.so.2", "-lc", "-lm", "-lrt", "-lpthread" },
|
|
Linker.Arguments);
|
|
|
|
// Link additional libraries needed by helpers
|
|
appendTo({ "-lgcc", "-lz", "-lunwind" }, Linker.Arguments);
|
|
|
|
// Link C++ standard library, we use its personality function
|
|
appendTo({ "-lstdc++" }, Linker.Arguments);
|
|
|
|
appendTo({ "-dynamic-linker", "/lib64/ld-linux-x86-64.so.2" },
|
|
Linker.Arguments);
|
|
|
|
// Link closing crt files
|
|
appendTo({ "-l:crtend.o", "-l:crtn.o" }, Linker.Arguments);
|
|
|
|
// Force text to start on the page after all the original program segments
|
|
uint64_t PageAddress = PageSize * ((Max + PageSize - 1) / PageSize);
|
|
auto HexPageAddress = UToHexStr(PageAddress).str();
|
|
Linker.Arguments.push_back("-Ttext-segment=0x" + HexPageAddress);
|
|
|
|
// Force a page before the lowest original address for the ELF header
|
|
auto Str = "--section-start=.elfheaderhelper=0x";
|
|
Linker.Arguments.push_back((Str + UToHexStr(Min - 1)).str());
|
|
|
|
// Link required dynamic libraries
|
|
Linker.Arguments.push_back("--no-as-needed");
|
|
for (const std::string &ImportedLibrary : Model.ImportedLibraries())
|
|
Linker.Arguments.push_back(linkFunctionArgument(ImportedLibrary));
|
|
Linker.Arguments.push_back("--as-needed");
|
|
|
|
// Define program headers-related symbols
|
|
llvm::copy(defineProgramHeadersSymbols(BinaryView, Buffer),
|
|
std::back_inserter(Linker.Arguments));
|
|
|
|
Result.enqueueCommand(std::move(Linker));
|
|
|
|
std::string MainExecutablePath = *revng::ResourceFinder.findFile("bin/revng");
|
|
Command MergeDynamic(MainExecutablePath);
|
|
|
|
// Invoke revng-merge-dynamic
|
|
MergeDynamic.Arguments = { "merge-dynamic",
|
|
LinkerOutput.path().str(),
|
|
InputBinary.str(),
|
|
OutputBinary.str() };
|
|
|
|
// Add base
|
|
if (BaseAddress.getNumOccurrences() > 0) {
|
|
MergeDynamic.Arguments.push_back("--base");
|
|
MergeDynamic.Arguments.push_back("0x" + UToHexStr(BaseAddress).str());
|
|
}
|
|
|
|
Result.enqueueCommand(std::move(MergeDynamic));
|
|
|
|
return Result;
|
|
}
|
|
|
|
void linkForTranslation(const model::Binary &Model,
|
|
llvm::StringRef InputBinary,
|
|
llvm::StringRef ObjectFile,
|
|
llvm::StringRef OutputBinary) {
|
|
CommandList Commands = linkingArgs(Model,
|
|
InputBinary,
|
|
ObjectFile,
|
|
OutputBinary);
|
|
Commands.run();
|
|
}
|