Golang中glibc_version_header的使用与配置

Golang中glibc_version_header的使用与配置 对于C程序,我可以使用glibc_version_header来强制代码链接特定版本的GLIBC符号。这样我就能在一个系统上构建程序,然后在另一个使用不同glibc版本的系统上运行,这对C程序很有效。

现在我正尝试让这个方法适用于Go语言程序。 我已经修改了所有依赖代码,使其包含版本化的符号。 然而,当我运行Go程序时,出现了以下错误:

test22$ ./SDS/src/SDS_fabric/service/bin/x86/bare-metal/SDS_fabric
./SDS/src/SDS_fabric/service/bin/x86/bare-metal/SDS_fabric: /lib64/libc.so.6: version `GLIBC_2.28’ not found (required by ./SDS/src/SDS_fabric/service/bin/x86/bare-metal/SDS_fabric)

问题: 我是否需要在main.go函数中添加一些CGO指令? 如何找出是哪个符号导致了这个问题,以及它包含在哪个代码中?


更多关于Golang中glibc_version_header的使用与配置的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于Golang中glibc_version_header的使用与配置的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go中使用glibc版本控制需要结合CGO和链接器选项。以下是解决方案:

1. 创建glibc版本头文件

首先创建glibc_version.h头文件:

// glibc_version.h
#ifndef GLIBC_VERSION_H
#define GLIBC_VERSION_H

#include <features.h>

// 定义你需要的GLIBC版本
#define GLIBC_VERSION "2.17"

// 版本化符号包装
#if defined(__GNUC__) && defined(__linux__)

#define GLIBC_COMPAT_SYMBOL(orig_func) \
    __asm__(".symver " #orig_func "_compat, " #orig_func "@GLIBC_" GLIBC_VERSION);

#define GLIBC_DEFAULT_SYMBOL(orig_func) \
    __asm__(".symver " #orig_func "_default, " #orig_func "@@GLIBC_" GLIBC_VERSION);

// 示例:memcpy的版本化
__asm__(".symver memcpy, memcpy@GLIBC_2.2.5");
__asm__(".symver memcpy_2_14, memcpy@GLIBC_2.14");

#else
#define GLIBC_COMPAT_SYMBOL(orig_func)
#define GLIBC_DEFAULT_SYMBOL(orig_func)
#endif

#endif // GLIBC_VERSION_H

2. 在Go中使用CGO

在main.go或需要的地方添加:

// main.go
package main

/*
// 包含glibc版本头
#include "glibc_version.h"
#include <string.h>

// 包装C函数
void* wrap_memcpy(void* dest, const void* src, size_t n) {
    return memcpy(dest, src, n);
}
*/
import "C"
import "unsafe"

func main() {
    // 使用C函数触发链接
    src := []byte("hello")
    dest := make([]byte, len(src))
    
    C.wrap_memcpy(
        unsafe.Pointer(&dest[0]),
        unsafe.Pointer(&src[0]),
        C.size_t(len(src)),
    )
    
    // 你的程序逻辑
}

3. 构建脚本

创建构建脚本build.sh

#!/bin/bash
# build.sh

export CGO_ENABLED=1
export CC=gcc
export CXX=g++

# 设置glibc版本
export GLIBC_VERSION=2.17

# 编译命令
go build -ldflags="-linkmode=external -extldflags='-Wl,--version-script=version.script'" -o output main.go

4. 创建版本脚本

version.script文件:

GLIBC_2.17 {
    global:
        *;
    local:
        *;
};

5. 诊断符号问题

使用以下命令找出问题符号:

# 查看二进制文件中的GLIBC依赖
objdump -T your_program | grep GLIBC

# 查看特定版本的符号
objdump -T your_program | grep GLIBC_2.28

# 查看所有版本化符号
readelf -s your_program | grep GLIBC

# 查看动态段
readelf -d your_program | grep NEEDED

# 使用scanelf工具(如果可用)
scanelf -n your_program

6. 完整示例:强制使用旧版本glibc

// +build linux
// glibc_compat.go

package main

/*
#define _GNU_SOURCE
#include <features.h>

// 强制使用GLIBC_2.17
__asm__(".symver memcpy, memcpy@GLIBC_2.2.5");
__asm__(".symver pthread_create, pthread_create@GLIBC_2.2.5");
__asm__(".symver clock_gettime, clock_gettime@GLIBC_2.2.5");

#include <string.h>
#include <pthread.h>
#include <time.h>

// 包装函数
void* compat_memcpy(void* dest, const void* src, size_t n) {
    return memcpy(dest, src, n);
}
*/
import "C"

import (
    "fmt"
    "unsafe"
)

func main() {
    // 触发链接
    var buf1, buf2 [10]byte
    C.compat_memcpy(
        unsafe.Pointer(&buf1[0]),
        unsafe.Pointer(&buf2[0]),
        C.size_t(10),
    )
    fmt.Println("Running with compat GLIBC")
}

7. 构建命令

# 使用外部链接器
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 \
go build -ldflags='-linkmode=external -extldflags="-Wl,--version-script=version.script -Wl,-z,now"' \
-o your_program main.go glibc_compat.go

8. 验证构建

# 检查glibc版本要求
objdump -p your_program | grep -A5 "Version References"

# 检查是否还有GLIBC_2.28依赖
strings your_program | grep GLIBC_2.28

# 使用ldd查看动态库
ldd your_program

通过以上配置,Go程序将链接到指定版本的GLIBC符号。使用objdumpreadelf工具可以精确找出哪个符号导致了GLIBC_2.28的依赖问题。

回到顶部