Если вы работаете в системе с dlopen(3)
и dlsym(3)
(например, Linux), вы сможете:
char thing_string[] = "thing_you_want_to_look_up";
void * handle = dlopen(NULL, RTLD_LAZY | RTLD_NOLOAD);
// you could do RTLD_NOW as well. shouldn't matter
if (!handle) {
fprintf(stderr, "Dynamic linking on main module : %s\n", dlerror() );
exit(1);
}
void * addr = dlsym(handle, thing_string);
fprintf(stderr, "%s is at %p\n", thing_string, addr);
Я не знаю лучший способ сделать это для других систем, и это, вероятно, не будет работать для статических переменных и функций. Имена символов C ++ будут искажены, если вы заинтересованы в работе с ними.
Чтобы расширить это для работы с общими библиотеками, вы, вероятно, могли бы получить имена загруженных в данный момент библиотек из /proc/self/maps
, а затем передать имена файлов библиотеки в dlopen
, хотя это может не получиться, если библиотека была переименована или удалена .
Возможно, есть несколько других, гораздо более эффективных способов сделать это.
изменить без с использованием dlopen
/* name_addr.h */
struct name_addr {
const char * sym_name;
const void * sym_addr;
};
typedef struct name_addr name_addr_t;
void * sym_lookup(cost char * name);
extern const name_addr_t name_addr_table;
extern const unsigned name_addr_table_size;
/* name_addr_table.c */
#include "name_addr.h"
#define PREMEMBER( X ) extern const void * X
#define REMEMBER( X ) { .sym_name = #X , .sym_addr = (void *) X }
PREMEMBER(strcmp);
PREMEMBER(printf);
PREMEMBER(main);
PREMEMBER(memcmp);
PREMEMBER(bsearch);
PREMEMBER(sym_lookup);
/* ... */
const name_addr_t name_addr_table[] =
{
/* You could do a #include here that included the list, which would allow you
* to have an empty list by default without regenerating the entire file, as
* long as your compiler only warns about missing include targets.
*/
REMEMBER(strcmp),
REMEMBER(printf),
REMEMBER(main),
REMEMBER(memcmp),
REMEMBER(bsearch),
REMEMBER(sym_lookup);
/* ... */
};
const unsigned name_addr_table_size = sizeof(name_addr_table)/sizeof(name_addr_t);
/* name_addr_code.c */
#include "name_addr.h"
#include <string.h>
void * sym_lookup(cost char * name) {
unsigned to_go = name_addr_table_size;
const name_addr_t *na = name_addr_table;
while(to_to) {
if ( !strcmp(name, na->sym_name) ) {
return na->sym_addr;
}
na++;
to_do--;
}
/* set errno here if you are using errno */
return NULL; /* Or some other illegal value */
}
Если вы сделаете это таким образом, компоновщик позаботится о заполнении адресов для вас после того, как все будет выложено. Если вы включите заголовочные файлы для всех символов, которые вы перечисляете в своей таблице, вы не получите предупреждений при компиляции файла таблицы, но будет намного проще просто иметь их все extern void *
и позволить компилятору предупреждать Вы о всех из них (что, вероятно, будет, но не обязательно).
Вы также, вероятно, захотите отсортировать символы по имени, так что вы можете использовать двоичный поиск в списке, а не перебирать его.
Вы должны заметить, что если у вас есть члены в таблице, на которые программа не ссылается иначе (например, если у вас есть запись для sqrt
в таблице, но вы ее не вызывали), тогда компоновщик захочет ( нужно), чтобы связать эти функции в вашем изображении. Это может привести к взрыву.
Кроме того, если вы используете глобальные оптимизации, использование этой таблицы, вероятно, сделает их менее эффективными, поскольку компилятор будет думать, что все перечисленные функции могут быть доступны через указатель из этого списка и что он не может видеть все вызовы. точек.
Поместить статические функции в этот список непросто. Вы можете сделать это, изменив таблицу на динамическую и выполнив ее во время выполнения из функции в каждом модуле, или, возможно, сгенерировав новый раздел в вашем объектном файле, в котором находится таблица. Если вы используете gcc:
#define SECTION_REMEMBER(X) \
static const name_addr_t _name_addr##X = \
{.sym_name= #X , .sym_addr = (void *) X } \
__attribute__(section("sym_lookup_table" ) )
И прикрепите их список в конец каждого файла .c со всеми символами, которые вы хотите запомнить из этого файла. Это потребует работы компоновщика, чтобы компоновщик знал, что делать с этими членами, но затем вы можете перебирать список, просматривая начало и конец раздела, в котором он находится (я не знаю точно, как это сделать это, но я знаю, что это может быть сделано и не слишком сложно). Это усложнит сортировку списка. Кроме того, я не совсем уверен, что инициализация .sym_name
для адреса строкового литерала не приведет к заполнению строки в этом разделе, но я не думаю, что это произойдет. Если бы это было так, это сломало бы вещи.
Вы все еще можете использовать objdump, чтобы получить список символов, который содержит объектный файл (возможно, elf), а затем отфильтровать его по интересующим вас символам, а затем заново сгенерировать файл таблицы, перечисленный членами таблицы.