main.c
使用了共享库中的符号和在main.c
中声明的本地函数。
有没有一种简单而优雅的方法在运行时打印所有可用函数名称和符号的列表?
这应该是可能的,因为数据加载到了.code
段中。
因为我有同样的需要在运行时检索所有加载的符号名称,所以我基于R..'s的答案进行了一些研究。下面是一个详细的解决方案,适用于ELF格式的Linux共享库,并可与我的gcc 4.3.4一起使用,但也希望适用于更新版本。
我主要使用以下来源来开发此解决方案:
这是我的代码。我使用了易于理解的变量名并添加了详细的注释。如果有什么错误或遗漏,请告诉我... (编辑:我刚意识到问题是针对C,而我的代码是针对C ++的。但是,如果省略vector和string,则应该也适用于C)
#include <link.h>
#include <string>
#include <vector>
using namespace std;
/* Callback for dl_iterate_phdr.
* Is called by dl_iterate_phdr for every loaded shared lib until something
* else than 0 is returned by one call of this function.
*/
int retrieve_symbolnames(struct dl_phdr_info* info, size_t info_size, void* symbol_names_vector)
{
/* ElfW is a macro that creates proper typenames for the used system architecture
* (e.g. on a 32 bit system, ElfW(Dyn*) becomes "Elf32_Dyn*") */
ElfW(Dyn*) dyn;
ElfW(Sym*) sym;
ElfW(Word*) hash;
char* strtab = 0;
char* sym_name = 0;
ElfW(Word) sym_cnt = 0;
/* the void pointer (3rd argument) should be a pointer to a vector<string>
* in this example -> cast it to make it usable */
vector<string>* symbol_names = reinterpret_cast<vector<string>*>(symbol_names_vector);
/* Iterate over all headers of the current shared lib
* (first call is for the executable itself) */
for (size_t header_index = 0; header_index < info->dlpi_phnum; header_index++)
{
/* Further processing is only needed if the dynamic section is reached */
if (info->dlpi_phdr[header_index].p_type == PT_DYNAMIC)
{
/* Get a pointer to the first entry of the dynamic section.
* It's address is the shared lib's address + the virtual address */
dyn = (ElfW(Dyn)*)(info->dlpi_addr + info->dlpi_phdr[header_index].p_vaddr);
/* Iterate over all entries of the dynamic section until the
* end of the symbol table is reached. This is indicated by
* an entry with d_tag == DT_NULL.
*
* Only the following entries need to be processed to find the
* symbol names:
* - DT_HASH -> second word of the hash is the number of symbols
* - DT_STRTAB -> pointer to the beginning of a string table that
* contains the symbol names
* - DT_SYMTAB -> pointer to the beginning of the symbols table
*/
while(dyn->d_tag != DT_NULL)
{
if (dyn->d_tag == DT_HASH)
{
/* Get a pointer to the hash */
hash = (ElfW(Word*))dyn->d_un.d_ptr;
/* The 2nd word is the number of symbols */
sym_cnt = hash[1];
}
else if (dyn->d_tag == DT_STRTAB)
{
/* Get the pointer to the string table */
strtab = (char*)dyn->d_un.d_ptr;
}
else if (dyn->d_tag == DT_SYMTAB)
{
/* Get the pointer to the first entry of the symbol table */
sym = (ElfW(Sym*))dyn->d_un.d_ptr;
/* Iterate over the symbol table */
for (ElfW(Word) sym_index = 0; sym_index < sym_cnt; sym_index++)
{
/* get the name of the i-th symbol.
* This is located at the address of st_name
* relative to the beginning of the string table. */
sym_name = &strtab[sym[sym_index].st_name];
symbol_names->push_back(string(sym_name));
}
}
/* move pointer to the next entry */
dyn++;
}
}
}
/* Returning something != 0 stops further iterations,
* since only the first entry, which is the executable itself, is needed
* 1 is returned after processing the first entry.
*
* If the symbols of all loaded dynamic libs shall be found,
* the return value has to be changed to 0.
*/
return 1;
}
int main()
{
vector<string> symbolNames;
dl_iterate_phdr(retrieve_symbolnames, &symbolNames);
return 0;
}
DT_GNU_HASH
没有简单的方法可以获取符号计数,除了遍历所有哈希桶并计数。您可以在这里看到我的代码:http://git.musl-libc.org/cgit/musl/tree/src/ldso/dynlink.c?id=c5ab5bd3be15eb9d49222df132a51ae8e8f78cbc#n1554 - R.. GitHub STOP HELPING ICEdl_iterate_phdr
的函数可用。如果是这样,它可以用于收集有关每个加载的共享库文件的信息,并且您获得的信息足以检查符号表。该过程基本上是:
dl_phdr_info
结构中获取程序头地址。PT_DYNAMIC
程序头查找模块的_DYNAMIC
表。_DYNAMIC
的DT_SYMTAB
、DT_STRTAB
和DT_HASH
条目来查找符号列表。DT_HASH
只需要获取符号表的长度,因为似乎没有其他地方存储它。<elf.h>
和<link.h>
中。我更新了来自Kanalpiroge答案的代码,以便在DT_HASH丢失的情况下也能正常工作(例如,RHEL)。它适用于64位,但相对容易修改为支持32位。启示来自这里:https://chromium-review.googlesource.com/c/crashpad/crashpad/+/876879/18/snapshot/elf/elf_image_reader.cc#b512。
#include <link.h>
#include <string>
#include <vector>
using namespace std;
static uint32_t GetNumberOfSymbolsFromGnuHash(Elf64_Addr gnuHashAddress)
{
// See https://flapenguin.me/2017/05/10/elf-lookup-dt-gnu-hash/ and
// https://sourceware.org/ml/binutils/2006-10/msg00377.html
typedef struct
{
uint32_t nbuckets;
uint32_t symoffset;
uint32_t bloom_size;
uint32_t bloom_shift;
} Header;
Header* header = (Header*)gnuHashAddress;
const void* bucketsAddress = (void*)gnuHashAddress + sizeof(Header) + (sizeof(uint64_t) * header->bloom_size);
// Locate the chain that handles the largest index bucket.
uint32_t lastSymbol = 0;
uint32_t* bucketAddress = (uint32_t*)bucketsAddress;
for (uint32_t i = 0; i < header->nbuckets; ++i)
{
uint32_t bucket = *bucketAddress;
if (lastSymbol < bucket)
{
lastSymbol = bucket;
}
bucketAddress++;
}
if (lastSymbol < header->symoffset)
{
return header->symoffset;
}
// Walk the bucket's chain to add the chain length to the total.
const void* chainBaseAddress = bucketsAddress + (sizeof(uint32_t) * header->nbuckets);
for (;;)
{
uint32_t* chainEntry = (uint32_t*)(chainBaseAddress + (lastSymbol - header->symoffset) * sizeof(uint32_t));
lastSymbol++;
// If the low bit is set, this entry is the end of the chain.
if (*chainEntry & 1)
{
break;
}
}
return lastSymbol;
}
/* Callback for dl_iterate_phdr.
* Is called by dl_iterate_phdr for every loaded shared lib until something
* else than 0 is returned by one call of this function.
*/
int retrieve_symbolnames(struct dl_phdr_info* info, size_t info_size, void* symbol_names_vector)
{
/* ElfW is a macro that creates proper typenames for the used system architecture
* (e.g. on a 32 bit system, ElfW(Dyn*) becomes "Elf32_Dyn*") */
ElfW(Dyn*) dyn;
ElfW(Sym*) sym;
ElfW(Word*) hash;
char* strtab = 0;
char* sym_name = 0;
ElfW(Word) sym_cnt = 0;
/* the void pointer (3rd argument) should be a pointer to a vector<string>
* in this example -> cast it to make it usable */
vector<string>* symbol_names = reinterpret_cast<vector<string>*>(symbol_names_vector);
/* Iterate over all headers of the current shared lib
* (first call is for the executable itself) */
for (size_t header_index = 0; header_index < info->dlpi_phnum; header_index++)
{
/* Further processing is only needed if the dynamic section is reached */
if (info->dlpi_phdr[header_index].p_type == PT_DYNAMIC)
{
/* Get a pointer to the first entry of the dynamic section.
* It's address is the shared lib's address + the virtual address */
dyn = (ElfW(Dyn)*)(info->dlpi_addr + info->dlpi_phdr[header_index].p_vaddr);
/* Iterate over all entries of the dynamic section until the
* end of the symbol table is reached. This is indicated by
* an entry with d_tag == DT_NULL.
*
* Only the following entries need to be processed to find the
* symbol names:
* - DT_HASH -> second word of the hash is the number of symbols
* - DT_STRTAB -> pointer to the beginning of a string table that
* contains the symbol names
* - DT_SYMTAB -> pointer to the beginning of the symbols table
*/
while(dyn->d_tag != DT_NULL)
{
if (dyn->d_tag == DT_HASH)
{
/* Get a pointer to the hash */
hash = (ElfW(Word*))dyn->d_un.d_ptr;
/* The 2nd word is the number of symbols */
sym_cnt = hash[1];
}
else if (dyn->d_tag == DT_GNU_HASH && sym_cnt == 0)
{
sym_cnt = GetNumberOfSymbolsFromGnuHash(dyn->d_un.d_ptr);
}
else if (dyn->d_tag == DT_STRTAB)
{
/* Get the pointer to the string table */
strtab = (char*)dyn->d_un.d_ptr;
}
else if (dyn->d_tag == DT_SYMTAB)
{
/* Get the pointer to the first entry of the symbol table */
sym = (ElfW(Sym*))dyn->d_un.d_ptr;
/* Iterate over the symbol table */
for (ElfW(Word) sym_index = 0; sym_index < sym_cnt; sym_index++)
{
/* get the name of the i-th symbol.
* This is located at the address of st_name
* relative to the beginning of the string table. */
sym_name = &strtab[sym[sym_index].st_name];
symbol_names->push_back(string(sym_name));
}
}
/* move pointer to the next entry */
dyn++;
}
}
}
/* Returning something != 0 stops further iterations,
* since only the first entry, which is the executable itself, is needed
* 1 is returned after processing the first entry.
*
* If the symbols of all loaded dynamic libs shall be found,
* the return value has to be changed to 0.
*/
return 1;
}
int main()
{
vector<string> symbolNames;
dl_iterate_phdr(retrieve_symbolnames, &symbolNames);
return 0;
}
应该是 dl_iterate_phdr(retrieve_symbolnames, &symbolNames);
C
库函数,而是从导入/导出部分的 API 可能存在。如果它们在代码部分中用作标签,则可以使用 C 函数。 - Grijesh Chauhan