Símbolos con Versión en Bibliotecas Compartidas
P+F
¿Cómo hacer símbolos de versiones para ayuda de usuarios?
Cuando el software esté compilado a un ejecutable enlazado dinámicamente, contendrá referencias para bibliotecas compartidas y para los símbolos en esas bibliotecas. Si ese ejecutable es ejecutado en un sistema donde los símbolos están ausentes desde una biblioteca compartida (tal vez porque la aplicación fue instalada sin además actualizar las bibliotecas instaladas), la ejecución fallará.
[root v1.1]# Bar 5 10 area Bar: error de enlace simbólico: Bar: símbolo indefinido: rectangle_area
El error dice al usuario que el problema es, pero no dice al usuario como resolverlo. El error no nombra la biblioteca que está obsoleta, y no indica la versión mínima.
Con los símbolos versionados, el error es mucho más útil.
[root v1.1]# Bar 5 10 area Bar: lib-versioned/libFoo.so.1: version `+LIBFOO_1.1+' no encontrada (requerido por Bar)
Este mensaje de error indica cual biblioteca compartida tiene el problema, y proporciona una pista razonablemente clara como a cómo para resolver el problema (lo cuál es para instalar libfoo.so.1, versión 1.1 o más nuevo).
RPM además puede utilizar esta información para generar mejor información de dependencia automáticamente, e impide estos problemas de suceder en primer lugar.
¿Qué son los símbolos versionados?
Los símbolos «versionados» son una etiquete de texto sin formato aplicada a un objeto compartido dinámico, y a símbolos en objetos compartidos dinámicamente.
¿Dónde son versionados los símbolos utilizados?
Los símbolos versionados son utilizados ampliamente entre las bibliotecas de núcleo en sistemas GNU/Linux, especialmente entre desarrolladores quiénes están manteniendo las ABI estables. Los símbolos versionados les proporcionan un mecanismo para desplegar cambios que requeriría rupturas del ABI y algunos baches sin símbolos versionados.
Demo
libFoo proporciona una demostración sencilla de bibliotecas versionadas en C y en C++, con ejemplos de configuración con elos sistemas de creación automake, CMake, y Meson.
En ese proyecto, encontrará foo.e, el cual proporcionar dos funciones, y libFoo.map, el cual describe cual versión de la biblioteca aparece en cada primer símbolo dentro:
LIBFOO_1.0 {
global:
rectangle_perimeter;
local:
*;
};
LIBFOO_1.1 {
global:
rectangle_area;
};
Ese archivo map y tan solo un argumento adicional, -Wl,--version-script=libFoo.map es todo eso es necesario para añadir versiones del símbolo a una biblioteca.
Upkeep también es directo. Siempre que una publicación nueva incluye símbolos nuevos que deseas exportar, añadir un conjunto nuevo de símbolos al mapa. Establece con una versión del mapa del símbolo nunca sería cambiado tras una publicación.
Si make este proyecto, tomarás una app con el nombre Bar, lo cual está enlazado a la creación sin versión de la biblioteca compartida, y una app con el nombre Bar-versioned lo cuál es enlazado a la creación versionada de la biblioteca compartida.
ldd ./Bar demostrará aquella Bar utiliza la biblioteca compartida en el lib-unversioned directorio por defecto (la ubicación es embedded utilizando rpath). Puedes ejecutar env LD_LIBRARY_PATH=lib-versioned ./Bar para utilizar la creación de biblioteca eso incluye símbolos versionados, demostrando que la biblioteca permanece compatible hacia atrás cuando son versionados los símbolos son añadidos.
Si estás desarrollando una biblioteca que actualmente no incluye símbolos versionados, no dejes que reconstruir el historial de versiones sea un motivo para posponer su adopción. La solución más sencilla es añadir todos tus símbolos a la versión que consideres actual y, posteriormente, añadir nuevas versiones para los símbolos que introduzcas en el futuro.
Cómo
Examine las capacidades proporcionadas por el paquete binario rpm: rpm -qp --provides <paquete>. Un paquete con bibliotecas compartidas listará la biblioteca como libc.so.6()(64bit) y si la biblioteca proporciona símbolos versionados, también listará la biblioteca con versiones como libm.so.6(GLIBC_2.41)(64bit).
Agregar versiones de símbolos es sencillo para la mayoría de las bibliotecas, ya que inicialmente solo requiere un mapa de símbolos y un argumento adicional para el enlazador durante el proceso de compilación.
#!/bin/sh
echo "/* Avoid modifying a symbol set after it has been released"
echo " When adding features in a new release, add a new set"
echo " Removing features is a breaking change */"
echo "$2 {"
echo " global:"
objdump -T $1 | \
grep -F .text | \
awk '{print $7;}' | \
c++filt | \
awk '/[() ]/ {print " \"" $0 "\";";} \
!/[() ]/ {print " " $0 ";";}' | \
sort
echo "};"
Ejecute generate_initial_map.sh /ruta/para/library.so.1 <BIBLIOTECA>_<VERSIÓN> para generar un archivo de relación.
Añadir script‐versión para Automate
El manual de la Biblioteca de Portabilidad de GNU incluye ejemplos de utilizar version-script en automake. En Makefile.am:
if HAVE_LD_VERSION_SCRIPT libfoo_la_LDFLAGS += -Wl,--version-script=$(srcdir)/libfoo.map endif
Añadir un version-script a CMake
El proyecto protobuf licenciado por BSD-3-Clause incluye ejemplos de uso de version-script en CMake.
En CMakeLists.txt, compruebe el enlazador para que admita:
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/cmaketest.map
"{
global:
main;
local:
*;
};")
# Modulo CheckLinkerFlag diponible en CMake >=3.18.
if(${CMAKE_VERSION} VERSION_GREATER_EQUAL 3.18)
include(CheckLinkerFlag)
check_linker_flag(CXX -Wl,--version-script=${CMAKE_CURRENT_BINARY_DIR}/cmaketest.map project_HAVE_LD_VERSION_SCRIPT)
endif()
file(REMOVE ${CMAKE_CURRENT_BINARY_DIR}/cmaketest.map)
Y, donde la biblioteca está definida:
if(project_HAVE_LD_VERSION_SCRIPT)
target_link_options(libfoo PRIVATE -Wl,--version-script=${protobuf_source_dir}/src/libfoo.map)
set_target_properties(libfoo PROPERTIES
LINK_DEPENDS ${project_source_dir}/src/libfoo.map)
endif()
Añadir script de versión a Meson
El enlace prueba casos de Meson incluye ejemplos de uso con version-script.
# Solaris 11.4 ld admite --version-script solo cuando además especifica
# -z gnu-version-script-compat
if meson.get_compiler('c').get_linker_id() == 'ld.solaris'
add_project_link_arguments('-Wl,-z,gnu-version-script-compat', language: 'C')
endif
# Archivo estático de relación
mapfile = 'bob.map'
vflag = '-Wl,--version-script,@0@/@1@'.format(meson.current_source_dir(), mapfile)
l = shared_library('bob', 'bob.c', link_args : vflag, link_depends : mapfile)
Consulte además
Want to help? Learn how to contribute to Fedora Docs ›