golang编译androidso无法加载

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了golang编译androidso无法加载相关的知识,希望对你有一定的参考价值。

您好,Golang编译android SO无法加载的原因可能是因为Golang不支持Android平台,也可能是因为Golang编译器的版本不兼容Android平台。此外,如果您的Android SO文件有语法错误,或者编译器版本不兼容,也可能导致加载失败。因此,要解决Golang编译Android SO无法加载的问题,您需要检查Android SO文件是否有语法错误,并确保Golang编译器的版本与Android平台兼容。 参考技术A 编译Android SO无法加载是一个比较复杂的问题,需要考虑很多因素。首先,您需要确保您的Go语言编译器是最新版本,并且支持Android SO的编译。其次,您需要确保您的Android SDK版本与Go语言编译器版本兼容,并且您的Android SDK中包含了所有必要的库文件。最后,您需要确保您的Go语言编译器能够正确识别Android SO文件,并且您的编译器能够正确编译Android SO文件。如果您仍然遇到问题,建议您可以尝试搜索相关的解决方案,或者咨询专业的技术人员。 参考技术B 1.检查编译的so文件是否与手机架构匹配,比如arm64-v8a,armeabi-v7a等;
2.检查so文件是否被正确安装,比如放在/data/data/<package name>/lib/目录下;
3.检查so文件是否有可执行权限,比如chmod 755 <so文件>;
4.检查so文件是否被正确加载,比如System.loadLibrary("<so文件>");
5.检查so文件是否有符号表,比如nm -D <so文件>;
6.检查so文件是否被正确链接,比如ldd <so文件>;
7.检查so文件是否有依赖库,比如ldd <so文件>;
8.检查so文件是否有编译错误,比如objdump -d <so文件>;
9.检查so文件是否有调试信息,比如objdump -g <so文件>;
10.检查so文件是否有版本信息,比如readelf -V <so文件>;
参考技术C Golang编译so动态库加载失败的原,首先,main.go 中创建了一个 http 服务,监听 8080 端口,访问 / 路由时,返回预设的 JSON 文本。

package main

import (
"github.com/gin-gonic/gin"
"log"
"net/http"
)

func main()
r := gin.Default()
r.GET("/", func(c *gin.Context)
c.JSON(http.StatusOK, gin.H
"msg": "hello world",
)
)
log.Fatalln(r.Run(":8080"))

第一步,编译,简单。Android 都是 linux arm64 (arm) 架构,如果不确定,可以进入 Android shell 里查看,下面再讲。

本文环境为 win10+cmd 。交叉编译生成适用于 linux arm64 的二进制文件待用。

set CGO_ENABLED=0
set GOOS=linux
set GOARCH=arm64
go build -o server main.go
第二步,把编译生成的 server 文件拷贝到 Android 的 /data/local/tmp/ 目录下。

第三步,进入到 Android 的 /data/local/tmp/ 目录,执行 ./server 启动服务。

完成结束。
参考技术D 如果Golang无法编译AndroidSO,可能是因为不支持ARM处理器或Android架构,建议检查 Golang安装包是否支持Android平台。另外,还应确保编译出的目标文件和Android设备所使用的操作系统是兼容的。

golang plugin源码分析

什么是Go Plugin

Golang是静态编译型语言,在编译时就将所有引用的包(库)全部加载打包到最终的可执行程序(或库文件)中,因此并不能在运行时动态加载其他共享库。Go Plugin提供了这样一种方式,能够让你在运行时动态加载外部功能。go在1.8 支持了这个功能,类似c语言的动态链接库


为什么用Go Plugin

其实应该问为什么要用Plugin,我觉得原因有很多,比如:


可插拔:有了Plugin,我的程序可以根据需要随时替换其中某些部件而不用修改我的程序;

动态加载的需要:有些模块只有在运行时才能确定,需要动态加载外部的功能模块;

独立开发:Plugin 可以和主程序独立建设,主程序只需要制定好框架,实现默认(模版)功能。Plugin 可根据用户需求随时自行扩展开发,运行时随意替换,提高了程序的可定制性;

怎么用Go plugin

Golang 对 Plugin 的实现在标准库plugin中。整个接口可以说相当简洁了。


type Plugin struct{ ... } func Open(path string) (*Plugin, error) func (p *Plugin) Lookup(symName string) (Symbol, error)type Symbol interface{}


是的,你没有看错,就只有两个type和两个方法。

type Plugin

即Golang加载的插件,与之有关的两个方法:


Open: 根据参数path提供的插件路径加载这个插件,并返回插件这个插件结构的指针*Glugin

Lookup: *Plugin

的惟一方法,通过名称symName在插件中寻找对应的变量或方法,以Symbol的形式返回

Symbol

根据定义

type Symbol interface{}

,Symbol是interface的别名,也就是说,我们可以从插件里面拿到任何类型的可导出元素。


注意几点问题:


插件中定义的 struct 无法暴露出来,可以让主程序和插件程序import公共的 package 来解决

私有方法、变量不会被暴露出来


官方文档在此:https://golang.org/pkg/plugin/

编写一个 Plugin 基本有以下几步:


1.Plguin 需要有自己的 main package

2.编译的时候,使用 go build -buildmode=plugin file.go 来编译

3.使用 plugin.Open(path string) 来打开.so文件,同一插件只能打开一次,重复打开会报错

4.使用 plugin.LookUp(name string) 来获取插件中对外暴露的方法或者类型

5.使用类型断言,断言后执行相应的方法


使用实例

定义plugin

//plugin.gopackage main
import "fmt"
var V int
func F() { fmt.Printf("Hello, number %d\n", V)}

加载使用plugin

//main.gopackage main
import "plugin"
func main() { p, err := plugin.Open("plugin.so") if err != nil { panic(err) } v, err := p.Lookup("V") if err != nil { panic(err) } f, err := p.Lookup("F") if err != nil { panic(err) } *v.(*int) = 7 f.(func())() // prints "Hello, number 7"}

编译

#!/bin/bashgo build --buildmode=plugin -o plugin.so plugin.gogo run main.go#Hello, number 7


plugin链接进golang程序会大量增加占用的内存。所以在使用plugin热更新的时候,当发现程序占用内存陡增的时候


下面看下plugin的源码,包含了4个文件

plugin.goplugin_dlopen.goplugin_stubs.goplugin_test.go

plugin.go

// Plugin is a loaded Go plugin.type Plugin struct { pluginpath string err string // set if plugin failed to load loaded chan struct{} // closed when loaded syms map[string]interface{}}

pluginpath:库的path


err:用于记录过程中的err


loaded:这个用于防止并发加载同一个库时候用


syms:这个记录的是库中所有的符号和其对应的值(可能是var、func等)


Open函数,封装了open函数

Lookup函数封装了lookup函数


plugin_stubs.go

// +build !linux,!darwin !cgo

这里是针对不支持平台的空实现,!linux,!darwin !cgo。可以看出,和文档中说的一样,非Linux,非darwin平台的时候编译成空实现。当然还有一个cgo,如果不支持cgo的话,也是无法实现plugin的。


plugin_dlopen.go

编译命令中,显示支持linux 和 darwin平台,当然要求是要支持cgo。

然后就是一个cgo的代码。其中封装了两个函数dlopen,dlsym

#cgo linux LDFLAGS: -ldlstatic uintptr_t pluginOpen(const char* path, char** err) { void* h = dlopen(path, RTLD_NOW|RTLD_GLOBAL);  
static void* pluginLookup(uintptr_t h, const char* name, char** err) { void* r = dlsym((void*)h, name);

这个是linux中标准的动态链接加载接口。

当然plugin只实现了封装了dlopen,dlsym,两个函数。这个和文档中所提供的接口和描述是符合的。

只提供了加载,并没有提供关闭

全局变量

var ( pluginsMu sync.Mutex plugins map[string]*Plugin)

pluginsMu:全局锁

plugins:保存加载的动态库


进入函数,一开始是一些字符串的转换。


重点是加锁后,会判断是否已经在加载,或者已经加载过的plugin。


这个时候,如果刚好plugin还在加载中,


<- p.loaded 会等待plugin加载完毕后,close掉p.loaded。


这种方式就是合并加载


这里就是调用了cgo代码pluginOpen,加载so库


初始化plugin结构体,并将其放入到全局的plugins这个map中。然后unlock全局锁。


继续,调用了cgo代码pluginLookup,查找init函数,并执行。


接着就是循环读取所有的符号,并将符号与其对应的值保存下来。保存在p.syms中。


最后close p.loaded,表示加载过程结束了。

所有的符号都保存在p.syms中,这个时候的查找,就只需要直接查找syms就可以了。


对于上面的问题,有如下解决方案:


1、每次生成的so带一个版本号比如game.1001.so


2、编译的时候新增--ldflags="-pluginpath=xxx"参数


3、使用unsafe进行转换(下面还会有注意事项)



以上是关于golang编译androidso无法加载的主要内容,如果未能解决你的问题,请参考以下文章

Qt Creator 编译后无法加载插件

C#程序在VS编译器加载时出现找不到方法,无法显示该程序窗体设计器

尝试执行编译任务但无法加载神秘模块

无法加载JIT编译器(CLR.DLL):文件可能丢失或损坏,请重新检查或重新安装,请问这个问题怎么解决!

尝试jetpack compose时显示错误:编译器后端并且无法由旧编译器加载

尝试编译代码 - 无法找到或加载主类 a