NASM Linux編

hanpen 2006/09/23
Last Update 2010/04/24
○ NASM(Netwide ASseMbler) .
LinuxでのNASMについて 『独習アセンブラ』の中に書いてあったDOS用のソース を Linux 用のソースに置き換えて書いてみました.

目 次

1 Linux で NASM アセンブリ言語

今回,Plamo-4.2 Linuxで NASMを使ってアセンブリ言語をやっていきます. Fedora Core5 も並行しなが検証していきますが, 説明は, ほとんど Plamo-4.2 でのアセンブリを対象にしています.『独習アセンブラ』では, 大半がDOSのアセンブリソースについて書いてありLinuxで検証するには本の中だけの説明ではアセンブラの初級者にはチッョト辛いものがあります.実際にソースを Linux 用に書き直し検証しながら現在進行形で進みます.(現在進行形のため途中で文章が修正されるかもしれませんが, 修正部分には訂正の斜線を引いておきます)

2 NASM の入手

  1. Plamo-4.2において,sourceforge.net よりNASMをgetします.
S1 nasm-0.98.39
#  cd /usr/local/src
#  tar jxvf  nasm-0.98.39.tar.bz2 
#  cd  nasm-0.98.39
#  ./configure
#  make
#  make install             ;(私はcheckinstallで入れました)

3 NASMのアセンブルとリンク

nasm のアセンブルとリンクの手順 (例は,hello.asm)
S2 nasm
$ nasm -f elf hello.asm      ;(Linux elfファイル形式の指定アセンブル)
$ ld -s -o hello hello.o     ;(アセンブルでできたオブジェクトファイルのリンク)
$ ./hello                    ;(パスが通ってない場合は ./helloで実行)

4 リスト1.1 〜 リスト1.4

※ 『独習アセンブラ』のリスト1.1から書かれているDOSのソースを Linux用に変換して記述していきます.

R01 dispchar.asm
  1. R01 リスト 1.1 一文字出力するプログラム.
  2. まず,しょっぱなからセグメント違反がでてしまい,参りました! "section .data"が書いてないとセグメント違反が出るようです.
    -- HDD がイマイチ調子がおかしいと思っていたら 10月1日にクラッシュしてしまいました! 早速, HDDを購入して Plamo -4.21β2を入れて nasmも再構築してテストしましたら,今度は,section .dataを書かなくとも"セグメンテーション違反" が発生しなくなりました? ( 本来は当り前なんですけど) 2006/10/3
"1"の文字を一文字標準出力.
; dispchar.asm

section .data    ; Plamo-4.03 Plamo-4.2 ではこれがないと セグメンテーション違反が発生(本来はセクション用)

section .text
global  _start

_start:
mov     eax, 0x0a31             ; 文字 "1"と改行      
push    eax                     ; スタックにバッファを設定
mov     eax, 4                  ; 出力(sys_write)      
mov     ebx, 1                  ; ファイルハンドル(1=標準出力 > ディスプレィに表示)
mov     edx, 2                  ; "1"の文字と改行の2文字出力指定       
mov     ecx, esp                ; 書き込むバッファ
int     0x80                    ; システムコール,出力する      
pop     eax                     ; push eaxでの eax回収

mov     eax, 1                  ; sys_exit 
mov     ebx, 0                  ; 終了ステータスコード 
int     0x80                    ; システムコール,終了する 
R02 hello.asm
  1. R02 リスト 1.2 文字列を出力するプログラム.
  2. ここでも,"section .data"を書くと問題なくアセンブラの実行ができるようです.
"Hello,World",と表示(標準出力)して改行.
; hello.asm
;; アセンブル "nasm -f elf hello.asm "
;; リンク     "ld -s -o hello hello.o "
;; 実行       "./hello "
        
section .data                   ; データセクションの定義       
message db 'Hello, World', 0x0a
length      equ $ -message      ; 文字列の長さ
section .text  
global _start                   ; エントリーポイント
        
_start:         
mov     ecx, message            ; 文字列の先頭アドレス
mov     edx, length             ; 文字列の長さ
mov     eax, 4                  ; 出力(sys_write)
mov     ebx, 1                  ; ファイルハンドル(1=標準出力 > ディスプレィに表示)
int     0x80                    ; システムコール,出力する   

mov     eax, 1                  ; sys.exit
mov     ebx, 0                  ; 終了ステータスコード 
int     0x80                    ; システムコール,終了する

R03 hello_jmp.asm
  1. R03 リスト 1.3 文字列を出力するプログラム2. (ジャンプ)
  2. 他の書き方もあるという,jmpを使ってもうひとつ "hello, world"の表示の例.
"hello, world",と表示して改行.
; hello_jmp.asm

section .data	 ; データセクションの定義  
message	db 'hello, world', 0x0a
section .text
global _start	                ; エントリーポイント

jmp  _main                      ; ジャンプ

_start:		
mov	edx, message		; スタックにバッファを設定
mov	eax, 4			; 出力(sys_write)
mov	ebx, 1			; ファイルハンドル(1=標準出力 > ディスプレィに表示)
_main:	
mov	ecx, message		; 文字列
int	0x80			; システムコール,出力する

mov	eax, 1                 ; sys.exit
mov	ebx, 0                 ; 終了ステータスコード 
int	0x80                   ; システムコール,終了する
R04 OnePlusfive.asm
  1. R04 リスト 1.4 1+5の結果を表示するプログラム.
  2. 1+5=6の計算結果の表示を,ここではしています
"6",と表示して改行.
; OnePlusfive.asm

section .data   ; Plamo-4.03 Plamo-4.2 ではこれがないとセグメンテーション違反が発生(本来はセクション用)

section .text
global _start
        
_start:         
mov     eax, 1                  ; eaxに"1"を移動
add     eax, 5                  ; eaxの値に"5"を加える
add     eax, 0x0a30             ; eaxの値の数の文字コードと改行
push    eax                     ; スタックにバッファを設定 
mov     eax, 4                  ; 出力(sys_write)
mov     ebx, 1                  ; ファイルハンドル(1=標準出力)
mov     edx, 2                  ; 1文字出力することを指定 
mov     ecx, esp                ; 書き込むバッファ
int     0x80                    ; システムコール,出力する 
pop     eax                     ; eaxの回収 

mov     eax, 1                  ; sys_exit 
mov     ebx, 0                  ; 終了ステータスコード 
int     0x80                    ; システムコール,終了する 

5 リスト4.1〜リスト4.4

※ リスト 2・3 は『独習アセンブラ』では,解説だけでサンプルリストはありません.
R05 hw.asm
  1. R05 リスト4.2 文字を入れ替えて出力するプログラム.
  2. messageの先頭の文字(h)と,messageの 8番目の文字[message+7](w)を参照してmovで移動する.移動すると言っても結局はコピーですね.改行コード追加 2006/09/29
hwと表示します.
; hw.asm
section .data                   ; データセクションの定義
message db      'hello, world', 0x0a
section .text
global _start

_start:         
mov     al, [message]           ; messageの先頭文字 "h"      
mov     ah, [message+7]         ; messageの8番目"w"
push    eax                     ; スタックにバッファを設定
mov     eax, 4                  ; 出力(sys_write)      
mov     ebx, 1                  ; ファイルハンドル(1=標準出力)
mov     edx, 3               ; "h""w"の文字と改行の3文字出力指定       
mov     edx, 2                  ; 二文字の出力の指定   
mov     ecx, esp                ; 書き込むバッファ
int     0x80                    ; システムコール,出力する
pop     eax                     ; eaxの回収

call    newline                 ; 改行用のcall                

mov     eax, 1                  ; sys_exit 
mov     ebx, 0                  ; 終了ステータスコード 
mov     0x80                    ; システムコール,終了する

newline:                        ; 以下改行用サブルーチン 
mov    dl,  0x0a                ; 
push   edx                      ; 
mov    eax, 4
mov    ebx, 1
mov    ecx, esp
mov    edx, 1
int    0x80
pop    edx
ret  
R06 xchg.asm
  1. R06 リスト4.3 xchgでメモリの内容を入れ替えて表示するプログラム.
  2. xchgで,"Hello"の "H"と "e"を入れ替えてます.便利なコードです.
eHllo,World と表示します.(xchgでソースオペランドとデイスティネーションオペランドの内容の交換)
; xchg.asm

section .data                   ; データセクションの定義
message db      'Hello, World',  0ah
length  equ     $ -message              ; 文字の長さ

section .text
global _start
_start:         
mov     edx, length             ; 文字列の長    
mov     cl, [message]           ; 以下の3行でmsgとmsg+1の内容を入れ換える
xchg    cl, [message+1]         ; 
mov     [message], cl           ; 
mov     ecx, message            ; 出力する文字の指定
mov     ebx, 1                  ; ファイルハンドル(1=標準出力)
mov     eax, 4                  ; 出力(sys_write)
int     0x80                    ; システムコール,出力する 
        
mov     eax, 1                  ; sys_exit 
mov     ebx, 0                  ; 終了ステータスコード 
int     0x80                    ; システムコール,終了する 

R07 push_pop.asm
  1. R07 リスト4.4 pushとpopの順序を意図的に変えたプログラム.
  2. Linux では,pushとpopは頻繁に使い,新に "eax"等を確保するのには便利です.
"1"と表示される.
; push_pop.asm

section .data
section .text
global _start
	
_start:		

mov	al, 0x31 		; "1"
mov	ah, 0x0a                ; 改行
;mov	eax, 0x0a31		; pop入れ替え検証用と改行
push	edx			; スタックにバッファを設定 
push	eax                     ; スタックにバッファを設定 
mov	eax, 4			; 出力(sys_write)
mov	ebx, 1			; ファイルハンドル(1=標準出力)
mov	ecx, esp		; 書き込むバッファ
mov	edx, 2                  ; 1文字と改行出力の指定
int	0x80			; システムコール,出力する  
pop     edx                     ; 普通は pop eaxとする
pop	eax                     ; 普通は pop edxとする
	                        ; 最初	edxにあった値は eaxに
	                        ; 最初	eaxにあった値は edxに入る
mov	eax, 1			; sys_exit 
mov	ebx, 0			; 終了ステータスコード 
int	0x80			; システムコール,終了する 

6 リスト5.1 〜 リスト5.10

R08 jmp.asm
  1. R08 リスト5.1 ジャンプ.
  2. ジャンプを使った無限ループです.ループを抜けるには,[ Ctrl ]キー + [ c ] キーです.
jmpを使った無限ループのプログラム.
; jmp.asm
;; [ Ctrl ]key + [ c ]key で無限ループ解除

section .data
section .text
global _start
        
_start:
label:                          ; ジャンプの指定場所(無限ループ)
mov     eax, 0x40               ; @ 記号
push    eax                     ; スタックにバッファを設定
mov     eax, 4                  ; 出力(sys_write)
mov     ebx, 1                  ; ファイルハンドル(1=標準出力)
mov     ecx, esp                ; 書き込むバッファ
mov     edx, 1                  ; 1文字出力することを指定  
int     0x80                    ; システムコール,出力する
pop     eax                     ; eaxの回収
jmp     label                   ; labelへジャンプ

mov     eax, 1                  ; プログラム終了(sys_exit) 
mov     ebx, 0                  ; 終了ステータスコード 
int     0x80                    ; システムコール,終了する 
R09 jnz.asm
  1. R09 リスト5.2 JNZジャンプ
  2. "A"〜"Z"まで表示するプログラムです.改行には,サブルーチンを使ってます.
AからZまでの文字を出力するプログラム.
; jnz.asm
section .data
section .text 
global _start
           
_start:	                        
mov     al, 0x41		; "A" アルファベット文字   
dispchr:			; ジャンプの場所
push    eax                     ; スタックにバッファを設定     
mov     eax, 4                  ; 出力(sys_write)
mov     ebx, 1                  ; ファイルハンドル(1=標準出力)
mov     ecx, esp                ; 書き込むバッファ 
mov	edx, 1			; 1文字出力することを指定        
int     0x80                    ; システムコール,出力する    
pop     eax                     ; eaxの回収          

inc	al			; alをインクリメントする
cmp     al, 0x5b		; alと0x5bを比べる
jnz	dispchr	 	        ; ZFがゼロならジャンプ

call    newline                 ; 改行用のcall

mov     eax, 1                  ; プログラム終了(sys_exit) 
mov     ebx, 0                  ; 終了ステータスコード 
int     0x80                    ; システムコール,終了する 

newline:                        ; 以下改行用サブルーチン 
mov    dl,  0x0a                ; 
push   edx                      ; 
mov    eax, 4
mov    ebx, 1
mov    ecx, esp
mov    edx, 1
int    0x80
pop    edx
ret  
R10 jecxz.asm
"Hello, World"を5回表示して改行するプログラム.
; jecxz.asm 

section .data
message	  db 'Hello, World', 0x0a	; 文字列と改行(LF)
length	  equ $ -message		; 文字列の長さ

section .text	 
global _start
       
_start:	                         
mov	ecx, 5			; 5回カウントする
doit:				; ジャンプの場所
push    ecx                     ; スタックにバッファを設定
mov     eax, 4                  ; 出力(sys_write)
mov     ebx, 1                  ; ファイルハンドル(1=標準出力)	
mov     ecx, message	        ; 書き込むバッファ 		
mov	edx, length		; 文字列を出力する         
int     0x80                    ; システムコール,出力する             	
pop	ecx			;※ snowfieldさんのご指摘によりpop ecxを追加 2010/04/24

dec ecx			        ; ecxを1だけ減らす	
jecxz	endquit	                ; ecxがゼロならジャンプ
jmp	doit			; doitへジャンプ		

endquit:
mov     eax, 1                  ; プログラム終了(sys_exit) 
mov     ebx, 0                  ; 終了ステータスコード 
int     0x80                    ; システムコール,終了する 
R11 loopdl.asm
loopを使って"Hello, World"を5回繰り返すプログラム.
; loop.asm
section .data
message  db	"Hello, World", 0x0a
length  equ $ -message

section .text
global _start
           
_start:	                        
mov	ecx, 5			; 5回繰り返す
doit:				; ここへloop
push	ecx			; スタックにバッファを設定
mov     eax, 4                  ; 出力(sys_write)
mov     ebx, 1                  ; ファイルハンドル(1=標準出力)
mov     ecx, message		; 文字列の先頭アドレス  
mov	edx, length		; 文字列の長さ        	
int     0x80                    ; システムコール,出力する             
pop	ecx			; ecxの回収

loop	doit			; doitへloopする
		    
mov     eax, 1                  ; プログラム終了(sys_exit) 
mov     ebx, 0                  ; 終了ステータスコード 
int     0x80                    ; システムコール,終了する 
R12 loopdl.asm
loopneを使ったループは, A〜Zまで表示するプログラム.
; loopdl.asm
section .data
section .text
        
global _start
           
_start:                         
mov     al, 0x41                ; "A" アルファベット文字  
mov     ah, 0x0a                ; 
doit:                           ; ここへloop
push    eax                     ; スタックにバッファを設定     
mov     eax, 4                  ; 出力(sys_write)
mov     ebx, 1                  ; ファイルハンドル(1=標準出力)
mov     ecx, esp                ; 書き込むバッファ  
mov     edx, 2                  ; 文字出力することを指定                
int     0x80                    ; システムコール,出力する             
pop     eax

inc     al                      ; alをインクリメント
cmp     al, 0x5b                ; alと " [ "を比較する
loopne  doit                    ; doitへloopする
                    
mov     eax, 1                  ; プログラム終了(sys_exit) 
mov     ebx, 0                  ; 終了ステータスコード 
int     0x80                    ; システムコール,終了する 
R13 loopne.asm
loopneでCXレジスタだけを使って繰り返すプログラム
; loopne.asm
section .data
message		db	'Hello,Assembler', 0x0a
length		equ $ -message

section .text
global _start
           
_start:	                        
mov	ecx, 5			; 5回まわす  
doit:				; ここへloop
push	ecx			; スタックにバッファを設定     
mov     eax, 4                  ; 出力(sys_write)
mov     ebx, 1                  ; ファイルハンドル(1=標準出力)
mov     ecx, message		; 文字列の先頭アドレス   
mov	edx, length		; 文字列の長さ        	
int     0x80                    ; システムコール,出力する             
pop	ecx			; ecxの回収

cmp	ecx, 3			; ecxと 3を比較する
loopne	doit			; doitへloopする
		    
mov     eax, 1                  ; プログラム終了(sys_exit) 
mov     ebx, 0                  ; 終了ステータスコード 
int     0x80                    ; システムコール,終了する 

6.1 リスト5.7 サブルーチン

R14 swapbd.asm
BXとDXの内容を入れ替えるサブルーチンプログラム.
; swapbd.asm
section .data
section .text
global _start
           
_start:	                        
mov	dl, 0x31		; dl="1"
mov	bl, 0x41		; bl="A"
 mov	al, 0x0a		; 改行
call	swapbd			; BXとDXの内容を入れ替える
push	eax			; スタックにバッファを設定
push	ebx			;   〃
push	edx			;   〃	
mov     eax, 4                  ; 出力(sys_write)
mov     ebx, 1                  ; ファイルハンドル(1=標準出力)
mov     ecx, esp		; 書き込むバッファ  
mov	edx, 0x0a		; 出力と改行指定        	
int	0x80			; システムコール.,出力する              
pop	eax			; eaxを回収
pop	ebx			; ebx  〃
pop	edx			; edx  〃
	    

mov     eax, 1                  ; プログラム終了(sys_exit) 
mov     ebx, 0                  ; 終了ステータスコード 
int     0x80                    ; システムコール,終了する 

swapbd:				; ebxとedxの内容を入れ替えるサブルーチン 
push	eax			; 現在のeaxの内容を保存しておく
mov	eax, edx		; 
mov	edx, ebx		; 
mov	ebx, eax		; 
pop	eax			; eaxの内容を復元する
ret				; 
R15 putch.asm
ABCと表示します
; putch.asm
section .data
chr	db   0	

section .text	
global _start
           
_start:
mov	[chr], byte 41h	; "A"
call	putch
mov	[chr], byte 42h	; "B"
call	putch
mov	[chr], byte 43h	; "C"
call	putch
	
mov     eax, 1                  ; プログラム終了(sys_exit) 
mov     ebx, 0                  ; 終了ステータスコード 
int     0x80                    ; システムコール,終了する 

putch:				; chrの文字を出力するサブルーチン
mov	dl, [chr]		; 出力する文字をdlに移動
push	eax			; 現在のeaxの内容を保存する
push	edx			; 現在のedxの内容を保存する
mov	eax, 4			; 出力(sys_write)
mov	ebx, 1			; ファイルハンドル(1=標準出力)
mov	ecx, esp		; 書き込むバッファ  
mov	edx, 1			; 文字出力することを指定    
int	0x80			; システムコール,出力する 	
pop	edx			; edxの内容を復元
pop	eax			; eaxの内容を復元
ret	
R16 subprint.asm
二種類の文字列を出力します
; subprint.asm
section .data
message1        db   'Hello, World', 0x0a
message2        db   'Good, Job', 0x0a

section .text
global _start
           
_start:
mov     edx, message1           
call    print
mov     edx, message2           
call    print

mov     eax, 1                  ; プログラム終了(sys_exit) 
mov     ebx, 0                  ; 終了ステータスコード 
int     0x80                    ; システムコール,終了する 

print:                          ; edxの文字列を出力するサブルーチン     
push    eax                     ; 現在のeaxの内容を保存する
mov     eax, 4                  ; 出力(sys_write)
mov     ebx, 1                  ; ファイルハンドル(1=標準出力)
mov     ecx, message1           ; 文字列出力指定  
mov     edx, 23                 ; 文字と改行の数の指定    
int     0x80                    ; システムコール,出力する       
pop     eax                     ; axの内容を復元
ret     
R17 stack.asm
AB と表示
; stack.asm
section .data
section .text
global _start
           
_start:	
mov	dl, 0x41		; "A"
push	edx
call	putchar
pop	edx
mov     dl, 0x42                ; "B" 
push	edx			 
call	putchar
pop	edx
mov     dl, 0x0a                ; 改行 
push	edx			 
call	putchar
pop	edx

mov	eax, 1			; プログラム終了(sys_exit) 
mov	ebx, 0			; 終了ステータスコード 
int     0x80                    ; システムコール,終了する 

putchar:			; スタックの文字を出力する 以下サブルーチン
push    ebp
mov	ebp, esp			
sub	esp, 0x4		; ローカルスタックスベース
mov	ebx, [ebp+8]		; 最初のパラメータ
mov	[ebp-4], ebx            ; 出力する文字をebpに移動

mov	eax, 4			; 出力(sys_write)
mov	ebx, 1			; ファイルハンドル(1=標準出力)
mov	ecx, esp		; 書き込むバッファ 
mov	edx, 1			; 文字出力することを指定   
int	0x80			; システムコール,出力する 	
		
mov	esp, ebp		; "sub sp,0x40"を取り消す	
pop	edp                     ; espの内容を復元
ret	

7 リスト6.1 〜 リスト%

R18 inpch.asm
一文字の入力
; inpch.asm
section .text
global _start
           
_start:	                        
push	eax			; スタックにバッファを設定
mov     eax, 3                  ; 入力(sys_read)
mov     ebx, 0                  ; ファイルデスクリプタ
mov     ecx, esp		; 読み込むバッファ  
mov	edx, 1			; 読み込むバイト数        	
int	0x80			; システムコール,入力する              
pop	eax			; eaxの復帰

mov     eax, 4                  ; 出力(sys_write) 
mov	ebx, 1			; ファイルハンドル(1=標準出力) 
mov	ecx, esp		; 書き込むバッファ  
mov	edx, 1			; 出力の指定  
int     0x80                    ; システムコール,出力する 

mov	eax, 1			; プログラム終了(sys_exit) 
mov	ebx, 0			; 終了ステータスコード 
int	0x80			; システムコール,終了する
R19 inps.asm
文字列の入力
   
; inps.asm
section .text
global _start
           
_start:                         
push    eax                     ; スタックにバッファを設定
mov     eax, 3                  ; 入力(sys_read)
mov     ebx, 0                  ; ファイルデスクリプタ
mov     ecx, esp                ; 読み込むバッファ  
mov     edx, 20                 ; 文字列の長さ           
int     0x80                    ; システムコール,入力する              
pop     eax

push    eax                          
mov     eax, 4                  ; 出力(sys_write) 
mov     ebx, 1                  ; ファイルハンドル(1=標準出力) 
mov     ecx, esp                ; 書き込むバッファ  
mov     edx, 1                  ; 出力の指定  
int     0x80                    ; システムコール,出力する 
pop     eax
                        
mov     eax, 1                  ; プログラム終了(sys_exit) 
mov     ebx, 0                  ; 終了ステータスコード 
int     0x80                    ; システムコール,終了する

_ ↑

hanpenのHPに戻る

参考HP
Jun's Homepage