strcpy在centos6.x,gcc4.4.7版本上会有bug,自我移动导致覆盖错误overlap

Gcc编译时无优化参数,以前曾经被-O坑过。

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

打印结果应该如下

218.26.242.56/0/0/1023/6d6168bf1a7294ae0e1c071171adcd48.mp4

218.26.242.56/06d6168bf1a7294ae0e1c071171adcd48.mp4

但是在centos6.3系统下,gcc4.4.7,

打印结果会是

218.26.242.56/0/0/1023/6d6168bf1a7294ae0e1c071171adcd48.mp4

218.26.242.56/0/f1a7294a1a7294ae0e1c071171adcd48.mp4

目前实验redhat5.05.7,centos7.2系统下都不会出现问题,唯有6.x(试了6.0、6.3、6.7)的gcc4.4.7会有问题

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

理论上不应该出现如此问题

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

同样看不出有什么问题。

将系统函数修改为自定义函数,使用一样的代码,结果均为正确。

网络上也找到过另外一种优化版本的strcpy代码,使用寄存器加速效果,在网上找到的号称gcc的优化代码也是类似

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

将之作为自定义函数使用后发现也没有问题。

继续发现strncpy和sprintf也会遇到同样的问题。

采用memcpy就没有问题了

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

看了下源码,跟strcpy也没什么区别

void *
memcpy (void *dest, const void *src, size_t len)
{
  char *d = dest;
  const char *s = src;
  while (len--)
    *d++ = *s++;
  return dest;
}

暂时不明白为什么strcpy、strncpy、sprintf在gcc4.4.7下,自我移动会导致问题。

以前曾经在网上看见过strcpy的优化函数,在64位系统里,采用八字节长整形来进行复制,但是未在库中见过,只是作为优化的自定义代码推荐。

在这里例子中,如果我们将p+15改成p+16,就一切正常,把字符串总长度缩小到p+32(即*(p+32)=0),那么也一切正常。错误字段长度8字节,跟8都有关系,怀疑系统在什么地方做了优化,但是实在搞不清是谁在优化,优化后的代码是什么样子的。

所以建议如果要进行字符串自我移动,不要使用str,使用mem函数。

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

同事提供了一个帖子,说的是内存重叠的问题

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

但是这个例子的作者其实没有分析到点子上

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

本身在代码逻辑上就是错的,从源码就能看出来,从前往后复制,会导致后面的内存覆盖。和我们这次遇到的不是一个情况。

按照源码应该结果是1212121212121212。。。。。。。。。。。一直到越界崩溃

但是实际结果是121234565678

在几个机器上试了下

在gcc4.1.1上,是12121212121。。。。。。 崩溃

Gcc4.4.7 显示121234565678

gcc4.8.5 显示12123456789

应该是在4.4.7上确实有优化,但是4.8.5应该是解决了,而且连这种逻辑异常的也一起支持了。

在网上找到了gcc4.7上strcpy的汇编bug,为什么是不是也有关系。

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

网上查了一下,发现被误导了,这里应该研究libc.so的代码,而不是看gcc的代码

看了下libc的源码,define了不少实现,在不同设备上使用了不同优化的汇编码,应该是使用8字节复制代替单个字符串复制,在libc2.12有bug,libc2.17上应该是解决了。

查看他们的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;
}

发现2.17相比2.12没什么改动,就是取消了寄存器(reg_char变成了char),在这个博客里面说过寄存器的重要性,可以提高copy速度,不明白为什么2.17取消了寄存器。

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

继续测试

发现在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实现

在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

直接进入.s汇编码,连libc.so都没有进入,不过这个汇编函数strcpy-sse2-unaligned也是glib里面的

对汇编实在无力,完全无从下手。

不过看来所谓的c源码对分析没有什么太大的帮助还容易引起误解,因为底层的库根本就不用c程序的源码啊。

时间: 2024-10-08 11:04:28

strcpy在centos6.x,gcc4.4.7版本上会有bug,自我移动导致覆盖错误overlap的相关文章

Mint17下安装TFS(taobao file sysytem)(带gcc4.8.2版本)

最近研究了一下淘宝的分布式文件系统TFS(Taobao file system). TFS(taobao file system)是一个高可扩展.高可用.高性能.面向互联网服务的分布式文件系统,其设计目标是支持海量的非结构化数据的存储:TFS使用C++语言开发,需要运行在64bit Linux OS上,本文介绍如何在Linux环境编译安装TFS. 在安装的过程中,遇到了许多奇葩的问题,在此吐槽一下淘宝的团队真是懒.TFS介绍里面提到他们团队是用的gcc4.1.2进行编译的,高版本可能会报错,不过

CentOS6.7 Mysql5.6.35版本 源码编译+Mysql数据库忘记root密码如何修改

源码编译MySQL [[email protected] ~]# yum install cmake nucrses-devel –y      //安装编译环境及终端操作的开发包 -- Installed: cmake.x86_64 0:2.8.12.2-4.el6 Dependency Installed: libarchive.x86_64 0:2.8.3-7.el6_8 Complete! 下载软件包 [[email protected] ~]# wget http://ftp.ntu.

Centos6 升级glibc-2.17,解决Requires: libc.so.6(GLIBC_2.14)(64bit)错误解决方法

Centos6 升级glibc-2.17,解决Requires: libc.so.6(GLIBC_2.14)(64bit)错误解决方法 出现问题 在Centos6安装mysql 5.7.x 系列提示错误如下: [[email protected]_0_5_centos mysql]# rpm -ivh mysql-community-libs-5.7.18-1.el6.x86_64.rpm warning: mysql-community-libs-5.7.18-1.el6.x86_64.rpm

升级CUDA版本导致VS2010错误:未找到导入的项目XXX,请确认&lt;Import&gt;声明中的路径正确,且磁盘上存在该文件。。。。

VS2010错误:未找到导入的项目XXX,请确认<Import>声明中的路径正确,且磁盘上存在该文件. E:\IGSNRR\dev\PhDThesisCode_CUDA\gtcg\gtcg.vcxproj : error : 未找到导入的项目“C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\BuildCustomizations\CUDA 5.5.props”.请确认 <Import> 声明中的路径正确,且磁盘上存在该文件. E:

4.3.3版本之引擎bug

bug描述: IOS设备上,当使用WWW www = WWW.LoadFromCacheOrDownload(url, verNum); 下载资源时,第一次下载某个资源,www.assetBundle必定为空. 解决办法: 引擎版本降到4.3.2或者升到4.3.4或更高. 这个bug绝对是神级坑,整整耗费了我十个工作日.引擎升到4.3.3以后,同样的代码,在PC/MAC上跑的都很好,在IOS上就必然崩掉,而且最无语的是没有任何错误提示或者异常信息,xcode里面就一条崩溃信息,我去.我对代码进行

server version for the right syntax to use near &#39;USING BTREE 数据库文件版本不合导致的错误

MySQL 返回:#1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'USING BTREE,  KEY `lang` (`language_id`)) ENGINE=MyISAM DEFAULT CHARSET=utf8' at line 5 解决办法是打开要导入

Centos6安装gcc4.8及以上版本

很多场景我们编译C源码,都需要使用gcc4.8及以上版本,比如编译MySQL 8.0.GRPC等,原因是需要支持C++11.但CentOS 6其内置版本是gcc4.4. 使用gcc --version可以查看版本. $gcc --version gcc version 4.4.7 20120313 (Red Hat 4.4.7-23) (GCC) 源码编译 常规升级办法是: 1.下载gcc新版本: 2.编译. 但gcc编译起来真的是非常非常的慢,而且特别耗资源,编译到后面选择放弃了(ps:编译的

CentOS6.5 gcc-4.8.2安装步骤

转载自:http://blog.sina.com.cn/s/blog_627b3f930101hnz8.html 非常感谢Trig_Yao CentOS6.5自带的gcc版本为4.4.7,有些年代了,一些新软件要用到的库没有,最新的gcc 4.8.2已经放出来了,于是下载源代码安装了一个试试. 1.首先把旧的gcc相关的编译工具安装好 [javascript] view plaincopy yum install gcc gcc-c++ glibc-static -y 2.下载gcc 4.8.2

centos6/7通用查看系统版本的方法

    因为最近要写一个centos的初始化脚本,但是centos6和centos7版本有很多命令都不相同,所以为了让脚本在两个版本之间都可以使用,就需要对centos系统版本进行判断.     通过查找各种资料,我发现基本有下面几种查看系统版本的方法: 方法一:可以用lsb_release -a来查看系统版本 [[email protected] ~]# lsb_release -a LSB Version: :base-4.0-amd64:base-4.0-noarch:core-4.0-a