Skip to content

Add emscripten guards to shared_library.c's dlopen path - #591

Merged
ahcorde merged 3 commits into
ros2:rollingfrom
Tobias-Fischer:fix/emscripten-shared-library-guards
Sep 15, 2026
Merged

ahcorde merged 3 commits into
ros2:rollingfrom
Tobias-Fischer:fix/emscripten-shared-library-guards

Conversation

@Tobias-Fischer

@Tobias-Fischer Tobias-Fischer commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Summary

Two related gaps in rcutils_load_shared_library() / rcutils_get_platform_library_name(), found while getting rcl_logging_implementation's dlopen-by-name backend selection working on emscripten-wasm32:

  • rcutils_get_platform_library_name() had no emscripten branch, so it always fell through with written == 0 ("failed to format library name"), regardless of which RCL_LOGGING_IMPLEMENTATION backend was requested. wasm32 side modules use the same lib<name>.so naming convention as Linux, so this reuses that branch.
  • The post-dlopen() path-resolution code took the _GNU_SOURCE branch (which emscripten's headers define) and called dlinfo(..., RTLD_DI_LINKMAP, ...). Emscripten's dlopen()/dlinfo() are a JS-backed shim, not glibc's, and don't support reading back a real struct link_map — so a successful dlopen() was getting treated as a failure once dlinfo() returned -1. The #else branch (reuse the path dlopen() was given) already covers this platform correctly, so this just excludes emscripten from the glibc-specific branch above it.

Both are narrow, additive #if/#elif guard changes — no behavior change on any existing platform.

Test plan

Verified end-to-end on a ROS 2 rolling + rmw_zenoh_pico + real-pthreads emscripten-wasm32 build (companion work in RoboStack/vinca#154 and RoboStack/ros-rolling#46): a wasm32 rclpy talker's logging initialization (rcl_logging_configurercl_logging_implementation → this dlopen path) now succeeds instead of aborting with "failed to load any logging implementations", regardless of which backend (rcl_logging_spdlog or rcl_logging_noop) is selected.

Full write-up

All the changes this required, across every repo, are documented together in Tobias-Fischer/ros2-emscripten-zenoh-demo — including a working rclc and rclpy browser demo verified end-to-end against a native zenohd router.

🤖 Generated with Claude Code

Tobias-Fischer added a commit to Tobias-Fischer/ros-rolling that referenced this pull request Sep 9, 2026
- Bump the vinca pin to 325d0af, which also sets
  CMAKE_SHARED_MODULE_CREATE_C/CXX_FLAGS (not just the SHARED-library
  variants) -- CMake's MODULE library type, used by pybind11_add_module()
  for every Python C extension (rclpy's own _rclpy_pybind11, and each
  message package's rosidl_generator_py typesupport accessor), was linking
  without USE_PTHREADS=1 even though its objects compiled with atomics
  fine, producing a load-time "mismatch in shared state of memory" next to
  the rest of a pthreads build. Invisible until something used
  pybind11_add_module() -- found getting a live rclpy demo running.

- Add two Emscripten guards to patch/ros-rolling-rcutils.emscripten.patch
  (upstreamed as ros2/rcutils#591) so rcl_logging_implementation's
  dlopen-by-name backend selection works: rcutils_get_platform_library_name()
  had no emscripten case (always "failed to format library name"), and the
  post-dlopen path resolution took a dlinfo(RTLD_DI_LINKMAP) branch that
  Emscripten's JS-backed dlopen doesn't support, treating a successful
  dlopen() as a failure.

- Document a known gap (see pixi.toml comment): rosidl_typesupport_
  microxrcedds_cpp's codegen doesn't handle service "_Event" messages, so
  action_msgs/lifecycle_msgs/rcl_interfaces/rosgraph_msgs/statistics_msgs/
  type_description_interfaces/micro_ros_msgs need a second, C-only rebuild
  pass rather than the global STATIC_ROSIDL_TYPESUPPORT_CPP override this
  task sets -- a real rclpy talker (not just rclc) verified end-to-end
  through this same rmw_zenoh_pico pipeline surfaced this.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@mjcarroll

Copy link
Copy Markdown
Member

Looks like AI was used here, but no AI attestation in the PR description, if you could update that please.

It also looks like DCO signoff wasn't done here either.

Two related gaps in rcutils_load_shared_library() / rcutils_get_platform_library_name(),
found while getting rcl_logging_implementation's dlopen-by-name backend
selection working on emscripten-wasm32:

- rcutils_get_platform_library_name() had no emscripten branch, so it always
  fell through with written == 0 ("failed to format library name"),
  regardless of which RCL_LOGGING_IMPLEMENTATION backend was requested.
  wasm32 side modules use the same "lib<name>.so" convention as Linux.

- The post-dlopen path-resolution code took the _GNU_SOURCE branch (which
  emscripten's headers define) and called dlinfo(..., RTLD_DI_LINKMAP, ...).
  Emscripten's dlopen()/dlinfo() are a JS-backed shim, not glibc's, and
  don't support reading back a real struct link_map -- so a successful
  dlopen() got treated as a failure once dlinfo() returned -1. The #else
  branch (reuse the path dlopen() was given) already covers this platform
  correctly.

Verified end-to-end on a ROS 2 rolling + rmw_zenoh_pico + real-pthreads
emscripten-wasm32 build: a wasm32 rclpy talker's logging initialization
(rcl_logging_configure -> rcl_logging_implementation -> this dlopen path)
now succeeds instead of aborting with "failed to load any logging
implementations".

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Tobias Fischer <info@tobiasfischer.info>
@Tobias-Fischer
Tobias-Fischer force-pushed the fix/emscripten-shared-library-guards branch from 595b55a to 2da7412 Compare September 10, 2026 23:10
@Tobias-Fischer

Copy link
Copy Markdown
Contributor Author

Looks like AI was used here, but no AI attestation in the PR description, if you could update that please.

It also looks like DCO signoff wasn't done here either.

Thanks for the review!
DCO done. Claude attribution is quite prominent in the PR description already?

@ahcorde

ahcorde commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Pulls: #591
Gist: https://gist.githubusercontent.com/ahcorde/94a734d3c584e0f191365371ac8a9ae3/raw/c9d704208e525b6b3a0bf5116c4b2a065bd60849/ros2.repos
BUILD args: --packages-above-and-dependencies rcutils
TEST args: --packages-above rcutils
ROS Distro: rolling
Job: ci_launcher
ci_launcher ran: https://ci.ros2.org/job/ci_launcher/20430

  • Linux Build Status
  • Linux-aarch64 Build Status
  • Linux-rhel Build Status
  • Windows Build Status

Signed-off-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
@ahcorde

ahcorde commented Sep 14, 2026

Copy link
Copy Markdown
Contributor
  • Linux Build Status
  • Linux-aarch64 Build Status
  • Linux-rhel Build Status
  • Windows Build Status

Signed-off-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
@ahcorde

ahcorde commented Sep 14, 2026

Copy link
Copy Markdown
Contributor
  • Linux Build Status
  • Linux-aarch64 Build Status
  • Linux-rhel Build Status
  • Windows Build Status

@ahcorde
ahcorde merged commit a2d8f50 into ros2:rolling Sep 15, 2026
2 checks passed
@ahcorde

ahcorde commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

@Mergifyio backport lyrical kilted jazzy humble

@mergify

mergify Bot commented Sep 15, 2026

Copy link
Copy Markdown

backport lyrical kilted jazzy humble

✅ Backports have been created

Details

Cherry-pick of a2d8f50 has failed:

On branch mergify/bp/humble/pr-591
Your branch is up to date with 'origin/humble'.

You are currently cherry-picking commit a2d8f50.
  (fix conflicts and run "git cherry-pick --continue")
  (use "git cherry-pick --skip" to skip this patch)
  (use "git cherry-pick --abort" to cancel the cherry-pick operation)

Unmerged paths:
  (use "git add <file>..." to mark resolution)
	both modified:   src/shared_library.c

no changes added to commit (use "git add" and/or "git commit -a")

To fix up this pull request, you can check it out locally. See documentation: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/checking-out-pull-requests-locally

ahcorde added a commit that referenced this pull request Sep 16, 2026
… (#601)

(cherry picked from commit a2d8f50)

Signed-off-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
Co-authored-by: Tobias Fischer <info@tobiasfischer.info>
Co-authored-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
ahcorde added a commit that referenced this pull request Sep 16, 2026
* fix: add emscripten guards to shared_library.c's dlopen path

Two related gaps in rcutils_load_shared_library() / rcutils_get_platform_library_name(),
found while getting rcl_logging_implementation's dlopen-by-name backend
selection working on emscripten-wasm32:

- rcutils_get_platform_library_name() had no emscripten branch, so it always
  fell through with written == 0 ("failed to format library name"),
  regardless of which RCL_LOGGING_IMPLEMENTATION backend was requested.
  wasm32 side modules use the same "lib<name>.so" convention as Linux.

- The post-dlopen path-resolution code took the _GNU_SOURCE branch (which
  emscripten's headers define) and called dlinfo(..., RTLD_DI_LINKMAP, ...).
  Emscripten's dlopen()/dlinfo() are a JS-backed shim, not glibc's, and
  don't support reading back a real struct link_map -- so a successful
  dlopen() got treated as a failure once dlinfo() returned -1. The #else
  branch (reuse the path dlopen() was given) already covers this platform
  correctly.

Verified end-to-end on a ROS 2 rolling + rmw_zenoh_pico + real-pthreads
emscripten-wasm32 build: a wasm32 rclpy talker's logging initialization
(rcl_logging_configure -> rcl_logging_implementation -> this dlopen path)
now succeeds instead of aborting with "failed to load any logging
implementations".

(cherry picked from commit a2d8f50)

Signed-off-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
Co-authored-by: Tobias Fischer <info@tobiasfischer.info>
Co-authored-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
ahcorde added a commit that referenced this pull request Sep 16, 2026
* fix: add emscripten guards to shared_library.c's dlopen path

Two related gaps in rcutils_load_shared_library() / rcutils_get_platform_library_name(),
found while getting rcl_logging_implementation's dlopen-by-name backend
selection working on emscripten-wasm32:

- rcutils_get_platform_library_name() had no emscripten branch, so it always
  fell through with written == 0 ("failed to format library name"),
  regardless of which RCL_LOGGING_IMPLEMENTATION backend was requested.
  wasm32 side modules use the same "lib<name>.so" convention as Linux.

- The post-dlopen path-resolution code took the _GNU_SOURCE branch (which
  emscripten's headers define) and called dlinfo(..., RTLD_DI_LINKMAP, ...).
  Emscripten's dlopen()/dlinfo() are a JS-backed shim, not glibc's, and
  don't support reading back a real struct link_map -- so a successful
  dlopen() got treated as a failure once dlinfo() returned -1. The #else
  branch (reuse the path dlopen() was given) already covers this platform
  correctly.

Verified end-to-end on a ROS 2 rolling + rmw_zenoh_pico + real-pthreads
emscripten-wasm32 build: a wasm32 rclpy talker's logging initialization
(rcl_logging_configure -> rcl_logging_implementation -> this dlopen path)
now succeeds instead of aborting with "failed to load any logging
implementations".

(cherry picked from commit a2d8f50)

Signed-off-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
Co-authored-by: Tobias Fischer <info@tobiasfischer.info>
Co-authored-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
ahcorde added a commit that referenced this pull request Sep 16, 2026
* fix: add emscripten guards to shared_library.c's dlopen path

Two related gaps in rcutils_load_shared_library() / rcutils_get_platform_library_name(),
found while getting rcl_logging_implementation's dlopen-by-name backend
selection working on emscripten-wasm32:

- rcutils_get_platform_library_name() had no emscripten branch, so it always
  fell through with written == 0 ("failed to format library name"),
  regardless of which RCL_LOGGING_IMPLEMENTATION backend was requested.
  wasm32 side modules use the same "lib<name>.so" convention as Linux.

- The post-dlopen path-resolution code took the _GNU_SOURCE branch (which
  emscripten's headers define) and called dlinfo(..., RTLD_DI_LINKMAP, ...).
  Emscripten's dlopen()/dlinfo() are a JS-backed shim, not glibc's, and
  don't support reading back a real struct link_map -- so a successful
  dlopen() got treated as a failure once dlinfo() returned -1. The #else
  branch (reuse the path dlopen() was given) already covers this platform
  correctly.

Verified end-to-end on a ROS 2 rolling + rmw_zenoh_pico + real-pthreads
emscripten-wasm32 build: a wasm32 rclpy talker's logging initialization
(rcl_logging_configure -> rcl_logging_implementation -> this dlopen path)
now succeeds instead of aborting with "failed to load any logging
implementations".

(cherry picked from commit a2d8f50)

Signed-off-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
Co-authored-by: Tobias Fischer <info@tobiasfischer.info>
Co-authored-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants