- 博客(12)
- 收藏
- 关注
原创 汇编语言入门——3.3 栈帧
仔细看这段代码,所有引用内存地址都是使用EBP寄存器,其中两个局部变量[EBP-4]和[EBP-8],除了[EBP-4]作为临时变量tmp外,还有一个[EBP-8]临时存放返回值,最终统一在L1的位置执行RET返回,好处在于不必像我们前面写的程序中重复两次RET(C编译器的习惯一般生成的代码中只有一个RET),不过对初学者而言看着逻辑就没那么直观了,瑕瑜互见吧。这样一来,所有的局部变量都写作[EBP-x]的形式,所有的参数都写用[EBP+x]的形式,一目了然,看着非常清楚。以此类推,栈的结构就很清晰了。
2026-08-09 16:44:48
217
原创 汇编语言入门——3.2 C语言与汇编语言的链接
当然事实上在相互调用的例子里,不仅可以用C语言写main函数,写在汇编语言中也是可以的,只要确保有且仅有一个main函数交给链接器就能正确链接(这是同样的道理,链接器不知道obj文件是由什么原始语言写的,只需要obj文件符合C语言的规范即可)。事实上无论是main函数的地址,asm_puts函数的地址,乃至LC0的地址,nasm都无从得知,因为这是独立编译的,nasm不可能知道将来这些函数或变量会放置在哪个偏移位置,编译器只有把它暂时设置为0,而最终这些信息都会被交给链接器处理,安排到内存中合理的位置。
2026-08-09 16:36:17
192
原创 汇编语言入门——3.1 C库函数调用
由于函数参数会被PUSH入栈,在main函数调用时两个变量各自都被拷贝一个副本到栈中,从swap函数方的角度看来,函数交换的只是栈中的副本,而没有交换原始的变量。对于swap函数(也就是没用指针的版本),其所有操作都是交换栈中的变量[ESP+4]和[ESP+8]的值,而由于栈中的两个变量是传递参数时被推入栈的(PUSH操作本质是一次SUB ESP,4和一次拷贝赋值),原变量保存在哪里这回事这个swap函数都不清楚,自然交换原变量的值就无从谈起了。这样返回的时候,读取的[ESP]就是正确的返回地址了。
2026-06-06 11:27:43
225
原创 汇编语言入门——2.5 递归函数
*在C语言中没有这个问题,因为上述的C代码中定义的都是局部变量,局部变量每调用一层都会复制一份,不同的函数调用的局部变量是不同的。——而在汇编语言中,寄存器是全局可见的,上一层级中的寄存器EBX和ECX,递归到下一层级中可能遭到改写,而上一层级不知道这一变化,处理结果数字就不对了,轻则输出错误结果,重则陷入无限递归,栈空间耗尽(上述的例子就是因为栈异常被操作系统干掉了)函数调用的返回地址是保存在栈中的,因为栈是后进先出的,和递归函数后调用的先返回,恰好是一致的。把上面的程序改写成汇编语言,就容易得多了。
2026-02-17 21:33:39
649
原创 汇编语言入门——2.4 函数调用基础
现在程序回到了main中,下一条指令是main函数的最后一个RET——如果再单步执行RET,下一步程序会弹出现在栈顶的0x4013e2作为新的EIP,程序返回到了一个我们不认识的地方__tmainCRTStartup,是C编译器为我们的程序做的一些准备工作与返回工作而链接的,有兴趣的读者可以自行查阅资料。CPU执行CALL和RET的时候,保存和读取EIP使用的就是栈,当然这个栈和数据结构中所说的栈不太一样,它是指内存中的一块区域,是硬件层面上的栈。在之前,我们的整个汇编代码都是只有一个main函数的。
2026-02-11 19:05:06
894
原创 汇编语言入门——2.3 流程控制(2)循环结构
汇编语言提供的指令是很有限的,毕竟作为机器语言一一对应的语言,所有指令都是直接调用CPU中的电路,因此单条指令能做的事情非常有限,复杂的逻辑结构都是通过各种基本指令花里胡哨的组合来实现的——各位可能在前面几节尚还体会不到,在流程控制这两节里一定能体会到了。要优化的话,ECX的值可以每次+4而不是+1,但即便如此,循环中还要反复地进行EBX+ECX的加法运算,而数组的基址EBX是确定的,好像这个加法运算没有执行的必要…流程控制的内容,到此就全部结束了,相比前面的章节而言,流程控制的难度要稍高些。
2026-02-09 18:43:50
896
原创 汇编语言入门——2.2 流程控制(1)分支结构
让EIP的值增大,使用常规的MOV语句行不通(像MOV一类的转送指令与ADD一类的演算指令不能对EIP进行操作),大概因为设计CPU的人认为EIP是一个非常特殊的寄存器,更改它的效果和更改其它寄存器产生的效果不一样吧。上述程序中,EAX=50,执行比较指令后发现50并非小于等于10,故不执行跳转,按照顺序执行下去,运行的结果是40;我们想实现的是“当a>10时,执行a-=10“——但在汇编语言中,没有”条件执行“,只有”条件跳转“,因此我们要转化一下逻辑,变成”当a<=10时,跳过中间的a-=10“。
2026-02-08 20:04:10
570
原创 汇编语言入门——2.1 内存地址
我们编写的程序本身都是放在内存中的(exe文件通常存储在磁盘中,但在运行的时候操作系统必须将其读入内存,存储的机器语言才能被执行),CPU执行机器语言的时候,就是一条一条指令地从内存中读取到CPU中,然后再执行的。之前讲虚拟地址的时候说过,操作系统对不同的段权限有所设置,应用程序访问内存地址的时候需要由操作系统和CPU把关,0x1000和0x1004地址很有可能存放着一些重要信息,我们的应用程序没有访问0x1000和0x1004地址的权限,于是被操作系统干掉了。之前几节中,我们的计算都在寄存器中进行的。
2026-02-07 18:49:19
802
原创 汇编语言入门——1.3 常用演算指令(二)
C语言中除法是向0取整的,表达式7/4的结果是1,-7/4的结果不是-2而是-1。EAX+3后,变成-4算术右移两位,结果就是-1,EAX+3的作用就是对结果进行向上取整。EAX保存的低32位,在后续没有被用到,EDX保存的则是乘积的高32位,也就是。很显然,核心处理就是SAR EAX,2一句将EAX右移两位,相当于进行除以4的操作,位运算的速度比除法快得多,C编译器如此优化就可以提升程序运行速度。至于SAR和SUB语句,一样是对负数的处理,保证对负数进行向0取整,和上面的逻辑很相似,笔者就不多说明了。
2026-02-07 18:26:38
869
原创 汇编语言入门——1.3 常用演算指令(一)
本质上,补码的环与无符号数的环相差了一个256,由于数据的回环溢出,计算出来的结果恰好是等价的。也因此,汇编语言中的指令都是实现一些最基础的功能,复杂的流程与逻辑(例如循环)都是靠这些基本的指令堆叠出来的。多个字节的数据,如16位或者32位的整数也是类似的。汇编中的除法是64位的,即将EDX看作除数的高32位,EAX看作除数的低32位。话说回来,CPU的本质就是一个处理电信号的工具,内部存在很多的计算电路,汇编语言可以直接调用演算指令来控制这些电路进行计算,ADD指令就属于演算指令中的加法指令。
2026-02-06 19:00:13
657
原创 汇编语言入门——1.2 NASM, 32位汇编入门
对上一章中像INT中断等操作系统向的内容比较感兴趣的同学,笔者推荐一本书《30天自制操作系统》,有关操作系统开发的,也有不少关于汇编语言的记述。鉴于16位汇编实在不常用,本节起我们就直接开始32位汇编。写32位汇编自然就不能用之前那个debug编译器了,那个debug也太难用,不适合作开发工作。我们使用一个叫做nasm的编译器。只需要下载下面的那个zip文件,解压后放在一个文件夹内(所在的路径不要有中文)。然后点一下上面的地址栏,就可以复制上面的地址了。复制地址是为了配置环境变量。
2026-02-05 21:27:44
822
原创 汇编语言入门——1.1 初识16位汇编
这里的AH,指的是AX寄存器的高8位,下一句的AL,指的是AX寄存器的低8位。以上两句,第一句代表在你的DOS虚拟环境中挂载一个虚拟磁盘A盘,对应实机中的D:\asm目录,以后在DOS虚拟环境中操作A盘就是操作实机中的这个目录。如果手头有32位的win7或者xp,这些较早的系统是兼容DOS环境的,可以直接在命令提示符(WIN+R,输入cmd)中运行debug。显示的0B0B:0100是一个地址,冒号前面的段号可能每次打开都有所不同,后面的0100是一样的,表示我们编写的程序会保存在内存的这个地址中。
2026-02-05 21:02:12
602
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人
RSS订阅