This is a creation in Article, where the information may have evolved or changed.
Recently upgraded go1.9, found a dependency to get Goroutine ID does not support 1.9, so manually wrote a, by the way to learn about the Go assembly. I hope you have a certain understanding of the Go assembly after reading this article.
Go Assembly
First Amway one gets the current Goroutine ID of the library, GID, support go1.7-go1.9, may be the smallest libraries now, the use is very simple: id := gid.Get() .
The Go assembly syntax is similar to Plan 9, which is not a direct representation of machine language, and has a semi-abstract instruction set. Generally speaking, machine-specific operations are generally their intention, other concepts such as memory move, subroutine call, return is an abstract expression.
Constant
Evaluation priority and C are different, such as 3&1<<2 = = 4, interpreted as (3&1) << 2.
Constants are considered unsigned 64-bit int, so 2 is not a negative number, but is interpreted as UInt64.
Symbol
4 predefined symbols representing Pseudo-registers, pseudo registers (virtual registers?). )。
- Fp:frame pointer, parameters and local variables
- Pc:program counter: Jump, Branch
- Sb:static base pointer: global symbols
- Sp:stack pointer: top of stack
User-defined symbols are represented by offsets (offset).
The SB register represents the global memory starting point, and foo (SB) indicates that the symbol Foo is used as the memory address. This form is used to name global functions, data. The <> restriction symbol can only be used in the current source file, similar to static in C. foo+4(SB)represents the address of Foo back 4 bytes.
The FP register points to the function parameter. 0 (FP) is the first parameter, and 8 (FP) is the second parameter (64-bit machine). first_arg+0(FP)This means that the first parameter address is bound to the symbol First_arg, which is different from the meaning of SB.
The SP register represents the stack pointer, pointing to the top of the local stack frame, so offset is negative and ranges from [-framesize, 0], such as x-8 (SP). For a schema with the hardware register name SP, x-8(SP) represents the virtual stack pointer register, which -8(SP) represents the hardware SP register.
Jumps and branches are offset, or label, for the PC, for example:
label: MOVW $0, R1 JMP label
Label scopes are function-level, and different functions can define labels of the same name.
Instructions
For example:
TEXT runtime·profileloop(SB),NOSPLIT,$8 MOVQ $runtime·profileloop1(SB), CX MOVQ CX, 0(SP) CALL runtime·externalthreadhandler(SB) RET
The TEXT directive defines the symbol runtime·profileloop , RET indicates the end, and if not declared, linker adds the JUMP-TO-SELF directive.
$8 represents the frame size, which is typically followed by a parameter size. Because there is nosplit here, can not add.
The global data symbol is declared by using data in the formDATA symbol+offset(SB)/width, value
GLOBL defines the data as global. For example:
DATA divtab<>+0x00(SB)/4, $0xf4f8fcffDATA divtab<>+0x04(SB)/4, $0xe6eaedf0...DATA divtab<>+0x3c(SB)/4, $0x81828384GLOBL divtab<>(SB), RODATA, $64GLOBL runtime·tlsoffset(SB), NOPTR, $4
Defines and initializes the Divtab<>, a read-only 64-byte table with 4 bytes per item. Defines a runtime Tlsoffset, 4-byte null value, non-pointer.
The directive has one or two parameters. If there are two, the first is a bit mask, which can be a numeric expression. The values are defined as follows:
- Noprof = 1; (for TEXT items.) Don ' t profile the marked function. This flag is deprecated. Abandoned
- Dupok = 2; It is the legal to has multiple instances of this symbol with a single binary. The linker would choose one of the duplicates to use. This symbol allows more than one, and the linker chooses to use it.
- Nosplit = 4; (for TEXT items.) Don ' t insert the preamble to check if the stack must is split. The frame for the routine, plus anything it calls, must fit in the spare space at the top of the stack segment. Used to protect routines such as the stack splitting code itself. Do not insert code, do not check if stack split is required. (In doubt, the high-version go uses a continuous stack, does this command still work?) )
- Rodata = 8; (For DATA and globl items.) Put this data in a read-only section. Data is stored in read-only areas
- Noptr = 16; (For DATA and globl items.) This data contains no pointers, and therefore does not need to being scanned by the garbage collector. Represents a non-pointer and does not require a GC.
- WRAPPER = 32; (for TEXT items.) This was a wrapper function and should not count as disabling recover.
- Needctxt = 64; (for TEXT items.) This function was a closure so it uses its incoming context register.
Example:add
//main.gopackage mainimport "fmt"func add(x, y int64) int64func main() { fmt.Println(add(2, 3))}
// add.sTEXT ·add(SB),$0-24 MOVQ x+0(FP), BX MOVQ y+8(FP), BP ADDQ BP, BX MOVQ BX, ret+16(FP) RET
A function is defined in the following way:TEXT package_name·function_name(SB),$frame_size-arguments_size
In the example, Package_name is empty, which indicates the current package. This is followed by a middle point (u+00b7) and a function name.
Frame_size is $, which indicates the space required for the stack, which is 0, which means that no stack is required and only registers are used. The size of the parameter and return value of the function is 3 * 8 = 24 bytes.
MOVQRepresents moving a 64bit value (Q for Quadword). This is moved from the FP (frame pointer, the starting position of the function parameter) to BX the and. The offset in the BP syntax symbol+offset(register) , which represents the address from register as the starting point for moving offset. Here x, y is the parameter symbol in the function definition.
ADDQThe line indicates that the values of the two 64bit registers are added to the BX.
Finally, the MOVQ value in the BX is moved to the position of the fp+16, where the ret symbol is the compiler's default return value symbol.
Example:hello
package mainimport _ "fmt"func hello()func main(){ hello()}
#include "textflag.h" Data world<>+0 (SB)/8, $ "Hello wo" data world<>+8 (SB)/4, $ "Rld" Globl world<>+0 ( SB), Rodata, $12//requires stack space of 88 bytes, no parameters and return values text Hello (SB), $88-0 subq $88, SP movq BP, (sp) Leaq (sp ), BP//create character, Presence my_string Leaq world<>+0 (SB), ax Movq ax, my_string+48 (SP) movq $11, my_string+56 (SP) Movq $, autotmp_0+64 (SP) Movq $, autotmp_0+72 (SP) leaq type String (SB), AX movq AX, (SP) Leaq my_string+48 (sp), ax Movq ax, 8 (SP)//Create a interface call Runtime convt2e (SB) MOVQ (SP), AX movq (SP), CX Movq CX, autotmp_0+64 (SP) MOV Q ax, autotmp_0+72 (sp) Leaq autotmp_0+64 (sp), ax Movq ax, (SP) Movq $, 8 (SP) Movq $, + (SP)//Call FMT. Println Call FMT Println (SB) movq (sp), BP addq $88, SP RET
The first line #include loads some constants, which we'll use here RODATA .
DATAUsed to store strings in memory, 1,2,4 or 8 bytes can be stored at one time. The function behind the symbol <> is to restrict the data to be used in the current file.
GLOBLSets the data to global, read-only, relative to position 12.
Example:gid
Functions used in the GID library
#include "go_asm.h"#include "go_tls.h"#include "textflag.h"// 返回值 8 bytes, 符号为 getgTEXT ·getg(SB), NOSPLIT, $0-8 // get_tls 的宏为: #define get_tls(r) MOVQ TLS, r // 等价于 MOVQ TLS, CX // 从 TLS(Thread Local Storage) 起始移动 8 byte 值 到 CX 寄存器 get_tls(CX) // g的宏为: g(r) 0(r)(TLS*1) // 等价于 0(CX)(TLS*1), AX // 查到意义为 indexed with offset, 这里 offset=0, 索引是什么意思不清楚 MOVQ g(CX), AX // 从AX起始移动 8 byte 值,到ret符号的位置 MOVQ AX, ret+0(FP) RET
Example:swapint32
A function of an atomic exchange int32
package atomicimport ( "unsafe")func SwapInt32(addr *int32, new int32) (old int32)
#include "textflag.h"// 参数大小 = 8 + 4 + 4 , + 4 (默认的 ret符号?)TEXT ·SwapInt32(SB),NOSPLIT,$0-20 JMP ·SwapUint32(SB)TEXT ·SwapUint32(SB),NOSPLIT,$0-20 // 第一个参数 移动 8 byte 到 BP MOVQ addr+0(FP), BP // 第二个参数 移动 4 byte 到 AX MOVL new+8(FP), AX // 原子操作, write-after-read, 把 (AX, offset=0) 与 (BP, offset=0) 交换 4 byte 数据 XCHGL AX, 0(BP) // 移动 AX 到 old 符号 MOVL AX, old+16(FP) RET