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小时

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

    参考资料

  • 相关阅读:
    优化SQL查询:如何写出高性能SQL语句
    提高SQL执行效率的16种方法
    Spring Ioc DI 原理
    java内存泄漏
    转:js闭包
    LeetCode Best Time to Buy and Sell Stock III
    LeetCode Best Time to Buy and Sell Stock with Cooldown
    LeetCode Length of Longest Fibonacci Subsequence
    LeetCode Divisor Game
    LeetCode Sum of Even Numbers After Queries
  • 原文地址:https://www.cnblogs.com/20155226thy/p/8052864.html
Copyright © 2011-2022 走看看