golang静态编译及编译失败排查步骤

文章目录

    • 一、背景
      • 前提
    • 二、静态编译概述
      • 1、执行静态编译
        • 设置CGO_ENABLED方式
        • 指定link方式
      • 2、编译报错分析
        • (1)确认系统上有没有安装libopus
        • (2)设置LD_LIBRARY_PATH
    • 三、详细排查过程
      • 1、下载bpf排查工具bcc, bcc-tools,python-bcc
      • 2、使用opensnoop 排查编译过程查找共享库的路径都有哪些
      • 3、pacman下载opus包
      • 4、查看当前的libopus.so文件的版本
      • 5、下载libopus.a
      • 6、.deb文件解压缩
      • 7、添加LD_LIBRARY_PATH
      • 8、添加LIBRARY_PATH为当前目录
      • 9、LD_LIBRARY_PATH和LIBRARY_PATH的区别

一、背景

      一个简单的音视频解析go程序需要放到一台没有go环境的机器中去运行,都是linux环境,本来以为是可以无缝迁移的。但实际上却发现运行报错,glibc版本不一致。。。

      因此打算直接编一个纯静态的可执行程序,依赖库都直接编译进去,这样就可以做到真正的无视平台限制。谁知道静态编译直接报错,好吧,那就总结一下静态编译相关知识点,并记录一下排查流程吧

前提

博主使用的是manjaro版本的linux,目标服务器是ubuntu版本且版本比较老。

二、静态编译概述

      go默认是使用静态编译的方式,如果go代码中使用的库不依赖C库的话。不过复杂点的go程序使用的包大概率是依赖系统C库的,所以编译出来的文件是动态的,例如可以通过ldd命令查看可执行程序以来的.so文件。

ldd 可执行程序linux-vdso.so.1 (0x00007ffeeaee7000)libresolv.so.2 => /usr/lib/libresolv.so.2 (0x00007ff3838a6000)libc.so.6 => /usr/lib/libc.so.6 (0x00007ff3836bf000)/lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007ff3838d3000)

具体为什么会动态编译,原理可以参考:

【go可执行文件的外部依赖】

1、执行静态编译

静态编译有两种方式

设置CGO_ENABLED方式

      默认情况下,goruntime环境变量CGO_ENABLED=1,即默认开始cgo,允许你在go代码中调用C代码,gopre-compiled标准库的.a文件也是在这种情况下编译出来的。
我们可以在命令行指定CGO_ENABLED=0就可以静态编译

CGO_ENABLED=0 go build .

指定link方式

      go默认是使用internal linking,而无需启动外部external linker(如:gcc、clang等)。而external linking机制则是cmd/link将所有生成的.o都打到一个.o文件中,再将其交给外部的链接器,比如gccclang去做最终链接处理。

我们想要静态编译的话,需要在 -ldflags 中指定linkmode参数为external,并且指定是静态链接。

-ldflags '-linkmode "external" -extldflags "-static"' 
忽略'-linkmode "external" ,只设置-extldflags 也是ok的

静态编译一个项目,编译命令:

go build -o myapp -ldflags '-w -s -extldflags "-static"'

编译报错

/usr/lib/go/pkg/tool/linux_amd64/link: running gcc failed: exit status 1
/usr/bin/ld: cannot find -lopus: No such file or directory
collect2: error: ld returned 1 exit status   

2、编译报错分析

/usr/bin/ld 是 Linux 系统中的链接器(linker),用于将目标文件和库文件等链接起
来,生成最终的可执行文件或共享库。在大多数情况下,这个链接器已经默认设置
好,并且可以自动被编译器调用。而对于 Go 语言的静态编译过程,我们需要在编译命令中加入相应的选项,指定使用
外部链接模式和静态链接方式,并将必要的库文件链接到生成的二进制文件中。具体
来说,可以使用 -ldflags 选项传递参数给链接器,包括 -linkmode external 表示启用
外部链接模式、-extldflags "-static" 表示启用静态链接方式等。

看起来是没找到libopus,我们先确认本机上有没有安装libopus

(1)确认系统上有没有安装libopus

ldconfig -p | grep libopus

通过包管理器查询libopus

pacman -Ql opus | grep libopus
opus /usr/lib/libopus.so   
opus /usr/lib/libopus.so.0   
opus /usr/lib/libopus.so.0.8.0

(2)设置LD_LIBRARY_PATH

这个环境变量用于指定程序在运行时动态加载共享库(也称为动态链接库)时所要搜索的路径。当程序需要加载某个共享库时,它会按照以下顺序搜索路径:

程序中已经指定的库路径(如使用 -L 参数指定的路径)
LD_LIBRARY_PATH 中指定的路径
export LD_LIBRARY_PATH=/usr/lib:$LD_LIBRARY_PATH  

继续静态编译,还是寻找libopus失败。 ok,开始有点意思了,我们下面详细排查下。

三、详细排查过程

1、下载bpf排查工具bcc, bcc-tools,python-bcc

      需要指定版本的话,使用这个命令:

sudo /usr/bin/pip install -i https://pypi.org/simple bcc==0.27.0 

bcc是一种跨平台的工具集,可用于在Linux系统上进行动态追踪和探查。其中的opensnoop工具可以用于监视应用程序打开、读取或写入文件的系统调用,以了解系统中哪些文件被访问,以及它们是如何被访问的。主要监听open()、read()、write()等与文件操作相关的系统调用。

strace也能查看系统调用函数,这里使用opensnoop来进行排查。

2、使用opensnoop 排查编译过程查找共享库的路径都有哪些

# 开启一个窗口,输入这个命令
sudo opensnoop# 开启另一个窗口,进行编译
go build -o myapp -ldflags '-w -s -extldflags "-static -lm"'# 查看opensnoop的输出
结果发现编译过程中查找的是libopus.a文件,我们只有libopus.so文件

3、pacman下载opus包

# 查看安装opus都会安装什么东西
sudo pacman -Ql opus# 结果是没有.a文件

看来只能自己编译出来.a文件或者去其他包管理平台下载了。。。

4、查看当前的libopus.so文件的版本

# 查看.so的版本
sudo pacman -Qo /usr/lib/libopus.so
/usr/lib/libopus.so is owned by opus 1.3.1-3

5、下载libopus.a

https://ubuntu.pkgs.org/20.04/ubuntu-main-amd64/libopus-dev_1.3.1-0ubuntu1_amd64.deb.html

查询到ubuntu上的libopus包是带有libopus.a文件的。版本也能对得上,下载即可。

6、.deb文件解压缩

# 下载地址
https://www.cyberciti.biz/faq/how-to-extract-a-deb-file-without-opening-it-on-debian-or-ubuntu-linux/# 查看下载的.deb包
file libopus-dev_1.3.1-0ubuntu1_amd64.deb
libopus-dev_1.3.1-0ubuntu1_amd64.deb: Debian binary package (format 2.0), with control.tar.xz, data compression xz# 解压缩deb包
ar x libopus-dev_1.3.1-0ubuntu1_amd64.deb 
# 解压完毕后会出现几个文件,主要用到data.tar.gz包,这个是存放二进制文件的压缩包# 解压缩tar
tar -xvf data.tar.xz  # 可以发现libopus.a文件
./usr/lib/x86_64-linux-gnu/libopus.a

7、添加LD_LIBRARY_PATH

直接把libopus.a文件放到当前目录,并设置搜索共享库路径。

export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH

执行编译依然报错,好像没有生效。

8、添加LIBRARY_PATH为当前目录

# 执行静态编译成功# 查看静态编译文件
file myapp
myapp: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, BuildID[sha1]=8afbe7cba97e5f860ac49cfb6692e5eb5ec18cd5, for GNU/Linux 4.4.0, stripped

9、LD_LIBRARY_PATH和LIBRARY_PATH的区别

      LD_LIBRARY_PATHLIBRARY_PATH 都是用于指定共享库搜索路径的环境变量,但有一些细微的区别。

LD_LIBRARY_PATH 主要用于控制程序运行时加载共享库的路径,
LIBRARY_PATH 则主要用于控制编译器在编译时寻找共享库的路径。

静态编译要链接的是.a文件,LD_LIBRARY_PATH主要是设置.so文件的搜索路径,所以就不生效

end

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/16858.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java开发 - Redis常见问题场景及解决办法一览

前言 前面几篇博客对Redis的讲解不可谓不详细,从单节点到主从,到sentinel哨兵,到Redis Cluster都一一搭配使用给大家做了讲解,但在使用Redis的过程中我们还是要注意一些比较常见的问题,比如穿透、击穿等等&#xff0c…

助力保险行业数字化创新,麒麟信安参展2023中国财险科技应用高峰论坛

2023年7月27日,由中科软科技股份有限公司主办的“中国财险科技应用高峰论坛”在北京古北水镇成功举办。作为享誉中国保险科技界的盛会,本次活动以“数智保险 创新未来”主题,汇聚全国数百位保险公司主管领导、资深保险行业信息化专家&#xf…

Spring中解决循环依赖为什么要用三级缓存,二级为什么不行呢?

在Spring中解决循环依赖时,使用三级缓存是因为循环依赖的解决过程需要多个阶段的处理,并且需要保留中间状态。而使用二级缓存无法满足这些需求。 循环依赖是指两个或多个Bean之间相互依赖的情况,例如A依赖B,B又依赖A。为了解决循…

Ae 效果:CC Kernel

颜色校正/CC Kernel Color Correction/CC Kernel CC Kernel(CC 卷积核)效果主要用于图像的卷积处理,通过在卷积矩阵中设置不同的权重值,可以实现图像的锐化 Sharpen、模糊 Blur、查找边缘 Find Edges以及浮雕 Emboss等效果。 ◆ …

服务调用---------Ribbon和Feign

目录​​​​​​​ 1、Ribbon 1.1 Ribbon简介 1.2 Ribbon负载均衡 负载均衡原理 负载均衡策略 Ribbon和Nginx的区别 1.3 服务调用和Ribbon负载均衡实现 2、Feign&openFeign 3、Feign支持的配置 日志功能 连接池 feign-api远程包 1、Ribbon 1.1 Ribbon简介 Ribb…

神经概率语言模型

本文主要参考《A Neural Probabilistic Language Model》这是一篇很重要的语言模型论文,发表于2003年。主要贡献如下: 提出了一种基于神经网络的语言模型,是较早将神经网络应用于语言模型领域的工作之一,具有里程碑意义。采用神经网络模型预测下一个单词…

源码阅读:classnames

源码阅读:classnames 源码阅读:classnames简介源码解读indexdedupebind类型声明 学习与收获 源码阅读:classnames 简介 classnames 一个简单的 JavaScript 实用程序,用于有条件地将类名连接在一起。 可以通过 npm 包管理器从 n…

【YOLO】模型推理(trt和engine)

【YOLO】模型推理(trt和engine) 上一章 yolov5+TensorRT推理 文章目录 【YOLO】模型推理(trt和engine)前言一、pt转onnx模型1.1 下载yolov51.2 模型导出二、Netron Viewer安装三、onnx转trt模型3.1 安装TensorRT3.2 模型转换四、执行推理4.1 推理五、模型量化FP16\INT85.1 性能…

05-向量的意义_n维欧式空间

线性代数 什么是向量?究竟为什么引入向量? 为什么线性代数这么重要?从研究一个数拓展到研究一组数 一组数的基本表示方法——向量(Vector) 向量是线性代数研究的基本元素 e.g. 一个数: 666,…

leetcode算法题--统计完全子数组的数目

原题链接:https://leetcode.cn/problems/count-complete-subarrays-in-an-array/ 一开始的做法比较简单粗暴,复杂度是O(n*n) func countCompleteSubarrays(nums []int) int {cnt1 : make(map[int]int)for _, num : range nums {cnt1[num] }count : len…

springboot整合tio-websocket方案实现简易聊天

写在最前: 常用的http协议是无状态的,且不能主动响应到客户端。最初想实现状态动态跟踪只能用轮询或者其他效率低下的方式,所以引入了websocket协议,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务…

实用上位机--QT

实用上位机–QT 通信协议如下 上位机设计界面 #------------------------------------------------- # # Project created by QtCreator 2023-07-29T21:22:32 # #-------------------------------------------------QT += core gui serialportgreaterThan(QT_MAJOR_V…

Unity实现在3D模型标记

Canvas 模式是UI与3D混合模式(Render modelScreen space-Camera) 实现在3D模型标记,旋转跟随是UI不在3D物体下 代码: using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public clas…

矩阵中的路径(JS)

矩阵中的路径 题目 给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。 单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是…

valgrind——内存泄漏检测介绍

文章目录 1. 概述1. 体系结构2. Linux 程序内存空间布局3. 内存检查原理 2. valgrind工具3. 常用选项4. 示例1. 内存泄漏2. 数组越界3. 内存覆盖4. 使用未初始化的值5. 内存申请与释放函数不匹配 5. 总结 1. 概述 1. 体系结构 Valgrind 是一套 Linux 下,开放源代码…

移动端个人中心UI设计

效果图 源码如下 页面设计 <template><div class"container"><!-- 顶部用户信息 start--><div class"header"><div class"user-info"><van-image class"user-img" round width"70" :sr…

Idea 2023.2 maven 打包时提示 waring 问题解决

Version idea 2023.2 问题 使用 Maven 打包 &#xff0c;控制台输出 Waring 信息 [WARNING] [WARNING] Plugin validation issues were detected in 7 plugin(s) [WARNING] [WARNING] * org.apache.maven.plugins:maven-dependency-plugin:3.3.0 [WARNING] * org.apache.…

HCIP--云计算题库 V5.0版本

在国家政策的支持下&#xff0c;我国云计算应用市场发展明显加快&#xff0c;越来越多的企业开始介入云产业&#xff0c;出现了大量的应用解决方案&#xff0c;云应用的成功案例逐渐丰富&#xff0c;用户了解和认可程度不断提高&#xff0c;云计算产业发展迎来了“黄金机遇期”…

【ArcGIS Pro二次开发】(55):给多个要素或表批量添加字段

在工作中可能会遇到这样的场景&#xff1a;有多个GDB要素、表格&#xff0c;或者是SHP文件&#xff0c;需要给这个要素或表添加相同的多个字段。 在这种情况下&#xff0c;手动添加就变得很繁琐&#xff0c;于是就做了这个工具。 需求具体如下图&#xff1a; 左图是待处理数据…

C数据结构——无向图(邻接矩阵方式) 创建与基本使用

源码注释 // // Created by Lenovo on 2022-05-13-上午 9:06. // 作者&#xff1a;小象 // 版本&#xff1a;1.0 //#include <stdio.h> #include <malloc.h>#define MAXSIZE 1000 // BFS队列可能达到的最大长度 #define MAX_AMVNUMS 100 // 最大顶点数typedef enu…