skip to Main Content

I’ve developed this little C++ program for Linux

#include <iostream>
#include <vector>
#include <string>
#include <link.h>
#include <climits>
#include <dlfcn.h>

using namespace std;

template<typename Fn>
    requires requires( Fn fn, dl_phdr_info *image, size_t n ) { { fn( image, n ) } -> same_as<int>; } 
int dlIteratePhdr( Fn &&fn )
{
    return dl_iterate_phdr(
        []( dl_phdr_info *image, size_t size, void *pFn ) -> int
        {
            return (*(Fn *)pFn)( image, size );
        }, &fn );
};

int main()
{
    size_t nImages = 0;
    dlIteratePhdr( [&]( dl_phdr_info *, size_t ) -> int { ++nImages; return 0; } );
    vector<string> images;
    images.reserve( nImages );
    if( dlIteratePhdr(
        [&]( dl_phdr_info *image, size_t ) -> int
        {
            try
            {
                images.emplace_back( image->dlpi_name );
                return 0;
            }
            catch( ... )
            {
                return 1;
            }
        } ) )
        return EXIT_FAILURE;
    for( string const &image : images )
        cout << """ << image << """ << endl;
}

For my Ubuntu machine this prints:

""
"linux-vdso.so.1"
"/lib/x86_64-linux-gnu/libstdc++.so.6"
"/lib/x86_64-linux-gnu/libgcc_s.so.1"
"/lib/x86_64-linux-gnu/libc.so.6"
"/lib/x86_64-linux-gnu/libm.so.6"
"/lib64/ld-linux-x86-64.so.2"

Why is the name of the first image empty ?
Is this reserved for the executable itself ?
And is it really necessary to copy the information given to the callback or would this be alive after dl_iterate_phdr ?

2

Answers


  1. Why is the name of the first image empty ?

    Because the name of the main executable is not known to the loader.

    Unlike the shared libraries, which are mmaped by the loader, the main executable is mapped by the kernel itself, and only the file descriptor is passed in to the loader.

    As Hasturkun commented, this behavior is documented in the man page.

    Is this reserved for the executable itself ?

    Yes.

    And is it really necessary to copy the information …

    The dl_phdr_info that is passed in is stack-allocated, and will get overwritten after each step (in fact, it’s the same pointer every time; but this is an implementation detail).

    The strings pointed by dlpi_name are dynamically allocated by the loader.

    Strings corresponding to the libraries loaded during executable startup are likely to persist throughout the lifetime of the process, but strings corresponding to dlopen()ed libraries may get free()d on dlclose(). This is also an implementation detail and you are better off not relying on it.

    Login or Signup to reply.
  2. I found a way to determine the executable path also, here’s the above program which also does that:

    #include <iostream>
    #include <vector>
    #include <string>
    #include <link.h>
    #include <climits>
    #include <dlfcn.h>
    
    using namespace std;
    
    template<typename Fn>
        requires requires( Fn fn, dl_phdr_info *image, size_t n ) { { fn( image, n ) } -> same_as<int>; } 
    int dlIteratePhdr( Fn fn )
    {
        return dl_iterate_phdr(
            []( dl_phdr_info *image, size_t size, void *pFn ) -> int
            {
                return (*(Fn *)pFn)( image, size );
            }, &fn );
    };
    
    int main()
    {
        size_t nImages = 0;
        dlIteratePhdr( [&]( dl_phdr_info *, size_t ) -> int { ++nImages; return 0; } );
        vector<string> images;
        images.reserve( nImages );
        if( dlIteratePhdr(
            [&]( dl_phdr_info *image, size_t ) -> int
            {
                try
                {
                    if( *image->dlpi_name )
                        images.emplace_back( image->dlpi_name );
                    else
                    {
                        Dl_info dlInfo;
                        if( !dladdr( (void *)image->dlpi_addr, &dlInfo ) )
                            return 1;
                        char exePath[PATH_MAX];
                        if( !realpath( dlInfo.dli_fname, exePath ) )
                            return 1;
                        images.emplace_back( exePath );
                    }
                    return 0;
                }
                catch( bad_alloc const & )
                {
                    return 1;
                }
            } ) )
            return EXIT_FAILURE;
        for( string const &image : images )
            cout << """ << image << """ << endl;
    }
    

    I don’t know why the dl_iterate_phdr doesn’t supply that path by itself if I can manage to get it otherwise.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search