Что означает эта ошибка GCC "... перемещение усечено до соответствия..."?

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

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

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

Построение цели: fft2d_host
Вызов: GCC C Linker
gcc -L / opt / adapteva / esdk / tools / host / x86_64 / lib -o "fft2d_host"./src/fft2d_host.o -le_host -lrt

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

fft2d_host.c:(. text + 0x280): перемещение усечено до соответствия: R_X86_64_PC32 для символа `Mailbox', определенного в разделе COMMON в./src/fft2d_host.o

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

11 ответов

Решение

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

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

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

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

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

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 _startиначе все равно не получится. Я думаю, что это происходит потому, что глобальные символы имеют ограничения выравнивания (0xFFFFFFF0 работает). ТОДО, где это задокументировано в стандарте ELF?

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

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

объяснение

Сначала вы должны понять, что такое перемещение, с минимальным примером: /questions/5420523/kak-rabotaet-svyazyivanie-c-na-praktike/5420531#5420531

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

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

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

  • b8 говорит, что это mov в %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, Смотрите также: /questions/45357824/chto-oznachayut-peremescheniya-rx866432s-i-rx866464/45357846#45357846

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

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

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

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 слишком большая ошибка от компилятора, потому что в более сложных случаях компилятор не может знать, что other_global будет вне досягаемости, поэтому он даже не пытается для простых случаев.

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

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

Я могу ошибаться, но по моему опыту есть еще одна возможная причина ошибки, основной причиной которой является ограничение компилятора (или платформы), которое легко воспроизвести и обойти. Далее самый простой пример

  1. определить массив размером 1 ГБ с помощью:
    char a[1024 x 1024 x 1024];

Результат: работает, никаких предупреждений. Естественно можно использовать 1073741824 вместо тройного произведения

  1. Удвоить предыдущий массив:
    char a[2 x 1024 x 1024 x 1024];

Результат в GCC: "ошибка: размер массива 'a' отрицателен" => Это намек на то, что принятый/ожидаемый аргумент массива имеет тип со знаком int

  1. Основываясь на предыдущем, приведите аргумент:
    char a[(unsigned)2 x 1024 x 1024 x 1024];

Результат: появляется ошибка перераспределения, усеченная до подгонки , вместе с этим предупреждением: "целочисленное переполнение в выражении типа 'int'"

  1. Обходной путь: используйте динамическую память. Функция malloc() принимает аргумент типа size_t, который является определением типа unsigned long long int , что позволяет избежать ограничения.

Это был мой опыт использования GCC в Windows. Просто мои 2 цента.

С GCC есть-Wl,--default-image-base-lowопция, которая иногда помогает справиться с такими ошибками, например, в некоторых конфигурациях MSYS2 / MinGW .

Я столкнулся с ошибкой «перемещение усечено» на машине MIPS. -mcmodel=mediumфлаг недоступен на mips, вместо этого-mxgotможет там помогут.

Я столкнулся с этой ошибкой в ​​64-битной Windows при компоновке программы на С++, которая вызывала функцию nasm. Я использовал nasm для сборки и g++ для компиляции c++ и компоновки.

В моем случае эта ошибка означала, что мне нужен DEFAULT REL в верхней части моего кода на ассемблере nasm.

Это написано в документации NASM: Глава 11: Написание 64-битного кода (Unix, Win64)

Оглядываясь назад, это очевидно, но мне потребовалось несколько дней, чтобы добраться туда, поэтому я решил опубликовать это.

Это минимальная версия программы C++:

      > extern "C" { void matmul(void); }
 int main(void) {
     matmul();
     return 0;
}

Это минимальная версия программы nasm:

          ; "DEFAULT REL" means we can access data in .bss, .data etc
; because we generate position-independent code in 64-bit "flat" memory model.
; see NASM docs
; Chapter 11: Writing 64-bit Code (Unix, Win64)
;DEFAULT REL

global matmul

section .bss
align 32       ; because we want to move 256 bit packed aligned floats to and from it
saveregs  resb 32

section .text
matmul:
push   rbp     ; prologue
mov    rbp,rsp ; aligns the stack pointer

    ; preserve ymm6 in local variable 'saveregs'
    vmovaps [saveregs], ymm6

    ; restore ymm6 from local variable 'saveregs'
    vmovaps ymm6, [saveregs]

mov   rsp,rbp ; epilogue
pop   rbp     ; re-aligns the stack pointer
ret

Когда DEFAULT REL закомментирован, я получил сообщение об ошибке выше:

      g++ -std=c++11 -c   SO.cpp -o SOcpp.o
\bin\nasm -f win64  SO.asm -o SOnasm.obj
g++ SOcpp.o SOnasm.obj -o SO.exe
SOnasm.obj:SO.asm:(.text+0x9): relocation truncated to fit: IMAGE_REL_AMD64_ADDR32 against `.bss'
SOnasm.obj:SO.asm:(.text+0x12): relocation truncated to fit: IMAGE_REL_AMD64_ADDR32 against `.bss'
collect2.exe: error: ld returned 1 exit status

Я столкнулся с той же проблемой. После компиляции без -fexceptions флаг сборки, файл скомпилирован без проблем

Другие вопросы по тегам