Как регистры работают как аргументы в сборке? - PullRequest
0 голосов
/ 26 сентября 2018

Я пытаюсь понять, как сборка работает с аргументами и возвращаемыми значениями.

До сих пор я узнал, что% eax - это возвращаемое значение, и чтобы загрузить один аргумент, мне нужно загрузить эффективныйадрес %rip + offset в %rid с помощью leaq var(%rip), %rdi.

Чтобы узнать больше об аргументах, я создал программу ac, которая принимает 10 (11 аргументов, включая строку форматирования), чтобы попытаться выяснить порядокрегистров.Затем я преобразовал код C в сборку, используя gcc на моем Mac.

Вот код C, который я использовал:

#include <stdio.h>

int main(){
  printf("%s %s %s %s %s %s %s %s %s %s", "1 ", "2", "3", "4", "5", "6", "7", "8", "9", "10");
  return 0;
}

И слышал вывод сборки:

.section  __TEXT,__text,regular,pure_instructions
  .macosx_version_min 10, 13
  .globl  _main                   ## -- Begin function main
  .p2align  4, 0x90
_main:                                  ## @main
  .cfi_startproc
## %bb.0:
  pushq %rbp
  .cfi_def_cfa_offset 16
  .cfi_offset %rbp, -16
  movq  %rsp, %rbp
  .cfi_def_cfa_register %rbp
  pushq %r15
  pushq %r14
  pushq %rbx
  pushq %rax
  .cfi_offset %rbx, -40
  .cfi_offset %r14, -32
  .cfi_offset %r15, -24
  subq  $8, %rsp
  leaq  L_.str.10(%rip), %r10
  leaq  L_.str.9(%rip), %r11
  leaq  L_.str.8(%rip), %r14
  leaq  L_.str.7(%rip), %r15
  leaq  L_.str.6(%rip), %rbx
  leaq  L_.str(%rip), %rdi
  leaq  L_.str.1(%rip), %rsi
  leaq  L_.str.2(%rip), %rdx
  leaq  L_.str.3(%rip), %rcx
  leaq  L_.str.4(%rip), %r8
  leaq  L_.str.5(%rip), %r9
  movl  $0, %eax
  pushq %r10
  pushq %r11
  pushq %r14
  pushq %r15
  pushq %rbx
  callq _printf
  addq  $48, %rsp
  xorl  %eax, %eax
  addq  $8, %rsp
  popq  %rbx
  popq  %r14
  popq  %r15
  popq  %rbp
  retq
  .cfi_endproc
                                        ## -- End function
  .section  __TEXT,__cstring,cstring_literals
L_.str:                                 ## @.str
  .asciz  "%s %s %s %s %s %s %s %s %s %s"

L_.str.1:                               ## @.str.1
  .asciz  "1 "

L_.str.2:                               ## @.str.2
  .asciz  "2"

L_.str.3:                               ## @.str.3
  .asciz  "3"

L_.str.4:                               ## @.str.4
  .asciz  "4"

L_.str.5:                               ## @.str.5
  .asciz  "5"

L_.str.6:                               ## @.str.6
  .asciz  "6"

L_.str.7:                               ## @.str.7
  .asciz  "7"


L_.str.8:                               ## @.str.8
  .asciz  "8"

L_.str.9:                               ## @.str.9
  .asciz  "9"

L_.str.10:                              ## @.str.10
  .asciz  "10"


.subsections_via_symbols

После этого я очистил код, который удаляет некоторые настройки только для MacOS?Код все еще работает.

.text
  .globl  _main                   ## -- Begin function main
_main:                                  ## @main
  pushq %rbp
  movq  %rsp, %rbp
  pushq %r15
  pushq %r14
  pushq %rbx
  pushq %rax
  subq  $8, %rsp
  leaq  L_.str.10(%rip), %r10
  leaq  L_.str.9(%rip), %r11
  leaq  L_.str.8(%rip), %r14
  leaq  L_.str.7(%rip), %r15
  leaq  L_.str.6(%rip), %rbx
  leaq  L_.str(%rip), %rdi
  leaq  L_.str.1(%rip), %rsi
  leaq  L_.str.2(%rip), %rdx
  leaq  L_.str.3(%rip), %rcx
  leaq  L_.str.4(%rip), %r8
  leaq  L_.str.5(%rip), %r9
  movl  $0, %eax
  pushq %r10
  pushq %r11
  pushq %r14
  pushq %r15
  pushq %rbx
  callq _printf
  addq  $48, %rsp
  xorl  %eax, %eax
  addq  $8, %rsp
  popq  %rbx
  popq  %r14
  popq  %r15
  popq  %rbp
  retq

.data
L_.str:                                 ## @.str
  .asciz  "%s %s %s %s %s %s %s %s %s %s"

L_.str.1:                               ## @.str.1
  .asciz  "1 "

L_.str.2:                               ## @.str.2
  .asciz  "2"

L_.str.3:                               ## @.str.3
  .asciz  "3"

L_.str.4:                               ## @.str.4
  .asciz  "4"

L_.str.5:                               ## @.str.5
  .asciz  "5"

L_.str.6:                               ## @.str.6
  .asciz  "6"

L_.str.7:                               ## @.str.7
  .asciz  "7"

L_.str.8:                               ## @.str.8
  .asciz  "8"

L_.str.9:                               ## @.str.9
  .asciz  "9"

L_.str.10:                              ## @.str.10
  .asciz  "10"

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

Leaq затем загружает каждую строку в каждый регистр, который будет использоваться в качестве аргумента для printf.

Что я хочу знать, так это почему регистры r10 r11 r14 и r15 раньшепервый аргумент загружается в память, и это регистрирует rsi rdx rcx r8 и 'r9', загруженные в память после первого аргумента?Кроме того, почему r14 и r15 используются вместо r12 и r13?

Кроме того, почему в этом случае 8 добавляется и вычитается из указателя стека, и имеет ли значение, в каком порядке расположены регистрытолкнул и лопнул?

Надеюсь, все подвопросы связаны с этим вопросом, если не дайте мне знать.Кроме того, возьмите меня на вооружение на любые знания, я могу ошибаться.Это то, что я узнал, преобразовав c в сборку.

1 Ответ

0 голосов
/ 26 сентября 2018

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

Посмотрите на состояние регистра непосредственно перед вызовом printf, который не помещается в стек:

rdi = format string
rsi = 1
rdx = 2
rcx = 3
r8 = 4
r9 = 5

Затем 6 ... 10 помещаются в стек в обратном порядке.

Это должно дать вам представление о соглашении о вызовах.Первые шесть параметров проходят через регистры.Остальные параметры передаются в стек.

Что я хочу знать, так это почему регистры r10 r11 r14 и r15 перед загрузкой первого аргумента в память и что регистры rsi rdx rcx r8 и 'r9'загружен в память после первого аргумента?

Это именно тот порядок, который выбрал компилятор.

Кроме того, почему вместо r12 и r13 используются r14 и r15?

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

Кроме того, почему в этом случае 8 добавляется и вычитается из указателя стека, и имеет ли значение, в каком порядке регистры перемещаются и выталкиваются?

Это может быть просто какой-то код функции котельной пластины, который генерирует компилятор.

...