C++ Logo

std-proposals

Advanced search

Re: [std-proposals] "shared libraries are outside the scope of the C++ standard"

From: Thiago Macieira <thiago_at_[hidden]>
Date: Wed, 12 Jul 2023 15:26:49 -0700
On Wednesday, 12 July 2023 07:56:38 PDT Arthur O'Dwyer via Std-Proposals
wrote:
> (2) More importantly, I'm aware of 2 clauses that the clash with the
>
> > Windows PE model:
> > (a) [expr.eq] http://eel.is/c++draft/expr.eq#3.2, has this to say about
> >
> > comparison of function-pointers: "... if the pointers ... both point to
> > the
> > same function ... they compare equal."
> > This is not so simple if the function in question is implemented in a
> > shared library: an actual `call` is made to the current binary's PLT (in
> > ELF systems) or IAT (in PE systems), so the direct call address is
> > different across binaries.
>
> IIUC, you're saying that an existing major implementation (MSVC on Windows,
> and perhaps clang-cl on Windows also) is non-conforming to the letter of
> the Standard.
> This is worthy of a bug report (if easily fixed), or a paper (if not, and I
> think you're saying not).

It's not a *bug* because it's a design decision. You can call it a "flaw" if
this was unintended, but more likely it's just "limitation".

> Your paper's goal will be to change the wording of [expr.eq] to bring the
> wording into line with the Windows reality. But, at the same time, your
> proposed wording ("equality of function pointers is
> implementation-defined") is simply too broad. We want to be able to rely on
> pointer equality to Do The Right Thing in C++ programs, don't we?
> So your paper should explore
> - heroic ways to make MSVC conform to the existing wording
> - ways to segregate functions at compile time into "those whose addresses
> definitely compare equal" and "those whose comparisons are
> implementation-defined"; this might relate to your (1) above, or it might
> not
> - ways to query at compile time whether we're on a weird platform or not,
> e.g. `#if __FUNCTION_POINTERS_MIGHT_SPURIOUSLY_COMPARE_UNEQUAL`
> - alternatively, what goes wrong with the status quo if we just say "okay,
> MSVC isn't conforming in this regard" and move on?

Please note function pointers themselves and their comparisons are not the
issue, because the compiler can (and does) compare to the address stored in
the IAT. See https://msvc.godbolt.org/z/9nxYeM7Pv.

The problem is that taking the address of a function or of a global variable
cannot be a core constant expression. So if you store that value in memory,
that value cannot be in read-only memory. See the difference between _exit and
a location function in https://msvc.godbolt.org/z/8ecavasez.

However, and I don't know when this changed[*], the compiler is able to work
it out if you force it with constexpr: https://msvc.godbolt.org/z/acbrj3sTM. A
local inspection of such a compiled binary reveals that the linker left
relocations to be resolved beyond the IAT, meaning that those read-only pages
aren't pure any more.

[*] Or I'm not triggering the correct condition for the problem. I know we've
run into this twice in the past 5 years in Qt. There may be more to this
problem than I have the time to write about right now.

> (b) [replacement.functions] http://eel.is/c++draft/replacement.functions#3,
> > lists flavours of new/delete that can be interposed (==overridden) from
> > user code:
> > "The program's definitions are used instead of the default versions
> > supplied by the implementation." [...]
> > This doesn't happen in PE DLLs, where the .idata section explicitly says
> > from which DLL to import each function. So for example `new` will by
> > default be imported from the Windows VC-Runtime Dlls, can also be resolved
> > from a static library linked to the DLL - but *NOT* from the main
> > executable.
>
> Same here: This sounds like MSVC is non-conforming. If it's easy to fix,
> this should be a bug report; if it's hard to fix, perhaps the wording
> should be changed so that MSVC can be conforming; and the paper should
> explore different avenues to get there. (And, alternatively, explain
> whether it's really even a problem if we just say MSVC is non-conforming in
> this area.)

Or you cop out by claiming you've stepped outside the standard: the
replacement is allowed and does work *within* each DLL. You just don't
override code from other DLLs.

On this one, I side with Microsoft: let's accept the limitation and deprecate
this functionality in C++. It's much better to have a function like
set_new_handler() or set_terminate() to do the overriding.

BTW, I'll add a 2(c): _exit() runs global destructors in DLLs. This is not an
OS ABI limitation, but caused by how _exit() is implemented (calls
TerminateProcess()) and how the runtimes implement global destruction. That
means it can be fixed without OS modifications, but still has a high cost.

There's also a 2(d): after exit() starts, on Windows all threads disappear, so
atexit() handlers (including destructors) are not able to join with pending
threads or lock mutexes that were found locked at the time the exiting
started. Combined with 2(c), this means code run from destructors that may
touch resources under mutex protection may deadlock the application on exit,
even after _exit(), abort(), or terminate().

-- 
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
   Software Architect - Intel DCAI Cloud Engineering

Received on 2023-07-12 22:26:52