При чтении книги «Компьютерная система. Перспектива программиста», раздел 7.7.1 Записи перемещения: краткое содержание этого раздела - как компоновщик перемещает ссылку в другом объектном файле.
Когда компилировать и objdump пример исходного кода:
void swap();
int buf[2] = {1, 2};
int main()
{
swap();
return 0;
}
Тогда gcc -Wall -c -o main.o main.c и objdump -S -r main.o> main.asm; и увидит запись о перемещении для подкачки:
6: e8 fc ff ff ff call 7 <main+0x7> swap();
7: R_386_PC32 swap relocation entry
Таким образом, когда ld связывает main.o и swap.o, ld будет использовать запись перемещения r из swap (смещение = 7, type = R_386_PC32) для определения адреса ссылки:
refaddr = ADDR(section .text) + r.offset
*refptr = (unsigned)(ADDR(r.symbol + *refptr - refptr)
И операнд инструкции вызова (fc ff ff ff) -4 идеально подходит для набора инструкций 386.
Но когда я повторяю это в Linux X86_64, я обнаружил, что код для вызова:
9: e8 00 00 00 00 callq e <main+0xe>
a: R_X86_64_PC32 swap relocation entry
Тогда Мой вопрос: почему операнд вызова (e8) в 386 равен -4 ((fc ff ff ff), а операнд в X86_64 main.o равен 00 00 00 00? Это из-за различного набора команд (call vs. callq), или просто GNU ld использует другой алгоритм для перемещения R_X86_64_PC32?
Надеюсь, что вы ответите, большое спасибо.