diff --git a/src/coreclr/debug/datadescriptor-shared/contract-descriptor.c.in b/src/coreclr/debug/datadescriptor-shared/contract-descriptor.c.in index 83c9e91da314d7..4ad66d93300c04 100644 --- a/src/coreclr/debug/datadescriptor-shared/contract-descriptor.c.in +++ b/src/coreclr/debug/datadescriptor-shared/contract-descriptor.c.in @@ -30,13 +30,33 @@ struct ContractDescriptor CONTRACT_NAME = { // Due to linking order, the export may also need to be added to // the EXPORTED_FUNCTIONS setting for emcc. // See https://emscripten.org/docs/getting_started/FAQ.html#why-do-functions-in-my-c-c-source-code-vanish-when-i-compile-to-webassembly +#define _GETTER_NAME(exportname) Get ## exportname +#define GETTER_NAME(exportname) _GETTER_NAME(exportname) +#define _GETTER_NAME_STR(exportname) "Get" #exportname +#define GETTER_NAME_STR(exportname) _GETTER_NAME_STR(exportname) + #if defined(EXPORT_CONTRACT) && defined(__EMSCRIPTEN__) #include -#define _GETTER_NAME(exportname) Get ## exportname -#define GETTER_NAME(exportname) _GETTER_NAME(exportname) extern void* EMSCRIPTEN_KEEPALIVE GETTER_NAME(CONTRACT_NAME)() { return (void*)&CONTRACT_NAME; } -#endif // EXPORT_CONTRACT && __EMSCRIPTEN__ +#elif defined(EXPORT_CONTRACT) && defined(__wasm__) +// Non-emscripten wasm (e.g. wasi-sdk): export the getter with an explicit export name so the +// descriptor symbol is both reachable by a native host and, critically, kept alive by the linker. +// Without an export rooting it, --gc-sections removes the descriptor from the final module (the +// emscripten path above relies on EMSCRIPTEN_KEEPALIVE for the same reason). +// +// This intentionally keys on the compiler-provided __wasm__ rather than the CoreCLR TARGET_WASM +// define. datadescriptor-shared is compiled by multiple consumers and TARGET_WASM (set only by +// CoreCLR's configurecompiler.cmake, and gated on IGNORE_DEFAULT_TARGET_ARCH) is not guaranteed to +// reach every one of them. Since the whole point of this branch is to keep the descriptor alive on +// any wasm toolchain, it must depend on a macro that is always defined when targeting wasm; __wasm__ +// is set by the compiler itself for exactly that. +__attribute__((export_name(GETTER_NAME_STR(CONTRACT_NAME)))) +void* GETTER_NAME(CONTRACT_NAME)() +{ + return (void*)&CONTRACT_NAME; +} +#endif // EXPORT_CONTRACT && (__EMSCRIPTEN__ || __wasm__)