Arm64アセンブラの実行

hello.s

.text
        .global _start
_start:
        mov     x2,     #13     // x2 length
        adr     x1,     msg     // x1 string address
        mov     x0,     #1      // x0 stdout
        mov     x8,     #64
        svc     #0              // sys_write
        mov     x0,     xzr
        mov     x8,     #93
        svc     #0              // exit
msg:
        .asciz  "hello, world\n"

asでアセンブルしてオブジェクトファイルを作り、リンカを使って実行ファイルを作成する
$ ld -o hello hello.o
$ ./hello
hello, world

ファイルサイズ
$ ls -l hello*
-rwxrwxr-x 1 vagrant vagrant 936 Jan 19 18:14 hello
-rw-rw-r– 1 vagrant vagrant 816 Jan 19 18:14 hello.o
-rw-rw-r– 1 vagrant vagrant 386 Jan 19 17:48 hello.s

シンボル情報の表示
$ nm hello
00000000004100a6 T __bss_end__
00000000004100a6 T _bss_end__
00000000004100a6 T __bss_start
00000000004100a6 T __bss_start__
00000000004100a6 T _edata
00000000004100a8 T __end__
00000000004100a8 T _end
0000000000400098 t msg
0000000000400078 T _start

objdumpによる逆アセンブル
$ objdump -d hello

hello: file format elf64-littleaarch64

Disassembly of section .text:

0000000000400078 <_start>:
400078: d28001a2 mov x2, #0xd // #13
40007c: 100000e1 adr x1, 400098
400080: d2800020 mov x0, #0x1 // #1
400084: d2800808 mov x8, #0x40 // #64
400088: d4000001 svc #0x0
40008c: aa1f03e0 mov x0, xzr
400090: d2800ba8 mov x8, #0x5d // #93
400094: d4000001 svc #0x0

0000000000400098 :
400098: 6c6c6568 .word 0x6c6c6568
40009c: 77202c6f .word 0x77202c6f
4000a0: 646c726f .word 0x646c726f
4000a4: Address 0x00000000004000a4 is out of bounds.

16進数、32ビットなどで出力も可

$ strings hello
hello, world
hello.o
__bss_start__
__bss_end__
__bss_start
__end__
_edata
_end
.symtab
.strtab
.shstrtab
.text

includeした場合

.include    "stdio.s"
.text
.global _start
_start:
    adr     x0, msg
    bl      OutAsciiZ
    bl      Exit
msg:
    .asciz  "Hello Hpscript\n"

$ as -o sample.o sample.s
$ ld -o sample sample.o
$ ./sample

ARM64とアセンブラ

ARM64 base instructions
https://developer.arm.com/documentation/ddi0602/2023-12/Base-Instructions?lang=en

汎用レジスタの数が30個、汎用レジスタのサイズが64ビット
よく使われる命令
– mov : レジスタ間のコピー
– ldr : メモリからレジスタに読み出し
– b : 無条件分岐
– bl : サブルーチン呼び出し
– add : 加算
– cmp : 比較
– str : レジスタからメモリへ格納
– ldb : スタックから取り出し
– stp : スタックへ格納
– adrp : レジスタにアドレスを設定
– cbz : 比較して0なら分岐
– b.eq : 等しければ分岐
– ret : サブルーチンから戻る
– sub : 減算
– b.ne : 異なれば分岐
– adr : レジスタにアドレスを設定
– cbnz : 比較して非0なら分岐
– and : ビット積

スタックへの複数レジスタの退避/復帰が2つのレジスタに限定されたLDP/STPに変わっている
ARM64では全ての命令を32ビットで表現

### メモリ、バイト、レジスタ、エンディアン
アセンブリでは2進数やエンディアンという概念を知っておく必要がある
8ビットが1バイト
アナログとは中間も扱うもので、デジタルは0と1のみ
10進数より16進数の方が使われる
補数(complement)を使うことで演算処理が早くなる
64ビットCPUではレジスタから16エクサバイトのメモリを指定できる
64bit レジスタはbytes, Half word, Word, Double Wordを扱える

### エンディアン
レジスタの内容の小さい側をメモリアドレスの小さい側に格納するのをリトルエンディアンと呼ぶ
x86, x86064, arm64はリトルエンディアン、PowerPCはバイエンディアン

Ubuntu(arm64)でassemblerの実行

NASMをインストールする
$ sudo apt install nasm

$ uname -sr
Linux 5.15.0-76-generic

$ nasm -v
NASM version 2.15.05

$ as -v
GNU assembler version 2.38 (aarch64-linux-gnu) using BFD version (GNU Binutils for Ubuntu) 2.38

$ ld -v
GNU ld (GNU Binutils for Ubuntu) 2.38

$ gcc –version
gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0

hello.s

.text
        .global _start
_start:
        mov     x2,  #13    // x2  length
        adr     x1,  msg    // x1  string address
        mov     x0,  #1     // x0  stdout
        mov     x8,  #64
        svc     #0          // sys_write
        mov     x0,  xzr
        mov     x8,  #93
        svc     #0          // exit
msg:
        .asciz  "hello, world\n"

$ as -o hello.o hello.s
$ ld -o hello hello.o
$ ./hello
hello, world

ちなみに、CPUアーキテクチャがx86ではないので x86-64で記述するとエラーになる。

### x86-64とarm64の違い
x86 x64 は CISC(Complex Instruction Set Computer) と呼ばれる命令セット
「複雑な処理」を「少ない命令」で実行しようとする思想です。

arm は RISC(Reduced Instruction Set Computer) と呼ばれる命令セット

x64は高速でパワフル、PCでよく使用される
ARM64は低電力でモバイルデバイスに向いている

アセンブラ入門

### 基礎用語
アセンブラは1行1件で記述され、最大で4つのフィールドを持つ
コメント、オペランド、ニーモニック、名前
ニーモニックは、CPU命令と、ディレクティブ(アセンブラに対する支持)がある。

### ディレクティブ
mov ax, 1234H

cpu命令、レジスタ名は小文字
ディレクティブやラベルは大文字

B 2進数、O,Q 8進数、D 10進数、H 16進数
16進数は0を先頭につける

MSG1 DB ‘ABCDE’

@@: ラベル
@B 直近の先頭行側の@@ラベル参照
@F 直近の最終行側の@@ラベル参照

システムマクロ
ローカルラベル機能がある

### 機械語
CPUの命令のことを機械語(マシン語)という
メモリから1バイトの命令コードをCPU内の実行ユニット部が読み込むと命令デコーダによって解析され、命令タイプが決定する
e.g. 01001000 という命令が来たら 8086CPUでは AXレジスタの内容を1だけ減じる という仕事をする
2進数は覚えにくいので、16進数で4ビットごとに1桁として数え、0~Fで表現する
16ビットは2バイト

48という16進数の数字からAXレジスタから1減じる という意味を読み取ることができないので、機械語と1対1に対応しながら表意的な名前を与える方法が考え出され、それがアセンブラというプログラミング言語
dec ax という記述

mov ax, MYDT1 -> A1 0101
add ax,MYDT2 -> 03 06 0103
mov MYDT3, ax -> A3 0105

MYDT1 DW 10
MYDT2 DW 20
MYDT3 DW 0

より自然な表記が可能となり、C, C++, Javaなどの高級言語が生まれた
Cの場合は、Cコンパイラが機械語に変換
アセンブラソースプログラムが機械語に変換