我正在尝试找出一种在 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 文件中的字符串符号?

最佳答案

从您的可执行文件中,只需声明

 extern const char MY_VERSION[];

顺便说一句,对于 C++,最好声明 extern "C" 那个符号(甚至在定义它的文件中)。

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

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

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

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

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

顺便说一句,ELF 有一些带有符号的版本信息,请参见例如dlvsym(3)

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

关于c - 在 ELF 文件中存储和检索版本信息,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/46284491/

10-16 10:25