【问题标题】:Elf file read symbol and symbol address 1 byte differenceelf文件读取符号和符号地址1个字节的区别
【发布时间】:2021-04-19 06:03:47
【问题描述】:

几天前,我查看了精灵符号表来比较字符串以找到我的函数。 我可以很好地找到目标字符串,并且成功获得了起始偏移量和大小。 但是,将这个偏移量与objdump的结果进行比较,可以看出1个字节是不同的。 怎么了? 我英语不好。

操作系统:Windows 10
ide : android studio ndk
目标拱门:armeabi-v7a

结果图片:
申请结果:0x00908b5
预期结果:0x00908b4

我检查了什么:

  1. elf32_sym、elf64_sym 已检查(没问题)
  2. 已检查结构字节填充(没问题)
  3. 结构,变量初始化检查

这是从开发者的 git 中克隆出来的。

template <typename ElfHeaderT, typename SectionHeaderT, typename CallbackT>
void read_sections(const void *image, size_t size, const CallbackT &callback)
{
    const ElfHeaderT *ehdr = static_cast<const ElfHeaderT *>(image);
    const SectionHeaderT *shdrs = (const SectionHeaderT *)((const uint8_t *)image + ehdr->e_shoff);
    const SectionHeaderT *strhdr = &shdrs[ehdr->e_shstrndx];
    const char *strtab = static_cast<const char *>(image) + strhdr->sh_offset;

    for (int i = 0; i < ehdr->e_shnum; ++i)
    {
        section s = {0,};

        s.index= i;
        s.name = strtab + shdrs[i].sh_name;
        s.type = shdrs[i].sh_type;
        s.virtual_address = static_cast<ptrdiff_t>(shdrs[i].sh_addr);
        s.file_offset = static_cast<ptrdiff_t>(shdrs[i].sh_offset);
        s.size = static_cast<size_t>(shdrs[i].sh_size);
        s.entry_size = static_cast<size_t>(shdrs[i].sh_entsize);
        s.address_align = static_cast<size_t>(shdrs[i].sh_addralign);
        callback(s);
    }
}


template <typename SymbolEntryT, typename CallbackT>
void read_symbols(const void *image, unsigned int code_section_index, const section &symbols, const char *names,
    const CallbackT &callback)
{
    const size_t total_syms = symbols.size / sizeof(SymbolEntryT);
    const SymbolEntryT *syms_data = (const SymbolEntryT *)((const uint8_t *)image + symbols.file_offset);

    for (size_t i = 0; i < total_syms; ++i)
    {
        symbol s = {0,};
        const SymbolEntryT &sd = syms_data[i];
        const unsigned type = ELF32_ST_TYPE(sd.st_info);

        if (type != STT_FUNC)
            continue;
        if (sd.st_shndx != code_section_index || !sd.st_size)
            continue;
        s.name = names + sd.st_name;
        s.size = static_cast<size_t>(sd.st_size);
        s.virtual_address = static_cast<size_t>(sd.st_value);
        callback(s);
    }
}

【问题讨论】:

  • 这里不是 100% 确定,但我认为低位表示拇指代码。所以分支到 0x00908b5 实际上将 pc 设置为 0x00908b4 但开始在该地址执行拇指代码。
  • 我知道 THUMB 模式是 16 位位命令集。我需要更详细的知识,你能解释一下吗?
  • 我检查了Thumb模式的内容。如果是这样,我可以从计算的函数偏移量中减去 1 吗?

标签: c++ elf armv7


【解决方案1】:

指令必须与其自然边界对齐,这意味着 16 位指令必须是 2 字节对齐的,而 32 位指令必须是字对齐的。 (除此之外还有更多,但这是本问题的重要位。)因此,分支目标的最低有效位必须始终为零,并且 LSB 实际上不包含任何信息。

当 16 位 Thumb 指令集与 armv4T 设备中的 32 位 ARM 指令集并行引入时(从 ARM7TDMI 开始),这个未使用的位被重新用于指示代码是否应该在 ARM 模式下解释或跟随分支的拇指模式。无论哪种方式,该函数仍然位于同一位置,LSB 在硬件中被有效清除以创建真正的分支目标,但它的值控制了分支之后的指令解码模式。

自从引入统一的Thumb-2指令集后,这个机制就不再使用了。但是 Thumb-2 被认为是 Thumb 指令集的后代,而不是 ARM 指令集的后代,并且(例如)Cortex-M 目标被认为是永久地在 Thumb 模式下运行。因此,当该地址用作分支目标时,需要设置该地址的 LSB。如果没有设置,你会得到一个错误,因为 CPU 认为它被要求执行它不支持的 32 位 ARM 代码。 xPSR 中的 Thumb 位也是如此,必须始终保持设置。

所以要回答你的问题,一点问题都没有。如果您想知道函数的位置,以便可以在内存窗口或其他东西中检查它,请使用偶值地址。如果要手动执行分支,例如在汇编代码中使用硬编码的数字分支目标,请确保使用带有 LSB 集的值。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-11-15
    • 1970-01-01
    • 2014-03-12
    • 2017-04-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多