Goパニック()、ランタイムエラー、およびGo + asmパート0x000c03fのOSでのそれらの実装(Float32)

みなさんこんにちは! 最近、私はGoでの空のインターフェイスの実装について書きました。その記事はご想像のとおり、GoでのOSの開発に直接関連していますが、このトピックは放棄されたり忘れられたりすることはなく、長い間延期されました。



カットの下で:asmプロキシメソッドを「破棄」し、panic()メソッドを実装し、ランタイムエラーをサポートします。



asmのすべてのスタブメソッドとプロキシメソッドを覚えていますか? 捨てて忘れてください。 残りのファイルは、multiboot.sとruntime.sの2つだけです。multiboot.sの内容は変更されず、runtime.sはこれに縮小されます。



global dummy dummy: ;   ret global __unsafe_get_addr; convert uint32 to pointer __unsafe_get_addr: push ebp mov ebp, esp mov eax, [ebp+8] mov esp, ebp pop ebp ret
      
      





残りはすべて容赦なくナイフの下に置いた。



DATAタスクの後に、テキストセクションでlink.ldを開きます。



  __go_new = go.runtime.New; __go_new_nopointers = go.runtime.New; __go_print_string = go.screen.PrintStr; __go_print_empty_interface = go.screen.PrintInterface; __go_print_nl = go.screen.PrintNl; __go_print_pointer = go.screen.PrintHex; __go_print_uint64 = go.screen.PrintUint64; __go_runtime_error = go.runtime.RuntimeError; __go_panic = go.runtime.Panic; runtime.efacetype = go.runtime.InterfaceType; runtime.ifacetypeeq = go.runtime.InterfaceTypeEq; runtime.ifaceE2T2 = go.runtime.InterfaceE2T2; __go_type_hash_identity = go.runtime.TypeHashIdentity; __go_type_equal_identity = go.runtime.TypeEqualIdentity; __go_strcmp = go.runtime.StrCmp; __go_type_hash_error = dummy; __go_type_equal_error = dummy; __go_register_gc_roots = dummy; __go_type_hash_identity_descriptor = dummy; __go_type_equal_error_descriptor = dummy; __go_type_equal_identity_descriptor = dummy; __go_type_hash_error_descriptor = dummy; __go_type_hash_empty_interface = dummy; __go_empty_interface_compare = dummy; __go_type_hash_string = dummy; __go_type_equal_string = dummy; __go_type_equal_empty_interface = dummy; __go_type_hash_string_descriptor = dummy; __go_type_equal_string_descriptor = dummy; __go_type_hash_empty_interface_descriptor = dummy; __go_type_equal_empty_interface_descriptor = dummy;
      
      





はい、急いで警告します。実装した方法の一部はまだ説明されていないため、ダミーに置き換えるか、自分で実装する必要があります。



ここで何が起こっていますか? シンボルエイリアスを作成します。これは、asmでプロキシメソッドを記述するよりもはるかに優れたソリューションです。



さて、私が捨てたasm-proxyを約束したので、パニックメソッドの実装に取り​​掛かりましょう。

この記事から 、TypeDescriptor、EmptyInterface、およびUncommonの構造をruntime.goファイルにコピーします。



追加する:



 //,    type PanicStack struct { Next *PanicStack //   Arg interface{} // WasRecovered bool //   IsForeign bool //  } //   panic() func Panic(arg interface{}) { //stackTrace(3) //,  ,  ,   =) p := PanicStack{} //     . :  ,    p.Arg = arg //   PrintPanic(&p) //  for { //    } } //     func PrintPanic(p *PanicStack) { if p.Next != nil { //     ,   ()   PrintPanic(p.Next) print("\t") } print("panic: ") //   print(p.Arg) //   if p.WasRecovered { //   print("[recovered]") //    } print("\n") }
      
      





正直なところ、パニック引数が文字列である場合、ここで終了できますが、悲しいかな-空のインターフェイスなので、空のインターフェイスの印刷を実装し、それに応じてインターフェイスを型にキャストする必要があります、これを行います。 ワーニング:このセクションで書かれたすべてのコードはMVPの初期段階の鮮明な例であり、もちろん動作しますが、あまりにも原始的であり、欠陥があります。



runtime.goでまだ作業中であることを思い出させてください。



 //     func StrCmp(s1, s2 string) int { if len(s1) < len(s2) { return -1 } if len(s2) < len(s1) { return 1 } for i := 0; i < len(s1); i++ { if s1[i] < s2[i] { return -1 } if s2[i] < s1[i] { return 1 } } return 0 } //  TypeDescriptor     func InterfaceType(arg *EmptyInterface) TypeDescriptor { return *(arg.__type_descriptor) } //    ,       ,     func InterfaceTypeEq(arg1, arg2 *EmptyInterface) bool { return *(arg1.__type_descriptor.string) == *(arg2.__type_descriptor.string) } //     //iface -   TypeDescriptor   //e -     //ret - ,     // ok -    func InterfaceE2T2(iface *TypeDescriptor, e EmptyInterface, ret uint32) (ok bool) { if *(iface.string) == *(e.__type_descriptor.string) { //   memcpy(ret, e.__object, uint32(iface.size)) //        return true } else { return false } }
      
      





screen.goファイルに切り替えます。



 //   ,         func PrintInterface(arg interface{}) { v, ok := arg.(string) if ok { print(v) } }
      
      





さて、コード(kernel.go、Loadメソッド)を記述できます。



 panic("Habrahabr")
      
      





そして、qemuの出力に感心します。



 panic: Habrahabr
      
      





すでに悪くないですよね? しかし、ランタイムエラー処理も約束しました。



runtime.go:



 //           const ( SLICE_INDEX_OUT_OF_BOUNDS = uint32(iota) ARRAY_INDEX_OUT_OF_BOUNDS STRING_INDEX_OUT_OF_BOUNDS SLICE_SLICE_OUT_OF_BOUNDS ARRAY_SLICE_OUT_OF_BOUNDS STRING_SLICE_OUT_OF_BOUNDS NIL_DEREFERENCE MAKE_SLICE_OUT_OF_BOUNDS MAKE_MAP_OUT_OF_BOUNDS MAKE_CHAN_OUT_OF_BOUNDS DIVISION_BY_ZERO MSG_INDEX_OUT_OF_RANGE = "index out of range" MSG_SLICE_BOUNDS_OUT_OF_RANGE = "slice vounds out of range" MSG_NIL_DEREFERENCE = "nil pointer dereference" MSG_MAKE_SLICE_OUT_OF_BOUNDS = "make slice len or cap out of range" MSG_MAKE_MAP_OUT_OF_BOUNDS = "make map len out of range" MSG_MAKE_CHAN_OUT_OF_BOUNDS = "make chan len out of range" MSG_DIVISION_BY_ZERO = "integer divide by zero" MSG_UNKNOWN = "unknown" ) // ,  ,   ,   ,   func RuntimeError(i uint32) { switch i { case SLICE_INDEX_OUT_OF_BOUNDS, ARRAY_INDEX_OUT_OF_BOUNDS, STRING_INDEX_OUT_OF_BOUNDS: panic(MSG_INDEX_OUT_OF_RANGE) case SLICE_SLICE_OUT_OF_BOUNDS, ARRAY_SLICE_OUT_OF_BOUNDS, STRING_SLICE_OUT_OF_BOUNDS: panic(MSG_SLICE_BOUNDS_OUT_OF_RANGE) case NIL_DEREFERENCE: panic(MSG_NIL_DEREFERENCE) case MAKE_SLICE_OUT_OF_BOUNDS: panic(MSG_MAKE_SLICE_OUT_OF_BOUNDS) case MAKE_MAP_OUT_OF_BOUNDS: panic(MSG_MAKE_MAP_OUT_OF_BOUNDS) case MAKE_CHAN_OUT_OF_BOUNDS: panic(MSG_MAKE_CHAN_OUT_OF_BOUNDS) case DIVISION_BY_ZERO: panic(MSG_DIVISION_BY_ZERO) default: panic(MSG_UNKNOWN) } }
      
      





したがって、今、どこかでミスをすると、それについてのメッセージが表示されます。



ワーニング:前の部分のコードは多くのランタイムエラーを生成しますが、今のところそれらを修正する方法は示しません。宿題にしましょう。



記事の準備にご協力いただきありがとうございます:



Victorya1-校正、 ラフニング

kirill_danshin-それらの議論。 パーツ、興味深い議論



UPD:文字列型へのインターフェイスのキャストの問題に関する興味深い議論

キリル:

私の考えは何ですか



[12:09:22 PM]イヴァン:

注意深く読んだ)



[12:09:35 PM]キリル:

gosh文字列は不変です



[12:09:45 PM]イヴァン:

うん

変更は新しいものの作成につながります



[12:10:25 PM]キリル:

InterfaceE2T2は、理論上、データをコピーしない場合があります

既存のものを参照



[12:12:47 PM]イヴァン:

それはできません、a)文字列だけでなく、b)戻りアドレスを取得し、さらに、ポインタではなく、期待されるタイプのデータをそこに書き込む必要があります



[12:13:31 PM]キリル:

私は何かを見落としていました

申し訳ありませんが、それはできません

私はいつも食べ物のこの制限に休みます

結局のところ、ここでは修正することはできません



[12:16:05 PM]イヴァン:

なぜ、ランタイムの拡張によって可能になるのか、つまり InterfaceToString(iface interface {})メソッドを実装します(str * string、ok bool)

しかし、これはランタイムメソッドではなく、ユーザーになります

うーん...この小さな議論を記事に追加しますか?



[12:17:01 PM]キリル:

はい、来ます

これは興味深いタスクです。遅かれ早かれ解決する必要があります

まったく同じインターフェイス{}-> []バイト


UPD2: Cyrilとの議論から関数を実装しようとしました。



All Articles