Loading executable or executing a library

戏子无情 提交于 2020-12-06 12:25:06

问题


There is a large number of questions on SO about how to execute a library or dynamically load an executable. As far as I can tell, all the answers come down to: compile your executable as position-independent code and load it with dlopen. This worked great --- and still works great on macOS --- until a recent change in glibc, which explicitly disabled dlopening PIEs. This change is now in the current version of glibc (2.30) on ArchLinux, for example, and trying to dlopen a position-independent executable gives an error: "cannot dynamically load position-independent executable".

It's difficult to guess what prompted such a radical change that breaks so much code and useful use cases. (The explanations on Patchwork and Bugzilla don't make much sense to me.) But there is now a question: what to do if you want to create an executable that's also a dynamic library, or vice versa?

A solution was linked from one of the comments. Reproducing it here for posterity:

#include <stdio.h>
#include <unistd.h>

const char service_interp[] __attribute__((section(".interp"))) = "/lib/ld-linux-x86-64.so.2";

extern "C" {

void lib_entry(void)
{
  printf("Entry point of the service library\n");    
  _exit(0);
}

}

Compiling with g++ -shared test-no-pie.cpp -o test-no-pie -Wl,-e,lib_entry produces a shared object (dynamic library) that can also be executed on Linux.

I have two questions:

  1. What if I want to pass command-line arguments? How to modify this solution so it accepts arc,argv?
  2. Are there other alternatives?

回答1:


It's difficult to guess what prompted such a radical change

Not really: it never worked correctly.

that breaks so much code

That code was broken already in subtle ways. Now you get a clear indication that it will not work.

Are there other alternatives?

Don't do that?

What problem does dlopening an executable solve?

If it's a real problem, open a GLIBC bugzilla feature request, explaining that problem and requesting a supported mechanism to achieve desired result.

Update:

at least say why "it never worked correctly". Is it some triviality like potentially clashing globals between the executables, or something real?

Thread-local variables is an example that doesn't work correctly. Whether you think they are "real" or not I have no idea.

Here is the code:

// foo.c
#include <stdio.h>

__thread int var;

__attribute__((constructor))
static void init()
{
  var = 42;
  printf("foo.c init: %d %p\n", var, &var);
}

int bar() {
  printf("foo.c bar:  %d %p\n", var, &var);
  return var;
}

int main()
{
  printf("foo.c main: %d %p bar()=%d\n", var, &var, bar());
  return 0;
}
gcc -g foo.c -o foo -Wl,-E -fpie -pie && ./foo
foo.c init: 42 0x7fb5dfd7d4fc
foo.c bar:  42 0x7fb5dfd7d4fc
foo.c main: 42 0x7fb5dfd7d4fc bar()=42
// main.c
// Error checking omitted for brevity
#include <dlfcn.h>
#include <stdio.h>

int main()
{
  void *h1 = dlopen("./foo", RTLD_LOCAL|RTLD_LAZY);
  int (*bar)(void) = dlsym(h1, "bar");

  printf("main.c: %d\n", bar());
  return 0;
}
gcc -g main.c -ldl && ./a.out
foo.c init: 42 0x7fb7305da73c
foo.c bar:  0 0x7fb7305da73c    <<< what?
main.c: 0                       <<< what?

This is using GNU C Library (Debian GLIBC 2.28-10) stable release version 2.28.

Bottom line: this was never designed to work, and you just happened to not step on many of the land-mines, so you thought it is working, when in fact you were exercising undefined behavior.



来源:https://stackoverflow.com/questions/64659713/why-does-dynamically-loading-of-pies-no-longer-work-in-glibc

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!