最近話題にのぼる事の多くなったOSで、Linuxと言う物があります。これはGPLで配布されているフリーのOSです。かくいう私も、Windowsの不安定さに耐えかねて、もっぱらこちらのOSを使用することが多いです。
さて、ここでは何を血迷ったかLinux上でのアセンブリ言語によるプログラムの作成方法について書かせていただくことにします。しかしこんな事を知っていてもあまり特にはならないので、暇な人以外は読み飛ばした方が無難です。また、x86のアセンブラの基本を知っている方が対象ですので、知らない人は暇でも読まない方が賢明です。
まずは、プラットフォームを決めたいと思います。アセンブラはその性質上、かなり機種依存をしますので、ある程度は範囲を決め手置かねばなりません。しかし、そう特殊な環境と言うわけではなく、至極一般的な環境を選んであります。
CPUは無難にx86系にします。PentiumとかAMD K6とか、Athlonとかが動いていればいいです。(CyrixとかWinchipとかCrusoeとかもそうです)まあ、一般的なパソコンだと思います。私みたいに未だに現役で386や486と格闘している人は大歓迎です。ただしMacはダメです。MacはCPUに68000系列のCPUを用いていて、x86とは別物です。Macの皆さんごめんなさい。
Linuxのカーネルのバージョンは2.0.x以上とします。1.x.xでも動作するかもしれませんが、私は2.0.36以上のカーネルしか触ったことがない為の処置です。動かせる自信のある人は頑張って下さい。ちなみに私が動作テストしたLinuxのバージョンは2.2.16と2.4.0-test9です。
アセンブラはNASMを使用することにします。最新バージョンは0.98です。古い物しか持っていない人はアップグレードしてください。そんなに大変な作業ではありません。configureしてmakeしてrootになってmake installするだけです。持っていない人もダウンロードしてきて下さい。yahoo等の検索エンジンでNASMを検索すれば結構当たるはずです。
なぜasやgasでないかというと、asやgasは本来ccやgccのフロントエンドとして作られたアセンブラであり、その文法も人間が記述するにはとても面倒な所があります。それが私がNASMを推奨する理由です。
今回私がテストに使用した環境を並べておきます。
Machine: PC-9821Np/340W CPU: i486DX4 75MHz MEM: 21.6MB HDD: hda: IDE-HDD 2.5" 3.2GB hda1 1.4GB MS-DOS 6.2 hda2 200MB FreeDOS(98) hda3 800MB / hda4 750MB /home hda5 50MB swap Distribution: Plamo Linux 2.0 Kernel ver: 2.2.14-002 (Linux/98) 2.2.16-003 (Linux/98) Machine: ViP KTN-ST800 CPU: Athlon(SocketA ThunderBird) 850MHz MEM: 256MB HDD: hda Ultra ATA/100 3.5" 40GB hda1 500MB FreeDOS (D:) hda2 7GB Windows (C:) hda3 ----- Linux extended hda4 19GB Windows (E:) hda5 50MB /boot hda6 500MB / hda7 3GB /usr hda8 200MB /var hda9 100MB /tmp hda10 9GB /home hda11 512MB swap CD-ROM: DVD 8x/40x Plextor CD-RW 1210TA Video: RIVA TNT2 M64 32MB Sound: Sound Blaster Live! Distribution: Vine Linux 2.0 (FTP) Kernel ver: 2.2.14 2.4.0-test9この2台でテストをしましたが、正常に動作する事を確認しました。
さて、プログラム講座恒例のHello, worldからスタートです。まずはいきなりソースを掲載します。
;---------- ; ここからがソースコードです ; hello.asm: coded by Yuki Mitsui ; アセンブル方法 ; $ nasm -f elf hello.asm ; $ ld -s -o hello hello.o ; segment .text global _start _start: mov eax, 4 mov ebx, 1 mov ecx, message mov edx, length int 0x80 mov eax, 1 int 0x80 segment .data message db "Hello, world!", 0x0a length equ $ - message ;----------さて、まずはDOS/WindowsとLinuxの基本的な差違について説明します。messageの定義部分を見ると、最後に0x0aと言う物があります。これは改行コードなのですが、Linux(UNIX全般)の改行コードは0x0aになっています。DOS/Windowsでは、改行コードは0x0d,0x0aの2バイトになります。ちなみにMacでは改行コードは0x0dです。
#ifndef _ASM_I386_UNISTD_H_ #define _ASM_I386_UNISTD_H_ /* * This file contains the system call numbers. */ #define __NR_exit 1 #define __NR_fork 2 #define __NR_read 3 #define __NR_write 4 #define __NR_open 5 #define __NR_close 6 : : :このファイルを掲載してしまったがためにこの文書はGPLの元に配布されなければいけないことになるのですね(笑)必要な部分だけ抜粋しましたが、#defineの次の__NR_を取った物がシステムコールの名前で、その次の番号がシステムコールを呼び出すときの番号です。システムコールを使用するときには、eaxにこの番号をいれます。そしてその後、引数をebx, ecx, edx, esi, ediに列挙していくのです。前述したソースファイルを見ると、ebxとecxとedxに引数が列挙されているのが分かります。システムコールの引数を調べる時には、manページが使えます。例えばwriteの引数は、write(2)のmanページを見ると、次のように書かれています。
Linuxでも当然のごとく引数を用いることができます。それも、DOSよりもずっと簡単な方法ですませることができます。Linuxでは、引数中に現れた*や?等は、シェルによって展開されます。そこがDOS/Windowsと違うところですので、注意して取り扱って下さい。
Linuxでの引数の取り扱いは、C言語で取り扱いやすい様なレイアウトになっています。つまりargc, argv, envpの順に取り出せます。(正確にはenvcもありますが)では、具体的な方法を書くことにします。
引数を取り扱うには、まずプログラムが始まってスタックに何も積んでいない状態で、32ビットのデータをpopします。すると、引数の個数を取得することが出来ます。つまりC言語でいうところのargcです。
それからはargcの個数分の引数の要素へのポインタが次々に同じ方法で取得できます。2回目のpopでは最初の引数、3回目のpopでは2番目の引数、というようにしていきます。全ての引数を読み終わった後にpopを実行すると、0が返されます。つまりNULLポインタですね。
さて、これからは本題に反しますが、引数の次に取得出来る環境変数についても説明していきます。これも引数の取得と全く同じ方法で行う事が出来ます。最初のpopで環境変数の要素の数を取得して、次からは実際の要素へのポインタを次々に取得していきます。環境変数の要素は、"環境変数名=値"の形で格納してあります。
では、サンプルを見てください。これは、コマンドラインで指定された引数を全て表示すると言うごく単純な物です。
;---------- ; ここからがソースコードです ; argument.asm: coded by Yuki Mitsui ; アセンブル方法 ; $ nasm -f elf argument.asm ; $ ld -s -o argument argument.o ; segment .text global _start _start: pop ecx ; 引数の個数(今回は使わない) jmp CheckFinish MainLoop: mov eax, 4 mov ebx, 1 StrLen: xor edx, edx jmp CountFinish CountLength: inc edx CountFinish: cmp byte [ecx+edx], 0 jnz CountLength int 0x80 PrintReturn: mov eax, 4 mov ebx, 1 mov ecx, Return mov edx, 1 int 0x80 CheckFinish: pop ecx test ecx, ecx jnz MainLoop Terminate: mov eax, 1 int 0x80 segment .data Return db 0x0a ;----------アセンブラプログラムのテクニックをいくつか使用していたりしますが、それほど難しいソースファイルでは無いと思います。システムコールもwriteとexitしか使用していません。
$ ./arguments foo bar baz quux ./arguments foo bar baz quux $この様に、一番最初にプログラムの名前が出力されています。これはLinuxでは、引数はC言語と同じようにargv[0]から取得される為です。従って、プログラム名はいらなかったら、argcを取得した後に一回から読みをする必要があるでしょう。
ソケットの使い方や、libc,glibc等の共有ライブラリを用いたアセンブリプログラミングについては解説しませんでした。理由は私の勉強不足だけです。これらは独学で頑張って下さい。
Linuxでのアセンブリプログラミングを行う際に参考になる資料を次に挙げておきます。この文書とシステムコールの参考書があればLinuxでアセンブリプログラミングを行う事も難しくは無いでしょう。
[★高収入が可能!WEBデザインのプロになってみない?!
自宅で仕事がしたい人必見!
]
[ CGIレンタルサービス | 100MBの無料HPスペース | 検索エンジン登録代行サービス ]
[ 初心者でも安心なレンタルサーバー。50MBで250円から。CGI・SSI・PHPが使えます。 ]
FC2 | ![]() ![]() |
![]() |
|