O que significa esse erro do GCC “… relocação truncada para caber…” significa?

Estou programando o lado do host de um sistema acelerador de host. O host é executado no PC no Ubuntu Linux e se comunica com o hardware embarcado por meio de uma conexão USB. A comunicação é realizada copiando pedaços de memory de e para a memory do hardware embarcado.

Na memory da placa há uma região de memory que eu uso como uma checkbox de correio onde escrevo e leio os dados. A checkbox de correio é definida como uma estrutura e eu uso a mesma definição para alocar uma checkbox de correio espelho no meu espaço de host.

Eu usei essa técnica com sucesso no passado, então agora copiei o projeto Eclipse do host para o espaço de trabalho do meu projeto atual e fiz as alterações de nome apropriadas. O estranho é que, ao criar o projeto do host, eu recebo a seguinte mensagem:

Meta de construção: fft2d_host
Invocando: GCC C Linker
gcc -L / opt / adaptev / esdk / tools / host / x86_64 / lib -o “fft2d_host” ./src/fft2d_host.o -le_host -lrt

./src/fft2d_host.o: Na function `main ‘:

fft2d_host.c :(. text + 0x280): relocação truncada para caber: R_X86_64_PC32 contra o símbolo `Mailbox ‘definido na seção COMMON em ./src/fft2d_host.o

O que esse erro significa e por que ele não se baseia no projeto atual, enquanto está tudo bem com o projeto antigo?

    Você está tentando vincular seu projeto de modo que o destino de um esquema de endereçamento relativo esteja mais distante do que pode ser suportado com o deslocamento de 32 bits do modo de endereçamento relativo escolhido. Isso pode ser porque o projeto atual é maior, porque está vinculando arquivos de object em uma ordem diferente ou porque há um esquema de mapeamento expansivo desnecessário em execução.

    Essa pergunta é um exemplo perfeito de por que é frequentemente produtivo fazer uma pesquisa na Web na parte genérica de uma mensagem de erro – você encontra coisas como esta:

    http://www.technovelty.org/code/c/relocation-truncated.html

    Que oferece algumas sugestões curativas.

    Exemplo mínimo que gera o erro

    main.S : move um endereço para %eax (32 bits):

     _start: mov $_start, %eax 

    linker.ld :

     SECTIONS { /* This says where `.text` will go in the executable. */ . = 0x100000000; .text : { *(*) } } 

    Compile no x86-64:

     as -o main.o main.S ld -o main.out -T linker.ld main.o 

    Resultado de ld :

     (.text+0x1): relocation truncated to fit: R_X86_64_32 against `.text' 

    Tenha em mente que:

    • as coloca tudo no .text se nenhuma outra seção for especificada
    • ld usa o .text como o ponto de input padrão, se ENTRY . Assim, _start é o primeiro byte de .text .

    Como corrigir isso: use este linker.ld vez disso e subtraia 1 desde o início:

     SECTIONS { . = 0xFFFFFFFF; .text : { *(*) } } 

    Notas:

    • Não podemos tornar _start global neste exemplo com .global _start , caso contrário, ele ainda falhará. Eu acho que isso acontece porque os símbolos globais têm restrições de alinhamento ( 0xFFFFFFF0 funciona). TODO onde está documentado no padrão ELF?

    • o segmento .text também possui uma restrição de alinhamento de p_align == 2M . Mas nosso vinculador é inteligente o suficiente para colocar o segmento em 0xFFE00000 , preencher com zeros até 0xFFFFFFFF e definir e_entry == 0xFFFFFFFF . Isso funciona, mas gera um executável superdimensionado.

    Testado no Ubuntu 14.04 AMD64, Binutils 2.24.

    Explicação

    Primeiro, você deve entender o que é a realocação com um exemplo mínimo: https://stackoverflow.com/a/30507725/895245

    Em seguida, dê uma olhada no objdump -Sr main.o :

     0000000000000000 <_start>: 0: b8 00 00 00 00 mov $0x0,%eax 1: R_X86_64_32 .text 

    Se analisarmos como as instruções são codificadas no manual da Intel, vemos que:

    • b8 diz que este é um mov para %eax
    • 0 é um valor imediato a ser movido para %eax . A realocação irá então modificá-lo para conter o endereço de _start .

    Ao mover para registradores de 32 bits, o imediato também deve ser de 32 bits.

    Mas aqui, a realocação tem que modificar os 32 bits para colocar o endereço de _start neles depois que a vinculação acontecer.

    0x100000000 não cabe em 32 bits, mas 0xFFFFFFFF faz. Assim o erro.

    Esse erro só pode acontecer em realocações que geram truncamento, por exemplo, R_X86_64_32 (8 bytes a 4 bytes), mas nunca em R_X86_64_64 .

    E há alguns tipos de realocação que exigem extensão de sinal em vez de extensão zero, como mostrado aqui, por exemplo, R_X86_64_32S . Veja também: https://stackoverflow.com/a/33289761/895245

    Lembre-se de lidar com mensagens de erro em ordem. No meu caso, o erro acima deste foi “referência indefinida”, e eu visualmente pulei para o mais interessante “relocation truncated” error. Na verdade, meu problema era uma biblioteca antiga que estava causando a mensagem “referência indefinida”. Depois que eu corrigi isso, a “realocação truncada” também desapareceu.

    Eu corri para este problema ao construir um programa que requer uma enorme quantidade de espaço de pilha (mais de 2 GiB). A solução foi adicionar a flag -mcmodel=medium , que é suportada pelos compiladores GCC e Intel.

    No Cygwin -mcmodel=medium já é padrão e não ajuda. Para mim, adicionando -Wl,--image-base -Wl,0x10000000 para vinculador GCC corrigiu o erro.

    Freqüentemente, o que esse erro significa é que seu programa é muito grande e muitas vezes é muito grande porque contém um ou mais objects de dados muito grandes. Por exemplo,

     char large_array[1ul < < 31]; int other_global; int main(void) { return other_global; } 

    produzirá um erro de "realocação truncada para caber" no x86-64 / Linux, se compilado no modo padrão e sem otimização. (Se você ativar a otimização, ela poderia, pelo menos teoricamente, descobrir que large_array não é usado e / ou que other_global nunca é gravado e, portanto, gera código que não aciona o problema.)

    O que está acontecendo é que, por padrão, o GCC usa seu "modelo de código pequeno" nessa arquitetura, na qual todo o código do programa e os dados alocados estaticamente devem caber nos 2 GB mais baixos do espaço de endereço. (O limite superior preciso é algo como 2 GB - 2 MB, porque os 2MB mais baixos do espaço de endereço de qualquer programa são permanentemente inutilizáveis. Se você estiver compilando uma biblioteca compartilhada ou um executável independente de posição, todo o código e dados ainda devem caber em dois gigabytes, mas eles não são pregados na parte inferior do espaço de endereço.) large_array consome todo o espaço por si mesmo, então é atribuído a other_global um endereço acima do limite, e o código gerado para main não pode alcançá-lo. Você recebe um erro large_array do vinculador, em vez de um large_array " large_array is too large" útil do compilador, porque em casos mais complexos, o compilador não pode saber que other_global estará fora de alcance, por isso nem mesmo tenta para os casos simples.

    Na maioria das vezes, a resposta correta para obter esse erro é refatorar seu programa para que ele não precise de matrizes estáticas gigantescas e / ou gigabytes de código de máquina. No entanto, se você realmente precisa tê-los por algum motivo, é possível usar os modelos de código "médio" ou "grande" para elevar os limites, ao preço de uma geração de código um pouco menos eficiente. Esses modelos de código são específicos do x86-64; algo semelhante existe para a maioria das outras arquiteturas, mas o conjunto exato de "modelos" e os limites associados irão variar. (Em uma arquitetura de 32 bits, por exemplo, você pode ter um modelo "pequeno" no qual a quantidade total de código e dados era limitada a algo como 2 24 bytes.)