Problemas de compilador de GCC heredado
-
13-09-2019 - |
Pregunta
Estamos utilizando un compilador heredado, basado en GCC 2.6.0, para compilar cruzados para un antiguo procesador incrustado que todavía estamos usando (sí, ¡todavía está en uso desde 1994!). El ingeniero que realizó el puerto GCC para este chip ha avanzado desde hace mucho tiempo. Aunque podríamos recuperar la fuente GCC 2.6.0 de algún lugar de la web, el cambio establecido para este chip ha desaparecido en los pasillos de la historia corporativa. Nos hemos confundido hasta hace poco, ya que el compilador todavía corría y produjo ejecutables viables, pero a partir del kernel de Linux 2.6.25 (y también 2.6.26) falla con el mensaje gcc: virtual memory exhausted
... incluso cuando se ejecuta sin parámetros o solo con -v
. He reiniciado mi sistema de desarrollo (de 2.6.26) usando el núcleo 2.6.24 y el compilador funciona nuevamente (reiniciar con 2.6.25 no).
Tenemos un sistema que mantenemos en 2.6.24 solo con el propósito de hacer compilaciones para este chip, pero nos sentimos un poco expuestos en caso de que el mundo de Linux pase al punto de que ya no podemos reconstruir un sistema que se ejecute El compilador (es decir, nuestro sistema 2.6.24 muere y no podemos obtener 2.6.24 para instalar y ejecutar en un nuevo sistema porque algunas de las piezas de software ya no están disponibles).
¿Alguien tiene alguna idea de lo que podríamos hacer a una instalación más moderna para que este compilador heredado se ejecute?
Editar:
Para responder algunos de los comentarios ...
Lamentablemente, son los cambios del código fuente los que son específicos de nuestro chip que se pierden. Esta pérdida ocurrió en dos principales reorgs de la compañía y varios sistemas (un par de los cuales realmente dejaron un desastre). Ahora usamos el control de configuración, pero eso está cerrando la puerta del granero demasiado tarde para este problema.
El uso de una VM es una buena idea, y puede ser lo que terminamos haciendo. Gracias por esa idea.
Finalmente, probé Strace como sugirió Efemient y descubrí que la última llamada del sistema fue BRK () que devolvió un error en el nuevo sistema (2.6.26 kernel) y devolvió el éxito en el sistema anterior (núcleo 2.6.24). Esto indicaría que realmente me estoy quedando sin memoria virtual, excepto que el "límite" de TCSH devuelve los mismos valores en sistemas antiguos y nuevos, y /Proc /MemInfo muestra que los nuevos sistemas tienen un poco más de memoria y un poco más de espacio de intercambio. ¿Quizás es un problema de fragmentación o dónde se está cargando el programa?
Sin embargo, hice más investigaciones y la "aleatorización de brk" en el núcleo 2.6.25, sin embargo, CONFIG_COMPAT_BRK
Supuestamente está habilitado por defecto (que deshabilita la aleatorización de BRK).
Editar:
Ok, más información: realmente parece que la aleatorización de BRK es el culpable, el Legacy GCC está llamando a BRK () para cambiar el final del segmento de datos y que ahora falla, lo que hace que el GCC heredado informe "Memoria virtual agotada". Hay algunas formas documentadas de deshabilitar la aleatorización de BRK:
sudo echo 0 > /proc/sys/kernel/randomize_va_space
sudo sysctl -w kernel.randomize_va_space=0
Comenzando un nuevo caparazón con
setarch i386 -R tcsh
(o "-r -l")
Los he probado y parecen tener un efecto en el sentido de que el valor de retorno BRK () es diferente (y siempre el mismo) que sin ellos (probado en el núcleo 2.6.25 y 2.6.26), pero el brk () Todavía falla, por lo que el Legacy GCC todavía falla :-(.
Además, he establecido vm.legacy_va_layout=1
y vm.overcommit_memory=2
sin cambios, y he reiniciado con el vm.legacy_va_layout=1
y kernel.randomize_va_space=0
Configuración guardada en /etc/sysctl.conf. Todavía no hay cambios.
Editar:
Usando kernel.randomize_va_space=0
en el kernel 2.6.26 (y 2.6.25) da como resultado la siguiente llamada brk () que se informa por strace legacy-gcc
:
brk(0x80556d4) = 0x8056000
Esto indica que BRK () falló, pero parece que falló porque el segmento de datos ya termina más allá de lo que se solicitó. Usando Objdump, puedo ver que el segmento de datos debe finalizar a 0x805518c, mientras que el fallido brk () indica que el segmento de datos actualmente termina a 0x8056000:
Sections: Idx Name Size VMA LMA File off Algn 0 .interp 00000013 080480d4 080480d4 000000d4 2**0 CONTENTS, ALLOC, LOAD, READONLY, DATA 1 .hash 000001a0 080480e8 080480e8 000000e8 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 2 .dynsym 00000410 08048288 08048288 00000288 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 3 .dynstr 0000020e 08048698 08048698 00000698 2**0 CONTENTS, ALLOC, LOAD, READONLY, DATA 4 .rel.bss 00000038 080488a8 080488a8 000008a8 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 5 .rel.plt 00000158 080488e0 080488e0 000008e0 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 6 .init 00000008 08048a40 08048a40 00000a40 2**4 CONTENTS, ALLOC, LOAD, READONLY, CODE 7 .plt 000002c0 08048a48 08048a48 00000a48 2**2 CONTENTS, ALLOC, LOAD, READONLY, CODE 8 .text 000086cc 08048d10 08048d10 00000d10 2**4 CONTENTS, ALLOC, LOAD, READONLY, CODE 9 .fini 00000008 080513e0 080513e0 000093e0 2**4 CONTENTS, ALLOC, LOAD, READONLY, CODE 10 .rodata 000027d0 080513e8 080513e8 000093e8 2**0 CONTENTS, ALLOC, LOAD, READONLY, DATA 11 .data 000005d4 08054bb8 08054bb8 0000bbb8 2**2 CONTENTS, ALLOC, LOAD, DATA 12 .ctors 00000008 0805518c 0805518c 0000c18c 2**2 CONTENTS, ALLOC, LOAD, DATA 13 .dtors 00000008 08055194 08055194 0000c194 2**2 CONTENTS, ALLOC, LOAD, DATA 14 .got 000000b8 0805519c 0805519c 0000c19c 2**2 CONTENTS, ALLOC, LOAD, DATA 15 .dynamic 00000088 08055254 08055254 0000c254 2**2 CONTENTS, ALLOC, LOAD, DATA 16 .bss 000003b8 080552dc 080552dc 0000c2dc 2**3 ALLOC 17 .note 00000064 00000000 00000000 0000c2dc 2**0 CONTENTS, READONLY 18 .comment 00000062 00000000 00000000 0000c340 2**0 CONTENTS, READONLY SYMBOL TABLE: no symbols
Editar:
Para hacer eco del comentario de Ephemient a continuación: "¡Tan extraño para tratar el GCC como un binario sin fuente"!
Entonces, usando Strace, Objdump, GDB y mi comprensión limitada del ensamblador y arquitectura 386, he rastreado el problema hasta la primera llamada Malloc en el código heredado. El Legacy GCC llama a MALLOC, que devuelve NULL, que da como resultado el mensaje "Memoria virtual agotada" en Stderr. Este malloc está en libc.so.5, y llama a Getenv un montón de veces y termina llamando a Brk () ... supongo que para aumentar el montón ... que falla.
A partir de esto, solo puedo suponer que el problema es más que la aleatorización de BRK, o no he desactivado completamente la aleatorización de BRK, a pesar de la configuración Randomize_VA_space = 0 y Legacy_VA_Layout = 1 SYSCTL.
Solución
Instale Linux + el GCC antiguo en una máquina virtual.
Otros consejos
Tienes el fuentes para este compilador personalizado? Si puede recuperar la línea de base 2.6.0 (y eso debería ser relativamente fácil), entonces la diff y el parche deben recuperar su conjunto de cambios.
Lo que recomendaría es usar ese cambio establecido para construir una nueva versión contra GCC actualizado. Y luego póngalo bajo el control de configuración.
Lo siento, no quiero gritar. Es solo que he estado diciendo lo mismo durante la mayoría de los 30 años.
Puede strace
la gcc-2.6.0
¿ejecutable? Puede estar haciendo algo como leer /proc/$$/maps
, y confundirse cuando la salida cambia de formas insignificantes. Un problema similar fue Recientemente notado entre 2.6.28 y 2.6.29.
Si es así, puedes hackear /usr/src/linux/fs/proc/task_mmu.c
o por ahí para restaurar la salida anterior, o configurar algunos $LD_PRELOAD
falsificar gcc
en leer otro archivo.
Editar
Desde que mencionaste brk
...
CONFIG_COMPAT_BRK
Hace el valor predeterminado kernel.randomize_va_space=1
en vez de 2
, pero eso todavía aleatoriza todo lo que no sea el montón (brk
).
Mira si tu problema desaparece si echo 0 > /proc/sys/kernel/randomize_va_space
o sysctl kernel.randomize_va_space=0
(equivalente).
Si es así, agregue kernel.randomize_va_space = 0
a /etc/sysctl.conf
o agregar norandmaps
a la línea de comando del núcleo (equivalente), y sé feliz nuevamente.
Me encontré con este y pensé en tu problema. ¿Puedes encontrar una manera de jugar con el binario para moverlo al formato elfo? O puede ser irrelevante, pero jugar con Objdump puede proporcionarle más información.
¿Puedes echar un vistazo al mapa de memoria del proceso?
Así que he resuelto algo ... no es una solución completa, pero supera el problema original que tuve con el Legacy GCC.
Poner puntos de interrupción en cada llamada LIBC en la .plt (tabla de enlace de procedimiento) Veo que Malloc (en libc.so.5) llama a getenv () para obtener:
MALLOC_TRIM_THRESHOLD_ MALLOC_TOP_PAD_ MALLOC_MMAP_THRESHOLD_ MALLOC_MMAP_MAX_ MALLOC_CHECK_
Así que los busqué en web y encontré este que aconsejó
setenv MALLOC_TOP_PAD_ 536870912
¡Entonces el Legacy GCC funciona!
Pero no en el hogar libre, llegó al enlace en la construcción antes de fallar, por lo que está sucediendo algo más con el legado que tenemos :-( está informando:
Virtual memory exceeded in `new'
En /etc/sysctl.conf tengo:
kernel.randomize_va_space=0 vm.legacy_va_layout=1
Todavía funciona igual si
kernel.randomize_va_space=1 vm.legacy_va_layout=0
Pero no si
kernel.randomize_va_space=2
Hubo una sugerencia para usar "LDD" para ver las dependencias de la biblioteca compartida: el Legacy GCC solo necesita libc5, pero el NLD heredado también necesita libg ++. So.27, libstdc ++. So.27, libm.so.5 y aparentemente hay Una versión libc5 de libg ++. So.27 (libg ++ 27-altdev?
Entonces, como dije, aún no está libre de casa ... estar más cerca. Probablemente publicaré una nueva pregunta sobre el problema de NLD.
Editar:
Originalmente iba a abstenerme de "aceptar" esta respuesta, ya que todavía tengo un problema con el enlazador heredado correspondiente, pero para obtener una finalidad en esta pregunta al menos, estoy repensando esa posición.
Gracias a salir a:
- an0nym0usc0ward para la sugerencia de usar una VM (que en última instancia puede convertirse en la respuesta aceptada)
- Efemient para sugerir usar Strace y ayudar con el uso de StackoverFlow
- shodanex por sugerir usar objdump
Editar
A continuación se presentan las últimas cosas que aprendí, y ahora aceptaré la solución VM ya que no pude resolverlo por completo de otra manera (al menos en el tiempo asignado para esto).
Los núcleos más nuevos tienen un indicador de compilación config_compat_brk para permitir que se use libc5, por lo que presumiblemente construir un nuevo núcleo con esta bandera solucionará el problema (y mirando a través del núcleo SRC, parece que lo hará, pero no puedo estar seguro de que lo hice. no seguir todos los caminos). También hay otra forma documentada de permitir el uso de libc5 en tiempo de ejecución (en lugar de en el tiempo de construcción del núcleo): sudo sysctl -w kernel.randomize_va_space = 0. Esto, sin embargo, no hace un trabajo completo y algunas (¿la mayoría?) Las aplicaciones libc5 aún se romperán, por ejemplo, nuestro compilador y enlazador heredado. Esto parece deberse a una diferencia en los supuestos de alineación entre los núcleos más nuevos y mayores. He parcheado el Binario de enlace para que piense que tiene una sección BSS más grande, para llevar el final del BSS a un límite de página, y esto funciona en el núcleo más nuevo cuando el SYSCTL var kernel.randomize_va_space = 0. Esta no es una solución satisfactoria para mí, ya que estoy parcheando ciegamente a un ejecutable binario crítico, y aunque ejecutar el enlazador parcheado en el núcleo más nuevo produjo una salida idéntica de un poco al enlazador original en el núcleo más antiguo, eso no prueba que demuestre que demuestre que demuestre que demuestre que demuestre que Algunas otras entradas de enlazador (es decir, cambiamos el programa que se está vinculando) también producirá resultados idénticos.
¿No podría simplemente hacer una imagen de disco que se pueda reinstalar si el sistema muere? o hacer una VM?