【问题标题】:undefined symbol error when loading a module, using AWS C++ SDK, in Freeswitch在 Freeswitch 中使用 AWS C++ SDK 加载模块时出现未定义符号错误
【发布时间】:2019-12-09 13:31:03
【问题描述】:

我再次问这个问题,因为模组决定在被问到的几分钟内将我的问题here 关闭为重复项(并且还被否决了!!)。现在我已经浏览了所有33 answers 被认为是我的解决方案的答案,但它没有帮助。所以我又问了。

我正在尝试构建一个 FreeSWITCH 模块,以使用 AWS Polly 和 AWS C++ SDK 添加文本转语音功能。 开发环境是 Debian 8,g++ 4.9.2。 AWS C++ SDK 是使用此处的说明构建的,但我关闭了共享库(生成 .a 库文件)。

AWS C++ 开发工具包是按照推荐的 here 构建的(基本上是带有 C++ 链接的 C++ 代码)。 mod_polly.cpp 也使用 C++ 链接构建以生成 mod_polly.so。它确实引用了一些 C 头文件和函数。这是构建为 -

g++ -shared -o mod_polly.so -L/usr/local/lib/ -laws-cpp-sdk-polly -laws-cpp-sdk-core -fPIC -g -ggdb -std=c++11 -墙 -Werror -I/usr/src/freeswitch/src/include/ -I/usr/src/freeswitch/libs/libteletone/src/mod_polly.cpp

以下来源-


extern "C" {
#include <switch.h>
}


#include <fstream>

#define BIG_ENDIAN_SYSTEM (*(uint16_t *)"\0\xff" < 0x100)

#define REVERSE_BYTES(...) do for(size_t REVERSE_BYTES=0; REVERSE_BYTES<sizeof(__VA_ARGS__)>>1; ++REVERSE_BYTES)\
    ((unsigned char*)&(__VA_ARGS__))[REVERSE_BYTES] ^= ((unsigned char*)&(__VA_ARGS__))[sizeof(__VA_ARGS__)-1-REVERSE_BYTES],\
    ((unsigned char*)&(__VA_ARGS__))[sizeof(__VA_ARGS__)-1-REVERSE_BYTES] ^= ((unsigned char*)&(__VA_ARGS__))[REVERSE_BYTES],\
    ((unsigned char*)&(__VA_ARGS__))[REVERSE_BYTES] ^= ((unsigned char*)&(__VA_ARGS__))[sizeof(__VA_ARGS__)-1-REVERSE_BYTES];\
while(0)

#include <aws/core/Aws.h>
#include <aws/core/auth/AWSCredentials.h>
#include <aws/core/client/ClientConfiguration.h>
#include <aws/core/utils/Outcome.h>
#include <aws/polly/PollyClient.h>
#include <aws/polly/model/SynthesizeSpeechRequest.h>
#include <aws/polly/model/SynthesizeSpeechResult.h>
#include <aws/polly/model/TextType.h>
#include <aws/polly/model/LanguageCode.h>
#include <aws/polly/model/OutputFormat.h>
#include <aws/polly/model/VoiceId.h>

typedef unsigned long DWORD;    // 32-bit unsigned integer
typedef unsigned short WORD;    // 16-bit unsigned integer

struct riff                                 // Data             Bytes   Total
{
    char            chunkID[4];             // "RIFF"           4       4
    DWORD           riffSize;               // file size - 8    4       8
    char            typeID[4];              // "WAVE"           4       12
    char            formatChunkID[4];       // "fmt "           4       16
    DWORD           formatChunkSize;        // 16 bytes         4       20          
    WORD            formatTag;              //                  2       22
    WORD            noOfChannels;           //                  2       24
    DWORD           samplesPerSec;          //                  4       28
    DWORD           bytesPerSec;            //                  4       32
    WORD            blockAlign;             //                  2       34
    WORD            bitsPerSample;          //                  2       36
    char            dataChunkID[4];         // "data"           4       40
    DWORD           dataChunkSize;          // not fixed        4       44
};

static struct {
    switch_mutex_t *mutex;
    switch_thread_rwlock_t *running_rwlock;
    switch_memory_pool_t *pool;
    int running;
} process;

static struct {
    Aws::Auth::AWSCredentials *credentials;
    Aws::Client::ClientConfiguration *config;
    Aws::SDKOptions *options;
} globals;

switch_loadable_module_interface_t *MODULE_INTERFACE;

static char *supported_formats[SWITCH_MAX_CODECS] = { 0 };

/* Prototypes */

SWITCH_MODULE_LOAD_FUNCTION(mod_polly_load);
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_polly_shutdown);
SWITCH_MODULE_DEFINITION(mod_polly, mod_polly_load, mod_polly_shutdown, NULL);

// ------------------------------------------------------------------------------------------------------------------

/* Implementation */

std::ostream& operator<<(std::ostream& out, const riff& h)
{
    if BIG_ENDIAN_SYSTEM {
        struct riff hdr = std::move(h);

        REVERSE_BYTES(hdr.riffSize);
        REVERSE_BYTES(hdr.formatChunkSize);
        REVERSE_BYTES(hdr.formatTag);
        REVERSE_BYTES(hdr.noOfChannels);
        REVERSE_BYTES(hdr.samplesPerSec);
        REVERSE_BYTES(hdr.bytesPerSec);
        REVERSE_BYTES(hdr.blockAlign);
        REVERSE_BYTES(hdr.bitsPerSample);
        REVERSE_BYTES(hdr.dataChunkSize);

        return out 
            .write(hdr.chunkID, 4)
            .write((const char *)&hdr.riffSize, 4)
            .write(hdr.typeID, 4)
            .write(hdr.formatChunkID, 4)
            .write((const char *)&hdr.formatChunkSize, 4)
            .write((const char *)&hdr.formatTag, 2)
            .write((const char *)&hdr.noOfChannels, 2)
            .write((const char *)&hdr.samplesPerSec, 4)
            .write((const char *)&hdr.bytesPerSec, 4)
            .write((const char *)&hdr.blockAlign, 2)
            .write((const char *)&hdr.bitsPerSample, 2)
            .write(hdr.dataChunkID, 4)
            .write((const char *)&hdr.dataChunkSize, 4);
    } else {
        return out
            .write(h.chunkID, 4)
            .write((const char *)&h.riffSize, 4)
            .write(h.typeID, 4)
            .write(h.formatChunkID, 4)
            .write((const char *)&h.formatChunkSize, 4)
            .write((const char *)&h.formatTag, 2)
            .write((const char *)&h.noOfChannels, 2)
            .write((const char *)&h.samplesPerSec, 4)
            .write((const char *)&h.bytesPerSec, 4)
            .write((const char *)&h.blockAlign, 2)
            .write((const char *)&h.bitsPerSample, 2)
            .write(h.dataChunkID, 4)
            .write((const char *)&h.dataChunkSize, 4);
    }
}

riff init_pcm_header(std::ostream& in)
{
    // get length of file
    in.seekp(0, in.end);
    DWORD sz = in.tellp();
    in.seekp(0, in.beg);

    struct riff result = {
        {'R','I','F','F'},      // chunkID
        sz + 0x24,              // riffSize         (size of stream + 0x24) or (file size - 8)
        {'W','A','V','E'},      // typeID
        {'f','m','t',' '},      // formatChunkID
        16,                     // formatChunkSize
        1,                      // formatTag        (PCM)
        1,                      // noOfChannels     (mono)
        8000,                   // samplesPerSec    (8KHz)
        16000,                  // bytesPerSec      ((Sample Rate * BitsPerSample * Channels) / 8)
        2,                      // blockAlign       ((bits per sample * channels) / 8)
        16,                     // bitsPerSample    (multiples of 8)
        {'d','a','t','a'},      // dataChunkID
        sz                      // dataChunkSize    (sample size)   
    };

    return result;
}

struct voice_sync {
    char* session_uuid;
    Aws::IOStream *audio_stream;
    switch_size_t blockAlign;
};

typedef struct voice_sync voice_sync_t;

static switch_status_t polly_file_open(switch_file_handle_t *handle, const char *path)
{
    voice_sync_t *sync_info = (voice_sync_t*)malloc(sizeof(voice_sync_t));
    sync_info->audio_stream = new Aws::StringStream(std::ios::in | std::ios::out | std::ios::binary);

    handle->private_info = sync_info;
    handle->samplerate = 8000;
    handle->channels = 1;
    handle->pos = 0;
    handle->format = 0;
    handle->sections = 0;
    handle->seekable = 0;
    handle->speed = 0.5;

    switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "submitting text [%s] to polly", path);

    Aws::Polly::PollyClient polly_client(*globals.credentials, *globals.config);
    Aws::Polly::Model::SynthesizeSpeechRequest request;

    request.SetLanguageCode(Aws::Polly::Model::LanguageCode::en_US);
    request.SetOutputFormat(Aws::Polly::Model::OutputFormat::pcm);
    request.SetSampleRate("8000");
    request.SetTextType(Aws::Polly::Model::TextType::text);  // or ssml
    request.SetVoiceId(Aws::Polly::Model::VoiceId::Matthew);
    request.SetText(path);

    if (handle->params) {
        // get the session UUID for this channel
        // note: this doesnt fire for a standard call session in the audio context; is there a way to make sure it is there?
        const char *uuid = switch_event_get_header(handle->params, "session");
        if (!zstr(uuid)) {
            sync_info->session_uuid = switch_core_strdup(handle->memory_pool, uuid);
            switch_log_printf(SWITCH_CHANNEL_UUID_LOG(sync_info->session_uuid), SWITCH_LOG_DEBUG, "Polly linked to session %s\n", sync_info->session_uuid);
        }
    }
    sync_info->audio_stream->clear();
//    sync_info->audio_stream.open(filename.c_str(), std::ios::out | std::ios::binary);

    auto outcome = polly_client.SynthesizeSpeech(request);

    // Output operation status
    if (outcome.IsSuccess()) {
        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "received audio response for %s", request.GetServiceRequestName());

        Aws::Polly::Model::SynthesizeSpeechResult& result = ((Aws::Polly::Model::SynthesizeSpeechResult&)(outcome));
        Aws::IOStream*  audio_stream = &result.GetAudioStream();

        // this is raw PCM so we need to add a wav header!
        riff header = init_pcm_header(*audio_stream);
        *sync_info->audio_stream << header;

        // tansfer audio data into stream
        *sync_info->audio_stream << audio_stream->rdbuf();
        sync_info->audio_stream->seekp(0, sync_info->audio_stream->beg);

        // update handle information about audio stream
        handle->samplerate = header.samplesPerSec;
        handle->channels = header.noOfChannels;
        handle->format = header.formatTag;
        handle->duration = header.dataChunkSize / header.bytesPerSec +1;
        handle->samples_in = header.dataChunkSize / header.blockAlign +1;

        sync_info->blockAlign = header.blockAlign;

        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "polly audio stream ready; duration: %ld secs", handle->duration);
        return SWITCH_STATUS_SUCCESS;
    }

    switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "something went wrong retrieving audio from polly");
    return SWITCH_STATUS_FALSE;
}

static switch_status_t polly_file_close(switch_file_handle_t *handle)
{
    switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "closiing polly audio stream");

    voice_sync_t *sync_info = (voice_sync_t*)handle->private_info;

    //sync_info->audio_stream->close(); -- doesnt exist on stringstream
    delete sync_info->audio_stream;

    if (sync_info->session_uuid) {
        switch_safe_free(sync_info->session_uuid);
    }

    switch_safe_free(sync_info);
    handle->private_info = NULL;

    return SWITCH_STATUS_SUCCESS;
}

static switch_status_t polly_file_read(switch_file_handle_t *handle, void *data, size_t *len)
{
    voice_sync_t *sync_info = (voice_sync_t*)handle->private_info;
    switch_size_t bytes;

    sync_info->audio_stream->read((char *)data, *len * sync_info->blockAlign);
    if ((bytes = sync_info->audio_stream->gcount()) <= 0) {
        return SWITCH_STATUS_FALSE;
    }

    *len = bytes / sync_info->blockAlign;
    return SWITCH_STATUS_SUCCESS;
}

SWITCH_MODULE_LOAD_FUNCTION(mod_polly_load)
{
    switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Initializing polly audio interface");

    supported_formats[0] = (char*)"polly";

    /*
        switch_application_interface_t *app_interface;
        switch_api_interface_t *api_interface;
    */
    switch_file_interface_t *file_interface;

    *module_interface = switch_loadable_module_create_module_interface(pool, modname);
    file_interface = (switch_file_interface_t*)switch_loadable_module_create_interface(*module_interface, SWITCH_FILE_INTERFACE);
    file_interface->interface_name = modname;
    file_interface->extens = supported_formats;
    file_interface->file_open = polly_file_open;
    file_interface->file_close = polly_file_close;
    file_interface->file_read = polly_file_read;

    MODULE_INTERFACE = *module_interface;

    memset(&process, 0, sizeof(process));
    memset(&globals, 0, sizeof(globals));
    process.pool = pool;

    switch_thread_rwlock_create(&process.running_rwlock, pool);
    switch_mutex_init(&process.mutex, SWITCH_MUTEX_NESTED, pool);

    globals.options = new Aws::SDKOptions();
    globals.options->loggingOptions.logLevel = Aws::Utils::Logging::LogLevel::Debug;

    globals.credentials = new Aws::Auth::AWSCredentials();
    globals.credentials->SetAWSAccessKeyId("your aws key");
    globals.credentials->SetAWSSecretKey("your aws secret");

    globals.config = new Aws::Client::ClientConfiguration();
    globals.config->region = "eu-west-1";  // Ireland

    switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Initializing aws api");

    Aws::InitAPI(*globals.options);

    switch_thread_rwlock_wrlock(process.running_rwlock);
    process.running = 1;
    switch_thread_rwlock_unlock(process.running_rwlock);

    switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Ready to rock!");

    /* indicate that the module should continue to be loaded */
    return SWITCH_STATUS_SUCCESS;
}

SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_polly_shutdown)
{   
    switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Shutting down polly polly audio interface");

    switch_thread_rwlock_wrlock(process.running_rwlock);
    process.running = 0;
    switch_thread_rwlock_unlock(process.running_rwlock);

    switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Closing aws api");
    Aws::ShutdownAPI(*globals.options);

    delete globals.credentials;
    delete globals.config;
    delete globals.options;

    switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Module shutdown finished");
    return SWITCH_STATUS_UNLOAD;
}

现在当我尝试在 Freeswitch 上加载它时,它会引发错误

2019-07-31 22:00:51.918181 [CRIT] switch_loadable_module.c:1522 加载模块 /usr/local/freeswitch/mod/mod_polly.so 时出错

/usr/local/freeswitch/mod/mod_polly.so:未定义符号:_ZNK3Aws35AmazonSerializableWebServiceRequest7GetBodyEv

Freeswitch 是在头文件中带有 C++ 保护的 C 代码(外部“C”声明)。

查看mod_polly.so中的符号

readelf -Ws mod_polly.so | grep _ZNK3Aws35AmazonSerializableWebServiceRequest7GetBodyEv

66: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _ZNK3Aws35AmazonSerializableWebServiceRequest7GetBodyEv

590: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _ZNK3Aws35AmazonSerializableWebServiceRequest7GetBodyEv

现在我对帖子here 的基本理解告诉我,该符号存在于so 文件中,但Freeswitch 找不到或加载它。

现在这个错误很可能与混合 C/C++ 代码有关,但查看 thisthis 并没有帮助我弄清楚如何修复它。

我不想构建 Freeswitch 来加载我的模块,我想我不应该这样做,因为这会使这个项目无法扩展。

我在这里错过了什么?

PS:

readelf -Ws libaws-cpp-sdk-core.a | grep AmazonSerializableWebServiceRequest7GetBodyEv

165: 0000000000000000 716 FUNC 全局默认值 42 _ZNK3Aws35AmazonSerializableWebServiceRequest7GetBodyEv

符号在libaws-cpp-sdk-core.a 中定义,这是mod_polly.cpp 的编译命令的一部分

【问题讨论】:

  • 不,.so 中不存在该符号,您看到的是引用本身。你必须弄清楚哪个其他共享库定义了这个符号,并确保你链接到它。
  • 它在libaws-cpp-sdk-core.a 中,包含在构建命令中。添加有问题的信息
  • 你只需要弄清楚这一点,因为只有你会知道它们是否可能是安装的重复库,或者相同静态库的共享库版本,从而导致混淆关联。你说“符号是在 libaws-cpp-sdk-core.a 中定义的”,但它是 libaws-cpp-sdk-core.a 其中-L/usr/local/lib 指示链接器进行搜索。诸如此类的小细节很重要,只有您拥有系统上工作方式的所有详细信息。
  • 是的,/usr/local/lib 是图书馆所在的位置。我知道我必须这样做,但您的第一条评论很有帮助。至少我现在知道我错误地读取了readelf 输出,所以也许它只是在构建时对 lib 文件进行排序。
  • 这个.a文件的构建过程是否包括运行ranlib?如果没有,那就是缺少的。

标签: c++ freeswitch amazon-polly aws-sdk-cpp


【解决方案1】:

@Sam V - 原来是构建时的订购问题。将构建命令的顺序更改为

g++ -shared -o mod_polly.so -fPIC -g -ggdb -std=c++11 -Wall -Werror -I/usr/src/freeswitch/src/include/ -I/usr/src/freeswitch/ libs/libteletone/src/ mod_polly.cpp -L/usr/local/lib/ -laws-cpp-sdk-polly -laws-cpp-sdk-core

解决了这个问题。您的第一条评论是关键。谢谢。

【讨论】:

  • 你能构建一个工作的 mod_polly 模块吗?我尝试使用 AWS C++ SDK 并不断遇到内存损坏错误。必须小心地将 C++ 对象转换为 C 对象并返回,这很痛苦,而且可能容易出错。
猜你喜欢
  • 2014-02-13
  • 1970-01-01
  • 2012-10-13
  • 1970-01-01
  • 2016-10-16
  • 1970-01-01
  • 2020-03-20
  • 2012-04-07
  • 2020-09-30
相关资源
最近更新 更多