Gcc編譯時(shí)無(wú)優(yōu)化參數(shù),以前曾經(jīng)被-O坑過(guò)。
創(chuàng)新互聯(lián)公司是一家集網(wǎng)站建設(shè),隆化企業(yè)網(wǎng)站建設(shè),隆化品牌網(wǎng)站建設(shè),網(wǎng)站定制,隆化網(wǎng)站建設(shè)報(bào)價(jià),網(wǎng)絡(luò)營(yíng)銷,網(wǎng)絡(luò)優(yōu)化,隆化網(wǎng)站推廣為一體的創(chuàng)新建站企業(yè),幫助傳統(tǒng)企業(yè)提升企業(yè)形象加強(qiáng)企業(yè)競(jìng)爭(zhēng)力??沙浞譂M足這一群體相比中小企業(yè)更為豐富、高端、多元的互聯(lián)網(wǎng)需求。同時(shí)我們時(shí)刻保持專業(yè)、時(shí)尚、前沿,時(shí)刻以成就客戶成長(zhǎng)自我,堅(jiān)持不斷學(xué)習(xí)、思考、沉淀、凈化自己,讓我們?yōu)楦嗟钠髽I(yè)打造出實(shí)用型網(wǎng)站。
#include#include int main() { char url[512]; sprintf(url,"218.26.242.56/0/0/1023/6d6168bf1a7294ae0e1c071171adcd48.mp4"); printf("%s\n",url); char*p = url; strcpy(p+15,p+22); printf("%s\n",url); return 0; }
打印結(jié)果應(yīng)該如下
218.26.242.56/0/0/1023/6d6168bf1a7294ae0e1c071171adcd48.mp4
218.26.242.56/06d6168bf1a7294ae0e1c071171adcd48.mp4
但是在centos6.3系統(tǒng)下,gcc4.4.7,
打印結(jié)果會(huì)是
218.26.242.56/0/0/1023/6d6168bf1a7294ae0e1c071171adcd48.mp4
218.26.242.56/0/f1a7294a1a7294ae0e1c071171adcd48.mp4
目前實(shí)驗(yàn)redhat5.05.7,centos7.2系統(tǒng)下都不會(huì)出現(xiàn)問(wèn)題,唯有6.x(試了6.0、6.3、6.7)的gcc4.4.7會(huì)有問(wèn)題
下載4.4.7源碼
wget http://ftp.gnu.org/gnu/gcc/gcc-4.4.7/gcc-4.4.7.tar.bz2
代碼如下
extern void abort (void); extern int inside_main; char * strcpy (char *d, const char *s) { char *r = d; #if defined __OPTIMIZE__ &&!defined __OPTIMIZE_SIZE__ if (inside_main) abort (); #endif while ((*d++ = *s++)); return r; }
理論上不應(yīng)該出現(xiàn)如此問(wèn)題
Centos6.x的strcpy源碼為匯編碼
char *strcpy(char *dest, const char *src) { return __kernel_strcpy(dest, src); } static inline char *__kernel_strcpy(char*dest, const char *src) { char *xdest = dest; asm volatile ("\n" "1: move.b (%1)+,(%0)+\n" " jne 1b" : "+a" (dest), "+a" (src) : : "memory"); return xdest; }
同樣看不出有什么問(wèn)題。
將系統(tǒng)函數(shù)修改為自定義函數(shù),使用一樣的代碼,結(jié)果均為正確。
網(wǎng)絡(luò)上也找到過(guò)另外一種優(yōu)化版本的strcpy代碼,使用寄存器加速效果,在網(wǎng)上找到的號(hào)稱gcc的優(yōu)化代碼也是類似
char * strcpy (dest, src) char *dest; const char *src; { register char c; char *__unbounded s = (char *__unbounded) CHECK_BOUNDS_LOW (src); const ptrdiff_t off = CHECK_BOUNDS_LOW (dest) - s - 1; size_t n; do { c = *s++; s[off] = c; } while (c != '\0'); n = s - src; (void) CHECK_BOUNDS_HIGH (src + n); (void) CHECK_BOUNDS_HIGH (dest + n); return dest; }
將之作為自定義函數(shù)使用后發(fā)現(xiàn)也沒(méi)有問(wèn)題。
繼續(xù)發(fā)現(xiàn)strncpy和sprintf也會(huì)遇到同樣的問(wèn)題。
采用memcpy就沒(méi)有問(wèn)題了
memcpy(p+11,p+18,strlen(p+18)+1);
看了下源碼,跟strcpy也沒(méi)什么區(qū)別
void * memcpy (void *dest, const void *src, size_t len) { char *d = dest; const char *s = src; while (len--) *d++ = *s++; return dest; }
暫時(shí)不明白為什么strcpy、strncpy、sprintf在gcc4.4.7下,自我移動(dòng)會(huì)導(dǎo)致問(wèn)題。
以前曾經(jīng)在網(wǎng)上看見(jiàn)過(guò)strcpy的優(yōu)化函數(shù),在64位系統(tǒng)里,采用八字節(jié)長(zhǎng)×××來(lái)進(jìn)行復(fù)制,但是未在庫(kù)中見(jiàn)過(guò),只是作為優(yōu)化的自定義代碼推薦。
在這里例子中,如果我們將p+15改成p+16,就一切正常,把字符串總長(zhǎng)度縮小到p+32(即*(p+32)=0),那么也一切正常。錯(cuò)誤字段長(zhǎng)度8字節(jié),跟8都有關(guān)系,懷疑系統(tǒng)在什么地方做了優(yōu)化,但是實(shí)在搞不清是誰(shuí)在優(yōu)化,優(yōu)化后的代碼是什么樣子的。
所以建議如果要進(jìn)行字符串自我移動(dòng),不要使用str,使用mem函數(shù)。
--------------------
同事提供了一個(gè)帖子,說(shuō)的是內(nèi)存重疊的問(wèn)題
http://blog.csdn.net/stpeace/article/details/39456645
但是這個(gè)例子的作者其實(shí)沒(méi)有分析到點(diǎn)子上
char str []="123456789"; strcpy(str + 2, str);
本身在代碼邏輯上就是錯(cuò)的,從源碼就能看出來(lái),從前往后復(fù)制,會(huì)導(dǎo)致后面的內(nèi)存覆蓋。和我們這次遇到的不是一個(gè)情況。
按照源碼應(yīng)該結(jié)果是1212121212121212。。。。。。。。。。。一直到越界崩潰
但是實(shí)際結(jié)果是121234565678
在幾個(gè)機(jī)器上試了下
在gcc4.1.1上,是12121212121。。。。。。 崩潰
Gcc4.4.7 顯示121234565678
gcc4.8.5 顯示12123456789
應(yīng)該是在4.4.7上確實(shí)有優(yōu)化,但是4.8.5應(yīng)該是解決了,而且連這種邏輯異常的也一起支持了。
在網(wǎng)上找到了gcc4.7上strcpy的匯編bug,為什么是不是也有關(guān)系。
-----------------
網(wǎng)上查了一下,發(fā)現(xiàn)被誤導(dǎo)了,這里應(yīng)該研究libc.so的代碼,而不是看gcc的代碼
看了下libc的源碼,define了不少實(shí)現(xiàn),在不同設(shè)備上使用了不同優(yōu)化的匯編碼,應(yīng)該是使用8字節(jié)復(fù)制代替單個(gè)字符串復(fù)制,在libc2.12有bug,libc2.17上應(yīng)該是解決了。
查看他們的strcpy代碼
libc2.12.1上
/* Copy SRC to DEST. */ char * strcpy (dest, src) char *dest; const char *src; { reg_char c; char *__unbounded s = (char *__unbounded) CHECK_BOUNDS_LOW (src); const ptrdiff_t off = CHECK_BOUNDS_LOW (dest) - s - 1; size_t n; do { c = *s++; s[off] = c; } while (c != '\0'); n = s - src; (void) CHECK_BOUNDS_HIGH (src + n); (void) CHECK_BOUNDS_HIGH (dest + n); return dest; }
libc2.17上
/* Copy SRC to DEST. */ char * strcpy (dest, src) char *dest; const char *src; { char c; char *__unbounded s = (char *__unbounded) CHECK_BOUNDS_LOW (src); const ptrdiff_t off = CHECK_BOUNDS_LOW (dest) - s - 1; size_t n; do { c = *s++; s[off] = c; } while (c != '\0'); n = s - src; (void) CHECK_BOUNDS_HIGH (src + n); (void) CHECK_BOUNDS_HIGH (dest + n); return dest; }
發(fā)現(xiàn)2.17相比2.12沒(méi)什么改動(dòng),就是取消了寄存器(reg_char變成了char),在這個(gè)博客里面說(shuō)過(guò)寄存器的重要性,可以提高copy速度,不明白為什么2.17取消了寄存器。
http://blog.csdn.net/astrotycoon/article/details/8114786
繼續(xù)測(cè)試
發(fā)現(xiàn)在libc2.12上
(gdb) bt #0 0x00000036a7532664 in __strcpy_ssse3 () from /lib64/libc.so.6 #1 0x0000000000400671 in main () at test.c:32
真正使用的是ssse3指令集下的strcpy.S實(shí)現(xiàn)
在glib2.17下
(gdb) bt #0 __strcpy_sse2_unaligned () at ../sysdeps/x86_64/multiarch/strcpy-sse2-unaligned.S:232 #1 0x0000000000400655 in main () at test.c:9
直接進(jìn)入.s匯編碼,連libc.so都沒(méi)有進(jìn)入,不過(guò)這個(gè)匯編函數(shù)strcpy-sse2-unaligned也是glib里面的
對(duì)匯編實(shí)在無(wú)力,完全無(wú)從下手。
不過(guò)看來(lái)所謂的c源碼對(duì)分析沒(méi)有什么太大的幫助還容易引起誤解,因?yàn)榈讓拥膸?kù)根本就不用c程序的源碼啊。