Depois de ler as notícias “
Código interpretador Perl portado oficialmente para o GitHub ” no LINUX.ORG.RU, decidi dar uma olhada no repositório Perl 5, que já está no GitHub.
É incrível como tremendamente e qualitativamente eles o transferiram, preservando não apenas absolutamente toda a história de 32 anos do projeto, mas também relatórios de erros (entrando em problemas), patches (entrando em PRs), lançamentos e ramificações. A inscrição "
32 years ago " ao lado dos arquivos causa um sorriso involuntário.
O que mais fazer nesta sexta-feira monótona, quando chuva e neve desagradam na rua, e todos os caminhos estão atolados na lama do outono? É isso mesmo, olhos vermelhos! Portanto, para fins de experiência e interesse, decidi montar e montar o Perl antigo em uma moderna máquina x86_64 com a versão mais recente do
GCC 9.2.0 como compilador. Esse código antigo pode passar no teste do tempo?
Demonstração do twm , um dos primeiros gerenciadores de janelas do X Window System, em uma distribuição moderna do Arch Linux.Para ser completamente autêntico e nekromantnenko, implantei uma máquina virtual com o X e o gerenciador de janelas bare, que também é de 1987. Quem sabe, talvez
Larry Wall tenha escrito seu Perl usando exatamente dois
twm , por assim dizer, a
tecnologia de ponta da época. A distribuição usada é o Arch Linux. Só porque existem algumas coisas úteis em seu repositório que foram úteis mais tarde. Então vamos lá!
Conteúdo:
1. Preparação do meio ambiente2. Configurando o código fonte3. Erros no arquivo de gramática Yacc4. Erros de compilação do código em "C"5. Correção de alguns erros Falha na segmentação6. Resumir1. Preparação do meio ambiente
Primeiro, instalamos em um sistema operacional implantado em uma máquina virtual todo o conjunto de utilitários e compiladores necessários para montar e editar o código-fonte:
gcc ,
make ,
vim ,
git ,
gdb , etc. Alguns deles já estão instalados, enquanto outros estão disponíveis no meta-pacote
base-devel , ele deve ser instalado se não estiver instalado. Depois que o ambiente estiver pronto para a ação, obtemos uma cópia do código-fonte Perl de 32 anos!
$ git clone https://github.com/Perl/perl5/ --depth=1 -b perl-1.0
Graças aos recursos do Git, não precisamos arrastar muitos arquivos para chegar ao primeiro lançamento do projeto:
* commit 8d063cd8450e59ea1c611a2f4f5a21059a2804f1 (grafted, HEAD, tag: perl-1.0) Commit: Larry Wall <lwall@jpl-devvax.jpl.nasa.gov> CommitDate: Fri Dec 18 00:00:00 1987 +0000 a "replacement" for awk and sed
Apenas baixamos uma pequena quantidade de dados e, como resultado, o repositório com o código-fonte da primeira versão do Perl leva apenas 150 KB.
Naquele momento sombrio e denso, não havia algo elementar como
autotools (
que bênção! ). No entanto, existe um script
Configure na raiz do repositório. Qual é o problema? Mas o fato é que Larry Wall é o inventor de tais scripts que permitiram gerar Makefiles para as máquinas UNIX mais heterogêneas da época. Como diz o artigo da Wikipedia sobre os scripts de
mesmo nome , Larry Wall forneceu ao arquivo
Configure alguns de seus softwares, por exemplo, um leitor de notícias
rn , mais três anos antes de escrever Perl. Posteriormente, o Perl não foi exceção, e um script que já foi executado em muitas máquinas foi usado para construí-lo. Mais tarde, outros desenvolvedores, por exemplo, programadores da Trolltech, também adotaram essa idéia. Eles usaram um script semelhante para configurar a construção de sua estrutura Qt, que muitas pessoas confundem com o
configure a partir de
ferramentas automáticas . Foi o zoológico desses scripts de diferentes desenvolvedores que serviu de impulso para a criação de ferramentas para sua geração simplificada e automática.
<< Pular para o conteúdo2. Configurando o código fonte
O script
Configure da "velha escola", que já é evidente em seu
Shebang ', que tem um espaço:
$ cat Configure | head -5
De acordo com o comentário, verifica-se que havia conchas nos scripts dos quais não era possível deixar comentários! A situação espacial parece incomum, mas uma vez que essa era a norma, consulte o link para mais informações
aqui . Mais importante ainda, não há diferença para os intérpretes modernos de shell, independentemente de haver um espaço ou não.
Chega de letras, vamos ao que interessa! Começamos o script e vemos uma suposição interessante, que acaba não sendo inteiramente verdadeira:
$ ./Configure (I see you are using the Korn shell. Some ksh's blow up on Configure, especially on exotic machines. If yours does, try the Bourne shell instead.) Beginning of configuration questions for perl kit. Checking echo to see how to suppress newlines... ...using -n. Type carriage return to continue. Your cursor should be here-->
Surpreendentemente, o script é interativo e contém várias informações de segundo plano. O modelo de interação do usuário é construído em diálogos, analisando as respostas para as quais o script altera seus parâmetros, de acordo com o qual ele subsequentemente irá gerar Makefiles. Eu estava pessoalmente interessado em verificar se todos os comandos do shell estão em vigor?
Locating common programs... expr is in /bin/expr. sed is in /bin/sed. echo is in /bin/echo. cat is in /bin/cat. rm is in /bin/rm. mv is in /bin/mv. cp is in /bin/cp. tr is in /bin/tr. mkdir is in /bin/mkdir. sort is in /bin/sort. uniq is in /bin/uniq. grep is in /bin/grep. Don't worry if any of the following aren't found... test is in /bin/test. egrep is in /bin/egrep. I don't see Mcc out there, offhand.
Aparentemente, antes disso estava longe do caso. Gostaria de saber por que o utilitário
Mcc é responsável, o que não foi encontrado? O engraçado é que esse script, nas melhores tradições de hackers da época, é cheio de humor amigável. Agora você dificilmente verá isso:
Is your "test" built into sh? [n] (OK to guess) OK Checking compatibility between /bin/echo and builtin echo (if any)... They are compatible. In fact, they may be identical. Your C library is in /lib/libc.a. You're normal. Extracting names from /lib/libc.a for later perusal...done Hmm... Looks kind of like a USG system, but we'll see... Congratulations. You aren't running Eunice. It's not Xenix... Nor is it Venix... Checking your sh to see if it knows about # comments... Your sh handles # comments correctly. Okay, let's see if #! works on this system... It does. Checking out how to guarantee sh startup... Let's see if '#!/bin/sh' works... Yup, it does.
Eu respondi a maioria das perguntas com o valor padrão ou com o que o script me ofereceu. Particularmente satisfeito e surpreso foi o pedido de sinalizadores para o compilador e o vinculador:
Any additional cc flags? [none] Any additional ld flags? [none]
Lá, você pode escrever algo interessante, por exemplo,
-m32 para criar um arquivo executável de 32 bits ou uma biblioteca, necessária durante a vinculação. Para a última pergunta do script:
Now you need to generate make dependencies by running "make depend". You might prefer to run it in background: "make depend > makedepend.out &" It can take a while, so you might not want to run it right now. Run make depend now? [n] y
Eu respondi positivamente. A julgar pela
página da Wikipedia, o antigo utilitário
makedepend foi criado no início da vida do
projeto Athena para facilitar o trabalho com Makefiles. Este projeto nos deu o Sistema X Window, Kerberos, Zephyr e influenciou muitas outras coisas que hoje são familiares. Tudo isso é maravilhoso, mas de onde vem esse utilitário em um ambiente Linux moderno? Ele tem sido usado por ninguém e em qualquer lugar. Mas se você olhar atentamente para a raiz do repositório, acontece que Larry Wall escreveu sua versão de script substituta, que cuidadosamente desempacotamos e executamos o script de configuração.
O Makedepend foi concluído com alguns erros estranhos:
./makedepend: command substitution: line 82: unexpected EOF while looking for matching `'' ./makedepend: command substitution: line 83: syntax error: unexpected end of file ./makedepend: command substitution: line 82: unexpected EOF while looking for matching `'' ./makedepend: command substitution: line 83: syntax error: unexpected end of file
Talvez tenham sido eles que causaram o problema, devido ao qual os Makefiles gerados foram um pouco mastigados:
$ make make: *** No rule to make target '<built-in>', needed by 'arg.o'. Stop.
Eu absolutamente não queria ir para a selva dos intrincados macarronetes do utilitário
makedepend e decidi examinar cuidadosamente os Makefiles, nos quais emergia um padrão estranho:
arg.o: arg.c arg.o: arg.h arg.o: array.h arg.o: <built-in> arg.o: cmd.h arg.o: <command-line> arg.o: config.h arg.o: EXTERN.h ... array.o: arg.h array.o: array.c array.o: array.h array.o: <built-in> array.o: cmd.h array.o: <command-line> array.o: config.h array.o: EXTERN.h ...
Aparentemente, algum utilitário inseriu incorretamente seus argumentos no escapamento. Pegando o utilitário
ax sed, decidi arrumar um pouco isso:
$ sed -i '/built-in/d' Makefile $ sed -i '/command-line/d' Makefile
Surpreendentemente, o truque funcionou e os Makefiles funcionaram como deveriam!
<< Pular para o conteúdo3. Erros no arquivo de gramática Yacc
Seria inacreditável se o código de 32 anos pegasse e montasse sem problemas. Infelizmente, milagres não acontecem. Estudando a árvore de origem, deparei-me com um arquivo
perl.y , que é uma descrição gramatical do utilitário
yacc , que há muito tempo é substituído pelo
bison nas distribuições modernas. O script localizado no caminho
/ usr / bin / yacc simplesmente chama o
bison no modo de compatibilidade com o
yacc . Só que essa compatibilidade não está completa e, ao processar esse arquivo, estão surgindo muitos erros, que eu não sei como corrigir e realmente não quero, porque há uma solução alternativa que aprendi recentemente.
Há apenas um ou dois anos, Helio Chissini de Castro, desenvolvedor do KDE, fez um trabalho semelhante e adaptou o KDE 1, 2 e o Qt 1, 2 aos ambientes e compiladores modernos. Interessei-me pelo trabalho dele, baixei os códigos-fonte dos projetos, mas durante a montagem me deparei com uma
armadilha semelhante por causa da incompatibilidade de
yacc e
bison , que foram usados para criar a versão antiga do metacompiler
moc . Posteriormente, consegui encontrar uma solução para esse problema na forma de substituir o
bison pelo utilitário
byacc (Berkeley Yacc), que acabou sendo compatível com as gramáticas antigas do
yacc e estava disponível em muitas distribuições Linux.
Uma simples substituição do
yacc pelo
byacc no sistema de compilação me ajudou, mas não por muito tempo, porque um pouco mais tarde nas novas versões do
byacc eles ainda quebravam a compatibilidade com o
yacc , interrompendo a depuração associada à entidade
yydebug . Portanto, tive que
corrigir um pouco
a gramática do utilitário.
Portanto, a estratégia para corrigir erros de construção no arquivo
perl.y foi prevista pela experiência anterior: instale o utilitário
byacc , altere
yacc para
byacc em todos os Makefiles e, em seguida, corte o
yydebug de qualquer lugar. Essas ações resolveram todos os problemas desse arquivo, os erros desapareceram e a compilação continuou.
<< Pular para o conteúdo4. Erros de compilação do código em "C"
O código antigo de Perl estava cheio de horrores, como a notação há muito obsoleta e esquecida das definições de função do tipo K&R:
format(orec,fcmd) register struct outrec *orec; register FCMD *fcmd; { ... } STR * hfetch(tb,key) register HASH *tb; char *key; { ... } fatal(pat,a1,a2,a3,a4) char *pat; { fprintf(stderr,pat,a1,a2,a3,a4); exit(1); }
Recursos semelhantes foram encontrados, por exemplo, no código do
Microsoft Word 1.1a , que também é bastante antigo. O primeiro padrão da linguagem de programação "C", chamado "C89", aparecerá apenas em dois anos. Compiladores modernos são capazes de trabalhar com esse código, mas alguns IDEs não facilitam a análise de tais definições e as destacam como erros de sintaxe, por exemplo, o
Qt Creator pecou antes de analisar o código na biblioteca
libclang .
O compilador GCC 9.2.0, lançando um grande número de avisos, se comprometeu a compilar o código antigo da primeira versão do Perl. As folhas dos avisos eram tão grandes que, para chegar ao erro, tivemos que rolar várias páginas do escapamento. Para minha surpresa, a maioria dos erros de compilação era típica e estava principalmente relacionada a definições predefinidas, que desempenhavam o papel de sinalizadores para a montagem.
O trabalho do moderno compilador GCC 9.2.0 e do depurador GDB 8.3.1 no gerenciador de janelas twm e no emulador de terminal xterm .Sob STDSTDIO
, Larry Wall experimentou alguma biblioteca de linguagem de programação antiga e fora do padrão “C”, e sob DEBUGGING
havia informações de depuração com o notório
yydebug , que mencionei acima. Por padrão, essas caixas de seleção foram ativadas. Desativando-os no arquivo
perl.h e adicionando algumas
definições esquecidas, consegui reduzir significativamente o número de erros.
Outro tipo de erro é substituir as funções agora padronizadas da biblioteca padrão e da camada POSIX. O projeto possui seu próprio
malloc () ,
setenv () e outras entidades que criaram conflitos.
Alguns lugares definiram funções estáticas sem declarações. Com o tempo, os compiladores começaram a adotar uma abordagem mais rígida para esse problema e
transformaram o aviso em erro . E, finalmente, alguns cabeçalhos esquecidos, para onde você iria sem eles.
Para minha surpresa, o patch para o código de 32 anos acabou sendo tão pequeno que pode ser totalmente citado aqui:
diff --git a/malloc.cb/malloc.c index 17c3b27..a1dfe9c 100644 --- a/malloc.c +++ b/malloc.c @@ -79,6 +79,9 @@ static u_int nmalloc[NBUCKETS]; #include <stdio.h> #endif +static findbucket(union overhead *freep, int srchlen); +static morecore(register bucket); + #ifdef debug #define ASSERT(p) if (!(p)) botch("p"); else static diff --git a/perl.hb/perl.h index 3ccff10..e98ded5 100644 --- a/perl.h +++ b/perl.h @@ -6,16 +6,16 @@ * */ -#define DEBUGGING -#define STDSTDIO /* eventually should be in config.h */ +//#define DEBUGGING +//#define STDSTDIO /* eventually should be in config.h */ #define VOIDUSED 1 #include "config.h" -#ifndef BCOPY -# define bcopy(s1,s2,l) memcpy(s2,s1,l); -# define bzero(s,l) memset(s,0,l); -#endif +//#ifndef BCOPY +//# define bcopy(s1,s2,l) memcpy(s2,s1,l); +//# define bzero(s,l) memset(s,0,l); +//#endif #include <stdio.h> #include <ctype.h> @@ -183,11 +183,11 @@ double atof(); long time(); struct tm *gmtime(), *localtime(); -#ifdef CHARSPRINTF - char *sprintf(); -#else - int sprintf(); -#endif +//#ifdef CHARSPRINTF +// char *sprintf(); +//#else +// int sprintf(); +//#endif #ifdef EUNICE #define UNLINK(f) while (unlink(f) >= 0) diff --git a/perl.yb/perl.y index 16f8a9a..1ab769f 100644 --- a/perl.y +++ b/perl.y @@ -7,6 +7,7 @@ */ %{ +#include <stdlib.h> #include "handy.h" #include "EXTERN.h" #include "search.h" diff --git a/perly.cb/perly.c index bc32318..fe945eb 100644 --- a/perly.c +++ b/perly.c @@ -246,12 +246,14 @@ yylex() static bool firstline = TRUE; retry: +#ifdef DEBUGGING #ifdef YYDEBUG if (yydebug) if (index(s,'\n')) fprintf(stderr,"Tokener at %s",s); else fprintf(stderr,"Tokener at %s\n",s); +#endif #endif switch (*s) { default: diff --git a/stab.cb/stab.c index b9ef533..9757cfe 100644 --- a/stab.c +++ b/stab.c @@ -7,6 +7,7 @@ */ #include <signal.h> +#include <errno.h> #include "handy.h" #include "EXTERN.h" #include "search.h" diff --git a/util.hb/util.h index 4f92eeb..95cb9bf 100644 --- a/util.h +++ b/util.h @@ -28,7 +28,7 @@ void prexit(); char *get_a_line(); char *savestr(); int makedir(); -void setenv(); +//void setenv(); int envix(); void notincl(); char *getval();
Ótimo resultado para código de 32 anos! A
referência indefinida ao bug de ligação
`crypt ' foi corrigida adicionando a diretiva
-lcrypt ao Makefile com a biblioteca
libcrypt apropriada, após a qual finalmente consegui o executável interpretador Perl desejado:
$ file perl perl: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=fd952ceae424613568530b3a2ca88ebd6477e0ae, for GNU/Linux 3.2.0, not stripped
<< Pular para o conteúdo5. Correção de alguns erros Falha na segmentação
Depois de uma compilação quase sem complicações, a sorte me deu as costas. Imediatamente após iniciar o interpretador Perl montado, obtive alguns erros estranhos e uma falha de segmentação no final:
$ ./perl -e 'print "Hello World!\n";' Corrupt malloc ptr 0x2db36040 at 0x2db36000 Corrupt malloc ptr 0x2db36880 at 0x2db36800 Corrupt malloc ptr 0x2db36080 at 0x2db36040 Corrupt malloc ptr 0x2db37020 at 0x2db37000 Segmentation fault (core dumped)
Tendo roído o texto de origem da frase
malloc corrompido , descobriu-se que, em vez do sistema
malloc (), algum tipo de alocador personalizado foi chamado a partir de 1982. Curiosamente,
Berkeley está escrito em uma das literais de string em seu código-fonte e
Caltech em um comentário ao lado. A colaboração entre essas universidades ficou evidente, então, muito forte. Em geral, comentei esse alocador de hackers e reconstruí o código fonte. Os erros de corrupção de memória desapareceram, mas a falha de segmentação permaneceu. Portanto, esse não era o ponto, e agora precisamos descobrir o depurador.
Executando o programa no
gdb, descobri que a falha ocorre quando a função de criar um arquivo temporário
mktemp () da libc é chamada:
$ gdb --args ./perl -e 'print "Hello, World!\n";' (gdb) r Starting program: /home/exl/perl5/perl -e print\ \"Hello\ World\!\\n\"\; Program received signal SIGSEGV, Segmentation fault. 0x00007ffff7cd20c7 in __gen_tempname () from /usr/lib/libc.so.6 (gdb) bt #0 0x00007ffff7cd20c7 in __gen_tempname () from /usr/lib/libc.so.6 #1 0x00007ffff7d71577 in mktemp () from /usr/lib/libc.so.6 #2 0x000055555556bb08 in main ()
A propósito, o vinculador jurou anteriormente nessa função. Não é um compilador, mas um vinculador, o que me surpreendeu:
/usr/bin/ld: perl.o: in function `main': perl.c:(.text+0x978c): warning: the use of `mktemp' is dangerous, better use `mkstemp' or `mkdtemp'
O primeiro pensamento que provavelmente veio à sua mente também foi substituir a
função insegura mktemp () por
mkstemp () , o que eu fiz. O aviso do vinculador desapareceu, mas a falha de segmentação permaneceu nesse local de qualquer maneira, só que agora estava na função
mkstemp () .
Portanto, agora você precisa examinar com muito cuidado o trecho de código associado a essa função. Lá, descobri uma coisa bastante estranha que é destacada neste trecho:
char *e_tmpname = "/tmp/perl-eXXXXXX"; int main(void) { mktemp(e_tmpname); e_fp = f_open(e_tmpname, "w"); ... }
Acontece que
mktemp () está tentando alterar o literal da máscara, localizada na seção
.rodata , que obviamente está fadada ao fracasso. Ou, afinal, 32 anos atrás, isso era aceitável, atendia ao código e até funcionava de alguma forma?
É claro que a substituição de
char * e_tmpname por
char e_tmpname [] corrigiu essa falha de segmentação e pude obter o que matei por toda a noite:
$ ./perl -e 'print "Hello World!\n";' $ Hello, World! $ ./perl -e '$a = 5; $b = 6.3; $c = $a+$b; print $c."\n";' $ 11.3000000000000007 $ ./perl -v $Header: perly.c,v 1.0 87/12/18 15:53:31 root Exp $ Patch level: 0
Verificamos a execução na linha de comando, mas e o arquivo? Eu baixei o primeiro “Hello World” para a linguagem de programação Perl da Internet:
Tentei executá-lo, mas, infelizmente, a falha de segmentação estava me esperando novamente. Desta vez em um lugar completamente diferente:
$ gdb --args ./perl test.pl (gdb) r Starting program: /home/exl/perl5/perl test.pl Program received signal SIGSEGV, Segmentation fault. 0x00007ffff7d1da75 in __strcpy_sse2_unaligned () from /usr/lib/libc.so.6 (gdb) bt #0 0x00007ffff7d1da75 in __strcpy_sse2_unaligned () from /usr/lib/libc.so.6 #1 0x00005555555629ea in yyerror () #2 0x0000555555568dd6 in yyparse () #3 0x000055555556bd4f in main ()
O seguinte ponto interessante foi encontrado na função
yyerror () , cito o trecho original:
Mais uma vez, a situação é semelhante à sobre a qual escrevi acima. Os dados na seção
.rodata são
modificados novamente . Talvez sejam apenas erros de digitação por causa do Copy-Paste e, em vez de
tname, eles quisessem escrever
tmpbuf ? Ou existe realmente algum tipo de significado oculto por trás disso? De qualquer forma, a substituição de
char * tokename [] por
char tokename [] [32] remove o erro de falha de segmentação e o Perl nos diz o seguinte:
$ ./perl test.pl syntax error in file test.pl at line 7, next token "strict" Execution aborted due to compilation errors.
Acontece que ele não gosta de todo tipo de
uso novo e
estrito , é o que ele está tentando nos dizer! Se você excluir ou comentar essas linhas no arquivo, o programa inicia:
$ ./perl test.pl Hello, World!
<< Pular para o conteúdo6. Resumir
De fato, alcancei meu objetivo e fiz o código antigo de 1987 não apenas compilar, mas também trabalhar em um ambiente Linux moderno. Sem dúvida, ainda resta uma grande pilha de vários erros de falha de segmentação, possivelmente relacionados ao tamanho do ponteiro em uma arquitetura de 64 bits. Tudo isso pode ser limpo após algumas noites com o depurador pronto. Mas essa não é uma tarefa muito agradável e entediante. Afinal, inicialmente esse experimento foi planejado como entretenimento para uma noite entediante, e não como um trabalho completo, que será encerrado. Existe algum benefício prático das ações tomadas? Talvez algum dia algum arqueólogo digital se depare com este artigo e será útil para ele. Mas no mundo real, mesmo a experiência extraída de tais pesquisas, na minha opinião, não é muito valiosa.
Se alguém estiver interessado, eu posto um conjunto de dois patches. O primeiro corrige erros de compilação e o segundo corrige alguns erros de falha de segmentação.
PS Eu me apresso em chatear os fãs de
jogadores destrutivos de linha única , isso não funciona aqui. Talvez a versão de Perl seja muito antiga para esse entretenimento.
PPS Tudo de bom e tenha um bom final de semana. Obrigado a
kawaii_neko por uma
pequena correção .
Atualização 28-Out-2019: Um usuário do fórum LINUX.ORG.RU, usando o apelido
utf8nowhere , forneceu links bastante interessantes
em seu comentário sobre este artigo, cujas informações não apenas esclarecem a situação com literais de string mutáveis, mas também consideram o problema de uso descrito acima funções
mktemp () ! Deixe-me citar essas fontes, que descrevem várias incompatibilidades entre o K&R C não padronizado e o GNU C:
Incompatibilidades do CCG
Existem várias incompatibilidades notáveis entre as versões GNU C e K&R (não ISO) do C.
O GCC normalmente cria constantes de seqüência de caracteres somente leitura. Se várias constantes de string com aparência idêntica forem usadas, o GCC armazenará apenas uma cópia da string.
Uma conseqüência é que você não pode chamar mktemp com um argumento constante de string. A função mktemp sempre altera a string para a qual seu argumento aponta.
Outra conseqüência é que o sscanf não funciona em alguns sistemas quando transmitida uma constante de cadeia como sua cadeia ou entrada de controle de formato.Isso ocorre porque o sscanf tenta gravar incorretamente na constante da string. Da mesma forma fscanf e scanf .
A melhor solução para esses problemas é alterar o programa para usar variáveis de matriz de caracteres com seqüências de caracteres de inicialização para esses fins, em vez de constantes de sequência. Mas se isso não for possível, você pode usar o sinalizador -fwritable-strings , que instrui o GCC a lidar com constantes de strings da mesma maneira que a maioria dos compiladores C.
Fonte: Usando o Manual Oficial da GNU Compiler Collection (GCC 3.3) .
O sinalizador do compilador -fwritable-strings foi descontinuado no GCC 3.4 e removido permanentemente no GCC 4.0.ANSI C rationale | String literals
String literals are specified to be unmodifiable. This specification allows implementations to share copies of strings with identical text, to place string literals in read-only memory, and perform certain optimizations. However, string literals do not have the type array of const char, in order to avoid the problems of pointer type checking, particularly with library functions, since assigning a pointer to const char to a plain pointer to char is not valid. Those members of the Committee who insisted that string literals should be modifiable were content to have this practice designated a common extension (see F.5.5).
Existing code which modifies string literals can be made strictly conforming by replacing the string literal with an initialized static character array. For instance,
char *p, *make_temp(char *str); p = make_temp("tempXXX");
can be changed to:
char *p, *make_temp(char *str); { static char template[ ] = "tempXXX"; p = make_temp( template ); }
: Rationale for American National Standard for Information Systems, Programming Language C .
O usuário VarfolomeyKote4ka propôs um hack sujo interessante que permite ignorar erros de falha de segmentação ao tentar alterar dados na seção .rodata , convertendo-os na seção .rwdata . Há pouco tempo, um artigo muito interessante apareceu na Internet “De .rodata a .rwdata - introdução ao mapeamento de memória e scripts LD” pelo programador guye1296 , que explica como fazer esse truque. Para facilitar a obtenção do resultado desejado, o autor do artigo preparou um script bastante volumoso para o vinculador padrão ld - rwdata.ld. Basta baixar esse script, colocá-lo na raiz do diretório de origem Perl, corrigir o sinalizador LDFLAGS da seguinte maneira: LDFLAGS = -T rwdata.ld e reconstruir o projeto. Como resultado, temos o seguinte: $ make clean && make -j1 $ mv perl perl_rodata $ curl -LOJ https://raw.githubusercontent.com/guye1296/ld_script_elf_blog_post/master/rwdata.ld $ sed -i 's/LDFLAGS =/LDFLAGS = -T rwdata.ld/' Makefile $ make clean && make -j1 $ mv perl perl_rwdata $ objdump -s -j .rodata perl_rodata | grep tmp -2 19da0 21233f5e 7e3d2d25 30313233 34353637 !
Acontece que, graças a esse hack, quase todas as alterações do segundo patch podem ser simplesmente omitidas! Embora, é claro, levar o código a uma aparência que não viole os padrões ainda é preferível.<< Pular para o conteúdo