Giao diện nhị phân ứng dụng (ABI) là gì?


493

Tôi chưa bao giờ hiểu rõ ABI là gì. Xin đừng chỉ cho tôi một bài viết trên Wikipedia. Nếu tôi có thể hiểu nó, tôi sẽ không ở đây để đăng một bài viết dài như vậy.

Đây là suy nghĩ của tôi về các giao diện khác nhau:

Điều khiển TV là giao diện giữa người dùng và TV. Nó là một thực thể hiện có, nhưng vô dụng (không cung cấp bất kỳ chức năng nào). Tất cả các chức năng cho từng nút trên điều khiển từ xa được triển khai trong TV.

Giao diện: Đây là một "thực thể hiện" lớp giữa functionalityconsumercác chức năng đó. Một giao diện tự nó không làm gì cả. Nó chỉ gọi các chức năng nằm phía sau.

Bây giờ tùy thuộc vào người dùng là ai, có nhiều loại giao diện khác nhau.

Các lệnh Giao diện dòng lệnh (CLI) là các thực thể hiện có, người tiêu dùng là người dùng và chức năng nằm phía sau.

functionality: chức năng phần mềm của tôi giải quyết một số mục đích mà chúng tôi đang mô tả giao diện này.

existing entities: lệnh

consumer: người dùng

Cửa sổ Giao diện người dùng đồ họa (GUI) , các nút, v.v. là các thực thể hiện có và một lần nữa người tiêu dùng là người dùng và chức năng nằm phía sau.

functionality: chức năng phần mềm của tôi giải quyết một số vấn đề mà chúng tôi đang mô tả giao diện này.

existing entities: cửa sổ, nút vv.

consumer: người dùng

Các chức năng Giao diện lập trình ứng dụng (API) (hoặc chính xác hơn) các giao diện (trong lập trình dựa trên giao diện) là các thực thể hiện có, người tiêu dùng ở đây là một chương trình khác không phải là người dùng và chức năng lại nằm sau lớp này.

functionality: chức năng phần mềm của tôi giải quyết một số vấn đề mà chúng tôi đang mô tả giao diện này.

existing entities: chức năng, Giao diện (mảng chức năng).

consumer: chương trình / ứng dụng khác.

Giao diện nhị phân ứng dụng (ABI) Đây là nơi vấn đề của tôi bắt đầu.

functionality: ???

existing entities: ???

consumer: ???

  • Tôi đã viết phần mềm bằng các ngôn ngữ khác nhau và cung cấp các loại giao diện khác nhau (CLI, GUI và API), nhưng tôi không chắc mình đã từng cung cấp bất kỳ ABI nào chưa.

Wikipedia nói:

ABI bao gồm các chi tiết như

  • kiểu dữ liệu, kích thước và căn chỉnh;
  • quy ước gọi, điều khiển cách các đối số của hàm được truyền và trả về các giá trị;
  • các số gọi hệ thống và cách ứng dụng thực hiện các cuộc gọi hệ thống đến hệ điều hành;

Các ABI khác chuẩn hóa các chi tiết như

  • xáo trộn tên C ++,
  • tuyên truyền ngoại lệ, và
  • quy ước gọi giữa các trình biên dịch trên cùng một nền tảng, nhưng không yêu cầu khả năng tương thích đa nền tảng.
  • Ai cần những chi tiết này? Xin đừng nói HĐH. Tôi biết lập trình lắp ráp. Tôi biết làm thế nào liên kết và tải hoạt động. Tôi biết chính xác những gì xảy ra bên trong.

  • Tại sao tên C ++ mangling đi vào? Tôi nghĩ rằng chúng ta đang nói chuyện ở cấp độ nhị phân. Tại sao ngôn ngữ đi vào?

Dù sao, tôi đã tải xuống [PDF] Phiên bản giao diện nhị phân ứng dụng System V 4.1 (1997-03-18) để xem chính xác nó chứa gì. Chà, hầu hết nó chẳng có ý nghĩa gì cả.

  • Tại sao nó chứa hai chương (thứ 4 & 5) để mô tả định dạng tệp ELF ? Trong thực tế, đây là hai chương quan trọng duy nhất của đặc điểm kỹ thuật đó. Phần còn lại của các chương là "bộ xử lý cụ thể". Dù sao, tôi mặc dù đó là một chủ đề hoàn toàn khác. Xin đừng nói rằng các đặc tả định dạng tệp ELF ABI. Nó không đủ điều kiện để trở thành một giao diện theo định nghĩa.

  • Tôi biết, vì chúng ta đang nói ở mức độ thấp như vậy nên nó phải rất cụ thể. Nhưng tôi không chắc nó "kiến trúc tập lệnh (ISA)" cụ thể như thế nào?

  • Tôi có thể tìm ABI của Microsoft Windows ở đâu?

Vì vậy, đây là những truy vấn chính đang làm phiền tôi.


7
"Xin đừng nói, HĐH" Trình biên dịch cần biết ABI. Người liên kết cần biết ABI. Nhân cần biết ABI để thiết lập chương trình trong RAM để nó chạy đúng. Đối với C ++ xem bên dưới, nó cố tình biến các nhãn thành vô nghĩa vì quá tải và các phương thức riêng tư, và trình liên kết và bất kỳ trình biên dịch nào khác cần phải có tên tương thích để hoạt động với nó, nói cách khác là cùng ABI.
Justin Smith

8
Tôi nghĩ rằng câu hỏi rất rõ ràng; mô tả chính xác định dạng câu trả lời dự kiến ​​và chưa có một câu trả lời thỏa đáng nào có thể được chấp nhận.
huyền thoại2k

3
@ legends2k Tôi coi vấn đề này là OP thực sự biết ABI là gì, nhưng không nhận ra điều đó. Đại đa số các lập trình viên sẽ không bao giờ thiết kế hoặc cung cấp ABI, bởi vì đó là công việc của các nhà thiết kế hệ điều hành / nền tảng.
JesperE

4
@JesperE: Tôi đồng ý với quan điểm của bạn. Nhưng có lẽ OP muốn biết rõ ràng, ở định dạng mà anh ấy thấy phù hợp, mặc dù anh ấy có thể không cần phải cung cấp ABI.
huyền thoại2k

2
Tôi đã không biết gì. Gần đây trong khi làm việc với tất cả những điều này. Tôi nhận ra ABI thực sự là gì. Vâng, tôi đồng ý rằng mẫu của tôi bị lỗi. Nó không thích hợp để phù hợp với ABI vào mẫu của tôi. Cảm ơn @ JasperE. Nó chỉ cần kinh nghiệm làm việc để nhận ra câu trả lời của bạn.
móng vuốt

Câu trả lời:


535

Một cách dễ dàng để hiểu "ABI" là so sánh nó với "API".

Bạn đã quen thuộc với khái niệm API. Nếu bạn muốn sử dụng các tính năng của một số thư viện hoặc HĐH của bạn, bạn sẽ lập trình dựa trên API. API bao gồm các kiểu / cấu trúc dữ liệu, hằng số, hàm, v.v. mà bạn có thể sử dụng trong mã của mình để truy cập chức năng của thành phần bên ngoài đó.

Một ABI rất giống nhau. Hãy nghĩ về nó như là phiên bản được biên dịch của API (hoặc là API ở cấp độ ngôn ngữ máy). Khi bạn viết mã nguồn, bạn truy cập thư viện thông qua API. Sau khi mã được biên dịch, ứng dụng của bạn sẽ truy cập dữ liệu nhị phân trong thư viện thông qua ABI. ABI xác định các cấu trúc và phương thức mà ứng dụng đã biên dịch của bạn sẽ sử dụng để truy cập thư viện bên ngoài (giống như API đã làm), chỉ ở cấp độ thấp hơn. API của bạn xác định thứ tự bạn chuyển đối số cho hàm. ABI của bạn xác định các cơ chế về cách thứccác đối số này được thông qua (thanh ghi, ngăn xếp, v.v.). API của bạn xác định chức năng nào là một phần của thư viện của bạn. ABI của bạn xác định cách mã của bạn được lưu trữ bên trong tệp thư viện, để bất kỳ chương trình nào sử dụng thư viện của bạn có thể xác định vị trí chức năng mong muốn và thực thi nó.

ABI rất quan trọng khi nói đến các ứng dụng sử dụng các thư viện bên ngoài. Thư viện có đầy đủ mã và các tài nguyên khác, nhưng chương trình của bạn phải biết cách xác định những gì nó cần trong tệp thư viện. ABI của bạn xác định cách nội dung của thư viện được lưu trữ bên trong tệp và chương trình của bạn sử dụng ABI để tìm kiếm trong tệp và tìm thấy những gì nó cần. Nếu mọi thứ trong hệ thống của bạn tuân thủ cùng một ABI, thì bất kỳ chương trình nào cũng có thể hoạt động với bất kỳ tệp thư viện nào, bất kể ai đã tạo ra chúng. Linux và Windows sử dụng các ABI khác nhau, vì vậy một chương trình Windows sẽ không biết cách truy cập thư viện được biên dịch cho Linux.

Đôi khi, những thay đổi ABI là không thể tránh khỏi. Khi điều này xảy ra, mọi chương trình sử dụng thư viện đó sẽ không hoạt động trừ khi chúng được biên dịch lại để sử dụng phiên bản mới của thư viện. Nếu ABI thay đổi nhưng API thì không, phiên bản thư viện cũ và mới đôi khi được gọi là "tương thích nguồn". Điều này ngụ ý rằng trong khi một chương trình được biên dịch cho một phiên bản thư viện sẽ không hoạt động với phiên bản kia, mã nguồn được viết cho một phiên bản sẽ hoạt động cho phiên bản kia nếu được biên dịch lại.

Vì lý do này, các nhà phát triển có xu hướng cố gắng giữ ABI của họ ổn định (để giảm thiểu gián đoạn). Giữ ổn định ABI có nghĩa là không thay đổi giao diện chức năng (kiểu trả về và số, kiểu và thứ tự của đối số), định nghĩa về kiểu dữ liệu hoặc cấu trúc dữ liệu, hằng số xác định, v.v. Có thể thêm các hàm và kiểu dữ liệu mới giống nhau. Chẳng hạn, nếu thư viện của bạn sử dụng số nguyên 32 bit để biểu thị phần bù của hàm và bạn chuyển sang số nguyên 64 bit, thì mã đã được biên dịch sử dụng thư viện đó sẽ không truy cập chính xác vào trường đó (hoặc bất kỳ trường hợp nào theo sau nó) . Các thành viên cấu trúc dữ liệu truy cập được chuyển đổi thành địa chỉ bộ nhớ và độ lệch trong quá trình biên dịch và nếu cấu trúc dữ liệu thay đổi,

ABI không nhất thiết là thứ bạn sẽ cung cấp rõ ràng trừ khi bạn đang thực hiện công việc thiết kế hệ thống cấp thấp. Nó cũng không phải là ngôn ngữ cụ thể, vì (ví dụ) một ứng dụng C và ứng dụng Pascal có thể sử dụng cùng ABI sau khi chúng được biên dịch.

Biên tập:Về câu hỏi của bạn về các chương liên quan đến định dạng tệp ELF trong tài liệu SysV ABI: Lý do thông tin này được đưa vào là do định dạng ELF xác định giao diện giữa hệ điều hành và ứng dụng. Khi bạn yêu cầu HĐH chạy chương trình, nó hy vọng chương trình sẽ được định dạng theo một cách nhất định và (ví dụ) hy vọng phần đầu tiên của nhị phân sẽ là tiêu đề ELF chứa thông tin nhất định tại các bộ nhớ cụ thể. Đây là cách ứng dụng truyền đạt thông tin quan trọng về chính nó tới hệ điều hành. Nếu bạn xây dựng chương trình ở định dạng nhị phân không phải ELF (chẳng hạn như a.out hoặc PE), thì HĐH dự kiến ​​các ứng dụng có định dạng ELF sẽ không thể diễn giải tệp nhị phân hoặc chạy ứng dụng.

IIRC, Windows hiện sử dụng định dạng Portable Executable (hoặc, PE). Có các liên kết trong phần "liên kết ngoài" của trang Wikipedia đó với nhiều thông tin hơn về định dạng PE.

Ngoài ra, liên quan đến lưu ý của bạn về việc xáo trộn tên C ++: Khi định vị một hàm trong tệp thư viện, hàm này thường được tra cứu theo tên. C ++ cho phép bạn quá tải tên hàm, vì vậy chỉ riêng tên là không đủ để xác định hàm. Trình biên dịch C ++ có cách xử lý riêng trong nội bộ này, được gọi là xáo trộn tên . Một ABI có thể định nghĩa một cách mã hóa tiêu chuẩn tên của hàm để các chương trình được xây dựng với ngôn ngữ hoặc trình biên dịch khác có thể định vị những gì chúng cần. Khi bạn sử dụng extern "c"trong chương trình C ++, bạn đang hướng dẫn trình biên dịch sử dụng cách ghi tiêu chuẩn hóa các tên mà phần mềm khác có thể hiểu được.


2
@bta, Cảm ơn câu trả lời tuyệt vời. Liệu gọi hội nghị là một loại ABI? Cảm ơn
camino

37
Câu trả lời tốt đẹp. Ngoại trừ đây không phải là ABI. ABI là một tập hợp các quy tắc xác định quy ước gọi và quy tắc để đặt ra các cấu trúc. Pascal chuyển các đối số trên ngăn xếp theo thứ tự ngược từ các ứng dụng C, vì vậy trình biên dịch pascal và C KHÔNG biên dịch sang cùng ABI. Các tiêu chuẩn tương ứng cho trình biên dịch C và Pascal hoàn toàn đảm bảo rằng đây sẽ là trường hợp. Trình biên dịch C ++ không thể định nghĩa một cách "tiêu chuẩn" cho tên xáo trộn, vì không có cách chuẩn nào. Các quy ước xáo trộn tên C ++ không tương thích giữa các trình biên dịch C ++ khi có các trình biên dịch C ++ cạnh tranh trên Windows.
Robin Davies


1
@RobinDavies: Trên các nền tảng mà trình biên dịch Pascal sẽ gọi các hàm đối số pop do người gọi đưa ra, trình biên dịch C thường định nghĩa các phương tiện mà lập trình viên có thể chỉ ra rằng các hàm cụ thể nên sử dụng hoặc nên sử dụng các quy ước gọi tương tự như Trình biên dịch Pascal mặc dù các trình biên dịch C thường mặc định sẽ sử dụng một quy ước trong đó các hàm được gọi để lại trên ngăn xếp bất cứ thứ gì được đặt bởi người gọi của chúng.
supercat

Tôi có thể nói các tệp obj được tạo bởi trình biên dịch C có chứa ABI không?
Mitu Raj

144

Nếu bạn biết lắp ráp và cách mọi thứ hoạt động ở cấp độ HĐH, bạn đang tuân thủ một ABI nhất định. ABI chi phối những thứ như cách các tham số được truyền, trong đó các giá trị trả về được đặt. Đối với nhiều nền tảng, chỉ có một ABI để lựa chọn và trong những trường hợp đó, ABI chỉ là "cách mọi thứ hoạt động".

Tuy nhiên, ABI cũng chi phối những thứ như cách các lớp / đối tượng được trình bày trong C ++. Điều này là cần thiết nếu bạn muốn có thể vượt qua các tham chiếu đối tượng qua các ranh giới mô-đun hoặc nếu bạn muốn trộn mã được biên dịch với các trình biên dịch khác nhau.

Ngoài ra, nếu bạn có HĐH 64 bit có thể thực thi các nhị phân 32 bit, bạn sẽ có các ABI khác nhau cho mã 32 và 64 bit.

Nói chung, bất kỳ mã nào bạn liên kết vào cùng một tệp thực thi phải tuân theo cùng ABI. Nếu bạn muốn giao tiếp giữa các mã bằng các ABI khác nhau, bạn phải sử dụng một số dạng RPC hoặc các giao thức tuần tự hóa.

Tôi nghĩ rằng bạn đang cố gắng quá nhiều để ép các loại giao diện khác nhau vào một tập hợp các đặc điểm cố định. Ví dụ: một giao diện không nhất thiết phải được chia thành người tiêu dùng và nhà sản xuất. Một giao diện chỉ là một quy ước mà hai thực thể tương tác với nhau.

ABI có thể là (một phần) thuyết bất khả tri. Một số khía cạnh (như gọi các quy ước) phụ thuộc vào ISA, trong khi các khía cạnh khác (như bố trí lớp C ++) thì không.

Một ABI được xác định rõ là rất quan trọng đối với những người viết trình biên dịch. Nếu không có ABI được xác định rõ, sẽ không thể tạo mã có thể tương tác.

EDIT: Một số lưu ý để làm rõ:

  • "Nhị phân" trong ABI không loại trừ việc sử dụng chuỗi hoặc văn bản. Nếu bạn muốn liên kết một DLL xuất một lớp C ++, ở đâu đó trong đó các phương thức và kiểu chữ ký phải được mã hóa. Đó là nơi mang tên C ++.
  • Lý do tại sao bạn không bao giờ cung cấp ABI là vì đại đa số các lập trình viên sẽ không bao giờ làm điều đó. ABI được cung cấp bởi cùng những người thiết kế nền tảng (tức là hệ điều hành) và rất ít lập trình viên sẽ có đặc quyền thiết kế ABI được sử dụng rộng rãi.

Tôi hoàn toàn không tin rằng mẫu của tôi bị lỗi. Bởi vì mọi nơi mà mẫu này cho giao diện đều được giữ đúng. Vì vậy, vâng tôi muốn tôi cũng mong ABI cũng phù hợp với mẫu này nhưng không phải vậy. Điều quan trọng là tôi vẫn không hiểu. Tôi không biết mình thật ngu ngốc hay cái gì khác nhưng nó không đi vào đầu tôi. Tôi không thể nhận ra câu trả lời và bài viết wiki.
móng vuốt

2
@jesperE, "ABI chi phối những thứ như cách các tham số được truyền, nơi đặt giá trị trả về." được dùng để chỉ "cdecl, stdcall, fastcall, pascal" phải không?
camino

3
Đúng. Tên thích hợp là "quy ước gọi", là một phần của ABI. vi.wikipedia.org/wiki/X86_calling_conventions
JesperE

4
đây là sự chính xác và chính xác câu trả lời mà không rườm rà (thay vì tiếng ồn )!
Nawaz

Tôi khuyên bạn nên viết một chút về lắp ráp. Điều này sẽ giúp mọi người hiểu ABI theo cách hữu hình hơn.
KunYu Tsai

40

Bạn thực sự không cần ABI nếu--

  • Chương trình của bạn không có chức năng, và--
  • Chương trình của bạn là một chương trình thực thi duy nhất đang chạy một mình (tức là một hệ thống nhúng) trong đó thực sự là thứ duy nhất đang chạy và nó không cần phải nói chuyện với bất cứ điều gì khác.

Một bản tóm tắt đơn giản:

API: "Đây là tất cả các chức năng bạn có thể gọi."

ABI: "Đây là cách gọi một hàm."

ABI là tập hợp các quy tắc mà trình biên dịch và trình liên kết tuân thủ để biên dịch chương trình của bạn để nó hoạt động chính xác. ABI bao gồm nhiều chủ đề:

  • Có thể cho rằng phần lớn nhất và quan trọng nhất của ABI là tiêu chuẩn gọi thủ tục đôi khi được gọi là "quy ước gọi". Các quy ước gọi chuẩn hóa cách "các hàm" được dịch sang mã lắp ráp.
  • ABIs cũng đưa ra cách trình bày tên của các hàm được phơi bày trong các thư viện để các mã khác có thể gọi các thư viện đó và biết các đối số nào sẽ được thông qua. Điều này được gọi là "tên xáo trộn".
  • ABI cũng chỉ ra loại dữ liệu nào có thể được sử dụng, cách chúng phải được căn chỉnh và các chi tiết cấp thấp khác.

Nhìn sâu hơn vào quy ước gọi vốn, mà tôi coi là cốt lõi của ABI:

Bản thân máy không có khái niệm về "chức năng". Khi bạn viết một hàm bằng ngôn ngữ cấp cao như c, trình biên dịch sẽ tạo ra một dòng mã lắp ráp như thế nào _MyFunction1:. Đây là một nhãn , cuối cùng sẽ được giải quyết thành một địa chỉ bởi trình biên dịch chương trình. Nhãn này đánh dấu "bắt đầu" "chức năng" của bạn trong mã lắp ráp. Trong mã cấp cao, khi bạn "gọi" chức năng đó, điều bạn thực sự đang làm là khiến CPU nhảy đến địa chỉ của nhãn đó và tiếp tục thực thi ở đó.

Để chuẩn bị cho bước nhảy, trình biên dịch phải thực hiện một loạt các công cụ quan trọng. Quy ước gọi giống như một danh sách kiểm tra mà trình biên dịch tuân theo để thực hiện tất cả các công cụ này:

  • Đầu tiên, trình biên dịch chèn một ít mã lắp ráp để lưu địa chỉ hiện tại, để khi "chức năng" của bạn hoàn thành, CPU có thể nhảy trở lại đúng nơi và tiếp tục thực thi.
  • Tiếp theo, trình biên dịch tạo mã lắp ráp để truyền các đối số.
    • Một số quy ước gọi ra lệnh rằng các đối số nên được đặt trên ngăn xếp (tất nhiên theo một thứ tự cụ thể ).
    • Các quy ước khác chỉ ra rằng các đối số nên được đặt trong các thanh ghi cụ thể ( tùy thuộc vào loại dữ liệu của khóa học).
    • Vẫn còn các quy ước khác cho rằng nên sử dụng kết hợp cụ thể giữa ngăn xếp và thanh ghi.
  • Tất nhiên, nếu trước đó có bất cứ điều gì quan trọng trong các thanh ghi đó, thì các giá trị đó sẽ bị ghi đè và bị mất mãi mãi, vì vậy một số quy ước gọi có thể ra lệnh rằng trình biên dịch nên lưu một số các thanh ghi đó trước khi đưa các đối số vào chúng.
  • Bây giờ trình biên dịch chèn một lệnh nhảy báo cho CPU đi đến nhãn mà nó đã tạo trước đó ( _MyFunction1:). Tại thời điểm này, bạn có thể coi CPU là "trong" chức năng "của bạn".
  • Khi kết thúc chức năng, trình biên dịch sẽ đặt một số mã lắp ráp sẽ làm cho CPU ghi giá trị trả về vào đúng vị trí. Quy ước gọi sẽ quyết định xem giá trị trả về sẽ được đưa vào một thanh ghi cụ thể (tùy thuộc vào loại của nó) hoặc trên ngăn xếp.
  • Bây giờ là lúc để dọn dẹp. Quy ước gọi sẽ ra lệnh nơi trình biên dịch đặt mã lắp ráp dọn dẹp.
    • Một số quy ước nói rằng người gọi phải dọn sạch ngăn xếp. Điều này có nghĩa là sau khi "chức năng" được thực hiện và CPU nhảy trở lại vị trí trước đó, mã tiếp theo sẽ được thực thi phải là một số mã dọn dẹp rất cụ thể.
    • Các quy ước khác nói rằng một số phần cụ thể của mã dọn dẹp phải ở cuối "chức năng" trước khi nhảy trở lại.

Có nhiều ABI / quy ước gọi khác nhau. Một số cái chính là:

  • Đối với CPU x86 hoặc x86-64 (môi trường 32 bit):
    • CDECL
    • ỔN ĐỊNH
    • NHANH CHÓNG
    • VÒI
    • CUỘC GỌI NÀY
  • Đối với CPU x86-64 (môi trường 64 bit):
    • HỆ THỐNG
    • ĐỐI TƯỢNG
    • VÒI
  • Đối với CPU ARM (32-bit)
    • AAPCS
  • Đối với CPU ARM (64-bit)
    • AAPCS64

Đây là một trang tuyệt vời thực sự cho thấy sự khác biệt trong hội đồng được tạo ra khi biên dịch cho các ABI khác nhau.

Một điều khác cần đề cập là ABI không chỉ liên quan bên trong mô-đun thực thi của chương trình của bạn. Nó cũng được sử dụng bởi trình liên kết để đảm bảo chương trình của bạn gọi các chức năng thư viện một cách chính xác. Bạn có nhiều thư viện chia sẻ đang chạy trên máy tính của mình và miễn là trình biên dịch của bạn biết ABI mà chúng sử dụng, chúng có thể gọi các hàm từ chúng đúng cách mà không làm nổ tung ngăn xếp.

Trình biên dịch của bạn hiểu làm thế nào để gọi các chức năng thư viện là vô cùng quan trọng. Trên nền tảng được lưu trữ (nghĩa là một trong đó HĐH tải các chương trình), chương trình của bạn thậm chí không thể nhấp nháy mà không thực hiện cuộc gọi kernel.


19

Giao diện nhị phân ứng dụng (ABI) tương tự như API, nhưng chức năng này không thể truy cập được đối với người gọi ở cấp mã nguồn. Chỉ có một đại diện nhị phân là có thể truy cập / có sẵn.

ABI có thể được xác định ở cấp kiến ​​trúc bộ xử lý hoặc ở cấp HĐH. Các ABI là các tiêu chuẩn được theo sau bởi giai đoạn tạo mã của trình biên dịch. Tiêu chuẩn được cố định bởi HĐH hoặc bởi bộ xử lý.

Chức năng: Xác định cơ chế / tiêu chuẩn để thực hiện các cuộc gọi chức năng độc lập với ngôn ngữ thực hiện hoặc trình biên dịch / liên kết / toolchain cụ thể. Cung cấp cơ chế cho phép JNI hoặc giao diện Python-C, v.v.

Các thực thể hiện có: Chức năng ở dạng mã máy.

Consumer: Một hàm khác (bao gồm một hàm trong ngôn ngữ khác, được biên dịch bởi trình biên dịch khác hoặc được liên kết bởi một trình liên kết khác).


Tại sao ABI sẽ được xác định bởi kiến ​​trúc? Tại sao các hệ điều hành khác nhau trên cùng một kiến ​​trúc không thể xác định các ABI khác nhau?
Andreas Haferburg

10

Chức năng: Một tập hợp các hợp đồng có ảnh hưởng đến trình biên dịch, trình soạn thảo lắp ráp, trình liên kết và hệ điều hành. Các hợp đồng xác định cách các chức năng được đặt ra, nơi các tham số được truyền, cách các tham số được truyền, cách trả về chức năng. Chúng thường đặc trưng cho một tuple (kiến trúc bộ xử lý, hệ điều hành).

Các thực thể hiện có: bố trí tham số, ngữ nghĩa chức năng, phân bổ đăng ký. Chẳng hạn, các kiến ​​trúc ARM có nhiều ABI (APCS, EABI, GNU-EABI, không bao giờ bận tâm đến một loạt các trường hợp lịch sử) - sử dụng ABI hỗn hợp sẽ dẫn đến mã của bạn không hoạt động khi gọi qua các ranh giới.

Consumer: Trình biên dịch, trình soạn thảo lắp ráp, hệ điều hành, kiến ​​trúc cụ thể của CPU.

Ai cần những chi tiết này? Trình biên dịch, trình soạn thảo lắp ráp, trình liên kết thực hiện việc tạo mã (hoặc yêu cầu căn chỉnh), hệ điều hành (xử lý ngắt, giao diện tòa nhà). Nếu bạn đã lập trình lắp ráp, bạn đã tuân thủ ABI!

Mangling tên C ++ là một trường hợp đặc biệt - đó là vấn đề trung tâm của trình liên kết và liên kết động - nếu việc xáo trộn tên không được chuẩn hóa, thì liên kết động sẽ không hoạt động. Do đó, C ++ ABI được gọi là C ++ ABI. Đây không phải là vấn đề cấp độ liên kết, mà thay vào đó là vấn đề tạo mã. Khi bạn có tệp nhị phân C ++, không thể làm cho nó tương thích với một C ++ ABI khác (tên xáo trộn, xử lý ngoại lệ) mà không cần biên dịch lại từ nguồn.

ELF là một định dạng tệp để sử dụng trình tải và liên kết động. ELF là một định dạng chứa cho mã nhị phân và dữ liệu và do đó chỉ định ABI của một đoạn mã. Tôi sẽ không coi ELF là một ABI theo nghĩa chặt chẽ, vì các thực thi PE không phải là một ABI.

Tất cả ABI là tập lệnh cụ thể. ARM ABI sẽ không có ý nghĩa trên bộ xử lý MSP430 hoặc x86_64.

Windows có một số ABI - ví dụ, fastcall và stdcall là hai ABI sử dụng phổ biến. Tòa nhà ABI khác biệt một lần nữa.


9

Hãy để tôi ít nhất trả lời một phần câu hỏi của bạn. Với một ví dụ về cách ABI của Linux ảnh hưởng đến các hệ thống và tại sao điều đó lại hữu ích.

Một systemcall là một cách để một chương trình không gian người dùng yêu cầu không gian hạt nhân cho một cái gì đó. Nó hoạt động bằng cách đặt mã số cho cuộc gọi và đối số trong một thanh ghi nhất định và kích hoạt ngắt. Hơn một chuyển đổi xảy ra với không gian hạt nhân và hạt nhân tìm kiếm mã số và đối số, xử lý yêu cầu, đưa kết quả trở lại vào một thanh ghi và kích hoạt chuyển đổi trở lại không gian người dùng. Điều này là cần thiết, ví dụ khi ứng dụng muốn phân bổ bộ nhớ hoặc mở một tệp (s tòa nhà "brk" và "open").

Bây giờ các tòa nhà có các tên ngắn "brk", v.v. và các mã tương ứng, chúng được định nghĩa trong một tệp tiêu đề cụ thể của hệ thống. Miễn là các opcodes này giữ nguyên, bạn có thể chạy các chương trình người dùng được biên dịch giống nhau với các hạt nhân được cập nhật khác nhau mà không phải biên dịch lại. Vì vậy, bạn có một giao diện được sử dụng bởi các nhị phân được biên dịch sẵn, do đó ABI.


4

Để gọi mã trong các thư viện dùng chung hoặc gọi mã giữa các đơn vị biên dịch, tệp đối tượng cần chứa nhãn cho các cuộc gọi. C ++ cung cấp tên của các nhãn phương thức để thực thi ẩn dữ liệu và cho phép các phương thức bị quá tải. Đó là lý do tại sao bạn không thể trộn các tệp từ các trình biên dịch C ++ khác nhau trừ khi chúng hỗ trợ rõ ràng cùng một ABI.


4

Cách tốt nhất để phân biệt giữa ABI và API là để biết tại sao và nó được sử dụng để làm gì:

Đối với x86-64 thường có một ABI (và đối với x86 32 bit có một bộ khác):

http://www.x86-64.org/documentation/abi.pdf

https://developer.apple.com/l Library / mac / Documentation / DevelopmentTools / Conceptionual / LowLevelABI / 140-x86-64_jord_Calling_Conventions / x86_64.html

http://people.freebsd.org/~obrien/amd64-elf-abi.pdf

Linux + FreeBSD + MacOSX tuân theo nó với một số biến thể nhỏ. Và Windows x64 có ABI của riêng mình:

http://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64/

Biết ABI và giả sử trình biên dịch khác cũng theo nó, sau đó về mặt lý thuyết, các nhị phân biết cách gọi nhau (đặc biệt là API thư viện) và truyền tham số qua ngăn xếp hoặc bằng các thanh ghi, v.v. Hoặc các thanh ghi sẽ được thay đổi khi gọi các hàm, v.v. Về cơ bản những kiến ​​thức này sẽ giúp phần mềm tích hợp với nhau. Biết thứ tự của các thanh ghi / bố trí ngăn xếp, tôi có thể dễ dàng ghép các phần mềm khác nhau được viết thành các cụm với nhau mà không gặp nhiều vấn đề.

Nhưng API thì khác:

Đó là một tên hàm cấp cao, với đối số được xác định, sao cho nếu các phần mềm khác nhau xây dựng bằng các API này, thì CÓ THỂ có thể gọi vào nhau. Nhưng một yêu cầu bổ sung của CÙNG ABI phải được tuân thủ.

Ví dụ: Windows được sử dụng để tuân thủ API POSIX:

https://en.wikipedia.org/wiki/Windows_Service_for_UNIX

https://en.wikipedia.org/wiki/POSIX

Và Linux cũng tuân thủ POSIX. Nhưng các nhị phân không thể được chuyển qua và chạy ngay lập tức. Nhưng vì họ đã sử dụng cùng một TÊN trong API tuân thủ POSIX, nên bạn có thể sử dụng cùng một phần mềm trong C, biên dịch lại nó trong các HĐH khác và ngay lập tức chạy nó.

API có nghĩa là để dễ dàng tích hợp phần mềm - giai đoạn tiền biên dịch. Vì vậy, sau khi biên dịch, phần mềm có thể trông hoàn toàn khác - nếu ABI khác.

ABI có nghĩa là để xác định tích hợp chính xác của phần mềm ở cấp độ nhị phân / lắp ráp.


Quy ước gọi Windows x86-64 không sử dụng quy ước gọi SysV mà tất cả các HĐH x86-64 khác sử dụng. Linux / OS X / FreeBSD đều có chung quy ước gọi điện, nhưng chúng không chia sẻ ABI đầy đủ. ABI của HĐH bao gồm các số gọi hệ thống. ví dụ: freebsd.org/doc/en_US.ISO8859-1/books/developers-handbook/ cảm nói rằng đó SYS_execvelà 11 trên linux 32 bit, nhưng 59 trên FreeBSD.
Peter Cordes

cảm ơn bình luận của bạn, tôi đã sửa đổi nhận xét của mình để trả lời tốt hơn sự khác biệt giữa ABI và API.
Peter Teoh

Bạn vẫn còn thiếu sự khác biệt giữa quy ước gọi và ABI đầy đủ (các cuộc gọi hệ thống và mọi thứ). Bạn có thể chạy một số nhị phân FreeBSD trên Linux, vì Linux (kernel) cung cấp lớp tương thích FreeBSD. Ngay cả khi đó, điều này chỉ giới hạn ở các tệp nhị phân không cố sử dụng bất kỳ phần nào của FreeBSD ABI mà Linux không cung cấp. (ví dụ: bất kỳ cuộc gọi hệ thống nào chỉ FreeBSD). Tương thích ABI có nghĩa là bạn có thể chạy cùng một nhị phân trên cả hai hệ thống, không chỉ là chúng sẽ biên dịch tương tự nhau.
Peter Cordes

"Lớp tương thích FreeBSD", chưa bao giờ nghe về điều đó. Bạn có thể chỉ đến mã nguồn hạt nhân linux có liên quan? Nhưng điều ngược lại tồn tại: freebsd.org/doc/en_US.ISO8859-1/books/handbook/linuxemu.html .
Peter Teoh

Đó không phải là thứ tôi sử dụng. Tôi đã nghĩ một cái gì đó như thế tồn tại, nhưng có lẽ nó không còn nữa. tldp.org/HOWTO/Linux+FreeBSD-6.html nói rằng nó không được biết đến và đó là từ năm 2000. xD. unix.stackexchange.com/questions/172038/ chất xác nhận rằng nó đã bị bỏ rơi và không bao giờ được thực hiện lại (vì không ai muốn nó đủ tệ để hoàn thành nó). personality(2)có thể thiết lập PER_BSD. Tôi nghĩ rằng tôi nhớ nhìn thấy personality(PER_LINUX)trong straceđầu ra tất cả các thời điểm đó, nhưng 64bit hiện đại nhị phân Linux không làm điều đó nữa.
Peter Cordes

4

Thư viện chia sẻ Linux ví dụ ABI có thể chạy tối thiểu

Trong ngữ cảnh của các thư viện dùng chung, ý nghĩa quan trọng nhất của việc "có ABI ổn định" là bạn không cần phải biên dịch lại các chương trình của mình sau khi thư viện thay đổi.

Ví dụ:

  • nếu bạn đang bán thư viện dùng chung, bạn sẽ tiết kiệm cho người dùng sự phiền toái khi biên dịch lại mọi thứ phụ thuộc vào thư viện của bạn cho mỗi bản phát hành mới

  • nếu bạn đang bán chương trình nguồn đóng phụ thuộc vào thư viện dùng chung trong bản phân phối của người dùng, bạn có thể phát hành và kiểm tra ít bản dựng sẵn hơn nếu bạn chắc chắn rằng ABI ổn định trên các phiên bản nhất định của HĐH đích.

    Điều này đặc biệt quan trọng trong trường hợp thư viện chuẩn C, mà nhiều chương trình trong hệ thống của bạn liên kết đến.

Bây giờ tôi muốn cung cấp một ví dụ cụ thể tối thiểu về điều này.

C chính

#include <assert.h>
#include <stdlib.h>

#include "mylib.h"

int main(void) {
    mylib_mystruct *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

Biên dịch và chạy tốt với:

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

Bây giờ, giả sử rằng đối với v2 của thư viện, chúng tôi muốn thêm một trường mới để mylib_mystructgọinew_field .

Nếu chúng ta đã thêm trường trước old_fieldnhư trong:

typedef struct {
    int new_field;
    int old_field;
} mylib_mystruct;

và xây dựng lại thư viện nhưng không main.out , sau đó khẳng định thất bại!

Điều này là do dòng:

myobject->old_field == 1

đã tạo ra hội đồng đang cố gắng truy cập vào đầu tiên intcủa cấu trúc, mà bây giờ new_fieldthay vì dự kiếnold_field .

Do đó, sự thay đổi này đã phá vỡ ABI.

Tuy nhiên, nếu chúng tôi thêm new_fieldsau old_field:

typedef struct {
    int old_field;
    int new_field;
} mylib_mystruct;

sau đó lắp ráp được tạo cũ vẫn truy cập đầu tiên int cấu trúc và chương trình vẫn hoạt động, bởi vì chúng tôi giữ cho ABI ổn định.

Đây là phiên bản hoàn toàn tự động của ví dụ này trên GitHub .

Một cách khác để giữ ABI ổn định này sẽ là xử lý mylib_mystructnhư một cấu trúc mờ và chỉ truy cập vào các trường của nó thông qua các trình trợ giúp phương thức. Điều này giúp cho việc giữ ABI ổn định dễ dàng hơn, nhưng sẽ phải chịu chi phí hoạt động khi chúng tôi thực hiện nhiều cuộc gọi chức năng hơn.

API so với ABI

Trong ví dụ trước, thật thú vị khi lưu ý rằng việc thêm new_fieldtrướcold_field , chỉ phá vỡ ABI chứ không phá vỡ API.

Điều này có nghĩa là, nếu chúng tôi đã biên dịch lại main.c chương trình chống lại thư viện, nó sẽ hoạt động bất kể.

Tuy nhiên, chúng tôi cũng đã phá vỡ API nếu chúng tôi đã thay đổi ví dụ chữ ký hàm:

mylib_mystruct* mylib_init(int old_field, int new_field);

vì trong trường hợp đó, main.csẽ ngừng biên dịch hoàn toàn.

API ngữ nghĩa và API lập trình

Chúng tôi cũng có thể phân loại các thay đổi API theo loại thứ ba: thay đổi ngữ nghĩa.

API ngữ nghĩa, thường là một mô tả ngôn ngữ tự nhiên về những gì API phải làm, thường được bao gồm trong tài liệu API.

Do đó, có thể phá vỡ API ngữ nghĩa mà không phá vỡ bản dựng chương trình.

Ví dụ: nếu chúng tôi đã sửa đổi

myobject->old_field = old_field;

đến:

myobject->old_field = old_field + 1;

sau đó, điều này sẽ không phá vỡ API lập trình, cũng không phải ABI, nhưng main.cAPI ngữ nghĩa sẽ phá vỡ.

Có hai cách để lập trình kiểm tra API hợp đồng:

  • kiểm tra một loạt các trường hợp góc. Dễ làm, nhưng bạn luôn có thể bỏ lỡ một.
  • xác minh chính thức . Khó hơn để làm, nhưng tạo ra bằng chứng toán học về tính chính xác, về cơ bản thống nhất tài liệu và kiểm tra thành một cách có thể kiểm chứng "con người" / máy móc! Miễn là không có lỗi trong mô tả chính thức của bạn ;-)

    Khái niệm này liên quan chặt chẽ đến việc chính thức hóa Toán học: /math/53969/what-does-formal-mean/3297537#3297537

Danh sách mọi thứ phá vỡ thư viện chia sẻ C / C ++ ABIs

TODO: tìm / tạo danh sách cuối cùng:

Ví dụ runnable tối thiểu Java

Tương thích nhị phân trong Java là gì?

Đã thử nghiệm trong Ubuntu 18.10, GCC 8.2.0.


3

ABI cần nhất quán giữa người gọi và callee để chắc chắn rằng cuộc gọi thành công. Sử dụng ngăn xếp, sử dụng đăng ký, kết thúc ngăn xếp pop thường xuyên. Tất cả những điều này là những phần quan trọng nhất của ABI.


3

Tóm lược

Có nhiều cách giải thích và ý kiến ​​mạnh mẽ của lớp chính xác xác định ABI (giao diện nhị phân ứng dụng).

Theo quan điểm của tôi, ABI là một quy ước chủ quan về những gì được coi là nền tảng / nền tảng cụ thể cho một API cụ thể. ABI là "phần còn lại" của các quy ước "sẽ không thay đổi" đối với một API cụ thể hoặc sẽ được xử lý bởi môi trường thời gian chạy: người thực thi, công cụ, trình liên kết, trình biên dịch, jvm và HĐH.

Xác định giao diện : ABI, API

Nếu bạn muốn sử dụng một thư viện như joda-time, bạn phải khai báo một phụ thuộc vào joda-time-<major>.<minor>.<patch>.jar. Thư viện tuân theo các thực tiễn tốt nhất và sử dụng Phiên bản ngữ nghĩa . Điều này xác định khả năng tương thích API ở ba cấp độ:

  1. Bản vá - Bạn không cần thay đổi tất cả mã của mình. Thư viện chỉ sửa một số lỗi.
  2. Nhỏ - Bạn không cần thay đổi mã của mình kể từ khi bổ sung
  3. Chính - Giao diện (API) được thay đổi và bạn có thể cần thay đổi mã của mình.

Để bạn sử dụng một bản phát hành chính mới của cùng một thư viện, rất nhiều quy ước khác vẫn phải được tôn trọng:

  • Ngôn ngữ nhị phân được sử dụng cho các thư viện (trong trường hợp Java là phiên bản đích của JVM xác định mã byte Java)
  • Quy ước gọi
  • Các quy ước JVM
  • Công ước liên kết
  • Các quy ước thời gian chạy Tất cả những điều này được xác định và quản lý bởi các công cụ chúng tôi sử dụng.

Ví dụ

Nghiên cứu trường hợp Java

Ví dụ, Java đã tiêu chuẩn hóa tất cả các quy ước này, không phải trong một công cụ, mà là trong một đặc tả JVM chính thức. Đặc điểm kỹ thuật cho phép các nhà cung cấp khác cung cấp một bộ công cụ khác có thể xuất các thư viện tương thích.

Java cung cấp hai trường hợp nghiên cứu thú vị khác cho ABI: phiên bản Scala và Dalvik ảo .

Máy ảo Dalvik đã phá vỡ ABI

Máy ảo Dalvik cần một loại mã byte khác với mã byte Java. Các thư viện Dalvik có được bằng cách chuyển đổi mã byte Java (có cùng API) cho Dalvik. Theo cách này, bạn có thể nhận được hai phiên bản của cùng một API: được xác định bởi bản gốc joda-time-1.7.2.jar. Chúng tôi có thể gọi cho tôi joda-time-1.7.2.jarjoda-time-1.7.2-dalvik.jar. Họ sử dụng một ABI khác nhau dành cho các vms Java tiêu chuẩn hướng ngăn xếp: một của Oracle, một của IBM, Java mở hoặc bất kỳ loại nào khác; và ABI thứ hai là một trong những xung quanh Dalvik.

Scala phát hành liên tiếp là không tương thích

Scala không có khả năng tương thích nhị phân giữa các phiên bản Scala nhỏ: 2.X. Vì lý do này, cùng một API "io.reactivex" %% "rxscala"% "0.26.5" có ba phiên bản (trong tương lai nhiều hơn): cho Scala 2.10, 2.11 và 2.12. Điều gì đã thay đổi? Tôi không biết bây giờ , nhưng các nhị phân không tương thích. Có lẽ các phiên bản mới nhất bổ sung thêm những thứ khiến các thư viện không thể sử dụng được trên các máy ảo cũ, có thể là những thứ liên quan đến các quy ước liên kết / đặt tên / tham số.

Các bản phát hành liên tiếp của Java không tương thích

Java cũng có vấn đề với các bản phát hành chính của JVM: 4,5,6,7,8,9. Họ chỉ cung cấp khả năng tương thích ngược. Jvm9 biết cách chạy mã được biên dịch / nhắm mục tiêu ( -targettùy chọn của javac ) cho tất cả các phiên bản khác, trong khi JVM 4 không biết cách chạy mã được nhắm mục tiêu cho JVM 5. Tất cả những điều này trong khi bạn có một thư viện joda. Sự không tương thích này bay dưới radar nhờ các giải pháp khác nhau:

  1. Phiên bản ngữ nghĩa: khi các thư viện nhắm mục tiêu JVM cao hơn, họ thường thay đổi phiên bản chính.
  2. Sử dụng JVM 4 làm ABI và bạn an toàn.
  3. Java 9 thêm một đặc tả về cách bạn có thể bao gồm mã byte cho JVM được nhắm mục tiêu cụ thể trong cùng một thư viện.

Tại sao tôi bắt đầu với định nghĩa API?

API và ABI chỉ là quy ước về cách bạn xác định khả năng tương thích. Các lớp thấp hơn là chung cho rất nhiều ngữ nghĩa cấp cao. Đó là lý do tại sao nó dễ dàng thực hiện một số quy ước. Loại quy ước đầu tiên là về căn chỉnh bộ nhớ, mã hóa byte, quy ước gọi, mã hóa endian lớn và nhỏ, v.v. Trên đầu chúng, bạn có các quy ước thực thi như các mô tả khác, các quy ước liên kết, mã byte trung gian như được sử dụng bởi Java hoặc LLVM IR được sử dụng bởi GCC. Thứ ba, bạn nhận được các quy ước về cách tìm các thư viện, cách tải chúng (xem các trình nạp lớp Java). Khi bạn đi lên cao hơn và cao hơn trong các khái niệm, bạn có những quy ước mới mà bạn coi là nhất định. Đó là lý do tại sao họ không tham gia phiên bản ngữ nghĩa . Họ đang ngầm hoặc sụp đổ trongphiên bản. Chúng tôi có thể sửa đổi phiên bản ngữ nghĩa với <major>-<minor>-<patch>-<platform/ABI>. Đây là những gì đang thực sự xảy ra đã: nền tảng đã được một rpm, dll, jar(JVM bytecode), war(JVM + web server), apk, 2.11(cụ thể Scala phiên bản) và vân vân. Khi bạn nói APK, bạn đã nói về một phần ABI cụ thể của API.

API có thể được chuyển đến ABI khác nhau

Mức độ trừu tượng cao nhất (các nguồn được viết dựa trên API cao nhất có thể được biên dịch lại / chuyển sang bất kỳ mức độ trừu tượng thấp hơn nào khác.

Hãy nói rằng tôi có một số nguồn cho rxscala. Nếu các công cụ Scala được thay đổi, tôi có thể biên dịch lại chúng. Nếu JVM thay đổi, tôi có thể tự động chuyển đổi từ máy cũ sang máy mới mà không bận tâm đến các khái niệm cấp cao. Trong khi chuyển có thể khó khăn sẽ giúp bất kỳ khách hàng khác. Nếu một hệ điều hành mới được tạo bằng mã biên dịch hoàn toàn khác, trình dịch có thể được tạo.

API được chuyển qua các ngôn ngữ

Có các API được chuyển sang nhiều ngôn ngữ như luồng phản ứng . Nói chung, họ xác định ánh xạ tới các ngôn ngữ / nền tảng cụ thể. Tôi cho rằng API là đặc tả chính được định nghĩa chính thức bằng ngôn ngữ của con người hoặc thậm chí là một ngôn ngữ lập trình cụ thể. Tất cả các "ánh xạ" khác là ABI theo một nghĩa nào đó, API khác nhiều hơn ABI thông thường. Điều tương tự đang xảy ra với các giao diện REST.


1

Tóm lại và trong triết học, chỉ có những thứ thuộc loại có thể hòa hợp với nhau, và ABI có thể được coi là loại công cụ phần mềm hoạt động cùng nhau.


1

Tôi cũng đã cố gắng để hiểu câu trả lời của ABI và JesperE rất hữu ích.

Từ góc độ rất đơn giản, chúng tôi có thể cố gắng hiểu ABI bằng cách xem xét khả năng tương thích nhị phân.

KDE wiki định nghĩa thư viện là tương thích nhị phân, nếu một chương trình được liên kết động với phiên bản cũ của thư viện tiếp tục chạy với các phiên bản mới hơn của thư viện mà không cần phải biên dịch lại. Để biết thêm về liên kết động, hãy tham khảo Liên kết tĩnh so với liên kết động

Bây giờ, chúng ta hãy thử xem xét các khía cạnh cơ bản nhất cần thiết cho một thư viện là khả năng tương thích nhị phân (giả sử không có thay đổi mã nguồn cho thư viện):

  1. Kiến trúc tập lệnh tương thích tương tự / ngược (hướng dẫn bộ xử lý, cấu trúc tệp đăng ký, tổ chức ngăn xếp, loại truy cập bộ nhớ, cùng với kích thước, bố cục và căn chỉnh các loại dữ liệu cơ bản mà bộ xử lý có thể truy cập trực tiếp)
  2. Các quy ước gọi giống nhau
  3. Quy ước xáo trộn cùng tên (điều này có thể cần thiết nếu nói rằng một chương trình Fortran cần gọi một số chức năng thư viện C ++).

Chắc chắn, có nhiều chi tiết khác nhưng đây chủ yếu là những gì ABI cũng đề cập.

Cụ thể hơn để trả lời câu hỏi của bạn, từ trên, chúng tôi có thể suy luận:

Chức năng ABI: tương thích nhị phân

các thực thể hiện có: chương trình / thư viện / HĐH hiện có

người tiêu dùng: thư viện, hệ điều hành

Hi vọng điêu nay co ich!


1

Giao diện nhị phân ứng dụng (ABI)

Chức năng:

  • Dịch từ mô hình của lập trình viên sang kiểu dữ liệu miền, kích thước, căn chỉnh, quy ước gọi của hệ thống cơ bản, điều khiển cách các đối số của hàm được truyền và trả về các giá trị; các số gọi hệ thống và cách ứng dụng thực hiện các cuộc gọi hệ thống đến hệ điều hành; lược đồ xáo trộn tên của trình biên dịch ngôn ngữ cấp cao, lan truyền ngoại lệ và quy ước gọi giữa các trình biên dịch trên cùng một nền tảng, nhưng không yêu cầu khả năng tương thích đa nền tảng ...

Các thực thể hiện có:

  • Các khối logic tham gia trực tiếp vào việc thực hiện chương trình: ALU, các thanh ghi mục đích chung, các thanh ghi cho ánh xạ bộ nhớ / I / O của I / O, v.v ...

khách hàng:

  • Trình xử lý ngôn ngữ liên kết, trình biên dịch ...

Chúng cần được đảm bảo bởi bất cứ ai phải đảm bảo rằng các chuỗi công cụ xây dựng hoạt động như một tổng thể. Nếu bạn viết một mô-đun bằng ngôn ngữ lắp ráp, một mô-đun khác bằng Python và thay vì bộ tải khởi động của riêng bạn muốn sử dụng một hệ điều hành, thì các mô-đun "ứng dụng" của bạn đang hoạt động trên các ranh giới "nhị phân" và yêu cầu thỏa thuận về "giao diện" đó.

Việc xáo trộn tên C ++ vì các tệp đối tượng từ các ngôn ngữ cấp cao khác nhau có thể được yêu cầu phải được liên kết trong ứng dụng của bạn. Cân nhắc sử dụng thư viện tiêu chuẩn GCC thực hiện các cuộc gọi hệ thống tới Windows được xây dựng bằng Visual C ++.

ELF là một kỳ vọng có thể có của trình liên kết từ một tệp đối tượng để giải thích, mặc dù JVM có thể có một số ý tưởng khác.

Đối với ứng dụng Windows RT Store, hãy thử tìm kiếm ARM ABI nếu bạn thực sự muốn làm cho một số chuỗi công cụ xây dựng hoạt động cùng nhau.


1

Thuật ngữ ABI được sử dụng để chỉ hai khái niệm riêng biệt nhưng có liên quan.

Khi nói về trình biên dịch, nó đề cập đến các quy tắc được sử dụng để dịch từ các cấu trúc mức nguồn sang các cấu trúc nhị phân. Làm thế nào lớn là các loại dữ liệu? ngăn xếp hoạt động như thế nào? Làm thế nào để tôi truyền tham số cho các chức năng? Những thanh ghi nào nên được lưu bởi người gọi so với callee?

Khi nói về các thư viện, nó đề cập đến giao diện nhị phân được trình bày bởi một thư viện được biên dịch. Giao diện này là kết quả của một số yếu tố bao gồm mã nguồn của thư viện, các quy tắc được sử dụng bởi trình biên dịch và trong một số trường hợp định nghĩa được chọn từ các thư viện khác.

Thay đổi đối với thư viện có thể phá vỡ ABI mà không phá vỡ API. Hãy xem xét ví dụ một thư viện có giao diện như thế nào.

void initfoo(FOO * foo)
int usefoo(FOO * foo, int bar)
void cleanupfoo(FOO * foo)

và lập trình viên ứng dụng viết mã như

int dostuffwithfoo(int bar) {
  FOO foo;
  initfoo(&foo);
  int result = usefoo(&foo,bar)
  cleanupfoo(&foo);
  return result;
}

Lập trình viên ứng dụng không quan tâm đến kích thước hoặc bố cục của FOO, nhưng nhị phân ứng dụng kết thúc với kích thước mã hóa cứng của foo. Nếu lập trình viên thư viện thêm một trường bổ sung vào foo và ai đó sử dụng nhị phân thư viện mới với nhị phân ứng dụng cũ thì thư viện có thể tạo ra các giới hạn truy cập bộ nhớ.

OTOH nếu tác giả thư viện đã thiết kế API của họ như thế nào.

FOO * newfoo(void)
int usefoo(FOO * foo, int bar)
void deletefoo((FOO * foo, int bar))

và lập trình viên ứng dụng viết mã như

int dostuffwithfoo(int bar) {
  FOO * foo;
  foo = newfoo();
  int result = usefoo(foo,bar)
  deletefoo(foo);
  return result;
}

Sau đó, nhị phân ứng dụng không cần biết gì về cấu trúc của FOO, tất cả có thể được ẩn trong thư viện. Cái giá bạn phải trả cho điều đó là các hoạt động heap có liên quan.


0

ABI- Application Binary Interfacelà về giao tiếp mã máy trong thời gian chạy giữa hai phần chương trình nhị phân như - ứng dụng, thư viện, HĐH ... ABImô tả cách các đối tượng được lưu trong bộ nhớ và cách các hàm được gọi ( calling convention)

Một ví dụ điển hình về API và ABI là hệ sinh thái iOS với ngôn ngữ Swift .

  • Application- Khi bạn tạo một ứng dụng bằng các ngôn ngữ khác nhau. Ví dụ: bạn có thể tạo ứng dụng bằng cách sử dụng SwiftObjective-C[Trộn Swift và Objective-C]

  • Application - OS- thời gian chạy - Swift runtimestandard librarieslà một phần của HĐH và chúng không nên được bao gồm trong mỗi gói (ví dụ: ứng dụng, khung). Nó giống như sử dụng Objective-C

  • Library- Module Stabilitytrường hợp - thời gian biên dịch - bạn sẽ có thể nhập một khung được xây dựng với một phiên bản trình biên dịch khác của Swift. Điều đó có nghĩa là an toàn khi tạo tệp nhị phân nguồn đóng (tiền xây dựng) sẽ được sử dụng bởi một phiên bản trình biên dịch khác ( .swiftinterfaceđược sử dụng với .swiftmodule) và bạn sẽ không nhận được

    Module compiled with _ cannot be imported by the _ compiler
    
  • Library- Library Evolutiontrường hợp

    1. Biên dịch thời gian - nếu một phụ thuộc được thay đổi, khách hàng không phải biên dịch lại.
    2. Thời gian chạy - một thư viện hệ thống hoặc khung động có thể được hoán đổi bởi một cái mới.

[API so với ABI]

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.