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?
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?
Respostas:
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.
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.
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:
call label_1
.dd 0xdeadbeef
.dd 0xfeedf00d
.dd 0x11223344
label_1:
pop ebp ; now ebp holds the address of the first dataword
; this works because the call pushes the **next**
; instructions address
; real code follows
mov eax, [ebp + 4] ; for example i'm accessing the '0xfeedf00d' in a PIC way
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.
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.
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.
position-independent code
tem uma sobrecarga de desempenho na maioria das arquiteturas, porque requer um registro extra.
Então, isso é para fins de desempenho.
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.
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."