gomonkey 全面支持 arm64 了

引言

gomonkey 是 Go 的一款打桩框架,目标是让用户在单元测试中低成本的完成打桩,从而将精力聚焦于业务功能的开发。gomonkey 接口友好,功能强大,目前已被很多项目使用,用户遍及世界多个国家。

image.png

提出问题

众所周知,这几年基于 arm64 架构的设备越来越多,包括服务器和终端等,尤其是 Apple M1芯片发布后,国内外多个 gopher 在 github 上提了希望 gomonkey 支持 arm64 架构 的 Issues

image.png

解决问题

新增一个 go 文件 jmp_arm64.go,实现 arm64 架构下的构建跳转指令函数 buildJmpDirective

buildJmpDirective

直接上代码:

func buildJmpDirective(double uintptr) []byte {
    res := make([]byte, 0, 24)
    d0d1 := double & 0xFFFF
    d2d3 := double >> 16 & 0xFFFF
    d4d5 := double >> 32 & 0xFFFF
    d6d7 := double >> 48 & 0xFFFF

    res = append(res, movImm(0B10, 0, d0d1)...)          // MOVZ x26, double[16:0]
    res = append(res, movImm(0B11, 1, d2d3)...)          // MOVK x26, double[32:16]
    res = append(res, movImm(0B11, 2, d4d5)...)          // MOVK x26, double[48:32]
    res = append(res, movImm(0B11, 3, d6d7)...)          // MOVK x26, double[64:48]
    res = append(res, []byte{0x4A, 0x03, 0x40, 0xF9}...) // LDR x10, [x26]
    res = append(res, []byte{0x40, 0x01, 0x1F, 0xD6}...) // BR x10

    return res
}

amd64 架构是 CISC 指令集,因此可以直接把立即数存放在指令中:

func buildJmpDirective(double uintptr) []byte {
    d0 := byte(double)
    d1 := byte(double >> 8)
    d2 := byte(double >> 16)
    d3 := byte(double >> 24)
    d4 := byte(double >> 32)
    d5 := byte(double >> 40)
    d6 := byte(double >> 48)
    d7 := byte(double >> 56)

    return []byte{
        0x48, 0xBA, d0, d1, d2, d3, d4, d5, d6, d7, // MOV rdx, double
        0xFF, 0x22,     // JMP [rdx]
    }
}

但 arm64 架构的指令长度只有 32 bit,所以指令中不能存放那么大的立即数,从而需要将立即数按 16 bit 值分四次移动到 x26 寄存器中,对应的汇编代码如下:

MOVZ x26, double[16:0]
MOVK x26, double[32:16]
MOVK x26, double[48:32]
MOVK x26, double[64:48]

为什么选择 arm64 架构下的 x26 寄存器?
因为 x26 与 amd64 架构下的 rdx 寄存器对应,属于闭包指针寄存器。如果选择其他寄存器,比如 x27,就会导致桩序列相关的所有测试用例运行失败,直接原因是 callReflect 函数的入参 ctxt 为空, 导致 reflect.MakeFunc 调用的地方出了问题,而所有桩序列的测试替身函数 double 都是调用 reflect.MakeFunc 函数动态创建的。

arm64 没有类似 amd64 的间接跳转指令 JMP,因此考虑将空闲的寄存器 x10 作为跳板,通过 LDR 和 BR 指令完成跳转:

LDR x10, [x26]
BR x10

movImm

buildJmpDirective 函数的实现依赖 movImm 函数,该函数用于移动立即数,同时兼容 MOVZ 和 MOVK 指令,代码如下:

func movImm(opc, shift int, val uintptr) []byte {
    var m uint32 = 26          // rd
    m |= uint32(val) << 5      // imm16
    m |= uint32(shift&3) << 21 // hw
    m |= 0b100101 << 23        // const
    m |= uint32(opc&0x3) << 29 // opc
    m |= 0b1 << 31             // sf

    res := make([]byte, 4)
    *(*uint32)(unsafe.Pointer(&res[0])) = m

    return res
}

movImm 函数的实现需要参考 arm64 的指令书册。

image.png

image.png

致谢

感谢 Go 社区国内外多个 gopher 对“支持 arm64”特性的关注和贡献,特别需要提到的是[@benshi001,@hengwu0,@sirkon, @chenxu2048, @Spongecaptain, @dgofman, @User979269852, @fran96, @JoanWu5, @nathan-jiao],没有你们的付出和接力,就不会有该特性的完整支持!

笔者在这里还要再强调一位大神。Bouke 是 Go 语言 monkey工程的创建者,在 2015 年就发表了 Go 语言猴子补丁原理的文章。毫无疑问,gomonkey 的思维底座主要来自 Bouke 的贡献,向他致敬,非常感谢!

image.png

当 Bouke 在 github 上看到 gomonkey 支持 arm64 架构后,他给笔者写了一封信:


image.png

收到信后,笔者心情有点小激动。这几年对 gomonkey 的贡献被大神肯定了,说明是非常值得的,后续要加倍努力,为用户发布更多既强大又易用的特性。

希望读者关注 gomonkey ,如果你感觉 gomonkey 对你打桩有帮助的话,那么请你将 gomonkey 推荐给你的朋友,同时期待你参与 gomonkey 社区的共建!

我们相信,持续演进的 gomonkey ,一定会变得越来越强大,越来越贴心,逐步成为国内外 gopher 们爱不释手all-in-one 的打桩神器。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容

  • 在日常工作中也许我们会接触arm64、x86、这几个名词,本篇整理一下它们的基础知识和区别。 今天小编就带你深入了...
    物非0人非阅读 27,137评论 0 6
  • ARM处理器 我们知道,目前为止Apple的所有iOS设备都采用的是ARM处理器。ARM处理器的特点是体积小、低功...
    VV木公子阅读 1,225评论 1 5
  • iOS汇编 iOS汇编语音有很多钟。常见的有8086汇编、arm汇编、x86汇编等等。 arm汇编 iOS的架构从...
    编程怪才_凌雨画阅读 1,706评论 1 4
  • 捣鼓了一段时间的iOS逆向相关的东西,在动态分析过程中会阅读汇编代码,分析代码的执行流程,在此记录下阅读汇编代码过...
    傻傻木阅读 3,120评论 0 14
  • # 概述 早期的程序员发现机器语言在阅读、书写方面的问题,是如此的难以辨别和记忆,需要记住所有抽象的二进制码,为了...
    Tenloy阅读 4,996评论 2 10