アセンブリと仲良くなりたい
アセンブリと仲良くなりたい
この記事は群馬高専アドベントカレンダー2022の22日目の記事になります。
目次
自己紹介
- NITGC 5J
- なまちゃ
- Twitter: @namacha_411
いろんなプログラミング言語に手を出すのと競プロが好きです。 浅くいろんなものに手を出してます。最近は音楽とプラモデルにはまってます。
アセンブリ難しくない?
この頃とても便利な高レベル言語がたくさんあり、アセンブリといった低レベル言語を使う機会はあまりなくなりました。 しかし、弊学J科のマイコンの授業ではアセンブリの読み書きができるようにならなければいけません。 そのうえ学校の実行環境は特殊なソフトとマイコンを使うのであまり手軽ではなく難しいものとなっています。 情報系の学生にとってこのような状況は意外と多いのではないでしょうか
家でもアセンブリを読み書き・実行できると便利だと思いこの記事を書きました。
形式とかアーキテクチャとかについてはまだよく調べられてないので適当ですみません
アセンブリ読みたい
c言語からアセンブリ
gcc
ではc言語からアセンブリの生成ができます。
gcc
のほかにもzig cc
やclang
でもできるらしいです。
コンパイルオプションとして-S
を追加します。
標準ではAT&T形式
のアセンブリが生成され、追加でオプションを追加することでAT&T形式
とintel形式
を変えることができるらしいです。
例として、
gcc -S test.c
を実行するとtest.s
が生成されます。
このtest.s
はこのような内容になっています。
そこそこ長いので折りたたみます
test.c
のソースコード
int main() { return 0; }
出力結果
.text .def @feat.00; .scl 3; .type 0; .endef .globl @feat.00 .set @feat.00, 0 .file "test.c" .def main; .scl 2; .type 32; .endef .globl main # -- Begin function main .p2align 4, 0x90 main: # @main .seh_proc main # %bb.0: pushq %rbp .seh_pushreg %rbp subq $48, %rsp .seh_stackalloc 48 leaq 48(%rsp), %rbp .seh_setframe %rbp, 48 .seh_endprologue callq __main movl $0, -4(%rbp) xorl %eax, %eax addq $48, %rsp popq %rbp retq .seh_endproc # -- End function .addrsig
実行ファイルからアセンブリ
objdump
コマンドを使用することで実行ファイルからアセンブリの生成ができます。
-d
オプションを使うことでdisassembleができます。
windowsでもこのコマンドを使用することができますが、
出力されるアセンブリがとても読みにくいため環境があればlinuxがおすすめです。
Linux 5.15.79.1-microsoft-standard-WSL2
での実行例を下にまとめます。
だいぶ長いので折りたたみます
test.c
元のC言語ファイル
int main() { return 0; }
コンパイルして実行ファイルにし、それをディスアセンブルする。
gcc test.c objdump -d ./a.out
出力結果
./a.out: file format elf64-x86-64 Disassembly of section .init: 0000000000001000 <_init>: 1000: f3 0f 1e fa endbr64 1004: 48 83 ec 08 sub $0x8,%rsp 1008: 48 8b 05 d9 2f 00 00 mov 0x2fd9(%rip),%rax # 3fe8 <__gmon_start__@Base> 100f: 48 85 c0 test %rax,%rax 1012: 74 02 je 1016 <_init+0x16> 1014: ff d0 call *%rax 1016: 48 83 c4 08 add $0x8,%rsp 101a: c3 ret Disassembly of section .plt: 0000000000001020 <.plt>: 1020: ff 35 a2 2f 00 00 push 0x2fa2(%rip) # 3fc8 <_GLOBAL_OFFSET_TABLE_+0x8> 1026: f2 ff 25 a3 2f 00 00 bnd jmp *0x2fa3(%rip) # 3fd0 <_GLOBAL_OFFSET_TABLE_+0x10> 102d: 0f 1f 00 nopl (%rax) Disassembly of section .plt.got: 0000000000001030 <__cxa_finalize@plt>: 1030: f3 0f 1e fa endbr64 1034: f2 ff 25 bd 2f 00 00 bnd jmp *0x2fbd(%rip) # 3ff8 <__cxa_finalize@GLIBC_2.2.5> 103b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1) Disassembly of section .text: 0000000000001040 <_start>: 1040: f3 0f 1e fa endbr64 1044: 31 ed xor %ebp,%ebp 1046: 49 89 d1 mov %rdx,%r9 1049: 5e pop %rsi 104a: 48 89 e2 mov %rsp,%rdx 104d: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp 1051: 50 push %rax 1052: 54 push %rsp 1053: 45 31 c0 xor %r8d,%r8d 1056: 31 c9 xor %ecx,%ecx 1058: 48 8d 3d ca 00 00 00 lea 0xca(%rip),%rdi # 1129 <main> 105f: ff 15 73 2f 00 00 call *0x2f73(%rip) # 3fd8 <__libc_start_main@GLIBC_2.34> 1065: f4 hlt 1066: 66 2e 0f 1f 84 00 00 cs nopw 0x0(%rax,%rax,1) 106d: 00 00 00 0000000000001070 <deregister_tm_clones>: 1070: 48 8d 3d 99 2f 00 00 lea 0x2f99(%rip),%rdi # 4010 <__TMC_END__> 1077: 48 8d 05 92 2f 00 00 lea 0x2f92(%rip),%rax # 4010 <__TMC_END__> 107e: 48 39 f8 cmp %rdi,%rax 1081: 74 15 je 1098 <deregister_tm_clones+0x28> 1083: 48 8b 05 56 2f 00 00 mov 0x2f56(%rip),%rax # 3fe0 <_ITM_deregisterTMCloneTable@Base> 108a: 48 85 c0 test %rax,%rax 108d: 74 09 je 1098 <deregister_tm_clones+0x28> 108f: ff e0 jmp *%rax 1091: 0f 1f 80 00 00 00 00 nopl 0x0(%rax) 1098: c3 ret 1099: 0f 1f 80 00 00 00 00 nopl 0x0(%rax) 00000000000010a0 <register_tm_clones>: 10a0: 48 8d 3d 69 2f 00 00 lea 0x2f69(%rip),%rdi # 4010 <__TMC_END__> 10a7: 48 8d 35 62 2f 00 00 lea 0x2f62(%rip),%rsi # 4010 <__TMC_END__> 10ae: 48 29 fe sub %rdi,%rsi 10b1: 48 89 f0 mov %rsi,%rax 10b4: 48 c1 ee 3f shr $0x3f,%rsi 10b8: 48 c1 f8 03 sar $0x3,%rax 10bc: 48 01 c6 add %rax,%rsi 10bf: 48 d1 fe sar %rsi 10c2: 74 14 je 10d8 <register_tm_clones+0x38> 10c4: 48 8b 05 25 2f 00 00 mov 0x2f25(%rip),%rax # 3ff0 <_ITM_registerTMCloneTable@Base> 10cb: 48 85 c0 test %rax,%rax 10ce: 74 08 je 10d8 <register_tm_clones+0x38> 10d0: ff e0 jmp *%rax 10d2: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1) 10d8: c3 ret 10d9: 0f 1f 80 00 00 00 00 nopl 0x0(%rax) 00000000000010e0 <__do_global_dtors_aux>: 10e0: f3 0f 1e fa endbr64 10e4: 80 3d 25 2f 00 00 00 cmpb $0x0,0x2f25(%rip) # 4010 <__TMC_END__> 10eb: 75 2b jne 1118 <__do_global_dtors_aux+0x38> 10ed: 55 push %rbp 10ee: 48 83 3d 02 2f 00 00 cmpq $0x0,0x2f02(%rip) # 3ff8 <__cxa_finalize@GLIBC_2.2.5> 10f5: 00 10f6: 48 89 e5 mov %rsp,%rbp 10f9: 74 0c je 1107 <__do_global_dtors_aux+0x27> 10fb: 48 8b 3d 06 2f 00 00 mov 0x2f06(%rip),%rdi # 4008 <__dso_handle> 1102: e8 29 ff ff ff call 1030 <__cxa_finalize@plt> 1107: e8 64 ff ff ff call 1070 <deregister_tm_clones> 110c: c6 05 fd 2e 00 00 01 movb $0x1,0x2efd(%rip) # 4010 <__TMC_END__> 1113: 5d pop %rbp 1114: c3 ret 1115: 0f 1f 00 nopl (%rax) 1118: c3 ret 1119: 0f 1f 80 00 00 00 00 nopl 0x0(%rax) 0000000000001120 <frame_dummy>: 1120: f3 0f 1e fa endbr64 1124: e9 77 ff ff ff jmp 10a0 <register_tm_clones> 0000000000001129 <main>: 1129: f3 0f 1e fa endbr64 112d: 55 push %rbp 112e: 48 89 e5 mov %rsp,%rbp 1131: b8 00 00 00 00 mov $0x0,%eax 1136: 5d pop %rbp 1137: c3 ret Disassembly of section .fini: 0000000000001138 <_fini>: 1138: f3 0f 1e fa endbr64 113c: 48 83 ec 08 sub $0x8,%rsp 1140: 48 83 c4 08 add $0x8,%rsp 1144: c3 ret
アセンブリ書きたい
gcc
ではアセンブリを.s
ファイルとして認識し、
gcc test.s
とするとアセンブリファイルから実行ファイルを生成してくれます。
これもgcc
のほかにもzig cc
やclang
でもできるらしいです。
これは余談なのですが、gcc
では.c
ファイル、.h
ファイルをコンパイルできるのは有名ですが、ほかのファイルを渡した場合どうなるのでしょう。
そもそもgcc
のcc
はC Compilerの略ではなく、Compiler Collectionのcc
らしいです。
そして、公式ホームページによると、
The GNU Compiler Collection includes front ends for C, C++, Objective-C, Fortran, Ada, Go, and D, as well as libraries for these languages (libstdc++,...)
と、いろいろな言語をサポートしているようです。(c言語以外で使ったことないですが、、、)
これもそこそこ長いので折りたたみます
適当にフィボナッチ数列のrdi
項目を求めるプログラムtest.s
を書きました。
.intel_syntax noprefix .globl main fib: mov rdx, 0 mov rcx, 1 fib_loop: mov rsi, 0 add rsi, rdx add rsi, rcx mov rdx, rcx mov rcx, rsi sub rdi, 1 cmp rdi, 0 jne fib_loop mov rax, rsi ret main: mov rdi, 5 call fib ret
gcc test.s
./a.out
を実行すると生成されたa.out
が実行されます。
(windowsならa.exe?)
出力結果は
echo $?
fish
echo $status
echo $LASTEXITCODE
で見ることができます。
さいごに
アセンブリと仲良くなりましょう!!
次は、「こひむ」さんの「高専プロコン失敗談」です。