llvm-project/lld/lib/ReaderWriter/PECOFF/EdataPass.cpp

230 lines
7.8 KiB
C++

//===- lib/ReaderWriter/PECOFF/EdataPass.cpp ------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "Pass.h"
#include "EdataPass.h"
#include "lld/Core/File.h"
#include "lld/Core/Pass.h"
#include "lld/Core/Simple.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Path.h"
#include <climits>
#include <ctime>
#include <utility>
using lld::pecoff::edata::EdataAtom;
using lld::pecoff::edata::TableEntry;
using llvm::object::export_address_table_entry;
using llvm::object::export_directory_table_entry;
namespace lld {
namespace pecoff {
typedef PECOFFLinkingContext::ExportDesc ExportDesc;
// dedupExports removes duplicate export entries. If two exports are
// referring the same symbol, they are considered duplicates.
// This could happen if the same symbol name is specified as an argument
// to /export more than once, or an unmangled and mangled name of the
// same symbol are given to /export. In the latter case, we choose
// unmangled (shorter) name.
static void dedupExports(PECOFFLinkingContext &ctx) {
std::vector<ExportDesc> &exports = ctx.getDllExports();
// Pass 1: find duplicate entries
std::set<const ExportDesc *> dup;
std::map<StringRef, ExportDesc *> map;
for (ExportDesc &exp : exports) {
if (!exp.externalName.empty())
continue;
StringRef symbol = exp.getRealName();
auto it = map.find(symbol);
if (it == map.end()) {
map[symbol] = &exp;
} else if (symbol.size() < it->second->getRealName().size()) {
map[symbol] = &exp;
dup.insert(it->second);
} else {
dup.insert(&exp);
}
}
// Pass 2: remove duplicate entries
auto pred = [&](const ExportDesc &exp) {
return dup.count(&exp) == 1;
};
exports.erase(std::remove_if(exports.begin(), exports.end(), pred),
exports.end());
}
static void assignOrdinals(PECOFFLinkingContext &ctx) {
std::vector<ExportDesc> &exports = ctx.getDllExports();
int maxOrdinal = -1;
for (ExportDesc &desc : exports)
maxOrdinal = std::max(maxOrdinal, desc.ordinal);
std::sort(exports.begin(), exports.end(),
[](const ExportDesc &a, const ExportDesc &b) {
return a.getExternalName().compare(b.getExternalName()) < 0;
});
int nextOrdinal = (maxOrdinal == -1) ? 1 : (maxOrdinal + 1);
for (ExportDesc &desc : exports)
if (desc.ordinal == -1)
desc.ordinal = nextOrdinal++;
}
static bool getExportedAtoms(PECOFFLinkingContext &ctx, SimpleFile *file,
std::vector<TableEntry> &ret) {
std::map<StringRef, const DefinedAtom *> definedAtoms;
for (const DefinedAtom *atom : file->defined())
definedAtoms[atom->name()] = atom;
for (PECOFFLinkingContext::ExportDesc &desc : ctx.getDllExports()) {
auto it = definedAtoms.find(desc.getRealName());
if (it == definedAtoms.end()) {
llvm::errs() << "Symbol <" << desc.name
<< "> is exported but not defined.\n";
return false;
}
const DefinedAtom *atom = it->second;
// One can export a symbol with a different name than the symbol
// name used in DLL. If such name is specified, use it in the
// .edata section.
ret.push_back(TableEntry(ctx.undecorateSymbol(desc.getExternalName()),
desc.ordinal, atom, desc.noname));
}
std::sort(ret.begin(), ret.end(),
[](const TableEntry &a, const TableEntry &b) {
return a.exportName.compare(b.exportName) < 0;
});
return true;
}
static std::pair<int, int> getOrdinalBase(std::vector<TableEntry> &entries) {
int ordinalBase = INT_MAX;
int maxOrdinal = -1;
for (TableEntry &e : entries) {
ordinalBase = std::min(ordinalBase, e.ordinal);
maxOrdinal = std::max(maxOrdinal, e.ordinal);
}
return std::pair<int, int>(ordinalBase, maxOrdinal);
}
edata::EdataAtom *
EdataPass::createAddressTable(const std::vector<TableEntry> &entries,
int ordinalBase, int maxOrdinal) {
EdataAtom *addressTable =
new (_alloc) EdataAtom(_file, sizeof(export_address_table_entry) *
(maxOrdinal - ordinalBase + 1));
for (const TableEntry &e : entries) {
int index = e.ordinal - ordinalBase;
size_t offset = index * sizeof(export_address_table_entry);
addDir32NBReloc(addressTable, e.atom, _ctx.getMachineType(), offset);
}
return addressTable;
}
edata::EdataAtom *
EdataPass::createNamePointerTable(const PECOFFLinkingContext &ctx,
const std::vector<TableEntry> &entries,
SimpleFile *file) {
EdataAtom *table =
new (_alloc) EdataAtom(_file, sizeof(uint32_t) * entries.size());
size_t offset = 0;
for (const TableEntry &e : entries) {
auto *stringAtom = new (_alloc) COFFStringAtom(
_file, _stringOrdinal++, ".edata", e.exportName);
file->addAtom(*stringAtom);
addDir32NBReloc(table, stringAtom, _ctx.getMachineType(), offset);
offset += sizeof(uint32_t);
}
return table;
}
edata::EdataAtom *EdataPass::createExportDirectoryTable(
const std::vector<edata::TableEntry> &namedEntries, int ordinalBase,
int maxOrdinal) {
EdataAtom *ret =
new (_alloc) EdataAtom(_file, sizeof(export_directory_table_entry));
auto *data = ret->getContents<export_directory_table_entry>();
data->TimeDateStamp = time(nullptr);
data->OrdinalBase = ordinalBase;
data->AddressTableEntries = maxOrdinal - ordinalBase + 1;
data->NumberOfNamePointers = namedEntries.size();
return ret;
}
edata::EdataAtom *
EdataPass::createOrdinalTable(const std::vector<TableEntry> &entries,
int ordinalBase) {
EdataAtom *ret =
new (_alloc) EdataAtom(_file, sizeof(uint16_t) * entries.size());
uint16_t *data = ret->getContents<uint16_t>();
int i = 0;
for (const TableEntry &e : entries)
data[i++] = e.ordinal - ordinalBase;
return ret;
}
std::error_code EdataPass::perform(std::unique_ptr<SimpleFile> &file) {
dedupExports(_ctx);
assignOrdinals(_ctx);
std::vector<TableEntry> entries;
if (!getExportedAtoms(_ctx, file.get(), entries))
return std::error_code();
if (entries.empty())
return std::error_code();
int ordinalBase, maxOrdinal;
std::tie(ordinalBase, maxOrdinal) = getOrdinalBase(entries);
std::vector<TableEntry> namedEntries;
for (TableEntry &e : entries)
if (!e.noname)
namedEntries.push_back(e);
EdataAtom *table =
createExportDirectoryTable(namedEntries, ordinalBase, maxOrdinal);
file->addAtom(*table);
COFFStringAtom *dllName =
new (_alloc) COFFStringAtom(_file, _stringOrdinal++, ".edata",
llvm::sys::path::filename(_ctx.outputPath()));
file->addAtom(*dllName);
addDir32NBReloc(table, dllName, _ctx.getMachineType(),
offsetof(export_directory_table_entry, NameRVA));
EdataAtom *addressTable =
createAddressTable(entries, ordinalBase, maxOrdinal);
file->addAtom(*addressTable);
addDir32NBReloc(
table, addressTable, _ctx.getMachineType(),
offsetof(export_directory_table_entry, ExportAddressTableRVA));
EdataAtom *namePointerTable =
createNamePointerTable(_ctx, namedEntries, file.get());
file->addAtom(*namePointerTable);
addDir32NBReloc(table, namePointerTable, _ctx.getMachineType(),
offsetof(export_directory_table_entry, NamePointerRVA));
EdataAtom *ordinalTable = createOrdinalTable(namedEntries, ordinalBase);
file->addAtom(*ordinalTable);
addDir32NBReloc(table, ordinalTable, _ctx.getMachineType(),
offsetof(export_directory_table_entry, OrdinalTableRVA));
return std::error_code();
}
} // namespace pecoff
} // namespace lld