トップQs
タイムライン
チャット
視点

機械語

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

Remove ads

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

概要

要約
視点

機械語は01を並べた形、ビット組み合わせパターン(ビット列)で表されるものであり[2][1]、人間が日常使う言葉とはかけ離れていて読み書きしやすい形式ではない[2][1]。 そのため、コンピュータのプログラムの開発に機械語が直接使われることはほとんどない[1]。通常は人間が読みやすい形式(高級言語)でプログラムを書いてそれをコンパイラで機械語に翻訳してその機械語でコンピュータを動かしている[1][※ 1]

機械語は中央処理装置(CPUMPU)の動作をひとつひとつ指示するものであり[1]、指示の内容はたとえば次のようなものである[1]

各プロセッサは、その設計段階で命令セットアーキテクチャ(ISA:Instruction Set Architecture)が定められ、そのISAの中で各命令の識別番号(オペコード)を定められる。機械語のプログラムは、基本的には、その識別番号(と操作対象のデータ)を実行順に並べた形式になっている[2]

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

現在ではコンピュータのプログラムは基本的には高級言語で書くということはすでに説明したが、ハードウェア寄りのエンジニアなどがあえて機械語レベルのプログラムを作成する場合でも、大抵は機械語そのものを直接書くのではなく、機械語と1対1で対応するアセンブリ言語を使っている。→#機械語とアセンブリ言語エンジニアが機械語を使用する状況

機械語の具体例

次に機械語の命令群が書かれた具体例を示す。

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

さらに見る アドレス, 機械語(16進表示) ...
Remove ads

構成

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

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

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

Remove ads

機械語と互換性

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

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

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

この常識を変えてみようと試みられたことがあり、1994年にIBMからコンセプトが公表されたプロセッサPowerPC 615英語版x86とPowerPC命令の両立を目指し、32-bit PowerPC コード、64-bit PowerPC コード、32-bit x86 コードの実行が可能になる、という構想のものだったが、結局量産には至らなかった。

なお、まったく役に立たない雑学だが、注意深く機械語命令を選ぶことによって、異なるアーキテクチャでもたまたま動作する、ごくごく小さなプログラムを、シャレで(お遊び的に)書くことは可能な場合がある[※ 5]

CPUによる仕様の差異

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

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

パターン

機械語で書かれたコードには頻出するパターンが存在する。

関数

機械語に関数構文は存在しないが、関数に相当するパターンが存在する。

関数は引数を受け取り、ローカル変数を確保し、ボディの命令を実行し、戻り値を返すルーチンである。このルーチンを呼び出す場合、制御が戻るポイントを控え、引数を用意し、ルーチンへ制御を移して実行し、戻り値を記録し、戻りポイントへ制御を移す。

  • 引数の準備
  • サブルーチン終了後の復帰ポイントメモ
  • ローカル変数の確保
  • サブルーチン冒頭への制御移行

これらの処理によりサブルーチンへ制御が移り、関数ボディの命令列が実行される。関数命令の最終行が実行されたのち、次の処理が必要となる。

  • ローカル変数の解放
  • 引数の解放
  • 戻り値の保存
  • 復帰ポイントへの制御移行

これらの処理によりメインルーチンへ制御が復帰する。

このパターンは関数の処理内容に関わらず普遍的である。前半の関数呼び出しに相当するパターンを関数プロローグ: function prologue)という。後半の関数からの復帰に相当するパターンを関数エピローグ: function epilogue)という。

関数プロローグ・エピローグは同じ結果が得られるいくつかのパターンが存在する(呼出規約)。例えば引数をスタックに積むパターンとレジスタに置くパターンがある。

Remove ads

機械語とアセンブリ言語

要約
視点

機械語を扱いたい場合でも機械語は0/1の組合せや16進表記であり扱いづらいので、代わりにアセンブリ言語を使うことが一般的である。アセンブリ言語は、プログラミング言語の中では機械語に一番近く、機械語とほぼ1対1に対応し、なおかつ人間に理解しやすいニーモニックで書ける。

アセンブリ言語で書かれたプログラムを機械語に変換すれば、その機械語でコンピュータは動く[※ 6]アセンブラというソフトウェアを使えばアセンブリ言語から機械語への変換を自動的に行うことができる。

一方、逆アセンブラというものもあり、これはアセンブラと逆向きの作業、つまり機械語のプログラム(機械語コード)をアセンブリ言語に変換するソフトウェアである。アセンブラと逆アセンブラを使うことで、機械語←→アセンブリ言語 の間の変換を自在に行うことができる。

なお人が自分の眼と頭脳と手を使ってアセンブルの作業を行うことをハンドアセンブルという。コンピュータの黎明期にはしばしばハンドアセンブルが行われていたが、近年ではほぼ全く行われていない。プロセッサが4ビット8ビット(2進数で4桁や8桁)の時代ならば、やろうとすればハンドアセンブルもできたが、その後CPUは16ビット32ビット64ビットとなり、16ビット以降は人間の頭脳や手の能力では扱いきれなくなった。
1970年代後半のマイクロコンピュータや1980年代前半のパーソナルコンピュータの登場で個人ユーザがコンピュータを使うようになったが、当時はアセンブラも高額で個人では購入しづらく、コンピュータがまだ8bitだったので、個人ユーザはハンドアセンブルを行うことがよくあった。

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

機械語コードの解析

システム開発の際の文書(仕様書)が失われることはよくある事だが、文書が残っていないシステムを分析する場合、プログラムを解析するしか方法が無いことがあり、特にソースコードも残っていないような場合には、機械語コードを解析するしかなくなる。そのような場合は、逆アセンブラで逆アセンブルつまり機械語からアセンブリ言語に変換することで、最低限ではあるが、機械語コードを解析できるようになる。さらにヒューリスティックな機能を備えたソフトウェアも使えれば、ソースコードのサブルーチン名や変数名などもある程度推測してくれる。ただしプログラムの"意味"を解析するのは人が行なう必要がある。

Remove ads

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

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

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

ダンプリスト

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

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

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

要約
視点

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

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

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

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

  • アセンブラが存在しないか高価なため購入できない、クロスアセンブラであるため別のコンピュータが必要、など
  • きわめて単純な処理の繰り返しで高速に処理させたいのにもかかわらず、コンピュータの性能が低いうえに、プログラミング言語もBASICインタプリタで望みの処理速度が得られず、機械語で書いた別ルーチンを呼び出す形にすれば可能な場合[※ 9]
  • 高効率で処理する機械語をコンパイラがうまく生成してくれない場合

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

Remove ads

脚注

参考文献

関連項目

Loading related searches...

Wikiwand - on

Seamless Wikipedia browsing. On steroids.

Remove ads