Linux 공유 라이브러리 최소 실행 가능 API 대 ABI 예제
이 답변은 다른 답변에서 발췌되었습니다 . 응용 프로그램 이진 인터페이스 (ABI) 란 무엇입니까? 그러나 나는 이것이 이것에도 직접 대답하고 질문은 중복되지 않는다고 생각했습니다.
공유 라이브러리와 관련하여 "안정적인 ABI를 갖는"의 가장 중요한 의미는 라이브러리가 변경된 후 프로그램을 다시 컴파일 할 필요가 없다는 것입니다.
아래 예에서 볼 수 있듯이 API가 변경되지 않아도 ABI를 수정하여 프로그램을 중단 할 수 있습니다.
main.c
#include <assert.h>
#include <stdlib.h>
#include "mylib.h"
int main(void) {
mylib_mystrict *myobject = mylib_init(1);
assert(myobject->old_field == 1);
free(myobject);
return EXIT_SUCCESS;
}
mylib.c
#include <stdlib.h>
#include "mylib.h"
mylib_mystruct* mylib_init(int old_field) {
mylib_mystruct *myobject;
myobject = malloc(sizeof(mylib_mystruct));
myobject->old_field = old_field;
return myobject;
}
mylib.h
#ifndef MYLIB_H
#define MYLIB_H
typedef struct {
int old_field;
} mylib_mystruct;
mylib_mystruct* mylib_init(int old_field);
#endif
다음을 사용하여 컴파일 및 실행
cc='gcc -pedantic-errors -std=c89 -Wall -Wextra'
$cc -fPIC -c -o mylib.o mylib.c
$cc -L . -shared -o libmylib.so mylib.o
$cc -L . -o main.out main.c -lmylib
LD_LIBRARY_PATH=. ./main.out
이제 라이브러리 v2에 대해 mylib_mystrict
이라는 새로운 필드를 추가하려고한다고 가정합니다 new_field
.
다음 old_field
과 같이 필드를 추가 한 경우 :
typedef struct {
int new_field;
int old_field;
} mylib_mystruct;
라이브러리를 다시 빌드했지만하지 않으면 main.out
어설 션이 실패합니다!
그 이유는 다음과 같습니다.
myobject->old_field == 1
는 int
구조체의 첫 번째 에 액세스하려고하는 어셈블리를 생성했으며 현재 new_field
는 예상이 아닙니다 old_field
.
따라서이 변경으로 인해 ABI가 중단되었습니다.
경우, 그러나, 우리는 추가 new_field
한 후 old_field
:
typedef struct {
int old_field;
int new_field;
} mylib_mystruct;
그런 다음 이전에 생성 된 어셈블리는 여전히 첫 번째 int
구조체에 액세스 하고 프로그램은 여전히 작동합니다 .ABI를 안정적으로 유지했기 때문입니다.
다음은 GitHub에 대한이 예제 의 완전 자동화 된 버전입니다 .
이 ABI를 안정적으로 유지하는 또 다른 방법 mylib_mystruct
은 불투명 구조체 로 취급 하고 메서드 도우미를 통해서만 해당 필드에 액세스하는 것입니다. 따라서 ABI를 안정적으로 유지할 수 있지만 더 많은 함수 호출을 수행하면 성능 오버 헤드가 발생합니다.
API 대 ABI
이전 예제에서 new_field
before 를 추가 old_field
하면 API가 아닌 ABI 만 중단 했다는 점에 주목하는 것이 흥미 롭습니다 .
이것이 의미하는 바는 main.c
프로그램을 라이브러리에 대해 다시 컴파일 했다면 상관없이 작동했을 것입니다.
그러나 예를 들어 함수 서명을 변경 한 경우 API를 중단했을 수도 있습니다.
mylib_mystruct* mylib_init(int old_field, int new_field);
이 경우 main.c
컴파일을 완전히 중지합니다.
시맨틱 API vs 프로그래밍 API vs ABI
또한 시맨틱 변경이라는 세 번째 유형으로 API 변경을 분류 할 수 있습니다.
예를 들어 수정 한 경우
myobject->old_field = old_field;
에:
myobject->old_field = old_field + 1;
그러면 API 나 ABI가 모두 깨지지 않았지만 main.c
여전히 깨 졌습니다 !
이는 프로그램 적으로 눈에 띄는 측면이 아니라 함수가 수행해야하는 기능에 대한 "인간 설명"을 변경했기 때문입니다.
방금 소프트웨어의 공식적인 검증이 "의미 론적 API"를보다 "프로그램 적으로 검증 가능한 API"로 옮긴다 는 철학적 통찰력을 얻었습니다 .
시맨틱 API vs 프로그래밍 API
또한 시맨틱 변경이라는 세 번째 유형으로 API 변경을 분류 할 수 있습니다.
시맨틱 API는 일반적으로 API가 수행해야하는 것에 대한 자연어 설명이며 일반적으로 API 문서에 포함됩니다.
따라서 프로그램 빌드 자체를 중단하지 않고 시맨틱 API를 중단 할 수 있습니다.
예를 들어 수정 한 경우
myobject->old_field = old_field;
에:
myobject->old_field = old_field + 1;
그러면 프로그래밍 API 나 ABI가 모두 깨지지 않았지만 main.c
의미 API가 깨졌습니다.
프로그래밍 방식으로 계약 API를 확인하는 두 가지 방법이 있습니다.
- 코너 케이스를 테스트하십시오. 쉬운 일이지만 항상 놓칠 수 있습니다.
- 공식적인 검증 . 수행하기는 더 어렵지만 수학적 정확성을 입증하여 문서와 테스트를 "인간"/ 기계 검증 가능한 방식으로 통합합니다! 공식적인 설명에 버그가없는 한 ;-)
우분투 18.10, GCC 8.2.0에서 테스트되었습니다.