-
Notifications
You must be signed in to change notification settings - Fork 418
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Set symbol visibility to hidden for rclcpp #638
Conversation
Enabling symbol visibility feature in gcc and clang compilers. This will hep find symbol export related issues in linux and potentially reduce compile times. Discourse topic link: https://discourse.ros.org/t/set-symbol-visibility-to-hidden-for-rmw-and-rcl-packages/7981 Signed-off-by: Sachin Suresh Bhat <bhatsach@amazon.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thanks Sachin!
It's awesome you actually found bugs while doing this.
rclcpp/CMakeLists.txt
Outdated
@@ -109,6 +109,7 @@ ament_target_dependencies(${PROJECT_NAME} | |||
"rosidl_typesupport_cpp" | |||
"rosidl_generator_cpp") | |||
|
|||
configure_rcl(${PROJECT_NAME}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs to be rclcpp?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am trying to reuse the configure_rcl macro in ros2/rcl. This is in a way similar to how the configure_rmw_library macro is being reused in rmw_fastrtps and other packages. The single macro takes care of both C and C++ code
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not possible because they are not part of the same shared library. See doc here: https://gcc.gnu.org/wiki/Visibility
edit:
You have two SOs librcl.so and librcl_cpp.so.
When you build librcl.so, you can access both any public symbol of any dependency and any internal symbol, even private ones (because they are part of the same library).
Same thing when you build librcl_cpp.so. You can access your own symbols and any public one but not private symbols from another lib like e.g. librcl.so.
By re-using the macro, you are actually allowing librcl_cpp.so to access librcl.so private symbols which will not work.
Removing the duplication is doable but you'ld need something like that:
- https://github.com/jrl-umi3218/jrl-cmakemodules/blob/bf10c6ca3565b96ab283edaa8f18b5b533e59da9/header.cmake
- https://github.com/jrl-umi3218/jrl-cmakemodules/blob/bf10c6ca3565b96ab283edaa8f18b5b533e59da9/config.hh.cmake
This would significantly increase the scope of this change so I won't recommend for this to be done in this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A possible alternative may be to use the CMake standard macro generate_export_header
and the DEFINE_SYMBOL
target property.
See ros/console_bridge#43 for a use in a ROS-related repo, and https://bitbucket.org/osrf/gazebo/issues/2262/symbol-visibility-issues-in-windows for a related issue in Gazebo.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Quick note: This is the way in which we are sharing the "configure_rcl" macro from rcl to rclcpp: https://github.com/ros2/rmw/blob/master/rmw/CMakeLists.txt#L62-L65
I am not sure if sharing just the macro is allowing librcl_cpp.so to access librcl.so private symbols, I am guessing it is not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The critical part is in https://github.com/ros2/rcl/pull/391/files#diff-19c13a4e66284bae327c94111b226946R71, in which you are defining the RCL_BUILDING_DLL
macro also for compilation units that are not part of the rcl
. The effect will be that when including https://github.com/ros2/rcl/blob/aa4ac7d06cc5e59d693408260868edb004154b69/rcl/include/rcl/visibility_control.h as part of a compilation unit outside of rcl, RCL_PUBLIC
(in Windows) will have the value __declspec(dllexport)
instead of the correct __declspec(dllimport)
. This is exactly the problem described in https://bitbucket.org/osrf/gazebo/issues/2262/symbol-visibility-issues-in-windows .
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A possible alternative may be to use the CMake standard macro
generate_export_header
and theDEFINE_SYMBOL
target property.
I personally don't like the generated code approach (I prefer a single, checked-in boil-plate file) and a custom define.
I am trying to reuse the configure_rcl macro in ros2/rcl. This is in a way similar to how the configure_rmw_library macro is being reused in rmw_fastrtps and other packages. The single macro takes care of both C and C++ code
As the others said, you can do this, but only for the visibility compiler options, not the visibility compiler definition.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the inputs @thomas-moulard , @traversaro and @wjwwood. I have removed the target definition specific code in configure_rcl in ros2/rcl#391 . This is because each rcl or rclcpp has its own target definition specified in the CMakeList. For eg:
https://github.com/ros2/rclcpp/blob/master/rclcpp/CMakeLists.txt#L112-L115
https://github.com/ros2/rclcpp/blob/master/rclcpp_action/CMakeLists.txt#L38-L41
Is this a reasonable solution? Do you think we should have a separate configure_rclcpp for rclcpp packages?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I personally don't like the generated code approach (I prefer a single, checked-in boil-plate file) and a custom define.
This is probably a bit OT in this PR, but for the sake of clarity: the generated header file (both CMake's and the example linked by @thomas-moulard ) is useful for supporting the case in which a library is compiled as a static library under Windows. Without that, in any downstream compilation unit RCLCPP_PUBLIC
is always defined as __declspec(dllimport)
and this annotation will generate a Visual Studio error LNK2019 if the RCLCPP library is a actually compiled as static. A possible alternative without having a generated file is to have an additional logic that defines RCLCPP_PUBLIC
as an empty macro if RCLCPP_STATIC
is defined, but then it is necessary some kind of build system logic to make sure that RCLCPP_STATIC
is always defined (even downstream) if RCLCPP is build as static, for example a conditional target_compile_definitions(rclcpp PUBLIC RCLCPP_STATIC)
called only if the library is built is static, or similar.
See ros/console_bridge#40 (comment) for a similar comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry - let me rescind my vote until the macro gets fixed.
rclcpp/CMakeLists.txt
Outdated
@@ -109,6 +109,7 @@ ament_target_dependencies(${PROJECT_NAME} | |||
"rosidl_typesupport_cpp" | |||
"rosidl_generator_cpp") | |||
|
|||
configure_rcl(${PROJECT_NAME}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not possible because they are not part of the same shared library. See doc here: https://gcc.gnu.org/wiki/Visibility
edit:
You have two SOs librcl.so and librcl_cpp.so.
When you build librcl.so, you can access both any public symbol of any dependency and any internal symbol, even private ones (because they are part of the same library).
Same thing when you build librcl_cpp.so. You can access your own symbols and any public one but not private symbols from another lib like e.g. librcl.so.
By re-using the macro, you are actually allowing librcl_cpp.so to access librcl.so private symbols which will not work.
Removing the duplication is doable but you'ld need something like that:
- https://github.com/jrl-umi3218/jrl-cmakemodules/blob/bf10c6ca3565b96ab283edaa8f18b5b533e59da9/header.cmake
- https://github.com/jrl-umi3218/jrl-cmakemodules/blob/bf10c6ca3565b96ab283edaa8f18b5b533e59da9/config.hh.cmake
This would significantly increase the scope of this change so I won't recommend for this to be done in this PR.
@@ -49,7 +49,7 @@ namespace node_interfaces | |||
class NodeBaseInterface; | |||
} // namespace node_interfaces | |||
|
|||
class ClientBase | |||
class RCLCPP_PUBLIC ClientBase |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you explain why? We explicitly avoided this, choosing to make each method public manually. We've had issues making these public on Windows due to the std data structures which are private members of the class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's a sample error I ran into while running rclcpp package with visibility set to hidden. Is there any other way to handle this instead of making the class visible?
collect2: error: ld returned 1 exit status
make[2]: *** [test_service] Error 1
make[1]: *** [CMakeFiles/test_service.dir/all] Error 2
make[1]: *** Waiting for unfinished jobs....
CMakeFiles/test_publisher.dir/test/test_publisher.cpp.o: In function `std::shared_ptr<rclcpp::Publisher<rcl_interfaces::msg::IntraProcessMessage_<std::allocator<void> >, std::allocator<void> > > std::dynamic_pointer_cast<rclcpp::Publisher<rcl_interfaces::msg::IntraProcessMessage_<std::allocator<void> >, std::allocator<void> >, rclcpp::PublisherBase>(std::shared_ptr<rclcpp::PublisherBase> const&)':
test_publisher.cpp:(.text._ZSt20dynamic_pointer_castIN6rclcpp9PublisherIN14rcl_interfaces3msg20IntraProcessMessage_ISaIvEEES5_EENS0_13PublisherBaseEESt10shared_ptrIT_ERKS9_IT0_E[_ZSt20dynamic_pointer_castIN6rclcpp9PublisherIN14rcl_interfaces3msg20IntraProcessMessage_ISaIvEEES5_EENS0_13PublisherBaseEESt10shared_ptrIT_ERKS9_IT0_E]+0x30): undefined reference to `typeinfo for rclcpp::PublisherBase'
CMakeFiles/test_publisher.dir/test/test_publisher.cpp.o:(.data.rel.ro._ZTIN6rclcpp9PublisherIN14rcl_interfaces3msg20IntraProcessMessage_ISaIvEEES4_EE[_ZTIN6rclcpp9PublisherIN14rcl_interfaces3msg20IntraProcessMessage_ISaIvEEES4_EE]+0x10): undefined reference to `typeinfo for rclcpp::PublisherBase'
collect2: error: ld returned 1 exit status
make[2]: *** [test_publisher] Error 1
make[1]: *** [CMakeFiles/test_publisher.dir/all] Error 2
CMakeFiles/test_externally_defined_services.dir/test/test_externally_defined_services.cpp.o:(.data.rel.ro._ZTIN6rclcpp7ServiceINS_3srv4MockEEE[_ZTIN6rclcpp7ServiceINS_3srv4MockEEE]+0x10): undefined reference to `typeinfo for rclcpp::ServiceBase'
collect2: error: ld returned 1 exit status
make[2]: *** [test_externally_defined_services] Error 1
make[1]: *** [CMakeFiles/test_externally_defined_services.dir/all] Error 2
CMakeFiles/test_client.dir/test/test_client.cpp.o:(.data.rel.ro._ZTIN6rclcpp6ClientIN14rcl_interfaces3srv14ListParametersEEE[_ZTIN6rclcpp6ClientIN14rcl_interfaces3srv14ListParametersEEE]+0x10): undefined reference to `typeinfo for rclcpp::ClientBase'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think adding it to the class may be required, but you need to test it on Windows too, because I think it might be in conflict.
If you do leave the public in the class declaration, then the RCLCPP_PUBLIC
for each method in the class is redundant and should be removed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed all the inner RCLCPP_PUBLIC keywords from the modified classes. Will request @thomas-moulard to run this on windows CI for a quick check
…ntroduce a separate configure_rclcpp.cmake Signed-off-by: Sachin Suresh Bhat <bhatsach@amazon.com>
8c5bbdd
to
1459878
Compare
This is what I was referring to (dll warnings from the linker): https://ci.ros2.org/job/ci_windows/6118/warnings43Result/new/ |
:( let me look into this tomorrow. @wjwwood, can you help close out the rcl related changes if they are good to go? ros2/rcl#391 |
Discarding this PR for now as we need further investigation before getting back onto this task:
|
Enabling symbol visibility feature in gcc and clang compilers.
This will hep find symbol export related issues in linux and
potentially reduce compile times.
Discourse topic link:
https://discourse.ros.org/t/set-symbol-visibility-to-hidden-for-rmw-and-rcl-packages/7981
Signed-off-by: Sachin Suresh Bhat bhatsach@amazon.com
Connects to ros2/rcl#391