x86-64 System V ABI не требует создания традиционного / устаревшего стекового фрейма.Это выглядит близко к традиционной установке фрейма стека, но это определенно не потому, что сразу после первого push %rbp
.
. *1005*
мы видим сгенерированный компиляторомкод, который просто использует RBP в качестве временного регистра и использует его для хранения указателя на локальный элемент в стеке. Это просто совпадение, что это случается с использованием инструкции mov %rsp, %rbp
через некоторое время после push %rbp
. Это не создание стекового фрейма.
В x86-64 System V, RBX и RBP являются единственными 2 «низкими 8» регистрами, которые сохраняются при вызове и, следовательно, могут использоваться без REX.префиксы в некоторых случаях (например, для push / pop и при использовании в режимах адресации), сохраняя размер кода.GCC предпочитает использовать их перед сохранением / восстановлением любого из R12..R15. Какие регистры сохраняются при вызове функции linux x86-64 (Для указателей при копировании их с mov
всегда требуется префикс REX для 64-битного размера операнда, поэтому экономия меньше, чем для 32-битовые целые числа, но gcc по-прежнему относится к RBX, а не к RBP, в том порядке, в котором необходимо сохранить / восстановить сохраненные вызовом регистры в функции.)
Разборка /lib/libc.so.6
(glibc) в моей системе (Arch Linux) показывает похожий, но разный код для fdopendir
.Вы остановили разборку слишком рано, прежде чем она вызовет функцию.Это проливает некоторый свет на то, почему ему нужен временный регистр, сохраняемый вызовом: ему нужна переменная в регистре для всего вызова.
00000000000c1260 <fdopendir>:
c1260: 55 push %rbp
c1261: 89 fe mov %edi,%esi
c1263: 53 push %rbx
c1264: 89 fb mov %edi,%ebx
c1266: bf 01 00 00 00 mov $0x1,%edi
c126b: 48 81 ec a8 00 00 00 sub $0xa8,%rsp
c1272: 64 48 8b 04 25 28 00 00 00 mov %fs:0x28,%rax # stack-check cookie
c127b: 48 89 84 24 98 00 00 00 mov %rax,0x98(%rsp)
c1283: 31 c0 xor %eax,%eax
c1285: 48 89 e5 mov %rsp,%rbp # save a pointer
c1288: 48 89 ea mov %rbp,%rdx # and pass it as a function arg
c128b: e8 90 7d 02 00 callq e9020 <__fxstat>
c1290: 85 c0 test %eax,%eax
c1292: 78 6a js c12fe <fdopendir+0x9e>
c1294: 8b 44 24 18 mov 0x18(%rsp),%eax
c1298: 25 00 f0 00 00 and $0xf000,%eax
c129d: 3d 00 40 00 00 cmp $0x4000,%eax
c12a2: 75 4c jne c12f0 <fdopendir+0x90>
....
c12c1: 48 89 e9 mov %rbp,%rcx # pass the pointer as the 4th arg
c12c4: 89 c2 mov %eax,%edx
c12c6: 31 f6 xor %esi,%esi
c12c8: 89 df mov %ebx,%edi
c12ca: e8 d1 f7 ff ff callq c0aa0 <__alloc_dir>
c12cf: 48 8b 8c 24 98 00 00 00 mov 0x98(%rsp),%rcx
c12d7: 64 48 33 0c 25 28 00 00 00 xor %fs:0x28,%rcx # check the stack cookie
c12e0: 75 38 jne c131a <fdopendir+0xba>
c12e2: 48 81 c4 a8 00 00 00 add $0xa8,%rsp
c12e9: 5b pop %rbx
c12ea: 5d pop %rbp
c12eb: c3 retq
Это довольно глупый код-ген;gcc мог просто использовать mov %rsp, %rcx
во второй раз, когда это было нужно.Я бы назвал это пропущенной оптимизацией.Он никогда не нуждался в этом указателе в регистре с сохранением вызова, потому что он всегда знал, где он был относительно RSP.
(Даже если бы он не был точно в RSP + 0, lea something(%rsp), %rdx
и lea something(%rsp), %rcx
будутбыли в полном порядке два раза, когда это было необходимо, с, вероятно, меньшей общей стоимостью, чем сохранение / восстановление RBP + требуемые mov
инструкции.)
Или он мог бы использовать mov 0x18(%rbp),%eax
вместо rsp для сохранениябайт размера кода в этом режиме адресации.Избегание прямых ссылок на RSP между вызовами функций уменьшает количество операций синхронизации стека, которые необходимо вставить процессорам Intel.