Thunks/gen: Support guest-side symbol tables and custom host symbol loaders

This commit is contained in:
Tony Wasserka 2021-12-10 11:24:58 +01:00
parent 4a848d7202
commit 3a2f9a4d46
2 changed files with 88 additions and 3 deletions

View File

@ -82,16 +82,36 @@ struct ThunkedAPIFunction : FunctionParams {
clang::QualType return_type;
// name of the function to load the native host symbol with
std::string host_loader;
// If true, no guest-side implementation of this function will be autogenerated
bool custom_guest_impl;
bool is_variadic;
// Index of the symbol table to store this export in (see guest_symtables).
// If empty, a library export is created, otherwise the function is entered into a function pointer array
std::optional<std::size_t> symtable_namespace;
};
static std::vector<ThunkedFunction> thunks;
static std::vector<ThunkedAPIFunction> thunked_api;
static std::optional<unsigned> lib_version;
struct NamespaceInfo {
std::string name;
// Function to load native host library functions with.
// This function must be defined manually with the signature "void* func(void*, const char*)"
std::string host_loader;
bool generate_guest_symtable;
};
// List of namespaces with a non-specialized fex_gen_config definition (including the global namespace, represented with an empty name)
static std::vector<NamespaceInfo> namespaces;
class ASTVisitor : public clang::RecursiveASTVisitor<ASTVisitor> {
clang::ASTContext& context;
@ -103,6 +123,8 @@ class ASTVisitor : public clang::RecursiveASTVisitor<ASTVisitor> {
struct NamespaceAnnotations {
std::optional<unsigned> version;
std::optional<std::string> load_host_endpoint_via;
bool generate_guest_symtable = false;
};
struct Annotations {
@ -122,9 +144,28 @@ class ASTVisitor : public clang::RecursiveASTVisitor<ASTVisitor> {
NamespaceAnnotations ret;
for (const clang::CXXBaseSpecifier& base : decl->bases()) {
auto annotation = base.getType().getAsString();
if (annotation == "fexgen::generate_guest_symtable") {
ret.generate_guest_symtable = true;
} else {
throw Error(base.getSourceRange().getBegin(), "Unknown namespace annotation");
}
}
for (const clang::FieldDecl* field : decl->fields()) {
auto name = field->getNameAsString();
if (name == "version") {
if (name == "load_host_endpoint_via") {
auto loader_function_expr = field->getInClassInitializer()->IgnoreCasts();
auto loader_function_str = llvm::dyn_cast_or_null<clang::StringLiteral>(loader_function_expr);
if (loader_function_expr && !loader_function_str) {
throw Error(loader_function_expr->getBeginLoc(),
"Must initialize load_host_endpoint_via with a string");
}
if (loader_function_str) {
ret.load_host_endpoint_via = loader_function_str->getString();
}
} else if (name == "version") {
auto initializer = field->getInClassInitializer()->IgnoreCasts();
auto version_literal = llvm::dyn_cast_or_null<clang::IntegerLiteral>(initializer);
if (!initializer || !version_literal) {
@ -194,7 +235,16 @@ public:
}
auto annotations = GetNamespaceAnnotations(decl->getTemplatedDecl());
auto namespace_decl = llvm::dyn_cast<clang::NamespaceDecl>(decl->getDeclContext());
namespaces.push_back({ namespace_decl ? namespace_decl->getNameAsString() : "",
annotations.load_host_endpoint_via.value_or(""),
annotations.generate_guest_symtable });
if (annotations.version) {
if (namespace_decl) {
throw Error(decl->getBeginLoc(), "Library version must be defined in the global namespace");
}
lib_version = annotations.version;
}
@ -216,6 +266,15 @@ public:
throw Error(decl->getBeginLoc(), "fex_gen_config may not be partially specialized\n");
}
std::string namespace_name;
if (auto namespace_decl = llvm::dyn_cast<clang::NamespaceDecl>(decl->getDeclContext())) {
namespace_name = namespace_decl->getNameAsString();
}
const auto namespace_idx = std::distance( namespaces.begin(),
std::find_if( namespaces.begin(), namespaces.end(),
[&](auto& info) { return info.name == namespace_name; }));
const NamespaceInfo& namespace_info = namespaces[namespace_idx];
const auto& template_args = decl->getTemplateArgs();
assert(template_args.size() == 1);
@ -272,8 +331,13 @@ public:
const bool has_nonstub_callbacks = std::any_of(data.callbacks.begin(), data.callbacks.end(),
[](auto& cb) { return !cb.second.is_stub && !cb.second.is_guest; });
thunked_api.push_back(ThunkedAPIFunction { (const FunctionParams&)data, data.function_name, data.return_type,
namespace_info.host_loader.empty() ? "dlsym" : namespace_info.host_loader,
has_nonstub_callbacks || data.is_variadic,
data.is_variadic });
data.is_variadic,
std::nullopt });
if (namespace_info.generate_guest_symtable) {
thunked_api.back().symtable_namespace = namespace_idx;
}
if (data.is_variadic) {
if (!annotations.uniform_va_type) {
@ -313,6 +377,7 @@ FrontendAction::FrontendAction(const std::string& libname_, const OutputFilename
: libname(libname_), output_filenames(output_filenames_) {
thunks.clear();
thunked_api.clear();
namespaces.clear();
lib_version = std::nullopt;
}
@ -419,6 +484,24 @@ void FrontendAction::EndSourceFileAction() {
}
file << ") -> " << data.return_type.getAsString() << ";\n";
}
for (std::size_t namespace_idx = 0; namespace_idx < namespaces.size(); ++namespace_idx) {
bool empty = true;
for (auto& symbol : thunked_api) {
if (symbol.symtable_namespace == namespace_idx) {
if (empty) {
file << "static struct { const char* name; void (*fn)(); } " << namespaces[namespace_idx].name << "_symtable[] = {\n";
empty = false;
}
file << " { \"" << symbol.function_name << "\", (void(*)())&" << symbol.function_name << " },\n";
}
}
if (!empty) {
file << " { nullptr, nullptr }\n";
file << "};\n";
}
}
file << "}\n";
}
@ -547,7 +630,7 @@ void FrontendAction::EndSourceFileAction() {
file << " if (!fexldr_ptr_" << libname << "_so) { return false; }\n\n";
for (auto& import : thunked_api) {
file << " (void*&)fexldr_ptr_" << libname << "_" << import.function_name << " = dlsym(fexldr_ptr_" << libname << "_so, \"" << import.function_name << "\");\n";
file << " (void*&)fexldr_ptr_" << libname << "_" << import.function_name << " = " << import.host_loader << "(fexldr_ptr_" << libname << "_so, \"" << import.function_name << "\");\n";
}
file << " return true;\n";
file << "}\n";

View File

@ -2,6 +2,8 @@ namespace fexgen {
struct returns_guest_pointer {};
struct custom_host_impl {};
struct generate_guest_symtable {};
struct callback_annotation_base {
// Prevent annotating multiple callback strategies
bool prevent_multiple;