[インデックス 17797] ファイルの概要
このコミットは、Goのcgo
ツールがClangコンパイラによって生成されたデバッグ情報(DWARF)を処理する際に発生するバグを修正するものです。特に、memset
のような組み込み関数におけるsize_t
型の誤った解釈に対処しています。
コミット
commit 56aeec31c6ea439188d1800b1167b193b7bfd14a
Author: Russ Cox <rsc@golang.org>
Date: Tue Oct 15 12:46:14 2013 -0400
cmd/cgo: work around bug in clang debug info for builtins like memset
Fixes #6506.
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/14682044
---
misc/cgo/test/issue6506.go | 52 ++++++++++++++++++++++++++++++++++++++++++++++
src/cmd/cgo/gcc.go | 48 ++++++++++++++++++++++++++++++++++++++++--
2 files changed, 98 insertions(+), 2 deletions(-)
diff --git a/misc/cgo/test/issue6506.go b/misc/cgo/test/issue6506.go
new file mode 100644
index 0000000000..e2a733206e
--- /dev/null
+++ b/misc/cgo/test/issue6506.go
@@ -0,0 +1,52 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cgotest
+
+// Test handling of size_t in the face of incorrect clang debug information.
+// golang.org/issue/6506.
+
+/*
+#include <stdlib.h>
+#include <string.h>
+
+// These functions are clang builtins but not standard on other systems.
+// Give them prototypes so that this test can be compiled on other systems.
+// One of the great things about this bug is that even with these prototypes
+// clang still generates the wrong debug information.
+
+void bzero(void*, size_t);\nint bcmp(const void*, const void*, size_t);\nint strncasecmp(const char*, const char*, size_t n);\nsize_t strlcpy(char*, const char*, size_t);\nsize_t strlcat(char*, const char*, size_t);\n*/
+import "C"
+
+func test6506() {
+ // nothing to run, just make sure this compiles
+ var x C.size_t
+
+ C.calloc(x, x)
+ C.malloc(x)
+ C.realloc(nil, x)
+ C.memcpy(nil, nil, x)
+ C.memcmp(nil, nil, x)
+ C.memmove(nil, nil, x)
+ C.strncpy(nil, nil, x)
+ C.strncmp(nil, nil, x)
+ C.strncat(nil, nil, x)
+ x = C.strxfrm(nil, nil, x)
+ C.memchr(nil, 0, x)
+ x = C.strcspn(nil, nil)
+ x = C.strspn(nil, nil)
+ C.memset(nil, 0, x)
+ x = C.strlen(nil)
+ C.alloca(x)
+ C.bzero(nil, x)
+ C.strncasecmp(nil, nil, x)
+ x = C.strlcpy(nil, nil, x)
+ x = C.strlcat(nil, nil, x)
+ _ = x
+}
diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go
index 6a919b4b6d..1cd938ba2c 100644
--- a/src/cmd/cgo/gcc.go
+++ b/src/cmd/cgo/gcc.go
@@ -530,7 +530,7 @@ func (p *Package) loadDWARF(f *File, names []*Name) {\n \t\tf, fok := types[i].(*dwarf.FuncType)\n \t\tif n.Kind != "type" && fok {\n \t\t\tn.Kind = "func"\n-\t\t\tn.FuncType = conv.FuncType(f, pos)\n+\t\t\tn.FuncType = conv.FuncType(n, f, pos)\n \t\t} else {\n \t\t\tn.Type = conv.Type(types[i], pos)\n \t\t\tif enums[i] != 0 && n.Type.EnumValues != nil {\n@@ -1314,6 +1314,41 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {\n \treturn t\n }\n \n+// Clang contains built-in prototypes for many functions in the standard library.\n+// If you use the function without a header, clang uses these definitions to print\n+// an error telling which header to #include and then to continue on with the correct\n+// prototype. Unfortunately, the DWARF debug information generated for one\n+// of these functions, even after the header has been #included, records each of\n+// the size_t arguments as an unsigned long instead. Go treats C.ulong and C.size_t\n+// as different types, so we must correct the prototype for code that works on other\n+// systems to work with clang and vice versa. See golang.org/issue/6506#c21.\n+var usesSizeT = map[string]bool{\n+\t"alloca": true,\n+\t"bzero": true,\n+\t"calloc": true,\n+\t"malloc": true,\t"memchr": true,\n+\t"memcmp": true,\n+\t"memcpy": true,\n+\t"memmove": true,\n+\t"memset": true,\n+\t"realloc": true,\n+\t"snprintf": true,\n+\t"stpncpy": true,\n+\t"strcspn": true,\n+\t"strlcat": true,\n+\t"strlcpy": true,\n+\t"strlen": true,\n+\t"strncasecmp": true,\n+\t"strncat": true,\n+\t"strncmp": true,\n+\t"strncpy": true,\n+\t"strndup": true,\n+\t"strspn": true,\n+\t"strxfrm": true,\n+\t"vsnprintf": true,\n+}\n+\n // FuncArg returns a Go type with the same memory layout as\n // dtype when used as the type of a C function argument.\n func (c *typeConv) FuncArg(dtype dwarf.Type, pos token.Pos) *Type {\n@@ -1330,6 +1365,7 @@ func (c *typeConv) FuncArg(dtype dwarf.Type, pos token.Pos) *Type {\n \t\t\tGo: &ast.StarExpr{X: t.Go},\n \t\t\tC: tr,\n \t\t}\n+\n \tcase *dwarf.TypedefType:\n \t\t// C has much more relaxed rules than Go for\n \t\t// implicit type conversions. When the parameter\n@@ -1357,7 +1393,7 @@ func (c *typeConv) FuncArg(dtype dwarf.Type, pos token.Pos) *Type {\n \n // FuncType returns the Go type analogous to dtype.\n // There is no guarantee about matching memory layout.\n-func (c *typeConv) FuncType(dtype *dwarf.FuncType, pos token.Pos) *FuncType {\n+func (c *typeConv) FuncType(name *Name, dtype *dwarf.FuncType, pos token.Pos) *FuncType {\n \tp := make([]*Type, len(dtype.ParamType))\n \tgp := make([]*ast.Field, len(dtype.ParamType))\n \tfor i, f := range dtype.ParamType {\n@@ -1371,6 +1407,10 @@ func (c *typeConv) FuncType(dtype *dwarf.FuncType, pos token.Pos) *FuncType {\n \t\t\tbreak\n \t\t}\n \t\tp[i] = c.FuncArg(f, pos)\n+\t\t// See comment on usesSizeT.\n+\t\tif id, ok := p[i].Go.(*ast.Ident); ok && id.Name == "_Ctype_ulong" && usesSizeT[name.C] {\n+\t\t\tp[i].Go = c.Ident("_Ctype_size_t")\n+\t\t}\n \t\tgp[i] = &ast.Field{Type: p[i].Go}\n \t}\n \tvar r *Type\n@@ -1379,6 +1419,10 @@ func (c *typeConv) FuncType(dtype *dwarf.FuncType, pos token.Pos) *FuncType {\n \t\tgr = []*ast.Field{{Type: c.goVoid}}\n \t} else if dtype.ReturnType != nil {\n \t\tr = c.Type(dtype.ReturnType, pos)\n+\t\t// See comment on usesSizeT.\n+\t\tif id, ok := r.Go.(*ast.Ident); ok && id.Name == "_Ctype_ulong" && usesSizeT[name.C] {\n+\t\t\tr.Go = c.Ident("_Ctype_size_t")\n+\t\t}\n \t\tgr = []*ast.Field{{Type: r.Go}}\n \t}\n \treturn &FuncType{\n```
## GitHub上でのコミットページへのリンク
[https://github.com/golang/go/commit/56aeec31c6ea439188d1800b1167b193b7bfd14a](https://github.com/golang/go/commit/56aeec31c6ea439188d1800b1167b193b7bfd14a)
## 元コミット内容
このコミットは、Goの`cgo`ツールがClangコンパイラによって生成されたデバッグ情報(DWARF)を処理する際に発生するバグを回避するためのものです。具体的には、`memset`のような標準ライブラリの組み込み関数において、Clangが`size_t`引数を誤って`unsigned long`としてDWARF情報に記録してしまう問題に対処しています。この問題は、Goが`C.ulong`と`C.size_t`を異なる型として扱うため、クロスプラットフォームでの互換性に影響を与えていました。
## 変更の背景
Goの`cgo`ツールは、GoプログラムからCコードを呼び出すための重要なブリッジです。`cgo`は、Cコンパイラ(GCCやClangなど)が生成するDWARFデバッグ情報を解析して、Cの型定義や関数シグネチャを理解します。これにより、Goの型とCの型との間のマッピングを確立し、GoコードからC関数を安全に呼び出せるようにします。
しかし、Clangコンパイラには、特定の標準ライブラリ組み込み関数(例: `memset`, `memcpy`, `malloc`など)のデバッグ情報を生成する際に、`size_t`型の引数を誤って`unsigned long`として記録してしまうバグが存在しました。C言語の標準では、`size_t`はメモリ上のオブジェクトのサイズを表す符号なし整数型として定義されていますが、その具体的な基底型(`unsigned int`, `unsigned long`, `unsigned long long`など)はコンパイラやターゲットアーキテクチャによって異なります。
Goの型システムはCよりも厳格であり、`C.ulong`と`C.size_t`は異なる型として扱われます。このClangのバグにより、`cgo`がClangによって生成されたDWARF情報を読み込むと、本来`size_t`であるべき引数が`unsigned long`として認識されてしまい、GoコードからこれらのC関数を呼び出す際に型ミスマッチのエラーが発生したり、予期せぬ動作を引き起こしたりする可能性がありました。
この問題は、特に異なるシステム(例えば、Clangを使用するmacOSや一部のLinuxディストリビューションと、GCCを使用する他のLinuxディストリビューション)間でコードを移植する際に顕著になります。このコミットは、このClangのバグを直接修正するのではなく、`cgo`側でこの誤ったDWARF情報を「回避」し、`size_t`として正しく解釈するようにすることで、GoプログラムがClang環境でもこれらのC関数を問題なく利用できるようにすることを目的としています。
## 前提知識の解説
* **cgo**: Go言語の標準ライブラリの一部であり、GoプログラムからC言語の関数やデータ構造を呼び出すためのツールです。GoとCの間の相互運用性を提供します。`cgo`は、Cのヘッダーファイルを解析し、Goから呼び出し可能なスタブコードを生成します。
* **DWARF (Debugging With Attributed Record Formats)**: プログラムのデバッグ情報(変数名、型情報、関数シグネチャ、ソースコードの行番号など)を格納するための標準的なフォーマットです。コンパイラ(GCCやClangなど)は、コンパイル時にこのDWARF情報を生成し、デバッガや他のツールがプログラムの内部構造を理解するために使用します。`cgo`もCの型情報を得るためにDWARF情報を利用します。
* **`size_t`**: C言語の標準ライブラリで定義されている符号なし整数型です。主にメモリのサイズや配列のインデックスを表すために使用されます。`stddef.h`ヘッダーで定義されており、その具体的なサイズはシステムに依存します(通常はポインタのサイズと同じ)。
* **`unsigned long`**: C言語の基本的な整数型の一つで、符号なしの長整数を表します。`size_t`と同様に、そのサイズはシステムやコンパイラによって異なりますが、`size_t`が常にメモリサイズを表現できるのに対し、`unsigned long`は必ずしもそうではありません。Goの`cgo`では、`C.size_t`と`C.ulong`は異なるGoの型として扱われます。
* **Clang Builtins**: Clangコンパイラには、標準ライブラリ関数(例: `memset`, `memcpy`, `malloc`など)の最適化された実装や、ヘッダーファイルがインクルードされていない場合でも基本的なプロトタイプを提供する「組み込み関数(builtins)」が多数含まれています。これらの組み込み関数は、コンパイラがコード生成時に特別な処理を行うことができ、パフォーマンス向上に寄与します。しかし、このコミットで言及されているように、これらの組み込み関数のDWARF情報生成にバグがあることがありました。
## 技術的詳細
この問題の核心は、Clangが特定の標準ライブラリ組み込み関数(`memset`, `malloc`など)のDWARFデバッグ情報を生成する際に、`size_t`型の引数や戻り値を誤って`unsigned long`として記録してしまうことにありました。
`cgo`は、Cの関数シグネチャをGoの型に変換する際に、Cコンパイラが生成したDWARF情報を参照します。通常、`size_t`はGoの`_Ctype_size_t`にマッピングされますが、Clangのバグにより`unsigned long`としてDWARFに記録されると、`cgo`はそれをGoの`_Ctype_ulong`として解釈してしまいます。Goの型システムでは`_Ctype_size_t`と`_Ctype_ulong`は異なる型であるため、GoコードからC関数を呼び出す際に型ミスマッチが発生し、コンパイルエラーや実行時エラーにつながる可能性がありました。
このコミットでは、`src/cmd/cgo/gcc.go`内の`typeConv`構造体に、この問題を回避するためのロジックが追加されています。
1. **`usesSizeT`マップの導入**:
`usesSizeT`という`map[string]bool`型のグローバル変数が導入されました。このマップには、Clangが`size_t`を誤って`unsigned long`としてDWARFに記録する可能性のある標準ライブラリ関数の名前(例: `alloca`, `bzero`, `calloc`, `malloc`, `memcpy`, `memset`, `strlen`など)がキーとして登録されています。
2. **`FuncType`関数の変更**:
`FuncType`関数は、Cの関数型(`dwarf.FuncType`)をGoの関数型に変換する役割を担っています。この関数は、関数の引数と戻り値の型を変換する際に、`usesSizeT`マップを参照するようになりました。
* 関数の引数または戻り値のGoの型が`_Ctype_ulong`として識別され、かつその関数名が`usesSizeT`マップに存在する場合、`cgo`はその型を強制的に`_Ctype_size_t`に修正します。
この修正により、`cgo`はClangの誤ったDWARF情報に依存せず、これらの特定の関数については`size_t`型を正しく解釈できるようになります。これは、Clangのバグ自体を修正するものではなく、`cgo`側でその影響を吸収するためのワークアラウンド(回避策)です。
## コアとなるコードの変更箇所
このコミットによる主要なコード変更は以下の2つのファイルにあります。
1. **`misc/cgo/test/issue6506.go`**:
* このファイルは、Clangの`size_t`に関するバグを再現し、修正が正しく機能するかを検証するための新しいテストケースとして追加されました。
* `stdlib.h`と`string.h`をインクルードし、`bzero`, `bcmp`, `strncasecmp`, `strlcpy`, `strlcat`といったClangの組み込み関数(ただし、他のシステムでは標準ではないもの)のプロトタイプを宣言しています。
* `test6506`関数内で、`C.size_t`型の変数`x`を使用し、`calloc`, `malloc`, `memcpy`, `memset`, `strlen`など、`size_t`を引数や戻り値として使用する多数のC標準ライブラリ関数を呼び出しています。このテストの目的は、これらの関数呼び出しがコンパイル時に型エラーを起こさないことを確認することです。
2. **`src/cmd/cgo/gcc.go`**:
* `Package.loadDWARF`関数内の`conv.FuncType`の呼び出しが変更され、関数名(`n`)が新しい引数として渡されるようになりました。これにより、`FuncType`内で関数名に基づいて型変換のロジックを適用できるようになります。
* `usesSizeT`という`map[string]bool`型の新しいグローバル変数が追加されました。このマップには、Clangが`size_t`を誤って`unsigned long`としてDWARFに記録する可能性のあるC関数の名前がリストアップされています。
* `typeConv.FuncType`関数のシグネチャが変更され、`name *Name`という新しい引数が追加されました。
* `typeConv.FuncType`関数内で、引数と戻り値の型を変換するループの中に、以下のロジックが追加されました。
```go
if id, ok := p[i].Go.(*ast.Ident); ok && id.Name == "_Ctype_ulong" && usesSizeT[name.C] {
p[i].Go = c.Ident("_Ctype_size_t")
}
```
このコードは、変換されたGoの型が`_Ctype_ulong`であり、かつ現在のC関数名が`usesSizeT`マップに含まれている場合、そのGoの型を`_Ctype_size_t`に強制的に変更します。同様のロジックが戻り値の型にも適用されています。
## コアとなるコードの解説
`src/cmd/cgo/gcc.go`における変更がこのコミットの核心です。
`usesSizeT`マップは、ClangのDWARF生成バグの影響を受ける可能性のあるC標準ライブラリ関数を特定するための「ホワイトリスト」として機能します。このマップに登録されている関数は、`cgo`がその引数や戻り値の型を処理する際に特別な注意を払うべき対象となります。
`typeConv.FuncType`関数は、Cの関数シグネチャをGoの型システムにマッピングする際の中心的なロジックを含んでいます。この関数は、Cの関数引数と戻り値のDWARF型情報を受け取り、それらを対応するGoの抽象構文木(AST)ノードに変換します。
追加されたロジックは以下の条件をチェックします。
1. `id, ok := p[i].Go.(*ast.Ident)`: 現在処理している引数(または戻り値)のGoの型が、識別子(`ast.Ident`)として表現されているかを確認します。これは、`_Ctype_ulong`や`_Ctype_size_t`のようなGoの組み込み型を表す場合に該当します。
2. `id.Name == "_Ctype_ulong"`: 変換されたGoの型名が`_Ctype_ulong`であるかを確認します。これは、Clangのバグによって`size_t`が誤って`unsigned long`としてDWARFに記録された場合に発生します。
3. `usesSizeT[name.C]`: 現在処理しているC関数名(`name.C`)が`usesSizeT`マップに存在するかを確認します。これにより、この修正がClangのバグの影響を受ける特定の関数にのみ適用されるように制限されます。
これらすべての条件が真である場合、つまり、Clangのバグによって`size_t`が`_Ctype_ulong`として誤って解釈された可能性のある関数である場合、`p[i].Go = c.Ident("_Ctype_size_t")`という行によって、Goの型が明示的に`_Ctype_size_t`に修正されます。
このアプローチは、Clangのバグを直接修正するのではなく、`cgo`がそのバグによって生成された誤ったDWARF情報を「修正」することで、Goプログラムが期待通りに動作するようにする、堅牢なワークアラウンドを提供します。これにより、Goのユーザーは、使用しているCコンパイラ(Clang)の特定のデバッグ情報生成の癖に起因する型ミスマッチの問題に遭遇することなく、`cgo`を介して標準Cライブラリ関数を呼び出すことができます。
## 関連リンク
* Go Issue 6506: [https://golang.org/issue/6506](https://golang.org/issue/6506)
* Go CL 14682044: [https://golang.org/cl/14682044](https://golang.org/cl/14682044)
## 参考にした情報源リンク
* Go issue 6506に関するウェブ検索結果:
* [https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQF_t0H8gru-T5JSROcL65kzeEkA86pSS4-15jDzIiuMBPme1-8k1FWSif7M6nqCn1S-F2zFfboeqTJTbWyCDgWbkPjO5qyF3KYCdYP4mqYXmjDWuUbZ](https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQF_t0H8gru-T5JSROcL65kzeEkA86pSS4-15jDzIiuMBPme1-8k1FWSif7M6nqCn1S-F2zFfboeqTJTbWyCDgWbkPjO5qyF3KYCdYP4mqYXmjDWuUbZ)
* [https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHjJom3RbkOnnzZHJT7dJ4RZ-E3OGiL7QiGEn6OZRasw7t97dlyhZYC_Aw7EAElTnJIkbtwjEgAANyK7dHYkoZpFh4VsS_isZ-6Hgj2Pa39d4dVbW4xzKG5qiAhCKbn5_rxI4lMy7gRF8rdy4GoVEDCJ9bfDQR3mAblpm-uogD5L73jcwEjk7mvfGKMICd2JGC9woToL0_732VZX4qa9HHd3Uk=](https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHjJom3RbkOnnzZHJT7dJ4RZ-E3OGiL7QiGEn6OZRasw7t97dlyhZYC_Aw7EAElTnJIkbtwjEgAANyK7dHYkoZpFh4VsS_isZ-6Hgj2Pa39d4dVbW4xzKG5qiAhCKbn5_rxI4lMy7gRF8rdy4GoVEDCJ9bfDQR3mAblpm-uogD5L73jcwEjk7mvfGKMICd2JGC9woToL0_732VZX4qa9HHd3Uk=)
* [https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGCbDQKy134_XLd5MIFMFw7wmtY1fQOQZg_r94ILsuMJIgnzZiNzBL23KgunS1WvpBrQkAVPAdFZrtoQVZNlqwjR2S8IUeBkmhiyKHQ-OxdfDZf2coCBHmYqFWjtKvKDGnq9PzSCF9ofkaN7fl0twRvtUl438HFdEJpPyDdbbScCqQZ-e4lGl0HqUXZDUhypDefM-OkGAfQJRrB8v3yjOXOn0xx](https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGCbDQKy134_XLd5MIFMFw7wmtY1fQOQZg_r94ILsuMJIgnzZiNzBL23KgunS1WvpBrQkAVPAdFZrtoQVZNlqwjR2S8IUeBkmhiyKHQ-OxdfDZf2coCBHmYqFWjtKvKDGnq9PzSCF9ofkaN7fl0twRvtUl438HFdEJpPyDdbbScCqQZ-e4lGl0HqUXZDUhypDefM-OkGAfQJRrB8v3yjOXOn0xx)
* [https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFhl_59C1hDnmOfGN3kW9yGWFB_TQYgPiglaGm90YzQnLzAM7UMDBTTI0Rw1RdhcnqeNeFljrtcjUbthelL1QlOXrR1d4CN2lvWRT3wqw0-iN0nzofQQ9r5uqVS6gq7ln6_sx_0_hj6-y0Srk0TI7nQD94hq45s86GBwgwOUvhXNDLOGv8SL4XgXAjmZaL-y0G5ZruiIe0klE_bwSNBkATGPhHOjNTiqxJTngrz0EVJUMls-HK5jMt](https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFhl_59C1hDnmOfGN3kW9yGWFB_TQYgPiglaGm90YzQnLzAM7UMDBTTI0Rw1RdhcnqeNeFljrtcjUbthelL1QlOXrR1d4CN2lvWRT3wqw0-iN0nzofQQ9r5uqVS6gq7ln6_sx_0_hj6-y0Srk0TI7nQD94hq45s86GBwgwOUvhXNDLOGv8SL4XgXAjmZaL-y0G5ZruiIe0klE_bwSNBkATGPhHOjNTiqxJTngrz0EVJUMls-HK5jMt)
* [https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQE2vxjSfEu2uxEEB4W_eLImYaIOYniA8cf2oIlq4i_lH-3iMSIpu9ofWspUWrktiadYG8pJa5aH7pGHBDIRWanENS8gQQWNmTn7cMTafKQYHPBxFzuo5FqcWdIJ7Q==](https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQE2vxjSfEu2uxEEB4W_eLImYaIOYniA8cf2oIlq4i_lH-3iMSIpu9ofWspUWrktiadYG8pJa5aH7pGHBDIRWanENS8gQQWNmTn7cMTafKQYHPBxFzuo5FqcWdIJ7Q==)
* [https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEwWyoOBy2XcXVIuHsNuD-24exMI0cF-vJ-QOXDIPQlwrbUg8AmC6b_U_MNtaCphpk7JUlX2LZa7zAEDTcU4xSCvuB0Rc6T-akNr6lVMLF-IpA-qTcZP5icf8uyNOL74RSsubka](https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEwWyoOBy2XcXVIuHsNuD-24exMI0cF-vJ-QOXDIPQlwrbUg8AmC6b_U_MNtaCphpk7JUlX2LZa7zAEDTcU4xSCvuB0Rc6T-akNr6lVMLF-IpA-qTcZP5icf8uyNOL74RSsubka)