Merge bitcoin/bitcoin#36285: refactor: Use static const over inline const to work around ld64 bug
What changed, and why it matters
This is a build-compatibility fix, not a security patch. It changes how some constant data is stored internally so that Apple's macOS linker (ld64) can build Bitcoin Core correctly. The change avoids a linker bug that caused build failures, but it does not fix any vulnerability that could be exploited by an attacker.
No security action needed. Treat as a normal build/maintenance fix. If backporting, include only for users building on macOS with the affected toolchain.
Security signals we found
No security-relevant code logic changed
Change is a linker bug workaround, not a vulnerability fix
Constants remain read-only; no new attack surface introduced
Binary size increases slightly due to duplicated static data
Evidence from the diff
The commit reverts some inline const variables back to static const to work around an ld64 linker bug (GitHub issue #36281). inline variables in C++17 are supposed to merge duplicate definitions across translation units, but ld64 mishandled the initialization dependencies, causing build issues. Using static const gives each translation unit its own copy, avoiding the linker problem at the cost of a ~70 kB binary size increase. The affected constants are test-only script fixtures and wallet flag string maps.
Changed components
src/test/util/script.hsrc/wallet/wallet.hInspect captured patch +8 / −8
### src/test/util/script.h
@@ -9,8 +9,8 @@
#include <script/script.h>
#include <script/verify_flags.h>
-inline const std::vector<uint8_t> WITNESS_STACK_ELEM_OP_TRUE{uint8_t{OP_TRUE}};
-inline const CScript P2WSH_OP_TRUE{
+static const std::vector<uint8_t> WITNESS_STACK_ELEM_OP_TRUE{uint8_t{OP_TRUE}};
+static const CScript P2WSH_OP_TRUE{
CScript{}
<< OP_0
<< ToByteVector([] {
@@ -19,17 +19,17 @@ inline const CScript P2WSH_OP_TRUE{
return hash;
}())};
-inline const std::vector<uint8_t> EMPTY{};
-inline const CScript P2WSH_EMPTY{
+static const std::vector<uint8_t> EMPTY{};
+static const CScript P2WSH_EMPTY{
CScript{}
<< OP_0
<< ToByteVector([] {
uint256 hash;
CSHA256().Write(EMPTY.data(), EMPTY.size()).Finalize(hash.begin());
return hash;
}())};
-inline const std::vector<std::vector<uint8_t>> P2WSH_EMPTY_TRUE_STACK{{static_cast<uint8_t>(OP_TRUE)}, {}};
-inline const std::vector<std::vector<uint8_t>> P2WSH_EMPTY_TWO_STACK{{static_cast<uint8_t>(OP_2)}, {}};
+static const std::vector<std::vector<uint8_t>> P2WSH_EMPTY_TRUE_STACK{{static_cast<uint8_t>(OP_TRUE)}, {}};
+static const std::vector<std::vector<uint8_t>> P2WSH_EMPTY_TWO_STACK{{static_cast<uint8_t>(OP_2)}, {}};
/** Flags that are not forbidden by an assert in script validation */
bool IsValidFlagCombination(script_verify_flags flags);
### src/wallet/wallet.h
@@ -162,7 +162,7 @@ inline constexpr uint64_t KNOWN_WALLET_FLAGS =
inline constexpr uint64_t MUTABLE_WALLET_FLAGS =
WALLET_FLAG_AVOID_REUSE;
-inline const std::map<WalletFlags, std::string> WALLET_FLAG_TO_STRING{
+static const std::map<WalletFlags, std::string> WALLET_FLAG_TO_STRING{
{WALLET_FLAG_AVOID_REUSE, "avoid_reuse"},
{WALLET_FLAG_BLANK_WALLET, "blank"},
{WALLET_FLAG_KEY_ORIGIN_METADATA, "key_origin_metadata"},
@@ -172,7 +172,7 @@ inline const std::map<WalletFlags, std::string> WALLET_FLAG_TO_STRING{
{WALLET_FLAG_EXTERNAL_SIGNER, "external_signer"}
};
-inline const std::map<std::string, WalletFlags> STRING_TO_WALLET_FLAG{
+static const std::map<std::string, WalletFlags> STRING_TO_WALLET_FLAG{
{WALLET_FLAG_TO_STRING.at(WALLET_FLAG_AVOID_REUSE), WALLET_FLAG_AVOID_REUSE},
{WALLET_FLAG_TO_STRING.at(WALLET_FLAG_BLANK_WALLET), WALLET_FLAG_BLANK_WALLET},
{WALLET_FLAG_TO_STRING.at(WALLET_FLAG_KEY_ORIGIN_METADATA), WALLET_FLAG_KEY_ORIGIN_METADATA},Why this scored 18/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.