создание .so, который также является исполняемым - PullRequest
47 голосов
/ 20 сентября 2009

Таким образом, каждый, вероятно, знает, что /lib/libc.so.6 glibc может быть выполнен в оболочке как обычный исполняемый файл, и в этом случае он печатает информацию о своей версии и завершает работу. Это делается путем определения точки входа в .so. В некоторых случаях было бы интересно использовать это и для других проектов. К сожалению, низкоуровневая точка входа, которую вы можете установить с помощью параметра ld -e, слишком низкоуровневая: динамический загрузчик недоступен, поэтому вы не можете вызывать какие-либо надлежащие библиотечные функции. По этой причине glibc реализует системный вызов write () через открытый системный вызов в этой точке входа.

Теперь у меня вопрос: может ли кто-нибудь придумать, как можно загрузить полностью динамический компоновщик из этой точки входа, чтобы можно было получить доступ к функциям из других .so?

Ответы [ 2 ]

48 голосов
/ 20 сентября 2009

Создайте свою общую библиотеку с опцией -pie, чтобы получить все, что вы хотите:

/* pie.c */
#include <stdio.h>
int foo()
{
  printf("in %s %s:%d\n", __func__, __FILE__, __LINE__);
  return 42; 
}
int main() 
{ 
  printf("in %s %s:%d\n", __func__, __FILE__, __LINE__);
  return foo(); 
}


/* main.c */
#include <stdio.h>

extern int foo(void);
int main() 
{ 
  printf("in %s %s:%d\n", __func__, __FILE__, __LINE__);
  return foo(); 
}


$ gcc -fPIC -pie -o pie.so pie.c -Wl,-E
$ gcc main.c ./pie.so


$ ./pie.so
in main pie.c:9
in foo pie.c:4
$ ./a.out
in main main.c:6
in foo pie.c:4
$

P.S. glibc реализует write(3) через системный вызов, потому что ему больше некуда звонить (это уже самый низкий уровень). Это не имеет ничего общего с возможностью выполнения libc.so.6.

0 голосов
/ 20 сентября 2009

Полагаю, у вас есть ld -e точка входа, которая затем использует семейство функций dlopen(), чтобы найти и загрузить остальную часть динамического компоновщика. Конечно, вам нужно убедиться, что сам dlopen() либо статически связан, либо вам, возможно, придется реализовать достаточно своей собственной заглушки компоновщика (используя интерфейсы системных вызовов, такие как mmap(), как это делает сама libc).

Ничто из этого не звучит "хорошо" для меня. На самом деле, одной лишь мысли о прочтении исходных кодов glibc (и исходного кода ld-linux, в качестве одного примера), достаточной для оценки объема работы, мне кажется довольно печальной. Это также может быть кошмар переносимости. Могут быть серьезные различия между тем, как Linux реализует ld-linux, и тем, как выполняются связи в OpenSolaris, FreeBSD и так далее. (Я не знаю).

...