diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7ecb0a47fc29684d604c0f82e275738e9a1e3906..94c6e21d77622c652de4704eb0c056dcaa1b28a2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -246,10 +246,18 @@ else()
     set_arch_target(ARB_CXXOPT_ARCH ${ARB_ARCH})
     set(ARB_CXX_FLAGS_TARGET_FULL ${ARB_CXX_FLAGS_TARGET} ${ARB_CXXOPT_ARCH})
 endif()
+
+# Compile with `-fvisibility=hidden` to ensure that the symbols of the generated
+# arbor static libraries are hidden from the dynamic symbol tables of any shared
+# libraries that link against them.
+list(APPEND ARB_CXX_FLAGS_TARGET_FULL
+            "$<$<BUILD_INTERFACE:$<COMPILE_LANGUAGE:CXX>>:-fvisibility=hidden>"
+            "$<$<BUILD_INTERFACE:$<COMPILE_LANGUAGE:CUDA>>:-Xcompiler=-fvisibility=hidden>")
 separate_arguments(ARB_CXX_FLAGS_TARGET_FULL)
 
 target_compile_options(arbor-private-deps INTERFACE ${ARB_CXX_FLAGS_TARGET_FULL})
 target_compile_options(arborenv-private-deps INTERFACE ${ARB_CXX_FLAGS_TARGET_FULL})
+target_compile_options(arborio-private-deps INTERFACE ${ARB_CXX_FLAGS_TARGET_FULL})
 
 # Profiling and test features
 #-----------------------------
diff --git a/mechanisms/generate_catalogue b/mechanisms/generate_catalogue
index 8e8df375cd9e3d45b3897b5f355907e9b5be9d7d..8f4b46bbc45e36a945e2fdc1eb7a74601ad85fe2 100755
--- a/mechanisms/generate_catalogue
+++ b/mechanisms/generate_catalogue
@@ -120,7 +120,7 @@ const mechanism_catalogue& global_${catalogue}_catalogue() {
 
 #ifdef STANDALONE
 extern "C" {
-    const void* get_catalogue() {
+    [[gnu::visibility("default")]] const void* get_catalogue() {
         static auto cat = arb::build_${catalogue}_catalogue();
         return (void*)&cat;
     }