바이너리 호환 라이브러리 작성

8657 단어 바이너리
개발팀의 공공 라이브러리 과정에서 2진법 호환 문제에 부딪혔다.다음은 바이너리 호환 및 코드 호환에 대한 구체적인 정의입니다.
A library is binary compatible, if a program linked dynamically to a former version of the library continues running with newer versions of the library without the need to recompile.
If a program needs to be recompiled to run with a new version of library but doesn't require any further modifications, the library is source compatible.
간단한 예를 들어 랜덤 생성 라이브러리에 두 개의 인터페이스를 제공합니다: 피드 설정 setseed 및 생성 랜덤 수 myrand.
  liba.h 
#ifndef _LIBA_H #define _LIBA_H struct A { int seed; }; int set_seed(struct A* a, int s); int my_rand(struct A a, int b); #endif

 liba.c
#include "liba.h" int set_seed(struct A* a, int s) { a->seed = s; return 0; } int my_rand(struct A a, int b) { int r = a.seed + b; return r; }

동적 라이브러리libcutil로 컴파일합니다.so, 호출자 코드도 간단합니다.
main.c
#include "liba.h" #include <stdio.h> #include <dlfcn.h> int main() { void* handle=dlopen("libcutil.so", RTLD_LAZY); if(!handle) { fprintf(stderr, "%s
", dlerror()); return -1; } struct A a; int (*fa)(struct A*, int); fa = dlsym(handle, "set_seed"); fa(&a, 31); int (*fb)(struct A, int); fb = dlsym(handle, "my_rand"); int b = fb(a, 4); printf("%d
", b); return 0; }

이렇게 하면 정상적으로 일할 수 있다.
export LD_LIBRARY_PATH="../api_r1/:$LD_LIBRARY_PATH"; ./main 35
릴리즈 2 버전h의 struct A에는 한 명의 멤버가 추가되었습니다.
#ifndef _LIBA_H #define _LIBA_H struct A { int add; //        int seed; }; int set_seed(struct A* a, int s); int my_rand(struct A a, int b); #endif 

 liba.c 거의 변하지 않는다.
#include "liba.h" int set_seed(struct A* a, int s) { a->add = 123; //     a->seed = s; return 0; } int my_rand(struct A a, int b) { int r = a.seed + b; return r; } 

컴파일 후 새 동적 라이브러리libcutil을 생성합니다.so, 사용자가 코드를 다시 컴파일하지 않고 실행:
export LD_LIBRARY_PATH="../api_r2/:$LD_LIBRARY_PATH"; ./main 4
결과 오류, 이 라이브러리libcutil.so 바이너리가 호환되지 않습니다.
이유: 라이브러리 헤더 파일liba.h에는 인터페이스뿐만 아니라 실현과 관련된 코드(struct A)도 포함되어 있다.이 라이브러리의 코드(main.c)include를 사용하면 이 헤더 파일은 struct A의 정의를 포함하고libcutil을 포함합니다.so는 struct A의 정의에 전적으로 의존합니다.고객 코드를 컴파일하지 않고 라이브러리만 교체할 때 라이브러리에 의존하는 struct A (최신 버전) 는 라이브러리를 사용하는 코드에서 struct A (오래된 버전) 와 일치하지 않습니다.
솔루션:
원리--헤드 파일에 인터페이스만 노출하고 관련 코드를 모두 넣는다.c/.cpp에서 인터페이스와 분리를 실현합니다.
실현 - 하나의 지침을 통해 인디케이션 결합을 증가시킨다.
두 가지 시나리오가 있습니다.
1. C 인터페이스는 void* 포인터를 사용하여 구체적인 struct를 가리킵니다.
#ifndef _LIBA_H #define _LIBA_H typedef void* Api; int init(Api *api, int s); //    int run(Api api, int b); int release(Api api); //     #endif

 .c 파일에는 구현과 관련된 모든 코드가 포함되어 있습니다.
#include "liba.h" #include <stdio.h> #include <stdlib.h> struct A { int seed; }; typedef struct A* ApiII; int init(Api* api, int s) { ApiII p = (ApiII)malloc(sizeof(struct A)); if(api == NULL || p == NULL) return -1; p->seed = s; *((ApiII*)api) = p; return 0; } int run(Api api, int b) { if(api == NULL) return -1; ((ApiII)api)->seed+=b; return ((ApiII)api)->seed; } int release(Api api) { if(api != NULL) { free(api); api = NULL; } return 0; } 

 2.C++ 인터페이스에서 pimpl 사용
#ifndef _LIBA_H #define _LIBA_H class A { public: A(); ~A(); int init(int s); int run(int b); private: class Aimpl; //    ,  A  Aimpl* pimpl; //      ,      }; #endif

 .cpp 코드:
#include "liba.h" #include <stdio.h> class A::Aimpl { public: Aimpl(int s):seed(s){} int run(int b) { seed+=b; return seed; } private: int seed; }; A::A():pimpl(NULL) { } A::~A() { if(pimpl != NULL) { delete pimpl; pimpl=NULL; } } int A::init(int s) { if(pimpl != NULL) delete pimpl; pimpl = new A::Aimpl(s); return 0; } int A::run(int b) { if(pimpl == NULL) return -1; return pimpl->run(b); } 

 
첨부 파일에는 관련 코드가 포함되어 있습니다.
참조 자료:
https://techbase.kde.org/Policies/Binary_Compatibility_Issues_With_C++#Definition

좋은 웹페이지 즐겨찾기