如何在Cgo中设置LD_LIBRARY_PATH?无需命令行导出
在Go代码中设置LD_LIBRARY_PATH以加载C++共享库
问题场景
我用Go调用C++共享库,代码如下:
package main /* #cgo LDFLAGS: -L/home/roroco/Dropbox/cpp/cpp_lib/draft/go_call_cpp -lprogress #include "progress_lib.h" // Callback function to be called from C++ extern void progressCallback(int progress); */ import "C" import ( "fmt" "sync" ) var wg sync.WaitGroup //export progressCallback func progressCallback(progress C.int) { fmt.Printf("Progress: %d%%\n", progress) if progress == 100 { wg.Done() } } func main() { wg.Add(1) go func() { C.do_work(C.progress_callback(C.progressCallback)) }() wg.Wait() }
手动在命令行导出LD_LIBRARY_PATH后,程序能正常运行:
~/Dropbox/cpp/cpp_lib/draft/go_call_cpp $ export LD_LIBRARY_PATH=/home/roroco/Dropbox/cpp/cpp_lib/draft/go_call_cpp:$LD_LIBRARY_PATH ~/Dropbox/cpp/cpp_lib/draft/go_call_cpp $ go run main.go Progress: 0% Progress: 10%
但直接运行会报错:
~/Dropbox/cpp/cpp_lib/draft/go_call_cpp $ go run main.go /tmp/go-build2581226776/b001/exe/main: error while loading shared libraries: libprogress.so: cannot open shared object file: No such file or directory exit status 127
希望不在命令行设置LD_LIBRARY_PATH,而是在Go代码中完成该操作。
解决方案
方法1:编译时嵌入库搜索路径(推荐)
直接修改#cgo LDFLAGS,添加-Wl,-rpath参数,把共享库的路径嵌入到编译后的二进制程序中。这样程序运行时会自动去指定路径找库,不需要依赖环境变量。
修改后的cgo部分:
/* #cgo LDFLAGS: -L/home/roroco/Dropbox/cpp/cpp_lib/draft/go_call_cpp -lprogress -Wl,-rpath=/home/roroco/Dropbox/cpp/cpp_lib/draft/go_call_cpp #include "progress_lib.h" // Callback function to be called from C++ extern void progressCallback(int progress); */ import "C"
修改后直接运行go run main.go就能正常工作,编译后的二进制文件也可以直接执行,无需额外设置环境变量。
方法2:运行时通过子进程重启程序
如果必须在代码中动态设置LD_LIBRARY_PATH,可以让程序先检查环境变量,若未包含目标路径则设置变量并重启自身作为子进程。因为主进程启动时动态链接库已经开始加载,所以只能通过子进程来生效。
修改后的完整代码:
package main /* #cgo LDFLAGS: -L/home/roroco/Dropbox/cpp/cpp_lib/draft/go_call_cpp -lprogress #include "progress_lib.h" // Callback function to be called from C++ extern void progressCallback(int progress); */ import "C" import ( "fmt" "os" "os/exec" "strings" "sync" ) var wg sync.WaitGroup //export progressCallback func progressCallback(progress C.int) { fmt.Printf("Progress: %d%%\n", progress) if progress == 100 { wg.Done() } } func main() { libPath := "/home/roroco/Dropbox/cpp/cpp_lib/draft/go_call_cpp" currentLD := os.Getenv("LD_LIBRARY_PATH") // 检查目标路径是否已在LD_LIBRARY_PATH中 if !strings.Contains(currentLD, libPath) { // 构造新的LD_LIBRARY_PATH newLD := libPath if currentLD != "" { newLD += ":" + currentLD } // 启动子进程,传递新的环境变量 cmd := exec.Command(os.Args[0], os.Args[1:]...) cmd.Env = append(os.Environ(), fmt.Sprintf("LD_LIBRARY_PATH=%s", newLD)) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { fmt.Printf("重启程序失败: %v\n", err) os.Exit(1) } os.Exit(0) } // 原业务逻辑 wg.Add(1) go func() { C.do_work(C.progress_callback(C.progressCallback)) }() wg.Wait() }
方法对比
- 方法1:操作简单,编译时就把路径固化到程序中,运行时无额外开销,适合路径固定的场景。
- 方法2:支持动态调整库路径,但会多一次进程启动,适合需要根据运行环境切换库位置的场景。
内容的提问来源于stack exchange,提问作者chikadance
相关产品推荐
相关产品推荐

