Что означает эта ошибка 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 ГБ с помощью:
char a[1024 x 1024 x 1024];
Результат: работает, никаких предупреждений. Естественно можно использовать 1073741824 вместо тройного произведения
- Удвоить предыдущий массив:
char a[2 x 1024 x 1024 x 1024];
Результат в GCC: "ошибка: размер массива 'a' отрицателен" => Это намек на то, что принятый/ожидаемый аргумент массива имеет тип со знаком int
- Основываясь на предыдущем, приведите аргумент:
char a[(unsigned)2 x 1024 x 1024 x 1024];
Результат: появляется ошибка перераспределения, усеченная до подгонки , вместе с этим предупреждением: "целочисленное переполнение в выражении типа 'int'"
- Обходной путь: используйте динамическую память. Функция 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
флаг сборки, файл скомпилирован без проблем