Por que nem todo código compilado é independente da posição?

87

Ao compilar bibliotecas compartilhadas no gcc, a opção -fPIC compila o código como independente da posição. Existe algum motivo (desempenho ou outro) pelo qual você não compilaria todas as posições do código independentemente?

ojblass
fonte
2
Mas wowest não está totalmente correto. Muitas chamadas de função e saltos usam saltos relativos, portanto, nem precisam de uma tabela de saltos após serem movidos.
Desconhecido
olhando para o código assembly gerado, parece que o endereço da função é carregado, enquanto o código não fpic parece ser simplesmente um salto. Estou entendendo mal sua declaração?
ojblass
@ojblass o que quero dizer é que alguns saltos são como "pule 50 instruções à frente daqui" ou "pule 5 instruções para trás" em vez de "pule para 0x400000". Portanto, dizer que você tem que carregar um endereço toda vez com -fPIC não é totalmente verdade.
Desconhecido
O artigo da Wikipedia fornece uma boa descrição. Basicamente, em algumas arquiteturas, não há uma maneira direta de pular para um endereço relativo. Conseqüentemente, o PIC é mais caro para usar nesses arcos. Veja a resposta de @ EvanTeran para mais informações.
Alexei Sholik

Respostas:

68

Ele adiciona uma indireção. Com o código independente de posição, você deve carregar o endereço de sua função e então pular para ela. Normalmente, o endereço da função já está presente no fluxo de instruções.

wowest
fonte
33

Este artigo explica como o PIC funciona e o compara com a alternativa - realocação do tempo de carregamento . Acho que é relevante para a sua pergunta.

Eli Bendersky
fonte
16
@ Nick: Eu discordo. Se ajudar quem pergunta, é uma resposta. Apontar para um ou dois artigos relevantes pode fornecer uma riqueza de informações.
Eli Bendersky
5
Não há nenhuma conclusão neste post, apenas um link para um artigo. Nem mesmo uma pista de que o PIC não é usado por padrão devido a problemas de desempenho.
Nick
10
Embora este link possa responder à pergunta, é melhor incluir as partes essenciais da resposta aqui e fornecer o link para referência. As respostas somente com link podem se tornar inválidas se a página vinculada mudar.
Rob
4
@Rob: o produtivo seria sugerir uma edição e não usar comentários para reclamar. Esta resposta tem 4 anos. Naquela época, a SO tinha regras menos rígidas sobre como uma resposta deveria parecer
Eli Bendersky
6
Esta postagem apareceu em "revisão" solicitando que eu o fizesse e assim fiz. Outra pessoa sinalizou. O "comentário lamentável" é produzido automaticamente por SO, não por mim.
Rob
27

Sim, existem motivos de desempenho. Alguns acessos estão efetivamente sob outra camada de indireção para obter a posição absoluta na memória.

Existe também o GOT (tabela de deslocamento global) que armazena deslocamentos de variáveis ​​globais. Para mim, isso se parece apenas com uma tabela de ajuste de IAT, que é classificada como dependente da posição pela Wikipedia e algumas outras fontes.

http://en.wikipedia.org/wiki/Position_independent_code

Desconhecido
fonte
23

Além da resposta aceita. Uma coisa que prejudica muito o desempenho do código PIC é a falta de "endereçamento relativo de IP" no x86. Com o "endereçamento relativo de IP", você pode solicitar dados que são X bytes do ponteiro de instrução atual. Isso tornaria o código PIC muito mais simples.

Saltos e chamadas são geralmente relativos ao EIP, então esses não representam realmente um problema. No entanto, o acesso aos dados exigirá alguns truques extras. Às vezes, um registro será temporariamente reservado como um "ponteiro base" para os dados que o código requer. Por exemplo, uma técnica comum é abusar da maneira como as chamadas funcionam no x86:

Esta e outras técnicas adicionam uma camada de indireção aos acessos de dados. Por exemplo, o GOT (tabela de deslocamento global) usado por compiladores gcc.

x86-64 adicionou um modo "RIP relativo" que torna as coisas muito mais simples.

Evan Teran
fonte
1
IIRC MIPS não tem endereçamento relativo ao PC também, exceto para saltos relativos
phuclv
1
Esta é uma técnica comum usada em shellcode para obter o endereço a partir do qual está executando. Eu usei isso em algumas soluções CTF.
Sherrellbc
2

Porque a implementação de código totalmente independente de posição adiciona uma restrição ao gerador de código que pode impedir o uso de operações mais rápidas ou adiciona etapas extras para preservar essa restrição.

Isso pode ser uma troca aceitável para obter multiprocessamento sem um sistema de memória virtual, onde você confia nos processos para não invadir a memória uns dos outros e pode precisar carregar um aplicativo específico em qualquer endereço base.

Em muitos sistemas modernos, as compensações de desempenho são diferentes, e um carregador de realocação geralmente é menos caro (custa sempre que o código é carregado pela primeira vez) do que o melhor que um otimizador pode fazer se tiver rédea solta. Além disso, a disponibilidade de espaços de endereço virtual oculta a maior parte da motivação para independência de posição em primeiro lugar.

RBerteig
fonte
1

Além disso, o hardware de memória virtual na maioria dos processadores modernos (usados ​​pela maioria dos sistemas operacionais modernos) significa que muitos códigos (todos os aplicativos de espaço do usuário, exceto o uso peculiar de mmap ou semelhantes) não precisam ser independentes da posição. Cada programa obtém seu próprio espaço de endereço que pensa começar do zero.

smcameron
fonte
4
Porém, mesmo com um código VM-MMU PIC, é necessário garantir que a mesma biblioteca .so seja carregada apenas uma vez na memória quando for usada por diferentes executáveis.
mmmmmmmm
1

position-independent code tem uma sobrecarga de desempenho na maioria das arquiteturas, porque requer um registro extra.

Então, isso é para fins de desempenho.

Trump 2020 - Justiça virá
fonte
0

Hoje em dia, o sistema operacional e o compilador fazem, por padrão, todo o código como código independente de posição. Tente compilar sem o sinalizador -fPIC, o código compilará bem, mas você receberá apenas um aviso. As janelas do SO usam uma técnica chamada mapeamento de memória para fazer isso.

Govardhan Murali
fonte
-5

A pergunta data de 2009. Dez anos se passaram e agora todo o código é, na verdade, independente de posição. Isso agora é imposto por sistemas operacionais e compiladores. Não há como cancelar. Todo o código é compilado à força com PIE, e o sinalizador -no-pic / -no-pie está sendo ignorado, como parte desta desculpa ASLR. A razão para isso é desacelerar aplicativos antes rápidos e vender hardware mais recente, sob o pretexto de maior segurança. Isso é completamente irracional, porque agora grandes tamanhos de memória nos permitem livrar-nos do inferno de links dinâmicos, compilando todos os aplicativos estaticamente.

O mesmo aconteceu antes, quando as pessoas silenciosamente aceitaram o modo real e outras liberdades sendo tiradas. E eu lembrei, MMU incorre em lentidão pesada, devido a mudanças de contexto e latência de tradução de endereço. Você não encontrará MMU em sistemas de desempenho crítico, como aqueles usados ​​por cientistas para amostrar experimentos de física.

Você não reclama, porque você nem sabe que seu código está sendo prejudicado por todas essas rodinhas. O que posso dizer? Desfrute de software 2 vezes mais lento com seu PIC agora! Ainda mais, com o advento do LLVM, em breve haverá o JIT (código gerenciado) imposto, sem acesso ao assembly embutido x86, o que tornará ainda mais lento qualquer código C / C ++. "Aqueles que sacrificam a liberdade pela segurança não merecem nenhum dos dois."

SmugLispWeenie
fonte
Isso é apenas uma constatação dos fatos: há 10 anos o PIC era opcional, mas hoje é padrão e obrigatório. Duvido que o código não-PIE tenha suporte em versões futuras do sistema operacional. Assim como o suporte ao modo real foi abandonado após o Windows 9x. Portanto, a questão de usar ou não o PIC torna-se mais um tópico teórico da ciência da computação, a menos que você de alguma forma desbloqueie seu sistema operacional e reative o suporte para ele. A coisa mais importante que as pessoas precisam saber sobre o PIC é que ele é lento o suficiente para que os compiladores até agora suportassem a compilação estática, e havia versões estáticas da maioria das DLLs.
SmugLispWeenie
2
Suas primeiras frases são apenas uma declaração de fatos. O resto é opinião, beirando a conspiração.
Mitch Lindgren de
Bem, apenas fale com as pessoas, pergunte a opinião delas sobre isso. Eu pessoalmente descobri que PIC versus não PIC também se tornou uma questão de ideologia. PIC é a programação equivalente ao comunismo, onde o código é produzido em massa e todos recebem a mesma cópia. Não-PIC é um equivalente de programação do Capitalismo, onde existem muitas versões concorrentes do mesmo código. Assim, as pessoas com uma mentalidade mais esquerdista, inconscientemente, apóiam o PIC para provar que sua ideologia favorita poderia funcionar pelo menos na computação. Essas mesmas pessoas aconselhariam você a não usar, digamos, libpng modificada pessoalmente.
SmugLispWeenie
3
Não podemos ter reclamações políticas em um site de programação, por favor, obrigado
Ryan McCampbell