2012-04-26 37 views
7

Toady Tôi cập nhật máy phát triển của mình từ Ubuntu 10.04 LTS lên Ubuntu 12.04 LTS (hoặc ghc 6.12.1 thành ghc 7.4.1) và tôi chạy vào một hành vi rất lạ tại dự án currenct của tôi.GHC: lỗi phân đoạn trong điều kiện lạ

Sau vài giờ, tôi giảm nó để đoạn mã sau:

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

Tôi đang cố gắng để tạo ra một số mã tại thời gian chạy, nhảy với nó, và trở lại một lần nữa. Sử dụng một Makefile, mọi thứ đều tốt:

$ 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 

Tuy nhiên, nếu tôi gọi là nhị phân trực tiếp từ vỏ: (? May mắn)

$ ./stackoverflow_segv 
entry point: 0x092547e0 

Segmentation fault (core dumped) 

Hành vi này là tái sản xuất.

Sử dụng gdb, objdump/proc tôi đã tìm ra:

$ 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 

trước khi nhấn enter, tôi chuyển sang một thiết bị đầu cuối thứ hai:

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

và ngược lại:

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

Boo. Hãy xem mã này làm gì:

$ 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, nhảy tới *%eax. Điều gì đã được %eax một lần nữa?

(gdb) info reg eax 
eax   0x80fc810  135251984 

Vâng, thực ra nó chỉ là bộ đệm mã. Tìm kiếm /proc/*/maps cho chúng tôi biết rằng trang này không thể thực thi (rw-p, phải không?). Nhưng, đó là tình huống tương tự khi thực hiện trong phạm vi make.

Có gì sai ở đây?

btw, mã cũng có sẵn thông qua gist

chỉnh sửa: ghc bug report

+1

Hoạt động tốt từ dòng lệnh tại đây (openSUSE 11.4, x86_64). Có thể là lỗi của Ubuntu? Hãy thử với một GHC tự xây dựng nếu bạn có thời gian. –

+0

Cảm ơn bạn đã trả lời! Tôi đã thử nó với gói nhị phân từ [haskell.org] (http://www.haskell.org/ghc): (1) ghc-7.4.1, vẫn còn cùng một vấn đề. (2) ghc-7.2.2 không còn nữa \ o/tốt đẹp! (3) ghc-6.12.1 tương tự. Bạn có nghĩ rằng tôi nên báo cáo rằng đó là một lỗi ở những người GHC? – lewurm

+0

Vani nhị phân (unknown-linux)? Tôi nhận được hành vi tương tự như bạn nếu tôi sử dụng 7.2.2 thay vì 7.4.1, mặc dù. Thực sự kỳ lạ. –

Trả lời

1

Một giải pháp tạm thời là sử dụng mprotect(3) và thiết lập các khu vực bộ nhớ một cách rõ ràng như thực thi. mprotect(3) yêu cầu khối bộ nhớ phù hợp, do đó yêu cầu 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"