Why and how are some shared libraries runnable, as though they are executables?
Solution 1
That library has a main()
function or equivalent entry point, and was compiled in such a way that it is useful both as an executable and as a shared object.
Here's one suggestion about how to do this, although it does not work for me.
Here's another in an answer to a similar question on S.O, which I'll shamelessly plagiarize, tweak, and add a bit of explanation.
First, source for our example library, test.c
:
#include <stdio.h>
void sayHello (char *tag) {
printf("%s: Hello!\n", tag);
}
int main (int argc, char *argv[]) {
sayHello(argv[0]);
return 0;
}
Compile that:
gcc -fPIC -pie -o libtest.so test.c -Wl,-E
Here, we are compiling a shared library (-fPIC
), but telling the linker that it's a regular executable (-pie
), and to make its symbol table exportable (-Wl,-E
), such that it can be usefully linked against.
And, although file
will say it's a shared object, it does work as an executable:
> ./libtest.so
./libtest.so: Hello!
Now we need to see if it can really be dynamically linked. An example program, program.c
:
#include <stdio.h>
extern void sayHello (char*);
int main (int argc, char *argv[]) {
puts("Test program.");
sayHello(argv[0]);
return 0;
}
Using extern
saves us having to create a header. Now compile that:
gcc program.c -L. -ltest
Before we can execute it, we need to add the path of libtest.so
for the dynamic loader:
export LD_LIBRARY_PATH=./
Now:
> ./a.out
Test program.
./a.out: Hello!
And ldd a.out
will show the linkage to libtest.so
.
Note that I doubt this is how glibc is actually compiled, since it is probably not as portable as glibc itself (see man gcc
with regard to the -fPIC
and -pie
switches), but it demonstrates the basic mechanism. For the real details you'd have to look at the source makefile.
Solution 2
Let's dive for an answer in a random glibc repo on GitHub.
This version provides a „banner“ in the file version.c
.
In the same file there are some interesting points:
the __libc_print_version
function that prints the text to stdout and the __libc_main (void)
symbol which is documented as the entry point.
So this symbol is called when running the library.
So how does the linker or compiler knows exactly that this is the entry point function?
Let's dive into the makefile. In the linker flags there is an interesting one:
# Give libc.so an entry point and make it directly runnable itself.
LDFLAGS-c.so += -e __libc_main
So this is the linker flag for setting the entry point for the library. When building a library you can provide the -e function_name
flag for the linker to enable an executable behavior.
What does it really do? Let's look into the manual (somewhat outdated but still valid):
The linker command language includes a command specifically for defining the first executable instruction in an output file (its entry point). Its argument is a symbol name:
ENTRY(symbol)
Like symbol assignments, the ENTRY command may be placed either as an independent command in the command file, or among the section definitions within the SECTIONS command--whatever makes the most sense for your layout.
ENTRY is only one of several ways of choosing the entry point. You may indicate it in any of the following ways (shown in descending order of priority: methods higher in the list override methods lower down).
the `-e' entry command-line option; the ENTRY(symbol) command in a linker control script; the value of the symbol start, if present; the address of the first byte of the .text section, if present; The address 0.
For example, you can use these rules to generate an entry point with an assignment statement: if no symbol start is defined within your input files, you can simply define it, assigning it an appropriate value---
start = 0x2020;
The example shows an absolute address, but you can use any expression. For example, if your input object files use some other symbol-name convention for the entry point, you can just assign the value of whatever symbol contains the start address to start:
start = other_symbol ;
(the current documentation can be found here)
The ld
linker actually does create an executable with an entry point function if you provide the command line option -e
(which is the most popular solution), provide a function symbol start
, or spcify a symbol address for the assembler.
However please note that it is not guaranteed to work with other linkers (I do not know if llvm's lld has the same flag). I am not aware why this should be useful for the purposes other than providing the information about the SO file.
Related videos on Youtube
Ho1
Updated on September 18, 2022Comments
-
Ho1 over 1 year
On 32-bit Linux systems, invoking this
$ /lib/libc.so.6
and on 64-bit systems this
$ /lib/x86_64-linux-gnu/libc.so.6
in a shell, provides an output like this:
GNU C Library stable release version 2.10.1, by Roland McGrath et al. Copyright (C) 2009 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Compiled by GNU CC version 4.4.0 20090506 (Red Hat 4.4.0-4). Compiled on a Linux >>2.6.18-128.4.1.el5<< system on 2009-08-19. Available extensions: The C stubs add-on version 2.1.2. crypt add-on version 2.1 by Michael Glad and others GNU Libidn by Simon Josefsson Native POSIX Threads Library by Ulrich Drepper et al BIND-8.2.3-T5B RT using linux kernel aio For bug reporting instructions, please see: <http://www.gnu.org/software/libc/bugs.html>.
Why and how does this happen, and how is it possible to do the same in other shared libraries?
I looked at
/usr/lib
to find executables, and I found/usr/lib/libvlc.so.5.5.0
. Running it led to a segmentation fault. :-/-
Joshua almost 9 yearsIt addition to all the following answers, my recall is if you set the x bit on a shared library, it was (maybe still is) possible to load it from an executable even with the r bit clear. It was once considered a good security practice to banish r bit from world on system executables and libraries. Due to widespread open source, this only really applies to the anonymous ftp directory's /bin/ls anymore. To me, leaving the x bit set looks like a visage of that old practice.
-
-
Ho1 almost 9 yearsI found two others:
ld
andlibpthread
. -
Random832 almost 9 years@Ho1
ld.so
is special in other ways. To some extent, it's more of a real executable than a normal dynamically-linked executable is. -
Erik Aronesty about 7 yearsIf it was python it would provide unit tests.
-
parasrish over 5 years@goldilocks any suggestions for passing the value through cmake? (-pie) (-Wl,-E) Something like -fPIC=
POSITION_INDEPENDENT_CODE
variable in cmake to be set (or similar). -
goldilocks over 5 years@parasrish You can pass compiler flags by setting
CFLAGS
orCMAKE_C_FLAGS
: file:///doc/HTML/cmake-v3.11/envvar/CFLAGS.html, although you might try and more properly set the linker flag-Wl,-E
via a different variable. -
parasrish over 5 yearsFor now with cmake, it can resolved as:
add_compile_options("-pie") link_libraries("-pie")
in the CMakeLists.txt. Seemingly, there is an unresolved issue for the cmake in this context : gitlab.kitware.com/cmake/cmake/issues/14983 -
parasrish over 5 yearsAbove options even though create the executable-shared-library, but is incomplete in the sense, that it flags error, when some executable tries to link to this. Detailed sample reference added here : unix.stackexchange.com/a/479334/152034
-
goldilocks about 4 yearsThe header would then be a normal library header and would have to be
#include
d inprogram.c
. -
Demi-Lune over 3 yearsSee also the compile/link options of this stackoverflow.com/a/7494273/7212665 answer, including the
-nostartfiles
in its comments. -
Irfan Latif about 3 yearsBuilt with
gcc 9.3.0
on Ubuntu, throwscannot dynamically load position-independent executable
. May be because of this: patchwork.ozlabs.org/project/glibc/patch/… -
x4444 over 2 years
-fpic
is position-independent code (PIC). Shared gcc flag is-shared