目次: Linux
前回(2026年1月23日の日記参照)はshebang(ファイル先頭で#!から始まるおまじない)がカーネルにインタプリタを指定する役割があって、間違っているとshebangとして認識されずにexecveシステムコールが失敗することを示しました。
今回はexecveが失敗する理由を追ってカーネル側のコードを見ようと思います。調査に使ったのはlinux-5.15です。このバージョンを選んだ意味は特にないです。そんなに変わる部分でもないし、どのバージョンでもほぼ一緒だと思います。
スクリプト実行形式をカーネルに登録するのは下記の部分です。
// linux/fs/binfmt_script.c
static struct linux_binfmt script_format = {
.module = THIS_MODULE,
.load_binary = load_script,
};
static int __init init_script_binfmt(void)
{
register_binfmt(&script_format);
return 0;
}
このregister_binfmt()関数は、カーネル内部にあるformatsリストに引数に渡したstruct linux_binfmtを追加するだけです。あとでexecveシステムコールの処理内でこのリストを頭からサーチする処理が出てきます。
スクリプト形式以外にはa.out(fs/binfmt_aout.c), ELF(fs/binfmt_elf.c), FLAT(fs/binfmt_flat.c)形式などが登録されています。
次はexecveシステムコールを見ていきます。アーキテクチャ依存部分を除くと、こんな経路を辿ってやってきます。
do_execve()
do_execveat_common()
bprm_execve()
exec_binprm()
search_binary_handler()
load_script()
呼び出し経路が深いので全部紹介はできないですけど、execveがエラーになる原因は最後のload_scriptを見るとわかります。
// linux/fs/binfmt_script.c
static int load_script(struct linux_binprm *bprm)
{
const char *i_name, *i_sep, *i_arg, *i_end, *buf_end;
struct file *file;
int retval;
/* Not ours to exec if we don't start with "#!". */
if ((bprm->buf[0] != '#') || (bprm->buf[1] != '!'))
return -ENOEXEC;
//...
ファイルの内容が入っているbprm->bufの1文字目と2文字目が#!であることをチェックしています。読み飛ばす処理は一切なく、#!以外の文字があると必ず失敗する実装になっています。わかりやすいですね。
以上のようにshebangが間違っているとexecveシステムコールが失敗します。しかし世の中のシェルは救済措置を持っていて、shebangが間違ったスクリプトでも実行できてしまいます。次はdashを例にシェル側のコードを紹介したいと思います。
前回UNIX shなどの処理についてコメントもらった話と一緒になってしまいますけど、気にしないでください。
この記事にコメントする
目次: Linux
スクリプトの先頭(例えばシェルスクリプトなど)に書く"#!〜"から始まるおまじないのことをshebangと言います。一見すると単なるコメントですが、カーネルに対してスクリプトを解釈するインタプリタを指定する役割を果たします。Linuxの場合は必ず1文字目が#、2文字目が!でなければなりません。それ以外の文字はshebangとして認識されません。
カーネルはexecveシステムコール内でshebangを解釈しますので、shebangが間違っていてカーネルが解釈できないときはexecveシステムコールがエラーを返します。正しいスクリプトok.shと間違ったスクリプトng.shの2つを用意して実験しましょう。
#!/bin/dash
echo aaaa
##!/bin/dash
echo aaaa
間違っている方(ng.sh)は#が1つ余計に付いていることが分かると思います。2つのスクリプトを実行しstraceで追ってみます。
$ strace ./ok.sh 2>&1 | head
execve("./ok.sh", ["./ok.sh"], 0x7ffcda167180 /* 47 vars */) = 0
brk(NULL) = 0x56295818e000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa96499b000
access("/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
...
$ strace ./ng.sh
execve("./ng.sh", ["./ng.sh"], 0x7ffc39d6c8f0 /* 47 vars */) = -1 ENOEXEC (Exec format error)
strace: exec: Exec format error
+++ exited with 1 +++
正しいスクリプトはexecveシステムコールが成功しますが、間違ったスクリプトはexecveシステムコールが失敗してENOEXEC(実行形式エラー)で止まりました。
一見するとng.shは実行できなさそうですが、不思議なことに両スクリプトに実行権限を付けて(chmod +xなど)実行してみると、どちらも正しく動いてしまいます。
$ ./ok.sh aaaa $ ./ng.sh aaaa
実行時のシェルの動作をstraceで追うと、execveは失敗してエラーが返っています。おそらくシェルが気を利かせ「こいつはスクリプトだ」と思って実行しているのでしょう。続きはまたの機会に調べます。
この記事にコメントする
ソニーがテレビ事業を分離するニュース(ソニーはなぜ、テレビ事業を「分離」するのか - 中国TCLをパートナーに選んだ"必然性" - ITmedia)を見ました。ざっと各社の歴史を見てみると、
シャープやパナソニックはテレビ作っているとはいえ、韓国&中国勢に逆転できる目はなさそうです。日系では比較的グローバル販売で強かったソニーすら陥落ですから、日本メーカー全滅したと言っても過言ではないでしょう。
昔、テレビ作る仕事に携わっていた身としては悲しいものがありますが、これも時代の流れですかね。
日本のテレビメーカー、正確には日本の電機メーカーのテレビ事業部門が輝いていた時代は、地デジ化(※)の少し後くらいまででした。2003年から地デジ化で強制的に買い替え特需を起こし、将来の買い替え需要を先食いしたのも良くなかったし、特需が終わることを考えずテレビ事業に投資したのが凋落の原因とも言われています(予測できた「地デジ特需」終了 テレビ巨額投資の謎 - 日本経済新聞(2014年7月))。
地デジ化が完了した2012年以降、1兆円overだった買い替え需要が2000億円くらいまで一気に冷え込み、テレビが全く売れなくなりました。メーカー各社は北米やら欧州やらグローバル向け販売を目指すも、韓国の2大メーカーSamsungとLG、追いかけてきた中国メーカーが強すぎて全然ダメでした。やがて電機メーカー各社は赤字まみれのテレビ事業を手放し、今日の全滅コースへ至ります。
振り返れば、特需と需要を勘違いして不要な投資をし、グローバル販売に失敗して爆死しただけです。しかし地デジ化の当時は「テレビが足りなくて地デジ化が間に合わねえ!すまんな!!」なんてことはおそらく許されなかったし、テレビメーカーは投資してとにかく作る以外の選択はなかったのでしょう。たぶんきっと。
(※)地上波アナログ放送の停波、それに伴う地上波放送のデジタル化のこと。当時は「地デジ化」が略称でした。
この記事にコメントする
目次: LLVM
LLVMの公式サイト(リンク)にある手順そのものなんですけど、いつもググっていて面倒くさくなってきたのでメモしておきます。
sudo apt-get install lsb-release gnupg wget software-properties-common wget https://apt.llvm.org/llvm.sh chmod +x llvm.sh sudo ./llvm.sh 21
あと./llvm.sh 21の後にallを付けるとLLVM以外のツールもインストールできるみたいです。必要に応じてどちらでも。
この記事にコメントする
| < | 2026 | > | ||||
| << | < | 01 | > | >> | ||
| 日 | 月 | 火 | 水 | 木 | 金 | 土 |
| - | - | - | - | 1 | 2 | 3 |
| 4 | 5 | 6 | 7 | 8 | 9 | 10 |
| 11 | 12 | 13 | 14 | 15 | 16 | 17 |
| 18 | 19 | 20 | 21 | 22 | 23 | 24 |
| 25 | 26 | 27 | 28 | 29 | 30 | 31 |
wiki
Linux JM
Java API
2002年
2003年
2004年
2005年
2006年
2007年
2008年
2009年
2010年
2011年
2012年
2013年
2014年
2015年
2016年
2017年
2018年
2019年
2020年
2021年
2022年
2023年
2024年
2025年
2026年
過去日記について
アクセス統計
サーバ一覧
サイトの情報合計:
本日: