1

I'm looking for solutions to a fun new wrinkle in Windows support for the PostgreSQL project.

When plugin DLLs are loaded into the main executable with a LoadLibrary call they expect the dynamic linker to resolve references to functions and global variables exposed by postgres.exe.

It's easy to forget to put a __declspec(dllimport) annotation, or rather, the PGDLLIMPORT macro that expands to it, on an extern that's accessed via a DLL, since almost all PostgreSQL development and testing happens on Linux and OS X, where none of this stuff applies.

The project relies on automated testing to detect when a __declspec(dllimport) is missing on a function, as this causes linker errors. Until yesterday the assumption was that the same was true for global variables, but it isn't; it turns out that dynamic linkage succeeds silently, producing a garbage result.

So - I'm looking for advice on how to detect and prevent such illegal accesses, where a global is not __declspec(dllimport)'ed.

This is complicated by the fact that on Windows, PostgreSQL's build system generates .def files that just export everything. (Not my doing, but I can't change it, yes, I know). This means that even if there's no PGDLLIMPORT marking the site __declspec(dllexport) during building of the main executable, the symbol is still exported.

Ideas? Is there any way to get the linker to throw a runtime error when an extern global is defined in another module and the extern isn't properly __declspec(dllimport) annotated?

If the project stopped generating .def files and instead used PGDLLIMPORT annotations that expand to __declspec(dllexport) when compiling the .exe, and __declspec(dllimport) when compiling plugins that use the exe's API, would that produce linker errors when a symbol isn't annotated properly? Is there any alternative to that?

I'm currently looking for more info and I'm going to be writing a few test programs to try to test ideas, but I'm far from an expert on Windows development and I'm looking for an authoritative "the right way to do it" if possible.

Craig Ringer
  • 307,061
  • 76
  • 688
  • 778
  • 2
    You want to make it *easier* for somebody to export global variables??? Let them suffer! And outlaw the practice as a project guideline. The alternatives are just too simple and superior. – Hans Passant Feb 05 '14 at 11:12
  • @HansPassant I completely agree, but unfortunately am not presently in a position to change the practice, only fix any resulting bugs. – Craig Ringer Feb 05 '14 at 11:13
  • Posted follow-up question here: http://stackoverflow.com/questions/21593281/generating-correct-def-files-to-export-non-static-functions-and-globals – Craig Ringer Feb 06 '14 at 03:20
  • Also, useful article (for the Windows-specific bits): http://www.lurklurk.org/linkers/linkers.html – Craig Ringer Feb 06 '14 at 03:23
  • 1
    @CraigRinger So, will it be trilogy? Or your problem can be considered fully solved? – qwm Feb 06 '14 at 19:05
  • @qwm I hope to find out for sure today ;-) but with a way to identify the symbol types in objdump output it looks good. I hugely appreciate your time and assistance, as I'm out of my depth with more than the simplest Windows-specific compiler/linker issues, and would've spent a *lot* more time staring at half-understood diassemblies and flailing. – Craig Ringer Feb 06 '14 at 22:58

1 Answers1

1

The best way is to make it clear in .def file to linker that you export data, not code:

EXPORTS
   i1 DATA
   i2 DATA

or not use .def at all. It takes precedence and defaults exported symbol to code.

Let's look what happens when you link your plugin to such malformed .lib file. And assume you declare:

   int __declspec(dllimport) i1;
   extern int i2;

This means your .obj file will have external dependencies __imp__i1 and _i2. And while the first one will point to the real imported symbol, jmp-stub

   jmp [addr]  ; FF 25 xx xx xx xx

will be generated for the second one ('cause it considered to be the code symbol) which is meant to fix difference between two kinds of call:

   call [addr] ; FF 15 xx xx xx xx
   call addr   ; E8 xx xx xx xx

Thus, your i2 will actually point to code section address of jmp-stub, so its value gonna be0x????25ff.

qwm
  • 1,005
  • 8
  • 10
  • Thankyou. I'll take a look at the generated def files to verify they're marking the exports correctly. This is a plausible explanation for the observed behaviour. I'd rather the "not use a .def file at all" approach, personally, but unfortunately that's not an option for non-technical reasons at present. I'll follow up (it will take me a while) with confirmation about whether the defs contain `DATA` annotations or nt. – Craig Ringer Feb 05 '14 at 09:33
  • @gwm Looks like the .def generator (which loops over `dumpbin` output) is not annotating exported globals with `DATA`. So that needs fixing, at least. I'll see what's involved and comment on how that goes. – Craig Ringer Feb 05 '14 at 12:03
  • @CraigRinger any progress? – qwm Feb 05 '14 at 16:38
  • @qvm In Australia, busy sleeping ;-) . Should have time to test patching the def generator today. – Craig Ringer Feb 05 '14 at 23:13
  • @qvm The def generator code uses dumpbin output, and there doesn't seem to be a good way to get the symbol type (data, function) from the dumpbin output. Fun. So that becomes a separate project. I'm convinced by this explanation, though. Thankyou _very_ much for taking the time to introduce me to a new and kind of gruesome twist to the Windows Linking Quirks saga. – Craig Ringer Feb 06 '14 at 02:56
  • @qvm Found a v.useful link explaining some of the issues, though: http://msdn.microsoft.com/en-us/library/aa271769(v=vs.60).aspx - particularly that an extra layer of indirection is assumed on extern vars w/o `__declspec(dllimport)`. And confirmed symbols default to code if DATA or CONSTANT isn't specified - http://msdn.microsoft.com/en-us/library/hyx1zcd3.aspx . Didn't find any reference to confirm the DEF file overrides __declspec annotations though, or in fact anything that explains the ordering/precedence/merging rules for multiple specifications except around handling of `NONAME`. – Craig Ringer Feb 06 '14 at 03:17
  • 1
    `Didn't find any reference to confirm the DEF file overrides __declspec annotations though` - you may try to confirm it by testing on a toy project. I didn't read any articles stating that, just parsed compiler/linker output – qwm Feb 06 '14 at 10:11