Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
5
Help us understand the problem. What is going on with this article?
@drib__

gasでx86アセンブラ(メモ)

More than 1 year has passed since last update.

GNU assembler(gas)でアセンブラを書く記事が少ないと思うので簡単にまとめてみました。
みなさんnasm、そしてintel記法が大好きだと思いますが今回は宗教上の理由によりgas,AT&T記法を使用します。

環境

bash
$uname -i -s -o -p -r
Linux 3.10.0-862.3.3.el7.x86_64 x86_64 x86_64 GNU/Linux

$ lsb_release -a
LSB Version:    :core-4.1-amd64:core-4.1-noarch:cxx-4.1-amd64:cxx-4.1-noarch:desktop-4.1-amd64:desktop-4.1-noarch:languages-4.1-amd64:languages-4.1-noarch:printing-4.1-amd64:printing-4.1-noarch
Distributor ID: CentOS
Description:    CentOS Linux release 7.5.1804 (Core) 
Release:    7.5.1804
Codename:   Core

$ gcc --version
gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-28)
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
.bashrc
PS1="\$?:[ssh]\W \u$ "
#(プログラムの返り値をすぐ確認できるように\$?を入れてます)

return 2 をする

ret2.s
.text
.global _start
_start:
    mov $1,%eax #systemcallの番号を指定
    mov $2,%ebx #引数(今回は戻り値)
    int $0x80

1行目の.textはそれ以下がtextセクション(命令が保存される場所)であることを指定します
2行目の.global _startはリンカに_startが存在することを知らせています。デフォルト(gnuのリンカスクリプト)は_startからプログラムが始まりますがリンカスクリプトを作成することで任意の関数から実行することができます。
3行目から_start関数の内容です。
Linux Syscall Referenceを参考にします。今回は戻り値を指定してプログラムを終了するだけなのでsys_exitが良さそうです。sys_exitはeaxが0x01,ebxがerror_code(プログラムの戻り値:今回は2)なので4,5行目でそのように指定しています。
6行目は割り込みハンドラ(sys_exit)を呼び出しています.

bash
0:[ssh]test proxy$ gcc -m32 -nostdlib ret2.s -o ret2
0:[ssh]test proxy$ ./ret2
2:[ssh]test proxy$ 

コンパイルはnostdlibオプションを指定することで単一ファイルで済ませれるようにしています(ただしオプション名からわかるようにgnuの標準ライブラリ(libc)を使用しないのでgnuのライブラリの恩恵を受けられなくなります。その代わりlibcを含まないとファイルサイズが小さくなるなどというメリットがあります)
-m32と指定することでx86のELFを生成します。

hello

hello.s
.data
msg:.asciz "hello\n"

.text
.global _start
_start:
    mov $0x04,%eax #sys_write
    mov $0x01,%ebx #fd:今回はstdout
    mov $msg,%ecx #文字列のアドレス
    mov $0x06,%edx #文字列の長さ
    int $0x80

    mov $0x1,%eax
    mov $0x2,%ebx
    int $0x80

今回はhelloを標準出力させていのでsystemcallのsys_writeを使用します。
Linux Syscall Referenceを参考にするとecxには文字列のあるアドレスを指定する必要があるので.data(dataセクション)に文字列を置きました。

bash

0:[ssh]test proxy$ gcc -m32 -nostdlib hello.s -o hello
0:[ssh]test proxy$ ./hello 
hello
2:[ssh]test proxy$ 

別ファイルの関数を呼び出す

call.s
.text
.global _start
_start:
    call func#f.sのfuncの呼び出し
    mov $1,%eax
    mov $2,%ebx
    int $0x80
f.s
.text
.global func
func:
    mov $0x04,%eax
    mov $0x01,%ebx
    mov $msg,%ecx
    mov $0x07,%edx
    int $0x80
    ret
.data
msg: .asciz "hello\n"
bash
0:[ssh]test proxy$ gcc -m32 -nostdlib call.s  f.s -o call
0:[ssh]test proxy$ ./call 
hello
2:[ssh]test proxy$ 

整理

syscall.inc
.equ sys_exit,1
.equ sys_write,4 
util.inc
.include "syscall.inc"
Exit:#プログラムを終了
    mov %eax,%ebx
    mov $sys_exit,%eax
    int $0x80
Write:
    mov %eax,%ecx
    mov $sys_write,%eax
    mov %ebx,%edx
    mov $0x1,%ebx
    int $0x80
    ret 
run.s
.include "util.inc"
.text
.global _start
_start:
    mov $msg,%eax
    mov $len,%ebx
    call Write
    mov $len,%eax
    call Exit
.data
msg:    .asciz "hello world!\n"
.equ len, .-msg

少し長くなりましたがやっていることはrun.sでWrite呼び出しをしてmsgのサイズを引数にExit呼び出しをしているだけです。
.equ,.includeはc言語でいうと#define,#includeのような感じです。
ftp://ftp.gnu.org/old-gnu/Manuals/gas/html_chapter/as_7.html#SEC72を参考にしました。

bash
0:[ssh]test proxy$ gcc -m32 -nostdlib run.s -o run
0:[ssh]test proxy$ ./run
hello world!
14:[ssh]test proxy$ 

出力結果を見るとhello world!と返り値が表示されています。
返り値に注目すると、run.sファイルの"hello world!\n"は13byteに対して14という値が帰ってきています。おそらく.ascizは文字列の最後に\0を入れてくれてそうです。(ftp://ftp.gnu.org/old-gnu/Manuals/gas/html_chapter/as_7.html#SEC72にもThe "z" in `.asciz' stands for "zero".と書いてありました。)。
これを利用することでWrite呼び出しの引数を2つから1つに減らせそうです。

Write改良

run.s
.include "util.inc"
.text
.global _start
_start:
    mov $msg,%eax
    call Write 
    call Exit #Writeはeaxに文字列の長さを返すのでそのままExitを呼ぶ
.data
msg:    .asciz "ok\n"
.equ len, .-msg
util.inc
.include "syscall.inc"
Exit:#プログラムを終了
    mov %eax,%ebx
    mov $sys_exit,%eax
    int $0x80
Write:#文字列を出力
    mov %eax,%ecx
    call Strlen
    mov %eax,%edx
    mov $sys_write,%eax
    mov $0x1,%ebx
    int $0x80
    ret 
Strlen:#文字列の長さを返す
    mov %eax,%edi
    push %eax
    xor %eax,%eax
    mov $0xFFFF,%ecx
    repne scasb
    pop %ecx
    sub %ecx,%edi
    mov %edi,%eax
    ret 

Writeを改良したことによりeaxレジスタだけでWrite呼び出しができるようになりました。
Write呼び出しの中でStrlenを呼び出して文字列の長さを求めています。Strlenのはhttp://softwaretechnique.jp/OS_Development/Tips/IA32_Instructions/SCAS.htmlを参考にしました.図がわかりやすかったので是非見てみてください。

bash
0:[ssh]test proxy$ gcc -m32 -nostdlib run.s -o run
0:[ssh]test proxy$ ./run
ok
4:[ssh]test proxy$ 

コマンドライン引数からhelloする

c言語ではコマンドライン引数にはargc,とargvでアクセスできるのでアセンブラでもアクセスする方法があるはずです。
https://www.mztn.org/lxasm/asm06.htmlを参考にさせてもらいました。

run.s
.include "util.inc"
.text
.global _start
_start:
    pop %ebx#argc
    xor %ecx,%ecx
    pop %eax#argv[0]
    inc %ecx
    call Write
    call Newline
    cmp %ecx,%ebx
    je end
loop:
    pop %eax
    inc %ecx
    call Write
    call Newline
    cmp %ecx,%ebx
    jne loop
end:
    xor %eax,%eax
    call Exit
util.inc
.include "syscall.inc"
Exit:#プログラムを終了
    mov %eax,%ebx
    mov $sys_exit,%eax
    int $0x80
Write:#文字列を出力
    push %ecx
    push %ebx
    mov %eax,%ecx
    call Strlen
    mov %eax,%edx
    mov $sys_write,%eax
    mov $0x1,%ebx
    int $0x80
    pop %ebx
    pop %ecx
    ret 
Strlen:#文字列の長さを返す
    mov %eax,%edi
    push %eax
    xor %eax,%eax
    mov $0xFFFF,%ecx
    repne scasb
    pop %ecx
    sub %ecx,%edi
    mov %edi,%eax
    ret 
Writechar:#1byte出力
    push %ebx
    push %ecx
    push %eax
    mov $sys_write,%eax
    mov $1,%ebx
    mov $1,%edx
    mov %esp,%ecx
    int $0x80
    pop %eax
    pop %ecx
    pop %ebx
    ret 
Newline:#改行
    mov $0x0a,%al
    call Writechar
    ret

変更点としてはutil.incに1byteの文字を出力する関数と改行する関数を整理し、これまでレジスタを関数の中で保存していなかったので保存するように変更しました。

bash
0:[ssh]test proxy$ gcc -m32 -nostdlib run.s -o run
0:[ssh]test proxy$ ./run hoge huga
./run
hoge
huga
0:[ssh]test proxy$ 

感想

ファイルサイズが大きくなってきたので中断しました。
アセンブラは小さい部品を使用してプログラムを書いているという感じがして、楽しいです。

5
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
drib__

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
5
Help us understand the problem. What is going on with this article?