cn体育门户网站源码,asp做素材网站,wordpress添加新页面是什么意思,长沙景点视频需要从Linux内核和ELF文件格式两个角度来回答。关于mm_struct和VMA#xff0c;需要解释清楚VMA的概念、红黑树组织方式#xff0c;以及它与页表的关系。关于PIC和GOT/PLT#xff0c;需要说明-fPIC编译选项的作用、GOT在PIC中的特殊要求#xff0c;以及整个过程的工作流程。…需要从Linux内核和ELF文件格式两个角度来回答。关于mm_struct和VMA需要解释清楚VMA的概念、红黑树组织方式以及它与页表的关系。关于PIC和GOT/PLT需要说明-fPIC编译选项的作用、GOT在PIC中的特殊要求以及整个过程的工作流程。我们继续深入到这两个紧密相关的核心机制中。它们一个是操作系统管理进程内存的“地图册”另一个是共享库能在内存任意位置运行的“自适应导航”。一、mm_struct与 VMA进程虚拟内存的“地图绘制与管理”每个进程的task_struct中都有一个mm_struct内存描述符它是内核管理该进程整个虚拟地址空间的总控结构。而虚拟内存区域VMA则是这张大地图中的一个个具体区块。核心数据结构关系structtask_struct{// ...structmm_struct*mm;// 指向内存描述符// ...};structmm_struct{unsignedlongstart_code,end_code;// 代码段起止unsignedlongstart_data,end_data;// 数据段起止unsignedlongstart_brk,brk;// 堆的起止unsignedlongstart_stack;// 栈的起始pgd_t*pgd;// 指向页全局目录顶级页表structvm_area_struct*mmap;// 指向VMA链表的头structrb_rootmm_rb;// VMA红黑树的根// ...};structvm_area_struct{unsignedlongvm_start;// 区域的起始地址unsignedlongvm_end;// 区域的结束地址后一字节structvm_area_struct*vm_next;// 链表中的下一个VMAstructrb_nodevm_rb;// 红黑树中的节点unsignedlongvm_flags;// 区域的权限和属性如读/写/执行structfile*vm_file;// 如果映射自文件指向file结构unsignedlongvm_pgoff;// 文件内的偏移以页为单位// ...};下图清晰地展示了进程虚拟内存空间中VMA的组织方式及其与页表的关系渲染错误:Mermaid 渲染失败: Parse error on line 2: ...bgraph A[“进程的虚拟地址空间 (由 mm_struct 管理)”] -----------------------^ Expecting SQE, DOUBLECIRCLEEND, PE, -), STADIUMEND, SUBROUTINEEND, PIPE, CYLINDEREND, DIAMOND_STOP, TAGEND, TRAPEND, INVTRAPEND, UNICODE_TEXT, TEXT, TAGSTART, got PS工作机制与实例查找与判定当进程访问一个地址例如0x4000abcd时CPU的MMU会先查询页表。如果该地址对应的页表项无效缺页会触发缺页异常。内核的异常处理程序会查询该进程的mm_struct通过红黑树快速找到包含该地址的VMA例如它属于libc.so的代码段VMA。如果找不到说明访问了非法地址内核会发送SIGSEGV(段错误)信号终止进程。分配物理内存找到VMA后内核检查vm_flags例如试图向一个只读的代码段VMA写入会触发段错误。如果权限合法内核会分配一个物理页帧并建立或更新页表项将虚拟地址映射到这个物理页帧。文件映射如果VMA的vm_file不为空如映射了libc.so的.text段内核会在分配物理页帧后从磁盘文件的对应偏移vm_pgoff处读取内容到内存这个过程称为按需分页。查看实例在Linux中你可以通过/proc/pid/maps文件查看任意进程的VMA地图。# 查看自身shell的内存映射cat/proc/$$/maps# 输出示例# 00400000-00401000 r-xp 00000000 08:01 1048576 /bin/bash # 代码段# 00600000-00601000 rw-p 00000000 08:01 1048576 /bin/bash # 数据段# 7ffff7a0e000-7ffff7bd0000 r-xp 00000000 08:01 524313 /usr/lib/libc-2.33.so # libc代码# 7ffff7bd0000-7ffff7dd0000 ---p 001c2000 08:01 524313 /usr/lib/libc-2.33.so # 保护间隙# 7ffff7dd0000-7ffff7dd4000 r--p 001c2000 08:01 524313 /usr/lib/libc-2.33.so # libc只读数据# 7ffff7dd4000-7ffff7dd6000 rw-p 001c6000 08:01 524313 /usr/lib/libc-2.33.so # libc可写数据# 7ffffffde000-7ffffffff000 rw-p 00000000 00:00 0 [stack] # 栈每一行对应一个VMA显示了其起止地址、权限r读/w写/x执行/p私有或s共享、文件偏移、设备号、inode和文件路径或区域名。二、 PIC GOT/PLT共享库的“任意门”技术位置无关代码是共享库.so能够被加载到任意虚拟地址而无需重定位其代码段的关键技术。它的核心思想是代码中不包含任何绝对地址所有对数据和函数包括外部函数的引用都通过一个“地址表”间接进行。PIC代码的生成使用-fPIC或-fpic编译器选项。gcc -fPIC -c mylib.c -o mylib.o gcc -shared mylib.o -o libmylib.soPIC如何工作以x86-64为例访问全局数据编译器无法知道数据最终在内存的哪个绝对地址。它利用了一个关键事实任何一条指令与其所在ELF文件的某个部分如GOT之间的偏移在链接时是固定的。编译器会生成这样的代码来获取GOT的地址; x86-64 示例获取_GLOBAL_OFFSET_TABLE_的地址 lea rdi, [rip _GLOBAL_OFFSET_TABLE_] ; RIP相对寻址获取GOT地址然后通过GOT中固定的偏移量来访问数据mov rax, [rdi global_var_offset] ; 从GOT中加载global_var的地址 mov eax, [rax] ; 通过该地址访问真正的全局变量调用外部函数这正是PLT/GOT发挥作用的场景我们之前已经详细讲过。在PIC模式下call printfplt中的printfplt地址也是通过RIP相对寻址计算的因此无论库被加载到何处这条调用指令本身都是正确的。PIC与GOT/PLT的配合流程下图结合了PIC的编译特性展示了从PIC代码编译到首次调用外部函数的完整流程渲染错误:Mermaid 渲染失败: Parse error on line 2: ... subgraph S1[“编译时 (生成PIC)”] C1 -----------------------^ Expecting SQE, DOUBLECIRCLEEND, PE, -), STADIUMEND, SUBROUTINEEND, PIPE, CYLINDEREND, DIAMOND_STOP, TAGEND, TRAPEND, INVTRAPEND, UNICODE_TEXT, TEXT, TAGSTART, got PS为什么需要PIC节省物理内存如果多个进程加载同一个非PIC库操作系统虽然可以将其映射到各自虚拟空间的不同地址但代码段中大量的绝对地址需要被重写写时复制导致每个进程的代码段物理页都不相同无法共享。而PIC库的代码段是完全纯净、只读的可以被所有进程共享同一份物理内存。地址空间布局随机化PIC是ASLR技术的基础。ASLR要求每次运行栈、堆、库的加载地址都随机变化以增加安全性。非PIC的库无法在随机地址运行而PIC库则毫无问题。查看实例# 对比编译非PIC和PIC的目标文件echoextern int var; void func() { var 1; }test.c gcc -c test.c -o test_no_pic.o gcc -fPIC -c test.c -o test_pic.o objdump -d test_no_pic.o# 可能会看到 movl $1, var (绝对地址尝试)objdump -d test_pic.o# 会看到通过 %rip 和 GOT 进行间接访问的复杂指令# 查看共享库的GOTreadelf -S /usr/lib/libc.so.6|grepgot.plt objdump -d -j .plt /usr/lib/libc.so.6|head-20 总结VMA与PIC/GOT/PLT的联系mm_struct/VMA是操作系统内核视角的内存地图它知道“库X的代码段被映射到了进程空间的地址Y并且具有Z权限”。PIC/GOT/PLT是用户程序共享库视角的导航系统。当PIC代码需要访问数据或调用函数时它通过精心设计的指令RIP相对寻址找到GOT这张“地址查询表”。连接点这张“地址查询表”GOT本身就是VMA管理的一个普通数据段它被加载在内存映射区域的某个地址。PIC代码通过相对寻址找到它然后通过它里面由动态链接器填写好的地址最终跳转到另一个VMA例如libc.so的代码段去执行函数。简单来说VMA告诉了系统“库在哪里、能做什么”而PIC/GOT/PLT则告诉库里的代码“你要的东西在哪里、怎么去”。两者结合共同实现了灵活、高效、安全的共享库动态链接机制。