学堂在线MOOC《汇编语言程序设计》题解

MOOC地址:

汇编语言程序设计 - 清华大学 - 学堂在线

1 浮点数的机器表示

1.1 填空题 浮点数

单精度浮点数的exp域的位宽是()位?

答案:8

1.2 填空题 浮点数

单精度浮点数的frac域位宽是()位?

答案:23

1.3 判断题

已知

int x = ...;
float f = ...;
double d = ...;

df都不是NaN,判断以下关系式是否成立?

x == (int)(float) x

答案:×

解析:float的frac域宽度不够,可能会有精度损失。

1.4 判断题

已知

int x = ...;

判断以下关系式是否成立?

x == (int)(double) x

答案:√

解析:double的frac域足够放下int,不会有精度损失。

1.5 判断题

已知

float f = ...;

f不是NaN,判断以下关系式是否成立?

f == (float)(double) f

答案:√

解析:float转换成double不会有精度损失。

1.6 判断题

已知

double d = ...;

d不是NaN,判断以下关系式是否成立?

d == (float) d

答案:×

解析:double转换成float可能会有精度损失。

1.7 判断题

已知

float f = ...;

f不是NaN,判断以下关系式是否成立?

f == -(-f)

答案:√

解析:浮点数取负只改变符号位,不会溢出。

1.8 判断题

判断以下关系式是否成立?

2/3 == 2/3.0

答案:×

解析:2/3的结果是整数,但2/3.0的结果是浮点数。

1.9 判断题

已知

double d = ...;

d不是NaN,判断以下关系式是否成立?

(d < 0.0) == ((d*2) < 0.0)

答案:√

解析:浮点数的下溢是逐步下溢,不像整数会从负数变成正数。

1.10 判断题

已知

float f = ...;
double d = ...;

df都不是NaN,判断以下关系式是否成立?

(d > f) == (-f > -d)

答案:√

解析:浮点数取负不会溢出。

1.11 判断题

已知

float f = ...;
double d = ...;

df都不是NaN,判断以下关系式是否成立?

(d + f) - d == f

答案:×

解析:d+f可能会溢出,损失精度。

2 80x80汇编与C语言-2

2.1 填空题

已知寄存器edx内的数值为0x8000ecx内的则为0x10;请给出地址表达式0x4(%edx, %ecx, 4)所表示的地址值。

答案:0x8044

解析:D(Rb, Ri, S) = Rb + S*Ri + D

2.2 填空题

x86-64体系结构具有()个通用寄存器,而x86-32只有()个。

答案:

  • 16
  • 8

2.3 判断题

leal (%edx, %eax), %ecx这条指令在执行过程中需要访问内存数据。

答案:×

解析:leal指令只计算地址,不访存。因此leal指令也可以用于整数运算。

2.4 单选题

请问哪个条件码可用于检测无符号整数运算的溢出?

  • CF
  • SF
  • ZF
  • OF

答案:CF

解析:

  • CF(Carry Flag),无符号整数运算溢出时置位
  • SF(Sign Flag),计算结果<0时置位
  • ZF(Zero Flag),计算结果=0时置位
  • OF(Overflow Flag),带符号整数运算溢出时置位。

2.5 单选题

setasetb指令适用于无符号还是带符号整数的条件码读取?

  • 无符号
  • 带符号

答案:无符号

解析:setasetb适用于无符号整数;其中a、b分别指above和below。带符号整数对应的是setgsetl(g和l分别指greater和less)。

2.6 填空题

请补充与下图中C语言对应的汇编代码中的遗漏部分(x86-32结构下编译生成)。

/* C */
int absdiff(int x, int y)
{
    int result;
    if (x > y) {
        result = x - y;
    } else {
        result = y - x;
    }
    return result;
}
# asm
pushl %ebp
movl %esp, %ebp
pushl %ebx
movl 8(%ebp), %ecx
movl 12(%ebp), %edx
movl %ecx, %ebx
subl %edx, %ebx
movl %edx, %eax
subl %ecx, %eax
cmpl %edx, %ecx
____ %ebx, %eax
popl %ebx
popl %ebp
ret

答案:cmovg

解析:将汇编代码“翻译”如下:

ecx := x
edx := y
ebx := x
ebx := x - y
eax := y
eax := y - x
Compare x to y
    if (?) (eax := x - y)
return eax

由分析可知,此处应该填写条件移动指令cmovg(若x>y则传送)。

3 80x80汇编与C语言-3

3.1 填空题

下图给出了一个C函数,并由gcc编译成相应的汇编代码(AT&T语法格式),请补全这段代码里头被省去的部分。(X86-32结构)

/* C */
int arith(int x, int y, int z)
{
    int t1 = x * y;
    int t2 = z - t1;
    int t3 = x + 40;
    int t4 = y * 2;
    int t5 = t3 >> t4;
    int rval = t2 * t5;
    return rval;
}
# asm
pushl %ebp
movl ____, %ebp
movl ____(%ebp), %edx
movl ____(%ebp), %ecx
pushl %ebx
movl 16(%ebp), %eax
movl %edx, %ebx
addl $40, %edx
imull ____, %ebx
addl %ecx, ____
sarl %cl, %edx
subl %ebx, %eax
imull ____, %eax
popl ____
popl %ebp
ret

答案:

  • %esp
  • 8
  • 12
  • %ecx
  • %ecx
  • %edx
  • %ebx

解析:

以下是填空完成后的完整汇编代码。推理过程略。

asm explanation
pushl %ebp setup, save old ebp
movl **%esp**, %ebp setup
movl **8**(%ebp), %edx edx = x
movl **12**(%ebp), %ecx ecx = y
pushl %ebx save old ebx
movl 16(%ebp), %eax eax = z
movl %edx, %ebx ebx = edx = x
addl $40, %edx edx += 40 (edx = x + 40 = t3)
imull **%ecx**, %ebx ebx *= y (ebx = x * y = t1)
addl %ecx, **%ecx** ecx += y (ecx = 2*y = t4)
sarl %cl, %edx edx >>= cl (cl = t4, edx = t5)
subl %ebx, %eax eax -= ebx (eax = z - t1 = t2)
imull **%edx**, %eax eax *= edx (eax = t2 * t5 = rval)
popl **%ebx** reset ebx
popl %ebp reset ebp
ret return eax (rval)

3.2 填空题

在X86-32位体系结构中,当前运行函数的帧(Frame)基址寄存器所指向的栈地址的“上方”(高地址)由低到高存放的是函数返回地址、( );“下方”存放的是( )、( )(此处无需考虑顺序)。

答案:

  • 输入参数
  • 局部变量
  • 临时存储(存在其他正确填法,如“保存的寄存器值”,此处只列出标准答案)

4 80x80汇编与C语言-4

4.1 填空题

请按顺序填写图左侧汇编代码对应的C代码(e.g. 右3)

# asm
foo1:
    pushl %ebp
    movl %esp, %ebp
    movl 8(%ebp), %eax
    sall %$4, %eax
    subl 8(%ebp), %eax
    movl %ebp, %esp
    popl %ebp
    ret

foo2:
    pushl %ebp
    movl %esp, %ebp
    movl 8(%ebp), %eax
    testl %eax, %eax
    jge .L4
    addl $15, %eax
.L4:
    shrl $4, %eax
    movl %ebp, %esp
    popl %ebp
    ret

foo3:
    pushl %ebp
    movl %esp, %ebp
    movl 8(%ebp), %eax
    shrl $31, %eax
    movl %ebp, %esp
    popl %ebp
    ret
/* C */
int choice1(int x)
{
    return (x < 0);
}

int choice2(int x)
{
    return (x << 31) & 1;
}

int choice3(int x)
{
    return 15 * x;
}

int choice4(int x)
{
    return (x + 15) / 4;
}

int choice5(int x)
{
    return x / 16;
}

int choice6(int x)
{
    return (x >> 31);
}

答案:

  • 右3
  • 右5
  • 右1

解析:值得注意的是右5和对应的汇编代码。由于算术右移指令对应的除法的取整模式是向下取整,而C语言要求的除法的取整模式是向0取整,因此负数除法的取整会出问题,不能直接右移。正确的计算公式是:

  • a ≥ 0时,a / 2^b = a >> b
  • a < 0时,a / 2^b = (a + 2^b − 1) >> b(此处除16相当于b=4,因此2^b - 1 = 15

这就解释了汇编代码中testl %eax, %eaxjge .L4的意义。testl的功能是,取后面两个操作数的与,根据运算结果置条件码。当%eax >= 0时,运算结果仍为%eax,SF为0,OF也为0;当%eax < 0时,SF为1,OF为0。当条件码满足SF=OF时,jge .L4会跳转到.L4直接右移(不是负数,不用加上15);否则先加上15再右移。

4.2 填空题

已知三个二维矩阵的定义如下,并已初始化。

/* C */
#define n 10
int a[n][n] ;
int b[n][n] ;
int c[n][n] ;

需要进行矩阵乘,即矩阵a x b结果置于c。下面这段C代码是一个矩阵乘函数。

/* C */
void column()
{
    int j, k, i;
    int r;
    for (j=0; j

下面是相应的汇编代码。请填空。

L11:
    movl -16(%ebp), %edx
    leal (%ecx, %edx), %eax
    leal (%ecx, %edi), %edx
    movl a_start_addr(, ____, 4), %eax
    addl $10, %ecx
    imull ____, %edx
    addl %edx, c_start_addr(, %eax, 4)
    decl %ebx
    jns L11
    addl $40, -20(%ebp)
    incl %edi
    cmpl ____, %edi
    jle L12
    incl -16(%ebp)
    cmpl ____, -16(%ebp)
    jle L13
    addl ____, %esp
    popl %ebx
    popl %esi
    popl %edi
    popl %ebp
    ret

_matrix:
    pushl %ebp
    movl ____, %ebp
    pushl %edi
    pushl %esi
    pushl %ebx
    subl $8, %esp
    movl $0, -16(____)

L13:
    movl -16(%ebp), %eax
    xorl %edi, ____
    leal b_start_addr(, ____, 4), %eax
    movl ____, -20(%ebp)

L12:
    movl ____(%ebp), %edx
    xorl %ecx, %ecx
    movl $9, %ebx
    movl (%edx), %esi

答案:

  • %esp
  • %ebp
  • %edi
  • %eax
  • %eax
  • -20
  • %edx
  • %esi
  • $9
  • $9
  • $8

解析:

以下是填空完成后的完整汇编代码。推理过程略。

asm explanation
_matrix:
pushl %ebp setup, save old ebp
movl %esp, %ebp setup
pushl %edi save edi
pushl %esi save esi
pushl %ebx save ebx
subl $8, %esp esp -= 8
movl $0, -16(%ebp) j = 0 (-16(%ebp) = j)
L13: the start of j loop
movl -16(%ebp), %eax eax = j
xorl %edi, %edi edi = 0 (edi = k)
leal b_start_addr(, %eax, 4), %eax eax = &b[4*j]
movl %eax, -20(%ebp) -20(%ebp) = &b[4*j + (40*k)]
L12: the start of k loop
movl -20(%ebp), %edx edx = &b[4*j + (40*k)]
xorl %ecx, %ecx ecx = 0 (ecx = i * 10)
movl $9, %ebx ebx = 9 // as "i"
movl (%edx), %esi esi = b[k][j], or r = b[k][j] (esi = r)
L11: the start of i loop
movl -16(%ebp), %edx edx = j
leal (%ecx, %edx), %eax eax = ecx + edx = 10 * i + j
leal (%ecx, %edi), %edx edx = ecx + edi = 10 * i + k
movl a_start_addr(,%edx, 4), %edx edx = a[4*edx] (edx = a[i][k])
addl $10, %ecx ecx += 10 (ecx = i * 10)
imull %esi, %edx edx *= r (edx = a[i][k] * r)
addl %edx, c_start_addr(, %eax, 4) c[4*eax] += edx (c[i][j] += a[i][k] * r)
decl %ebx ebx-- // as "i"
jns L11 if (ebx > 0) goto L11 (the end of i loop)
addl $40, -20(%ebp) -20(%ebp) += 40 (-20(%ebp) = 40\\*k+4*j)
incl %edi edi++ (k++)
cmpl $9, %edi compare k : 9
jle L12 if (k <= 9) goto L12 (the end of k loop)
incl -16(%ebp) -16(%ebp)++ (-16(%ebp)=j) (j++)
cmpl $9, -16(%ebp) compare j : 9
jle L13 if (j <= 9) goto L13 (the end of j loop)
addl $8, %esp esp += 8
popl %ebx finish, reset ebx
popl %esi finish, reset esi
popl %edi finish, reset edi
popl %ebp finish, reset ebp
ret return

5 80x80汇编编程-1

5.1 填空题

在X86-32位编程中有一种简单的获得所运行的指令地址的方法(X86-32位结构下eip寄存器是无法直接访问的)。比如说我们要获得下面程序中XXXX这条指令的地址并置于eax寄存器中,那么可以采用如下代码段。请补充完函数GetAddress的第一条语句(AT&T语法)。

Getaddress:
    movl ____, ____
    ret

call GetAddress
xxxx

答案:

  • (%esp)
  • %eax

解析:在call之后,GetAddress的返回地址,也就是xxxx的地址被压栈。此时,只需将栈顶所指向位置的内容((%esp),注意括号代表访存)存入eax中再返回。

平台上第二空给的答案是$eax,是错的。我18年上这个课的时候就错了,当时向老师反映来着。现在还没改。

5.2 填空题

已知一个C语言结构类型的定义如下图所示,请问在X86 32位Linux系统下变量p所占的空间大小是____字节,对齐的要求为____字节对齐?

struct S1 {
    char c;
    int i[2];
    my_struct v;
} p;

typedef TagStruct {
    int k[2];
    char c2;
} my_struct;

答案:

  • 24
  • 4

解析:

TagStruct的内存分布:

| k[0] (4 bytes) | k[1] (4 bytes) | c2 (1 byte) | padding (3 bytes) |
| -------------- | -------------- | ----------- | ----------------- |

大小为12字节,4对齐。

S1的内存分布:

| c (1 byte) | padding(3 bytes) | i[0] (4 bytes) | i[1] (4 bytes) | v (12 bytes) |
| ---------- | ---------------- | -------------- | -------------- | ------------ |

大小为24字节,4对齐。

6 80x80汇编编程-1

6.1 填空题

有如下的C代码以及对应的反汇编出来的汇编代码(x86-32体系结构):

/* copy string x to buf */
void foo(char* x) {
    int buf[1];
    strcpy((char *)buf, x);
}

void callfoo() {
    foo("abcdefghi");
}
080484f4 :
080484f4: 55                     pushl %ebp
080484f5: 89 e5                  movl %esp, %ebp
080484f7: 83 ec 18               subl $0x18, $esp
080484fa: 8b 45 08               movl 0x8(%ebp), %eax
080484fd: 83 c4 f8               addl $0xfffffff8, %esp
08048500: 50                     pushl %eax
08048501: 8d 45 fc               leal 0xfffffffc(%ebp), %eax
08048504: 50                     pushl %eax
08048505: e8 ba fe ff ff         call 80483c4 
0804850a: 89 ec                  movl %ebp, %esp
0804850c: 5d                     popl %ebp
0804850d: c3                     ret
08048510 :
08048510: 55                     pushl %ebp
08048511: 89 e5                  movl %esp, %ebp
08048513: 83 ec 08               subl $0x8, %esp
08048516: 83 c4 f4               addl $0xfffffff4, %esp
08048519: 68 9c 85 04 08         pushl $0x804859c  # push string address
0804851e: e8 d1 ff ff ff         call 80484f4 
08048523: 89 ec                  movl %ebp, %esp
08048525: 5d                     popl %ebp
08048526: c3                     ret

strcpy调用完成返回到foo过程时,buf[0]buf[1]buf[2]的值分别是多少?

在执行0x0804850dret指令前(popl后),ebp的值是多少?上述ret指令执行后,eip的值是多少?用32位16进制表示,注意大小端。

0x00000000 字符的十六进制转换表如下:

Character Hex Value
'a' 0x61
'b' 0x62
'c' 0x63
'd' 0x64
'e' 0x65
'f' 0x66
'g' 0x67
'h' 0x68
'i' 0x69
'\0' 0x00

答案:

  • 0x64636261
  • 0x68676665
  • 0x08040069
  • 0x68676665
  • 0x08040069

解析:

通过分析可以画出调用strcpy之前完整的栈(具体分析过程略)如下表(每个格子代表4个字节,address向下递减):

内容 指向该位置的指针
callfoo过程保存的%ebp
empty
empty
empty
empty
empty
string address (0x0804859c)
foo过程的返回地址 (0x08048523)
foo过程保存的%ebp %ebp
buf
empty
empty
empty
empty
empty
empty
empty
string address (strcpy的第二个参数)
%ebp - 4 (buf)

可以看出,传递给strcpybuf指针就是%ebp-4,在复制了字符串"abcdefghi"之后,会发生溢出,破坏栈中保存的%ebp和返回地址。但现在的问题是,在考虑大小端之后,栈中的实际内容到底应该是什么样子的呢?

由于X86的字节序为小端(“低对低,高对高”),可以画出从buf指针开始向上到string address位置中实际的保存内容(一个格子代表一个字节,地址从上往下递减):

描述 内容
string address (高) 08
04
85
string address (低) 9c
foo的返回地址 (高) 08
04
85
foo的返回地址 (低) 23
保存的%ebp (高) ??
??
??
保存的%ebp (低) ??
buf[0] (高) ??
??
??
buf[0] (低) ??

在向以buf开头的地址中写入字符串"abcdefghi\\0"(转换成16进制,就是0x61626364656667686900)时,由于char类型的大小只有一个字节,大小端对它来说是无所谓的,只要从低地址向高地址覆写就可以。于是,我们得到了修改过的栈帧:

描述 内容
string address (高) 08
04
85
string address (低) 9c
foo的返回地址 (buf[2]) (高) 08
04
85 00
foo的返回地址 (buf[2]) (低) 23 69
保存的%ebp (buf[1]) (高) ?? 68
?? 67
?? 66
保存的%ebp (buf[1]) (低) ?? 65
buf[0] (高) ?? 64
?? 63
?? 62
buf[0] (低) ?? 61

而寄存器会以小端模式来解释内存中的内容,因此可得,buf[0] = 0x64636261buf[1] = 0x68676665buf[2] = 0x08040069popl后得到的%ebp0x68676665,执行ret%eip的值(也就是要返回到什么地址)为0x08040069

7 MIPS32指令集与编程

7.1 填空题

异常(exception)可以分类为 ____ 和____两类,其中系统调用属于____异常、时钟中断属于____异常、Page Fault是 ____异常、机器cold reset是 ____异常。

答案:

  • 同步
  • 异步
  • 同步
  • 异步
  • 同步
  • 异步

解析:同步异常一般是指令引起的,异步异常一般是硬件引起的。

7.2 填空题

位于某个跳转指令的Branch Delay Slot中的指令(这一slot中的指令地址为A)发生了异常,那么异常处理完成后,恢复执行的指令地址是 ____;如果该跳转指令是JAL,那么该跳转指令执行完成后31号寄存器的内容是 ____。

答案:

  • A-4
  • A+4

解析:精确异常处理要求,延迟槽中的指令如果发生异常,恢复执行的指令是跳转指令;函数返回到下一条指令。

8 期末考试

我当年学的时候还有一个期末考试,现在已经没有了,不过姑且还是把当时的题整理在这里。

8.1 填空题

X、Y的数据宽度均为16位,计算结果也用16进制表示)已知[X]补=0019H,[Y]补=FE6AH,则[X+Y]补=____,[X-Y]补=____。

答案:

  • FE83H
  • 00AFH

解析:显然,X是正数,Y是负数。

[X+Y]补 = X补+Y补 = 0x0019 + 0xFE6A = 0xFE83
−Y原 = (Y补 − 1)反 = 0x0096
[X−Y]补 = X补 − Y原 = 0x0019 + 0x0096 = 0x00AF

8.2 文字填空题

在X86-32位体系结构中,C语言过程调用的默认传参规则是将过程参数从____至____压入栈,过程返回值(32位)通过____寄存器传出。

答案:

  • %eax

8.3 填空题

给出13/8这一数字的32位浮点数(符合IEEE 754标准)表示,即exp= ____;frac= ____

答案:

  • 01111111
  • 10100000000000000000000

解析:

13/8 = (1101)2/2^3 = (1.101)_2

因此E=0exp = E+bias = 0+(2^(E_length−1)−1) = 0+(01111111)_2 = (01111111)_2

省略掉有效数字开头的1,frac=10100000000000000000000。(后面一共20个0)

8.4 填空题

寄存器EAX,EBX内存储的为带符号32位整数,若%EAX >%EBX,则指令cmpl %EAX,%EBX执行后 SF=____,OF= ____。(若不确定,可以填“不确定”)

答案:

  • 不确定
  • 不确定

解析:cmpl指令根据%ebx - %eax的值设置条件码。因为补码加法可能溢出也可能不溢出,带符号数的减法结果可能溢出到正数也可能不溢出,所以两空均为不确定。

8.5 填空题

X86 32位linux系统下的float类型的数据对齐要求是____字节对齐,double类型的是____字节对齐;X86 32位Windows系统下的double类型数据是____字节对齐。

答案

  • 4
  • 4
  • 8

8.6 填空题

lw $t6, 65536($sp)经过MIPS 32汇编器处理后,产生的代码如下,请补全。

lui $1,____ 
addu $1, $1,____ 
lw $t6, 0($1)

答案:

  • 1
  • $sp

解析:lui将立即数装载到寄存器的高16位,低16位清零,而65536=(10000000000000000)_2,因此,执行lui指令后,$1=65536

8.7 填空题

li $6, 0x345678经过MIPS 32汇编器处理后,产生的代码如下,请补全

lui $1, ____
____ $6, $1, ____

答案:

  • 0x34
  • oriaddiu
  • 0x5678

解析:lui的作用同上题;ori作用的是寄存器的低16位。

你可能感兴趣的:(学堂在线MOOC《汇编语言程序设计》题解)