1
votes

In computer software, an application binary interface (ABI) is an interface between two binary program modules; often, one of these modules is a library or operating system facility, and the other is a program that is being run by a user.

An ABI defines how data structures or computational routines are accessed in machine code, which is a low-level, hardware-dependent format; in contrast, an API defines this access in source code, which is a relatively high-level, relatively hardware-independent, often human-readable format. A common aspect of an ABI is the calling convention, which determines how data is provided as input to or read as output from computational routines; examples are the x86 calling conventions.

-- https://en.wikipedia.org/wiki/Application_binary_interface

I am sure that the standard "Function Calling Sequence" described in Sys V ABI specs (both i386 and AMD64) constraints the calling of those extern functions in a C library, but does it constraints the calling of those static functions too?

Here is an example:

$cat abi.c

#include<stdio.h>
typedef void (*ret_function_t)(int,int);
ret_function_t gl_fp = NULL;
static void prnt(int i, int j){
    printf("hi from static prnt:%d:%d\n", i, j);
}
void api_1(int i){
    gl_fp = prnt;
    printf("hi from extern api_1:%d\n", i);
}
ret_function_t api_2(void){
    return gl_fp;
}

$cat abi_main.c

#include<stdio.h>
typedef void (*ret_function_t)(int,int);
extern void api_1(int i);
extern ret_function_t api_2(void);

int main(){
    api_1(1111);
    api_2()(2222, 3333);
}

$gcc abi_main.c abi.c -o abi_test

$./abi_test
hi from extern api_1:1111
hi from static prnt:2222:3333

The function calling sequence (including registers usage, stack frame, parameters passing, variable arguments...) details are defined in the Sys V ABI when abi_main.c call the api_1 and api_2 since they are extern, but what about the calling of the static function prnt which been defined in abi.c? Does it belong to the ABI standard or to the compiler to decide?

1
static functions are not subject of the ABI. The compiler might choose them not to exist at all but inline them, for example.Ctx
@Ctx What if the address of the function is taken and passed to code outside of the translation unit as a function pointer?EOF
Hi Ctx, thank you very much for your reply. But what about the cases on the above code example? If the pointer of a static function being shared across C libraries and there would must be at least one copy of the un-inlined static function code, in this case does the ABI calling sequence standard applies to the static functions? If the answer is "no" and then there would be some problems when the code api_2()(2222, 3333) being executing.remus
@remus I would take a more minimalistic approach: If the function pointer is not taken anywhere in the object defining the static function, then it does not have to follow the ABI but can be optimized. I do not think, that this is explicitly stated somewhere, it is a pure consequence from that "it has to work". Exporting a function pointer to a static function and calling it from external is well-defined after all.Ctx
@remus The ABI is out of scope of the C standard, it is merely an inevitable necessity to make object files, (dynamic and static) linking and using libraries work. The C standard specifies, that passing "pointers to static functions" and invoking these has to work. How exactly is unspecified, but it leads to ABIs and finally, that such static functions have to follow an ABI, otherwise it would not work. Perhaps some C environment could come up with another concept than ABIs (but I couldn't really think of one)Ctx

1 Answers

0
votes

Yes, they do apply. Static functions are just plain functions with traslation-unit visibility. The ABI is a compiler generation task, C standard deliberately says nothing about it. It becomes clear when removing the static word from your code. The reasoning is the same. The drawback with this approach is that compiler cannot check the linkage right (caller-callee), but only its type (void (*ret_function_t)(int,int);) at compile time, since you are the one who links at runtime. So, it is not recommended.

What happens is that your compiler will generate code for any calling function, following some ABI, lets call it ABI-a. And it will generate code for a function being called according to some other ABI, lets say ABI-b. If ABI-a == ABI-b, that always work, and this is the case if you compile both files with the same ABI.

For example, this works if prnt function were located at address 0x12345678:

ret_function_t gl_fp = (ret_function_t)0x12345678;

It also works as long as there is a function with the right arguments at 0x12345678. As you can see, the function cannot be inlined because the compiler does not know which function definition will end up in that memory spot, there could be many.