日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Golang 汇编入门知识总结

發(fā)布時間:2024/2/28 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Golang 汇编入门知识总结 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

作者:ivansli,騰訊 IEG 運(yùn)營開發(fā)工程師

在深入學(xué)習(xí) Golang 的 runtime 和標(biāo)準(zhǔn)庫實(shí)現(xiàn)的時候發(fā)現(xiàn),如果對 Golang 匯編沒有一定了解的話,很難深入了解其底層實(shí)現(xiàn)機(jī)制。在這里整理總結(jié)了一份基礎(chǔ)的 Golang 匯編入門知識,通過學(xué)習(xí)之后能夠?qū)ζ涞讓訉?shí)現(xiàn)有一定的認(rèn)識。

0. 為什么寫本文

平時業(yè)務(wù)中一直使用 PHP 編寫代碼,但是一直對 Golang 比較感興趣,閑暇、周末之余會看一些 Go 底層源碼。

近日在分析 go 的某些特性底層功能實(shí)現(xiàn)時發(fā)現(xiàn):有些又跟 runtime 運(yùn)行時有關(guān),而要掌握這一部分的話,有一道坎是繞不過去的,那就是 Go 匯編。索性就查閱了很多大佬們寫的資料,在閱讀之余整理總結(jié)了一下,并在這里分享給大家。

本文使用 Go 版本為 go1.14.1

1. 為什么需要匯編

眾所周知,在計(jì)算機(jī)的世界里,只有 2 種類型。那就是:0 和 1。

計(jì)算機(jī)工作是由一系列的機(jī)器指令進(jìn)行驅(qū)動的,這些指令又是一組二進(jìn)制數(shù)字,其對應(yīng)計(jì)算機(jī)的高低電平。而這些機(jī)器指令的集合就是機(jī)器語言,這些機(jī)器語言在最底層是與硬件一一對應(yīng)的。

顯而易見,這樣的機(jī)器指令有一個致命的缺點(diǎn):可閱讀性太差(恐怕也只有天才和瘋子才有能力把控得了)。

為了解決可讀性的問題以及代碼編輯的需求,于是就誕生了最接近機(jī)器的語言:匯編語言(在我看來,匯編語言更像一種助記符,這些人們?nèi)菀子涀〉拿恳粭l助記符都映射著一條不容易記住的由 0、1 組成的機(jī)器指令。你覺得像不像域名與 IP 地址的關(guān)系呢?)。

1.1 程序的編譯過程

以 C 語言為例來說,從 hello.c 的源碼文件到 hello 可執(zhí)行文件,經(jīng)過編譯器處理,大致分為幾個階段:

編譯器在不同的階段會做不同的事情,但是有一步是可以確定的,那就是:源碼會被編譯成匯編,最后才是二進(jìn)制。

2. 程序與進(jìn)程

源碼經(jīng)過編譯之后,得到一個二進(jìn)制的可執(zhí)行文件。文件這兩個字也就表明,目前得到的這個文件跟其他文件對比,除了是具有一定的格式(Linux 中是 ELF 格式,即:可運(yùn)行可鏈接。executable linkable formate)的二進(jìn)制組成,并沒什么區(qū)別。

在 Linux 中文件類型大致分為 7 種:

b:?塊設(shè)備文件 c:字符設(shè)備文件 d:目錄 -:普通文件 l:鏈接 s:socket p:管道

通過上面可以看到,可執(zhí)行文件 main 與源碼文件 main.go,都是同一種類型,屬于普通文件。(當(dāng)然了,在 Unix 中有一句很經(jīng)典的話:一切皆文件)。

那么,問題來了:

  • 什么是程序?

  • 什么是進(jìn)程?

  • 2.1 程序

    維基百科告訴我們:程序是指一組指示計(jì)算機(jī)或其他具有消息處理能力設(shè)備每一步動作的指令,通常用某種程序設(shè)計(jì)語言編寫,運(yùn)行于某種目標(biāo)體系結(jié)構(gòu)上。

    從某個層面來看,可以把程序分為靜態(tài)程序、動態(tài)程序:靜態(tài)程序:單純的指具有一定格式的可執(zhí)行二進(jìn)制文件。動態(tài)程序:則是靜態(tài)可執(zhí)行程序文件被加載到內(nèi)存之后的一種運(yùn)行時模型(又稱為進(jìn)程)。

    2.2 進(jìn)程

    首先,要知道的是,進(jìn)程是分配系統(tǒng)資源的最小單位,線程(帶有時間片的函數(shù))是系統(tǒng)調(diào)度的最小單位。進(jìn)程包含線程,線程所屬于進(jìn)程。

    創(chuàng)建進(jìn)程一般使用 fork 方法(通常會有個拉起程序,先 fork 自身生成一個子進(jìn)程。然后,在該子進(jìn)程中通過 exec 函數(shù)把對應(yīng)程序加載進(jìn)來,進(jìn)而啟動目標(biāo)進(jìn)程。當(dāng)然,實(shí)際上要復(fù)雜得多),而創(chuàng)建線程則是使用 pthread 線程庫。

    以 32 位 Linux 操作系統(tǒng)為例,進(jìn)程經(jīng)典的虛擬內(nèi)存結(jié)構(gòu)模型如下圖所示:

    其中,有兩處結(jié)構(gòu)是靜態(tài)程序所不具有的,那就是運(yùn)行時堆(heap)與運(yùn)行時棧(stack)。

    運(yùn)行時堆從低地址向高地址增長,申請的內(nèi)存空間需要程序員自己或者由 GC 釋放。運(yùn)行時棧從高地址向低地址增長,內(nèi)存空間在當(dāng)前棧楨調(diào)用結(jié)束之后自動釋放(并不是清除其所占用內(nèi)存中數(shù)據(jù),而是通過棧頂指針 SP 的移動,來標(biāo)識哪些內(nèi)存是正在使用的)。

    3. Go 匯編

    對于 Go 編譯器而言,其輸出的結(jié)果是一種抽象可移植的匯編代碼,這種匯編(Go 的匯編是基于 Plan9 的匯編)并不對應(yīng)某種真實(shí)的硬件架構(gòu)。Go 的匯編器會使用這種偽匯編,再為目標(biāo)硬件生成具體的機(jī)器指令。

    偽匯編這一個額外層可以帶來很多好處,最主要的一點(diǎn)是方便將 Go 移植到新的架構(gòu)上。

    相關(guān)的信息可以參考 Rob Pike 的 The Design of the Go Assembler。

    要了解 Go 的匯編器最重要的是要知道 Go 的匯編器不是對底層機(jī)器的直接表示,即 Go 的匯編器沒有直接使用目標(biāo)機(jī)器的匯編指令。Go 匯編器所用的指令,一部分與目標(biāo)機(jī)器的指令一一對應(yīng),而另外一部分則不是。這是因?yàn)榫幾g器套件不需要匯編器直接參與常規(guī)的編譯過程。

    相反,編譯器使用了一種半抽象的指令集,并且部分指令是在代碼生成后才被選擇的。匯編器基于這種半抽象的形式工作,所以雖然你看到的是一條 MOV 指令,但是工具鏈針對對這條指令實(shí)際生成可能完全不是一個移動指令,也許會是清除或者加載。也有可能精確的對應(yīng)目標(biāo)平臺上同名的指令。概括來說,特定于機(jī)器的指令會以他們的本尊出現(xiàn), 然而對于一些通用的操作,如內(nèi)存的移動以及子程序的調(diào)用以及返回通常都做了抽象。細(xì)節(jié)因架構(gòu)不同而不一樣,我們對這樣的不精確性表示歉意,情況并不明確。

    匯編器程序的工作是對這樣半抽象指令集進(jìn)行解析并將其轉(zhuǎn)變?yōu)榭梢暂斎氲芥溄悠鞯闹噶睢?/p>

    The most important thing to know about Go’s assembler is that it is not a direct representation of the underlying machine. Some of the details map precisely to the machine, but some do not. This is because the compiler suite needs no assembler pass in the usual pipeline. Instead, the compiler operates on a kind of semi-abstract instruction set, and instruction selection occurs partly after code generation. The assembler works on the semi-abstract form, so when you see an instruction like MOV what the toolchain actually generates for that operation might not be a move instruction at all, perhaps a clear or load.

    Or it might correspond exactly to the machine instruction with that name. In general, machine-specific operations tend to appear as themselves, while more general concepts like memory move and subroutine call and return are more abstract. The details vary with architecture, and we apologize for the imprecision; the situation is not well-defined.

    The assembler program is a way to parse a description of that semi-abstract instruction set and turn it into instructions to be input to the linker.

    Go 匯編使用的是caller-save模式,被調(diào)用函數(shù)的入?yún)?shù)、返回值都由調(diào)用者維護(hù)、準(zhǔn)備。因此,當(dāng)需要調(diào)用一個函數(shù)時,需要先將這些工作準(zhǔn)備好,才調(diào)用下一個函數(shù),另外這些都需要進(jìn)行內(nèi)存對齊,對齊的大小是 sizeof(uintptr)。

    3.1 幾個概念

    在深入了解 Go 匯編之前,需要知道的幾個概念:

    • 棧:進(jìn)程、線程、goroutine 都有自己的調(diào)用棧,先進(jìn)后出(FILO)

    • 棧幀:可以理解是函數(shù)調(diào)用時,在棧上為函數(shù)所分配的內(nèi)存區(qū)域

    • 調(diào)用者:caller,比如:A 函數(shù)調(diào)用了 B 函數(shù),那么 A 就是調(diào)用者

    • 被調(diào)者:callee,比如:A 函數(shù)調(diào)用了 B 函數(shù),那么 B 就是被調(diào)者

    3.2 Go 的核心寄存器

    go 匯編中有 4 個核心的偽寄存器,這 4 個寄存器是編譯器用來維護(hù)上下文、特殊標(biāo)識等作用的:

    寄存器說明
    SB(Static base pointer)global symbols
    FP(Frame pointer)arguments and locals
    PC(Program counter)jumps and branches
    SP(Stack pointer)top of stack

    • FP: 使用如 symbol+offset(FP)的方式,引用 callee 函數(shù)的入?yún)?shù)。例如 arg0+0(FP),arg1+8(FP),使用 FP 必須加 symbol ,否則無法通過編譯(從匯編層面來看,symbol 沒有什么用,加 symbol 主要是為了提升代碼可讀性)。另外,需要注意的是:往往在編寫 go 匯編代碼時,要站在 callee 的角度來看(FP),在 callee 看來,(FP)指向的是 caller 調(diào)用 callee 時傳遞的第一個參數(shù)的位置。假如當(dāng)前的 callee 函數(shù)是 add,在 add 的代碼中引用 FP,該 FP 指向的位置不在 callee 的 stack frame 之內(nèi)。而是在 caller 的 stack frame 上,指向調(diào)用 add 函數(shù)時傳遞的第一個參數(shù)的位置,經(jīng)常在 callee 中用symbol+offset(FP)來獲取入?yún)⒌膮?shù)值。

    • SB: 全局靜態(tài)基指針,一般用在聲明函數(shù)、全局變量中。

    • SP: 該寄存器也是最具有迷惑性的寄存器,因?yàn)闀袀?SP 寄存器和硬件 SP 寄存器之分。plan9 的這個偽 SP 寄存器指向當(dāng)前棧幀第一個局部變量的結(jié)束位置(為什么說是結(jié)束位置,可以看下面寄存器內(nèi)存布局圖),使用形如 symbol+offset(SP) 的方式,引用函數(shù)的局部變量。offset 的合法取值是 [-framesize, 0),注意是個左閉右開的區(qū)間。假如局部變量都是 8 字節(jié),那么第一個局部變量就可以用 localvar0-8(SP) 來表示。與硬件寄存器 SP 是兩個不同的東西,在棧幀 size 為 0 的情況下,偽寄存器 SP 和硬件寄存器 SP 指向同一位置。手寫匯編代碼時,如果是 symbol+offset(SP)形式,則表示偽寄存器 SP。如果是 offset(SP)則表示硬件寄存器 SP。務(wù)必注意:對于編譯輸出(go tool compile -S / go tool objdump)的代碼來講,所有的 SP 都是硬件 SP 寄存器,無論是否帶 symbol(這一點(diǎn)非常具有迷惑性,需要慢慢理解。往往在分析編譯輸出的匯編時,看到的就是硬件 SP 寄存器)。

    • PC: 實(shí)際上就是在體系結(jié)構(gòu)的知識中常見的 pc 寄存器,在 x86 平臺下對應(yīng) ip 寄存器,amd64 上則是 rip。除了個別跳轉(zhuǎn)之外,手寫 plan9 匯編代碼時,很少用到 PC 寄存器。

    通過上面的講解,想必已經(jīng)對 4 個核心寄存器的區(qū)別有了一定的認(rèn)識(或者是更加的迷惑、一頭霧水)。那么,需要留意的是:如果是在分析編譯輸出的匯編代碼時,要重點(diǎn)看 SP、SB 寄存器(FP 寄存器在這里是看不到的)。如果是,在手寫匯編代碼,那么要重點(diǎn)看 FP、SP 寄存器。

    3.2.1 偽寄存器的內(nèi)存模型

    下圖描述了棧楨與各個寄存器的內(nèi)存關(guān)系模型,值得注意的是要站在 callee 的角度來看。

    有一點(diǎn)需要注意的是,return addr 也是在 caller 的棧上的,不過往棧上插 return addr 的過程是由 CALL 指令完成的(在分析匯編時,是看不到關(guān)于 addr 相關(guān)空間信息的。在分配棧空間時,addr 所占用空間大小不包含在棧幀大小內(nèi))。

    在 AMD64 環(huán)境,偽 PC 寄存器其實(shí)是 IP 指令計(jì)數(shù)器寄存器的別名。偽 FP 寄存器對應(yīng)的是 caller 函數(shù)的幀指針,一般用來訪問 callee 函數(shù)的入?yún)?shù)和返回值。偽 SP 棧指針對應(yīng)的是當(dāng)前 callee 函數(shù)棧幀的底部(不包括參數(shù)和返回值部分),一般用于定位局部變量。偽 SP 是一個比較特殊的寄存器,因?yàn)檫€存在一個同名的 SP 真寄存器,真 SP 寄存器對應(yīng)的是棧的頂部。

    在編寫 Go 匯編時,當(dāng)需要區(qū)分偽寄存器和真寄存器的時候只需要記住一點(diǎn):偽寄存器一般需要一個標(biāo)識符和偏移量為前綴,如果沒有標(biāo)識符前綴則是真寄存器。比如(SP)、+8(SP)沒有標(biāo)識符前綴為真 SP 寄存器,而 a(SP)、b+8(SP)有標(biāo)識符為前綴表示偽寄存器。

    3.2.2 幾點(diǎn)說明

    我們這里對容易混淆的幾點(diǎn)簡單進(jìn)行說明:

    • 偽 SP 和硬件 SP 不是一回事,在手寫匯編代碼時,偽 SP 和硬件 SP 的區(qū)分方法是看該 SP 前是否有 symbol。如果有 symbol,那么即為偽寄存器,如果沒有,那么說明是硬件 SP 寄存器。

    • 偽 SP 和 FP 的相對位置是會變的,所以不應(yīng)該嘗試用偽 SP 寄存器去找那些用 FP+offset 來引用的值,例如函數(shù)的入?yún)⒑头祷刂怠?/p>

    • 官方文檔中說的偽 SP 指向 stack 的 top,可能是有問題的。其指向的局部變量位置實(shí)際上是整個棧的棧底(除 caller BP 之外),所以說 bottom 更合適一些。

    • 在 go tool objdump/go tool compile -S 輸出的代碼中,是沒有偽 SP 和 FP 寄存器的,我們上面說的區(qū)分偽 SP 和硬件 SP 寄存器的方法,對于上述兩個命令的輸出結(jié)果是沒法使用的。在編譯和反匯編的結(jié)果中,只有真實(shí)的 SP 寄存器。

    3.2.3 IA64 和 plan9 的對應(yīng)關(guān)系

    在 plan9 匯編里還可以直接使用的 amd64 的通用寄存器,應(yīng)用代碼層面會用到的通用寄存器主要是: rax, rbx, rcx, rdx, rdi, rsi, r8~r15 這些寄存器,雖然 rbp 和 rsp 也可以用,不過 bp 和 sp 會被用來管理?xiàng)m敽蜅5?#xff0c;最好不要拿來進(jìn)行運(yùn)算。

    plan9 中使用寄存器不需要帶 r 或 e 的前綴,例如 rax,只要寫 AX 即可: MOVQ $101, AX = mov rax, 101

    下面是通用通用寄存器的名字在 IA64 和 plan9 中的對應(yīng)關(guān)系:

    3.3 常用操作指令

    下面列出了常用的幾個匯編指令(指令后綴Q 說明是 64 位上的匯編指令)

    助記符指令種類用途示例
    MOVQ傳送數(shù)據(jù)傳送MOVQ 48, AX // 把 48 傳送到 AX
    LEAQ傳送地址傳送LEAQ AX, BX // 把 AX 有效地址傳送到 BX
    PUSHQ傳送棧壓入PUSHQ AX // 將 AX 內(nèi)容送入棧頂位置
    POPQ傳送棧彈出POPQ AX // 彈出棧頂數(shù)據(jù)后修改棧頂指針
    ADDQ運(yùn)算相加并賦值ADDQ BX, AX // 等價于 AX+=BX
    SUBQ運(yùn)算相減并賦值SUBQ BX, AX // 等價于 AX-=BX
    CMPQ運(yùn)算比較大小CMPQ SI CX // 比較 SI 和 CX 的大小
    CALL轉(zhuǎn)移調(diào)用函數(shù)CALL runtime.printnl(SB) // 發(fā)起調(diào)用
    JMP轉(zhuǎn)移無條件轉(zhuǎn)移指令JMP 0x0185 //無條件轉(zhuǎn)至 0x0185 地址處
    JLS轉(zhuǎn)移條件轉(zhuǎn)移指令JLS 0x0185 //左邊小于右邊,則跳到 0x0185

    4. 匯編分析

    說了那么多,it is code show time。

    4.1 如何輸出 Go 匯編

    對于寫好的 go 源碼,生成對應(yīng)的 Go 匯編,大概有下面幾種

    • 方法 1 先使用 go build -gcflags "-N -l" main.go 生成對應(yīng)的可執(zhí)行二進(jìn)制文件 再使用 go tool objdump -s "main\." main 反編譯獲取對應(yīng)的匯編

    反編譯時"main\." 表示只輸出 main 包中相關(guān)的匯編"main\.main" 則表示只輸出 main 包中 main 方法相關(guān)的匯編

    • 方法 2 使用 go tool compile -S -N -l main.go 這種方式直接輸出匯編

    • 方法 3 使用go build -gcflags="-N -l -S" main.go 直接輸出匯編

    注意:在使用這些命令時,加上對應(yīng)的 flag,否則某些邏輯會被編譯器優(yōu)化掉,而看不到對應(yīng)完整的匯編代碼

    -l 禁止內(nèi)聯(lián) -N 編譯時,禁止優(yōu)化 -S 輸出匯編代碼

    4.2 Go 匯編示例

    go 示例代碼

    package?mainfunc?add(a,?b?int)?int{sum?:=?0?//?不設(shè)置該局部變量sum,add棧空間大小會是0sum?=?a+breturn?sum }func?main(){println(add(1,2)) }

    編譯 go 源代碼,輸出匯編

    go?tool?compile?-N?-l?-S?main.go

    截取主要匯編如下:

    "".add STEXT nosplit size=60 args=0x18 locals=0x100x0000 00000 (main.go:3) TEXT "".add(SB), NOSPLIT, $16-240x0000 00000 (main.go:3) SUBQ $16, SP ;;生成add棧空間0x0004 00004 (main.go:3) MOVQ BP, 8(SP)0x0009 00009 (main.go:3) LEAQ 8(SP), BP;; ...omitted FUNCDATA stuff...0x000e 00014 (main.go:3) MOVQ $0, "".~r2+40(SP) ;;初始化返回值0x0017 00023 (main.go:4) MOVQ $0, "".sum(SP) ;;局部變量sum賦為00x001f 00031 (main.go:5) MOVQ "".a+24(SP), AX ;;取參數(shù)a0x0024 00036 (main.go:5) ADDQ "".b+32(SP), AX ;;等價于AX=a+b0x0029 00041 (main.go:5) MOVQ AX, "".sum(SP) ;;賦值局部變量sum0x002d 00045 (main.go:6) MOVQ AX, "".~r2+40(SP) ;;設(shè)置返回值0x0032 00050 (main.go:6) MOVQ 8(SP), BP0x0037 00055 (main.go:6) ADDQ $16, SP ;;清除add棧空間0x003b 00059 (main.go:6) RET......"".main STEXT size=107 args=0x0 locals=0x280x0000 00000 (main.go:9) TEXT "".main(SB), $40-0......0x000f 00015 (main.go:9) SUBQ $40, SP ;; 生成main棧空間0x0013 00019 (main.go:9) MOVQ BP, 32(SP)0x0018 00024 (main.go:9) LEAQ 32(SP), BP;; ...omitted FUNCDATA stuff...0x001d 00029 (main.go:10) MOVQ $1, (SP) ;;add入?yún)?#xff1a;10x0025 00037 (main.go:10) MOVQ $2, 8(SP) ;;add入?yún)?#xff1a;20x002e 00046 (main.go:10) CALL "".add(SB) ;;調(diào)用add函數(shù)0x0033 00051 (main.go:10) MOVQ 16(SP), AX0x0038 00056 (main.go:10) MOVQ AX, ""..autotmp_0+24(SP)0x003d 00061 (main.go:10) CALL runtime.printlock(SB)0x0042 00066 (main.go:10) MOVQ ""..autotmp_0+24(SP), AX0x0047 00071 (main.go:10) MOVQ AX, (SP)0x004b 00075 (main.go:10) CALL runtime.printint(SB)0x0050 00080 (main.go:10) CALL runtime.printnl(SB)0x0055 00085 (main.go:10) CALL runtime.printunlock(SB)0x005a 00090 (main.go:11) MOVQ 32(SP), BP0x005f 00095 (main.go:11) ADDQ $40, SP ;;清除main棧空間0x0063 00099 (main.go:11) RET......

    這里列舉了一個簡單的 int 類型加法示例,實(shí)際開發(fā)中會遇到各種參數(shù)類型,要復(fù)雜的多,這里只是拋磚引玉 :)

    4.3 Go 匯編解析

    針對 4.2 輸出匯編,對重要核心代碼進(jìn)行分析。

    4.3.1 add 函數(shù)匯編解析
    • TEXT "".add(SB), NOSPLIT|ABIInternal, $16-24

    TEXT "".add TEXT 指令聲明了 "".add 是 .text 代碼段的一部分,并表明跟在這個聲明后的是函數(shù)的函數(shù)體。在鏈接期,""這個空字符會被替換為當(dāng)前的包名: 也就是說,"".add 在鏈接到二進(jìn)制文件后會變成 main.add

    (SB) SB 是一個虛擬的偽寄存器,保存靜態(tài)基地址(static-base) 指針,即我們程序地址空間的開始地址。"".add(SB) 表明我們的符號位于某個固定的相對地址空間起始處的偏移位置 (最終是由鏈接器計(jì)算得到的)。換句話來講,它有一個直接的絕對地址: 是一個全局的函數(shù)符號。

    NOSPLIT: 向編譯器表明不應(yīng)該插入 stack-split 的用來檢查棧需要擴(kuò)張的前導(dǎo)指令。在我們 add 函數(shù)的這種情況下,編譯器自己幫我們插入了這個標(biāo)記: 它足夠聰明地意識到,由于 add 沒有任何局部變量且沒有它自己的棧幀,所以一定不會超出當(dāng)前的棧。不然,每次調(diào)用函數(shù)時,在這里執(zhí)行棧檢查就是完全浪費(fèi) CPU 時間了。

    $0-16

    24 指定了調(diào)用方傳入的參數(shù)+返回值大小(24 字節(jié)=入?yún)?a、b 大小8字節(jié)*2+返回值8字節(jié))

    通常來講,幀大小后一般都跟隨著一個參數(shù)大小,用減號分隔。(這不是一個減法操作,只是一種特殊的語法) 幀大小 $24-8 意味著這個函數(shù)有 24 個字節(jié)的幀以及 8 個字節(jié)的參數(shù),位于調(diào)用者的幀上。如果 NOSPLIT 沒有在 TEXT 中指定,則必須提供參數(shù)大小。對于 Go 原型的匯編函數(shù),go vet 會檢查參數(shù)大小是否正確。

    In the general case, the frame size is followed by an argument size, separated by a minus sign. (It’s not a subtraction, just idiosyncratic syntax.) The frame size $24-8 states that the function has a 24-byte frame and is called with 8 bytes of argument, which live on the caller’s frame. If NOSPLIT is not specified for the TEXT, the argument size must be provided. For assembly functions with Go prototypes, go vet will check that the argument size is correct.

    • SUBQ $16, SPSP 為棧頂指針,該語句等價于 SP-=16(由于棧空間是向下增長的,所以開辟棧空間時為減操作),表示生成 16 字節(jié)大小的棧空間。

    • MOVQ $0, "".~r2+40(SP)此時的 SP 為 add 函數(shù)棧的棧頂指針,40(SP)的位置則是 add 返回值的位置,該位置位于 main 函數(shù)棧空間內(nèi)。該語句設(shè)置返回值類型的 0 值,即初始化返回值,防止得到臟數(shù)據(jù)(返回值類型為 int,int 的 0 值為 0)。

    • MOVQ "".a+24(SP), AX從 main 函數(shù)棧空間獲取入?yún)?a 的值,存到寄存器 AX

    • ADDQ "".b+32(SP), AX從 main 函數(shù)棧空間獲取入?yún)?b 的值,與寄存器 AX 中存儲的 a 值相加,結(jié)果存到 AX。相當(dāng)于 AX=a+b

    • MOVQ AX, "".~r2+40(SP)把 a+b 的結(jié)果放到 main 函數(shù)棧中, add(a+b)返回值所在的位置

    • ADDQ $16, SP歸還 add 函數(shù)占用的棧空間

    4.3.2 函數(shù)棧楨結(jié)構(gòu)模型

    根據(jù) 4.2 對應(yīng)匯編繪制的函數(shù)棧楨結(jié)構(gòu)模型

    還記得前面提到的,Go 匯編使用的是caller-save模式,被調(diào)用函數(shù)的參數(shù)、返回值、棧位置都需要由調(diào)用者維護(hù)、準(zhǔn)備嗎?

    在函數(shù)棧楨結(jié)構(gòu)中可以看到,add()函數(shù)的入?yún)⒁约胺祷刂刀加烧{(diào)用者 main()函數(shù)維護(hù)。也正是因?yàn)槿绱?#xff0c;GO 有了其他語言不具有的,支持多個返回值的特性。

    4.4 Go 匯編語法

    這里重點(diǎn)講一下函數(shù)聲明、變量聲明。

    4.4.1 函數(shù)聲明

    來看一個典型的 Go 匯編函數(shù)定義

    //?func?add(a,?b?int)?int //?該add函數(shù)聲明定義在同一個?package?name?下的任意?.go文件中 //?只有函數(shù)頭,沒有實(shí)現(xiàn)//?add函數(shù)的Go匯編實(shí)現(xiàn) //?pkgname?默認(rèn)是??"" TEXT?pkgname·add(SB),?NOSPLIT,?$16-24MOVQ?a+0(FP),?AXADDQ?b+8(FP),?AXMOVQ?AX,?ret+16(FP)RET

    Go 匯編實(shí)現(xiàn)為什么是 TEXT 開頭?仔細(xì)觀察上面的進(jìn)程內(nèi)存布局圖就會發(fā)現(xiàn),我們的代碼在是存儲在.text 段中的,這里也就是一種約定俗成的起名方式。實(shí)際上在 plan9 中 TEXT 是一個指令,用來定義一個函數(shù)。

    定義中的 pkgname 是可以省略的,(非想寫也可以寫上,不過寫上 pkgname 的話,在重命名 package 之后還需要改代碼,默認(rèn)為"") 編譯器會在鏈接期自動加上所屬的包名稱。

    中點(diǎn) · 比較特殊,是一個 unicode 的中點(diǎn),該點(diǎn)在 mac 下的輸入方法是 option+shift+9。在程序被鏈接之后,所有的中點(diǎn)·都會被替換為句號.,比如你的方法是runtime·main,在編譯之后的程序里的符號則是runtime.main。

    簡單總結(jié)一下, Go 匯編實(shí)現(xiàn)函數(shù)聲明,格式為:

    ?靜態(tài)基地址(static-base)?指針||?????????add函數(shù)入?yún)?#43;返回值總大小|???????????????| TEXT?pkgname·add(SB),NOSPLIT,$16-24|??????|????????????????| 函數(shù)所屬包名??函數(shù)名??????????add函數(shù)棧幀大小
    • 函數(shù)棧幀大小:局部變量+可能需要的額外調(diào)用函數(shù)的參數(shù)空間的總大小,不包括調(diào)用其它函數(shù)時的 ret address 的大小。

    • (SB): SB 是一個虛擬寄存器,保存了靜態(tài)基地址(static-base) 指針,即我們程序地址空間的開始地址。"".add(SB) 表明我們的符號位于某個固定的相對地址空間起始處的偏移位置 (最終是由鏈接器計(jì)算得到的)。換句話來講,它有一個直接的絕對地址: 是一個全局的函數(shù)符號。

    • NOSPLIT: 向編譯器表明,不應(yīng)該插入 stack-split 的用來檢查棧需要擴(kuò)張的前導(dǎo)指令。在我們 add 函數(shù)的這種情況下,編譯器自己幫我們插入了這個標(biāo)記: 它足夠聰明地意識到,add 不會超出當(dāng)前的棧,因此沒必要調(diào)用函數(shù)時在這里執(zhí)行棧檢查。

    4.4.2 變量聲明

    匯編里的全局變量,一般是存儲在.rodata或者.data段中。對應(yīng)到 Go 代碼,就是已初始化過的全局的 const、var 變量/常量。

    使用 DATA 結(jié)合 GLOBL 來定義一個變量。

    DATA 的用法為:

    DATA?symbol+offset(SB)/width,?value

    大多數(shù)參數(shù)都是字面意思,不過這個 offset 需要注意:其含義是該值相對于符號 symbol 的偏移,而不是相對于全局某個地址的偏移。

    GLOBL 匯編指令用于定義名為 symbol 的全局變量,變量對應(yīng)的內(nèi)存寬度為 width,內(nèi)存寬度部分必須用常量初始化。

    GLOBL?·symbol(SB),?width

    下面是定義了多個變量的例子:

    DATA?·age+0(SB)/4,?$8??;;?數(shù)值8為?4字節(jié) GLOBL?·age(SB),?RODATA,?$4DATA?·pi+0(SB)/8,?$3.1415926?;;?數(shù)值3.1415926為float64,?8字節(jié) GLOBL?·pi(SB),?RODATA,?$8DATA?·year+0(SB)/4,?$2020?;;?數(shù)值2020為?4字節(jié) GLOBL?·year(SB),?RODATA,?$4;;?變量hello?使用2個DATA來定義 DATA?·hello+0(SB)/8,?$"hello?my"?;;?`hello?my`?共8個字節(jié) DATA?·hello+8(SB)/8,?$"???world"?;;?`???world`?共8個字節(jié)(3個空格) GLOBL?·hello(SB),?RODATA,?$16?;;?`hello?my???world`??共16個字節(jié)DATA?·hello<>+0(SB)/8,?$"hello?my"?;;?`hello?my`?共8個字節(jié) DATA?·hello<>+8(SB)/8,?$"???world"?;;?`???world`?共8個字節(jié)(3個空格) GLOBL?·hello<>(SB),?RODATA,?$16?;;?`hello?my???world`??共16個字節(jié)

    大部分都比較好理解,不過這里引入了新的標(biāo)記<>,這個跟在符號名之后,表示該全局變量只在當(dāng)前文件中生效,類似于 C 語言中的 static。如果在另外文件中引用該變量的話,會報 relocation target not found 的錯誤。

    5. 手寫匯編實(shí)現(xiàn)功能

    在 Go 源碼中會看到一些匯編寫的代碼,這些代碼跟其他 go 代碼一起組成了整個 go 的底層功能實(shí)現(xiàn)。下面,我們通過一個簡單的 Go 匯編代碼示例來實(shí)現(xiàn)兩數(shù)相加功能。

    5.1 使用 Go 匯編實(shí)現(xiàn) add 函數(shù)

    Go 代碼

    package?mainfunc?add(a,?b?int64)?int64func?main(){println(add(2,3)) }

    Go 源碼中 add()函數(shù)只有函數(shù)簽名,沒有具體的實(shí)現(xiàn)(使用 GO 匯編實(shí)現(xiàn))

    使用 Go 匯編實(shí)現(xiàn)的 add()函數(shù)

    TEXT?·add(SB),?$0-24?;;?add棧空間為0,入?yún)?#43;返回值大小=24字節(jié)MOVQ?x+0(FP), AX ;;?從main中取參數(shù):2ADDQ?y+8(FP), AX ;;?從main中取參數(shù):3MOVQ?AX,?ret+16(FP)?;;?保存結(jié)果到返回值RET

    把 Go 源碼與 Go 匯編編譯到一起(我這里,這兩個文件在同一個目錄)

    go?build?-gcflags?"-N?-l"?.

    我這里目錄為 demo1,所以得到可執(zhí)行程序 demo1,運(yùn)行得到結(jié)果:5

    5.2 反編譯可執(zhí)行程序

    對 5.1 中得到的可執(zhí)行程序 demo1 使用 objdump 進(jìn)行反編譯,獲取匯編代碼

    go?tool?objdump?-s?"main\."?demo1

    得到匯編

    ...... TEXT?main.main(SB)?/root/go/src/demo1/main.gomain.go:5???0x4581d0?????64488b0c25f8ffffff??????MOVQ?FS:0xfffffff8,?CXmain.go:5???0x4581d9?????483b6110????????????????CMPQ?0x10(CX),?SPmain.go:5???0x4581dd?????7655????????????????????JBE?0x458234main.go:5???0x4581df?????4883ec28????????????????SUBQ?$0x28,?SP?;;生成main棧楨main.go:5???0x4581e3?????48896c2420??????????????MOVQ?BP,?0x20(SP)main.go:5???0x4581e8?????488d6c2420??????????????LEAQ?0x20(SP),?BPmain.go:6???0x4581ed?????48c7042402000000????????MOVQ?$0x2,?0(SP)?;;參數(shù)值?2main.go:6???0x4581f5?????48c744240803000000??????MOVQ?$0x3,?0x8(SP)?;;參數(shù)值?3main.go:6???0x4581fe?????e83d000000??????????????CALL?main.add(SB);;call?addmain.go:6???0x458203?????488b442410??????????????MOVQ?0x10(SP),?AXmain.go:6???0x458208?????4889442418??????????????MOVQ?AX,?0x18(SP)main.go:6???0x45820d?????e8fe2dfdff??????????????CALL?runtime.printlock(SB)main.go:6???0x458212?????488b442418??????????????MOVQ?0x18(SP),?AXmain.go:6???0x458217?????48890424????????????????MOVQ?AX,?0(SP)main.go:6???0x45821b?????e87035fdff??????????????CALL?runtime.printint(SB)main.go:6???0x458220?????e87b30fdff??????????????CALL?runtime.printnl(SB)main.go:6???0x458225?????e8662efdff??????????????CALL?runtime.printunlock(SB)main.go:7???0x45822a?????488b6c2420??????????????MOVQ?0x20(SP),?BPmain.go:7???0x45822f?????4883c428????????????????ADDQ?$0x28,?SPmain.go:7???0x458233?????c3??????????????????????RETmain.go:5???0x458234?????e89797ffff??????????????CALL?runtime.morestack_noctxt(SB)main.go:5???0x458239?????eb95????????????????????JMP?main.main(SB);;?反編譯得到的匯編與add_amd64.s文件中的匯編大致操作一致 TEXT?main.add(SB)?/root/go/src/demo1/add_amd64.sadd_amd64.s:2???0x458240????488b442408????MOVQ?0x8(SP),?AX?;;?獲取第一個參數(shù)add_amd64.s:3???0x458245????4803442410????ADDQ?0x10(SP),?AX?;;參數(shù)a+參數(shù)badd_amd64.s:5???0x45824a????4889442418????MOVQ?AX,?0x18(SP)?;;保存計(jì)算結(jié)果add_amd64.s:7???0x45824f????c3????????????RET

    通過上面操作,可知:

  • (FP)偽寄存器,只有在編寫 Go 匯編代碼時使用。FP 偽寄存器指向 caller 傳遞給 callee 的第一個參數(shù)

  • 使用 go tool compile / go tool objdump 得到的匯編中看不到(FP)寄存器的蹤影

  • 6. Go 調(diào)試工具

    這里推薦 2 個 Go 代碼調(diào)試工具。

    6.1 gdb 調(diào)試 Go 代碼

    測試代碼

    package?maintype?Ier?interface{add(a,?b?int)?intsub(a,?b?int)?int }type?data?struct{a,?b?int }func?(*data)?add(a,?b?int)?int{return?a+b }func?(*data)?sub(a,?b?int)?int{return?a-b }func?main(){var?t?Ier?=?&data{3,4}println(t.add(1,2))println(t.sub(3,2)) }

    編譯 go build -gcflags "-N -l" -o main

    使用 GDB 調(diào)試

    > gdb mainGNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-80.el7 Copyright (C) 2013 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later http://gnu.org/licenses/gpl.html This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "x86_64-redhat-linux-gnu". For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>... Reading symbols from /root/go/src/interface/main...done. Loading Go Runtime support. (gdb) list // 顯示源碼 14 func (*data) add(a, b int) int{ 15 return a+b 16 } 17 18 func (*data) sub(a, b int) int{ 19 return a-b 20 } 21 22 23 func main(){ (gdb) list 24 var t Ier = &data{3,4} 25 26 println(t.add(1,2)) 27 println(t.sub(3,2)) 28 } 29 (gdb) b 26 // 在源碼26行處設(shè)置斷點(diǎn) Breakpoint 1 at 0x45827c: file /root/go/src/interface/main.go, line 26. (gdb) r Starting program: /root/go/src/interface/mainBreakpoint 1, main.main () at /root/go/src/interface/main.go:26 26 println(t.add(1,2)) (gdb) info locals // 顯示變量 t = {tab = 0x487020 <data,main.Ier>, data = 0xc000096000} (gdb) ptype t // 打印t的結(jié)構(gòu) type = struct runtime.iface {runtime.itab *tab;void *data; } (gdb) p *t.tab.inter // 打印t.tab.inter指針指向的數(shù)據(jù) $2 = {typ = {size = 16, ptrdata = 16, hash = 2491815843, tflag = 7 '\a', align = 8 '\b', fieldAlign = 8 '\b',kind = 20 '\024', equal = {void (void *, void *, bool *)} 0x466ec0,gcdata = 0x484351 "\002\003\004\005\006\a\b\t\n\f\r\016\017\020\022\025\026\030\033\034\036\037\"&(,-5<BUXx\216\231\330\335\377", str = 6568, ptrToThis = 23808}, pkgpath = {bytes = 0x4592b4 ""}, mhdr = []runtime.imethod = {{name = 277,ityp = 48608}, {name = 649, ityp = 48608}}} (gdb) disass // 顯示匯編 Dump of assembler code for function main.main:0x0000000000458210 <+0>: mov %fs:0xfffffffffffffff8,%rcx0x0000000000458219 <+9>: cmp 0x10(%rcx),%rsp0x000000000045821d <+13>: jbe 0x458324 <main.main+276>0x0000000000458223 <+19>: sub $0x50,%rsp0x0000000000458227 <+23>: mov %rbp,0x48(%rsp)0x000000000045822c <+28>: lea 0x48(%rsp),%rbp0x0000000000458231 <+33>: lea 0x10dc8(%rip),%rax # 0x4690000x0000000000458238 <+40>: mov %rax,(%rsp)0x000000000045823c <+44>: callq 0x40a5c0 <runtime.newobject>

    常用的 gdb 調(diào)試命令

    • run

    • continue

    • break

    • backtrace 與 frame

    • info break、locals

    • list 命令

    • print 和 ptype 命令

    • disass

    除了 gdb,另外推薦一款 gdb 的增強(qiáng)版調(diào)試工具 cgdb

    https://cgdb.github.io/

    效果如下圖所示,分兩個窗口:上面顯示源代碼,下面是具體的命令行調(diào)試界面(跟 gdb 一樣):

    6.2 delve 調(diào)試代碼

    delve 項(xiàng)目地址

    https://github.com/go-delve/delve

    帶圖形化界面的 dlv 項(xiàng)目地址

    https://github.com/aarzilli/gdlv

    dlv 的安裝使用,這里不再做過多講解,感興趣的可以嘗試一下。

    • gdb 作為調(diào)試工具自是不用多說,比較老牌、強(qiáng)大,可以支持多種語言。

    • delve 則是使用 go 語言開發(fā)的,用來調(diào)試 go 的工具,功能也是十分強(qiáng)大,打印結(jié)果可以顯示 gdb 支持不了的東西,這里不再做過多講解,有興趣的可以查閱相關(guān)資料。

    7. 總結(jié)

    對于 Go 匯編基礎(chǔ)大致需要熟悉下面幾個方面:

    通過上面的例子相信已經(jīng)讓你對 Go 的匯編有了一定的理解。當(dāng)然,對于大部分業(yè)務(wù)開發(fā)人員來說,只要看的懂即可。如果想進(jìn)一步的了解,可以閱讀相關(guān)的資料或者書籍。

    最后想說的是:鑒于個人能力有限,在閱讀過程中你可能會發(fā)現(xiàn)存在的一些問題或者缺陷,歡迎各位大佬指正。如果感興趣的話,也可以一起私下交流。

    8. 參考資料

    在整理的過程中,部分參考、引用下面鏈接地址內(nèi)容。有一些寫的還是不錯的,感興趣的同學(xué)可以閱讀。

    [1] https://github.com/cch123/golang-notes/blob/master/assembly.md plan9 assembly?

    [2] https://segmentfault.com/a/1190000019753885 匯編入門?

    [3] https://www.davidwong.fr/goasm/ Go Assembly by Example?

    [4] https://juejin.im/post/6844904005630443533#heading-3?

    [5] https://github.com/go-internals-cn/go-internals/blob/master/chapter1_assembly_primer/README.md?

    [6] https://lrita.github.io/2017/12/12/golang-asm/?

    [7] https://chai2010.cn/advanced-go-programming-book/ch3-asm/ch3-01-basic.html

    總結(jié)

    以上是生活随笔為你收集整理的Golang 汇编入门知识总结的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

    亚洲婷婷免费 | 五月开心激情网 | 一级a性色生活片久久毛片波多野 | www.色综合.com | 成人综合婷婷国产精品久久免费 | 成年人视频在线观看免费 | 三级av免费看 | 久久艹人人| 国产精品女主播一区二区三区 | 六月丁香在线观看 | 91漂亮少妇露脸在线播放 | 五月天婷婷视频 | 欧美日韩aa | 丁香六月网 | 在线免费黄色av | 91九色国产在线 | 亚洲春色综合另类校园电影 | 久久夜色电影 | www.天天成人国产电影 | 97超碰人人模人人人爽人人爱 | 国产免费美女 | 最近更新中文字幕 | 丁香激情综合久久伊人久久 | 日本午夜在线观看 | 国内久久久| 蜜臀av麻豆| 欧美另类69 | 一区二区观看 | 午夜精品三区 | 亚洲精品视频在线观看视频 | 天天操天天操天天操 | 99热.com| 欧美精品九九99久久 | 99re久久资源最新地址 | 亚洲精品日韩在线观看 | 夜夜骑首页 | 91人人视频在线观看 | 天天色成人网 | 伊人午夜视频 | 久草网站在线 | 99九九热只有国产精品 | 精品国产自在精品国产精野外直播 | 99r在线观看| 国产亚洲一区二区在线观看 | 欧美日韩另类在线观看 | 国产精品毛片久久久久久久 | 欧美日韩国产在线一区 | 高清免费在线视频 | 国产精品成人在线观看 | 久久久一本精品99久久精品66 | 在线亚洲高清视频 | www.天天综合 | 91大神免费视频 | 永久免费视频国产 | 亚洲激情婷婷 | 黄色在线网站噜噜噜 | 久久久久久久久久久久亚洲 | 天天天天色射综合 | 亚洲午夜av电影 | 色天天中文| 色噜噜在线观看视频 | 色97在线 | 日韩精品不卡 | 成人免费 在线播放 | 久久久久久国产精品久久 | 9在线观看免费高清完整版 玖玖爱免费视频 | 免费av福利 | 又长又大又黑又粗欧美 | 成人性生交大片免费看中文网站 | 久久免费看a级毛毛片 | 亚洲综合激情小说 | 中文字字幕在线 | 人人干人人模 | 日韩av成人在线观看 | 国产精品亚洲综合久久 | 五月激情亚洲 | 欧美日韩一区久久 | 久久视频这里只有精品 | 俺要去色综合狠狠 | 成人精品久久久 | 成年人免费看片网站 | 国产精品免费久久久久久久久久中文 | 日韩专区 在线 | 国产一线二线三线性视频 | 亚洲精品视频二区 | 日本xxxx裸体xxxx17 | 久久久久久久久免费视频 | aaa日本高清在线播放免费观看 | 免费高清在线观看成人 | 96香蕉视频 | 成人免费观看在线视频 | 中文字幕在线专区 | 4438全国亚洲精品在线观看视频 | 久精品视频免费观看2 | 久久久久久久毛片 | av大全免费在线观看 | 91豆麻精品91久久久久久 | 99久久久国产免费 | 日韩区在线观看 | 色av男人的天堂免费在线 | 99视频精品 | 国产一区久久 | 午夜黄色影院 | 国产69精品久久99的直播节目 | 一本—道久久a久久精品蜜桃 | 中文字幕av专区 | 99在线视频观看 | 黄色午夜 | 国产精品午夜免费福利视频 | 国产精品一区二区在线 | 国产精品乱码久久久久 | 亚洲免费小视频 | 国产一级做a| 99精品视频中文字幕 | 国内视频1区 | 激情一区二区三区欧美 | 91福利视频免费观看 | 久久69精品 | 9999精品视频 | 欧美国产日韩一区二区三区 | 国产一级性生活 | 色av男人的天堂免费在线 | 99在线精品视频在线观看 | 成人av在线一区二区 | 亚洲aⅴ一区二区三区 | 国产精品岛国久久久久久久久红粉 | av免费片 | 国产精品一区二区久久国产 | 亚洲蜜桃在线 | 国产日产高清dvd碟片 | 欧美一二三视频 | 久久视频一区二区 | 欧洲精品码一区二区三区免费看 | 69欧美视频 | 天海冀一区二区三区 | 福利视频一二区 | 国产一区二区免费 | 国产黄色在线看 | 日韩在线一级 | 免费视频久久 | 国产视频美女 | 日韩资源在线播放 | 国产精品福利一区 | 色婷婷婷 | 免费av片在线 | 日韩理论影院 | 懂色av懂色av粉嫩av分享吧 | 成人午夜电影在线播放 | 欧美另类人妖 | 久久久久在线观看 | 亚洲精品午夜久久久 | 九九免费在线视频 | 国产日产精品久久久久快鸭 | 国产日产精品一区二区三区四区 | 精品国产久| 丁香花五月 | 99国产精品久久久久老师 | 久久69精品 | 中文字幕在线观看不卡 | 狠狠狠色丁香综合久久天下网 | 99久久婷婷国产综合精品 | 亚洲我射av | 99精品亚洲 | 免费观看视频的网站 | 在线观看中文字幕亚洲 | 五月天电影免费在线观看一区 | 国产麻豆精品传媒av国产下载 | 国产无吗一区二区三区在线欢 | 久久久亚洲麻豆日韩精品一区三区 | 久久成人精品视频 | 欧美在线资源 | av电影在线免费 | 91成人天堂久久成人 | 国产精品二区在线 | 久草在线视频中文 | 91女子私密保健养生少妇 | 狠狠狠色丁香综合久久天下网 | 日韩精品三区四区 | 免费日韩视频 | 日本久久久久久科技有限公司 | www.夜夜草 | 天天se天天cao天天干 | 亚洲精品在线免费 | 精品国产自在精品国产精野外直播 | 成人国产精品免费 | 天天操夜夜爱 | 精品久久久久久国产偷窥 | 亚洲成人黄色网址 | 国产色黄网站 | 亚洲女同videos | 丁香激情网 | 在线观看视频国产 | 手机在线看片日韩 | 国产视频首页 | 精品成人在线 | 日韩免费网站 | av成人免费 | 中文字幕精品一区二区精品 | 中文国产在线观看 | 欧美精品乱码99久久影院 | 日产中文字幕 | 五月激情久久 | 国产精品嫩草55av | 日韩中文字幕在线 | av色综合网| 亚洲欧洲成人精品av97 | 高清国产一区 | 91污在线观看| 在线观看日韩专区 | 一区在线电影 | 亚洲最新av在线网址 | 怡红院久久 | 国产99久久| 中文十次啦 | 69精品在线观看 | 亚洲精品国精品久久99热 | 亚洲国产成人av网 | 婷婷深爱网 | 嫩嫩影院理论片 | 久久av影院 | 国产精品美女久久久久久2018 | 中文字幕日本在线观看 | 一区二区三区四区精品视频 | 欧美大香线蕉线伊人久久 | 人人射人人爱 | 国产在线观看91 | 亚洲涩涩色 | 九九视频精品在线 | 中文字幕在线视频网站 | 国产麻豆视频 | 看片网站黄色 | av在观看 | 中文字幕在线播放视频 | 亚洲精品视频在线观看免费视频 | 911av视频| 四虎影视成人精品国库在线观看 | 亚洲精选在线 | 黄色av三级在线 | 国产黄色一级大片 | 中文字幕在线观看的网站 | 日日夜夜国产 | 正在播放一区二区 | 免费无遮挡动漫网站 | 99人久久精品视频最新地址 | 99精品国产在热久久下载 | 国产成人精品久久久久蜜臀 | 久久国产精品久久w女人spa | 久久久2o19精品 | 狠狠久久综合 | 在线看国产日韩 | 免费看亚洲毛片 | 91精品1区| 美女视频黄是免费的 | 最新午夜| 99精品国产一区二区三区麻豆 | 草久中文字幕 | 午夜精品区| 精品在线观看一区二区 | 热久久免费国产视频 | 欧美精品一区在线 | 视频一区在线播放 | 国产福利91精品 | 99热这里精品 | 亚洲精品综合欧美二区变态 | 色综合激情网 | 91视频在线网址 | 精品成人a区在线观看 | 国产精品二区在线观看 | 丝袜制服综合网 | 国产97超碰 | 亚洲天天综合 | 国产亚洲成人精品 | 麻花豆传媒一二三产区 | 国产亚洲欧美一区 | 国产999在线观看 | 午夜精品久久久久久久久久久 | 丁香久久久 | 99免费看片 | 久久综合9988久久爱 | 免费网址在线播放 | 日韩中文在线字幕 | 亚洲一区动漫 | 91在线视频导航 | 国产在线一区观看 | 欧美一区三区四区 | 日韩欧美99| 又黄又爽又色无遮挡免费 | 国产999精品久久久久久 | 日韩欧美电影在线 | 一区二区三区在线观看中文字幕 | 久久久久99精品成人片三人毛片 | 可以免费观看的av片 | 久久免费看 | 成人小视频在线 | 少妇搡bbbb搡bbb搡忠贞 | 日韩大片在线看 | 国产激情久久久 | av一级久久 | 国产精品久久久久久久99 | www.狠狠色.com | 亚洲高清在线视频 | 天天射夜夜爽 | 精品亚洲欧美一区 | 久久一区二区三区国产精品 | 国产精品久久久一区二区三区网站 | 免费a级毛片在线看 | 日韩精品2区 | 中文字幕在线观看2018 | 国产精品免费观看国产网曝瓜 | 91中文字幕 | 狠狠做深爱婷婷综合一区 | 福利视频网址 | 日韩欧美一区二区三区视频 | 婷婷丁香六月 | 国产主播大尺度精品福利免费 | 中文字幕中文字幕在线中文字幕三区 | 亚洲成人av一区 | 欧美在线视频一区二区 | 国产又粗又猛又爽又黄的视频先 | 97超级碰碰碰视频在线观看 | 天天射天天艹 | 日韩毛片在线播放 | 97国产精品免费 | 亚洲精品美女久久久久 | 超碰人人草人人 | 免费看v片网站 | 成人免费网站在线观看 | 日韩精品久久久久久 | 色多多在线观看 | 亚洲乱码一区 | 日韩精品一区二区三区在线视频 | 欧美一区二区三区免费观看 | 中文字幕日韩国产 | 不卡av在线免费观看 | 激情婷婷在线 | free,性欧美| 综合网成人 | 美女免费视频一区 | 精品成人在线 | 久久精品电影网 | 久久av免费 | 97精品国产97久久久久久免费 | 91亚洲精品在线观看 | 久草观看| 在线免费视频 你懂得 | 国际精品网 | 欧美精品亚洲二区 | 美女视频又黄又免费 | 久久成人在线 | 97日日碰人人模人人澡分享吧 | 精品免费国产一区二区三区四区 | 亚洲精品免费看 | 91av大全 | 国产少妇在线观看 | 国产精品网站一区二区三区 | 97精产国品一二三产区在线 | 成人h动漫精品一区二 | 人人澡av | 久久高清 | 成人一级在线观看 | 日本视频网 | 国产 日韩 欧美 中文 在线播放 | 久久99日韩 | 欧美中文字幕久久 | 91福利视频免费观看 | 欧美热久久| 成年人国产精品 | 亚洲综合视频在线 | 久草视频在线资源站 | 91精品第一页 | 午夜久久久久 | 成人免费在线播放 | 亚洲免费不卡 | www.91av在线 | 丁香伊人网 | 超碰在线国产 | 亚洲一级黄色大片 | 夜夜操综合网 | 久久成人精品电影 | 玖玖在线观看视频 | 亚洲一一在线 | 久久久精品欧美一区二区免费 | 九七视频在线 | 天天干天天干天天操 | 国产精品ⅴa有声小说 | 天天色综合1 | 亚洲第一伊人 | 天天射综合 | 在线观看免费一级片 | 久久久久国产a免费观看rela | 婷婷亚洲五月色综合 | avwww在线| 中文字幕在线观看完整版 | 中文字幕欧美激情 | 福利在线看片 | 在线 国产 亚洲 欧美 | 日韩激情av在线 | 三级黄色欧美 | 欧美日韩不卡一区二区三区 | www.成人sex| 福利片免费看 | 97视频在线观看视频免费视频 | 国产精品欧美久久 | 国产在线永久 | 久久久久久久久久电影 | 精品色999 | 天天操天天操天天操 | 免费精品视频在线观看 | 免费91麻豆精品国产自产在线观看 | 美女网站在线观看 | 蜜臀av性久久久久蜜臀av | 欧美乱熟臀69xxxxxx | 欧美日韩综合在线观看 | 久久久精品视频网站 | 69精品视频在线观看 | 一区二区三区在线免费观看视频 | 久久精品久久99精品久久 | 亚洲美女视频在线观看 | 国产中文视频 | 亚洲一区日韩在线 | 日韩高清在线不卡 | 亚洲jizzjizz日本少妇 | 中文av在线播放 | 91欧美国产| 国产精品成人一区二区三区吃奶 | 亚洲高清视频在线观看免费 | 五月婷婷色播 | 中文字幕二区 | 美女免费黄视频网站 | 婷婷成人在线 | 91黄色免费看 | 九九色在线观看 | 夜夜夜草| 国产精品久久久久久久久久免费 | 日韩在线观看视频一区二区三区 | 国产精品久久久久永久免费观看 | 黄色网址中文字幕 | 中文字幕一区二区三区四区在线视频 | 日本公妇在线观看 | 久久夜靖品 | 狠狠躁日日躁夜夜躁av | 国产不卡一区二区视频 | 免费av 在线 | 久久在线看| 婷婷精品国产欧美精品亚洲人人爽 | 激情图片区 | 天天爽人人爽夜夜爽 | 国产成人99久久亚洲综合精品 | 在线看国产日韩 | 欧美色伊人 | 久久久蜜桃一区二区 | 欧美国产91| 亚洲国内精品在线 | 亚洲午夜小视频 | 成人在线免费观看视视频 | 日韩一区二区三免费高清在线观看 | 丝袜美女在线 | 日韩av在线小说 | 亚洲人久久久 | 丁香六月在线观看 | 国内精品久久久久影院男同志 | 欧美日韩精品免费观看 | 久久久久久久国产精品视频 | 一本一道波多野毛片中文在线 | 天天干天天操天天爱 | 五月婷婷六月丁香 | 欧美精品小视频 | 99视频精品全国免费 | 欧美激情综合色 | 九九久久精品视频 | 在线电影日韩 | 中文字幕一区在线观看视频 | 又黄又爽又色无遮挡免费 | 福利网在线| 色狠狠综合 | 黄色软件在线观看 | 国产色综合天天综合网 | 日韩中字在线 | 91最新在线视频 | 日本视频高清 | 国产日韩欧美在线免费观看 | 国产精品一区二区三区99 | 96看片| 国产日韩视频在线播放 | 丁香花五月 | 日韩精品91偷拍在线观看 | 日韩在线免费小视频 | 97干com| 日日夜夜天天人人 | 黄色成人av网址 | 99精品欧美一区二区三区 | 激情丁香月| 午夜精品福利一区二区 | 国产成人在线网站 | 欧美色操 | 国产色网站 | 久久久精品国产一区二区电影四季 | 免费看的黄色网 | 国产精品久久久一区二区三区网站 | 久久8精品 | av短片在线| 国产日韩精品一区二区在线观看播放 | 国产精品欧美久久 | 久久精品专区 | 日本99久久 | 色a资源在线 | 444av| 免费久久99精品国产 | 日韩欧美在线国产 | 99电影| 久久国产精品一国产精品 | 99精品欧美一区二区蜜桃免费 | 日日碰狠狠添天天爽超碰97久久 | 中文字幕乱在线伦视频中文字幕乱码在线 | 91亚洲精品久久久蜜桃借种 | 中文字幕视频观看 | 韩日在线一区 | 999久久久| av免费在线网 | 日韩视频一 | 三级av网 | 久久亚洲欧美日韩精品专区 | 久久精品国产一区二区电影 | 在线精品亚洲一区二区 | 蜜臀久久99静品久久久久久 | 成人免费视频播放 | 日韩,中文字幕 | 天天草天天操 | 日韩av线观看| 激情av网址| 欧美一级在线观看视频 | 亚洲专区欧美 | 日韩二区三区在线观看 | 国产精品久久久久久久久久久久 | 深爱激情五月网 | 国产伦精品一区二区三区免费 | 四虎免费在线观看视频 | 亚洲美女免费精品视频在线观看 | 国产只有精品 | 亚洲免费av网站 | 日本公妇在线观看高清 | 免费黄色特级片 | 国产玖玖在线 | 国产亚洲在线视频 | 九九视频精品免费 | 综合在线亚洲 | 综合色中文| 中文字幕免费高清av | 久久er99热精品一区二区三区 | 国产福利一区在线观看 | 亚洲伊人天堂 | 国产91电影在线观看 | av网站手机在线观看 | 亚洲欧美婷婷六月色综合 | 国产精品一区二区三区在线看 | 日韩欧美在线第一页 | 日韩精品专区在线影院重磅 | 国产三级精品在线 | 欧美日韩中文国产一区发布 | 97成人在线观看视频 | 久久国产视屏 | 欧美日韩久久一区 | 亚洲精品伦理在线 | 欧美激情视频免费看 | 岛国片在线 | 操操操日日日 | 精品久久久久久一区二区里番 | 中文字幕黄色网 | 免费瑟瑟网站 | а天堂中文最新一区二区三区 | 亚洲婷婷在线 | 99色在线播放 | 久久久久 | 免费观看全黄做爰大片国产 | 国产精品99久久久久 | 天堂av网址 | 99在线视频观看 | 亚洲国产欧美在线看片xxoo | 一级欧美黄 | japanese黑人亚洲人4k | 在线观看久久久久久 | 在线蜜桃视频 | 国产视频69 | 99精品视频播放 | 在线国产高清 | 欧美99久久 | a√资源在线 | 日韩在线三级 | 日韩视频a| 爱色av.com| 国产婷婷色 | 日产乱码一二三区别免费 | 色综合天天色 | 亚洲闷骚少妇在线观看网站 | 欧美日韩国产精品一区二区 | 久久99电影 | 亚洲美女视频在线观看 | 色视频在线免费观看 | 久久理论电影 | 美女免费黄视频网站 | 亚洲区另类春色综合小说校园片 | 2018亚洲男人天堂 | 亚洲干视频在线观看 | 欧美国产在线看 | 国产a高清 | 黄色精品久久久 | 中文字幕乱码视频 | 日产中文字幕 | 日本三级人妇 | 日韩av中文在线 | 国产中文字幕在线观看 | 视频在线观看国产 | 超碰在线公开免费 | 91欧美视频网站 | 国产精品久久久久久99 | 国产精品毛片一区二区在线看 | 亚洲 中文 欧美 日韩vr 在线 | 国产 字幕 制服 中文 在线 | 特级片免费看 | 天天操天天草 | 亚洲视频在线免费看 | 日韩视频在线不卡 | 日日干天天 | 天天干天天在线 | 日韩免费一区二区在线观看 | 精品久久久久久久久亚洲 | 国产精品18久久久久久久 | 四虎成人精品在永久免费 | 五月香视频在线观看 | 911精品视频 | 亚洲成人精品影院 | 九九爱免费视频在线观看 | 久久夜色精品国产亚洲aⅴ 91chinesexxx | 911久久| 福利视频入口 | 久久国产热视频 | 久久看看| 97av影院| 亚洲国产经典视频 | 久久少妇免费视频 | 国产99久久九九精品免费 | 亚洲精品午夜一区人人爽 | 日本精品二区 | 福利一区在线视频 | 欧美一二三在线 | 亚洲高清网站 | 在线观看视频福利 | 日韩高清成人 | 在线中文字幕av观看 | 国产男男gay做爰 | 天天玩天天操天天射 | 狠狠色丁香婷婷综合最新地址 | 在线激情电影 | 一区二区三区免费网站 | 国产一区二区三区免费在线 | 国产视频一区二区三区在线 | 97超碰中文 | 不卡av在线免费观看 | 欧美精品国产综合久久 | 99视频在线观看视频 | 国产不卡视频在线 | 日本久久99 | 成人中心免费视频 | 亚洲精品在线国产 | 97国产一区| 狠狠干婷婷 | 一区二区三区四区久久 | 99综合视频 | 91人人视频在线观看 | 欧美激情xxxx性bbbb | 91av影视| 成人免费在线播放视频 | 麻豆mv在线观看 | 日韩午夜电影院 | 91污在线| 久久不卡日韩美女 | 欧美一区,二区 | 日p视频在线观看 | 国产在线国偷精品产拍 | 六月丁香社区 | 日本不卡123区 | av成人免费在线看 | 黄色一级在线免费观看 | 国产一区二区在线播放视频 | 人人搞人人干 | 日韩精品视频一二三 | 人人射人人爱 | 天天综合天天综合 | 色婷婷成人 | 91亚洲免费| 美女久久99 | 国产97在线播放 | 久久精品欧美日韩精品 | 久久久黄色免费网站 | 特级毛片在线免费观看 | 很黄很污的视频网站 | 91精品国产自产在线观看 | 99热国内精品 | 欧美亚洲专区 | 国产很黄很色的视频 | 黄色特一级片 | 成年一级片 | 在线观看日本高清mv视频 | 免费看的毛片 | 手机在线看a | 久久精品毛片基地 | 国产日韩高清在线 | 亚洲激情精品 | 亚洲婷婷伊人 | 久久综合九色欧美综合狠狠 | 精品91久久久久 | av线上看| 中文成人字幕 | 天天射天天舔天天干 | 国产精品v欧美精品 | 久久99欧美 | 人人插人人澡 | 激情久久伊人 | 99一区二区三区 | 91午夜精品 | 欧美一级免费 | 人人爱人人舔 | 欧美日韩99 | 成年人在线观看免费视频 | 福利视频网址 | 国产高清在线免费观看 | 国产群p| www.com黄色 | 日韩精品视频在线观看网址 | 久久久久久国产一区二区三区 | 美女免费电影 | 五月天电影免费在线观看一区 | 国产福利在线免费观看 | 成人久久视频 | 丁香九月婷婷综合 | 91精品电影 | www.99av| 国产黄色网 | 亚洲精品综合一区二区 | 黄色a视频| 看v片| 欧美狠狠色 | 国色天香在线 | 免费高清无人区完整版 | 不卡的av在线播放 | 五月婷亚洲 | 五月婷婷综合久久 | 亚洲精品黄色 | 在线播放你懂 | 激情狠狠干 | 国产午夜不卡 | 亚洲高清精品在线 | 成人91av | 亚洲精品国偷自产在线91正片 | 五月婷在线 | 天天操天天操天天操天天操天天操天天操 | 又紧又大又爽精品一区二区 | 美国av大片| 玖玖爱国产在线 | 日韩视频免费播放 | 免费福利视频网 | 成人国产一区二区 | 国产又粗又猛又黄又爽视频 | 久草精品电影 | 999久久久久 | 日韩区在线观看 | 亚洲理论片在线观看 | 在线观看一级视频 | 91亚洲精品国偷拍自产在线观看 | 欧美国产亚洲精品久久久8v | 成年人在线看片 | 99免在线观看免费视频高清 | 五月天中文字幕 | 精品国产一区二区三区蜜臀 | 欧美精选一区二区三区 | 久久超碰免费 | av免费在线观看网站 | 99久久精 | 在线免费av电影 | 五月激情亚洲 | 蜜臀91丨九色丨蝌蚪老版 | 超碰人人草 | 丰满少妇一级 | 玖玖视频网 | 亚洲精品国精品久久99热 | 天天综合网天天综合色 | 久草视频手机在线 | 夜夜夜草 | 综合久久一本 | 91传媒视频在线观看 | 国产色女 | 国产免费观看久久 | 久久久国产精品人人片99精片欧美一 | 在线观看免费av网站 | 成人蜜桃视频 | 亚洲国产精品一区二区久久,亚洲午夜 | 最近最新最好看中文视频 | 亚洲黄色在线播放 | 国产一区二区三区高清播放 | 日韩av男人的天堂 | 成人av免费网站 | 国产黄色在线网站 | 91精品国产入口 | www色综合| 91亚洲精品久久久中文字幕 | 日韩在线观看高清 | 中文字幕乱码日本亚洲一区二区 | 久久久久免费精品 | av午夜电影 | 国产视频欧美视频 | 国产精品久久综合 | 欧美坐爱视频 | 新版资源中文在线观看 | 午夜免费电影院 | 色综合色综合久久综合频道88 | 久久伊人八月婷婷综合激情 | 久久黄色片 | 天天干天天草天天爽 | 九九久久成人 | 国产精品免费观看久久 | 成人在线免费观看视视频 | 欧美电影在线观看 | 国产精品久久久久久久久久 | 亚洲精品午夜aaa久久久 | 日韩综合视频在线观看 | 免费网站黄 | 99久久国产免费,99久久国产免费大片 | 综合视频在线 | a级成人毛片 | 国产精品久久久久毛片大屁完整版 | 国产精品毛片一区视频播 | 韩日精品在线 | 亚洲二级片 | 麻豆成人精品视频 | www.av在线播放| 久久 在线 | 成人在线播放av | 在线亚洲精品 | 在线婷婷 | 久久99精品久久只有精品 | 国产在线精品区 | 69xx视频 | 日韩免费在线一区 | 亚洲一区二区三区毛片 | 久久在视频 | 狠狠色狠狠色综合日日小说 | 国产最新91| 在线小视频 | 久久成人免费视频 | 色婷婷在线观看视频 | 久99久在线 | 91你懂的| 日本大尺码专区mv | 一区二区视频电影在线观看 | 丁香午夜婷婷 | 精品国产一区二区三区日日嗨 | 国产三级视频在线 | 9色在线视频| 欧美在线视频一区二区 | 一区中文字幕在线观看 | 91av免费看 | 日韩av中文字幕在线 | www.天天干| 国产手机在线播放 | 亚洲精品国产拍在线 | 九九视频一区 | 黄av免费在线观看 | 丝袜美腿亚洲 | 一区二区三区四区影院 | 婷婷久久国产 | 国产精品99精品 | 综合色在线| 国产97色在线 | 97av视频| 97精品久久人人爽人人爽 | 国产精品va在线观看入 | 国产999精品久久久影片官网 | 久久精品a| 国产日韩欧美视频 | 天天搞天天干天天色 | 精品国产电影一区二区 | 成人av一区二区兰花在线播放 | 国产综合精品一区二区三区 | 九九色在线观看 | 午夜av电影 | 国产亚洲免费的视频看 | 日韩激情精品 | 国产精品中文在线 | 婷婷丁香视频 | 97精品国自产拍在线观看 | 国产视频九色蝌蚪 | 51精品国自产在线 | 欧美极品少妇xxxxⅹ欧美极品少妇xxxx亚洲精品 | 免费观看av | 97超在线| 美女黄视频免费 | 国产精品二区三区 | 亚洲97在线| 国产一区二区在线精品 | 亚洲伦理一区 | 波多野结衣久久精品 | 性色在线视频 | 亚洲午夜小视频 | 日韩最新av | 婷婷色资源| 亚洲最新在线视频 | 国产资源中文字幕 | 黄色av在 | 三级黄色在线观看 | 99精彩视频在线观看免费 | 综合天天久久 | 亚洲午夜久久久影院 | 91视频 - 114av| 国产第一福利 | 99这里只有精品视频 | 日韩在线电影观看 | 久久一区国产 | 不卡av电影在线观看 | 精品视频久久 | 久久国产精品99久久久久久丝袜 | 免费看黄在线 | 五月婷婷中文字幕 | 狠狠色丁香久久婷婷综 | 国产精品伦一区二区三区视频 | 九九精品视频在线看 | 91大神在线观看视频 | 中文一区在线观看 | 久久成人高清 | 国产精品美女视频 | 高清视频一区二区三区 | 婷婷福利影院 | 亚洲小视频在线观看 | www免费视频com━ | 国产精品一区二区久久精品爱微奶 | 国产视频亚洲精品 | 麻豆小视频在线观看 | 天天色官网| 曰韩精品| 国产日韩欧美中文 | 久久国产香蕉视频 | 国产精品久久久久久久久久ktv | 97免费公开视频 | 天天操天天操天天爽 | 国产99久久九九精品免费 | 激情五月五月婷婷 | 久久久穴 | 国产流白浆高潮在线观看 | 亚洲美女免费精品视频在线观看 | av免费网站在线观看 | 狠狠狠色丁香婷婷综合久久五月 | 亚洲春色综合另类校园电影 | 在线看国产日韩 | 免费在线国产 | 521色香蕉网站在线观看 | 91色偷偷 | 热久久最新地址 | 天天摸天天舔 | 超碰在线1| 国产精品6999成人免费视频 | 色天天 | 蜜臀久久99静品久久久久久 | 久久久亚洲国产精品麻豆综合天堂 | 97成人资源 | 91av中文字幕| 色就色,综合激情 | 国产中文在线播放 | 在线观看亚洲精品 | 香蕉视频在线免费 | 黄色三级在线看 | 国产免费成人av | 91免费看黄| 美女视频国产 | 国产精品人成电影在线观看 | 久久手机视频 | www视频在线播放 | 欧美一级片免费播放 | 亚洲精品在线观看av | 亚洲免费色 | 中文字幕乱码在线播放 | 在线观看91久久久久久 | av在线激情 | 国产色婷婷 | 亚洲视频免费在线 | 东方av免费在线观看 | 国产91小视频| 久久综合五月天婷婷伊人 | 久久欧洲视频 | 国产精品高清av | 伊人婷婷激情 | 成人免费观看视频大全 | 天堂av免费观看 | 国外成人在线视频网站 | 91精品一区二区三区久久久久久 | 国产理伦在线 | 欧美日韩二区在线 | 国产美女免费观看 | 99在线观看精品 | 99热都是精品 |