Problem with coupling shared object symbol visibility with protection

Rainer Schuetze via Digitalmars-d digitalmars-d at puremagic.com
Tue Jan 27 14:29:38 PST 2015



On 20.01.2015 13:23, Benjamin Thaut wrote:
> I'm currently working on Windows DLL support which has stronger rules
> than linux shared objects for which symbols actually get exported from a
> shared library. But as we want to replicate the same behaviour on linux
> using symbol visibility (e.g. gcc 4 -fVisibility=hidden) this issue also
> applies to linux once implemented.
>
> Currently export means two things:
> - the symbol is publicy accessible (same as public)
> - the symbol will be accisble across shared library boundaries
>
>
> This has the following issue:
>
> export void templateFunc(T)()
> {
>    someHelperFunc();
> }
>
> private void someHelperFunc()
> {
>
> }
>
> And you don't even have to go into phobos to hit this problem. It is
> already in druntime see core.time.FracSec._enforceValid
>
> This works with the current linux shared objects because they simply
> export all symbols. But once only symbols with export get exported this
> breaks.

I would not mind if we export all symbols on Windows aswell. It doesn't 
seem to bother a lot of people for the linux version, even though it's 
unsafer and slower than on Windows (at least that was my experience more 
than 10 years ago). It might get us a first working version without 
adding "export" throughout the druntime/phobos source code.

>
> The problem here is that you don't want to make someHelperFunc() export
> because that would mean users could call it directly, but you want it to
> be available for cross shared library calls. The cross shared library
> call happens if a template is instanced from a different shared library
> / executable than the module it was originally located in.
>
> There are two solutions for this.
>
> 1) Given that export is transitive (that means if a struct or class is
> declared export every member that is _not_ private will be accessible
> across shared library boundaries. This behaviour is required to make the
> export protection level work on windows)
>
> You can now do the following:
>
> export struct SomeStruct
> {
>    static public void templateFunc(T)()
>    {
>      someHelperFunc();
>    }
>
>    static package void someHelperFunc()
>    {
>
>    }
> }
>
> Because of the transitivity someHelperFunc will be exported but still
> not be callable by the user directly. This can be used to fix the issue
> in core.time but may not be so well suited if you want the template to
> be on module level instead of nesting it into a struct.
>
> 2) Make export an attribute. If export is no longer an protection level
> but instead an attribute this issue can easily be solved by doing.
>
> export public void templateFunc(T)()
> {
>    someHelperFunc();
> }
>
> export private void someHelperFunc()
> {
>
> }
>
> But this would require grammar changes. Which are generally avoided if
> possible.

I don't have a clear favorite, but the second version makes it clearer 
that visibility and protection are separate issues.

A note on:
 > export public void templateFunc(T)()

I don't think it is well defined what exporting a template is supposed 
to mean. My guess: whenever an instance of the template is created, its 
symbols are exported. This could make for a lot of duplicate symbols 
across multiple DLLs, though. Maybe there should be a method of 
explicitly exporting/importing a template instance from another DLL, e.g.

export alias symbol = templateFunc!int;

Please note, that the problem raised above by Benjamin applies just as 
well to non-exported templates.


More information about the Digitalmars-d mailing list