gcc 展開前置處理的技巧
編譯 C/C++ 程式前,編譯器會用 C 前置處理器 (cpp, C Preprocessor) 展開 #include
、#define
、#if …
等指令後的程式碼。gcc -E
作完前置處理就會停下來,不會繼續編譯,並可配合不同參數觀看展開的內容。
保留 define 內容
gcc -E -dM
可以保留 define 內容,方便查詢原始名稱。比方說 Linux 的 system call 的錯誤值會存在 errno
,是個數字。要查詢數字對到的意思,需要看 <errno.h>
的內容。
假設程式輸出 errno
的值是 111,可以這麼查:
$ echo "#include <errno.h>" | gcc -E -dM - | grep " 111$"
#define ECONNREFUSED 111
然後 google ECONNREFUSED
或看 system call 的 man page 說明,就知道是怎麼一回事了。
列出 include 的內容
想知道是否有正確 include 到標頭檔,或是想看間接 include 什麼檔,可以用 gcc -M
。這參數原本的用途是找出相依的標頭檔,用來寫入 makefile 讓 make 知道。若希望過濾掉系統標頭檔,可以改用 -MM
。
$ echo "#include <stdio.h>" | gcc -E -M -
-: /usr/include/stdc-predef.h /usr/include/stdio.h \
/usr/include/features.h /usr/include/x86_64-linux-gnu/sys/cdefs.h \
/usr/include/x86_64-linux-gnu/bits/wordsize.h \
/usr/include/x86_64-linux-gnu/gnu/stubs.h \
/usr/include/x86_64-linux-gnu/gnu/stubs-64.h \
/usr/lib/gcc/x86_64-linux-gnu/5/include/stddef.h \
/usr/include/x86_64-linux-gnu/bits/types.h \
/usr/include/x86_64-linux-gnu/bits/typesizes.h /usr/include/libio.h \
/usr/include/_G_config.h /usr/include/wchar.h \
/usr/lib/gcc/x86_64-linux-gnu/5/include/stdarg.h \
/usr/include/x86_64-linux-gnu/bits/stdio_lim.h \
/usr/include/x86_64-linux-gnu/bits/sys_errlist.h
綜合實例: 找出實作 GetReq() 的原始碼
在觀察 libxext 原始碼 src/XSync.c 的時候,想找出 GetReq()
的實作。但在 libxext 的原始碼裡沒看到。所以先查 GetReq()
是由那個標頭檔引入的:
$ gcc -E ./src/XSync.c | grep GetReq
extern void *_XGetRequest(Display *dpy, CARD8 type, size_t len);
= (xSyncInitializeReq *) _XGetRequest(dpy, 0, 8)
...
結果沒看到宣告。猜測 GetReq()
可能是 #define
的結果,改成從 #define
查:
$ gcc -E -dM ./src/XSync.c | grep GetReq
#define GetReqSized(name,sz,req) req = (x ##name ##Req *) _XGetRequest(dpy, X_ ##name, sz)
#define GetReq(name,req) GetReqSized(name, SIZEOF(x ##name ##Req), req)
...
得知 GetReq()
其實是 _XGetRequest()
。
查看 _XGetRequest()
從那裡引入的:
gcc -E ./src/XSync.c | less
...
# 418 "/usr/include/X11/Xlibint.h" 3 4
extern void *_XGetRequest(Display *dpy, CARD8 type, size_t len);
...
接著查 /usr/include/X11/Xlibint.h
放在那個套件:
# 查詢已安裝套件, 速度較快
$ dpkg --search /usr/include/X11/Xlibint.h
libx11-dev:amd64: /usr/include/X11/Xlibint.h# 查詢全部套件
$ apt-file search /usr/include/X11/Xlibint.h
libx11-dev: /usr/include/X11/Xlibint.h
取得 libx11 的原始碼,然後找到原始碼的位置:
$ apt-get source libx11-dev
$ grep -RI _XGetRequest .
./src/XlibInt.c:void *_XGetRequest(Display *dpy, CARD8 type, size_t len)
至此大功告成!
附帶一提,若想查看 #define
從那裡引入的,可以用 -dD
:
$ gcc -E -dD ./src/XSync.c | egrep "(GetReq|include)"
# 418 "/usr/include/X11/Xlibint.h" 3 4
extern void *_XGetRequest(Display *dpy, CARD8 type, size_t len);
#define GetReqSized(name,sz,req) req = (x ##name ##Req *) _XGetRequest(dpy, X_ ##name, sz)
# 435 "/usr/include/X11/Xlibint.h" 3 4
#define GetReq(name,req) GetReqSized(name, SIZEOF(x ##name ##Req), req)
查看 man gcc 的 -dletters 或 -dCHARS 可以得知更多用法。