日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Microsoft的CL编译器与GCC到底有什么区别?

發布時間:2024/3/7 编程问答 50 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Microsoft的CL编译器与GCC到底有什么区别? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

解析CL編譯器與GCC的各種差異

  • 編譯器版本
  • 統一編譯、查看命令
  • 函數調用
    • 棧幀分配
      • gcc結果
      • CL結果
    • 函數的調用及傳參
      • gcc結果
      • CL結果
  • 總結

編譯器版本

gcc -v:
gcc version 11.2.0 (MinGW-W64 x86_64-ucrt-posix-seh, built by Brecht Sanders)
cl:
用于 x64 的 Microsoft (R) C/C++ 優化編譯器 19.29.30136 版

CL作為微軟的非開源編譯器,聽上去似乎比開源的GNU套件GCC編譯器更“高級”,但事實真的如此嗎?
咱們統一使用普遍的x64架構,看看兩個編譯器對同一段C代碼的匯編輸出有何異同。

統一編譯、查看命令

gcc -O0 -c src.c -o gcc_obj.o
cl src.c // 輸出a.obj,但無法鏈接

反匯編:
objdump -d [obj.o] -M intel // 我更習慣于intel語法,有需要的可以把intel改成att,即輸出AT&T語法。

函數調用

研究底層機制,最重要的就是函數調用相關的差異。

棧幀分配

方便起見,寫幾個具有代表性的函數,但不實現任何實際功能,讓匯編代碼更簡單,更能突出重點。

void func1(void) {return; }int func2(void) {return 0x1234; // 方便反匯編查看 }int func3(int arg) {return arg++; }

這段代碼中,并不包含任何函數調用語句,也就是說它是用來分析函數棧的。
編譯,然后分別反匯編,得到如下結果:

gcc結果

0000000000000000 <func1>:0: 55 push rbp1: 48 89 e5 mov rbp,rsp4: 90 nop5: 5d pop rbp6: c3 ret0000000000000007 <func2>:7: 55 push rbp8: 48 89 e5 mov rbp,rspb: b8 34 12 00 00 mov eax,0x123410: 5d pop rbp11: c3 ret0000000000000012 <func3>:12: 55 push rbp13: 48 89 e5 mov rbp,rsp16: 89 4d 10 mov DWORD PTR [rbp+0x10],ecx19: 8b 45 10 mov eax,DWORD PTR [rbp+0x10]1c: 8d 50 01 lea edx,[rax+0x1]1f: 89 55 10 mov DWORD PTR [rbp+0x10],edx22: 5d pop rbp23: c3 ret24: 90 nop25: 90 nop // 好多個nop(0x90)2f: 90 nop

可以看見,它嚴格按照x86_64架構調用約定,在函數頭部寫上跟32位無差異(僅僅寄存器位數變化)的序言代碼,采用基址指針壓棧->備份棧頂指針->(開辟棧空間,由于我們的函數都沒有局部變量所以沒有進行SUB ESP,0x28)->函數本體->返回值存RAX(這里是EAX,因為改變低32位就能自動高32位清零,但速度更快)->平棧,返回

關注到0x13->0x1f的代碼,翻譯成中文即:把RCX里的參數低32位(int)拿出來,放進備用空間,然后從備用空間拿出來,放進EAX寄存器,把EDX換成RAX+1的值,即arg++,然后由于++運算符改變原值,所以需要再把EDX里自增過的值放回去,即使后文沒用到了,然后回復基址寄存器并返回。

CL結果

0000000000000000 <func1>:0: c2 00 00 ret 0x03: cc int34: cc int35: cc int36: cc int37: cc int38: cc int39: cc int3a: cc int3b: cc int3c: cc int3d: cc int3e: cc int3f: cc int30000000000000010 <func2>:10: b8 34 12 00 00 mov eax,0x123415: c3 ret16: cc int317: cc int318: cc int319: cc int31a: cc int31b: cc int31c: cc int31d: cc int31e: cc int31f: cc int30000000000000020 <func3>:20: 89 4c 24 08 mov DWORD PTR [rsp+0x8],ecx24: 48 83 ec 18 sub rsp,0x1828: 8b 44 24 20 mov eax,DWORD PTR [rsp+0x20]2c: 89 04 24 mov DWORD PTR [rsp],eax2f: 8b 44 24 20 mov eax,DWORD PTR [rsp+0x20]33: ff c0 inc eax35: 89 44 24 20 mov DWORD PTR [rsp+0x20],eax39: 8b 04 24 mov eax,DWORD PTR [rsp]3c: 48 83 c4 18 add rsp,0x1840: c3 ret

通過func1和func2可以看見,(由于CL默認開始/Od選項,禁止優化),CL編譯器確實更高明,發現幾乎是個空函數就直接返回,至于那么多int3調試中斷不在討論范圍內。
重點看到0x20的func3,首先,把ECX存進備份區,然后開辟0x18的空間(至于為什么是0x18,因為它以為func3里要調用別的函數,所以預先開辟空間,但可以看出0x18模16=8,并不對齊,這是因為call指令會壓棧一個0x08字節的返回地址,這就棧對齊了)
然后取值到EAX里面,EAX自增1,放回去,歸還棧空間,然后返回。

函數的調用及傳參

這個部分按照常理來說,兩種編譯器應該沒多大區別,因為都得遵守64位平臺上fastcall調用約定
測試代碼:

int func1(int arg) {return arg+1; }void func2(void) {int local_varible = 1;local_varible = func1(local_varible); // 自增1 }

gcc結果

0000000000000000 <func1>:0: 55 push rbp1: 48 89 e5 mov rbp,rsp4: 89 4d 10 mov DWORD PTR [rbp+0x10],ecx7: 8b 45 10 mov eax,DWORD PTR [rbp+0x10]a: 83 c0 01 add eax,0x1d: 5d pop rbpe: c3 ret000000000000000f <func2>:f: 55 push rbp10: 48 89 e5 mov rbp,rsp13: 48 83 ec 30 sub rsp,0x3017: c7 45 fc 01 00 00 00 mov DWORD PTR [rbp-0x4],0x11e: 8b 45 fc mov eax,DWORD PTR [rbp-0x4]21: 89 c1 mov ecx,eax23: e8 d8 ff ff ff call 0 <func1>28: 89 45 fc mov DWORD PTR [rbp-0x4],eax2b: 90 nop2c: 48 83 c4 30 add rsp,0x3030: 5d pop rbp31: c3 ret32: 90 nop

func2函數開辟了0x30字節空間,然后使用相對于RBP的尋址(也是一種棧尋址方式),把1這個值放進對應的局部變量空間,利用EAX寄存器為過渡,取出來,塞進ECX傳參,調用,然后把返回值塞回去(這一行代碼,完成了)
最后平棧,回復基址,返回。

CL結果

0000000000000000 <func1>:0: 89 4c 24 08 mov DWORD PTR [rsp+0x8],ecx4: 8b 44 24 08 mov eax,DWORD PTR [rsp+0x8]8: ff c0 inc eaxa: c3 ret// int3 * n0000000000000020 <func2>:20: 48 83 ec 38 sub rsp,0x3824: c7 44 24 20 01 00 00 mov DWORD PTR [rsp+0x20],0x12b: 002c: 8b 4c 24 20 mov ecx,DWORD PTR [rsp+0x20]30: e8 00 00 00 00 call 35 <func2+0x15>35: 89 44 24 20 mov DWORD PTR [rsp+0x20],eax39: 48 83 c4 38 add rsp,0x383d: c3 ret

相比之下CL的代碼短得多,它不涉及對RBP的操作,僅僅修改RSP足矣。而且,cl的棧尋址都是采用RSP偏移,更直觀(這對于機器來說確實是一句廢話),一眼能看出每個局部變量的位置。
這里的func2屬于經典調用了……首先開辟常見的0x38空間,存局部變量到對應的棧空間,然后提取到ECX傳參,調用,返回,塞回棧空間局部變量區,平棧,退出。

總結

在最基本的邏輯控制——函數調用方面,CL似乎就比不開優化開關的GCC高明了不少,尤其是指令短,如果實驗中的func1,func2被平凡調用,即使每個指令的時鐘周期再短,累計起來也是客觀的時間。僅從這一個方面看起來,CL就拿時間和空間都換掉了,每個都節省了不少。

總結

以上是生活随笔為你收集整理的Microsoft的CL编译器与GCC到底有什么区别?的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 久久精品7 | 久久国产视频一区 | 蜜桃精品噜噜噜成人av | 未满十八岁禁止进入 | cao我| 二区三区偷拍浴室洗澡视频 | 多男调教一女折磨高潮高h 国内毛片毛片毛片毛片毛片 | 欧美视频xxxx | 激情都市一区二区 | 亚洲综合图片一区 | 亚洲大乳| 成人手机在线视频 | 欧美性猛交xxxx免费看久久久 | 激情另类小说 | 日本乱偷中文字幕 | 男同互操gay射视频在线看 | 欧美一级一级一级 | 亚洲一级影片 | 草草视频在线播放 | 热久久精| 久在操| 朝鲜女人性猛交 | 老女人黄色片 | 尤物国产| 免费成人黄 | 亚洲人视频在线 | 国产成人精品综合久久久久99 | 在线中文天堂 | 欧美亚洲一区二区在线观看 | av影片在线看 | 丝袜人妻一区二区三区 | 成人国产三级 | 久久久久亚洲av无码专区首jn | 在线观看v片 | 欧美日韩在线播放三区四区 | 日韩av线上 | 亚洲天堂二区 | 亚洲一区二区高清视频 | 无码日韩精品一区二区 | 91嫩草精品| 激情国产在线 | 中文字幕亚洲日本 | 亚洲一卡二卡三卡四卡 | 日韩黄色av | 福利第一页 | 欧美一级性生活视频 | 欧洲成人综合网 | 一级特黄录像免费看 | 中文字幕三级视频 | 成人作爱视频 | 老司机一区二区三区 | 国色天香一区二区 | 欧美精品日韩在线观看 | www.天天操.com | 激情文学88 | 青青草原伊人 | 美国黄色一级毛片 | www.4虎 | 天天上天天干 | 午夜免费在线 | 久久久久麻豆v国产精华液好用吗 | 久久久久久久久久久影院 | 国产成人久久婷婷精品流白浆 | 免费在线 | 99久久国 | 国产午夜无码视频在线观看 | 国产永久免费无遮挡 | 乌克兰少妇性做爰 | h视频在线看 | 欧美偷拍亚洲 | 91亚洲国产精品 | 日本aa大片 | 粗大的内捧猛烈进出在线视频 | 欧美色第一页 | jizzjizz亚洲 | 国产成人在线视频免费观看 | 91成人精品一区在线播放 | 流白浆视频 | 午夜大片网 | 亚洲一区二区成人 | 亚洲少妇色| 男女羞羞动态图 | 91成人福利| 最新的av网站 | 国内av片| 天天综合亚洲 | 国产精品久久不卡 | 国产成人自拍视频在线 | 四虎影视永久地址 | 亚洲成熟女性毛茸茸 | 白浆av| 国产精品3区 | 欧美性猛交久久久久 | 国产1区二区 | 国产成人精品一区在线播放 | 免费搞黄网站 | 日本三级生活片 | 久久伊人婷婷 | 日本少妇一级 |