Что означают параметры команды callq?

Я знаю, что делает инструкция callq; это описано в этом ответе. Инструкция должна иметь один параметр, который должен быть адресом вызываемой функции.

Однако при дизассемблировании объектного файла я вижу следующую строку, показывающую инструкцию callq с двумя значениями вместо одного параметра:

  1e:   e8 00 00 00 00          callq  23 <main+0x19>

Я предполагаю, что 23 — это адрес возврата, а <main+0x19> — адрес вызываемой функции. Однако вызываемая функция находится не по адресу main+0x19.

Что означают два значения 23 и main+0x19, показанные на дизассемблере инструкции callq?


Оригинальный код C:

#include <stdio.h>

void fun(int a)
{
}

int main()
{
    int a = 1234;
    fun(a);
}

objdump -D main.o main.asm Код дизассемблирования:

0000000000000000 <fun>:
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   89 7d fc                mov    %edi,-0x4(%rbp)
   7:   90                      nop
   8:   5d                      pop    %rbp
   9:   c3                      retq   

000000000000000a <main>:
   a:   55                      push   %rbp
   b:   48 89 e5                mov    %rsp,%rbp
   e:   48 83 ec 10             sub    $0x10,%rsp
  12:   c7 45 fc 03 00 00 00    movl   $0x3,-0x4(%rbp)
  19:   8b 45 fc                mov    -0x4(%rbp),%eax
  1c:   89 c7                   mov    %eax,%edi
  1e:   e8 00 00 00 00          callq  23 <main+0x19>
  23:   b8 00 00 00 00          mov    $0x0,%eax
  28:   c9                      leaveq 
  29:   c3                      retq   


person Edward    schedule 14.05.2019    source источник
comment
Я не думаю, что это дубликат, потому что я думаю, что недоразумения в вопросе не рассматриваются в этих ответах.   -  person prl    schedule 14.05.2019
comment
Я проголосовал за повторное открытие, потому что ни в якобы дублирующем вопросе не задается вопрос о параметре (ах) инструкции, ни один из ответов там не отвечает на него.   -  person Martin Rosenau    schedule 14.05.2019
comment
Я также изменил вопрос так, как я- его понял. @ Эдвард: Надеюсь, я правильно понял вашу проблему, и вопрос все еще спрашивает то, что вы хотели спросить.   -  person Martin Rosenau    schedule 14.05.2019
comment
@MartinRosenau: согласен. Речь идет не о callq против call, а о понимании того, как x86 call rel32 работает в первую очередь и как дизассемблеры помогают вычислять абсолютные цели по относительному смещению. И как работают несвязанные объектные файлы.   -  person Peter Cordes    schedule 14.05.2019
comment
@PeterCordes Я думаю, что у пользователя проблемы с пониманием двух значений 23 и main+0x19. Поэтому я соответствующим образом изменил вопрос. У пользователя возникают проблемы с пониманием того, что по адресу 0x1F происходит какое-то перемещение. ... и проблема в понимании того, что objdump -D не показывает перемещения.   -  person Martin Rosenau    schedule 14.05.2019
comment
@MartinRosenau: да, точно. Вот почему я проголосовал за ответ prl. Но хороший момент в отображении перемещений: я использую objdump -drwC -Mintel.   -  person Peter Cordes    schedule 14.05.2019
comment
Извините, я использую stackoverlflow в первый раз, поэтому я мог по ошибке нажать на дубликат ссылки, хотя я не хотел этого делать.   -  person Edward    schedule 14.05.2019


Ответы (1)


Инструкция callq имеет только один операнд, а не два, как подразумевается в вопросе. Дизассемблер отображает его в двух видах, как адрес и как символ + смещение.

Вы просматриваете дизассемблированный несвязанный объектный файл. Поскольку дизассемблируемый файл не связан, показанный адрес назначения не является адресом fun. Ассемблер помещает 0 в поле операнда инструкции и создает запись о перемещении, чтобы компоновщик заполнил смещение до конечного адреса места назначения.

Операнд инструкции вызова представляет собой смещение относительно адреса следующей инструкции после вызова. Таким образом, значение 0 в поле операнда заставляет дизассемблер отображать адрес следующей инструкции в качестве пункта назначения вызова. В показанной разборке это адрес 23.

Если вы сделаете fun статической функцией, ассемблер может заполнить истинное смещение функции, так как это не потребует перемещения, и вы увидите это в дизассемблере. (Это может зависеть от конкретных инструментов и параметров, которые вы используете.)

Если вы дизассемблируете связанный исполняемый файл, дизассемблер покажет истинный адрес назначения вызова.

person prl    schedule 14.05.2019
comment
дизассемблирование связанного исполняемого файла PIE, используемого по умолчанию в более новой Ubuntu, но не в OP 16.04, по-прежнему предполагает базовый адрес 0 для всего исполняемого файла. Но да, относительные смещения будут заполнены корректно. Когда вы на самом деле запустите его, изменится базовый адрес для всего этого, но не относительные смещения или symbol+x способ отображения целей перехода. - person Peter Cordes; 14.05.2019
comment
Я использую objdump -DrwC -Mintel main.o ассемблерный код стал таким: 1e: e8 00 00 00 00 call 23 ‹main+0x19› 1f: R_X86_64_PC32 fun-0x4 23: b8 00 00 00 00 mov eax,0x0 почему relocate addr fun-0x4 вместо fun - person Edward; 14.05.2019
comment
@Edward Этот объектный файл еще не перемещен. Как видите, все байты операнда для call равны 00 00 00 00, т. е. их значения еще не заполнены. Посмотрите на связанный двоичный файл, чтобы увидеть правильные значения. - person fuz; 14.05.2019
comment
Компоновщик заполняет относительное смещение символа перемещения относительно первого байта заполняемого поля, но инструкции требуется смещение до места назначения относительно следующей инструкции, что составляет разницу в 4 байта. - person prl; 14.05.2019
comment
@prl Спасибо, я нашел ответы на все свои вопросы в 《Компьютерные системы: взгляд программиста》 - person Edward; 15.05.2019