Home > Enterprise >  dl_iterate_phdr returns empty image-name on first entry
dl_iterate_phdr returns empty image-name on first entry

Time:09-12

I've developed this little C program for Linux

#include <iostream>
#include <vector>
#include <string>
#include <functional>
#include <link.h>

using namespace std;

int main()
{
    vector<string> images;
    auto fnDlIterate = bind(
        [&]( dl_phdr_info *image, size_t size ) -> int
        {
            try
            {
                images.emplace_back( image->dlpi_name );
            }
            catch( bad_alloc const & )
            {
                return 1;
            }
            return 0;
        }, placeholders::_1, placeholders::_2 );
    using fn_callback_t = decltype(fnDlIterate);
    if( dl_iterate_phdr(
        []( dl_phdr_info *image, size_t size, void *pFn ) -> int
        {
            return (*(fn_callback_t *)pFn)( image, size );
        }, &fnDlIterate ) )
        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 ?

CodePudding user response:

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.

CodePudding user response:

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 <functional>
#include <link.h>
#include <climits>
#include <dlfcn.h>

using namespace std;

int main()
{
    size_t n = 0;
    dl_iterate_phdr( []( dl_phdr_info *, size_t, void *pN ) -> int {   *(size_t *)pN; return 0; }, &n );
    vector<string> images;
    images.reserve( n );
    auto populate = bind(
        [&]( dl_phdr_info *image, size_t size ) -> 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;
            }
        }, placeholders::_1, placeholders::_2 );
    using populate_t = decltype(populate);
    if( dl_iterate_phdr(
        []( dl_phdr_info *image, size_t size, void *pFn ) -> int
        {
            return (*(populate_t *)pFn)( image, size );
        }, &populate ) )
        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.

  • Related