Considere el siguiente programa simple:
__attribute__((weak)) void weakf(void); int main(int argc, char *argv[]) { weakf(); }Al compilar esto con gcc y ejecutarlo en una PC con Linux, falla. Cuando se ejecuta en ARM CM0 (arm-none-eabi-gcc), el enlazador reemplaza el símbolo indefinido por un salto a la siguiente instrucción y un nop.
¿Dónde está documentado este comportamiento? ¿Hay formas posibles de cambiarlo a través de las opciones de la línea de comandos? He revisado las documentaciones de GCC y LD , no hay información al respecto.
Sin embargo, si reviso el documento del compilador ARM, esto se explica claramente .
man nm
Estaba leyendo algunos documentos y me encontré con una cita relacionada con esto:
man nmdice:
"V"
"v" El símbolo es un objeto débil. Cuando un símbolo definido débil se vincula con un símbolo definido normal, el símbolo definido normal se utiliza sin error. Cuando se vincula un símbolo indefinido débil y el símbolo no está definido, el valor del símbolo débil se convierte en cero sin error. En algunos sistemas, las mayúsculas indican que se ha especificado un valor predeterminado."W"
"w" El símbolo es un símbolo débil que no se ha etiquetado específicamente como un símbolo de objeto débil. Cuando un símbolo definido débil se vincula con un símbolo definido normal, el símbolo definido normal se utiliza sin error. Cuando se vincula un símbolo indefinido débil y el símbolo no está definido, el valor del símbolo se determina de manera específica del sistema sin errores. En algunos sistemas, las mayúsculas indican que se ha especificado un valor predeterminado.
nm es parte de Binutils, que GCC usa bajo el capó, por lo que debería ser lo suficientemente canónico.
Luego, ejemplo en su archivo fuente:
C Principal
__attribute__((weak)) void weakf(void); int main(int argc, char *argv[]) { weakf(); }hacemos:
gcc -O0 -ggdb3 -std=c99 -Wall -Wextra -pedantic -o main.out main.c nm main.outque contiene:
w weakfy por lo tanto es un valor específico del sistema. Sin embargo, no puedo encontrar dónde se define el comportamiento por sistema. No creo que puedas hacer nada mejor que leer el código fuente de Binutils aquí.
v se fijaría en 0, pero eso se usa para variables indefinidas (que son objetos): ¿Cómo hacer que los enlaces débiles funcionen con GCC?
Entonces:
gdb -batch -ex 'disassemble/rs main' main.outda:
Dump of assembler code for function main: main.c: 4 { 0x0000000000001135 <+0>: 55 push %rbp 0x0000000000001136 <+1>: 48 89 e5 mov %rsp,%rbp 0x0000000000001139 <+4>: 48 83 ec 10 sub $0x10,%rsp 0x000000000000113d <+8>: 89 7d fc mov %edi,-0x4(%rbp) 0x0000000000001140 <+11>: 48 89 75 f0 mov %rsi,-0x10(%rbp) 5 weakf(); 0x0000000000001144 <+15>: e8 e7 fe ff ff callq 0x1030 <weakf@plt> 0x0000000000001149 <+20>: b8 00 00 00 00 mov $0x0,%eax 6 } 0x000000000000114e <+25>: c9 leaveq 0x000000000000114f <+26>: c3 retq End of assembler dump.lo que significa que se resuelve en el PLT .
Luego, dado que no entiendo completamente PLT, verifico experimentalmente que se resuelve en la dirección 0 y segfaults:
gdb -nh -ex run -ex bt main.outSupongo que sucede lo mismo en ARM, también debe configurarlo en 0.
En ARM con gcc este código no me funciona (prueba en armv7 con gcc Debian 4.6.3-14+rpi1). Parece que la cadena de herramientas del compilador arm tiene un comportamiento diferente.
No encontré documentación útil para este comportamiento. Parece que el valor débil es NULL si no está definido en el momento del enlace.
Así que te sugiero que lo pruebes:
if (weakf == NULL) printf ("weakf not found\n"); else weakf();