module Linker {-(merge)-} where

import Control.Arrow
import Data.Int
import qualified Data.Map as M
import Data.Word
import Debug.Trace

import ObjectFile
import Utils

merge :: ObjectFile -> ObjectFile -> ObjectFile
merge (ObjectFile lsecs) (ObjectFile rsecs) =
	ObjectFile (mergeSections lsecs rsecs)

mergeSections xs ys =
	number $ adjustBases 0 $ map snd $ M.toList $ M.unionWith mergeSection xs' ys'
	where
		convert = M.fromList . map ((secName &&& id) . snd) . M.toList
		xs' = convert xs
		ys' = convert ys

mergeSection :: Section -> Section -> Section
mergeSection x y | (secFlags x) == (secFlags y) =
	Section
		(secName x)
		0
		((secSize x)+(secSize y))
		(secFlags x)
		(mergeSymbols x y')
		(mergeData x y')
		(mergeRelocs x y')
	where
		y' = rebaseSection (secBase x + secSize x) y
mergeSection _ _ = error "Fail"

mergeSymbols x y = (M.unionWith mergeSymbol `on` secSymbols) x y

mergeData x y = ((++) `on` secData) x y
mergeRelocs x y = ((++) `on` secRelocs) x y

adjustBases :: Word64 -> [Section] -> [Section]
adjustBases _    [] = []
adjustBases base (x:xs) = x' : adjustBases base' xs
	where
		x' = rebaseSection base x
		base' = roundUp 4096 (base + secSize x')

roundUp align x = x' - (x' `mod` align) where x' = (x + align - 1)

rebaseSection base section =
	trace ("rebaseSection: "++show section++" new base "++show base++" baseDiff " ++ show baseDiff) $
		modifySymbols (fmap $ rebaseSymbol $ fromIntegral . (+) baseDiff . fromIntegral) $
		section { secBase = base }
	where
		baseDiff = fromIntegral (base - secBase section) :: Int64

rebaseSymbol f sym = sym { symValue = f (symValue sym) }

number :: [Section] -> M.Map Int Section
number = M.mapWithKey numberSection . M.fromList . zip [1..]
