Я подозреваю, что ваш код работает нормально, и в конце возникают проблемы: что вы ожидаете, что произойдет после a++
?
mymain()
простообычная функция C, которая будет пытаться вернуться к своему вызывающему.
Но вы установили ее в качестве точки входа ELF, которая говорит загрузчику ELF перейти к ней после загрузки сегментов программы вв нужном месте - и он не ожидает, что вы вернетесь.
Эти "другие объектные файлы, такие как crt1.o
, crti.o
и crtn.o
" обычно обрабатывают это для программ на Си.Точка входа ELF для программы на C не является main()
- вместо этого это оболочка, которая устанавливает подходящую среду для main()
(например, установка аргументов argc
и argv
в стеке или в регистрах,в зависимости от платформы), вызывает main()
(с ожиданием того, что он может вернуться), а затем вызывает системный вызов exit
(с кодом возврата от main()
).
[Обновить следующие комментарии:]
Когда я пробую ваш пример с gdb
, я вижу, что он действительно не возвращается после mymain()
: после установки точки останова на mymain
, изатем, следуя инструкциям, я вижу, что он выполняет инкремент, а затем возникает проблема в эпилоге функции:
$ gcc -g -c main.c
$ ld -o prog -T my_script.lds main.o
$ gdb ./prog
...
(gdb) b mymain
Breakpoint 1 at 0x10006: file main.c, line 4.
(gdb) r
Starting program: /tmp/prog
Breakpoint 1, mymain () at main.c:4
4 a++;
(gdb) display/i $pc
1: x/i $pc
0x10006 <mymain+6>: addl $0x1,-0x4(%ebp)
(gdb) si
5 }
1: x/i $pc
0x1000a <mymain+10>: leave
(gdb) si
Cannot access memory at address 0x4
(gdb) si
0x00000001 in ?? ()
1: x/i $pc
Disabling display 1 to avoid infinite recursion.
0x1: Cannot access memory at address 0x1
(gdb) q
По крайней мере, для i386 загрузчик ELF устанавливает разумный стек перед входом взагруженный код, так что вы можете установить точку входа ELF в функцию C и получить разумное поведение;однако, как я упоминал выше, вы должны самостоятельно обработать выход из процесса.И если вы не используете среду выполнения C, вам лучше не использовать библиотеки, зависящие от среды выполнения C.
Так что вот пример этого, используя ваш оригинальный скрипт компоновщика - но скод C изменен, чтобы инициализировать a
известным значением и вызвать системный вызов exit
(с использованием встроенной сборки) с конечным значением a
в качестве кода выхода.(Примечание: я только что понял, что вы не сказали точно, какую платформу вы используете; я предполагаю, что Linux здесь.)
$ cat main2.c
void mymain(void)
{
int a = 42;
a++;
asm volatile("mov $1,%%eax; mov %0,%%ebx; int $0x80" : : "r"(a) : "%eax" );
}
$ gcc -c main2.c
$ ld -o prog2 -T my_script.lds main2.o
$ ./prog2 ; echo $?
43
$