前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何找到linux内核中at&t风格的汇编指令最权威最详细的文档

如何找到linux内核中at&t风格的汇编指令最权威最详细的文档

作者头像
KINGYT
发布2020-05-01 18:09:52
3.9K1
发布2020-05-01 18:09:52
举报

汇编的语法风格分为两种,一种是intel风格,一种是at&t风格,intel风格主要用于windows平台,at&t风格主要用于unix平台。

因为linux是类unix型的操作系统,所以其内核中的汇编代码也是使用的at&t风格。

在编译linux内核时,默认使用的编译器是gcc,当涉及到内核汇编代码的编译时,gcc通过调用gnu的as命令来完成,as命令官方文档地址如下:

https://sourceware.org/binutils/docs-2.34/as/index.html

既然linux内核的汇编代码是根据as命令指定的格式编写的,那理论上来说,as的官方文档中应该有at&t风格的汇编指令的相关描述。

可惜并没有。

这种情况下,当我们在看linux内核的汇编代码时,只能通过阅读在网上找到的一些零散的at&t风格的汇编文档,以此来尝试理解内核逻辑。

但很多时候,这些文档并不能给出一个精准全面的解释,致使我们有时无法真正理解内核代码的用意。

那到哪里才能找到最精确,最全面的汇编指令相关解释呢?

下面我们来说个方法。

我们知道,不管是at&t风格,还是intel风格,最终这些汇编代码都会被编译成cpu可识别的二进制编码的机器指令,这个是唯一的,是不会随着汇编代码写法风格的改变而改变的。

对于x86型的cpu来说,其可识别的汇编指令,及该指令对应的编码格式的最权威最详细的文档介绍,莫过于 Intel® 64 and IA-32 Architectures Software Developer’s Manual 文档了,其官方地址如下:

https://software.intel.com/sites/default/files/managed/39/c5/325462-sdm-vol-1-2abcd-3abcd.pdf

在我们阅读linux内核代码的过程中,当遇到有疑问的at&t风格的汇编指令时,我们只需要查看该汇编指令编译后的二进制格式的机器指令,然后通过这些机器指令数据,在上面的intel sdm文档中找到对应的intel汇编指令,这样我们就算是找到了该at&t风格的汇编指令最精确最权威的定义了。

我们举个例子看下。

如果没有使用boot loader,比如grub,而是直接启动编译好的linux内核,执行的第一条汇编指令是ljmp:

假设我们对这条指令有些疑问,想看下其具体的文档说明,下面来看下要怎么做。

首先,我们用objdump反编译下这部分代码,查看其编译后的机器指令是什么样的:

代码语言:javascript
复制
$ objdump -S arch/x86/boot/setup.elf | more

其输出如下:

linux内核编译后的可运行文件是bzImage,而setup.elf正是bzImage文件的开始部分,所以我们上面是objdump的setup.elf,而不是bzImage。

我们用以下命令再确认下setup.elf确实是bzImage的开始部分:

对比以上两个图片,我们会发现它们的字节顺序及内容都是一样的,所以确定了我们上面的说法。

我们再继续看第一个图片中的反编译内容。

该内容中,前两个字节分别是4d 5a,其对应为内核代码中MZ_MAGIC宏的定义:

代码语言:javascript
复制
// include/linux/pe.h#define MZ_MAGIC        0x5a4d  /* "MZ" */

由上可见,MZ_MAGIC宏的值是0x5a4d,这个和我们反编译出来的字节顺序正好相反,这说明我们用的机器是小端模式。

接下来再看ljmp指令对应的字节内容:ea 07 00 c0 07。

由intel sdm文档可知,ea 对应的汇编指令可能有下面两种情况(选中行):

我们再对应看下文档中描述的 ea 后面的 cd 和 cp 的定义:

由上可知,cd 和 cp 分别表示该汇编指令(ea)后会有4个字节或6个字节的操作数。

结合我们上面反编译后的ljmp指令的字节数据 ea 07 00 c0 07,以及内核初始是在16位的real-address mode下执行,我们可以最终得到结论,该 ljmp 指令对应的就是上面 ea cd 代表的那行 intel 汇编指令 JMP ptr16:16。

该指令表示其是一次 far jump,看下其更详细的描述:

由上可知,我们要jump到的目标,是由segment selector和offset组成的一个4个字节的数值。

再结合最开始提到的,这条ljmp指令的原始代码是 ljmp $BOOTSEG, $start2,我们可根据BOOTSEG的值0x07C0和start2的值 0x0007(由反汇编结果得到),最终拼出一个4个byte组成的值 07 C0 00 07。

又由于我们是小端模式,所以该值在存储时要把顺序反过来,最终值为 07 00 C0 07。

再加上ljmp对应的值 ea,最终这条ljmp指令编译后的字节顺序应该为 ea 07 00 c0 07,对比一下上面反汇编的字节顺序:

是不是刚好一样。这就进一步确认了,我们找到的ljmp对应的intel汇编指令是正确的。

通过这种方式,我们就可以找到任意at&t风格的汇编指令最权威,最详尽的描述了。

好了,就这些,希望对你有所帮助。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-04-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Linux内核及JVM底层相关技术研究 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档