计算机系统第三章答案

习 题

3. 参考答案:

(1)后缀:w , 源:基址+比例变址+偏移, 目:寄存器 (2)后缀:b , 源:寄存器, 目:基址+偏移 (3)后缀:l , 源:比例变址, 目:寄存器 (4)后缀:b , 源:基址, 目:寄存器 (5)后缀:l , 源:立即数, 目:栈 (6)后缀:l , 源:立即数, 目:寄存器 (7)后缀:w , 源:寄存器, 目:寄存器 (8)后缀:l , 源:基址+变址+偏移, 目:寄存器 4.参考答案:

(1)源操作数是立即数0xFF ,需在前面加‘$’

(2)源操作数是16位,而长度后缀是字节‘b ’,不一致 (3)目的操作数不能是立即数寻址

(4)操作数位数超过16位,而长度后缀为16位的‘w ’ (5)不能用8位寄存器作为目的操作数地址所在寄存器 (6)源操作数寄存器与目操作数寄存器长度不一致 (7)不存在ESX 寄存器

(8)源操作数地址中缺少变址寄存器 5.参考答案:

6.参考答案:

(1)xptr 、yptr 和zptr 对应实参所存放的存储单元地址分别为:R[ebp]+8、R[ebp]+12、

R[ebp]+16。

(2)函数func 的C 语言代码如下: void func(int *xptr, int *yptr, int *zptr) { int tempx=*xptr; int tempy=*yptr; int tempz=*zptr;

*yptr=tempx; *zptr = tempy;

*xptr = tempz;

}

7.参考答案:

(1)R[edx]=x

(2)R[edx]=x+y+4 (3)R[edx]=x+8*y (4)R[edx]=y+2*x+12 (5)R[edx]=4*y (6)R[edx]=x+y 8.参考答案:

(1)指令功能为:R[edx]←R[edx]+M[R[eax]]=0x00000080+M[0x8049300],寄存器EDX 中内容改变。改变后的

内容为以下运算的结果:00000080H+FFFFFFF0H

0000 0000 0000 0000 0000 0000 1000 0000 + 1111 1111 1111 1111 1111 1111 1111 0000 1 0000 0000 0000 0000 0000 0000 0111 0000

因此,EDX 中的内容改变为0x00000070。根据表3.5可知,加法指令会影响OF 、SF 、ZF 和CF 标志。OF=0,ZF=0,SF=0,CF=1。

(2)指令功能为:R[ecx]←R[ecx]-M[R[eax]+R[ebx]]=0x00000010+M[0x8049400], 寄存器ECX 中内容改变。

改变后的内容为以下运算的结果:00000010H -80000008H

0000 0000 0000 0000 0000 0000 0001 0000 + 0111 1111 1111 1111 1111 1111 1111 1000 0 1000 0000 0000 0000 0000 0000 0000 1000

因此,ECX 中的内容改为0x80000008。根据表3.5可知,减法指令会影响OF 、SF 、ZF 和CF 标志。OF=1,ZF=0,SF=1,CF=1 0=1。

(3)指令功能为:R[bx]←R[bx] or M[R[eax]+R[ecx]*8+4],寄存器BX 中内容改变。改变后的内容为以下运算

的结果:0x0100 or M[0x8049384]=0100H or FF00H

0000 0001 0000 0000 or 1111 1111 0000 0000 1111 1111 0000 0000

因此,BX 中的内容改为0xFF00。由3.3.3节可知,OR 指令执行后OF=CF=0;因为结果不为0,故ZF=0;因为最高位为1,故SF=1。

(4)test 指令不改变任何通用寄存器,但根据以下“与”操作改变标志:R[dl] and 0x80

1000 0000 and 1000 0000 1000 0000

由3.3.3节可知,TEST 指令执行后OF=CF=0;因为结果不为0,故ZF=0;因为最高位为1,故SF=1。 (5)指令功能为:M[R[eax]+R[edx]]←M[R[eax]+R[edx]]*32,即存储单元0x8049380中的内容改变为以下运算

的结果:M[0x8049380]*32=0x908f12a8*32,也即只要将0x908f12a8左移5位即可得到结果。 1001 0000 1000 1111 0001 0010 1010 1000

=0001 0001 1110 0010 0101 0101 0000 0000

因此,指令执行后,单元0x8049380中的内容改变为0x11e25500。显然,这个结果是溢出的。但是,根据表3.5可知,乘法指令不影响标志位,也即并不会使OF=1。

(6)指令功能为:R[cx] ←R[cx]-1,即CX 寄存器的内容减一。

0000 0000 0001 0000 + 1111 1111 1111 1111 1 0000 0000 0000 1111

因此,指令执行后CX 中的内容从0x0010变为0x000F 。由表3.5可知,DEC 指令会影响OF 、ZF 、SF ,根据上述运算结果,得到OF=0,ZF=0,SF=0。

9.参考答案:

movl 12(%ebp), %ecx //R[ecx]←M[R[ebp]+12],将y 送ECX sall $8, %ecx //R[ecx]←R[ecx]

从汇编代码的第2行和第4行看,y 应该是占8个字节,R[ebp]+20开始的4个字节为高32位字节,记为y h ;R[ebp]+16开始的4个字节为低32位字节,记为y l 。根据第4行为无符号数乘法指令,得知y 的数据类型num_type为unsigned long long。

movl 12(%ebp), %eax //R[eax]←M[R[ebp]+12],将x 送EAX movl 20(%ebp), %ecx //R[ecx]←M[R[ebp]+20],将y h 送ECX imull %eax, %ecx //R[ecx]←R[ecx]*R[eax],将y h *x的低32位送ECX mull 16(%ebp) //R[edx]R[eax]←M[R[ebp]+16]*R[eax],将y l *x送EDX-EAX (%ecx, %edx), %edx

// R[edx]←R[ecx]+R[edx],将y l *x的高32位与y h *x的低32位相加后送EDX

movl 8(%ebp), %ecx //R[ecx]←M[R[ebp]+8],将d 送ECX movl %eax, (%ecx) //M[R[ecx]]←R[eax],将x*y低32位送d 指向的低32位 movl %edx, 4(%ecx) //M[R[ecx]+4]←R[edx],将x*y高32位送d 指向的高32位 11.参考答案:

根据第3.3.4节得知,条件转移指令都采用相对转移方式在段内直接转移,即条件转移指令的转移目标地址为:(PC )+偏移量。

(1)因为je 指令的操作码为01110100,所以机器代码7408H 中的08H 是偏移量,故转移目标地址为:0x804838c+2+0x8=0x8048396。

call 指令中的转移目标地址0x80483b1=0x804838e+5+0x1e,由此,可以看出,call 指令机器代码中后面的4个字节是偏移量,因IA-32采用小端方式,故偏移量为0000001EH 。call 指令机器代码共占5个字节,因此,下条指令的地址为当前指令地址0x804838e 加5。

(2)jb 指令中F6H 是偏移量,故其转移目标地址为:0x8048390+2+0xf6=0x8048488。

movl 指令的机器代码有10个字节,前两个字节是操作码等,后面8个字节为两个立即数,因为是小端方式,所以,第一个立即数为0804A800H ,即汇编指令中的目的地址0x804a800,最后4个字节为立即数00000001H ,即汇编指令中的常数0x1。

leal

(3)jle 指令中的7EH 为操作码,16H 为偏移量,其汇编形式中的0x80492e0是转移目的地址,因此,假定后面的mov 指令的地址为x ,则x 满足以下公式:0x80492e0=x +0x16,故x =0x80492e0-0x16=0x80492ca。

(4)jmp 指令中的E9H 为操作码,后面4个字节为偏移量,因为是小端方式,故偏移量为FFFFFF00H ,即-100H=-256。后面的sub 指令的地址为0x804829b ,故jmp 指令的转移目标地址为0x804829b+0xffffff00=0x804829b-0x100=0x804819b。 12.参考答案:

(1)汇编指令的注解说明如下:

1 movb 8(%ebp), %dl //R[dl]←M[R[ebp]+8],将x 送DL 2 movl 12(%ebp), %eax //R[eax]←M[R[ebp]+12],将p 送EAX 3 testl %eax, %eax //R[eax] and R[eax],判断p 是否为0 4 je .L1 //若p 为0,则转.L1执行 5 testb $0x80, %dl //R[dl] and 80H,判断x 的第一位是否为0 6 je .L1 //若x >=0,则转.L1执行 7 addb %dl, (%eax) //M[R[eax]]←M[R[eax]]+R[dl],即*p+=x

8 .L1:

因为C 语言if 语句中的条件表达式可以对多个条件进行逻辑运算,而汇编代码中一条指令只能进行一种逻辑运算,并且在每条逻辑运算指令生成的标志都是存放在同一个EFLAGS 寄存器中,所以,最好在一条逻辑指令后跟一条条件转移指令,把EFLAGS 中标志用完,然后再执行另一次逻辑判断并根据条件进行转移的操作。 (2)按照书中图3.22给出的“if () goto …”语句形式写出汇编代码对应的C 语言代码如下: 1 void comp(char x, int *p) 2 {

3 if (p!=0) 4 if (x

13.参考答案:

1 int func(int x, int y) 2 {

3 int z = 4 if ( ) { 5 if ( ) 6 ; 7 else 8 z = ; 9 } else if ( ) 10 z = ; 11 return z; 12 }

14.参考答案:

(1)每个入口参数都要按4字节边界对齐,因此,参数x 、y 和k 入栈时都占4个字节。 1 movw 8(%ebp), %bx //R[bx]←M[R[ebp]+8],将x 送BX 2 movw 12(%ebp), %si //R[si]←M[R[ebp]+12],将y 送SI 3 movw 16(%ebp), %cx //R[cx]←M[R[ebp]+16],将k 送CX

4 .L1:

5 movw %si, %dx 6 movw %dx, %ax 7 sarw $15, %dx 8 idiv %cx 9 imulw %dx, %bx 10 decw %cx 11 testw %cx, %cx 12 jle .L2 13 cmpw %cx, %si 14 jg .L1

//R[dx]←R[si],将y 送DX //R[ax]←R[dx],将y 送AX

//R[dx]←R[dx]>>15,将y 的符号扩展16位送DX //R[dx]←R[dx-ax]÷R[cx]的余数,将y %k 送DX //R[ax]←R[dx-ax]÷R[cx]的商,将y /k 送AX //R[bx]←R[bx]*R[dx],将x *(y %k ) 送BX //R[cx]←R[cx]-1,将k -1 送CX

//R[cx] and R[cx],得OF=CF=0,负数则SF=1,零则ZF=1 //若k 小于等于0,则转.L2

//R[si] - R[cx],将y 与k 相减得到各标志 //若y 大于k ,则转.L1

15 .L2:

16 movswl %bx, %eax // R[eax]←R[bx],将x *(y %k ) 送AX

(2)被调用者保存寄存器有BX 、SI ,调用者保存寄存器有AX 、CX 和DX 。

在该函数过程体前面的准备阶段,被调用者保存的寄存器EBX 和ESI 必须保存到栈中。

(3)因为执行第8行除法指令前必须先将被除数扩展为32位,而这里是带符号数除法,因此,采用算术右移以扩展16位符号,放在高16位的DX 中,低16位在AX 中。 15.参考答案:

1 int f1(unsigned x)

2 {

3 int y = 0 ; 4 5 ; 6 7 } 8 ; 9 }

函数f1的功能返回:( x ^ x>>1 ^ x>>2 ^ ….) & 0x1,因此f1用于检测x 的奇偶性,当x 中有奇数个1,则返回为1,否则返回0。 16.参考答案:

函数sw 只有一个入口参数x ,根据汇编代码的第2~5行指令知,当x +3>7时转标号.L7处执行,否则,按照跳

转表中的地址转移执行,x 与跳转目标处标号的关系如下: x +3=0:.L7 x +3=1:.L2 x +3=2:.L2

x +3=3:.L3 x +3=4:.L4 x +3=5:.L5 x +3=6:.L7 x +3=7:.L6

由此可知,switch (x) 中省略的处理部分结构如下: case -2: case -1: …… // .L2标号处指令序列对应的语句 break;

case 0:

……

break; case 1: …….

break; case 2: …… break; case 4: ……

break; default: ……

// .L3标号处指令序列对应的语句

// .L4标号处指令序列对应的语句 // .L5标号处指令序列对应的语句

// .L6标号处指令序列对应的语句

// .L7标号处指令序列对应的语句

17.参考答案: 根据第2、3行指令可知,参数a 是char 型,参数p 是指向short 型变量的指针;根据第4、5行指令可知,参数

b 和c 都是unsigned short型,根据第6行指令可知,test 的返回参数类型为unsigned int。因此,test 的原型为: unsigned int test(char a, unsigned short b, unsigned short c, short *p); 18.参考答案: 每次执行pushl 指令后,R[esp]=R[esp]-4,因此,第2行指令执行后R[esp]=0xbc00001c。

(1)执行第3行指令后,R[ebp]=R[esp]=0xbc00001c。到第12条指令执行结束都没有改变EBP 的内容,因而

执行第10行指令后,EBP 的内容还是为0xbc00001c 。执行第13行指令后,EBP 的内容恢复为进入函数funct 时的值0xbc000030。

(2)执行第3行指令后,R[esp]=0xbc00001c。执行第4行指令后R[esp]= R[esp]-40=0xbc00001c-0x28=0xbbfffff4。

因而执行第10行指令后,未跳转到scanf 函数执行时,ESP 中的内容为0xbbfffff4-4=0xbbfffff0;在从scanf 函数返回后ESP 中的内容为0xbbfffff4。执行第13行指令后,ESP 的内容恢复为进入函数funct 时的旧值,即R[esp]=0xbc000020。

(3)第5、6两行指令将scanf 的第三个参数&y入栈,入栈的内容为R[ebp]-8=0xbc000014;第7、8两行指令

将scanf 的第二个参数&x入栈,入栈的内容为R[ebp]-4=0xbc000018。故x 和y 所在的地址分别为0xbc000018和0xbc000014。

(4)执行第10行指令后,funct 栈帧的地址范围及其内容如下:

0xbc00001c 0xbc0000180xbc0000140xbc0000100xbc00000c 0xbc0000080xbc0000040xbc0000000xbbfffffc 0xbbfffff8 0xbbfffff4 0xbbfffff0

EBP 栈帧底部

19.参考答案:

第1行汇编指令说明参数x 存放在EBX 中,根据第4行判断x =0则转.L2,否则继续执行第5~10行指令。根据第5、6、7行指令可知,入栈参数nx 的计算公式为x >>1;根据第9、10、11行指令可知,返回值为(x &1)+rv 。由此

推断出C 缺失部分如下:

1 2 3 4 5 6

int refunc(unsigned x) { if ( ) return ; unsigned nx = ; int rv = refunc(nx) ; return ;

7 }

该函数的功能为计算x 的各个数位中1的个数。 20.参考答案:

21

22.参考答案:

根据汇编指令功能可以推断最终在EAX 中返回的值为:

M[a+28*i+4*j]+M[b+20*j+4*i],因为数组a 和b 都是int 型,每个数组元素占4B ,因此,M=5, N=7。 23.参考答案:

执行第11行指令后,a[i][j][k]的地址为a+4*(63*i+9*j+k),所以,可以推断出M=9,N=63/9=7。根据第12行指令,可知数组a 的大小为4536字节,故L=4536/(4*L*M)=18。 24.参考答案:

(1)常数M=76/4=19,存放在EDI 中,变量j 存放在ECX 中。 (2)上述优化汇编代码对应的函数trans_matrix的C 代码如下: 1 void trans_matrix(int a[M][M]) { 2 int i, j, t, *p; 3 int c=(M

6 t=*p; 7 *p = a[i][j]; 8 a[i][j] = t; 9 p += c; 10 } 11 } 12 }

25.参考答案:

(1)node 所需存储空间需要4+(4+4)+4=16字节。成员p 、s.x 、s.y 和next 的偏移地址分别为0、4、8和12。 (2)np_init中缺失的表达式如下:

void np_init(struct node *np) { ;

; ; }

26

27.参考答案: (1)S1: s c i d

0 2 4 8 总共12字节,按4字节边界对齐 (2)S2: i s c d

0 4 6 7 总共8字节,按4字节边界对齐 (3)S3: c s i d

0 2 4 8 总共12字节,按4字节边界对齐 (4)S4: s c

0 6 总共8字节,按2字节边界对齐 (5)S5: c s i d e

0 4 8 12 16 总共24字节,按4字节边界对齐(Linux 下double 型按4字节对齐) (6)S6: c s d

0 36 40 总共44字节,按4字节边界对齐 28.参考答案:

Windows 平台要求不同的基本类型按照其数据长度进行对齐。每个成员的偏移量如下:

c d i s p l g v 0 8 16 20 24 28 32 40

结构总大小为48字节,因为其中的d 和g 必须是按8字节边界对齐,所以,必须在末尾再加上4个字节,即44+4=48字节。变量长度按照从大到小顺序排列,可以使得结构所占空间最小,因此调整顺序后的结构定义如下:

double d; long long g ; int i; char *p;

long l; void *v;

short s; char c;

} test;

d g i p l v s c

0 8 16 20 24 28 32 34 结构总大小为34+6=40字节。 29.参考答案:

(1)执行第7行和第10行指令后栈中的信息存放情况如下图所示。其中gets 函数的入口参数为buf 数组首

地址,应等于getline 函数的栈帧底部指针EBP 的内容减0x14,而getline 函数的栈帧底部指针EBP 的内容应等于执行完getline 中第2行指令(push %ebp)后ESP 的内容,此时,R[esp]= =0xbffc07f0-4=0xbffc07ec,故buf 数组首地址为R[ebp]-0x14= R[esp]-0x14=0xbffc07ec-0x14=0xbffc07d8。

P 的地址

在P 中旧值 被调用者保存寄存器在P 中的旧值 ~ buf[4] ~ buf[0]

P 的地址 在P 中旧值 被调用者保存寄存器在P 中的旧值

struct {

~ buf[4] ~ buf[0]

a) 执行第7行后的栈

入口参数

b) 执行第10行后的栈

(2)当执行到getline 的ret 指令时,假如程序不发生段错误,则正确的返回地址应该是0x80485c8,发生段

错误是因为执行getline 的ret 指令时得到的返回地址为0x8413938,这个地址所在存储段可能是不可执行的数据段,因而发生了段错误(segmentation fault)。

(3)执行完第10行汇编指令后,被调用者保存寄存器EBX 、ESI 和EDI 在P 中的内容已被破坏,同时还

破坏了EBP 在P 中的内容。

(4)getline 的C 代码中malloc 函数的参数应该为strlen(buf)+1,此外,应该检查malloc 函数的返回值是否

为NULL 。

30.参考答案:

x86-64过程调用时参数传递是通过通用寄存器进行的,前三个参数所用寄存器顺序为RDI 、RSI 、RDX 。 abc 的4种合理的函数原型为: ① viod abc(int c, long *a, int *b);

② viod abc(unsigned c, long *a, int *b);

③ viod abc(long c, long *a, int *b);

④ viod abc(unsigned long c, long *a, int *b);

根据第3、4行指令可知,参数b 肯定指向一个32位带符号整数类型;根据第5行指令可知,参数a 指向

64位带符号整数类型;而参数c 可以是32位,也可以是64位,因为*b为32位,所以取RDI 中的低32位R[edi](截断为32位),再和*b相加。同时,参数c 可以是带符号整数类型,也可以是无符号整数类型,因为第2行加法指令addl 的执行结果对于带符号整数和无符号整数都一样。 31.参考答案:

(1)汇编指令注释如下: 1 movl 8(%ebp), %edx //R[edx]←M[R[ebp]+8],将x 送EDX 2 movl 12(%ebp), %ecx //R[ecx]←M[R[ebp]+12],将k 送ECX 3 movl $255, %esi //R[esi]←255,将255送ESI 4 movl $-2147483648, %edi //R[edi]←-2147483648,将0x80000000送EDI

5 .L3: 6 movl 7 andl 8 xorl 9 movl 10 shrl 11 testl 12 jne

%edi, %eax %edx, %eax %eax, %esi %ecx, %ebx %bl, %edi %edi, %edi .L3

//R[eax]←R[edi],将i 送EAX

//R[eax]←R[eax] and R[edx],将i and x 送EAX

//R[esi]←R[esi] xor R[eax],将val xor (i and x ) 送ESI //R[ebx]←R[ecx],将k 送ECX

//R[edi]←R[edi] >> R[bl],将i 逻辑右移k 位送EDI

//若R[edi]≠0,则转.L3

13 movl %esi, %eax //R[eax]←R[esi]

(2)x 和k 分别存放在EDX 和ECX 中。局部变量val 和i 分别存放在ESI 和EDI 中。 (3)局部变量val 和i 的初始值分别是255和-2147483648。

(4)循环终止条件为i 等于0。循环控制变量i 每次循环被逻辑右移k 位。 (5)C 代码中缺失部分填空如下,注意:对无符号整数进行的是逻辑右移。

1 2 3 4 5 6 7 8

int {

lproc(int x, int k)

int val = ; int i;

; }

return val;

9 }

32.参考答案:

从第5行指令可知,i 在EAX 中;从第6行指令可知,sptr 在ECX 中。由第7行指令可知,i*28在EBX

中。由第8、9和10行指令可猜出,x 的每个数组元素占28B ,并且xptr->idx的地址为sptr+i*28+4,故在line_struct中的第一个分量为idx ,因而后面的24B 为6个数组元素a[0]~a[5],类型与val 变量的类型相同,即unsigned int。 line_struct结构类型的定义如下:

typedef struct { int idx; unsigned a[6]; } line_struct;

由第11、12行指令可知,x 数组所占空间为0xc8-4=200-4=196B,所以LEN=196/28=7。

33.参考答案:

(1)n1.ptr 、n1.data1、n2.data2、n2.next 的偏移量分别是0、4、0和4。

(2)node 类型总大小占8字节。

(3)chain_proc的C 代码中缺失的表达式如下: –34.参考答案:

(1)函数trace 的入口参数tptr 通过RDI 寄存器传递。

(2)函数trace 完整的C 语言代码如下:

long trace( tree_ptr tptr)

{

long ret_val=0;

tree_ptr p=tptr;

while (p!=0) {

ret_val=p->val;

p=p->left;

}

return ret_val;

}

(3)函数trace 的功能是:返回二叉树中最左边叶子节点中的值val 。

习 题

3. 参考答案:

(1)后缀:w , 源:基址+比例变址+偏移, 目:寄存器 (2)后缀:b , 源:寄存器, 目:基址+偏移 (3)后缀:l , 源:比例变址, 目:寄存器 (4)后缀:b , 源:基址, 目:寄存器 (5)后缀:l , 源:立即数, 目:栈 (6)后缀:l , 源:立即数, 目:寄存器 (7)后缀:w , 源:寄存器, 目:寄存器 (8)后缀:l , 源:基址+变址+偏移, 目:寄存器 4.参考答案:

(1)源操作数是立即数0xFF ,需在前面加‘$’

(2)源操作数是16位,而长度后缀是字节‘b ’,不一致 (3)目的操作数不能是立即数寻址

(4)操作数位数超过16位,而长度后缀为16位的‘w ’ (5)不能用8位寄存器作为目的操作数地址所在寄存器 (6)源操作数寄存器与目操作数寄存器长度不一致 (7)不存在ESX 寄存器

(8)源操作数地址中缺少变址寄存器 5.参考答案:

6.参考答案:

(1)xptr 、yptr 和zptr 对应实参所存放的存储单元地址分别为:R[ebp]+8、R[ebp]+12、

R[ebp]+16。

(2)函数func 的C 语言代码如下: void func(int *xptr, int *yptr, int *zptr) { int tempx=*xptr; int tempy=*yptr; int tempz=*zptr;

*yptr=tempx; *zptr = tempy;

*xptr = tempz;

}

7.参考答案:

(1)R[edx]=x

(2)R[edx]=x+y+4 (3)R[edx]=x+8*y (4)R[edx]=y+2*x+12 (5)R[edx]=4*y (6)R[edx]=x+y 8.参考答案:

(1)指令功能为:R[edx]←R[edx]+M[R[eax]]=0x00000080+M[0x8049300],寄存器EDX 中内容改变。改变后的

内容为以下运算的结果:00000080H+FFFFFFF0H

0000 0000 0000 0000 0000 0000 1000 0000 + 1111 1111 1111 1111 1111 1111 1111 0000 1 0000 0000 0000 0000 0000 0000 0111 0000

因此,EDX 中的内容改变为0x00000070。根据表3.5可知,加法指令会影响OF 、SF 、ZF 和CF 标志。OF=0,ZF=0,SF=0,CF=1。

(2)指令功能为:R[ecx]←R[ecx]-M[R[eax]+R[ebx]]=0x00000010+M[0x8049400], 寄存器ECX 中内容改变。

改变后的内容为以下运算的结果:00000010H -80000008H

0000 0000 0000 0000 0000 0000 0001 0000 + 0111 1111 1111 1111 1111 1111 1111 1000 0 1000 0000 0000 0000 0000 0000 0000 1000

因此,ECX 中的内容改为0x80000008。根据表3.5可知,减法指令会影响OF 、SF 、ZF 和CF 标志。OF=1,ZF=0,SF=1,CF=1 0=1。

(3)指令功能为:R[bx]←R[bx] or M[R[eax]+R[ecx]*8+4],寄存器BX 中内容改变。改变后的内容为以下运算

的结果:0x0100 or M[0x8049384]=0100H or FF00H

0000 0001 0000 0000 or 1111 1111 0000 0000 1111 1111 0000 0000

因此,BX 中的内容改为0xFF00。由3.3.3节可知,OR 指令执行后OF=CF=0;因为结果不为0,故ZF=0;因为最高位为1,故SF=1。

(4)test 指令不改变任何通用寄存器,但根据以下“与”操作改变标志:R[dl] and 0x80

1000 0000 and 1000 0000 1000 0000

由3.3.3节可知,TEST 指令执行后OF=CF=0;因为结果不为0,故ZF=0;因为最高位为1,故SF=1。 (5)指令功能为:M[R[eax]+R[edx]]←M[R[eax]+R[edx]]*32,即存储单元0x8049380中的内容改变为以下运算

的结果:M[0x8049380]*32=0x908f12a8*32,也即只要将0x908f12a8左移5位即可得到结果。 1001 0000 1000 1111 0001 0010 1010 1000

=0001 0001 1110 0010 0101 0101 0000 0000

因此,指令执行后,单元0x8049380中的内容改变为0x11e25500。显然,这个结果是溢出的。但是,根据表3.5可知,乘法指令不影响标志位,也即并不会使OF=1。

(6)指令功能为:R[cx] ←R[cx]-1,即CX 寄存器的内容减一。

0000 0000 0001 0000 + 1111 1111 1111 1111 1 0000 0000 0000 1111

因此,指令执行后CX 中的内容从0x0010变为0x000F 。由表3.5可知,DEC 指令会影响OF 、ZF 、SF ,根据上述运算结果,得到OF=0,ZF=0,SF=0。

9.参考答案:

movl 12(%ebp), %ecx //R[ecx]←M[R[ebp]+12],将y 送ECX sall $8, %ecx //R[ecx]←R[ecx]

从汇编代码的第2行和第4行看,y 应该是占8个字节,R[ebp]+20开始的4个字节为高32位字节,记为y h ;R[ebp]+16开始的4个字节为低32位字节,记为y l 。根据第4行为无符号数乘法指令,得知y 的数据类型num_type为unsigned long long。

movl 12(%ebp), %eax //R[eax]←M[R[ebp]+12],将x 送EAX movl 20(%ebp), %ecx //R[ecx]←M[R[ebp]+20],将y h 送ECX imull %eax, %ecx //R[ecx]←R[ecx]*R[eax],将y h *x的低32位送ECX mull 16(%ebp) //R[edx]R[eax]←M[R[ebp]+16]*R[eax],将y l *x送EDX-EAX (%ecx, %edx), %edx

// R[edx]←R[ecx]+R[edx],将y l *x的高32位与y h *x的低32位相加后送EDX

movl 8(%ebp), %ecx //R[ecx]←M[R[ebp]+8],将d 送ECX movl %eax, (%ecx) //M[R[ecx]]←R[eax],将x*y低32位送d 指向的低32位 movl %edx, 4(%ecx) //M[R[ecx]+4]←R[edx],将x*y高32位送d 指向的高32位 11.参考答案:

根据第3.3.4节得知,条件转移指令都采用相对转移方式在段内直接转移,即条件转移指令的转移目标地址为:(PC )+偏移量。

(1)因为je 指令的操作码为01110100,所以机器代码7408H 中的08H 是偏移量,故转移目标地址为:0x804838c+2+0x8=0x8048396。

call 指令中的转移目标地址0x80483b1=0x804838e+5+0x1e,由此,可以看出,call 指令机器代码中后面的4个字节是偏移量,因IA-32采用小端方式,故偏移量为0000001EH 。call 指令机器代码共占5个字节,因此,下条指令的地址为当前指令地址0x804838e 加5。

(2)jb 指令中F6H 是偏移量,故其转移目标地址为:0x8048390+2+0xf6=0x8048488。

movl 指令的机器代码有10个字节,前两个字节是操作码等,后面8个字节为两个立即数,因为是小端方式,所以,第一个立即数为0804A800H ,即汇编指令中的目的地址0x804a800,最后4个字节为立即数00000001H ,即汇编指令中的常数0x1。

leal

(3)jle 指令中的7EH 为操作码,16H 为偏移量,其汇编形式中的0x80492e0是转移目的地址,因此,假定后面的mov 指令的地址为x ,则x 满足以下公式:0x80492e0=x +0x16,故x =0x80492e0-0x16=0x80492ca。

(4)jmp 指令中的E9H 为操作码,后面4个字节为偏移量,因为是小端方式,故偏移量为FFFFFF00H ,即-100H=-256。后面的sub 指令的地址为0x804829b ,故jmp 指令的转移目标地址为0x804829b+0xffffff00=0x804829b-0x100=0x804819b。 12.参考答案:

(1)汇编指令的注解说明如下:

1 movb 8(%ebp), %dl //R[dl]←M[R[ebp]+8],将x 送DL 2 movl 12(%ebp), %eax //R[eax]←M[R[ebp]+12],将p 送EAX 3 testl %eax, %eax //R[eax] and R[eax],判断p 是否为0 4 je .L1 //若p 为0,则转.L1执行 5 testb $0x80, %dl //R[dl] and 80H,判断x 的第一位是否为0 6 je .L1 //若x >=0,则转.L1执行 7 addb %dl, (%eax) //M[R[eax]]←M[R[eax]]+R[dl],即*p+=x

8 .L1:

因为C 语言if 语句中的条件表达式可以对多个条件进行逻辑运算,而汇编代码中一条指令只能进行一种逻辑运算,并且在每条逻辑运算指令生成的标志都是存放在同一个EFLAGS 寄存器中,所以,最好在一条逻辑指令后跟一条条件转移指令,把EFLAGS 中标志用完,然后再执行另一次逻辑判断并根据条件进行转移的操作。 (2)按照书中图3.22给出的“if () goto …”语句形式写出汇编代码对应的C 语言代码如下: 1 void comp(char x, int *p) 2 {

3 if (p!=0) 4 if (x

13.参考答案:

1 int func(int x, int y) 2 {

3 int z = 4 if ( ) { 5 if ( ) 6 ; 7 else 8 z = ; 9 } else if ( ) 10 z = ; 11 return z; 12 }

14.参考答案:

(1)每个入口参数都要按4字节边界对齐,因此,参数x 、y 和k 入栈时都占4个字节。 1 movw 8(%ebp), %bx //R[bx]←M[R[ebp]+8],将x 送BX 2 movw 12(%ebp), %si //R[si]←M[R[ebp]+12],将y 送SI 3 movw 16(%ebp), %cx //R[cx]←M[R[ebp]+16],将k 送CX

4 .L1:

5 movw %si, %dx 6 movw %dx, %ax 7 sarw $15, %dx 8 idiv %cx 9 imulw %dx, %bx 10 decw %cx 11 testw %cx, %cx 12 jle .L2 13 cmpw %cx, %si 14 jg .L1

//R[dx]←R[si],将y 送DX //R[ax]←R[dx],将y 送AX

//R[dx]←R[dx]>>15,将y 的符号扩展16位送DX //R[dx]←R[dx-ax]÷R[cx]的余数,将y %k 送DX //R[ax]←R[dx-ax]÷R[cx]的商,将y /k 送AX //R[bx]←R[bx]*R[dx],将x *(y %k ) 送BX //R[cx]←R[cx]-1,将k -1 送CX

//R[cx] and R[cx],得OF=CF=0,负数则SF=1,零则ZF=1 //若k 小于等于0,则转.L2

//R[si] - R[cx],将y 与k 相减得到各标志 //若y 大于k ,则转.L1

15 .L2:

16 movswl %bx, %eax // R[eax]←R[bx],将x *(y %k ) 送AX

(2)被调用者保存寄存器有BX 、SI ,调用者保存寄存器有AX 、CX 和DX 。

在该函数过程体前面的准备阶段,被调用者保存的寄存器EBX 和ESI 必须保存到栈中。

(3)因为执行第8行除法指令前必须先将被除数扩展为32位,而这里是带符号数除法,因此,采用算术右移以扩展16位符号,放在高16位的DX 中,低16位在AX 中。 15.参考答案:

1 int f1(unsigned x)

2 {

3 int y = 0 ; 4 5 ; 6 7 } 8 ; 9 }

函数f1的功能返回:( x ^ x>>1 ^ x>>2 ^ ….) & 0x1,因此f1用于检测x 的奇偶性,当x 中有奇数个1,则返回为1,否则返回0。 16.参考答案:

函数sw 只有一个入口参数x ,根据汇编代码的第2~5行指令知,当x +3>7时转标号.L7处执行,否则,按照跳

转表中的地址转移执行,x 与跳转目标处标号的关系如下: x +3=0:.L7 x +3=1:.L2 x +3=2:.L2

x +3=3:.L3 x +3=4:.L4 x +3=5:.L5 x +3=6:.L7 x +3=7:.L6

由此可知,switch (x) 中省略的处理部分结构如下: case -2: case -1: …… // .L2标号处指令序列对应的语句 break;

case 0:

……

break; case 1: …….

break; case 2: …… break; case 4: ……

break; default: ……

// .L3标号处指令序列对应的语句

// .L4标号处指令序列对应的语句 // .L5标号处指令序列对应的语句

// .L6标号处指令序列对应的语句

// .L7标号处指令序列对应的语句

17.参考答案: 根据第2、3行指令可知,参数a 是char 型,参数p 是指向short 型变量的指针;根据第4、5行指令可知,参数

b 和c 都是unsigned short型,根据第6行指令可知,test 的返回参数类型为unsigned int。因此,test 的原型为: unsigned int test(char a, unsigned short b, unsigned short c, short *p); 18.参考答案: 每次执行pushl 指令后,R[esp]=R[esp]-4,因此,第2行指令执行后R[esp]=0xbc00001c。

(1)执行第3行指令后,R[ebp]=R[esp]=0xbc00001c。到第12条指令执行结束都没有改变EBP 的内容,因而

执行第10行指令后,EBP 的内容还是为0xbc00001c 。执行第13行指令后,EBP 的内容恢复为进入函数funct 时的值0xbc000030。

(2)执行第3行指令后,R[esp]=0xbc00001c。执行第4行指令后R[esp]= R[esp]-40=0xbc00001c-0x28=0xbbfffff4。

因而执行第10行指令后,未跳转到scanf 函数执行时,ESP 中的内容为0xbbfffff4-4=0xbbfffff0;在从scanf 函数返回后ESP 中的内容为0xbbfffff4。执行第13行指令后,ESP 的内容恢复为进入函数funct 时的旧值,即R[esp]=0xbc000020。

(3)第5、6两行指令将scanf 的第三个参数&y入栈,入栈的内容为R[ebp]-8=0xbc000014;第7、8两行指令

将scanf 的第二个参数&x入栈,入栈的内容为R[ebp]-4=0xbc000018。故x 和y 所在的地址分别为0xbc000018和0xbc000014。

(4)执行第10行指令后,funct 栈帧的地址范围及其内容如下:

0xbc00001c 0xbc0000180xbc0000140xbc0000100xbc00000c 0xbc0000080xbc0000040xbc0000000xbbfffffc 0xbbfffff8 0xbbfffff4 0xbbfffff0

EBP 栈帧底部

19.参考答案:

第1行汇编指令说明参数x 存放在EBX 中,根据第4行判断x =0则转.L2,否则继续执行第5~10行指令。根据第5、6、7行指令可知,入栈参数nx 的计算公式为x >>1;根据第9、10、11行指令可知,返回值为(x &1)+rv 。由此

推断出C 缺失部分如下:

1 2 3 4 5 6

int refunc(unsigned x) { if ( ) return ; unsigned nx = ; int rv = refunc(nx) ; return ;

7 }

该函数的功能为计算x 的各个数位中1的个数。 20.参考答案:

21

22.参考答案:

根据汇编指令功能可以推断最终在EAX 中返回的值为:

M[a+28*i+4*j]+M[b+20*j+4*i],因为数组a 和b 都是int 型,每个数组元素占4B ,因此,M=5, N=7。 23.参考答案:

执行第11行指令后,a[i][j][k]的地址为a+4*(63*i+9*j+k),所以,可以推断出M=9,N=63/9=7。根据第12行指令,可知数组a 的大小为4536字节,故L=4536/(4*L*M)=18。 24.参考答案:

(1)常数M=76/4=19,存放在EDI 中,变量j 存放在ECX 中。 (2)上述优化汇编代码对应的函数trans_matrix的C 代码如下: 1 void trans_matrix(int a[M][M]) { 2 int i, j, t, *p; 3 int c=(M

6 t=*p; 7 *p = a[i][j]; 8 a[i][j] = t; 9 p += c; 10 } 11 } 12 }

25.参考答案:

(1)node 所需存储空间需要4+(4+4)+4=16字节。成员p 、s.x 、s.y 和next 的偏移地址分别为0、4、8和12。 (2)np_init中缺失的表达式如下:

void np_init(struct node *np) { ;

; ; }

26

27.参考答案: (1)S1: s c i d

0 2 4 8 总共12字节,按4字节边界对齐 (2)S2: i s c d

0 4 6 7 总共8字节,按4字节边界对齐 (3)S3: c s i d

0 2 4 8 总共12字节,按4字节边界对齐 (4)S4: s c

0 6 总共8字节,按2字节边界对齐 (5)S5: c s i d e

0 4 8 12 16 总共24字节,按4字节边界对齐(Linux 下double 型按4字节对齐) (6)S6: c s d

0 36 40 总共44字节,按4字节边界对齐 28.参考答案:

Windows 平台要求不同的基本类型按照其数据长度进行对齐。每个成员的偏移量如下:

c d i s p l g v 0 8 16 20 24 28 32 40

结构总大小为48字节,因为其中的d 和g 必须是按8字节边界对齐,所以,必须在末尾再加上4个字节,即44+4=48字节。变量长度按照从大到小顺序排列,可以使得结构所占空间最小,因此调整顺序后的结构定义如下:

double d; long long g ; int i; char *p;

long l; void *v;

short s; char c;

} test;

d g i p l v s c

0 8 16 20 24 28 32 34 结构总大小为34+6=40字节。 29.参考答案:

(1)执行第7行和第10行指令后栈中的信息存放情况如下图所示。其中gets 函数的入口参数为buf 数组首

地址,应等于getline 函数的栈帧底部指针EBP 的内容减0x14,而getline 函数的栈帧底部指针EBP 的内容应等于执行完getline 中第2行指令(push %ebp)后ESP 的内容,此时,R[esp]= =0xbffc07f0-4=0xbffc07ec,故buf 数组首地址为R[ebp]-0x14= R[esp]-0x14=0xbffc07ec-0x14=0xbffc07d8。

P 的地址

在P 中旧值 被调用者保存寄存器在P 中的旧值 ~ buf[4] ~ buf[0]

P 的地址 在P 中旧值 被调用者保存寄存器在P 中的旧值

struct {

~ buf[4] ~ buf[0]

a) 执行第7行后的栈

入口参数

b) 执行第10行后的栈

(2)当执行到getline 的ret 指令时,假如程序不发生段错误,则正确的返回地址应该是0x80485c8,发生段

错误是因为执行getline 的ret 指令时得到的返回地址为0x8413938,这个地址所在存储段可能是不可执行的数据段,因而发生了段错误(segmentation fault)。

(3)执行完第10行汇编指令后,被调用者保存寄存器EBX 、ESI 和EDI 在P 中的内容已被破坏,同时还

破坏了EBP 在P 中的内容。

(4)getline 的C 代码中malloc 函数的参数应该为strlen(buf)+1,此外,应该检查malloc 函数的返回值是否

为NULL 。

30.参考答案:

x86-64过程调用时参数传递是通过通用寄存器进行的,前三个参数所用寄存器顺序为RDI 、RSI 、RDX 。 abc 的4种合理的函数原型为: ① viod abc(int c, long *a, int *b);

② viod abc(unsigned c, long *a, int *b);

③ viod abc(long c, long *a, int *b);

④ viod abc(unsigned long c, long *a, int *b);

根据第3、4行指令可知,参数b 肯定指向一个32位带符号整数类型;根据第5行指令可知,参数a 指向

64位带符号整数类型;而参数c 可以是32位,也可以是64位,因为*b为32位,所以取RDI 中的低32位R[edi](截断为32位),再和*b相加。同时,参数c 可以是带符号整数类型,也可以是无符号整数类型,因为第2行加法指令addl 的执行结果对于带符号整数和无符号整数都一样。 31.参考答案:

(1)汇编指令注释如下: 1 movl 8(%ebp), %edx //R[edx]←M[R[ebp]+8],将x 送EDX 2 movl 12(%ebp), %ecx //R[ecx]←M[R[ebp]+12],将k 送ECX 3 movl $255, %esi //R[esi]←255,将255送ESI 4 movl $-2147483648, %edi //R[edi]←-2147483648,将0x80000000送EDI

5 .L3: 6 movl 7 andl 8 xorl 9 movl 10 shrl 11 testl 12 jne

%edi, %eax %edx, %eax %eax, %esi %ecx, %ebx %bl, %edi %edi, %edi .L3

//R[eax]←R[edi],将i 送EAX

//R[eax]←R[eax] and R[edx],将i and x 送EAX

//R[esi]←R[esi] xor R[eax],将val xor (i and x ) 送ESI //R[ebx]←R[ecx],将k 送ECX

//R[edi]←R[edi] >> R[bl],将i 逻辑右移k 位送EDI

//若R[edi]≠0,则转.L3

13 movl %esi, %eax //R[eax]←R[esi]

(2)x 和k 分别存放在EDX 和ECX 中。局部变量val 和i 分别存放在ESI 和EDI 中。 (3)局部变量val 和i 的初始值分别是255和-2147483648。

(4)循环终止条件为i 等于0。循环控制变量i 每次循环被逻辑右移k 位。 (5)C 代码中缺失部分填空如下,注意:对无符号整数进行的是逻辑右移。

1 2 3 4 5 6 7 8

int {

lproc(int x, int k)

int val = ; int i;

; }

return val;

9 }

32.参考答案:

从第5行指令可知,i 在EAX 中;从第6行指令可知,sptr 在ECX 中。由第7行指令可知,i*28在EBX

中。由第8、9和10行指令可猜出,x 的每个数组元素占28B ,并且xptr->idx的地址为sptr+i*28+4,故在line_struct中的第一个分量为idx ,因而后面的24B 为6个数组元素a[0]~a[5],类型与val 变量的类型相同,即unsigned int。 line_struct结构类型的定义如下:

typedef struct { int idx; unsigned a[6]; } line_struct;

由第11、12行指令可知,x 数组所占空间为0xc8-4=200-4=196B,所以LEN=196/28=7。

33.参考答案:

(1)n1.ptr 、n1.data1、n2.data2、n2.next 的偏移量分别是0、4、0和4。

(2)node 类型总大小占8字节。

(3)chain_proc的C 代码中缺失的表达式如下: –34.参考答案:

(1)函数trace 的入口参数tptr 通过RDI 寄存器传递。

(2)函数trace 完整的C 语言代码如下:

long trace( tree_ptr tptr)

{

long ret_val=0;

tree_ptr p=tptr;

while (p!=0) {

ret_val=p->val;

p=p->left;

}

return ret_val;

}

(3)函数trace 的功能是:返回二叉树中最左边叶子节点中的值val 。


相关内容

  • 第三章 会计软件的应用-系统初始化
  • 2015年会计从业资格考试内部资料 初级会计电算化 第三章 会计软件的应用 知识点:系统初始化 ● 详细描述: (一)系统初始化的特点和作用 系统初始化是系统首次使用时,根据企业的实际情况进行参数设置,并录入基础档案与初始数据的过程. 系统初始化是会计软件运行的基础.它将通用的会计软件转变为满足特定 ...

  • 信息系毕业论文
  • 南通纺织职业技术学院毕业设计(论文) 网上考试系统 XXX 班 级 05网络 专 业 计算机网络 教 学 系 信息系 指导教师 完成时间年日至 目录 ......................................................................... 3 ...

  • 第三章 需求分析习题及答案
  • 第三章 需求分析 一. 填空题 1. 需求分析的步骤 , , , . 2. 需求分析阶段需编写的文档有 , , . 3. 系统规格说明,数据要求, , ,这四份文档资料是在书写文档阶段必需完成的. 4. 在书写文档阶段,数据要求主要包括通过需求分析建立起来的 ,以及描绘数据结构的层次方框图. 5. ...

  • 以太网交换机的配置
  • 以太网交换机的配置 一.单选题(选择一项正确的答案,共10题,每题5分) 1.在交换机中用户权限分为几个级别( ). A.1 B.2 C.3 D.4 考生答案:D 具体得分:5 2.网络管理系统中,管理对象是指( ). A.网络系统中各种具体设备 B.网络系统中各种具体软件 C.网络系统中各类管理人 ...

  • 计算机基础习题集第三版答案
  • 计算机教研室 0250 以下题目重复: 第一章 一.32 第二章 一.24 第三章 一.14,18,20 二.15 第五章 一.30 第一章 计算机与信息技术概述 一. 选择题 BBCBC CDAAB CDDCA DDDAA ACBDA AAAAB DACC 二. 填空题 1. 比特 2. 无符号, ...

  • 电大职业技能实训-电子商务专业
  • 职业技能实训 1.网络实用技术基础 2.电子商务概论 3.Dreamweaver网页设计 4.网络营销与策划 5.电子商务法律法规 友情提示: 1.不保证你所有的问题这里都有答案 可以保证大部分题目都在这里面 2.选项顺序是不一样的,要看清楚答案 3.使用Ctrl+F 把需要搜的问题粘贴到到里面搜索 ...

  • 计算机系统概论_随堂练习及答案
  • 第一章 计算机系统概论 1. 大约( )年,计算机的运算速度提高10倍,成本 降为原来的1/10. A.10~12 B.5~10 C.5~8 D.6~9 参考答案:C 2. 计算机划为巨型机.大型机.中型机.小型机.微型 计算机和单片机的依据是( ) A.价格 2.体积 C.体系结构 D.整机性能 ...

  • 税法一二三章作业答案及合同撤销权与债务人行为撤销权的区别
  • 第一章,第二章答案 一.单选题: 1.关于税收实体法构成要素,下列说法不正确的是( ). A.纳税人是税法规定的直接负有纳税义务的单位和个人,是实际负担税款的单位和个人 B.征税对象是税法中规定的征纳双方权利义务共同指向的客体或标的物 C.税率是对征税对象的征收比例或征收额度,是计算税额的尺度 D. ...

  • 超多大学课后习题答案与大家分享啦~~
  • 超多大学课后习题答案与大家分享啦~~.txt男人应该感谢20多岁陪在自己身边的女人.因为20岁是男人人生的最低谷,没钱,没事业:而20岁,却是女人一生中最灿烂的季节.只要锄头舞得好,哪有墙角挖不到?2500份课后答案,很值得收藏,这里只介绍了一部分. 还有很多,可以去课后答案网(http://bbs ...

  • 第五章 操作风险管理-操作风险缓释
  • 2015年银行业专业人员职业资格考试内部资料 风险管理 第五章 操作风险管理 知识点:操作风险缓释 ● 定义: 运用缓释工具分散或者降低操作风险 ● 详细描述: 分类: 1.业务连续性管理计划(BCP) 1)对于低频高损事件 2)为实现业务连续性而制定的各类规划及实施的各项流程. 3)商业银行应定期 ...