From d13836eb1e1919f483ab72e33251611e28170238 Mon Sep 17 00:00:00 2001
From: Mara Bos <m-ou.se@m-ou.se>
Date: Sun, 18 Nov 2018 19:05:08 +0100
Subject: [PATCH] Add dynamic modules on Posix.

You can now add `DYNAMIC` as an option to `px4_add_module`, which will
cause that module to no longer be compiled into the px4 executable, but
instead produce a separate shared library file, which can be loaded and
executed with the new `dyn` command:

    pxh> dyn ./hello.px4mod start

This will load the shared object file `hello.px4mod` if it wasn't
already loaded, and execute its main function with the given arguments.
---
 cmake/common/px4_base.cmake             | 25 +++++--
 cmake/configs/posix_rpi_common.cmake    |  1 +
 cmake/configs/posix_sitl_default.cmake  |  1 +
 platforms/posix/cmake/px4_impl_os.cmake |  5 ++
 src/systemcmds/dyn/CMakeLists.txt       |  8 +++
 src/systemcmds/dyn/dyn.cpp              | 91 +++++++++++++++++++++++++
 6 files changed, 127 insertions(+), 4 deletions(-)
 create mode 100644 src/systemcmds/dyn/CMakeLists.txt
 create mode 100644 src/systemcmds/dyn/dyn.cpp

diff --git a/cmake/common/px4_base.cmake b/cmake/common/px4_base.cmake
index 60703be25a..58ed8fe3c6 100644
--- a/cmake/common/px4_base.cmake
+++ b/cmake/common/px4_base.cmake
@@ -163,6 +163,7 @@ endfunction()
 #			[ SRCS <list> ]
 #			[ MODULE_CONFIG <list> ]
 #			[ EXTERNAL ]
+#			[ DYNAMIC ]
 #			)
 #
 #	Input:
@@ -178,10 +179,12 @@ endfunction()
 #		INCLUDES		: include directories
 #		DEPENDS			: targets which this module depends on
 #		EXTERNAL		: flag to indicate that this module is out-of-tree
+#		DYNAMIC			: don't compile into the px4 binary, but build a separate dynamically loadable module (posix)
 #		UNITY_BUILD		: merge all source files and build this module as a single compilation unit
 #
 #	Output:
 #		Static library with name matching MODULE.
+#		(Or a shared library when DYNAMIC is specified.)
 #
 #	Example:
 #		px4_add_module(MODULE test
@@ -198,7 +201,7 @@ function(px4_add_module)
 		NAME px4_add_module
 		ONE_VALUE MODULE MAIN STACK STACK_MAIN STACK_MAX PRIORITY
 		MULTI_VALUE COMPILE_FLAGS LINK_FLAGS SRCS INCLUDES DEPENDS MODULE_CONFIG
-		OPTIONS EXTERNAL UNITY_BUILD
+		OPTIONS EXTERNAL DYNAMIC UNITY_BUILD
 		REQUIRED MODULE MAIN
 		ARGN ${ARGN})
 
@@ -226,16 +229,30 @@ function(px4_add_module)
 
 		add_library(${MODULE} STATIC EXCLUDE_FROM_ALL ${CMAKE_CURRENT_BINARY_DIR}/${MODULE}_unity.cpp)
 		target_include_directories(${MODULE} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
+	elseif(DYNAMIC AND MAIN AND (${OS} STREQUAL "posix"))
+		add_library(${MODULE} SHARED ${SRCS})
+		target_compile_definitions(${MODULE} PRIVATE ${MAIN}_main=px4_module_main)
+		set_target_properties(${MODULE} PROPERTIES
+			PREFIX ""
+			SUFFIX ".px4mod"
+			)
+		target_link_libraries(${MODULE} PRIVATE px4)
+		if(APPLE)
+			# Postpone resolving symbols until loading time, which is the default on most systems, but not Mac.
+			set_target_properties(${MODULE} PROPERTIES LINK_FLAGS "-undefined dynamic_lookup")
+		endif()
 	else()
 		add_library(${MODULE} STATIC EXCLUDE_FROM_ALL ${SRCS})
 	endif()
 
 	# all modules can potentially use parameters and uORB
 	add_dependencies(${MODULE} uorb_headers)
-	target_link_libraries(${MODULE} PRIVATE prebuild_targets parameters_interface platforms__common px4_layer systemlib)
 
-	set_property(GLOBAL APPEND PROPERTY PX4_MODULE_LIBRARIES ${MODULE})
-	set_property(GLOBAL APPEND PROPERTY PX4_MODULE_PATHS ${CMAKE_CURRENT_SOURCE_DIR})
+	if(NOT DYNAMIC)
+		target_link_libraries(${MODULE} PRIVATE prebuild_targets parameters_interface platforms__common px4_layer systemlib)
+		set_property(GLOBAL APPEND PROPERTY PX4_MODULE_LIBRARIES ${MODULE})
+		set_property(GLOBAL APPEND PROPERTY PX4_MODULE_PATHS ${CMAKE_CURRENT_SOURCE_DIR})
+	endif()
 
 	px4_add_optimization_flags_for_target(${MODULE})
 
diff --git a/cmake/configs/posix_rpi_common.cmake b/cmake/configs/posix_rpi_common.cmake
index 0bba4a1cfe..2b8968b793 100644
--- a/cmake/configs/posix_rpi_common.cmake
+++ b/cmake/configs/posix_rpi_common.cmake
@@ -34,6 +34,7 @@ set(config_module_list
 	# System commands
 	#
 	systemcmds/param
+	systemcmds/dyn
 	systemcmds/led_control
 	systemcmds/mixer
 	systemcmds/ver
diff --git a/cmake/configs/posix_sitl_default.cmake b/cmake/configs/posix_sitl_default.cmake
index ca43114198..a8a5cdbb96 100644
--- a/cmake/configs/posix_sitl_default.cmake
+++ b/cmake/configs/posix_sitl_default.cmake
@@ -24,6 +24,7 @@ set(config_module_list
 	#systemcmds/bl_update
 	#systemcmds/config
 	#systemcmds/dumpfile
+	systemcmds/dyn
 	systemcmds/esc_calib
 	systemcmds/led_control
 	systemcmds/mixer
diff --git a/platforms/posix/cmake/px4_impl_os.cmake b/platforms/posix/cmake/px4_impl_os.cmake
index ed29e8347a..d21bccd3fc 100644
--- a/platforms/posix/cmake/px4_impl_os.cmake
+++ b/platforms/posix/cmake/px4_impl_os.cmake
@@ -48,6 +48,11 @@
 include(common/px4_base)
 list(APPEND CMAKE_MODULE_PATH ${PX4_SOURCE_DIR}/cmake/posix)
 
+# This makes it possible to dynamically load code which depends on symbols
+# inside the px4 executable.
+set(CMAKE_POSITION_INDEPENDENT_CODE ON)
+set(CMAKE_ENABLE_EXPORTS ON)
+
 #=============================================================================
 #
 #	px4_posix_generate_builtin_commands
diff --git a/src/systemcmds/dyn/CMakeLists.txt b/src/systemcmds/dyn/CMakeLists.txt
new file mode 100644
index 0000000000..1d2ec09f5f
--- /dev/null
+++ b/src/systemcmds/dyn/CMakeLists.txt
@@ -0,0 +1,8 @@
+px4_add_module(
+	MODULE systemcmds__dyn
+	MAIN dyn
+	SRCS
+		dyn.cpp
+	)
+
+target_link_libraries(systemcmds__dyn PUBLIC dl)
diff --git a/src/systemcmds/dyn/dyn.cpp b/src/systemcmds/dyn/dyn.cpp
new file mode 100644
index 0000000000..150d42f146
--- /dev/null
+++ b/src/systemcmds/dyn/dyn.cpp
@@ -0,0 +1,91 @@
+/****************************************************************************
+ *
+ *   Copyright (C) 2018 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ *    used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/**
+ * @file dyn.cpp
+ *
+ * @author Mara Bos <m-ou.se@m-ou.se>
+ */
+
+#include <dlfcn.h>
+
+#include <px4_module.h>
+#include <px4_log.h>
+
+static void usage();
+
+extern "C" {
+	__EXPORT int dyn_main(int argc, char *argv[]);
+}
+
+static void usage()
+{
+	PRINT_MODULE_DESCRIPTION(
+		R"(
+### Description
+Load and run a dynamic PX4 module, which was not compiled into the PX4 binary.
+
+### Example
+$ dyn ./hello.px4mod start
+
+)");
+	PRINT_MODULE_USAGE_NAME_SIMPLE("dyn", "command");
+	PRINT_MODULE_USAGE_ARG("<file>", "File containing the module", false);
+	PRINT_MODULE_USAGE_ARG("arguments...", "Arguments to the module", true);
+}
+
+int dyn_main(int argc, char *argv[]) {
+	if (argc < 2) {
+		usage();
+		return 1;
+	}
+
+	void *handle = dlopen(argv[1], RTLD_NOW);
+
+	if (!handle) {
+		PX4_ERR("%s", dlerror());
+		return 1;
+	}
+
+	void *main_address = dlsym(handle, "px4_module_main");
+
+	if (!main_address) {
+		PX4_ERR("%s", dlerror());
+		dlclose(handle);
+		return 1;
+	}
+
+	auto main_function = (int (*)(int, char **))main_address;
+
+	return main_function(argc - 1, argv + 1);
+}
-- 
GitLab