TCL extensions that depends on each other - c

My question is a little bit similar to this but it is about TCL extensions.
I am using C on Linux (gcc) and I have a package with three modules A, B, and C. Module A contains functions and also define (not only declare) global variables. I compile and link module A into a dynamic library (libA.so).
Now, I want that B and C are TCL extensions. Both are using functions and global variables from A, while C is also using functions from B. I have made B and C shared library (B.so and C.so) but without using "-Wl -soname". I made B.so depends on A.so, while C.so is without user dependencies. Although this is strange, bot extensions loaded and worked properly. Here is, what I have (A=libbiddy.so, B=bddscout.so, C=bddscoutIFIP.so):
meolic#meolic:/usr/lib/bddscout$ ldd *.so
bddscout.so:
linux-gate.so.1 => (0x00177000)
libbiddy.so.1 => /usr/lib/libbiddy.so.1 (0x00eca000)
libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0x00342000)
/lib/ld-linux.so.2 (0x0061f000)
bddscoutIFIP.so:
linux-gate.so.1 => (0x00fc2000)
libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0x00110000)
/lib/ld-linux.so.2 (0x00c75000)
meolic#meolic:/usr/lib/bddscout$ wish
% puts $tcl_patchLevel
8.5.8
% load ./bddscout.so
% load ./bddscoutIFIP.so
% info loaded
{./bddscoutIFIP.so Bddscoutifip} {./bddscout.so Bddscout} {{} Tk}
The problem is, that exactly the same package is not working everywhere. On a new computer extension C.so does not load.
meolic#altair:/usr/lib/bddscout$ ldd *.so
bddscout.so:
linux-gate.so.1 => (0xb76ef000)
libbiddy.so.1 => /usr/lib/libbiddy.so.1 (0xb76c9000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb754d000)
/lib/ld-linux.so.2 (0xb76f0000)
bddscoutIFIP.so:
linux-gate.so.1 => (0xb7780000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75e8000)
/lib/ld-linux.so.2 (0xb7781000)
meolic#altair:/usr/lib/bddscout$ wish
% puts $tcl_patchLevel
8.5.10
% load ./bddscout.so
% load ./bddscoutIFIP.so
couldn't load file "./bddscoutIFIP.so": ./bddscoutIFIP.so: undefined symbol: biddy_termFalse
The reported undefined symbol is one of global variables from A. Question1: is my approach correct as it works on some systems? Question2: why it does not work on a new system?

Tcl's load command uses dlopen() under the covers (on Linux; it's different on other platforms of course) and it uses it with the RTLD_LOCAL flag; symbols in the library are not exported to the rest of the application. Because of this, unbound symbols in one dynamically-loaded library will not resolve against another one; this boosts isolation, but forces you to do more work to make things all function correctly where you want such a dependency to actually exist.
Your options are:
If libscoutIFIP.so depends on libbiddy.so's symbols, tell this to the linker when building the library and the dynamic linker engine will sort it all out so that the dependency doesn't get loaded multiple times. That is, if a library depends on a symbol in another library, it should explicitly list that library as a dependency.
Arrange for libbiddy.so to export its symbols as a stub table (i.e., structure of pointers to functions/variables) through Tcl's package API (Tcl_PkgProvide()). Then when libscoutIFIP.so does Tcl_PkgRequireEx() on the biddy package, it will get a pointer to that stub table and can use the references within it instead of doing direct linking. This is how Tcl's stub mechanism works, and its awesome and portable and lets you do fairly complex API version management (if necessary). It's a bit more work to set up though. The Tcler's Wiki goes into quite a lot more depth on this topic.
If option 1 works for you, go with that; for Linux-specific code that should be just fine as the system dynamic linker isn't desperately dense (unlike the situation on Windows).
[EDIT]: Note that older versions of Tcl (up to 8.5.9) used RTLD_GLOBAL instead. It seems that this change should have been labelled ***POTENTIAL INCOMPATIBILITY*** in the release notes and trailed more widely. Apologies on behalf of the Tcl developers.

Related

Intercepting Statically Linked Dependencies of a Dynamically Linked Library

I'm trying to debug an API in use by a third party app.
The third party app has a configuration I had thought impossible:
The application binary contains exports required by one of its shared library dependencies. That is, they dynamically linked their dependency, but that dependency's dependency is statically linked.
This is occurring on a MIPS based linux with an ancient kernel.
I have confirmed this using Ghidra to disassemble the executable/shared library.
Basically we have the binary and the shared object
file initApp
initApp: ELF 32-bit LSB executable, MIPS, MIPS32 rel2 version 1 (SYSV), dynamically linked, interpreter /lib/ld-uClibc.so.0, stripped
file libhyCoreSdk.so
libhyCoreSdk.so: ELF 32-bit LSB shared object, MIPS, MIPS32 rel2 version 1 (SYSV), dynamically linked, stripped
objdump -T libhyCoreSdk.so | grep -i IMP_ISP_Open
00047900 DF *UND* 00000000 IMP_ISP_Open
initApp calls "coreAvInit" in libhyCoreSdk.so, and then libhyCoreSdk.so calls IMP_ISP_Open() back inside initApp.
You might be thinking "that's not that unusual, you can do that with callbacks, passing pointers, a call to dlsym etc. etc." But this isn't any of those things, this is a direct import/export of symbols exactly what you'd expect to see if libhyCoreSdk depended on another shared library.
All that aside, my real problem is that I am trying to figure out what parameters are being passed by IMP_ISP_Open and LD_PRELOAD does not help this strange/unique circumstance. I have another example application which is calling the same APIs against a shared library version of the API. When I use LD_PRELOAD (loading a little interceptor program I wrote) against that version it works great. But when I use it against this version that links back to the binary, it doesn't work.
I'm hoping someone has ideas on how I can intercept those API calls.
Partial Solution:
Daniel Kleinstein has given me a good start. I renamed the target functions from IMP_... to XMP_... (e.g. IMP_ISP_DisableSensor-> XMP_ISP_DisableSensor). Now the IMP_ISP_DisableSensor from my LD_PRELOAD is correctly hit. Unfortunately, I'm still running into trouble. Calling dlsym(RTLD_DEFAULT, "XMP_ISP_DisableSensor") returns NULL for no obvious reason... This means I can't redirect back to the original function.
Despite objdump showing this:
objdump -T initApp_mod | grep -i XMP
0061fb5c g DF .text 00000338 Base XMP_FrameSource_CreateChn
00613c6c g DF .text 00000204 Base XMP_ISP_DisableSensor
006097f0 g DF .text 00000eb0 Base XMP_Encoder_CreateChn
Your dynamic dependency is not statically linked against your main executable. When the dynamic dependency is loaded, a search is made for its import and is resolved by a symbol exported by the executable - indeed, the executable's symbols will always resolve before any of the other dynamic dependencies.
Unfortunately, this also prevents your usage of LD_PRELOAD.
LD_PRELOAD doesn't work because its injected library does not take precedence over symbols exported by the main executable itself - only over symbols exported by shared libraries.
If you wish to intercept the call using LD_PRELOAD, a crude but effective solution is to patch the main executable's exported symbol to a different name - this way it won't resolve when the dynamic dependency gets loaded, and your injected library can supply the symbol.

Can a dynamically linked library override a static one?

nokogori gem comes with its own version of libxml2. Moreover it warns about libxml2.so of a different version being loaded before it was required:
if compiled_parser_version != loaded_parser_version
["Nokogiri was built against LibXML version #{compiled_parser_version}, but has dynamically loaded #{loaded_parser_version}"]
It basically compares LIBXML_DOTTED_VERSION macro and xmlParserVersion global variable:
rb_const_set( mNokogiri,
rb_intern("LIBXML_VERSION"),
NOKOGIRI_STR_NEW2(LIBXML_DOTTED_VERSION)
);
rb_const_set( mNokogiri,
rb_intern("LIBXML_PARSER_VERSION"),
NOKOGIRI_STR_NEW2(xmlParserVersion)
);
And I'm experiencing it firsthand. When rmagick (which dynamically links to libxml2.so, ldd confirms that) is required before nokogiri, the latter complains.
From what I can see nokogiri is linked to libxml2 statically. First that is the default (supposedly). Then when rmagick is not required I can't see libxml2.so in /proc/PID/maps. I neither can see another version of libxml2.so. ldd doesn't list libxml2.so as a nokogiri.so's dependency. objdump lists xmlReadMemory (and friends) as a nokogori.so's symbol (probably a sign that it was linked statically).
So how come can nokogiri access libxml2.so's variables? Does that mean that loading libxml2.so overrides any statically linked versions? Can that happen in the middle of code execution?
So how come can nokogiri access libxml2.so's variables?
This is by design (and due to the fact that nokogiri was built incorrectly).
UNIX shared libraries are designed to emulate archive libraries. This means that the first ELF image to export a given symbol wins (there are some complications for libraries linked with -Bstatic flag, but we'll ignore them for now).
Does that mean that loading libxml2.so overrides any statically linked versions?
Yes, if statically linked version is also exported and calls to it go though PLT.
Can that happen in the middle of code execution?
With lazy symbol resolution (which is default, except when LD_BIND_NOW or -z now are in effect) it will always happen in the middle of code execution.
Now, the problem is that if nokogiri links in a static copy of libxml.a, it should hide that fact by localizing that copy within itself and not exporting any of its symbols. That would prevent end users from having to deal with symbol conflicts.
Your best bet is to either build your own nokogiri compiling and linking it against the same version of libxml, or to contact nokogiri maintainers and ask them to fix their builds.

Port glibc 2.25 and test memory functions

I was investigating whether a few memory functions(memcpy, memset, memmove) in glibc-2.25 with various versions(sse4, ssse3, avx2, avx512) could have performance gain for our server programs in Linux(glibc 2.12).
My first attempt was to download a tar ball of glibc-2.25 and build/test following the instructions here https://sourceware.org/glibc/wiki/Testing/Builds. I manually commented out kernel version check and everything went well. Then a test program was linked with newly built glibc with the procedure listed in section "Compile against glibc build tree" of glibc wiki and 'ldd test' shows that it indeed depended on the expected libraries:
# $GLIBC is /data8/home/wentingli/temp/glibc/build
libm.so.6 => /data8/home/wentingli/temp/glibc/build/math/libm.so.6 (0x00007fe42f364000)
libc.so.6 => /data8/home/wentingli/temp/glibc/build/libc.so.6 (0x00007fe42efc4000)
/data8/home/wentingli/temp/glibc/build/elf/ld-linux-x86-64.so.2 => /lib64/ld-linux-x86-64.so.2 (0x00007fe42f787000)
libdl.so.2 => /data8/home/wentingli/temp/glibc/build/dlfcn/libdl.so.2 (0x00007fe42edc0000)
libpthread.so.0 => /data8/home/wentingli/temp/glibc/build/nptl/libpthread.so.0 (0x00007fe42eba2000)
I use gdb to verify which memset/memcpy was actually called but it always shows that __memset_sse2_unaligned_erms is used while I was expecting that some more advanced version of the function(avx2,avx512) could be in use.
My questions are:
Did glibc-2.25 select the most suitable version of memory functions automatically according to cpu/os/memory address? If not, am I missing any configuration during glibc build or something wrong with my setup?
Is there any other alternatives for porting memory functions from newer glibc?
Any help or suggestion would be appreciated.
On x86, glibc will automatically select an implementation which is most suitable for the CPU of the system, usually based on guidance from Intel. (Whether this is the best choice for your scenario might not be clear because the performance trade-offs for many of the vector instructions are extremely complex.) Only if you explicitly disable IFUNCs in the toolchain, this will not happen, but __memset_sse2_unaligned_erms isn't the default implementation, so this does not apply here. The ERMS feature is pretty recent, so this is not completely unreasonable.
Building a new glibc is probably the right approach to test these string functions. Theoretically, you could also use LD_PRELOAD to override the glibc-provided functions, but it is a bit cumbersome to build the string functions outside the glibc build system.
If you want to run a program against a patched glibc without installing the latter, you need to use the testrun.sh script in the glibc build directory (or a similar approach).

How do I compile a binary which works with both libcrypto.so.0.9.8 and libcryto.so.1.0.0?

I have an autotools C project.
How do I compile a binary which works with both libcrypto.so.0.9.8 and libcryto.so.1.0.0? (i.e. Ubuntu 9.10 and 12.04)
Depending on the age of the OS on which I do the build, the binary requires one version or the other.
Is there a way of making it not care, or are the differences between the libcryto versions insurmountable?
In my opinion, you want to use some function of libcrypto.so.0.9.8 and some from libcryto.so.1.0.0. If most of the functions are required from 1.0.0 or is preferred choice then link with libcrypto.so.1.0.0.
And you may need some function from libcrypto.so.0.9.8 or you may have other good reasons to use libcrypto.so.0.9.8.
In my view, if you link from both the library, you will get linker error (duplicate symbols as both of the library contains same symbols).
If you need to use 0.9.8, then load it dynamically using dlopen and get the function callback which you want use with dlsym.
This can be accomplished as follows:
void * handle;
/*reqd_callback is the callback of required function.*/
reqd_callback cb;
handle = dlopen ("libcrypto.so.0.9.8", RTLD_LAZY);
cb = (reqd_callback)dlsym(handle, "reqd_function");
//Call the cb
cb (parameters);
//Close the library.
dlclose(handle);
I think this may solve your purpose. If the preference is inverse, invert the library in linking and in loading through program.
Can you make a soft link that will "point" to either libcrypto.so.0.9.8 or libcryto.so.1.0.0.
Give it a generic name and then use that, then whatever version of the library the link "points" to, will be picked up? On app install, you set your soft link to point to the library version available. Your software might be tested up to 1.0.0 if the lib is backward compatible enough, i.e. you don't rely on somthing in 1.0.0 that's not in 0.9.8, your ok.
You can rebuild ssl-0.9.8 (not 1.x because it contains some things that won't work on the older version) and change the line in the makefile where it does the final shared library linkage and embeds the SONAME
recompile it with the SONAME changed from libssl.so.0.9.8 to libssl.so.0
it will look something like: -Wl,-soname,libssl.so.0.9.8
change it to: -Wl,-soname,libssl.so.0
now when you compile against this library the binaries built against it will look for libssl.so.0 (which is included as a symlink in both versions)

How do you create a lua plugin that calls the c lua api?

I'm getting errors in the lua plugin that I'm writing that are symptomatic of linking in two copies of the lua runtime, as per this message:
http://lua-users.org/lists/lua-l/2008-01/msg00671.html
Quote:
Which in turn means the equality test for dummynode is failing.
This is the usual symptom, if you've linked two copies of the Lua
core into your application (causing two instances of dummynode to
appear).
A common error is to link C extension modules (shared libraries)
with the static library. The linker command line for extension
modules must not ever contain -llua or anything similar!
The Lua core symbols (lua_insert() and so on) are only to be
exported from the executable which contains the Lua core itself.
All C extension modules loaded afterwards can then access these
symbols. Under ELF systems this is what -Wl,-E is for on the
linker line. MACH-O systems don't need this since all non-static
symbols are exported.
This is exactly the error I'm seeing... what I don't know is what I should be doing instead.
I've added the lua src directory to the include path of the DLL that is the c component of my lua plugin, but when I link it I get a pile of errors like:
Creating library file: libmo.dll.a
CMakeFiles/moshared.dir/objects.a(LTools.c.obj): In function `moLTools_dump':
d:/projects/mo-pong/deps/mo/src/mo/lua/LTools.c:38: undefined reference to `lua_gettop'
d:/projects/mo-pong/deps/mo/src/mo/lua/LTools.c:47: undefined reference to `lua_type'
d:/projects/mo-pong/deps/mo/src/mo/lua/LTools.c:48: undefined reference to `lua_typename'
d:/projects/mo-pong/deps/mo/src/mo/lua/LTools.c:49: undefined reference to `lua_tolstring'
So, in summary, I have this situation:
A parent binary that is statically linked to the lua runtime.
A lua library that loads a DLL with C code in it.
The C code in the DLL needs to invoke the lua c api (eg. lua_gettop())
How do I link that? Surely the dynamic library can't 'see' the symbols in the parent binary, because the parent binary isn't loading them from a DLL, they're statically linked.
...but if I link the symbols in as part of the plugin, I get the error above.
Help? This seems like a problem that should turn up a lot (dll depends on symbols in parent binary, how do you link it?) but I can't seem to see any useful threads about it.
(before you ask, no, I dont have control over the parent binary and I cant get it to load the lua symbols from the DLL)
It's probably best to use libtool for this to make your linking easier and more portable. The executable needs to be linked with -export-dynamic to export all the symbols in it, including the Lua symbols from the static library. The module needs to then be linked with -module -shared -avoid-version and, if on Windows, additionall -no-undefined; if on MacOS, additionally -no-undefined -flat_namespace -undefined suppress -bundle; Linux and FreeBSD need no other symbols. This will leave the module with undefined symbols that are satisfied in the parent. If there are any missing, the module will fail to be dlopened by the parent.
The semantics are slightly different for each environment, so it might take some fiddling. Sometimes order of the flags matters. Again, libtool is recommended since it hides much of the inconsistency.

Resources