Сфокусируясь только на небольшой части вашего вопроса.
#include <stdio.h>
// find_start.c
unsigned long find_start(void)
{
__asm__("movl %esp, %eax");
}
unsigned long nest ( void )
{
return(find_start());
}
int main()
{
printf("0x%lx\n",find_start());
printf("0x%lx\n",nest());
}
gcc so.c -o so
./so
0x50e381a0
0x50e38190
Здесь нет магов c. Виртуальное пространство позволяет программам быть построенными одинаково. Мне не нужно знать, где будет жить моя программа, каждая программа может быть скомпилирована для одного и того же адресного пространства, при загрузке и запуске может видеть одно и то же виртуальное адресное пространство, поскольку все они сопоставлены с отдельными / разными физическими адресными пространствами.
readelf -a so
(не но я предпочитаю objdump
objdump -D так что
Disassembly of section .text:
0000000000000540 <_start>:
540: 31 ed xor %ebp,%ebp
542: 49 89 d1 mov %rdx,%r9
545: 5e pop %rsi
....
000000000000064a <find_start>:
64a: 55 push %rbp
64b: 48 89 e5 mov %rsp,%rbp
64e: 89 e0 mov %esp,%eax
650: 90 nop
651: 5d pop %rbp
652: c3 retq
0000000000000653 <nest>:
653: 55 push %rbp
654: 48 89 e5 mov %rsp,%rbp
657: e8 ee ff ff ff callq 64a <find_start>
65c: 5d pop %rbp
65d: c3 retq
000000000000065e <main>:
65e: 55 push %rbp
65f: 48 89 e5 mov %rsp,%rbp
662: e8 e3 ff ff ff callq 64a <find_start>
667: 48 89 c6 mov %rax,%rsi
66a: 48 8d 3d b3 00 00 00 lea 0xb3(%rip),%rdi # 724 <_IO_stdin_used+0x4>
671: b8 00 00 00 00 mov $0x0,%eax
676: e8 a5 fe ff ff callq 520 <printf@plt>
67b: e8 d3 ff ff ff callq 653 <nest>
так, две вещи или, может быть, более двух вещей. Наша точка входа _start находится в ram по низкому адресу. низкий виртуальный адрес. в этой системе с этим компилятором я бы ожидал, что все / большинство программ будут запускаться в одном и том же месте или в одном и том же месте, или в некоторых случаях это может зависеть от того, что находится в моей программе, но это должно быть где-то низко.
Хотя указатель стека, если вы проверяете выше и сейчас, когда я набираю вещи:
0x355d38d0
0x355d38c0
он изменился.
0x4ebf1760
0x4ebf1750
0x31423240
0x31423230
0xa63188d0
0xa63188c0
несколько раз в течение нескольких секунд. Стек является относительной вещью, а не абсолютной, поэтому нет необходимости создавать постоянный адрес, который всегда будет одинаковым. Нужно находиться в пространстве, связанном с этим пользователем / поток и виртуальный, так как он проходит через MMU по соображениям защиты. Нет никаких причин для виртуального адреса не совпадать с физическим адресом. Код / драйвер ядра, который управляет MMU для платформы, запрограммирован, чтобы делать это определенным образом. у вас может быть адресное пространство для кода, начинающееся с 0x0000 для каждой программы, и вы можете иметь sh адресное пространство для данных, которые будут одинаковыми, с нуля. но для стека это не имеет значения. и на моей машине, на моей ОС, эта конкретная версия в этот конкретный день не соответствует.
Первоначально я думал, что ваш вопрос будет другим в зависимости от факторов, которые c указаны для вашей сборки, и настроек. Для указанной c сборки один вызов find_start будет иметь фиксированный относительный адрес для указателя стека, каждая функция, использующая этот стек, вернет его так, как он был найден, при условии, что вы не можете изменить компиляцию программы. во время выполнения указателя стека для одного экземпляра вызова вложенность будет одинаковой, потребление стека каждой функцией по пути будет одинаковым.
Я добавил еще один слой и, глядя на разборку, основной , nest и find_start все путаются с указателем стека (неоптимизировано), поэтому для этих прогонов они разнесены на 0x10. если бы я добавил / удалил больше кода для каждой функции, чтобы изменить использование стека в одной или нескольких функциях, то эта дельта могла бы измениться.
Но
gcc -O2 so.c -o so
objdump -D so > so.txt
./so
0x0
0x0
Disassembly of section .text:
0000000000000560 <main>:
560: 48 83 ec 08 sub $0x8,%rsp
564: 89 e0 mov %esp,%eax
566: 48 8d 35 e7 01 00 00 lea 0x1e7(%rip),%rsi # 754 <_IO_stdin_used+0x4>
56d: 31 d2 xor %edx,%edx
56f: bf 01 00 00 00 mov $0x1,%edi
574: 31 c0 xor %eax,%eax
576: e8 c5 ff ff ff callq 540 <__printf_chk@plt>
57b: 89 e0 mov %esp,%eax
57d: 48 8d 35 d0 01 00 00 lea 0x1d0(%rip),%rsi # 754 <_IO_stdin_used+0x4>
584: 31 d2 xor %edx,%edx
586: bf 01 00 00 00 mov $0x1,%edi
58b: 31 c0 xor %eax,%eax
58d: e8 ae ff ff ff callq 540 <__printf_chk@plt>
592: 31 c0 xor %eax,%eax
594: 48 83 c4 08 add $0x8,%rsp
598: c3 retq
оптимизатор не распознал возвращаемое значение по какой-то причине.
unsigned long fun ( void )
{
return(0x12345678);
}
00000000000006b0 <fun>:
6b0: b8 78 56 34 12 mov $0x12345678,%eax
6b5: c3 retq
соглашение о вызовах выглядит нормально.
поместите find_start в отдельный файл, чтобы оптимизатор не смог удалить его
gcc -O2 so.c sp.c -o so
./so
0xb1192fc8
0xb1192fc8
./so
0x7aa979d8
0x7aa979d8
./so
0x485134c8
0x485134c8
./so
0xa8317c98
0xa8317c98
./so
0x2ba70b8
0x2ba70b8
Disassembly of section .text:
0000000000000560 <main>:
560: 48 83 ec 08 sub $0x8,%rsp
564: e8 67 01 00 00 callq 6d0 <find_start>
569: 48 8d 35 f4 01 00 00 lea 0x1f4(%rip),%rsi # 764 <_IO_stdin_used+0x4>
570: 48 89 c2 mov %rax,%rdx
573: bf 01 00 00 00 mov $0x1,%edi
578: 31 c0 xor %eax,%eax
57a: e8 c1 ff ff ff callq 540 <__printf_chk@plt>
57f: e8 4c 01 00 00 callq 6d0 <find_start>
584: 48 8d 35 d9 01 00 00 lea 0x1d9(%rip),%rsi # 764 <_IO_stdin_used+0x4>
58b: 48 89 c2 mov %rax,%rdx
58e: bf 01 00 00 00 mov $0x1,%edi
593: 31 c0 xor %eax,%eax
595: e8 a6 ff ff ff callq 540 <__printf_chk@plt>
Я не позволил этому встроить те функции, которые он может видеть, вкладывают в него, удаляя изменение стека, которое пришло с ним. Так что теперь значение, вложенное или нет, одинаково.