linux指针地址做运算,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

可以看到:

结束地址减去起始地址正好是所有Entry的大小

end addr-start addr = 534289383936 - 534289383488 = 448

entry count * entry size = 8 * 56 = 448

在for循环中,phdr_start_addr++获取到就是对应index的起始地址

Entry之间的间隔大小正好为56,534289383544 - 534289383488 = 56是一个Entry的大小

原因

对于问题3而言,由于elf_file->base_address是ELF_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); // 计算地址的方式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值