Почему указатель стека не уменьшается, когда я использую 64-битную локальную переменную? - PullRequest
0 голосов
/ 30 ноября 2018

Вот код дизассемблирования, скомпилированный из C:

00799d60 <sub_799d60>:
  799d60:   b573        push    {r0, r1, r4, r5, r6, lr}
  799d62:   0004        movs    r4, r0
  799d64:   f000 e854   blx 799e10 <jmp_sub_100C54>
  799d68:   4b15        ldr r3, [pc, #84]   ; (799dc0 <sub_799d60+0x60>)
  799d6a:   0005        movs    r5, r0
  799d6c:   4668        mov r0, sp
  799d6e:   4798        blx r3

Цель вызова подпрограммы (799d6e: 4798 blx r3) принимает аргумент указателя на 64-битное целое и возвращает 64-битное целое число.И эта подпрограмма является библиотечной функцией, поэтому я не могу ее модифицировать.Может ли эта операция перезаписать стек, в котором хранятся значения lr и r6?

1 Ответ

0 голосов
/ 30 ноября 2018

Вы говорите, что цель перехода "принимает аргумент указателя на 64-разрядное целое число и возвращает 64-разрядное целое число", но это не так.Он принимает указатель на 64-разрядное целое число в качестве единственного аргумента (и этот указатель имеет длину 32 бита, если вы не используете aarch64, что я сомневаюсь, учитывая оставшуюся часть кода);и он ничего не возвращает, он просто перезаписывает 64-битное значение, на которое указывает аргумент, который вы передали. Я уверен, что это то, что вы имели в виду, но будьте осторожны при использовании терминологии, потому что разница между этими вещами важна!В частности, ни одна из 64-битных аргументов не передается в нашу функцию, которую вы вызываете.

В отношении самого вопроса.Ключ к пониманию того, что делает компилятор, заключается в том, чтобы взглянуть на самую первую строку:

push    {r0, r1, r4, r5, r6, lr}

Соглашение о вызовах ARM *1007* не требует r0 и r1 длябыть сохраненным по вызову, так что они делают в списке?Ответ в том, что компилятор добавил эти «фиктивные» нажатия, чтобы создать место в стеке.Операция push, указанная выше, по существу эквивалентна

push    {r4, r5, r6, lr}
sub     sp, sp, #0x08

, за исключением того, что она сохраняет инструкцию.Конечно, результат не совсем тот же, потому что все, что было в r0 и r1, в итоге записывается в эти места;но, учитывая, что нет никакого способа узнать, что там было заранее, и собранные значения в любом случае будут перезаписаны, это не имеет значения.Таким образом, мы имеем в качестве фрейма стека

      lr
      r6
      r5
      r4
      (r1)
sp -> (r0)

с указателем стека, указывающим на пространство, создаваемое фиктивным толчком r0 и r1.Теперь у нас просто есть

mov   r0, sp

, который копирует указатель стека в r0 для использования в качестве аргумента-указателя для функции, которую вы вызываете, которая затем перезапишет два слова в этом месте, что приведет ккадр стека

      lr
      r6
      r5
      r4
      (64-bit value, high word)
sp -> (64-bit value, low word)

Вы не показали никакого кода, кроме blx r3, поэтому невозможно точно сказать, что происходит со стеком в конце функции.Но если эта функция не возвращает аргументов, я ожидаю увидеть совпадение

pop     {r0, r1, r4, r5, r6, pc}

, что, конечно, приведет к тому, что ваш 64-битный результат останется в r0 и r1.Но эти регистры закрыты в соответствии с соглашением о вызовах, поэтому проблем нет.

...