zoukankan      html  css  js  c++  java
  • Linux内核分析课程笔记(一)

    linux内核分析课程笔记(一)

    冯诺依曼体系结构

    冯诺依曼体系结构实际上就是存储程序计算机。

    从两个层面来讲:

    • 从硬件的角度来看,冯诺依曼体系结构逻辑上可以抽象成CPU和内存,通过总线相连。CPU上有一些寄存器,IP(Instruction Pointer)是一个指针,总是指向内存的某一块区域CS(Code Segment),CPU即从IP指向的地址取一条指令进行执行,执行完之后IP自增1,加到下一条指令(逻辑意义上的1,因为有些指令系统是变长指令)

    • 从程序员的角度来看,存储程序计算机。CPU从程序员的角度可以抽象成一个无条件的for循环,总是执行下一条指令。

    for(;;){
    next instruction();
    }
    

    API(Application Program Interface)
    ABI(Application Binary Interface)

    32位的x86中EIP的三条特性:

    • 每条指令执行完成后EIP自加1
    • 指令的长度不同
    • EIP还可能会被一些指令修改,如:CALL、RET、JMP以及条件JUMP

    x86汇编基础

    x86寄存器

    有8位、16位和32位的寄存器。

    16位:AX、BX、CX、DX、BP、SI、DI、SP

    32位:EAX(累加器)、EBX(基地址寄存器)、ECX(计数寄存器)、EDX(数据寄存器)、EBP(堆栈基址指针)、ESI与EDI(变址寄存器)、ESP(堆栈顶指针)

    段寄存器:

    • CS(Code Segment Register),代码段
    • DS(Data Segment Register),数据段
    • ES(Extra Segment Register),附加数据段
    • SS(Stack Segment Register),堆栈段
    • FS与GS段寄存器用于区分x86的两种模式

    CPU在实际取指令时根据CS:EIP来准确定位一个指令。

    x86指令

    mov指令与寻址方式

    • 寄存器寻址
    movl %eax,%edx(%为寄存器标识,操作均为寄存器,与内存无关)
    

    执行结果:edx = eax

    • 立即寻址
    movl $0x123,%edx ($表示数值,表示将立即数存入寄存器中)
    

    执行结果:edx = 0x123

    • 直接寻址
    movl 0x123,%edx (将某个内存地址的内容存入寄存器中)
    

    执行结果: edx = *(int32_t*)0x123

    这里将0x123当作指针,寻找其指向的内存内容

    • 间接寻址
    movl (%ebx), %edx (将寄存器中存放的值作为内存地址,取出其中内容,使用()表示**从地址取出内容**)
    

    执行结果: edx = *(int32_t*)ebx

    • 变址寻址
    movl 4(%ebx),%edx (将寄存器中存放的值作为基内存地址,加入偏移量后再取出相应内存地址对应内容)
    

    执行结果: edx = *(int32_t*)(ebx+4)

    b,w,l,q分别代表8位,16位,32位和64位

    (样例中是movl,即表示是32位mov指令)

    对于x86来说,大部分指令都能直接访问一个内存地址。

    AT&T汇编格式与Intel汇编格式略有不同,Linux内核使用的是AT&T汇编

    PUSH、POP、CALL和RET

    pushl %eax
    拆解指令:

    subl $4,%esp 
    movl %eax,(%esp)
    

    含义:将栈顶指针减4(栈从高到低增长),然后将eax里的值放入现在的栈顶所指向的内存中。

    popl %eax
    拆解指令:

    movl (%esp),%eax
    addl $4,%esp
    

    含义:将栈顶值放入eax寄存器中,栈顶指针+4表示退栈

    call 0x12345
    拆解指令:

    pushl %eip (当前的eip压栈,即要保存返回地址)
    movl $0x12345,%eip (将 eip = 0x12345,即CPU要执行的下一条指令是0x12345指向的指令)
    

    ret

    拆解指令:

    popl %eip (将call保存的eip还原到eip寄存器中)
    

    约定

    写汇编代码的时候不能直接修改eip寄存器,所以形如movl $8,%eip的用法是错误的!

    C到汇编

    enter:

    pushl %ebp
    movl %esp,%ebp
    

    建立堆栈

    leave:

    movl %ebp,%esp
    popl %ebp
    

    退栈

    实验截图

    以下是我的实验截图,因为在学校学号是13061193,所以使用了这个数字作为个人的特殊标识:

    函数调用堆栈分析

    一些知识点

    • 函数的调用堆栈是由逻辑上的多个堆栈叠加起来形成的。
    • 函数的返回值默认使用eax寄存器存储返回给上一级函数,跟MIPS中的$v0寄存器的作用一样。

    汇编代码分析

    int g(int x)
    {
      return x + 3;
    }
     
    int f(int x)
    {
      return g(x);
    }
     
    int main(void)
    {
      return f(13061193) + 1;
    }
    

    首先看最简单的 int g(int x)函数,它不涉及到嵌套函数调用,只有一条语句用于返回 x+3

    g函数编译成的汇编代码段即:

    pushl %ebp
    movl  %esp,%ebp
    movl  8(%ebp),%eax
    addl  $3,%eax
    popl %ebp
    ret
    

    第1、2行汇编代码等价于enter,是一个存储函数基地址的过程。而在存储函数基地址之前,实际上还需要存储的是函数的参数与返回地址。返回地址的存入不是显式的,是在call语句时eip被存入栈中。

    从开始到结束,整个过程可以用如下动图所示:

    • 左侧堆栈为执行完红色指令后的堆栈状态。
    • 使用main-eip、f-eip的意思是指,函数调用的返回点分别是在main、f函数中。
    • 使用main-ebp、f-ebp、g-ebp的意思是 这些分别是三个函数的基地址。
    • f-x 和 g-x 的意思是 这两个函数的参数 x 。

    总结

    冯诺依曼结构下的计算机是依靠CPU执行存储器中的代码段,配合存储器中的数据段等来工作的。

  • 相关阅读:
    LINUX内核参数调优集锦
    性能测试基础-开门篇3(LR常用函数介绍)
    高并发WEB服务的演变
    数据链路层学习之LLDP
    Win8.1 Metro应用无法联网终极解决方法
    Win8.1 Metro应用无法联网,提示“无法加载此页面”解决方法!(看红色字体部分)
    SecureCrt脚本(三)二级对象之Screen详解
    SecureCrt自动化
    Python数据类型一:数字与运算符
    高阶函数
  • 原文地址:https://www.cnblogs.com/SivilTaram/p/linux_kernel_lab1.html
Copyright © 2011-2022 走看看