3

I'm doing a dynamic library (loader.so) and want to open a file depending on the name of the library. The idea is to copy/symlink the library to open different files.

Is it possible to find the filename of a library opened with dlopen(), from within the library? I need to do it at runtime.

edit: More context: The library gets loaded as a plugin and acts as a wrapper for Python plugins. This means that the library is loaded multiple times by the same process. Therefore, the methods of looking up a symbol fails. (Searching matches the first instance, so I get the same name returned to all library instances.)

Program -> loader_plugin1.so -> plugin1.py
        -> loader_another_plugin.so -> another_plugin.py
thomasa88
  • 630
  • 1
  • 4
  • 8
  • No. A single file can have multiple names in Unix-like filesystems. However, you might be able to do something like `lsof` on your own process, which could give you the info which name was used in opening the file. – Ulrich Eckhardt Jul 21 '19 at 10:50
  • 3
    See [`dladdr`](http://man7.org/linux/man-pages/man3/dladdr.3.html), pass it the address to a function in your shared object. – Geoffrey Jul 21 '19 at 10:51
  • If I were you, I would just parse `/proc/self/maps`; it's no big deal. –  Jul 21 '19 at 11:30

1 Answers1

3

You can do it with dl_iterate_phdr.

Example script (for building an example project; run in an empty directory):

#!/bin/sh -eu
cat > main.c <<EOF
void pr_libnm(void);
int main()
{
    pr_libnm();
    return 0;
}
EOF
cat > pr_libnm.c <<'EOF'
#define _GNU_SOURCE
#include  <link.h>
#include  <stdio.h>
#include  <stdint.h>
static int cb_(struct dl_phdr_info *Info, size_t Sz, void *Data)
{
    //printf("Name=%s\n",Info->dlpi_name);
    uintptr_t a = (uintptr_t)cb_;

    //for each elf-header, iterate thru program headers
    for(size_t i=0; i<Info->dlpi_phnum; i++){
        // [b,e) is the corresponding segment
        uintptr_t b = Info->dlpi_addr + Info->dlpi_phdr[i].p_paddr;
        uintptr_t e = b + Info->dlpi_phdr[i].p_memsz;
        if(a>=b && a<e){
            //if this is cb_'s segment, we're done
            printf("NAME=%s\n", Info->dlpi_name);
            //nonzero signals end of iteration
            return 1;
        }

    }
    return 0;
}
void pr_libnm(void)
{
    //iterate thru elf-headers
    dl_iterate_phdr(cb_ ,(void*)0);
}
EOF
: ${CC:=gcc}
for c in *.c; do $CC -fpic -c $c; done
$CC -shared -o libpr.so pr_libnm.o
$CC main.o $PWD/libpr.so
./a.out #prints NAME=$PWD/libpr.so

(Naturally, on Unix, a file can have multiple names. You'll just get the name of the shared object that was used to map it.)

dl_iterate_phdr iterates through the elf headers of the mapped object. Each elf header contains a pointer to the name of the object.

The program goes through each elf-object's segments and when it finds a segment that a function from the calling shared object maps to, it prints the name from the elf header and exits the iteration.

Petr Skocik
  • 58,047
  • 6
  • 95
  • 142
  • What is ``? – alk Jul 21 '19 at 11:24
  • @alk The header that declares `dl_iterate_phdr()` along with some other functions and structs related to dynamic linking. – Petr Skocik Jul 21 '19 at 11:26
  • Ah ok, yes, this is Linux specific. – alk Jul 21 '19 at 11:27
  • Thanks, but I forgot to mention that the library is loaded multiple times by the same process, making the search returning the first match library for both libraries. Interestingly, removing `return 1` in the loop did not produce two outputs - I would have expected one for each library. – thomasa88 Jul 21 '19 at 13:01
  • 1
    @thomasa88: I doubt any recent OS loaded a library more then once for the same process. – alk Jul 21 '19 at 16:51
  • @alk: Would that apply for `dlopen() `? The library is loaded as a plugin in a game. The game states that it loads loader_1.so and then loader_2.so. – thomasa88 Jul 22 '19 at 05:25
  • @thomasa88: Rendering my last comment more precisely: "*... loaded one specific library more than once ...*" – alk Jul 22 '19 at 05:30