GNU/Linux >> Linux の 問題 >  >> Linux

プログラムで共有ライブラリの関数名を取得する

更新 | TL;DR :

私は実際にもっと短い方法を見つけました:

    auto library = dlopen("/path/to/lib.so", RTLD_LAZY | RTLD_GLOBAL);
    const char * libname = "lib.so";
    struct link_map * map = nullptr;
    dlinfo(library, RTLD_DI_LINKMAP, &map);

    Elf64_Sym * symtab = nullptr;
    char * strtab = nullptr;
    int symentries = 0;
    for (auto section = map->l_ld; section->d_tag != DT_NULL; ++section)
    {
        if (section->d_tag == DT_SYMTAB)
        {
            symtab = (Elf64_Sym *)section->d_un.d_ptr;
        }
        if (section->d_tag == DT_STRTAB)
        {
            strtab = (char*)section->d_un.d_ptr;
        }
        if (section->d_tag == DT_SYMENT)
        {
            symentries = section->d_un.d_val;
        }
    }
    int size = strtab - (char *)symtab;
    for (int k = 0; k < size / symentries; ++k)
    {
        auto sym = &symtab[k];
        // If sym is function
        if (ELF64_ST_TYPE(symtab[k].st_info) == STT_FUNC)
        {
            //str is name of each symbol
            auto str = &strtab[sym->st_name];
            printf("%s\n", str);
        }
    }

古い

作者はもうこれを必要としないと思いますが、おそらく誰かが実際のコードを必要としており、ここにあります(以前の回答に基づく)

まず、dl_iterate_phdr() のコールバックが必要です :

static int callback(struct dl_phdr_info *info, size_t size, void *data)
{
    // data is copy of 2nd arg in dl_iterate_phdr
    // you can use it for your lib name as I did
    const char * libname = (const char *)data;

    // if current elf's name contains your lib
    if (strstr(info->dlpi_name, libname))
    {

        printf("loaded %s from: %s\n", libname, info->dlpi_name);

        for (int j = 0; j < info->dlpi_phnum; j++)
        {
            // we need to save dyanmic section since it contains symbolic table
            if (info->dlpi_phdr[j].p_type == PT_DYNAMIC)
            {
                Elf64_Sym * symtab = nullptr;
                char * strtab = nullptr;
                int symentries = 0;
                auto dyn = (Elf64_Dyn *)(info->dlpi_addr + info->dlpi_phdr[j].p_vaddr);
                for (int k = 0; k < info->dlpi_phdr[j].p_memsz / sizeof(Elf64_Dyn); ++k)
                {
                    if (dyn[k].d_tag == DT_SYMTAB)
                    {
                        symtab = (Elf64_Sym *)dyn[k].d_un.d_ptr;
                    }
                    if (dyn[k].d_tag == DT_STRTAB)
                    {
                        strtab = (char*)dyn[k].d_un.d_ptr;
                    }
                    if (dyn[k].d_tag == DT_SYMENT)
                    {
                        symentries = dyn[k].d_un.d_val;
                    }
                }
                int size = strtab - (char *)symtab;
                // for each string in table
                for (int k = 0; k < size / symentries; ++k)
                {
                    auto sym = &symtab[k];
                    auto str = &strtab[sym->st_name];
                    printf("%s\n", str);
                }
                break;
            }
        }
    }
    return 0;
}

次に、dl_iterate_phdr() を呼び出します :

int main()
{
    auto library = dlopen("/path/to/library.so", RTLD_LAZY | RTLD_GLOBAL);
    const char * libname = "library.so";
    dl_iterate_phdr(callback, (void*)libname);
    return 0;
}

これらの名前をどこかに保存する必要がある場合は、ポインターをコンテナーに渡し、キャストで復元してそこに書き込むことができます。

サンプル ライブラリの場合:

#include "simple_lib.h"

#include <cstdio>

void __attribute__ ((constructor)) initLibrary(void)
{
    printf("Library is initialized\n");
}
void __attribute__ ((destructor)) cleanUpLibrary(void)
{

    printf("Library is exited\n");
}

void make_number()
{
    printf("1\n");
}

これを印刷します:

Library is initialized

_ITM_deregisterTMCloneTable
puts
__gmon_start__
_ITM_registerTMCloneTable
__cxa_finalize
_Z11initLibraryv
make_number
_Z14cleanUpLibraryv
Library is exited

それを行う libc 関数はありません。ただし、自分で作成することもできます (または、readelf などのツールからコードをコピーして貼り付けることもできます)。

Linux では、dlopen() link_map のアドレスを返します l_addr という名前のメンバーを持つ構造体 これは、ロードされた共有オブジェクトのベース アドレスを指します (システムが共有ライブラリの配置をランダム化せず、ライブラリが事前にリンクされていないと仮定します)。

Linux では、ベース アドレス (Elf*_Ehdr のアドレス) を見つける方法 ) は dl_iterate_phdr() を使用することです dlopen()以降

ELF ヘッダーがあれば、最初に Elf*_Phdr を見つけて、エクスポートされたシンボルのリスト (動的シンボル テーブル) を反復処理できるはずです。 タイプ PT_DYNAMIC の 、そして DT_SYMTAB を見つけます 、 DT_STRTAB エントリ、および動的シンボル テーブル内のすべてのシンボルを反復処理します。 /usr/include/elf.h を使用

さらに、個人的によく知らない libelf を使用することもできます。

ただし、定義済み関数のリストは取得できますが、それらを呼び出す方法がわからないことに注意してください。


Linux
  1. Linux 共有ライブラリの紹介 (共有ライブラリの作成方法)

  2. Linux 上の C++ 動的共有ライブラリ

  3. gcc の共有ライブラリ関数のスタティック リンク

  1. プログラムで別のプロセスの親 pid を取得しますか?

  2. Linux で共有ライブラリを初期化する方法

  3. シェル スクリプトで関数名のリストを取得する

  1. Linux で共有ライブラリのバージョン管理を行う方法は?

  2. libcrypto ライブラリ エラーが見つかりません

  3. 共有ライブラリの読み込みエラー (glew)