2012-04-26 14 views
7

Toady Ho aggiornato la mia macchina di sviluppo da Ubuntu 10.04 LTS a Ubuntu 12.04 LTS (o ghc 6.12.1 a ghc 7.4.1) e mi imbatto in un comportamento molto strano nel mio progetto di currenct.GHC: errore di segmentazione in condizioni strane

Dopo alcune ore, mi hanno ridotta al seguente codice:

{-# LANGUAGE ForeignFunctionInterface #-} 
module Main where 

import Data.Word 
import Text.Printf 
import Foreign 

foreign import ccall "dynamic" 
    code_void :: FunPtr (IO()) -> (IO()) 

main :: IO() 
main = do 
    entryPtr <- (mallocBytes 2) 
    poke entryPtr (0xc390 :: Word16) -- nop (0x90); ret(0xc3) (little endian order) 

    _ <- printf "entry point: 0x%08x\n" ((fromIntegral $ ptrToIntPtr entryPtr) :: Int) 
    _ <- getLine -- for debugging 
    code_void $ castPtrToFunPtr entryPtr 
    putStrLn "welcome back" 

sto cercando di generare del codice in fase di esecuzione, saltare ad esso, e tornare di nuovo. Utilizzando un Makefile, tutto va bene:

$ make 
ghc --make -Wall -O2 Main.hs -o stackoverflow_segv 
[1 of 1] Compiling Main    (Main.hs, Main.o) 
Linking stackoverflow_segv ... 
./stackoverflow_segv 
entry point: 0x098d77e0 

welcome back 

Tuttavia, se io chiamo il binario direttamente dalla shell: (? Per fortuna)

$ ./stackoverflow_segv 
entry point: 0x092547e0 

Segmentation fault (core dumped) 

Questo comportamento è riproducibile.

Utilizzando gdb, objdump e /proc ho capito:

$ gdb -q stackoverflow_segv 
Reading symbols from /home/lewurm/stackoverflow/stackoverflow_segv...(no debugging symbols found)...done. 
(gdb) run 
Starting program: /home/lewurm/stackoverflow/stackoverflow_segv 
[Thread debugging using libthread_db enabled] 
Using host libthread_db library "/lib/i386-linux-gnu/libthread_db.so.1". 
entry point: 0x080fc810 

prima enter, a passare ad un secondo terminale:

$ cat /proc/`pgrep stackoverflow`/maps 
[...] 
08048000-080ea000 r-xp 00000000 08:01 2492678 /home/lewurm/stackoverflow/stackoverflow_segv 
080ea000-080eb000 r--p 000a2000 08:01 2492678 /home/lewurm/stackoverflow/stackoverflow_segv 
080eb000-080f1000 rw-p 000a3000 08:01 2492678 /home/lewurm/stackoverflow/stackoverflow_segv 
080f1000-08115000 rw-p 00000000 00:00 0   [heap] 
[...] 

e viceversa:

<enter> 
Program received signal SIGSEGV, Segmentation fault. 
0x0804ce3c in s2aV_info() 

Boo. Vediamo cosa fa questo codice:

$ objdump -D stackoverflow_segv | grep -C 3 804ce3c 
804ce31:  89 44 24 4c    mov %eax,0x4c(%esp) 
804ce35:  83 ec 0c    sub $0xc,%esp 
804ce38:  8b 44 24 4c    mov 0x4c(%esp),%eax 
804ce3c:  ff d0     call *%eax 
804ce3e:  83 c4 0c    add $0xc,%esp 
804ce41:  83 ec 08    sub $0x8,%esp 
804ce44:  8b 44 24 54    mov 0x54(%esp),%eax 

uhm, saltando a *%eax. Che cosa era di nuovo %eax?

(gdb) info reg eax 
eax   0x80fc810  135251984 

Beh, in realtà è solo il buffer di codice. La ricerca /proc/*/maps ci dice che questa pagina non è eseguibile (rw-p, giusto?). Ma, è la stessa situazione quando si esegue entro make.

Cosa c'è di sbagliato qui?

btw, il codice è disponibile anche tramite gist

edit: ghc bug report

+1

Funziona bene dalla riga di comando qui (openSUSE 11.4, x86_64). Potrebbe essere colpa di Ubuntu? Prova con un GHC autocostruito se ne hai il tempo. –

+0

Grazie per la tua risposta! Ho provato con il pacchetto binario da [haskell.org] (http://www.haskell.org/ghc): (1) ghc-7.4.1, ancora lo stesso problema. (2) ghc-7.2.2 non più \ o/bello! (3) ghc-6.12.1 allo stesso modo. Pensi che dovrei denunciarlo come un insetto alla gente del GHC? – lewurm

+0

Il binario alla vaniglia (unknown-linux)? Ho lo stesso comportamento di te se uso 7.2.2 invece di 7.4.1, comunque. Strano davvero. –

risposta

1

Una soluzione temporanea è quella di utilizzare mprotect(3) e impostare la regione di memoria esplicitamente come eseguibile. mprotect(3) richiede un blocco di memoria allineato, pertanto è richiesto memalign(3).

{-# LANGUAGE ForeignFunctionInterface #-} 
module Main where 

import Data.Word 
import Text.Printf 
import Foreign 
import Foreign.C.Types 

foreign import ccall "dynamic" 
    code_void :: FunPtr (IO()) -> (IO()) 

foreign import ccall "static sys/mman.h" 
    mprotect :: CUInt -> CUInt -> Int -> IO() 

foreign import ccall "static stdlib.h" 
    memalign :: CUInt -> CUInt -> IO (Ptr a) 


main :: IO() 
main = do 
    entryPtr <- memalign 0x1000 0x2 
    poke entryPtr (0xc390 :: Word16) -- nop (0x90); ret(0xc3) (little endian order) 
    let i_entry = (fromIntegral $ ptrToIntPtr entryPtr) :: Int 
    -- 0x7 = PROT_{READ,WRITE,EXEC} 
    mprotect (fromIntegral i_entry) 2 0x7 

    _ <- printf "entry point: 0x%08x\n" i_entry 
    _ <- getLine -- for debugging 
    code_void $ castPtrToFunPtr entryPtr 
    putStrLn "welcome back"