0

I'm trying to create a static application that will not load libraries on run time. Specifically (for the proof of concept), I've written a short program that calls gethostbyname:

#include <netdb.h>
#include <unistd.h>
#include <cstdlib>
#include <iostream>
#include <string>

using namespace std;

int main() {
    string hostname(100, 0);

    const int hostname_length = gethostname(
        const_cast<char *>(hostname.data()),
        hostname.capacity()
    );

    hostname.resize(hostname.size());

    cout << "Hostname: " << hostname << endl;

    const struct hostent *host_entry = gethostbyname(hostname.c_str());

    cout << "Hostname 2: " << host_entry->h_name << endl;

    getchar(); // Keep the program alive so I can look at /proc/#/maps
}

When compiling with the standard glibc and -static, this shows a warning message (shown below) and loads several libraries such as libresolv, libnss_files, libnss_dns and more.

/usr/bin/ld: /tmp/cc1EWL0m.o: in function `main':
main.cpp:(.text+0xe2): warning: Using 'gethostbyname' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking

In order to prevent this I compiled glibc 2.33 from source in the following way:

../glibc/configure --enable-static-pie --enable-static-nss --prefix=/opt/glibc --disable-nscd
make
sudo make install

Then, following the technique presented here, I compiled my program with:

g++ -o custom main.cpp -nostdlib -nostartfiles -static /opt/glibc/lib/crt1.o /opt/glibc/lib/crti.o `gcc --print-file-name=crtbegin.o` -L/opt/glibc/lib -Wl,--start-group -lstdc++ -lgcc -lgcc_eh -lc -Wl,--end-group `gcc --print-file-name=crtend.o` /opt/glibc/lib/crtn.o

Sure enough the warning about loading libraries in runtime was gone. But running cat /proc/$(pgrep custom)/maps, I can see libraries are still getting loaded:

cat /proc/`pgrep custom`/maps
00400000-00401000 r--p 00000000 08:10 962                                /home/dan/projects/l/custom_glibc/example/custom
00401000-00565000 r-xp 00001000 08:10 962                                /home/dan/projects/l/custom_glibc/example/custom
00565000-005bd000 r--p 00165000 08:10 962                                /home/dan/projects/l/custom_glibc/example/custom
005bd000-005c7000 r--p 001bc000 08:10 962                                /home/dan/projects/l/custom_glibc/example/custom
005c7000-005ca000 rw-p 001c6000 08:10 962                                /home/dan/projects/l/custom_glibc/example/custom
005ca000-005cf000 rw-p 00000000 00:00 0 
019df000-01a01000 rw-p 00000000 00:00 0                                  [heap]
7f3e9f5cf000-7f3e9f6cf000 rw-p 00000000 00:00 0 
7f3e9f6cf000-7f3e9f6d0000 r--p 00000000 08:10 337811                     /opt/glibc/lib/ld-2.33.so
7f3e9f6d0000-7f3e9f6f4000 r-xp 00001000 08:10 337811                     /opt/glibc/lib/ld-2.33.so
7f3e9f6f4000-7f3e9f6fd000 r--p 00025000 08:10 337811                     /opt/glibc/lib/ld-2.33.so
7f3e9f6fd000-7f3e9f6fe000 ---p 0002e000 08:10 337811                     /opt/glibc/lib/ld-2.33.so
7f3e9f6fe000-7f3e9f700000 r--p 0002e000 08:10 337811                     /opt/glibc/lib/ld-2.33.so
7f3e9f700000-7f3e9f702000 rw-p 00030000 08:10 337811                     /opt/glibc/lib/ld-2.33.so
7f3e9f702000-7f3e9f728000 r--p 00000000 08:10 330240                     /opt/glibc/lib/libc-2.33.so
7f3e9f728000-7f3e9f869000 r-xp 00026000 08:10 330240                     /opt/glibc/lib/libc-2.33.so
7f3e9f869000-7f3e9f8b5000 r--p 00167000 08:10 330240                     /opt/glibc/lib/libc-2.33.so
7f3e9f8b5000-7f3e9f8b6000 ---p 001b3000 08:10 330240                     /opt/glibc/lib/libc-2.33.so
7f3e9f8b6000-7f3e9f8b9000 r--p 001b3000 08:10 330240                     /opt/glibc/lib/libc-2.33.so
7f3e9f8b9000-7f3e9f8bc000 rw-p 001b6000 08:10 330240                     /opt/glibc/lib/libc-2.33.so
7f3e9f8bc000-7f3e9f8c0000 rw-p 00000000 00:00 0 
7f3e9f8c0000-7f3e9f8c3000 r--p 00000000 08:10 337766                     /opt/glibc/lib/libnss_files-2.33.so
7f3e9f8c3000-7f3e9f8c9000 r-xp 00003000 08:10 337766                     /opt/glibc/lib/libnss_files-2.33.so
7f3e9f8c9000-7f3e9f8cb000 r--p 00009000 08:10 337766                     /opt/glibc/lib/libnss_files-2.33.so
7f3e9f8cb000-7f3e9f8cc000 r--p 0000a000 08:10 337766                     /opt/glibc/lib/libnss_files-2.33.so
7f3e9f8cc000-7f3e9f8cd000 rw-p 0000b000 08:10 337766                     /opt/glibc/lib/libnss_files-2.33.so
7ffcb4dd2000-7ffcb4df4000 rw-p 00000000 00:00 0                          [stack]
7ffcb4df9000-7ffcb4dfc000 r--p 00000000 00:00 0                          [vvar]
7ffcb4dfc000-7ffcb4dfe000 r-xp 00000000 00:00 0                          [vdso]

When trying to add -lnss_files to link statically against it I found that there is no libnss_files.a in my custom glibc installation. I was able to find libnss_files_pic.a where I build glibc, but even copying it to the glibc installation and linking against it didn't help.

I wonder if there's even a way to prevent library loading at runtime except for moving to an alternative libc implementation...

Dan Arad
  • 241
  • 1
  • 13
  • 1
    `gethostbyname` uses [nsswitch.conf(5)](https://man7.org/linux/man-pages/man5/nsswitch.conf.5.html) which requires different behavior at runtime. You could patch https://musl.libc.org/ to fit your needs – Basile Starynkevitch Feb 26 '21 at 12:50
  • 1
    Generally speaking, you oughtn't to need to build your own glibc to build (all-)static executables, but there are exceptions. In particular, anything that offers a plugin-style model for modules or extensions almost certainly uses dynamic linking to do so, and this will require some shared-library components. Glibc's name resolver is a component that does this. – John Bollinger Feb 26 '21 at 13:12
  • @BasileStarynkevitch So if I understand correctly, even `musl` can't be built in a way that wouldn't load libraries at runtime without patching? If that's the case, why patch `musl` and not `glibc`? – Dan Arad Feb 26 '21 at 13:13
  • I don't know `musl libc` well enough. Please download it and study its source code. You could also try [dietlibc](https://www.fefe.de/dietlibc/). But **you really should explain** (in several paragraphs of written English) ***why* are you trying to create a static application that will not load libraries on run time.** – Basile Starynkevitch Feb 26 '21 at 13:19
  • **In 2021, not using any shared libraries is really strange**. Read Drepper's paper [*How to write shared libraries*](https://software.intel.com/sites/default/files/m/a/1/e/dsohowto.pdf). I don't understand why you refuse to use shared libraries, and that should be explained in your question (in several paragraphs of written English) – Basile Starynkevitch Feb 26 '21 at 13:27
  • I simply want my program to run on many unix-based operating systems and thought it was a good way to eliminate missing dependencies. I didn't even think it was relevant to my question as it is purely technical. I'll read Drepper's paper and see if it can help me. Thank you for your answer. – Dan Arad Feb 26 '21 at 14:38
  • @JohnBollinger, I know. I read about it a bit. In my case I do want to build an all-static executable and avoid this behavior... – Dan Arad Feb 26 '21 at 14:40
  • 1
    In that case, @DanArad, either `gethostbyname()` and other resolver functions must be avoided, or you need an alternative C library. If you choose the latter then you'll need to look into how to configure the alternative library's name resolver, as that might or might not be interoperable with the configuration for glibc's resolver. – John Bollinger Feb 26 '21 at 17:47
  • Thank you. I'm using libraries that call `gethostbyname` so I'll have to look into other implementations. – Dan Arad Feb 26 '21 at 20:16

1 Answers1

2

This is a regression introduced in glibc 2.33 as part of the refactoring to support automatic reloading of /etc/nsswitch.conf. I've reported it here:

Florian Weimer
  • 32,022
  • 3
  • 48
  • 92