【问题标题】:Python ctypes : OSError undefined symbol when loading libraryPython ctypes:加载库时出现OSError未定义符号
【发布时间】:2015-07-06 16:07:58
【问题描述】:

在 Ubuntu 14.04 中,我编写了一个名为 hash.c 的 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;

并用它编译

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

之后,我尝试在 Python 脚本中加载 test.so(用于测试),但出现以下错误:“OSError: .../test.so: undefined symbol: hash_fn”

hash_fn 是哈希表结构中的函数指针。文件后面的函数多次引用它。

我不明白为什么会发生此错误。我已经用 Google 搜索过,但所有其他情况都涉及 C++ 或包含。就我而言,我只有 1 个仅包含 stdio 和 stdlib 的 C 文件。


这里是完整的代码。 当我注释掉除了 hash_create 和 print_info 之外的所有内容时,它会成功加载。当我取消注释 find() 时,就会发生错误。 (print_info 仅用于测试 ctypes 是否有效)

/* 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;
        }
    }
}

【问题讨论】:

  • 看起来hash_fn 应该是一个全局函数。结构成员名称不会作为符号编译到可执行文件中。
  • 我们确实需要查看错误来自的代码行。你在哪里以及如何打电话给hash_fn
  • @cdarke 完成。见 OP。

标签: python c ctypes


【解决方案1】:

当我使用您的编译行时,我收到五个警告。这里有几个问题。首先,您尝试在多个位置将int 分配给void *。这会引发警告,它会在运行时崩溃,因为您将 2 和 3 作为地址传递。

其次,你在几个地方打电话给hash_fn,而不是ht-&gt;hash_fn。这会导致链接器错误,但您应该考虑我的其他更改,否则它将在运行时崩溃并出现 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;
        }
    }
}

我只围绕错误和警告进行编码,我没有检查逻辑。你会看到我使用mallockeyvalue 分配内存。显然,您需要对这两个进行内存管理(即free())。

【讨论】:

  • 非常感谢您的修复。我没有想到 -shared 编译不能解析所有符号,这就是为什么当我引用 hash_fn 而不是 ht->hash_fn 时 gcc 没有导致错误的原因。所以直到你指出来我才注意到它
  • @user1299784:它在我在 OS X 上使用的 gcc 上给出了链接器错误。
  • 双重检查,在 Ubuntu 14.04 上没有错误。我使用了“gcc -shared hash.c -o test.so -fPIC”——这些是为 ctypes 创建共享库的正确设置吗?
  • @user1299784:是的,我使用的完全一样,我复制并粘贴了你的命令行。它可能只是 gcc 的不同版本。顺便说一句,我还添加了-Wall 以显示所有警告。
  • @user1299784:你愿意接受我的回答吗?
猜你喜欢
  • 1970-01-01
  • 2021-12-13
  • 1970-01-01
  • 1970-01-01
  • 2012-04-07
  • 1970-01-01
  • 2012-01-08
  • 1970-01-01
相关资源
最近更新 更多