Linux で Rust の環境構築をするまでに必要なものを 1 つずつインストールしてみる

結論
rustup
と Rust ツールチェーンと gcc
と libc6-dev
で Hello, world! プログラムの cargo run
はできる。
実用的には build-essential
を入れた方が良い。
最終的にできた環境は以下:
背景
よく Dockerfile を書いてコンテナを実行するときや、仮想マシン(VM)を作成して設定するとき、build-essential
とか cmake
とかのビルドに必要なソフトウェアをインストールする設定を書くが、これらが 1 つ 1 つ何のために必要なのかを正確に理解できていない。
なんとなく「コンパイラ、libc、ビルドツール make あたりをインストールする必要があるんだな」くらいの認識はあるが、それぞれ何がインストールされるか、インストールされる 1 つ 1 つのソフトウェアはなぜ必要なのか、まで理解が及んでいないので、一つずつパッケージをインストールしていき「なぜこのソフトウェアをインストールする必要があるのか」を見ていく。

目次
メイン
- VM マシンの起動
- なにはともあれ rustup をインストールする
- Rust プロジェクトを作成して Hello, world! する
補足
本題じゃないのでやらない
-
apt-get update
-
apt
とは -
apt-get
とapt
は何が違う? DEBIAN_FRONTEND=noninteractive
-

メモ:同期フォルダの権限エラー
cargo init でディレクトリを作成できない
cd ~/toy-tcpip-rs/workdir
cargo init hello-rust
# Creating binary (application) package
# error: Failed to create package `hello-rust` at `/home/vagrant/toy-tcpip-rs/workdir/hello-rust`
#
# Caused by:
# failed to create directory `/home/vagrant/toy-tcpip-rs/workdir/hello-rust`
#
# Caused by:
# Permission denied (os error 13)
Vagrantfile
Vagrant.configure("2") do |config|
# ...
# 同期フォルダの設定
config.vm.synced_folder ".", "/home/vagrant/toy-tcpip-rs",
# Vagrant UTM プラグインでは default で UTM QEMU VirtFS を同期フォルダの実装として使用している。
# ref: https://naveenrajm7.github.io/vagrant_utm/features/synced_folders.html
owner: "vagrant",
group: "vagrant",
create: true
# Provider specific configs
config.vm.provider "utm" do |u|
# ...
# QEMU Directoy Share mode for the VM.
# Takes none, webDAV or virtFS
u.directory_share_mode = $vm_directory_share_mode
end

pwd
# /home/vagrant
ls -lah
# --- 出力 ---
total 80K
drwxr-x--- 8 vagrant vagrant 4.0K Sep 12 07:40 .
drwxr-xr-x 3 root root 4.0K Nov 28 2024 ..
-rw------- 1 vagrant vagrant 827 Sep 12 06:51 .bash_history
-rw-r--r-- 1 vagrant vagrant 220 Mar 31 2024 .bash_logout
-rw-r--r-- 1 vagrant vagrant 3.8K Sep 11 16:51 .bashrc
drwx------ 2 vagrant vagrant 4.0K Nov 28 2024 .cache
drwxrwxr-x 3 vagrant vagrant 4.0K Sep 11 16:50 .cargo
-rw------- 1 vagrant vagrant 20 Sep 12 06:56 .lesshst
-rw-r--r-- 1 vagrant vagrant 828 Sep 11 16:50 .profile
drwxrwxr-x 6 vagrant vagrant 4.0K Sep 11 16:50 .rustup
drwx------ 2 vagrant vagrant 4.0K Sep 11 16:49 .ssh
-rw-r--r-- 1 vagrant vagrant 0 Nov 28 2024 .sudo_as_admin_successful
drwxr-xr-x 17 vagrant vagrant 544 Sep 12 06:06 toy-tcpip-rs
-rw-r--r-- 1 vagrant vagrant 5 Dec 1 2024 .utm_version
-rw------- 1 vagrant vagrant 26K Nov 29 2024 .viminfo
drwxrwxr-x 2 vagrant vagrant 4.0K Sep 12 07:40 workdir
同期フォルダ ~/toy-tcpip-rs
内の権限を見てみる:
cd toy-tcpip-rs/
pwd
# /home/vagrant/toy-tcpip-rs
ls -lah
# --- 出力 ---
total 32K
drwxr-xr-x 17 vagrant vagrant 544 Sep 12 06:06 .
drwxr-x--- 8 vagrant vagrant 4.0K Sep 12 07:46 ..
-rw-r--r-- 1 501 dialout 153 Aug 9 07:41 Cargo.lock
-rw-r--r-- 1 501 dialout 104 Aug 9 08:28 Cargo.toml
drwxr-xr-x 13 501 dialout 416 Sep 12 06:26 .git
drwxr-xr-x 3 501 dialout 96 Aug 9 07:41 .github
-rw-r--r-- 1 501 dialout 173 Sep 11 09:05 .gitignore
-rw-r--r-- 1 501 dialout 1.1K Aug 9 07:41 LICENSE
-rw-r--r-- 1 501 dialout 2.4K Sep 11 11:24 README.md
-rw-r--r-- 1 501 dialout 122 Aug 9 08:24 rust-toolchain.toml
drwxr-xr-x 3 501 dialout 96 Sep 11 11:13 scripts
drwxr-xr-x 3 501 dialout 96 Aug 9 07:41 src
drwxr-xr-x 6 501 dialout 192 Sep 11 06:43 target
drwxr-xr-x 5 501 dialout 160 Sep 11 08:54 .vagrant
-rw-r--r-- 1 501 dialout 2.0K Sep 12 07:30 Vagrantfile
drwxr-xr-x 3 501 dialout 96 Sep 11 08:18 .vscode
drwxr-xr-x 3 501 dialout 96 Sep 11 15:09 workdir

Vagrant の「同期フォルダ」の仕組み
コピーではなく、マウントを利用。
Vagrant の synced folder(いまの virtFS/9p)では、ホストのフォルダを VM にマウントしているため、ファイルの所有者(UID/GID)もホストの数値のまま見える。そのため macOS の 501:20(= staff)が、そのまま Linux 側では 501:dialoutとして表示される。
※ dialout は gid=20 のエイリアス
Linux は名前ではなく数値 UID/GID で権限判定、つまり「そのディレクトリ、ファイルを読めるか、書き込めるか、実行できるか」が決まるため、vagrant ssh
で入ったときのデフォルトのユーザ vagrant
(1000:1000, UID=1000, GID=1000) ではマウントされたディレクトリ、ファイルに対しては書き込み、実行権限がない(-rw-r--r--
と表示されているので)。
引用元:ネットワークエンジニアとして | Linux - ファイルのパーミッションと所有者の確認
- virtFS/9p(UTMの既定):共有マウント。所有者はホストの UID/GID をそのまま提示(または xattr に保持)。
chown
してもホスト側は変わらないか、拒否される。 - NFS:共有マウント。やはり UID/GID は数値で突き合せ。map_uid/map_gid を指定すれば合せられる。
- rsync:ただのコピー。VM 側に新規作成されるので所有者は vagrant になる(ただし基本は片方向、双方向編集には向かない)。同期するには
rsync
コマンドが必要。

Vagrant のファイルの権限を調べる
ホスト側の UID / GID を確認しておく。
- ユーザ名:
nukopy
- UID:
501
- GID:
20
(staff)
whoami
# nukopy
id nukopy
# uid=501(nukopy) gid=20(staff) -> 長いので改行
# groups=20(staff),...
VM に入る
vagrant ssh
以降 VM 内でコマンドを実行:
pwd
# /home/vagrant
# vagrant ssh 直後ではホームディレクトリからセッション開始
Vagrant で作成した VM のデフォルトのユーザ(vagrant up
、vagrant ssh
をしたときに設定されているユーザ)の確認。
whoami
# vagrant
# 現在のユーザは `vagrant` だと分かる
# ユーザ一覧表示
who -aH
# NAME LINE TIME IDLE PID COMMENT EXIT
# system boot 2025-09-12 06:46
# run-level 5 2025-09-12 06:46
# LOGIN ttyAMA0 2025-09-12 06:46 813 id=AMA0
# LOGIN tty1 2025-09-12 06:46 819 id=tty1
# vagrant + pts/0 2025-09-12 08:20 . 1407 (10.0.2.2)
# UID / GID の確認
id vagrant
# uid=1000(vagrant) gid=1000(vagrant) -> 長いので改行
# groups=1000(vagrant),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),101(lxd)
ユーザ vagrant
の UID / GID は 1000 / 1000 ということが分かる。
次にホームディレクトリ配下にマウントしたディレクトリの権限を見ていく。
ホストのディレクトリを VM の /home/vagrant/toy-tcpip-rs
にマウントしている。
pwd
/home/vagrant
# --- 出力 ---
total 76
drwxr-x--- 7 vagrant vagrant 4096 Sep 12 08:29 .
drwxr-xr-x 3 root root 4096 Nov 28 2024 ..
-rw------- 1 vagrant vagrant 1601 Sep 12 08:20 .bash_history
-rw-r--r-- 1 vagrant vagrant 220 Mar 31 2024 .bash_logout
-rw-r--r-- 1 vagrant vagrant 3816 Sep 11 16:51 .bashrc
drwx------ 2 vagrant vagrant 4096 Nov 28 2024 .cache
drwxrwxr-x 3 vagrant vagrant 4096 Sep 11 16:50 .cargo
-rw------- 1 vagrant vagrant 20 Sep 12 06:56 .lesshst
-rw-r--r-- 1 vagrant vagrant 828 Sep 11 16:50 .profile
drwxrwxr-x 6 vagrant vagrant 4096 Sep 11 16:50 .rustup
drwx------ 2 vagrant vagrant 4096 Sep 11 16:49 .ssh
-rw-r--r-- 1 vagrant vagrant 0 Nov 28 2024 .sudo_as_admin_successful
# ホームから VM へマウントしたディレクトリ
drwxr-xr-x 16 vagrant vagrant 512 Sep 12 08:18 toy-tcpip-rs
-rw-r--r-- 1 vagrant vagrant 5 Dec 1 2024 .utm_version
-rw------- 1 vagrant vagrant 26593 Nov 29 2024 .viminfo
マウントしたディレクトリの情報は以下のようになっている。
drwxr-xr-x 16 vagrant vagrant 512 Sep 12 08:18 toy-tcpip-rs
整理する。
- ディレクトリ
/vagrant/home/toy-tcpip-rs
の所有者:vagrant
- ディレクトリ
/vagrant/home/toy-tcpip-rs
の所属グループ:vagrant
- 権限:755
- 所有者:
rwx
(4 + 2 + 1 = 7) - 所属グループ:
r-x
(4 + 0 + 1 = 5) - その他のユーザ:
r-x
(4 + 0 + 1 = 5)
- 所有者:

drwxr-xr-x 16 vagrant vagrant 512 Sep 12 08:18 <dir>
の意味
この権限の意味は以下の通り:
-
toy-tcpip-rs
というディレクトリの所有者がユーザvagrant
(UID=1000)、所属グループがvagrant
(GID=1000)である - ユーザ
vagrant
は、ディレクトリに対して読み・書き・実行(ディレクトリに移動する)権限がある - 所属グループ
vagrant
は、ディレクトリに対して読み・実行(ディレクトリに移動する)権限がある - それ以外のユーザは、ディレクトリに対して読み・実行(ディレクトリに移動する)権限がある
ls -la
# ...
# drwxr-xr-x 16 vagrant vagrant 512 Sep 12 08:18 toy-tcpip-rs
# ...

ディレクトリ /home/vagrant/toy-tcpip-rs
は、VM 作成時に作られ、UID / GID が vagrant
/ vagrant
に設定されている。続いてこのディレクトリ内のディレクトリ、ファイルの権限を見てみる。
cd /home/vagrant/toy-tcpip-rs
ls -la
# --- 出力 ---
total 32
drwxr-xr-x 16 vagrant vagrant 512 Sep 12 08:18 .
drwxr-x--- 7 vagrant vagrant 4096 Sep 12 08:29 ..
-rw-r--r-- 1 501 dialout 153 Aug 9 07:41 Cargo.lock
-rw-r--r-- 1 501 dialout 104 Aug 9 08:28 Cargo.toml
drwxr-xr-x 13 501 dialout 416 Sep 12 06:26 .git
drwxr-xr-x 3 501 dialout 96 Aug 9 07:41 .github
-rw-r--r-- 1 501 dialout 173 Sep 11 09:05 .gitignore
-rw-r--r-- 1 501 dialout 1077 Aug 9 07:41 LICENSE
-rw-r--r-- 1 501 dialout 2373 Sep 11 11:24 README.md
-rw-r--r-- 1 501 dialout 122 Aug 9 08:24 rust-toolchain.toml
drwxr-xr-x 3 501 dialout 96 Sep 11 11:13 scripts
drwxr-xr-x 3 501 dialout 96 Aug 9 07:41 src
drwxr-xr-x 6 501 dialout 192 Sep 11 06:43 target
drwxr-xr-x 5 501 dialout 160 Sep 11 08:54 .vagrant
-rw-r--r-- 1 501 dialout 1976 Sep 12 07:30 Vagrantfile
drwxr-xr-x 3 501 dialout 96 Sep 11 08:18 .vscode
ざっと見てみると、所有者は 501、所属グループ 20 (= dialout) となっており、ホスト側の UID / GID に一致する。つまり、ホストから VM にディレクトリがマウントされたとき、そのディレクトリ内のディレクトリ、ファイルの権限はホストの権限がそのまま反映されることが分かる。これは mount (virtFS/9p) の方式。
- ホストのユーザ名:nukopy
- UID:501
- GID:20

こっちで続き

VM の作成
今回は UTM、Vagrant で仮想マシン(VM)を作成する。
UTM は仮想化ソフトウェアと呼ばれるもので、VM を作成したり起動したり管理する役割のソフトウェア。有名なものだと VirtualBox や VMWare がある。Vagrant は仮想化ソフトウェアの違いを吸収してくれる層で、各仮想化ソフトウェアを統一されたインタフェースで扱えるようにしてくれるツールである。正確ではないかもしれないが、ざっくりいうと仮想化ソフトウェアの抽象化レイヤー、ラッパーの立ち位置だと理解すれば OK。
Vagrant とかよくわからんって人は以下などを読むと良いかも。
ホスト(VM を起動するマシン)
- OS: macOS 14.7
- UTM 4.6.5
- Ruby 3.3.0
- Vagrant 2.4.9
- vagrant_utm 0.1.3
- vagrant-bindfs 1.3.1
ゲスト(VM)
- OS: Ubuntu Server 24.04
- Rust 1.89.0stable (at 2025/06/23)

まずは VM の設定を記述する Vagrantfile
を作成する。
まだ VM を作っていないので当然だが、以下のコマンドはホストマシン上で実行する。
mkdir workdir
touch Vagrantfile
今回使用する Vagrantfile
は以下の通り。
$vm_name = "toy-tcpip"
$vm_cpus = 4
$vm_memory = 4096
$vm_notes = "Vagrant: For toy-tcpip"
$vm_directory_share_mode = "none"
$vm_forwarded_port = 8080
$vm_host = 8080
# ref: https://naveenrajm7.github.io/vagrant_utm/configuration.html
Vagrant.configure("2") do |config|
# ref: https://portal.cloud.hashicorp.com/vagrant/discover/utm/ubuntu-24.04
config.vm.box = "utm/ubuntu-24.04"
config.vm.box_version = "0.0.1"
# Hostname inside the VM
config.vm.hostname = $vm_name
# Ports to forward
config.vm.network "forwarded_port", guest: $vm_forwarded_port, host: $vm_host
# Synced folder
config.vm.synced_folder ".", "/vagrant"
# Provider specific configs
config.vm.provider "utm" do |u|
# Name in UTM UI
u.name = $vm_name
# CPU in cores
u.cpus = $vm_cpus
# Memory in MB
u.memory = $vm_memory
# Notes for UTM VM (Appears in UTM UI)
u.notes = $vm_notes
# QEMU Directoy Share mode for the VM.
# Takes none, webDAV or virtFS
u.directory_share_mode = $vm_directory_share_mode
end
# Provisioner config, supports all built provisioners
# shell, ansible
config.vm.provision "shell", inline: <<-SHELL
# apt-get update
echo "Hello, world!"
SHELL
end
config.vm.provision
のところに VM の作成(provision)時に実行する処理を書くことができる。インラインでシェルスクリプトを書いたり、初期化用のシェルスクリプトを設定したり、Ansible の設定を置くこともできる。要はサーバの初期セットアップに使われる。
今回の主題はまさにこの VM 作成時に実行される処理に着目している。大体のユースケースでは curl、wget、git などの基本的なツールや、ビルド用のソフトウェアをインストールする処理が記述される。開発用のコンテナを定義する Dockerfile でも似たような処理が散見される。
今回は Rust の Linux 開発環境を作るというのが目的だったので、Rust の環境構築を例として Rust がビルドできるまでに必要なソフトウェアを深堀りしていく。

VM の起動
まずは VM を起動する。ここでのコマンドはホストマシン上で実行される。
# ディレクトリ構成の確認
tree workdir
# workdir
# └── Vagrantfile
1 directory, 1 file
# VM を起動
cd workdir
vagrant up
# --- 以下出力 ---
Bringing machine 'default' up with 'utm' provider...
==> default: Checking if box 'utm/ubuntu-24.04' version '0.0.1' is up to date...
==> default: Setting the name of the VM: toy-tcpip
==> default: Clearing any previously set forwarded ports...
==> default: Forwarding ports...
default: 8080 (guest) => 8080 (host) (adapter 1)
default: 22 (guest) => 2222 (host) (adapter 1)
==> default: Running 'pre-boot' VM customizations...
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
default: SSH address: 127.0.0.1:2222
default: SSH username: vagrant
default: SSH auth method: private key
==> default: Machine booted and ready!
==> default: Checking for guest additions in VM...
default: Guest additions detected
==> default: Setting hostname...
==> default: Mounting shared folders...
default: /Users/nukopy/Projects/LowLayer/toy-tcpip-rs => /vagrant
==> default: Running provisioner: shell...
# ここで provision 時のスクリプトが実行される
default: Running: inline script
default: Hello, World!
VM が起動できていることを確認する。
vagrant status
# Current machine states:
#
# default started (utm)
#
# The VM is started. To stop this VM, you can run `vagrant halt` to
# shut it down forcefully, or you can run `vagrant suspend` to simply
# suspend the virtual machine. In either case, to restart it again,
# simply run `vagrant up`.

補足:Vagrant が仮想化ソフトウェアを操作する仕組み
Vagrant は仮想化ソフトウェアの抽象化レイヤである。つまり vagrant xxx
というコマンドは操作対象の仮想化ソフトウェア(仮想化の機能を提供する意味で provider と呼ばれる)から提供されている API を使用することで VM の管理をしている。
(TODO: もっと良い図がある。または作成する。)
引用元: Vagrant 公式ドキュメント
仮想マシンを管理するための主要な機能として仮想マシンの起動、停止、削除があるが、 UTM の場合、Vagrant は UTM が提供する utmctl
という CLI を介してこれを実行する。Vagrant のコマンドと utmctl
の対応の対応は以下の表の通り。これは Vagrant の UTM 用のプラグイン vagrant-utm のドキュメントより引用。
引用元:Vagrant UTM
vagrant-utm のコマンド呼び出し部分を見てみる。
-
vagrant start
->utmctl start
-
execute
関数
UTM に限らず、各 provider が VM を管理する API を提供し、provider ごとのプラグインがそれを実行することで Vagrant が成り立っている(はず)。

ちなみに UTM では以下 3 種類の API を提供している。
The plugin invokes UTM API inorder to implement Vagrant required actions. As there are several ways to control UTM, we use the following order
- UTM Command Line (utmctl)
- Apple Scripting Bridge (osascript)
- 2.a Applescript
- 2.b JavaScript
- Shell command
なぜ 3 種類あるかというと、UTM では CLI である utmctl
でカバーできない機能があるためである。vagrant-utm の作者は、
the goal of this plugin is to use UTM command line tool utmctl as single point of control.
のように「CLI ツールである utmctl
を唯一の制御点とすることがゴールである」と書いたうえで、現状 CLI ツールでカバーできていない領域を osascript やシェルコマンドに頼らざるを得ないことになっている。こればっかり公式頼みになってしまうので、迂回策を取らざるを得ない。
- Apple Script の例

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
なにはともあれ rustup をインストールする: Vagrant で起動した VM の中に入る
VM が起動したので、早速 VM の中に入って Rust をインストールしてみる。
Vagrant では、VM に SSH で接続するための便利なコマンド vagrant ssh
が提供されている。
# VM 起動
cd workdir
vagrant up
# VM へ SSH へ接続
vagrant ssh
# --- 以下 VM 環境内 ---
lsb_release -a
# No LSB modules are available.
# Distributor ID: Ubuntu
# Description: Ubuntu 24.04.1 LTS
# Release: 24.04
# Codename: noble
uname -srvmpio
# Linux 6.8.0-49-generic #49-Ubuntu SMP PREEMPT_DYNAMIC Sun Nov 3 21:21:58 UTC 2024 aarch64 aarch64 aarch64 GNU/Linux
起動したときの様子。Ubuntu 24.04 の VM に接続でき、プロンプトが表示されている。接続直後のカレントディレクトリはホームディレクトリ /home/vagrant
となっている。
補足:VSCode の Remote SSH 拡張を利用する
ちなみに、VSCode の Remote SSH のような機能で接続したい場合、vagrant ssh-config
というコマンドで ssh config を取得できる。vagrant ssh
はこの情報を使っている。
vagrant ssh-config
# Host default
# HostName 127.0.0.1
# User vagrant
# Port 2222
# UserKnownHostsFile /dev/null
# StrictHostKeyChecking no
# PasswordAuthentication no
# IdentityFile /Users/nukopy/Projects/LowLayer/toy-tcpip-rs/.vagrant/machines/default/utm/private_key
# IdentitiesOnly yes
# LogLevel FATAL
# PubkeyAcceptedKeyTypes +ssh-rsa
# HostKeyAlgorithms +ssh-rsa
Remote SSH の設定に ssh config を追加した様子は以下の通り(図中の toy-tcpip
という名前の接続情報が SSH TARGETS に表示されている)。これで VM に拡張を使って接続できるようになる。

rustup のインストール
rustup をインストールする。
rustup は Rust コンパイラ、リンター、フォーマッターを含めた Rust でソフトウェア開発を行うために必要なツールチェーン(Rust の開発に必要なツール一式、Rust toolchains)を管理するための CLI ツール。
以下のコマンドを実行する:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
出力
info: downloading installer
Welcome to Rust!
This will download and install the official compiler for the Rust
programming language, and its package manager, Cargo.
Rustup metadata and toolchains will be installed into the Rustup
home directory, located at:
/home/vagrant/.rustup
This can be modified with the RUSTUP_HOME environment variable.
The Cargo home directory is located at:
/home/vagrant/.cargo
This can be modified with the CARGO_HOME environment variable.
The cargo, rustc, rustup and other commands will be added to
Cargo's bin directory, located at:
/home/vagrant/.cargo/bin
This path will then be added to your PATH environment variable by
modifying the profile files located at:
/home/vagrant/.profile
/home/vagrant/.bashrc
You can uninstall at any time with rustup self uninstall and
these changes will be reverted.
Current installation options:
default host triple: aarch64-unknown-linux-gnu
default toolchain: stable (default)
profile: default
modify PATH variable: yes
1) Proceed with standard installation (default - just press enter)
2) Customize installation
3) Cancel installation
>1
info: profile set to 'default'
info: default host triple is aarch64-unknown-linux-gnu
info: syncing channel updates for 'stable-aarch64-unknown-linux-gnu'
info: latest update on 2025-08-07, rust version 1.89.0 (29483883e 2025-08-04)
info: downloading component 'cargo'
9.2 MiB / 9.2 MiB (100 %) 4.5 MiB/s in 2s
info: downloading component 'clippy'
info: downloading component 'rust-docs'
20.2 MiB / 20.2 MiB (100 %) 3.4 MiB/s in 6s
info: downloading component 'rust-std'
26.6 MiB / 26.6 MiB (100 %) 539.4 KiB/s in 27s
info: downloading component 'rustc'
58.2 MiB / 58.2 MiB (100 %) 494.0 KiB/s in 1m 15s
info: downloading component 'rustfmt'
info: installing component 'cargo'
9.2 MiB / 9.2 MiB (100 %) 6.9 MiB/s in 1s
info: installing component 'clippy'
info: installing component 'rust-docs'
20.2 MiB / 20.2 MiB (100 %) 3.1 MiB/s in 5s
info: installing component 'rust-std'
26.6 MiB / 26.6 MiB (100 %) 5.9 MiB/s in 4s
info: installing component 'rustc'
58.2 MiB / 58.2 MiB (100 %) 6.0 MiB/s in 9s
info: installing component 'rustfmt'
info: default toolchain set to 'stable-aarch64-unknown-linux-gnu'
stable-aarch64-unknown-linux-gnu installed - rustc 1.89.0 (29483883e 2025-08-04)
Rust is installed now. Great!
To get started you may need to restart your current shell.
This would reload your PATH environment variable to include
Cargo's bin directory ($HOME/.cargo/bin).
To configure your current shell, you need to source
the corresponding env file under $HOME/.cargo.
This is usually done by running one of the following (note the leading DOT):
. "$HOME/.cargo/env" # For sh/bash/zsh/ash/dash/pdksh
source "$HOME/.cargo/env.fish" # For fish
source $"($nu.home-path)/.cargo/env.nu" # For nushell
続いて、cargo の bin ディレクトリに PATH
を通す。
# set cargo path
source $HOME/.cargo/env
# add to .bashrc
echo 'source $HOME/.cargo/env' >> $HOME/.bashrc
ちなみに $HOME/.cargo/env
の中身はシェルスクリプトになっている。
/home/vagrant/.cargo/env
#!/bin/sh
# rustup shell setup
# affix colons on either side of $PATH to simplify matching
case ":${PATH}:" in
*:"$HOME/.cargo/bin":*)
;;
*)
# Prepending path in case a system-installed rustc needs to be overridden
export PATH="$HOME/.cargo/bin:$PATH"
;;
esac
$HOME/.cargo/bin
には rustup
をはじめ、Rust のツールチェーンが置かれている。ここにパスを通すことで、cargo
コマンドなど Rust の開発でよく使うコマンドが実行できるようになる。
ls ~/.cargo/bin
# cargo cargo-fmt clippy-driver rust-analyzer rustdoc rust-gdb rust-lldb
# cargo-clippy cargo-miri rls rustc rustfmt rust-gdbgui rustup
cargo
や rustc
のバージョンが取得できれば PATH
が通っていることが確認できる。
これで Rust のプロジェクトを始める準備ができた。
rustup -V
# rustup 1.28.2 (e4f3ad6f8 2025-04-28)
# info: This is the version for the rustup toolchain manager, not the rustc compiler.
# info: The currently active `rustc` version is `rustc 1.89.0 (29483883e 2025-08-04)`
cargo -V
# cargo 1.89.0 (c24e10642 2025-06-23)
rustc -V
# rustc 1.89.0 (29483883e 2025-08-04)

補足:uname コマンド
uname
コマンドは現在カーネルのバージョンなど、システムの情報を表示するためのコマンド。
以下に man uname
の出力を示す:
先に実行した uname -srvmpio
の意味は以下の通り。-a
オプションを使用すると全ての情報を出力する。
UNAME(1) User Commands UNAME(1)
NAME
uname - print system information
SYNOPSIS
uname [OPTION]...
DESCRIPTION
Print certain system information. With no OPTION, same as -s.
-a, --all
print all information, in the following order, except omit -p and -i if unknown:
-s, --kernel-name
print the kernel name
-n, --nodename
print the network node hostname
-r, --kernel-release
print the kernel release
-v, --kernel-version
print the kernel version
-m, --machine
print the machine hardware name
-p, --processor
print the processor type (non-portable)
-i, --hardware-platform
print the hardware platform (non-portable)
-o, --operating-system
print the operating system
--help display this help and exit
--version
output version information and exit
AUTHOR
Written by David MacKenzie.
REPORTING BUGS
GNU coreutils online help: <https://www.gnu.org/software/coreutils/>
Report any translation bugs to <https://translationproject.org/team/>
COPYRIGHT
Copyright © 2023 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later
<https://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the
extent permitted by law.
SEE ALSO
arch(1), uname(2)
Full documentation <https://www.gnu.org/software/coreutils/uname>
or available locally via: info '(coreutils) uname invocation'
GNU coreutils 9.4 April 2024 UNAME(1)

Rust プロジェクトを作成して Hello, world! する
VM の作成
ごちゃごちゃしたので改めて VM の構成ファイル Vagrantfile
は以下の通り:
Vagrantfile
$vm_name = "toy-tcpip"
$vm_cpus = 4
$vm_memory = 4096
$vm_notes = "VM for toy-tcpip"
$vm_directory_share_mode = "virtFS"
$vm_forwarded_port = 80
$vm_host_port = 8080
# for synced folder
$base_dir = "/home/vagrant"
$sync_dir = "#{$base_dir}/_mnt_toy-tcpip-rs"
$work_dir = "#{$base_dir}/toy-tcpip-rs"
# ref: https://naveenrajm7.github.io/vagrant_utm/configuration.html
Vagrant.configure("2") do |config|
# ref: https://portal.cloud.hashicorp.com/vagrant/discover/utm/ubuntu-24.04
config.vm.box = "utm/ubuntu-24.04"
config.vm.box_version = "0.0.1"
# Hostname inside the VM
config.vm.hostname = $vm_name
# Ports to forward
# localhost:80 access is forwarded to <vm>:8080
config.vm.network "forwarded_port", guest: $vm_forwarded_port, host: $vm_host_port
# Synced folder
config.vm.synced_folder ".", $sync_dir, create: true
# 501:20 → 1000:1000 に見せる
config.bindfs.bind_folder $sync_dir, $work_dir,
map: "501/1000:@20/@1000", perms: "u=rwX:g=rwX:o=rX", create_as_user: true
# Provider specific configs
config.vm.provider "utm" do |u|
# Name in UTM UI
u.name = $vm_name
# Machine type
# not supported on vagrant_utm plugin
# u.machine = "vf"
# CPU in cores
u.cpus = $vm_cpus
# Memory in MB
u.memory = $vm_memory
# Notes for UTM VM (Appears in UTM UI)
u.notes = $vm_notes
# QEMU Directoy Share mode for the VM.
# Takes none, webDAV or virtFS
u.directory_share_mode = $vm_directory_share_mode
end
# Provisioner config, supports all built provisioners
# ref: https://developer.hashicorp.com/vagrant/docs/provisioning/shell
config.vm.provision "shell", privileged: false, path: "scripts/bootstrap.sh"
end
VM 作成時に実行するスクリプトは以下の通り:
-
scripts/bootstrap.sh
- Rust のインストールまでを書いてある
#!/bin/bash
set -exu pipefail
export DEBIAN_FRONTEND=noninteractive
echo "Bootstrapping VM for toy-tcpip-rs..."
echo "Run script as non-root user: whoami=$(whoami)"
# install packages
sudo apt-get update -y
# install rust non interactive
# ref: https://github.com/rust-lang-deprecated/rustup.sh/issues/83
curl --proto "=https" --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
# set cargo path
source $HOME/.cargo/env
echo "cargo version: $(cargo --version)"
# add to bashrc to add Rust toolchains to PATH
echo "source $HOME/.cargo/env" >> $HOME/.bashrc
set +exuo pipefail
echo "Bootstrap script completed successfully!"

Rust プロジェクトを作成して Hello, world! する
VM に接続し、Rust プロジェクトを作成し、Hello, world! と出力されるプログラムを実行する。
まずは VM へ接続:
vagrant up
vagrant ssh
以下 VM 内でコマンドを実行する。
作業ディレクトリ ~/toy-tcpip-rs
は Vagrantfile
に設定してあるとおり。これはホームディレクトリ配下であればどこでも良い。
# --- 以下 VM 内で実行 ---
# 作業ディレクトリへ移動
cd ~/toy-tcpip-rs
# rust-toochain.toml の確認(事前に用意したもの)
cat rust-toolchain.toml
# [toolchain]
# # Rust stable version is 1.89.0 at 2025.09.13 (ref: https://releases.rs/)
# channel = "1.89.0"
# components = ["rustfmt", "clippy", "rust-analyzer"]
# targets = ["aarch64-unknown-linux-gnu"]
# Rust プロジェクトの作成
cargo init .
# --- ログ ---
# 初回 cargo init 時は各ツールチェーンのインストールが走る
info: syncing channel updates for '1.89.0-aarch64-unknown-linux-gnu'
info: latest update on 2025-08-07, rust version 1.89.0 (29483883e 2025-08-04)
info: downloading component 'cargo'
info: downloading component 'clippy'
info: downloading component 'rust-analyzer'
info: downloading component 'rust-docs'
info: downloading component 'rust-std'
info: downloading component 'rustc'
info: downloading component 'rustfmt'
info: installing component 'cargo'
info: installing component 'clippy'
info: installing component 'rust-analyzer'
info: installing component 'rust-docs'
info: installing component 'rust-std'
info: installing component 'rustc'
info: installing component 'rustfmt'
Creating binary (application) package
note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
# cargo init で作成されたファイルの確認
ls
# scripts と Vagrantfile はホストからマウントされたもの
# Cargo.lock LICENSE rust-toolchain.toml src Vagrantfile
# Cargo.toml README.md scripts target
Rust プロジェクトの初期化までできた。初期化時には Hello, world! を出力するプログラムが作成されているので、早速これを実行してみる。
# src/main.rs の中身の確認
cat src/main.rs
# fn main() {
# println!("Hello, world!");
# }
# 実行 (build & execute)
cargo run
# error: linker `cc` not found
# |
# = note: No such file or directory (os error 2)
#
# error: could not compile `toy-tcpip-rs` (bin "toy-tcpip-rs") due to 1 previous error
error: linker
cc not found
というエラーメッセージとともにプログラムの実行が失敗する。
このエラーで検索すると大体 GCC (GNU C Compiler) を入れろとか、build-essential
を入れろとか書いてある。

builld-essential
を入れるとコンパイルできるのは分かっているが、これらを単体でインストールしていくとどうなるのか試してみたい。
build-essential
の構成要素は以下の通り。
まずは cc
がないと言われたので、build-essential
の中の gcc
をインストールしてみる。
- dpkg-dev (>= 1.17.11)
- Debian package development tools
- g++ (>= 4:10.2)
- GNU C++ compiler
- gcc (>= 4:10.2)
- GNU C compiler
- libc
- libc6-dev
- GNU C Library: Development Libraries and Header Files
- or libc-dev
- virtual package provided by libc6-dev
- libc6-dev
- make
- utility for directing compilation

sudo apt install gcc
あえて build-essential
ではなく、gcc
のみをインストールしてみる。
sudo apt update -y && sudo apt install -y gcc
# --- ログ ---
# apt update のログは省略
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following additional packages will be installed:
binutils binutils-aarch64-linux-gnu binutils-common cpp cpp-13 cpp-13-aarch64-linux-gnu
cpp-aarch64-linux-gnu fontconfig-config fonts-dejavu-core fonts-dejavu-mono gcc-13
gcc-13-aarch64-linux-gnu gcc-13-base gcc-aarch64-linux-gnu libaom3 libasan8 libatomic1
libbinutils libc-bin libc-dev-bin libc-devtools libc6 libc6-dev libcc1-0 libcrypt-dev
libctf-nobfd0 libctf0 libde265-0 libdeflate0 libfontconfig1 libgcc-13-dev libgd3 libgomp1
libgprofng0 libheif-plugin-aomdec libheif-plugin-aomenc libheif-plugin-libde265 libheif1
libhwasan0 libisl23 libitm1 libjbig0 libjpeg-turbo8 libjpeg8 liblerc4 liblsan0 libmpc3
libsframe1 libsharpyuv0 libtiff6 libtsan2 libubsan1 libwebp7 libx11-6 libx11-data libxau6
libxcb1 libxdmcp6 libxpm4 linux-libc-dev linux-tools-common locales manpages-dev
rpcsvc-proto
Suggested packages:
binutils-doc gprofng-gui cpp-doc gcc-13-locales cpp-13-doc gcc-multilib make autoconf
automake libtool flex bison gdb gcc-doc gcc-13-doc gdb-aarch64-linux-gnu glibc-doc
libnss-nis libnss-nisplus libgd-tools libheif-plugin-x265 libheif-plugin-ffmpegdec
libheif-plugin-jpegdec libheif-plugin-jpegenc libheif-plugin-j2kdec libheif-plugin-j2kenc
libheif-plugin-rav1e libheif-plugin-svtenc
The following NEW packages will be installed:
binutils binutils-aarch64-linux-gnu binutils-common cpp cpp-13 cpp-13-aarch64-linux-gnu
cpp-aarch64-linux-gnu fontconfig-config fonts-dejavu-core fonts-dejavu-mono gcc gcc-13
gcc-13-aarch64-linux-gnu gcc-13-base gcc-aarch64-linux-gnu libaom3 libasan8 libatomic1
libbinutils libc-dev-bin libc-devtools libc6-dev libcc1-0 libcrypt-dev libctf-nobfd0
libctf0 libde265-0 libdeflate0 libfontconfig1 libgcc-13-dev libgd3 libgomp1 libgprofng0
libheif-plugin-aomdec libheif-plugin-aomenc libheif-plugin-libde265 libheif1 libhwasan0
libisl23 libitm1 libjbig0 libjpeg-turbo8 libjpeg8 liblerc4 liblsan0 libmpc3 libsframe1
libsharpyuv0 libtiff6 libtsan2 libubsan1 libwebp7 libx11-6 libx11-data libxau6 libxcb1
libxdmcp6 libxpm4 linux-libc-dev manpages-dev rpcsvc-proto
The following packages will be upgraded:
libc-bin libc6 linux-tools-common locales
4 upgraded, 61 newly installed, 0 to remove and 217 not upgraded.
Need to get 66.7 MB of archives.
再度 cargo run
を実行。Hello, world! が出力された。
Hello, world!
を出力するための println
マクロは標準出力なので libc
必要ではと思ったけど、当然 gcc
の依存関係に含まれている。上記のログの libc6-dev というのがこれ。
cargo run
# Compiling toy-tcpip-rs v0.1.0 (/home/vagrant/toy-tcpip-rs)
# Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.45s
# Running `target/debug/toy-tcpip-rs`
# Hello, world!

gcc の依存関係を見る
apt
でインストールした gcc
の依存関係を見ていく。
まずはパッケージのメタデータ。Recommends
というところに libc6-dev
と書かれている。
dpkg -s gcc
# --- output ---
Package: gcc
Status: install ok installed
Priority: optional
Section: devel
Installed-Size: 37
Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
Architecture: arm64
Source: gcc-defaults (1.214ubuntu1)
Version: 4:13.2.0-7ubuntu1
Provides: c-compiler
Depends: cpp (= 4:13.2.0-7ubuntu1), cpp-aarch64-linux-gnu (= 4:13.2.0-7ubuntu1), gcc-13 (>= 13.2.0-11~), gcc-aarch64-linux-gnu (= 4:13.2.0-7ubuntu1)
Recommends: libc6-dev | libc-dev
Suggests: gcc-multilib, make, manpages-dev, autoconf, automake, libtool, flex, bison, gdb, gcc-doc
Conflicts: gcc-doc (<< 1:2.95.3)
Description: GNU C compiler
This is the GNU C compiler, a fairly portable optimizing compiler for C.
.
This is a dependency package providing the default GNU C compiler.
Original-Maintainer: Debian GCC Maintainers <debian-gcc@lists.debian.org>
もう少し分かりやすく出力したものがこれ:
apt-cache depends gcc
# --- output ---
gcc
Depends: cpp
Depends: cpp-aarch64-linux-gnu
Depends: gcc-13
Depends: gcc-aarch64-linux-gnu
Conflicts: gcc-doc
|Recommends: libc6-dev
Recommends: <libc-dev>
libc6-dev
Suggests: <gcc-multilib>
Suggests: make
make-guile
Suggests: manpages-dev
Suggests: autoconf
Suggests: automake
Suggests: libtool
Suggests: flex
Suggests: bison
Suggests: gdb
Suggests: gcc-doc
libc6-dev
の依存関係を見てみる:
dpkg -s libc6-dev
# --- output ---
Package: libc6-dev
Status: install ok installed
Priority: optional
Section: libdevel
Installed-Size: 9456
Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
Architecture: arm64
Multi-Arch: same
Source: glibc
Version: 2.39-0ubuntu8.5
Replaces: libc6 (<< 2.38-1ubuntu1)
Provides: libc-dev (= 2.39-0ubuntu8.5)
Depends: libc6 (= 2.39-0ubuntu8.5), libc-dev-bin (= 2.39-0ubuntu8.5), linux-libc-dev, libcrypt-dev, rpcsvc-proto
Suggests: glibc-doc, manpages-dev
Breaks: binutils (<< 2.38), catch (<< 1.12.2-0.1), heimdal-multidev (<= 7.7.0+dfsg-4), igblast (<= 1.19.0-1), libassimp-dev (<= 5.2.4~ds0-1), libasyncns-dev (<= 0.8-6build2), libatm1-dev (<= 1:2.5.1-4), libaws20-dev (<= 20.2-2+b1), libboinc-app-dev (<= 7.20.2+dfsg-1), libbson-dev (<= 1.22.0-1), libc6-dev-arm64-cross (<< 2.39~), libcups2-dev (<= 2.4.2-1), libdeal.ii-dev (<= 9.4.0-1), libdkim-dev (<= 1:1.0.21-4build3), libdolfin-dev-common (<= 2019.2.0~git20220407.d29e24d-5), libeckit-dev (<= 1.20.0-1), libfclib-dev (<= 3.1.0+dfsg-2), libfltk1.3-dev (<= 1.3.8-4+b1), libghc-resolv-dev (<= 0.1.2.0-3), libghc-resolv-prof (<= 0.1.2.0-3), libglib2.0-dev (<= 2.72.3-1), libgloox-dev (<= 1.0.24-2+b1), libhesiod-dev (<= 3.2.1-3.1+b1), libinfinity-0.7-dev (<= 0.7.2-1build1), libinsighttoolkit4-dev (<= 4.13.3withdata-dfsg2-3+b1), libinsighttoolkit5-dev (<= 5.2.1-5+b1), libismrmrd-dev (<= 1.8.0-2), libldap-dev (<= 2.5.12+dfsg-2), liblog4cplus-dev (<= 2.0.7-1), libloudmouth1-dev (<= 1.5.4-1), libmgl-dev (<= 8.0.1-2), libmimalloc2.0 (<= 2.0.6+ds-1), libminc-dev (<= 2.4.03-5), libmongoc-dev (<= 1.22.1-1), libmrpt-ros1bridge-dev (<= 1:2.4.9+ds-4+b2), libmysqlclient-dev (<= 8.0.29-1), libnetcdf-dev (<= 1:4.9.0-3), libnetcdf-mpi-dev (<= 1:4.9.0-1), libnetcdf-pnetcdf-dev (<= 1:4.9.0-1), libnfsidmap-dev (<= 1:2.6.1-2), libns3-dev (<= 3.36.1+dfsg-4), libola-dev (<= 0.10.8.nojsmin-2), libopenafs-dev (<= 1.8.8.1-3), libopendkim-dev (<= 2.11.0~beta2-7), libopendmarc-dev (<= 1.4.2-1), libopenms-dev (<= 2.6.0+cleaned1-3build4), libopenzwave1.6-dev (<= 1.6.1914+ds-1), libpg-query-dev (<= 13-2.1.2-2), librbl-dev (<= 2.11.0~beta2-7), libre-dev (<= 1.1.0-1build1), libshishi-dev (<= 1.0.2-11), libslurm-dev (<= 21.08.8.2-1), libsocksd0-dev (<= 1.4.2+dfsg-7build7), libspf2-dev (<= 1.2.10-7.1+b1), libstrophe-dev (<= 0.12.1-2), libtaningia-dev (<= 0.2.2-2), libtrilinos-amesos-dev (<= 13.2.0-3), libtrilinos-amesos2-dev (<= 13.2.0-3), libtrilinos-anasazi-dev (<= 13.2.0-3), libtrilinos-aztecoo-dev (<= 13.2.0-3), libtrilinos-belos-dev (<= 13.2.0-3), libtrilinos-epetra-dev (<= 13.2.0-3), libtrilinos-epetraext-dev (<= 13.2.0-3), libtrilinos-galeri-dev (<= 13.2.0-3), libtrilinos-ifpack-dev (<= 13.2.0-3), libtrilinos-ifpack2-dev (<= 13.2.0-3), libtrilinos-intrepid-dev (<= 13.2.0-3), libtrilinos-intrepid2-dev (<= 13.2.0-3), libtrilinos-isorropia-dev (<= 13.2.0-3), libtrilinos-kokkos-dev (<= 13.2.0-3), libtrilinos-kokkos-kernels-dev (<= 13.2.0-3), libtrilinos-komplex-dev (<= 13.2.0-3), libtrilinos-ml-dev (<= 13.2.0-3), libtrilinos-moertel-dev (<= 13.2.0-3), libtrilinos-muelu-dev (<= 13.2.0-3), libtrilinos-nox-dev (<= 13.2.0-3), libtrilinos-phalanx-dev (<= 13.2.0-3), libtrilinos-pike-dev (<= 13.2.0-3), libtrilinos-piro-dev (<= 13.2.0-3), libtrilinos-pliris-dev (<= 13.2.0-3), libtrilinos-rol-dev (<= 13.2.0-3), libtrilinos-rtop-dev (<= 13.2.0-3), libtrilinos-rythmos-dev (<= 13.2.0-3), libtrilinos-sacado-dev (<= 13.2.0-3), libtrilinos-shylu-dev (<= 13.2.0-3), libtrilinos-stokhos-dev (<= 13.2.0-3), libtrilinos-stratimikos-dev (<= 13.2.0-3), libtrilinos-teko-dev (<= 13.2.0-3), libtrilinos-teuchos-dev (<= 13.2.0-3), libtrilinos-thyra-dev (<= 13.2.0-3), libtrilinos-tpetra-dev (<= 13.2.0-3), libtrilinos-trilinoscouplings-dev (<= 13.2.0-3), libtrilinos-triutils-dev (<= 13.2.0-3), libtrilinos-xpetra-dev (<= 13.2.0-3), libtrilinos-zoltan2-dev (<= 13.2.0-3), libvbr-dev (<= 2.11.0~beta2-7), libvisp-dev (<= 3.5.0-2+b1), libvotca-dev (<= 2022-3), libvtk6-dev (<= 6.3.0+dfsg2-8.1+b1), libvtk7-dev (<= 7.1.1+dfsg2-10.2), open-vm-tools-dev (<= 2:12.0.5-2), pidgin-librvp (<= 0.9.7cvs-3), proftpd-dev (<= 1.3.7d+dfsg-2), slurm-wlm-basic-plugins-dev (<= 21.08.8.2-1)
Conflicts: libc0.3-dev, libc6.1-dev
Description: GNU C Library: Development Libraries and Header Files
Contains the symlinks, headers, and object files needed to compile
and link programs which use the standard C library.
Homepage: https://www.gnu.org/software/libc/libc.html

cargo run
で生成されたバイナリを調べる
続いて、cargo run
で生成されたバイナリを調べる。cargo run
を実行すると、カレントディレクトリの target/debug/<package name>
に実行バイナリが生成される。
ls -la target/debug
# --- output
total 3792
drwxrwxr-x 10 vagrant vagrant 320 Sep 13 13:43 .
drwxrwxr-x 5 vagrant vagrant 160 Sep 13 13:43 ..
drwxrwxr-x 2 vagrant vagrant 64 Sep 13 13:43 build
-rw-rw-r-- 1 vagrant vagrant 0 Sep 13 13:43 .cargo-lock
drwxrwxr-x 4 vagrant vagrant 128 Sep 13 13:43 deps
drwxrwxr-x 2 vagrant vagrant 64 Sep 13 13:43 examples
drwxrwxr-x 3 vagrant vagrant 96 Sep 13 13:43 .fingerprint
drwxrwxr-x 3 vagrant vagrant 96 Sep 13 13:43 incremental
# ↓ これ ↓
-rwxrwxr-x 2 vagrant vagrant 3877088 Sep 13 13:43 toy-tcpip-rs
-rw-rw-r-- 1 vagrant vagrant 93 Sep 13 13:43 toy-tcpip-rs.d
動的リンクを調べる
Hello, world!
の出力には write
システムコールが必要なので、libc が動的リンクされているはず。実行ファイルが動的リンクするライブラリを ldd
コマンドで一覧してみる。
ldd target/debug/toy-tcpip-rs
# --- output ---
linux-vdso.so.1 (0x0000ebc402c5e000)
libgcc_s.so.1 => /lib/aarch64-linux-gnu/libgcc_s.so.1 (0x0000ebc402b70000)
libc.so.6 => /lib/aarch64-linux-gnu/libc.so.6 (0x0000ebc4029b0000)
/lib/ld-linux-aarch64.so.1 (0x0000ebc402c21000)
libc.so.6
というのがまさに libc がリンクされているところ。

解釈が分からないので ChatGPT 5 を頼る。
行ごとの意味
ldd target/debug/toy-tcpip-rs
# --- output ---
linux-vdso.so.1 (0x0000ebc402c5e000)
libgcc_s.so.1 => /lib/aarch64-linux-gnu/libgcc_s.so.1 (0x0000ebc402b70000)
libc.so.6 => /lib/aarch64-linux-gnu/libc.so.6 (0x0000ebc4029b0000)
/lib/ld-linux-aarch64.so.1 (0x0000ebc402c21000)
linux-vdso.so.1 (0x0000ebc402c5e000)
- vDSO(virtual DSO)。ファイルはディスクに存在しない、カーネルが直接プロセスに挿す共有オブジェクト。
-
gettimeofday
など一部の syscalls をユーザー空間で高速化するための入り口。 - 右側のアドレスはマッピング先(毎回変わる=ASLR)。パスが無いのは“仮想”だから。
libgcc_s.so.1 => /lib/aarch64-linux-gnu/libgcc_s.so.1
-
GCC の低レベルランタイム。代表例は
- 例外/スタックアンワインドのルーチン(
_Unwind_*
) - いくつかの算術ビルトイン(アーキ依存)
- 例外/スタックアンワインドのルーチン(
- Rust バイナリでも
panic=unwind
のときや、一部ビルトインの都合で NEEDED に乗ることがある。
(panic=abort
にすると外れることも多いが、ケース次第)
libc.so.6 => /lib/aarch64-linux-gnu/libc.so.6
-
glibc 本体(C ランタイム/標準ライブラリ)。
write(2)
など OS への呼び出しの窓口。 -
println!
は最終的に 標準出力へ書く → libc 経由でカーネルへ、なのでここに依存。
/lib/ld-linux-aarch64.so.1
-
動的リンカ/ローダ(ELF インタプリタ)。プロセス起動時に読み込まれ、
- 依存
.so
を探す - シンボル解決・再配置を行う
- その後に
_start
へ制御を渡す
- 依存
-
バイナリの
PT_INTERP
フィールドでこのパスが指定されている。
(ldd
では “解決先”ではなくコイツ自身が行として出る)
補足:
/lib/aarch64-linux-gnu/
は Debian/Ubuntu の Multi-Arch ディレクトリ。アーキ別の標準ライブラリ置き場。
これらが示す“起動の流れ”(ざっくり)
-
execve
で起動 - カーネルが
PT_INTERP
を読んで 動的リンカ/lib/ld-linux-aarch64.so.1
をロード - 動的リンカが
libgcc_s.so.1
とlibc.so.6
を解決・マップ - カーネルが vDSO をマップ
- すべて解決後、
_start
→ Rust ランタイム →main
実行
この4行、要するに「カーネルの高速化おまけ(vDSO)+ C ランタイム(glibc)+ GCC ランタイム(場合により)+ 動的ローダ」ってこと。
ここまで見えたら、もう依存関係の“骨格”は掴めてる。次は readelf -d
の生ログを並べて、「NEEDED と ldd の対応表」を作ってもいいよ。

続いて ELF(Linux の実行ファイル形式)の「動的セクション(Dynamic Section)」を生で読んでみる。
以下のコマンドにより、実行ファイルの中には「依存ライブラリの名前」が書かれていることが分かる。ただし、依存ライブラリのパスまでは分からない。名前解決は実行時リンカ (ld-linux) がやる。
readelf -d target/debug/toy-tcpip-rs | grep NEEDED
# --- output ---
0x0000000000000001 (NEEDED) Shared library: [libgcc_s.so.1]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
出力結果を見ると、target/debug/toy-tcpip-rs
が共有ライブラリ libc.so.6
に依存していることが分かる。

続いて write
システムコールの存在をバイナリの中で確認したい。
Rust バイナリには write
システムコールの実装はなく、動的ライブラリである libc がこの実装を提供してくれる。ここでは、動的ライブラリ(共有ライブラリ)libc.so.6
内の write
のシンボルを探しに行く。シンボルを探すには nm -D <shared library>
というコマンドを使用する。
まずは実行バイナリ target/debug/toy-tcpip-rs
のシンボルテーブルを表示してみる。
出力を見ると、write
関数が U
(U write@GLIBC_2.17
)、つまり未定義となっており、これはどこか別のライブラリから解決されることを表す。ちなみに @GLIBC_2.17
は「最低でも glibc 2.xx が必要」ということを表す。
nm -D target/debug/toy-tcpip-rs
# --- output ---
U abort@GLIBC_2.17
U bcmp@GLIBC_2.17
U calloc@GLIBC_2.17
U close@GLIBC_2.17
w __cxa_finalize@GLIBC_2.17
w __cxa_thread_atexit_impl@GLIBC_2.18
U dl_iterate_phdr@GLIBC_2.17
U __errno_location@GLIBC_2.17
U fcntl@GLIBC_2.17
U free@GLIBC_2.17
U fstat64@GLIBC_2.33
U getauxval@GLIBC_2.17
U getcwd@GLIBC_2.17
U getenv@GLIBC_2.17
w __gmon_start__
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
U __libc_start_main@GLIBC_2.34
U lseek64@GLIBC_2.17
U malloc@GLIBC_2.17
U memcpy@GLIBC_2.17
U memmove@GLIBC_2.17
U memset@GLIBC_2.17
U mmap64@GLIBC_2.17
U mprotect@GLIBC_2.17
U munmap@GLIBC_2.17
U open64@GLIBC_2.17
U pause@GLIBC_2.17
U poll@GLIBC_2.17
U posix_memalign@GLIBC_2.17
U pthread_attr_destroy@GLIBC_2.17
U pthread_attr_getguardsize@GLIBC_2.34
U pthread_attr_getstack@GLIBC_2.34
U pthread_getattr_np@GLIBC_2.32
U pthread_key_create@GLIBC_2.34
U pthread_key_delete@GLIBC_2.34
U pthread_self@GLIBC_2.17
U pthread_setspecific@GLIBC_2.34
U read@GLIBC_2.17
U readlink@GLIBC_2.17
U realloc@GLIBC_2.17
U realpath@GLIBC_2.17
U sigaction@GLIBC_2.17
U sigaltstack@GLIBC_2.17
U signal@GLIBC_2.17
U stat64@GLIBC_2.33
w statx@GLIBC_2.28
U strlen@GLIBC_2.17
U syscall@GLIBC_2.17
U sysconf@GLIBC_2.17
U _Unwind_Backtrace@GCC_3.3
U _Unwind_DeleteException@GCC_3.0
U _Unwind_GetDataRelBase@GCC_3.0
U _Unwind_GetIP@GCC_3.0
U _Unwind_GetIPInfo@GCC_4.2.0
U _Unwind_GetLanguageSpecificData@GCC_3.0
U _Unwind_GetRegionStart@GCC_3.0
U _Unwind_GetTextRelBase@GCC_3.0
U _Unwind_RaiseException@GCC_3.0
U _Unwind_Resume@GCC_3.0
U _Unwind_SetGR@GCC_3.0
U _Unwind_SetIP@GCC_3.0
# ↓ write はここ ↓
U write@GLIBC_2.17
U writev@GLIBC_2.17
U __xpg_strerror_r@GLIBC_2.17

続いて、共有ライブラリである libc のシンボルテーブルを見に行く。libc のパスは ldd
コマンドの出力を見れば分かる。
ldd target/debug/toy-tcpip-rs
# --- output ---
linux-vdso.so.1 (0x0000ede54ecd3000)
libgcc_s.so.1 => /lib/aarch64-linux-gnu/libgcc_s.so.1 (0x0000ede54ebf0000)
# ↓ ここ ↓
libc.so.6 => /lib/aarch64-linux-gnu/libc.so.6 (0x0000ede54ea30000)
/lib/ld-linux-aarch64.so.1 (0x0000ede54ec96000)
libc (glibc) に対して、nm
コマンドを実行してシンボルテーブルを表示してみる。
nm -D /lib/aarch64-linux-gnu/libc.so.6 | grep write
# --- output ---
0000000000090c20 T aio_write@@GLIBC_2.34
0000000000090c20 T aio_write@GLIBC_2.17
0000000000090c20 T aio_write64@@GLIBC_2.34
0000000000090c20 T aio_write64@GLIBC_2.17
00000000000ebf40 T eventfd_write@@GLIBC_2.17
00000000000708a0 W fwrite@@GLIBC_2.17
000000000007b900 T fwrite_unlocked@@GLIBC_2.17
000000000007c9a0 T _IO_do_write@@GLIBC_2.17
000000000007d854 T _IO_file_write@@GLIBC_2.17
00000000000708a0 T _IO_fwrite@@GLIBC_2.17
0000000000076350 T _IO_wdo_write@@GLIBC_2.17
00000000000c6d10 T __libc_pwrite@@GLIBC_PRIVATE
00000000000ec5e0 T process_vm_writev@@GLIBC_2.17
00000000000c6d10 W pwrite@@GLIBC_2.17
00000000000c6d10 W __pwrite64@@GLIBC_2.17
00000000000c6d10 W pwrite64@@GLIBC_2.17
00000000000e8170 T pwritev@@GLIBC_2.17
00000000000e8270 T pwritev2@@GLIBC_2.26
00000000000e8170 T pwritev64@@GLIBC_2.17
00000000000e8270 T pwritev64v2@@GLIBC_2.26
00000000000e1d20 W __write@@GLIBC_2.17
00000000000e1d20 W write@@GLIBC_2.17
00000000000e24a0 T __write_nocancel@@GLIBC_PRIVATE
00000000000eb5e0 W writev@@GLIBC_2.17

解説は GPT 5 におまかせ。要点だけ。
-
write
は glibc が公開するデフォルト版 GLIBC_2.17 に解決される。 -
W
は弱い公開名、実装本体は別名(内部)にあり、同じアドレスに束ねられているのが普通。 -
@GLIBC_PRIVATE
はアプリ不可視の内部用(nocancel 系など)。 - 同一アドレスで
@@GLIBC_2.34
と@GLIBC_2.17
が並ぶのは、1つの実装を複数の ABI 版としてエクスポートして後方互換してるから。
ここまでで Rust の実行バイナリ → libc の write
までを追えた。
最高の“標本”が採れたね。これは glibc (libc.so.6
) が外部に公開している「write系」API の目録。
行ごとの記号とバージョン、エイリアス関係を噛み砕くよ。
まず記号の読み方
- 先頭の 16 進数: そのシンボルのオフセット(PIE なのでロード時ベース+この値が実アドレス)
-
T
: Text(実装あり)。このライブラリ内に本体がある -
W
: weak(弱い定義 or 弱い別名)。たいてい本体へのエイリアス(歴史的互換や内部実装の都合) -
@@GLIBC_x.y
: その名前の“デフォルト版”(通常アプリが解決するのはコレ) -
@GLIBC_x.y
: 非デフォルト版(古い互換 ABI 等) -
@GLIBC_PRIVATE
: アプリからリンク禁止の内部用(ld が解決しない、libc 内部だけで使う)
ざっくり分類と要点
A. 低レベル write 系(あなたが探してた“本丸”)
000... W __write@@GLIBC_2.17
000... W write@@GLIBC_2.17
000... T __write_nocancel@@GLIBC_PRIVATE
-
write
は デフォルト版が GLIBC_2.17。 -
W
(weak)なのは、実装本体が別名(内部名)にあり、公開名は弱い別名としてエクスポートしているため(glibc の流儀)。
実体はビルド時には__GI_write
などの内部記号に束ねられていることが多い。 -
__write_nocancel
は スレッドキャンセルポイントを発生させない内部用。libc 内部で自分たちが安全に呼ぶためのフックで、アプリからは見えない(GLIBC_PRIVATE
)。
B. ベクタ版/プロセス間版
... W writev@@GLIBC_2.17
... T pwritev@@GLIBC_2.17
... T pwritev2@@GLIBC_2.26
... T pwritev64@@GLIBC_2.17
... T pwritev64v2@@GLIBC_2.26
... T process_vm_writev@@GLIBC_2.17
-
writev
: iovec の配列を一度に書く。 -
pwritev
/pwritev2
: オフセット指定で書く(ファイル位置を動かさない)。v2
は追加フラグ対応(RWF_*
など)。 -
process_vm_writev
: 別プロセスのアドレス空間に書き込む特殊 API(ptrace 系の代替用途)。
@@GLIBC_2.26
等は 「その機能は glibc 2.26 以降の ABI」 を意味。古い glibc では解決できない。
C. pwrite 系(別名と内部実装)
... T __libc_pwrite@@GLIBC_PRIVATE
... W pwrite@@GLIBC_2.17
... W __pwrite64@@GLIBC_2.17
... W pwrite64@@GLIBC_2.17
- 公開名の
pwrite
/pwrite64
は weak エイリアス、本体は内部名(__libc_pwrite
など)にある典型パターン。 -
...64
は LFS(Large File Support)の歴史的互換。aarch64 では“中身は最初から 64bit 対応”だが、ABI 名として残っている。
fwrite
)と _IO_*
層
D. stdio(... W fwrite@@GLIBC_2.17
... T fwrite_unlocked@@GLIBC_2.17
... T _IO_fwrite@@GLIBC_2.17
... T _IO_do_write@@GLIBC_2.17
... T _IO_file_write@@GLIBC_2.17
... T _IO_wdo_write@@GLIBC_2.17
-
fwrite
は stdio(バッファ付き)。最終的にはwrite
相当へ落ちるが、_IO 層(glibc の FILE* 実装)が間に入る。 -
fwrite_unlocked
は ロック無し高速版(スレッドセーフ性を自分で担保する前提)。
E. 非同期 I/O / eventfd
... T aio_write@@GLIBC_2.34 / @GLIBC_2.17
... T aio_write64@@GLIBC_2.34 / @GLIBC_2.17
... T eventfd_write@@GLIBC_2.17
-
aio_write*
: POSIX AIO(glibc 実装はスレッドベースエミュが多い)。
**同じアドレスで 2 つの版(@@ と @)**が見えるのは、同一実装に複数の“シンボルバージョン”を割り当てて後方互換してるから。 -
eventfd_write
:eventfd
カウンタへ書くユーティリティ。
ここから読み取れること(Rust 目線)
- あなたの Rust バイナリの
U write@GLIBC_2.17
は、このwrite@@GLIBC_2.17
(デフォルト版)に解決される。 -
writev
も同様。println!
自体はwrite
/writev
どちらでもあり得るが、strace -e write,writev
で実測が一番早い。 -
weak
(W)は**“弱い別名として公開してる”というだけで、使えないわけではない。実体(T
を持つ内部名)に正しく束ねられている**。
合わせ技で“呼び出し経路”まで見る
-
実行時に何が呼ばれたか:
strace -e write,writev ./target/debug/toy-tcpip-rs
-
動的リンカがどの版に結びつけたか(明示):
readelf --dyn-syms /lib/aarch64-linux-gnu/libc.so.6 | grep -w ' write'
→write@@GLIBC_2.17
が“デフォルト版”であることが分かる -
write の実装を逆アセンブル:
objdump -d /lib/aarch64-linux-gnu/libc.so.6 | sed -n '/<write>/,/<.*>:/p' | head -n 40
→ AArch64 なら最終的にsvc #0
(システムコール発行)が見えるはず
まとめ(要点だけ)
-
write
は glibc が公開するデフォルト版GLIBC_2.17
に解決される。 -
W
は弱い公開名、実装本体は別名(内部)にあり、同じアドレスに束ねられているのが普通。 -
@GLIBC_PRIVATE
はアプリ不可視の内部用(nocancel 系など)。 - 同一アドレスで
@@GLIBC_2.34
と@GLIBC_2.17
が並ぶのは、1つの実装を複数の ABI 版としてエクスポートして後方互換してるから。

write
システムコールが実際に呼び出されているかを見る
最後に、実行バイナリを strace
で追って、write
システムコールが呼び出されているところを見る。
Hello, world!\n
という文字列は制御文字を含めて 14 byte なので、戻り値として write されたバイト数が返っている。
strace -o strace.write.log -e write target/debug/toy-tcpip-rs
cat strace.write.log
# --- output ---
write(1, "Hello, world!\n", 14) = 14
+++ exited with 0 +++
全体像はこちら:
strace -o strace.all.log target/debug/toy-tcpip-rs
cat strace.all.log
# --- output ---
execve("target/debug/toy-tcpip-rs", ["target/debug/toy-tcpip-rs"], 0xffffc2ec6e80 /* 23 vars */) = 0
brk(NULL) = 0xc283e9df1000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xe08588f13000
faccessat(AT_FDCWD, "/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=23703, ...}) = 0
mmap(NULL, 23703, PROT_READ, MAP_PRIVATE, 3, 0) = 0xe08588f0d000
close(3) = 0
openat(AT_FDCWD, "/lib/aarch64-linux-gnu/libgcc_s.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0\267\0\1\0\0\0\0\0\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0644, st_size=133696, ...}) = 0
mmap(NULL, 263104, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_DENYWRITE, -1, 0) = 0xe08588e99000
mmap(0xe08588ea0000, 197568, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0) = 0xe08588ea0000
munmap(0xe08588e99000, 28672) = 0
munmap(0xe08588ed1000, 33728) = 0
mprotect(0xe08588ebf000, 65536, PROT_NONE) = 0
mmap(0xe08588ecf000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1f000) = 0xe08588ecf000
close(3) = 0
openat(AT_FDCWD, "/lib/aarch64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0\267\0\1\0\0\0\360\206\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1722920, ...}) = 0
mmap(NULL, 1892240, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_DENYWRITE, -1, 0) = 0xe08588cd2000
mmap(0xe08588ce0000, 1826704, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0) = 0xe08588ce0000
munmap(0xe08588cd2000, 57344) = 0
munmap(0xe08588e9e000, 8080) = 0
mprotect(0xe08588e7a000, 77824, PROT_NONE) = 0
mmap(0xe08588e8d000, 20480, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x19d000) = 0xe08588e8d000
mmap(0xe08588e92000, 49040, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xe08588e92000
close(3) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xe08588f0b000
set_tid_address(0xe08588f0b0f0) = 7140
set_robust_list(0xe08588f0b100, 24) = 0
rseq(0xe08588f0b740, 0x20, 0, 0xd428bc00) = 0
mprotect(0xe08588e8d000, 12288, PROT_READ) = 0
mprotect(0xe08588ecf000, 4096, PROT_READ) = 0
mprotect(0xc283ba45d000, 12288, PROT_READ) = 0
mprotect(0xe08588f18000, 8192, PROT_READ) = 0
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
munmap(0xe08588f0d000, 23703) = 0
ppoll([{fd=0, events=0}, {fd=1, events=0}, {fd=2, events=0}], 3, {tv_sec=0, tv_nsec=0}, NULL, 0) = 0 (Timeout)
rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[PIPE], sa_flags=SA_RESTART}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
getrandom("\x23\xf1\xac\x7f\x02\xe3\x68\xee", 8, GRND_NONBLOCK) = 8
brk(NULL) = 0xc283e9df1000
brk(0xc283e9e12000) = 0xc283e9e12000
openat(AT_FDCWD, "/proc/self/maps", O_RDONLY|O_CLOEXEC) = 3
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
fstat(3, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
read(3, "c283ba400000-c283ba44d000 r-xp 0"..., 1024) = 1024
read(3, "/lib/aarch64-linux-gnu/libgcc_s."..., 1024) = 1024
read(3, "7000 rw-p 00000000 00:00 0 "..., 1024) = 60
close(3) = 0
sched_getaffinity(7140, 32, [0 1 2 3]) = 8
rt_sigaction(SIGSEGV, NULL, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
sigaltstack(NULL, {ss_sp=NULL, ss_flags=SS_DISABLE, ss_size=0}) = 0
mmap(NULL, 20480, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0xe08588f0e000
mprotect(0xe08588f0e000, 4096, PROT_NONE) = 0
sigaltstack({ss_sp=0xe08588f0f000, ss_flags=0, ss_size=16384}, NULL) = 0
rt_sigaction(SIGSEGV, {sa_handler=0xc283ba424c3c, sa_mask=[], sa_flags=SA_ONSTACK|SA_SIGINFO}, NULL, 8) = 0
rt_sigaction(SIGBUS, NULL, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGBUS, {sa_handler=0xc283ba424c3c, sa_mask=[], sa_flags=SA_ONSTACK|SA_SIGINFO}, NULL, 8) = 0
write(1, "Hello, world!\n", 14) = 14
sigaltstack({ss_sp=NULL, ss_flags=SS_DISABLE, ss_size=16384}, NULL) = 0
munmap(0xe08588f0e000, 20480) = 0
exit_group(0) = ?
+++ exited with 0 +++

ここまでで、バイナリ -> write まで

結論
build-essential
の中で Rust プログラムを書くのに最低限必要なのは、gcc
、libc6-dev
。
実用上は build-essential
を入れておいた方が良い。g++
、make
は各種ツールのビルドに使われていることがあるので、インストールしようとしたときにビルドが走って必要なツールが入ってなくてエラーになる、みたいなことが起きる。
あとソフトウェアのソースコードから何かをビルドするときは make
はほぼ必須級。
build-essential
- dpkg-dev (>= 1.17.11)
- Debian package development tools
-
.deb
を作りたい、.deb
を配布したいときに利用。
- g++ (>= 4:10.2)
- GNU C++ compiler
- C++ を書くときに必要。または C++ に依存しているライブラリをビルドするときに必要。
- gcc (>= 4:10.2)
- GNU C compiler
- libc
- libc6-dev
- GNU C Library: Development Libraries and Header Files
- or libc-dev
- virtual package provided by libc6-dev
- libc6-dev
- make
- utility for directing compilation
-
Makefile
でビルドのフローを定義するのに必要。
最後に build-essential
の各パッケージのメタデータを確認し、実際に最低限必要なものしか VM に入っていないことを確認する。
dpkg -s dpkg-dev
# dpkg-query: package 'dpkg-dev' is not installed and no information is available
# Use dpkg --info (= dpkg-deb --info) to examine archive files.
dpkg -s g++
# dpkg-query: package 'g++' is not installed and no information is available
# Use dpkg --info (= dpkg-deb --info) to examine archive files.
dpkg -s gcc
# Package: gcc
# Status: install ok installed
# ...
# Architecture: arm64
# Version: 4:13.2.0-7ubuntu1
# Provides: c-compiler
# Depends: cpp (= 4:13.2.0-7ubuntu1), cpp-aarch64-linux-gnu (= 4:13.2.0-7ubuntu1), gcc-13 (>= 13.2.0-11~), gcc-aarch64-linux-gnu (= 4:13.2.0-7ubuntu1)
# Recommends: libc6-dev | libc-dev
# Description: GNU C compiler
# This is the GNU C compiler, a fairly portable optimizing compiler for C.
# .
# This is a dependency package providing the default GNU C compiler.
dpkg -s libc6-dev
# Package: libc6-dev
# Status: install ok installed
# ...
# Architecture: arm64
# Version: 2.39-0ubuntu8.5
# Replaces: libc6 (<< 2.38-1ubuntu1)
# Provides: libc-dev (= 2.39-0ubuntu8.5)
# Depends: libc6 (= 2.39-0ubuntu8.5), libc-dev-bin (= 2.39-0ubuntu8.5), linux-libc-dev, libcrypt-dev, rpcsvc-proto
# Description: GNU C Library: Development Libraries and Header Files
# Contains the symlinks, headers, and object files needed to compile
# and link programs which use the standard C library.
# Homepage: https://www.gnu.org/software/libc/libc.html
# ...
dpkg -s make
# dpkg-query: package 'make' is not installed and no information is available
# Use dpkg --info (= dpkg-deb --info) to examine archive files.

ここまでで Rust の実行バイナリ → libc の write
までを追えた。
最後に、アセンブリレベルで syscall が呼ばれているところまで追いたい。

libssl-dev
TODO