微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

elf文件读取符号和符号地址1字节差

如何解决elf文件读取符号和符号地址1字节差

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

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

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

我检查了什么:

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

enter image description here

这是从开发人员的 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);
    }
}

解决方法

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

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

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

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

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。