質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

Windows

Windowsは、マイクロソフト社が開発したオペレーティングシステムです。当初は、MS-DOSに変わるOSとして開発されました。 GUIを採用し、主にインテル系のCPUを搭載したコンピューターで動作します。Windows系OSのシェアは、90%を超えるといわれています。 パソコン用以外に、POSシステムやスマートフォンなどの携帯端末用、サーバ用のOSもあります。

Q&A

解決済

1回答

2293閲覧

fatal error: invalid stack pointerの原因

toko

総合スコア144

Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

Windows

Windowsは、マイクロソフト社が開発したオペレーティングシステムです。当初は、MS-DOSに変わるOSとして開発されました。 GUIを採用し、主にインテル系のCPUを搭載したコンピューターで動作します。Windows系OSのシェアは、90%を超えるといわれています。 パソコン用以外に、POSシステムやスマートフォンなどの携帯端末用、サーバ用のOSもあります。

3グッド

1クリップ

投稿2016/04/21 05:41

編集2016/05/28 08:46

###質問内容
現在Go言語でプログラミングを勉強中で、cgoでDLLの関数を呼び出すようなプログラムを作っています。

呼び出したいライブラリproprietarydll.dllは下記のproprietarydll.hにあるような関数を持っており、
こちらをGo言語から呼びだそうとしていました。
ところが、proprietarydll.dllの関数を呼び出した後しばらくすると、
**"fatal error: invalid stack pointer"**というエラーが出てプログラムが終了してしまいました。

エラーはGCの際に起きているようだったので、
proprietarydll.dllの関数呼び出しの後にGCを呼び出すcrash.goのようなプログラムを書いたところ、
GCが行われた時に同様のエラーが発生していることが確認できました。

しかし、エラーが発生している箇所はわかったのですが、
このエラーが何故起きているのかはわかりませんでした。

類似した質問もなく、修正のための原因がわからず、質問しました。

このエラーの原因は何でしょうか。

(※なお、DLLは頂いたもので、中で何をやっているのかは分かりません。
もともとはC/C++から使っていたライブラリであったようです。)

###発生している問題・エラーメッセージ
複数回funcCを呼び出すプログラムの吐くエラーから、
GCの前後でエラーが起きている様子である、というところまではわかりました。

したがって、下記のようにCの関数呼び出しの前後にruntime.GC()を呼び出すと、
同様のエラー**"fatal error: invalid stack pointer"**を出してプログラムが落ちました。

エラーメッセージは次のとおりです。

before: GC(funcB) 0xe630020 after: GC(funcB) 0xe630020 before: GC(funcC) 0x1a0 runtime: bad pointer in frame main.main at 0x222cdf0c: 0x1a0 fatal error: invalid stack pointer runtime stack: runtime.throw(0x4eeb10, 0x15) C:/Go/src/runtime/panic.go:547 +0x7f fp=0x2a2fa0c sp=0x2a2fa00 runtime.adjustpointers(0x222cdf0c, 0x2a2facc, 0x2a2fcac, 0x50e784) C:/Go/src/runtime/stack.go:579 +0x235 fp=0x2a2fa80 sp=0x2a2fa0c runtime.adjustframe(0x2a2fc3c, 0x2a2fcac, 0x2a2fb01) C:/Go/src/runtime/stack.go:644 +0x163 fp=0x2a2fae8 sp=0x2a2fa80 runtime.gentraceback(0x44aa80, 0x222cdcf8, 0x0, 0x222b8000, 0x0, 0x0, 0x7fffffff, 0x506e60, 0x2a2fcac, 0x0, ...) C:/Go/src/runtime/traceback.go:369 +0xbce fp=0x2a2fc68 sp=0x2a2fae8 runtime.copystack(0x222b8000, 0x1000) C:/Go/src/runtime/stack.go:759 +0x161 fp=0x2a2fda0 sp=0x2a2fc68 runtime.shrinkstack(0x222b8000) C:/Go/src/runtime/stack.go:1026 +0x122 fp=0x2a2fdb8 sp=0x2a2fda0 runtime.markroot(0x5) C:/Go/src/runtime/mgcmark.go:155 +0x23e fp=0x2a2fe0c sp=0x2a2fdb8 runtime.gcDrain(0x2a2fe44, 0x0) C:/Go/src/runtime/mgcmark.go:812 +0x26a fp=0x2a2fe34 sp=0x2a2fe0c runtime.gchelper() C:/Go/src/runtime/mgc.go:1806 +0x87 fp=0x2a2fe64 sp=0x2a2fe34 runtime.stopm() C:/Go/src/runtime/proc.go:1541 +0x10c fp=0x2a2fe74 sp=0x2a2fe64 runtime.findrunnable(0x222b4a00, 0x0) C:/Go/src/runtime/proc.go:1976 +0x62a fp=0x2a2febc sp=0x2a2fe74 runtime.schedule() C:/Go/src/runtime/proc.go:2075 +0x202 fp=0x2a2fedc sp=0x2a2febc runtime.park_m(0x222b84e0) C:/Go/src/runtime/proc.go:2140 +0x163 fp=0x2a2fef0 sp=0x2a2fedc runtime.mcall(0x933658) C:/Go/src/runtime/asm_386.s:255 +0x47 fp=0x2a2fef8 sp=0x2a2fef0 goroutine 1 [copystack]: runtime.systemstack_switch() C:/Go/src/runtime/asm_386.s:267 fp=0x222cdcfc sp=0x222cdcf8 runtime.gcMarkTermination() C:/Go/src/runtime/mgc.go:1182 +0x121 fp=0x222cdec4 sp=0x222cdcfc runtime.gcStart(0x2, 0x222c2000) C:/Go/src/runtime/mgc.go:1018 +0x3f4 fp=0x222cdee4 sp=0x222cdec4 runtime.GC() C:/Go/src/runtime/mgc.go:840 +0x26 fp=0x222cdef0 sp=0x222cdee4 main.main() C:/dev/crash/crash.go:40 +0x3b4 fp=0x222cdfa8 sp=0x222cdef0 runtime.main() C:/Go/src/runtime/proc.go:188 +0x234 fp=0x222cdfd0 sp=0x222cdfa8 runtime.goexit() C:/Go/src/runtime/asm_386.s:1585 +0x1 fp=0x222cdfd4 sp=0x222cdfd0 goroutine 17 [syscall, locked to thread]: runtime.goexit() C:/Go/src/runtime/asm_386.s:1585 +0x1 fp=0x222cbfd4 sp=0x222cbfd0 goroutine 2 [force gc (idle)]: runtime.gopark(0x506f84, 0x55b488, 0x4e7b40, 0xf, 0x14, 0x1) C:/Go/src/runtime/proc.go:262 +0x130 fp=0x222befa4 sp=0x222bef90 runtime.goparkunlock(0x55b488, 0x4e7b40, 0xf, 0x222b8014, 0x1) C:/Go/src/runtime/proc.go:268 +0x4b fp=0x222befc0 sp=0x222befa4 runtime.forcegchelper() C:/Go/src/runtime/proc.go:229 +0xaa fp=0x222befd8 sp=0x222befc0 runtime.goexit() C:/Go/src/runtime/asm_386.s:1585 +0x1 fp=0x222befdc sp=0x222befd8 created by runtime.init.4 C:/Go/src/runtime/proc.go:218 +0x2a goroutine 3 [GC sweep wait]: runtime.gopark(0x506f84, 0x55b500, 0x4e70b0, 0xd, 0x41c914, 0x1) C:/Go/src/runtime/proc.go:262 +0x130 fp=0x222bff98 sp=0x222bff84 runtime.goparkunlock(0x55b500, 0x4e70b0, 0xd, 0x14, 0x1) C:/Go/src/runtime/proc.go:268 +0x4b fp=0x222bffb4 sp=0x222bff98 runtime.bgsweep(0x222aa0c0) C:/Go/src/runtime/mgcsweep.go:63 +0xa0 fp=0x222bffd0 sp=0x222bffb4 runtime.goexit() C:/Go/src/runtime/asm_386.s:1585 +0x1 fp=0x222bffd4 sp=0x222bffd0 created by runtime.gcenable C:/Go/src/runtime/mgc.go:191 +0x52 goroutine 4 [finalizer wait]: runtime.gopark(0x506f84, 0x56b1e8, 0x4e7aa0, 0xe, 0x14, 0x1) C:/Go/src/runtime/proc.go:262 +0x130 fp=0x222c0f84 sp=0x222c0f70 runtime.goparkunlock(0x56b1e8, 0x4e7aa0, 0xe, 0x14, 0x1) C:/Go/src/runtime/proc.go:268 +0x4b fp=0x222c0fa0 sp=0x222c0f84 runtime.runfinq() C:/Go/src/runtime/mfinal.go:158 +0x9e fp=0x222c0fd8 sp=0x222c0fa0 runtime.goexit() C:/Go/src/runtime/asm_386.s:1585 +0x1 fp=0x222c0fdc sp=0x222c0fd8 created by runtime.createfing C:/Go/src/runtime/mfinal.go:139 +0x5c

###該当のソースコード
proprietarydll.h

C

1#include <winsock2.h> 2#include <windows.h> 3 4// dummy value 5#define SUCCESS 12345 6 7// funcA is called to initialize this dll. 8DWORD funcA(); 9 10// funcB returns a handle for funcC 11HANDLE funcB(); 12 13// funcC requires a handle returned by funcB 14HANDLE funcC(HANDLE);

crash.go

go

1package main 2 3import ( 4 "fmt" 5 "log" 6 "runtime" 7) 8 9/* 10#cgo LDFLAGS: -L. -lproprietarydll 11#cgo CFLAGS: -I . 12#include "proprietarydll.h" 13*/ 14import "C" 15 16func main() { 17 if status, err := C.funcA(); status != C.SUCCESS || err != nil { 18 log.Fatal(err) 19 } 20 21 handleB, err := C.funcB() 22 if handleB == nil || err != nil { 23 if err != nil { 24 log.Fatal(err) 25 } 26 log.Fatal("handleB is nil") 27 } 28 fmt.Println("before:\tGC(funcB)", handleB) 29 runtime.GC() // <- ここはエラーが出ない 30 fmt.Println("after:\tGC(funcB)", handleB) 31 32 handleC, err := C.funcC(handleB) 33 if handleC == nil || err != nil { 34 if err != nil { 35 log.Fatal("handleC is nil") 36 } 37 return 38 } 39 fmt.Println("before:\tGC(funcC)", handleC) 40 runtime.GC() // <- ここはエラーが出る 41 fmt.Println("after:\tGC(funcC)", handleC) 42}

###補足情報(言語/FW/ツール等のバージョンなど)
OS: Windows 7 64bit
言語: Go 1.6.2 32bit

dsk, ikuwow, maisumakun👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

自己解決

エラー原因

Windows APIにおいて使われるHANDLEは、Windows Data Typesによるとvoid *として定義されています。このvoid *はGoではunsafe.Pointerに相当し、ポインタとして扱われます。しかし、Windows APIが返す実際の値は、ある特定のデータを指すポインタではなく、Handle TableというHandleを管理するテーブルのインデックスになっています。

ここで、runtime.GC()によってGCを実行すると、開放すべきオブジェクトを探すためにポインタならばその先をたどる必要があります。ところがHANDLEの中身は有効なアドレスではありません。この結果エラーが出ているようです。

参考に、HANDLEを返すWindows APIを使って、質問文よりももっと短いプログラムで同じようなエラーが再現できます。
再現プログラム

Go

1package main 2 3import ( 4 "fmt" 5 "log" 6 "runtime" 7) 8 9/* 10#include <windows.h> 11HANDLE getMailslot() { 12 return CreateMailslot("\\\\.\\mailslot\\mailslotname", 0, 0, NULL); 13} 14*/ 15import "C" 16 17func main() { 18 m, err := C.getMailslot() 19 if err != nil { 20 log.Fatal(err) 21 } 22 fmt.Println("before: ", m) 23 runtime.GC() 24 fmt.Println("after: ", m) 25}

エラー出力

before: 0x198 runtime: bad pointer in frame main.main at 0xc082031e90: 0x198 fatal error: invalid stack pointer runtime stack: runtime.throw(0x52da20, 0x15) C:/Go/src/runtime/panic.go:530 +0x97 fp=0x7ef680 sp=0x7ef668 runtime.adjustpointers(0xc082031e90, 0x7ef800, 0x7efb28, 0x554568) C:/Go/src/runtime/stack.go:579 +0x2e2 fp=0x7ef778 sp=0x7ef680 runtime.adjustframe(0x7efa48, 0x7efb28, 0x7ef801) C:/Go/src/runtime/stack.go:644 +0x1d2 fp=0x7ef838 sp=0x7ef778 runtime.gentraceback(0x453af0, 0xc082031c08, 0x0, 0xc08201c000, 0x0, 0x0, 0x7fffffff, 0x546f08, 0x7efb28, 0x0, ...) C:/Go/src/runtime/traceback.go:369 +0xda3 fp=0x7efaa0 sp=0x7ef838 runtime.copystack(0xc08201c000, 0x2000) C:/Go/src/runtime/stack.go:759 +0x1c5 fp=0x7efc90 sp=0x7efaa0 runtime.shrinkstack(0xc08201c000) C:/Go/src/runtime/stack.go:1026 +0x170 fp=0x7efcc0 sp=0x7efc90 runtime.markroot(0x5) C:/Go/src/runtime/mgcmark.go:155 +0x2d2 fp=0x7efd50 sp=0x7efcc0 runtime.gcDrain(0x7efdc0, 0x0) C:/Go/src/runtime/mgcmark.go:812 +0x24f fp=0x7efd88 sp=0x7efd50 runtime.gcMark(0xdd715aff0e34) C:/Go/src/runtime/mgc.go:1559 +0x100 fp=0x7efde8 sp=0x7efd88 runtime.gcMarkTermination.func1() C:/Go/src/runtime/mgc.go:1173 +0x2a fp=0x7efdf8 sp=0x7efde8 runtime.systemstack(0x5a9e00) C:/Go/src/runtime/asm_amd64.s:291 +0x7e fp=0x7efe00 sp=0x7efdf8 runtime.mstart() C:/Go/src/runtime/proc.go:1048 fp=0x7efe08 sp=0x7efe00 goroutine 1 [copystack]: runtime.systemstack_switch() C:/Go/src/runtime/asm_amd64.s:245 fp=0xc082031c10 sp=0xc082031c08 runtime.gcMarkTermination() C:/Go/src/runtime/mgc.go:1181 +0x14b fp=0xc082031e20 sp=0xc082031c10 runtime.gcStart(0x2, 0xc082026000) C:/Go/src/runtime/mgc.go:1017 +0x3b5 fp=0xc082031e48 sp=0xc082031e20 runtime.GC() C:/Go/src/runtime/mgc.go:840 +0x2c fp=0xc082031e60 sp=0xc082031e48 main.main() C:/dev/sample/main.go:23 +0x22d fp=0xc082031f40 sp=0xc082031e60 runtime.main() C:/Go/src/runtime/proc.go:188 +0x27e fp=0xc082031f90 sp=0xc082031f40 runtime.goexit() C:/Go/src/runtime/asm_amd64.s:1998 +0x1 fp=0xc082031f98 sp=0xc082031f90 goroutine 17 [syscall, locked to thread]: runtime.goexit() C:/Go/src/runtime/asm_amd64.s:1998 +0x1 fp=0xc082025fa8 sp=0xc082025fa0 goroutine 2 [force gc (idle)]: runtime.gopark(0x547160, 0x5a98f0, 0x51a050, 0xf, 0x14, 0x1) C:/Go/src/runtime/proc.go:262 +0x17c fp=0xc082023f38 sp=0xc082023f10 runtime.goparkunlock(0x5a98f0, 0x51a050, 0xf, 0xc08201c014, 0x1) C:/Go/src/runtime/proc.go:268 +0x5b fp=0xc082023f70 sp=0xc082023f38 runtime.forcegchelper() C:/Go/src/runtime/proc.go:229 +0xc6 fp=0xc082023fa0 sp=0xc082023f70 runtime.goexit() C:/Go/src/runtime/asm_amd64.s:1998 +0x1 fp=0xc082023fa8 sp=0xc082023fa0 created by runtime.init.4 C:/Go/src/runtime/proc.go:218 +0x32 goroutine 3 [GC sweep wait]: runtime.gopark(0x547160, 0x5a9a00, 0x5181d0, 0xd, 0x41d614, 0x1) C:/Go/src/runtime/proc.go:262 +0x17c fp=0xc08201ff28 sp=0xc08201ff00 runtime.goparkunlock(0x5a9a00, 0x5181d0, 0xd, 0x14, 0x1) C:/Go/src/runtime/proc.go:268 +0x5b fp=0xc08201ff60 sp=0xc08201ff28 runtime.bgsweep(0xc082032000) C:/Go/src/runtime/mgcsweep.go:63 +0xbf fp=0xc08201ff98 sp=0xc08201ff60 runtime.goexit() C:/Go/src/runtime/asm_amd64.s:1998 +0x1 fp=0xc08201ffa0 sp=0xc08201ff98 created by runtime.gcenable C:/Go/src/runtime/mgc.go:191 +0x5a goroutine 4 [finalizer wait]: runtime.gopark(0x547160, 0x5c3e08, 0x519db0, 0xe, 0x14, 0x1) C:/Go/src/runtime/proc.go:262 +0x17c fp=0xc082021ef8 sp=0xc082021ed0 runtime.goparkunlock(0x5c3e08, 0x519db0, 0xe, 0x14, 0x1) C:/Go/src/runtime/proc.go:268 +0x5b fp=0xc082021f30 sp=0xc082021ef8 runtime.runfinq() C:/Go/src/runtime/mfinal.go:158 +0xb8 fp=0xc082021fa0 sp=0xc082021f30 runtime.goexit() C:/Go/src/runtime/asm_amd64.s:1998 +0x1 fp=0xc082021fa8 sp=0xc082021fa0 created by runtime.createfing C:/Go/src/runtime/mfinal.go:139 +0x67 exit status 2

解決策

このようなHANDLEを扱うCの関数を扱いたい場合は、Go側でポインタとして扱われてしまうHANDLEではなく、ハンドルの中身の整数値を直接扱うようにすると良いです。例えば、質問文中のプログラムを参考にするならば、HANDLEを返す/受け取る関数をラップし、GoではHANDLEではなくuintptr_tを返す/受け取る関数を使います。

diff

1@@ -10,6 +10,12 @@ 2 #cgo LDFLAGS: -L. -lproprietarydll 3 #cgo CFLAGS: -I . 4 #include "proprietarydll.h" 5+uintptr_t _funcB() { 6+ return (uintptr_t)funcB(); 7+} 8+uintptr_t _funcC(uintptr_t ptr) { 9+ return (uintptr_t)funcC((HANDLE)ptr)); 10+} 11 */ 12 import "C" 13 14@@ -18,8 +24,8 @@ 15 log.Fatal(err) 16 } 17 18- handleB, err := C.funcB() 19- if handleB == nil || err != nil { 20+ handleB, err := C._funcB() 21+ if handleB == 0 || err != nil { 22 if err != nil { 23 log.Fatal(err) 24 } 25@@ -29,14 +35,14 @@ 26 runtime.GC() // <- cause no error 27 fmt.Println("after:\tGC(funcB)", handleB) 28 29- handleC, err := C.funcC(handleB) 30- if handleC == nil || err != nil { 31+ handleC, err := C._funcC(handleB) 32+ if handleC == 0 || err != nil { 33 if err != nil { 34 log.Fatal("handleC is nil") 35 } 36 return 37 } 38 fmt.Println("before:\tGC(funcC)", handleC) 39- runtime.GC() // <- ここはエラーが出る 40+ runtime.GC() // <- もうエラーが出ない 41 fmt.Println("after:\tGC(funcC)", handleC) 42 }

自己解決に至るまで

上述のように、HANDLEがポインタではあるものの、Handle Tableのインデックスを表しているという点について知らなかったことが今回のエラー原因でした。この事について自力で気づけなかった私は、(マルチポストは避けるべきだとは認識しておりましたが、投稿日からの経過日数と閲覧数からもはや回答は得られないのではと思っていたという言い訳をしつつ)本家に問い合わせました。golang/Go#Issue 15794

この結果以下の様な回答をいただきました。

  • Windows APIはたとえHANDLEがポインタであったとしても、一般に小さな整数値を返す
  • Goのランタイムはポインタとして宣言されているものはポインタとして扱うので、有効なポインタでなければクラッシュする
  • したがって、HANDLEはGoでは(ポインタそのままの)unsafe.Pointerではなく、uintptrとして扱う必要がある

本回答の解決策に載せたプログラムは、本家の方から頂いたこの回答を元に作成したものです。

また、頂いた回答を元に、そもそもHANDLEとは何かについて下記のリンク等を参考にした結果、HANDLEが表すものはHandle Tableのインデックスであり、特定のデータを指す有効なアドレスになっていない(ためエラーが出ていた)という今回の回答に行き着きました。

投稿2016/05/28 08:51

編集2016/05/28 09:19
toko

総合スコア144

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問