実行形式ファイルをいろいろな方法で作ってみる

<!-- 実行形式ファイルをいろいろな方法で作ってみる -->

※この記事は[Udon Advent Calendar 2024 - Adventar](https://adventar.org/calendars/10764)の24日目の記事です。

## はじめに

[この記事](https://qiita.com/Q_Udon/items/175287fbac37dbfa65d6)でUbuntuサーバにおいて独自コマンドを定義する方法を紹介しましたが、肝心の中身の作り方は解説していませんでした。

コマンドとして利用できるファイルには以下のようなものがあります。

- シェルスクリプト
- 実行形式ファイル

前者はコマンドを羅列することによって作成することができます。Windowsで言うところのバッチファイルです。内容もコマンドそのものなので人間にも読むことができ、作成も比較的容易です。

問題は後者です。

なので、ここでは実行形式ファイルをいろいろな方法で作ってみた様子を紹介しようと思います。

## そもそも実行形式ファイルとは

バイナリファイルともいい、コンピュータが直接実行できる形式のファイルです。Windowsで言うところのexeファイルです。

Linuxでは拡張子がないことが多いですね。

このファイルの中身を`type`やら`cat`で見ても、意味不明な文字列しか出てきません。これは機械語が書かれているためです。ところどころ意味の分かる文字列はありますが、全体的には意味不明です。文字コードがおかしいものもあります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3468660/2dac4591-b048-f406-ab03-78ea6a5d2f98.png)

人間が読めない代わりに、コンピュータによる閲覧に最適化されているので、高速に実行することができるという利点があります。

## 作成方法

ではどんなことをすると実行形式ファイルは作成されるのでしょうか。普通にプログラミングしてできるファイルは人間が読めるので、実行形式ファイルではありません。しかし、それをコンパイルすることによって実行形式ファイルが作成されるわけです。

それではやってみます。アセンブリ言語以外は以下のような環境を想定しています。

- Linux
- gccインストール済
- g++インストール済
- makeインストール済

アセンブリ言語の場合、WindowsにMASMがインストールされていることを想定しています。

### C言語でプログラミング→コンパイル

まずはC言語で適当にプログラムを書いてみます。

```c
#include <stdio.h>
int main(){
    printf("Hello, World!\n");
    return 0;
}
```

このファイルを`hello.c`として保存し、以下のコマンドでコンパイルします。

```bash
$ gcc -o hello hello.c
```

これで`hello`という実行形式ファイルが作成されました。

```bash
$ ./hello
Hello, World!
```

`gcc`はコンパイラの名前です。`-o`オプションは出力ファイル名を指定するものです。`hello`という名前を指定しているので、`hello`という実行形式ファイルが作成されます。その名前をコマンドの要領で叩くことで、プログラムが実行されます。

ちなみに、`-o`オプションを指定しない場合、デフォルトで`a.out`という名前の実行形式ファイルが作成されます。

```bash
$ gcc hello.c
$ ./a.out
Hello, World!
```

これだと不便なので、`-o`オプションを使うことが一般的です。

ちなみに、上述の`hello`の中身を見てみると、以下のようになっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3468660/a3a8b9fd-403c-e022-8b46-0adac290b191.png)

### makeファイルを使ってみる

コンパイルの際に様々なオプションがある場合、いちいちコマンドを打つのは面倒です。そこで、makeファイルを使ってみましょう。

makeファイルはコンパイルの手順を記述しておくことができるファイルです。これを使うことで、コンパイルの手順を一括で行うことができます。

以下のようなファイルを名前を`Makefile`として作成します。

```makefile
hello: hello.c
    gcc -o hello hello.c
```

まずルール名を指定し、コロンのあとに対象のCファイルの名前、そのあとにインデントしつつコンパイルのコマンドを書きます。

これを保存して、`make`コマンドを実行します。

```bash
$ make
gcc -o hello hello.c
```

これで`hello`という実行形式ファイルが作成されました。

```bash
$ ./hello
Hello, World!
```

makefile内にはいくつかのルールをまとめて書くこともできます。

```makefile
hello: hello.c
    gcc -o hello hello.c
goodbye: goodbye.c
    gcc -o goodbye goodbye.c
```

この場合、`make`のあとにルール名を指定することで使い分けを行います。

```bash
$ make hello
gcc -o hello hello.c
$ make goodbye
gcc -o goodbye goodbye.c
```

`Makefile`を編集することにより、柔軟にコンパイル方法を変更することができるほか、いくつかの依存関係のあるファイルを一括でコンパイルし、一つの実行形式ファイルにまとめることも可能です。

### C++でプログラミング→コンパイル

C++もCと同様実行の前にコンパイルが必要です。

```cpp
#include <iostream>
using namespace std;
int main(){
    cout << "Hello, World!" << endl;
    return 0;
}
```

このファイルを`hello.cpp`として保存し、以下のコマンドでコンパイルします。

```bash
$ g++ -o hello hello.cpp
```

これで`hello`という実行形式ファイルが作成されました。

```bash
$ ./hello
Hello, World!
```

### アセンブリ言語をアセンブル+リンク

[この記事](https://qiita.com/Q_Udon/items/392e058d6988d0ea4199)に書いてあります。

アセンブリは機械語に近いわけですが、完全に機械語というわけではないのでやはりコンパイルが必要です。

## まとめ

様々な方法で実行形式ファイルを作ることができました。これらの方法は全体のごく一部であり、非常に簡略化されているものです。

## 参考文献

[実行ファイルとは｜「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典](https://wa3.i-3-i.info/word11896.html)