スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

【同じタグを付けた記事の一覧】

LLVM を Windows で使う(clang 編)

2012年09月17日(月)00時49分

clang」で「~.s」ファイルを作る

前々回「インタプリタ編」と前回「EXE ファイル生成編」では、LLVM 用の中間言語で書かれた「~.s」ファイルを実行する方法について調べました。
これは、そもそもが「オリジナルな自作プログラミング言語を作ってみたい」という妄想から始まっており、「~.s」ファイルは自力で作るという前提があったためです。
ですので、次はその「~.s」ファイル、すなわち「LLVM 用中間言語でのソースコード」の書き方を知る必要があるわけですが、そのために、とりあえず慣れているC言語で書き、それが「~.s」ファイルではどうなるのか?が確認できれば便利です。
ということで、C言語や C++ で書かれたソースを LLVM 用中間言語に変換できるコンパイラである「clang」を使えるようにしてみました。

ポータブル

前回・前々回もそうでしたが、個人的な趣味もあり、今回もポータブルな環境構築を目標とします。
具体的には、USB メモリを「U:」ドライブに割り当てたと仮定し、必要なファイルを「U:\LLVM」フォルダにまとめます。
ただし、目的が「LLVM 用中間言語記述の参考にすること」ですので、単純に一個の簡単な「~.c」ファイルから、それに対応する LLVM 用の中間言語コードを得ることのみに主眼を置いています。
つまり、今回作る環境はあくまでテスト用で、「本格的なソフトウェア開発」や「大規模なプロジェクトのビルド」といったことに使えるようなもでは、多分ありません。

clang の準備

前回・前々回の続きで…、とすると説明がややこしくなりますので、また最初から作っていくことにします。
まず clang ですが、公式サイト「http://llvm.org/」から LLVM の Windows 版をダウンロードしてきます。
2012年09月の時点では「clang+llvm-3.1-i386-mingw32-EXPERIMENTAL.tar.bz2」でした。
この BZ2 ファイルを解凍(展開)し、含まれる「bin」フォルダにある「clang.exe」「llc.exe」「lli.exe」「llvm-as.exe」をの四ファイルを「U:\LLVM\bin」フォルダにコピーします。
といっても、今回の目的に必要なのは「clang.exe」だけで、後の三つは前回・前々回の内容のために必要なものです。

MinGW をインストールする場合

前回「EXE ファイル生成編」でも書きましたが、もともと LLVMGCC の一部を代替するような形で始まっているそうで、今回の目的でも GCC を部分的に利用します。
ということで、GCC の Windows 移植版である MinGW からファイルを借りてくるわけですが、これも前回書いた通り、MinGW をインストールする場合には非常に簡単に済みます。
まず、公式サイトは「http://www.mingw.org/」からインストーラをダウンロードし、「C Compiler」と「C++ Compiler」の二つを選択してインストールします。
インストールするフォルダはどこでも構わないのですが、説明の便宜上「C:\MinGW」フォルダにインストールしたものとして話を進めます。
で、あとは以下の通りにフォルダやファイルをコピーすれば準備完了です。

  • 「C:\MinGW\bin」フォルダから「as.exe」「gcc.exe」「ld.exe」の三ファイルを「U:\LLVM\bin」フォルダにコピー。
  • 「C:\MinGW\include」フォルダと「C:\MinGW\lib」フォルダを「U:\LLVM」フォルダにコピー。
  • 「C:\MinGW\libexec\gcc\mingw32\4.6.2\liblto_plugin-0.dll」を「U:\LLVM\libexec\gcc\mingw32\4.6.2」フォルダにコピー。

MinGW をインストールしない場合

MinGW をインストールしない場合は、「http://sourceforge.net/projects/mingw/files/」から、「MinGW → Base」と辿って以下のファイルを集め、そこから必要なフォルダ・ファイルを抽出し、「U:\LLVM」フォルダにコピーします。

binutils-2.22-1-mingw32-bin.tar.lzma

「U:\LLVM\bin」フォルダに「bin\as.exe」と「bin\ld.exe」をコピー。
「U:\LLVM\include」フォルダに「include」フォルダ内のファイル群をコピー。
「U:\LLVM\lib」に「lib」フォルダ内のファイル群をコピー。

pthreads-w32-2.9.0-mingw32-pre-20110507-2-dev.tar.lzma

「U:\LLVM\include」フォルダに「include」フォルダ内のファイル群をコピー。
「U:\LLVM\lib」に「lib」フォルダ内のファイル群をコピー。

mingwrt-3.20-mingw32-dev.tar.gz

「U:\LLVM\include」フォルダに「include」フォルダ内のフォルダとファイル群をコピー。
「U:\LLVM\lib」に「lib」フォルダ内のファイル群をコピー。

w32api-3.17-2-mingw32-dev.tar.lzma

「U:\LLVM\include」フォルダに「include」フォルダ内のファイル群とフォルダをコピー。
「U:\LLVM\lib」に「lib」フォルダ内のファイル群をコピー。

gcc-core-4.6.2-1-mingw32-bin.tar.lzma

「U:\LLVM\bin」フォルダに「bin\gcc.exe」をコピー。
「U:\LLVM\lib\gcc\mingw32\4.6.2」に「lib\gcc\mingw32\4.6.2」フォルダ内のフォルダとファイル群をコピー。
「U:\LLVM\libexec\gcc\mingw32\4.6.2」フォルダに「libexec\gcc\mingw32\4.6.2\liblto_plugin-0.dll」をコピー。

gcc-c++-4.6.2-1-mingw32-bin.tar.lzma

「U:\LLVM\lib\gcc\mingw32\4.6.2」に「lib\gcc\mingw32\4.6.2」フォルダ内のフォルダとファイル群をコピー。

hello world

ということで準備が完了し、早速「hello world」に行きます。
「U:\LLVM\home」フォルダを作り、以下の内容で「hello.c」「hello.bat」の二ファイルを置き、「hello.bat」を実行します。

#include <stdio.h>
int main() {
  printf("hello world\n");
  return 0;
}
set PATH=..\bin;..\libexec\gcc\mingw32\4.6.2
set C_INCLUDE_PATH=..\include;..\lib\gcc\mingw32\4.6.2\include
set CPLUS_INCLUDE_PATH=%C_INCLUDE_PATH%;..\lib\gcc\mingw32\4.6.2\include\c++;..\lib\gcc\mingw32\4.6.2\include\c++\mingw32
set LIBRARY_PATH=..\lib;..\lib\gcc\mingw32\4.6.2
clang -S -emit-llvm -o hello.s hello.c
clang -o hello.exe hello.c
hello.exe
pause

すると、「hello.s」「hello.exe」の二ファイルが生成され、「hello.exe」が実行されます。
「hello.bat」の五行目、「clang -S -emit-llvm -o hello.s hello.c」が LLVM 用中間言語である「~.s」ファイルを生成するためのコマンドです。
実行ファイル「hello.exe」を生成するだけであればこの五行目は必要ないのですが、今回は「~.s」ファイルを得ることこそが目的ですので、むしろこの五行目こそが話の中心になります。

「-v」スイッチとリンカ・オプション

前回「EXE ファイル生成編」では、BAT ファイル内でリンカ「ld.exe」に大量のオプション(ほとんどがライブラリの指定)を与えていましたが、実はそれは、今回の方法で clang が生成したリンカ・コマンドの書き写しです。
例えば上記「hello.bat」の六行目、「clang -o hello.exe hello.c」に「-v」スイッチを追加し、「clang -o hello.exe hello.c -v」のようにすると、普段は抑制されている clang(GCC)のメッセージ文字列が全て出力されるようになります。
そこでリンカ「ld」を呼び出している個所(たいていは最後の行)を見れば、S(LLVM 用中間言語)ファイルから生成したO(オブジェクト)ファイルに対して、どのようなライブラリを指定すれば実行ファイルができるのか、ということがわかります。
ちなみに、「出力メッセージが多すぎて見るのが面倒」という場合には、「clang -o hello.exe hello.c -v 2> hello.txt」のようにすれば、本来コンソールに出力される内容が「hello.txt」に書き出されますので、少し楽になります。

clang は何をするのか

で、上記「hello.txt」を見ると、「clang が何をしているのか?」がわかってきます。
まず clang は、与えられた「~.c」ファイルをコンパイルして「~.s」ファイルを作ります。
この「~.s」ファイルは、これまで作っていた LLVM 用中間言語のソースコードではなく、GAS(GNU アセンブラ)用のソースコードです。
そのあと、作った「~.c」ファイルを「gcc.exe」に渡します。
gcc.exe」は、それ自体で何か複雑な処理をするものではなく、与えられたソースコードの種別を判別し、「適切なコンパイラ→ アセンブラ → リンカ」と実行ファイル生成に必要な一連の外部ツールを順に呼び出していくツールです。
しかしながらこの場合、GCCclang から GAS 用ソースコードだけを受け取りますから、本来必要になる GNU C(++)コンパイラを呼び出すこともなく、ただその後に必要となるアセンブルとリンクのみを行うことになります。
つまり、clang はC言語や C++ のソースコードを GAS 用ソースコードにコンパイルする処理のみを担当し、残りは GCC に任せてしまっているわけです。

C++(STL)の場合

話は戻り、clang の使い方ですが、C++ で STL を使用している場合は、少し注意が必要になります。
例えば以下のような C++ のコード「hellopp.cpp」があったとします。

#include <iostream>
int main() {
  std::cout<<"hello world!\n";
  return 0;
}

ここから「~.s」ファイルおよび実行ファイルを得るには、以下のような「hellopp.bat」を書くことになります。

set PATH=..\bin;..\libexec\gcc\mingw32\4.6.2
set C_INCLUDE_PATH=..\include;..\lib\gcc\mingw32\4.6.2\include
set CPLUS_INCLUDE_PATH=%C_INCLUDE_PATH%;..\lib\gcc\mingw32\4.6.2\include\c++;..\lib\gcc\mingw32\4.6.2\include\c++\mingw32
set LIBRARY_PATH=..\lib;..\lib\gcc\mingw32\4.6.2
clang -S -emit-llvm -o hellopp.s hellopp.cpp
clang -o hellopp.exe hellopp.cpp -static -Wl,-lstdc++
hellopp.exe
pause

まず、「-Wl,-lstdc++」を付ける必要があります(付けないとリンクエラーでビルドができません)。
「-Wl」はリンカに渡すコマンドを指定するスイッチで、これはつまり「ld.exe」実行時に「-lstdc++」をつけるように、という指示です。
それはリンカに「-l」スイッチで「stdc++」を指定したということなり、これを受けて「ld.exe」は、「libstdc++.a」というファイルを探し出してリンクすることになります。
この「libstdc++.a」が STL 用のライブラリです(そして結構サイズの大きなファイルです)。
また、「-static」というスイッチも付ける必要があります。
これをつけずともビルド自体は成功しますが、生成された EXE ファイルを実行すると、「コンピューターにlibstdc++-6.dllがないため、プログラムを開始できません。この問題を解決するには、プログラムを再インストールしてみてください。」というメッセージで終了してしまうことになります。
この「-static」というスイッチは、「ライブラリを静的リンク(実行ファイル内に埋め込む)しろ」という指示で、これをつけていない場合は「-dynamic」が指示された扱いになります(なお、リンカ「ld.exe」に直接指示する場合には、それぞれ「-Bstatic」「-Bdynamic」と、「-B」スイッチに続けて内容を書く形式になります)。
もしこれが「-dynamic」だった場合、ライブラリ内に存在する機能は、それが必要になったときに「どこかにある」DLL ファイルを読み込んで、その中にある該当機能部分を呼び出して使うことになります(動的リンク)。
ところがこの「どこかにある」はずの DLL ファイル「libstdc++-6.dll」が、Windows では(標準では)存在しないのです。
もちろん、MinGW 製の何かしらのソフトウェアを使用しており、それとともに DLL ファイルもインストールされていた、という環境であれば実行できる可能性もありますが、より多くの環境で確実に動作させるためには「静的リンク」しておく必要があります。
ただし、前述のように「libstdc++.a」はサイズが大きく、これを「静的リンク」すると実行ファイルのサイズが肥大化してしまうのか残念なところです(上記「hello.exe」が 50KB に満たないのに対して「hellopp.exe」は 1MB を余裕で超えてしまいます)。

Windows 用は「-mwindows」

WinMain 関数から始まる Windows 用アプリケーションの場合、「-mwindows」を指定することになります。

#include <windows.h>
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow) {
  MessageBox(NULL,"Text","Caption",MB_OK);
  return 0;
}

というような内容の「MessageBox.c」から、「~.s」ファイルおよび実行ファイルを得るには、以下のような「hellopp.bat」を書きます。

set PATH=..\bin;..\libexec\gcc\mingw32\4.6.2
set C_INCLUDE_PATH=..\include;..\lib\gcc\mingw32\4.6.2\include
set CPLUS_INCLUDE_PATH=%C_INCLUDE_PATH%;..\lib\gcc\mingw32\4.6.2\include\c++;..\lib\gcc\mingw32\4.6.2\include\c++\mingw32
set LIBRARY_PATH=..\lib;..\lib\gcc\mingw32\4.6.2
clang -S -emit-llvm -o MessageBox.s MessageBox.c
clang -o MessageBox.exe MessageBox.c -mwindows
MessageBox.exe
pause

これで実行時にコンソール画面の出ないアプリケーションとなります。

省サイズ EXE ファイル

前回「EXE ファイル生成編」の最後に書いた「省サイズ EXE ファイル」のソース「NoStdLib.c」とそれ用の BAT ファイルは以下のようになります。

#include <windows.h>
void WinMainCRTStartup() {
  MessageBox(NULL,"Text","Caption",MB_OK);
  ExitProcess(0);
}
set PATH=..\bin;..\libexec\gcc\mingw32\4.6.2
set C_INCLUDE_PATH=..\include;..\lib\gcc\mingw32\4.6.2\include
set CPLUS_INCLUDE_PATH=%C_INCLUDE_PATH%;..\lib\gcc\mingw32\4.6.2\include\c++;..\lib\gcc\mingw32\4.6.2\include\c++\mingw32
set LIBRARY_PATH=..\lib;..\lib\gcc\mingw32\4.6.2
clang -S -emit-llvm -o NoStdLib.s NoStdLib.c
clang -o NoStdLib.exe NoStdLib.c -mwindows -nostdlib -Wl,--entry,_WinMainCRTStartup -lkernel32 -luser32
NoStdLib.exe
pause

古い環境のサポートに?

Microsoft の Visual C++ は、バージョンが上がるごとに「古い環境の戦略的切捨て」が行われ、例えばごくシンプルな空のウインドウを表示するだけのプログラムでも、Visual C++ 2010 でビルドした場合は、Win2k 以前では動かない EXE ファイルが生成されます。
しかし、今回の「clang + MinGW」の組み合わせで生成した実行ファイルは、Win2k どころか Win95 ですら動作可能でした。
ということで、古い環境のサポートという点から、「clang + MinGW」で生成するというのはありかもしれません(別に clang でなくとも、MinGW(GCC) だけで作っても同じかと思いますが)。
もっとも、今更 Win95 で動いたところで…、という気もしなくはないですが、とはいえ、無駄に「動かないようにする」こともないと思いますので。
ただし、「clang + MinGW」自体は Win2k では使えませんでした。
これはどうも、公式で配布されている clangMinGW のバイナリが、Visual C++ を使用してビルドされているからではないか?という気がします(違うかもしれませんが)。

関連記事

【同じタグを付けた記事の一覧】
ソースコード プログラミング ポータブル C++ LLVM

スポンサーサイト

コメントの投稿

非公開コメント

最新記事
最新コメント
Amazonおまかせリンク
カテゴリ
タグクラウド
Amazonお買い得ウィジェット
カレンダー
03 | 2017/04 | 05
- - - - - - 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 - - - - - -
月別アーカイブ
プロフィール

電脳太助

Author:電脳太助
Website:電脳スピーチ web

RSSリンクの表示
メールフォーム

名前:
メール:
件名:
本文:

サイト内検索
Ads by Google
FC2アクセスランキング
Ads by Google
FC2拍手ランキング
ユーザータグ

音楽管理(65)
ポータブル(57)
ソフト紹介(44)
プログラミング(42)
音声技術(41)
自作ソフト(34)
サイト運営(32)
FC2(31)
ブログ(30)
iTunes(26)
Windows(25)
LISMO(24)
音声合成(23)
音声認識(22)
x-アプリ(22)
電子ブック(22)
eラーニング(20)
バックアップ(19)
語学学習(19)
foobar2000(18)
ソースコード(17)
画像管理(15)
WindowsLiveWriter(15)
C++(14)
アフィリエイト(10)
DnspTools(10)
ウォークマン(9)
fi-6130(9)
FLAC(9)
Gracenote(8)
英語音読学習計画(8)
Prolog(8)
JavaScript(8)
ベクター(8)
雑記(8)
CodeBlocks(7)
SyntaxHighlighter(7)
TraConv(7)
wxWidgets(7)
spcbght(7)
DCP-J552N(6)
W63CA(6)
MP3Gain(6)
WinRT(6)
iGoinLM(6)
VirtualBox(6)
WindowsLiveMesh(6)
英語発音矯正実験(6)
ExactAudioCopy(6)
楽器演奏(5)
Mery(5)
LAME(5)
音楽技術(5)
GalateaProject(4)
LLVM(4)
nLite(4)
MIDI(4)
ホームページ(4)
WindowsLiveSkyDrive(4)
GalateaTalk(4)
PC-98(3)
カウンター(3)
AACGain(3)
iTCDini(3)
OverCutChecker(3)
拍手(3)
PK-513L(3)
UniversalExtractor(3)
アクセスランキング(3)
ImageCompositeEditor(2)
アクセス解析(2)
OCR(2)
qtaacenc(2)
資格試験(1)
AquesTalk(1)
AquesCmdDl(1)

FC2アクセスランキング
最新トラックバック
アクセスランキング
[ジャンルランキング]
コンピュータ
102位
アクセスランキングを見る>>

[サブジャンルランキング]
ソフトウェア
10位
アクセスランキングを見る>>
FC2カウンター
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。