Linux编程--指针的++操作

背景

在学习ELF文件的过程中,看到Matrix中有类似于如下的代码来遍历ELF文件的e->phdr程序头表的内容。

// 根据e_phoff找到Program Header的位置
    elf_file->elf_phdr = (ELF_Phdr *) (elf_file->base_address + elf_file->elf_ehdr->e_phoff);
    // ELF文件Program Header的结束位置
    void *phdr_end_addr = elf_file->elf_phdr + elf_file->elf_ehdr->e_phnum;
    // 从phdr开始到结束寻找,开始找到PT_LOAD的段
    for (ELF_Phdr *phdr_start_addr = elf_file->elf_phdr; phdr_start_addr < phdr_end_addr; phdr_start_addr++) {
        // 开始遍历程序头
        // 在C中,数组指针的++代表指向下个元素
        auto *program_head_table = (ELF_Phdr *) phdr_start_addr;
        if (program_head_table->p_type == PT_LOAD) {
            LOGE(TAG, "TYPE:%u, PT LOAD FIND", program_head_table->p_type);
        }
        LOGE(TAG, "TYPE:%u", program_head_table->p_type);
    }

问题

  • elf_file->elf_phdr:程序头表的起始位置
  • elf_file->elf_ehdr->e_phnum:程序头表中段的总数
  • elf_file->elf_ehdr->e_phentsize:程序头表中每个Entry的大小

问题1:为何elf_file->elf_phdr + elf_file->elf_ehdr->e_phnum就能得到程序头表的结束地址呢?而不是简单的地址加上对应的数字?

问题2:在for循环中,phdr_start_addr++为何能找到程序头表中的下一个段结构呢?

问题3:在elf_file->base_address + elf_file->elf_ehdr->e_phoff计算ELF_Phdr的地址时,是直接加的,和问题1中的有何区别?

日志验证

在代码中加入日志,打印phdr的起始与结束地址,以及entry的总数与每个entry的大小。

e->phdr start addr:534289383488
e->phdr end addr:534289383936
e->phdr entry count:8, entry size:56
Program Header: index:0...addr:534289383488
Program Header: index:1...addr:534289383544
Program Header: index:2...addr:534289383600
Program Header: index:3...addr:534289383656
Program Header: index:4...addr:534289383712
Program Header: index:5...addr:534289383768
Program Header: index:6...addr:534289383824
Program Header: index:7...addr:534289383880

可以看到:

  1. 结束地址减去起始地址正好是所有Entry的大小
    • end addr-start addr = 534289383936 - 534289383488 = 448
    • entry count * entry size = 8 * 56 = 448
  2. 在for循环中,phdr_start_addr++获取到就是对应index的起始地址
    • Entry之间的间隔大小正好为56,534289383544 - 534289383488 = 56是一个Entry的大小

原因

对于问题3而言,由于elf_file->base_addressELF_Addr类型,本身就是无符号的32位或者64位整形,所以直接加减即可。

typedef __u64 Elf64_Addr;
typedef __u32 Elf32_Addr;

elf_file->elf_phdr则是(Elf64_Phdr *),也就是指针类型,而对于指针来说,+1或者-1都是对于整个结构体而言,所以对于指针的操作,每次加1或者-1都会偏移sizeof(struct)的大小。

而这个计算,都是在编译期间编译器已经计算好了的。

(void *)的计算

C和C++中不允许(void *)pointer与整数相加,否则在编译的时候会报错误:

arithmetic on a pointer to void

原因是C和C++禁止在(void *)的指针中进行运算,因为在运行的时候会有很多字节对齐的操作,如果没有指定类型的话,无法知道对应的结构体的大小,所以禁止(void *)指针的计算。

结论

对于地址的加减来说,可以正常按数加减,而且加减完后可以赋值给结构体指针。

而对于指针的加减来说,是对于指针对应的结构体大小而言的,每次加减都是计算的N个结构体大小的偏移。 例如:phdr_start_addr++可以理解成: phdr_start_addr = phdr_start_addr + sizeof(ELF_Phdr); // 计算地址的方式

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券