undefined referenceに関して

MinGWでビルドする際に特有?なはまり方をしたのでメモ

その1呼び出し規約の食い違い

#include <GL/glut.h>

void display(void)
{
}

int main(int argc, char *argv[])
{
  glutInit(&argc, argv);
  glutCreateWindow(argv[0]);
  glutDisplayFunc(display);
  glutMainLoop();
  return 0;
}

例えばこんなソースを

TARGET=sample.exe
OBJS=main.obj

CC=i686-mingw32-gcc
CFLAGS=-I/usr/local/i686-mingw32/include

LD=i686-mingw32-gcc
LDFLAGS=-L/usr/local/i686-mingw32/lib -lglut32 -lopengl32 -lgdi32 -lglu32 -lwinmm

all: $(TARGET)

$(TARGET): $(OBJS)
  $(LD) -o $@ $^ $(LDFLAGS)

.SUFFIXES:.obj
.c.obj:
  $(CC) -c -o $@ $< $(CFLAGS)

clean:
  rm -f $(TARGET) $(OBJS)

でビルドすると・・・

$ make
i686-mingw32-gcc -c -o main.obj main.c -I/usr/local/i686-mingw32/include
i686-mingw32-gcc -o sample.exe main.obj -L/usr/local/i686-mingw32/lib -lglut32 -lopengl32 -lgdi32 -lglu32 -lwinmm
main.obj:main.c:(.text+0x1c): undefined reference to `___glutInitWithExit'
main.obj:main.c:(.text+0x37): undefined reference to `___glutCreateWindowWithExit'
main.obj:main.c:(.text+0x52): undefined reference to `___glutCreateMenuWithExit'
main.obj:main.c:(.text+0x92): undefined reference to `_glutDisplayFunc'
main.obj:main.c:(.text+0x97): undefined reference to `_glutMainLoop'
collect2: ld はステータス 1 で終了しました
make: *** [sample.exe] エラー 1

何故かglutのリンクに失敗する。
なんでかと思って調べてみたところシンボル名が違うのを発見。

$ objdump.exe -t /usr/local/i686-mingw32/lib/libglut32.a | grep glutMainLoop
[ 15](sec  1)(fl 0x00)(ty  20)(scl   2) (nx 0) 0x000007e4 _glutMainLoop@0 # @0がついてる

glut.hを確認すると

GLUTAPI void APIENTRY glutMainLoop(void);

となっている。
ここに至ってAPIENTRYが未定義くさいことに気づく。
ソースの先頭のglut.hより前に

#include <windows.h>

とすることで解決した。


今回使っているglut
http://vision.kuee.kyoto-u.ac.jp/~nob/doc/opengl/opengl.html
で紹介されている手順で作成した。

その2リンクする順番

上の例だとLDFLAGSの

-lglut32 -lopengl32

-lopengl32 -lglut32

の順にするとglut32から呼んでいるopengl32の関数がundefined referenceになる。
左のライブラリから右のライブラリを呼ぶ関係になっていないといけない。
(glut32がスタティックライブラリのため)

同様の問題にSDL

-lSDLmain -lSDL
(SDLmainがスタティック)

-lmingw32 -lSDLmain
(両方ともスタティック)

の順序問題がある。
要するに3つを

 -lmingw32 -lSDLmain -lSDL

の順で書く必要がある。

Makefileだと

$(TARGET): $(OBJS)
  gcc -o $@ $^ $(LDFLAGS)

のように自分のオブジェクト($^)のあとにライブラリ($LDFLAGS)が来る順番でないといけない。


Linuxでも試してみたがリンクの順番による問題は無いようだった。
順番が関係あるのはMingw特有なのかと思ったが解説発見。
http://www.hakodate-ct.ac.jp/~tokai/tokai/gtkmm/etc/p1.htm

静的ライブラリのせいだった。
今回の例だと
libglut32.a

libSDLmain.a、libmingw32.a
が該当する。
なるほど。