機械語

コンピュータのプロセッサが直接解釈実行可能な一連の命令群のデータそのもの

機械語きかいごまたはマシン語マシンご英語: machine languageあるいはmachine code[1])は、コンピュータ中央処理装置(CPU)が直接理解し実行することができる命令からなる言語[1]

概要編集

中央処理装置(CPUMPU)を直接コントロールするための命令群である。中央処理装置の動作をひとつひとつ指示するものであり[1]、指示の内容はたとえば下記のようなものである[1]


機械語というものは、CPU(MPU)のアーキテクチャごとに大きく異なっている。あるアーキテクチャのCPUのための機械語は、別のアーキテクチャのCPUには全く使えない。→#機械語と互換性

現在では、エンジニアなどが機械語のプログラムを作成する場合でも、大抵はアセンブラを使っている。つまり現在のエンジニアは大抵は、機械語を直接自分の手で書いたりタイピングするのではなく、機械語とほぼ1対1に対応する、そして機械語そのものよりは人間にとって理解しやすいニーモニックを用いたアセンブリ言語を使うのが一般的である。アセンブリ言語で書かれたプログラムを機械語に変換することをアセンブル(する)と言い、その処理系つまり自動的に行うソフトウェアをアセンブラと言う。アセンブラによるアセンブルに対し、人が自分の眼と頭脳と手を使って行っていたアセンブルをハンドアセンブルという。コンピュータの黎明期にはしばしばハンドアセンブルが行われていたが、近年ではほぼ全く行われていないのである。CPUが4bitや8bit(2進数で4桁や8桁)などの時代ならば、やろうとすればハンドアセンブルもできたが、その後CPUは16bit、32bit、64bit...となり、16bit以降は人間の頭脳や手の能力では扱いきれなくなった。) →#機械語とアセンブリ言語エンジニアが機械語を使用する状況

機械語の具体例編集

一般論的な説明ばかり、抽象的な説明ばかり、を延々と続けても初心者には機械語の具体的なイメージすら湧かず理解が進まないであろうから、ある機械語の命令群が書かれた具体例をひとつ示す。

CASLのペーパーマシンCOMETについて、情報処理技術者試験の出題範囲を示す資料に処理系作成者に対する便宜として、定義の後に付されている参考資料(定義ではない)にもとづく機械語の例を以下に挙げる。なお、この機械語コードは16進数表現(※)であり、2バイト単位で区切って(※)ある。

(※ CPU自体が実行している「生(なま)の」機械語は、実態としては(4個から64個ほどの)電気的スイッチ群の各電圧の高・低で表現された2進数であるが、それを画面モニタ)上で人間でも読めるように表示する場合は 0/1 という文字を多数連ねて表現された2進数(8、16桁、32桁、64桁など)で表示することも一応は可能だが、大抵はもっと人間が読みやすいように、その2進数を16進数に変換したものを表示するのが一般的である。そして16進の文字列をただ長々と何十文字や何百文字も連ねてしまうと、人間の認識能力では誰でも誤読を連発するので、 2バイトつまり16進数の4文字ごとに、あるいは1バイトつまり16進2文字ごとに区切って間に空白を入れて可読性を高めるということが画面上や紙面上では一般的である。だから情報処理技術者試験の文面でも、受験者に分かりやすくなるように16進表示にしてあり、4文字ごとに空白をはさんでいる。)

アドレス 機械語 CASL

8000


800C
800D

7001 0000 7002 0000
1210 800D 1220 800C
F000 0002 7120 7110
000C
0048 0065 006C 006C
006F 0020 0077 006F
0072 006C 0064 0021




LEN
TEXT

OUT


DC
DC

TEXT,LEN


12
'Hello world!'

構成編集

一般的な機械語プログラムは以下のような構成となっている。

命令部(オペレーションコード、あるいはオペコード
CPUに処理をさせるための命令の番号を記録している。
アドレス部(オペランド)
情報として利用するデータが格納されている、あるいは結果の記録先のレジスタやメモリアドレス、ジャンプ先などを示す、後述のイミディエイト値もこれに含めることもある。命令によって、個数や長さが異なる。オペランドの数について、0アドレス方式、1アドレス方式、2アドレス方式、3アドレス方式がある。0アドレス方式はオペレーションコードだけで、オペランドは存在しない。
イミディエイト値
オペランドの一部に含めることもある。演算に使用する整数値などのデータが命令に引き続いて置かれているもの。即値とも。
データ部
データ部は実行されない部分である。プログラムで使用するデータのうち前述のイミディエイト値に収まらないもの、文字列リテラルなどのような定数データ、グローバル変数(機械語やアセンブリ言語プログラミングの用語ではワークエリア)のためのヒープ領域やローカル変数のためのスタック領域など。通常ある程度まとめて置かれる。通例、命令として解釈することはできないが、強制的にデータ部にジャンプさせ、命令部として解釈させることで、仮にデータ部に機械語相当の命令データが配置されていた場合はそれらを実行することができる環境も存在する。これを悪用して、リターンアドレスの書き換えなどにより不正なコードを実行させてしまうような悪意のあるソフトウェアからコンピュータを保護するため、データ実行防止(DEP)と呼ばれる機能を備えている環境もある[3][4]

以上の各部分に具体的に何ビットずつ割り振って、どういう順番に並べるか、という形式(フォーマット)のことを機械語フォーマットなどと言う。アーキテクチャにより機械語フォーマットはまちまちだが、1命令を構成するデータ長が固定の「固定長」式と、命令やオペランドの種類により変化する「可変長」式に大別される。可変長の場合、機械語命令の種類によってアドレス部やデータ部、そして中には命令部までも長さが変わる。このため、読み込み位置が1バイトずれれば機械語の命令はそれ以降のすべての命令が正しく読み込まれず意味を失うため、そういった機械語フォーマットのバイナリを対象とする逆アセンブラは工夫を要する[※ 1]。またメモリが限られるシステムでは本来の命令の途中に飛び込み別の意味に使うというトリック的な手法もある。

機械語と互換性編集

基本的に、あるアーキテクチャの中央処理装置のための機械語は、別のアーキテクチャの中央処理装置のためには全く使えない。

たとえば、Pentium系列とPowerPC系列の双方で動く機械語プログラムが存在しないのは、命令セットに互換性が無いからである。

たとえ同じメーカー(や同系列企業)の中央処理装置でも、ある世代の中央処理装置のための機械語が、そのアーキテクチャの「世代」が代替わりし、アーキテクチャが変化すると、全く動かない。機械語プログラムがそのまま動くか否か、という互換性を「バイナリ互換性」と言う。

なおごくわずかな例外はあり、注意深く機械語命令を選ぶことによって、異なるアーキテクチャでもたまたま動作するプログラムを、シャレで(お遊び的に)書くことは可能な場合がある。[※ 2]

CPUによる仕様の差異編集

CPUの仕様が異なれば、機械語もそれぞれのCPUごとに異なる。上記類似点の範囲でのCPUごとの機械語の仕様の差異には、以下のようなものが挙げられる。

  • CPUが理解できる命令の種類や数が異なる(CISCRISCVLIW
  • 命令の長さが異なる(CISCとRISCとでは長さが異なることがある。また、同じアーキテクチャでも、命令のビット数の違いも影響する)
  • 命令部の命令番号が一致しない
  • 同じ処理を行う命令でも処理結果が異なる
  • 演算方法が異なる(レジスタ - レジスタ間演算やメモリ - レジスタ間演算の違い。RISCでは後者の演算ができない)
  • データの記録方法が異なる(エンディアンアラインメントの相違)
  • 実行形式のバイナリファイルの記録形式が異なる(PECOFFELFなど)

機械語とアセンブリ言語編集

機械語を直接プログラミングに使うのは人間には負担が大きく、またコンパイラ等のコード生成でも(命令セットの設計にもよるが)見通しが良くないこともある。そういったこともあり、命令をオペコードではなくニモニックと呼ばれる英単語風のもので表現し、オペランドもレジスタ名などシンボリックに表現できる、アセンブリ言語を通じて扱うことが多く、C言語などの(処理系拡張による)インラインアセンブラ等でもアセンブリ言語の利用が一般的である。

基本的にはアセンブリ言語は機械語と1対1に対応するが、簡単なマクロなどを備えているものは多く、遅延スロットを利用するコードに自動的に変形するなどといった機能を持つものもある。また特殊な短縮形など(x86でAXがオペランドの場合など)について、機械語では違いがある場合をアセンブリ言語では明示的に指定できない場合もある。

アセンブリ言語で書いたコードを、手作業でアセンブルして機械語コードにする作業を「ハンドアセンブル」という。通常、メーカー等での新機種の開発などでは、旧機種の環境でクロス開発を行うわけで、わざわざ効率の悪い作業であるハンドアセンブル等を行うことは普通は無いが、1970年代のマイクロコンピュータや1980年代のパーソナルコンピュータでは、個人の場合、クロス開発のためのコンピュータ(当時はミニコンピュータなどが、メーカー等では使われていた)を別に持っているわけなどなく、またアセンブラも高額だったりすることも多く、ハンドアセンブルは一般的であった。

機械語と解析編集

アセンブラの逆を行う処理系が逆アセンブラである。開発の際の文書が失われることはよくある事だが、文章が残っていないシステムを分析するために、プログラムを解析するしかないことがある。さらにソースコードも残っていないような場合には、機械語コードからなんとかして解析するしかないが、逆アセンブラで逆アセンブルすることで、基本的な手間は省けるかもしれない。しかし、各種のヒューリスティックによってサブルーチンや変数の名前などもある程度はそれらしく推測してくれるものもあるが、プログラムの意味を解析するのは人が行なう必要がある。

機械語プログラムの読み込み編集

ここでは、プログラム内蔵方式を前提とする。一般に電源投入ないし、いわゆるコールドリセットの直後にCPUが実行するコードはROMに置いておくか、CPUの動作に依らない方法でRAMに書き込まれている必要がある(ブート)。

オペレーティングシステム(OS)がブートされた後の、OS運用下では、ファイルシステムが存在するシステムの場合、補助記憶装置中のファイルシステムに、いわゆる「実行可能バイナリ」などと呼ばれる実行ファイルとして機械語プログラムが存在しており、それがファイルシステムから主記憶にロードされて実行される、というような形態が一般的である。なお、実行時に共有ライブラリ動的リンクするなど、近年はこの「ロードして実行する」という手続きが複雑になる傾向もあり、実行時コンパイル等が一般的になると主流の形態も変化するかもしれない。

ダンプリスト編集

ダンプリストそのものは機械語に限らず、コアダンプなど、バイナリをリスティング出力したものであり、オクテット単位を基本とするコンピュータ[※ 3]では十六進法の2桁ずつで表現される。また1980年頃の「マイコン雑誌」の誌面に機械語プログラムが掲載される際の形態でもあった[※ 4]

命令セットと命令フォーマットの設計によって、ダンプリストではほとんど意味不明なコードの場合もあれば、比較的読みやすいものもある。前述のようなハンドアセンブルやハンド逆アセンブルの経験者であれば、かなりその場で読めるような者もいる。そうでなくとも、デバッグ等で頻出するパターン(システムコールやサブルーチン呼出、プッシュ・ポップ等)は、経験で覚えてしまうことも多い。

機械語が動いている状況とエンジニアが機械語を使用する状況編集

現在でも全てのプログラムは、たとえ高級言語で書かれていようが、ユーザには見えていなかろうが、結局は全て機械語に変換されて実行されている。コンピュータの中央処理装置は常に機械語で動いている。コンピュータはどの瞬間も、機械語無しでは全く動かない。

コンピュータサイエンティストやエンジニアたちの数十年以上の努力の積み重ねのおかげでアセンブラが作られ、コンパイラも作られ、高級言語も作られ、便利なアプリケーション・ソフトウェアもあるので、現在では、一般ユーザも、パソコンしか触ったことがないような巷の若いソフトウェアエンジニアも、機械語は直接書いたり読んだりしなくても、コンピュータをそれなりに操れる。

だが、一般ユーザや巷のソフトウェアエンジニアが機械語を直接書いたり読んだりしていなくても、それは「彼らは直接書いたり読んだりしていない」というだけのことでしかなく、実際にはコンピュータの核心部分の中央処理装置は常に機械語で動いている。また、現在でもいわゆる「組み込み系のエンジニア」や「ハードウェア系のエンジニア」などは、しばしばアセンブラや逆アセンブラを扱う必要があり、アセンブラや逆アセンブラを使えばその画面には機械語そのものが表示されているのでそれを目にすることになる。こうしたエンジニアは、時には機械語を直接自分の目で読んだり書いたりする場合もある。また忘れてならないが、CPUやMPUの開発企業(たとえばインテルAMDなど)で新たなCPUやMPUのアーキテクチャを設計するエンジニアたちは、しばしば機械語についてかなり深いレベルで検討しており、新たなアーキテクチャを創造する場合は新たな機械語も作り出す。

人間がわざわざ直接機械語を書いたり読んだりする場合は、以前は次のような理由であった。

  • アセンブラが存在しないか高価なため購入できない、クロスアセンブラであるため別のコンピュータが必要、など
  • きわめて単純な処理の繰り返しで高速に処理させたいのにもかかわらず、コンピュータの性能が低いうえに、プログラミング言語もBASICインタプリタで望みの処理速度が得られず、機械語で書いた別ルーチンを呼び出す形にすれば可能な場合(たとえば1970年代~1980年代など、たとえば2Dシューティングゲームソフトの上下左右の画面スクロール処理など)
  • 高効率で処理する機械語をコンパイラがうまく生成してくれない場合

今日では、GNU Binutilsないし同様なライブラリがあることも多く、そういったユーティリティやライブラリを使うことで、アセンブラ・逆アセンブラを書いたりリバースエンジニアリングなどですら、機械語に直接触れずできることも多い。そのため、巷のエンジニアたちが機械語を直接読んだり書いたりするのは、そのようなユーティリティやライブラリが(まだ)無い新しいプロセッサの場合や対応していない新機能などを使う場合、プログラミング言語には馴染まない特殊な命令を扱う場合、trampoline[5]のようなテクニックが必要な場合、プロセッサのバグに当たった(等の可能性が疑われる)場合、何らかの理由でコアダンプを直接解析しなければならない場合、などに限られてきている。

注釈編集

  1. ^ 全て機械任せで良い結果を得ようとするよりも、人による補助をうまく取り入れられるようにするほうが良い場合もある。
  2. ^ こうした変則的な機械語プログラミングはPolyglotの極端な1ジャンルともいえる。たとえばPC-98とX68k両対応のブートセクタ電脳インストーラ2」が書かれたことがある。第1回IOCCCの入選作のひとつでmullenderによるPDP-11とVAX両対応プログラム「Previous IOCCC Winners」。だが、その後コンテストのルールが変更されこのようなプログラムは禁止されている
  3. ^ たいていはオクテット単位だが、CASLのCOMETが16ビット単位のように、そうでないものもある。
  4. ^ 「絶対にBASICプログラムという形態で載せる」という掟のあった、『マイコンBASICマガジン』(ベーマガ)を除く。このためベーマガでは、DATA文のデータから、BASICプログラムで機械語コードを書き込むようなプログラムを掲載していた。

出典編集

関連項目編集