美文网首页linuxalready
10章 内存: 进程地址空间 / 函数调用栈 & 反汇编 / h

10章 内存: 进程地址空间 / 函数调用栈 & 反汇编 / h

作者: my_passion | 来源:发表于2022-07-10 16:38 被阅读0次

    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

    相关文章

      网友评论

        本文标题:10章 内存: 进程地址空间 / 函数调用栈 & 反汇编 / h

        本文链接:https://www.haomeiwen.com/subject/tswwbrtx.html