Search code examples
haskellimportffiobject-files

How do I get the address of a C global variable through the Haskell FFI?


For example, in C, I have something like this declared:

extern char _binary_res_txt_start[];

It came from this command:

ld -r -b binary -o binary.o res.txt

How can I get a Ptr in Haskell that points there? If it were a function, I'd do something like foreign import ccall "&exp" a_exp :: FunPtr (Double -> Double), but I don't think that works for variables.


Solution

  • The foreign import syntax works for variables too, as long as you use &. You can then use peekCString or packCString to read it (or peekCStringLen or packCStringLen if it's not null-terminated but you know its length). Here's a full example:

    printf 'foo\0bar' > res.txt
    ld -r -b binary -o binary.o res.txt
    
    extern char _binary_res_txt_start[];
    extern char _binary_res_txt_end[];
    
    import Foreign.C.Types
    import Foreign.C.String
    import Foreign.Ptr
    
    foreign import ccall "&_binary_res_txt_start" txt_start :: Ptr CChar
    foreign import ccall "&_binary_res_txt_end" txt_end :: Ptr CChar
    
    main = do
      str <- peekCStringLen (txt_start, txt_end `minusPtr` txt_start)
      print str
    
    "foo\NULbar"
    

    Note that while ld -b binary also produces a _binary_res_txt_size symbol, it's no longer useful now that ASLR is a thing. See Why doesn't a linked binary file's _size symbol work correctly? for details on that.