Date: Sat, 19 Oct 2024 13:16:30 -0700
Hi folks, long time reader, first time commenter here
I wonder if the solution to the dynamic linking issue is to specify some
"implementation defined (read: dllexport)" functions with specific names to
query what global arrays dynamic library contains, and specify some
functions that allow the programmer to manually link these global
containers together when the dynamic library is loaded through whatever
platform api is used. The objects and arrays themselves could be
immutable, but some range-like accessor API could be mutable to allow
linking global object stores together. Users would have to figure out how
to de-duplicate things themselves, but I think reflection/generation would
give us tools to generate some kind of comparable handle if that was
something a user's codebase needs.
This would require users to handle linking the arrays together in a
dynamic linking situation and to handle the duplication issues, but I don't
see any elegant way to specify dynamic linking without actually specifying
how dynamic linking works... a massive can of worms that would kill this
extremely useful proposal/idea.
On Sat, Oct 19, 2024 at 12:59 PM Federico Kircheis via Std-Discussion <
std-discussion_at_[hidden]> wrote:
> >> Or dlopen, loadlibrary,... Which are just functions; outside of the
> standard, like the rest of dynamic linking
> >
> > Those are also non-standard.
>
> Exactly my point, dynamic linking is non-standard!
>
> >> No need for attributes, assembly, or other compiler extensions, or
> strange constructs.
> >> Global variables are problematic in shared libraries, that's it.
> >
> > Problematic how?
>
>
> Time to take out my favorite example:
>
> const std::string answer_of_life = "42";
>
> This line of code, a global constant std::string, can lead to undefined
> behavior.
>
> Depending on the environment, you will have one copy, multiple copies,
> one copy with the constructor/destructor executed multiple times, and
> other things.
>
> There is no function outside of those defined by the standard, not
> attributes, nothing.
> Normal and boring c++ code.
> It's what the runtime is doing behind our back that is problematic, it's
> not that if I do no call dlopen explicitly then the problem disappears ;)
>
>
> As an example, a class that logs in the destructor it's address and a
> string:
>
> https://godbolt.org/z/sb8n8fn1a
>
> the output:
>
> > 0x7fd71efe616a goodbye
> > 0x7fd71efe6169 goodbye
> > 0x7fd71efe016a goodbye
> > 0x7fd71efe0169 goodbye
>
> there are thus four independent instances
>
>
> Same class, slightly different context:
>
> https://godbolt.org/z/Thv8xhc6Y
>
> output:
>
> > 0x7a9bf9cb6169 goodbye
> > 0x7a9bf9cb6169 goodbye
>
>
> one instance, destructor is executed more than once (UB, most probably
> you will consume double amount of memory, and eventually notice the
> crash at shutdown)
>
>
> Another example that shows the same behavior
>
> https://godbolt.org/z/ecPPq8vns
>
>
> Note that in none of those examples, I've used anything outside of the
> standard or platform specific.
>
>
> There are ways to have well-defined behavior (no way to ensure, because
> it does not depend on the c++ language, but on your toolchain/environment).
>
> The most secure way is to use constexpr, because no code is executed.
> If that would be UB, then you would also have the same issue for
> function pointers, which are effectively global symbols that do not
> execute any code during load/unload.
>
> Changing visibility settings and hiding everything avoids the UB, but
> then you get multiple copies fo the same object.
> It could be problematic if some objects handles some types of resources.
>
> inline variables since c++17 help too.
>
> Everything that changes the visibility and linkage (implicitly through
> different c++ tools, or explicitly through compiler specific settings),
> of the variable will change the observed behavior.
>
> But everything is unspecified (in c++ at least): how many copies you
> get, how many times the constructor and destructor are executed on every
> instance, and so on.
>
> C++ has no concept of visibility because it has no concept of libraries.
>
> Some results on this particular issue grouped together:
>
> * https://fekir.info/post/global-variables-in-cpp-libraries/
> * https://www.youtube.com/watch?v=xOJGc72gf8M
>
>
> Another issue, that fortunately I've never encountered:
>
> Microsoft documents some things that should not be done in DllMain or
> before.
> Thus some operations are fine in an application, but not in a dynamic
> library:
>
>
> https://learn.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-best-practices
>
> I'm not sure if similar restrictions exists for shared libraries on
> other OS, but it would not surprise me.
>
> Federico
> --
> Std-Discussion mailing list
> Std-Discussion_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion
>
I wonder if the solution to the dynamic linking issue is to specify some
"implementation defined (read: dllexport)" functions with specific names to
query what global arrays dynamic library contains, and specify some
functions that allow the programmer to manually link these global
containers together when the dynamic library is loaded through whatever
platform api is used. The objects and arrays themselves could be
immutable, but some range-like accessor API could be mutable to allow
linking global object stores together. Users would have to figure out how
to de-duplicate things themselves, but I think reflection/generation would
give us tools to generate some kind of comparable handle if that was
something a user's codebase needs.
This would require users to handle linking the arrays together in a
dynamic linking situation and to handle the duplication issues, but I don't
see any elegant way to specify dynamic linking without actually specifying
how dynamic linking works... a massive can of worms that would kill this
extremely useful proposal/idea.
On Sat, Oct 19, 2024 at 12:59 PM Federico Kircheis via Std-Discussion <
std-discussion_at_[hidden]> wrote:
> >> Or dlopen, loadlibrary,... Which are just functions; outside of the
> standard, like the rest of dynamic linking
> >
> > Those are also non-standard.
>
> Exactly my point, dynamic linking is non-standard!
>
> >> No need for attributes, assembly, or other compiler extensions, or
> strange constructs.
> >> Global variables are problematic in shared libraries, that's it.
> >
> > Problematic how?
>
>
> Time to take out my favorite example:
>
> const std::string answer_of_life = "42";
>
> This line of code, a global constant std::string, can lead to undefined
> behavior.
>
> Depending on the environment, you will have one copy, multiple copies,
> one copy with the constructor/destructor executed multiple times, and
> other things.
>
> There is no function outside of those defined by the standard, not
> attributes, nothing.
> Normal and boring c++ code.
> It's what the runtime is doing behind our back that is problematic, it's
> not that if I do no call dlopen explicitly then the problem disappears ;)
>
>
> As an example, a class that logs in the destructor it's address and a
> string:
>
> https://godbolt.org/z/sb8n8fn1a
>
> the output:
>
> > 0x7fd71efe616a goodbye
> > 0x7fd71efe6169 goodbye
> > 0x7fd71efe016a goodbye
> > 0x7fd71efe0169 goodbye
>
> there are thus four independent instances
>
>
> Same class, slightly different context:
>
> https://godbolt.org/z/Thv8xhc6Y
>
> output:
>
> > 0x7a9bf9cb6169 goodbye
> > 0x7a9bf9cb6169 goodbye
>
>
> one instance, destructor is executed more than once (UB, most probably
> you will consume double amount of memory, and eventually notice the
> crash at shutdown)
>
>
> Another example that shows the same behavior
>
> https://godbolt.org/z/ecPPq8vns
>
>
> Note that in none of those examples, I've used anything outside of the
> standard or platform specific.
>
>
> There are ways to have well-defined behavior (no way to ensure, because
> it does not depend on the c++ language, but on your toolchain/environment).
>
> The most secure way is to use constexpr, because no code is executed.
> If that would be UB, then you would also have the same issue for
> function pointers, which are effectively global symbols that do not
> execute any code during load/unload.
>
> Changing visibility settings and hiding everything avoids the UB, but
> then you get multiple copies fo the same object.
> It could be problematic if some objects handles some types of resources.
>
> inline variables since c++17 help too.
>
> Everything that changes the visibility and linkage (implicitly through
> different c++ tools, or explicitly through compiler specific settings),
> of the variable will change the observed behavior.
>
> But everything is unspecified (in c++ at least): how many copies you
> get, how many times the constructor and destructor are executed on every
> instance, and so on.
>
> C++ has no concept of visibility because it has no concept of libraries.
>
> Some results on this particular issue grouped together:
>
> * https://fekir.info/post/global-variables-in-cpp-libraries/
> * https://www.youtube.com/watch?v=xOJGc72gf8M
>
>
> Another issue, that fortunately I've never encountered:
>
> Microsoft documents some things that should not be done in DllMain or
> before.
> Thus some operations are fine in an application, but not in a dynamic
> library:
>
>
> https://learn.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-best-practices
>
> I'm not sure if similar restrictions exists for shared libraries on
> other OS, but it would not surprise me.
>
> Federico
> --
> Std-Discussion mailing list
> Std-Discussion_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion
>
-- Garrett Fleenor (They/Them or He/Him)
Received on 2024-10-19 20:16:46