54

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. :-/

  • It 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. – Joshua Aug 16 '15 at 20:44
50

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.

  • 1
    Great answer, thanks! :-) I tried to use nm on the shared library, but it was not a debug version. So, why libvlc and others crash? – Ho1 Aug 15 '15 at 17:22
  • 1
    Because most shared libraries aren't intended to be executable, GNU libc is an exception. – goldilocks Aug 15 '15 at 17:25
  • I found two others: ld and libpthread. – Ho1 Aug 15 '15 at 17:28
  • @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. – Random832 Aug 15 '15 at 22:44
  • 1
    Above 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 – parasrish Nov 2 '18 at 8:20
20

Let's dive for an answer in random glibc repo in github. This version provides a „banner“ at file version.c: https://github.com/lattera/glibc/blob/a2f34833b1042d5d8eeb263b4cf4caaea138c4ad/csu/version.c

In same file there is a few interesting points: __libc_print_version the function that provides printing to stdin same text and symbol __libc_main (void) which is documented as an entry point. So this symbol is called when running library.

So how does linker/compiler knows that this is exactly entry point function?

Let's dive in makefile. In linker flags there is interesting flag: https://github.com/lattera/glibc/blob/a2f34833b1042d5d8eeb263b4cf4caaea138c4ad/Makerules

# Give libc.so an entry point and make it directly runnable itself.
LDFLAGS-c.so += -e __libc_main

So this is linker flag for setting entry point in library. When building a library you can provide -e function_name for linker create executable behavior. What it really does? Let's look in manual (somewhat dated but still valid): http://ftp.gnu.org/old-gnu/Manuals/ld-2.9.1/html_node/ld_24.html#SEC24

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 ;

Really ld linker does create an executable with entry point function if you provide it with command line option -e(most practical solution), provide function symbol start, or inject symbol address in assembler.

However please note that it is clearly not guaranteed to work with other linkers (I do not know if llvm's lld has same flag). Why this should be useful for purposes other than providing info on so file, I am not aware.

  • 1
    If it was python it would provide unit tests. – Erik Aronesty Mar 19 '17 at 11:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.