在ELF文件中存储和检索版本信息

时间:2017-09-18 16:51:13

标签: c linux elf bfd

我正在尝试找出一种在Linux上存储和检索C / C ++可执行文件和库中的版本信息的好方法。我正在为我的C和C ++程序使用GCC编译器。

存储部分非常简单;声明一个这样的变量将它存储在输出文件的.rodata部分:

const char MY_VERSION[] = "some_version_information";

但是,从外部程序中检索信息时,我遇到了非常困难的时间。使用共享库,使用dlopendlsym来加载库并查找符号相当容易,但这可能不是最好的方法,它不适用于所有可执行文件。此外,如果可能的话,我希望这可以使用为不同架构构建的可执行文件和库。

我认为由于共享库和可执行文件都使用ELF格式,因此使用知道如何读取ELF文件的库是有意义的。我能找到的两个是libelf和BFD;我很难为每个人找到合适的文件。是否可以使用更好的库?

这是我到目前为止所使用的BFD:

#include <stdio.h>                                                                                                                                                                                                               [6/1356]
#include <string.h>
#include <bfd.h>

int main(int argc, char* argv[])
{
    const char *filename;
    int i;
    size_t storage;
    bfd *b = NULL;
    asymbol **symbol_table;
    long num_symbols;

    if(argc != 2) return 1; // todo: print a useful message
    else filename = argv[1];

    b = bfd_openr(filename, NULL);

    if(b == NULL){
        fprintf(stderr, "Error: failed to open %s\n", filename);
        return 1;
    }

    // make sure we're opening a file that BFD understands
    if(!bfd_check_format(b, bfd_object)){
        fprintf(stderr, "Error: unrecognized format\n");
        return 1;
    }

    // how much memory is needed to store the symbol table
    storage = bfd_get_symtab_upper_bound(b);

    if(storage < 0){
        fprintf(stderr, "Error: unable to find storage bound of symbol table\n");
        return 1;
    } else if((symbol_table = malloc(storage)) == NULL){
        fprintf(stderr, "Error: failed to allocate memory for symbol table\n");
        return 1;
    } else {
        num_symbols = bfd_canonicalize_symtab(b, symbol_table);
    }

    for(i = 0; i < num_symbols; i++){
        if(strcmp(symbol_table[i]->name, "MY_VERSION") == 0){
            fprintf(stderr, "found MY_VERSION\n");

            // todo: print the string?
        }
    }

    return 0;
}

我意识到由于ELF格式,打印字符串可能不是很简单。

是否有直接的方法来打印存储在ELF文件中的字符串符号?

2 个答案:

答案 0 :(得分:2)

我发现我可以使用自定义部分来存储版本信息,然后只是转储部分以“提取”字符串。

以下是如何声明版本信息:

__attribute__((section("my_custom_version_info"))) const char MY_VERSION[] = "some.version.string";

然后,在使用BFD的程序中,我们可以通过几种不同的方式获得该部分。我们可以使用bfd_get_section_by_name

asection *section = bfd_get_section_by_name(b, "my_custom_version_info");

现在我们有了该部分的句柄,我们可以将它加载到内存中。我选择使用bfd_malloc_and_get_section(您应该确保section先不是NULL):

bfd_byte *buf;
if(!bfd_malloc_and_get_section(b, section, &buf)){
    // error: failed to malloc or read the section
}

现在我们将部分加载到缓冲区中,我们可以打印其内容:

for(int i = 0; i < section->size && buf[i]; i++){
    printf("%c", buf[i]);
}
printf("\n");

不要忘记free缓冲区。

答案 1 :(得分:2)

从可执行文件内部,只需声明

 extern const char MY_VERSION[];

BTW,对于C ++,更好地声明extern "C"该符号(即使在定义它的文件中)。

然后您的问题是如何在某些外部ELF可执行文件中找到符号MY_VERSION(简单的方法可能是popen某个nm进程,请参阅nm(1))。顺便说一句,它与功能符号(或数据符号)相同。您可以使用libelflibelfin(或古老的libbfd)等库,也可以自己解析ELF格式(请务必先阅读that wikipage )

您应学习并了解 ELF格式。您需要仔细阅读有关ELF和x86-64 ABI的文档。使用objdump(1)&amp;探索现有的ELF可执行文件readelf(1)。另请阅读elf(5)。阅读如何表示符号表,以及如何计算其哈希码。当然,请详细阅读所有可能的relocations。你可以阅读Levine关于Linkers and Loaders的书和Drepper关于How to Write Shared Libraries(都解释ELF)和Assembler Language HowTo以及Ian Taylor paper on goldELF: better symbol lookup via DT_GNU_HASH的论文。另请参见Solaris文档,例如在Hash Table Section和OSDEV ELF tutorial&amp; ELF页面

您不需要任何特定的部分(或部分)。

(我大约20年前为Sparc做过这件事;这并不是特别难)

您还可以查看emacs源代码,其unexec.c正在编写一些ELF文件

BTW,ELF有一些带符号的版本信息,参见例如dlvsym(3)

您可能还想了解execve(2)ld-linux(8)的工作原理,流程的虚拟地址空间是什么(请参阅proc(5),尝试cat /proc/$$/maps