Re: problem with msvc linker - cannot build orafce - Mailing list pgsql-hackers

From Craig Ringer
Subject Re: problem with msvc linker - cannot build orafce
Date
Msg-id CAMsr+YFjHG8ROjZsCaQ3vQ5RXt+XghH_LtFSQ4CtTV=a0rxbfw@mail.gmail.com
Whole thread Raw
In response to Re: problem with msvc linker - cannot build orafce  (Chapman Flack <chap@anastigmatix.net>)
Responses Re: problem with msvc linker - cannot build orafce  (Craig Ringer <craig@2ndquadrant.com>)
List pgsql-hackers


On 24 November 2015 at 07:12, Chapman Flack <chap@anastigmatix.net> wrote:

What I (think I) took away from it was:

1.  Un-PGDLLIMPORTed references to global *functions* work ok.
    Maybe they are thunked and a little less efficient, but they work.

2.  Un-PGDLLIMPORTed references to global *variables*, not so much.
    They used to silently link (at least on some buildfarm critters)
    but hold bogus data (maybe a thunk, taken as data?).

3.  The one concrete *action* taken in the course of that thread was to
    tweak the build process to make sure such cases at least *fail*
    because that's better than silent bogosity.

Correct on all points.

The question that interests me most right now: how, if at all, can the
extension author/maintainer work around this issue when it crops up?

AFAIK the only way to do it is to use the .pdb files to find the address of the variable's storage, then create a pointer-to-var that way, after adjusting for the DLL/EXE's base load address - which on some Windows versions is randomized.

You might be able to do it with dbghelp.dll . I haven't looked into it properly.
 
Obviously, the Right Thing To Do is report it and get the PGDLLIMPORT
added here, but still for years to come the extension will have to cope
with being built against PG distributions that lack it.

It's safe to add in a point-release because nobody could've been using it before, so all you have to do is require a certain minimum point release. We all know how easy it is to get users to apply point releases ;) 

If we annotated extern functions too, we could build on UNIX with -fvisibility=hidden and get immediate linker errors on *nix too. As far as I can tell gcc doesn't have the option to control default visibility separately for functions and data exports. We do that on Windows by generating a .DEF file, since Windows doesn't support it directly either. Doing that on *nix would require using readelf then using --retain-symbols-file at link time. Imagine how popular that'd be.

So currently we rely on the buildfarm complaining if things are broken on Windows, but of course that only works for core and in-tree extensions. (Who cares about anything else, right?).

See https://gcc.gnu.org/wiki/Visibility for details on visibility.

Now, I thought I spotted, somewhere in that long thread, the hint of an
idea that the magic works as long as the *extension* has the variable
declared PGDLLIMPORT, even if it wasn't declared that way when the PG
executable itself was built. Anybody else remember that, or did I
imagine it?

I haven't tested, but I believe you can declare it with an extra level of pointer indirection, without __declspec(dllimport), and chase the pointer.

As I understand it (and I'm far from an expert here) the symbol in the PE symbol table points to the address of a pointer to the data. MSVC doesn't know if your "extern" references a variable in the same module (in which case you don't have that indirection) or will be resolved by dynamic linking to point to a pointer to the data. I find this bizarre, given that ELF "just does it"... but PE is pretty weird and a fair bit older, a hacked-up variant of COFF. Anyway, __declspec(dllimport) tells MSVC "I'm going to be linking this from an external DLL, do the pointer chasing for me please".

See https://msdn.microsoft.com/en-us/library/aa271769(v=vs.60).aspx "Using __declspec(dllexport) and __declspec(dllimport) on Data".

I *think* it's safe to do this even if the var is declared __declspec(dllexport). The dllexport declaration makes sure the export is present without the use of a .DEF file to force it. You can __declspec(dllimport) whether it was __declspec(dllexported)'ed or exported via a .DEF file.

We auto-export functions in our DEF files, but not variables.
 
You *might* get away with creating a separate C file (how about
chamberofhorrors.c?) that, rather revoltingly, *doesn't* include the
proper PostgreSQL .h files, only duplicates the necessary declarations
with PGDLLIMPORT added, and exports some getter/setter methods
to the rest of the extension code.  (I like the idea of one chamberofhorrors
better than scattering such rubbish all over the project.)

I don't think that's necessary, per above. You just have to access the vars via pointer indirection always, so long as *any* Pg version you support has ever lacked dllexport or DEF entry, so you can't dllimport the var.

You could enable direct dllimport if PG_VERSION_NUM shows you're on a new enough version, but you'd need to use conditionally compiled inlines or something to switch between the methods of accessing it, so there's not much point. You just declare

extern int* log_min_messages_p;

... and use that, probably also #define'ing log_min_messages away after including the Pg headers so that you can't reference it accidentally.

--
 Craig Ringer                   http://www.2ndQuadrant.com/
 PostgreSQL Development, 24x7 Support, Training & Services

pgsql-hackers by date:

Previous
From: Amit Langote
Date:
Subject: Re: Minor comment edits in nodeGather.c
Next
From: Pavel Stehule
Date:
Subject: Re: custom function for converting human readable sizes to bytes