10章 内存: 进程地址空间 / 函数调用栈 & 反汇编 / h
2022-07-10 本文已影响0人
my_passion
1 Linux 进程地址空间 布局
——————————————————————————————————————————————————
| 6) OS kernel space |
| |
| 5) stack: 维护 函数调用 的 context/上下文 |
| | |
| \|/ |
| |
| 4) 动态链接库 映射区 | 0x4000 0000
| |
| /|\ |
| | |
| 3) heap: 容纳 应用程序 动态分配的内存区 |
| |
| 2) 可执行文件映像 |
| read/write sections (.data .bss) |
| |
| readobly sections (.init .rodata .text) | 0x0804 8000
| |
| 1) 保留区 |
| |
—————————————————————————————————————————————————— 0 = NULL addr
2 栈 与 调用惯例
(1) 栈 & 函数调用 机制
进入 funcBody 第1条指令前:
1) para 压栈 // 有的 para 用 register 传
2) call func
[1] call指令 next 指令地址 压栈
[2] PC 设为 funcBody 第1条指令地址
|
| <=> jump 到 funcBody 执行
|/
CPU 要执行的 next 指令
进入 funcBody 第1条指令: 1-7 配对 / 3-6 配对
1) 保存 ebp + ebp 指向 当前栈顶
[1] push ebp // 保存 caller 的 `帧指针 ebp` 到 当前栈顶
|
| push = sub + mov
|
sub $4, esp
mov (esp), ebp // ebp -> (esp): 将 caller 的 `帧指针 ebp 保存`
[2] mov ebp, esp // esp -> ebp: 让 帧指针 ebp 指向 当前栈顶
| |
| |
| 栈顶指针: 随栈的长消 动态变化
|
帧指针: 定位 函数栈帧 中 各数据
2) sub esp, 0x... // 栈上开辟空间
3) push 保存的 register (调用前后需保持不变 的 register -> 用于 -> 保存 context/上下文)
push ebx / esi / edi
| |
| 第 1 para
|
第 2 para
4) funcCalc
5) mov eax, returnVal // 返回值 用 eax 传递
6) pop edi / esi / ebx
7) 恢复 `进入 func 前` 的 esp + ebp
[1] mov esp, ebp // ebp -> esp
[2] pop ebp
|
|
mov ebp, (esp)
add $4, esp
8) ret // 返回
|
|
[1] pop ( func 的) `返回地址`
[2] 设给 PC
|
|
CPU 要执行的 next 指令地址
// 取消 `帧指针 ebp`
|
|
用 栈顶指针 esp 定位
|
|
|/
帧上寻址慢 + 无法准确定位 函数的 `调用轨迹(Stack Trace)`
// 反汇编
int foo()
{
return 123;
}
(2) 调用惯例 (Calling Convention): C++ 名字粉碎(Name-mangling)
默认 `调用惯例 cdecl`
int foo(int n, float m) -> 完整形式 -> int _cdecl foo(int n, float m)
(3) 函数 返回值传递
1) 返回值 size <= eax 的 size (= 4Byte) -> 用 eax 传递
func 存 returnValue 到 eax
caller 读 eax
2) >
临时变量
地址存到 eax
3 Linux 堆 内存管理
(1) 2 种 `堆空间 分配方式` / 2 个 `系统调用`
brk()
mmap()
void *mmap(
void *start, // 要申请空间 的 起始地址 -> 设为 0 -> OS 自动挑选 合适的起始地址
size_t length, // ............. 长度
int prot, // 申请的空间 权限 R/W/X
int flags, // 映射类型(2种): 文件映射 / `匿名空间`
int fd, // 文件映射 时的 文件描述符
off_t offset); // 文件映射 时的 文件偏移
glibc 中 malloc 实现
请求 <= 128KB -> 现有堆空间按 `堆分配算法 (free list / 位图 / )` 分配/拨出 一块空间 返回
> 128KB -> 用 mmap() 分配一块 `匿名空间`
(2) 堆分配算法: 空闲链表 (free list)
多分配 4 Byte
-> 存 所分配的内存块 size -> free/delete 据这 4Byte 知应该 释放多少空间
类 C++2.91 内存池 设计: 16 条 free list (空闲链表)
程序环境.jpg
Linux 进程地址空间 布局.jpg
程序栈.jpg
栈帧结构.jpg
函数调用机制: 反汇编 分析.jpg
函数栈布局.jpg
函数调用链: 栈长消.jpg
返回值 传递: 返回值 size > eax.jpg