如何处理Linux和Windows之间的共享库文件名差异

问题描述 投票:0回答:1

这个问题与这个问题直接相关;有人建议我不要将许多问题混为一谈,因此我将围绕该主题发布单独的问题。

我正在开发一个项目,其源代码是为 Linux 和 Windows 构建的。该项目最初是使用手写的 Makefile 构建的,因此共享库在 Linux 和 Windows 上都使用

.so
扩展构建。将项目迁移到基于
CMake
的构建后,在 Linux 上使用
.so
扩展构建共享库,在 Windows 上使用
.dll
扩展构建共享库。因此,应用程序在运行时会失败,因为它已被硬编码为带有
dlopen()
扩展的延迟加载 (
.so
) 共享库。

我相信动态链接库和共享库之间的差异与当前需求无关,因为如果我将生成的

.dll
文件重命名为
.so
,应用程序将成功运行完成。

我的问题是:是否有标准或最佳实践方法来处理Linux和Windows之间的共享库文件扩展名差异?我可以想象一个基于宏的解决方案,例如:

#ifdef SOMETHING
#define EXTENSION ".so"
#else
#define EXTENSION ".dll"
#endif
...
  void* handle = dlopen("libfunc" EXTENSION, RTLD_NOW)

...但我想了解是否有广泛采用的最佳实践或普遍的惯例,或者完全正确的方法来处理这个问题。本质上,需求是“在 Windows 中,加载带有

.dll
扩展名的文件;在 Linux 中,加载带有
.so
扩展名的文件。”

(我假设为所有平台创建

Makefile
文件的手写
.so
是出于缺乏经验或方便等原因而这样做的,并且
CMake
通过在 Linux 中生成
.so
文件来执行正确或首选的操作,并且Windows 中的
.dll
文件。即,我假设开发人员有责任适应他在代码中使用
dlopen()
加载的共享库扩展的编译时或运行时条件)


以下是最小的可编译示例的资源:

// main.c
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char* argv[]) {
  int (*fptr)(void) = NULL;
  const char* filename = "libfunc.so"; // Nota bene
  void* handle = dlopen(filename, RTLD_NOW);

  if (! handle) {
    fprintf(stderr, "%s\n", dlerror());
    exit(EXIT_FAILURE);
  }

  fptr = dlsym(handle, "func");
  fprintf(stdout, "fptr returns %d\n", (*fptr)());
  dlclose(handle);

  return 0;
}
// dlfcn.h
#ifndef DLFCN_H
#define DLFCN_H

#include <windows.h>

#define RTLD_NOW (1<<0)

extern inline void *dlopen(const char *lib, int flags) {
  (void) flags;

  return LoadLibraryExA(lib, NULL, 0);
}

extern inline void *dlsym(void *handle, const char *name) {
  FARPROC fp = GetProcAddress((HINSTANCE) handle, name);

  return (void *)(intptr_t)fp;
}

extern inline char *dlerror() {
  // Not thread-safe!
  static char msg[1024];
  msg[0] = '\0';

  FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                 NULL, GetLastError(), 0, msg, sizeof(msg), NULL);

  return msg;
}

extern inline int dlclose(void *handle) {
  return FreeLibrary(handle) ? 0 : -1;
}
// func.c
int func(void) {
  return 42;
}
# Makefile
.PHONY: all

CFLAGS :=

ifeq ($(shell uname), Windows_NT)
  CFLAGS += -isystem .
endif

all: libfunc.so a.out

libfunc.so: func.o
        cc -shared -fpic -o $@ $^

a.out: libfunc.so main.c
        cc -o $@ main.c ${CFLAGS}
# CMakeLists.txt
cmake_minimum_required(VERSION 3.5)
project(hello_world)

add_library(func SHARED func.c)

add_executable(a.out main.c)

if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
  include_directories(SYSTEM ${CMAKE_CURRENT_SOURCE_DIR})
endif()

使用

make
编译并在Linux上运行:

$ make
cc    -c -o func.o func.c
cc -shared -fpic -o libfunc.so func.o
cc -o a.out main.c
$ LD_LIBRARY_PATH=$LD_LIBRARY_PATH:. ./a.out
fptr returns 42
$

使用

cmake
编译并在Linux上运行:

$ cmake -B build && cmake --build build
-- The C compiler identification is GNU 11.4.0
-- The CXX compiler identification is GNU 11.4.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/user/dev/shared-lib-test/build
[ 25%] Building C object CMakeFiles/func.dir/func.c.o
[ 50%] Linking C shared library libfunc.so
[ 50%] Built target func
[ 75%] Building C object CMakeFiles/a.out.dir/main.c.o
[100%] Linking C executable a.out
[100%] Built target a.out
$

$ LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/build ./build/a.out
fptr returns 42
$

使用

make
编译并在w64devkit中运行:

$ make
cc -isystem .   -c -o func.o func.c
cc -shared -fpic -o libfunc.so func.o
cc -o a.out main.c -isystem .
$
$ ./a.out
fptr returns 42
$

使用

cmake
编译并在w64devkit中运行:

$ cmake -G "Unix Makefiles" -B build && cmake --build build
-- The C compiler identification is GNU 13.2.0
-- The CXX compiler identification is GNU 13.2.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: C:/Program Files/w64devkit/bin/cc.exe - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: C:/Program Files/w64devkit/bin/c++.exe - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (3.1s)
-- Generating done (0.1s)
-- Build files have been written to: C:/Users/user/dev/shared-lib-test/build
[ 25%] Building C object CMakeFiles/func.dir/func.c.obj
[ 50%] Linking C shared library libfunc.dll
[ 50%] Built target func
[ 75%] Building C object CMakeFiles/a.out.dir/main.c.obj
[100%] Linking C executable a.out.exe
[100%] Built target a.out
$
$ ./build/a.out.exe
The specified module could not be found.

$ mv ./build/libfunc.dll ./build/libfunc.so
$
$ ./build/a.out.exe
fptr returns 42
$
c cmake dll shared-libraries
1个回答
0
投票

是否有标准或最佳实践方法来处理 Linux 和 Windows 之间的共享库文件扩展名差异?

您加载库的代码已经非常不同 - Windows 版本使用

LoadLibraryExA
,而 UNIX 版本使用
dlopen

处理平台差异的标准/最佳实践方法是不是在Windows上模拟

dlopen
(就像您的代码那样),而是提供特定于操作系统的
load_library
函数(在不同的源文件中)。

也就是说,您的大多数应用程序并不关心差异,而只是调用

load_library("func");

unix.c
load_library()
实现为(为了清楚起见,省略了溢出检查):

void *load_library(const char *name)
{
  char buf[1024] = "lib";
  strcpy(buf + 3, name);
  strcat(buf, ".so");
  return dlopen(buf, ...);  // dlopen("lib{name}.so")
}

windows.c
将其实现为:

void *load_library(const char *name)
{
  char buf[1024];
  strcpy(buf, name);
  strcat(buf, ".dll");
  return LoadLibraryExA(buf, NULL, 0);  // LoadLibraryExA("{name}.dll")
}

然后您在

Makefile
中有一个选择器,它根据需要使用
unix.c
windows.c

© www.soinside.com 2019 - 2024. All rights reserved.