strcpy在centos6.x,gcc4.4.7版本上會(huì)有bug,自我移動(dòng)導(dǎo)致覆蓋錯(cuò)誤overlap

Gcc編譯時(shí)無優(yōu)化參數(shù),以前曾經(jīng)被-O坑過。

創(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ò)營銷,網(wǎng)絡(luò)優(yōu)化,隆化網(wǎng)站推廣為一體的創(chuàng)新建站企業(yè),幫助傳統(tǒng)企業(yè)提升企業(yè)形象加強(qiáng)企業(yè)競爭力??沙浞譂M足這一群體相比中小企業(yè)更為豐富、高端、多元的互聯(lián)網(wǎng)需求。同時(shí)我們時(shí)刻保持專業(yè)、時(shí)尚、前沿,時(shí)刻以成就客戶成長自我,堅(jiān)持不斷學(xué)習(xí)、思考、沉淀、凈化自己,讓我們?yōu)楦嗟钠髽I(yè)打造出實(shí)用型網(wǎng)站。

 

#include <stdio.h>
#include <string.h>
 
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)問題,唯有6.x(試了6.0、6.3、6.7)的gcc4.4.7會(huì)有問題

 

下載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)如此問題

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;
}

同樣看不出有什么問題。

 

將系統(tǒng)函數(shù)修改為自定義函數(shù),使用一樣的代碼,結(jié)果均為正確。

網(wǎng)絡(luò)上也找到過另外一種優(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)也沒有問題。

繼續(xù)發(fā)現(xiàn)strncpy和sprintf也會(huì)遇到同樣的問題。

采用memcpy就沒有問題了

memcpy(p+11,p+18,strlen(p+18)+1);

看了下源碼,跟strcpy也沒什么區(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)致問題。

以前曾經(jīng)在網(wǎng)上看見過strcpy的優(yōu)化函數(shù),在64位系統(tǒng)里,采用八字節(jié)長×××來進(jìn)行復(fù)制,但是未在庫中見過,只是作為優(yōu)化的自定義代碼推薦。

在這里例子中,如果我們將p+15改成p+16,就一切正常,把字符串總長度縮小到p+32(即*(p+32)=0),那么也一切正常。錯(cuò)誤字段長度8字節(jié),跟8都有關(guān)系,懷疑系統(tǒng)在什么地方做了優(yōu)化,但是實(shí)在搞不清是誰在優(yōu)化,優(yōu)化后的代碼是什么樣子的。

所以建議如果要進(jìn)行字符串自我移動(dòng),不要使用str,使用mem函數(shù)。

--------------------

同事提供了一個(gè)帖子,說的是內(nèi)存重疊的問題

http://blog.csdn.net/stpeace/article/details/39456645

但是這個(gè)例子的作者其實(shí)沒有分析到點(diǎn)子上

char str []="123456789";
strcpy(str + 2, str);

本身在代碼邏輯上就是錯(cuò)的,從源碼就能看出來,從前往后復(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沒什么改動(dòng),就是取消了寄存器(reg_char變成了char),在這個(gè)博客里面說過寄存器的重要性,可以提高copy速度,不明白為什么2.17取消了寄存器。

http://blog.csdn.net/astrotycoon/article/details/8114786

繼續(xù)測試

發(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都沒有進(jìn)入,不過這個(gè)匯編函數(shù)strcpy-sse2-unaligned也是glib里面的

對匯編實(shí)在無力,完全無從下手。

不過看來所謂的c源碼對分析沒有什么太大的幫助還容易引起誤解,因?yàn)榈讓拥膸旄揪筒挥胏程序的源碼啊。

當(dāng)前題目:strcpy在centos6.x,gcc4.4.7版本上會(huì)有bug,自我移動(dòng)導(dǎo)致覆蓋錯(cuò)誤overlap
文章分享:http://bm7419.com/article42/jdddec.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供靜態(tài)網(wǎng)站、商城網(wǎng)站、手機(jī)網(wǎng)站建設(shè)、企業(yè)網(wǎng)站制作、用戶體驗(yàn)服務(wù)器托管

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

營銷型網(wǎng)站建設(shè)