Что означает эта ошибка GCC "... усечение, усеченное, чтобы соответствовать..."?

Я программирую хост-систему хост-ускорителя. Хост работает на ПК под Ubuntu Linux и взаимодействует со встроенным оборудованием через USB-соединение. Связь выполняется путем копирования блоков памяти в и из встроенной аппаратной памяти.

В памяти платы есть область памяти, которую я использую в качестве почтового ящика, где я пишу и читаю данные. Почтовый ящик определяется как структура, и я использую то же определение, чтобы выделить зеркальный почтовый ящик в моем пространстве хоста.

Я успешно использовал эту технику в прошлом, поэтому теперь я скопировал проект Eclipse хоста в текущее рабочее пространство проекта и внес соответствующие изменения имен. Странно то, что при создании проекта-хозяина я получаю следующее сообщение:

Целевая задача: fft2d_host
Вызов: GCC C Linker
 gcc -L/opt/adapteva/esdk/tools/host/x86_64/lib -o "fft2d_host". /src/fft 2d_host.o -le_host -lrt

./src/fft2d_host.o: В функции `main ':

fft2d_host.c:(. text + 0x280): усечение переместилось, чтобы соответствовать: R_X86_64_PC32 против символа `Почтовый ящик ', определенного в разделе ОБЩИЙ. /src/fft 2d_host.o

Что означает эта ошибка и почему она не будет основываться на текущем проекте, в то время как с предыдущим проектом все в порядке?

Ответы

Ответ 1

Вы пытаетесь связать свой проект таким образом, что цель схемы относительной адресации находится дальше, чем может быть поддержана с 32-битным смещением выбранного режима относительной адресации. Это может быть связано с тем, что текущий проект больше, потому что он связывает объектные файлы в другом порядке или потому, что в игре есть излишне обширная схема сопоставления.

Этот вопрос является прекрасным примером того, почему часто полезно выполнять веб-поиск в общей части сообщения об ошибке - вы находите такие вещи:

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

Что предлагает некоторые лечебные предложения.

Ответ 2

Минимальный пример, который генерирует ошибку

main.S: перемещает адрес в %eax (32-разрядный):

_start:
    mov $_start, %eax

linker.ld:

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

Скомпилировать на x86-64:

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

Результат ld:

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

Имейте в виду, что:

  • as помещает все в .text, если не указан другой раздел
  • ld использует .text как точку входа по умолчанию, если ENTRY. Таким образом, _start является самым первым байтом .text.

Как это исправить: вместо этого используйте linker.ld и вычтите 1 из начала:

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

Примечания:

  • мы не можем сделать _start global в этом примере с помощью .global _start, иначе он все равно не будет выполнен. Я думаю, это происходит потому, что глобальные символы имеют ограничения выравнивания (0xFFFFFFF0 работает). TODO, где указано в стандарте ELF?

  • сегмент .text также имеет ограничение на выравнивание p_align == 2M. Но наш компоновщик достаточно умен, чтобы поместить сегмент в 0xFFE00000, заполнить нулями до 0xFFFFFFFF и установить e_entry == 0xFFFFFFFF. Это работает, но генерирует негабаритный исполняемый файл.

Протестировано на Ubuntu 14.04 AMD64, Binutils 2.24.

Объяснение

Сначала вы должны понять, что такое перемещение с минимальным примером: fooobar.com/questions/4539/...

Затем взгляните на objdump -Sr main.o:

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

Если мы рассмотрим, как инструкции закодированы в руководстве Intel, мы видим, что:

  • b8 говорит, что это mov to %eax
  • 0 - это немедленное значение, которое нужно переместить в %eax. Перемещение затем изменит его, чтобы содержать адрес _start.

При переходе в 32-разрядные регистры немедленное также должно быть 32-разрядным.

Но здесь перемещение должно изменить эти 32-битные, чтобы поместить в него адрес _start после того, как произойдет соединение.

0x100000000 не вписывается в 32-битный, но 0xFFFFFFFF делает. Таким образом, ошибка.

Эта ошибка может произойти только при перестановках, которые генерируют усечение, например. R_X86_64_32 (от 8 байтов до 4 байтов), но никогда не на R_X86_64_64.

И есть некоторые типы перемещений, которые требуют расширения знака вместо нулевого расширения, как показано здесь, например. R_X86_64_32S. Смотрите также: fooobar.com/questions/2439/...

Ответ 3

Не забывайте обрабатывать сообщения об ошибках по порядку. В моем случае ошибка над этим была "undefined reference", и я визуально пропустил ее до более интересной "усеченной" перестановки. На самом деле, моя проблема была старой библиотекой, которая вызывала сообщение "undefined reference". Как только я исправил это, "усечение переселения" тоже ушло.

Ответ 4

Я столкнулся с этой проблемой при создании программы, требующей огромного пространства стека (более 2 гигабайт). Решением было добавить флаг -mcmodel=medium, который поддерживается как компиляторами GCC, так и Intel.

Ответ 5

В Cygwin -mcmodel=medium уже установлен по умолчанию и не помогает. Для меня добавление ссылки -Wl,--image-base -Wl,0x10000000 в GCC устранило ошибку.

Ответ 6

Часто эта ошибка означает, что ваша программа слишком велика, а часто слишком велика, поскольку содержит один или несколько очень больших объектов данных. Например,

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

в x86-64/Linux выдаст ошибку "усечено для размещения", если она скомпилирована в режиме по умолчанию и без оптимизации. (Если вы включите оптимизацию, это может, по крайней мере теоретически, выяснить, что large_array не используется и/или что other_global никогда не пишется, и, таким образом, сгенерировать код, который не вызывает проблему.)

Что происходит, так это то, что по умолчанию GCC использует свою "модель небольшого кода" в этой архитектуре, в которой весь программный код и статически распределенные данные должны вписываться в самые низкие 2 ГБ адресного пространства. (Точный верхний предел составляет примерно 2 ГБ - 2 МБ, поскольку самые нижние 2 МБ любого адресного пространства программы невозможно использовать постоянно. Если вы компилируете разделяемую библиотеку или независимый от позиции исполняемый файл, весь код и данные все равно должны помещаться в два гигабайт, но они больше не прибиты к нижней части адресного пространства.) large_array все это пространство само по себе, поэтому other_global назначается адрес, превышающий ограничение, и код, сгенерированный для main не может его достичь. Вы получаете загадочную ошибку от компоновщика, а не полезную large_array " large_array is too large" от компилятора, потому что в более сложных случаях компилятор не может знать, что other_global будет вне досягаемости, поэтому он даже не пытается для простых случаев.

В большинстве случаев правильным ответом на получение этой ошибки является рефакторинг вашей программы, чтобы она не нуждалась в гигантских статических массивах и/или гигабайтах машинного кода. Однако, если вам действительно по какой-то причине они нужны, вы можете использовать "средние" или "большие" модели кода для снятия ограничений за счет несколько менее эффективной генерации кода. Эти модели кода специфичны для x86-64; что-то подобное существует для большинства других архитектур, но точный набор "моделей" и связанные с ними ограничения будут различаться. (Например, в 32-битной архитектуре у вас может быть "маленькая" модель, в которой общий объем кода и данных ограничен чем-то вроде 2 24 байтов.)