Date: Thu, 13 Jul 2023 07:37:50 +0100
Hi,
On 12 July 2023 23:26:49 BST, Thiago Macieira via Std-Proposals <std-proposals_at_[hidden]> wrote:
>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.
AFAIK current efforts around contracts are doubling down on this design and propose contract violation handlers to be replaceable function, similarly to to the allocation functions.
Also I strongly disagree for runtime replacement for allocation functions in the style of set_terminate() for the simple reason that new and delete should better come from the same allocator and and it becomes quite hard to guarantee if you replace allocation functions in the middle of your program.
>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().
>
On 12 July 2023 23:26:49 BST, Thiago Macieira via Std-Proposals <std-proposals_at_[hidden]> wrote:
>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.
AFAIK current efforts around contracts are doubling down on this design and propose contract violation handlers to be replaceable function, similarly to to the allocation functions.
Also I strongly disagree for runtime replacement for allocation functions in the style of set_terminate() for the simple reason that new and delete should better come from the same allocator and and it becomes quite hard to guarantee if you replace allocation functions in the middle of your program.
>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().
>
Received on 2023-07-13 06:37:59