Un símbolo débil denota un símbolo especialmente anotado durante el enlace de archivos de objetos de formato ejecutable y enlazable (ELF) . De forma predeterminada, sin ninguna anotación, un símbolo en un archivo de objeto es fuerte . Durante la vinculación, un símbolo fuerte puede anular un símbolo débil del mismo nombre. Por el contrario, en presencia de dos símbolos fuertes con el mismo nombre, el enlazador resuelve el símbolo a favor del primero encontrado. Este comportamiento permite que un ejecutable anule las funciones de biblioteca estándar, como malloc (3). Al vincular un ejecutable binario, una declaración débilel símbolo no necesita una definición. En comparación, (de forma predeterminada) un símbolo fuerte declarado sin una definición desencadena un error de enlace de símbolo indefinido.
Los símbolos débiles no se mencionan en los estándares del lenguaje C o C ++; como tal, insertarlos en el código no es muy portátil. Incluso si dos plataformas admiten la misma sintaxis o una similar para marcar símbolos como débiles, la semántica puede diferir en puntos sutiles, por ejemplo, si los símbolos débiles durante el enlace dinámico en tiempo de ejecución pierden su semántica o no. [1]
Sintaxis
La colección de compiladores GNU y el Solaris Studio cuota de compilador de C de la misma sintaxis para los símbolos de anotación como débil, a saber, una especial #pragma , #pragma weak
y, alternativamente, una función y un atributo variable __attribute__((weak))
. [2] [3] [4] [5] [6] [7]
Pragma
// declaración de función #pragma débil power2int power2 ( int x );
Atributo
// declaración de funciónint __attribute__ (( débil )) power2 ( int x ); // oint power2 ( int x ) __attribute__ (( débil ));// declaración de variable; extern int __attribute__ (( débil )) global_var ;
Soporte de herramientas
El comando nm identifica símbolos débiles en archivos de objetos, bibliotecas y ejecutables. En Linux, un símbolo de función débil se marca con "W" si una definición predeterminada débil está disponible, y con "w" si no lo está. Los símbolos de variable débilmente definidos están marcados con "V" y "v". En Solaris, "nm" imprime "DÉBIL" en lugar de "GLOB" para un símbolo débil.
Ejemplos de
Los siguientes ejemplos funcionan en Linux y Solaris con GCC y Solaris Studio.
Ejemplo estático
main.c :
#include #include #include "power_slow.h"int main ( int argc , char ** argv ) { fprintf ( stderr , "power3 () =% d \ n " , power3 ( atoi ( argv [ 1 ]))); return 0 ; }
power_slow.h:
#ifndef POWER2_SLOW_H #define POWER2_SLOW_H// sintaxis alternativa // #pragma débil power2 int __attribute__ (( débil )) power2 ( int x ) // alternativamente después del símbolo // __attribute __ ((débil)) ;int power3 ( int x );#terminara si
power_slow.c :
#include #include "power_slow.h"int power2 ( int x ) { fprintf ( stderr , "lento power2 () \ n " ); return x * x ; }int power3 ( int x ) { retorno power2 ( x ) * x ; }
power.c :
#include int power2 ( int x ) { fprintf ( stderr , "fast power2 () \ n " ); return x * x ; }
Comandos de construcción:
cc -g -c -o main.o main.c cc -g -c -o power_slow.o power_slow.c cc -g -c -o power.o power.ccc main.o power_slow.o -o lentocc main.o power_slow.o power.o -o rápido
Producción:
$ ./slow 3 potencia lenta2 potencia3 () = 27 $ ./fast 3 potencia rápida2 potencia3 () = 27
Al eliminar el atributo débil y volver a ejecutar los comandos de compilación, el último falla con el siguiente mensaje de error (en Linux):
definición múltiple de 'power2'
El penúltimo todavía tiene éxito y tiene el mismo resultado../slow
Tomando main.c del ejemplo anterior y agregando:
#ifndef NO_USER_HOOK void user_hook ( void ) { fprintf ( stderr , "main: user_hook () \ n " ); } #endif
Reemplazando power_slow.c con:
#include #include "power_slow.h"void __attribute__ (( débil )) user_hook ( void ); #ifdef ENABLE_DEF void user_hook ( void ) { fprintf ( stderr , "power_slow: user_hook () \ n " ); } #endifint power2 ( int x ) { if ( user_hook ) // solo es necesario ifndef ENABLE_DEF user_hook (); return x * x ; }int power3 ( int x ) { retorno power2 ( x ) * x ; }
Comandos de construcción:
cc -g -c -o main.o main.ccc -g -fpic -c -o power_slow.po power_slow.ccc -shared -fpic -o libpowerslow.so power_slow.pocc main.o -L ' pwd ' -Wl, -R ' pwd ' -lpowerslow -O principalcc -g -DENABLE_DEF -fpic -c -o power_slow.po power_slow.ccc -shared -fpic -o libpowerslow.so power_slow.pocc main.o -L ' pwd ' -Wl, -R ' pwd ' -lpowerslow -o main2cc -g -DNO_USER_HOOK -c -o main.o main.ccc -g -fpic -c -o power_slow.po power_slow.ccc -shared -fpic -o libpowerslow.so power_slow.pocc main.o -L ' pwd ' -Wl, -R ' pwd ' -lpowerslow -o main3cc -g -DNO_USER_HOOK -c -o main.o main.ccc -g -DENABLE_DEF -fpic -c -o power_slow.po power_slow.ccc -shared -fpic -o libpowerslow.so power_slow.pocc main.o -L ' pwd ' -Wl, -R ' pwd ' -lpowerslow -o Main4
Producción:
$ ./main 3 main: user_hook () power3 () = 27 $ ./main2 3 main: user_hook () power3 () = 27 $ ./main3 3 power3 () = 27 $ ./main4 3 power_slow: user_hook () potencia3 () = 27
Eliminar el atributo débil y volver a ejecutar los comandos de compilación no produce errores de compilación y conduce a la misma salida (en Linux) para main
y main2
. Los comandos de compilación para el main3
cliente potencial a los siguientes mensajes de error y advertencia (en Linux):
advertencia: la dirección de 'user_hook' siempre se evaluará como 'verdadero'libpowerslow.so: referencia indefinida a `user_hook '
El compilador emite la advertencia porque puede determinar estáticamente que en la expresión se evalúa siempre como verdadero, porque contiene una entrada de tabla de salto ELF. El vinculador emite el mensaje de error. La compilación de incluye la misma advertencia pero sin error de enlace.if (user_hook)
user_hook
main4
Casos de uso
Los símbolos débiles pueden usarse como un mecanismo para proporcionar implementaciones predeterminadas de funciones que pueden ser reemplazadas por otras más especializadas (por ejemplo, optimizadas) en el momento del enlace. Luego, la implementación predeterminada se declara como débil y, en ciertos objetivos, los archivos objeto con símbolos fuertemente declarados se agregan a la línea de comando del vinculador.
Si una biblioteca define un símbolo como débil, un programa que vincula esa biblioteca es gratuito para proporcionar uno fuerte, por ejemplo, con fines de personalización.
Otro caso de uso de símbolos débiles es el mantenimiento de la compatibilidad binaria con versiones anteriores .
Limitaciones
En sistemas descendientes de UNIX System V , durante el tiempo de ejecución del programa, el enlazador dinámico resuelve definiciones de símbolos débiles como fuertes. Por ejemplo, un binario está vinculado dinámicamente con las bibliotecas libfoo.so y libbar.so. libfoo define el símbolo f
y lo declara débil. libbar también lo define f
y lo declara como fuerte. Dependiendo del orden de la biblioteca en la línea de comando del enlace (es decir -lfoo -lbar
), el enlazador dinámico usa la f débil de libfoo.so, aunque hay una versión fuerte disponible en tiempo de ejecución. GNU ld
proporciona la variable de entorno LD_DYNAMIC_WEAK
para proporcionar semántica débil para el enlazador dinámico. [1] [8]
Cuando se utilizan construcciones como
#pragma débil func void func ();void bar () { if ( func ) func (); }
, según el compilador y el nivel de optimización utilizado, el compilador puede interpretar el condicional como siempre verdadero (porque func
puede verse como indefinido desde el punto de vista de los estándares). [7] Una alternativa a la construcción anterior es usar una API del sistema para verificar si func
está definida (por ejemplo, dlsym con RTLD_DEFAULT
). La comprobación anterior también puede fallar por otras razones, por ejemplo, cuando func contiene una entrada de tabla de salto elf. [9]
El uso de símbolos débiles en bibliotecas estáticas tiene otra semántica que en las compartidas, es decir, con una biblioteca estática, la búsqueda de símbolos se detiene en el primer símbolo, incluso si es débil y también se incluye un archivo de objeto con un símbolo fuerte en el archivo de la biblioteca. En Linux, la opción del enlazador --whole-archive
cambia ese comportamiento. [10]
Se supone que el atributo de función débil se usa en declaraciones de función. Usarlo en una definición de función puede producir resultados inesperados, según el compilador y el nivel de optimización. [11]
En Solaris, los símbolos débiles también se utilizan dentro del kernel. La parte genérica del kernel (llamada genunix
) especifica las funciones débiles que se anulan en la parte específica de la plataforma del kernel (llamada unix
), como las rutinas de memoria virtual. El enlazador de tiempo de ejecución del kernel establece las direcciones de estas funciones cuando el kernel se combina en la memoria durante el arranque. Sin embargo, esto no funciona para los módulos cargables del kernel: el símbolo débil en el kernel no se reemplaza con el símbolo del módulo del kernel cuando se carga el módulo.
Métodos relacionados
Las construcciones condicionales del preprocesador C (CPP) también se pueden utilizar para cambiar entre diferentes versiones de un símbolo. La diferencia con los símbolos débiles es que el enlazador interpreta los símbolos débiles. El CPP se ejecuta durante la compilación de cada unidad de traducción antes del compilador de C.
El proceso de construcción (por ejemplo, make) se puede implementar de forma condicional, de modo que solo se creen diferentes versiones de un símbolo o se usen y enlacen diferentes bibliotecas (especializadas) según el objetivo.
Ver también
Referencias
- ↑ a b Drepper, Ulrich (7 de junio de 2000). "manejo débil" .
- ^ "Manual de CCG, 6.58.9 Pragmas débiles" .
- ^ "Manual GCC, 6.30 Declaración de atributos de funciones" . GNU . Consultado el 29 de mayo de 2013 .
- ^ "Manual GCC, 6.36 Especificación de atributos de variables" .
- ^ "Oracle Solaris Studio 12.3: C User's Guide, 2.11.27 débil" .
- ^ "Oracle Solaris Studio 12.3: C User's Guide, 2.9 Atributos admitidos" .
- ^ a b "Guía de bibliotecas y vinculadores de Oracle Solaris 11 Express 11/10, 2.11 Símbolos débiles" .
- ^ Drepper, Ulrich (octubre de 2011). "Cómo escribir bibliotecas compartidas (versión 4.1.2), 1.5.2 Reubicaciones de símbolos, página 6" (PDF) .
- ^ "Bibliotecas compartidas de Linux y enlaces débiles" .
- ^ "Página de manual de GNU LD" .
- ^ Kiszka, enero (23 de mayo de 2006). "Re: sobreoptimización de atributos débiles con 4.1" .