Как получить абсолютное имя файла библиотеки, соответствующее относительному пути, указанному для dlopen? - PullRequest
11 голосов
/ 13 июля 2011

В моей программе есть код, подобный следующему

/* libname may be a relative path */
void loadLib(char const *libname) {
   void *handle = dlopen(libname);
   /* ... */
   dlclose(handle);
}

В /* .. */ мне нужно прочитать файл карты памяти /proc/self/maps, чтобы найти адрес виртуальной памяти, на который отображается libname, а также мне нужно открыть библиотеку, чтобы найти в ней определенные разделы. Для этого мне нужно абсолютное имя, найденное dlopen при поиске в различных местах (например, в файле кэша ldconfig). Как я могу получить это имя файла?


Это то, к чему я наконец пришел ( да, это код C ++, тем не менее, тег C имеет смысл для этого вопроса, поскольку dlopen используется как с C ++, так и с C, и мой вопрос подходит как для POSIX определяет его для C. ).

   boost::shared_ptr<void> dl;
   if(void *handle = dlopen(libfile, RTLD_LAZY)) {
      dl.reset(handle, &dlclose);
   } else {
      printdlerr();
      return -1;
   }

   /* update sofile to be an absolute file name */
   {
      struct link_map *map;
          dlinfo(dl.get(), RTLD_DI_LINKMAP, &map);
      if(!map) {
         return -1;
      }
      char *real = realpath(map->l_name, NULL);
      if(!real)
         return -1;
      sofile.reset(real, &free);
   }

libfile - относительное / простое имя файла. Карта даст непростое имя файла (т.е. не foo.so, но может быть ./foo.so). После этого я использовал realpath, чтобы получить окончательный абсолютный путь. Работает хорошо!

Ответы [ 3 ]

10 голосов
/ 13 июля 2011

вы можете использовать

... dlinfo(handle, RTLD_DI_LINKMAP, p)
p->l_name ...

где p имеет тип Link_map **

см. Man dlinfo для деталей

1 голос
/ 13 июля 2011

Единственное решение - имитировать алгоритм системы.Это не так сложно, как кажется (хотя, как всегда, дьявол кроется в деталях): я использую следующее, чтобы найти путь к исполняемому файлу:

std::string retval = our_argv0;
if ( !isAbsolute( retval ) )
{
    char const* tmp = getenv( "PATH" );
    if ( tmp == NULL )
        throw std::runtime_error( "$PATH not set" );
    std::vector<std::string> dirs( split( std::string( tmp ), ":" ) );
    std::vector<std::string>::const_iterator i = dirs.begin();
    while ( i != dirs.end() 
            && ! access( (*i + '/' + retval).c_str(), X_OK ) == 0)
        ++ i;
    if ( i == dirs.end() )
        throw std::runtime_error("Cannot find load path");
    retval = *i + '/' + retval;
}
return std::string(
    retval.begin(),
    std::find( retval.rbegin(), retval.rend(), '/' ).base() );

Вы должны иметь возможность адаптировать его длябиблиотека, используя имя библиотеки вместо argv[0], LD_LIBRARY_PATH вместо PATH и соответствующее значение по умолчанию вместо броска, если он не установлен.Вероятно, есть особые случаи, которые он не обрабатывает, но вышесказанное помогает нам найти исполняемый файл.(split и isAbsolute - это другие функции в нашей библиотеке, которые делают очевидные вещи.)

0 голосов
/ 13 июля 2011

Один вариант, о котором я могу подумать, это использовать функцию pathfind():

char *pathfind(const char *path, const char *name, const char *mode);

DL может быть загружен из одного из трех мест: текущего каталога, каталога, в котором находился exec, и LD_LIBRARY_PATH - вы можете проверитьпоследние два - и используйте pathfind с параметром getenv("LD_LIBRARY_PATH") для path, чтобы попытаться найти другой.

...