Why not login to Qiita and try out its useful features?

We'll deliver articles that match you.

You can read useful information later.

5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

NetBSDAdvent Calendar 2017

Day 1

アセンブリ言語で様々なアーキテクチャ向けのHello,Worldプログラムを書こう!という話。

Posted at

NetBSD Advent Calendar 2017 1日目の記事です。今日はアセンブリ言語でのHello,World.プログラムについて書こうと思います。

アセンブリ言語でのHello,World.プログラム

NetBSDをインストールすると、 /usr/share/examples/asm というディレクトリがあるということに気がつきます。
中身は何やらアセンブリ言語のプログラムのようです。

$ /usr/share/examples/asm
$ ls -R
Makefile.inc  README        hello/

./hello:
Makefile   powerpc.s

READMEには「ハハッ(甲高い声) このディレクトリには様々なプラットフォーム向けのアセンブリ言語サンプルが入っているよ!」(意訳)と書かれているようですが、どうみてもPowerPCのサンプルしか存在していない状態です...。

$ cat README
$NetBSD: README,v 1.2 2011/11/27 09:07:11 skrll Exp $

This directory contains example programs written in assembly language
for a variety of platforms.  They are intended to illustrate the
specific details of how to write assembly code on a given platform;
they are not supposed to teach assembly (although they might have this
side-effect).

If you want to build one of these example programs, you can "cp -rf"
the corresponding directory anywhere else where you have write
permissions and then issue a "make" within the directory.

hello/powerpc.s の中身はこんな感じ。write(2)システムコールを直接呼び出す形で"Hello, world!"を表示するという動作になっています。
とはいえ、Hello,World.を様々なプラットフォーム向けに最小限のアセンブリ言語で書くという、まさにNetBSD向けの題材にも思えます。

.section ".note.netbsd.ident", "a"
        # This ELF section is used by the kernel to determine, among other
        # things, the system call interface used by the binary.
        #
        # See http://www.netbsd.org/docs/kernel/elf-notes.html for more
        # details.

        .int 7                  # Length of the OS name field below.
        .int 4                  # Length of the description field below.
        .int 0x01               # The type of the note: NetBSD OS Version.
        .ascii  "NetBSD\0\0"    # The OS name, padded to 8 bytes.
        .int 0x23b419a0         # The description value; 5.99.56.

# ------------------------------------------------------------------------

.section ".data"

message:
        .ascii "Hello, world!\n"
        .set MESSAGE_SIZE, . - message

# ------------------------------------------------------------------------

.section ".text"

        .balign 4

        .globl _start
        .type _start, @function
_start:
        # write(STDOUT_FILENO, message, MESSAGE_SIZE)
        li      %r0, 4                  # r0: write(2) syscall number.
        li      %r3, 1                  # r3: first argument.
        addis   %r4, %r0, message@h     # r4: second argument.
        ori     %r4, %r4, message@l
        li      %r5, MESSAGE_SIZE       # r5: third argument.
        sc

        # exit(EXIT_SUCCESS)
        li      %r0, 1                  # r0: exit(2) syscall number.
        li      %r3, 0                  # r3: first argument.
        sc

    .size _start, . - _start

i386アーキテクチャでのアセンブリ言語によるHello,World.

某所で「大熱血!アセンブラ入門 読書会」のお手伝いをさせていだいているという経緯もあり、自分の分かる範囲でサンプルを増やしたいところです。
と思っていたら、かなり前にi386アーキテクチャ向けに上記のプログラムを移植したのを忘れていました...。

i386アーキテクチャ向けのHello,World.は以下になります。やっていることは write(2) で"Hello,World."を出力し、 exit(3) で終了しているだけです。

 .section ".note.netbsd.ident", "a"
        # This ELF section is used by the kernel to determine, among other
        # things, the system call interface used by the binary.
        #
        # See http://www.netbsd.org/docs/kernel/elf-notes.html for more
        # details.

        .int 7                  # Length of the OS name field below.
        .int 4                  # Length of the description field below.
        .int 0x01               # The type of the note: NetBSD OS Version.
        .ascii  "NetBSD\0\0"    # The OS name, padded to 8 bytes.
        .int 0x23b419a0         # The description value; 5.99.56.

# ------------------------------------------------------------------------

.section ".data"

message:
        .ascii "Hello, world!\n"
        .set MESSAGE_SIZE, . - message

# ------------------------------------------------------------------------

.section ".text"

        .balign 4

        .globl _start
        .type _start, @function
_start:
        # write
        movl    $MESSAGE_SIZE, 0xc(%esp)
        movl    $message, 0x8(%esp)
        movl    $0x1, 0x4(%esp)
        mov     $0x4, %eax
        int     $0x80
        # exit(EXIT_SUCCESS)
        movl    $0x0,(%esp)
        mov     $0x1,%eax
        int     $0x80
        ret

    .size _start, . - _start

実行例は以下になります。

$ uname -a 
NetBSD vmnbsd71 7.1 NetBSD 7.1 (GENERIC.201703111743Z) amd64
$ gcc -m32 -nostdlib i386.s 
$ file a.out
a.out: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, for NetBSD 5.99.56, not stripped
$ ./a.out 
Hello, world!

amd64アーキテクチャでも動かしたい!

これで今日のAdvent Calendarに間にあわせることができましたぞ、と言いたいところですが、64bit環境(amd64)でも動かしたいのが人情というものです。

(なにも考えずに)そのままアセンブルして動かすと、もちろんクラッシュします。rip レジスタの指しているアドレスは 0x400100_start の開始アドレスなので、実行即クラッシュという状態ですね...。
なんでクラッシュするの→64bitでの引数渡しはスタックじゃなくてレジスタを使うから、この呼び出し方だと変なレジスタに変な値が入った状態でシステムコールが呼ばれているからだよね...という話なので、64bit環境向けにも別途Hello,World.プログラムを作成したいものです。

$ gcc -nostdlib i386.s
$ file a.out
a.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, for NetBSD 5.99.56, not stripped
$ gdb a.out
GNU gdb (GDB) 7.7.1
...
(gdb) run
Starting program: /home/fpig/work/advcal/2017/1201/fpig_sample/hello/nbsd/a.out 

Program received signal SIGSEGV, Segmentation fault.
0x0000000000400100 in _start ()
(gdb) bt
#0  0x0000000000400100 in _start ()
(gdb) info register
rax            0x0      0
rbx            0x7f7fffffffe0   140187732541408
rcx            0x400100 4194560
rdx            0x0      0
rsi            0x0      0
rdi            0x0      0
rbp            0x0      0x0
rsp            0x7f7fffffdb88   0x7f7fffffdb88
r8             0x0      0
r9             0x0      0
r10            0x7f7ff6c83a0a   140187577891338
r11            0x202    514
r12            0x74c968 7653736
r13            0x74a008 7643144
r14            0x74d188 7655816
r15            0x74d208 7655944
rip            0x400100 0x400100 <_start>
eflags         0x10202  [ IF RF ]
cs             0x47     71
ss             0x3f     63
ds             0x3f     63
es             0x3f     63
fs             0x0      0
gs             0x0      0
(gdb) 

フィーリングでシステムコールの呼び出し方を調べる

大熱血!アセンブラ入門にある熱血バイナリアン十訓の中に、「わからなくても気にせず読め!」という項目があります。
12月1日も終わりつつあるなか、NetBSD-amd64環境でアセンブリ言語からシステムコールを呼び出す方法を今から(!)調べるのはなかなか無茶がありますが、熱血バイナリアン十訓の教えに従い、がんばって調べてみます。

exit(3)で試してみる。

話を簡単にするため、引数を持たない exit(3) を64bit環境で呼び出すという例で考えてみます。
まずはi386向けのアセンブリで exit(3) を呼ぶだけのプログラムにしてみます。

.section ".text"

        .balign 4

        .globl _start
        .type _start, @function
_start:
        # exit(EXIT_SUCCESS)
        movl    $0x0,(%esp)
        mov     $0x1,%eax
        int     $0x80
        ret

もちろんクラッシュします。

$ gcc -nostdlib i386_exit.s 
$ file a.out
a.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, for NetBSD 5.99.56, not stripped
$ gdb ./a.out
GNU gdb (GDB) 7.7.1
...
(gdb) rn
Target exec does not support this command.
(gdb) run
Starting program: /home/fpig/work/advcal/2017/1201/fpig_sample/hello/nbsd/a.out 

Program received signal SIGSEGV, Segmentation fault.
0x0000000000400100 in _start ()

ではどうやって exit(3) を呼び出すのだろうという話ですが、ここは一つ、実際に exit(3) を読んでいる箇所から調べてみましょう。
libcのライブラリであれば必ず exit(3) を呼び出している箇所があるはずなので、それを手がかりにしてみます。

$ objdump -d /lib/libc.so
...
000000000010e6c0 <_Exit>:
  10e6c0:       b8 01 00 00 00          mov    $0x1,%eax
  10e6c5:       49 89 ca                mov    %rcx,%r10
  10e6c8:       0f 05                   syscall 
  10e6ca:       c3                      retq   
  10e6cb:       0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)

000000000010e6d0 <_lwp_self>:

なにやらそれっぽい箇所があります。 eax レジスタに入れるのはシステムコール種別のようなので、 esp レジスタが指している(スタック領域として使っているメモリ)に入れていた値を rcx r10 で渡せば良さそうです。
さっそく以下のように書き換えて試してみます。

_start:
        # exit(EXIT_SUCCESS)
        mov     $0x1,%eax
        mov     $0x7,%rcx
        mov     %rcx,%r10
        syscall
        retq

今度はクラッシュせずに動作しました! ただ、確認のため終了ステータスを 7 としたつもりなのですが、 "exited normally"と言われているので、引数の渡し方がまだちょっと間違っているようです。
でもとりあえずは64bit環境でシステムコールが呼べるようになったので、引数の渡し方についてはおいおい調べて行こうと思います。

$ gcc -nostdlib i386_exit.s 
$ file a.out
a.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, for NetBSD 5.99.56, not stripped
$ gdb a.out
GNU gdb (GDB) 7.7.1
...
(gdb) run
Starting program: /home/fpig/work/advcal/2017/1201/fpig_sample/hello/nbsd/a.out 
[Inferior 1 (process 25952) exited normally]

まとめ

NetBSDの /usr/share/examples/asm ディレクトリに置かれている、アセンブリ言語でのHello,World.プログラムについて紹介しました。
様々なアーキテクチャ向けのHello,World.プログラムはNetBSDに向いている題材だと思うので、ぜひサンプルを増やして行きたいところです。

5
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up

Qiita Advent Calendar is held!

Qiita Advent Calendar is an article posting event where you post articles by filling a calendar 🎅

Some calendars come with gifts and some gifts are drawn from all calendars 👀

Please tie the article to your calendar and let's enjoy Christmas together!

5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?