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 18:17:26 -0700
On Wednesday, 12 July 2023 15:26:49 PDT Thiago Macieira via Std-Proposals
wrote:
> 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.

Ah, so it *wasn't* fixed. The problem is there in that code, just wasn't
visible because I didn't run it. I was only trying to prove it with
compilation. But it does refuse to compile if you use constinit.

That means that there is still a function pointer comparison problem if you
you're comparing pointers to functions stored in memory. My understanding is
that this is a limitation of the COFF PE file format: there's only one type of
relocation possible in image files and that's to add the difference between the
load address and the preferred load base address. Therefore, it's impossible
to relocate to a symbol whose address isn't known at link time. See
https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#the-reloc-section-image-only

Here's the test:
$ tail -n50 test.cpp other.cpp
==> test.cpp <==
#include <stdlib.h>
#include <stdio.h>

const decltype(&malloc) *fptrptr() noexcept;

int main()
{
  printf("malloc = %p\nfptr = %p\n", &_exit, *fptrptr());
}

==> other.cpp <==
#include <stdlib.h>

extern constexpr auto fptr = &malloc;
const decltype(&malloc) *fptrptr() noexcept
{
  return &fptr;
}
$ cl -nologo -Z7 -MD -O2 test.cpp other.cpp -link -subsystem:console
test.cpp
other.cpp
Generating Code...
$ ./test.exe
malloc = 00007FF8F3231380
fptr = 00007FF63C132934

Running in the debugger shows that the value printed for malloc is indeed the
entry point for malloc in ucrtbase.dll, whereas the other pointer is still
somewhere in the current binary. Disassembling it shows it's a jump
instruction:

    0x7ff63c132934: ff 25 c0 79 00 00 jump jmpq *0x79c0(%rip)

And if you inspect that memory address:

0x7ff602f7a168: 0x00007ff8f31c1ac0 ucrtbase.dll`malloc

So this is address is a thunk to malloc.

Ditto with GCC:
$ g++ -O2 -g test.cpp other.cpp -o test.exe
$ ./test.exe
malloc = 00007ff639247a10
fptr = 00007ff639247a78

Do note both addresses are in the current image and yet are different (this
toolchain included a static copy of malloc in the .exe for some reason).

Of course, this does not happen on Linux:
$ g++ -O2 test.cpp other.cpp
$ ./a.out
malloc = 0x401040
fptr = 0x401040

Even with -mno-direct-extern-access:
$ g++ -mno-direct-extern-access -O2 test.cpp other.cpp
$ ./a.out
malloc = 0x7f3a277ba91e
fptr = 0x7f3a277ba91e

Or on macOS:
$ clang++ -std=c++17 -O2 test.cpp other.cpp
$ ./a.out
malloc = 0x7ff81bf47f20
fptr = 0x7ff81bf47f20

Or FreeBSD:
 $ clang++ -std=c++17 -O2 test.cpp other.cpp
$ ./a.out
malloc = 0x201a20
fptr = 0x201a20


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

Received on 2023-07-13 01:17:29