Problem with coupling shared object symbol visibility with protection

Walter Bright via Digitalmars-d digitalmars-d at puremagic.com
Mon Jan 26 14:24:23 PST 2015


On 1/20/2015 4:23 AM, 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.
>
> 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.

exporting a template and then having the user instantiate outside of the library 
doesn't make a whole lot of sense, because the instantiation won't be there in 
the library. The library will have to instantiate every use case. If the 
compiler knows the library instantiated it, it won't re-instantiate it locally.


> 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.
>
> There would be a third option, which I rather avoid. Doing a
> "pramga(forceExport)" or something like that.
>
> My implementation, which I ran into this issue with, currently usses approach 1.
> What do you think how this sould be solved?

I'd be thinking that what a shared library exports is fixed, and expecting the 
user to make more instantiations makes for a fairly unresolvable issue. The 
solution is design the templates to be expanded only on one side of the dll/exe 
boundary, not straddle it.



More information about the Digitalmars-d mailing list