A solution to avoid introducing external dynamic libraries when compiling GO

Introduction

I encountered a problem recently. There is a library github.com/google/gopacket used in a traffic collection component. Everything works fine with this library, but there is only one shortcoming. The compiled binary file depends on the dynamics of libpcap.so library. This caused some trouble for the installation package to be compatible with multiple platforms, so I thought about how to link libpcap, an externally dependent static library, into the executable program while compiling the go program.

How is gopacket built?

Here first intercept a small piece of source code (github.com/google/gopacket/pcap/pcap_unix.go), here you can see that some compilation parameters are specified in cgo, among which "-lpcap" is the specified link The name of the library. It can be said to be quite rough.

#cgo solaris LDFLAGS: -L /opt/local/lib -lpcap
#cgo linux LDFLAGS: -lpcap
#cgo dragonfly LDFLAGS: -lpcap
#cgo freebsd LDFLAGS: -lpcap
#cgo openbsd LDFLAGS: -lpcap
#cgo netbsd LDFLAGS: -lpcap
#cgo darwin LDFLAGS: -lpcap

demo

// 使用gopacket 抓包的简单示例
package main

import (
	"github.com/google/gopacket"
	"github.com/google/gopacket/layers"
	"github.com/google/gopacket/pcap"
	logger "github.com/sirupsen/logrus"
	"log"
)

const (
	device  = "ens32"
	SnapLen = int32(65535) // libpcap 接收数据的长度
	Promisc = false        // 是否开启混杂模式
	BPF     = "icmp"
)

func main() {
	handle, err := pcap.OpenLive(device, SnapLen, Promisc, pcap.BlockForever)
	if err != nil {
		log.Fatal(err)
	}
	defer handle.Close()

	// 编译并设置bpf过滤规则
	if err = handle.SetBPFFilter(BPF); err != nil {
		log.Fatal(err)
	}

	// 开始获取流量
	packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
	packetSource.NoCopy = true
	packetChan := packetSource.Packets()

	for packet := range packetChan {
		if packet.TransportLayer() == nil {
			// icmp流量
			icmpStreamHandle(packet)
		} else if packet.TransportLayer().LayerType() == layers.LayerTypeTCP {
			// tcp流量
			tcpStreamHandle(packet)
		} else if packet.TransportLayer().LayerType() == layers.LayerTypeUDP {
			// udp流量
			udpStreamHandle(packet)
		}
	}
}

func icmpStreamHandle(packet gopacket.Packet) {
	logger.Info("get icmp packet")
}
func tcpStreamHandle(packet gopacket.Packet) {
}
func udpStreamHandle(packet gopacket.Packet) {
}

Compile and ldd to view the usage of dependent libraries

[root@localhost ddk]# go build main.go && ldd main
	linux-vdso.so.1 =>  (0x00007ffe965f3000)
	libpcap.so.1 => /lib64/libpcap.so.1 (0x00007f6be101f000)
	libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f6be0e03000)
	libc.so.6 => /lib64/libc.so.6 (0x00007f6be0a35000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f6be1260000)
[root@localhost ddk]# 

It is easy to see the dependency on the dynamic library libpcap.so.1

Prepare static library

Find the libpcap.a file corresponding to your libpcap.so, either by installing the libpcap-devel (libpcap-dev) library or building it directly from scratch. Here has been rebuilt as an example:

yum install -y gcc flex byacc
cd /usr/local/source
wget http://www.tcpdump.org/release/libpcap-1.9.1.tar.gz
tar zxvf libpcap-1.9.1.tar.gz
cd libpcap-1.9.1 && ./configure && make

Specify compilation parameters

The "-lpcap" parameter can be used to link both dynamic libraries and static libraries. The dynamic library is preferred. Then let the go compiler execute the search library path during compilation and place the static library under the path. .

[root@localhost ddk]# CGO_LDFLAGS="-g -O2 -L/usr/local/source/libpcap-1.9.1 -I/usr/local/source/libpcap-1.9.1" go build -ldflags '-w -s' -o main main.go
[root@localhost ddk]# ldd main
	linux-vdso.so.1 =>  (0x00007fff6cde4000)
	libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f1e767fa000)
	libc.so.6 => /lib64/libc.so.6 (0x00007f1e7642c000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f1e76a16000)
[root@localhost ddk]# 

Explain this compilation command a little bit CGO_LDFLAGS="-g -O2 -L/usr/local/source/libpcap-1.9.1 -I/usr/local/source/libpcap-1.9.1" go build -ldflags '-w -s' -o main main.go. The CGO_LDFLAGS environment variable is used to specify the parameters of cgo when building, -L specifies the location to find the dynamic and static libraries, -I is used to specify the specified path of the source docker file, and is used to remove debug and symbol -ldflags '-w -s' table information, it’s okay if you don’t add it.
Now we can see that the dynamic library dependency on libpcap.so has disappeared, because libpcap has been linked into the go-compiled program in the form of a static library.

Guess you like

Origin blog.csdn.net/weixin_47367099/article/details/127458666