Opened 12 months ago

Last modified 8 months ago

#12875 new bug

GHC fails to link all StaticPointers-defining modules of a library in an executable

Reported by: bgamari Owned by:
Priority: normal Milestone: 8.4.1
Component: Compiler Version: 8.0.1
Keywords: StaticPointers Cc: facundo.dominguez, mboes, dcoutts
Operating System: Unknown/Multiple Architecture: Unknown/Multiple
Type of failure: None/Unknown Test Case:
Blocked By: Blocking:
Related Tickets: Differential Rev(s):
Wiki Page:

Description (last modified by bgamari)

Consider that you have a package called lib which exposes these modules,

module ALib.Types where

data AThing = AThing String
            deriving (Show)


{-# LANGUAGE StaticPointers #-}
module ALib.Things where

import GHC.StaticPtr
import ALib.Types

thing1 :: StaticPtr AThing
thing1 = static (AThing "hello")

Now consider that you have a server which reads a StaticKey of StaticPtr AThing and shows it,

import ALib.Types

main :: IO ()
main = do
    key <- readFingerprint <$> getContents :: IO StaticKey
    Just thing <- unsafeLookupStaticPtr key :: IO (Maybe (StaticPtr AThing))
    print $ deRefStaticPtr thing

Naturally this executable will link against lib. However, this executable as-written will fail if given the key of ALib.Things.thing1. Fixing this requires that the executable explicitly import and use a definition from ALib.Things.

The problem appears to be that the linker elides the ALib.Things object from the final executable unless it refers to a symbol. Note that things also work fine if the server executable is dynamically linked.

Change History (7)

comment:1 Changed 12 months ago by bgamari

Description: modified (diff)

See https://github.com/bgamari/T12875-repro for a functional example of this.

comment:2 Changed 12 months ago by bgamari

Cc: dcoutts added

comment:3 Changed 12 months ago by facundo.dominguez

Perhaps it is possible to stop the linker from removing modules from the final executable when they use the StaticPointers language extension. That failing, the simplest solution could be to document that the module defining static pointers needs to be imported transitively into the main module of an executable supposed to find them.

comment:4 Changed 12 months ago by mboes

That failing, the simplest solution could be to document that the module defining static pointers needs to be imported transitively into the main module of an executable supposed to find them.

That solution would be as anti-modular as non StaticPtr remote tables. Losing modularity would defeat much of the purpose of this language extension.

Surely we can let the linker know that downstream modules *might* depend on ALib.Things?

comment:5 Changed 12 months ago by simonpj

Can you explain a bit more what the problem is? In the description what is things1? In general if a Haskell process deserialises, say from a network message, the key of a static pointer, there is no guarantee that it'll be in its SPT.

Also I don't understand the API. What's unsafe about looking up a static pointer? Surely we shouldn't be exposing low-level details like fingerprints?

comment:6 Changed 12 months ago by mboes

Also I don't understand the API. What's unsafe about looking up a static pointer? Surely we shouldn't be exposing low-level details like fingerprints?

I think both of these points were already discussed in previous tickets (but I don't have pointers handy right now). The only lookup operation we have right now is one that doesn't check that you're looking up at the right type. That's because we don't yet store in the SPT what the type of the pointer really should be.

As for fingerprints: an alternative is to expose

serializeStaticPtr :: ... => StaticPtr a -> ByteString
unserializeStaticPtr :: ... => ByteString -> StaticPtr a

As discussed, not a viable API, because then base depends on bytestring. That API would have the compiler make more assumptions than it needs to. With the current API the user has the freedom to serialize/deserialize in whatever way she wants (including e.g. integers created by a perfect hash function). IOW we try to bake into the compiler as little as possible, leaving the rest to implementation choices in "userland" libraries.

comment:7 Changed 8 months ago by bgamari

Milestone: 8.2.18.4.1

Given that 8.2.1-rc1 is imminent, I'm bumping these off to the 8.4

Note: See TracTickets for help on using tickets.