title: undefined symbol error when loading a module, using AWS C++ SDK, in Freeswitch
body: <p>I am asking this question again as the mods decided to close my question <a href="https://stackoverflow.com/questions/57297465/undefined-symbol-znk3aws35amazonserializablewebservicerequest7getbodyev-tr">here</a> as a duplicate, within minutes of it being asked (and also down-voted!!). Now I have gone through all <a href="https://stackoverflow.com/questions/12573816/what-is-an-undefined-reference-unresolved-external-symbol-error-and-how-do-i-fix">33 answers</a> of what was thought to be an answer to my solution, but it didn't help. So I am asking again.</p>
  <blockquote>
  <p>I am trying to build a FreeSWITCH module to add text-to-speech functionality using AWS Polly &amp; the AWS C++ SDK.
  Dev environment is Debian 8, g++ 4.9.2. AWS C++ SDK is built using instructions here except that I turned off shared libs (produces .a lib files).</p>
  </blockquote>
  <p>The AWS C++ SDK was built as recommended <a href="https://github.com/aws/aws-sdk-cpp/wiki/Building-the-SDK-from-source-on-EC2" rel="nofollow noreferrer">here</a> (basically C++ code with C++ linkage). <code>mod_polly.cpp</code> is built with C++ linkage as well to produce <code>mod_polly.so</code>. It does refer to some C headers &amp; functions. This was built as -</p>
  <blockquote>
  <p>g++ -shared -o mod_polly.so -L/usr/local/lib/ -laws-cpp-sdk-polly -laws-cpp-sdk-core -fPIC -g -ggdb -std=c++11 -Wall -Werror -I/usr/src/freeswitch/src/include/ -I/usr/src/freeswitch/libs/libteletone/src/ mod_polly.cpp</p>
  </blockquote>
  <p>Source below -</p>
  <pre><code>
  extern &quot;C&quot; {
  #include &lt;switch.h&gt;
  }
  
  
  #include &lt;fstream&gt;
  
  #define BIG_ENDIAN_SYSTEM (*(uint16_t *)&quot;\0\xff&quot; &lt; 0x100)
  
  #define REVERSE_BYTES(...) do for(size_t REVERSE_BYTES=0; REVERSE_BYTES&lt;sizeof(__VA_ARGS__)&gt;&gt;1; ++REVERSE_BYTES)\
      ((unsigned char*)&amp;(__VA_ARGS__))[REVERSE_BYTES] ^= ((unsigned char*)&amp;(__VA_ARGS__))[sizeof(__VA_ARGS__)-1-REVERSE_BYTES],\
      ((unsigned char*)&amp;(__VA_ARGS__))[sizeof(__VA_ARGS__)-1-REVERSE_BYTES] ^= ((unsigned char*)&amp;(__VA_ARGS__))[REVERSE_BYTES],\
      ((unsigned char*)&amp;(__VA_ARGS__))[REVERSE_BYTES] ^= ((unsigned char*)&amp;(__VA_ARGS__))[sizeof(__VA_ARGS__)-1-REVERSE_BYTES];\
  while(0)
  
  #include &lt;aws/core/Aws.h&gt;
  #include &lt;aws/core/auth/AWSCredentials.h&gt;
  #include &lt;aws/core/client/ClientConfiguration.h&gt;
  #include &lt;aws/core/utils/Outcome.h&gt;
  #include &lt;aws/polly/PollyClient.h&gt;
  #include &lt;aws/polly/model/SynthesizeSpeechRequest.h&gt;
  #include &lt;aws/polly/model/SynthesizeSpeechResult.h&gt;
  #include &lt;aws/polly/model/TextType.h&gt;
  #include &lt;aws/polly/model/LanguageCode.h&gt;
  #include &lt;aws/polly/model/OutputFormat.h&gt;
  #include &lt;aws/polly/model/VoiceId.h&gt;
  
  typedef unsigned long DWORD;    // 32-bit unsigned integer
  typedef unsigned short WORD;    // 16-bit unsigned integer
  
  struct riff                                 // Data             Bytes   Total
  {
      char            chunkID[4];             // &quot;RIFF&quot;           4       4
      DWORD           riffSize;               // file size - 8    4       8
      char            typeID[4];              // &quot;WAVE&quot;           4       12
      char            formatChunkID[4];       // &quot;fmt &quot;           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];         // &quot;data&quot;           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&amp; operator&lt;&lt;(std::ostream&amp; out, const riff&amp; 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 *)&amp;hdr.riffSize, 4)
              .write(hdr.typeID, 4)
              .write(hdr.formatChunkID, 4)
              .write((const char *)&amp;hdr.formatChunkSize, 4)
              .write((const char *)&amp;hdr.formatTag, 2)
              .write((const char *)&amp;hdr.noOfChannels, 2)
              .write((const char *)&amp;hdr.samplesPerSec, 4)
              .write((const char *)&amp;hdr.bytesPerSec, 4)
              .write((const char *)&amp;hdr.blockAlign, 2)
              .write((const char *)&amp;hdr.bitsPerSample, 2)
              .write(hdr.dataChunkID, 4)
              .write((const char *)&amp;hdr.dataChunkSize, 4);
      } else {
          return out
              .write(h.chunkID, 4)
              .write((const char *)&amp;h.riffSize, 4)
              .write(h.typeID, 4)
              .write(h.formatChunkID, 4)
              .write((const char *)&amp;h.formatChunkSize, 4)
              .write((const char *)&amp;h.formatTag, 2)
              .write((const char *)&amp;h.noOfChannels, 2)
              .write((const char *)&amp;h.samplesPerSec, 4)
              .write((const char *)&amp;h.bytesPerSec, 4)
              .write((const char *)&amp;h.blockAlign, 2)
              .write((const char *)&amp;h.bitsPerSample, 2)
              .write(h.dataChunkID, 4)
              .write((const char *)&amp;h.dataChunkSize, 4);
      }
  }
  
  riff init_pcm_header(std::ostream&amp; 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-&gt;audio_stream = new Aws::StringStream(std::ios::in | std::ios::out | std::ios::binary);
  
      handle-&gt;private_info = sync_info;
      handle-&gt;samplerate = 8000;
      handle-&gt;channels = 1;
      handle-&gt;pos = 0;
      handle-&gt;format = 0;
      handle-&gt;sections = 0;
      handle-&gt;seekable = 0;
      handle-&gt;speed = 0.5;
  
      switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, &quot;submitting text [%s] to polly&quot;, 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(&quot;8000&quot;);
      request.SetTextType(Aws::Polly::Model::TextType::text);  // or ssml
      request.SetVoiceId(Aws::Polly::Model::VoiceId::Matthew);
      request.SetText(path);
  
      if (handle-&gt;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-&gt;params, &quot;session&quot;);
          if (!zstr(uuid)) {
              sync_info-&gt;session_uuid = switch_core_strdup(handle-&gt;memory_pool, uuid);
              switch_log_printf(SWITCH_CHANNEL_UUID_LOG(sync_info-&gt;session_uuid), SWITCH_LOG_DEBUG, &quot;Polly linked to session %s\n&quot;, sync_info-&gt;session_uuid);
          }
      }
      sync_info-&gt;audio_stream-&gt;clear();
  //    sync_info-&gt;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, &quot;received audio response for %s&quot;, request.GetServiceRequestName());
  
          Aws::Polly::Model::SynthesizeSpeechResult&amp; result = ((Aws::Polly::Model::SynthesizeSpeechResult&amp;)(outcome));
          Aws::IOStream*  audio_stream = &amp;result.GetAudioStream();
  
          // this is raw PCM so we need to add a wav header!
          riff header = init_pcm_header(*audio_stream);
          *sync_info-&gt;audio_stream &lt;&lt; header;
  
          // tansfer audio data into stream
          *sync_info-&gt;audio_stream &lt;&lt; audio_stream-&gt;rdbuf();
          sync_info-&gt;audio_stream-&gt;seekp(0, sync_info-&gt;audio_stream-&gt;beg);
  
          // update handle information about audio stream
          handle-&gt;samplerate = header.samplesPerSec;
          handle-&gt;channels = header.noOfChannels;
          handle-&gt;format = header.formatTag;
          handle-&gt;duration = header.dataChunkSize / header.bytesPerSec +1;
          handle-&gt;samples_in = header.dataChunkSize / header.blockAlign +1;
  
          sync_info-&gt;blockAlign = header.blockAlign;
  
          switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, &quot;polly audio stream ready; duration: %ld secs&quot;, handle-&gt;duration);
          return SWITCH_STATUS_SUCCESS;
      }
  
      switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, &quot;something went wrong retrieving audio from polly&quot;);
      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, &quot;closiing polly audio stream&quot;);
  
      voice_sync_t *sync_info = (voice_sync_t*)handle-&gt;private_info;
  
      //sync_info-&gt;audio_stream-&gt;close(); -- doesnt exist on stringstream
      delete sync_info-&gt;audio_stream;
  
      if (sync_info-&gt;session_uuid) {
          switch_safe_free(sync_info-&gt;session_uuid);
      }
  
      switch_safe_free(sync_info);
      handle-&gt;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-&gt;private_info;
      switch_size_t bytes;
  
      sync_info-&gt;audio_stream-&gt;read((char *)data, *len * sync_info-&gt;blockAlign);
      if ((bytes = sync_info-&gt;audio_stream-&gt;gcount()) &lt;= 0) {
          return SWITCH_STATUS_FALSE;
      }
  
      *len = bytes / sync_info-&gt;blockAlign;
      return SWITCH_STATUS_SUCCESS;
  }
  
  SWITCH_MODULE_LOAD_FUNCTION(mod_polly_load)
  {
      switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, &quot;Initializing polly audio interface&quot;);
  
      supported_formats[0] = (char*)&quot;polly&quot;;
  
      /*
          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-&gt;interface_name = modname;
      file_interface-&gt;extens = supported_formats;
      file_interface-&gt;file_open = polly_file_open;
      file_interface-&gt;file_close = polly_file_close;
      file_interface-&gt;file_read = polly_file_read;
  
      MODULE_INTERFACE = *module_interface;
  
      memset(&amp;process, 0, sizeof(process));
      memset(&amp;globals, 0, sizeof(globals));
      process.pool = pool;
  
      switch_thread_rwlock_create(&amp;process.running_rwlock, pool);
      switch_mutex_init(&amp;process.mutex, SWITCH_MUTEX_NESTED, pool);
  
      globals.options = new Aws::SDKOptions();
      globals.options-&gt;loggingOptions.logLevel = Aws::Utils::Logging::LogLevel::Debug;
  
      globals.credentials = new Aws::Auth::AWSCredentials();
      globals.credentials-&gt;SetAWSAccessKeyId(&quot;your aws key&quot;);
      globals.credentials-&gt;SetAWSSecretKey(&quot;your aws secret&quot;);
  
      globals.config = new Aws::Client::ClientConfiguration();
      globals.config-&gt;region = &quot;eu-west-1&quot;;  // Ireland
  
      switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, &quot;Initializing aws api&quot;);
  
      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, &quot;Ready to rock!&quot;);
  
      /* 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, &quot;Shutting down polly polly audio interface&quot;);
  
      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, &quot;Closing aws api&quot;);
      Aws::ShutdownAPI(*globals.options);
  
      delete globals.credentials;
      delete globals.config;
      delete globals.options;
  
      switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, &quot;Module shutdown finished&quot;);
      return SWITCH_STATUS_UNLOAD;
  }
  </code></pre>
  <p>Now when i try to load this on <code>Freeswitch</code> , it throws an error</p>
  <blockquote>
  <p>2019-07-31 22:00:51.918181 [CRIT] switch_loadable_module.c:1522 Error Loading module /usr/local/freeswitch/mod/mod_polly.so</p>
  <p>/usr/local/freeswitch/mod/mod_polly.so: undefined symbol: _ZNK3Aws35AmazonSerializableWebServiceRequest7GetBodyEv</p>
  </blockquote>
  <p>Freeswitch is C code with C++ guards in header files (extern &quot;C&quot; declaration).</p>
  <p>Looking at symbols in <code>mod_polly.so</code></p>
  <blockquote>
  <p>readelf -Ws mod_polly.so | grep _ZNK3Aws35AmazonSerializableWebServiceRequest7GetBodyEv</p>
  <p>66: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _ZNK3Aws35AmazonSerializableWebServiceRequest7GetBodyEv</p>
  <p>590: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _ZNK3Aws35AmazonSerializableWebServiceRequest7GetBodyEv</p>
  </blockquote>
  <p>Now my basic understanding of the post <a href="https://stackoverflow.com/questions/12573816/what-is-an-undefined-reference-unresolved-external-symbol-error-and-how-do-i-fix/26233563#26233563">here</a> tells me that the symbol is present in the <code>so</code> file but <code>Freeswitch</code> cannot find it or load it.</p>
  <p>Now this error has very likely to do with mixing C/C++ code but looking at <a href="https://stackoverflow.com/questions/12573816/what-is-an-undefined-reference-unresolved-external-symbol-error-and-how-do-i-fix/12574420#12574420">this</a> and <a href="https://www.oracle.com/technetwork/articles/servers-storage-dev/mixingcandcpluspluscode-305840.html" rel="nofollow noreferrer">this</a> hasn't helped me figure out how to fix it.</p>
  <p>I do not want to build <code>Freeswitch</code> to load my module and I am thinking I shouldn't have to, as that renders this project un-scalable.</p>
  <p>What am I missing here?</p>
  <p>PS:</p>
  <blockquote>
  <p>readelf -Ws libaws-cpp-sdk-core.a | grep AmazonSerializableWebServiceRequest7GetBodyEv</p>
  <p>165: 0000000000000000   716 FUNC    GLOBAL DEFAULT   42 _ZNK3Aws35AmazonSerializableWebServiceRequest7GetBodyEv</p>
  </blockquote>
  <p>Symbol is defined in <code>libaws-cpp-sdk-core.a</code> which is part of the compilation command for <code>mod_polly.cpp</code></p>