Jump to content

"Segmentation fault" no "Hello World!"


Fabiano Furtado

Recommended Posts

  • Supporter - Nibble

Pessoal...

fiz um "Hello World!" em C para testes e estou tendo um "Segmentation fault". Vou reproduzir os passos que fiz.

Primeiramente, compilei com "gcc -Wall -O3 -save-temps hello.c -o hello". Até aí, tudo certo. Execução sem erros.

Como queria fazer a link edição "na mão", dei um "ldd hello" e...

        linux-vdso.so.1 (0x00007fffb3558000)
        libc.so.6 => /usr/lib/libc.so.6 (0x00007fc34bbc7000)
        /lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007fc34bde6000)

Após isso, link editei o arquivo com o comando "ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 /usr/lib/libc.so.6  hello.o -o hello", recebi um "ld: warning: cannot find entry symbol _start; defaulting to 0000000000401020" e executei...

$ ./hello
Hello World!
Segmentation fault (core dumped)


Fiz um debug no GDB e no ret da main() o RIP apresenta um valor esquisito!

O que estou fazendo de errado nesta link edição?

Desde já, agradeço.

Link to comment
Share on other sites

Provavelmente seu GCC está instalado errado. Não consigo reproduzir o erro com o simples código de um "hello world":

#include <stdio.h>

int main( void ) { puts( "Hello, world!" ); }

Por que você está mantendo os arquivos temporários (-save-temps)?
 

PS: Que /usr/lib64/  é esse? Normalmente esse diretório não existe em distros Linux...

Link to comment
Share on other sites

  • Supporter - Nibble
Em 27/09/2019 em 12:36, fredericopissarra disse:

Provavelmente seu GCC está instalado errado. Não consigo reproduzir o erro com o simples código de um "hello world":


#include <stdio.h>

int main( void ) { puts( "Hello, world!" ); }

Por que você está mantendo os arquivos temporários (-save-temps)?
 

PS: Que /usr/lib64/  é esse? Normalmente esse diretório não existe em distros Linux...

Bom... o GCC não está instalado errado. Isso eu posso te garantir.

Eu uso o Arch Linux e nele há links simbólicos apontando para /lib64/ld-2.29.so e /usr/lib64/ld-2.29.so, que são arquivos idênticos.

Eu mantive os arquivos temporários para poder utilizar o hello.o no ld e usar o ld para fazer a link edição, e não o GCC.

Utilizando-se o GCC, o erro não ocorre, mas usando o ld, sim.

Link to comment
Share on other sites

Não há necessidade de usar -save-temps para compilar o fonte apenas para o objeto, basta fazer:

$ gcc -O3 -c -o hello.o hello.c   # Compila para o objeto hello.o

A opção -c faz isso. Quanto ao ld, qual a linha de comando você está usando? Note que código em C exige a linkagem de uma série de outros objetos (Scrt0.o, crti.o, crtn.o), bem como outras libs além da libc (libgcc, por exemplo). O GCC toma conta disso sozinho, o ld, não.

Veja algumas linhas de comando usadas pelo GCC adicionando a opção -v

Link to comment
Share on other sites

  • Supporter - Nibble
16 horas atrás, fredericopissarra disse:

Não há necessidade de usar -save-temps para compilar o fonte apenas para o objeto, basta fazer:


$ gcc -O3 -c -o hello.o hello.c   # Compila para o objeto hello.o

A opção -c faz isso. Quanto ao ld, qual a linha de comando você está usando? Note que código em C exige a linkagem de uma série de outros objetos (Scrt0.o, crti.o, crtn.o), bem como outras libs além da libc (libgcc, por exemplo). O GCC toma conta disso sozinho, o ld, não.

Veja algumas linhas de comando usadas pelo GCC adicionando a opção -v

Oi Frederico... agora sim! Usando alguns parâmetros que a opção -v me mostrou, eu consegui fazer o hello world funcionar e entender o problema. Muito obrigado!

Link to comment
Share on other sites

  • Supporter - Nibble
Em 30/09/2019 em 11:51, Fernando Mercês disse:

Posta aí como, vai! :D

Fiquei curioso hehehehe

Opa...

Segue a linha...

$ ld --hash-style=gnu -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie -o hello /usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/../../../../lib/Scrt1.o /usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/../../../../lib/crti.o /usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/crtbeginS.o -L/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0 -L/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/../../../../lib -L/lib/../lib -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/../../.. -lgcc --push-state --as-needed -lgcc_s --pop-state -lc -lgcc --push-state --as-needed -lgcc_s --pop-state /usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/crtendS.o /usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/../../../../lib/crtn.o hello.o

Link to comment
Share on other sites

2 horas atrás, Fabiano Furtado disse:

Opa...

Segue a linha...

$ ld --hash-style=gnu -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie -o hello /usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/../../../../lib/Scrt1.o /usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/../../../../lib/crti.o /usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/crtbeginS.o -L/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0 -L/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/../../../../lib -L/lib/../lib -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/../../.. -lgcc --push-state --as-needed -lgcc_s --pop-state -lc -lgcc --push-state --as-needed -lgcc_s --pop-state /usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/crtendS.o /usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/../../../../lib/crtn.o hello.o

Algumas coisas podem ser retiradas... Eis um makefile para seu hello.c:
 

CFLAGS=-O2 -fno-stack-protector

# No meu caso, as libs do GCC 7 estão no diretório CRT_GCC_PATH.
# CRT de "C RunTime".
CRT_GCC_PATH=/usr/lib/gcc/x86_64-linux-gnu/7
CRT_PATH=/usr/lib/x86_64-linux-gnu/

# O linker precisa do plugin dynamic linker para linkar códigos do GCC (por quê executáveis no modo x86-64 são, essencialmente, shared
# objects que são carregados por esse "runtime");
#
# O executável precisa ser PIE (para o x86-64);
#
# Scrt1.o, crti.o, crtbeginS.o, crtendS.o e crtn.o precisam ser linkados junto com libgcc_s e libgcc, bem como a libc.
# Esses são os códigos de inicialização e finalização da libc (e builtins do GCC).
#
# O formato do elf é elf_x86_64 por default, assim a opção -m é desnecessária.
LDFLAGS=--dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie -L$(CRT_GCC_PATH) -L$(CRT_PATH) -s
CRT_OBJECTS=$(CRT_PATH)/Scrt1.o $(CRT_PATH)/crti.o $(CRT_GCC_PATH)/crtbeginS.o $(CRT_GCC_PATH)/crtendS.o $(CRT_PATH)/crtn.o
LIBS=-lc -lgcc_s -lgcc

# Em essência isso faz a mesma coisa que `gcc -o hello hello.o`.
# Adicionei um exemplo onde apago a section .comment do binário final, diminuindo o tamanho um pouquinho...
hello: hello.o Makefile
	$(LD) $(LDFLAGS) -o $@.elf $(CRT_OBJECTS) $< $(LIBS)
	objcopy -R .comment $@.elf $@ && rm $@.elf

hello.o: hello.c

Como demonstração da alegação do executável ser um shared object:

$ make
cc -O2 -fno-stack-protector   -c -o hello.o hello.c
ld --dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie -L/usr/lib/gcc/x86_64-linux-gnu/7 -L/usr/lib/x86_64-linux-gnu/ -s -o hello.elf /usr/lib/x86_64-linux-gnu//Scrt1.o /usr/lib/x86_64-linux-gnu//crti.o /usr/lib/gcc/x86_64-linux-gnu/7/crtbeginS.o /usr/lib/gcc/x86_64-linux-gnu/7/crtendS.o /usr/lib/x86_64-linux-gnu//crtn.o hello.o -lc -lgcc_s -lgcc
objcopy -R .comment hello.elf hello && rm hello.elf

$ ./hello
Hello

$ file hello
hello: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 3.2.0, stripped

 

Link to comment
Share on other sites

  • 2 weeks later...
  • Supporter - Nibble
Em 07/10/2019 em 14:48, fredericopissarra disse:

Algumas coisas podem ser retiradas... Eis um makefile para seu hello.c:
.....

Como demonstração da alegação do executável ser um shared object:

.....

Cara... vc é o mestre!!!! Achei muito legal esse make! Depois vou testar...

Em tempo, já que vc usou o objcopy para diminuir o tamanho do binário (aliás, achei bem legal isso), reparei que os meus binários estavam ficando MUITO grandes (mesmo os mais simples) e descobri o motivo.

A partir da versão 2.30 (ou 2.31.. não sei ao certo) do LD, há um novo parâmetro para fazer a link edição chamado "-z noseparate-code". O default passa a ser "-z separate-code" e isso faz com que o linker preencha o binário com vários Null Bytes entre as seções.

Eu li sobre esse parâmetro e o motivo da existência seria uma maior segurança contra ataques, em prol de um binário maior e relativamente mais lento.

Você sabe me dizer mais sobre o uso desse parâmetro? Eu particularmente prefiro um binário menor.

Link to comment
Share on other sites

53 minutos atrás, Fabiano Furtado disse:

A partir da versão 2.30 (ou 2.31.. não sei ao certo) do LD, há um novo parâmetro para fazer a link edição chamado "-z noseparate-code". O default passa a ser "-z separate-code" e isso faz com que o linker preencha o binário com vários Null Bytes entre as seções.

Eu li sobre esse parâmetro e o motivo da existência seria uma maior segurança contra ataques, em prol de um binário maior e relativamente mais lento.

Você sabe me dizer mais sobre o uso desse parâmetro? Eu particularmente prefiro um binário menor.

Pelo que vi, isso garante que a página onde a sessão de código (.text e outras semelhantes) seja carregadas será isolada das páginas de dados e pilha. Isso permite o uso do bit NX na tabela de página, protegendo o código e evitando que você crie um "shell code"... Quanto ao preenchimento, provavelmente isso não tem nenhum problema, já que, mesmo que a imagem binária seja pequena, o mínimo que será alocado na memória será algumas páginas de 4 KiB (pelo menos algumas para código: SEU código + códigos de inicialização da libc como todos aqueles ?crt*.o; algumas para dados (acho que a libc aloca, pelo menos 128 KiB; e pelo menos uma para a pilha)...

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...