Next Previous Contents

12. DSO(Dynamic Symbol Object)

You might know so(shared object) or dll(dynamic link library). Essentially, DSO is dependent on so/dll. Now, we call so/dll 'dynamic library' to distinguish from dso.

To understand dso, we should know how link and load work. In general, when we use dynamic libraries, we link them with your program at a build time. ld(1) takes care of it on Unix. ld(1) is known as link editor. Since ld(1) is usually called by gcc(1) implicitly, you wouldn't be aware of ld(1). We sometimes happen to see link error messages at a compile time. It means ld(1) can't resolve the symbols that you're using in your program. At a runtime, ld.so(8) loads the dynamic libraries. If it failed, you would see load error messages. A summary is that link is done at a compile(build) time, and load is done at a runtime. If you want to know more, please read manuals of ld(1) and ld.so(8).

By dso, both link and load are done at a runtime. Why do we use such a mechanism? The reason is that we can make plugin architecture. All we have to do during a build time is to define the interfaces(symbol names and how we call them) of loadable modules. The program loads the modules at a runtime and use them through the interfaces. Loadable modules can be developed by independent programmers. This can make the system very flexible and extensible.

Let's take a look at dso-sample.c. You can find two strings "libm.so" and "pow" in dso-sample.c. In fact, pow(3) is a trivial function and it doesn't worth calling as dso, but it is just an example code.

At first, we call apr_dso_load() and to pass the library file name, "libm.so".

/* excerpted from dso-sample.c, but I omitted error checks */

const char fname[] = "libm.so";
apr_dso_handle_t *dso_h;
apr_dso_load(&dso_h, fname, mp);

As you can imagine, if you want your program to have plugins, you need a way to indicate the plugin file names. We can find such a system, apache modules. Their module names are specified in "httpd.conf" file.

apr_dso_load() happens to fail. The cause is usually that it can't find the dynamic library file. The search path for dynamic library files at a runtime is dependent on OS. On GNU/Linux, it depends on LD_LIBRARY_PATH environment variable. On MS-Windows, it depends on PATH environment variable. After apr_dso_load() returns successfully, we can call apr_dso_sym(). The prototype declaration is as follows:

/* excerpted from apr_dso.h */

APR_DECLARE(apr_status_t) apr_dso_sym(apr_dso_handle_sym_t *ressym, 
                                      apr_dso_handle_t *handle,
                                      const char *symname);

By apr_dso_sym(), we can get a symbol object by a symbol name. The first argument is result argument. The second argument is dso handle, which we can get by apr_dso_open() above. The third argument is symbol name.

The following code is excerpted from dso-sample.c. The symbol name is "pow" and we get a function pointer as a symbol object. Since we know pow(3)'s interface, we can have typedef'd pow_fn_t.

/* excerpted from dso-sample.c, but I omitted error checks */

typedef double (*pow_fn_t)(double x, double y);
pow_fn_t pow_fn;

/* seek pow(3) function from libm.so */
apr_dso_sym((apr_dso_handle_sym_t*)&pow_fn, dso_h, "pow");

/* call pow(3) */
printf("%d ^ %d = %f\n", 2, 2, pow_fn(2, 2));

If your program has plugins, you have to define the symbol names and their interfaces. Then, plugin developers must follow them.

Finally, we call apr_dso_unload() to release the loadable module. It could reduce memory consumption.


Next Previous Contents