Orden de destrucción de estática incorrecta después de abrir / cerrar una biblioteca compartida más de una vez

Tengo un problema extraño con una biblioteca compartida de C ++ en Linux.

El proceso carga y descarga mi biblioteca dos veces (es por diseño y no se puede cambiar).

  1. Durante el primer dlopen (), todos los miembros estáticos de mi biblioteca se inicializan en un orden corregido.
  2. Luego, cuando se llama al primer dlclose (), la destrucción también ocurre como se esperaba, en el orden opuesto a la construcción.
  3. Durante el segundo dlopen (), todo está bien: el orden de construcción es correcto, al igual que la primera vez.
  4. Pero en el segundo dlclose (), el orden de destrucción se rompe repentinamente: los destructores se llaman en el mismo orden en que se inicializaron y, a menudo, provoca un SEGFAULT si algún objeto estático intenta acceder a un objeto ya destruido.
  5. Los intentos posteriores de dlopen () / dlclose () repiten exactamente los pasos tercero y cuarto.

Intenté reproducir el problema con un ejemplo mínimo, pero no tuve éxito: con una pequeña biblioteca compartida falsa, todo funciona como se esperaba. Hay algo en mi gran biblioteca que causa estragos en el segundo dlclose (), y es muy grande.

No encontré dependencia en gcc (probé 3.2 / 3.4 / 4.1.2) ni distribución de Linux (RHEL 4/5, SuSE 10). La búsqueda de casos similares en la Web arrojó 0 resultados, nada similar.

Durante los experimentos, intenté incrustar algunas llamadas a atexit () en constructores de objetos estáticos, para ver si el orden de los controladores atexit () se ve afectado, y descubrí que sí. Los pasos 1/2/3 funcionan bien (dlopen / dlclose / dlopen), luego en el segundo dlclose el orden de los manejadores registrados atexit es incorrecto.

Realmente no espero obtener una respuesta, pero agradecería cualquier sugerencia sobre cómo abordar el problema.

Gracias de antemano,

Andrew Schetinin

Actualización de PS: depuré el código en atexit () en GLIBC y descubrí que hay un error que estoy detectando, un error bastante simple, en realidad. Está arreglado en GLIBC 2.4, y tuve la mala suerte de trabajar con GLIBC 2.3.4

preguntado el 08 de noviembre de 11 a las 16:11

Depuraré el código en atexit () en GLIBC y descubrí que hay un error que estoy detectando, un error bastante simple, en realidad. Está arreglado en GLIBC 2.4 y tuve la mala suerte de trabajar con GLIBC 2.3.4. -

2 Respuestas

Es un error en GLIBC 2.3.x que se corrigió en GLIBC 2.4.

El error se desencadena en condiciones muy específicas: cuando un proceso abre y cierra una biblioteca de C ++ con muchas variables estáticas en diferentes archivos de objeto más de una vez.

Las entradas atexit () se organizan en una lista de un solo enlace de matrices de 32 punteros de controlador, y el código antiguo en atexit () insertó nuevos controladores en una página incorrecta en la lista de un solo enlace, rompiendo así el contrato atexit () de llamada a la finalización manipuladores en el orden inverso al de su registro.

respondido 15 nov., 11:12

No creo que pueda confiar en el orden estático ctor / dtor por ningún tramo de la imaginación. Simplemente no te ofrecen garantías.

Esperaría ver funciones de configuración y desmontaje a las que solo llama una vez cada una. Asegurarse de hacer esto una vez es suyo para hacer cumplir.

respondido 09 nov., 11:03

Si bien no puedo confiar en el orden de inicialización de mis fuentes, una vez compilada y vinculada la biblioteca, el vinculador "congela" el orden. Y por cierto, en la mayoría de los enlazadores modernos existen ciertas convenciones sobre cómo se ordena la inicialización (orden directo en gcc, orden opuesto en Sun C ++ y orden totalmente controlado en IBM C ++). Mi problema no es ese. Mi problema es que el orden se rompe después de la recarga de una biblioteca compartida, que es algo que nunca esperaría que sucediera. - Andrew Schetinin

Dice que tienen estas convenciones, congelaciones, etc., pero su evidencia no lo respalda. La memoria estática no puede depender del orden de inicialización. Tal vez funcione durante meses y luego, por alguna heurística oscura, la cadena de herramientas cambiará sus estrategias y no le dirá por qué. - Tom Kerr

Nuevamente, en el 99% de los casos funciona correctamente; de ​​lo contrario, el software C ++ difícilmente podría escribirse en absoluto, se basa en muchas suposiciones debido a la falta de una forma estandarizada de hacer todas esas cosas como bibliotecas compartidas. En mi caso, creo que es un error que encontré, y lo tengo 100% reproducible para una sola biblioteca específica. Me faltan ideas sobre qué hacer al respecto :-) - Andrew Schetinin

El estándar C ++ establece explícitamente que (simplificado) el orden o la destrucción de objetos estáticos es inverso a su orden de construcción. - Alex che

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.