Cr/D/Fail这个有什么特别之处吗

您所在位置: &
&nbsp&&nbsp&nbsp&&nbsp
FractalkineCX3CR1在肿瘤细胞的表达及其在NK细胞依赖的免疫监视机制中的重要作用论文.pdf 103页
本文档一共被下载:
次 ,您可全文免费在线阅读后下载本文档。
下载提示
1.本站不保证该用户上传的文档完整性,不预览、不比对内容而直接下载产生的反悔问题本站不予受理。
2.该文档所得收入(下载+内容+预览三)归上传者、原创者。
3.登录后可充值,立即自动返金币,充值渠道很便利
需要金币:200 &&
--优秀硕士毕业论文,完美PDF格式,可在线免费浏览全文和下载,支持复制编辑,可为大学生本专业本院系本科专科大专和研究生学士硕士相关类学生提供毕业论文范文范例指导,也可为要代写发表职称论文的提供参考!!!
你可能关注的文档:
··········
··········
中国科学技术大学学位论文相关声明
本人声明所呈交的学位论文,是本人在导师指导下进行研究
工作所取得的成果。除已特另dTJr_i以标注和致谢的地方外,论文中
不包含任何他人已经发表或撰写过的研究成果。与我一同工作的
同志对本研究所做的贡献均已在论文中作了明确的说明。
本人授权中国科学技术大学拥有学位论文的部分使用权,
即:学校有权按有关规定向国家有关部门或机构送交沦文的复印
件和电子版,允许论文被查阅和借阅,可以将学位论文编入有关
数据库进行检索,可以采用影印、缩印或扫描等复制手段保存、
汇编学位论文。
保密的学位论文在解密后也遵守此规定。
作者签名:
z∞7年盯月26日
中宦科学技术人学博七学位论文
肿瘤免疫是免疫学研究的重要方向。虽然围绕肿瘤细胞发生、发展已经提出
了不少的机制,对于肿瘤细胞如何逃避免疫细胞,特别是T细胞、NK细胞的监
视,也有不少的实验解释。但是到目前为止肿瘤免疫学的研究对于肿瘤的II缶床治
疗仍然不能完全的理论指导,还需要更广泛的研究去揭示肿瘤发生的秘密,为肿
瘤的消除提供足够的支持。
趋化因子是一群分子量不大的可溶性蛋白,是免疫系统的重要组成部分,在
免疫细胞的迁移和归巢方面起关键作用。同其他免疫系统成员一样,肿瘤细胞对
趋化因子的利用也是其逃避免疫系统监视,完成肿瘤扩散的一个有效的手段。
Fractalkine是一种较晚发现的趋化因子,其独特的分子结构,双重的存在形式,
使其成为趋化因子研究的一个重要分子。但是其在肿瘤细胞中的表达情况的研究
还不是很充分,其是否是肿瘤逃避免疫监视的一个机制成为令人关心的课题。
我们的工作首先检测了多种肿瘤细胞系中趋化因子fractalkine和它的受体
CX3CRI的表达情况。该部分的结果显示了该趋化因子在肿瘤细胞系中的广泛表
达,表明该趋化因子在肿瘤发生重的重要作用。该部分的结果还显示不仅该趋化
因子的表达十分广泛,它的受体的表达也比较的多。原始组织不表达该受体的,
发生肿瘤后有可能获得该受体的表达:来源细胞表达CX3CRI的,肿瘤发生后
更有可能保留该特性。这一发现可能暗示,CX3CRl/fraetalkine在肿瘤的转移中
可能扮演不能忽视的角色。在功能方面,我们发现了肿瘤的对NK细胞的敏感性
fracmlkine既可以作为趋化因子招募活化NK细胞,又同时拥有黏附分子的功能。
为了充分的了解CX3CRl/fraetalkine系统对于NK细胞杀伤肿瘤中的贡献,
我们具体研究了这一对分子在K562细胞对NK细胞的杀伤敏感性方面的作用。
我们的工作部分解释了K562细胞对NK细胞敏感的原因。为此我们设计了多个
提示了我们K562不仅表达fraetalkine,并且在它的条件培养基中还含有可以活
化NK细胞的可溶性蛋白。接下来的杀伤实验证实了我们的推测。加入适当比例
中国科学技术大学博士学位论文
的条件培养基,培养体系中的NK细胞可以被活化,而更好的杀伤肿瘤细胞。我
们还是发现可溶性的fractalkine对NK一92细胞也有相同的效果。以可溶性
杀伤,降低K562细胞对NK细胞毒活性的敏感性。这些工作就部分的阐述了
K562细胞表达的fractalkine所具有的生理学意义
在天然表达这一分子的肿瘤细胞上做研究之外,还需要仔细比较只有fractalkine
表达有差异的两个细胞系不同。为此我们构建了两个不同的HeLa细胞系。它们
部导入了peDNA3载体,它们的唯一差异在于FKN.HeLa在细胞表面重组表达
基础上,通过体外的细胞刺激和细胞杀伤实验,我们比较全面地证实了膜表面的
fractalkine对NK细胞的活化功能,包括细胞因子的分泌和细胞毒活性。这些结
果证明了肿瘤细胞可以通过趋化因子的产生而直接的作用于NK细胞,并可以通
过NK细胞的这两种能力影响整个免疫系统。
布情况,并且具体研究了这一对分子在NK细胞杀伤某些肿瘤细胞中的作用。在
这基础上,通过构建的两个细胞系,我们证实了肿瘤细胞表面的fmctalkine可以
直接的和NK细胞相互作用,对免疫系统做出深刻的影响。这些结果为揭示
正在加载中,请稍后...linux kernel内核调试方法
我的图书馆
linux kernel内核调试方法
按Esc退出全屏模式2 贡献值949.5K 大小&
发表评论:
喜欢该文的人也喜欢Default kernel command string 里输入
mem=32M console=ttySAC0 root=/dev/ram initrd=0xcx ramdisk_size=2048 rw
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
6.从skyeye的测试套件中拷贝相应的文件initrd.img和skyeye.conf到linux-2.6.26源码根目录下。这两个文件的位于
skyeye-testsuite-1.25/linux/s3c0x-2.6.14/中
7.启动虚拟机
XXX@ubuntu:~/dt/linux-2.6.26$ sudo skyeye -e vmlinux
8.启动完成后那激动人心的logo如下
Welcome to
| |__| | / /
_ /| | | |/ // /
/ /___/ / | |__/ / | |
| || |___ | | |_| | |_| |/
|_||_____||_|_| |_|/____|/_//_/
ARMLinux for Skyeye
For further information please check:
http://www.skyeye.org/
BusyBox v1.4.1 ( 01:19:06 CST) Built-in shell (ash)
Enter 'help' for a list of built-in commands.
/bin/ash: can' job control turned off
/ $ uname -a
Linux skyeye 2.6.26 #2 Sun Oct 5 19:56:57 CST 2008 armv4tl unknown
1. 在linux-2.6.26源码根目录下新建文件”.gdbinit”,内容是:
(gdb) target remote:12345
2. 在linux-2.6.26源码根目录下命令:
sudo skyeye -d -e vmlinux
3. 在源码根目录下新开一个终端,并运行:
arm-linux-gnueabi-gdb ./vmlinux
之后可以看到,下断点,查看汇编等一切调试功能和x86下都一样。
4. ddd下如何调用arm-linux-gnueabi-gdb ? 答
$ ddd --debugger arm-linux-gnueabi-gdb ./vmlinux
XXX@ubuntu:~/桌面/test/linux-2.6.26_s3c2410$ sudo skyeye -d -e vmlinux
big_endian is false.
cpu info: armv4, arm920t, , ff00fff0, 2
mach info: name s3c2410x, mach_init addr 0x805f030
dbct info: Note: DBCT not compiled in. This option will be ignored
uart_mod:0, desc_in:, desc_out:, converter:
SKYEYE: use arm920t mmu ops
Loaded RAM
./initrd.img
start addr is set to 0xc0008000 by exec file.
debugmode= 1, filename = skyeye.conf, server TCP port is 12345
------------------------
fqh@ubuntu:~/桌面/test/linux-2.6.26_s3c2410$ arm-linux-gnueabi-gdb vmlinux
GNU gdb 6.8-debian
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying"
and "show warranty" for details.
This GDB was configured as "--host=i486-linux-gnu --target=arm-linux-gnueabi"...
stext () at arch/arm/kernel/head.S:80
msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode
Current language: currently asm
(gdb) source extendinstr //载入辅助的gdb宏
--------------
用快捷键 ctrl+x+2 打开tui模式后的图示,可看到调试是从第一条指令开始的。这对研究系统启动过程提供了极大的便利。
┌──arch/arm/kernel/head.S────────────────────────────────────────────────────────────────────────────┐
cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode
@ and irqs disabled
p15, 0, r9, c0, c0
@ get processor id
__lookup_processor_type
@ r5=procinfo r9=cpuid
@ invalid processor (r5=0)?
@ yes, error 'p'
__lookup_machine_type
@ r5=machinfo
@ invalid machine (r5=0)?
@ yes, error 'a'
__vet_atags
__create_page_tables
└────────────────────────────────────────────────────────────────────────────────────────────────────┘
>│0xc0008000
CPSR_c, #211 0xd3
│0xc0008004
15, 0, r9, cr0, cr0, {0}
│0xc0008008
0xc00082f8
│0xc000800c
│0xc0008010
0xc0008190
│0xc0008014
0xc0008358
│0xc0008018
│0xc000801c
0xc00081e8
│0xc0008020
0xc00083a0
│0xc0008024
0xc0008078
│0xc0008028
sp, [pc, #240] 0xc0008120
└────────────────────────────────────────────────────────────────────────────────────────────────────┘
remote Thread 42000 In: stext
PC: 0xc0008000
(gdb) b sys_read
Breakpoint 1 at 0xc008cc4c: file fs/read_write.c, line 354.
----------------
调试示意图
效果可能与你机器上看到的不一样。这个例子中,每个gdb单步指令都会自动显示backtrace。这是因为本人使用了章节“gdb宏”中的extendinstr宏。
┌──include/asm/thread_info.h──────────────────────────────────────────────────────────────────────────────────────────────┐
static inline struct thread_info *current_thread_info(void) __attribute_const__;
static inline struct thread_info *current_thread_info(void)
register unsigned long sp asm ("sp");
return (struct thread_info *)(sp & ~(THREAD_SIZE - 1));
/* thread information allocation */
#ifdef CONFIG_DEBUG_STACK_USAGE
#define alloc_thread_info(tsk) /
((struct thread_info *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, /
THREAD_SIZE_ORDER))
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
│0xc008d480
│0xc008d484
{r11, r12, lr, pc}
│0xc008d488
r11, r12, #4 0x4
│0xc008d48c
r3, sp, #fc0
>│0xc008d490
r3, r3, #63 0x3f
│0xc008d494
r3, [r3, #12]
│0xc008d498
r12, #0 ; 0x0
│0xc008d49c
r2, [r3, #560]
│0xc008d4a0
│0xc008d4a4
│0xc008d4a8
r3, #1 0x1
│0xc008d4ac
0xc008d4d0
│0xc008d4b0
r2, [r2, #4]
│0xc008d4b4
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
remote Thread 42000 In: fget_light
PC: 0xc008d490
Program received signal SIGHUP, Hangup.
0xc008d490 in fget_light (fd=1, fput_needed=0xc1c17ed4) at include/asm/thread_info.h:97
-------------------
0xc008d490 in fget_light (fd=1, fput_needed=0xc1c17ed4) at include/asm/thread_info.h:97
0xc008cc5c in sys_read (fd=1, buf=0xc1196800 "", count=512) at fs/read_write.c:359
0xc000ac7c in rd_load_image (from=0xc02b43bc "/initrd.image") at init/do_mounts_rd.c:108
0xc000bbe8 in initrd_load () at init/do_mounts_initrd.c:121
0xc00094c0 in prepare_namespace () at init/do_mounts.c:384
0xc0008a9c in kernel_init (unused=) at init/main.c:878
0xc0048484 in sys_waitid (which=, upid=-, infop=0x0, options=0, ru=Cannot access memory at
address 0x4
) at kernel/exit.c:1689
Backtrace stopped: previous frame inner to this frame (corrupt stack?)
使用最新的skyeye
1. 新版本的改进
在ubuntu下利用在线安装命令所安装的skyeye是旧的版本,新版本修正了旧版本的一些小问题。比如,旧版本在调试时会出现下面一些烦人的小提示。
Can't send signals to this remote system.
SIGHUP not sent.
Program received signal SIGHUP, Hangup.
但是,两个版本并不是完全兼容的,主要是skyeye.conf的处理上。不过,幸好这些都是很容易解决的问题。
2. 新版本的安装
http://sourceforge.net/project/showfiles.php?group_id=85554
到上面的网站下载最新版本,目前是skyeye-1.2.6_rc1。解压后用下面命令编译就可以了
$./configure
$ make STATIC=1
然后把在源码根目录下生成的skyeye拷到内核目录下运行即可。这样系统中的老版本skyeye还照样可以使用。
sudo ./skyeye -d -e vmlinux
3. 新老版本的兼容问题
主要是skyeye.conf的格式识别上。老版本要求load_address,load_address_mask不能写在 skyeye.conf文件内部,只能用-l选项指定。如果运行老版本时提示skyeye.conf出错,你就得去查查那里,并手动修改处理一下即可。
arm开发板调试环境的建立
为qq2440平台移植2.6.26或更新内核,并建立kgdb调试环境
[移植中的一些零碎的笔记]
1.内核版本
使用linus的git,但是已知2.6.25中arm已经支持kgdb了。
XXX@ubuntu:/storage/linus-git/linux-2.6$ git-describe
v2.6.27-rc9-2-g85ba94b
arm体系的默认配置文件在
arch/arm/configs
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- s3c2410_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- menuconfig
选取以下选现
CONFIG_DEBUG_INFO=y
CONFIG_KGDB=y
CONFIG_KGDB_SERIAL_CONSOLE=y
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
windows:硬盘安装的真实系统(XP)
ubuntu: 运行在windows下的vmware虚拟机中
qq2440开发板:真实开发板,IP是192.168.1.230
第一天:(完成)
熟悉开发板,PC机,虚拟机的网络互连
理解内核启动过程
开发板与PC机(XP)PING不通的原因有
1. PC机开着防火
2. PC机上的VMWARE的网络设置有问题(先卸载确认)
3. 安全类软件造成,比如卡巴司机(先卸载,不行重装系统)
ubuntu的网络配置分两种情况,一种是平时上网用的,一种是和开发板通讯用的。
平时使用虚拟机ubuntu上网的配置:
连接方式选出NAT: used to share the host's IP address
虚拟系统启动后,桌面右上角的
wired connection->properties->configuration选automatic configuration(DHCP)
开发板挂载ubuntu虚拟系统中的nfs
1.虚拟机本身的网络设置不用动
2.虚拟系统如ubuntu的网卡设置改为桥接
edit virtual machine settings->virtual machine setting->hardware->ethernet
->bridged:connected directly to the physical network
3.虚拟系统启动后,桌面右上角的manual network configuration要改.
点左键->network settings->wired connection->properties:enable roaming mode不选,
connection settings
configuration:static IP address
IP address:192.168.1.111 与PC机IP,开发板IP同个网段
subnet mask:255.255.255.0
gateway address:空
PC机网络信息:
Ethernet adapter 本地连接:
Connection-specific DNS Suffix
IP Address. . . . . . . . . . . . : 192.168.1.100
Subnet Mask . . . . . . . . . . . : 255.255.255.0
Default Gateway . . . . . . . . . :
开发板的网络信息:
[root@(none) /]# ifconfig
Link encap:Ethernet
HWaddr 08:00:3E:26:0A:5B
inet addr:192.168.1.230
Bcast:192.168.1.255
Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST
RX packets:1011 errors:0 dropped:0 overruns:0 frame:0
TX packets:610 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:9.2 KiB)
TX bytes:5 KiB)
Interrupt:53 Base address:0x300
windows打开ubuntu中的samba共享目录的方法
//192.168.1.111
ubuntu中nfs服务的安装和启用
$ sudo apt-get install nfs-common
$ sudo apt-get install nfs-kernel-server
$ sudo vi /etc/exports
/new/root_nfs *(rw,sync)
$ sudo /etc/init.d/nfs-kernel-server start
$ showmount -e localhost
开发板挂载nfs成功后可看到显示结果是
All mount points on localhost:
192.168.1.230:/new/root_nfs
开发板挂载ubuntu中的nfs
(此时运行的文件系统还是在开发板上)
mount -t nfs -o nolock 192.168.1.111:/new/root_nfs /tmp/fuck
192.168.1.111:ubuntu的IP
/tmp/fuck:开发板中的挂载点
[root@(none) /]# mount -t nfs -o nolock 192.168.1.111:/new/root_nfs /tmp/fuck
[root@(none) /]# cd /tmp/fuck/
[root@(none) fuck]# ls
shanghaitan.mp3
通过nfs启动开发板
(挂载的文件系统是在ubuntu虚拟系统上)
下面文字来自于:Embedded Linux Primer: A Practical, Real-World Approach
ip=192.168.1.139:192.168.1.1:192.168.1.1:255.255.255.0:coyote1:eth0:off
Here, client-ip is the target's IP server-ip is the address of the NFS gw-ip is the gateway (router), in case the server-ip is o and netmask defines the class of IP addressing. hostname is a string that is passed as device is the Linux device name, such as eth0; and PROTO defines the protocol used to obtain initial IP parameters.
本人的实际操作的命令参数是:
param set linux_cmd_line "console=ttySAC0 root=/dev/nfs nfsroot=192.168.1.111:/new/root_nfs ip=192.168.1.130:192.168.1.111:192.168.1.111:255.255.255.0:sbc2440.arm9.net:eth0:off"
注意把编辑器的换行功能去掉后,再复制上面的命令。
192.168.1.130是开发板的IP,系统启动后,用ifconfig就会显示这个IP地址。可以随意设置,当然要满足和PC机,ubuntu的IP在同个网段,而且不能冲突的先前条件。
130:192.168.1.111:nfs的server,也就是ubuntu的IP
按住空格健重启开发板,出现:
+---------------------------------------------+
| S3C2440A USB Downloader ver R0.03 2004 Jan
+---------------------------------------------+
USB: IN_ENDPOINT:1 OUT_ENDPOINT:3
FORMAT: +++
NOTE: Power off/on or press the reset button for 1 sec
in order to get a valid USB device address.
NAND device: Manufacture ID: 0xec, Chip ID: 0x76 (Samsung K9D1208V0M)
Found saved vivi parameters.
Press Return to start the LINUX/Wince now, any other key for vivi
type "help" for help.
Supervivi> menu
##### FriendlyARM BIOS for 2440 #####
[x] bon part 0 320k 2368k
[v] Download vivi
[k] Download linux kernel
[y] Download root_yaffs image
[c] Download root_cramfs image
[n] Download Nboot
[e] Download Eboot
[i] Download WinCE NK.nb0
[w] Download WinCE NK.bin
[d] Download & Run
[f] Format the nand flash
[p] Partition for Linux
[b] Boot the system
[s] Set the boot parameters
[t] Print the TOC struct of wince
[q] Goto shell of vivi
Enter your selection: s //<--
##### Parameter Menu #####
[r] Reset parameter table to default table
[s] Set parameter
[v] View the parameter table
[w] Write the parameter table to flash memeory
Enter your selection: s //<--
Enter the parameter's name(mach_type, media_type, linux_cmd_line, etc): linux_cmd_line
Enter the parameter's value(if the value contains space, enclose it with "): "console=ttySAC0 root=/dev/nfs nfsroot=192.168.1.111:/new/root_nfs ip=192.168.1.130:192.168.1.111:192.168.1.111:255.255.255.0:sbc2440.arm9.net:eth0:off"
Change linux command line to "console=ttySAC0 root=/dev/nfs nfsroot=192.168.1.111:/new/root_nfs ip=192.168.1.130:192.168.1.111:192.168.1.111:255.255.255.0:sbc2440.arm9.net:eth0:off"
##### Parameter Menu #####
[r] Reset parameter table to default table
[s] Set parameter
[v] View the parameter table
[w] Write the parameter table to flash memeory
Enter your selection: w
// test.image.gz
本人这个文件系统解压后的大小是6.4M,制作成8M大的test.image,压缩成test.image.gz后只有2.9M大。
但是利用skyeye启动时,解压花的时间比较长。
命令行中的ramdisk_size太小,修改.
mem=32M console=ttySAC0 root=/dev/ram initrd=0xcx ramdisk_size=8192 rw initcall_debug
ramdisk_size=N
This parameter tells the RAM disk driver to set up RAM disks of N k size.
问题,文件系统没创建console设备节点:
RAMDISK: Loading 8192KiB [1 disk] into ram disk... done.
VFS: Mounted root (ext2 filesystem).
Freeing init memory: 132K
Warning: unable to open an initial console.
创建rootfs过程中,在/dev目录下手动创建如下节点:
mknod -m 660 null c 1 3
mknod -m 660 console c 5 1
VFS: Mounted root (ext2 filesystem).
Freeing init memory: 132K
hwclock: Could not access RTC: No such file or directory
mknod: /dev/pts/0: No such file or directory
mount: Mounting none on /tmp failed: Invalid argument
mount: Mounting none on /var failed: Invalid argument
/etc/init.d/rcS: /etc/init.d/rcS: 44: cannot create /dev/vc/0: Directory nonexistent
/etc/init.d/rcS: /etc/init.d/rcS: 45: cannot create /dev/vc/0: Directory nonexistent
/etc/rc.d/init.d/httpd: /etc/rc.d/init.d/httpd: 16: /sbin/boa: not found
/etc/init.d/rcS: /etc/init.d/rcS: 48: cannot create /dev/vc/0: Directory nonexistent
/etc/init.d/rcS: /etc/init.d/rcS: 49: cannot create /dev/vc/0: Directory nonexistent
/etc/rc.d/init.d/leds: /etc/rc.d/init.d/leds: 16: /etc/init.d/rcS: /etc/init.d/rcS: 52: cannot create /dev/vc/0: Directory nonexistent
/etc/init.d/rcS: /etc/init.d/rcS: 53: cannot create /dev/vc/0: Directory nonexistent
/sbin/led-player: not found
SIOCSIFADDR: No such device
SIOCGIFFLAGS: No such device
/etc/init.d/rcS: /etc/init.d/rcS: 59: /sbin/madplay: not found
Please press Enter to activate this console.
-sh: can' job control turned off
id: unknown uid 0
[@FriendlyARM /]# ls
lost+found
[@FriendlyARM /dev]# ls
还有一堆提示,但总算系统能跑了。
现在我的心头大患是udev的问题,因为2.6.26内核中没有devfs了。但有下面这篇文章参考
udev轻松上路
http://www.linuxforum.net/forum/showflat.php?Cat=&Board=embedded&Number=628054&page=0&view=collapsed&sb=5&o=0&fpart=
第三天:(完成)
移植内核2.6.27-rc9到qq2440开发板,实现基本功能,能挂载板上文件系统.
1.使用vivi修改mach_type参数
2.修改时钟频率
3.修改源码正确分区
4.禁止nand的ECC校验
问题1.表现
Uncompressing Linux................................................................................................................. done, booting the kernel.
Error: unrecognized/unsupported machine ID (r1 = 0x0000030e).
Available machine support:
IPAQ-H1940
Simtec-BAST
Nex Vision - Otom 1.1
Thorcom-VR1000
Simtec-Anubis
Simtec-OSIRIS
IPAQ-RX3715
NexVision - Nexcoder 2440
Please check your kernel config and/or bootloader.
解决方法:
##### Parameter Menu #####
[r] Reset parameter table to default table
[s] Set parameter
[v] View the parameter table
[w] Write the parameter table to flash memeory
Enter your selection: s
Enter the parameter's name(mach_type, media_type, linux_cmd_line, etc): mach_type
Enter the parameter's value(if the value contains space, enclose it with "): 362 //disable_ecc)
chip->ecc.mode = NAND_ECC_NONE;
chip->ecc.mode = NAND_ECC_NONE;///dev/null ./ remote target localhost:1234
3. kdbg: 缺点:功能比ddd弱。字体太小,c和反汇编代码交错显示,反汇编代码折叠隐藏在C代码之间,要显示反汇编代码要手动展开,不可忍受。太过界面化,居然找不到是在哪里手动打gdb命令。致命缺点是,内核跑起来后,如果没有断点拦截,就没法把内核的运行暂停下来,kdbg成了没事姥,源码窗口的显示不更新。另一个致命缺点是,如果没有源码只有二进制文件,虽然可以下断点,但无法显示反汇编代码,没意义。据说kdbg是用来调试kde程序的,实际上也能调试内核。优点:窗口可以整合到一块,稳定。有变化的寄存器会显示红色。提示 kdbg -r localhost:1234 ./vmlinux
4. insight: 和ddd都是基于TCL/TK,比较相似。优点:源码显示功能最强,可以选择C和反汇编代码分开和交叉显示。可以选择反汇编代码使用intel还是 at&t格式。可以列出当前有哪些源文件,当前文件有哪些函数。变化的寄存器有改变颜色的功能,ddd则没有。缺点:和ddd一样,小窗口无法整合到到窗口中,但比ddd差的是,主窗口最大化后小窗口无法保持置顶。相对ddd的大劣势是没有一个强大的data windows。感觉界面比ddd强大,但灵活性比ddd差点。对于调试内核来说,还有一个和kdbg相同的大缺点,内核只能通过断点暂停运行,而ddd 下还可以用ctrl+c暂停内核。另外它有个SB错误,显示backtrace的窗口,标题居然是stack. 提示: insight ./vmlinux
5. xxgdb: 古董级别。没事干的时候可以玩玩
6. 其实,gdb自带了一个基于curses的gui。启动方式是 或者在gdb启动之后用命令layout启动gui。很好用,可以至多同时显示三个分窗口。要是代码有着色功能就好了。
针对内核调试的总结:
1. kdbg不适合调试内核
3. 如果想复习gdb强大的命令,选cgdb或纯gdb。
4. 如果想学习汇编,insight是不二选择。
5 如果倾向于把调试器当作浏览器使用,作为source insight等工具的辅助工具,在内核运行中拦截函数,分析函数的调用关系,不需要反汇编的话,则cgdb是不错的选择 .(source insight等源码分析工具有个共同的缺点,因为体系和内核配置不同,一个函数有很多的定义,借助调试器可以在内核运行的时候找出实际调用的那个)
6.insight和ddd很接近,各有千秋。但如果侧重于追溯数据结构体间的联系,ddd更好一点,因为它有data window,它的强项是数据和数据结构关系分析并用图像方式显示出来(What is DDD? Data Display Debugger)。如果侧重于分析汇编指令是怎么在cpu中跑的,推荐用insight,因为它汇编代码显示功能更细致。
7.可惜目前在ubuntu8.04下,ddd+qemu组合用来调试驱动时有bug:驱动函数被拦截时如果正在qemu的系统下操作,鼠标就会冻结在qemu的屏幕中。其实调试单个驱动,用gdb就足够了。ddd等gui一般用来调试理解内核原理。
另外有用的命令 ptype, whatis
更多相关技巧:
1. 获取struct page结构的大小
(gdb) p mem_map
$80 = (struct page *) 0xc1000000
(gdb) p mem_map+1
$81 = (struct page *) 0xc1000020
(gdb) p/x 0xc1000020 - 0xc1000000
$82 = 0×20
打印前从指针mem_map所指起的5个page结构体
(gdb) p *mem_map@5
$83 = {{flags = 1024, _count = {counter = 1}, {_mapcount = {counter = -1}, {inuse = 65535, objects = 65535}}, {{private = 0, mapping = 0×0}, ptl =…
用ddd的图形显示命令是 (gdb) graph display *mem_map@5
参考 p *array@len
@的左边是数组的首地址的值,也就是变量array所指向的内容,右边则是数据的长度,其保存在变量len中
每运行一次stepi/next等命令后显示下一步要将要运行的反汇编指令
(gdb) display/i $pc
6: x/i $pc
0xc0144fb6 :
(gdb) stepi
6: x/i $pc
0xc0144fb8 :
%edx,0×44(%eax)
提示:display的管理:
undisplay delete display disable display enable display info display
4.使结构体的显示更漂亮
(gdb) show print pretty
Prettyprinting of structures is on.
(gdb) set print pretty off
(gdb) p *init_task->group_info
$12 = {ngroups = 0, usage = {counter = 14}, small_block = {0 }, nblocks = 0, blocks = 0xc0355530}
(gdb) set print pretty on
(gdb) p *init_task->group_info
ngroups = 0,
counter = 14
small_block = {0 },
nblocks = 0,
blocks = 0xc0355530
(注:6.7.条来自//content_173.html)
5. 使用自定义命令。
(gdb) define nid
Type commands for definition of “nid”.
End with a line saying just “end”.
>disassemble $pc $pc+16
6. 纯gdb的多窗口显示 GUI调试器可以同时打开多个小窗口,分别显示寄存器、汇编和源代码等。在gdb里也可以做到,但同时最多只能显示两个窗口,试了一下也很方便的。基本命令如下:
a) `layout src’ 仅显示源代码窗口。
b) `layout asm’ 仅显示汇编代码窗口。
c) `layout split’ 显示源代码和汇编代码窗口。
d) `layout regs’ 显示寄存器和源代码窗口,或者寄存器和汇编代码窗口。
e) `layout next` 和 `layout prev’ 切换窗口。
f) ctrl + L 刷新屏幕。
g) `C-x 1′ 单窗口模式。
h) `C-x 2′ 双窗口模式。
i) `C-x a’ 回到传统模式。
7. 字符gdb中,如何在每执行一次next命令后都自动显示backtrace的内容 这个问题实际是如何一次执行多条命令。用自定义命令解决
(gdb) define nbt
Type commands for definition of “nbt”.
End with a line saying just “end”.
early_cpu_init () at arch/x86/kernel/cpu/common.c:626
0xc0384ca9 in setup_arch (cmdline_p=0xc0379fe8)
at arch/x86/kernel/setup_32.c:765
0xc037f62e in start_kernel () at init/main.c:564
0xc037f008 in i386_start_kernel () at arch/x86/kernel/head32.c:13
0× in ?? ()
8. gdb在TUI模式下如何把光标焦点炸转移到command窗口,以便能用上下箭头键能快速翻出历史指令?
实际是转换“active”窗口。
C-x o: ctrl+x,接着放开这两个键,然后在按o(不需要+ctrl)
关于TUI更多信息:
http://sourceware.org/gdb/current/onlinedocs/gdb_23.html#SEC236
还有组合键
C-x A 退出TUI模式
C-x 1 只用一个窗口
C-x 2 用两个窗口,按多次会有不同两个窗口的组合形式
C-x o active 窗口转移
C-x s 进入和退出TUI SingleKey 模式
注:C-x o多次使用相当于依次执行以下命令
focus src 转移焦点到源码窗口。
focus regs
TUI模式还有以下专用命令
layout next
layout prev
layout src
layout asm
layout split
layout regs
focus next
tui reg float
tui reg general
tui reg next
tui reg system
winheight name +count
winheight name -count
tabset nchars
9. 如何在子函数调用和退出时都暂停运行 watch $ebp
10. 如何获取结构体中特定域的相对偏移量,比如struct stak_struct 中lock_depth的相对偏移量?
(gdb) p/x &(*(struct task_struct *)0).lock_depth
11. 如何能够交换使用ddd与gdb,也就是说使用ddd调试时,想换回使用纯gdb,同时保证启用gdb后保证“调试上下文”没任何变化?
只要.gdbinit 文件没包含 c, next..等等能驱动gdb继续调试的命令就可以。
12. 如何通过函数名确定所在的源文件
(gdb) info line vfs_mkdir
Line 2131 of "fs/namei.c" starts at address 0xc017c048
and ends at 0xc017c052 .
13. 由汇编指令地址确定该指令所对应源码的所在行(注:一行c语言一般对应几行汇编指令)
info line *xxxxxxx (xxx是汇编指令地址)
14. 如何快速定位函数中某句C语句对应汇编指令的开始地址。比如以下 [内容太大,准备移到其他位置]
2130 int vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
DQUOT_INIT(dir);
error = dir->i_op->mkdir(dir, dentry, mode);//<-我们想确定这句语句的汇编指令开始地址,注意它在源文件中的行数
if (!error)
fsnotify_mkdir(dir, dentry);
首先,通过函数名查询对应的源文件
(gdb) info line vfs_mkdir
Line 2131 of "fs/namei.c" starts at address 0xc017c048
and ends at 0xc017c052 .
然后,利用info line 源文件:目标语句的行数 就能查询到
(gdb) info line fs/namei.c:2146
Line 2146 of "fs/namei.c" starts at address 0xc017c0ee
and ends at 0xc017c0fe .
(gdb) disass 0xc017c0ee
Dump of assembler code for function vfs_mkdir:
0xc017c048 : push
0xc017c0e4 : mov
0x24(%eax),%ecx
0xc017c0e7 : or
$0xffffffff,%edx
0xc017c0ea : mov
0xc017c0ec : call
0xc017c0ee : mov
0x98(%esi),%ebx
0xc017c0f4 : mov
//参数 dentry -> %edx
0xc017c0f6 : mov
//参数dir -> %eax
0xc017c0f8 : mov
-0x10(%ebp),%ecx //参数mode -> %ecx
0xc017c0fb : call
*0x14(%ebx)
//dir->i_op->mkdir(dir, dentry, mode)
0xc017c0fe : test
//判断返回值(error = dir->i_op->mkdir(dir, dentry, mode);)
0xc017c100 : mov
//保存返回值
0xc017c102 : jne
0xc017c15d
//如果返回值 != 0,也就是mkdir失败,跳到最后返回。成功则继续
0xc017c104 : testb
$0x4,0x11c(%esi)
//内联函数fsnotify_mkdir 及子函数->inode_dir_notify在这里展开
//static inline void inode_dir_notify(struct inode *inode, unsigned long event)
// if (inode->i_dnotify_mask & (event)) <-注意这里判断位,刚好对应testb
$0x4,0x11c(%esi)
0xc017c10b : je
0xc017c119
0xc017c15d : lea
-0xc(%ebp),%esp
0xc017c160 : mov
我们通过mkdir参数个数,及testb 指令基本判定我们的猜测没错。也就是说vfs_mkdir函数中dir→i_op→mkdir的实际调用是在0xc017c0fb : call *0×14(%ebx)
15. 下断点的形式
1. b 函数名
2. b *指令地址
3. b 源码:行数
(gdb) b fs/namei.c:2146
Breakpoint 9 at 0xc017c0ee: file fs/namei.c, line 2146.
16. 陷入循环语句后,想自动运行到循环语句结束:
17. 重复当前的gdb指令
按enter键即可
本小节意义:为了方便把调试内容复制出来,而又需要一定的功能,本人经常使用的工具是gdb的tui。所以gdb宏的使用更是成了不可缺少的辅助手段。比如extendinstr宏,能实时显示调用链的情况,相当于实现了ddd的backtrace分窗口。其他宏的作用就不说了。
kgdb官方的gdb宏 /downloads.htm
“Fun with strace and the GDB Debugger” /developerworks/aix/library/au-unix-strace.html
“GNU Project Debugger: More fun with GDB” /developerworks/aix/library/au-gdb.html
“14.3.4. Useful Kernel gdb Macros” from “Embedded Linux Primer” http://book./embedded/embeddedprime/
gdb宏的使用
假设要使用下节的lsmod,该gdb宏能列举内核中的模块。 在内核源码目录下建立一个新文件lsmod,内容见下节。
(gdb) source lsmod
(gdb) help lsmod
list module struct's address, text address and their module name
(gdb) lsmod
(gdb) lsmod
0xE014DDA0 0xE014D000 nls_iso8859_1
0xExE0164000 isofs
0xE014BA20 0xE0148000 zlib_inflate
0xExE0152000 udf
0xExE000B000 processor
0xExE0008000 fan
0xExE0020000 thermal_sys
----end----
我们查看一下processor模块结构体的内容
(gdb) p *(struct module *)0xE0012DE0
state = MODULE_STATE_LIVE,
next = 0xe0008ea4,
prev = 0xe0018984
name = "processor", '/0' ,
name = 0xd5910ba0 "processor",
refcount = {
counter = 3
next = 0xe00189d0,
为了方便查看该结构中指针域所指向的结构体,可在ddd下用以下命令打开数据图形然后展开查看
(gdb) graph display *(struct module *)0xE0012DE0
给出的例子都在2.6.26内核上上测试通过。
链表遍历类
宏名: lsmod(有小bug,饭后再看)
作用: 列举内核模块的名称及对应模块结构体的地址,以及text段的地址[todo,导出.bss,.data地址]
define lsmod
printf "Address/t/ttext/t/tModule/n"
set $m=(struct list_head *)&modules
set $done=0
#获取结构体内特定域的相对偏移,见"gdb技巧"
set $offset=&(*(struct module *)0).list
while ( !$done )
set $mp=(struct module *)((char *)$m->next - (char *)$offset)
printf "0x%X/t0x%X/t%s/n", $mp, $mp->module_core,$mp->name
if ( $mp->list->next == &modules)
set $done=1
set $m=$m->next
printf "----end----/n"
document lsmod
list module struct's address, text address and their module name
(gdb) lsmod
0xE014DDA0 0xE014D000 nls_iso8859_1
0xExE0164000 isofs
0xE014BA20 0xE0148000 zlib_inflate
0xExE0152000 udf
0xE001BEA0 0xE001A000 8390
0xE017EEC0 0xE016C000 ide_core
0xExE0015000 thermal
0xExE000B000 processor
0xExE0008000 fan
0xExE0020000 thermal_sys
----end----
宏名: psusr,pskern
作用: 列举所有task的结构地址,状态,PID,PPID,comm。
psusr,只列举用户层可见的进程;pskern,列举内核层可见的所有进程。
define __show_state
if ($arg0->state == 0)
printf "running/t/t"
if ($arg0->state == 1)
printf "sleeping/t"
if ($arg0->state == 2)
printf "disksleep/t"
if ($arg0->state == 4)
printf "zombie/t"
if ($arg0->state == 8)
printf "stopped/t"
if ($arg0->state == 16)
printf "wpaging/t"
printf "%d/t/t", $arg0->state
document __show_state
internel macro, don't call it by hand
define psusr
printf "address/t/tstate/t/tuid/tpid/tppid/tcomm/n"
set $init_t = &init_task
set $tasks_off=((size_t)&((struct task_struct *)0)->tasks)
set $next_t=(((char *)($init_t->tasks).next) - $tasks_off)
while ($next_t != $init_t)
set $next_t=(struct task_struct *)$next_t
printf "0x%08X/t", $next_t
__show_state $next_t
printf "%d/t%d/t%d/t%s/n", /
$next_t->uid, $next_t->pid, /
$next_t->parent->pid, $next_t->comm
$next_t=(char *)($next_t->tasks.next) - $tasks_off
printf "address/t/tstate/t/tuid/tpid/tppid/tcomm/n"
printf "----end----/n"
document psusr
print information for all tasks, but not including thread members.
This command looks like "ps -aux" in userspace.
define pskern
printf "address/t/tstate/t/tuid/tpid/tppid/tcomm/n"
set $init_t = &init_task
printf "0x%08X/t", $init_t
__show_state $init_t
printf "%d/t%d/t%d/t%s/n", /
$init_t->uid, $init_t->pid, /
$init_t->parent->pid, $init_t->comm
set $tasks_off=((size_t)&((struct task_struct *)0)->tasks)
set $thread_off=((size_t)&((struct task_struct *)0)->thread_group.next)
set $next_t=(((char *)($init_t->tasks).next) - $tasks_off)
while ($next_t != $init_t)
set $next_t=(struct task_struct *)$next_t
printf "0x%08X/t", $next_t
__show_state $next_t
printf "%d/t%d/t%d/t%s/n", /
$next_t->uid, $next_t->pid, /
$next_t->parent->pid, $next_t->comm
set $next_th=(((char *)$next_t->thread_group.next) - $thread_off)
while ($next_th != $next_t)
set $next_th=(struct task_struct *)$next_th
printf "0x%08X/t", $next_th
__show_state $next_th
printf "%d/t%d/t%d/t%s/n", /
$next_th->uid, $next_th->pid, /
$next_th->parent->pid, $next_th->comm
set $next_th=(((char *)$next_th->thread_group.next) - $thread_off)
$next_t=(char *)($next_t->tasks.next) - $tasks_off
printf "address/t/tstate/t/tuid/tpid/tppid/tcomm/n"
printf "----end----/n"
document pskern
print infor for all tasks viewed in kernel, including all thread members
and swapper(PID==0).
(gdb) source ps
(gdb) psusr
uid pid ppid comm
0xDC43F8A0 sleeping 0 1 0 init
0xDC43F490 sleeping 0 2 0 kthreadd
0xDC43F080 sleeping 0 3 2 migration/0
0xDC43EC70 sleeping 0 4 2 ksoftirqd/0
0xDC43E860 sleeping 0 5 2 watchdog/0
0xDC44E060 sleeping 0 1707 1 acpid
0xD8AE6100 sleeping 104 1716 1 dbus-daemon
0xDC46ECD0 sleeping 0 1739 1 cupsd
0xDC45E080 sleeping 101 2009 1 exim4
0xD5A6C0E0 sleeping 0 2026 1 inetd
0xD5A6CD10 sleeping 0 2034 1 dhcdbd
0xDBD45160 sleeping 105 2044 1 hald
0xDBD45570 sleeping 0
hald-runner
uid pid ppid comm
----end----
宏名: lssp
作用: 列举超级块地址及其s_id域
define lssp
printf "address/t/ts_id/n"
set $sb_lh=(struct list_head *)&super_blocks
#获取结构体内特定域的相对偏移,见"gdb技巧"
set $offset=&(*(struct super_block *)0).s_list
set $sbp=(struct super_block *)((char *)$sb_lh->next - (char *)$offset)
while ( &$sbp->s_list != $sb_lh )
printf "0x%08X/t%s/n", $sbp, $sbp->s_id
set $sbp=(struct super_block *)((char *)$sbp->s_list.next - (char *)$offset)
printf "----end----/n"
document lssp
List the super_block and their start addresses
(gdb) lssp
0xDC40DC00 sysfs
0xDC40DA00 rootfs
0xDC40D800 bdev
0xDC40D400 proc
0xDC41B200 sockfs
0xDC431C00 debugfs
0xDC486600 pipefs
0xDC486000 anon_inodefs
0xD58C5A00 tmpfs
0xD58C5200 inotifyfs
0xD8C09800 devpts
0xD8C09600 hugetlbfs
0xD8C09400 mqueue
0xD590E000 tmpfs
0xD59E4C00 hda1
0xD5908A00 tmpfs
0xD7753200 tmpfs
0xDBD66400 hdc
----end----
功能增强类
宏名: eih, lih, ooi
作用: 克服时钟中断干扰与中断无关的目标代码的调试(X86下适用),解释请看“工程方法”
说明: 使用gdb或ddd时,进入中断后用finish命令的话常常是要么无法返回被中断的原指令处后停住,而是继续运行,要么是会进入到另一个时钟中断中;但是好像在insight下没这个问题。使用这个gdb宏可以解决该问题。
define eih
b common_interrupt
b native_iret
document eih
eih: early interrupt hacking, break common_interrupt and native_iret
define lih
b apic_timer_interrupt
b irq_return
document lih
lih: late interrupt hacking, break apic_timer_interrupt and irq_return
define ooi
document ooi
ooi: out of interrupt, return to the instruction interrupted by interrupt handler
宏名: extendinstr
作用: 扩展指令集。配合gdb自带的tui使用,能代替ddd等界面工具的部分功能。
说明: 指令开头:s→step,si→stepi,n→next,ni→nexti,中间bt→bt,末尾i→info args && info local
define inar
printf "-----args start----/n"
define inlo
printf "-----local start----/n"
info local
define btl
printf "-------------------/n"
define sibt
define sbt
define nibt
define nbt
define sibti
define sbti
define nibti
define nbti
宏名: quick
作用: 超级快捷键。gdb的快捷键并没用用尽所有的按键。我们可以利用空余的按键定义自己的命令。方便起见,我只是利用自定义命令简单的实现该该功能,而不是自定义快捷键。可以根据自己偏好来定义。
说明: 这个宏是配合前面的宏ooi和宏extendinstr使用的。这样,如果调试时进入了时钟中断,按a+enter就可以瞬间返回;q+enter–> z+enter–>finish。
宏名:bttnobp,btt,psusr,pskern,trapinfo,btpid,dmesg
内核文档gdbmacros.txt 的gdb宏的升级版本,还修正了一个bug,已在2.6.26下测试。
如果你运行这个脚本有错误,那说明你的内核版本太低了,请运行内核源码中原文件的宏。
本人这个文件的补丁还在提交的过程中。
能提供non-running进程的backtrace功能,还实现了dmesg。
说明bttnobp没在!CONFIG_FRAME_POINTER的配置下测试过,但是估计结果很不可靠,
因为条件判断太宽大了。
# This file contains a few gdb macros (user defined commands) to extract
# useful information from kernel crashdump (kdump) like stack traces of
# all the processes or a particular process and trapinfo.
# These macros can be used by copying this file in .gdbinit (put in home
# directory or current directory) or by invoking gdb command with
# --command= option
# Credits:
# Alexander Nyberg
# V Srivatsa
# Maneesh Soni
define __show_state
if ($arg0->state == 0)
printf "running/t/t"
if ($arg0->state == 1)
printf "sleeping/t"
if ($arg0->state == 2)
printf "disksleep/t"
if ($arg0->state == 4)
printf "zombie/t"
if ($arg0->state == 8)
printf "stopped/t"
if ($arg0->state == 16)
printf "wpaging/t"
printf "%d/t/t", $arg0->state
document __show_state
internel macro, don't call it by hand
define psusr
printf "address/t/tstate/t/tuid/tpid/tppid/tcomm/n"
set $init_t = &init_task
set $tasks_off=((size_t)&((struct task_struct *)0)->tasks)
set $next_t=(((char *)($init_t->tasks).next) - $tasks_off)
while ($next_t != $init_t)
set $next_t=(struct task_struct *)$next_t
printf "0x%08X/t", $next_t
__show_state $next_t
printf "%d/t%d/t%d/t%s/n", /
$next_t->uid, $next_t->pid, /
$next_t->parent->pid, $next_t->comm
$next_t=(char *)($next_t->tasks.next) - $tasks_off
printf "address/t/tstate/t/tuid/tpid/tppid/tcomm/n"
printf "----end----/n"
document psusr
print information for all tasks, but not including thread members.
This command looks like "ps -aux" in userspace.
define pskern
printf "address/t/tstate/t/tuid/tpid/tppid/tcomm/n"
set $init_t = &init_task
printf "0x%08X/t", $init_t
__show_state $init_t
printf "%d/t%d/t%d/t%s/n", /
$init_t->uid, $init_t->pid, /
$init_t->parent->pid, $init_t->comm
set $tasks_off=((size_t)&((struct task_struct *)0)->tasks)
set $thread_off=((size_t)&((struct task_struct *)0)->thread_group.next)
set $next_t=(((char *)($init_t->tasks).next) - $tasks_off)
while ($next_t != $init_t)
set $next_t=(struct task_struct *)$next_t
printf "0x%08X/t", $next_t
__show_state $next_t
printf "%d/t%d/t%d/t%s/n", /
$next_t->uid, $next_t->pid, /
$next_t->parent->pid, $next_t->comm
set $next_th=(((char *)$next_t->thread_group.next) - $thread_off)
while ($next_th != $next_t)
set $next_th=(struct task_struct *)$next_th
printf "0x%08X/t", $next_th
__show_state $next_th
printf "%d/t%d/t%d/t%s/n", /
$next_th->uid, $next_th->pid, /
$next_th->parent->pid, $next_th->comm
set $next_th=(((char *)$next_th->thread_group.next) - $thread_off)
$next_t=(char *)($next_t->tasks.next) - $tasks_off
printf "address/t/tstate/t/tuid/tpid/tppid/tcomm/n"
printf "----end----/n"
document pskern
print infor for all tasks viewed in kernel, including all thread members
and swapper(PID==0).
define __prinfo_nobp
printf "/npid %d; addr:0x%08x; comm %s:/n", /
$arg0.pid, $arg0, $m
printf "=====================================/n"
set var $stackp = $arg0.thread.sp
set var $stack_top = ($stackp & ~4095) + 4096
while ($stackp
_stext && *($stackp) tasks)
set $thread_off=((size_t)&((struct task_struct *)0)->thread_group.next)
set $init_t=&init_task
set $next_t=(((char *)($init_t->tasks).next) - $tasks_off)
while ($next_t != $init_t)
set $next_t=(struct task_struct *)$next_t
__prinfo_nobp $next_t
set $next_th=(((char *)$next_t->thread_group.next) - $thread_off)
while ($next_th != $next_t)
set $next_th=(struct task_struct *)$next_th
__prinfo_nobp $next_th
set $next_th=(((char *)$next_th->thread_group.next) - $thread_off)
set $next_t=(char *)($next_t->tasks.next) - $tasks_off
document bttnobp
dump all thread stack traces on a kernel compiled with !CONFIG_FRAME_POINTER
define __prinfo
printf "/npid %d; addr:0x%08x; comm %s:/n", /
$arg0.pid, $arg0, $m
printf "=====================================/n"
set var $stackp = $arg0.thread.sp
set var $stack_top = ($stackp & ~4095) + 4096
set var $stack_bot = ($stackp & ~4095)
set $stackp = *($stackp)
while (($stackp
$stack_bot))
set var $addr = *($stackp + 4)
info symbol $addr
set $stackp = *($stackp)
document __prinfo
internal macro, don't call it by hand.
define btt
set $tasks_off=((size_t)&((struct task_struct *)0)->tasks)
set $thread_off=((size_t)&((struct task_struct *)0)->thread_group.next)
set $init_t=&init_task
set $next_t=(((char *)($init_t->tasks).next) - $tasks_off)
while ($next_t != $init_t)
set $next_t=(struct task_struct *)$next_t
__prinfo $next_t
set $next_th=(((char *)$next_t->thread_group.next) - $thread_off)
while ($next_th != $next_t)
set $next_th=(struct task_struct *)$next_th
__prinfo $next_th
set $next_th=(((char *)$next_th->thread_group.next) - $thread_off)
set $next_t=(char *)($next_t->tasks.next) - $tasks_off
document btt
dump all thread stack traces on a kernel compiled with CONFIG_FRAME_POINTER
define btpid
set var $pid = $arg0
set $tasks_off=((size_t)&((struct task_struct *)0)->tasks)
set $thread_off=((size_t)&((struct task_struct *)0)->thread_group)
set $init_t=&init_task
set $next_t=(((char *)($init_t->tasks).next) - $tasks_off)
set var $pid_task = 0
while ($next_t != $init_t)
set $next_t=(struct task_struct *)$next_t
if ($next_t.pid == $pid)
set $pid_task = $next_t
set $next_th=(((char *)$next_t->thread_group.next) - $thread_off)
while ($next_th != $next_t)
set $next_th=(struct task_struct *)$next_th
if ($next_th.pid == $pid)
set $pid_task = $next_th
set $next_th=(((char *)$next_th->thread_group.next) - $thread_off)
set $next_t=(char *)($next_t->tasks.next) - $tasks_off
__prinfo $pid_task
document btpid
backtrace of pid
define trapinfo
set var $pid = $arg0
set $tasks_off=((size_t)&((struct task_struct *)0)->tasks)
set $thread_off=((size_t)&((struct task_struct *)0)->thread_group.next)
set $init_t=&init_task
set $next_t=(((char *)($init_t->tasks).next) - $tasks_off)
set var $pid_task = 0
while ($next_t != $init_t)
set $next_t=(struct task_struct *)$next_t
if ($next_t.pid == $pid)
set $pid_task = $next_t
set $next_th=(((char *)$next_t->thread_group.next) - $thread_off)
while ($next_th != $next_t)
set $next_th=(struct task_struct *)$next_th
if ($next_th.pid == $pid)
set $pid_task = $next_th
set $next_th=(((char *)$next_th->thread_group.next) - $thread_off)
set $next_t=(char *)($next_t->tasks.next) - $tasks_off
printf "Trapno %ld, cr2 0x%lx, error_code %ld/n", $pid_task.thread.trap_no, /
$pid_task.thread.cr2, $pid_task.thread.error_code
document trapinfo
Run info threads and lookup pid of thread #1
'trapinfo ' will tell you by which trap & possibly
address the kernel panicked.
define dmesg
set $i = 0
set $end_idx = (log_end - 1) & (log_buf_len - 1)
while ($i < logged_chars)
set $idx = (log_end - 1 - logged_chars + $i) & (log_buf_len - 1)
if ($idx + 100 <= $end_idx) || /
($end_idx <= $idx && $idx + 100 < log_buf_len)
printf "%.100s", &log_buf[$idx]
set $i = $i + 100
printf "%c", log_buf[$idx]
set $i = $i + 1
document dmesg
print the kernel ring buffer
宏名:vmap, lsvmaps, lsmod, lsmodsects, lsallmodsects
说明:没测试,待更新
来源 http://jeanmarc.saffroy.free.fr/kdump2gdb/
# Copyright Jean-Marc Saffroy
# This program is free software, distributed under the terms of the
# GNU General Public License version 2.
# a few useful(?) macros for x86-64 VMM hacks
# useful constants
set $PAGE_SIZE = (1<<12)
set $__PHYSICAL_MASK = (1 <> 39 & (1<> 30 & (1<> 21 & (1<> 12 & (1<<9)-1
# offset in page
set $off = $addr & (1<pgd
set $pgd_page = ((long)$pgd_off->pgd & $PTE_MASK) + $__PAGE_OFFSET
#printf "pgd_page: %lx/n", $pgd_page
set $pud_off = ((pud_t *) $pgd_page) + $pud
#printf "pud_off: %lx pud: %lx/n", $pud_off, (long)$pud_off->pud
set $pud_page = ((long)$pud_off->pud & $PTE_MASK) + $__PAGE_OFFSET
#printf "pud_page: %lx/n", $pud_page
set $pmd_off = ((pmd_t *) $pud_page) + $pmd
#printf "pmd_off: %lx pmd: %lx/n", $pmd_off, (long)$pmd_off->pmd
set $pmd_page = ((long)$pmd_off->pmd & $PTE_MASK) + $__PAGE_OFFSET
#printf "pmd_page: %lx/n", $pmd_page
if ((long)$pmd_off->pmd & $_PAGE_PSE) != 0
#printf "PSE page! "
set $paddr = $pmd_page + ($addr & (1<pte
set $pte_page = ((long)$pte_off->pte & $PTE_MASK) + $__PAGE_OFFSET
#printf "pte_page: %lx/n", $pte_page
set $paddr = $pte_page + $off
#printf "remapped physical addr: %lx/n", $paddr
printf "%lx -> %lx/n", $addr, $paddr
document vmap
Usage: vmap
Convert a kernel remapped virtual address to an identity-mapped address.
define lsvmaps
set $map = vmlist
set $gcount = 0
while $map != 0
if $map->pages != 0
set $vaddr = (long)$map->addr
set $count = (long)$map->size / $PAGE_SIZE
set $gcount = $gcount + $count -1
while $count > 1
vmap $vaddr
set $vaddr = $vaddr + $PAGE_SIZE
set $count = $count - 1
set $map = $map->next
printf "page count: %d/n", $gcount
document lsvmaps
List all kernel remapped pages (vmalloc regions) and corresponding identity-mapped pages.
define lsmod
set $mod = modules.next
printf "struct module
while $mod != &modules
set $m = (struct module *)((char*)$mod-(char*)(&((struct module*)0)->list))
printf "0x%lx % 8d %s/n", $m, $m->core_size, $m->name
set $mod = $mod->next
document lsmod
List loaded kernel modules.
define lsmodsects
set $mod = (struct module *)$arg0
printf "add-symbol-file %s.ko 0x%lx ", $mod->name, $mod->sect_attrs->attrs[0].address
set $i = 1
while $mod->sect_attrs->grp->attrs[$i] != 0
printf "-s %s ", (char*)$mod->sect_attrs->attrs[$i].name
printf "0x%lx ", $mod->sect_attrs->attrs[$i].address
set $i = $i + 1
printf "/n"
document lsmodsects
Usage: lsmodsects
Prints "add-symbol-file..." command to load sections of the given module.
define lsallmodsects
set $mdl = modules.next
while $mdl != &modules
set $m = (struct module *)((char*)$mdl-(char*)(&((struct module*)0)->list))
lsmodsects $m
set $mdl = $mdl->next
document lsallmodsects
Calls lsmodsects on all modules.
汇编基础--X86篇
注意:某些内容不具备普遍性。比如给出的反汇编代码,在不同的优化等级下是不同的。但是在熟悉了典型的函数调用链反汇编代码,对于有变化的其他形式也就不难理解了。
Intel(R) 64 and IA-32 Architectures Software Developer’s Manuals
/products/processor/manuals/index.htm
AT&T汇编格式
“AT&T汇编语言与GCC内嵌汇编简介” http://blog.chinaunix.net/u2/73528/showart_1110874.html
[杂类文章]
“Linux Assembly and Disassembly an Introduction” /papers/47
GCC-Inline-Assembly-HOWTO http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html
汇编与C函数的相互调用
调用链形成和参数传递
参考文章 [多如牛毛]
“Guide: Function Calling Conventions” /djgpp/doc/ug/asm/calling.html
“Intel x86 Function-call Conventions - Assembly View” http://www.unixwiz.net/techtips/win32-callconv-asm.html
“C Function Call Conventions and the Stack” http://www.cs.umbc.edu/~chang/cs313.s02/stack.shtml
“The C Calling Convention and the 8086: Using the Stack Frame” http://www.et.byu.edu/groups/ece425web/stable/labs/StackFrame.html
“C Function Calling Convention” http://adamw-/2007/05/c-function-calling-convention.html
“C函数调用在GNU汇编中的实现” http://www.unixresources.net/linux/clf/cpu/archive/00/00/59/75/597564.html
“函数调用的几个概念:_stdcall,_cdecl....” http://blog.chinaunix.net/u2/67530/showart_601750.html
“Calling conventions(调用规则)” /itschool/Program/delphi/200612/itschool_12084.html
[扩展,简要说明原理。并用实例解析]
x86终极参考
CHAPTER 6 PROCEDURE CALLS, INTERRUPTS, AND EXCEPTIONS of
IA-32 Intel_ Architecture Software Developer’s Manual Volume 1_ Basic Architecture.pdf /design/processor/manuals/253665.pdf
寄存器的角色与保护
寄存器的角色
1. %esp: 栈指针
指向栈的顶端,也就是指向栈的最后一个正在使用的元素。%esp的值隐式地受到几个机器指令的影响,比如push,pop,call,ret等。
2. %ebp: 基址指针
指向当前栈的基地址,有时也称为“帧指针”。与%esp不同的是,它必须显式地进行操作才能改变值。
3. %eip: 指令指针
保存着下一个被执行机器指令的地址。当CPU执行call指令时,%eip的值自动被保存到栈中。还有,任何一个“jump”跳转指令都会直接地改变%eip
1. gcc要求在函数调用的前后,寄存器%ebx,%esi,%edi,%ebp,%esp,%ds, %es,%ss的值保持不变。所以被调用函数如果需要修改这些寄存器的值,被调用函数必须负责对它们进行保护。[后三个??]
2. gcc规定在函数调用的前后,寄存器%eax,%edx,%ecx的值可以改变。所以调用函数如果需要防止子函数破坏这三个寄存器的值,调用者必须在函数调用前自己负责保护它们。
我们注意到,是保护,不一定是保存。如果确认没用到某寄存器,那么该寄存器就不需要一定要有一个先保存到栈而后再恢复原值的过程。
这两条规则实际是定义了对系统资源使用的权限和义务。
第一条规则,是银行和借贷者的关系。有人向银行借了几千万,结果赌博全输光了。还钱的期限到了,银行的行长对借贷者说“没事,你回家吧。几千万而已,我拿我工资给你垫上”。我想这样的事决不会发生,行长一个电话110过去,借贷者一天后就把钱还清了。所以,这里,调用函数是银行行长,子函数是借贷者。
第二条规则,则是老爸和儿子的关系了。儿子对老爸说“老爸,解我100去买球鞋,我明天还你”。结果,第二天,老爸没钱吃饭了,问儿子“还钱”。儿子说“昨晚逛街碰到一个美女,请了一顿,把钱化光了”。老爸没法子,总不能把儿子绳以正法吧。怪只能怪自己事前没防这招咯。所以,这里,调用函数是老爸,子函数是儿子你。
1. Integers (of any size up to 32 bits) and pointers are returned in the %eax register.
2. Floating point values are returned in the 387 top-of-stack register, st(0).
3. Return values of type long long int are returned in %edx:%eax (the most significant word in %edx and the least significant in %eax).
4. Returning a structure is complicat try to avoid it. (Note that this is different from returning a pointer to a structure.)
5. If your function returns void (e.g. no value), the contents of these registers are not used.
调用链的形成
应用层实例解析
我们回头看看“寄存器的角色”这一小节,很快就能明白调用链的形成的本质。
调用链包含两方面的内容
1.返回地址的保存与恢复
2.旧栈帧的保存与恢复
因为在普通的调用形式中(call调用),返回地址的保存与恢复是由处理器机制本身保证的,不需人工维护。调用指令call的执行自动将call指令之下的指令地址压入栈中,被调用函数返回时,ret指令的执行会重新将返回地址从栈弹出传送到pc中。要求下面分析旧栈帧的保存与恢复。
旧栈帧的保存与恢复,无非就是要解决两大问题:
1. 建立新栈帧 这一步很简单,栈帧无非有两个头,底端和顶端。%esp指向栈的顶端,而%esp是不需要手工维护的,随着push,pop等指令,它自己就在改变自己。那么又怎么建立栈帧的底端呢?我们知道,栈底(也就是基址)是由%ebp指定的,在一个栈帧的整个生命周期里,%ebp的值都不变,也就是说,赋个合适的值给它就完事。怎么赋值就是问题所在了。我们知道,%esp指向栈中最后一个被使用的元素。所以,当我们正在使用(我们认为的)第一个元素时,把%esp 的值赋给%ebp,%ebp不就是指向栈的基址了吗?
2. 保护旧栈帧的信息 同样的问题,保护旧栈帧的信息,就是保存旧栈帧指向底端和顶端的指针值,也就是旧%ebp,%esbp的值。当函数调用指令刚执行完,马上就要保护作案现场了。首先,push %ebp,这句就把旧栈帧的基地址保存在栈的顶端。此时,%esp指向的内存地址中,就放着旧栈帧的基地址的值。但是还不够啊,%esp是个不可靠的东西,它经常在变化,必须把这个地址放到一个不会隐式变化的寄存器中。于是选择了%ebp。mov %esp %ebp.这样,%ebp指向的内存地址中,就放着旧栈帧的基地址的值。这就解放了%esp,可以用%esp来动态指向新栈帧的顶端了。按照定义,%ebp所指向的地址是新栈帧的底端,也就是新栈帧的第一个元素,也就是说新栈帧第一个元素的值是旧栈帧基址。
但是注意,%ebp指向的地址再加4bytes的地址上,存放的是被调用函数的返回地址。在执行call指令时,call指令后面的那个指令的地址(也就是被调用函数的返回地址)被自动隐式地放到了栈中。
当子函数返回时,再按照上面文字进行逆操作,就能恢复旧栈帧的信息。
void func()
void funb()
void funa()
int main()
void func()
void funb()
e8 f3 ff ff ff
void funa()
e8 ee ff ff ff
0804835d :
int main()
8d 4c 24 04
0x4(%esp),%ecx
$0xfffffff0,%esp
-0x4(%ecx)
e8 e3 ff ff ff
-0x4(%ecx),%esp
func被调用后内存如下
+--------------/
+---+ main's %ebp
+-> +--------------+ --funa's frame
| ret to funa
+--------------+X
+---+ funa's %ebp
+-->+--------------+ ---funb's frame
| ret to funb
+--------------+
+---+ funb's %ebp
| +--------------+<---- %ebp
内核层实例解析
栈帧结构与参数传递
栈元素引用的就近原则
为了说明就近原则,我们先看看典型和全面的栈帧是怎样的。函数caller调用子函数callee所形成的栈帧。
1. 从被调用的子函数callee来看,获取caller的传递的实参,以及建立自身本地变量时,因为内存地址都靠近栈帧的基址,所以这两种引用都是利用%ebp加上偏移量的形式。
2. 相反,主函数在调用子函数前,在为子函数准备实参时,因为实参位于栈帧末端,所以对实参的引用都是利用%esp加上偏移量的形式(没画出来)
caller's frame pointer
+-------------------+
| caller saved
| %eax,%ecx,%edx
(as needed)
+-------------------+
| argument
| [%ebp+16]
+-------------------+
| argument
| [%ebp+12]
+-------------------+
| argument
| [%ebp+8]
+-------------------+
| return address
+-------------------+
+-----+ caller's %ebp
+-------------------+
| local var #1
| [%ebp-4]
+-------------------+
| local var #2
| [%ebp-8]
+-------------------+
+-------------------+
callee saved
callee stack frame
| %ebx,%esi,%edi
(as needed)
+-------------------+
|<----%esp
caller:调用者 callee:被调用者
完整的调用过程
函数caller调用子函数callee,这是应用层的普通函数调用过程。如果是远调用,跨态调用要考虑的东西更多。但这个例子已经充分展示了调用过程的繁复部分。
函数调用前调用者的动作
1.%eax,%edx,%ecx入栈(可选)
2.子函数的参数入栈
函数调用 call callee
call机器指令,原子性自动地完成了两种任务.
1.%eip入栈, 保存了callee函数的返回地址
2.callee的函数地址传递到%eip.
所以下一指令就从callee函数的第一指令开始运行。控制权转移给callee
函数调用后被调用者的动作
1.保存caller栈帧基址 push %ebp
2.建立callee栈帧基址 mov %esp,%ebp
3.分配本地变量和临时存储的空间 sub $XXX, %esp
4.本地变量赋值
5.%ebx,%esi,%edi入栈(可选)
调用返回前被调用者的动作
1.%ebx,%esi,%edi还原(出栈,可选)
2.释放本地变量和临时存储的栈空间mov %ebp,%esp
3.还原caller栈帧的基址 pop %ebp
或者2.3.步用一条元语指令完成 leave
4.调用返回 ret
该指令把存放于栈的返回地址取出(出栈),存放到%eip中。下一指令就从call callee指令的下一指令开始运行。控制权返回给caller
调用返回后调用者的动作
1.释放存放callee参数的栈空间 add $XXX, %esp
2.转移%eax的值(子函数的返回值,可选)
3.还原%eax,%edx,%ecx(出栈,可选)
应用层实例解析
应用层参数的传入: 用户层参数的传递是利用栈来完成的。函数右边的参数先入栈,位于栈的高地址。反之, 函数左边的参数后入栈,位于栈的低地址。
例子请看 “C难点的汇编解释”
内核层实例解析
内核层参数的传入: 混合使用寄存器和栈来传递参数。当参数个数不多于3个时,参数从左到右依次传递到%eax, %edx, %ecx.当参数个数多于3时,从第4个起的其余参数通过栈传递。同样,函数右边的参数先入栈,位于栈的高地址。反之, 函数左边的参数后入栈,位于栈的低地址。
系统调用实例解析
系统调用的参数传递:[以后再看]
ssize_t read(int fd, void *buf, size_t count);
000b6a30 :
65 83 3d 0c 00 00 00
$0x0,%gs:0xc
8b 54 24 10
0x10(%esp),%edx
8b 4c 24 0c
0xc(%esp),%ecx
8b 5c 24 08
0x8(%esp),%ebx
b8 03 00 00 00
//系统调用号
3d 01 f0 ff ff
$0xfffff001,%eax
e8 14 ae 01 00
8b 54 24 14
0x14(%esp),%edx
8b 4c 24 10
0x10(%esp),%ecx
8b 5c 24 0c
0xc(%esp),%ebx
b8 03 00 00 00
%eax,(%esp)
e8 c6 ad 01 00
3d 01 f0 ff ff
$0xfffff001,%eax
e8 8e 5a 04 00
81 c1 6c e5 07 00
$0x7e56c,%ecx
8b 89 e0 ff ff ff
-0x20(%ecx),%ecx
65 03 0d 00 00 00 00
%gs:0x0,%ecx
%edx,(%ecx)
$0xffffffff,%eax
调用号#define __NR_read
(gdb) disass sys_read
Dump of assembler code for function sys_read:
0xc017585a : push
0xc017585b : mov
0xc017585d : push
0xc017585e : mov
$0xfffffff7,%esi
0xc0175863 : push
0xc0175864 : sub
0xc0175867 : mov
0x8(%ebp),%eax
0xc017586a : lea
-0xc(%ebp),%edx
0xc017586d : call
0xc0175f65
0xc0175872 : test
0xc0175874 : mov
0xc0175876 : je
0xc01758b1
0xc0175878 : mov
0x24(%ebx),%edx
0xc017587b : mov
0x20(%eax),%eax
0xc017587e : mov
0x10(%ebp),%ecx
0xc0175881 : mov
%edx,-0x10(%ebp)
0xc0175884 : mov
0xc(%ebp),%edx
0xc0175887 : mov
%eax,-0x14(%ebp)
0xc017588a : lea
-0x14(%ebp),%eax
0xc017588d : push
0xc017588e : mov
0xc0175890 : call
0xc01753c1
0xc0175895 : mov
-0x10(%ebp),%edx
0xc0175898 : mov
0xc017589a : mov
-0x14(%ebp),%eax
0xc017589d : mov
%edx,0x24(%ebx)
0xc01758a0 : mov
%eax,0x20(%ebx)
0xc01758a3 : cmpl
$0x0,-0xc(%ebp)
0xc01758a7 : pop
0xc01758a8 : je
0xc01758b1
0xc01758aa : mov
0xc01758ac : call
0xc0175eae
0xc01758b1 : lea
-0x8(%ebp),%esp
0xc01758b4 : mov
0xc01758b6 : pop
0xc01758b7 : pop
0xc01758b8 : pop
0xc01758b9 : ret
End of assembler dump.
(gdb) list fget_light
* holds a refcnt to that file. That check has to be done at fget() only
* and a flag is returned to be passed to the corresponding fput_light().
* There must not be a cloning between an fget_light/fput_light pair.
317 struct file *fget_light(unsigned int fd, int *fput_needed)
来自2.6.11
378 #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, /
type5,arg5,type6,arg6) /
380 type name (type1 arg1,type2 arg2,type3 arg3,type4 arg4,type5 arg5,type6 arg6) /
382 long __ /
383 __asm__ volatile ("push %% movl %%eax,%% movl %1,%% int $0x80 ; pop %%ebp" /
: "=a" (__res) /
: "i" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2)), /
"d" ((long)(arg3)),"S" ((long)(arg4)),"D" ((long)(arg5)), /
"0" ((long)(arg6))); /
388 __syscall_return(type,__res); /
调用链回溯的代码实现
内核中(x86)对调用链的回溯的代码实现在文件dumpstack_32.c文件中。主要函数是dump_trace和 print_context_stack.
C难点的汇编解释
if ... else if
这个例子有人看来也许是非常非常地简单,但就这个例子,有的人还真给我考”倒”了。他的回话是“还真没见过这样子的代码”。但是,这样的代码在内核中比比皆是,比如后面附上的函数代码 do_path_lookup。如果对if ... else if 理解有偏差,对内核代码的逻辑理解根本就是差以千里。
int main()
int i = 1;
int j = 2;
if (i == 1)
printf("i,ok/n");
else if (j == 2)
printf("j,ok/n");
这个例子,有人会疑问为什么”j,ok”没打印出来。现在我们分析下它的汇编代码
8d 4c 24 04
0x4(%esp),%ecx
$0xfffffff0,%esp
-0x4(%ecx)
//以上汇编码保存旧栈帧信息,建立新栈帧
//%ecx入栈保护
$0x14,%esp
//建立本地变量栈空间,以及子函数实参栈空间
c7 45 f8 01 00 00 00
$0x1,-0x8(%ebp)
//变量i赋值,记得本地变量的地址靠近栈帧的基地址,所以用%ebp引用
c7 45 f4 02 00 00 00
$0x2,-0xc(%ebp)
//变量j赋值
83 7d f8 01
$0x1,-0x8(%ebp)
//i和1比较
//如果i-1不等0,跳到地址80483a7执行。否则继续执行下面指令
c7 04 24 90 84 04 08
$0x8048490,(%esp)
//printf函数第一个参数入栈,它的栈空间之前已经建好。
//记得子函数的实参空间靠近栈顶,所以引用实参用%esp
e8 2f ff ff ff
//调用printf
//printf返回后,接着执行这个指令,将跳到地址80483b9继续运行
83 7d f4 02
$0x2,-0xc(%ebp)
c7 04 24 95 84 04 08
$0x8048495,(%esp)
e8 1b ff ff ff
b8 00 00 00 00
//%eax赋值0,%eax放的也就是main函数返回结果
$0x14,%esp
//撤销新栈帧的本地变量栈空间,以及子函数实参栈空间
//恢复保存的旧%ecx的值
//以下汇编码都是恢复旧栈帧的信息,main函数返回等
-0x4(%ecx),%esp
经过上面的汇编代码分析,可见c代码块
else if (j == 2)
printf("j,ok/n");
对应的汇编代码是:
83 7d f4 02
$0x2,-0xc(%ebp)
c7 04 24 95 84 04 08
$0x8048495,(%esp)
e8 1b ff ff ff
上面的代码指令根本就没有机会运行。
结论,一个if ... else if ..else..
if (判断语句1)
else if (判断语句2)
else if ....
else 代码块N;
语句块1,2..N的运行机会是一种互斥的关系。当然它们的“机会优先级”是不一样的。语句块1,2..N只有一个有被运行的机会,如果没有 else甚至可能没有一个语句块能被运行。
内核代码实例
static int do_path_lookup(int dfd, const char *name,
unsigned int flags, struct nameidata *nd)
int retval = 0;
struct file *
struct fs_struct *fs = current->
nd->last_type = LAST_ROOT; /* if there are only slashes... */
nd->flags =
nd->depth = 0;
if (*name=='/') {
read_lock(&fs->lock);
if (fs->altroot.dentry && !(nd->flags & LOOKUP_NOALT)) {
nd->path = fs->
path_get(&fs->altroot);
read_unlock(&fs->lock);
if (__emul_lookup_dentry(name,nd))
/* found in altroot */
read_lock(&fs->lock);
nd->path = fs->
path_get(&fs->root);
read_unlock(&fs->lock);
} else if (dfd == AT_FDCWD) {
read_lock(&fs->lock);
nd->path = fs->
path_get(&fs->pwd);
read_unlock(&fs->lock);
struct dentry *
file = fget_light(dfd, &fput_needed);
retval = -EBADF;
if (!file)
dentry = file->f_path.
retval = -ENOTDIR;
if (!S_ISDIR(dentry->d_inode->i_mode))
goto fput_
retval = file_permission(file, MAY_EXEC);
if (retval)
goto fput_
nd->path = file->f_
path_get(&file->f_path);
fput_light(file, fput_needed);
retval = path_walk(name, nd);
if (unlikely(!retval && !audit_dummy_context() && nd->path.dentry &&
nd->path.dentry->d_inode))
audit_inode(name, nd->path.dentry);
fput_fail:
fput_light(file, fput_needed);
短路逻辑算法。
这样的例子在内核代码中也是非常地多,一般用在短的函数或宏中。
int main()
int a = 1;
int b = 2;
if (a || ++b)
printf("%d/n", b);
这个例子,有人会疑问为什么b的值没有变化,还是为2。现在我们分析下它的汇编代码
8d 4c 24 04
0x4(%esp),%ecx
$0xfffffff0,%esp
-0x4(%ecx)
//以上汇编码保存旧栈帧信息,建立新栈帧
//%ecx入栈保护
$0x24,%esp
//创建本地变量和子函数实参的栈空间(实际上没全部使用到)
c7 45 f8 01 00 00 00
$0x1,-0x8(%ebp)
//变量a赋值,记得本地变量的地址靠近栈帧的基地址,所以用%ebp引用
c7 45 f4 02 00 00 00
$0x2,-0xc(%ebp)
//变量b赋值
83 7d f8 00
$0x0,-0x8(%ebp)
//变量a和0比较,其实就是判断“表达式 a”是不是为假
//a-0如果不等0,也就是a为真时就跳到地址80483a3执行。
//已经知道a==1,表达式a为真,所以将跳到地址80483a3执行
83 45 f4 01
$0x1,-0xc(%ebp)
83 7d f4 00
$0x0,-0xc(%ebp)
-0xc(%ebp),%eax
//把变量b的值放到临时寄存器%eax
89 44 24 04
%eax,0x4(%esp)
//接着把它作为printf函数第二个实参入栈,
//记得子函数的实参空间靠近栈顶,所以引用实参用%esp
c7 04 24 90 84 04 08
$0x8048490,(%esp)
//printf函数第一个实参入栈。记得X86下用户层的子函数参数
//是保存到栈的,而且是从右到左依次入栈
e8 22 ff ff ff
//调用printf函数
b8 00 00 00 00
//%eax赋值0,%eax放的也就是main函数返回结果
$0x24,%esp
//撤销新栈帧的本地变量栈空间,以及子函数实参栈空间
//恢复保存的旧%ecx的值
//以下汇编码都是恢复旧栈帧的信息,main函数返回等
-0x4(%ecx),%esp
分析可见C语句 if (a || ++b)中的++b对应的汇编码是
83 45 f4 01
$0x1,-0xc(%ebp)
83 7d f4 00
$0x0,-0xc(%ebp)
可是因为a==1,表达式a已经为真,++b这个语句,也就是上面的汇编码,根本就没运行。所以变量b的值没有自增,还是保持为2。
表达式 a, b
a || b: 如果a为真,b就不管;如果运行到b,a必已是假
a && b: 如果a为假,b就不管;如果运行到b,a必已是真
内核代码实例
static struct char_device_struct *
__register_chrdev_region(unsigned int major, unsigned int baseminor,
int minorct, const char *name)
i = major_to_index(major);
for (cp = &chrdevs[i]; * cp = &(*cp)->next)
if ((*cp)->major > major ||
((*cp)->major == major &&
(((*cp)->baseminor >= baseminor) ||
((*cp)->baseminor + (*cp)->minorct > baseminor))))
自增自减,以及增减的前后问题。这类代码在内核数不胜数。理解稍有偏差,就会产生“边界问题”,或者在条件判断时理解出错。
int main()
int i = -1;
if (!i++) {
printf("inner: %d/n", i);
printf("outer: %d/n", i);
8d 4c 24 04
0x4(%esp),%ecx
$0xfffffff0,%esp
-0x4(%ecx)
$0x24,%esp
c7 45 f8 ff ff ff ff
$0xffffffff,-0x8(%ebp)
83 45 f8 01
$0x1,-0x8(%ebp)
83 7d f8 01
$0x1,-0x8(%ebp)
-0x8(%ebp),%eax
89 44 24 04
%eax,0x4(%esp)
c7 04 24 90 84 04 08
$0x8048490,(%esp)
e8 2f ff ff ff
-0x8(%ebp),%eax
89 44 24 04
%eax,0x4(%esp)
c7 04 24 9b 84 04 08
$0x804849b,(%esp)
e8 1c ff ff ff
b8 00 00 00 00
$0x24,%esp
-0x4(%ecx),%esp
内核代码实例
int platform_add_devices(struct platform_device **devs, int num)
int i, ret = 0;
for (i = 0; i = 0)
/*没错,devs[i]没注册成功的话,从devs[i-1]起反注册*/
platform_device_unregister(devs[i]);
解释在“穿越交叉索引工具的盲区”→函数指针
int main()
int myfunc(int a, int b)
int c = a +
printf("%d/n", c);
int (*funa)(int, int) =
int (*funb)(int, int) = &
int (*func)(int, int) = (int (*)(int, int))
int (*fund)(int, int) = (int (*)(int, int))(&myfunc);
myfunc(1, 2);
funa(3, 4);
funb(5, 6);
func(7, 8);
fund(9, 10);
$ gcc -g -Wall fuk.c //注意,没任何警告
int main()
8d 4c 24 04
0x4(%esp),%ecx
int (*funa)(int, int) =
c7 45 f8 13 84 04 08
$0xx8(%ebp)
int (*funb)(int, int) = &
c7 45 f4 13 84 04 08
$0xxc(%ebp)
int (*func)(int, int) = (int (*)(int, int))
c7 45 f0 13 84 04 08
$0xx10(%ebp)
int (*fund)(int, int) = (int (*)(int, int))(&myfunc);
c7 45 ec 13 84 04 08
$0xx14(%ebp)
myfunc(1, 2);
funa(3, 4);
c7 44 24 04 04 00 00
$0x4,0x4(%esp)
c7 04 24 03 00 00 00
$0x3,(%esp)
-0x8(%ebp),%eax
funb(5, 6);
....省略,funb, func,fund汇编码和funa完全相同
b8 00 00 00 00
$0x24,%esp
int main()
int myfunc(int a, int b)
xxx@ubuntu:~/dt/test$ gdb a.out
GNU gdb 6.8-debian
(gdb) list
1 #include
funa(3, 4);
(gdb) b 17
Starting program: /home/xxx/桌面/test/a.out
Breakpoint 1, main () at fuck.c:17
funa(3, 4);
(gdb) display/i $pc
1: x/i $pc
0x80483b5 : movl
$0x4,0x4(%esp)
(gdb) stepi
0x080483bd 17
funa(3, 4);
1: x/i $pc
0x80483bd : movl
$0x3,(%esp)
funa(3, 4);
1: x/i $pc
0x80483c4 : mov
-0x8(%ebp),%eax
funa(3, 4);
1: x/i $pc
0x80483c7 : call
(gdb) p/x $eax
$4 = 0x8048413
(gdb) info line
*0x8048413
Line 6 of "fuck.c" starts at address 0x8048413
and ends at 0x8048419 .
优化级别的影响
这部分内容有点偏题,没必要这么钻牛角尖。但是为了说明“调试用的代码和实际运行的代码是不一样”的这个事实以及因为代码优化导致的“非理想状态” 的调用链问题(见“内核初窥”),有必要用观察一个实例,以便有个直观的印象。
首先应该知道,有没有指定调试选项-g(–debug),在相同优先级下生成的代码都是一样的。差别只是,指定-g后,多生成了一个调试表。
下面文字来自“ARM 系列应用技术完全手册”
使用-Onum选择编译器的优化级别。优化级别分别有
-O0: 除一些简单的代码编号外,关闭所有优化,该选项可提供最直接的优化信息。
-O1: 关闭严重影响调试效果的优化功能。使用该编译选项,编译器会移除程序中未使用到的内联函数和静态函数。如果于–debug(也就是-g)一起使用,该选项可以在较好的代码密度下,给出最佳调试视图。
-O2: 生成充分优化代码。如果与–debug一起使用,调试效果可能不令人满意,因为对目标代码到源代码的映射可能因为代码优化而发生变化。如果不生成调试表,这是默认优化级别。
-O3: 最高优化级别。使用该优化级别,使生成的代码在时间和空间上寻求平衡。
int add(int a, int b)
return (a + b);
void funa()
int a = 3 + 4;
printf("%d/n", a);
b = add(5,6);
printf("%d/n", b);
int main()
int m = 1 + 2;
printf("%d/n", m);
$ gcc -g -O0 src.c (或者不指定优化选项: gcc -g src.c,编译出的机器码一样)
$ objdump -d a.out
得到一个结论:如果指定了-g而没指定优化等级,那么默认优化等级是最低的-O0
0xc(%ebp),%eax
804837a: 03 45 08
0x8(%ebp),%eax
804837d: 5d
804837e: c3
0804837f :
804837f: 55
//保存旧栈帧,建立新栈帧
$0x18,%esp
//分配栈帧空间,注意分配了$0x18
8048385: c7 45 fc 07 00 00 00
$0x7,-0x4(%ebp)
//-0x4(%ebp)是本地变量a的地址,int a = 3 + 4;
//注意编译器已经完成了计算
804838c: 8b 45 fc
-0x4(%ebp),%eax
//a放到临时寄存器%eax
804838f: 89 44 24 04
%eax,0x4(%esp)
//接着作为printf第二个参数入栈
8048393: c7 04 24 d0 84 04 08
$0x80484d0,(%esp) //printf第一个参数入栈
804839a: e8 39 ff ff ff
//printf("%d/n", }

我要回帖

更多关于 D/A 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信