[PATCH] D43184: [WIP] [ItaniunCXXABI] Add an option for loading the type info vtable with dllimport

Martin Storsjö via Phabricator via cfe-commits cfe-commits at lists.llvm.org
Mon Feb 12 12:58:44 PST 2018


mstorsjo added a comment.

In https://reviews.llvm.org/D43184#1005281, @smeenai wrote:

> FYI, binutils auto-import actually creates a fake IAT entry rather than using a dynamic initializer. I think it's actually a pretty cute trick. http://blog.omega-prime.co.uk/2011/07/04/everything-you-never-wanted-to-know-about-dlls/#how-auto-import-works has details. That only works when you're referencing an external imported symbol directly though, and breaks when you need an offset from the imported symbol.


Yup - except that it also can emit "pseudo-relocs" that require making the code segment writable, and these relocations are fixed by the mingw runtime on load. These are exceptional/ugly enough that they're not enabled by default but require a linker flag.

In https://reviews.llvm.org/D43184#1005258, @rnk wrote:

> You can see it that way, but having the linker synthesize a dynamic initializer to initialize dllimport data seems a bit heroic to me. MSVC link doesn't do it. It requires making assumptions about how your C runtime runs dynamic initializers. Your DLL might not even link msvcrt, so how does the linker know where to put the initializer?


As @smeenai said, it's done by IAT tricks, not actually creating runtime initializers.

> On the other hand, you can be assured that users will ask us for this bfd linker feature indefinitely if we don't implement it. It papers over differences between the COFF and ELF object models, and mingw usually papers things over rather than pushing the cost onto ELF-oriented codebases.
> 
> Clang already creates dynamic initializers for stuff like:
> 
>   __declspec(dllimport) int imported;
>   int *const pi = &imported;
>   int foo() { return *pi; }

A related observation on the topic of this: There's a subtle difference between what both GCC and MSVC does here, compared to clang. The case with a single variable works the same in all of them, but if you have a struct with many initialized elements, GCC and MSVC will initialize as many as possible of them statically, and only fill in the dllimport ones via a dynamic initializer. clang, on the other hand, will not initialize anything statically at all if it emits a dynamic initializer.

> So, it doesn't seem like a bridge too far to make dynamic initializers for globals with vtables. It's dicey, though. It means there's a point in your program when your vptr is null. =/

Yes, and that case is also already present with the normal struct members with dllimport. That case actually turned into a real bug in trying to run Qt. Qt has got constructors that will touch a struct that contains a dllimported field. The constructor doesn't touch or use the dllimported field, only the others. This means that as long as it's built with GCC and MSVC, there's no race condition/static initialization order fiasco between the struct-with-dllimport and the constructor, since GCC and MSVC fill in all the other members statically. When built with clang though, if you're unlucky, the Qt defined constructor may run before the clang generated initializer fills in all of the struct.

I managed to work around this issue by adding a constructor priority to these structs, making sure that all the clang generated initializers run before the normal constructors: https://codereview.qt-project.org/215792

In this PoC, I also emit the initializers with a high priority (actually higher priority than can be set from normal user code) - so I think the fact that these pointers are null originally shouldn't be observable, unless using special runtime internals to hook up code to run before normal constructors.

> Do you think we should do something like local vftables for itanium instead? Can we assume all methods in the vtable will be exported by the DLL exporting the class?

This I don't know yet (I only know the details in cases that I've had to study when debugging some issue), but if you think it'd (and can hint about what to change in order to use that), I can try it and see if it works for my testcase.


Repository:
  rC Clang

https://reviews.llvm.org/D43184





More information about the cfe-commits mailing list