Вземете имена и адреси на експортирани функции в linux

Мога да получа списък с експортирани имена на функции и указатели от изпълним файл в Windows, като използвам PIMAGE_DOS_HEADER API (пример).

Какъв е еквивалентният 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? Или използвате mono и това извежда 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, би било много по-добре да се използва това, вместо да се анализира изхода на човешките помощни програми. Въпреки това няма такава библиотека (библиотеката libbfd, която binutils използва вътрешно, не е специфична за 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 и макросите за тип са специфични за размера на думата, така че, за да избегна караницата, декларирах типовете enum по-горе. Пропуснах обаче някои безинтересни типове (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

За да получите списък с експортирани символи от споделена библиотека (a .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 (версията elfutils на readelf, да не се бърка с binutils readelf). 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, вероятно също работи, но може да е по-лесно просто да оставите клеветата да свърши тежката работа. Между другото: ето някои 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