build-process linker static-libraries

build process - ld pregunta del enlazador: la opción--archivo completo



build-process linker (5)

El único uso real de la opción de enlazador de --whole-archive que he visto es la creación de bibliotecas compartidas a partir de las estáticas. Recientemente me encontré con Makefile (s) que siempre usan esta opción al enlazar con bibliotecas estáticas internas. Por supuesto, esto hace que los ejecutables extraigan innecesariamente código objeto no referenciado. Mi reacción a esto fue que esto está completamente equivocado, ¿me estoy perdiendo algo aquí?

La segunda pregunta que tengo tiene que ver con algo que leí sobre la opción de archivo completo pero que no pude analizar. Algo en el sentido de que --whole-archive opción de --whole-archive debe usar al vincular con una biblioteca estática si el ejecutable también se vincula con una biblioteca compartida que a su vez tiene (en parte) el mismo código objeto que la biblioteca estática. Esa es la biblioteca compartida y la biblioteca estática se superponen en términos de código objeto. El uso de esta opción obligaría a todos los símbolos (independientemente del uso) a resolverse en el ejecutable. Se supone que esto evita la duplicación del código objeto. Esto es confuso, si un símbolo es arbitrado en el programa, debe resolverse de manera única en el momento del enlace, ¿qué tiene que ver este negocio con la duplicación? (Perdóname si este párrafo no es el epítome de la claridad)

Gracias


Consulta anterior, pero en su primera pregunta ("Por qué"), he visto --archivo integral utilizado también para bibliotecas internas, principalmente para eludir las referencias circulares entre esas bibliotecas. Tiende a esconder la arquitectura pobre de las bibliotecas, así que no lo recomendaría. Sin embargo, es una manera rápida de obtener una prueba rápida de trabajo.

Para su segunda consulta, si el mismo símbolo estaba presente en un objeto compartido y una biblioteca estática, el enlazador satisfará la referencia con la biblioteca que encuentre primero.
Si la biblioteca compartida y la biblioteca estática comparten exactamente el código, todo puede funcionar. Pero cuando la biblioteca compartida y la biblioteca estática tienen implementaciones diferentes de los mismos símbolos, su programa aún se compilará pero se comportará de manera diferente según el orden de las bibliotecas.

Obligar a que todos los símbolos se carguen desde la biblioteca estática es una forma de eliminar la confusión sobre qué se carga desde dónde. Pero en general esto suena como resolver el problema equivocado; en su mayoría no querrá los mismos símbolos en diferentes bibliotecas.


Estoy de acuerdo en que usar -whole-archive para compilar ejecutables probablemente no sea lo que quieres (debido a la vinculación en el código innecesario y la creación de software inflado). Si tenían una buena razón para hacerlo, deberían haberlo documentado en el sistema de compilación, ya que ahora se lo deja adivinar.

En cuanto a su segunda parte de la pregunta. Si un ejecutable vincula tanto una biblioteca estática como una biblioteca dinámica que tiene (en parte) el mismo código de objeto que la biblioteca estática, entonces -whole-archive garantizará que en el tiempo del enlace se prefiera el código de la biblioteca estática. Esto es generalmente lo que quieres cuando haces enlaces estáticos.


Existen usos legítimos de --whole-archive cuando se vincula ejecutable con bibliotecas estáticas. Un ejemplo es la creación de código C ++, donde las instancias globales se "registran" a sí mismas en sus constructores (advertencia: código no probado):

main.cc

typedef void (*handler)(const char *protocol); typedef map<const char *, handler> M; M m; void register_handler(const char *protocol, handler) { m[protocol] = handler; } int main(int argc, char *argv[]) { for (int i = 1; i < argc-1; i+= 2) { M::iterator it = m.find(argv[i]); if (it != m.end()) it.second(argv[i+1]); } }

http.cc (parte de libhttp.a)

class HttpHandler { HttpHandler() { register_handler("http", &handle_http); } static void handle_http(const char *) { /* whatever */ } }; HttpHandler h; // registers itself with main!

Tenga en cuenta que no hay símbolos en http.cc que necesita main.cc Si vincula esto como

g++ main.cc -lhttp

no obtendrá un manejador de http vinculado al ejecutable principal y no podrá llamar a handle_http() . Contraste esto con lo que sucede cuando se vincula como:

g++ main.cc -Wl,--whole-archive -lhttp -Wl,--no-whole-archive

El mismo estilo de "autorregistro" también es posible en plain-C, por ejemplo, con la __attribute__((constructor)) GNU.


Otro uso legítimo de --whole-archive es que los desarrolladores de --whole-archive de herramientas distribuyan bibliotecas que contienen múltiples funciones en una única biblioteca estática. En este caso, el proveedor no tiene idea de qué partes de la biblioteca usará el consumidor y, por lo tanto, debe incluir todo.


Un buen escenario adicional en el que --whole-archive está bien utilizado es cuando se trata de bibliotecas estáticas y enlaces incrementales.

Supongamos que:

  1. libA implementa las libA a() y b() .
  2. Alguna porción del programa tiene que estar enlazada solo con libA , por ejemplo debido a algún --wrap función usando --wrap (un ejemplo clásico es malloc )
  3. libC implementa las funciones c() y usa a()
  4. el programa final usa a() y c()

Los pasos de enlace incrementales podrían ser:

ld -r -o step1.o module1.o --wrap malloc --whole-archive -lA ld -r -o step2.o step1.o module2.o --whole-archive -lC cc step3.o module3.o -o program

Si no se puede insertar, todo el archivo quitaría la función c() que el program utiliza de todos modos, lo que impide el proceso de compilación correcto.

Por supuesto, este es un caso de esquina particular en el que se debe realizar un enlace incremental para evitar envolver todas las llamadas a malloc en todos los módulos, pero es un caso que es soportado con éxito por --whole-archive .