Linux基本反汇编结构与GDB入门
(转载)gdb反汇编

(转载)gdb反汇编(转载)这⾥详细讨论⼀下disassemble/disass命令GDB⽂档:* 反汇编⼀个函数disass func_name*反汇编⼀段内存地址, 第1个参数是起始地址,第2个是终⽌地址disassemble 0×0 0×10* info line 命令来映射⼀个源码⾏到程序地址,然后使⽤命令disassemble显⽰⼀个地址范围的机器指令。
例1. 查看main函数从哪⾥开始(gdb) info line mainLine 34 of “rank.c” starts at address 0×804847fand ends at 0×8048493 .例2.(gdb) info line *0×804847fLine 34 of “rank.c” starts at address 0×804847fand ends at 0×8048493 .info line会修改 x/i 命令的默认的起始地址* disassemble不带参数,默认的反汇编范围是所选择帧的pc附近的函数单个参数, 就是pc, 当然也可以是函数名,因为函数名也是⼀个地址; 这样范围就是该pc附近的函数两个参数,就是内存地址范围* set disassembly-flavor intel将汇编指令格式设置为intel格式,默认是att(gdb) show disassembly-flavorThe disassembly flavor is “att”.* 使⽤x查看反汇编指令x/3i $pc显⽰pc开始的3条指令。
Linux 汇编语言开发指南

二、Linux 汇编语法格式绝大多数 Linux 程序员以前只接触过DOS/Windows 下的汇编语言,这些汇编代码都是 Intel 风格的。
但在 Unix 和 Linux 系统中,更多采用的还是 AT&T 格式,两者在语法格式上有着很大的不同:1.在 AT&T 汇编格式中,寄存器名要加上 '%' 作为前缀;而在 Intel 汇编格式中,寄存器名不需要加前缀。
例如:2.在 AT&T 汇编格式中,用 '$' 前缀表示一个立即操作数;而在 Intel 汇编格式中,立即数的表示不用带任何前缀。
例如:3.AT&T 和 Intel 格式中的源操作数和目标操作数的位置正好相反。
在Intel 汇编格式中,目标操作数在源操作数的左边;而在 AT&T 汇编格式中,目标操作数在源操作数的右边。
例如:4.在 AT&T 汇编格式中,操作数的字长由操作符的最后一个字母决定,后缀'b'、'w'、'l'分别表示操作数为字节(byte,8 比特)、字(word,16 比特)和长字(long,32比特);而在 Intel 汇编格式中,操作数的字长是用 "byte ptr" 和 "word ptr" 等前缀来表示的。
例如:5.在 AT&T 汇编格式中,绝对转移和调用指令(jump/call)的操作数前要加上'*'作为前缀,而在 Intel 格式中则不需要。
6.远程转移指令和远程子调用指令的操作码,在 AT&T 汇编格式中为"ljump" 和 "lcall",而在 Intel 汇编格式中则为 "jmp far" 和 "call far",即:7.与之相应的远程返回指令则为:8.在 AT&T 汇编格式中,内存操作数的寻址方式是section:disp(base, index, scale)而在 Intel 汇编格式中,内存操作数的寻址方式为:section:[base + index*scale + disp]由于 Linux 工作在保护模式下,用的是 32 位线性地址,所以在计算地址时不用考虑段基址和偏移量,而是采用如下的地址计算方法:disp + base + index * scale下面是一些内存操作数的例子:三、Hello World!真不知道打破这个传统会带来什么样的后果,但既然所有程序设计语言的第一个例子都是在屏幕上打印一个字符串 "Hello World!",那我们也以这种方式来开始介绍 Linux 下的汇编语言程序设计。
韦东山Linux视频课程介绍_可达目标_课程表

类别描述A 想深入了解嵌入式开发或是觉得学校课程讲得不深入、不贴近实际的专科、本科、研究生B 想从单片机开发转入嵌入式开发的工程师C 不希望局限在上层,想深入了解整个系统的工程师D 想成为系统工程师、软件框架设计师的程序员E想从硬件开发转为软件开发的工程师操作系统Linux基本操作-会使用若干条基本命令即可开发语言基本的C语言知识,汇编不要求(视频里有介绍汇编,就几条而已)硬件知识能看得懂原理图最好,不会也没关系(视频里有专门一课)课程名称:韦东山Linux视频第1期、第2期课程针对人群学习课程的能力要求课程掌握后的能力值概述(技能目标)裸板操作及bootloader深入理解ARM体系统架构,可以写出具备中断功能的裸板程序,对程序现场的保存、恢复有所了解,这些原理适用于任何架构CPU,适用于内核及应用程序掌握常用的硬件部件的操作,比如GPIO,UART,I2C,LCD,触摸屏;可以写出这些硬件部件的裸板程序掌握写汇编代码的能力,可以分析任意裸板包括u-boot、内核里的相关汇编代码能自己写出2期视频里讲到的各种驱动,它们已经涉及了工作中的绝大部分驱动掌握系统级别的应用调试方法:使用工具,修改内核自制工作掌握驱动调试方法:打印,自制打印,分析oops信息,栈回溯,理解内核原理然后修改内核,自制调试工具掌握驱动程序中各种操作:休眠-唤醒, 同步互斥, 异步通知, 定时器,上下半部等学习Linux驱动的套路:分配-设置-注册,掌握驱动的分析方法调试手段掌握裸板调试方法:点灯、打印、用JTAG工具进行源码级别的调试,查看反汇编熟悉裸板程序的结构,给你一个bootloader就能分析它的初始化部分、内存使用情况、链接地址、重定位、程序的相对跳转/绝对跳转等掌握bootloader启动内核的原理,能完全自己写出一个bootloader内核移植及启动过程可以配置、移植一个全新的内核了解内核的启动过程,解决启动中碰到的问题了解内核的基本原理:进程调度、系统调用的过程、内存管理等与程序开发密切相关的知识驱动程序开发掌握三大类驱动程序的框架:字符设备驱动、块设备驱动、网卡驱动分析内核启动的第1个进程的运行过程,进而构造根文件系统课程创新价值讲师及简介授课形式及服务配套更新频率项目三:电源管理后续课程:第3期项目开发实战(跟第2期是分开销售的)Linux系统工程师:再加上些应用开发的经验,很容易成为系统架构师<嵌入式Linux应用开发完全手册>可以说是最好的嵌入式Linux入门书将在6月1号发布,录完1节发布1节;未录制完提前购买价格180元;录制完后购买价格280已经有4个超级QQ群:173968594,49256475,177535949,176512363学完后具备相当于1-2年Linux底层开发的实际工作经验主讲:韦东山2003年毕业于中国科学技术大学,电子专业、软件专业双学位。
Linux 驱动调试 linux driver debug

movl
$0x0,(%rax)
• • • • • • • • • • • • • • • • • • •
3.使用 objdump 反汇编出所有的信息 查看: [root@localhost ~]# objdump -d apioops > log …………………………………. 0000000000400498 <main>: 400498: 55 push %rbp 400499: 48 89 e5 mov %rsp,%rbp 40049c: 48 83 ec 10 sub $0x10,%rsp 4004a0: 89 7d fc mov %edi,0xfffffffffffffffc(%rbp) 4004a3: 48 89 75 f0 mov %rsi,0xfffffffffffffff0(%rbp) 4004a7: be c8 05 40 00 mov $0x4005c8,%esi 4004ac: bf cb 05 40 00 mov $0x4005cb,%edi 4004b1: b8 00 00 00 00 mov $0x0,%eax 4004b6: e8 dd fe ff ff callq 400398 <printf@plt> 4004bb: b8 00 00 00 00 mov $0x0,%eax 4004c0: c7 00 00 00 00 00 movl $0x0,(%rax) 4004c6: c9 leaveq 4004c7: c3 retq 4004c8: 90 nop …………………………………………
kernelsourcexxxrpm自己编译的kernelsource7ssh工具sshsecureshell用于windows系统与linux系统之间的文件传输8串口工具用于调试拿log信息windos下用超级终端或者securecrtlinux下用minicomckermit12printkdefinekernemergdefinekernalertactionmusttakenimmediatelydefinekerncritcriticalconditionsdefinekernerrerrorconditionsdefinekernwarningwarningconditionsdefinekernnoticesignificantconditiondefinekerninfodefinekerndebugdebuglevelmessages通过procsyskernelprintk文件可以调节printk的输出级别同时设置grubconfkernel这一行加上13oops和panic131apioopsdebug1311
反汇编入门教程(适合新手看)

反汇编入门教程(适合新手看)一、反汇编基础知识1.反汇编的目的反汇编的目的在于研究和修改程序代码。
将二进制代码转换为汇编代码后,我们可以理解程序的操作和逻辑,更容易进行代码分析和修改。
2.反汇编的工具常用的反汇编工具有IDAPro,0llyDbg等。
它们可以在不运行程序的情况下分析程序的代码,帮助我们理解程序的逻辑和结构。
3.反汇编的方法反汇编可以分为静态反汇编和动态反汇编两种方法。
静态反汇编是通过分析二进制文件进行反汇编,而动态反汇编则是基于程序运行时的反汇编。
两种方法各有优缺点,需要根据实际情况选择合适的方法。
二、反汇编的入门流程反汇编需要一定的汇编和计算机基础知识,下面是反汇编的入门流程:1.准备工作首先需要安装反汇编工具,如IDAPro软件,并准备一份需要分析的程序。
2.打开程序打开需要分析的程序,可以用IDAPro软件进行打开。
在打开程序时,选择正确的文件格式和架构,以便正确解析二进制代码。
3.分析程序打开程序后,就可以对程序进行分析。
首先是可执行文件的头部分析,这一部分包括程序入口点,文件大小等信息。
然后是代码分杉逐段分析程序,查找代码中有用的信息。
4.反汇编代码在分析程序代码后,就可以使用反汇编工具将代码转换为汇编代码并进一步分析代码逻辑和结构。
在IDAPro软件中,选择需要反汇编的代码段,然后点击反汇编按钮即可。
5.理解代码反汇编后,我们需要理解汇编代码的含义和逻辑,以便进一步分析代码和修改程序。
这需要一定的汇编和计算机基础知识。
三、反汇编常用技巧1.查找字符串和函数反汇编时,可以根据关键字查找字符串和函数。
在IDAPro软件中使用搜索功能进行查找,可以快速定位到对应的代码段。
2.重命名变量和函数反汇编时,如果代码中的变量或函数名称不具有可读性,可以使用重命名功能对它们进行重命名。
在IDAPro软件中,可以选定代码段,然后使用重命名功能进行重命名。
3.调试程序反汇编时,可以使用调试功能来检查程序的行为和操作。
想要成为Linux底层驱动开发高手这些技巧绝对不能错过

想要成为Linux底层驱动开发高手这些技巧绝对不能错过对于想要成为Linux底层驱动开发高手的人来说,掌握一些关键技巧是非常重要的。
本文将介绍一些不能错过的技巧,帮助读者提升自己在Linux底层驱动开发领域的能力。
1. 深入理解Linux内核:在成为Linux底层驱动开发高手之前,你需要对Linux内核有深入的理解。
了解内核的基本概念、代码结构和内核模块之间的关系是非常重要的。
阅读Linux内核的源代码、参与内核邮件列表的讨论以及阅读相关的文献资料都是提升自己技能的好途径。
2. 熟悉底层硬件知识:作为底层驱动开发者,你需要熟悉底层硬件的工作原理。
这包括了解处理器架构、设备的寄存器操作、中断处理等。
掌握底层硬件知识可以帮助你编写高效、稳定的驱动程序。
3. 学习使用适当的开发工具:在Linux底层驱动开发中,使用适当的开发工具是非常重要的。
例如,使用调试器可以帮助你快速定位驱动程序中的问题。
掌握使用GCC编译器、GNU调试器(GDB)和性能分析工具(如OProfile)等工具可以提高你的开发效率。
4. 阅读相关文档和源代码:Linux底层驱动开发涉及到大量的文档和源代码。
阅读设备供应商提供的文档、Linux内核源代码以及其他相关文献资料可以帮助你更好地了解特定设备的工作原理和使用方法。
5. 编写清晰、高效的代码:编写清晰、高效的代码对于成为Linux底层驱动开发高手是至关重要的。
使用良好的编码风格、注释和命名规范可以提高代码的可读性。
此外,了解Linux内核的设计原则和最佳实践也是编写高质量驱动程序的关键。
6. 多实践、调试和优化:在实际开发过程中,积累经验是非常重要的。
通过多实践、调试和优化不同类型的驱动程序,你可以更好地理解Linux底层驱动开发的技巧和要点。
此外,学会使用内核调试工具和性能分析工具可以帮助你提高驱动程序的质量和性能。
7. 参与开源社区:参与开源社区是成为Linux底层驱动开发高手的好方法。
100个gdb小技巧

100个gdb小技巧(原创实用版)目录1.GDB 简介2.GDB 基本命令3.GDB 进阶命令4.GDB 高级技巧5.总结正文1.GDB 简介GDB(GNU Debugger)是一个开源的、跨平台的调试器,主要用于 C/C++程序的调试。
GDB 可以帮助程序员查找代码中的错误,理解程序运行时的状态,并对程序进行性能分析。
在嵌入式系统、Linux 系统等领域,GDB 被广泛应用。
2.GDB 基本命令GDB 的基本命令主要包括:(1)启动 GDB:使用“gdb”命令启动 GDB,并加载需要调试的程序。
(2)运行程序:使用“run”命令开始执行程序。
(3)设置断点:使用“break”命令在指定位置设置断点,以便在程序运行到该位置时暂停执行。
(4)单步执行:使用“step”或“next”命令执行一行代码,然后暂停。
(5)继续执行:使用“continue”命令继续执行程序,直到遇到下一个断点或程序结束。
(6)退出 GDB:使用“quit”命令退出 GDB。
3.GDB 进阶命令GDB 的进阶命令可以让我们更深入地了解程序的运行状态,主要包括:(1)查看变量值:使用“print”命令查看指定变量的值。
(2)调用函数:使用“call”命令调用函数。
(3)查看栈信息:使用“backtrace”或“bt”命令查看当前栈的调用情况。
(4)修改变量值:使用“set”或“store”命令修改变量的值。
(5)执行表达式:使用“expression”命令执行一个表达式,并显示结果。
4.GDB 高级技巧GDB 还提供了一些高级技巧,可以帮助我们更高效地进行调试:(1)使用条件断点:使用“break”命令结合条件,仅在满足条件时暂停程序。
(2)使用监视表达式:使用“watch”命令监视一个表达式,当表达式的值发生变化时,会自动暂停程序。
(3)使用命令行历史:使用“history”命令查看并执行之前的命令。
(4)使用脚本调试:编写 GDB 脚本,实现自动化调试。
反汇编调试内核驱动Oops提示【转】

反汇编调试内核驱动Oops提⽰【转】反汇编调试内核驱动arm-none-linux-gnueabi-objdump -S kmod-demo1.o > a.txt什么是Oops?从语⾔学的⾓度说,Oops应该是⼀个拟声词。
当出了点⼩事故,或者做了⽐较尴尬的事之后,你可以说"Oops",翻译成中国话就叫做“哎呦”。
“哎呦,对不起,对不起,我真不是故意打碎您的杯⼦的”。
看,Oops就是这个意思。
在Linux内核开发中的Oops是什么呢?其实,它和上⾯的解释也没什么本质的差别,只不过说话的主⾓变成了Linux。
当某些⽐较致命的问题出现时,我们的Linux内核也会抱歉的对我们说:“哎呦(Oops),对不起,我把事情搞砸了”。
Linux内核在发⽣kernel panic时会打印出Oops信息,把⽬前的寄存器状态、堆栈内容、以及完整的Call trace都show给我们看,这样就可以帮助我们定位错误。
下⾯,我们来看⼀个实例。
为了突出本⽂的主⾓--Oops,这个例⼦唯⼀的作⽤就是造⼀个空指针引⽤错误。
#include <linux/kernel.h>#include <linux/module.h>static int__init hello_init(void){int*p = 0;*p = 1;return0;}static void__exit hello_exit(void){return;}module_init(hello_init);module_exit(hello_exit);MODULE_LICENSE("GPL");很明显,错误的地⽅就是第8⾏。
接下来,我们把这个模块编译出来,再⽤insmod来插⼊到内核空间,正如我们预期的那样,Oops出现了。
[ 100.243737] BUG: unable to handle kernel NULL pointer dereference at (null)[ 100.244985] IP: [<f82d2005>] hello_init+0x5/0x11 [hello][ 100.262266] *pde = 00000000[ 100.288395] Oops: 0002 [#1] SMP[ 100.305468] last sysfs file: /sys/devices/virtual/sound/timer/uevent[ 100.325955] Modules linked in: hello(+) vmblock vsock vmmemctl vmhgfs acpiphp snd_ens1371 gameport snd_ac97_codec ac97_bus snd_pcm_oss snd_mixer_oss snd_pcm snd_seq_dummy snd_seq_oss snd_seq_midi snd_rawmidisnd_seq_midi_event snd_seq snd_timer snd_seq_device ppdev psmouse serio_raw fbcon tileblit font bitblit softcursor sndparport_pc soundcore snd_page_alloc vmci i2c_piix4 vga16fb vgastate intel_agp agpgart shpchp lp parport floppy pcnet32 mii mptspi mptscsih mptbase scsi_transport_spi vmxnet[ 100.472178] [ 100.494931] Pid: 1586, comm: insmod Not tainted (2.6.32-21-generic #32-Ubuntu) VMware Virtual Platform[ 100.540018] EIP: 0060:[<f82d2005>] EFLAGS: 00010246 CPU: 0[ 100.562844] EIP is at hello_init+0x5/0x11 [hello][ 100.584351] EAX: 00000000 EBX: fffffffc ECX: f82cf040 EDX: 00000001[ 100.609358] ESI: f82cf040 EDI: 00000000 EBP: f1b9ff5c ESP: f1b9ff5c[ 100.631467] DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068[ 100.657664] Process insmod (pid: 1586, ti=f1b9e000 task=f137b340 task.ti=f1b9e000)[ 100.706083] Stack:[ 100.731783] f1b9ff88 c0101131 f82cf040 c076d240 fffffffc f82cf040 0072cff4 f82d2000[ 100.759324] <0> fffffffc f82cf040 0072cff4 f1b9ffac c0182340 f19638f8 f137b340 f19638c0[ 100.811396] <0> 00000004 09cc9018 09cc9018 00020000 f1b9e000 c01033ec 09cc9018 00015324[ 100.891922] Call Trace:[ 100.916257] [<c0101131>] ? do_one_initcall+0x31/0x190[ 100.943670] [<f82d2000>] ? hello_init+0x0/0x11 [hello][ 100.970905] [<c0182340>] ? sys_init_module+0xb0/0x210[ 100.995542] [<c01033ec>] ? syscall_call+0x7/0xb[ 101.024087] Code: <c7> 05 00 00 00 00 01 00 00 00 5d c3 00 00 00 00 00 00 00 00 00 00[ 101.079592] EIP: [<f82d2005>] hello_init+0x5/0x11 [hello] SS:ESP 0068:f1b9ff5c[ 101.134682] CR2: 0000000000000000[ 101.158929] ---[ end trace e294b69a66d752cb ]---Oops⾸先描述了这是⼀个什么样的bug,然后指出了发⽣bug的位置,即“IP: [<f82d2005>] hello_init+0x5/0x11 [hello]”。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Linux下的汇编与Windows汇编最大的不同就是第一个操作数是原操作数,第二个是目的操作数,而Windows下却是相反。
1、基本操作指令简单的操作数类型说明,一般有三种,(1)立即数操作数,也就是常数值。
立即数的书写方式是“$”后面跟一个整数,比如$0x1F,这个会在后面的具体分析中见到很多。
(2)寄存器操作数,它表示某个寄存器的内容,用符号Ea来表示任意寄存器a,用引用R[Ea]来表示它的值,这是将寄存器集合看成一个数组R,用寄存器表示符作为索引。
(3)操作数是存储器引用,它会根据计算出来的地址(通常称为有效地址)访问某个存储器位置。
用符号Mb[Addr]表示对存储在存储器中从地址Addr开始的b字节值的引用。
通常可以省略下标b。
图1表示有多种不同的寻址模式,一个立即数偏移Imm,一个基址寄存器Eb,一个变址或索引寄存器Ei和一个伸缩因子s。
有效地址被计算为Imm+R[Eb]+R[Ei]*s,对于这中寻址方式,我们可以在数组或者结构体中进行对元注:操作数可以是立即数值、寄存器值或是来自存储器的值,伸缩因子必须是1、2、4、或者是8。
从上面的图我们就可以大致了解操作数的类型了。
在操作指令中,最频繁使用的指令是执行数据传送的指令。
对于传送指令的两个操作数不能都指向存储器位置(我的理解是一般存储器存储的都是地址,不能够对地址和地址进行操作)。
将一个值从一个存储器位置拷到另一个存储器位置需要两条指令——第一条指令将源值加载到寄存器中,第二条将该寄存器值写入到目的位置。
下面给出源操作数和目的操作数的五种可能组合。
1、movl $0x4050, %eax 立即数——寄存器2、movl %ebp, %esp 寄存器——寄存器3、movl (%edi, %ecx), %eax 存储器——寄存器4、movl $-17, (%esp) 立即数——存储器5、movl %eax, -12(%ebp) 寄存器——存储器注意这里的指令mov可能有不同的形式,不同平台的汇编一般是有些不一样的,结合例子来进行讲解一下指令的具体操作,在这里将会正式接触到Linux下的GCC开发环境和GDB调试器,不过都是比较简单的应用。
我的Linux操作系统是Ubuntu9.10,其它版本的差别应该不大,如果我们要编写一个程序,我们可以用Linux下自带的vi或vim编辑器,studyrush@studyrush-desktop:~/C$ vi exchange.cvi 后面加我们要创建的程序文件的名字,在这里是exchange.cstudyrush@studyrush-desktop:~/C$ gcc -o exchange exchange.cgcc -o exchange exchange.c 或gcc exchange –o exchange这两者都可以对源文件进行编译,-o exchange 表示对我们要输出的文件名称,可能表达的不够准确,大家可以先熟悉一下gcc编译器,应该就会明白的了。
studyrush@studyrush-desktop:~/C$ ./exchange 点加斜线再加输出文件名就表示运行程序,下面是运行的结果。
a = 3,b = 4使用GDB可以参考附件里面的教程,这份教程写的很不错,看雪论坛本身也有,大家可以看这个贴(/showthread.php?t=77746),现在我只是把它放在附件里面。
studyrush@studyrush-desktop:~/C$ gdb exchange命令disas就表示反汇编(disassembly),后面再加上要显示的函数名,我们就可以看到函数对应的反汇编代码了。
(gdb) disas exchangeDump of assembler code for function exchange:0x080483c4 <exchange+0>: push %ebp0x080483c5 <exchange+1>: mov %esp,%ebp0x080483c7 <exchange+3>: sub $0x10,%esp0x080483ca <exchange+6>: mov 0x8(%ebp),%eax0x080483cd <exchange+9>: mov (%eax),%eax0x080483cf <exchange+11>: mov %eax,-0x4(%ebp)0x080483d2 <exchange+14>: mov 0x8(%ebp),%edx0x080483d5 <exchange+17>: mov 0xc(%ebp),%eax0x080483d8 <exchange+20>: mov %eax,(%edx)0x080483da <exchange+22>: mov -0x4(%ebp),%eax0x080483dd <exchange+25>: leave0x080483de <exchange+26>: retEnd of assembler dump.进一步来分析上面的反汇编代码,这里的代码可能与前面的讲的指令操作有一些不同,因为这是很正常的,不同的操作系统应该有所差别,这也是汇编语言一般与平台有关,移植性并不好。
push %ebpmov %esp,%ebpsub $0x10,%esp对有过反汇编的人对上面的代码肯定不会陌生,这里就是将ebp进栈,保存esp 的值,并为局部变量预留空间。
要记得源操作数与目的操作数的位置,前面有讲的。
这里也一定程度对C语言中的指针使用进行了说明。
看源代码可以知道。
mov 0x8(%ebp),%eaxmov (%eax),%eaxmov %eax,-0x4(%ebp)mov 0x8(%ebp),%eax 取得xp,其实是指针本身,即是地址,汇编表示即可为mov %ebp + 8, %eax,这里就是存储器——寄存器的操作方式。
mov (%eax),%eax 这里是取得指针指向的值,即*xp,(%eax)表示取%eax 中的地址指向的值,比如寄存器值地址值%eax 0x100 0x100 0xFF(%eax)的值就是0xFF了。
mov %eax,-0x4(%ebp) 这里是将值赋给局部变量,局部变量是在ebp的上面的,表示其地址比ebp的地址要小。
mov 0x8(%ebp),%edxmov 0xc(%ebp),%eaxmov %eax,(%edx)这里与上面的分析类似,并没有出现局部变量,可以表明这里是函数参数在进行交换。
这里的操作数方式是寄存器——存储器mov -0x4(%ebp),%eax 通过寄存器eax将值返回,记住一般函数的返回值都是通过eax寄存器返回的。
0x080483dd <exchange+25>: leave0x080483de <exchange+26>: ret 函数返回分析完反汇编代码之后有没有看出来函数调用是采用哪种调用约定呢?其实就是C调用方式(__cdecl)的方式。
//程序exchange.c,表示交换两个数。
#include <stdio.h>#include <math.h>int exchange(int *xp, int y){int x = *xp;*xp = y;return (x);}int main(){int a = 4;int b = exchange(&a, 3);printf("a = %d, b = %d\n", a, b);return 0;}通过上面的学习我们可以明白其实C语言中的指针本质上就是地址。
抓住这一点我们对指针的理解就会更加明白。
当然复杂的指针操作还是要在实践之中慢慢的掌握。
2、算术和逻辑操作加载有效地址(Load Effective Address)指令lea实际上是mov指令的变形,因为mov不能够直接对两个存储器操作数,,指令将有效地址写入到目的操作数(如寄存器)。
举个例子:如果寄存器%edx的值为x,那么指令lea 7(%edx, %edx, 4), %eax将设置寄存注:后缀l表示对双字进行操作,还可以为w和b分别表示字和字节。
练习一下下面的移位操作指令#include <stdio.h>int shiftlr(int x, int n){x <<= 2;x >>= n;return x;}int main(){int a = 4, b = 3;int ans = shiftlr(a, b);printf("%d\n", ans);return 0;}shiftlr函数反汇编代码:0x080483c4 <shiftlr+0>: push %ebp0x080483c5 <shiftlr+1>: mov %esp,%ebp0x080483c7 <shiftlr+3>: shll $0x2,0x8(%ebp)0x080483cb <shiftlr+7>: mov 0xc(%ebp),%ecx0x080483ce <shiftlr+10>: sarl %cl,0x8(%ebp)0x080483d1 <shiftlr+13>: mov 0x8(%ebp),%eax0x080483d4 <shiftlr+16>: pop %ebp0x080483d5 <shiftlr+17>: ret从上面的代码我们可以看到,移位量用单个字节编码,因为只允许进行0到31位的移位。
位移量可以是一个立即数,或者放在单字节寄存器元素%cl中。
给个例子和其相应的反汇编代码练习一下。
#include <stdio.h>int main(){int a = 6, b = 3;unsigned int c = 10, d = 2;int ans1 = a / b;unsigned int ans2 = c * d;return 0;}0x08048394 <main+0>: lea 0x4(%esp),%ecx0x08048398 <main+4>: and $0xfffffff0,%esp0x0804839b <main+7>: pushl -0x4(%ecx)0x0804839e <main+10>: push %ebp0x0804839f <main+11>: mov %esp,%ebp0x080483a1 <main+13>: push %ecx0x080483a2 <main+14>: sub $0x28,%esp0x080483a5 <main+17>: movl $0x6,-0x8(%ebp)0x080483ac <main+24>: movl $0x3,-0xc(%ebp)0x080483b3 <main+31>: movl $0xa,-0x10(%ebp)0x080483ba <main+38>: movl $0x2,-0x14(%ebp)0x080483c1 <main+45>: mov -0x8(%ebp),%eax0x080483c4 <main+48>: mov %eax,-0x2c(%ebp)0x080483c7 <main+51>: mov -0x2c(%ebp),%edx0x080483ca <main+54>: mov %edx,%eax0x080483cc <main+56>: sar $0x1f,%edx0x080483cf <main+59>: idivl -0xc(%ebp)0x080483d2 <main+62>: mov %eax,-0x18(%ebp)0x080483d5 <main+65>: mov -0x10(%ebp),%eax0x080483d8 <main+68>: imul -0x14(%ebp),%eax0x080483dc <main+72>: mov %eax,-0x1c(%ebp)0x080483df <main+75>: mov $0x0,%eax0x080483e4 <main+80>: add $0x28,%esp0x080483e7 <main+83>: pop %ecx0x080483e8 <main+84>: pop %ebp0x080483e9 <main+85>: lea -0x4(%ecx),%esp0x080483ec <main+88>: ret2、控制结构CF:进位标志。