signal SEGV (access to address exceeded protections)2015年03月03日 01時08分46秒

プロセスが正体不明のクラッシュをする。同じコードでもプログラムによって、問題なかったり、クラッシュをしたりする場合がある。また、デバッガによっては問題ない時もあった。そこで、dbx で動かしたら、signal SEGV (access to address exceeded protections) が出て止まった。

原因は、スタックに大きい構造体の配列を作ったことだった。特にスレッドプログラムの各々のスタックサイズを越えてしまったかららしい。修正は、malloc/calloc と free を用いてヒープからメモリを割り当てること。

Prevent Multiple Include in GNU Make2013年09月20日 12時50分03秒

GNU Make は随分と元の Make に手が加わっていて全く別物になっている。変数に値を設定する方法もいくつもあって、かなりややこしい。GNU Make を使いこなすには include が肝になる。

さて、この include だが、いわゆる C/C++ のヘッダーファイルの様に、無造作に読み込みたいが、複数回読み込むと問題が起きる。まず、変数に += を使っていると、無限に増えていくこと、また開けるファイルに上限があるなど、悩ましい。

GNU Make には条件式もあるのだが、この動作がややこしく、さらになやましいものにしている。なお、条件には ifndef の様に定義の有無を調べるものと、infeq の様に変数の値を調べる物がある。

そして、実験。四つのファイルを準備して、少しずつ違う式を書く。二つは ifndef で、あとのものは infeq で。そして、各々で :== を使う。:= は、読み込んでいる最中に評価され、読み込まれた時点で即刻、値が代入される。= の値の代入は遅延型で、全てのファイルを読み込んだ後になる。大きな違いは、VAR = $(VAR) 等と、自身に代入できないことだ。これは無限ループになり値を決められない。


% cat ifndef:=.mk
ifndef INCLUDED
INCLUDED := 1

include ifndef:=.mk

endif
% cat ifndef=.mk
ifndef INCLUDED
INCLUDED = 1

include ifndef=.mk

endif
% cat ifneq:=.mk
ifneq ( $INCLUDED, 1 )
INCLUDED := 1

include ifneq:=.mk

endif
% cat ifneq=.mk
ifneq ( $INCLUDED, 1 )
INCLUDED = 1

include ifneq=.mk

endif

さて、この四つの場合の各々の意図は汲めたであろうか。なるべく判りやすい様に、ファイルにそのままの名前を付けてみた。

同じ順に実行した結果はこうなる。


% gmake -f ifndef:=.mk
gmake: *** No targets.  Stop.
% gmake -f ifndef=.mk
gmake: *** No targets.  Stop.
% gmake -f ifneq=.mk
ifneq=.mk:4: ifneq=.mk: Too many open files
gmake: *** No targets.  Stop.
% gmake -f ifneq:=.mk
ifneq:=.mk:4: ifneq:=.mk: Too many open files
gmake: *** No targets.  Stop.

ifndef で既に定義されているかだと、一度しか読み込まれない。そのため、ターゲットが無く終了している。ifneq で変数の値を調べようとしたが、読み込んでいる最中はどちらの代入式でも効果が無いようだ。そして、ファイルが開けなくなって終了している。

つまり、C/C++ の様に複数回読み込まれるのを防ぐには ifndef を用いる必要がある。


ifndef INCLUDED
INCLUDED = 1

include ifndef=.mk

endif

実はがっかりした結果だった。ifneq を用いると、一回目に読み込まれる動作と二回目に読み込まれる動作を変えられるかと期待したが、狸の皮算用だった。

-xs を用いずにデバッグシンボルを組み込むのを抑制する2013年08月04日 11時20分32秒

動作するプログラムを書くことを出来る人は大勢いるが、正しくコンパイルする事、効率的にコンパイル、リンクをする事が出来る人はほとんどいない。大部分の人達が、自分達が毎日、自分で何を行なっているかを把握していないのだ。知らず知らずのうちに、無駄を毎日の様に繰り返していることもしばしば。少しの変更で、大きな無駄を省けることも多々ある。一人で一割、二割の時間を有効に使えるようになるとしよう。それが、百人に及んだら事は重大だ。

Solaris には -xs というコンパイルオプションがある。これによりオブジェクトファイルなしに dbx でデバッグできるようになる。これを用いると、各々のオブジェクトファイルに入っているデバッグ情報が、実行ファイルへ全てコピーされるのだ。これが、リンクオプションで無いのは若干の不思議。

さて、このオプションを有効にするには -xs を指定する必要がある。これは、コードや中間生成物を共に移さない、本番系に有効なオプションだ。

つまり、このオプションを利用しないと、プログラムのサイズが大幅に小さくなる。元の一割やそれ以下の大きさにまでなってしまった等というのは日常茶飯事だ。

これを省くと、プログラムのリンクの時間が大幅に減る。デバッガの起動と再読み込みの時間も大きく減る。そして、ディスクの圧迫も回避となる。

もちろん、これを使わない不利益は、オブジェクトファイルが必要なこと。何かあった時に心配との事で、常に指定している組織も多い。

逆にいえば、一般的な開発環境では必要無い。そして、本番環境にオブジェクトファイルが無くても、core ファイルを持ってくれば、しっかりとデバッグが出来る。つまり、コードとオブジェクトファイルとしっかりと保持する運用体勢を確立していれば、用いなくても問題はない。または、本番用にのみ指定する運用でもいい。

成果物からは、弘法筆を選ばずの世界なのだが、その過程には、過ぎたるは猶及ばざるが如し、そして、労多くして功少なしの世界でもあるのだ。

なお、-xs は -xdebugformat=stabs を指定し、stabs 形式のデバッグ情報を用いる時に有効になる。-xdebugformat=dwarf で古い型の dwarf 形式を用いている場合には無意味だ。

AIX はある筋の情報によると stabs で、実行ファイル外にデバッグ情報を置くことは出来るが、dbx がオブジェクトファイルからその情報を読むことがまだ出来ない為、似たようなオプションは無いそうだ。

getopts でシェルスクリプトの引数を処理2012年10月17日 13時30分38秒

シェルスクリプトでコマンドの引数を処理するのは getopts がいい。第一引数がオプションの指定で、追加の引数を取るときにはコロンを後に付ける。

使い方はほとんど雛型定型。


#!/bin/sh

while getopts a:b:c: flag
do
    case $flag in
        a)
            echo $flag "'$OPTARG'"
        ;;
        b)
            echo $flag "'$OPTARG'"
        ;;
        c)
            echo $flag "'$OPTARG'"
        ;;
        ?)
            echo $flag "'$OPTARG'"
        ;;
    esac
done

shift `expr $OPTIND - 1``

最後の shift で処理した分の引数を飛ばす。

セミコロンを指定したときには、必ず引数を与えなければいけない。そうしないとずれてしまう。もし、何も渡さないのであれば、二つのシングルクォートか、ダブルクォートを用いる。


sun $ sh getopts.sh -a AA -b -c CC
a 'AA'
b '-c'
sun $ sh getopts.sh -a AA -b '' -c CC
a 'AA'
b ''
c 'CC'

Solaris の elf_find_sym で動的ロードで2012年01月07日 12時44分07秒

Solaris で elf_find_sym が SIGSEGV を起こして、プログラムが止まってしまう。何と古来からある標準 C ライブラリ関数を呼び出そうとしている時に、プログラムが落ちる。全ての環境で起きるわけでもなく、特定のバイナリのみで起きる。取り敢えず、見つけたことをまとめてみた。今回の場合は、strtok を呼び出す瞬間に落ちるので、かなり厄介だ。

Solaris ではバイナリの実行中に呼び出す関数がまだ組み込まれていなければ、 elf_find_sym を通して関数をロードして行く。libc 等は、共有ライブラリとして、リンクされているので典型的な例だ。nm で実行ファイルを調べると、strtok を参照しているのが分る。ldd では、libc を必要としているのが判る。

elf_find_sym 関数内で落ちるので、デバッガでブレイクポイントを仕掛けて、幾つも観察しているとやはり、プログラムの実行を初めてから最初の関数呼び出しのときに、呼び出され共有ライブラリから関数を読みだしているのが確認できた。つまり、プログラム起動時に、全てのシンボルを読み込んでおくのではなく、必要になるたびに、随時読み込んでいく遅延方式になる。

コンパイル時には、何のエラーもない。また、リンク時にも問題はない。実行時に関数を呼び出そうとしている時に問題があるので、ldd などの動的ロード関連かとは思うのだが、調べても有効なものが見付からず困っていた。Solaris のバグだとは思うのだが、確証が掴めず取り敢えずのところは別の実装を静的に組み込むことで回避した。三ヵ月ぐらい放置したところで、再度試したところ再現しなくなっていたのだが…。

月の最終 X 曜日の日付2011年10月12日 18時09分45秒

月の最終 X 曜日の日付を求める。例えば、2011 年 11 月の最後の金曜日は 28 日だ。このぐらいだったら、cal の手助けを得れば、大したことは無い。

% cal
    October 2011      
Su Mo Tu We Th Fr Sa  
                   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

cal の出力は曜日によって固定されている。そこで、最終月曜日を得るには最後の列の二行目を取得すればいい。monday の値を順次更新していく。

% cal | awk '{monday=$2}END{print monday}'
31

ところがこれでは、最後の列に日付が無いと狂ってしまう。そこで、行数を点検する。


% cal | awk 'NF>=6{friday=$6}END{print friday}'
28

trap で sh 内の signal を捕捉2011年09月29日 12時39分43秒

trap コマンド シグナル名 [シグナル名]という書式で指定できる。signal(2) に加えてシグナル 0 があり、スクリプト終了時に送出される。スクリプト終了時には exit での終了も含まれる。

% trap "rm -f /tmp/tmp.$$" HUP INT QUIT TERM

デフォルト動作に戻すには単にコマンドに - か空行を設定する。


% trap - HUP INT QUIT TERM
% trap HUP INT QUIT TERM

C++ での covariant return type2010年07月18日 12時01分33秒

C++ では、派生型の戻り値で関数をオーバーライドすることが出来る。多態性の例。
struct A
{
    virtual A* f() { return NULL; }
};

struct B : public A
{
    virtual B* f() { return NULL; }
};

同じテンプレートクラスを二度基底クラスにしてみる2010年06月06日 11時41分19秒

今回もまた、同じクラスを基底クラスとして二回指定してみる。すこし違うところはテンプレートクラスだと言うこと。今回のはコンパイルは通るはずだ。


% cat b.cpp 
template
class A
{
};

class B: public A<1>, public A<2>
{
};

int main()
{
}
sun% CC b.cpp
sun%
frebsd% g++ b.cpp
freebsd%
ibm% xlC_r a.cpp
ibm%
hp% aCC a.cpp
hp%

全てのプラットフォームで成功。

前回

同じクラスを二度基底クラスにしてみる2010年06月05日 11時33分24秒

同じクラスを基底クラスとして二度指定したらどうなるか試してみた。これ自体はエラーが出るのは十中八九だが、これ自体は次の実験のための布石。

% cat a.cpp 
class A
{
};

class B: public A, public A
{
};

int main()
{
}
sun% CC a.cpp 
"a.cpp", line 6: Error: The base class A is included more than once.
1 Error(s) detected.
freebsd% g++ a.cpp 
a.cpp:6: error: duplicate base type 'A' invalid
ibm% xlC_r a.cpp
"a.cpp", line 55.17: 1540-0409 (S) "A" must not be used more than once in the lis
t of base classes.
hp% aCC a.cpp
"a.cpp", line 5:error #2263: duplicate base class name
  class B: public A, public A
                            ^


1 error detected in the compilation of "a.cpp".

全てのプラットフォームで失敗。

次回