Wenn man von einer ABI redet, dann normal in Abgrenzung zur API. In diesem Kontext bezieht sich dann die API auf den Quellcode und die ABI auf bereits kompilierten Binärcode. Interessant ist das vor allem in Sachen Kompatibilität, also die Frage, welche von beiden stabil bleibt (wenn sie sich verändert, funktionieren alte Programme logischerweise nicht mehr).
Nehmen wir mal ein Beispiel. Du hast einen Kernel, der einen Syscall Nummer 3 anbietet, um ein Zeichen auszugeben, das in eax übergeben wird (das ist die ABI). Du hast außerdem eine libc, die eine Funktion sys_print_char(int c) anbietet (das ist die API). Wenn du die Funktion jetzt in sys_putc umbenennst, änderst du die API. Programme müssen also geändert werden, wenn man sie mit der neuen Version kompilieren möchte. Bereits kompilierte alte Anwendungen funktionieren aber noch, weil sie eben den Code enthalten, um Syscall Nummer 3 aufzurufen.
Umgekehrt, wenn du entscheidest, dass 42 eigentlich eine viel bessere Nummer wäre, aber du lässt den Namen gleich, dann änderst du die ABI und die API bleibt stabil (weil die libc natürlich auch auf die neue Nummer geändert wird). Alte Programme laufen dann erst einmal nicht mehr, aber du kannst sie einfach unverändert neu kompilieren und dann tun sie wieder.
Das ganze funktioniert natürlich nicht nur mit Syscallnummern und Funktionsnamen, sondern auch, wenn die Funktion einen Pointer als Parameter nimmt, der auf eine Struktur zeigt, die ein neues Feld bekommen hat. Oder auf eine andere Struktur zeigt, die wiederum eine Änderung hat. Deswegen ist es manchmal gar nicht so einfach zu sehen, ob die ABI jetzt unverändert geblieben ist oder nicht.