如何在go中使用golang c共享库

发布于 2025-01-10 23:59:38 字数 6283 浏览 0 评论 0原文

我使用 go 编写了 c 共享库。

package main

import "C"
import "log"

//export RunLib
func RunLib() {
    log.Println("Call RunLib")
}

func init() {
    log.Println("Call init")
}

func main() {
    log.Println("Call main")
}

我使用以下命令创建了库: go build -buildmode=c-shared -o lib.so lib.go

为了使用该库,我编写了这段 golang 代码。

package main

/*
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

static void callFromLib() {
    void (*fn)();
    void *h = dlopen("lib.so", RTLD_LAZY);
    if (!h) {
        fprintf(stderr, "Error: %s\n", dlerror());
        return;
    }

    *(void**)(&fn) = dlsym(h, "RunLib");
    if (!fn) {
        fprintf(stderr, "Error: %s\n", dlerror());
        dlclose(h);
        return;
    }

    fn();
    dlclose(h);
}

 */
import "C"

func main() {
    C.callFromLib()
} 

如果我运行最后一个代码,它会抛出此错误(go run call.go):

fatal error: bad sweepgen in refill

goroutine 1 [running, locked to thread]:
runtime.throw({0xb990782, 0xc000042a38})
        /usr/local/go/src/runtime/panic.go:1198 +0x71 fp=0xc000042a18 sp=0xc0000429e8 pc=0xb930691
runtime.(*mcache).refill(0x41215b8, 0x2)
        /usr/local/go/src/runtime/mcache.go:156 +0x24e fp=0xc000042a68 sp=0xc000042a18 pc=0xb91434e
runtime.(*mcache).nextFree(0x41215b8, 0x2)
        /usr/local/go/src/runtime/malloc.go:880 +0x85 fp=0xc000042ab0 sp=0xc000042a68 pc=0xb90ba85
runtime.mallocgc(0x8, 0xb9ba300, 0x1)
        /usr/local/go/src/runtime/malloc.go:1071 +0x4e8 fp=0xc000042b30 sp=0xc000042ab0 pc=0xb90c108
runtime.growslice(0xb9ba300, {0x0, 0x41a1910, 0x2}, 0xc00009c000)
        /usr/local/go/src/runtime/slice.go:267 +0x4ea fp=0xc000042b98 sp=0xc000042b30 pc=0xb94586a
sync.(*Pool).pinSlow(0xba394e0)
        /usr/local/go/src/sync/pool.go:223 +0x105 fp=0xc000042c30 sp=0xc000042b98 pc=0xb960ec5
sync.(*Pool).pin(0xba394e0)
        /usr/local/go/src/sync/pool.go:206 +0x4e fp=0xc000042c48 sp=0xc000042c30 pc=0xb960d8e
sync.(*Pool).Get(0xba394e0)
        /usr/local/go/src/sync/pool.go:128 +0x25 fp=0xc000042c80 sp=0xc000042c48 pc=0xb960ac5
fmt.newPrinter()
        /usr/local/go/src/fmt/print.go:137 +0x25 fp=0xc000042ca8 sp=0xc000042c80 pc=0xb985d45
fmt.Sprintln({0xc000042d38, 0x1, 0x1})
        /usr/local/go/src/fmt/print.go:280 +0x28 fp=0xc000042cf0 sp=0xc000042ca8 pc=0xb986008
log.Println({0xc000042d38, 0x24, 0x0})
        /usr/local/go/src/log/log.go:329 +0x1e fp=0xc000042d20 sp=0xc000042cf0 pc=0xb98cd5e
main.RunLib(...)
        /Users/.../demo/lib.go:8
_cgoexp_6b951f94a90e_RunLib(0xc000042d90)
        _cgo_gotypes.go:36 +0x45 fp=0xc000042d58 sp=0xc000042d20 pc=0xb98cf85
runtime.cgocallbackg1(0xb98cf40, 0xc000042e60, 0x0)
        /usr/local/go/src/runtime/cgocall.go:306 +0x29a fp=0xc000042e28 sp=0xc000042d58 pc=0xb903d1a
runtime.cgocallbackg(0xc0000001a0, 0x300000002, 0xc0000001a0)
        /usr/local/go/src/runtime/cgocall.go:232 +0x109 fp=0xc000042eb8 sp=0xc000042e28 pc=0xb9039e9
runtime.cgocallbackg(0xb98cf40, 0x7ffeefbff737, 0x0)
        <autogenerated>:1 +0x2f fp=0xc000042ee0 sp=0xc000042eb8 pc=0xb95e32f
runtime: unexpected return pc for runtime.cgocallback called from 0x4053e00
stack: frame={sp:0xc000042ee0, fp:0xc000042f08} stack=[0xc000042000,0xc000043000)
0x000000c000042de0:  0x000000c000042d9d  0x000000c000042e18 
0x000000c000042df0:  0x000000000b95843b <runtime.exitsyscall+0x00000000000000fb>  0x000000c0000001a0 
0x000000c000042e00:  0x000000c000042dd8  0x0000000000000000 
0x000000c000042e10:  0x000000000b9c3dc0  0x000000c000042ea8 
0x000000c000042e20:  0x000000000b9039e9 <runtime.cgocallbackg+0x0000000000000109>  0x000000000b98cf40 <_cgoexp_6b951f94a90e_RunLib+0x0000000000000000> 
...
<_cgoexp_6b951f94a90e_RunLib+0x0000000000000000> 
0x000000c000042ea0:  0x00007ffeefbff737  0x000000c000042ed0 
0x000000c000042eb0:  0x000000000b95e32f <runtime.cgocallbackg+0x000000000000002f>  0x000000c0000001a0 
...
<runtime.cgocallback+0x00000000000000b4> 
0x000000c000042ee0: <0x000000000b98cf40 <_cgoexp_6b951f94a90e_RunLib+0x0000000000000000>  0x00007ffeefbff737 
...
runtime.cgocallback(0x4004165, 0x4058340, 0xc000042f70)
        /usr/local/go/src/runtime/asm_amd64.s:915 +0xb4 fp=0xc000042f08 sp=0xc000042ee0 pc=0xb95c134

goroutine 1 [runnable, locked to thread]:
unicode.init()
        /usr/local/go/src/unicode/tables.go:9 +0x79
exit status 2

但如果我使用 python,一切正常!

>>> import ctypes
>>> lib = ctypes.cdll.LoadLibrary("lib.so")
>>> 2022/03/02 01:08:17 Call init

>>> lib.RunLib()
2022/03/02 01:08:22 Call RunLib
0
>>> 

信息

操作系统:macOs Big Sur 11.6.3 (20G415)

>>> clang --version
Apple clang version 13.0.0 (clang-1300.0.29.30)
>>> nm lib.so| grep RunLib
000000000008cfa0 T _RunLib
000000000008cf40 t __cgoexp_6b951f94a90e_RunLib
00000000000c4200 s __cgoexp_6b951f94a90e_RunLib.stkobj
>>> go env
GO111MODULE="on"
GOARCH="amd64"
GOBIN=""
GOCACHE="~/Library/Caches/go-build"
GOENV="~/Library/Application Support/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOINSECURE=""
GOMODCACHE="~/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="darwin"
GOPATH="~/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"
GOVCS=""
GOVERSION="go1.17"
GCCGO="gccgo"
AR="ar"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD="~/projects/go/.../go.mod"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -arch x86_64 -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/wl/9rtfdx8x7jvgyn6t8hpq7yh00000gn/T/go-build401121298=/tmp/go-build -gno-record-gcc-switches -fno-common"

我用谷歌搜索并找到了这篇文章。但文章中并没有golang。

https://medium.com /学习go编程语言/从其他语言调用go函数4c7d8bcc69bf

I wrote c-shared library using go.

package main

import "C"
import "log"

//export RunLib
func RunLib() {
    log.Println("Call RunLib")
}

func init() {
    log.Println("Call init")
}

func main() {
    log.Println("Call main")
}

I created the library using this command:
go build -buildmode=c-shared -o lib.so lib.go

To use the library, I wrote this golang code.

package main

/*
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

static void callFromLib() {
    void (*fn)();
    void *h = dlopen("lib.so", RTLD_LAZY);
    if (!h) {
        fprintf(stderr, "Error: %s\n", dlerror());
        return;
    }

    *(void**)(&fn) = dlsym(h, "RunLib");
    if (!fn) {
        fprintf(stderr, "Error: %s\n", dlerror());
        dlclose(h);
        return;
    }

    fn();
    dlclose(h);
}

 */
import "C"

func main() {
    C.callFromLib()
} 

If I run the last code it throws this error (go run call.go):

fatal error: bad sweepgen in refill

goroutine 1 [running, locked to thread]:
runtime.throw({0xb990782, 0xc000042a38})
        /usr/local/go/src/runtime/panic.go:1198 +0x71 fp=0xc000042a18 sp=0xc0000429e8 pc=0xb930691
runtime.(*mcache).refill(0x41215b8, 0x2)
        /usr/local/go/src/runtime/mcache.go:156 +0x24e fp=0xc000042a68 sp=0xc000042a18 pc=0xb91434e
runtime.(*mcache).nextFree(0x41215b8, 0x2)
        /usr/local/go/src/runtime/malloc.go:880 +0x85 fp=0xc000042ab0 sp=0xc000042a68 pc=0xb90ba85
runtime.mallocgc(0x8, 0xb9ba300, 0x1)
        /usr/local/go/src/runtime/malloc.go:1071 +0x4e8 fp=0xc000042b30 sp=0xc000042ab0 pc=0xb90c108
runtime.growslice(0xb9ba300, {0x0, 0x41a1910, 0x2}, 0xc00009c000)
        /usr/local/go/src/runtime/slice.go:267 +0x4ea fp=0xc000042b98 sp=0xc000042b30 pc=0xb94586a
sync.(*Pool).pinSlow(0xba394e0)
        /usr/local/go/src/sync/pool.go:223 +0x105 fp=0xc000042c30 sp=0xc000042b98 pc=0xb960ec5
sync.(*Pool).pin(0xba394e0)
        /usr/local/go/src/sync/pool.go:206 +0x4e fp=0xc000042c48 sp=0xc000042c30 pc=0xb960d8e
sync.(*Pool).Get(0xba394e0)
        /usr/local/go/src/sync/pool.go:128 +0x25 fp=0xc000042c80 sp=0xc000042c48 pc=0xb960ac5
fmt.newPrinter()
        /usr/local/go/src/fmt/print.go:137 +0x25 fp=0xc000042ca8 sp=0xc000042c80 pc=0xb985d45
fmt.Sprintln({0xc000042d38, 0x1, 0x1})
        /usr/local/go/src/fmt/print.go:280 +0x28 fp=0xc000042cf0 sp=0xc000042ca8 pc=0xb986008
log.Println({0xc000042d38, 0x24, 0x0})
        /usr/local/go/src/log/log.go:329 +0x1e fp=0xc000042d20 sp=0xc000042cf0 pc=0xb98cd5e
main.RunLib(...)
        /Users/.../demo/lib.go:8
_cgoexp_6b951f94a90e_RunLib(0xc000042d90)
        _cgo_gotypes.go:36 +0x45 fp=0xc000042d58 sp=0xc000042d20 pc=0xb98cf85
runtime.cgocallbackg1(0xb98cf40, 0xc000042e60, 0x0)
        /usr/local/go/src/runtime/cgocall.go:306 +0x29a fp=0xc000042e28 sp=0xc000042d58 pc=0xb903d1a
runtime.cgocallbackg(0xc0000001a0, 0x300000002, 0xc0000001a0)
        /usr/local/go/src/runtime/cgocall.go:232 +0x109 fp=0xc000042eb8 sp=0xc000042e28 pc=0xb9039e9
runtime.cgocallbackg(0xb98cf40, 0x7ffeefbff737, 0x0)
        <autogenerated>:1 +0x2f fp=0xc000042ee0 sp=0xc000042eb8 pc=0xb95e32f
runtime: unexpected return pc for runtime.cgocallback called from 0x4053e00
stack: frame={sp:0xc000042ee0, fp:0xc000042f08} stack=[0xc000042000,0xc000043000)
0x000000c000042de0:  0x000000c000042d9d  0x000000c000042e18 
0x000000c000042df0:  0x000000000b95843b <runtime.exitsyscall+0x00000000000000fb>  0x000000c0000001a0 
0x000000c000042e00:  0x000000c000042dd8  0x0000000000000000 
0x000000c000042e10:  0x000000000b9c3dc0  0x000000c000042ea8 
0x000000c000042e20:  0x000000000b9039e9 <runtime.cgocallbackg+0x0000000000000109>  0x000000000b98cf40 <_cgoexp_6b951f94a90e_RunLib+0x0000000000000000> 
...
<_cgoexp_6b951f94a90e_RunLib+0x0000000000000000> 
0x000000c000042ea0:  0x00007ffeefbff737  0x000000c000042ed0 
0x000000c000042eb0:  0x000000000b95e32f <runtime.cgocallbackg+0x000000000000002f>  0x000000c0000001a0 
...
<runtime.cgocallback+0x00000000000000b4> 
0x000000c000042ee0: <0x000000000b98cf40 <_cgoexp_6b951f94a90e_RunLib+0x0000000000000000>  0x00007ffeefbff737 
...
runtime.cgocallback(0x4004165, 0x4058340, 0xc000042f70)
        /usr/local/go/src/runtime/asm_amd64.s:915 +0xb4 fp=0xc000042f08 sp=0xc000042ee0 pc=0xb95c134

goroutine 1 [runnable, locked to thread]:
unicode.init()
        /usr/local/go/src/unicode/tables.go:9 +0x79
exit status 2

But if I use python everything works correctly!

>>> import ctypes
>>> lib = ctypes.cdll.LoadLibrary("lib.so")
>>> 2022/03/02 01:08:17 Call init

>>> lib.RunLib()
2022/03/02 01:08:22 Call RunLib
0
>>> 

Informations

OS: macOs Big Sur 11.6.3 (20G415)

>>> clang --version
Apple clang version 13.0.0 (clang-1300.0.29.30)
>>> nm lib.so| grep RunLib
000000000008cfa0 T _RunLib
000000000008cf40 t __cgoexp_6b951f94a90e_RunLib
00000000000c4200 s __cgoexp_6b951f94a90e_RunLib.stkobj
>>> go env
GO111MODULE="on"
GOARCH="amd64"
GOBIN=""
GOCACHE="~/Library/Caches/go-build"
GOENV="~/Library/Application Support/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOINSECURE=""
GOMODCACHE="~/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="darwin"
GOPATH="~/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"
GOVCS=""
GOVERSION="go1.17"
GCCGO="gccgo"
AR="ar"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD="~/projects/go/.../go.mod"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -arch x86_64 -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/wl/9rtfdx8x7jvgyn6t8hpq7yh00000gn/T/go-build401121298=/tmp/go-build -gno-record-gcc-switches -fno-common"

I googled and found this article. But there is no golang in the article.

https://medium.com/learning-the-go-programming-language/calling-go-functions-from-other-languages-4c7d8bcc69bf

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

向地狱狂奔 2025-01-17 23:59:38

一位 Go 贡献者写道,一个镜像中不可能有两个 Go 运行时。该评论指的是 Windows,但似乎也适用于 macOS,因为我可以可靠地重现 OP 提到的崩溃:

如果我理解正确的话,您正在使用 Go 程序打开 C++ DLL,而 C++ DLL 打开 Go DLL。抱歉,这在 Windows 上不起作用。 Windows 上的程序映像中只能有一份 Go 运行时副本,但您试图拥有两份。

请参阅 https://github.com/golang/go/issues/50304#issuecomment- 999302888

但是,由于 OP 使用 macOS,因此还有另一种选择:使用插件,请参阅https://pkg.go.dev/plugin

Package插件实现了Go插件的加载和符号解析。

创建库的命令将更改如下:

go build -buildmode=plugin -o lib.so lib.go

因此,您对库中定义的函数的调用在 Go 中将类似于以下内容:

package main

import (
    "plugin"
)

func main() {
    plug, err := plugin.Open("lib.so")
    if err != nil {
        panic(err)
    }

    runLib, err := plug.Lookup("RunLib")
    if err != nil {
        panic(err)
    }

    runLib.(func())()
}

使用此程序的测试会生成以下日志输出:

stephan@mac golang-c-lib-in-go % go run call.go                                 
2022/10/29 23:23:01 Call init
2022/10/29 23:23:01 Call RunLib

如果还要使用该库对于从 C 和/或 Python 调用,那么一种可能性是以两种变体创建库:一种是从 C 和/或 Python 调用,如问题所示,另一种是在插件变体中,然后可以使用去节目吧。

在您的示例中,您不需要在 lib.go 中进行任何更改,唯一的区别是创建库的命令行。

最后,来自文档的重要说明:

目前插件仅支持 Linux、FreeBSD 和 macOS。

A Go contributor writes that it is not possible to have two Go runtimes in one image. The comment refers to Windows, but seems to apply to macOS as well, since I can reliably reproduce the crash mentioned by the OP:

If I am understanding correctly, you are using a Go program to open a C++ DLL and the C++ DLL opens a Go DLL. I'm sorry, this won't work on Windows. There can only be one copy of the Go runtime in the program image on Windows, but you are trying to have two.

see https://github.com/golang/go/issues/50304#issuecomment-999302888

However since the OP uses macOS there is an alternative: the use of plugins, see https://pkg.go.dev/plugin:

Package plugin implements loading and symbol resolution of Go plugins.

The command to create the library would change as follows:

go build -buildmode=plugin -o lib.so lib.go

Accordingly, your call to the function defined in your library would then look something like this in Go:

package main

import (
    "plugin"
)

func main() {
    plug, err := plugin.Open("lib.so")
    if err != nil {
        panic(err)
    }

    runLib, err := plug.Lookup("RunLib")
    if err != nil {
        panic(err)
    }

    runLib.(func())()
}

A test with this program generates the following logging outputs:

stephan@mac golang-c-lib-in-go % go run call.go                                 
2022/10/29 23:23:01 Call init
2022/10/29 23:23:01 Call RunLib

If the library is also to be used for calling from C and/or Python, then one possibility is to create the library in two variants: one that is called from C and/or Python, as shown in the question, and one in the plugin variant that can then be used by Go programs.

In your example, you don't need to make any changes in lib.go, the only difference is the command line to create the library.

Finally, an important note from the documentation:

Currently plugins are only supported on Linux, FreeBSD, and macOS.

颜漓半夏 2025-01-17 23:59:38

原海报所描述的问题看起来很奇怪。我无法在 Linux 上重现它。

我的设置:Ubuntu 18、Go 1.19.2、gcc 7.5.0

我只做了一项更改:将 pragma #cgo LDFLAGS: -ldl 添加到 C 代码段。 GCC 需要启用 dlopen &有限公司。

package main

/*
#cgo LDFLAGS: -ldl
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

static void callFromLib() {
    void (*fn)();
    void *h = dlopen("lib.so", RTLD_LAZY);
    if (!h) {
        fprintf(stderr, "Error: %s\n", dlerror());
        return;
    }

    *(void**)(&fn) = dlsym(h, "RunLib");
    if (!fn) {
        fprintf(stderr, "Error: %s\n", dlerror());
        dlclose(h);
        return;
    }

    fn();
    dlclose(h);
}

*/
import "C"

func main() {
    C.callFromLib()
}

启动为:

LD_LIBRARY_PATH=/home/user/tmp/try-go/ go run call.go

LD_LIBRARY_PATH 变量指向包含 lib.so 的目录

输出:

2022/10/26 09:39:10 Call init
2022/10/26 09:39:10 Call RunLib

正如预期的那样,没有错误。

原始错误 bad swinggen in refill 是由垃圾收集器引发的。对象的缓存变得不连贯。我怀疑问题在于程序中有两个运行时和两个垃圾收集器 - 一个来自调用程序,另一个来自加载的 lib.so。看起来两个垃圾收集器在同一个堆上运行时发生了冲突。

无论如何,在 go 1.19 中代码可以工作。我仍然不会通过 C dlopen 加载 Go 共享对象 - 谁知道两个 Go 运行时如何交互,特别是如果它们来自不同的编译器版本。

The problem described by the original poster looks weird. I could not reproduce it on Linux.

My setup: Ubuntu 18, Go 1.19.2, gcc 7.5.0

I made only one change: added pragma #cgo LDFLAGS: -ldl to the C snippet. It is required by GCC to enable dlopen & Co.

package main

/*
#cgo LDFLAGS: -ldl
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

static void callFromLib() {
    void (*fn)();
    void *h = dlopen("lib.so", RTLD_LAZY);
    if (!h) {
        fprintf(stderr, "Error: %s\n", dlerror());
        return;
    }

    *(void**)(&fn) = dlsym(h, "RunLib");
    if (!fn) {
        fprintf(stderr, "Error: %s\n", dlerror());
        dlclose(h);
        return;
    }

    fn();
    dlclose(h);
}

*/
import "C"

func main() {
    C.callFromLib()
}

Launched as:

LD_LIBRARY_PATH=/home/user/tmp/try-go/ go run call.go

LD_LIBRARY_PATH variable points to the directory with lib.so

Output:

2022/10/26 09:39:10 Call init
2022/10/26 09:39:10 Call RunLib

As expected, no errors.

The original error bad sweepgen in refill is raised by the garbage collector. The cache of objects became incoherent. I suspect the issue is that there are two runtimes and two garbage collectors in the program - one comes from the calling program and the other is from the loaded lib.so. It looks like two garbage collectors clashed while running over the same heap.

Anyway, in go 1.19 the code works. Still I wouldn't load Go shared object through C dlopen - who knows how two Go runtimes could interact, especially if they are from different compilers versions.

甜妞爱困 2025-01-17 23:59:38

我建议这个 博客 详细讨论了 go 程序与 c 库的链接。

go 程序中的代码似乎缺少编译器和链接器指令:

#cgo CFLAGS: -I./src

#cgo LDFLAGS: -L./lib -lmylib -Wl,-rpath=./lib

I suggest this blog which discusses linking go programs with c libraries in detail.

Your code in the go program seems to be missing the compiler and linker instructions:

#cgo CFLAGS: -I./src

#cgo LDFLAGS: -L./lib -lmylib -Wl,-rpath=./lib

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文