Golang static compilation and compilation failure troubleshooting steps

1. Background

      A simple audio and video parsing goprogram needs to be run on a gomachine without an environment. It is all linuxan environment, and I thought it could be migrated seamlessly. But in fact, I found that the running error was reported and glibcthe version was inconsistent. . .

      Therefore, I plan to directly compile a purely static executable program, and compile all dependent libraries directly into it, so that I can truly ignore platform restrictions. Who knew that static compilation directly reports errors? Well, let’s summarize the relevant knowledge points of static compilation and record the troubleshooting process.

premise

The blogger is using manjaroversion linux, the target server is ubuntuversion and the version is older.

2. Overview of static compilation

      goThe default is to use static compilation if gothe library used in the code does not depend on Cthe library. However, the packages used by more complex goprograms are likely to rely on system Clibraries, so the compiled files are dynamic. For example, you can lddview the files from the executable program through commands .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)

Specifically why dynamic compilation occurs, please refer to the principle:

[External dependencies of go executable files]

1. Perform static compilation

There are two ways to statically compile

Set CGO_ENABLED mode

      By default, gothe runtimeenvironment variable CGO_ENABLED=1, that is, the default start cgo, allows you goto call Cthe code in the code, and the files goof pre-compiledthe standard library .aare also compiled in this case.
We can CGO_ENABLED=0statically compile by specifying it on the command line

CGO_ENABLED=0 go build .
Specify link method

      goThe default is to use it internal linkingwithout starting externally external linker(如:gcc、clang等). The external linkingmechanism cmd/linkis to type all the generated .ofiles into a .ofile, and then hand it over to an external linker, such as gccor clangfor final link processing.

If we want to compile statically, we need to -ldflagsspecify linkmodethe parameters in externaland specify static linking.

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

To statically compile a project, compile the command:

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

Compilation error

/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. Compilation error analysis

/usr/bin/ld 是 Linux 系统中的链接器(linker),用于将目标文件和库文件等链接起
来,生成最终的可执行文件或共享库。在大多数情况下,这个链接器已经默认设置
好,并且可以自动被编译器调用。

而对于 Go 语言的静态编译过程,我们需要在编译命令中加入相应的选项,指定使用
外部链接模式和静态链接方式,并将必要的库文件链接到生成的二进制文件中。具体
来说,可以使用 -ldflags 选项传递参数给链接器,包括 -linkmode external 表示启用
外部链接模式、-extldflags "-static" 表示启用静态链接方式等。

It seems that it is not found libopus. Let’s first confirm whether it is installed on this machine.libopus

(1) Confirm whether libopus is installed on the system
ldconfig -p | grep libopus

Query via package managerlibopus

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) Set LD_LIBRARY_PATH

This environment variable is used to specify the path that the program searches when dynamically loading shared libraries (also called dynamic link libraries) at runtime. When a program needs to load a shared library, it searches the path in the following order:

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

Continue static compilation, but the search still libopusfails. Ok, it’s starting to look interesting. Let’s check it out in detail below.

3. Detailed investigation process

1. Download the bpf troubleshooting tools bcc, bcc-tools, python-bcc

      If you need to specify the version, use this command:

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

bccis a cross-platform toolset for Linuxdynamic tracing and probing on systems. Tools opensnoopcan be used to monitor the system calls an application makes to open, read, or write files to understand which files in the system are accessed and how they are accessed. Mainly monitors open()、read()、write()system calls related to file operations.

straceYou can also view the system call function, which is used here opensnoopfor troubleshooting.

2. Use opensnoop to troubleshoot the compilation process and find the paths for shared libraries.

# 开启一个窗口,输入这个命令
sudo opensnoop

# 开启另一个窗口,进行编译
go build -o myapp -ldflags '-w -s -extldflags "-static -lm"'

# 查看opensnoop的输出
结果发现编译过程中查找的是libopus.a文件,我们只有libopus.so文件

3. Download the opus package from pacman

# 查看安装opus都会安装什么东西
sudo pacman -Ql opus

# 结果是没有.a文件

It seems that you can only compile .athe file yourself or download it from other package management platforms. . .

4. Check the current version of libopus.so file

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

5. Download libopus.a

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

ubuntuThe package found in the query libopuscontains libopus.afiles. The version is also compatible, just download it.

6. Decompress the .deb file

# 下载地址
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. Add LD_LIBRARY_PATH

Directly put libopus.athe file into the current directory and set the search shared library path.

export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH

Executing compilation still reports an error, and it seems that it does not take effect.

8. Add LIBRARY_PATH as the current directory

# 执行静态编译成功

# 查看静态编译文件
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. The difference between LD_LIBRARY_PATH and LIBRARY_PATH

      LD_LIBRARY_PATHand LIBRARY_PATHare both environment variables used to specify shared library search paths, but there are some subtle differences.

LD_LIBRARY_PATHIt is mainly used to control the path for loading shared libraries when the program is running,
while LIBRARY_PATHit is mainly used to control the path for the compiler to find shared libraries during compilation.

Static compilation needs to link .afiles, LD_LIBRARY_PATHmainly to set .sothe search path of files, so it will not take effect.

end

Guess you like

Origin blog.csdn.net/LJFPHP/article/details/132031641