[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH] Introduce symbol versioning


Hello,

Currently libssh doesn't provide versioned symbols. That have the following disadvantages:
 * in an ABI change, there  is no way to keep the old version for compatibility and switch to the new version due to symbol clashes
 * in an API change, there is no way to keep the old symbol for old applications, and introduce a new symbol for the newly compiled applications
 * an application cannot have two or more dependencies which are linked with different versions of libssh

More information can be found in [0].

The attached patch introduces symbol versioning to libssh by adding a linker map containing all exported symbols (marked as LIBSSH_API in the code).
For each new release with changes in the exported symbols, the linker map have to be updated to keep the versions compatible.
If an incompatible change is made, all the symbols have to be moved to a single new version.

Regards,
Anderson

[0]: https://www.akkadia.org/drepper/dsohowto.pdf

PS.: I tried to follow the "SubmittingPatches" orientation, but I could not send an email to contributing@xxxxxxxxxx. The emails are returning.

From 3e38ba07df7667300714771dfbe72bbd3077f582 Mon Sep 17 00:00:00 2001
From: Anderson Toshiyuki Sasaki <ansasaki@xxxxxxxxxx>
Date: Thu, 8 Mar 2018 13:15:34 +0100
Subject: [PATCH] Introduce symbol versioning

This adds a linker map, which adds version information for exported
symbols, and the option to compile with symbol versioning. The symbol
versioning is enabled by default, but disabled in non UNIX like
platforms. It can be disabled by passing "-DWITH_SYMBOL_VERSIONING=OFF"
option to cmake or "-withoutsymbolversioning" to "obj/build_make.sh".
After adding symbol versioning, modifications in the exported interface
must be treated with care and the linker map updated accordingly.

Signed-off-by: Anderson Toshiyuki Sasaki <ansasaki@xxxxxxxxxx>
---
 CMakeLists.txt      |   6 +
 DefineOptions.cmake |   2 +
 obj/build_make.sh   |   6 +-
 src/CMakeLists.txt  |  11 ++
 src/libssh.map      | 425 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 449 insertions(+), 1 deletion(-)
 create mode 100644 src/libssh.map

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2904ffbe..46143cdd 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -86,6 +86,11 @@ if (BSD OR SOLARIS OR OSX)
     find_package(Argp)
 endif (BSD OR SOLARIS OR OSX)
 
+# Disable symbol versioning in non UNIX platforms
+if (NOT UNIX)
+  set(WITH_SYMBOL_VERSIONING OFF)
+endif (NOT UNIX)
+
 # config.h checks
 include(ConfigureChecks.cmake)
 configure_file(config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h)
@@ -174,5 +179,6 @@ else (WITH_INTERNAL_DOC)
     message(STATUS "Public API documentation generation")
 endif (WITH_INTERNAL_DOC)
 message(STATUS "Benchmarks: ${WITH_BENCHMARKS}")
+message(STATUS "Symbol versioning: ${WITH_SYMBOL_VERSIONING}")
 message(STATUS "********************************************")
 
diff --git a/DefineOptions.cmake b/DefineOptions.cmake
index 43457e54..7816b956 100644
--- a/DefineOptions.cmake
+++ b/DefineOptions.cmake
@@ -15,6 +15,7 @@ option(WITH_CLIENT_TESTING "Build with client tests; requires a running sshd" OF
 option(WITH_BENCHMARKS "Build benchmarks tools" OFF)
 option(WITH_EXAMPLES "Build examples" ON)
 option(WITH_NACL "Build with libnacl (curve25519)" ON)
+option(WITH_SYMBOL_VERSIONING "Build with symbol versioning" ON)
 option(FUZZ_TESTING "Build with fuzzer for the server" OFF)
 if (WITH_ZLIB)
     set(WITH_LIBZ ON)
@@ -33,3 +34,4 @@ endif (WITH_TESTING)
 if (WITH_NACL)
   set(WITH_NACL ON)
 endif (WITH_NACL)
+
diff --git a/obj/build_make.sh b/obj/build_make.sh
index 2f2e4a6c..f61d62d1 100755
--- a/obj/build_make.sh
+++ b/obj/build_make.sh
@@ -63,7 +63,8 @@ function clean_build_dir() {
 
 function usage () {
 echo "Usage: `basename $0` [--prefix /install_prefix|--build [debug|final]|--clean|--verbose|--libsuffix (32|64)|--help|--clang|--cmakedir /directory|--make
-(gmake|make)|--ccompiler (gcc|cc)|--withstaticlib|--unittesting|--clientunittesting|--withssh1|--withserver]"
+(gmake|make)|--ccompiler
+(gcc|cc)|--withstaticlib|--unittesting|--clientunittesting|--withssh1|--withserver|--withoutsymbolversioning]"
     cleanup_and_exit
 }
 
@@ -148,6 +149,9 @@ while test -n "$1"; do
 		*-withserver)
 			OPTIONS="${OPTIONS} -DWITH_SERVER=ON"
 		;;
+		*-withoutsymbolversioning)
+			OPTIONS="${OPTIONS} -DWITH_SYMBOL_VERSIONING=OFF"
+		;;
 		----noarg)
 			echo "$ARG does not take an argument"
 			cleanup_and_exit
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index da87313e..da055909 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -245,6 +245,13 @@ if (NOT WITH_NACL)
   )
 endif (NOT WITH_NACL)
 
+if (WITH_SYMBOL_VERSIONING)
+  set(libssh_SRCS
+    ${libssh_SRCS}
+    libssh.map
+  )
+endif (WITH_SYMBOL_VERSIONING)
+
 include_directories(
   ${LIBSSH_PUBLIC_INCLUDE_DIRS}
   ${LIBSSH_PRIVATE_INCLUDE_DIRS}
@@ -275,6 +282,10 @@ if (MINGW)
     set_target_properties(${LIBSSH_SHARED_LIBRARY} PROPERTIES LINK_FLAGS "-Wl,--enable-stdcall-fixup")
 endif ()
 
+if (WITH_SYMBOL_VERSIONING)
+    set_target_properties(${LIBSSH_SHARED_LIBRARY} PROPERTIES LINK_FLAGS
+            "-Wl,--version-script,\"${libssh_SOURCE_DIR}/src/libssh.map\"")
+endif (WITH_SYMBOL_VERSIONING)
 
 install(
   TARGETS
diff --git a/src/libssh.map b/src/libssh.map
new file mode 100644
index 00000000..ed5818ad
--- /dev/null
+++ b/src/libssh.map
@@ -0,0 +1,425 @@
+# When new symbols are added in a release, they must be added as:
+#
+# LIBSSH_<NEW_RELEASE_VERSION>
+# {
+#     global:
+#         new_symbol;
+#         another_new_symbol;
+# } LIBSSH_<CURRENT_RELEASE>;
+#
+# If the ABI is broken, which leads to a bump in the soname version, all the
+# previous symbol versions must be combined into a single new version.
+#
+# The "local: *;" catch-all case must be kept only in the oldest compatible
+# version.
+
+LIBSSH_4_5_0
+{
+    global:
+        buffer_free;
+        buffer_get;
+        buffer_get_len;
+        buffer_new;
+        channel_accept_x11;
+        channel_change_pty_size;
+        channel_close;
+        channel_forward_accept;
+        channel_forward_cancel;
+        channel_forward_listen;
+        channel_free;
+        channel_get_exit_status;
+        channel_get_session;
+        channel_is_closed;
+        channel_is_eof;
+        channel_is_open;
+        channel_new;
+        channel_open_forward;
+        channel_open_session;
+        channel_poll;
+        channel_read;
+        channel_read_buffer;
+        channel_read_nonblocking;
+        channel_request_env;
+        channel_request_exec;
+        channel_request_pty;
+        channel_request_pty_size;
+        channel_request_send_signal;
+        channel_request_sftp;
+        channel_request_shell;
+        channel_request_subsystem;
+        channel_request_x11;
+        channel_select;
+        channel_send_eof;
+        channel_set_blocking;
+        channel_write;
+        channel_write_stderr;
+        privatekey_free;
+        privatekey_from_file;
+        publickey_free;
+        publickey_from_file;
+        publickey_from_privatekey;
+        publickey_to_string;
+        sftp_async_read;
+        sftp_async_read_begin;
+        sftp_attributes_free;
+        sftp_canonicalize_path;
+        sftp_chmod;
+        sftp_chown;
+        sftp_client_message_free;
+        sftp_client_message_get_data;
+        sftp_client_message_get_filename;
+        sftp_client_message_get_flags;
+        sftp_client_message_get_type;
+        sftp_client_message_set_filename;
+        sftp_close;
+        sftp_closedir;
+        sftp_dir_eof;
+        sftp_extensions_get_count;
+        sftp_extensions_get_data;
+        sftp_extensions_get_name;
+        sftp_extension_supported;
+        sftp_file_set_blocking;
+        sftp_file_set_nonblocking;
+        sftp_free;
+        sftp_fstat;
+        sftp_fstatvfs;
+        sftp_fsync;
+        sftp_get_client_message;
+        sftp_get_error;
+        sftp_handle;
+        sftp_handle_alloc;
+        sftp_handle_remove;
+        sftp_init;
+        sftp_lstat;
+        sftp_mkdir;
+        sftp_new;
+        sftp_new_channel;
+        sftp_open;
+        sftp_opendir;
+        sftp_read;
+        sftp_readdir;
+        sftp_readlink;
+        sftp_rename;
+        sftp_reply_attr;
+        sftp_reply_data;
+        sftp_reply_handle;
+        sftp_reply_name;
+        sftp_reply_names;
+        sftp_reply_names_add;
+        sftp_reply_status;
+        sftp_rewind;
+        sftp_rmdir;
+        sftp_seek;
+        sftp_seek64;
+        sftp_send_client_message;
+        sftp_server_init;
+        sftp_server_new;
+        sftp_server_version;
+        sftp_setstat;
+        sftp_stat;
+        sftp_statvfs;
+        sftp_statvfs_free;
+        sftp_symlink;
+        sftp_tell;
+        sftp_tell64;
+        sftp_unlink;
+        sftp_utimes;
+        sftp_write;
+        ssh_accept;
+        ssh_add_channel_callbacks;
+        ssh_auth_list;
+        ssh_basename;
+        ssh_bind_accept;
+        ssh_bind_accept_fd;
+        ssh_bind_fd_toaccept;
+        ssh_bind_free;
+        ssh_bind_get_fd;
+        ssh_bind_listen;
+        ssh_bind_new;
+        ssh_bind_options_set;
+        ssh_bind_set_blocking;
+        ssh_bind_set_callbacks;
+        ssh_bind_set_fd;
+        ssh_blocking_flush;
+        ssh_buffer_add_data;
+        ssh_buffer_free;
+        ssh_buffer_get;
+        ssh_buffer_get_data;
+        ssh_buffer_get_len;
+        ssh_buffer_new;
+        ssh_buffer_reinit;
+        ssh_channel_accept_forward;
+        ssh_channel_accept_x11;
+        ssh_channel_cancel_forward;
+        ssh_channel_change_pty_size;
+        ssh_channel_close;
+        ssh_channel_free;
+        ssh_channel_get_exit_status;
+        ssh_channel_get_session;
+        ssh_channel_is_closed;
+        ssh_channel_is_eof;
+        ssh_channel_is_open;
+        ssh_channel_listen_forward;
+        ssh_channel_new;
+        ssh_channel_open_auth_agent;
+        ssh_channel_open_forward;
+        ssh_channel_open_reverse_forward;
+        ssh_channel_open_session;
+        ssh_channel_open_x11;
+        ssh_channel_poll;
+        ssh_channel_poll_timeout;
+        ssh_channel_read;
+        ssh_channel_read_nonblocking;
+        ssh_channel_read_timeout;
+        ssh_channel_request_auth_agent;
+        ssh_channel_request_env;
+        ssh_channel_request_exec;
+        ssh_channel_request_pty;
+        ssh_channel_request_pty_size;
+        ssh_channel_request_send_exit_signal;
+        ssh_channel_request_send_exit_status;
+        ssh_channel_request_send_signal;
+        ssh_channel_request_sftp;
+        ssh_channel_request_shell;
+        ssh_channel_request_subsystem;
+        ssh_channel_request_x11;
+        ssh_channel_select;
+        ssh_channel_send_eof;
+        ssh_channel_set_blocking;
+        ssh_channel_set_counter;
+        ssh_channel_window_size;
+        ssh_channel_write;
+        ssh_channel_write_stderr;
+        ssh_clean_pubkey_hash;
+        ssh_connect;
+        ssh_connector_free;
+        ssh_connector_new;
+        ssh_connector_set_in_channel;
+        ssh_connector_set_in_fd;
+        ssh_connector_set_out_channel;
+        ssh_connector_set_out_fd;
+        ssh_copyright;
+        ssh_dirname;
+        ssh_disconnect;
+        ssh_dump_knownhost;
+        ssh_event_add_connector;
+        ssh_event_add_fd;
+        ssh_event_add_session;
+        ssh_event_dopoll;
+        ssh_event_free;
+        ssh_event_new;
+        ssh_event_remove_connector;
+        ssh_event_remove_fd;
+        ssh_event_remove_session;
+        ssh_execute_message_callbacks;
+        ssh_finalize;
+        ssh_forward_accept;
+        ssh_forward_cancel;
+        ssh_forward_listen;
+        ssh_free;
+        ssh_get_cipher_in;
+        ssh_get_cipher_out;
+        ssh_get_clientbanner;
+        ssh_get_disconnect_message;
+        ssh_get_error;
+        ssh_get_error_code;
+        ssh_get_fd;
+        ssh_get_hexa;
+        ssh_get_hmac_in;
+        ssh_get_hmac_out;
+        ssh_get_issue_banner;
+        ssh_get_kex_algo;
+        ssh_get_log_callback;
+        ssh_get_log_level;
+        ssh_get_log_userdata;
+        ssh_get_openssh_version;
+        ssh_getpass;
+        ssh_get_poll_flags;
+        ssh_get_pubkey;
+        ssh_get_pubkey_hash;
+        ssh_get_publickey;
+        ssh_get_publickey_hash;
+        ssh_get_random;
+        ssh_get_serverbanner;
+        ssh_get_server_publickey;
+        ssh_get_status;
+        ssh_get_version;
+        ssh_gssapi_get_creds;
+        ssh_gssapi_set_creds;
+        ssh_handle_key_exchange;
+        ssh_init;
+        ssh_is_blocking;
+        ssh_is_connected;
+        ssh_is_server_known;
+        ssh_key_cmp;
+        ssh_key_free;
+        ssh_key_is_private;
+        ssh_key_is_public;
+        ssh_key_new;
+        ssh_key_type;
+        ssh_key_type_from_name;
+        ssh_key_type_to_char;
+        _ssh_log;
+        ssh_log;
+        ssh_message_auth_interactive_request;
+        ssh_message_auth_kbdint_is_response;
+        ssh_message_auth_password;
+        ssh_message_auth_pubkey;
+        ssh_message_auth_publickey;
+        ssh_message_auth_publickey_state;
+        ssh_message_auth_reply_pk_ok;
+        ssh_message_auth_reply_pk_ok_simple;
+        ssh_message_auth_reply_success;
+        ssh_message_auth_set_methods;
+        ssh_message_auth_user;
+        ssh_message_channel_request_channel;
+        ssh_message_channel_request_command;
+        ssh_message_channel_request_env_name;
+        ssh_message_channel_request_env_value;
+        ssh_message_channel_request_open_destination;
+        ssh_message_channel_request_open_destination_port;
+        ssh_message_channel_request_open_originator;
+        ssh_message_channel_request_open_originator_port;
+        ssh_message_channel_request_open_reply_accept;
+        ssh_message_channel_request_pty_height;
+        ssh_message_channel_request_pty_pxheight;
+        ssh_message_channel_request_pty_pxwidth;
+        ssh_message_channel_request_pty_term;
+        ssh_message_channel_request_pty_width;
+        ssh_message_channel_request_reply_success;
+        ssh_message_channel_request_subsystem;
+        ssh_message_channel_request_x11_auth_cookie;
+        ssh_message_channel_request_x11_auth_protocol;
+        ssh_message_channel_request_x11_screen_number;
+        ssh_message_channel_request_x11_single_connection;
+        ssh_message_free;
+        ssh_message_get;
+        ssh_message_global_request_address;
+        ssh_message_global_request_port;
+        ssh_message_global_request_reply_success;
+        ssh_message_reply_default;
+        ssh_message_retrieve;
+        ssh_message_service_reply_success;
+        ssh_message_service_service;
+        ssh_message_subtype;
+        ssh_message_type;
+        ssh_mkdir;
+        ssh_new;
+        ssh_options_copy;
+        ssh_options_get;
+        ssh_options_getopt;
+        ssh_options_get_port;
+        ssh_options_parse_config;
+        ssh_options_set;
+        ssh_pcap_file_close;
+        ssh_pcap_file_free;
+        ssh_pcap_file_new;
+        ssh_pcap_file_open;
+        ssh_pki_copy_cert_to_privkey;
+        ssh_pki_export_privkey_file;
+        ssh_pki_export_privkey_to_pubkey;
+        ssh_pki_export_pubkey_base64;
+        ssh_pki_export_pubkey_file;
+        ssh_pki_generate;
+        ssh_pki_import_cert_base64;
+        ssh_pki_import_cert_file;
+        ssh_pki_import_privkey_base64;
+        ssh_pki_import_privkey_file;
+        ssh_pki_import_pubkey_base64;
+        ssh_pki_import_pubkey_file;
+        ssh_pki_key_ecdsa_name;
+        ssh_print_hexa;
+        ssh_privatekey_type;
+        ssh_publickey_to_file;
+        ssh_remove_channel_callbacks;
+        ssh_scp_accept_request;
+        ssh_scp_close;
+        ssh_scp_deny_request;
+        ssh_scp_free;
+        ssh_scp_init;
+        ssh_scp_leave_directory;
+        ssh_scp_new;
+        ssh_scp_pull_request;
+        ssh_scp_push_directory;
+        ssh_scp_push_file;
+        ssh_scp_push_file64;
+        ssh_scp_read;
+        ssh_scp_request_get_filename;
+        ssh_scp_request_get_permissions;
+        ssh_scp_request_get_size;
+        ssh_scp_request_get_size64;
+        ssh_scp_request_get_warning;
+        ssh_scp_write;
+        ssh_select;
+        ssh_send_debug;
+        ssh_send_ignore;
+        ssh_send_keepalive;
+        ssh_server_init_kex;
+        ssh_service_request;
+        ssh_set_agent_channel;
+        ssh_set_agent_socket;
+        ssh_set_auth_methods;
+        ssh_set_blocking;
+        ssh_set_callbacks;
+        ssh_set_channel_callbacks;
+        ssh_set_counters;
+        ssh_set_fd_except;
+        ssh_set_fd_toread;
+        ssh_set_fd_towrite;
+        ssh_set_log_callback;
+        ssh_set_log_level;
+        ssh_set_log_userdata;
+        ssh_set_message_callback;
+        ssh_set_pcap_file;
+        ssh_set_server_callbacks;
+        ssh_silent_disconnect;
+        ssh_string_burn;
+        ssh_string_copy;
+        ssh_string_data;
+        ssh_string_fill;
+        ssh_string_free;
+        ssh_string_free_char;
+        ssh_string_from_char;
+        ssh_string_get_char;
+        ssh_string_len;
+        ssh_string_new;
+        ssh_string_to_char;
+        ssh_threads_get_noop;
+        ssh_threads_get_pthread;
+        ssh_threads_set_callbacks;
+        ssh_try_publickey_from_file;
+        ssh_userauth_agent;
+        ssh_userauth_agent_pubkey;
+        ssh_userauth_autopubkey;
+        ssh_userauth_gssapi;
+        ssh_userauth_kbdint;
+        ssh_userauth_kbdint_getanswer;
+        ssh_userauth_kbdint_getinstruction;
+        ssh_userauth_kbdint_getname;
+        ssh_userauth_kbdint_getnanswers;
+        ssh_userauth_kbdint_getnprompts;
+        ssh_userauth_kbdint_getprompt;
+        ssh_userauth_kbdint_setanswer;
+        ssh_userauth_list;
+        ssh_userauth_none;
+        ssh_userauth_offer_pubkey;
+        ssh_userauth_password;
+        ssh_userauth_privatekey_file;
+        ssh_userauth_pubkey;
+        ssh_userauth_publickey;
+        ssh_userauth_publickey_auto;
+        ssh_userauth_try_publickey;
+        ssh_version;
+        ssh_write_knownhost;
+        string_burn;
+        string_copy;
+        string_data;
+        string_fill;
+        string_free;
+        string_from_char;
+        string_len;
+        string_new;
+        string_to_char;
+    local:
+        *;
+};
-- 
2.14.3

Follow-Ups:
Re: [PATCH] Introduce symbol versioningJakub Jelen <jjelen@xxxxxxxxxx>
Archive administrator: postmaster@lists.cynapses.org