zoukankan      html  css  js  c++  java
  • 2017-2018-1 20155226《信息安全系统设计基础》第13周学习总结

    2017-2018-1 20155226《信息安全系统设计基础》第13周学习总结

    教材学习内容深度学习

    章节练习题

    3.58

    long decode2(long x, long y, long z)
    {
      int result = x * (y - z);
      if((y - z) & 1)
        result = ~result;
      return result;
    }
    

    3.60

    A. x : %rdi n : %esi result : %rax mask : %rdx

    B. result = 0 mask = 1

    C. mask != 0

    D. mask >>= n

    E. result |= (x & mask)

    long loop(long x, int n)
    {
        long result = 0;
        long mask;
        for(mask = 1; mask != 0; mask >>= n)
        {
            result |= (x & mask);
        }
        return result;
    }
    

    3.61

    long cread_alt(long *xp)
    {
        static long tmp = 0;
        if(xp == 0)
        {
            xp = &tmp;
        }
        return *xp;
    }
    

    这个地方也是很无语,在我的环境下必须将tmp的存储类型设置为静态存储,并且将gcc的优化设置为O3,这样才能生成使用conditional transfer的指令(才能让gcc相信优化是值得的。。):

    00000000004004f0 <cread_alt>:
      4004f0:   48 85 ff                test   %rdi,%rdi
      4004f3:   b8 38 10 60 00          mov    $0x601038,%eax
      4004f8:   48 0f 44 f8             cmove  %rax,%rdi
      4004fc:   48 8b 07                mov    (%rdi),%rax
      4004ff:   c3                      retq   
    

    3.62

    typedef enum {MODE_A, MODE_B, MODE_C, MODE_D, MODE_E}
    long switch3(long *p1, long *p2, mode_t action)
    {
      long result = 0;
      switch(action)
      {
        case MODE_A:
          result = *p2;
          *p2 = *p1;
          break;
          
        case MODE_B:
          result = *p1 + *p2;
          *p1 = result;
          break;
          
        case MODE_C:
          *p1 = 59;
          result = *p2;
          break;
          
        case MODE_D:
          *p1 = *p2;
          
        case MODE_E:
          result = 27;
          break;
          
        default:
          result = 12;
      }
      return result;
    }
    

    3.63

    long switch_prob(long x, long n)
    {
      long result = x;
      switch(n)
      {
        case 0:
        case 2:
          result += 8;
          break;
        case 3:
          result >>= 3;
          break;
        case 4:
          result = (result << 4) - x;
        case 5:
          result *= result;
        default:
          result += 0x4b;
      }
      return result;
    }
    

    3.64

    A. &A[i][j][k] = Xa + L(iST + j*T + k)

    B. R = 7,S = 5,T = 13

    3.65

    A. rdx (每次移位8,即按行移动)

    B. rax(每次移位120 = 8 * 15,按列移动)

    C. 由B,M = 15

    3.66

    NR(n)是数组的行数,所以我们找循环的次数,即rdi,得到rdi = 3n.

    NC(n)是数组的列数,所以我们应该找每次循环更新时对指针增加的值,这个值等于sizeof(long) * NC(n),即r8,得到r8 = 8 * (4n + 1).

    综上,可知两个宏定义:

    #define NR(n) (3*(n))
    #define NC(n) (4*(n)+1)
    

    3.67

    A.

    B. %rsp + 64

    C. 通过以%rsp作为基地址,偏移8、16、24来获取strA s的内容(由于中间夹了一个返回地址,所以都要加8)

    D. 通过传进来的参数%rdi(%rsp + 64 + 8),以此作为基地址,偏移8、16、24来写入strB r

    E.

    F. 我记得我在看《C语言程序设计: 现代方法 2rd》的时候,里面说传递聚合类型的变量可以使用指针,这样比传递整个数据结构要快一些(当然写操作会改变实参)。这个题目里面也都是读操作,可以发现编译器自动进行了优化——传递了基地址而非复制了整个数据结构。返回就是在调用它的函数的栈帧中存入一个相关的数据结构。(这个题里面process其实没有栈帧,如果返回地址算eval的话)3.68

    这题考察的是内存对齐。通过结构体成员的位置逐渐缩小范围:

        int t 为8(%rsi),所以4<B<=8
        long u 为32(%rsi),所以24 < 8 + 4 + 2*a <= 32,得到6<A<=10
        long y 为184(%rdi),所以176 < 4*A*B <= 184,得44 < A*B <=46。
    

    所以AB = 45 或者AB = 46,结合A, B各自的范围,只可能为A = 9, B = 5.

    3.69

    A. 根据第4、5行的指令, idx的值为(bp + 40i + 8),由第1、2行指令,这里的8是因为第一个int first整数和内存对齐的原因,所以每一个a_struct的大小为40字节。

    由于0x120 - 0x8 = 280字节,所以CNT = 280/40 = 7.

    B. 由第6、7行指令知,idx和x数组内元素都是signed long类型的。由于整个a_struct数据类型大小为40字节,所以其内部应该为85 = 8 + 84:

    typedef struct
    {
        long idx;
        long x[4];
    }
    
    

    3.70

    A.

    e1.p : 0

    e1.y : 8

    e2.x : 0

    e2.next : 8

    B. 16 bytes

    C.

    void proc(union ele *up)
    {
        up->x = *(up->e2.next->e1.p) - up->e2.next->e1.y;
    }
    

    3.71

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #define SIZE_OF_BUFFER 10
    
    int good_echo(void)
    {
        char *buffer = calloc(SIZE_OF_BUFFER, sizeof(char));
        if (buffer == NULL)
        {
            fprintf(stderr, "Error: failed to allocate buffer.
    ");
            return -1;
        }
        while(1)
        {
            fgets(buffer, SIZE_OF_BUFFER, stdin);
            if (strlen(buffer) == SIZE_OF_BUFFER-1) /*两种情况,一种是刚好输入了能填满缓冲区的字符数,另一种是大于缓冲区,一次不能读完*/
            {
                fputs(buffer, stdout);
                if (buffer[SIZE_OF_BUFFER-1-1] == '
    ')/*刚好输入了能填满缓冲区的字符数,结束读入*/
                {
                    break;
                }
                memset(buffer, 0, strlen(buffer));/*清空缓冲区,因为要通过strlen判断读入了多少字符,继续读入*/
            }
            else if (strlen(buffer) < SIZE_OF_BUFFER-1)/*一定是最后一次读入,结束读入*/
            {
                fputs(buffer, stdout);
                break;
            }
            else
            {
                break;
            }
        }
        free(buffer);
        return 0;
    }
    
    int main(int argc, char const *argv[])
    {
        return good_echo();
    }
    

    3.72

    A. andq $-16, X这条指令相当于将低4位置零,也就是使得rax中保存的8n+30对16取整。所以s2-s1为8n+30对16取整的结果。

    B. p的值为rsp(r8)-15对16取整的结果,确保了p数组的起始地址为16的整数倍。

    C. 8n + 30对16取整有两种可能:一种是8n本身就是16的整数倍即n = 2k,此时取整后为8n+16; 另一种是8n = 16k + 8即n = 2k + 1,此时取整后为8n + 24。由System V AMD64 ABI标准可知,s1的地址为16的整数倍(即结尾为0000),所以s2的地址也肯定是16的整数倍(结尾为0000)。又因p是由s2减15对16取整得到的结果,所以p和s2之间肯定相差2字节,即e2 = 2 bytes. 所以e1最大为(n为奇数) :8n + 24 - 16 - 8n = 8 bit, 最小为(n为偶数):8n + 16 -16 - 8n = 0.(这个题我估计没有考虑到ABI标准对于栈帧对齐的问题,s1的地址本来就应该是16的整数倍)

    D. 由A B C可知,这种方法保证了s2 和 p的起始地址为16的整数倍,而且保证了e1最小为8n,能够存储p数组。

    浮点数部分并未测试

    3.73

    find_range:
        vxorps %xmm1, %xmm1, %xmm1
        vucomiss %xmm0, %xmm1
        ja .L5
        jp .L8
        movl $1, %eax
        je .L3
        .L8:
        seta %al
        movzbl %al, %eax
        addl $2, %eax
        ret
        .L5:
        movl $0, %eax
        .L3:
        rep;ret
    

    3.74

    find_range:
        vxorps %xmm1, %xmm1, %xmm1
        vucomiss %xmm0, %xmm1
        cmova $0, %eax
        cmove $1, %eax
        cmovb $2, %eax
        cmovp $3, %eax
        rep;ret
    

    3.75

    A. 每一个复数变量使用两个%xmm寄存器传送。

    B. 通过%xmm0和%xmm1返回一个复数类型值。

    教材学习内容总结

    3.1历史观点

    • X86 寻址方式经历三代:

      DOS时代的平坦模式,不区分用户空间和内核空间,很不安全
      8086的分段模式
      IA32的带保护模式的平坦模式
      
    • Linux使用平坦寻址方式,使程序员将整个存储空间看做一个大的字节数组。

    3.2程序编码

    • ISA:指令集体系结构,机器级程序的格式和行为,它定义了处理器状态、指令的格式以及每条指令对状态的影响。
    • 程序计数器(通常称为PC,用%eip表示),指示将要执行的下一条指令在存储器中的地址。
    • 整数寄存器文件:存储地(对应于C语言的指针)或整数数据。
      条件码寄存器:保存着最近执行的算数或逻辑指令的状态信息,用来实现控制或者数据流中的条件变化。
    • 浮点寄存器:用来存放浮点数据。

    编译过程:

    • C预处理器插入宏和头文件:gcc -E xxx.c -o xxx.i
    • 编译器产生源代码的汇编代码:gcc -S xxx.i -o xxx.s
    • 汇编器化成二进制目标代码:gcc -c xxx.s -o xxx.o
    • 链接器生成最终可执行文件:gcc xxx. -o xxx
    • objdump -d xxx.o -o 反汇编
    • 建立函数调用栈帧的汇编代码:
    pushl   %ebp 将寄存器%ebp中的内容压入程序栈
    movl    %esp,%ebp  将%ebp中的内容放入寄存器%esp
    ......
    popl    %ebp
    寄存器%ebp中内容出栈
    ret 返回结果
    

    注意:

    1. 64位机器上想要得到32代码:gcc -m32 -S xxx.c

    2. Ubuntu中 gcc -S code.c (不带-O1)产生的代码更接近教材中代码(删除"."开头的语句)

    3. 找到程序的字节表示:(gdb) x/17xb sum

    4. 二进制文件可以用od命令查看,也可以用gdb的x命令查看。有些输出内容过多,我们可以使用 more或less命令结合管道查看,也可以使用输出重定向来查看od code.o | more od code.o > code.txt

    3.4访问信息

    寄存器

    一个IA32中央处理单元(CPU)包含一组8个存储32位值的寄存器。用来存储整数数据和指针。

    %eax    %ax (%ah %al)  通用寄存器
    %ecx    %cx (%ch %cl)  通用寄存器
    %edx    %dx  (%dh %dl)   通用寄存器
    %ebx    %bx  (%bh %bl)   通用寄存器
    %esi    %si             用来操纵数组
    %edi    %di             用来操纵数组
    %esp    %sp             操纵栈帧
    %ebp    %bp             操纵栈帧
    
    寻址方式
    • 根据操作数的不同类型,寻址方式可分为以下三种:
    1. 立即数寻址方式:操作数为常数值,写作$后加一个整数。
    2. 寄存器寻址方式:操作数为某个寄存器中的内容。
    3. 存储器寻址方式:根据计算出来的地址访问某个存储器的位置。
    • 寻址模式:一个立即数偏移Imm,一个基址寄存器Eb,一个变址寄存器Ei,一个比例因子s(必须为1,2,4,8)有效地址计算为:Imm(Eb,Ei,s) = Imm + R[Eb] + R[Ei]*s
    数据传送指令
    • MOV相当于C语言的赋值'='
    • mov S,D S中的字节传送到D中

    3.6控制

    条件码

    描述最近的算数或者逻辑操作的属性,可以检测这些寄存器来执行条件分支指令。

    • CF:进位标志,最近操作使高位产生进位,用来检测无符号操作数的溢出
    • ZF:零标志,最近操作得出的结果为0
    • SF:符号标志,最近操作得到的结果为负数
    • OF:溢出标志,最近操作导致一个补码溢出-正溢出或负溢出。
    访问条件码的读取方式
    1. 根据条件码的某个组合,将一个字节设置成0或1;
    2. 跳转到程序某个其他的部分;
    3. 有条件的传送数据。
    • SET指令根据t=a-b的结果设置条件码
    跳转指令及其编码
    • 控制中最核心的是跳转语句:

            有条件跳转(实现if,switch,while,for)
      无条件跳转jmp(实现goto)
      
    • 当执行PC相关的寻址时,程序计数器的值是跳转指令后面那条指令的地址,而不是跳转指令本身的地址。

    翻译条件分支
    • 将条件和表达式从C语言翻译成机器代码,最常用的方式是结合有条件和无条件跳转。
    • C语言中if-else语句的通用形式:
    if(test-expr)
    then-statement
    else
    else-statement
    
    汇编结构:
    t=test-expr;
    if!(t)
        goto false;
    then-statement
    goto done;
    false:
    else-statement
    done:
    
    循环
    • do-while循环
    1. C语言中do-while语句的通用形式:
    do
    body-statement
    while(test-expr);
    
    1. 汇编结构:
    loop:
    body-statement
    t=test-expr;
    if(t)
        goto loop;
    
    • while循环
    1. C语言中while语句的通用形式:
    while(test-expr)
    body-statement
    
    1. 汇编结构:
      t=test-expr;
    if(!t)
        goto done;
    loop:
    body-statement
    t=test-expr;    
    if(t)
        goto loop;
    done:
    
    • for循环
    1. C语言中for语句的通用形式:
    for(init-expr;test-expr;update-expr)
    body-statement
    
    1. 汇编结构
    init-expr
    t=test-expr;
    if(!t)
        goto done;
    loop:
    body-statement
    update-expr;
    t=test-expr;
    if(t)
        goto loop;
    done:
    

    3.7过程

    • 数据传递、局部变量的分配和释放通过操纵程序栈来实现。
    栈帧结构
    • 为单个过程分配的栈叫做栈帧,寄存器%ebp为帧指针,而寄存器指针%esp为栈指针,程序执行时栈指针移动,大多数信息的访问都是相对于帧指针。
    • 栈向低地址方向增长,而栈指针%esp指向栈顶元素。
    转移控制
    • call:目标是指明被调用过程起始的指令地址,效果是将返回地址入栈,并跳转到被调用过程的起始处。
    • ret:从栈中弹出地址,并跳转到这个位置。
    • 函数返回值存在%eax中
    寄存器使用惯例
    • 程序寄存器是唯一能被所有过程共享的资源,调用者保存寄存器 和 被调用者保存寄存器是分开的,对于哪一个寄存器保存函数调用过程中的返回值要有统一的约定。

    教材及课堂学习和总结

    • 问题和解决方法已在测试中列出

    代码托管

    尝试一下记录「计划学习时间」和「实际学习时间」,到期末看能不能改进自己的计划能力。这个工作学习中很重要,也很有用。
    耗时估计的公式
    :Y=X+X/N ,Y=X-X/N,训练次数多了,X、Y就接近了。

    参考:软件工程软件的估计为什么这么难软件工程 估计方法

    • 计划学习时间:25小时

    • 实际学习时间:20小时

    (有空多看看现代软件工程 课件
    软件工程师能力自我评价表
    )

    参考资料

  • 相关阅读:
    微服务、SpringCloud、k8s、Istio杂谈
    php环境安装
    最近重构公司消息服务的架构设计
    test
    博文目录(最新更新:2019.8.5)
    读过的书
    我在北京这几年(全)
    【原】深度学习的一些经验总结和建议 | To do v.s Not To Do
    如何高效利用一场技术分享?
    深度学习分布式训练及CTR预估模型应用
  • 原文地址:https://www.cnblogs.com/20155226thy/p/8052864.html
Copyright © 2011-2022 走看看