2012-04-26 13 views
7

टोडी मैंने उबंटू 10.04 एलटीएस से उबंटू 12.04 एलटीएस (या ghc 6.12.1 से ghc 7.4.1) में अपनी विकासशील मशीन अपडेट की और मैं अपने धारावाहिक परियोजना में एक बहुत ही अजीब व्यवहार में भाग गया।जीएचसी: अजीब परिस्थितियों में सेगमेंटेशन गलती

कुछ घंटे के बाद, मैं इसे निम्नलिखित कोड करने के लिए कम:

{-# 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" 

मैं, रन-टाइम में कुछ कोड उत्पन्न इसे करने के लिए कूद, और फिर से वापस आने के लिए कोशिश कर रहा हूँ। , अगर मैं खोल से सीधे द्विआधारी फोन

$ 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 

हालांकि: एक Makefile का उपयोग करना, सब कुछ ठीक है (? सौभाग्य से)

$ ./stackoverflow_segv 
entry point: 0x092547e0 

Segmentation fault (core dumped) 

यह व्यवहार प्रतिलिपि प्रस्तुत करने योग्य है।

का उपयोग gdb, objdump और /proc मैं पता लगा:

$ 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 

दबाने में प्रवेश करने से पहले, मैं एक दूसरे टर्मिनल के लिए स्विच:

$ 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] 
[...] 

और फिर से वापस:

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

बू। चलो देखते हैं क्या इस कोड को करता है:

$ 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, *%eax पर कूद। %eax फिर से क्या था?

(gdb) info reg eax 
eax   0x80fc810  135251984 

ठीक है, वास्तव में यह केवल कोड बफर है। /proc/*/maps देखकर हमें बताता है कि यह पृष्ठ निष्पादन योग्य नहीं है (rw-p, है ना?)। लेकिन, यह उसी स्थिति में है जब इसे make के भीतर निष्पादित किया जाता है।

यहां क्या गलत है?

btw, कोड भी माध्यम से gist

संपादित करें उपलब्ध है: ghc bug report

+1

यहां कमांड लाइन से ठीक काम करता है (openSUSE 11.4, x86_64)। उबंटू की गलती हो सकती है? यदि आपके पास समय है तो स्वयं निर्मित जीएचसी के साथ प्रयास करें। –

+0

आपके उत्तर के लिए धन्यवाद! मैंने इसे [haskell.org] (http://www.haskell.org/ghc) से बाइनरी पैकेज के साथ करने की कोशिश की: (1) ghc-7.4.1, अभी भी वही समस्या है। (2) ghc-7.2.2 अब और नहीं \ अच्छा/अच्छा! (3) ghc-6.12.1 इसी प्रकार। क्या आपको लगता है कि मुझे जीएचसी लोगों में एक बग के रूप में रिपोर्ट करनी चाहिए? – lewurm

+0

वेनिला बाइनरी (अज्ञात-लिनक्स)? यदि मैं 7.4.1 के बजाय 7.2.2 का उपयोग करता हूं, तो मुझे वही व्यवहार मिलता है। वास्तव में अजीब। –

उत्तर

1

एक अस्थायी समाधान mprotect(3) का उपयोग करें और स्मृति क्षेत्र स्पष्ट रूप से सेट निष्पादन योग्य के रूप में किया जा सके। mprotect(3) को एक गठबंधन स्मृति ब्लॉक की आवश्यकता है, इसलिए 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"