tech.guitarrapc.cóm

Technical updates

git や ssh が Win32 error 487: Couldn't reserve space for cygwin's heap, Win32 error 0 で実行できない場合の対処

PowerShell から ssh 経由で Linux に入って任意のコマンドを実行する。

これを可能にするために、sshをラップしたモジュールを以前書きました。

これを利用することで sshでLinux サーバーにログインする手間をかけることなく、ぽちっとLinux上のCapisranoを実行したりとか自由にできます。

裏では PowerShellからssh実行してコマンドを発行しているわけですが、今回ssh自体ができなくなる問題にあったので、対処をご紹介します。

すぐに解決できましたが、んーって悩んだのと、ssh だけじゃなく git でも起こりえるので。

目次

エラーメッセージ

sshを実行すると、このエラーですぐに終了してしまいます。git.exe などでも一緒です。

    0 [main] us 0 init_cheap: VirtualAlloc pointer is null, Win32 error 487
AllocationBase 0x0, BaseAddress 0x68560000, RegionSize 0x390000, State 0x10000
C:\Program Files\Git\bin\sh.exe: *** Couldn't reserve space for cygwin's heap, Win32 error 0

原因

git も ssh も内部で cygwin を使っているのですが、これが参照する msys-1.0.dll に問題があります。

この辺とかが参考になります。

何も考えずに作っていると、みんな同じアドレスにロードしようとして、アドレスの再計算が発生するからシャレにならないよってことでした。

この辺にも言及があります。

副作用が引き金となる問題のうち、一般によく知られている例は、Windows NT 4.0 の Service Pack 4 で発生したものです。多くのカスタマから、SP4 をインストールした後、アプリケーションでメモリのアクセス違反が発生するという報告がありました。私が調べたほぼ全てのケースで、クラッシュしたアプリケーションは、再割り当てのため、呼び出しによって開放または移動されたメモリ アドレスを使っていました。旧バージョンのヒープ マネージャでは、プログラマはアクセス違反を起こさずに、たいていこのコーディング上のエラーを逃れることができました。SP4 では、そのバグがすぐに表面化してしまうのです。副作用 DLL Hell は、新しい DLL のバグではなく、DLL の副作用(よくある例では DLL の持つ既存のバグ)に依存しているアプリケーションによって起こります。そして、その副作用はサービスパックなどによって解消してしまう可能性がある訳です。 アプリケーションが依存する副作用は、SP4 のメモリ バグのようなコーディング エラーではない場合もあります。例えば、クラスのデータ メンバをドキュメント化せずに使っているプログラマがたくさんいます。今後のバージョンでは、そのメンバをプライベートにするか、あるいは別の構造体に移動するかもしれません。

あとはこれでしょうか。

ベースアドレスが設定されていない3つのDLLを同一プロセス空間にロード 1つのEXEファイルがベースアドレスを設定していない複数のDLLを利用する場合, プロセス(EXEファイル)が実行を開始する度にリロケーションが実行されます. このため,DLLの数が多ければ多いほど,起動時間が遅くなることになります. このオーバヘッドを回避するには,各DLLのベースアドレスをあらかじめ衝突しない アドレスに設定しておく必要があります.

ようは、msys-1.0.dll のアドレス割り当て問題。

対処

いくつかパターンがあります。

Stack Overflow ではこのような方法が提示されています。

Windows再起動

msys-1.0.dllが利用するメモリのアドレス空間割り当ての問題なので、うまく行く場合はもちろんありますよね。ダメな場合はダメですし、一時しのぎですけど。

msys-1.0.dll の差し替え

もし自分の環境で、msys-1.0.dllが複数あった場合、差し替えてうまく行く場合があります。私の場合はダメでしたが。

ようは、この問題が起こっていない msys-1.0.dll にめぐりあれば治ったように見えるってことです。これも一時しのぎ。

環境変数の pathを別のmsys-1.0.dll が優先して参照されるように変更してみる

これもdllの差し替えと意図していることは 一緒です。

msys-1.0.dllを複数持っている場合に、ファイルの読み込みで利用される 環境変数の $env:PATH *1 で、C:\Program Files (x86)\git\bin などを優先して指すことで、もしそのパスに正常な msys-1.0.dll があればうまくいくと。

これまた一時しのぎ。

やるなら rebase

一時しのぎではなく、問題の根本を対処すればいいのです。

で、利用するのが rebase.exe です。rebase といっても、 git rebase とはまったく違います。Windows の rebase です。

これですね。

私も自分のお仕事で作っているライブラリ類はNT 3.1時代にrebaseの重要性を懇々と説かれたので、特に共有するようなDLLはもれなくrebaseしています。

この msys-1.0.dll の問題に関して、msysgitのIssue #133 でやり取りを見つけることができます。

msysgit - Issue 133: Can't run Git bash (Win32 error 487)

今回 rebase で行うのは、レジストリ操作のようなWindows OSに変化を与えるわけではなく、msys-1.0.dll が利用するメモリアドレスを変更するだけです。

復旧手順

手順を紹介します。

  1. rebase.exe があるパスで、PowerShell か cmdを起動する
  2. msys-1.0.dllに対して、次のrebaseコマンドを実行する。*2
rebase -b 0x30000000 msys-1.0.dll

以上です。

私の場合は、 chocolatey で、 msysgit と poshgit を入れて、sourcetreeを利用している環境で起こりました。

msys-1.0.dll も rebase.exe も C:\Program Files (x86)\git\bin にあったので、PowerShellを起動したパスはC:\Program Files (x86)\git\binです。

まとめ

はい。rebase これまで知らなかったのですが、大事です。

問題さえわかっていればすぐに直せるので、もし知らなくて見ていただいた方は安心していただければと思います。

ssh は他のPCからでも....ですが、git.exe もできないのは困りますからね。

*1:バッチふぁいるなら%PATH%

*2:0x30000000 は任意のメモリアドレスなので、 0x50000000 などでもok