How do I make a struct for FFI that contains a nullable function pointer? - c

I have an existing C program that loads shared library plugins. The main C program interacts with those plugins through a C struct containing integers, strings, function pointers, etc. How can I create such a plugin from Rust?
Note that the (real) C program cannot be changed, nor can the API be changed, those are fixed, existing things, so this is not a question about "how best to support plugins in Rust", it's how can Rust make *.so files which interoperate with an existing C program.
Here's a simplified example of a C program + C plugin:
/* gcc -g -Wall test.c -o test -ldl
./test ./test-api.so
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include <dlfcn.h>
struct api {
uint64_t i64;
int i;
const char *name; /* can be NULL */
void (*load) (void); /* must not be NULL */
void (*hello) (const char *str); /* can be NULL */
};
int
main (int argc, char *argv[])
{
void *dl = dlopen (argv[1], RTLD_NOW);
if (!dl) { fprintf (stderr, "%s: %s\n", argv[1], dlerror ()); exit (1); }
struct api *(*get_api) (void) = dlsym (dl, "get_api");
printf ("calling get_api ...\n");
struct api *api = get_api ();
printf ("api->i64 = %" PRIi64 "\n", api->i64);
printf ("api->i = %d\n", api->i);
if (api->name)
printf ("api->name = %s\n", api->name);
printf ("calling api->load ...\n");
api->load ();
if (api->hello) {
printf ("calling api->hello ...\n");
api->hello ("world");
}
printf ("exiting\n");
exit (0);
}
/* gcc -g -shared -fPIC -Wall test-api.c -o test-api.so */
#include <stdio.h>
#include <stdint.h>
static void
load (void)
{
printf ("this is the load function in the plugin\n");
}
static void
hello (const char *str)
{
printf ("hello %s\n", str);
}
static struct api {
uint64_t i64;
int i;
const char *name;
void (*load) (void);
void (*hello) (const char *str);
} api = {
1042,
42,
"this is the plugin",
load,
hello,
};
struct api *
get_api (void)
{
return &api;
}
Here's what I wrote in Rust to try to get a plugin, but it doesn't compile:
extern crate libc;
use libc::*;
use std::ffi::*;
use std::ptr;
use std::os::raw::c_int;
#[repr(C)]
pub struct api {
i64: uint64_t,
i: c_int,
name: *const c_char,
load: extern fn (),
hello: extern fn (), // XXX
}
extern fn hello_load () {
println! ("hello this is the load method");
}
#[no_mangle]
pub extern fn get_api () -> *const api {
println! ("hello from the plugin");
let api = Box::new (api {
i64: 4201,
i: 24,
name: CString::new("hello").unwrap().into_raw(), // XXX memory leak?
load: hello_load,
hello: std::ptr::null_mut,
});
return Box::into_raw(api); // XXX memory leak?
}
This is compiled using Cargo.toml containing:
[package]
name = "embed"
version = "0.1.0"
[dependencies]
libc = "0.2"
[lib]
name = "embed"
crate-type = ["cdylib"]
The error is:
error[E0308]: mismatched types
--> src/lib.rs:32:16
|
32 | hello: std::ptr::null_mut,
| ^^^^^^^^^^^^^^^^^^ expected "C" fn, found "Rust" fn
|
= note: expected type `extern "C" fn()`
found type `fn() -> *mut _ {std::ptr::null_mut::<_>}`
error: aborting due to previous error
I didn't get to try loading the module but when I tried this before with the real program the fields were all wrong indicating something much more fundamental was wrong.

tl;dr Use Option to represent nullable function pointers and None for null.
The error message is confusing, first, because std::ptr::null_mut isn't a pointer; it's a generic function that returns a pointer, and you haven't called it. So Rust is seeing you pass a function that has the wrong signature and calling convention, and complaining about that.
But once you fix that, you'll get this error instead:
error[E0308]: mismatched types
--> src/lib.rs:29:16
|
29 | hello: std::ptr::null_mut(),
| ^^^^^^^^^^^^^^^^^^^^ expected fn pointer, found *-ptr
|
= note: expected type `extern "C" fn()`
found type `*mut _`
Function pointers and object pointers are not compatible (this is also the case in C), so you can't cast between them. null_mut returns an object pointer, so you need to find another way to create a null function pointer.
Function pointers (values of type fn(...) -> _) have another interesting property: unlike raw pointers (*const _ and *mut _), they can't be null. You don't need an unsafe block to call a function via pointer, and so creating a null function pointer is unsafe, like creating a null reference.
How do you make something nullable? Wrap it in Option:
#[repr(C)]
pub struct api {
// ...
load: Option<extern fn ()>,
hello: Option<extern fn ()>, // assuming hello can also be null
}
And populate it with Some(function) or None:
let api = Box::new (api {
// ...
load: Some(hello_load),
hello: None,
});
It's not usually a good idea to use enums, including Option, in a repr(C) struct, because C doesn't have an enum equivalent and so you don't know what you're going to get on the other side. But in the case of Option<T> where T is something non-nullable, None is represented by the null value, so it should be okay.
The use of Option to represent a nullable function pointer for FFI is documented in the Unsafe Code Guidelines:
null values are not supported by the Rust function pointer types -- just like references, the expectation is that you use Option to create nullable pointers. Option<fn(Args...) -> Ret> will have the exact same ABI as fn(Args...) -> Ret, but additionally allows null pointer values.

Related

how to write a function pointer "pointing" to a macro which expands to the actual function?

I have function in library lib.so which I'm linking to my application dynamically using dlopen()
lib.h
void DebugPrint( unsigned char logLevel,
const char *programName,
const char *format,
... );
#define DBG_PRINT(logLvl, format, ...) \
DebugPrint(logLvl,TL_MODULE_NAME, format, ## __VA_ARGS__)
myapp.c
void (*DBG_PRINT_ptr)( unsigned char logLevel,
const char *programName,
const char *format,
... );
void *handle = NULL;
bool ret = RESULT_SUCCESS;
/* Open Shared Lib into the same Process */
/* LDRA_INSPECTED 496 S */
handle = dlopen(lib.so, RTLD_NOW);
if (NULL == handle)
{
/* fail to load the library */
LLOG_error(" dlopen Error to open handle: %s\n", dlerror());
ret = RESULT_FAILURE;
}
if(RESULT_SUCCESS == ret)
{
DBG_PRINT_ptr = dlsym(handle, "DebugPrint");
if( DBG_PRINT_ptr == NULL)
{
LLOG_error("Failed in DBG_PRINT dlsym(): Err:%s", dlerror());
dlclose(handle);
ret = RESULT_FAILURE;
}
}
(void)DBG_PRINT_ptr ("loglevel1","dd","printval");
but I'm getting error while runtime
Failed in DBG_PRINT dlsym(): Err:Symbol not found
what is the correct way to define function pointer for the following requirement.
There is no way to point to a macro with a function pointer. Only functions can be pointed by a function pointer.
You can have a pointer to the function that the macro calls however. Like this:
auto fptr = &DebugPrint;
Symbol not found
This means that there is no symbol by that name in the dynamic library.
One typical mistake is to attempt to load a function which has C++ language linkage. The symbol of such function will be mangled and won't match the name of the function.
Functions can be declared to have C linkage with a language linkage declaration:
extern "C"

How to correctly assign a pointer returned by dlsym into a variable of function pointer type?

I am trying to use dlopen() and dlsym() in my code and compile it with gcc.
Here is the first file.
/* main.c */
#include <dlfcn.h>
int main()
{
void *handle = dlopen("./foo.so", RTLD_NOW);
if (handle) {
void (*func)() = dlsym(handle, "func");
func();
}
return 0;
}
Here is the second file.
/* foo.c */
#include <stdio.h>
void func()
{
printf("hello, world\n");
}
Here is how I compile and run the code.
$ gcc -std=c99 -pedantic -Wall -Wextra -shared -fPIC -o foo.so foo.c
$ gcc -std=c99 -pedantic -Wall -Wextra -ldl -o main main.c
main.c: In function ‘main’:
main.c:10:26: warning: ISO C forbids initialization between function pointer and ‘void *’ [-Wpedantic]
void (*func)() = dlsym(handle, "func");
^
$ ./main
hello, world
How can I get rid of the warning?
Type casting doesn't help. If I try to type cast the return value of dlsym() into a function pointer, I get this warning instead.
main.c:10:26: warning: ISO C forbids conversion of object pointer to function pointer type [-Wpedantic]
void (*func)() = (void (*)()) dlsym(handle, "func");
^
What would convince the compiler that this code is fine?
If you want to be pedantically correct, don't try to resolve the address of a function. Instead, export some kind of structure from the dynamic library:
In the library
struct export_vtable {
void (*helloworld)(void);
};
struct export_vtable exports = { func };
In the caller
struct export_vtable {
void (*helloworld)(void);
};
int main() {
struct export_vtable* imports;
void *handle = dlopen("./foo.so", RTLD_NOW);
if (handle) {
imports = dlsym(handle, "exports");
if (imports) imports->helloworld();
}
return 0;
}
This technique is actually quite common, not for portability -- POSIX guarantees that function pointers can be converted to and from void* -- but because it allows more flexibility.
This made my code sufficiently pedantic:
*(void**)(&func_ptr) = dlsym(handle, "function_name");
(I found it here http://pubs.opengroup.org/onlinepubs/009695399/functions/dlsym.html)
The problem here is that a pointer to object is subtly separated from a function pointer. In ISO/IEC 9899:201x paper §6.3.2.3 Pointers it's stated:
A pointer to void may be converted to or from a pointer to any
object type. A pointer to any object type may be converted to a
pointer to void and back again; the result shall compare equal to
the original pointer.
.
A pointer to a function of one type may be converted to a pointer to
a function of another type and back again; the result shall compare
equal to the original pointer. If a converted pointer is used to
call a function whose type is not compatible with the pointed-to
type, the behavior is undefined.
So a function pointer is different from object pointers, and consequently the assignment of a void * to a function pointer is always non strictly compliant.
Anyway, as I said in comments, in 99.9999....9999% of cases it is permitted thanks to the ANNEX J - Portability issues, §J.5.7 Function pointer casts of the previously mentioned paper that states:
A pointer to an object or to void may be cast to a pointer to a
function, allowing data to be invoked as a function (6.5.4).
A pointer to a function may be cast to a pointer to an object or to
void, allowing a function to be inspected or modified (for example,
by a debugger) (6.5.4).
Now on the practical side a technique that avoid the splitting of code in more files is to use pragmas to suppress pedantic warnings for a small piece of code.
The more brutal form can be:
/* main.c */
#include <dlfcn.h>
#pragma GCC diagnostic push //Save actual diagnostics state
#pragma GCC diagnostic ignored "-pedantic" //Disable pedantic
int main()
{
void *handle = dlopen("./foo.so", RTLD_NOW);
if (handle) {
void (*func)() = dlsym(handle, "func");
func();
}
return 0;
}
#pragma GCC diagnostic pop //Restore diagnostics state
A more sophisticated way could be actuated isolating the offending code in a small function, then forcing its inlining. It's more a makeup than effective solution, but will suppress the unwanted diagnostic:
/* main.c */
#include <dlfcn.h>
#pragma GCC diagnostic push //Save actual diagnostics state
#pragma GCC diagnostic ignored "-pedantic" //Disable pedantic
void (*)() __attribute__((always_inline)) Assigndlsym(void *handle, char *func)
{
return dlsym(handle, func); //The non compliant assignment is done here
}
#pragma GCC diagnostic pop //Restore diagnostics state
int main()
{
void *handle = dlopen("./foo.so", RTLD_NOW);
if (handle) {
void (*func)() = Assigndlsym(handle, "func"); //Now the assignment is compliant
func();
}
return 0;
}
To keep the -pedantic option for your code while having parts of code that are not strictly conforming, separate that code into a separate file with custom warning options.
So, make a function that wraps the dlsym function and returns a function pointer. Put it in a separate file and compile that file without -pedantic.
you can use union, like this:
union {
void *ptr;
void (*init_google_logging) (char* argv0);
} orig_func;
orig_func.ptr = dlsym (RTLD_NEXT, "_ZN6google17InitGoogleLoggingEPKc");
orig_func.init_google_logging (argv0);
The POSIX Standard explicitly states that you can “run” an address passed as a pointer to object (emphasis mine):
Note that conversion from a void * pointer to a function pointer as in:
fptr = (int (*)(int))dlsym(handle, "my_function");
is not defined by the ISO C standard. This standard requires this conversion to work correctly on conforming implementations.
To do that you need to “animate” the object pointer first.
#define ANIMATED_POINTER(FUNC_TYPE, OBJECT_POINTER) \
(*((FUNC_TYPE *)(&(OBJECT_POINTER))))
You can then then invoke the object variable directly,
#include <dlfcn.h>
#define ANIMATED_POINTER(FUNC_TYPE, OBJECT_POINTER) \
(*((FUNC_TYPE *)(&(OBJECT_POINTER))))
typedef void (* MyFuncType) ();
int main () {
void * handle = dlopen("./foo.so", RTLD_NOW);
if (handle) {
void * func_obj_ptr = dlsym(handle, "func");
ANIMATED_POINTER(MyFuncType, func_obj_ptr)();
}
return 0;
}
or you can convert it into another variable of appropriate function type.
#include <dlfcn.h>
#define ANIMATED_POINTER(FUNC_TYPE, OBJECT_POINTER) \
(*((FUNC_TYPE *)(&(OBJECT_POINTER))))
typedef void (* MyFuncType) ();
int main () {
void * handle = dlopen("./foo.so", RTLD_NOW);
if (handle) {
void * func_obj_ptr = dlsym(handle, "func");
MyFuncType func = ANIMATED_POINTER(MyFuncType, func_obj_ptr);
func();
}
return 0;
}
Addition
Although the conversion from an object pointer to a function pointer can be problematic, in every standard C allows to convert different types of function pointers between them. You can exploit this permission and use void (*) (void) as a fixed destination type for your macro.
#define UNTYPED_ANIMATED_POINTER(OBJECT_POINTER) \
(*((void (**) (void))(&(OBJECT_POINTER))))
Hence, imagining an addition.c library containing only,
int add_numbers (int first, int second) {
return first + second;
}
below are four variations of the same code.
#1
#include <dlfcn.h>
#include <stdio.h>
#define UNTYPED_ANIMATED_POINTER(OBJECT_POINTER) \
(*((void (**) (void))(&(OBJECT_POINTER))))
int main () {
void * handle = dlopen("./addition.so", RTLD_NOW);
if (handle) {
int (* add_numbers) (int, int);
void * func_obj_ptr = dlsym(handle, "add_numbers");
add_numbers = (int (*) (int, int)) UNTYPED_ANIMATED_POINTER(func_obj_ptr);
int result = add_numbers(19, 23);
printf("The result is: %d\n", result); // "The result is: 42"
}
return 0;
}
#2
#include <dlfcn.h>
#include <stdio.h>
#define UNTYPED_ANIMATED_POINTER(OBJECT_POINTER) \
(*((void (**) (void))(&(OBJECT_POINTER))))
int main () {
void * handle = dlopen("./addition.so", RTLD_NOW);
if (handle) {
void * func_obj_ptr = dlsym(handle, "add_numbers");
int result = ((int (*) (int, int)) UNTYPED_ANIMATED_POINTER(func_obj_ptr))(19, 23);
printf("The result is: %d\n", result); // "The result is: 42"
}
return 0;
}
#3
#include <dlfcn.h>
#include <stdio.h>
#define UNTYPED_ANIMATED_POINTER(OBJECT_POINTER) \
(*((void (**) (void))(&(OBJECT_POINTER))))
typedef int (* AdditionFunc) (int, int);
int main () {
void * handle = dlopen("./addition.so", RTLD_NOW);
if (handle) {
void * func_obj_ptr = dlsym(handle, "add_numbers");
AdditionFunc add_numbers = (AdditionFunc) UNTYPED_ANIMATED_POINTER(func_obj_ptr);
int result = add_numbers(19, 23);
printf("The result is: %d\n", result); // "The result is: 42"
}
return 0;
}
#4
#include <dlfcn.h>
#include <stdio.h>
#define UNTYPED_ANIMATED_POINTER(OBJECT_POINTER) \
(*((void (**) (void))(&(OBJECT_POINTER))))
typedef int (* AdditionFunc) (int, int);
int main () {
void * handle = dlopen("./addition.so", RTLD_NOW);
if (handle) {
void * func_obj_ptr = dlsym(handle, "add_numbers");
int result = ((AdditionFunc) UNTYPED_ANIMATED_POINTER(func_obj_ptr))(19, 23);
printf("The result is: %d\n", result); // "The result is: 42"
}
return 0;
}
The compiler only "tries to help", so you have to use two typecasts:
#include <stdint.h>
void (*func)() = (void (*)())(intptr_t)dlsym(handle, "func");

How to use macro for calling function?

I want to call function according to func_name string.
My code is here below:
#define MAKE_FUNCNAME func_name##hello
void call_func(void* (*func)(void))
{
func();
}
void *print_hello(void)
{
printf("print_hello called\n");
}
int main(void)
{
char func_name[30] = "print_";
call_func(MAKE_FUNCNAME);
return 0;
}
But this code doesn't work. I want code to work like call_func(print_hello). But preprocessor treated my code like call_func("print_hello"). How to use macro in C to make my exception? Or is it not possible using C?
Then problem with your code is that the value of func_name is only known at run-time.
You can however to it like this:
#define MAKE_FUNCNAME(FUNCNAME) FUNCNAME##hello
void call_func(void* (*func)(void))
{
func();
}
void *print_hello(void)
{
printf("print_hello called\n");
}
int main(void)
{
call_func(MAKE_FUNCNAME(print_));
return 0;
}
But it is not possible to use a string value within macro parameters like in your code snippet.
If you want to get call functions with their names using string values you can use a table to store function pointer with function names like this:
struct {
const char *name;
void (*ptr)(void);
};
You can use an array of this structure to find out the function pointer at run-time using a string value. This is the most common solution to using run-time strings to call functions using their names.
You can't do that. The value of func_name is known at run-time (even though it is a const char *), while you want to determine what to call at precompile-time. You should turn your cpp macro into something different (such as an if/switch statement or using an indirection).
Maybe you could have a look to dlsym().
Not sure I really understand the question, but if you want to "build" the function name at runtime and then call the corresponding function, it should be possible with dlsym()
/* compile with: gcc example.c -ldl -rdynamic */
#include <dlfcn.h>
#include <stdio.h>
int print_hello(void)
{
return printf("hello\n");
}
int main(int argc, char *argv[])
{
const char *name = "print_hello";
if (argc == 42)
print_hello(); /* for compiler not to remove print_hello at
* compile time optimisation in this example*/
void *handle = dlopen(NULL /* self */, RTLD_NOW);
int (*f)(void) = dlsym(handle, name);
f();
return dlclose(handle);
}

Is function pointer type in _Generic assoc-list not working as expected?

I was experimenting with "hacking" the type-system, by not restricting the function pointer argument to accept a function with a specific type of arguments. However, I still wanted to make it type-safe, so I thought I will combine this "hack" with the possibilities of the _Generic keyword.
I have the following four functions:
#include <stdio.h> /* printf() */
#include <stdlib.h> /* EXIT_SUCCESS */
static void
function_i(int *i)
{
printf("%d\n", *i);
}
static void
function_f(float *f)
{
printf("%.2ff\n", *f);
}
static void
caller(void(*func)(),
void *arg)
{
func(arg);
}
static void
except(void(*func)(),
void *arg)
{
printf("unsupported type\n");
}
The first and second will be passed to the third, and I want to make sure, if the type of the function and the argument passed to the third is not right, then the fourth function will be called. Therefore I created the following _Generic selector:
#define handler(func, arg) _Generic((func), \
void(*)(int*): _Generic((arg), \
int* : caller, \
default : except), \
void(*)(float*): _Generic((arg), \
float* : caller, \
default : except), \
default: except)(func, arg)
And then I called them:
int main(void)
{
int i = 12;
float f = 3.14f;
void(*func_ptr_i)(int*) = function_i;
void(*func_ptr_f)(float*) = function_f;
handler(function_i, &i);
handler(function_f, &f);
handler(func_ptr_i, &i);
handler(func_ptr_f, &f);
return EXIT_SUCCESS;
}
The output is very interesting:
unsupported type
unsupported type
12
3.14f
I expected this to work for the first two cases as well, without the need to create a specific function pointer variable for the passed functions. The question is: is this an implementation error in clang's _Generic, or this is the expected behavior? Is so, I'm very curious about why exactly? And how to make it work without creating extra function pointers?
Thanks in advance!
SYS-INFO:
compiler: Apple LLVM version 5.1 (clang-503.0.40) (based on LLVM 3.4svn)
flags: cc -std=c11 -Wall -v -g
The problem that you are facing is that the choice expression of _Generic is not evaluated. If it would be, your function names would decay to function pointers and everything would work.
Adding a & to your choice expression should fix that.

using #defines and passing functions to them

gcc 4.4.2 c89
I re-engineering some code in c89. However, I am totally confused with the code that uses the following #defines. So I created a small application that maybe I would understand more of how this is working.
From what I can gather the MODULE_API will pass a function name and call the macro MODULE_SOURCE_API and concatenate name and func. So I create a simple function called print_name and ran the code. I got the following error messages:
implicit declaration of function ‘print_name’
undefined reference to `print_name'
What would be the main reason for doing this?
#include <stdio.h>
#define MODULE_SOURCE_API(name, func) name##_##func
#define MODULE_API(func) MODULE_SOURCE_API(mod_print, func)
void MODULE_API(print_name)(const char const *name);
int main(void)
{
printf("=== Start program ===\n");
print_name("Joe bloggs");
printf("== End of program ===\n");
return 0;
}
void MODULE_API(print_name)(const char const *name)
{
printf("My name is [ %s ]\n", name);
}
Many thanks for any advice,
EDIT ====
I have just made a correction I should be calling
MODULE_API(print_name)("Joe Bloggs");
But how can I print out what will be the outcome of concatenating? And what is the reason for doing this?
Many thanks,
#define MODULE_SOURCE_API(name, func) name##_##func
#define MODULE_API(func) MODULE_SOURCE_API(mod_print, func)
void MODULE_API(print_name)(const char const *name);
That will be producing a function named mod_print_print_name instead of print_name
You can check it on gcc with the -E option.
gcc -E ak.c gives
/* ...... */
void mod_print_print_name(const char const *name);
int main(void)
{
printf("=== Start program ===\n");
print_name("Joe bloggs");
printf("== End of program ===\n");
return 0;
}
void mod_print_print_name(const char const *name)
{
printf("My name is [ %s ]\n", name);
}
You can try to manually expand the macros to understand what is going on:
void MODULE_API( print_name )( const char * name ); // the second const there is redundant
// maybe you meant 'const char * const??
=(expand MODULE_API)=>
void MODULE_SOURCE_API( mod_print, print_name )( const char* name );
=(expand MODULE_SOURCE_API)=>
void mod_print_print_name( const char *);
As you see, the function being declared (and defined at the end of the code) is not print_name, but rather mod_print_print_name. Go back to the initial code and see how the macro is intended to be used. I would assume that function calls are performed with the same macros that are used for declarations and definitions.

Resources