From f0e6b88cfd4afc3ee3b6f5ccb8b4c615bda24078 Mon Sep 17 00:00:00 2001 From: Luke Street Date: Tue, 28 Jul 2026 19:26:53 -0600 Subject: [PATCH] GCC mod section fix --- sdk/include/mods/hook.hpp | 9 +++++++++ sdk/include/mods/meta.hpp | 42 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/sdk/include/mods/hook.hpp b/sdk/include/mods/hook.hpp index 07999da84c..a8d91ab68c 100644 --- a/sdk/include/mods/hook.hpp +++ b/sdk/include/mods/hook.hpp @@ -139,6 +139,14 @@ struct NamedHook : HookImpl, R, A...> {}; * leading underscore) or the demangled qualified display name; overloaded display names are * ambiguous and need the mangled form. */ +#if defined(__GNUC__) && !defined(__clang__) && defined(__ELF__) +#define DEFINE_HOOK(target, alias) \ + MOD_META_RECORD static constinit auto mod_meta_hook_##alias = \ + ::mods::detail::make_local_hook_record<(target), ::mods::FixedString{#target}>(); \ + struct alias : ::mods::Hook<(target)> { \ + static void* resolved_target() { return mod_meta_hook_##alias.resolved; } \ + } +#else #define DEFINE_HOOK(target, alias) \ [[maybe_unused]] static const void* const mod_meta_hook_##alias = \ &::mods::detail::HookRecordFor<(target), ::mods::FixedString{#target}>::Holder::record; \ @@ -148,6 +156,7 @@ struct NamedHook : HookImpl, R, A...> {}; ::mods::FixedString{#target}>::Holder::record.resolved; \ } \ } +#endif #define DEFINE_HOOK_SYMBOL(name, sig, alias) \ MOD_META_RECORD static constinit auto mod_meta_hook_##alias = \ diff --git a/sdk/include/mods/meta.hpp b/sdk/include/mods/meta.hpp index 3c6223e6fb..6b1de96ad8 100644 --- a/sdk/include/mods/meta.hpp +++ b/sdk/include/mods/meta.hpp @@ -241,6 +241,48 @@ consteval auto make_hook_mem_names() { return r; } +#if defined(__GNUC__) && !defined(__clang__) && defined(__ELF__) +/* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=41091 prevents inline static template members from + * sharing an explicit ELF section with ordinary variables. GCC can instead constant-evaluate a + * file-local record at each DEFINE_HOOK. */ +template +void materialize_hook_mem(unsigned char* outPmf) { + const auto target = Target; + std::memcpy(outPmf, &target, sizeof(target)); +} + +template +consteval auto make_local_hook_record() { + using F = decltype(Target); + if constexpr (std::is_member_function_pointer_v) { + constexpr auto names = make_hook_mem_names(); + static_assert(sizeof(F) <= MOD_META_HOOK_MEM_EXT_CAPACITY, + "unsupported pointer-to-member representation"); + if constexpr (sizeof(F) > MOD_META_HOOK_MEM_CAPACITY) { + HookMemExtRecord record = { + {sizeof(HookMemExtRecord), MOD_META_HOOK_MEM_EXT, 0}, sizeof(F), + materialize_hook_mem, nullptr, {}}; + for (size_t i = 0; i < names.len; ++i) { + record.names[i] = names.chars[i]; + } + return record; + } else { + HookMemRecord record = { + {sizeof(HookMemRecord), MOD_META_HOOK_MEM, 0}, 0, {Target}, nullptr, + {}}; + for (size_t i = 0; i < names.len; ++i) { + record.names[i] = names.chars[i]; + } + return record; + } + } else { + static_assert(std::is_pointer_v && std::is_function_v>, + "hook target must be a function or member function"); + return HookFnRecord{{sizeof(HookFnRecord), MOD_META_HOOK_FN, 0}, 0, Target, nullptr}; + } +} +#endif + /* * MSVC constant-evaluates a compact pointer-to-member only when every other operand in the * initializer is a literal: no consteval calls, constexpr-object copies, or default member