我的問題非常具體,我希望強制編譯器獲取一個函式的代碼并將其復制到另一個函式中,就像 inline 或 __forceinline 關鍵字可以做的那樣,但我想傳遞我想在另一個函式中復制的函式,作為爭論。這是一個簡單的例子。
using pFunc = void(*)();
void func_1() { /*some code*/ }
void func_2(pFunc function) { /*some code*/ } //after compile i want this funtion takes no argument and copy the func_1 inside this.
int main()
{
func_2(func_1);
}
所以在這個例子中,編譯器將 func_1 的指標作為引數傳遞給 func_2,正如預期的那樣。我嘗試為 func_1 添加 inline 關鍵字,并嘗試將 func_2 的引數作為參考傳遞,但編譯器沒有將 func_1 復制到 func_2 中。知道我該怎么做嗎?
我將 Visual Studio(msvc) 的編譯器與工具集 2017(v141) 一起使用。我的專案平臺是 x64。
uj5u.com熱心網友回復:
您可以使用 noinline 模板函式來獲取所需的 asm
因此,您希望編譯器將常量傳播到void func_2(pFunc f){ f(); }? 就像 GCC 可能會做什么__attribute__((noinline))但不是noclone?
例如,
using pFunc = void(*)();
int sink, sink2;
#ifdef _MSC_VER
#define NOINLINE _declspec(noinline)
#else
#define NOINLINE __attribute__((noinline)) // noclone and/or noipa
#endif
__attribute__((always_inline)) // without this, gcc chooses to clone .constprop.0 with just a jmp func_2
void func_1() { sink = 1; sink2 = 2; }
NOINLINE static void func_2(pFunc function) { function(); }
int main()
{
func_2(func_1);
}
使用 GCC11.3 -O2 或更高版本,或-O1 -fipa-cp在Godbolt 上產生。(Clang 類似):
# GCC11 -O3 with C name demangling
func_1():
mov DWORD PTR sink[rip], 1
mov DWORD PTR sink2[rip], 2
ret
func_2(void (*)()) [clone .constprop.0]:
mov DWORD PTR sink[rip], 1
mov DWORD PTR sink2[rip], 2
ret
main:
# note no arg passed, calling a special version of the function
# specialized for function = func_1
call func_2(void (*)()) [clone .constprop.0]
xor eax, eax
ret
當然,如果我們沒有禁用 func_2 的行內,main 將只是call func_1. 或者將該主體行內func_1到 main 中而不做任何呼叫。
MSVC 可能不愿意做那個“優化”,而是寧愿只行內func_2到mainas 中call func_1。
如果您想強制它生成func_1不必要地重復的笨重 asm,您可以使用模板來執行與 constprop 相同的操作,將函式指標作為模板 arg,因此您可以實體func_2<func1>化為獨立的非行內函式,如果你真的想要。(也許與_declspec(noinline))。
如果您愿意,您func_2可以接受func_1作為未使用的引數。
using pFunc = void(*)();
int sink, sink2;
#ifdef _MSC_VER
#define NOINLINE _declspec(noinline)
#define ALWAYS_INLINE /* */
#else
#define NOINLINE __attribute__((noinline)) // not noclone or noipa, we *want* those to happen
#define ALWAYS_INLINE __attribute__((always_inline))
#endif
//ALWAYS_INLINE // Seems not needed for this case, with the template version
void func_1() { sink = 1; sink2 = 2; }
template <pFunc f>
NOINLINE void func_2() { f(); }
int main()
{
func_2<func_1>();
}
使用 MSVC -O2 ( Godbolt ) 和 GCC/clang根據需要編譯
int sink DD 01H DUP (?) ; sink
int sink2 DD 01H DUP (?) ; sink2
void func_2<&void func_1(void)>(void) PROC ; func_2<&func_1>, COMDAT
mov DWORD PTR int sink, 1 ; sink
mov DWORD PTR int sink2, 2 ; sink2
ret 0
void func_2<&void func_1(void)>(void) ENDP ; func_2<&func_1>
void func_1(void) PROC ; func_1, COMDAT
mov DWORD PTR int sink, 1 ; sink
mov DWORD PTR int sink2, 2 ; sink2
ret 0
void func_1(void) ENDP ; func_1
main PROC ; COMDAT
$LN4:
sub rsp, 40 ; 00000028H
call void func_2<&void func_1(void)>(void) ; func_2<&func_1>
xor eax, eax
add rsp, 40 ; 00000028H
ret 0
main ENDP
注意 func_1 和 func_2 的重復體。
您應該(使用反匯編程式)檢查聯結器是否執行相同的代碼折疊,只需將兩個符號名稱附加到一個機器代碼塊。
我不認為這看起來像是一種混淆技術。IDK 為什么擁有具有相同機器代碼的函式的第二個副本對于逆向工程來說是一個問題。我想這可能會創造更多的整體作業,人們不會注意到對不同函式的兩次呼叫實際上是在做同樣的事情。
我的回答主要是作為讓編譯器吐出我想要的匯編的練習,無論這對其他人是否有價值。
顯然它只適用于編譯時常量函式指標;評論者一直在討論自修改代碼和腳本語言。如果您希望這個非常量函式指標 args 指向func_1,那么在像 C 這樣專為嚴格提前編譯而設計的語言中,您完全不走運。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/496194.html
