problemas do compilador legado gcc
-
13-09-2019 - |
Pergunta
Estamos usando um compilador legado, baseado no gcc 2.6.0, para compilação cruzada para um processador embutido antigo que ainda usamos (sim, ainda está em uso desde 1994!).O engenheiro que fez a porta gcc para este chip já mudou há muito tempo.Embora possamos recuperar a fonte do GCC 2.6.0 de algum lugar da Web, o conjunto de alterações para este chip desapareceu nos corredores da história corporativa.Nós nos atrapalhamos até recentemente, pois o compilador ainda rodava e produzia executáveis viáveis, mas a partir do kernel Linux 2.6.25 (e também 2.6.26) ele falha com a mensagem gcc: virtual memory exhausted
...mesmo quando executado sem parâmetros ou apenas com -v
.Reinicializei meu sistema de desenvolvimento (de 2.6.26) usando o kernel 2.6.24 e o compilador funciona novamente (reinicializar com 2.6.25 não).
Temos um sistema que mantemos em 2.6.24 apenas com o propósito de fazer compilações para este chip, mas estamos nos sentindo um pouco expostos caso o mundo Linux avance a ponto de não podermos mais reconstruir um sistema que funcione o compilador (ou seja,nosso sistema 2.6.24 morre e não conseguimos instalar e executar o 2.6.24 em um novo sistema porque algumas partes do software não estão mais disponíveis).
Alguém tem alguma idéia do que poderíamos fazer em uma instalação mais moderna para executar esse compilador legado?
Editar:
Respondendo a alguns comentários...
Infelizmente, são as alterações no código-fonte específicas do nosso chip que são perdidas.Essa perda ocorreu em duas grandes reorganizações de empresas e vários administradores de sistemas (alguns dos quais realmente deixaram uma bagunça).Agora usamos o controle de configuração, mas isso significa fechar a porta do celeiro tarde demais para esse problema.
O uso de uma VM é uma boa ideia e pode ser o que acabaremos fazendo.Obrigado por essa ideia.
Finalmente, tentei strace conforme sugerido pelo ephemient e descobri que a última chamada do sistema foi brk() que retornou um erro no novo sistema (kernel 2.6.26) e retornou sucesso no sistema antigo (kernel 2.6.24).Isso indicaria que realmente estou ficando sem memória virtual, exceto que tcsh "limit" retorna os mesmos valores em sistemas antigos e novos, e /proc/meminfo mostra que os novos sistemas têm um pouco mais de memória e um pouco mais de espaço de troca.Talvez seja um problema de fragmentação ou de onde o programa está sendo carregado?
Eu fiz mais algumas pesquisas e a "randomização brk" foi adicionada no kernel 2.6.25, no entanto CONFIG_COMPAT_BRK
é supostamente habilitado por padrão (o que desativa a randomização brk).
Editar:
OK, mais informações:Realmente parece que a randomização brk é a culpada, o gcc legado está chamando brk() para alterar o final do segmento de dados e agora falha, fazendo com que o gcc legado relate "memória virtual esgotada".Existem algumas maneiras documentadas de desabilitar a randomização do brk:
sudo echo 0 > /proc/sys/kernel/randomize_va_space
sudo sysctl -w kernel.randomize_va_space=0
iniciando um novo shell com
setarch i386 -R tcsh
(ou "-R -L")
Eu os experimentei e eles parecem ter um efeito no sentido de que o valor de retorno do brk() é diferente (e sempre o mesmo) do que sem eles (tentei no kernel 2.6.25 e 2.6.26), mas o brk() ainda falha, então o gcc legado ainda falha :-(.
Além disso eu configurei vm.legacy_va_layout=1
e vm.overcommit_memory=2
sem alterações, e eu reiniciei com o vm.legacy_va_layout=1
e kernel.randomize_va_space=0
configurações salvas em /etc/sysctl.conf.Ainda não há mudança.
Editar:
Usando kernel.randomize_va_space=0
no kernel 2.6.26 (e 2.6.25) resulta na seguinte chamada brk() sendo relatada por strace legacy-gcc
:
brk(0x80556d4) = 0x8056000
Isso indica que brk() falhou, mas parece que falhou porque o segmento de dados já termina além do que foi solicitado.Usando objdump, posso ver que o segmento de dados deve terminar em 0x805518c, enquanto o brk() com falha indica que o segmento de dados atualmente termina em 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 ecoar o comentário do efemiente abaixo:“Tão estranho tratar o GCC como um binário sem fonte”!
Portanto, usando strace, objdump, gdb e meu conhecimento limitado de assembler e arquitetura 386, rastreei o problema até a primeira chamada de malloc no código legado.O gcc legado chama malloc, que retorna NULL, o que resulta na mensagem "memória virtual esgotada" no stderr.Este malloc está em libc.so.5, e chama Getenv várias vezes e acaba chamando Brk () ...Acho que para aumentar a pilha ...que falha.
A partir disso, só posso supor que o problema é mais do que a randomização do brk, ou não desativei totalmente a randomização do brk, apesar das configurações de sysctl randomize_va_space=0 e legado_va_layout=1.
Solução
Instale o linux + o antigo gcc em uma máquina virtual.
Outras dicas
Você tem o fontes para este compilador personalizado?Se você puder recuperar a linha de base 2.6.0 (e isso deve ser relativamente fácil), então diff e patch deverão recuperar seu conjunto de alterações.
O que eu recomendo é usar esse conjunto de alterações para criar uma nova versão em relação ao gcc atualizado.E ENTÃO COLOQUE-O SOB CONTROLE DE CONFIGURAÇÃO.
Desculpe, não quero gritar.É que venho dizendo a mesma coisa há quase 30 anos.
Você pode strace
o gcc-2.6.0
executável?Pode estar fazendo algo como ler /proc/$$/maps
, e ficar confuso quando a saída muda de maneira insignificante.Um problema semelhante foi notei recentemente entre 2.6.28 e 2.6.29.
Se sim, você pode hackear /usr/src/linux/fs/proc/task_mmu.c
ou algo próximo para restaurar a saída antiga ou configurar algum $LD_PRELOAD
fingir gcc
em ler outro arquivo.
Editar
Desde que você mencionou brk
...
CONFIG_COMPAT_BRK
faz o padrão kernel.randomize_va_space=1
em vez de 2
, mas isso ainda randomiza tudo, exceto o heap (brk
).
Veja se o seu problema desaparece se você echo 0 > /proc/sys/kernel/randomize_va_space
ou sysctl kernel.randomize_va_space=0
(equivalente).
Se sim, adicione kernel.randomize_va_space = 0
para /etc/sysctl.conf
ou adicione norandmaps
para a linha de comando do kernel (equivalente) e seja feliz novamente.
eu me deparei esse e pensei sobre o seu problema.Talvez você encontre uma maneira de brincar com o binário para movê-lo para o formato ELF?Ou pode ser irrelevante, mas brincar com o objdump pode fornecer mais informações.
Você pode dar uma olhada no mapa de memória do processo?
Então eu resolvi algo...não é uma solução completa, mas supera o problema original que tive com o gcc legado.
Colocando pontos de interrupção em cada chamada libc no .plt (tabela de ligação de procedimentos), vejo que malloc (em libc.so.5) chama getenv() para obter:
MALLOC_TRIM_THRESHOLD_ MALLOC_TOP_PAD_ MALLOC_MMAP_THRESHOLD_ MALLOC_MMAP_MAX_ MALLOC_CHECK_
Então eu pesquisei isso na web e encontrei esse que aconselhou
setenv MALLOC_TOP_PAD_ 536870912
então o legado gcc FUNCIONA!!!!
Mas não é gratuito, ele atingiu o link na compilação antes de falhar, então há algo mais acontecendo com o nld legado que temos :-( Está relatando:
Virtual memory exceeded in `new'
Em /etc/sysctl.conf eu tenho:
kernel.randomize_va_space=0 vm.legacy_va_layout=1
Ainda funciona da mesma forma se
kernel.randomize_va_space=1 vm.legacy_va_layout=0
mas não se
kernel.randomize_va_space=2
Houve uma sugestão de usar "ldd" para ver as dependências da biblioteca compartilhada:O Legacy GCC precisa apenas de libc5, mas o NLD legado também precisa de libg ++. So.27, libstdc ++. ??) e e quanto a libc5-compat?
Então, como eu disse, ainda não estou livre para casa...estar se aproximando.Provavelmente postarei uma nova pergunta sobre o problema do nld.
Editar:
Originalmente, eu iria me abster de "Aceitar" esta resposta, pois ainda tenho um problema com o vinculador legado correspondente, mas para obter pelo menos alguma finalidade nesta questão, estou repensando essa posição.
Os agradecimentos vão para:
- an0nym0usc0ward pela sugestão de usar uma VM (que pode eventualmente se tornar a resposta aceita)
- efemiente por sugerir o uso de strace e ajuda com o uso de stackoverflow
- shodanex por sugerir o uso de objdump
Editar
Abaixo estão as últimas coisas que aprendi, e agora aceitarei a solução VM já que não consegui resolver totalmente de outra forma (pelo menos no tempo previsto para isso).
Os kernels mais novos têm um sinalizador de construção CONFIG_COMPAT_BRK para permitir que libc5 seja usado, então, presumivelmente, construir um novo kernel com este sinalizador resolverá o problema (e olhando através do kernel src, parece que sim, mas não posso ter certeza, já que fiz isso não seguir todos os caminhos).Há também outra maneira documentada de permitir o uso da libc5 em tempo de execução (em vez de em tempo de construção do kernel):sudo sysctl -w kernel.randomize_va_space=0.Isso, no entanto, não faz um trabalho completo e alguns (a maioria?) Aplicativos libc5 ainda quebrarão, por exemplo,nosso compilador e vinculador legado.Isto parece ser devido a uma diferença nas suposições de alinhamento entre os kernels mais novos e mais antigos.Eu corrigi o binário do vinculador para fazê-lo pensar que tem uma seção bss maior, a fim de trazer o final do bss até um limite de página, e isso funciona no kernel mais recente quando o sysctl var kernel.randomize_va_space=0.Esta NÃO é uma solução satisfatória para mim, já que estou corrigindo cegamente um executável binário crítico e, embora a execução do vinculador corrigido no kernel mais recente tenha produzido uma saída idêntica em bits ao vinculador original executado no kernel mais antigo, isso não prova que alguma outra entrada do vinculador (ou seja,mudarmos o programa que está sendo vinculado) também produzirá resultados idênticos.
Você não poderia simplesmente criar uma imagem de disco que possa ser reinstalada se o sistema falhar?ou fazer uma VM?