c++ - vectorial - longitud de curva ejercicios resueltos
¿Por qué la información de longitud de la función de otras bibliotecas compartidas en ELF? (2)
Nuestro proyecto (C ++, Linux, gcc, PowerPC) consta de varias bibliotecas compartidas. Cuando se lanza una nueva versión del paquete, solo deben cambiarse aquellas librerías cuyo código fuente se haya visto realmente afectado. Con "cambio" me refiero a la identidad binaria absoluta (se compara la suma de comprobación sobre el archivo. Diferencia de comprobación diferente -> versión diferente según la política). (Debo mencionar que todo el proyecto siempre se construye a la vez, no importa si algún código ha cambiado o no por biblioteca).
Por lo general, esto puede lograrse ocultando partes privadas de los archivos de encabezado incluidos y no cambiando los públicos.
Sin embargo, hubo un caso en el que se agregó una mera delete
al destructor de una clase TableManager (¡en el archivo TableManager.cpp!) De la biblioteca libTableManager.so, y aún así el binario / suma de comprobación de la biblioteca libB.so (que usa la clase TableManager ) ha cambiado .
TableManager.h:
class TableManager
{
public:
TableManager();
~TableManager();
private:
int* myPtr;
}
TableManager.cpp:
TableManager::~TableManager()
{
doSomeCleanup();
delete myPtr; // this delete has been added
}
Al inspeccionar libB.so con readelf --all libB.so
, mirando la sección .dynsym, resultó que la longitud de todas las funciones, incluso las que se usan dinámicamente de otras bibliotecas, se almacenan en libB! Se parece a esto (la longitud es el 668 en la tercera columna):
527: 00000000 668 FUNC GLOBAL DEFAULT UND _ZN12TableManagerD1Ev
Así que mis preguntas son:
- ¿Por qué la longitud de una función se almacena realmente en la biblioteca del cliente? ¿No sería suficiente una dirección de inicio?
- ¿Se puede suprimir esto de alguna manera al compilar / enlazar libB.so (tipo de "eliminación")? Realmente nos gustaría reducir este grado de dependencia ...
Bingo. En realidad, es una especie de "error" en binutils que encontraron y repararon en 2008. ¡ La información sobre el tamaño no es realmente útil !
Lo que Simon Baldwin wrote en la lista de correo de binutils describe exactamente el problema (énfasis hecho por mí):
Actualmente, el tamaño de un símbolo ELF no definido se copia fuera del archivo objeto o DSO que proporciona el símbolo, al vincular. Este tamaño no es confiable , por ejemplo, en el caso de dos DSO, uno vinculado al otro. El DSO de nivel inferior podría realizar un cambio de preservación ABI que altera el tamaño del símbolo, sin necesidad de reconstruir el DSO de nivel superior. Y si se reconstruye el DSO de nivel superior, las herramientas que monitorean las sumas de comprobación de archivos registrarán un cambio debido al tamaño alterado del símbolo indefinido, aunque no se haya modificado nada más sobre el DSO de nivel superior. Esto puede llevar a una reconstrucción innecesaria e indeseable y cambiar las cascadas en sistemas basados en suma de control.
Tenemos el problema con un sistema antiguo (binutils 2.16). Lo comparé con la versión 2.20 en el sistema de escritorio y, voilà, las longitudes de los símbolos globales compartidos fueron 0:
157: 00000000 0 FUNC GLOBAL DEFAULT UND _ZN12TableManagerD1Ev
158: 00000000 0 FUNC GLOBAL DEFAULT UND _ZNSs6assignERKSs@GLIBCXX_3.4 (2)
159: 00000000 0 FUNC GLOBAL DEFAULT UND sleep@GLIBC_2.0 (6)
160: 00000000 0 FUNC GLOBAL DEFAULT UND _ZN4Gpio11setErrorLEDENS_
Así que comparé ambos códigos fuente de binutils y, voilà otra vez, está la solución que Alan sugirió en la lista de correo:
Tal vez solo apliquemos el parche y recompilemos binutils ya que necesitamos quedarnos con la plataforma más antigua. Gracias por su paciencia.
Necesitará leer el código para que el cargador esté seguro, pero creo que en este caso podemos hacer una estimación bastante razonable de lo que se pretende lograr con este campo de longitud.
El cargador debe tomar todas las funciones que se van a poner en el proceso y asignarlas a las direcciones de memoria. Entonces, le da a la primera función una dirección. Luego, el segundo viene después del final del primero, pero para saber "el final del primero", necesita saber cuánto tiempo dura la primera función.
Puedo ver dos formas para que se acerque a esa longitud: puede tenerlo codificado en el archivo (como lo había visto en ELF) o puede abrir el archivo que contiene la función y obtener la longitud de ahí.
Este último parece (para mí) tener dos desventajas bastante obvias. La primera es la velocidad: abrir todos esos archivos adicionales, analizar sus encabezados, etc., solo para obtener la longitud de las funciones es casi seguro que leer cuatro bytes adicionales para cada función del archivo actual. La segunda es la conveniencia: mientras no llame a ninguna de las funciones de un archivo, no necesita que el archivo esté presente en absoluto. Si lees las longitudes directamente del archivo (por ejemplo, como Windows normalmente lo hace con DLL), necesitarás que ese archivo esté presente en el sistema de destino, incluso si nunca se usa en realidad.
Edit: dado que algunas personas aparentemente no entendieron la implicación (aparentemente también) sutil de "la intención de lograr", permítanme ser completamente claro: estoy razonablemente seguro de que este campo no (y nunca se ha utilizado) realmente se usa.
Sin embargo, cualquiera que piense que hace que esta respuesta sea incorrecta, debe volver a la programación 101 y aprender la diferencia entre una interfaz y una implementación.
En este caso, el formato de archivo define una interfaz, un conjunto de capacidades que puede utilizar un cargador. En el caso específico de Linux, parece que este campo nunca se usa.
Sin embargo, eso no cambia el hecho de que el campo aún existe, ni que el OP preguntó por qué existe. El simple hecho de decir "no se usa", aunque es verdadero en sí mismo, no responde la pregunta que hizo.