聊聊go語言的多返回值的實現

go語言具有多返回值的能力,我們本文就來探究其實現方法。

首先我們弄一個測試程序如下:

func doOperator(x, y int) (addRet int, subRet int) {

	return x + y, x - y
}

func main() {
	var x = 10
	var y = 3
	var a, b = doOperator(x, y)
	if a > b {
		fmt.Printf("abc")
		return
	}

}

我們以非優化的方式進行編譯,並且設置arch爲arm

go build -gcflags "-N -l"

最終我們得到的彙編代碼主要如下:

.text:0049D13C ; =============== S U B R O U T I N E =======================================
.text:0049D13C
.text:0049D13C
.text:0049D13C                 EXPORT main.main
.text:0049D13C main.main                               ; CODE XREF: runtime.main+1E4p
.text:0049D13C                                         ; main.main+9Cj
.text:0049D13C                                         ; DATA XREF: ...
.text:0049D13C
.text:0049D13C var_3C          = -0x3C
.text:0049D13C var_38          = -0x38
.text:0049D13C var_34          = -0x34
.text:0049D13C var_30          = -0x30
.text:0049D13C var_2C          = -0x2C
.text:0049D13C var_28          = -0x28
.text:0049D13C var_18          = -0x18
.text:0049D13C var_14          = -0x14
.text:0049D13C var_10          = -0x10
.text:0049D13C var_C           = -0xC
.text:0049D13C var_8           = -8
.text:0049D13C var_4           = -4
.text:0049D13C
.text:0049D13C                 LDR             R1, [R10,#8]
.text:0049D140                 CMP             SP, R1
.text:0049D144                 BLS             loc_49D1D0
.text:0049D148                 STR             LR, [SP,#var_3C]!
.text:0049D14C                 MOV             R0, #0xA
.text:0049D150                 STR             R0, [SP,#0x3C+var_14]
.text:0049D154                 MOV             R0, #3
.text:0049D158                 STR             R0, [SP,#0x3C+var_18]
.text:0049D15C                 LDR             R1, [SP,#0x3C+var_14]
.text:0049D160                 STR             R1, [SP,#0x3C+var_38]
.text:0049D164                 STR             R0, [SP,#0x3C+var_34]
.text:0049D168                 BL              main.doOperator
.text:0049D16C                 LDR             R0, [SP,#0x3C+var_30]
.text:0049D170                 STR             R0, [SP,#0x3C+var_4]
.text:0049D174                 LDR             R0, [SP,#0x3C+var_2C]
.text:0049D178                 STR             R0, [SP,#0x3C+var_8]
.text:0049D17C                 LDR             R0, [SP,#0x3C+var_4]
.text:0049D180                 STR             R0, [SP,#0x3C+var_C]
.text:0049D184                 LDR             R0, [SP,#0x3C+var_8]
.text:0049D188                 STR             R0, [SP,#0x3C+var_10]
.text:0049D18C                 LDR             R0, [SP,#0x3C+var_10]
.text:0049D190                 LDR             R1, [SP,#0x3C+var_C]
.text:0049D194                 CMP             R1, R0
.text:0049D198                 BGT             loc_49D1A0
.text:0049D19C                 B               loc_49D1CC
.text:0049D1A0 ; ---------------------------------------------------------------------------
.text:0049D1A0
.text:0049D1A0 loc_49D1A0                              ; CODE XREF: main.main+5Cj
.text:0049D1A0                 LDR             R0, =(a25Cccfcocslllm+0x13A) ; "abcendgc gp intip4mapnilobjpc=ptr碌s渭s"...
.text:0049D1A4                 STR             R0, [SP,#0x3C+var_38]
.text:0049D1A8                 MOV             R0, #3
.text:0049D1AC                 STR             R0, [SP,#0x3C+var_34]
.text:0049D1B0                 MOV             R0, #0
.text:0049D1B4                 STR             R0, [SP,#0x3C+var_30]
.text:0049D1B8                 MOV             R0, #0
.text:0049D1BC                 STR             R0, [SP,#0x3C+var_2C]
.text:0049D1C0                 STR             R0, [SP,#0x3C+var_28]
.text:0049D1C4                 BL              fmt.Printf
.text:0049D1C8                 LDR             PC, [SP+0x3C+var_3C],#0x3C
.text:0049D1CC ; ---------------------------------------------------------------------------
.text:0049D1CC
.text:0049D1CC loc_49D1CC                              ; CODE XREF: main.main+60j
.text:0049D1CC                 LDR             PC, [SP+0x3C+var_3C],#0x3C
.text:0049D1D0 ; ---------------------------------------------------------------------------
.text:0049D1D0
.text:0049D1D0 loc_49D1D0                              ; CODE XREF: main.main+8j
.text:0049D1D0                 MOV             R3, LR
.text:0049D1D4                 BL              runtime.morestack_noctxt
.text:0049D1D8                 B               main.main
.text:0049D1D8 ; End of function main.main
.text:0049D1D8
.text:0049D1DC ; ---------------------------------------------------------------------------
.text:0049D1DC
.text:0049D1DC loc_49D1DC                              ; CODE XREF: .text:loc_49D1DCj
.text:0049D1DC                 B               loc_49D1DC
.text:0049D1DC ; ---------------------------------------------------------------------------
.text:0049D1E0 off_49D1E0      DCD a25Cccfcocslllm+0x13A ; DATA XREF: main.main:loc_49D1A0r
.text:0049D1E0                                         ; "abcendgc gp intip4mapnilobjpc=ptr碌s渭s"...
.text:0049D1E4
.text:0049D1E4 ; =============== S U B R O U T I N E =======================================
.text:0049D1E4
.text:0049D1E4
.text:0049D1E4                 EXPORT main.doOperator
.text:0049D1E4 main.doOperator                         ; CODE XREF: main.main+2Cp
.text:0049D1E4                                         ; main.doOperator+58j
.text:0049D1E4                                         ; DATA XREF: ...
.text:0049D1E4
.text:0049D1E4 var_C           = -0xC
.text:0049D1E4 var_8           = -8
.text:0049D1E4 var_4           = -4
.text:0049D1E4 arg_4           =  4
.text:0049D1E4 arg_8           =  8
.text:0049D1E4 arg_C           =  0xC
.text:0049D1E4 arg_10          =  0x10
.text:0049D1E4
.text:0049D1E4                 LDR             R1, [R10,#8]
.text:0049D1E8                 CMP             SP, R1
.text:0049D1EC                 BLS             loc_49D234
.text:0049D1F0                 STR             LR, [SP,#var_C]!
.text:0049D1F4                 MOV             R0, #0
.text:0049D1F8                 STR             R0, [SP,#0xC+arg_C]
.text:0049D1FC                 STR             R0, [SP,#0xC+arg_10]
.text:0049D200                 LDR             R0, [SP,#0xC+arg_8]
.text:0049D204                 LDR             R1, [SP,#0xC+arg_4]
.text:0049D208                 ADD             R0, R1, R0
.text:0049D20C                 STR             R0, [SP,#0xC+var_4]
.text:0049D210                 LDR             R0, [SP,#0xC+arg_4]
.text:0049D214                 LDR             R1, [SP,#0xC+arg_8]
.text:0049D218                 SUB             R0, R0, R1
.text:0049D21C                 STR             R0, [SP,#0xC+var_8]
.text:0049D220                 LDR             R0, [SP,#0xC+var_4]
.text:0049D224                 STR             R0, [SP,#0xC+arg_C]
.text:0049D228                 LDR             R0, [SP,#0xC+var_8]
.text:0049D22C                 STR             R0, [SP,#0xC+arg_10]
.text:0049D230                 LDR             PC, [SP+0xC+var_C],#0xC
.text:0049D234 ; ---------------------------------------------------------------------------
.text:0049D234
.text:0049D234 loc_49D234                              ; CODE XREF: main.doOperator+8j
.text:0049D234                 MOV             R3, LR
.text:0049D238                 BL              runtime.morestack_noctxt
.text:0049D23C                 B               main.doOperator
.text:0049D23C ; End of function main.doOperator
.text:0049D23C

我們來看執行到 .text:0049D168                 BL              main.doOperator 之前,堆棧的分佈如下:

SP 0x0  
  0x4  
  0x8  
  0xc  
  0x10  
  0x14 0xA
  0x18 0x3
  0x1c  
  0x20  
  0x24  
  0x28  
  0x2c  
  0x30  
  0x34 0x3
  0x38 0xA
SP 0x3c LR

此時r0= 0x3  r1 = 0xA

進入doOperator函數後,略過前面的堆棧檢查,

  .text:0049D1F0                 STR             LR, [SP,#var_C]!

地址如下:

  0x2c    
  0x30    
  0x34 0x3  
  0x38 0xA  
SP 0x3c LR  
  0x40    
  0x44    
SP 0x48 LR 實際是地址0049D16C

.text:0049D1F4                 MOV             R0, #0
.text:0049D1F8                 STR             R0, [SP,#0xC+arg_C]   此時 0x30 = 0
.text:0049D1FC                 STR             R0, [SP,#0xC+arg_10]  此時 0x2c = 0
.text:0049D200                 LDR             R0, [SP,#0xC+arg_8]   此時r0加載成爲 0x3
.text:0049D204                 LDR             R1, [SP,#0xC+arg_4]   此時r1加載成爲  0xa
.text:0049D208                 ADD             R0, R1, R   這裏計算得到和

.text:0049D20C                 STR             R0, [SP,#0xC+var_4]   此時0x40 就是和,這個實際是 doOperator 裏面的臨時變量,存放了和

同理我們得到下面的

.text:0049D210                 LDR             R0, [SP,#0xC+arg_4]   此時r0加載成爲 0xA
.text:0049D214                 LDR             R1, [SP,#0xC+arg_8]  此時r0加載成爲 0x3
.text:0049D218                 SUB             R0, R0, R1
.text:0049D21C                 STR             R0, [SP,#0xC+var_8]   此時0x44 就是差,這個實際是doOperator裏面的臨時變量,存放了差。

此時內存如下:

  0x28    
  0x2c 0  
  0x30 0  
  0x34 0x3  
  0x38 0xA  
SP 0x3c LR  
  0x40 add_result  
  0x44 sub_result  
SP 0x48 LR 實際是地址0049D16C

繼續接下來看彙編代碼

.text:0049D220                 LDR             R0, [SP,#0xC+var_4]   將add_result放到r0
.text:0049D224                 STR             R0, [SP,#0xC+arg_C]   將add_result轉存到  0x30 位置
.text:0049D228                 LDR             R0, [SP,#0xC+var_8]   將sub_result放到r0
.text:0049D22C                 STR             R0, [SP,#0xC+arg_10]  將sub_result轉存到 0x2c  位置

此時內存分佈

  0x24    
  0x28    
  0x2c sub_result  
  0x30 add_result  
  0x34 0x3  
  0x38 0xA  
SP 0x3c LR  
  0x40 add_result  
  0x44 sub_result  
SP 0x48 LR 實際是地址0049D16C

接下來我們執行了函數返回,函數跳回到0049D16C地址執行,SP也回到前一個地址,也就是SP=0x3c
.text:0049D230                 LDR             PC, [SP+0xC+var_C],#0xC

此時內存如下:

  0x24    
  0x28    
  0x2c sub_result  
  0x30 add_result  
  0x34 0x3  
  0x38 0xA  
SP 0x3c LR  
  0x40 add_result 這裏的棧內容已經釋放
  0x44 sub_result
  0x48 LR

.text:0049D16C                 LDR             R0, [SP,#0x3C+var_30]   將 add_result 放到r0
.text:0049D170                 STR             R0, [SP,#0x3C+var_4]    將 add_result 轉存到 0x4
.text:0049D174                 LDR             R0, [SP,#0x3C+var_2C]   將 sub_result 放到r0
.text:0049D178                 STR             R0, [SP,#0x3C+var_8]   將sub_result轉存到 0x8
.text:0049D17C                 LDR             R0, [SP,#0x3C+var_4]   將add_result 放到 r0
.text:0049D180                 STR             R0, [SP,#0x3C+var_C]   將add_result轉存到 0xc
.text:0049D184                 LDR             R0, [SP,#0x3C+var_8]    將 sub_result 放到r0
.text:0049D188                 STR             R0, [SP,#0x3C+var_10]   將 sub_result 轉存到 0x10
.text:0049D18C                 LDR             R0, [SP,#0x3C+var_10]   將sub_result 放到r0
.text:0049D190                 LDR             R1, [SP,#0x3C+var_C]   將add_result放到r1

概括一下就是下面這樣,也沒想太明白爲啥這裏反覆的複製數據。。。

SP 0x0  
  0x4 add_result
  0x8 sub_result
  0xc add_result
  0x10 sub_result
  0x14 0xA
  0x18 0x3
  0x1c  

.text:0049D194                 CMP             R1, R0   比較add_result 和 sub_result
.text:0049D198                 BGT             loc_49D1A0   如果add_result大則執行打印部分代碼

總結一下,go語言的多參數傳遞其實是下面這回事

返回值存儲的空間(0x2c,0x30)是跟着參數一起,傳遞給 doOperator,傳遞的規則如下:

  0x2c sub_result doOperator 返回參數2
  0x30 add_result doOperator 返回參數1
  0x34 0x3 doOperator 的參數2
  0x38 0xA doOperator 的參數1
SP 0x3c LR  

在doOperator執行完後,會將數據copy回去到這個地址,這就是奧祕 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章