鱼C论坛

 找回密码
 立即注册
查看: 1640|回复: 5

[已解决]为什么汇编指令在执行过程中会改变?

[复制链接]
发表于 2022-7-14 22:38:11 | 显示全部楼层 |阅读模式
30鱼币
本帖最后由 SKY_DOR/cy 于 2022-7-14 22:39 编辑

assume cs:code,ss:c,ds:b,es:a
a segment
        db 1,2,3,4,5,6,7,8
a ends
b segment
        db 1,2,3,4,5,6,7,8
b ends
c segment
        db 0,0,0,0 , 0,0,0,0
c ends
code segment

start: mov ax,a
        mov es,ax
        mov ax,c
        mov ss,ax
        mov sp,30h
        mov bx,0
        mov cx,8

     mov ax,02h
     mov ax,4c00h
     int 21h

code ends
end start
上面是我写的汇编代码
编译出来的结果 076D:0016处的指令是:mov ax,4c00h

最开始的编译结果

最开始的编译结果

ss段赋值前的指令

ss段赋值前的指令



我执行完下面的代码段之后
mov ss,ax
mov sp,30h
076D:0016处的指令变为:ADD al,[BX+SI]

ss段赋值后的指令

ss段赋值后的指令

实际执行结果.png

想了很久一直想不通是什么原因,想向大家请教一下



最佳答案
2022-7-14 22:38:12
本帖最后由 jackz007 于 2022-7-15 17:31 编辑

        根据调试信息,不难获取代码段与堆栈段所占用内存情况如下:
  1. 堆栈区域:076C0 ~ 076F0  长度 30H
  2. 代码区域: 076D0 ~ 076EB  长度 1BH
复制代码

        这里的内存地址是 20 位绝对地址
        对于 exe 文件而言,其各个段所属的内存空间是不可以发生重叠的,不难看出,在本例中,堆栈段完全覆盖了代码段,整个代码段全部位于堆栈段内,这样,当堆栈中有数据存入时,就很容易破坏代码段的数据,这就是本例代码存在的问题。
        
        又是 X 爽教的,赶快换教材吧,其实,assume 伪指令一般是不需要声明堆栈段的,在程序代码中也无需为 ss、sp 赋初值,因为 exe 文件头中有专门的字段记载它们的数值,一个 exe 在加载进内存的时候,操作系统会根据文件头的信息自动设置这些寄存器的初始数值,根本不需要用代码去设置,我们要做的,是告诉编译程序,哪个是堆栈段就可以了。

  1. assume cs:code , ds:b , es:a

  2. a segment para public 'data'
  3.         db 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8
  4. a ends

  5. b segment para public 'data'
  6.         db 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8
  7. b ends

  8. c segment para stack 'stack'   ; 这样定义的堆栈段可以被编译器自动识别
  9.         400h db dup(0)         ; 定义 400h 个字节的堆栈空间,并全部初始化为字节 00
  10. c ends

  11. code segment para public 'code'
  12. start:  mov ax,c               ; 多余指令,可以删除
  13.         mov ss,ax              ; debug 时看看 ss 在被赋值前是否已经与 ax 相同?多余指令,可以删除
  14.         mov sp,400h            ; debug 时看看 sp 在被赋值前其值是否已经是 400h?多余指令,可以删除
  15.         mov ax,a               ; 这里应该成为第一条指令
  16.         mov es,ax
  17.         mov bx,0
  18.         mov cx,8
  19.         mov ax,02h
  20.         mov ax,4c00h
  21.         int 21h
  22. code ends
  23. end start
复制代码

        这样定义堆栈段,编译的时候,就不会再出现没有堆栈的警告信息,在跟踪的时候,也不会再出现代码被修改的问题了。
        堆栈必须定义足够大,因为不止我们自己的代码在用,调用 DOS、BIOS 功能时,操作系统也在使用,定义小了会给程序埋下隐患,可能危害程序的正常运行。你的程序在为 sp 赋值后,代码被改变,可是,代码却并没有使用堆栈,是不是很奇怪?这是因为堆栈在我们并不知情的情况下,已经被操作系统使用,由于被使用的堆栈区域与代码区域重合,从而,造成代码莫名其妙被修改的后果。
        

最佳答案

查看完整内容

根据调试信息,不难获取代码段与堆栈段所占用内存情况如下: 这里的内存地址是 20 位绝对地址 对于 exe 文件而言,其各个段所属的内存空间是不可以发生重叠的,不难看出,在本例中,堆栈段完全覆盖了代码段,整个代码段全部位于堆栈段内,这样,当堆栈中有数据存入时,就很容易破坏代码段的数据,这就是本例代码存在的问题。 又是 X 爽教的,赶快换教材吧,其实,assume 伪指令一 ...
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2022-7-14 22:38:12 | 显示全部楼层    本楼为最佳答案   
本帖最后由 jackz007 于 2022-7-15 17:31 编辑

        根据调试信息,不难获取代码段与堆栈段所占用内存情况如下:
  1. 堆栈区域:076C0 ~ 076F0  长度 30H
  2. 代码区域: 076D0 ~ 076EB  长度 1BH
复制代码

        这里的内存地址是 20 位绝对地址
        对于 exe 文件而言,其各个段所属的内存空间是不可以发生重叠的,不难看出,在本例中,堆栈段完全覆盖了代码段,整个代码段全部位于堆栈段内,这样,当堆栈中有数据存入时,就很容易破坏代码段的数据,这就是本例代码存在的问题。
        
        又是 X 爽教的,赶快换教材吧,其实,assume 伪指令一般是不需要声明堆栈段的,在程序代码中也无需为 ss、sp 赋初值,因为 exe 文件头中有专门的字段记载它们的数值,一个 exe 在加载进内存的时候,操作系统会根据文件头的信息自动设置这些寄存器的初始数值,根本不需要用代码去设置,我们要做的,是告诉编译程序,哪个是堆栈段就可以了。

  1. assume cs:code , ds:b , es:a

  2. a segment para public 'data'
  3.         db 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8
  4. a ends

  5. b segment para public 'data'
  6.         db 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8
  7. b ends

  8. c segment para stack 'stack'   ; 这样定义的堆栈段可以被编译器自动识别
  9.         400h db dup(0)         ; 定义 400h 个字节的堆栈空间,并全部初始化为字节 00
  10. c ends

  11. code segment para public 'code'
  12. start:  mov ax,c               ; 多余指令,可以删除
  13.         mov ss,ax              ; debug 时看看 ss 在被赋值前是否已经与 ax 相同?多余指令,可以删除
  14.         mov sp,400h            ; debug 时看看 sp 在被赋值前其值是否已经是 400h?多余指令,可以删除
  15.         mov ax,a               ; 这里应该成为第一条指令
  16.         mov es,ax
  17.         mov bx,0
  18.         mov cx,8
  19.         mov ax,02h
  20.         mov ax,4c00h
  21.         int 21h
  22. code ends
  23. end start
复制代码

        这样定义堆栈段,编译的时候,就不会再出现没有堆栈的警告信息,在跟踪的时候,也不会再出现代码被修改的问题了。
        堆栈必须定义足够大,因为不止我们自己的代码在用,调用 DOS、BIOS 功能时,操作系统也在使用,定义小了会给程序埋下隐患,可能危害程序的正常运行。你的程序在为 sp 赋值后,代码被改变,可是,代码却并没有使用堆栈,是不是很奇怪?这是因为堆栈在我们并不知情的情况下,已经被操作系统使用,由于被使用的堆栈区域与代码区域重合,从而,造成代码莫名其妙被修改的后果。
        
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2022-7-14 23:07:33 | 显示全部楼层
1.
mov sp,30h
你的堆栈定义了几个字节?
你使用了几个字节?
你堆栈定义了8个字节
你堆栈使用了30h个字节
8 != 30h

2.
es 和 ss 设置了,为什么不设置 ds ?

3. 我承认给程序中用到的变量、函数 这些取一个有意义的名字很难,但是你取的 abcd 这名字也太不负责任了吧
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2022-7-15 08:35:28 | 显示全部楼层
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2022-7-16 14:13:22 From FishC Mobile | 显示全部楼层
jackz007 发表于 2022-7-15 14:32
根据调试信息,不难获取代码段与堆栈段所占用内存情况如下:

        这里的内存地址是 20 位绝 ...

谢谢你详细、耐心的解答!阅读你的答案后调整代码,程序顺利跑通了!也让我收获了一个全新的知识点:操作系统会对栈空间的数据进行改写
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2022-7-16 14:18:35 From FishC Mobile | 显示全部楼层
人造人 发表于 2022-7-14 23:07
1.
mov sp,30h
你的堆栈定义了几个字节?

谢谢你的回答!你指出的问题是准确的
因为我在定位是哪句代码执行时导致问题的产生,所以就删了一部分不影响问题出现的语句,包括对ds的赋值
感谢你的提醒!我很赞同你的建议,在写程序应当取一个合适的名称。不过,因为我是做练习,对段的命名也是照抄书本,所以没特别留意。但是还是感谢你给予的提醒!
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-4-20 15:49

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表