0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

嵌入式汇编中go to到c代码label最简单的用法

Linux阅码场 来源:CSDN 作者:dog250 2021-04-04 17:18 次阅读

越来越多的工作现如今都交给了编译器,甚至连动态代码修改的数据组织这种事都交给了编译器。gcc提供了一个特性用于嵌入式汇编,那就是asm goto,其实这个特性没有什么神秘之处,就是在嵌入式汇编中go to到c代码的label,其最简单的用法如下(来自gcc的文档):

d0f90b8c-8cdd-11eb-8b86-12bb97331649.png

asm goto其实就是在outputs,inputs,registers-modified之外提供了嵌入式汇编的第四个“:”,后面可以跟一系列的c语言的label,然后你可以在嵌入式汇编中go to到这些label中一个。然而使用asm goto可以巧妙地将“一个大家都能想到的点子”规范化,就是说你只需要调用一个统一的接口--一个宏,编译器就将你想实现的东西给实现了,要不然代码写起来会很麻烦,这点上,编译器不嫌麻烦。这一个大家都能想出的点子的由来还得从内核的效率说起。

以下的代码来自lwn的《Jump label》:

d11a53be-8cdd-11eb-8b86-12bb97331649.png

即使有了unlikey优化,既然有if判断,cpu的分支预测就有可能失败,再者do_trace在代码上离if这么近,即使编译器再聪明,二进制代码的do_trace也不会离前面的代码太远的,这样由于局部性原理和cpu的预取机制,do_trace的代码很有可能就被预取入了cpu的cache,就算我们从来不打算trace代码也是如此。

我们需要的是如果不开启trace,那么do_trace永远不被欲取或者被预测,唯一的办法就是去掉if判断,永远不调用goto语句,像下面这样:

d16ccc02-8cdd-11eb-8b86-12bb97331649.png

在运行时修改载入内存的二进制代码就是我们大家都能想到的点子,就是说在运行的时候当我们知道trace_foo_enabled在某一时刻被设置为0的时候,我们动态的将二进制代码修改掉,将if代码段去掉,这样一个分支预测就不存在了,而且trace_foo_enabled这一个变量也不需要再被访问了(该变量在内存中,访问它肯定会涉及load/flush cache的动作,为了一个很可能没有用的变量操作cache很不值)。提前要说的是,我们可以使用这种方式去掉所有的分支预测,然而这并不可取,因为程序是动态运行的,很多用于判断的变量值都是根据程序的执行瞬息万变,正是这种根据判断结果采取不同动作的机制给与了程序灵活性,如果每当我们确定一个值时就修改二进制代码取消分支预测的话,其本身的开销将会远远大于分支预测的开销,更重要的是,紧接着那个值又变化了,我们不得不再次修改二进制代码,这期间要访问那个变量好几次。所以,只有在我们确定不经常变化的变量的判断上才能用这种方式取消分支预测,而像trace与否的判断正好符合我们的需求。

gcc编译器提供了asm goto的机制来满足我们的需求,使得我们可以在asm goto的基础上构建出一个叫做jump label的东西。下面的代码段说明了jump label的用法和原理:

d1e01e96-8cdd-11eb-8b86-12bb97331649.png

标号0仅仅执行一个nop,不涉及cache,后面的pushsection保存现有的section,很多情况下当前的section就是text,然后定义一个“表”,表中有两个元素:0b和trace#NUM,其实就是两个标号,在asm goto机制中,标号还可以更多,它们在嵌入式汇编的最后一个“:”后面依次排布。这些标号就是供选择的标号,执行流将跳入其中的一个标号处,具体跳到哪一个就看当前的二进制代码被修改成了“跳到哪一个”,因此asm goto为我们做的仅仅是提供一个地方(一个“:”)供我们将label传入,保存了一系列的表还是需要我们的c代码逻辑--jump label实现,这些表(其实就是一系列的三元组)方便我们根据这些表来修改运行中的二进制代码,最终修改二进制代码还是要由我们自己写代码完成的。

有了这个asm goto以及我们jump label代码的支持,内核对于是否trace这种小事就再也不用愁了(使用中的kernel一般是不用trace的,只有在出了问题以后或者调试内核时才使用trace,因此在主代码中加入“是否trace”的判断实在是一种沉重的负担),如果对于某一个函数不需要trace,内核只需要执行一个操作将asm goto附近的代码改掉即可,比如改称下面这样:

d1fcdf54-8cdd-11eb-8b86-12bb97331649.png

如果需要trace,那么就改成:

d227dd30-8cdd-11eb-8b86-12bb97331649.png

这一切在kernel中的用法如下:

d246edba-8cdd-11eb-8b86-12bb97331649.png

第一行的“1”是一个标号,该标号后的代码执行的内容就是nop-第二行,第三行重新开始了一个section,这样的意义很大,下面的三元组:[instruction address] [jump target] [tracepoint key]的二进制代码就不会紧接着标号1(nop)了,这个三元组就是jump label机制的核心,指示了所有可能跳转到的标号,这里的技巧在于标号1,标号1也作为一个合法的可能跳转到的标号存在,和标号label是并列的,由于pushsection和popsection的存在,上面的代码汇编结果看起来是下面这样:

d262c2c4-8cdd-11eb-8b86-12bb97331649.png

如果启用了trace,那么只需要将标号1修改成标号label就可以了:

d2b347f8-8cdd-11eb-8b86-12bb97331649.png

内核之所以能够找到需要修改代码的地址,就是借助于上面说的那个三元组(instruction address,jump target,tracepoint key),其中instruction address就是这个地址,在linux的JUMP LABEL机制中,它固定为标号1,也就是nop的标号,如果不启用trace,那么直接执行nop,如果启用了trace,那么将nop修改为jmp label即可,如果后来又禁用了trace,只需将它再次修改成三元组中的标号1即可,这一切过程中,三元组本身是不会改变的。注意,三元组中的tracepoint key在jump label机制中并没有什么实质的意义,它仅仅是为了组织kernel中“是否trace”变量用的,所有的“是否trace”变量组织成一个链表,链表的每一个节点下面挂着另一个子链表,该子链表中元素是所有使用这个“是否trace”变量的代码环境,包括代码的地址,标号的地址等。

下面看一下kernel对于JUMP_LABEL的实现框架。首先看一下三元组的数据结构:

d2f035e6-8cdd-11eb-8b86-12bb97331649.png

其次一个比较重要的数据结构是一个key节点,表示一个“是否trace”的变量:

d33b7420-8cdd-11eb-8b86-12bb97331649.png

启用一个trace意味着需要将一个key(类似于trace_foo_enabled)设置为1,然后修改所有判断该key的代码附近的二进制代码:

d3842de6-8cdd-11eb-8b86-12bb97331649.png

d3b8bc78-8cdd-11eb-8b86-12bb97331649.png

以上就是使用asm goto实现的jump label,在2.6.37内核中被引入。

附:.section以及.previous

在汇编语言中使用.section和.previous指令可以将它们之间的代码编译到不同的section中,也就是不紧接着.section上面的代码。linux kernel中的异常处理就是用这两个伪指令实现的,定义了一个叫做fix的section和一个叫做ex_table的section,可能出现exception的代码用一个标号表示,ex_table中保存了一些二元组(出现异常代码的标号,异常处理程序的标号),异常处理程序在fix这个section中,这样虽然代码看起来是下面这样:

d3f8c854-8cdd-11eb-8b86-12bb97331649.png

然而编译器会将fix和ex_table放到离text很远的地方的,这样cpu预取时就不会将fix或者ex_table的代码预取到执行cache了,只有在发生异常的时候才会使用fix和ex_table,而发生异常毕竟是一种罕见现象,这就是一种优化。

原文标题:asm goto与JUMP_LABEL

文章出处:【微信公众号:Linuxer】欢迎添加关注!文章转载请注明出处。

责任编辑:haq

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 代码
    +关注

    关注

    30

    文章

    4556

    浏览量

    66814
  • 编译器
    +关注

    关注

    1

    文章

    1577

    浏览量

    48627

原文标题:asm goto与JUMP_LABEL

文章出处:【微信号:LinuxDev,微信公众号:Linux阅码场】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    如何成为一名嵌入式C语言高手?

    。 三、通过实践项目提升技能理论知识是建立在实践基础之上的。选择一些小型的嵌入式项目,例如LED闪烁、温度监测等简单的应用,将所学的C语言知识应用到实际。通过实践,你可以了解如何将
    发表于 04-07 16:03

    如何成为一名嵌入式C语言高手?

    。 三、通过实践项目提升技能理论知识是建立在实践基础之上的。选择一些小型的嵌入式项目,例如LED闪烁、温度监测等简单的应用,将所学的C语言知识应用到实际。通过实践,你可以了解如何将
    发表于 03-25 14:12

    嵌入式工程师需要掌握哪些技术?

    一些必要的技术能力是至关重要的。在本篇,我们将讨论入行嵌入式所必须的技术能力。 1.C/C++编程能力:C/
    发表于 03-04 16:38

    嵌入式学习-ElfBoard ELF 1开发板-共创官学习笔记分享|将Go程序编译ELF 1开发板

    ElfBoard组建的共创社是嵌入式科技创新与学习实践的前沿阵地,我们有幸见证着每一位共创官积极投身于嵌入式技术的热潮,用实际行动诠释着探索精神。今天就跟各位小伙伴分享一下共创官是怎样将Go
    发表于 02-21 10:22

    嵌入式学习步骤

    硬件组件。 (4).开发固件:编写嵌入式系统的固件,这是嵌入式系统的软件部分。固件负责控制硬件并执行特定任务。 (5).调试和测试:在将嵌入式系统部署实际环境
    发表于 02-02 15:24

    嵌入式自学好书推荐

    嵌入式自学好书推荐 在数字时代的浪潮嵌入式系统一直是数字电子产品的重要组成部分。无论是家用电器、工业控制、汽车电子、医疗保健、军事应用还是物联网,
    发表于 01-11 15:13

    如何成为一名优秀的嵌入式工程师?

    来分配和释放内存。同时,也需要理解指针的概念及用法,以及指针在内存管理的作用。 2.掌握指针:指针是C语言的一个重要特性,它允许你直接操作内存。理解指针的概念和用法对于
    发表于 11-07 15:36

    什么是__attribute__?嵌入式C代码属性怎么定义?

    嵌入式开发,离不开 C 语言,C语言中有很多语法会直接或间接影响你代码的质量,下面就来讲讲__attribute__ 关键字的用法
    的头像 发表于 10-13 15:55 2056次阅读
    什么是__attribute__?<b class='flag-5'>嵌入式</b>C<b class='flag-5'>代码</b>属性怎么定义?

    什么是嵌入式Linux?

    Linux到底是什么呢? 嵌入式linux 是将日益流行的Linux操作系统进行裁剪修改,使之能在嵌入式计算机系统上运行的一种操作系统。简单来说,是除了电脑之外可以运行程序的设备,将CPU嵌入
    发表于 10-11 13:47

    IAR在进行C语言代码开发的时候怎么嵌入汇编代码

    IAR在进行C语言代码开发的时候怎么嵌入汇编代码
    发表于 10-11 06:45

    嵌入式新手应该怎么学?

    的图形界面操作都要转化为命令传输给硬件的。 4、Linux设备驱动的架构要了解,结合原理图和Linux设备驱动相关书籍把每一行代码分析清楚。 上面只是一些简单嵌入式学习要点,你要先掌握这些,才能再继续
    发表于 09-08 10:22

    RealView开发工具包汇编程序指南

    RealView编译工具(RVCT)具有: ·一个独立的汇编程序,ARMASM·优化CC++编译器内置的内联和嵌入式
    发表于 08-12 07:30

    如何构建我自己的嵌入式C编译器?

    只是为了获得如何构建我自己的嵌入式 C 编译器的知识,如预处理、汇编、链接器、生成 Hex 文件、elf 文件、映射文件。 任何有用的链接可用? 我是嵌入式软件工程师,并且具有
    发表于 06-08 08:10

    嵌入式用什么语言编程?

    、结构化等方面对C进行了卓有成效的改进,但是在程序代码容量、执行速度和程序复杂程度等方面比C语言程序性能差一些。 当然也有很多学员或是已经就业的学员说,嵌入式技术
    发表于 05-18 10:08

    嵌入式中使用设计模式的思想

    嵌入式的标签多为:低配,偏硬件,底层,资源紧张,代码多以C语言,汇编为主,代码应用逻辑简单
    的头像 发表于 05-05 14:35 429次阅读
    在<b class='flag-5'>嵌入式</b>中使用设计模式的思想