#15338: ghc-pkg misbehaves after possible miscompilation on m68k and sh4 -------------------------------------+------------------------------------- Reporter: glaubitz | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: 8.6.1 Component: Compiler | Version: 8.4.3 Resolution: | Keywords: Operating System: Linux | Architecture: m68k Type of failure: Incorrect result | Test Case: at runtime | Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by slyfox): Replying to [comment:4 slyfox]:
Normally '''COPY''' relocations are used only for immutable ('''const''' in C land) data. But '''_closure'''s are mutable. I'll double-check generated C code and file a toolchain bug upstream.
James (jrtc27) pointed out that '''COPY''' relocations are fine for mutable data as long as shared library allows interposition of the symbol. James also noted that GHC uses '''-Bsymbolic''' which forbids symbol interposition and binds global symbols to library's definition. '''-Bsymbolic''' is set in '''GHC'''s driver: http://git.haskell.org/ghc.git/blob/HEAD:/compiler/main/SysTools.hs#l550 Thus smaller C-only reproducer that illustrates the problem is the following: {{{#!c /* lib.c: */ int things[] = { 99,98,97,96 }; int shlib_f(int i) { return things[i]; } }}} {{{#!c /* bin.c */ #include <stdio.h> /* declarations from lib.c */ extern int things[]; int shlib_f(int i); int main() { printf("main (before store): things[0] = %i\n", things[0]); printf("shlib (before store): things[0] = %i\n", shlib_f(0)); things[0] = 45; /* check if library sees 'things' changed. It should */ printf("main (after store): things[0] = %i\n", things[0]); printf("shlib (after store): things[0] = %i\n", shlib_f(0)); return 0; } }}} {{{#!sh #/bin/bash # a.sh #cross=sh4-unknown-linux-gnu- cc=${cross}gcc cflags="-O1 -Wall" $cc $cflags -shared -fPIC lib.c -o libbug.so -Wl,-Bsymbolic $cc $cflags -fno-PIC -fno-PIE -no-pie bin.c -o bug.no-pie -L. -lbug -Wl,-rpath=. $cc $cflags -fPIC -fPIE -pie bin.c -o bug.pie -L. -lbug -Wl,-rpath=. echo "target: ${cross}; emulator=${emulator}; no-pie:" ${emulator} ./bug.no-pie echo "target: ${cross}; emulator=${emulator}; pie:" ${emulator} ./bug.pie }}} Here we define a shared library with '''things''' global symbol and '''shlib_f()''' that refers to that global symbol. Here is how things break (even on '''x86_64'''): {{{ $ cross=x86_64-pc-linux-gnu- emulator= ./a.sh target: x86_64-pc-linux-gnu-; emulator=; no-pie: main (before store): things[0] = 99 shlib (before store): things[0] = 99 main (after store): things[0] = 45 shlib (after store): things[0] = 99 target: x86_64-pc-linux-gnu-; emulator=; pie: main (before store): things[0] = 99 shlib (before store): things[0] = 99 main (after store): things[0] = 45 shlib (after store): things[0] = 99 }}} Note: the value of '''things[0]''' disagrees between binary and library copy. That's how we get '''stdout''' closure evaluated twice. It matter because '''stdout''' is devined via '''unsafePerformIO''': {{{#!hs -- libraries/base/GHC/IO/Handle/FD.hs stdout :: Handle {-# NOINLINE stdout #-} stdout = unsafePerformIO $ do -- ToDo: acquire lock setBinaryMode FD.stdout enc <- getLocaleEncoding mkHandle FD.stdout "<stdout>" WriteHandle True (Just enc) nativeNewlineMode{-translate newlines-} (Just stdHandleFinalizer) Nothing }}} Here we effectively allocate the buffer cache as many times as '''stdout''' is evaluated. This breaks the invariant of '''unsafePerformIO''' being evaluated only once. -- Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/15338#comment:5> GHC <http://www.haskell.org/ghc/> The Glasgow Haskell Compiler