[GHC] #15338: ghc-pkg misbehaves after possible miscompilation on m68k and sh4

GHC ghc-devs at haskell.org
Wed Jul 11 19:05:48 UTC 2018


#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


More information about the ghc-tickets mailing list