Symbol Visibility Migration
Table of Contents
- Problem
- Solution
- Approach
- Component checklist
- Foundation layer
- Infrastructure layer
- Variability
- Domain — reference data
- Domain — IAM
- Domain — FpML / ORE
- Domain — trading
- Domain — market data
- Domain — analytics
- Domain — data quality
- Domain — reporting
- Domain — scheduler
- Domain — workflow
- Domain — synthetic
- Domain — compute
- Domain — controller
- Domain — assets
- Application — HTTP
- Application — Qt (already done)
- Application — Qt plugins
- Final step — remove global flag
- Success criteria
Problem
The root CMakeLists.txt sets CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON, which
causes every symbol compiled into any shared library to appear in the Windows
PE export table. As the codebase grows this eventually exceeds the PE
hard limit of 65 535 named exports per DLL.
The immediate failure (2026-05-13) is ores.refdata.core.dll, which reached
73 721 exported symbols after the convention-types work (PRs #728, #733, #734).
The Linux and macOS builds are unaffected by the symbol count but will benefit
from the resulting ABI hygiene.
Solution
Apply explicit visibility annotations to every shared library following the
pattern already established in ores.qt.api (see Shared Library Symbol
Visibility):
- Each library gains an
export.hppusingboost/config.hppmacros. - Public types are annotated with
ORES_X_COMPONENT_EXPORT. -fvisibility=hiddenis set per-target on GCC/Clang.WINDOWS_EXPORT_ALL_SYMBOLS OFFis set on the target on Windows.CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ONis removed from the rootCMakeLists.txtonce all libraries carry annotations.
Approach
Each component is migrated independently:
- Add
include/ores.X.COMPONENT/export.hpp. - Annotate every exported class and free function in public headers.
- Update
src/CMakeLists.txt: add compile definition, visibility flags, andWINDOWS_EXPORT_ALL_SYMBOLS OFF. - Build the target locally:
make -C build/output/linux-clang-debug-make -j$(nproc) ores.X.COMPONENT.lib(or equivalent target name). - Run the component's tests.
- Move to the next component.
Deliver as a single PR. The PR description documents which components were migrated and what changed. After the PR is merged, perform a full manual bootstrap to verify the complete system runs correctly.
Per-CMakeLists.txt template
# In src/CMakeLists.txt, after add_library(...)
# Explicit symbol visibility — only exported types are visible to consumers.
target_compile_definitions(${lib_target_name} PRIVATE
ORES_X_COMPONENT_LIBRARY)
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
target_compile_options(${lib_target_name} PRIVATE
-fvisibility=hidden -fvisibility-inlines-hidden)
endif()
if(WIN32)
set_target_properties(${lib_target_name} PROPERTIES
WINDOWS_EXPORT_ALL_SYMBOLS OFF)
endif()
Export header template
Each library gets include/ores.X.COMPONENT/export.hpp:
#pragma once #include <boost/config.hpp> #ifdef ORES_X_COMPONENT_LIBRARY # define ORES_X_COMPONENT_EXPORT BOOST_SYMBOL_EXPORT #else # define ORES_X_COMPONENT_EXPORT BOOST_SYMBOL_IMPORT #endif
What to annotate
| Library type | Annotate |
|---|---|
ores.X.api |
All public structs, enums, free functions, classes |
ores.X.core |
Registration entry point only (register_message_handlers) |
| Qt plugin DLL | Nothing explicit; Qt macro handles plugin factory |
| Infrastructure / Foundation | All public types and functions |
Component checklist
Components are listed in bottom-up build order. Tick each item as it is completed.
Foundation layer
These libraries have no ores dependencies; annotation effort is low.
[ ]ores.utility→ export macroORES_UTILITY_EXPORT[ ]ores.platform→ export macroORES_PLATFORM_EXPORT[ ]ores.logging→ export macroORES_LOGGING_EXPORT[ ]ores.telemetry→ export macroORES_TELEMETRY_EXPORT[ ]ores.database→ export macroORES_DATABASE_EXPORT[ ]ores.security→ export macroORES_SECURITY_EXPORT[ ]ores.geo→ export macroORES_GEO_EXPORT
Infrastructure layer
[ ]ores.nats→ export macroORES_NATS_EXPORT[ ]ores.eventing→ export macroORES_EVENTING_EXPORT[ ]ores.testing→ export macroORES_TESTING_EXPORT[ ]ores.service→ export macroORES_SERVICE_EXPORT[ ]ores.storage→ export macroORES_STORAGE_EXPORT[ ]ores.connections→ export macroORES_CONNECTIONS_EXPORT[ ]ores.http.api→ export macroORES_HTTP_API_EXPORT[ ]ores.telemetry.database→ export macroORES_TELEMETRY_DATABASE_EXPORT
Variability
[ ]ores.variability.api→ export macroORES_VARIABILITY_API_EXPORT[ ]ores.variability.core→ export macroORES_VARIABILITY_CORE_EXPORT(registration only)
Domain — reference data
[ ]ores.refdata.api→ export macroORES_REFDATA_API_EXPORT(all domain/messaging/generator types)[ ]ores.refdata.core→ export macroORES_REFDATA_CORE_EXPORT(register_message_handlersonly)Consumers to switch from
core→api:ores.qt.analytics,ores.qt.refdata,ores.qt,ores.qt.mktdata,ores.qt.party,ores.qt.trading. Consumers that use core internals directly (ores.http.core,ores.synthetic.core,ores.wt.service,ores.iam.core) are addressed separately in their own steps below.
Domain — IAM
[ ]ores.iam.api→ export macroORES_IAM_API_EXPORT(all public types)[ ]ores.iam.client→ export macroORES_IAM_CLIENT_EXPORT[ ]ores.iam.core→ export macroORES_IAM_CORE_EXPORT(registration only)ores.iam.corecurrently includesparty_repository.hppfromores.refdata.core. Before hiding refdata.core internals, audit whether the IAM handlers should call the refdata service over NATS instead of accessing the repository directly.
Domain — FpML / ORE
[ ]ores.fpml→ export macroORES_FPML_EXPORT[ ]ores.ore.api→ export macroORES_ORE_API_EXPORT[ ]ores.ore→ export macroORES_ORE_EXPORT
Domain — trading
[ ]ores.trading.api→ export macroORES_TRADING_API_EXPORT[ ]ores.trading.core→ already STATIC on Windows; add-fvisibility=hiddenon GCC/Clang and annotate the registration entry point.
Domain — market data
[ ]ores.marketdata.api→ export macroORES_MARKETDATA_API_EXPORT[ ]ores.marketdata.core→ export macroORES_MARKETDATA_CORE_EXPORT(registration only)
Domain — analytics
[ ]ores.analytics.api→ export macroORES_ANALYTICS_API_EXPORT[ ]ores.analytics.core→ export macroORES_ANALYTICS_CORE_EXPORT(registration only)
Domain — data quality
[ ]ores.dq.api→ export macroORES_DQ_API_EXPORT[ ]ores.dq.core→ export macroORES_DQ_CORE_EXPORT(registration only)
Domain — reporting
[ ]ores.reporting.api→ export macroORES_REPORTING_API_EXPORT[ ]ores.reporting.core→ export macroORES_REPORTING_CORE_EXPORT(registration only)
Domain — scheduler
[ ]ores.scheduler.api→ export macroORES_SCHEDULER_API_EXPORT[ ]ores.scheduler.core→ export macroORES_SCHEDULER_CORE_EXPORT(registration only)
Domain — workflow
[ ]ores.workflow.api→ export macroORES_WORKFLOW_API_EXPORT[ ]ores.workflow→ export macroORES_WORKFLOW_EXPORT(registration only)
Domain — synthetic
[ ]ores.synthetic.api→ export macroORES_SYNTHETIC_API_EXPORT[ ]ores.synthetic.core→ export macroORES_SYNTHETIC_CORE_EXPORT(registration only)ores.synthetic.corecurrently usesparty_mapper.hppfromores.refdata.coredirectly. Evaluate whether synthetic data generation should call the refdata service instead.
Domain — compute
[ ]ores.compute.api→ export macroORES_COMPUTE_API_EXPORT[ ]ores.compute.core→ export macroORES_COMPUTE_CORE_EXPORT(registration only)
Domain — controller
[ ]ores.controller.api→ export macroORES_CONTROLLER_API_EXPORT[ ]ores.controller.core→ export macroORES_CONTROLLER_CORE_EXPORT(registration only)
Domain — assets
[ ]ores.assets.api→ export macroORES_ASSETS_API_EXPORT[ ]ores.assets.core→ export macroORES_ASSETS_CORE_EXPORT(registration only)
Application — HTTP
[ ]ores.http.core→ export macroORES_HTTP_CORE_EXPORTAudited uses of
ores.refdata.core(currency_service): consider routing through NATS or accept the core dependency as intentional.
Application — Qt (already done)
[X]ores.qt.api— already usesORES_QT_APIwithboost/config.hpp.
Application — Qt plugins
For each plugin DLL, add -fvisibility=hidden and
WINDOWS_EXPORT_ALL_SYMBOLS OFF. Qt plugin factory export is handled by
Q_PLUGIN_METADATA / Q_EXPORT_PLUGIN2; no manual annotation needed.
[ ]ores.qt.admin[ ]ores.qt.analytics[ ]ores.qt.compute[ ]ores.qt.data_transfer[ ]ores.qt.mktdata[ ]ores.qt.party[ ]ores.qt.refdata[ ]ores.qt.scheduler[ ]ores.qt.trading[ ]ores.qt.workflow
Final step — remove global flag
[ ]Deleteset(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)from rootCMakeLists.txt.[ ]Full local build (all platforms).[ ]All tests green.[ ]Manual full bootstrap.
Success criteria
- Windows CI green; no DLL exceeds 65 535 exported symbols.
- No library exports internal types (repositories, mappers, entities, services).
- Consumers of
ores.X.coreare limited to service executables,ores.cli, and test executables, except where an explicit architectural decision permits cross-boundary access. CMAKE_WINDOWS_EXPORT_ALL_SYMBOLSis absent from the build system.- Full bootstrap passes manually after the PR is merged.