Detecting missing __declspec(dllimport) in access

2019-07-25 00:57发布

问题:

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.

回答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.