次のページ 前のページ 目次へ

9. memcpy()

9.1 現象

まずは以下のコードをコンパイルしてみましょう.


#include <string.h>

int foo ( char *to, char *from )
{
        char buf[1024];
        memcpy ( buf, "12345", 6 );
}

$ gcc -O0 -c foo.c
$ nm foo.o
00000000 T foo
$
あれ,memcpy() はどこに行ってしまったんでしょう. -O0 を指定したので最適化はしてないはずです.

こんどはこんなプログラム.


#include <string.h>

int foo ( char *to, char *from, int len )
{
        memcpy ( to, from, len );
}

同様にコンパイルしてみます.
$ gcc -O0 -c foo2.c
$ nm foo2.o
00000000 T foo
         U memcpy
今度はmemcpy()はありますね.

9.2 インライン展開

この現象は,gcc により memcpy() がインライン展開されるために生じます. おそらくは x86 のストリング命令を有効に利用するためにこのような最適化が入っているのでしょう.

gcc の info によると,このようなインライン展開は

で行われるそうです.

9.3 問題点

で,このようなインライン展開が,文脈として正しいところで正しいコードに展開されている限りは何の問題もありません.

が,gcc のバージョンによっては,誤ったインライン展開が行われることがあります. 私が見たことがあるものでは,「コピー元とコピー先のアドレスがコンパイル時に決定できないにも関わらず,32bit word align を仮定したコードでインライン展開されている」というものがありました. そのときコンパイルしていたのはブートローダだったので,例外が発生した時点でプログラムは動作しなくなりました.

9.4 対策

誤ったインライン展開をしない gcc に入れ換える,というのが正しい対処法でしょう.

が,何らかの都合でバグ入りのバージョンを使わなければならない場合は


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

int foo ( char *to, char *from )
{
        char buf[1024];
        my_memcpy ( buf, "12345", 6 );
}

と,私家版の memcpy を my_memcpy() として定義してやれば,誤ったインライン展開から逃れることができるでしょう.


次のページ 前のページ 目次へ