Closed dmik closed 3 years ago
Tests show that this does not solve all problems related to running processes with a different copy of LIBC DLL in parallel with system LIBC DLL already loaded in memory by other processes (which is almost always the case on a real system). But it's at least much more useable now and won't screw up the system DLL (so that other processes using it won't be affected). This is already a big improvement.
What about some edge cases which are still out there , one of the reasons for them is that LIBPATHSTRICT=T implementation is not perfect. Consider that three is some app using e.g. both LIBC (LIBCN0.DLL) and LIBCx (LIBCX0.DLL) and it is already running (some system daemon like SMBD or CUPSD), let it be DAEMON. Now imagine there is a TEST app which also uses both LIBC and LIBCx and which we try to supply with a different copy of LIBC using BEGINLIBPATH and LIBPATHSTRICT=T. So, TEST loads the new LIBCN0.DLL (separate module handle etc) and then attempts to load LIBCx. But since we did not supply it with another copy of LIBCX0.DLL in BEGINLIBPATH, it will load it from the system location but the one from the system location has been already loaded by DAEMON. And this system copy has also already loaded system LIBCN0.DLL.
So TEST ends up with two copies of LIBCN0.DLL — one which it sees and uses, and the other one which LIBCX.DLL sees and uses. If we look at things such as file handles, there will be two copies of them either. So TEST and LIBCX will disagree here which will lead to unexpected errors (like LIBCX being unable to recognize file handles TEST supplies it with).
The situation is not specific for LIBC or LIBCx. Imagine any DLL that may get loaded from the system location and load a system copy of the DLL you are trying to feed the app with via LIBPATHSTRICT=T. The app will end with two copies — one for itself and the other one for the rest. The thing is that for most DLLs it's not a problem because they rarely use shared data (DLL data is usually private to the application and each application has its own copy of it). But LIBC as well as LIBCx are two obvious exceptions here. LIBC and LIBCx need to share data between all running processes in order to implement many things so this is sort of inevitable here.
One might think that having the same shared memory region for any copy of LIBC (i.e. like it is now) is a solution here but in fact it is not for the reasons I described in bitwiseworks/libcx#99 (leaving references to copy's private code and data in the shared memory is one of them) — many apps simply crash under this scenario and this is why I ended up with a solution in a form of this ticket.
Ideally it's the OS/2 loader who needs a fix here. It should have loaded LIBCx for the second time when under LIBPATHSTRICT=T and assign it a different module handle, even though it's still at the same (system) location. This would solve this and similar problems completely (for the cost of using extra memory resources of course, but it's expected anyway in this scenario).
I don't know if it's possible to fix the OS/2 loader but is one obvious workaround, at least for the LIBC/LIBCx pair: always put them to BEGINLIBPATH together when LIBPATHSTRICT=T is used so that two new copies of them will be loaded for the app. This approach solves the problem nicely here (of course provided that the shared mutex and memory name depends on the module handle as described in the title). The only one still puzzling me is that when I supply a release version of LIBCx to BEGINLIBPATH I have KLIBCCFG.INI created in the current directory (which means that PREWRITE.DLL somehow couldn't find the one installed in %ETC% for whatever reason). But when I supply a debug version of LIBCx, somehow everyone is happy. But that's a task for another ticket I guess.
Anyway, I'm going to apply the idea of this ticket for now. Comments from @StevenLevine and others on the subject are also welcome.
As another side note, there is already a /usr/lib/log
directory (owned by LIBC RPM so far). This sis where the log-enabled LIBC version goes. We may put other log-enabled libraries there (i.e. LIBCx) and then it will just work but adding this dir to BEGINLIBPATH and using LIBPATHSTRICT=T whenever someone wants logs for their app's run session (which will be very useful for debugging apps).
This is similar to https://github.com/bitwiseworks/libcx/issues/99 but for LIBC. The rationale is the same: make sure that two DLLs that co-exist in memory due to LIBPATHSTRICT=T don't screw up each other's data.