2009-07-01 10 views
7

Когда я использую gdb для отладки программы, написанной на C, команда дизассемблирования показывает коды и их адреса в сегментации памяти кода. Возможно ли узнать эти адреса памяти во время выполнения? Я использую Ubuntu OS. Спасибо.Найти код программы во время выполнения?

[edit] Чтобы быть более конкретным, я продемонстрирую его в следующем примере.

#include <stdio.h> 

int main(int argc,char *argv[]){ 
    myfunction(); 
    exit(0); 
} 

Теперь я хотел бы иметь адрес MyFunction() в сегментации кода памяти при запуске программы.

+0

Вы спрашиваете о дизассемблере времени выполнения, встроенном в вашу программу? – 2009-07-01 15:52:35

+0

Что вы действительно хотите? –

+0

Спасибо всем. У меня есть хорошее понимание этого вопроса: D – wakandan

ответ

16

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

void* myfunction_address = myfunction; 

Если вы захватывая функцию динамически из общей библиотеки, то возвращается значение из dlsym() (POSIX) или G etProcAddress() (windows) также является адресом функции.

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

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

И, наконец, обратите внимание, что «адрес» функции немного неоднозначен. Если функция была загружена динамически или является ссылкой extern на экспортированный символ, то, что вы действительно получаете, обычно является указателем на некоторый код исправления в «PLT» (термин Unix/ELF, хотя механизм PE/COFF на окнах аналогичен), который затем переходит к функции.

+0

Большое спасибо за ваш фантастический ответ. Могу ли я задать этот вопрос заранее: как получить адрес определенной строки кода? – wakandan

+2

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

+0

Хороший ответ, хорошо объяснил. Вы также можете обойти тот факт, что преобразование кода и указателей данных не определено, применяя этот трюк (как это сделано в «man dlsym» в обратном направлении): void * p; * (void (**)()) & p = (void (*)()) & myfunction; или с помощью объединения. –

5

Чтобы получить обратную трассировку, используйте execinfo.h как задокументировано in the GNU libc manual.

Например:

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


void trace_pom() 
{ 
    const int sz = 15; 
    void *buf[sz]; 

    // get at most sz entries 
    int n = backtrace(buf, sz); 

    // output them right to stderr 
    backtrace_symbols_fd(buf, n, fileno(stderr)); 

    // but if you want to output the strings yourself 
    // you may use char ** backtrace_symbols (void *const *buffer, int size) 
    write(fileno(stderr), "\n", 1); 
} 


void TransferFunds(int n); 

void DepositMoney(int n) 
{ 
    if (n <= 0) 
     trace_pom(); 
    else TransferFunds(n-1); 
} 


void TransferFunds(int n) 
{ 
    DepositMoney(n); 
} 


int main() 
{ 
    DepositMoney(3); 

    return 0; 
} 

скомпилированного

 
gcc a.c -o a -g -Wall -Werror -rdynamic 

Согласно названному сайте:

В настоящее время имя функции и смещения можно получить только в системах, использующих ELF бинарный формат для программ и библиотек. В других системах будет присутствовать только шестнадцатеричный обратный адрес . Кроме того, вам может потребоваться передать дополнительные флаги компоновщику в , чтобы имена функций были доступны программе. (Например, в системах, использующих GNU Л.Д., вы должны пройти (-rdynamic.)

Выход

 
./a(trace_pom+0xc9)[0x80487fd] 
./a(DepositMoney+0x11)[0x8048862] 
./a(TransferFunds+0x11)[0x8048885] 
./a(DepositMoney+0x21)[0x8048872] 
./a(TransferFunds+0x11)[0x8048885] 
./a(DepositMoney+0x21)[0x8048872] 
./a(TransferFunds+0x11)[0x8048885] 
./a(DepositMoney+0x21)[0x8048872] 
./a(main+0x1d)[0x80488a4] 
/lib/i686/cmov/libc.so.6(__libc_start_main+0xe5)[0xb7e16775] 
./a[0x80486a1] 
+0

Есть ли способ, чтобы я мог извлечь функцию только в адрес DepositMoney, значения 0x8048872 должны быть единственным выходом вместо того, чтобы распечатывать весь backtrace? – wakandan

+1

А, извините, я думал, что вам нужна полная обратная трасса. –

8

Если вы знаете имя функции перед запуском программы, просто используйте

void * addr = myfunction; 

Если имя функции задаются во время выполнения, я когда-то написал функцию, чтобы узнать адрес символа динамически с помощью библиотеки BFD. Вот код x86_64, вы можете получить адрес через find_symbol («a.out», «myfunction») в примере.

#include <bfd.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <type.h> 
#include <string.h> 

long find_symbol(char *filename, char *symname) 
{ 
    bfd *ibfd; 
    asymbol **symtab; 
    long nsize, nsyms, i; 
    symbol_info syminfo; 
    char **matching; 

    bfd_init(); 
    ibfd = bfd_openr(filename, NULL); 

    if (ibfd == NULL) { 
     printf("bfd_openr error\n"); 
    } 

    if (!bfd_check_format_matches(ibfd, bfd_object, &matching)) { 
     printf("format_matches\n"); 
    } 

    nsize = bfd_get_symtab_upper_bound (ibfd); 
    symtab = malloc(nsize); 
    nsyms = bfd_canonicalize_symtab(ibfd, symtab); 

    for (i = 0; i < nsyms; i++) { 
     if (strcmp(symtab[i]->name, symname) == 0) { 
      bfd_symbol_info(symtab[i], &syminfo); 
      return (long) syminfo.value; 
     } 
    } 

    bfd_close(ibfd); 
    printf("cannot find symbol\n"); 
} 
+0

Спасибо, это то, что я искал – Ulterior

+0

Извините за некромант; однако, я нашел это действительно полезным. Знаете ли вы учебник, чтобы помочь с bfd? – SailorCire

3

О комментарий в ответ (получение адреса инструкции), вы можете использовать это очень некрасиво трюк

#include <setjmp.h> 

void function() { 
    printf("in function\n"); 
    printf("%d\n",__LINE__); 
    printf("exiting function\n"); 

} 

int main() { 
    jmp_buf env; 
    int i; 

    printf("in main\n"); 
    printf("%d\n",__LINE__); 
    printf("calling function\n"); 
    setjmp(env); 
    for (i=0; i < 18; ++i) { 
     printf("%p\n",env[i]); 
    }  
    function(); 
    printf("in main again\n"); 
    printf("%d\n",__LINE__); 

} 

Это должно быть окр [12] (далее EIP), но быть осторожно, поскольку он выглядит зависящим от машины, поэтому тройное подтверждение моего слова. Это выход

in main 
13 
calling function 
0xbfff037f 
0x0 
0x1f80 
0x1dcb 
0x4 
0x8fe2f50c 
0x0 
0x0 
0xbffff2a8 
0xbffff240 
0x1f 
0x292 
0x1e09 
0x17 
0x8fe0001f 
0x1f 
0x0 
0x37 
in function 
4 
exiting function 
in main again 
37 

Получайте удовольствие!

Смежные вопросы