読者です 読者をやめる 読者になる 読者になる

by shigemk2

当面は技術的なことしか書かない

熱血!アセンブラ入門 読書会1 #hotasm

勉強会

人と違うことをすること

P1

熱血バイナリアン十訓

  • まずは読め
  • 楽しんで読め
  • 無理やり読め
  • 勘で読め
  • 暇を見つけて読め
  • 思うままに読め
  • 納得行くまで読め
  • 明日も読め
  • 飽きたら別のを読め
  • わからなくても気にせず読め

基礎技術を知っていれば不足の事態でも自力でなんとかできるという強み

  • どれだけ高レイヤーですごいものを書けるか
  • どれだけ低レイヤーでなんでも書けるか

役に立たない分野のほうが勉強して得する部分がいっぱいある

役に立つ技術はみんな勉強するのでナンバーワンになるのは難しい

はじめに

フィーリング

CPUのアーキテクチャの設計思想や歴史を知る

でもアセンブラは敷居が高い。高すぎる。

というわけで、資料をあまり用意せず調べずにアセンブラを読む

面白いと思ってもらうことが重要

方向性

  • 資料をあまり真面目に調べずになんとなく読む
  • 知識や経験や解析力ではなく、想像力、推測、ノリで読む
  • わからなくても気にせずに読み進める

気楽に読んでもらうことが重要

  • アーキテクチャをきちんと理解すること
  • CPUやアーキテクチャをきちんと勉強する
  • きちんと勉強しないと読んではいけない
  • きちんと理解してから次に進んではいけない

上記のようなことは考えない

気楽に読む

命令を解釈するだけではなく、様々なアプローチから読む

従来のアセンブラの読み方

  1. CPUのドキュメント
  2. レジスタやスタックの詳細な移り変わりを追う
  3. わからない命令が出たら都度ドキュメントで確認
  4. 内部でやっていることを推測

失敗の理由はきちんと読もうとすること。

予備知識無しでアセンブラを読んで勘とフィーリングでなんとなく読む

本書の読み方

「このへんはこう動いているのだろう」というアタリをつけて読む 読み方を工夫する

  • Cのサンプルプログラムをコンパイル、逆アセンブルして対比しながら読む
  • 短いサンプルで読む
  • まとまりごとに処理の目的を意識してブロック単位で読む
  • まず適当に読む
  • わからなくても気にしない
  • 他のCPUと比較する(他の本とは一線を画す部分)
  • 手を動かして理解を深める

普通にやるとページ数が多すぎて挫折するので、勉強会を開いた

C言語のサンプルは非常に簡単

アセンブラを知るために、C言語で簡単なプログラムを書いて、逆アセンブルしてアセンブラを確認してみる

多角的な方法で理解する

  • 一字一句の解析
  • 他のCPUのアセンブラと比較
  • サンプルプログラムの書き換え
  • オブジェクト解析

目的を意識する

コードから目的を見るのではなく、目的からコードを見る

「何をしているコードなのか」が予め分かっていれば、コードも読みやすい

そもそも全体として何をやっているか > 一字一句正確に解読する

サンプル

P12

sample/powerpc-elf.d

00fe1580 <call_complex1>:
  fe1580:   94 21 ff f0   stwu    r1,-16(r1)
  fe1584:   7c 08 02 a6   mflr    r0
  fe1588:   90 01 00 14     stw     r0,20(r1)
  fe158c:   38 60 00 fe  li      r3,254
  fe1590:   4b ff fe cd   bl      fe145c <return_arg1>
  fe1594:   38 63 00 01     addi    r3,r3,1
  fe1598:   80 01 00 14     lwz     r0,20(r1)
  fe159c:   7c 08 03 a6   mtlr    r0
  fe15a0:   38 21 00 10     addi    r1,r1,16
  fe15a4:   4e 80 00 20  blr

sample/powerpc-elf.c

int call_complex1()
{
  return return_arg1(0xfe) + 1;
}

アセンブラとプログラムを見比べて、アタリをつけてみる

li r3, 254 ! r3 = 254
addi r3,r3,1 ! r3 = r3 + 1

cf. Blackfin - Wikipedia

最初と最後にスタックの確保と解放をやっているんだろうなあという「アタリ」

推測とノリでやる

この時点で「スタック」ってなんだよって話

00fe1580 <call_complex1>:
  ! スタック準備
  fe1580:   94 21 ff f0   stwu    r1,-16(r1)
  ! スタックへの退避
  fe1584:   7c 08 02 a6   mflr    r0
  fe1588:   90 01 00 14     stw     r0,20(r1)
  ! 引数0xfeの準備
  fe158c:   38 60 00 fe  li      r3,254
  ! 関数return_arg1()の呼び出し
  fe1590:   4b ff fe cd   bl      fe145c <return_arg1>
  ! 戻り値の加工
  fe1594:   38 63 00 01     addi    r3,r3,1
  ! スタックからの復旧
  fe1598:   80 01 00 14     lwz     r0,20(r1)
  fe159c:   7c 08 03 a6   mtlr    r0
  ! スタックの解放
  fe15a0:   38 21 00 10     addi    r1,r1,16
  ! 呼び出し元から戻る
  fe15a4:   4e 80 00 20  blr

推測点

  • スタックを操作しているのはr1
  • r1がスタックポインタ
  • 引数はr3で渡すのか
  • 関数の戻り値もr3に格納されている

本書の構成

P14

第一部 PowerPCをメインにアセンブリを適当に読む→レジスタやスタック利用のパターンを身につける

PowerPC - Wikipedia

Macintoshとかゲーム機とかで使われてるやつ

MIPSを読みつつ、PowerPCと対比する→「アセンブラはなんとなくでも読める」ことを体感する

MIPSアーキテクチャ - Wikipedia

PS1 PS2とかで使われていた

遅延スロットが初見殺しなのでMIPSではなくPowerPCを出したのではないかと。

そしてSHやARMとかいろいろ出してみて、「アセンブラはなんとなくでも(ry」を実感する

「わかりやすいから」という理由でPowerPCとかMIPSを出したのだと思われる。

これらを踏まえて、「きちんと調べてから読む」のではなく、勘と想像で「アセンブラはなんとなく(ry」を実感する

アセンブラはi386だけじゃないっ

第二部はいろいろなCPUのアセンブラを読む

第三部は環境構築

コツを覚えてみよう的なノリで始まる

本書のサポートについて

P16

サポートサイト http://kozos.jp/books/asm/

著者はよくOSCに行くらしい

確実な情報はなく、「こうではなかろうか」という推測でものを書いていることもあるので、不確実な記述もある。

本書の読み進め方

この本は技術書というより読み物。丁寧に読んでもいいし、飛ばし飛ばし読んでもいい。環境構築してガッツリやってもいいし、環境構築せずにソースコードを眺めながら読んでもいいし、ソースコードもなく本をパラパラ読んでもいい。

初心者で読み物として読み進めたい人

順番に読む

初心者でPCに環境を構築して読み進めたい人

P656→P36→順番に読む

ある程度自身のある人

本節→好きなところから

環境構築

遅いPCだとすごく遅い。

newlibをコンパイルする必要はない

binutilとgccがあればとりあえず使える。ガチの組み込み開発の場合はnewlibが必要だけど、ちょっと実験でコンパイルや逆アセンブラをやりたい場合はnewlibは必要ない

CPU

他のメーカーだとMPUとかマイクロプロセッサとか呼ばれる部分だが、厳密に言うとプロセッサとかアーキテクチャと呼ばれるけども、「中央演算装置」としての意味合いとして、CPUという呼称で統一する。

馬鹿でかいコンピュータだとマイクロですらないので、中央演算装置部分はすべてCPUに統一する

サンプルプログラムについて

サンプルプログラムはすべてsample.cで統一する→補遺Aを参照のこと

本書の内容を実践するためにはバイナリエディタや16進電卓などを使う必要がある→補遺Bを参照のこと

目次

第一部

「アセンブラ入門」といえばi386入門書みたいなイメージあるけど、この本はそこから始まらない

PowerPC→MIPS→ARM→SH→H8→i386

アセンブラ入門の場合、ARMとi386がメインになるわけだが、マイナーなCPUを並列で勉強している

第二部

多分作者はRISCが好み

SPARC→PA-RISC→64ビットプロセッサ→32ビットプロセッサ→マイコン→昔ながらのマイコン→CISC→メインフレーム→その他

そしてVAXとかPDP-11とかも出てくる

第三部

環境構築

シミュレータ対応

シミュレータ(タイミングはともかく入出力さえ同じになっていればいい) エミュレータ(タイミングも含めて完全再現)

だいたい800ページ

第一部 基礎編: まずはアセンブラに慣れよう

P35

PowerPC→MIPS→ARM→SH→H8→i386

まずはPowerPC

PowerPCを選んだのは、アセンブラが素直で初心者にも読みやすいと思ったから。

サンプルコード http://kozos.jp/books/asm

いきなりサンプルコードを全部説明しようとすると死ぬので、小出しに説明してく。

  • 何もしない
  • 0を返す
  • 1を返す
void null()
{
  return;
}

int return_zero()
{
  return 0;
}

int return_one()
{
  return 1;
}

コンパイル→逆アセンブルしたもの

00fe1400 <null>:
  fe1400:   4e 80 00 20  blr

00fe1404 <return_zero>:
  fe1404:   38 60 00 00     li      r3,0
  fe1408:   4e 80 00 20  blr

00fe140c <return_one>:
  fe140c:   38 60 00 01     li      r3,1
  fe1410:   4e 80 00 20  blr

クロスコンパイラ 別のCPU用の実行モジュールを作成するためのコンパイラ

  • 開発環境と本番環境が同じなのをセルフ
  • 開発環境と本番環境が違うのをクロス

別の環境でPowerPC用のアセンブラを作った→クロスコンパイル

目的を意識して読む

P39

アセンブラの内容を詳細に説明しません

blrはreturn r3というレジスタに1やら0やらを代入(li)してからblrするというアタリがつく

r3というレジスタに値を代入して、blrしてreturnする

とりあえずliとblrという命令が存在するんだろうなあという推測

  • レジスタ CPUが持っている固定の記憶領域=変数
  • 命令セット(ISA) CPUがもっている命令の種類のこと(徐々にたくさんでてくる)

推測して想像力で読む

アセンブラの読み方を知る

アドレス 機械語が配置されている場所

4e 80 00 20 8桁の16進数は機械語のバイナリコード 実際のメモリ上に命令コードとして配置されるバイト列

0123456789abcdef 16進数(C言語の場合は小文字で書く)

00fe1400 <null>:
  fe1400:   4e 80 00 20  blr

00fe1404 <return_zero>:
  fe1404:   38 60 00 00     li      r3,0
  fe1408:   4e 80 00 20  blr

00fe140c <return_one>:
  fe140c:   38 60 00 01     li      r3,1
  fe1410:   4e 80 00 20  blr
  • 00fe1400 : fe1400: 4e 80 00 20 blr 関数のアセンブラ
  • 00fe1400 関数のアドレス
  • 38 60 00 00 機械語のバイナリ(16進数文字列の羅列)
  • li r3,1 (アセンブラのニーモニック)

ニーモニックは人間に読みやすくしたやつで、実際に機械が読んでいるのはバイナリのほう

バイナリを直接修正したりハンドアセンブルしたりすることも、ある

命令が少なくて単純なので、至極読みやすい

アドレスが4つずつ進んでいるのは機械語が4バイトずつ入っているから。アドレスは1バイト単位で書かれている。

fe1400:    4e(0) 80(1) 00(2) 20(3)  blr
fe1404: 38(4) 60(5) 00(6) 00(7)     li      r3,0
fe1408: 4e 80 00 20  blr
fe140c: 38 60 00 01     li      r3,1
fe1410: 4e 80 00 20  blr

機械は16進数文字列を順次実行している。実際に解釈しているのは2進数文字列だけれども、人間により読みやすくするために16進数表記にしてある。

これが、「コンピュータは0と1でできた命令しか理解出来ない」ということ

16進数文字列だけで表記するとすごく分かりづらいので、4e 80 00 20はblrと表記するようにしよう、というのがニーモニック

4e 80 00 20 blr は1体1

機械語の命令とニーモニックは通常1体1なので、人間が機械語のプログラムを書くときはニーモニックで表記してて、ニーモニックを実際の機械語に変換することを「アセンブラ」という。

アセンブリ言語のことをアセンブラということもあるし、ニーモニックを機械語に変換するプログラムのことをアセンブラということもある。

固定長の命令

P43

4e 80 00 20

38 60 00 01

すべて固定長4バイト命令であることに気づく。

1つの命令が固定長なのは全てのCPUに共通することではない。

1つの命令の長さが2バイトだったり3バイトだったりする可変長命令もありうる

固定長命令がRISCで、可変長命令がCISC というステレオタイプな分類でいってみる。