¿Puedo declarar una variable global en una biblioteca compartida?

¿Puedo declarar una variable global dentro de una biblioteca que luego se comstack en un objeto compartido? ¿Es seguro hacer referencia a él desde otras bibliotecas o desde el código de la aplicación principal al declararlo externo?

En teoría funciona:

[niko@dev1 snippets]$ cat libcode.c int variable; // <-- this is a global variable declared in a Library void set_var(int value) { variable=value; } int get_var(void) { return variable; } [niko@dev1 snippets]$ gcc -g -fPIC -c libcode.c [niko@dev1 snippets]$ gcc -o libcode.so -shared libcode.o [niko@dev1 snippets]$ cat appcode.c #include  // simplified .h declarations: extern int variable; void set_var(int value); int get_var(void); void main(void) { set_var(44); printf("var=%d\n",variable); variable=33; int var_copy=get_var(); printf("var_copy=%d\n",var_copy); } [niko@dev1 snippets]$ gcc -g -o app -L./ -lcode appcode.c [niko@dev1 snippets]$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./ [niko@dev1 snippets]$ ./app var=44 var_copy=33 [niko@dev1 snippets]$ 

Vamos a revisarlo con el depurador:

 [niko@dev1 snippets]$ gdb ./app ..... (gdb) break main Breakpoint 1 at 0x40077e: file appcode.c, line 9. (gdb) run Starting program: /home/deptrack/depserv/snippets/app Missing separate debuginfos, use: dnf debuginfo-install glibc-2.22-16.fc23.x86_64 Breakpoint 1, main () at appcode.c:9 9 set_var(44); (gdb) print &variable $1 = (int *) 0x601044  (gdb) s set_var (value=44) at libcode.c:4 4 variable=value; (gdb) s 5 } (gdb) s main () at appcode.c:10 10 printf("var=%d\n",variable); (gdb) s var=44 11 variable=33; (gdb) s 12 int var_copy=get_var(); (gdb) s get_var () at libcode.c:7 7 return variable; (gdb) s 8 } (gdb) s main () at appcode.c:13 13 printf("var_copy=%d\n",var_copy); (gdb) s var_copy=33 14 } (gdb) s 0x00007ffff7839580 in __libc_start_main () from /lib64/libc.so.6 (gdb) s Single stepping until exit from function __libc_start_main, which has no line number information. [Inferior 1 (process 28380) exited with code 014] (gdb) 

Digo que “en teoría” funciona porque al usar este enfoque en un proyecto grande me encontré con un error donde la referencia a dicha variable me dio resultados inesperados. La dirección de la variable fue inusualmente alta (0x7ffff767c640) y la única solución fue declarar todas las variables globales dentro del código principal de la aplicación y usar ‘extern’ para hacer referencia al código de la biblioteca. Sin embargo, de esta manera, la biblioteca no podría tener variables por sí misma. Consulte esta pregunta para obtener más información: obtener la dirección incorrecta de una variable durante una llamada de función

Las bibliotecas compartidas no son un concepto en C. Las implementaciones de bibliotecas compartidas de diferentes sistemas operativos y plataformas informáticas, donde existen, exhiben diferencias en forma y comportamiento.

Sin embargo, sí, todas las implementaciones de bibliotecas compartidas que conozco admiten variables con, desde una perspectiva C, la duración del almacenamiento estático y el enlace externo, que supongo que es lo que quiere decir con “global”. En la medida en que parezca que estás usando Linux, tus bibliotecas compartidas serán del tipo ELF. En ese caso, cada proceso que vincula dinámicamente la biblioteca compartida obtendrá su propia copia de dichas variables.

La dirección variable grande que describe no tiene ninguna consecuencia particular. Las bibliotecas compartidas de ELF no tienen que cargarse en ninguna dirección en particular, y de hecho, Linux implementa ASLR, lo que hace que las direcciones de carga de la biblioteca varíen activamente. Su biblioteca compartida se puede cargar más o menos en cualquier lugar del espacio de direcciones virtuales de 64 bits de su sistema, por lo que realmente no puede leer mucho sobre el hecho de que la dirección de la variable es numéricamente grande.


En cuanto al error que describe, me inclino a pensar que surgió de un código incorrecto, no (directamente) de la participación de una biblioteca compartida. Por su descripción, sospecho que terminó con varias variables con el mismo nombre, todas con vínculos externos. Eso es un error con la vinculación estática, pero en ese caso el comstackdor puede (y de manera predeterminada, GCC lo hace) fusionar las variables duplicadas en lugar de rechazar el código.

Con ELF, por otro lado, está bien que el mismo símbolo se defina en varios objetos compartidos vinculados al mismo proceso, y se puede hacer referencia a diferentes definiciones desde diferentes puntos en el proceso general. Dado que una biblioteca compartida se comstack por separado del progtwig principal, el comstackdor no tiene la oportunidad de combinar símbolos, y no es obvio que debería hacerlo incluso si pudiera. Pero las múltiples declaraciones de un símbolo dado es una posibilidad, no una necesidad . Si sucede, probablemente se deba a que sus encabezados declaran la variable incorrectamente.

Puede haber muchas declaraciones de cualquier variable dada en un progtwig, pero debe haber exactamente una definición. Las declaraciones normalmente provienen de un archivo de encabezado, y deberían tener este aspecto:

 extern int foo; 

El extern es obligatorio si el encabezado se va a usar en más de un archivo de origen, eso y la ausencia de un inicializador establecen que la statement no puede interpretarse como una definición. Entonces debería haber una definición de la variable en exactamente un archivo de origen, que se parezca a esto:

 int foo = 0; 

La presencia de un inicializador establece que la statement es también una definición. Podría seguir siendo una definición si se omitiera el inicializador, siempre que no se incluya el calificador extern , pero si no desea conocer todos los detalles, entonces es seguro proporcionar un inicializador.

Un problema como el que usted describe surgirá si hay definiciones de foo en múltiples objetos compartidos. Eso sucedería si, por ejemplo, un archivo de encabezado contenga una statement de cualquiera de estas formas:

mal.h

 int foo; /* WRONG - without extern, this is a tentative definition */ extern int bar = 0; /* WRONG - because of the initializer, this is a definition */ 

Sí, las bibliotecas pueden contener variables globales.

Pero dependiendo de las opciones del comstackdor / enlazador, pueden no ser visibles. Por ejemplo, es una práctica común que las bibliotecas se construyan con -fvisibility=hidden y que solo se exporten ciertos símbolos (mediante un mapa de __attribute__((__visibility__)) o __attribute__((__visibility__)) explícitas __attribute__((__visibility__)) ).

Su proyecto “grande” probablemente ha sido construido de tal manera.

La dirección alta también puede indicar que esa variable es algún otro tipo de símbolo (función) en otra biblioteca.