go 语言一个很好的地方是可以方便的进行交叉编译,也就是编译出在不同于当前硬件平台 + 操作系统 上运行的程序。
今天尝试编译一段代码到小米路由器 3G 上运行,具体过程记录在下面,供大家参考。
环境:
编译环境:mac + go 1.11
运行环境:路由器操作系统是 openwrt,cpu 为 mips 架构的 32 位处理器 MT7621,双核四线程 (居然一个路由器 cpu 也带超线程技术 ),无 fpu。
代码:
hello.go
package main
import "fmt"
func main() {
fmt.Printf("hello, worldn")
}
编译:
交叉编译的命令和平时一样,也是 go build,只需要通过 GOOS 和 GOARCH 指定目标平台的操作系统和 cpu 架构,编译出来的就是其他平台上的可执行文件了
这里因为 MT7621 没有 fpu 也就是浮点处理器,所以指定使用软浮点,这样 go 会通过其他指令来模拟浮点运算,编译出来的程序里面不会包含浮点计算指令,否则调用不存在的 fpu 会出现 Illegal instruction
GOOS=linux GOARCH=mipsle GOMIPS=softfloat CGO_ENABLED=0 go build
早期版本的 go 需要借助第三方库才可以实现 mips 平台的交叉编译,但 1.11 版本的 go 已经内置了 mips 平台的支持,所以不需要第三方库
对 MT7621 无 FPU 的另外一个处理办法是在编译 openwrt 时打开内核的浮点模拟器,这样不需要指定 GOMIPS=softfloat 编译出来的程序也可以运行。不过我觉得既然都是模拟,在内核模拟跟在这里模拟也差不多,所以对无 FPU 的处理器,直接指定 GOMIPS=softfloat 即可
编译得到 hello , scp 至目标平台运行
root@OpenWrt:/tmp# ./hello
hello, world
root@OpenWrt:/tmp#
一切正常,我们已经成功编译出在路由器上执行的可执行文件了
过程中的坑:
1,go 1.11 版本编译缓存处理有问题,切换 GOMIPS=softfloat/hardfloat 时,生成的产物不会变化,需要手动清理缓存,才能得到正确的结果。经过搜索在 github 上找到了对应的 issue,并且是已修复状态。于是升级到最新的 go 1.11.5,问题解决。
2,一开始 GOARCH 指定成了 mips,即 mips 处理器的大端架构,编译出来的产物,执行时出现
./hello: line 1: syntax error: unexpected "("
经查发现 GOARCH 可以为 mips/mipsle 分别对应大小端,这里 MT7621 为小端架构,于是将 GOARCH 换成 mipsle 后解决