skip to Main Content

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)

2

Answers


  1. I should have put this as a comment, but I put it as an answer for formatting purposes.

    The problem is not about the difference between redirection and touch, because in your dash/touch run, shared.so has been loaded twice, once for dash, once for touch.
    fini fired only with touch.

    You can see the diffrence with these two runs :

    $ LD_PRELOAD=$PWD/shared.so  dash  /dev/null
    # fini is not called
    $ LD_PRELOAD=$PWD/shared.so  touch /dev/null
    # fini is called
    

    So this has something to do with dash.

    Hope this provide more materials for your investigation.

    Login or Signup to reply.
  2. 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("nstartnn");
    }
    void fini() {
            out("nstopnn");
    }
    

    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
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search