lpereira / lwan

Experimental, scalable, high performance HTTP server
https://lwan.ws
GNU General Public License v2.0
5.92k stars 549 forks source link

looking up handlers in .conf fail with dynamically linked objects #226

Closed diviaki closed 6 years ago

diviaki commented 6 years ago

I'm toying with the minimal.c example with minimal.conf from the wiki page.

When I link the example statically, it runs just fine. Adding the hello handler also works. Compile command for reference:

cc -I ../lwan/src/lib/ lwan.c -o lwan -Wl,-whole-archive ../lwan/build/src/lib/liblwan.a -Wl,-no-whole-archive -lz -upthread

Dynamic linking also works now. Compile line:

cc -I ../lwan/src/lib/ lwan.c -o lwan ../lwan/build/src/lib/liblwan.so

However, when adding the handler reference to the config file, now the server cries

Could not find handler "hello_world"

Both with static and dynamic linking the critical strings is present:

$ strings lwan |grep hello
hello_world
lwan_handler_info_hello_world
wan_handler_hello_world

Looking at the source, lwan looks up specific sections for the handlers. There are differences between these sections in the two kind of binaries.

With static link, objdump -s -j lwan_module lwan is 4x16 bytes, and objdump -s -j lwan_handler lwan is 2x16 bytes long.

With dynamic link, the lwan_module section is missing and lwan_handlers is 1x16 bytes.

I need some guidance how to dig deeper, or even better, a fix. :)

diviaki commented 6 years ago

In both cases, the first handler in section lwan_handler is indeed hello_world. With static linking, the additional handler is brew_coffee. So hello_world is there, why isn't it recognized?

lpereira commented 6 years ago

I suspect this has to do with the fact that the special start/stop symbols (__start_lwan_handler and __stop_lwan_handler respectively) are created and defined during the linking time of liblwan.so. Since this step happens before considering minimal.o, the code in liblwan.so is unable to find the handlers defined outside the shared library. This doesn't happen with static linking since a library archive is essentially just a bunch of .o files packaged together.

Short of changing how the main() is written (e.g. by providing an explicit url map, and referencing the hello_world handler), I don't know how to fix this portably.

It's possible to amend the linker script provided by the platform, which is nice, but it seems to be supported only by the GNU ld; the linkers on FreeBSD and macOS will only accept a "full" script. And maintaining a linker script is kind of a PITA.

diviaki commented 6 years ago

Thanks for explaining the situation, it makes sense now.

Indeed, doing an objdump -h lwan/build/src/lib/liblwan.so reveals the sections the handler/module lookup code (in the .so) use.

I've also ended up adding my handlers programmatically as you suggested.

One note to other newbies reading this. When I added my handler via lwan_set_url_map my handler was fine but static files stopped serving. It took me time to realize lwan_set_url_map overrides mappings of the config file. So just map the SERVE_FILES module too (example in main()).