简体   繁体   中英

Python ctypes : OSError undefined symbol when loading library

In Ubuntu 14.04, I wrote a C file called hash.c:

/* hash.c: hash table with linear probing */

typedef struct {
    void *key;
    void *value;
} ht_entry;

typedef struct {
    ht_entry *table;
    int len;
    int num_entries;
    int (*hash_fn)(void *key);
    int (*key_cmp)(void *k1, void *k2);
} hashtable;

and compiled it with

gcc -shared hash.c -o test.so -fPIC

Afterwards, I tried to load test.so in a Python script (for testing), but I got the following error: "OSError: .../test.so: undefined symbol: hash_fn"

hash_fn is a function pointer in the hashtable struct. It is referenced a number of times by functions later in the file.

I do not understand why this error is happening. I have Googled but all other cases either concern C++ or includes. In my case I just have 1 C file that includes only stdio and stdlib.


here is the FULL code. When I comment out all but hash_create and print_info, it loads succesfully. When I uncomment find(), it the error happens. (print_info is just for testing that ctypes works)

/* hash.c: hash table with linear probing */

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

typedef struct {
    void *key;
    void *value;
} ht_entry;

typedef struct {
    ht_entry *table;
    int len;
    int num_entries;
    int (*hash_fn)(void *key);
    int (*key_cmp)(void *k1, void *k2);
} hashtable;

static void close_gap(hashtable *ht, int i);
static int find(hashtable *ht, void *key);

hashtable* hash_create(int len, int (*hash_fn)(void*), int (*key_cmp)(void*, void*))
{
    hashtable* ht = (hashtable*) malloc(sizeof(hashtable));
    ht->len = len;
    ht->table = calloc(len, sizeof(ht_entry));  
    ht->hash_fn = hash_fn;
    ht->key_cmp = key_cmp;
    ht->table[0].key = 2;
    ht->table[0].value = 3;
    return ht;
}

void print_info(hashtable *ht)
{
    printf("%d, %d, %d\n", ht->len, ht->table[0].key, ht->table[0].value);
}

void* hash_retrieve(hashtable* ht, void *key)
{
    int i = find(ht, key);
    if(i < 0) {
        return NULL;
    }

    return ht->table[i].value;
}

void hash_insert(hashtable* ht, void *key, void *value)
{
    if(ht->num_entries == ht->len) {
        return;
    }

    int i = hash_fn(key) % ht->len;
    while(ht->table[i].key != NULL) {
        i = (i + i) % ht->len;
    }
    ht->table[i].key = key;
    ht->table[i].value = value;
}

void hash_remove(hashtable *ht, void *key)
{
    int i = find(ht, key);
    if(i < 0) {
        return;
    }   
    ht->table[i].key = 0;
    ht->table[i].value = 0;
    close_gap(ht, i);
}

static int find(hashtable *ht, void *key)
{
    int i = hash_fn(key) % ht->len;
    int num_checked = 0;
    while(ht->table[i].key && num_checked != ht->len) {
        if(!ht->key_cmp(ht->table[i].key, key)) {
            return i;
        }
        num_checked++;
        i = (i + i) % ht->len;
    }
    return -1;
}

static void close_gap(hashtable *ht, int i)
{
    int j = (i + 1) % ht->len;
    while(ht->table[j].key) {
        int loc = ht->hash_fn(ht->table[j].key);
        if((j > i && (loc <= i || loc > j)) || (j < i && (loc <= i && loc > j))) {
            ht->table[i] = ht->table[j];
            ht->table[j].key = 0;
            ht->table[j].value = 0;
            close_gap(ht, j);
            return;
        }
    }
}

When I use your compilation line I get five warnings. There are several problems here. First you are trying to assign an int to void * in several places. That raises a warning, and it would crash at runtime because you are passing 2 and 3 as addresses.

Second, you are calling hash_fn in a couple of places instead of ht->hash_fn . That causes the linker error, but you should consider my other changes, otherwise it will crash at runtime with a SIGSEGV:

/* hash.c: hash table with linear probing */

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

typedef struct {
    void *key;
    void *value;
} ht_entry;

typedef struct {
    ht_entry *table;
    int len;
    int num_entries;
    int (*hash_fn)(void *key);
    int (*key_cmp)(void *k1, void *k2);
} hashtable;

static void close_gap(hashtable *ht, int i);
static int find(hashtable *ht, void *key);


hashtable* hash_create(int len, int (*hash_fn)(void*), int (*key_cmp)(void*, void*))
{
    hashtable* ht = (hashtable*) malloc(sizeof(hashtable));
    ht->len = len;
    ht->table = calloc(len, sizeof(ht_entry));
    ht->hash_fn = hash_fn;
    ht->key_cmp = key_cmp;

    // <<< Code changed here
    /*
    ht->table[0].key = 2;
    ht->table[0].value = 3;
    */

    {
        int *p = malloc(sizeof(int));
        *p = 2;
        ht->table[0].key = p;

        p = malloc(sizeof(int));
        *p = 3;
        ht->table[0].value = p;
    }
    // end of code change   

    return ht;
}

void print_info(hashtable *ht)
{
    // <<<<  Code changed
    printf("%d, %d, %d\n", ht->len,
           *(int *)ht->table[0].key, *(int *)ht->table[0].value);
}

void* hash_retrieve(hashtable* ht, void *key)
{
    int i = find(ht, key);
    if(i < 0) {
        return NULL;
    }

    return ht->table[i].value;
}

void hash_insert(hashtable* ht, void *key, void *value)
{
    if(ht->num_entries == ht->len) {
        return;
    }

    // <<<  Code changed
    int i = ht->hash_fn(key) % ht->len;

    while(ht->table[i].key != NULL) {
        i = (i + i) % ht->len;
    }
    ht->table[i].key = key;
    ht->table[i].value = value;
}

void hash_remove(hashtable *ht, void *key)
{
    int i = find(ht, key);
    if(i < 0) {
        return;
   ht->table[i].key = 0;
    ht->table[i].value = 0;
    close_gap(ht, i);
}

static int find(hashtable *ht, void *key)
{
    // <<<  Code changed
    int i = ht->hash_fn(key) % ht->len;

    int num_checked = 0;
    while(ht->table[i].key && num_checked != ht->len) {
        if(!ht->key_cmp(ht->table[i].key, key)) {
            return i;
        }
        num_checked++;
        i = (i + i) % ht->len;
    }
    return -1;
}


static void close_gap(hashtable *ht, int i)
{
    int j = (i + 1) % ht->len;
    while(ht->table[j].key) {
        int loc = ht->hash_fn(ht->table[j].key);
        if((j > i && (loc <= i || loc > j)) || (j < i && (loc <= i && loc > j))) {
            ht->table[i] = ht->table[j];
            ht->table[j].key = 0;
            ht->table[j].value = 0;
            close_gap(ht, j);
            return;
        }
    }
}

I only coded around the errors and warnings, I did not check the logic. You will see that I have used malloc to allocate memory for key and value . Obviously you will need memory management on these two (ie free() ).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM