Linux ELF文件:静态和动态ELF程序哪个字节有区别?

4
我正在处理 Linux ELF 文件。
我想检测给定的 ELF 程序是否是静态链接(完全静态链接,ldd 显示 "not a dynamic executable")或动态链接。该 ELF 用于嵌入式 Linux,因此我不能仅运行它或使用 ldd 实用程序。
我希望在我的程序中完全通过读取和检查某些字节来完成此操作。我不想依赖于 file 实用程序或 libelf、binutils 等。
哪些字节将不同?

我认为你需要寻找一个动态程序头。 - Johannes Schaub - litb
1个回答

4
如何使用ldd.c来自μClibc?如果需要,剥离任何不必要的依赖项/检查应该相当容易。我认为这比尝试从阅读man 5 elf中找出所有边角情况更明智,尽管FWIW它看起来只是检查一个PT_INTERP程序头,就像您在注释中怀疑的那样。
更新:有几个检查。我已经尝试提取相关部分,但我不能确定是否漏掉了任何东西,请自行检查。代码检查32位和64位x86 ELF文件。它假定是小端架构。
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <ctype.h>
#include <inttypes.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>

#include <elf.h>

int main(int argc, char* argv[])
{
    const char* fname = argv[0];
    if (argc >= 2) fname = argv[1];

    int fd;
    struct stat st;
    void *mapping;

    if ((fd = open(fname, O_RDONLY)) == -1) {
        perror(fname);
        return 1;
    }

    if (fstat(fd, &st)) {
        perror("fstat");
        close(fd);
        return 1;
    }

    if ((mapping = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
        perror("mmap");
        close(fd);
        return 1;
    }
    const Elf32_Ehdr* eh = mapping;

    if (st.st_size < (off_t)sizeof(Elf32_Ehdr) ||
        eh->e_ident[EI_MAG0] != ELFMAG0 || 
        eh->e_ident[EI_MAG1] != ELFMAG1 || 
        eh->e_ident[EI_MAG2] != ELFMAG2 || 
        eh->e_ident[EI_MAG3] != ELFMAG3 ||
        eh->e_ident[EI_VERSION] != EV_CURRENT) {
        printf("Not a valid ELF file\n");
        return 0;
    }

    if (eh->e_type != ET_EXEC && eh->e_type != ET_DYN) {
        printf("Not executable or shared object\n");
        return 0;
    }

    int is_dynamic = 0;

    // change as appropriate, but remember that byteswapping might be needed in some cases
    if (eh->e_ident[EI_CLASS] == ELFCLASS32 && eh->e_ident[EI_DATA] == ELFDATA2LSB && eh->e_machine == EM_386) {
        uint16_t ph_cnt;
        for (ph_cnt = 0; ph_cnt < eh->e_phnum; ph_cnt++) {
            const Elf32_Phdr* ph = (const Elf32_Phdr*)((const uint8_t*)mapping + eh->e_phoff + ph_cnt * eh->e_phentsize);
            if (ph->p_type == PT_DYNAMIC || ph->p_type == PT_INTERP) {
                is_dynamic = 1;
            }
        }
    } else if (eh->e_ident[EI_CLASS] == ELFCLASS64 && eh->e_ident[EI_DATA] == ELFDATA2LSB && eh->e_machine == EM_X86_64) {
        const Elf64_Ehdr* eh = mapping;
        uint16_t ph_cnt;
        for (ph_cnt = 0; ph_cnt < eh->e_phnum; ph_cnt++) {
            const Elf64_Phdr* ph = (const Elf64_Phdr*)((const uint8_t*)mapping + eh->e_phoff + ph_cnt * eh->e_phentsize);
            if (ph->p_type == PT_DYNAMIC || ph->p_type == PT_INTERP) {
                is_dynamic = 1;
            }
        }
    } else {
        printf("Unsupported architecture\n");
        return 0;
    }

    munmap(mapping, st.st_size);
    close(fd);
    printf("%s: %sdynamic\n", fname, is_dynamic?"":"not ");
    return 0;
}

这看起来不错。它几乎不依赖于外部工具。 - osgx
这是一个相当大的代码块,显然是从某个现有的东西派生而来的,我认为你应该说明你认为适用的许可证。 - Chris Stratton
@Chris Stratton:mmap 部分来自于我在8月27日8:59的评论,其余部分则来自于阅读 man 5 elf。检查部分是受到阅读链接的 ldd 源代码的启发,所以我认为将其作为 GPLv2 的 uClibc 是可以接受的。 - user786653
Chris Stratton,我不会复制代码,我只想知道思路和字段的名称。而且,这位用户发布的代码是他自己的,并且采用了CC-BY许可证。如果我复制这段代码,我会在代码中提到他的名字。 - osgx

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接