Ответ 1
Не делай этого; пусть ваш компилятор и компоновщик заполняют разделы по своему усмотрению.
Вместо этого отметьте свои функции соответствующими атрибутами , чтобы компилятор и компоновщик поместили их в правильные разделы.
Например,
static void before_main(void) __attribute__((constructor));
static void after_main(void) __attribute__((destructor));
static void before_main(void)
{
/* This is run before main() */
}
static void after_main(void)
{
/* This is run after main() returns (or exit() is called) */
}
Вы также можете назначить приоритет (скажем, __attribute__((constructor (300)))
), целое число от 101 до 65535 включительно, причем функции, имеющие номер меньшего приоритета, запускаются первыми.
Обратите внимание, что для иллюстрации я отметил функции static
. То есть, функции не будут видны вне области файла. Функции не обязательно должны быть экспортированы для автоматического вызова символов.
Для тестирования я предлагаю сохранить следующее в отдельном файле, например tructor.c
:
#include <unistd.h>
#include <string.h>
#include <errno.h>
static int outfd = -1;
static void wrout(const char *const string)
{
if (string && *string && outfd != -1) {
const char *p = string;
const char *const q = string + strlen(string);
while (p < q) {
ssize_t n = write(outfd, p, (size_t)(q - p));
if (n > (ssize_t)0)
p += n;
else
if (n != (ssize_t)-1 || errno != EINTR)
break;
}
}
}
void before_main(void) __attribute__((constructor (101)));
void before_main(void)
{
int saved_errno = errno;
/* This is run before main() */
outfd = dup(STDERR_FILENO);
wrout("Before main()\n");
errno = saved_errno;
}
static void after_main(void) __attribute__((destructor (65535)));
static void after_main(void)
{
int saved_errno = errno;
/* This is run after main() returns (or exit() is called) */
wrout("After main()\n");
errno = saved_errno;
}
чтобы вы могли компилировать и связывать его как часть любой программы или библиотеки. Чтобы скомпилировать его как общую библиотеку, используйте, например,
gcc -Wall -Wextra -fPIC -shared tructor.c -Wl,-soname,libtructor.so -o libtructor.so
и вы можете вставить его в любую динамически связанную команду или двоичный код с помощью
LD_PRELOAD=./libtructor.so some-command-or-binary
Функции сохраняют errno
неизменными, хотя на практике это не имеет значения, и используйте syscall для низкоуровневого write()
для вывода сообщений на стандартную ошибку. Начальная стандартная ошибка дублируется на новый дескриптор, потому что во многих случаях стандартная ошибка сама закрывается до того, как последний глобальный деструктор - наш деструктор здесь - запускается.
(Некоторые параноидальные двоичные файлы, обычно чувствительные к безопасности, закрывают все дескрипторы, о которых они не знают, поэтому вы можете не видеть сообщение After main()
во всех случаях.)