[api] 응용 프로그램 이진 인터페이스 (ABI) 란 무엇입니까?

나는 ABI가 무엇인지 명확하게 이해하지 못했습니다. 저에게 Wikipedia 기사를 알려주지 마십시오. 내가 이해할 수 있다면, 나는 그렇게 긴 게시물을 게시하지 않을 것입니다.

이것은 다른 인터페이스에 대한 나의 사고 방식입니다.

TV 리모컨은 사용자와 TV 간의 인터페이스입니다. 기존 엔터티이지만 그 자체로는 쓸모가 없습니다 (기능을 제공하지 않음). 리모컨의 각 버튼에 대한 모든 기능은 텔레비전 세트에서 구현됩니다.

인터페이스 : 해당 기능 functionalityconsumer기능 사이의 “기존 엔티티”계층
입니다. 인터페이스 자체는 아무것도하지 않습니다. 그것은 뒤에있는 기능을 호출합니다.

이제 사용자가 누구인지에 따라 다른 유형의 인터페이스가 있습니다.

CLI (명령 줄 인터페이스) 명령은 기존 엔터티이며 소비자는 사용자이며 기능은 뒤에 있습니다.

functionality: 이 인터페이스를 설명하는 목적을 해결하는 소프트웨어 기능.

existing entities: 명령

consumer: 사용자

GUI (Graphical User Interface) 창, 버튼 등은 기존 엔터티이며 다시 소비자는 사용자이며 기능은 뒤에 있습니다.

functionality: 이 인터페이스를 설명하는 몇 가지 문제를 해결하는 소프트웨어 기능.

existing entities: 창, 버튼 등

consumer: 사용자

API (Application Programming Interface) 기능 (또는보다 정확한 인터페이스) (인터페이스 기반 프로그래밍) 인터페이스는 기존 엔터티이며 여기서 소비자는 사용자가 아닌 다른 프로그램이며 기능은이 계층 뒤에 있습니다.

functionality: 이 인터페이스를 설명하는 몇 가지 문제를 해결하는 소프트웨어 기능.

existing entities: 기능, 인터페이스 (기능 배열).

consumer: 다른 프로그램 / 응용 프로그램.

응용 프로그램 이진 인터페이스 (ABI) 여기에서 문제가 시작됩니다.

functionality: ???

existing entities: ???

consumer: ???

  • 다른 언어로 소프트웨어를 작성했으며 다른 종류의 인터페이스 (CLI, GUI 및 API)를 제공했지만 ABI를 제공했는지 확실하지 않습니다.

위키피디아의 말 :

ABI는 다음과 같은 세부 사항을 다룹니다.

  • 데이터 유형, 크기 및 정렬;
  • 함수의 인수가 전달되고 반환 값을 검색하는 방법을 제어하는 ​​호출 규칙;
  • 시스템 호출 번호 및 응용 프로그램이 운영 체제를 시스템 호출하는 방법

다른 ABI는 다음과 같은 세부 정보를 표준화합니다

  • C ++ 이름 맹 글링
  • 예외 전파
  • 동일한 플랫폼에서 컴파일러 간 호출 규칙이지만 플랫폼 간 호환성이 필요하지 않습니다.
  • 누가 이러한 세부 정보가 필요합니까? OS를 말하지 마십시오. 어셈블리 프로그래밍을 알고 있습니다. 링크 및로드 작동 방식을 알고 있습니다. 나는 무슨 일이 일어나는지 정확히 알고 있습니다.

  • C ++ 이름 맹 글링이 왜 들어 왔습니까? 나는 우리가 이진 레벨에서 이야기하고 있다고 생각했다. 언어는 왜 들어 옵니까?

어쨌든 [PDF] System V Application Binary Interface Edition 4.1 (1997-03-18) 을 다운로드하여 정확히 무엇이 포함되어 있는지 확인했습니다. 글쎄, 대부분은 말이되지 않았다.

  • ELF 파일 형식 을 설명하기 위해 왜 두 개의 장 (4 및 5)이 포함되어 있습니까? 실제로, 이들은이 사양에서 유일하게 중요한 두 장입니다. 나머지 장은 “프로세서 별”입니다. 어쨌든, 나는 그것이 완전히 다른 주제라고 생각합니다. ELF 파일 형식 사양 ABI 라고 말하지 마십시오 . 정의에 따라 인터페이스 가 될 수 없습니다 .

  • 나는 우리가 그렇게 낮은 수준에서 이야기하기 때문에 매우 구체적이어야한다는 것을 알고 있습니다. 그러나 “ISA (Instruction Set Architecture)”가 어떻게 특정되는지 잘 모르겠습니다.

  • Microsoft Windows ABI는 어디에서 찾을 수 있습니까?

이것이 저를 괴롭히는 주요 질문입니다.



답변

“ABI”를 이해하는 한 가지 쉬운 방법은 “ABI”를 “API”와 비교하는 것입니다.

이미 API 개념에 익숙합니다. 예를 들어 일부 라이브러리 또는 OS의 기능을 사용하려면 API에 대해 프로그래밍합니다. API는 외부 유형의 기능에 액세스하기 위해 코드에서 사용할 수있는 데이터 유형 / 구조, 상수, 함수 등으로 구성됩니다.

ABI는 매우 유사합니다. API의 컴파일 된 버전 (또는 기계 언어 레벨의 API)으로 생각하십시오. 소스 코드를 작성할 때 API를 통해 라이브러리에 액세스합니다. 코드가 컴파일되면 응용 프로그램은 ABI를 통해 라이브러리의 이진 데이터에 액세스합니다. ABI는 컴파일 된 응용 프로그램이 API와 마찬가지로 외부 라이브러리에 액세스하는 데 사용하는 구조와 메서드를 낮은 수준에서만 정의합니다. API는 함수에 인수를 전달하는 순서를 정의합니다. ABI는 방법 의 역학을 정의 합니다이러한 인수는 전달됩니다 (레지스터, 스택 등). API는 라이브러리의 일부 기능을 정의합니다. ABI는 라이브러리를 사용하는 모든 프로그램이 원하는 기능을 찾아서 실행할 수 있도록 라이브러리 파일 내에 코드가 저장되는 방식을 정의합니다.

외부 라이브러리를 사용하는 응용 프로그램의 경우 ABI가 중요합니다. 라이브러리는 코드 및 기타 리소스로 가득 차 있지만 프로그램은 라이브러리 파일 내에서 필요한 것을 찾는 방법을 알아야합니다. ABI는 라이브러리 내용이 파일 내에 저장되는 방법을 정의하고 프로그램은 ABI를 사용하여 파일을 검색하고 필요한 것을 찾습니다. 시스템의 모든 항목이 동일한 ABI를 준수하는 경우, 모든 프로그램은 누가 만든지에 관계없이 모든 라이브러리 파일을 사용할 수 있습니다. Linux와 Windows는 다른 ABI를 사용하므로 Windows 프로그램은 Linux 용으로 컴파일 된 라이브러리에 액세스하는 방법을 모릅니다.

때로는 ABI 변경이 불가피합니다. 이 경우 해당 라이브러리를 사용하는 모든 프로그램은 새 버전의 라이브러리를 사용하도록 다시 컴파일되지 않으면 작동하지 않습니다. ABI가 변경되었지만 API가 변경되지 않으면 이전 및 새 라이브러리 버전을 “소스 호환”이라고도합니다. 즉, 한 라이브러리 버전 용으로 컴파일 된 프로그램은 다른 라이브러리 버전과 작동하지 않지만, 하나를 위해 작성된 소스 코드는 다시 컴파일 된 경우 다른 소스 용으로 작동합니다.

이러한 이유로 개발자는 중단을 최소화하기 위해 ABI를 안정적으로 유지하려고합니다. ABI를 안정적으로 유지한다는 것은 함수 인터페이스 (반환 유형 및 수, 유형 및 인수 순서), 데이터 유형 또는 데이터 구조의 정의, 정의 된 상수 등을 변경하지 않음을 의미합니다. 새로운 함수 및 데이터 유형을 추가 할 수 있지만 기존 유형은 그대로 있어야합니다. 똑같다. 예를 들어 라이브러리가 함수의 오프셋을 나타 내기 위해 32 비트 정수를 사용하고 64 비트 정수로 전환하면 해당 라이브러리를 사용하는 이미 컴파일 된 코드가 해당 필드 (또는 그 뒤를 따르는)에 올바르게 액세스하지 못합니다 . 데이터 구조 멤버에 액세스하면 컴파일 중에 메모리 주소 및 오프셋으로 변환되고 데이터 구조가 변경되면

매우 낮은 수준의 시스템 설계 작업을 수행하지 않는 한 ABI가 반드시 명시 적으로 제공하는 것은 아닙니다. C 애플리케이션과 Pascal 애플리케이션은 컴파일 된 후에 동일한 ABI를 사용할 수 있으므로 언어별로 다릅니다.

편집하다:SysV ABI 문서의 ELF 파일 형식 관련 장에 대한 질문과 관련하여 :이 정보가 포함 된 이유는 ELF 형식이 운영 체제와 응용 프로그램 간의 인터페이스를 정의하기 때문입니다. OS에게 프로그램을 실행하도록 지시하면 프로그램이 특정 방식으로 포맷 될 것으로 예상하고 (예를 들어) 바이너리의 첫 번째 섹션이 특정 메모리 오프셋에서 특정 정보를 포함하는 ELF 헤더가 될 것으로 예상합니다. 응용 프로그램이 자신에 대한 중요한 정보를 운영 체제와 통신하는 방법입니다. ELF 이외의 이진 형식 (예 : a.out 또는 PE)으로 프로그램을 빌드하면 ELF 형식의 응용 프로그램이 필요한 OS에서 이진 파일을 해석하거나 응용 프로그램을 실행할 수 없습니다.

IIRC, Windows는 현재 Portable Executable (또는 PE) 형식을 사용합니다. 해당 위키 백과 페이지의 “외부 링크”섹션에 PE 형식에 대한 자세한 정보가있는 링크가 있습니다.

또한 C ++ 이름 맹 글링에 대한 참고 사항 : 라이브러리 파일에서 함수를 찾을 때 함수는 일반적으로 이름별로 검색됩니다. C ++에서는 함수 이름을 오버로드 할 수 있으므로 이름만으로는 함수를 식별하기에 충분하지 않습니다. C ++ 컴파일러에는 내부적으로 name mangling 이라고하는 고유 한 방식이 있습니다. ABI는 다른 언어 나 컴파일러로 빌드 된 프로그램이 필요한 것을 찾을 수 있도록 함수 이름을 인코딩하는 표준 방법을 정의 할 수 있습니다. extern "c"C ++ 프로그램에서 사용할 때는 다른 소프트웨어에서 이해할 수있는 표준화 된 이름의 녹음 방법을 사용하도록 컴파일러에 지시합니다.


답변

OS 수준에서 어셈블리 및 작동 방식을 알고 있다면 특정 ABI를 준수하는 것입니다. ABI는 매개 변수가 전달되는 방식, 반환 값이 배치되는 방식 등을 관리합니다. 많은 플랫폼에서 선택할 수있는 ABI는 하나 뿐이며이 경우 ABI는 “일이 어떻게 작동 하는가”입니다.

그러나 ABI는 클래스 / 객체가 C ++로 배치되는 방식과 같은 사항도 관리합니다. 모듈 경계에 걸쳐 객체 참조를 전달하거나 다른 컴파일러로 컴파일 된 코드를 혼합하려는 경우에 필요합니다.

또한 32 비트 바이너리를 실행할 수있는 64 비트 OS가있는 경우 32 비트 및 64 비트 코드에 대해 서로 다른 ABI가 있습니다.

일반적으로 동일한 실행 파일에 연결하는 모든 코드는 동일한 ABI를 준수해야합니다. 다른 ABI를 사용하여 코드간에 통신하려면 일부 형식의 RPC 또는 직렬화 프로토콜을 사용해야합니다.

다른 유형의 인터페이스를 고정 된 특성 세트로 짜내기 위해 너무 열심히 노력하고 있다고 생각합니다. 예를 들어 인터페이스를 반드시 소비자와 생산자로 분리 할 필요는 없습니다. 인터페이스는 두 엔티티가 상호 작용하는 규칙입니다.

ABI는 (부분적으로) ISA에 구애받지 않을 수 있습니다. 호출 규칙과 같은 일부 측면은 ISA에 의존하지만 다른 측면 (예 : C ++ 클래스 레이아웃)은 그렇지 않습니다.

잘 정의 된 ABI는 컴파일러를 작성하는 사람들에게 매우 중요합니다. 잘 정의 된 ABI가 없으면 상호 운용 가능한 코드를 생성 할 수 없습니다.

편집 : 명확히해야 할 몇 가지 참고 사항 :

  • ABI의 “이진”은 문자열이나 텍스트 사용을 배제하지 않습니다. C ++ 클래스를 내보내는 DLL을 링크하려면 메소드 및 유형 서명을 인코딩해야합니다. 그것이 C ++ 이름 관리가 나오는 곳입니다.
  • ABI를 제공하지 않은 이유는 대다수의 프로그래머가 절대로 그렇게하지 않기 때문입니다. ABI는 플랫폼 (예 : 운영 체제)을 디자인하는 동일한 사람들이 제공하며, 거의 사용되지 않는 ABI를 설계 할 수있는 프로그래머는 거의 없습니다.

답변

실제로 ABI가 필요 하지 않습니다.

  • 프로그램에 기능이 없으며-
  • 귀하의 프로그램은 말 그대로 실행되는 유일한 프로그램이며 다른 것과 대화 할 필요가없는 단독 실행 파일 (예 : 임베디드 시스템)입니다.

지나치게 단순화 된 요약 :

API : “여기에 호출 할 수있는 모든 기능이 있습니다.”

ABI : “이것은 함수를 호출하는 방법 입니다.”

ABI는 제대로 작동하도록 프로그램을 컴파일하기 위해 컴파일러와 링커가 준수하는 규칙 세트입니다. ABI는 여러 주제를 다룹니다.

  • 아마도 ABI의 가장 크고 중요한 부분은 “호출 규칙”이라고도 하는 프로 시저 호출 표준 입니다. 호출 규칙은 “함수”가 어셈블리 코드로 변환되는 방식을 표준화합니다.
  • ABI는 또한 이름 이 어떻게 다른 코드가 해당 라이브러리를 호출하고 전달할 인수를 알 수 있도록 라이브러리에서 노출 된 함수 을 표시 . 이것을 “이름 맹 글링”이라고합니다.
  • ABI는 또한 어떤 유형의 데이터 유형을 사용할 수 있는지, 어떻게 정렬해야하는지 및 기타 하위 수준의 세부 사항을 지시합니다.

ABI의 핵심이라고 생각하는 전화 회의에 대해 자세히 살펴보십시오.

기계 자체에는 “기능”이라는 개념이 없습니다. c와 같은 고급 언어로 함수를 작성할 때 컴파일러는와 같은 어셈블리 코드 행을 생성합니다 _MyFunction1:. 이 레이블 은 결국 어셈블러에서 주소로 확인됩니다. 이 레이블은 어셈블리 코드에서 “기능”의 “시작”을 표시합니다. 고급 코드에서 해당 함수를 “호출”할 때 실제로 수행하는 작업은 CPU가 점프하는 것입니다. 해당 레이블의 주소에 계속 거기에 실행.

점프 준비를 위해 컴파일러는 중요한 것들을 많이해야합니다. 호출 규칙은 컴파일러가이 모든 작업을 수행하기 위해 따르는 점검 목록과 같습니다.

  • 먼저 컴파일러는 현재 주소를 저장하기 위해 약간의 어셈블리 코드를 삽입하므로 “기능”이 완료되면 CPU가 올바른 위치로 되돌아 가서 계속 실행할 수 있습니다.
  • 다음으로 컴파일러는 인수를 전달하는 어셈블리 코드를 생성합니다.
    • 일부 호출 규칙에 따르면 인수는 스택에 있어야합니다 ( 물론 특정 순서 로).
    • 다른 규칙에 따르면 인수는 특정 레지스터에 넣어야합니다 ( 물론 데이터 유형따라 다름 ).
    • 또 다른 규칙에 따르면 특정 스택 및 레지스터 조합을 사용해야합니다.
  • 물론, 이전에 이러한 레지스터에 중요한 것이 있었으면 이제 해당 값을 덮어 쓰고 영원히 잃어 버렸으므로 일부 호출 규칙에 따라 인수를 저장하기 전에 컴파일러가 해당 레지스터 중 일부를 저장하도록 지시 할 수 있습니다.
  • 이제 컴파일러는 CPU에게 이전에 만든 레이블 ( _MyFunction1:) 로 이동하도록 지시하는 점프 명령을 삽입합니다 . 이 시점에서 CPU가 “기능”에 있다고 간주 할 수 있습니다.
  • 함수의 끝에서 컴파일러는 CPU가 올바른 위치에 반환 값을 쓰도록하는 어셈블리 코드를 넣습니다. 호출 규칙은 반환 값을 특정 레지스터 (유형에 따라 다름)에 넣을 것인지 아니면 스택에 넣을 것인지를 지시합니다.
  • 이제 정리할 차례입니다. 호출 규칙은 컴파일러가 정리 어셈블리 코드를 배치 할 위치를 나타냅니다.
    • 일부 규칙에 따르면 호출자는 스택을 정리해야합니다. 이는 “기능”이 완료되고 CPU가 이전 위치로 되돌아 간 후 실행되는 다음 코드는 매우 구체적인 정리 코드 여야 함을 의미합니다.
    • 다른 규칙에 따르면 클린업 코드의 일부 특정 부분은 건너 뛰기 전에 “기능”의 끝에 있어야합니다 .

많은 ABI / 통화 규칙이 있습니다. 몇 가지 주요 사항은 다음과 같습니다.

  • x86 또는 x86-64 CPU (32 비트 환경)의 경우 :
    • CDECL
    • STDCALL
    • 패스트 콜
    • 벡터 통화
    • 이 통화
  • x86-64 CPU (64 비트 환경)의 경우 :
    • SYSTEMV
    • MSNATIVE
    • 벡터 통화
  • ARM CPU (32 비트)
    • AAPCS
  • ARM CPU (64 비트)
    • AAPCS64

다음 은 다른 ABI를 위해 컴파일 할 때 생성되는 어셈블리의 차이점을 실제로 보여주는 훌륭한 페이지입니다.

언급해야 할 또 다른 사항은 ABI가 프로그램의 실행 모듈 에서만 관련이 없다는 것 입니다. 것 또한 프로그램이 라이브러리 기능을 제대로 호출을 확인하기 위해 링커가 사용. 컴퓨터에서 여러 개의 공유 라이브러리를 실행하고 있으며 컴파일러가 각 ABI가 사용하는 것을 알고있는 한 스택을 폭파하지 않고 적절하게 함수를 호출 할 수 있습니다.

라이브러리 함수를 호출하는 방법을 이해하는 컴파일러는 매우 중요합니다. 호스팅 된 플랫폼 (즉, OS가 프로그램을로드하는 플랫폼)에서 커널 호출 없이는 프로그램을 깜박일 수도 없습니다.


답변

ABI (Application Binary Interface)는 API와 유사하지만 소스 코드 레벨에서 호출자가 액세스 할 수 없습니다. 이진 표현 만 액세스 가능 / 사용 가능합니다.

ABI는 프로세서 아키텍처 레벨 또는 OS 레벨에서 정의 될 수 있습니다. ABI는 컴파일러의 코드 생성기 단계가 따르는 표준입니다. 표준은 OS 또는 프로세서에 의해 고정됩니다.

기능 : 구현 언어 또는 특정 컴파일러 / 링커 / 툴체인과 독립적으로 함수 호출을 수행하는 메커니즘 / 표준을 정의하십시오. JNI 또는 Python-C 인터페이스 등을 허용하는 메커니즘을 제공하십시오.

기존 엔티티 : 기계 코드 형태의 기능.

소비자 : 다른 함수 (다른 언어로 된 함수 포함, 다른 컴파일러에서 컴파일 또는 다른 링커로 링크)


답변

기능 : 컴파일러, 어셈블리 작성자, 링커 및 운영 체제에 영향을주는 계약 세트입니다. 계약은 함수 배치 방법, 매개 변수 전달 위치, 매개 변수 전달 방법, 함수 리턴 작동 방식을 지정합니다. 이들은 일반적으로 (프로세서 아키텍처, 운영 체제) 튜플에 고유합니다.

기존 엔티티 : 매개 변수 레이아웃, 함수 의미론, 레지스터 할당 예를 들어 ARM 아키텍처에는 수많은 ABI가 있습니다 (APCS, EABI, GNU-EABI, 여러 역사적 사례는 신경 쓰지 마십시오). 혼합 ABI를 사용하면 경계를 넘어 호출 할 때 코드가 작동하지 않습니다.

소비자 : 컴파일러, 어셈블리 작성자, 운영 체제, CPU 특정 아키텍처.

누가 이러한 세부 정보가 필요합니까? 컴파일러, 어셈블리 작성자, 코드 생성 (또는 정렬 요구 사항)을 수행하는 링커, 운영 체제 (인터럽트 처리, syscall 인터페이스). 어셈블리 프로그래밍을 수행 한 경우 ABI를 준수한 것입니다!

C ++ 이름 맹 글링은 특별한 경우입니다-링커 및 동적 링커 중심 문제-이름 맹 글링이 표준화되지 않으면 동적 연결이 작동하지 않습니다. 따라서 C ++ ABI는 바로 C ++ ABI라고합니다. 링커 수준 문제가 아니라 코드 생성 문제입니다. C ++ 바이너리가 있으면 소스에서 다시 컴파일하지 않고 다른 C ++ ABI (이름 변경, 예외 처리)와 호환되도록 할 수 없습니다.

ELF는 로더 및 동적 링커 사용을위한 파일 형식입니다. ELF는 이진 코드 및 데이터를위한 컨테이너 형식이며, 따라서 코드의 ABI를 지정합니다. PE 실행 파일이 ABI가 아니기 때문에 ELF를 엄격한 의미에서 ABI로 간주하지 않습니다.

모든 ABI는 명령어 세트에 따라 다릅니다. ARM ABI는 MSP430 또는 x86_64 프로세서에서 의미가 없습니다.

Windows에는 여러 ABI가 있습니다. 예를 들어 fastcall과 stdcall은 두 가지 일반적인 ABI입니다. syscall ABI는 다시 다릅니다.


답변

적어도 귀하의 질문에 답변을 드리겠습니다. Linux ABI가 시스템 호출에 어떤 영향을 미치는지, 왜 유용한 지에 대한 예를 들어 보자.

시스템 호출은 사용자 공간 프로그램이 커널 공간에 무언가를 요구하는 방법입니다. 호출에 대한 숫자 코드와 인수를 특정 레지스터에 넣고 인터럽트를 트리거하여 작동합니다. 커널 공간으로의 전환이 발생하고 커널은 숫자 코드와 인수를 찾고 요청을 처리하고 결과를 레지스터에 다시 넣고 사용자 공간으로의 전환을 트리거합니다. 예를 들어 응용 프로그램이 메모리를 할당하거나 파일을 열 때 (syscall은 “brk”및 “open”) 필요합니다.

이제 syscall은 짧은 이름 “brk”등과 해당 opcode를 가지며 시스템 고유 헤더 파일에 정의됩니다. 이 opcode가 동일하게 유지되는 한 재 컴파일하지 않고도 다른 업데이트 된 커널로 동일한 컴파일 된 userland 프로그램을 실행할 수 있습니다. 따라서 사전 컴파일 된 이진에서 사용되는 인터페이스이므로 ABI가 있습니다.


답변

공유 라이브러리에서 코드를 호출하거나 컴파일 단위 사이의 코드를 호출하려면 객체 파일에 호출 레이블이 포함되어 있어야합니다. C ++는 데이터 숨기기를 강제하고 오버로드 된 메소드를 허용하기 위해 메소드 레이블의 이름을 맹 글링합니다. 따라서 동일한 ABI를 명시 적으로 지원하지 않으면 다른 C ++ 컴파일러의 파일을 혼합 할 수 없습니다.