Получить имена и адреса экспортированных функций в linux

Я могу получить список экспортированных имен функций и указателей из исполняемого файла в Windows с помощью API PIMAGE_DOS_HEADER (пример).

Что такое эквивалентный API для Linux?

Для контекста я создаю исполняемые файлы модульного теста и экспортирую функции, начинающиеся с имени «test_», и я хочу, чтобы исполняемый файл просто прокручивался и выполнял все тестовые функции при запуске.

Пример псевдокода:

int main(int argc, char** argv)
{
    auto run = new_trun();
    auto module = dlopen(NULL);
    auto exports = get_exports(module);  // <- how do I do this on unix?
    for( auto i = 0; i < exports->length; i++)
    {
        auto export = exports[i];
        if(strncmp("test_", export->name, strlen("test_")) == 0)
        {
          tcase_add(run, export->name, export->func);
        }
    }

    return trun_run(run);
}

ИЗМЕНИТЬ:

Я смог найти то, что я был после использования верхнего ответа на этот вопрос: Перечислите все функции/символы на лету в C?

Кроме того, мне пришлось использовать функцию gnu_hashtab_symbol_count из ответа Nominal Animal ниже, чтобы обрабатывать DT_GNU_HASH вместо DT_HASH.

Моя финальная тестовая основная функция выглядит так:

int main(int argc, char** argv)
{
    vector<string> symbols;
    dl_iterate_phdr(retrieve_symbolnames, &symbols);

    TRun run;
    auto handle = dlopen(NULL, RTLD_LOCAL | RTLD_LAZY);
    for(auto i = symbols.begin(); i != symbols.end(); i++)
    {
        auto name = *i;
        auto func = (testfunc)dlsym(handle, name.c_str());
        TCase tcase;
        tcase.name = string(name);
        tcase.func = func;
        run.test_cases.push_back(tcase);
    }

    return trun_run(&run);
}

Затем я определяю тесты в сборке, например:

// test.h
#define START_TEST(name) extern "C" EXPORT TResult test_##name () {
#define END_TEST return tresult_success(); }

// foo.cc
START_TEST(foo_bar)
{
    assert_pending();
} 
END_TEST

Что производит вывод, который выглядит следующим образом:

test_foo_bar: pending

  1 pending
  0 succeeded
  1 total

person justin.m.chase    schedule 27.04.2015    source источник
comment
Просто я правильно понял: вы хотите получить символы, экспортированные из .so под Linux, верно? Вы не хотите анализировать .dll? Или вы используете моно, и это выводит файлы DLL даже в Linux?   -  person Christian Aichinger    schedule 27.04.2015
comment
Правильно, .so под Linux. Я могу успешно сделать это на .dll для Windows, и поэтому я хочу знать, как сделать то же самое под Linux.   -  person justin.m.chase    schedule 27.04.2015
comment
Вы искали документацию? Формат файла для Linux называется ELF — возможно, вы этого не знали.   -  person Jongware    schedule 27.04.2015
comment
Вы можете изучить динамическое связывание и dlsym (linux.die.net/man/3/dlsym)   -  person Morten Jensen    schedule 28.04.2015
comment
Спасибо @MortenJensen, по сути, это то, что я сделал. Я получил имена экспортированных функций, а затем использовал dlsym, чтобы получить указатель на функцию.   -  person justin.m.chase    schedule 28.04.2015


Ответы (2)


Меня очень раздражает, когда я вижу вопросы о том, как сделать что-то в операционной системе X, что вы делаете в Y.

В большинстве случаев это бесполезный подход, потому что каждая операционная система (семейство) имеет свой собственный подход к проблемам, поэтому пытаться применить что-то, что работает в X, в Y — все равно, что засовывать куб в круглую дыру.

Обратите внимание: текст здесь задуман как резкий, а не снисходительный; мое владение английским языком не так хорошо, как хотелось бы. Судя по моему опыту, жесткость в сочетании с реальной помощью и указанием на известные рабочие решения лучше всего помогает преодолеть нетехнические ограничения.

В Linux тестовая среда должна использовать что-то вроде

LC_ALL=C LANG=C readelf -s FILE

чтобы перечислить все символы в FILE. readelf является частью пакета binutils и устанавливается, если вы собираетесь создавать в системе новые двоичные файлы. Это приводит к переносимому, надежному коду. Не забывайте, что Linux включает в себя несколько аппаратных архитектур, которые действительно отличаются друг от друга.

Для создания бинарных файлов в Linux вы обычно используете некоторые инструменты, предоставляемые в binutils. Если бы binutils предоставляла библиотеку или существовала библиотека ELF, основанная на коде, используемом в binutils, было бы гораздо лучше использовать ее, а не анализировать вывод утилит, созданных человеком. Однако такой библиотеки нет (внутренне используемая binutils библиотека libbfd не является специфичной для ELF). [URL=http://www.mr511.de/software/english.html]libelf[/URL] хорошая библиотека, но это совершенно отдельная работа, в основном одного автора. Об ошибках в нем сообщалось в binutils, что непродуктивно, поскольку они не связаны. Проще говоря, нет никаких гарантий, что он обрабатывает файлы ELF на данной архитектуре так же, как это делает binutils. Таким образом, для обеспечения устойчивости и надежности вам обязательно следует использовать binutils.

Если у вас есть тестовое приложение, оно должно использовать сценарий, скажем, /usr/lib/yourapp/list-test-functions, для вывода списка функций, связанных с тестированием:

#!/bin/bash
export LC_ALL=C LANG=C
for file in "$@" ; do
    readelf -s "$file" | while read num value size type bind vix index name dummy ; do
        [ "$type" = "FUNC" ] || continue
        [ "$bind" = "GLOBAL" ] || continue
        [ "$num" = "$[$num]" ] || continue
        [ "$index" = "$[$index]" ] || continue
        case "$name" in
            test_*) printf '%s\n' "$name"
                    ;;
        esac
    done
done

Таким образом, если есть особенности архитектуры (в частности, в формате вывода readelf binutils), вам нужно только изменить скрипт. Модифицировать такой простой скрипт несложно, и легко проверить правильность работы скрипта — просто сравните необработанный вывод readelf с выводом скрипта; любой может это сделать.

Подпрограмма, которая создает канал, fork()s дочерний процесс, выполняет сценарий в дочернем процессе и использует, например. getline() в родительском процессе для чтения списка имен довольно просто и чрезвычайно надежно. Поскольку это также одно из уязвимых мест, мы упростили исправление любых причуд или проблем здесь, используя этот внешний скрипт (который можно настраивать/расширять для устранения этих причуд и легко отлаживать). Помните, что если в самом binutils есть ошибки (кроме ошибок форматирования вывода), любые созданные двоичные файлы почти наверняка будут иметь те же ошибки.

Будучи ориентированным на Microsoft человеком, вам, вероятно, будет трудно понять преимущества такого модульного подхода. (Это относится не только к Microsoft, но и к экосистеме, контролируемой одним поставщиком, где подход, продвигаемый поставщиком, заключается в использовании всеобъемлющих фреймворков и черных ящиков с чистыми, но очень ограниченными интерфейсами. Я думаю, что это ограничение структуры, или обнесенный стеной сад, навязанный поставщиком, или сад-тюрьма. Выглядит хорошо, но выбраться сложно. Описание и историю модульного подхода, который я пытаюсь описать, см., например, в философия Unix в Википедии.)

Следующее показывает, что ваш подход действительно возможен и в Linux, хотя и неуклюжий и хрупкий; этот материал предназначен для использования стандартных инструментов. Это просто не правильный подход в целом.

Интерфейс symbols.h проще всего реализовать с помощью функции обратного вызова, которая вызывается для каждого найденного символа:

#ifndef  SYMBOLS_H
#ifndef _GNU_SOURCE
#error You must define _GNU_SOURCE!
#endif
#define  SYMBOLS_H
#include <stdlib.h>

typedef enum {
    LOCAL_SYMBOL  = 1,
    GLOBAL_SYMBOL = 2,
    WEAK_SYMBOL   = 3,
} symbol_bind;

typedef enum {
    FUNC_SYMBOL   = 4,
    OBJECT_SYMBOL = 5,
    COMMON_SYMBOL = 6,
    THREAD_SYMBOL = 7,
} symbol_type;

int symbols(int (*callback)(const char *libpath, const char *libname, const char *objname,
                            const void *addr, const size_t size,
                            const symbol_bind binding, const symbol_type type,
                            void *custom),
            void *custom);

#endif /* SYMBOLS_H */

Привязка символа ELF и макросы типов зависят от размера слова, поэтому, чтобы избежать хлопот, я объявил типы перечисления выше. Однако я пропустил некоторые неинтересные типы (STT_NOTYPE, STT_SECTION, STT_FILE).

Реализация, symbols.c:

#define _GNU_SOURCE
#include <stdlib.h>
#include <limits.h>
#include <string.h>
#include <stdio.h>
#include <fnmatch.h>
#include <dlfcn.h>
#include <link.h>
#include <errno.h>
#include "symbols.h"

#define UINTS_PER_WORD (__WORDSIZE / (CHAR_BIT * sizeof (unsigned int)))

static ElfW(Word) gnu_hashtab_symbol_count(const unsigned int *const table)
{
    const unsigned int *const bucket = table + 4 + table[2] * (unsigned int)(UINTS_PER_WORD);
    unsigned int              b = table[0];
    unsigned int              max = 0U;

    while (b-->0U)
        if (bucket[b] > max)
            max = bucket[b];

    return (ElfW(Word))max;
}

static symbol_bind elf_symbol_binding(const unsigned char st_info)
{
#if __WORDSIZE == 32
    switch (ELF32_ST_BIND(st_info)) {
#elif __WORDSIZE == 64
    switch (ELF64_ST_BIND(st_info)) {
#else
    switch (ELF_ST_BIND(st_info)) {
#endif
    case STB_LOCAL:  return LOCAL_SYMBOL;
    case STB_GLOBAL: return GLOBAL_SYMBOL;
    case STB_WEAK:   return WEAK_SYMBOL;
    default:         return 0;
    }
}

static symbol_type elf_symbol_type(const unsigned char st_info)
{
#if __WORDSIZE == 32
    switch (ELF32_ST_TYPE(st_info)) {
#elif __WORDSIZE == 64
    switch (ELF64_ST_TYPE(st_info)) {
#else
    switch (ELF_ST_TYPE(st_info)) {
#endif
    case STT_OBJECT: return OBJECT_SYMBOL;
    case STT_FUNC:   return FUNC_SYMBOL;
    case STT_COMMON: return COMMON_SYMBOL;
    case STT_TLS:    return THREAD_SYMBOL;
    default:         return 0;
    }
}

static void *dynamic_pointer(const ElfW(Addr) addr,
                             const ElfW(Addr) base, const ElfW(Phdr) *const header, const ElfW(Half) headers)
{
    if (addr) {
        ElfW(Half) h;

        for (h = 0; h < headers; h++)
            if (header[h].p_type == PT_LOAD)
                if (addr >= base + header[h].p_vaddr &&
                    addr <  base + header[h].p_vaddr + header[h].p_memsz)
                    return (void *)addr;
    }

    return NULL;
}

struct phdr_iterator_data {
    int  (*callback)(const char *libpath, const char *libname,
                     const char *objname, const void *addr, const size_t size,
                     const symbol_bind binding, const symbol_type type,
                     void *custom);
    void  *custom;
};

static int iterate_phdr(struct dl_phdr_info *info, size_t size, void *dataref)
{
    struct phdr_iterator_data *const data = dataref;
    const ElfW(Addr)                 base = info->dlpi_addr;
    const ElfW(Phdr) *const          header = info->dlpi_phdr;
    const ElfW(Half)                 headers = info->dlpi_phnum;
    const char                      *libpath, *libname;
    ElfW(Half)                       h;

    if (!data->callback)
        return 0;

    if (info->dlpi_name && info->dlpi_name[0])
        libpath = info->dlpi_name;
    else
        libpath = "";

    libname = strrchr(libpath, '/');
    if (libname && libname[0] == '/' && libname[1])
        libname++;
    else
        libname = libpath;

    for (h = 0; h < headers; h++)
        if (header[h].p_type == PT_DYNAMIC) {
            const ElfW(Dyn)  *entry = (const ElfW(Dyn) *)(base + header[h].p_vaddr);
            const ElfW(Word) *hashtab;
            const ElfW(Sym)  *symtab = NULL;
            const char       *strtab = NULL;
            ElfW(Word)        symbol_count = 0;

            for (; entry->d_tag != DT_NULL; entry++)
                switch (entry->d_tag) {
                case DT_HASH:
                    hashtab = dynamic_pointer(entry->d_un.d_ptr, base, header, headers);
                    if (hashtab)
                        symbol_count = hashtab[1];
                    break;
                case DT_GNU_HASH:
                    hashtab = dynamic_pointer(entry->d_un.d_ptr, base, header, headers);
                    if (hashtab) {
                        ElfW(Word) count = gnu_hashtab_symbol_count(hashtab);
                        if (count > symbol_count)
                            symbol_count = count;
                    }
                    break;
                case DT_STRTAB:
                    strtab = dynamic_pointer(entry->d_un.d_ptr, base, header, headers);
                    break;
                case DT_SYMTAB:
                    symtab = dynamic_pointer(entry->d_un.d_ptr, base, header, headers);
                    break;
                }

            if (symtab && strtab && symbol_count > 0) {
                ElfW(Word)  s;

                for (s = 0; s < symbol_count; s++) {
                    const char *name;
                    void *const ptr = dynamic_pointer(base + symtab[s].st_value, base, header, headers);
                    symbol_bind bind;
                    symbol_type type;
                    int         result;

                    if (!ptr)
                        continue;

                    type = elf_symbol_type(symtab[s].st_info);
                    bind = elf_symbol_binding(symtab[s].st_info);
                    if (symtab[s].st_name)
                        name = strtab + symtab[s].st_name;
                    else
                        name = "";

                    result = data->callback(libpath, libname, name, ptr, symtab[s].st_size, bind, type, data->custom);
                    if (result)
                        return result;
                }
            }
        }

    return 0;
}

int symbols(int (*callback)(const char *libpath, const char *libname, const char *objname,
                            const void *addr, const size_t size,
                            const symbol_bind binding, const symbol_type type,
                            void *custom),
            void *custom)
{
    struct phdr_iterator_data data;

    if (!callback)
        return errno = EINVAL;

    data.callback = callback;
    data.custom = custom;

    return errno = dl_iterate_phdr(iterate_phdr, &data);
}

При компиляции вышеперечисленного не забудьте скомпоновать библиотеку dl.

Вы можете найти функцию gnu_hashtab_symbol_count() выше интересной; формат таблицы не очень хорошо документирован нигде, что я могу найти. Это протестировано для работы как с архитектурой i386, так и с архитектурой x86-64, но его следует проверить на соответствие исходным кодам GNU, прежде чем полагаться на него в производственном коде. Опять же, лучший вариант — просто использовать эти инструменты напрямую через вспомогательный скрипт, поскольку они будут установлены на любой машине разработки.

Технически таблица DT_GNU_HASH сообщает нам первый динамический символ, а наивысший индекс в любом хэш-сегменте сообщает нам последний динамический символ, но, поскольку записи в таблице символов DT_SYMTAB всегда начинаются с 0 (на самом деле запись 0 — это «нет», ), я рассматриваю только верхний предел.

Для сопоставления имен библиотек и функций я рекомендую использовать strncmp() для сопоставления префикса для библиотек (совпадение в начале имени библиотеки, до первого .). Конечно, вы можете использовать fnmatch(), если предпочитаете шаблоны глобусов, или regcomp()+regexec(), если вы предпочитаете регулярные выражения (они встроены в библиотеку GNU C, никакие внешние библиотеки не используются). нужный).

Вот пример программы example.c, которая просто выводит все символы:

#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <dlfcn.h>
#include <errno.h>
#include "symbols.h"

static int my_func(const char *libpath, const char *libname, const char *objname,
                   const void *addr, const size_t size,
                   const symbol_bind binding, const symbol_type type,
                   void *custom __attribute__((unused)))
{
    printf("%s (%s):", libpath, libname);

    if (*objname)
        printf(" %s:", objname);
    else
        printf(" unnamed");

    if (size > 0)
        printf(" %zu-byte", size);

    if (binding == LOCAL_SYMBOL)
        printf(" local");
    else
    if (binding == GLOBAL_SYMBOL)
        printf(" global");
    else
    if (binding == WEAK_SYMBOL)
        printf(" weak");

    if (type == FUNC_SYMBOL)
        printf(" function");
    else
    if (type == OBJECT_SYMBOL || type == COMMON_SYMBOL)
        printf(" variable");
    else
    if (type == THREAD_SYMBOL)
        printf(" thread-local variable");

    printf(" at %p\n", addr);
    fflush(stdout);

    return 0;
}

int main(int argc, char *argv[])
{
    int  arg;

    for (arg = 1; arg < argc; arg++) {
        void *handle = dlopen(argv[arg], RTLD_NOW);
        if (!handle) {
            fprintf(stderr, "%s: %s.\n", argv[arg], dlerror());
            return EXIT_FAILURE;
        }

        fprintf(stderr, "%s: Loaded.\n", argv[arg]);
    }

    fflush(stderr);

    if (symbols(my_func, NULL))
        return EXIT_FAILURE;

    return EXIT_SUCCESS;
}

Чтобы скомпилировать и запустить вышеуказанное, используйте, например

gcc -Wall -O2 -c symbols.c
gcc -Wall -O2 -c example.c
gcc -Wall -O2 example.o symbols.o -ldl -o example
./example | less

Чтобы увидеть символы в самой программе, используйте флаг -rdynamic во время компоновки, чтобы добавить все символы в динамическую таблицу символов:

gcc -Wall -O2 -c symbols.c
gcc -Wall -O2 -c example.c
gcc -Wall -O2 -rdynamic example.o symbols.o -ldl -o example
./example | less

В моей системе последний распечатывает

 (): stdout: 8-byte global variable at 0x602080
 (): _edata: global at 0x602078
 (): __data_start: global at 0x602068
 (): data_start: weak at 0x602068
 (): symbols: 70-byte global function at 0x401080
 (): _IO_stdin_used: 4-byte global variable at 0x401150
 (): __libc_csu_init: 101-byte global function at 0x4010d0
 (): _start: global function at 0x400a57
 (): __bss_start: global at 0x602078
 (): main: 167-byte global function at 0x4009b0
 (): _init: global function at 0x4008d8
 (): stderr: 8-byte global variable at 0x602088
/lib/x86_64-linux-gnu/libdl.so.2 (libdl.so.2): unnamed local at 0x7fc652097000
/lib/x86_64-linux-gnu/libdl.so.2 (libdl.so.2): unnamed local at 0x7fc652097da0
/lib/x86_64-linux-gnu/libdl.so.2 (libdl.so.2): __asprintf: global function at 0x7fc652097000
/lib/x86_64-linux-gnu/libdl.so.2 (libdl.so.2): free: global function at 0x7fc652097000
...
/lib/x86_64-linux-gnu/libdl.so.2 (libdl.so.2): dlvsym: 118-byte weak function at 0x7fc6520981f0
/lib/x86_64-linux-gnu/libc.so.6 (libc.so.6): unnamed local at 0x7fc651cd2000
/lib/x86_64-linux-gnu/libc.so.6 (libc.so.6): unnamed local at 0x7fc651cf14a0
/lib/x86_64-linux-gnu/libc.so.6 (libc.so.6): unnamed local at 0x7fc65208c740
/lib/x86_64-linux-gnu/libc.so.6 (libc.so.6): _rtld_global: global variable at 0x7fc651cd2000
/lib/x86_64-linux-gnu/libc.so.6 (libc.so.6): __libc_enable_secure: global variable at 0x7fc651cd2000
/lib/x86_64-linux-gnu/libc.so.6 (libc.so.6): __tls_get_addr: global function at 0x7fc651cd2000
/lib/x86_64-linux-gnu/libc.so.6 (libc.so.6): _rtld_global_ro: global variable at 0x7fc651cd2000
/lib/x86_64-linux-gnu/libc.so.6 (libc.so.6): _dl_find_dso_for_object: global function at 0x7fc651cd2000
/lib/x86_64-linux-gnu/libc.so.6 (libc.so.6): _dl_starting_up: weak at 0x7fc651cd2000
/lib/x86_64-linux-gnu/libc.so.6 (libc.so.6): _dl_argv: global variable at 0x7fc651cd2000
/lib/x86_64-linux-gnu/libc.so.6 (libc.so.6): putwchar: 292-byte global function at 0x7fc651d4a210
...
/lib/x86_64-linux-gnu/libc.so.6 (libc.so.6): vwarn: 224-byte global function at 0x7fc651dc8ef0
/lib/x86_64-linux-gnu/libc.so.6 (libc.so.6): wcpcpy: 39-byte weak function at 0x7fc651d75900
/lib64/ld-linux-x86-64.so.2 (ld-linux-x86-64.so.2): unnamed local at 0x7fc65229b000
/lib64/ld-linux-x86-64.so.2 (ld-linux-x86-64.so.2): unnamed local at 0x7fc65229bae0
/lib64/ld-linux-x86-64.so.2 (ld-linux-x86-64.so.2): _dl_get_tls_static_info: 21-byte global function at 0x7fc6522adaa0
/lib64/ld-linux-x86-64.so.2 (ld-linux-x86-64.so.2): GLIBC_PRIVATE: global variable at 0x7fc65229b000
/lib64/ld-linux-x86-64.so.2 (ld-linux-x86-64.so.2): GLIBC_2.3: global variable at 0x7fc65229b000
/lib64/ld-linux-x86-64.so.2 (ld-linux-x86-64.so.2): GLIBC_2.4: global variable at 0x7fc65229b000
/lib64/ld-linux-x86-64.so.2 (ld-linux-x86-64.so.2): free: 42-byte weak function at 0x7fc6522b2c40
...
/lib64/ld-linux-x86-64.so.2 (ld-linux-x86-64.so.2): malloc: 13-byte weak function at 0x7fc6522b2bf0
/lib64/ld-linux-x86-64.so.2 (ld-linux-x86-64.so.2): _dl_allocate_tls_init: 557-byte global function at 0x7fc6522adc00
/lib64/ld-linux-x86-64.so.2 (ld-linux-x86-64.so.2): _rtld_global_ro: 304-byte global variable at 0x7fc6524bdcc0
/lib64/ld-linux-x86-64.so.2 (ld-linux-x86-64.so.2): __libc_enable_secure: 4-byte global variable at 0x7fc6524bde68
/lib64/ld-linux-x86-64.so.2 (ld-linux-x86-64.so.2): _dl_rtld_di_serinfo: 1620-byte global function at 0x7fc6522a4710

Я использовал ..., чтобы отметить, где я удалил много строк.

Вопросы?

person Nominal Animal    schedule 28.04.2015
comment
Спасибо за ваш ответ, особенно за функцию gnu_hashtab_symbol_count, на которой я застрял и не мог понять. Мои тесты теперь работают, как я надеялся, и я счастлив. Несмотря на вашу снисходительность, вы должны быть правы насчет меня, потому что я нахожу перспективу обращения к сценарию, который затем вызывает другой процесс, очень непривлекательной. У меня нет проблем с пониманием преимуществ модульности, но у меня есть проблемы с пониманием того, что вызов функций во внешних библиотеках также не считается модульным, как вы, кажется, подразумеваете. - person justin.m.chase; 28.04.2015
comment
@justin.m.chase: Мне не хватает английского языка. Я предпочел резкость, а не снисходительность. В сочетании с реальной, реальной помощью в этом вопросе для меня это имеет тенденцию разрушать мои неправильные предубеждения. У меня их очень много. Теперь, если libelf был частью GNU binutils, вы были бы абсолютно правы: правильным подходом было бы просто использовать эту библиотеку. К сожалению, это не так; это в основном один автор, Майкл Рипе. (продолжение) - person Nominal Animal; 28.04.2015
comment
@justin.m.chase: libelf, безусловно, хорошая работа и практичное решение, но и пользователи, и разработчики, использующие его, должны понимать, что оно не является частью и изначально не является binutils. Это отдельная работа. Это означает, что возможны несоответствия и ошибки между тем, что делает binutils, и тем, что делает libelf. libelf активно поддерживается и имеет долгую историю, поэтому о любых найденных ошибках следует сообщать - но на mr511.de, а не на binutils. (продолжение еще немного) - person Nominal Animal; 28.04.2015
comment
@justin.m.chase: Итак, самый надежный вариант — использовать те же самые инструменты, которые вы используете для создания своих двоичных файлов в первую очередь. Поскольку библиотеки не связаны друг с другом, нам остается использовать отдельный скрипт для получения и преобразования удобочитаемого вывода инструментов во что-то, что потребляет приложение. Если вывод изменится, любой разработчик может отладить проблему и исправить скрипт, при условии, что он хорошо задокументирован в приложении. На несколько порядков проще просто исправить скрипт, чем получить исправление ошибки в библиотеке, а затем отправить изменения вверх по течению. - person Nominal Animal; 28.04.2015
comment
Спасибо, за помощь и объяснение. Если у меня возникнут какие-либо проблемы, я буду знать, как это исправить сейчас. - person justin.m.chase; 28.04.2015
comment
@Jongware: я добавил редактирование в попытке, но мне очень сложно передать эти концепции в краткой форме. Если вы считаете, что есть отрывки, которые можно было бы отформатировать лучше, отредактируйте. Пожалуйста, не предлагайте отказаться от какой-либо резкости, так как я искренне считаю, что это необходимо и полезно, и определенно хочу сохранить это. Без этого читатели проигнорируют реальные ограничения их подхода/точки зрения, а те, кто не желает рассматривать какие-либо изменения своей точки зрения и/или находят текст неудобным, не те, кому я хочу помочь. - person Nominal Animal; 01.05.2015
comment
Есть ли шанс заставить его работать на iOS? Похоже, <link.h> отсутствует. - person k06a; 06.10.2016
comment
@ k06a: Нет, я так не думаю, потому что Apple написала свой собственный динамический компоновщик (dyld); это очень отличается от GNU. У меня также нет устройств iOS, поэтому я не могу вам помочь, но вы можете начать с просмотра man 3 dyld информации для iOS; они, кажется, больше не в сети. Удачи и надеюсь, что Apple останется великодушной к вашим усилиям по разработке :) - person Nominal Animal; 07.10.2016
comment
Прямо сейчас я нашел MachO-Kit на GitHub и пытаюсь его использовать. - person k06a; 07.10.2016

Чтобы получить список экспортированных символов из общей библиотеки (.so) под Linux, есть два способа: простой и немного более сложный.

Самый простой — использовать уже доступные консольные инструменты: objdump (входит в GNU binutils):

$ objdump -T /usr/lib/libid3tag.so.0
00009c15 g    DF .text  0000012e  Base        id3_tag_findframe
00003fac g    DF .text  00000053  Base        id3_ucs4_utf16duplicate
00008288 g    DF .text  000001f2  Base        id3_frame_new
00007b73 g    DF .text  000003c5  Base        id3_compat_fixup
...

Немного сложнее использовать libelf и написать программу на C/C++ для самостоятельного перечисления символов. Взгляните на пакет elfutils, который также собран из исходников libelf. Существует программа под названием eu-readelf (версия readelf для elfutils, не путать с readelf для binutils). eu-readelf -s $LIB перечисляет экспортированные символы с помощью libelf, поэтому вы сможете использовать его в качестве отправной точки.

person Christian Aichinger    schedule 27.04.2015
comment
Я посмотрю libelf, спасибо. Мне нужно на самом деле вызывать функции, поэтому просто делать это с консоли - не совсем то, что я ищу. - person justin.m.chase; 27.04.2015
comment
@justin.m.chase: может ли этот предыдущий вопрос чем-нибудь помочь? stackoverflow.com/questions/15779185/ - person Jongware; 28.04.2015
comment
@justin.m.chase: обновил мой ответ. eu-readelf должно стать хорошей отправной точкой. Выполнение этого вручную, как предложил Jongware, вероятно, также работает, но может быть проще просто заставить libelf выполнять тяжелую работу. Кстати: вот некоторые документы ELF, которые я написал давным-давно. - person Christian Aichinger; 28.04.2015
comment
Спасибо всем. Я смотрел на ту ссылку, которую предложил Jongware, и она почти работает... Проблема, с которой я застрял прямо сейчас, заключается в том, что я получаю не DT_HASH, а вместо этого DT_GNU_HASH, и я еще не уверен, как получить количество сегментов. из этой структуры. Подойдя ближе, я прочитаю эльфийские документы, которые вы связали с Кристианом. - person justin.m.chase; 28.04.2015
comment
@ChristianAichinger В частности, я смотрю на: sco.com/developers/gabi /latest/ch5.dynamic.html#hash Но если вы получите DT_GNU_HASH вместо DT_HASH, структура будет неправильной. - person justin.m.chase; 28.04.2015
comment
См. здесь ( найден с помощью поиска кода Debian). - person Christian Aichinger; 28.04.2015