Summary:ASTERISK-18399: When using autoload=yes in modules.conf, res_odbc_conf.so complaints about undefined symbol
Reporter:Mikael Carlsson (mickecarlsson)Labels:
Date Opened:2011-08-31 15:14:59Date Closed:2011-09-01 14:15:44
Status:Closed/CompleteComponents:. I did not set the category correctly.
Versions: Frequency of
Environment:Centos 6.0, x64Attachments:( 0) 20110831__asterisk-18399.diff
Description:When starting Asterisk an error is listed in log file:
[Aug 31 21:54:45] WARNING[860] loader.c: Error loading module 'res_config_odbc.so': /usr/lib/asterisk/modules/res_config_odbc.so: undefined symbol: ast_odbc_clear_cache

After a conversation with Corydon76-home on #asterisk-dev I emptied out modules.conf:

The error still exists.
If I preload odbc:
preload => res_odbc.so
preload => res_config_odbc.so

The error is gone.
This is from IRC:
<Corydon76-home> mickecarlsson: I think I know what the problem is, but it's not solveable, because it's a bug in the DL library
<Corydon76-home> Specifically, the DL library is not paying attention to the RTLD_LAZY flag
Comments:By: Walter Doekes (wdoekes) 2011-08-31 16:25:12.038-0500

Is there really a problem here?

When I test this with 1.8-svn, I get see these things:

(1) autoloaded modules are loaded in the readdir(2) order

(2) if res_config_odbc is loaded before res_odbc, it's retried again

(3) if you wrap ast_clear_odbc_cache in a res_config_odbc function (my_clear_odbc_cache) the error goes away

(I added ast_log(NOTICE.. "Loading..)) before the 2 dlopen calls)


Module order in the directory:

$ find /usr/lib/asterisk/modules/ -type f | head

Module order at load time:

189 modules will be loaded.
Loading module (a) 'app_waitforring.so': /usr/lib/asterisk/modules/app_waitforring.so
Loading module (a) 'app_stack.so': /usr/lib/asterisk/modules/app_stack.so
Loading module (a) 'func_rand.so': /usr/lib/asterisk/modules/func_rand.so


Now, if I reshuffle the files in the path to get res_config_odbc first, we get this:

(I got it in the dir in the preceding order by renaming it to rres_config_odbc.so, your mileage may vary)

Loading module (a) 'rres_config_odbc.so': /usr/lib/asterisk/modules/rres_config_odbc.so
Error loading module 'rres_config_odbc.so': /usr/lib/asterisk/modules/rres_config_odbc.so: undefined symbol: ast_odbc_clear_cache
Loading module (a) 'res_odbc.so': /usr/lib/asterisk/modules/res_odbc.so
Loading module (b) 'res_odbc.so': /usr/lib/asterisk/modules/res_odbc.so
Loading module (a) 'rres_config_odbc.so': /usr/lib/asterisk/modules/rres_config_odbc.so
Loading module (b) 'rres_config_odbc.so': /usr/lib/asterisk/modules/rres_config_odbc.so

It gets loaded a second time because load_resource returns AST_MODULE_LOAD_SKIP after the failure.


Wrapping ast_clear_odbc_cache makes the error go away. res_config_odbc still gets loaded twice though:

--- res/res_config_odbc.c (revision 334212)
+++ res/res_config_odbc.c (working copy)
@@ -1131,6 +1131,10 @@
#undef warn_length
#undef warn_type

+static int my_odbc_clear_cache(const char *database, const char *tablename) {
+ return ast_odbc_clear_cache(database, tablename);
static struct ast_config_engine odbc_engine = {
.name = "odbc",
.load_func = config_odbc,
@@ -1141,7 +1145,7 @@
.update_func = update_odbc,
.update2_func = update2_odbc,
.require_func = require_odbc,
- .unload_func = ast_odbc_clear_cache,
+ .unload_func = my_odbc_clear_cache,

static int unload_module (void)

(example with autoload=no and load=>res_config_odbc.so,load=>res_odbc.so)

Loading module (a) 'res_config_odbc.so': /usr/lib/asterisk/modules/res_config_odbc.so
Loading module (a) 'res_odbc.so': /usr/lib/asterisk/modules/res_odbc.so
Loading module (b) 'res_odbc.so': /usr/lib/asterisk/modules/res_odbc.so
Loading module (a) 'res_config_odbc.so': /usr/lib/asterisk/modules/res_config_odbc.so
Loading module (b) 'res_config_odbc.so': /usr/lib/asterisk/modules/res_config_odbc.so

By: Tilghman Lesher (tilghman) 2011-08-31 20:46:06.492-0500


Aha, you're right.  The issue is that because we're dealing with a function pointer, it's resolved as a variable, which means lazy binding won't work.  I agree with your workaround (wrapping the function).