【转】Golang汇编命令解读

原文: https://www.cnblogs.com/yjf512/p/6132868.html

-------------------------------------------------

我们可以很容易将一个golang程序转变成汇编语言。

比如我写了一个main.go:

package main

func g(p int) int {
     return p+1;
}

func main() {
     c := g(4) + 1
     _ = c
}

使用命令:

GOOS=linux GOARCH=386 go tool compile -S main.go >> main.S

我们就获取了main.S是main.go的汇编版本。

"".g t=1 size=16 value=0 args=0x10 locals=0x0
     0x0000 00000 (main.go:4)     TEXT     "".g(SB), $0-16
     0x0000 00000 (main.go:4)     NOP
     0x0000 00000 (main.go:4)     NOP
     0x0000 00000 (main.go:4)     FUNCDATA     $0, gclocals·23e8278e2b69a3a75fa59b23c49ed6ad(SB)
     0x0000 00000 (main.go:4)     FUNCDATA     $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
     0x0000 00000 (main.go:5)     MOVQ     "".p+8(FP), BX
     0x0005 00005 (main.go:5)     INCQ     BX
     0x0008 00008 (main.go:5)     MOVQ     BX, "".~r1+16(FP)
     0x000d 00013 (main.go:5)     RET
     0x0000 48 8b 5c 24 08 48 ff c3 48 89 5c 24 10 c3        H.\$.H..H.\$..
"".main t=1 size=16 value=0 args=0x0 locals=0x0
     0x0000 00000 (main.go:8)     TEXT     "".main(SB), $0-0
     0x0000 00000 (main.go:8)     NOP
     0x0000 00000 (main.go:8)     NOP
     0x0000 00000 (main.go:8)     FUNCDATA     $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
     0x0000 00000 (main.go:8)     FUNCDATA     $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
     0x0000 00000 (main.go:9)     MOVQ     $4, BX
     0x0007 00007 (main.go:9)     INCQ     BX
     0x000a 00010 (main.go:9)     INCQ     BX
     0x000d 00013 (main.go:11)     RET
     0x0000 48 c7 c3 04 00 00 00 48 ff c3 48 ff c3 c3        H......H..H...
"".init t=1 size=80 value=0 args=0x0 locals=0x0
     0x0000 00000 (main.go:11)     TEXT     "".init(SB), $0-0
     0x0000 00000 (main.go:11)     MOVQ     (TLS), CX
     0x0009 00009 (main.go:11)     CMPQ     SP, 16(CX)
     0x000d 00013 (main.go:11)     JLS     62
     0x000f 00015 (main.go:11)     NOP
     0x000f 00015 (main.go:11)     NOP
     0x000f 00015 (main.go:11)     FUNCDATA     $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
     0x000f 00015 (main.go:11)     FUNCDATA     $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
     0x000f 00015 (main.go:11)     MOVBQZX     "".initdone·(SB), BX
     0x0016 00022 (main.go:11)     CMPB     BL, $0
     0x0019 00025 (main.go:11)     JEQ     47
     0x001b 00027 (main.go:11)     MOVBQZX     "".initdone·(SB), BX
     0x0022 00034 (main.go:11)     CMPB     BL, $2
     0x0025 00037 (main.go:11)     JNE     40
     0x0027 00039 (main.go:11)     RET
     0x0028 00040 (main.go:11)     PCDATA     $0, $0
     0x0028 00040 (main.go:11)     CALL     runtime.throwinit(SB)
     0x002d 00045 (main.go:11)     UNDEF
     0x002f 00047 (main.go:11)     MOVB     $1, "".initdone·(SB)
     0x0036 00054 (main.go:11)     MOVB     $2, "".initdone·(SB)
     0x003d 00061 (main.go:11)     RET
     0x003e 00062 (main.go:11)     CALL     runtime.morestack_noctxt(SB)
     0x0043 00067 (main.go:11)     JMP     0

首先这个程序根据TEXT是定义函数的,分为3个部分

0x0000 00000 (main.go:4)     TEXT     "".g(SB), $0-16
0x0000 00000 (main.go:8)     TEXT     "".main(SB), $0-0
0x0000 00000 (main.go:11)     TEXT     "".init(SB), $0-0

这个"". 代表的是这个函数的命名空间。
g(SB) 这里就有个SB的伪寄存器。全名未Static Base,代表g这个函数地址,0−16中的0−16中的0代表局部变量字节数总和,0表示不存在局部变量。-16代表在0的地址基础上空出16的长度作为传入和返回对象。这个也就是golang如何实现函数的多返回值的方法了。它在定义函数的时候,开辟了一定空间存储传入和传出对象。

NOP命令是作为占位符使用,提供给编译器使用的。可以忽略不看。

下面是

     0x0000 00000 (main.go:4)     FUNCDATA     $0, gclocals·23e8278e2b69a3a75fa59b23c49ed6ad(SB)
     0x0000 00000 (main.go:4)     FUNCDATA     $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)

这里的FUNCDATA是golang编译器自带的指令,plan9和x86的指令集都是没有的。它用来给gc收集进行提示。提示0和0和1是用于局部函数调用参数,需要进行回收。

下面是

     0x0000 00000 (main.go:5)     MOVQ     "".p+8(FP), BX
     0x0005 00005 (main.go:5)     INCQ     BX
     0x0008 00008 (main.go:5)     MOVQ     BX, "".~r1+16(FP)

这里有一个FP寄存器,FP是frame pointer,是指向栈底,SP是指向栈顶。BX是一个临时寄存器,那么上面的句子是代表把FP+8这个位置的数据(参数p),保存到BX中。FP+8代表什么呢,按照上面的图,代表的是参数

INCQ是自增算法,BX里面的数自加1,然后把BX里面的数存储到FP+16,代表的是返回值。
下面就是RET,直接返回。

下面再看看main,我们会发现,main函数里面并没有call g函数,这是由于go汇编编译器会把一些短的函数变成内嵌函数,减少函数调用。

if

我们在main里面增加一个判断逻辑。代码为:

package main

func g(p int) int {
     return p+1;
}

func main() {
     c := g(4) + 1
     var d bool
     if (c > 4) {
          d = true
     } else {
          d = false
     }
     _ = d
     return
}

对应的main的汇编为:

     0x0000 00000 (main.go:8)     TEXT     "".main(SB), $0-0
     0x0000 00000 (main.go:8)     NOP
     0x0000 00000 (main.go:8)     NOP
     0x0000 00000 (main.go:8)     FUNCDATA     $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
     0x0000 00000 (main.go:8)     FUNCDATA     $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
     0x0000 00000 (main.go:9)     MOVQ     $4, BX
     0x0007 00007 (main.go:9)     INCQ     BX
     0x000a 00010 (main.go:9)     INCQ     BX
     0x000d 00013 (main.go:11)     CMPQ     BX, $4
     0x0011 00017 (main.go:11)     JLE     27
     0x0013 00019 (main.go:12)     MOVQ     $1, AX
     0x001a 00026 (main.go:17)     RET
     0x001b 00027 (main.go:14)     MOVQ     $0, AX
     0x001d 00029 (main.go:17)     JMP     26

可以看是试用CMPQ来进行比较的,JLE代表CMP比较之后的结果,如果BX小于等于4,那么就跳到27指令,就是MOVQ $0, AX,把AX赋值为0,就是false,否则赋值为1,true

这里的JLE是条件转移指令:http://baike.baidu.com/link?url=pdfEpBZ-c-owW0YJrAL71MUi1nIlEpBqljD3agfLtB5KrAaFembK6yKKUNycaSUWTTTyynKhNIfw2LXvGFx4euTIgyXQcVLz5HPxS4AO-kVT9wQxlL6_O-1ygTwfshEgas91S14FU3CRU7a1jImTSK

for

程序改为:

package main

func g(p int) int {
     var sum int
     for i := 0; i < p; i++ {
          sum = sum + i
     }
     return sum
}

func main() {
     c := g(4) + 1
     _ = c
}

这里面有一个for循环,产生的汇编为:

     0x0000 00000 (main.go:4)     TEXT     "".g(SB), $0-16
     0x0000 00000 (main.go:4)     NOP
     0x0000 00000 (main.go:4)     NOP
     0x0000 00000 (main.go:4)     MOVQ     "".p+8(FP), DX
     0x0005 00005 (main.go:4)     FUNCDATA     $0, gclocals·23e8278e2b69a3a75fa59b23c49ed6ad(SB)
     0x0005 00005 (main.go:4)     FUNCDATA     $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
     0x0005 00005 (main.go:5)     MOVQ     $0, CX
     0x0007 00007 (main.go:6)     MOVQ     $0, AX
     0x0009 00009 (main.go:6)     CMPQ     AX, DX
     0x000c 00012 (main.go:6)     JGE     $0, 25
     0x000e 00014 (main.go:7)     ADDQ     AX, CX
     0x0011 00017 (main.go:6)     INCQ     AX
     0x0014 00020 (main.go:6)     NOP
     0x0014 00020 (main.go:6)     CMPQ     AX, DX
     0x0017 00023 (main.go:6)     JLT     $0, 14
     0x0019 00025 (main.go:9)     MOVQ     CX, "".~r1+16(FP)
     0x001e 00030 (main.go:9)     RET

AX存的是变量i,DX存的是参数p,CX存的是变量sum,下面的几个命令:

0x0009 00009 (main.go:6)     CMPQ     AX, DX
     0x000c 00012 (main.go:6)     JGE     $0, 25
     0x000e 00014 (main.go:7)     ADDQ     AX, CX
     0x0011 00017 (main.go:6)     INCQ     AX
     0x0014 00020 (main.go:6)     NOP
     0x0014 00020 (main.go:6)     CMPQ     AX, DX
     0x0017 00023 (main.go:6)     JLT     $0, 14

实际上是使用CMP,JGE,JLT来不断控制循环过程。

原文地址:https://www.cnblogs.com/oxspirt/p/11260350.html

时间: 2024-10-14 21:56:06

【转】Golang汇编命令解读的相关文章

golang bytes包解读

golang中的bytes标准库实现了对字节数组的各种操作,与strings标准库功能基本类似. 功能列表:1.字节切片 处理函数 (1).基本处理函数(2).字节切片比较函数(3).前后缀检查函数(4).字节切片位置索引函数(5).分割函数(6).大小写处理函数(7).子字节切片处理函数2.Buffer 对象3.Reader 对象 基本处理函数Contains() :返回是否包含子切片func Contains(b, subslice []byte) bool 案例:执行结果:[email p

使用 Elastic Stack 来监控和调优 Golang 应用程序

Golang 因为其语法简单,上手快且方便部署正被越来越多的开发者所青睐,一个 Golang 程序开发好了之后,势必要关心其运行情况,今天在这里就给大家介绍一下如果使用 Elastic Stack 来分析 Golang 程序的内存使用情况,方便对 Golang 程序做长期监控进而调优和诊断,甚至发现一些潜在的内存泄露等问题. Elastic Stack 其实是一个集合,包含 Elasticsearch.Logstash 和 Beats 这几个开源软件,而 Beats 又包含 Filebeat.P

深入Golang之sync.Pool详解

我们通常用golang来构建高并发场景下的应用,但是由于golang内建的GC机制会影响应用的性能,为了减少GC,golang提供了对象重用的机制,也就是sync.Pool对象池. sync.Pool是可伸缩的,并发安全的.其大小仅受限于内存的大小,可以被看作是一个存放可重用对象的值的容器. 设计的目的是存放已经分配的但是暂时不用的对象,在需要用到的时候直接从pool中取. 任何存放区其中的值可以在任何时候被删除而不通知,在高负载下可以动态的扩容,在不活跃时对象池会收缩. sync.Pool首先

golang -- 字符串就地取反

字符串 定义 在golang中字符串是一种不可变的字节序列,它可以包含任意的数据,包括0值字节,但主要是人类可以阅读的文本.golang中默认字符串被解读为utf-8编码的Unicode码点(文字符号)序列. 特性 golang中字符串具有不可变性.例如 str := "hello 世界!" str[0] = 'L' 这种写法会引起编译错误:str[0] 不可赋值 字符串支持类似数组中分片的引用写法: fmt.Println(str[:5]) // 输出 hello fmt.Print

小猿圈之解读Go语言的特点?

随着互联网的不断发展,语言也在不断的更新,现在时代正在走向数据化.人工智能的新时代,python.Go语言开始崛起,python已成为当今时代的主流,go语言也开始流行,现在正在向Go发展,越来越多的公司需要这门技术,薪资也在不断提升,那小猿圈为你解读Go语言的特点都有哪些? 一.什么是Go语言 Golang(GO)是由Google在2009年推出的编程语言,是一门全新的.现代的.语法简洁并容易上手的编程语言,是具备独立的运行时库,就像C语言一样. 大神们创建Go语言的初衷是:近十年来开发程序之

简析 Golang IO 包

简析 Golang IO 包 io 包提供了 I/O 原语(primitives)的基本接口.io 包中定义了四个最基本接口 Reader.Writer.Closer.Seeker 用于表示二进制流的读.写.关闭和寻址操作.这些原语和接口是对底层操作的封装,因此如没有特殊说明,这些原语和接口都不能被视为线程安全的. Reader Reader 接口封装了基本的 Read 方法.Read 读取长度为 len(p) 字节的数据,并写入到 p.返回结果包含读取数据字节数(0 <= n <= len(

可能是国内第一篇全面解读 Java 现状及趋势的文章

作者 | 张晓楠 Dragonwell JDK?最新版本 8.1.1-GA 发布,包括全新特性和更新! 导读:InfoQ 发布<2019 中国 Java 发展趋势报告>,反映 Java 在中国发展的独特性,同时也希望大家对 Java 有一个正确的认识. 2 个月前,InfoQ 英文站发布了一份<2019 Java 发展趋势报告>,从技术采用生命周期的角度,分析了 Java 这门 20 多年历史语言的发展现状.这份报告发布后,发生了几个我们没想到的问题:一是有些开发者对 Java 产

ViewGroup源码解读

我们之前刚刚分析完事件传递机制和view的源码,如果没有看过的,建议看完View的事件拦截机制浅析以及View的事件源码解析.这次我们来分析下viewgroup的. 可能有人会想,怎么又是源码分析,肯定又是一大通.其实没你想的那么复杂.仔细分析一波就行了. 解读ViewGroup 我们都知道,一个事件完整的流程是从dispatchTouchevent–>onInterceptTouchevent–>onTouchEvent.我们先不说事件监听的问题.上述三个步骤就是正常一个点击的流程.前面我们

mysql之show engine innodb status解读(转)

add by zhj: 我第一次知道这个命令是线上服务出了问题,然后同事用这个命令去查看死锁.但用这个命令看死锁有一定的局限性,它只能看到最后一次死锁, 而且只能看到死锁环中的两个事务所执行的最后一条语句(即被死锁卡住的那条语句),看不到整个死锁环,也看到不整个事务的语句.但是即使这亲,对我 们来说也非常有用,因为一般来说,数据库同时存在多个死锁环的可能性比较小,而且有了死锁环中的事务的最后一条语句,我们找到整个死锁环不是太难. "show engine innodb status"这