banner
biuaxia

biuaxia

"万物皆有裂痕,那是光进来的地方。"
github
bilibili
tg_channel

[Repost] Golang Compilation Optimization: Static Linking

title: [Reprint] Golang Compilation Optimization: Static Linking
date: 2022-05-26 14:34:00
toc: false
index_img: https://puep.qpic.cn/coral/Q3auHgzwzM4fgQ41VTF2rAOrGvBASaprDnwukcq5GutBULkN9WJT9A/0
category:

  • Go
    tags:
  • Resources
  • Java
  • Projects
  • Files
  • Golang
  • Solutions
  • Format
  • Different
  • Occurrence

Original Article: Golang Compilation Optimization: Static Linking - FranzKafka Blog. This site is only for saving records and adjusting formatting, and does not engage in commercial activities for profit.

Recently, I started learning Golang and I am quite satisfied with some of its features. It reminds me of the phrase that was popular when Python emerged - "Life is short, I use Python." However, Python is ultimately an interpreted language, and as a true programmer, it is not reasonable to not write compiled languages. So, I unilaterally declare (just kidding~) that a programmer who does not write compiled languages is not a qualified programmer. So, going back to Golang itself, a similar phrase can be derived: "Backend is short, I use Golang"~

About half a month ago, I briefly studied the basics of Golang and started writing my first Golang program. Whether it's language features, public and private definitions, interfaces similar to Java, built-in formatting tools, rich standard libraries and a large number of third-party libraries, or efficient compilation, testing, and execution, they all made me doubt my choice of C++, which is my main language. This thing is so good, why write C++!

Later, I started participating in a small open-source project and added some features myself. After compiling and running it, there were no problems, so I confidently released a release. However, many users reported errors on some older versions of the system, and the compilation errors were all related to "GLIBC_2.28 not found". After Googling for a long time, I found that there were quite a few similar problems, so I thought about solving this problem. I found many answers on Google, but they all boiled down to two options: either upgrade the system or upgrade the glibc dependency. But what if I don't want to choose either of these options (because they are troublesome)? Are there any other ways to solve the whole problem? This article is a record of this problem~

Background: Why is there an error#

Before we dive into finding a solution, we need to understand why this error occurs. Let's start with the error message itself. What is Glibc? We can see from Wikipedia that it is a basic component library that our Linux kernel relies on to run, providing many standard API interfaces. These standard interfaces include open, read, write, malloc, printf, dlopen, and so on. It is easy to imagine how important it is.

In different Linux distributions, Glibc versions are different. Naturally, the API functions provided by these different versions are also different. But what does this have to do with the Go program reporting an error? Here we have to introduce another topic, which is CGO.

CGO is actually designed for calling C functions in the Go language. We all know that Linux or Unix kernels are implemented in C. After many years of development, C/C++ has a complete ecosystem and a large number of standard APIs. So when designing the Go language, it was thought to directly use these APIs or C programs wrapped after packaging. To achieve this mechanism, CGO must be used. Here is an example of using CGO:

package cgoexample

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

void myprint(char* s) {
	printf("%s\n", s);
}
*/
import "C"

import "unsafe"

func Example() {
	cs := C.CString("Hello from stdio\n")
	C.myprint(cs)
	C.free(unsafe.Pointer(cs))
}

Since we want to call the standard API interfaces provided by C through CGO, there may be differences in the implementation of these interfaces due to different versions of the Glibc library. This is something we don't want to see in Go programs. Therefore, many standard libraries that use CGO, such as the net standard library and the os/user standard library, have requirements for the version of Glibc.

In our error message, we can see that this program should depend on Glibc version 2.28. We can use the ldd --version command to confirm the Glibc version on our machine.

image

In the system where the problem occurs, our Glibc version is 2.23. But the Golang program we compiled requires Glibc version 2.28 or above. Due to the low version of Glibc, our program cannot run normally and reports an error.

Solution: How to solve the error#

Here are several possible solutions.

Solution 1: Upgrade to the latest system. Generally, major Linux distributions will integrate the latest Glibc version when releasing the latest versions, so upgrading the system can obtain the latest Glibc version. The following table shows the Glibc versions integrated in some popular Linux distributions:

GlibcRHELCentOSSLEDebianUbuntuKylinManylinux
2021/2/12.3321.04 hirsute
2.3220.10 groovy
2020/2/12.3111 bullseye20.04 LTS focal
2.319.10 eoan
2.2919.04 disco
2018/8/12.288.3 Ootpa8.2.200410 buster18.10 cosmicV10 Tercel
2018/2/12.2718.04 LTS bionic
2.2617.10 artful
2.2615-SP2
2016/8/42.249 stretch17.04 zesty
16.10 yakkety

From the table, we can see that if you want to use Glibc version 2.28 or above, your system should be one of the following: CentOS 8.1, Fedora 30, Gentoo - 2020-01-22, Arch Linux 2020.01.22, Ubuntu 18.10, Debian 8.

Solution 2: Upgrade the Glibc version. Actually, this is not the method I recommend. Because Glibc is a very important component of the Linux system, upgrading or downgrading it may cause the system to not work properly. Of course, in some cases, you can try to upgrade Glibc separately.

Solution 3: Compile the Go program with static linking. When not cross-compiling, CGO is enabled by default, and the compiled program will generate an executable file in the form of dynamic linking. We can use the file command or ldd command to check and confirm this information. As shown below:

image

Dynamic linking is originally used to link shared libraries in a dynamically loaded form to reduce the size of the software itself and achieve maximum software reuse. However, it generally has requirements for the version of the shared libraries dynamically linked by the host system. Static linking, on the other hand, links with the shared libraries contained in the compilation system as resources during compilation and does not depend on the system's shared libraries at runtime.

So, for this problem, we can compile it with full static linking.

Use the following command to enable CGO and compile with static linking:

CGO_ENABLED=1 go build -trimpath -ldflags='-extldflags=-static' -tags musl,osusergo,netgo,sqlite_omit_load_extension -o output -v main.go

After successful compilation, we can use the command to check and see that it has been statically linked.

image

Afterwards, we can run the executable file on the older system and it will run normally~

References:

  1. https://www.arp242.net/static-go.html

  2. https://www.kawabangga.com/posts/4495#comment-34735

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.