Если я правильно понимаю, вы пытаетесь реализовать функции в стандартной библиотеке C, пытаясь использовать некоторые части среды выполнения C (а именно то, что вызывает основную функцию и завершается).
Часть кода, которая обычно делает это, является функцией _start
. Обычно это точка входа для двоичных файлов ELF с загрузчиком Linux.
Эта _start
функция определена во время выполнения C, которое использует ваш компилятор, и вызов для выхода уже связан (адрес, исправленный в месте вызова). Вполне вероятно, что он просто встроен в функцию _start
.
Чтобы функция _start
вызывала ваш exit
, вам нужно переопределить сам _start
. Затем вы должны убедиться, что _start
среды выполнения C не используется.
Я бы пошел с чем-то вроде этого -
// Assuming your have included files that declare the puts and fflush functions and the stdout macro.
int main(int argc, char* argv[]); // This is here just so that there is a declaration before the call
void _start(void) {
char *argv[] = {"executable"}; // There is a way to get the real arguments, but I think you will have to write some assembly for that.
int return_value = main(1, argv);
exit(return_value);
// Control should NEVER reach here, because you cannot return from the _start function
}
void exit(int ret) {
puts("ok");
fflush(stdout); // Assuming your runtime defines these functions and stdout somewhere.
// To simulate an exit, we will just spin infinitely -
while(1);
}
int main(int argc, char* argv[]) {
puts("hello world\n");
return 0;
}
Теперь вы можете скомпилировать и связать файл как -
gcc test.c -o executable -nostdlib
-nostdlib
указывает компоновщику не ссылаться на стандартную среду выполнения, которая имеет реализацию _start
.
Теперь вы можете выполнить свой исполняемый файл, и он будет вызывать ваш «выход», как вы и ожидали, а затем продолжит цикл навсегда. Вы можете убить его, нажав Ctrl + C или отправив SIGKILL другим способом.
Приложение
Просто для полноты картины я также попытался записать реализацию для остальных функций.
Сначала вы можете добавить следующие объявления и определения вверху вашего кода.
#define stdout (1)
int puts(char *s);
long unsigned int strlen(const char *s) {
int len = 0;
while (s[len])
len++;
return len;
}
int fflush(int s) {
}
void exit(int n);
strlen
определено как ожидается, а fflush
не используется, поскольку мы не реализуем буферизацию для наших функций stdio.
Теперь в отдельном файле put.s напишите следующую сборку (предполагая x64 linux. Измените числа и аргументы системного вызова, если ваша платформа отличается).
.text
.globl puts
puts:
movq %rdi, %r12
callq strlen
movq $1, %rdi
movq %r12, %rsi
movq %rax, %rdx
movq $1, %rax
syscall
retq
Это простейшая реализация puts
, которая вызывает функцию strlen, за которой следует системный вызов write.
Теперь вы можете скомпилировать и связать все как -
gcc test.c put.s -o executable -nostdlib
Когда я запускаю произведенный executable
, я получаю следующий вывод -
hello world
ok
и тогда процесс просто зависает. Я могу убить его, нажав Ctrl + C.