Re: [問題] 函式的參數

看板C_and_CPP作者 (殺人貓™)時間10年前 (2014/06/18 04:40), 10年前編輯推噓8(8026)
留言34則, 5人參與, 最新討論串2/2 (看更多)
edit:由於x64下cdecl會學fastcall把特定型別的args放register去傳 所以這問題應該是無解。 簡單的說,以下方的code為例子,根本不可能拿到正確的&a 編譯器在發現你試圖取址a/b的時候 會把他們從esi/edi(都是x64的register) 拷貝出來以後隨意丟在一個無法預期的位置讓你去取址 以這邊來講,就是丟一個離function frame stack 16byte的位置 所以本題應該是無解,而且當args不是int的時候他傳法不見得會相同 重點asm放在這給大家參考一下 movl $30, %esi movl $20, %edi call _Z8testCallii -------以下是本來的原文 我嘗試解了一下這題,中間gcc -S的部分就不贅述了 在osx下這個是可以過關的 先假設大家都知道call convention,function frame的基本概念 另外__builtin都是gcc的東西 我不確定cl.exe能不能認得出來 所以請都用g++去compile https://gist.github.com/Rayer/7ba704f442fa10bc326d OSX : Function pointer frame : 0x7fff5c472960 Param a pointer = 0x7fff5c47295c Param b pointer = 0x7fff5c472958 a = 20, at address : 0x7fff5c47295c b = 30, at address : 0x7fff5c472958 記憶體資料結構長這樣 |====|====| b a frame pointer(請注意他是往後長的,原因不贅述) 4 4 但是我放到我主機(CentOS)以後,這個就不work了 可以發現傳進去真正的a/b跟用__builtin_frame_address(0)拿到位置的差16 bytes Function pointer frame : 0x7fff72b63990 Param a pointer = 0x7fff72b6397c Param b pointer = 0x7fff72b63978 a = 32767, at address : 0x7fff72b6398c b = 1924544904, at address : 0x7fff72b63988 記憶體結構看起來變這樣 |====|====|================| b a (不知道是啥) frame pointer 4 4 16 __builtin_frame_address基本上應該是正解,但是看起來還是有很多問題 在同一台機器下硬用offset去拿應該是可以拿到正確解答 但是在不同平台下似乎會有很多其他的問題 另外這些都是cdecl下,stdcall我就懶得測了,我又不用windows... XD 不過stdcall下參數放進frame stack的順序是反過來的 所以我猜應該ab會互調就是 -- ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 118.160.25.44 ※ 文章網址: http://www.ptt.cc/bbs/C_and_CPP/M.1403037600.A.C65.html ※ 編輯: Killercat (118.160.25.44), 06/18/2014 04:55:01

06/18 09:25, , 1F
謝謝 killer大 雖然很多專業名詞看不懂 但文中的意思是原
06/18 09:25, 1F

06/18 09:27, , 2F
宣告的參數a,b 位置會有offset的現象 但會隨著硬體有所不
06/18 09:27, 2F

06/18 09:28, , 3F
同.. 我這樣的理解應該是對的吧..
06/18 09:28, 3F

06/18 09:50, , 4F
簡單的說 a b都會跟__builtin_frame_address有一段固定
06/18 09:50, 4F

06/18 09:50, , 5F
的距離 你可以試試看用這段距離去取值
06/18 09:50, 5F

06/18 09:50, , 6F
但是我不知道他因為什麼而不同就是
06/18 09:50, 6F

06/18 11:21, , 7F
好文
06/18 11:21, 7F

06/18 13:48, , 8F
你的測試環境是linux x64吧, int應該會用register傳
06/18 13:48, 8F

06/18 13:50, , 9F
a本來放在rdi, b在rsi
06/18 13:50, 9F

06/18 13:53, , 10F
因為你要取址, compiler才在stack上隨便找個地方放a,b
06/18 13:53, 10F

06/18 13:54, , 11F
要放在哪裡已經不歸calling convention管了
06/18 13:54, 11F

06/18 14:18, , 12F
所以gcc會因為我執行__builtin_frame_address(0)所以隨
06/18 14:18, 12F

06/18 14:19, , 13F
便找一個地方先把arg stack dump出來嗎? 好詭異的行為
06/18 14:19, 13F

06/18 14:22, , 14F
因為你要&a, 所以把a從rdi搬到stack上
06/18 14:22, 14F

06/18 14:27, , 15F
我懂了 你是說他在compile time發現我要對a取址所以會多
06/18 14:27, 15F

06/18 14:27, , 16F
作一些額外的行為,把他從真正的frame stack搬出來對吧?
06/18 14:27, 16F

06/18 14:28, , 17F
因為就我理解用register傳應該只有fastcall會這樣玩...
06/18 14:28, 17F

06/18 14:28, , 18F
我回去試試看用別的型別 感謝
06/18 14:28, 18F

06/18 14:32, , 19F
欸 仔細看了一下 -S 你是對的 不過我這邊是放esi/edi
06/18 14:32, 19F

06/18 14:32, , 20F
x64因為多了一大堆register, 前幾個參數會跟fastcall
06/18 14:32, 20F

06/18 14:32, , 21F
一樣用register傳
06/18 14:32, 21F

06/18 14:33, , 22F
了解 看了一下.s的確也是如此.... 感謝!
06/18 14:33, 22F

06/18 14:36, , 23F
rsi/rdi是筆誤XD int是32bit所以會用e開頭的register
06/18 14:36, 23F

06/18 14:46, , 24F
不過這樣說的話 其實本來問題應該是無解了...
06/18 14:46, 24F

06/18 14:47, , 25F
因為型別不同他會用不同方法去傳 很傷腦筋
06/18 14:47, 25F
※ 編輯: Killercat (59.124.251.135), 06/18/2014 14:56:59 ※ 編輯: Killercat (59.124.251.135), 06/18/2014 14:58:26

06/18 14:58, , 26F
所以交給gdb去撈debug symbol又快又方便 XD
06/18 14:58, 26F

06/18 15:08, , 27F
http://ideone.com/P2QxLN 印參數從caller比較簡單吧..
06/18 15:08, 27F

06/18 15:08, , 28F
可是request是說要從callee... 所以才有這篇文章囧
06/18 15:08, 28F

06/18 15:15, , 29F
我覺得他不見得只能改callee,可能只是想偏了...
06/18 15:15, 29F

06/18 15:16, , 30F
如果型態不定數量不定那麻煩事還更多
06/18 15:16, 30F

06/18 15:17, , 31F
沒reflection做這種事吃力不討好
06/18 15:17, 31F

06/18 15:17, , 32F
callee確定是不可行的了 這年頭連直接對frame stack hac
06/18 15:17, 32F

06/18 15:17, , 33F
k的最後希望都沒了 還能怎麼搞 :D
06/18 15:17, 33F

06/18 15:18, , 34F
大概就剩下Astral說的拿debug symbol拖出來鞭屍了
06/18 15:18, 34F
文章代碼(AID): #1JeAUWnb (C_and_CPP)
文章代碼(AID): #1JeAUWnb (C_and_CPP)