You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何读取嵌入程序自身ELF的数据?如何将自定义段加入PT_LOAD?

解决ELF嵌入文件运行时无法访问的问题

问题背景

使用objcopy工具可将文件嵌入ELF可执行文件,命令如下:

objcopy --add-section program.file1=file1.dat \
        --add-section program.file2=file2.dat \
        program program+files

但生成的program+files运行时无法直接访问嵌入的file1、file2——这些文件被添加为ELF的命名section,而Linux仅加载PT_LOAD标记的segment,此类非必需section不会被操作系统加载到内存。

尝试通过ELF头指针查找section的代码会得到悬空指针,无法正常使用:

uintptr_t address = getauxval(AT_PHDR) & -4096;
Elf64_Ehdr *elf = (Elf64_Ehdr *) address;

// 悬空指针,操作系统未加载这些section
Elf64_Shdr *sections = ((unsigned char *) elf) + elf->e_shoff;

需求为:运行时动态查找所有以program.为前缀的section,直接获取其内存指针并当作普通内存块使用,且需支持任意数量(含零个)的嵌入文件,不能依赖预定义符号。

核心思路:将嵌入section归入PT_LOAD段

Linux仅加载PT_LOAD标记的segment,只要把嵌入的program.* section加入PT_LOAD段,就能在运行时访问其内存数据。objcopy无法直接添加PT_LOAD段,但可通过以下两种方法实现:

方法1:链接阶段用脚本定义PT_LOAD段

在编译链接时,通过自定义链接脚本将program.* section归入PT_LOAD段,后续再用objcopy嵌入文件:

  1. 编写链接脚本(示例命名为embed.ld):
SECTIONS {
  .embedded : {
    *(.program.*)
  }
}
PHDRS {
  embedded PT_LOAD FILEHDR PHDRS;
}

该脚本将所有.program.*类型的section汇总到.embedded段,并标记此段为PT_LOAD。

  1. 编译程序时指定该链接脚本:
gcc your_program.c -Wl,-Tembed.ld -o program
  1. 用objcopy嵌入目标文件:
objcopy --add-section program.file1=file1.dat \
        --add-section program.file2=file2.dat \
        program program+files

此时嵌入的program.* section会被包含在PT_LOAD段中,运行时会被操作系统加载到内存。

方法2:手动扩展现有PT_LOAD段(进阶)

若不想修改编译链接流程,可先添加section,再手动调整最后一个PT_LOAD段的大小以包含新section:

  1. 先用objcopy添加section:
objcopy --add-section program.file1=file1.dat program program+files
  1. 用readelf查看现有PT_LOAD段的信息:
readelf -l program+files

找到最后一个PT_LOAD段的Virtual Address和MemSize,计算其结束地址:end_addr = p_vaddr + p_memsz

  1. 查看新添加的program.file1 section的地址与大小:
readelf -S program+files

获取该section的Addr(虚拟地址)和Size。

  1. 若section地址在PT_LOAD段结束地址之后,用objcopy扩展PT_LOAD段的MemSize和FileSize,同时将section标记为ALLOC(告知系统需分配内存):
objcopy --set-section-flags program.file1=ALLOC program+files program+files

此方法需手动计算地址与大小,易出错,更推荐方法1。

运行时动态查找section的实现

当section被加载到内存后,可通过以下代码动态查找所有program.* section:

#include <elf.h>
#include <sys/auxv.h>
#include <string.h>
#include <stdlib.h>

typedef struct {
    const char *name;
    void *data;
    size_t size;
} EmbeddedFile;

// 获取当前进程ELF文件的基地址
uintptr_t get_elf_base() {
    Elf64_Phdr *phdr = (Elf64_Phdr *)getauxval(AT_PHDR);
    Elf64_Ehdr *ehdr = (Elf64_Ehdr *)((uintptr_t)phdr - phdr->p_offset);
    return (uintptr_t)ehdr;
}

// 查找所有以program.为前缀的嵌入文件
void find_embedded_files(EmbeddedFile **out_files, size_t *out_count) {
    uintptr_t elf_base = get_elf_base();
    Elf64_Ehdr *ehdr = (Elf64_Ehdr *)elf_base;
    Elf64_Shdr *shdr = (Elf64_Shdr *)(elf_base + ehdr->e_shoff);
    const char *shstrtab = (const char *)(elf_base + shdr[ehdr->e_shstrndx].sh_addr);

    size_t count = 0;
    EmbeddedFile *files = NULL;

    for (int i = 0; i < ehdr->e_shnum; i++) {
        const char *sec_name = shstrtab + shdr[i].sh_name;
        if (strncmp(sec_name, "program.", 8) == 0) {
            files = realloc(files, sizeof(EmbeddedFile) * (count + 1));
            files[count].name = sec_name + 8; // 去除program.前缀
            files[count].data = (void *)(elf_base + shdr[i].sh_addr);
            files[count].size = shdr[i].sh_size;
            count++;
        }
    }

    *out_files = files;
    *out_count = count;
}

调用find_embedded_files即可获取所有嵌入文件的名称、内存指针和大小,直接当作普通内存块使用。


内容的提问来源于stack exchange,提问作者Matheus Moreira

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 07:27:50