0

This is tricky (at least to me :-) , maybe unfeasible. But I try to ask to you.

I have this c shared library:

#include <stdio.h>
#include <stdlib.h>

static int variable = -666;

int get_value() {
    return variable;
}

void print_pointer_to_get_value() {
    printf("pointer_to_get_value: %p\n", &get_value);
}

Compiled this way (on Linux):

gcc -fPIC -c -O2 shared.c && gcc -shared -o shared.so shared.o

Now I load the library and call print_pointer_to_get_value():

>>> import ctypes
>>> so = ctypes.cdll.LoadLibrary('./shared.so')
>>> so.print_pointer_to_get_value()
pointer_to_get_value: 0x7f46e178f700

I'd like to get from ctypes the actual address, as integer, of the get_value function as printed by print_pointer_to_get_value(). My final target is to move that address to a Cython module and call that function inside a "nogil" Cython function. I need to load the .so library at runtime, therefore I cannot compile my Cython module linking it to the library.

Thanks 1000.

user6369958
  • 357
  • 3
  • 16

1 Answers1

1

It's a nasty multistep process that isn't easy to do elegantly:

Some Cython code:

ctypedef double (*math_function_t)(double) nogil

import ctypes

def call_f(f, double x):
    cdef math_function_t cy_f_ptr = (<math_function_t*><size_t>ctypes.addressof(f))[0]

    cdef double res
    with nogil:
        res = cy_f_ptr(x)
    return res

Here I pass Cython a Ctypes function type (f) and get the address in Cython. I don't think it's possible to obtain the address in Python. As an example of how you might initialise f, on Linux you could do:

lib = ctypes.cdll.LoadLibrary("libm.so.6")
f = lib.sin

call_f(f,0.5) # returns sin(0.5)

(to use the standard library sin function).

The Cython line cdef math_function_t cy_f_ptr = (<math_function_t*><size_t>ctypes.addressof(f))[0] can be broken down as follows:

  1. ctypes.addressof(f) gets the address that the ctypes variable f is held in. __This is NOT the value you're after_ - it's the place where the value you're after is stored.
  2. This is cast first to a size_t integer then to a pointer to a cdef function pointer type. Cython requires the two step cast.
  3. [0] dereferences your math_function_t* to get a math_function_t. This is the function pointer (i.e. the value you want)

The information for this answer was got from this newsgroup thread (which I can't currently access)

DavidW
  • 29,336
  • 6
  • 55
  • 86
  • That's sooooooooo perfect! You even told me why `ctypes.addressof()` gives a different pointer. One more question: why ``? I'm not asking about the necessity of this cast but why size_t and not some other type. – user6369958 Apr 04 '18 at 06:11
  • 1
    It needs to be a big enough int to hold a pointer. Practically size_t does that (although it isn't guaranteed...). Although I think `uintptr_t` would actually have been the right thing to use – DavidW Apr 04 '18 at 06:20