免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 822 | 回复: 0
打印 上一主题 下一主题

[内核入门] Linux汇编(使用场合、AT&T、嵌入C) [复制链接]

论坛徽章:
13
15-16赛季CBA联赛之八一
日期:2016-07-08 21:00:1415-16赛季CBA联赛之同曦
日期:2017-02-15 14:26:1515-16赛季CBA联赛之佛山
日期:2017-02-20 14:19:2615-16赛季CBA联赛之青岛
日期:2017-05-07 16:49:1115-16赛季CBA联赛之广夏
日期:2017-07-30 09:13:1215-16赛季CBA联赛之广东
日期:2018-07-05 22:34:3615-16赛季CBA联赛之江苏
日期:2018-09-03 12:10:2115-16赛季CBA联赛之上海
日期:2018-09-25 03:49:2215-16赛季CBA联赛之广东
日期:2018-09-25 04:09:12
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2016-09-16 14:50 |只看该作者 |倒序浏览
本帖最后由 _nosay 于 2016-09-16 15:15 编辑

  • Linux内核使用汇编的场合
    汇编看起来是一门很装逼的语言,乱七八糟的,很多人对这门语言的第一错觉肯定是:大牛才会去碰。
    其实不是这样,语言只负责表达,熟悉了都一样,高级语言在表达方面确实更有优势一些,但从整个编程的大局观来看,难的主要是构思和想法,表达是次要的,所以不要过于吹嘘自己用的语言最天下无敌,大型的程序不用面向对象语言就不行等等,举个最简单的例子,Linux内核这么大个程序,怎么都是c和汇编写的呢,是吧。每种语言都有适合使用它的场合,比如对内存/执行效率要求不高,对开发进度要求比较高的,c++可能比较合适,对内存/执行效率要求较高的,c可能就比较合适。
    汇编在Linux内核中使用的场合大致有如下几点:
    ① 对指令块大小有特殊限制的,比如系统引导程序,由于要能装进引导区(即第一个扇区),所以必须用汇编写甚至机器码编写,才能做到精打细算;
        分享一个有意思的东西,但需要一点背景知识:
        @ 机器码“75 xx”,表示一条jnz汇编指令,跳转“xx+当前机器码长度(2byte)”这么远,比如“75 00”相当于“jnz 02”,“75 EF”相当于“jnz 0”(没跳!)
        @ 机器码“04 AC”,表示一条“ADD AL, 0AC”汇编指令(先不要纠结机器码是怎么来的,不看Intel文档,自己看不出来规律的)
        @ 机器码“AC”,表示汇编指令lodsb
        OK,背景知识够了,其实就是知道三个对应关系:“75 xx”-jnz x、“04 AC”-ADD AL, 0AC、“AC”-lodsb。
        现在如果想表达这样一段逻辑:
  1. ; 伪代码
  2. if ZF=0
  3.     lodsb
  4. else
  5.     add al, 0AC
复制代码
       代码1,生成机器码“75 04 04 AC EB 01 AC”:
  1. jnz 1
  2. add al, 0AC
  3. jmp 2
  4. 1:
  5. lodsb
  6. 2:
复制代码
       代码2,生成机器码“75 01 04 AC”:
  1. jnz $+1
  2. add al, 0AC
复制代码
       C程序→汇编程序→机器码,整个过程由编译器完成,所以这两个过程都可能有优化空间,比如上述例子经过手动优化一下,利用了“add al, 0AC”指令中现存的“AC”,就为最终的可执行程序节省了3个字节。
    ② 对执行效率要求极高的场合,同①,编译器编译出来的代码,在执行过程上可能不是最优;
    ③ 内核中经常要访问或设置一些寄存器,C语言里面没有这样的语法成分。


  • AT&T格式汇编
    没有理解难度,需要查资料与Intel格式对比,不需要死记硬背,多看些代码自然就知道了。


  • 嵌入到C语言中的汇编代码
    Linux内核中有纯汇编的代码文件.S,没有什么好介绍的,所以主要看一段嵌入到C函数中的汇编代码:
  
    ① 无脑将最后面的部分编号(从0开始);
    ② 将这段代码分成两部分看:“{}”内除掉“__asm__ __volatile__();”(C地盘)、“__asm__ __volatile__();”(asm地盘)
    ③ 第一个冒号开始,表示“输出部”,表示这段代码执行完,哪些变量或寄存器的值从逻辑上讲是会被修改的;
        第二个冒号开始,表示“输入部”,对寄存器或C地盘的变量进行初始化;
        第三个冒号开始,表示“损坏部”,~!@#$^&……
        其中"aa"(bb)这种,不妨认为它是一种“捆绑”操作,比如"=&c"(d0),就表示将ecx寄存器与d0变量“捆绑”,修改ecx就是修改d0,修改d0就是修改ecx(关于更多类似&c代表ecx这种,需要自己查资料总结,不需要死记硬背)。
        另外,除了可以用"&c"表示“捆绑对象”外,还可以用编号,比如"1" ((long) to),表示将编号1处的edi寄存器/d1变量,与C的to参数“捆绑”。
    ④ 数据拷贝是个非常底层的操作,系统随便运行一会,就不知道要调用多少多少多少次,所以运行效率就非常重要,所以虽然__memcpy()是个C函数,但内部完全用汇编编写,会比C写出来再经过编译得到的更精悍。        分如下几个步骤分析它的逻辑:
        @ 初始化(看输入部):0/ecx/d0 = n/4, q(eax/ebx/ecx/edx之一) = n, 1/eid = to, 2/esi = from;
        @ "rep; movsl\n\t":重复执行movsl n/4(ecx)次,页movsl是从esi拷由4byte到edi,并esi+4, edi+4,执行完可能还剩1/2/3byte没有拷贝;
        @ "testb $2,%b4\n\t":AT&T要求立即数前面要加$,%加编号表示去输入/输出部对应,所以这句就是判断%b4(n)的低第二位是否为1,如果为1,则至少还有2byte没有拷,则执行movsw再拷2byte,否则"je 1f\n\t"("f"表示forward,往前方最近的1标号处跳转,也有"je 1b\n\t");
        @ "1:testb $1,%b4\n\t":经过以上的步骤,现在最多还剩1byte没的拷,如同上一步的的道理,进行处理,代码就执行完了。




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

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP