crontab -e で設定したときのユーザのcrontabで環境変数PATHがどうなっているのか調べてみました。
環境はCentOS6.5です。

crontabのPATHは貧弱であった

crontab -e して
*/1 * * * * printenv > /tmp/printenv.txt
このように設定して、1分後に作成されたファイルを見てみると、
$ cat /tmp/printenv.txt
SHELL=/bin/sh
USER=vagrant
PATH=/usr/bin:/bin
PWD=/home/vagrant
LANG=en_US.UTF-8
SHLVL=1
HOME=/home/vagrant
LOGNAME=vagrant
_=/usr/bin/printenv
貧弱ぅ貧弱ぅ!な環境になっておりました。
PATHが/usr/binと/binだけというのは何ともお粗末ですね・・・。

私は最新のPerlをplenvを使って /opt/plenv/shims/perl のようなパスに入れてるのですが、このままだとcrontabにフルパスを書かねばなりません。(まあそれでもいいのですが)

crontab内でPATHを設定する

crontabは冒頭で任意の環境変数をセットすることができるので、PATHを自分で定義できるかどうか試してみました。
PATH=/opt/plenv/shims:/usr/bin:/bin

*/1 * * * * perl -v > /tmp/perlversion.txt
1分まって結果を見てみると、
$ cat /tmp/perlversion.txt

This is perl 5, version 18, subversion 2 (v5.18.2) built for x86_64-linux
できました!
PATHの一番左に記述した/opt/plenv/shimsが優先されて、見事に最新版のperlが呼び出されました!
find . -type f | xargs perl -pi -e 's/use strict/use v5.18/g'
Perlは1年に1回くらいしか(がっつりは)書かないので、触れるたびに「モダンPerl」という言葉が指すものが変わっている気がします。
勉強会とかWebで情報収集しておかないと自分の知識がすぐ陳腐化してしまいそう。
Shellscriptを書いていて、
diff <(cmd1) <(cmd2)
みたいなdiff呼び出しをループの中でやっていたら、表題のエラーが出ました。
どうやら、diffでプロセス置換を使うと、diffコマンドを呼び出すたびにファイルディスクリプタを新規作成してしまうみたいで、1プロセスあたりのファイルディスクリプタ使用数の上限に達してしまったのではないかと推測されます。

解決方法

diffで比較するのをやめて、普通に文字列比較するようにしたら解決しました。
if [ $(cmd1) == $(cmd2) ]  ; then
    echo ok
else
    echo ng
fi
PODを日本語で書く方法を紹介します。

PODを普通に日本語で書いて、普通にperldoc hoge.pl すると日本語が"XXX"になる上にgroffが古いみたいな警告が出てしまいます。
$ perldoc  ~/pod.pl
You have an old groff. Update to version 1.20.1 for good Unicode support.
If you don't upgrade, wide characters may come out oddly.

 at /opt/plenv/versions/5.18.2/lib/perl5/5.18.2/Pod/Perldoc.pm line 1346.
You have an old groff. Update to version 1.20.1 for good Unicode support.
If you don't upgrade, wide characters may come out oddly.

 at /opt/plenv/versions/5.18.2/lib/perl5/5.18.2/Pod/Perldoc.pm line 1346.
POD(1)                User Contributed Perl Documentation               POD(1)



NAME
       XXXXXXXXXX

SINOPSYS
       XXXXXXXXXXXXX

日本語でPODを書く方法

  • utf8で書いて保存する
  • =encoding utf8と書く
  • perldoc -t で表示する
日本語PODのサンプルコード
pod.pl
#!/usr/bin/env perl

print "Hello World\n";

=pod

=encoding utf8

=head1 NAME

はろーわーるどです。

=head1 SINOPSYS

とくに使い方はありません。

=cut
実行結果
$ perldoc -t  pod.pl
NAME
    はろーわーるどです。

SINOPSYS
    とくに使い方はありません。
やったー!
.git/indexを解釈するパーサをC言語で書きました。
git ls-files --stage とほぼ同じ働きをします。

全部自前で書いたのではなくて、本家Gitのソースコードから使えそうなところだけ切り貼りしてつなぎ合わせたような感じです。
この辺の仕組みは、Git初期にLinus Torvaldsが書いたコードがまだそのまま使われていたりして大変感慨深いですね。

なお、.git/indexのデータ構造のついてはこちらの記事をご覧ください。
[Git].git/indexのデータ構造を詳しく調べてみた

使い方

gcc parse_git_index.c -o parse_git_index
./parse_git_index  path/to/.git/index
Gitの「インデックス(ステージング)」情報を保持している.git/indexファイルについて詳しく調べてみました。

要約

  • .git/indexは、"ステージング/インデックス"情報を保持してるバイナリファイルである。
  • 中身を表示するには git ls-files --stage で表示できる

.git/indexファイルの構造

例として"minigit"プロジェクトの.git/indexをとりあげます。
$ git clone git://github.com/DQNEO/minigit.git
$ cd minigit
$ git checkout
git ls-files --stageで中身を表示
$ git ls-files --stage | head
100644 95ba71b8e7c2d70965d9a5086a9fabd58e87d259 0       .gitignore
100644 960a0c393e5e493e911ac24ac42d6eb125409263 0       Makefile
100644 9d7f6b8213c452a8e698ccb5557cdf5d36ccf25c 0       README.md
100644 0392373249530f848adca4413ed159a6cde3887e 0       c_samples/is_substr.c
100644 b439e0a12503c7744ed2fb8da7dd3bf1a541acae 0       c_samples/parse_git_index.c
hexdump -Cで生のバイナリデータを表示
$ hexdump -C .git/index  | head
00000000  44 49 52 43 00 00 00 02  00 00 00 29 53 32 25 dd  |DIRC.......)S2%.|
00000010  1c 22 62 97 53 32 25 dd  1c 22 62 97 00 00 fd 00  |."b.S2%.."b.....|
00000020  01 a4 07 e1 00 00 81 a4  00 00 01 f5 00 00 00 00  |................|
00000030  00 00 00 71 95 ba 71 b8  e7 c2 d7 09 65 d9 a5 08  |...q..q.....e...|
00000040  6a 9f ab d5 8e 87 d2 59  00 0a 2e 67 69 74 69 67  |j......Y...gitig|
00000050  6e 6f 72 65 00 00 00 00  00 00 00 00 53 32 25 dd  |nore........S2%.|
00000060  1c 22 62 97 53 32 25 dd  1c 22 62 97 00 00 fd 00  |."b.S2%.."b.....|
00000070  01 a4 07 e2 00 00 81 a4  00 00 01 f5 00 00 00 00  |................|
00000080  00 00 00 b5 96 0a 0c 39  3e 5e 49 3e 91 1a c2 4a  |.......9>^I>...J|
00000090  c4 2d 6e b1 25 40 92 63  00 08 4d 61 6b 65 66 69  |.-n.%@.c..Makefi|

データ構造

  • ファイルの中身はヘッダ部とボディ部からなりたっている。
  • ヘッダ部は固定長12バイト。
  • ボディ部はエントリの連続体になっている。
  • エントリとは、ファイル1個を表す情報である。
// ヘッダー
44 49 52 43 // シグネチャ("DIRC")
00 00 00 02 // ヘッダのバージョン (=2)
00 00 00 29 // エントリ数 0x29 (=41個)
// ここから各エントリ
// ここからファイル".gitignore"の情報
53 32 25 dd // ctime sec
1c 22 62 97 // ctime nano sec
53 32 25 dd // m_time sec
1c 22 62 97 // m_time nano sec
00 00 fd 00 // dev
01 a4 07 e1 // inode
00 00 81 a4 // mode (=100644)
00 00 01 f5 // uid (=501)
00 00 00 00 // guid (=0)
00 00 00 71 // size (=27)
95 ba 71 b8  e7 c2 d7 09 65 d9 a5 08 6a 9f ab d5 8e 87 d2 59 // sha1
00 0a       // namelen (=10 ".gitignore"は10文字)
2e 67 69 74 69 67 6e 6f 72 65 // ".gitignore"
00 00 00 00  00 00 00 00 // zero padding
// ここからファイル"Makefile"の情報
53 32 25 dd // ctime sec
以下、同じように続く

おまけ:壊れたときの対処法

まれに壊れることがあるが、簡単に作り直せる
rm .git/index
git reset

参考

デフォルトだとシステムコールのmanは入ってないようです。
$ man 2 stat
No entry for stat in section 2 of the manual
"man-pages"というパッケージをインストールすれば見れるようになります。
sudo yum install man-pages
http://q.hatena.ne.jp/1382454839
"読み取り権限がなく実行権限だけのファイルが実行できるのはなぜ? - カーネルのソースを読む - - 猫型の蓄音機は 1 分間に 45 回にゃあと鳴く"
こちらの記事が大変素晴らしかったので、実際の実行時システムコール呼び出しを追ってみました。
平たく言うと、strace・システムコールの入門です。

バイナリ実行するのに読み取り権限は必要ないことを実験で確かめる

C言語でhello worldを書いてみます。
hello.c
#include <stdio.h>

int main()
{
  printf("hello world\n");
  return 0;
}
コンパイルして、パーミッションを100にします。
$ gcc hello.c -o hello
$ chmod 0100 hello
$ ls -l
合計 12
---x------ 1 vagrant vagrant 6417  3月 30 01:43 2014 hello*
-rw-r--r-- 1 vagrant vagrant   74  3月 30 01:42 2014 hello.c
helloバイナリに読み取り権限がないことを確認しました。
では実行します。
$ ./hello
hello world
おお!
実行できました!!
ちょっと感動。
今まで実行ファイルはなんとなく755にしてたのですが、今後はもうちょっとパーミッションを厳しくしてもいいのかも。

スクリプト実行するのに読み取り権限が必要であることを実験で確かめる

お次はPerlスクリプトでhello worldを作ってみます。
hello.pl
#!/usr/bin/perl

print "hello world\n";
パーミッションを100にして実行してみます。
$ chmod 0100 hello.pl
$ ls -l hello.pl
---x------ 1 vagrant vagrant 40  3月 30 01:50 2014 hello.pl*
$ ./hello.pl
Can't open perl script "./hello.pl": 許可がありません
実行することが、できませんでしたぁぁ!
書き込み権限を付与しても
$ chmod 0300 hello.pl
$ ./hello.pl
Can't open perl script "./hello.pl": 許可がありません
実行することが、できませんでしたぁぁ!

読み取り+実行権限を付与したら
$ chmod 0500 hello.pl
$ ./hello.pl
hello world
実行できました!

straceでバイナリファイル実行のシステムコールを追ってみる

さて、実際のシステムコール呼び出しを見てみましょう。 straceコマンドを使います。
$ strace ./hello
execve("./hello", ["./hello"], [/* 28 vars */]) = 0
brk(0)                                  = 0x13d7000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f58523c1000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY)      = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=31577, ...}) = 0
mmap(NULL, 31577, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f58523b9000
close(3)                                = 0
open("/lib64/libc.so.6", O_RDONLY)      = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0000\356\1\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1921216, ...}) = 0
mmap(NULL, 3750152, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f5851e0f000
mprotect(0x7f5851f9a000, 2093056, PROT_NONE) = 0
mmap(0x7f5852199000, 20480, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x18a000) = 0x7f5852199000
mmap(0x7f585219e000, 18696, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f585219e000
close(3)                                = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f58523b8000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f58523b7000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f58523b6000
arch_prctl(ARCH_SET_FS, 0x7f58523b7700) = 0
mprotect(0x7f5852199000, 16384, PROT_READ) = 0
mprotect(0x7f58523c2000, 4096, PROT_READ) = 0
munmap(0x7f58523b9000, 31577)           = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 2), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f58523c0000
write(1, "hello world\n", 12hello world
)           = 12
exit_group(0)                           = ?
冒頭でexexveシステムコールが./helloファイルを引数にして呼ばれていることがわかります。
一方、./helloバイナリファイルに対してopenやreadなどの呼び出しはされていません。
バイナリファイルを実行するのに読み取り権限が必要ないことがこれで納得がいきます。

straceでスクリプト実行のシステムコールを追ってみる

次はstraceでhello.plを実行します。
わざと失敗させるために、パーミッションを100にしてからstraceします。
$ chmod 0100 hello.pl
$ strace ./hello.pl
execve("./hello.pl", ["./hello.pl"], [/* 28 vars */]) = 0
brk(0)                                  = 0x17e3000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe174cff000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/usr/lib64/perl5/CORE/tls/x86_64/libperl.so", O_RDONLY) = -1 ENOENT (No such file or directory)
stat("/usr/lib64/perl5/CORE/tls/x86_64", 0x7fff8744e2c0) = -1 ENOENT (No such file or directory)
open("/usr/lib64/perl5/CORE/tls/libperl.so", O_RDONLY) = -1 ENOENT (No such file or directory)
stat("/usr/lib64/perl5/CORE/tls", 0x7fff8744e2c0) = -1 ENOENT (No such file or directory)
open("/usr/lib64/perl5/CORE/x86_64/libperl.so", O_RDONLY) = -1 ENOENT (No such file or directory)
stat("/usr/lib64/perl5/CORE/x86_64", 0x7fff8744e2c0) = -1 ENOENT (No such file or directory)
open("/usr/lib64/perl5/CORE/libperl.so", O_RDONLY) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\20M\3\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1485896, ...}) = 0
mmap(NULL, 3581608, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fe174776000
mprotect(0x7fe1748d8000, 2097152, PROT_NONE) = 0
mmap(0x7fe174ad8000, 36864, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x162000) = 0x7fe174ad8000
close(3)                                = 0
open("/usr/lib64/perl5/CORE/libresolv.so.2", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY)      = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=31577, ...}) = 0
mmap(NULL, 31577, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fe174cf7000
close(3)                                = 0
open("/lib64/libresolv.so.2", O_RDONLY) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\00009\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=110960, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe174cf6000
mmap(NULL, 2202248, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fe17455c000
mprotect(0x7fe174572000, 2097152, PROT_NONE) = 0
mmap(0x7fe174772000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x16000) = 0x7fe174772000
mmap(0x7fe174774000, 6792, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fe174774000
close(3)                                = 0
open("/usr/lib64/perl5/CORE/libnsl.so.1", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/lib64/libnsl.so.1", O_RDONLY)    = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0p@\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=113432, ...}) = 0
mmap(NULL, 2198192, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fe174343000
mprotect(0x7fe174359000, 2093056, PROT_NONE) = 0
mmap(0x7fe174558000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x15000) = 0x7fe174558000
mmap(0x7fe17455a000, 6832, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fe17455a000
close(3)                                = 0
open("/usr/lib64/perl5/CORE/libdl.so.2", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/lib64/libdl.so.2", O_RDONLY)     = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\340\r\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=19536, ...}) = 0
mmap(NULL, 2109696, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fe17413f000
mprotect(0x7fe174141000, 2097152, PROT_NONE) = 0
mmap(0x7fe174341000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2000) = 0x7fe174341000
close(3)                                = 0
open("/usr/lib64/perl5/CORE/libm.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/lib64/libm.so.6", O_RDONLY)      = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0p>\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=596264, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe174cf5000
mmap(NULL, 2633912, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fe173ebb000
mprotect(0x7fe173f3e000, 2093056, PROT_NONE) = 0
mmap(0x7fe17413d000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x82000) = 0x7fe17413d000
close(3)                                = 0
open("/usr/lib64/perl5/CORE/libcrypt.so.1", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/lib64/libcrypt.so.1", O_RDONLY)  = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0\f\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=40400, ...}) = 0
mmap(NULL, 2318816, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fe173c84000
mprotect(0x7fe173c8b000, 2097152, PROT_NONE) = 0
mmap(0x7fe173e8b000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x7000) = 0x7fe173e8b000
mmap(0x7fe173e8d000, 184800, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fe173e8d000
close(3)                                = 0
open("/usr/lib64/perl5/CORE/libutil.so.1", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/lib64/libutil.so.1", O_RDONLY)   = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\20\16\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=14584, ...}) = 0
mmap(NULL, 2105600, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fe173a81000
mprotect(0x7fe173a83000, 2093056, PROT_NONE) = 0
mmap(0x7fe173c82000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1000) = 0x7fe173c82000
close(3)                                = 0
open("/usr/lib64/perl5/CORE/libpthread.so.0", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/lib64/libpthread.so.0", O_RDONLY) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\340]\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=142640, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe174cf4000
mmap(NULL, 2212848, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fe173864000
mprotect(0x7fe17387b000, 2097152, PROT_NONE) = 0
mmap(0x7fe173a7b000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x17000) = 0x7fe173a7b000
mmap(0x7fe173a7d000, 13296, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fe173a7d000
close(3)                                = 0
open("/usr/lib64/perl5/CORE/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/lib64/libc.so.6", O_RDONLY)      = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0000\356\1\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1921216, ...}) = 0
mmap(NULL, 3750152, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fe1734d0000
mprotect(0x7fe17365b000, 2093056, PROT_NONE) = 0
mmap(0x7fe17385a000, 20480, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x18a000) = 0x7fe17385a000
mmap(0x7fe17385f000, 18696, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fe17385f000
close(3)                                = 0
open("/usr/lib64/perl5/CORE/libfreebl3.so", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/lib64/libfreebl3.so", O_RDONLY)  = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0@<\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=469528, ...}) = 0
mmap(NULL, 2582368, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fe173259000
mprotect(0x7fe1732ca000, 2093056, PROT_NONE) = 0
mmap(0x7fe1734c9000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x70000) = 0x7fe1734c9000
mmap(0x7fe1734cc000, 14176, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fe1734cc000
close(3)                                = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe174cf3000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe174cf2000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe174cf1000
arch_prctl(ARCH_SET_FS, 0x7fe174cf2700) = 0
mprotect(0x7fe1734c9000, 8192, PROT_READ) = 0
mprotect(0x7fe17385a000, 16384, PROT_READ) = 0
mprotect(0x7fe173a7b000, 4096, PROT_READ) = 0
mprotect(0x7fe173c82000, 4096, PROT_READ) = 0
mprotect(0x7fe173e8b000, 4096, PROT_READ) = 0
mprotect(0x7fe17413d000, 4096, PROT_READ) = 0
mprotect(0x7fe174341000, 4096, PROT_READ) = 0
mprotect(0x7fe174558000, 4096, PROT_READ) = 0
munmap(0x7fe174cf7000, 31577)           = 0
set_tid_address(0x7fe174cf29d0)         = 19648
set_robust_list(0x7fe174cf29e0, 0x18)   = 0
futex(0x7fff8744ebbc, FUTEX_WAKE_PRIVATE, 1) = 0
futex(0x7fff8744ebbc, FUTEX_WAIT_BITSET_PRIVATE|FUTEX_CLOCK_REALTIME, 1, NULL, 7fe174cf2700) = -1 EAGAIN (Resource temporarily unavailable)
rt_sigaction(SIGRTMIN, {0x7fe173869c60, [], SA_RESTORER|SA_SIGINFO, 0x7fe173873710}, NULL, 8) = 0
rt_sigaction(SIGRT_1, {0x7fe173869cf0, [], SA_RESTORER|SA_RESTART|SA_SIGINFO, 0x7fe173873710}, NULL, 8) = 0
rt_sigprocmask(SIG_UNBLOCK, [RTMIN RT_1], NULL, 8) = 0
getrlimit(RLIMIT_STACK, {rlim_cur=10240*1024, rlim_max=RLIM_INFINITY}) = 0
rt_sigaction(SIGFPE, {SIG_IGN, [FPE], SA_RESTORER|SA_RESTART, 0x7fe1735029a0}, {SIG_DFL, [], 0}, 8) = 0
brk(0)                                  = 0x17e3000
brk(0x1804000)                          = 0x1804000
getuid()                                = 501
geteuid()                               = 501
getgid()                                = 501
getegid()                               = 501
open("/usr/lib/locale/locale-archive", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=99154480, ...}) = 0
mmap(NULL, 99154480, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fe16d3c9000
close(3)                                = 0
open("/dev/urandom", O_RDONLY)          = 3
read(3, "\355\216m\263", 4)             = 4
close(3)                                = 0
readlink("/proc/self/exe", "/usr/bin/perl", 4095) = 13
ioctl(0, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 opost isig icanon echo ...}) = 0
lseek(0, 0, SEEK_CUR)                   = -1 ESPIPE (Illegal seek)
ioctl(1, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 opost isig icanon echo ...}) = 0
lseek(1, 0, SEEK_CUR)                   = -1 ESPIPE (Illegal seek)
ioctl(2, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 opost isig icanon echo ...}) = 0
lseek(2, 0, SEEK_CUR)                   = -1 ESPIPE (Illegal seek)
open("./hello.pl", O_RDONLY)            = -1 EACCES (Permission denied)
write(2, "Can't open perl script \"./hello."..., 55Can't open perl script "./hello.pl": Permission denied
) = 55
rt_sigaction(SIG_0, NULL, {0x9f0ed807, [], SA_RESTORER|SA_RESTART|SA_INTERRUPT|SA_NODEFER|SA_NOCLDWAIT|0xaeae58, (nil)}, 8) = -1 EINVAL (Invalid argument)
rt_sigaction(SIGHUP, NULL, {SIG_DFL, [], 0}, 8) = 0
冒頭でexecveが呼ばれているのはさっきのバイナリ実行と同じですが、最後の方でhello.plに対してopenシステムコールがO_RDONLYモードで呼ばれており、これが "Permission denied" で失敗しています。
なので、hello.plを実行するには読み取り権限が必要なのだとわかります。
考えてみれば、Perlインタープリタがhello.plを解釈するためにファイルオープンするのだがら、当然といえば当然ですね。

まとめ

straceたのしい!

Emacsでgdbを使ってデバグする方法

| カテゴリ:
題材として、Linux + Emacs + gdbでImageMagickの挙動を調べてみましょう。

Emacsとgdbをインストールする

たいていのLinuxではgdbはデフォルトでは入っていないので、yumなりapt-getなりでインストールしましょう。

ImageMagickをデバグオプション付きでビルドする

ftp://ftp.kddlabs.co.jp/graphics/ImageMagick/ からtar.gzを落としてきてコンパイルします。

今回は実験なのでシステムワイドな環境を汚さないために /tmp/ImageMagick/ 下にインストールします。
GDBデバグ可能にするために、./configureのところで"-g3 -O0"オプションをつけるのがポイントです。
コンパイルオプションに"-g3"をつけるとできます。
"-O0" (オーゼロ)もあわせてつけておくとよいでしょう。
gccを直接たたく場合
gcc -g3 -O0 -o foo foo.c
./configureする場合
./configure CFLAGS="-g3 -O0" --prefix=/usr/local

参考

GDBでデバッグするなら-g3オプション - ククログ(2013-05-08)
購読する

人気記事

最近の人気記事