Я уже встречал здесь вопросы, похожие на этот, но я не смог решить свою проблему с помощью ответов на них.
У меня есть файл libfoo.c
, из которого я создаю общий объект. Используя gcc __attribute__((constructor))
, я хотел бы распечатать сообщение в файл, когда этот общий объект загружен:
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
static char *term = NULL;
static void init (void) __attribute__((constructor));
static void
init (void)
{
FILE *f = fopen ("lib.log", "w");
if (f)
{
if (isatty (STDOUT_FILENO))
{
char *tmp = ttyname (STDERR_FILENO);
if (tmp && *tmp)
{
term = strdup (tmp); // nevermind the memory leak
fprintf (f, "Found terminal %s\n", term);
}
}
else
fprintf (f, "Failed to find terminal\n");
fclose(f);
}
}
void *
malloc (size_t size)
{
return NULL;
}
Реализация фиктивного malloc (которая будет расширена позже) должна заменить malloc stdlib.h в другой фиктивной программе, исходный код которой находится в main.c
:
#include <stdlib.h>
int main (void)
{
char *m = malloc(1024);
(void)m;
return 0;
}
Я компилирую и связываю оба файла следующим образом:
gcc -o main main.c
gcc -fpic -shared -o libfoo libfoo.c
А затем, когда я выполняю следующее, файл не создается и вывод не регистрируется:
LD_PRELOAD=$(readlink -f libfoo) ./main
Что здесь происходит? В качестве примечания: как я могу попытаться отладить подобную проблему с помощью ltrace? ltrace "LD_PRELOAD=... ./main"
не является допустимой командой.
Обновлено: Что здесь происходит? Кажется, что по крайней мере printf
работает внутри общего объекта, но только внутри функции настройки, если я вызываю printf
внутри malloc
, программа перестает работать. Пытаясь хотя бы выяснить, к какому терминалу подключен stdout
, я сделал следующее (внутри функции настройки):
// ...
char buf[100];
buf[readlink ("/proc/self/fd/1", buf, 100)] = '\0';
printf ("STDOUT (buf): %s\n", buf);
char *tmp = ttyname (1);
printf ("STDOUT (ttyname): %s, %s\n", tmp, strerror (errno));
// ...
Это печатает:
STDOUT (buf): /dev/pts/1
STDOUT (ttyname): (null), Success
Согласно справочной странице ttyname
, эта вторая строка вывода должна быть невозможна. Я что-то совершенно не понимаю?
Вы также можете попробовать: ltrace env LD_PRELOAD=... ./main
Да, это работает, и вызывается правильная реализация malloc, я просто понял, что могу просто использовать strace
, и системные вызовы, выполняемые внутри общего объекта, также будут напечатаны, поэтому я попытаюсь найти такую проблему.
Кажется, что fopen вообще не выполняет никаких системных вызовов, если я вместо этого использую open("lib.log", O_WRONLY | O_CREATE, 0666)
, файл создается, как ожидалось. Это кажется странным, я не уверен, как fopen
реализован внутри, я разберусь с этим.
Я обнаружил, что strace
более полезен, чем ltrace
. В инициализации fopen
может быть слишком рано, потому что он более весомый (требует больше ресурсов / символов для загрузки / связывания), чем функция open
(которая представляет собой лишь тонкую оболочку вокруг системного вызова). Помните, что в вашем malloc
printf
может вызывать malloc
, поэтому вам может потребоваться флаг состояния i_am_already_in_malloc
для обработки этого случая (то есть для подавления вызова printf
). В противном случае вы можете получить бесконечную рекурсию.
Что касается ttyname
, он делает то, что делает ваш код. Но он вызывает malloc
и может возвращать ноль, если malloc
возвращает ноль, и может вызывать вашmalloc
. Попробуйте использовать ttyname_r
, который [AFAICT] не вызывает malloc
.
Рекурсия printf, кажется, была причиной того, что моя программа изначально вылетела, я просто предположил, что printf будет использовать статическую память внутри, спасибо за подсказку, я думаю, что теперь все выяснено.
вот то, что я запустил, созданный из вашего кода, и он отлично работал, создав файл: lib.log
Содержимое файла: lib.log
Found terminal /dev/pts/1
и вот код, который я запустил:
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
static char *term = NULL;
static void init (void) __attribute__((constructor));
static void init (void)
{
FILE *f = fopen ("lib.log", "w");
if (f)
{
if (isatty (STDOUT_FILENO))
{
char *tmp = ttyname (STDERR_FILENO);
if (tmp && *tmp)
{
term = strdup (tmp); // nevermind the memory leak
fprintf (f, "Found terminal %s\n", temp);
}
}
else
fprintf (f, "Failed to find terminal\n");
fclose(f);
}
else
{
perror( "fopen failed" );
}
}
void *
myMalloc (size_t size)
{
(void)size;
return NULL;
}
int main (void)
{
char *m = myMalloc(1024);
(void)m;
return 0;
}
Конечно, в опубликованном вами коде отсутствует файл заголовка. На такой заголовочный файл следует ссылаться / включать его в ваш файл main.c
и ваш файл libfoo.c
Конечно, это работает, но в этом нет сути. Я хочу создать общую библиотеку, которая может заменять вызовы malloc в произвольных программах (аналогично valgrind) и может записывать журналы в файл.
Предположительно, ваш
init
не выдаст сигнал, еслиfopen
вышел из строя. Можете ли вы подтвердить в любом случае?