1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
|
{- Linux library copier and binary shimmer
-
- Copyright 2013 Joey Hess <id@joeyh.name>
-
- Licensed under the GNU GPL version 3 or higher.
-}
module Main where
import System.Process
import System.Directory hiding (isSymbolicLink)
import System.Environment
import Data.Maybe
import System.FilePath
import Control.Monad
import Data.List
import System.Posix.Files
import Control.Applicative
import Prelude
import Utility.LinuxMkLibs
main :: IO ()
main = getArgs >>= go
where
go [] = error "specify LINUXSTANDALONE_DIST"
go (top:_) = mklibs top
mklibs :: FilePath -> IO ()
mklibs top = do
fs <- lines <$> readProcess "find" [top, "-type", "f"] ""
exes <- filterM checkExe fs
libs <- parseLdd <$> readProcess "ldd" exes ""
glibclibs <- glibcLibs
let libs' = nub $ libs ++ glibclibs
libdirs <- nub . catMaybes <$> mapM (installLib installFile top) libs'
-- Various files used by runshell to set up env vars used by the
-- linker shims.
writeFile (top </> "libdirs") (unlines libdirs)
writeFile (top </> "gconvdir")
(takeDirectory $ Prelude.head $ filter ("/gconv/" `isInfixOf`) glibclibs)
let linker = Prelude.head $ filter ("ld-linux" `isInfixOf`) libs'
mapM_ (installLinkerShim top linker) exes
{- Installs a linker shim script around a binary.
-
- Note that each binary is put into its own separate directory,
- to avoid eg git looking for binaries in its directory rather
- than in PATH.
-}
installLinkerShim :: FilePath -> FilePath -> FilePath -> IO ()
installLinkerShim top linker exe = do
createDirectoryIfMissing True (top </> shimdir)
createDirectoryIfMissing True (top </> exedir)
islink <- isSymbolicLink <$> getSymbolicLinkStatus exe
if islink
then do
sl <- readSymbolicLink exe
removeFile exe
removeFile exedest
-- Assume that for a symlink, the destination
-- will also be shimmed.
let sl' = ".." </> takeFileName sl </> takeFileName sl
createSymbolicLink sl' exedest
else renameFile exe exedest
writeFile exe $ unlines
[ "#!/bin/sh"
, "exec \"$DEBUG_ME_DIR/" ++ linker ++ "\" --library-path \"$DEBUG_ME_LD_LIBRARY_PATH\" \"$DEBUG_ME_DIR/shimmed/" ++ base ++ "/" ++ base ++ "\" \"$@\""
]
setFileMode exe $ ownerExecuteMode
`unionFileModes` groupExecuteMode
`unionFileModes` otherExecuteMode
`unionFileModes` ownerReadMode
`unionFileModes` groupReadMode
`unionFileModes` otherReadMode
where
base = takeFileName exe
shimdir = "shimmed" </> base
exedir = "exe"
exedest = top </> shimdir </> base
installFile :: FilePath -> FilePath -> IO ()
installFile top f = do
createDirectoryIfMissing True destdir
callProcess "cp" [f, destdir]
where
destdir = inTop top $ takeDirectory f
checkExe :: FilePath -> IO Bool
checkExe f
| ".so" `isSuffixOf` f = return False
| otherwise = checkFileExe <$> readProcess "file" ["-L", f] ""
{- Check that file(1) thinks it's a Linux ELF executable, or possibly
- a shared library (a few executables like ssh appear as shared libraries). -}
checkFileExe :: String -> Bool
checkFileExe s = and
[ "ELF" `isInfixOf` s
, "executable" `isInfixOf` s || "shared object" `isInfixOf` s
]
|