linuxlinux-kernelshared-librariesld-preload

why isn't shared library fini function called when preloaded to dash?


I am on the latest Ubuntu Linux.

Here is a shared library with functions that are called when loading and unloading:

shared.c:

#include <fcntl.h>
#include <sys/stat.h>

void init() {
    open("init", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
}

void fini() {
    open("fini", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
}

and this is compiled with

gcc -shared -fPIC -Wl,-init=init -Wl,-fini=fini shared.c -o shared.so

and then I do this:

$ rm init fini
$ LD_PRELOAD=$PWD/shared.so  dash /dev/null
$ echo $?
0
$ ls init
init
$ ls fini
ls: cannot access 'fini': No such file or directory

So... loading function is called, but unloading function is not

If I replace dash with bash , both are called.

Using __attribute__((destructor)) does not make a difference.

Why isn't the unloading function called for dash?

Added per Marco Bonelli's request:

$ file $(which dash)
/usr/bin/dash: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=f7ab02fc1b8ff61b41647c1e16ec9d95ba5de9f0, for GNU/Linux 3.2.0, stripped

$ ldd $(which dash)
        linux-vdso.so.1 (0x00007ffd931c0000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f6b19e84000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f6b1a0ec000)

Solution

  • why isn't shared library fini function called when preloaded to dash?

    Because dash calls _exit here which calls exit_group syscall here which terminates the program immediately.

    Let's also do a small example:

    # Makefile
    all:
            gcc -shared -fPIC -Wl,-init=init -Wl,-fini=fini -Wl,-soname,shared.so shared.c -o shared.so
            gcc main.c -o main.out
            LD_PRELOAD=$(PWD)/shared.so ./main.out /dev/null
    
    # main.c
    #include <unistd.h>
    int main() {
            _exit(0);
    }
    
    # shared.c
    #include <fcntl.h>
    #include <sys/stat.h>
    #include <unistd.h>
    #include <string.h>
    void out(const char *str) {
            int fd = open("/dev/tty", O_WRONLY);
            write(fd, str, strlen(str));
            close(fd);
    }
    void init() {
            out("\nstart\n\n");
    }
    void fini() {
            out("\nstop\n\n");
    }
    

    On execution does not output stop:

    $ make
    gcc -shared -fPIC -Wl,-init=init -Wl,-fini=fini -Wl,-soname,shared.so shared.c -o shared.so
    gcc main.c -o main.out
    LD_PRELOAD=..../shared.so ./main.out /dev/null
    
    start