Как получить имя функции из указателя функции в C? - PullRequest
52 голосов
/ 09 декабря 2008

Как получить имя функции из указателя функции в C?

Редактировать: Реальный случай таков: я пишу модуль ядра Linux и вызываю функции ядра. Некоторые из этих функций являются указателями, и я хочу проверить код этой функции в исходном коде ядра. Но я не знаю, на какую функцию он указывает. Я думал, что это можно сделать, потому что, когда система выходит из строя (паника ядра), она выводит на экран текущий стек вызовов с именами функций. Но, думаю, я ошибался ... я?

Ответы [ 12 ]

63 голосов
/ 09 декабря 2008

Я удивлен, почему все говорят, что это невозможно. Это возможно в Linux для нестатических функций.

Я знаю как минимум два способа добиться этого.

Существуют функции GNU для печати обратной трассировки: backtrace() и backtrace_symbols() (см. man). В вашем случае вам не нужен backtrace(), так как у вас уже есть указатель на функцию, вы просто передаете его в backtrace_symbols().

Пример (рабочий код):

#include <stdio.h>
#include <execinfo.h>

void foo(void) {
    printf("foo\n");
}

int main(int argc, char *argv[]) {
    void    *funptr = &foo;

    backtrace_symbols_fd(&funptr, 1, 1);

    return 0;
}

Компилировать с gcc test.c -rdynamic

Выход: ./a.out(foo+0x0)[0x8048634]

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

Другой способ - использовать dladdr() (другое расширение), я думаю, print_backtrace() использует dladdr(). dladdr() возвращает структуру Dl_info с именем функции в поле dli_sname. Я не привожу здесь пример кода, но он очевиден - подробности см. man dladdr.

NB! Оба подхода требуют, чтобы функция была нестатичной!

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

29 голосов
/ 09 декабря 2008

Это невозможно напрямую без дополнительной помощи.

Вы могли бы:

  1. поддерживать таблицу в вашей программе, отображающую указатели на имена

  2. проверить таблицу символов исполняемого файла, если она есть.

Последний, однако, сложен и не переносим. Метод будет зависеть от двоичного формата операционной системы (ELF, a.out, .exe и т. Д.), А также от любого перемещения, выполняемого компоновщиком.

РЕДАКТИРОВАТЬ: Поскольку вы теперь объяснили, каков ваш реальный вариант использования, ответ на самом деле не так сложно. Таблица символов ядра доступна в /proc/kallsyms, и есть API для доступа к ней:

#include <linux/kallsyms.h>

const char *kallsyms_lookup(unsigned long addr, unsigned long *symbolsize,
                            unsigned long *ofset, char **modname, char *namebuf)

void print_symbol(const char *fmt, unsigned long addr)

Для простых целей отладки последний, вероятно, будет делать именно то, что вам нужно - он берет адрес, форматирует его и отправляет его в printk, или вы можете использовать printk с указателем формата %pF. *

15 голосов
/ 19 февраля 2014

В ядре Linux вы можете напрямую использовать "% pF" формат printk!

void *func = &foo;
printk("func: %pF at address: %p\n", func, func);
5 голосов
/ 16 апреля 2013

В Linux работает следующее:

  • распечатать адрес функции, используя %p
  • Затем выполните nm <program_path> | grep <address> (без префикса 0x)
  • Он должен показать вам имя функции.

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

Если вы можете узнать адреса загрузки загруженных общих библиотек, вы можете вычесть адрес из напечатанного номера и использовать nm в библиотеке, чтобы узнать имя функции.

2 голосов
/ 06 января 2012

Если список функций, на которые можно указать, не слишком велик или если вы уже подозреваете о небольшой группе функций, вы можете распечатать адреса и сравнить их с той, которая использовалась во время выполнения. Пример:

typedef void (*simpleFP)();
typedef struct functionMETA {
    simpleFP funcPtr;
    char * funcName;
} functionMETA;

void f1() {/*do something*/}
void f2() {/*do something*/}
void f3() {/*do something*/}

int main()
{
    void (*funPointer)() = f2; // you ignore this
    funPointer(); // this is all you see

    printf("f1 %p\n", f1);
    printf("f2 %p\n", f2);
    printf("f3 %p\n", f3);

    printf("%p\n", funPointer);

    // if you want to print the name
    struct functionMETA arrFuncPtrs[3] = {{f1, "f1"}, {f2, "f2"} , {f3, "f3"}};

    int i;
    for(i=0; i<3; i++) {
        if( funPointer == arrFuncPtrs[i].funcPtr )
            printf("function name: %s\n", arrFuncPtrs[i].funcName);
    }
}

Выход:

f1 0x40051b
f2 0x400521
f3 0x400527
0x400521
function name: f2

Этот подход будет работать и для статических функций.

2 голосов
/ 09 декабря 2008

Вы не можете напрямую, но вы можете реализовать другой подход к этой проблеме, если хотите. Вы можете сделать указатель структуры вместо того, чтобы указывать на функцию, а также на описательную строку, которую вы можете установить по своему усмотрению. Я также добавил отладочную позебилету, так как вы, вероятно, не хотите, чтобы эти переменные были вечными.

// Define it like this
typedef struct
{
  char        *dec_text;
  #ifdef _DEBUG_FUNC
  void        (*action)(char);
  #endif
} func_Struct;

// Initialize it like this
func_Struct func[3]= {
#ifdef _DEBUG_FUNC
{"my_Set(char input)",&my_Set}};
{"my_Get(char input)",&my_Get}};
{"my_Clr(char input)",&my_Clr}};
#else
{&my_Set}};
{&my_Get}};
{&my_Clr}};
#endif 

// And finally you can use it like this
func[0].action( 0x45 );
#ifdef _DEBUG_FUNC
printf("%s",func.dec_text);
#endif
1 голос
/ 09 декабря 2008

Вообще нет способа сделать это.

Если вы скомпилируете соответствующий код в DLL / Shared Library, вы сможете включить все точки входа и сравнить с указателем, который у вас есть. Еще не пробовал, но у меня есть некоторый опыт работы с DLL / Shared Libs, и я ожидаю, что он будет работать. Это может быть реализовано даже для кросс-платформенности.

Кто-то еще упоминал, что уже компилировал символы отладки, тогда вы можете попытаться найти способ проанализировать их из запущенного приложения, подобно тому, что будет делать отладчик. Но это абсолютно запатентованное и не переносимое приложение.

0 голосов
/ 18 марта 2019

Не совсем то, о чем спрашивает вопрос, но после прочтения ответов здесь Я, хотя из этого решения аналогичной моей проблемы:

/**
* search methods */
static int starts(const char *str, const char *c);
static int fuzzy(const char *str, const char *c);

int (*search_method)(const char *, const char *);

/* asign the search_method and do other stuff */
[...]

printf("The search method is %s\n", search_method == starts ? "starts" : "fuzzy")

Если вашей программе это очень нужно, вы можете определить имена методов вместе со строкой в ​​XMacro и использовать #define X(name, str) ... #undef X в коде, чтобы получить соответствующую строку из имени функции.

0 голосов
/ 27 июня 2016
  1. Используйте kallsyms_lookup_name(), чтобы найти адрес kallsyms_lookup.

  2. Используйте для вызова указатель функции, который указывает на kallsyms_lookup.

0 голосов
/ 09 декабря 2008

Ты бы не знал, как ты выглядишь без отражающего зеркала. Вам придется использовать язык с поддержкой отражений, такой как C #.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...