#はじめに
@orleika さんの書かれた記事『アセンブリでQuine』のパクリです。
も少しシンプルにできそうだったのでその方法について解説します。
環境は Ubuntu 18.10 を使用しています。
#コードとビルド、実行結果
$ uname -a
Linux ubuntu 4.18.0-13-generic #14-Ubuntu SMP Wed Dec 5 09:04:24 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
$ gcc --version
gcc (Ubuntu 8.2.0-7ubuntu1) 8.2.0
Copyright (C) 2018 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.
$ as --version
GNU assembler (GNU Binutils for Ubuntu) 2.31.1
Copyright (C) 2018 Free Software Foundation, Inc.
This program is free software; you may redistribute it under the terms of
the GNU General Public License version 3 or later.
This program has absolutely no warranty.
This assembler was configured for a target of `x86_64-linux-gnu'.
$ ld --version
GNU ld (GNU Binutils for Ubuntu) 2.31.1
Copyright (C) 2018 Free Software Foundation, Inc.
This program is free software; you may redistribute it under the terms of
the GNU General Public License version 3 or (at your option) a later version.
This program has absolutely no warranty.
$ ls -l quine.S
-rw-r--r-- 1 fujita fujita 107 Jan 12 05:07 quine.S
$ cat -n quine.S
1 mov $1,%eax
2 mov $1,%edi
3 mov $m,%esi
4 mov $107,%edx
5 syscall
6 mov $60,%eax
7 dec %edi
8 syscall
9 m:.incbin __FILE__
$ gcc -nostdlib quine.S -o quine -no-pie -Wl,-e0x401000 -s
$ file quine
quine: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, BuildID[sha1]=72f2314cad9072b32e08b9ac44ff52d34261921a, stripped
$ ./quine
mov $1,%eax
mov $1,%edi
mov $m,%esi
mov $107,%edx
syscall
mov $60,%eax
dec %edi
syscall
m:.incbin __FILE__
$ diff quine.S <(./quine)
$
#解説
キモはソースコードの最後の行、
m:.incbin __FILE__
だけです。
ソースコードのファイル名はパクリ元の記事に倣い quine.S となっています。ファイル名の拡張子に大文字の '.S' を持つアセンブラのソースコードは gcc コマンドを使用してアセンブルするとデフォルトの動作としてアセンブルの前に C のプリプロセッサで前処理が行われます。その際に __FILE__ はソースコードのファイル名に置き換わります。
$ gcc -E quine.S
# 1 "quine.S"
# 1 "<built-in>"
# 1 "<command-line>"
# 31 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 32 "<command-line>" 2
# 1 "quine.S"
mov $1,%eax
mov $1,%edi
mov $m,%esi
mov $107,%edx
syscall
mov $60,%eax
dec %edi
syscall
m:.incbin "quine.S"
$
続くアセンブルの際に、疑似命令 .incbin は引数のファイル名の内容を直接バイナリ列としてインクルードします。結果、ソースコードの内容全体が .ascii 疑似命令で展開されるのと同じにアセンブルされます。
mov $1,%eax
mov $1,%edi
mov $m,%esi
mov $107,%edx
syscall
mov $60,%eax
dec %edi
syscall
m:.ascii "mov $1,%eax\nmov $1,%edi\nmov $m,%esi\nmov $107,%edx\nsyscall\nmov $60,%eax\ndec %edi\nsyscall\nm:.incbin __FILE__\n"
そんなわけで、あとはそれをシステムコールで出力し終了しているだけです。やってることはハローワールドと変わりません。
#おわりに
おわりです。
追記(2019/01/15)
Ubuntu 16.04 上でビルドしてみたところ決め打ちしてたエントリアドレスがテキストセクションの開始と一致せず動作しなかった。折角なので手抜き箇所もついでに修正。
$ cat -n quine.S
1 .globl _start
2 _start:mov $1,%eax
3 mov $1,%edi
4 lea m(%rip),%rsi
5 mov $n-m,%edx
6 syscall
7 mov $60,%eax
8 dec %edi
9 syscall
10 m:.incbin __FILE__
11 n:
$ gcc -nostdlib quine.S -o quine -s
$ file quine
quine: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=4d7ac2448781a64ea8f82027fa9f16181850ba49, stripped
$ ./quine
.globl _start
_start:mov $1,%eax
mov $1,%edi
lea m(%rip),%rsi
mov $n-m,%edx
syscall
mov $60,%eax
dec %edi
syscall
m:.incbin __FILE__
n:
$ diff quine.S <(./quine)
$
追記(2019/03/07)
NASM に移植しました。
global _start
_start:mov eax,1
mov edi,1
lea rsi,[m]
mov edx,n-m
syscall
mov eax,60
dec edi
syscall
m:incbin __FILE__
n:
global _start
_start:mov eax,4
mov ebx,1
lea ecx,[m]
mov edx,n-m
int 80h
mov eax,1
dec ebx
int 80h
m:incbin __FILE__
n: