mirror of
https://github.com/FEX-Emu/FEX.git
synced 2025-02-03 04:53:00 +00:00
Thunks/gen: Support guest-side symbol tables and custom host symbol loaders
This commit is contained in:
parent
4a848d7202
commit
3a2f9a4d46
@ -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";
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user