Почему дополнительные аргументы указателя исчезли в сборке? - PullRequest
1 голос
/ 25 апреля 2020

C Код:

void PtrArg1(int* a,int* b,int* c, int* d, int* e, int* f)
{
    return;
}

void PtrArg2(int* a,int* b,int* c, int* d, int* e, int* f, int* g, int* h)
{
    return;
}

Компиляция с

gcc -c -m64 -o basics basics.c -O0

Запуск

objdump -d basics -M intel -r

, что приводит к следующей разборке (синтаксис Intel):

000000000000000b <PtrArg1>:
   b:   f3 0f 1e fa             endbr64 
   f:   55                      push   rbp
  10:   48 89 e5                mov    rbp,rsp
  13:   48 89 7d f8             mov    QWORD PTR [rbp-0x8],rdi
  17:   48 89 75 f0             mov    QWORD PTR [rbp-0x10],rsi
  1b:   48 89 55 e8             mov    QWORD PTR [rbp-0x18],rdx
  1f:   48 89 4d e0             mov    QWORD PTR [rbp-0x20],rcx
  23:   4c 89 45 d8             mov    QWORD PTR [rbp-0x28],r8
  27:   4c 89 4d d0             mov    QWORD PTR [rbp-0x30],r9
  2b:   90                      nop
  2c:   5d                      pop    rbp
  2d:   c3                      ret    

000000000000002e <PtrArg2>:
  2e:   f3 0f 1e fa             endbr64 
  32:   55                      push   rbp
  33:   48 89 e5                mov    rbp,rsp
  36:   48 89 7d f8             mov    QWORD PTR [rbp-0x8],rdi
  3a:   48 89 75 f0             mov    QWORD PTR [rbp-0x10],rsi
  3e:   48 89 55 e8             mov    QWORD PTR [rbp-0x18],rdx
  42:   48 89 4d e0             mov    QWORD PTR [rbp-0x20],rcx
  46:   4c 89 45 d8             mov    QWORD PTR [rbp-0x28],r8
  4a:   4c 89 4d d0             mov    QWORD PTR [rbp-0x30],r9
  4e:   90                      nop
  4f:   5d                      pop    rbp
  50:   c3                      ret 

Количество аргументов отличается для PtrArg1 и PtrArg2, но инструкции по сборке одинаковы для обоих. Почему?

Ответы [ 2 ]

3 голосов
/ 25 апреля 2020

Это связано с соглашением о вызовах (System V AMD64 ABI, текущая версия 1.0 ). Первые шесть параметров передаются в целочисленных регистрах, все остальные помещаются в стек.

После выполнения до местоположения PtrArg2+0x4e, вы получите следующий макет стека:

+----------+-----------------+
|  offset  |     content     |
+----------+-----------------+
| rbp-0x30 | f               |
| rbp-0x28 | e               |
| rbp-0x20 | d               |
| rbp-0x18 | c               |
| rbp-0x10 | b               |
| rbp-0x8  | a               |
| rbp+0x0  | saved rbp value |
| rbp+0x8  | return address  |
| rbp+0x10 | g               |
| rbp+0x18 | h               |
+----------+-----------------+

С * Вызывающий абонент нажимает 1009 * и h, вы получаете одинаковую разборку для обеих функций. Для вызывающей стороны

void Caller()
{
    PtrArg2(1, 2, 3, 4, 5, 6, 7, 8);
}

(для ясности я опустил необходимые приведения) мы получили бы следующую разборку:

Caller():
    push    rbp
    mov     rbp, rsp
    push    8
    push    7
    mov     r9d, 6
    mov     r8d, 5
    mov     ecx, 4
    mov     edx, 3
    mov     esi, 2
    mov     edi, 1
    call    PtrArg2
    add     rsp, 16
    nop
    leave
    ret

( см. Проводник компилятора )

Параметры h = 8 и g = 7 помещаются в стек перед вызовом PtrArg2.

1 голос
/ 25 апреля 2020

Исчезают? Как вы ожидали, что функция будет делать с ними, что компилятор будет выдавать ассемблерные инструкции для реализации?

Вы буквально return; как единственный оператор в функции void, поэтому функция ничего не должна делать для других чем ret. Если вы компилируете с нормальным уровнем оптимизации, таким как -O2, это все, что вы получите. Код в режиме отладки обычно не интересен для просмотра и полон лишних / бесполезных вещей. Как удалить "шум" из вывода сборки GCC / clang?

Единственная причина, по которой вы видите какие-либо инструкции для некоторых аргументов, заключается в том, что вы скомпилировали в режиме отладки, то есть уровень оптимизации по умолчанию -O0, антиоптимизированный режим отладки. Каждый C объект (кроме register localals) имеет адрес памяти, а режим отладки гарантирует, что значение действительно находится в памяти до / после каждое C утверждение. Это означает, что при входе в функцию происходит сброс аргументов регистра в стек. Почему clang создает неэффективный asm с -O0 (для этой простой суммы с плавающей запятой)?

Соглашение о вызовах x86-64 System V ABI передает первые 6 целочисленных аргументов в регистрах, остальные в стеке. Аргументы стека уже имеют адреса памяти; компилятор не генерирует код для копирования их рядом с другими локальными переменными ниже адреса возврата; это было бы бессмысленно. Вызываемый «владеет» своими собственными аргументами стека, т. Е. Он может хранить новые значения в пространстве стека, в котором вызывающий объект записал аргументы, так что пространство может быть истинным адресом аргументов, даже если функция изменяет их.

...