summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.ruby-version1
-rw-r--r--Rakefile0
-rw-r--r--elna.cabal5
-rw-r--r--lib/Language/Elna/Object/Elf.hs464
-rw-r--r--rakelib/cross.rake307
-rw-r--r--src/Main.hs102
6 files changed, 868 insertions, 11 deletions
diff --git a/.ruby-version b/.ruby-version
new file mode 100644
index 0000000..fa7adc7
--- /dev/null
+++ b/.ruby-version
@@ -0,0 +1 @@
+3.3.5
diff --git a/Rakefile b/Rakefile
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Rakefile
diff --git a/elna.cabal b/elna.cabal
index 136cd43..d18a628 100644
--- a/elna.cabal
+++ b/elna.cabal
@@ -17,7 +17,9 @@ extra-doc-files: TODO README
common warnings
build-depends:
base >=4.7 && <5,
+ bytestring ^>= 0.12.1,
megaparsec ^>= 9.6,
+ vector ^>= 0.13.1,
text ^>= 2.0
ghc-options: -Wall
default-extensions:
@@ -25,6 +27,7 @@ common warnings
ExplicitForAll,
LambdaCase,
OverloadedStrings,
+ DuplicateRecordFields,
RecordWildCards
default-language: GHC2021
@@ -36,6 +39,7 @@ library elna-internal
Language.Elna.Intermediate
Language.Elna.Location
Language.Elna.NameAnalysis
+ Language.Elna.Object.Elf
Language.Elna.Parser
Language.Elna.SymbolTable
Language.Elna.TypeAnalysis
@@ -45,7 +49,6 @@ library elna-internal
hashable ^>= 1.4.3,
parser-combinators ^>= 1.3,
transformers ^>= 0.6.1,
- vector ^>= 0.13.1,
unordered-containers ^>= 0.2.20
hs-source-dirs: lib
diff --git a/lib/Language/Elna/Object/Elf.hs b/lib/Language/Elna/Object/Elf.hs
new file mode 100644
index 0000000..1a4bcc8
--- /dev/null
+++ b/lib/Language/Elna/Object/Elf.hs
@@ -0,0 +1,464 @@
+module Language.Elna.Object.Elf
+ ( ByteOrder(..)
+ , Elf32_Addr
+ , Elf32_Off
+ , Elf32_Half
+ , Elf32_Word
+ , Elf32_Sword
+ , Elf32_Ehdr(..)
+ , Elf32_Rel(..)
+ , Elf32_Rela(..)
+ , Elf32_Shdr(..)
+ , Elf32_Sym(..)
+ , ElfEncodingError(..)
+ , ElfIdentification(..)
+ , ElfMachine(..)
+ , ElfVersion(..)
+ , ElfClass(..)
+ , ElfData(..)
+ , ElfType(..)
+ , ElfSectionType(..)
+ , ElfSymbolBinding(..)
+ , ElfSymbolType(..)
+ , elf32Addr
+ , elf32Half
+ , elf32Off
+ , elf32Shdr
+ , elf32Sword
+ , elf32Word
+ , elf32Ehdr
+ , elf32Rel
+ , elf32Rela
+ , elf32Sym
+ , elfIdentification
+ , rInfo
+ , stInfo
+ ) where
+
+import Control.Exception (Exception(..))
+import Data.Bits (Bits(..))
+import qualified Data.ByteString.Builder as ByteString.Builder
+import Data.Int (Int32)
+import Data.Word (Word8, Word16, Word32)
+import qualified Data.ByteString as ByteString
+
+-- * Data types.
+
+type Elf32_Addr = Word32 -- ^ Unsigned program address.
+type Elf32_Half = Word16 -- ^ Unsigned medium integer.
+type Elf32_Off = Word32 -- ^ Unsigned file offset.
+type Elf32_Sword = Int32 -- ^ Signed large integer.
+type Elf32_Word = Word32 -- ^ Unsigned large integer.
+
+data ElfClass
+ = ELFCLASSNONE -- ^ Invalid class.
+ | ELFCLASS32 -- ^ 32-bit objects.
+ | ELFCLASS64 -- ^ 64-bit objects.
+ deriving Eq
+
+instance Show ElfClass
+ where
+ show ELFCLASSNONE = "ELFCLASSNONE"
+ show ELFCLASS32 = "ELFCLASS32"
+ show ELFCLASS64 = "ELFCLASS64"
+
+instance Enum ElfClass
+ where
+ toEnum 0 = ELFCLASSNONE
+ toEnum 1 = ELFCLASS32
+ toEnum 2 = ELFCLASS64
+ toEnum _ = error "Unknown Elf class"
+ fromEnum ELFCLASSNONE = 0
+ fromEnum ELFCLASS32 = 1
+ fromEnum ELFCLASS64 = 1
+
+-- | Data encoding.
+data ElfData
+ = ELFDATANONE
+ | ELFDATA2LSB
+ | ELFDATA2MSB
+ deriving Eq
+
+instance Show ElfData
+ where
+ show ELFDATANONE = "ELFDATANONE"
+ show ELFDATA2LSB = "ELFDATA2LSB"
+ show ELFDATA2MSB = "ELFDATA2MSB"
+
+instance Enum ElfData
+ where
+ toEnum 0 = ELFDATANONE
+ toEnum 1 = ELFDATA2LSB
+ toEnum 2 = ELFDATA2MSB
+ toEnum _ = error "Unknown elf data"
+ fromEnum ELFDATANONE = 0
+ fromEnum ELFDATA2LSB = 1
+ fromEnum ELFDATA2MSB = 2
+
+data ElfIdentification = ElfIdentification ElfClass ElfData
+ deriving Eq
+
+-- | ELF header.
+data Elf32_Ehdr = Elf32_Ehdr
+ { e_ident :: ElfIdentification
+ , e_type :: ElfType
+ , e_machine :: ElfMachine
+ , e_version :: ElfVersion
+ , e_entry :: Elf32_Addr
+ , e_phoff :: Elf32_Off
+ , e_shoff :: Elf32_Off
+ , e_flags :: Elf32_Word
+ , e_ehsize :: Elf32_Half
+ , e_phentsize :: Elf32_Half
+ , e_phnum :: Elf32_Half
+ , e_shentsize :: Elf32_Half
+ , e_shnum :: Elf32_Half
+ , e_shstrndx :: Elf32_Half
+ } deriving Eq
+
+-- | Section header.
+data Elf32_Shdr = Elf32_Shdr
+ { sh_name :: Elf32_Word
+ , sh_type :: ElfSectionType
+ , sh_flags :: Elf32_Word
+ , sh_addr :: Elf32_Addr
+ , sh_offset :: Elf32_Off
+ , sh_size :: Elf32_Word
+ , sh_link :: Elf32_Word
+ , sh_info :: Elf32_Word
+ , sh_addralign :: Elf32_Word
+ , sh_entsize :: Elf32_Word
+ } deriving Eq
+
+data ElfMachine
+ = ElfMachine Elf32_Half
+ | EM_NONE -- ^ No machine.
+ | EM_M32 -- ^ AT&T WE 32100.
+ | EM_SPARC -- ^ SPARC.
+ | EM_386 -- ^ Intel Architecture.
+ | EM_68K -- ^ Motorola 68000.
+ | EM_88K -- ^ Motorola 88000.
+ | EM_860 -- ^ Intel 80860.
+ | EM_MIPS -- ^ MIPS RS3000 Big-Endian.
+ | EM_MIPS_RS4_BE -- ^ MIPS RS4000 Big-Endian.
+ | EM_RISCV -- ^ RISC-V.
+ deriving Eq
+
+instance Enum ElfMachine
+ where
+ toEnum 0x0 = EM_NONE
+ toEnum 0x1 = EM_M32
+ toEnum 0x2 = EM_SPARC
+ toEnum 0x3 = EM_386
+ toEnum 0x4 = EM_68K
+ toEnum 0x5 = EM_88K
+ toEnum 0x7 = EM_860
+ toEnum 0x8 = EM_MIPS
+ toEnum 0xa = EM_MIPS_RS4_BE
+ toEnum 0xf3 = EM_RISCV
+ toEnum x = ElfMachine $ fromIntegral x
+ fromEnum EM_NONE = 0x0
+ fromEnum EM_M32 = 0x1
+ fromEnum EM_SPARC = 0x2
+ fromEnum EM_386 = 0x3
+ fromEnum EM_68K = 0x4
+ fromEnum EM_88K = 0x5
+ fromEnum EM_860 = 0x7
+ fromEnum EM_MIPS = 0x8
+ fromEnum EM_MIPS_RS4_BE = 0xa
+ fromEnum EM_RISCV = 0xf3
+ fromEnum (ElfMachine x) = fromIntegral x
+
+data ElfVersion
+ = ElfVersion Elf32_Word
+ | EV_NONE -- ^ Invalid versionn.
+ | EV_CURRENT -- ^ Current version.
+ deriving Eq
+
+instance Enum ElfVersion
+ where
+ toEnum 0 = EV_NONE
+ toEnum 1 = EV_CURRENT
+ toEnum x = ElfVersion $ fromIntegral x
+ fromEnum EV_NONE = 0
+ fromEnum EV_CURRENT = 1
+ fromEnum (ElfVersion x) = fromIntegral x
+
+data ElfType
+ = ElfType Elf32_Half
+ | ET_NONE -- ^ No file type.
+ | ET_REL -- ^ Relocatable file.
+ | ET_EXEC -- ^ Executable file.
+ | ET_DYN -- ^ Shared object file.
+ | ET_CORE -- ^ Core file.
+ | ET_LOPROC -- ^ Processor-specific.
+ | ET_HIPROC -- ^ Processor-specific.
+ deriving Eq
+
+instance Enum ElfType
+ where
+ toEnum 0 = ET_NONE
+ toEnum 1 = ET_REL
+ toEnum 2 = ET_EXEC
+ toEnum 3 = ET_DYN
+ toEnum 4 = ET_CORE
+ toEnum 0xff00 = ET_LOPROC
+ toEnum 0xffff = ET_HIPROC
+ toEnum x = ElfType $ fromIntegral x
+ fromEnum ET_NONE = 0
+ fromEnum ET_REL = 1
+ fromEnum ET_EXEC = 2
+ fromEnum ET_DYN = 3
+ fromEnum ET_CORE = 4
+ fromEnum ET_LOPROC = 0xff00
+ fromEnum ET_HIPROC = 0xffff
+ fromEnum (ElfType x) = fromIntegral x
+
+data Elf32_Sym = Elf32_Sym
+ { st_name :: Elf32_Word
+ , st_value :: Elf32_Addr
+ , st_size :: Elf32_Word
+ , st_info :: Word8
+ , st_other :: Word8
+ , st_shndx :: Elf32_Half
+ } deriving Eq
+
+data ElfSymbolBinding
+ = ElfSymbolBinding Word8
+ | STB_LOCAL
+ | STB_GLOBAL
+ | STB_WEAK
+ | STB_LOPROC
+ | STB_HIPROC
+ deriving Eq
+
+instance Enum ElfSymbolBinding
+ where
+ toEnum 0 = STB_LOCAL
+ toEnum 1 = STB_GLOBAL
+ toEnum 2 = STB_WEAK
+ toEnum 13 = STB_LOPROC
+ toEnum 15 = STB_HIPROC
+ toEnum x = ElfSymbolBinding $ fromIntegral x
+ fromEnum STB_LOCAL = 0
+ fromEnum STB_GLOBAL = 1
+ fromEnum STB_WEAK = 2
+ fromEnum STB_LOPROC = 13
+ fromEnum STB_HIPROC = 15
+ fromEnum (ElfSymbolBinding x) = fromIntegral x
+
+data ElfSymbolType
+ = ElfSymbolType Word8
+ | STT_NOTYPE
+ | STT_OBJECT
+ | STT_FUNC
+ | STT_SECTION
+ | STT_FILE
+ | STT_LOPROC
+ | STT_HIPROC
+ deriving Eq
+
+instance Enum ElfSymbolType
+ where
+ toEnum 0 = STT_NOTYPE
+ toEnum 1 = STT_OBJECT
+ toEnum 2 = STT_FUNC
+ toEnum 3 = STT_SECTION
+ toEnum 4 = STT_FILE
+ toEnum 13 = STT_LOPROC
+ toEnum 15 = STT_HIPROC
+ toEnum x = ElfSymbolType $ fromIntegral x
+ fromEnum STT_NOTYPE = 0
+ fromEnum STT_OBJECT = 1
+ fromEnum STT_FUNC = 2
+ fromEnum STT_SECTION = 3
+ fromEnum STT_FILE = 4
+ fromEnum STT_LOPROC = 13
+ fromEnum STT_HIPROC = 15
+ fromEnum (ElfSymbolType x) = fromIntegral x
+
+data Elf32_Rel = Elf32_Rel
+ { r_offset :: Elf32_Addr
+ , r_info :: Elf32_Word
+ } deriving Eq
+
+data Elf32_Rela = Elf32_Rela
+ { r_offset :: Elf32_Addr
+ , r_info :: Elf32_Word
+ , r_addend :: Elf32_Sword
+ } deriving Eq
+
+data ElfSectionType
+ = ElfSectionType Elf32_Word
+ | SHT_NULL
+ | SHT_PROGBITS
+ | SHT_SYMTAB
+ | SHT_STRTAB
+ | SHT_RELA
+ | SHT_HASH
+ | SHT_DYNAMIC
+ | SHT_NOTE
+ | SHT_NOBITS
+ | SHT_REL
+ | SHT_SHLIB
+ | SHT_DYNSYM
+ | SHT_LOPROC
+ | SHT_HIPROC
+ | SHT_LOUSER
+ | SHT_HIUSER
+ deriving Eq
+
+instance Enum ElfSectionType
+ where
+ toEnum 0 = SHT_NULL
+ toEnum 1 = SHT_PROGBITS
+ toEnum 2 = SHT_SYMTAB
+ toEnum 3 = SHT_STRTAB
+ toEnum 4 = SHT_RELA
+ toEnum 5 = SHT_HASH
+ toEnum 6 = SHT_DYNAMIC
+ toEnum 7 = SHT_NOTE
+ toEnum 8 = SHT_NOBITS
+ toEnum 9 = SHT_REL
+ toEnum 10 = SHT_SHLIB
+ toEnum 11 = SHT_DYNSYM
+ toEnum 0x70000000 = SHT_LOPROC
+ toEnum 0x7fffffff = SHT_HIPROC
+ toEnum 0x80000000 = SHT_LOUSER
+ toEnum 0xffffffff = SHT_HIUSER
+ toEnum x = ElfSectionType $ fromIntegral x
+ fromEnum SHT_NULL = 0
+ fromEnum SHT_PROGBITS = 1
+ fromEnum SHT_SYMTAB = 2
+ fromEnum SHT_STRTAB = 3
+ fromEnum SHT_RELA = 4
+ fromEnum SHT_HASH = 5
+ fromEnum SHT_DYNAMIC = 6
+ fromEnum SHT_NOTE = 7
+ fromEnum SHT_NOBITS = 8
+ fromEnum SHT_REL = 9
+ fromEnum SHT_SHLIB = 10
+ fromEnum SHT_DYNSYM = 11
+ fromEnum SHT_LOPROC = 0x70000000
+ fromEnum SHT_HIPROC = 0x7fffffff
+ fromEnum SHT_LOUSER = 0x80000000
+ fromEnum SHT_HIUSER = 0xffffffff
+ fromEnum (ElfSectionType x) = fromIntegral x
+
+-- * Help types and functions.
+
+data ByteOrder = LSB | MSB
+ deriving Eq
+
+data ElfEncodingError
+ = ElfInvalidByteOrderError
+ | ElfUnsupportedClassError ElfClass
+ deriving Eq
+
+instance Show ElfEncodingError
+ where
+ show ElfInvalidByteOrderError = "Invalid byte order."
+ show (ElfUnsupportedClassError class') =
+ concat ["Elf class \"", show class', "\" is not supported."]
+
+instance Exception ElfEncodingError
+
+fromIntegralEnum :: (Enum a, Num b) => a -> b
+fromIntegralEnum = fromIntegral . fromEnum
+
+-- * Encoding functions.
+
+elf32Addr :: ByteOrder -> Elf32_Addr -> ByteString.Builder.Builder
+elf32Addr LSB = ByteString.Builder.word32LE
+elf32Addr MSB = ByteString.Builder.word32BE
+
+elf32Half :: ByteOrder -> Elf32_Half -> ByteString.Builder.Builder
+elf32Half LSB = ByteString.Builder.word16LE
+elf32Half MSB = ByteString.Builder.word16BE
+
+elf32Off :: ByteOrder -> Elf32_Off -> ByteString.Builder.Builder
+elf32Off LSB = ByteString.Builder.word32LE
+elf32Off MSB = ByteString.Builder.word32BE
+
+elf32Sword :: ByteOrder -> Elf32_Sword -> ByteString.Builder.Builder
+elf32Sword LSB = ByteString.Builder.int32LE
+elf32Sword MSB = ByteString.Builder.int32BE
+
+elf32Word :: ByteOrder -> Elf32_Word -> ByteString.Builder.Builder
+elf32Word LSB = ByteString.Builder.word32LE
+elf32Word MSB = ByteString.Builder.word32BE
+
+elfIdentification :: ElfIdentification -> ByteString.Builder.Builder
+elfIdentification (ElfIdentification elfClass elfData)
+ = ByteString.Builder.word8 0x7f
+ <> ByteString.Builder.string7 "ELF"
+ <> ByteString.Builder.word8 (fromIntegralEnum elfClass)
+ <> ByteString.Builder.word8 (fromIntegralEnum elfData)
+ <> ByteString.Builder.word8 (fromIntegralEnum EV_CURRENT)
+ <> ByteString.Builder.byteString (ByteString.replicate 9 0)
+
+elf32Ehdr :: Elf32_Ehdr -> Either ElfEncodingError ByteString.Builder.Builder
+elf32Ehdr Elf32_Ehdr{..} = encode <$> byteOrder
+ where
+ encode byteOrder'
+ = elfIdentification e_ident
+ <> elf32Half byteOrder' (fromIntegralEnum e_type)
+ <> elf32Half byteOrder' (fromIntegralEnum e_machine)
+ <> elf32Word byteOrder' (fromIntegralEnum e_version)
+ <> elf32Addr byteOrder' e_entry
+ <> elf32Off byteOrder' e_phoff
+ <> elf32Off byteOrder' e_shoff
+ <> elf32Word byteOrder' e_flags
+ <> elf32Half byteOrder' e_ehsize
+ <> elf32Half byteOrder' e_phentsize
+ <> elf32Half byteOrder' e_phnum
+ <> elf32Half byteOrder' e_shentsize
+ <> elf32Half byteOrder' e_shnum
+ <> elf32Half byteOrder' e_shstrndx
+ byteOrder
+ | ElfIdentification class' _ <- e_ident
+ , class' /= ELFCLASS32 = Left $ ElfUnsupportedClassError class'
+ | ElfIdentification _ ELFDATA2MSB <- e_ident = Right MSB
+ | ElfIdentification _ ELFDATA2LSB <- e_ident = Right LSB
+ | ElfIdentification _ ELFDATANONE <- e_ident = Left ElfInvalidByteOrderError
+
+elf32Shdr :: ByteOrder -> Elf32_Shdr -> ByteString.Builder.Builder
+elf32Shdr byteOrder Elf32_Shdr{..}
+ = elf32Word byteOrder sh_name
+ <> elf32Word byteOrder (fromIntegralEnum sh_type)
+ <> elf32Word byteOrder sh_flags
+ <> elf32Addr byteOrder sh_addr
+ <> elf32Off byteOrder sh_offset
+ <> elf32Word byteOrder sh_size
+ <> elf32Word byteOrder sh_link
+ <> elf32Word byteOrder sh_info
+ <> elf32Word byteOrder sh_addralign
+ <> elf32Word byteOrder sh_entsize
+
+elf32Sym :: ByteOrder -> Elf32_Sym -> ByteString.Builder.Builder
+elf32Sym byteOrder Elf32_Sym{..}
+ = elf32Word byteOrder st_name
+ <> elf32Addr byteOrder st_value
+ <> elf32Word byteOrder st_size
+ <> ByteString.Builder.word8 st_info
+ <> ByteString.Builder.word8 st_other
+ <> elf32Half byteOrder st_shndx
+
+elf32Rel :: ByteOrder -> Elf32_Rel -> ByteString.Builder.Builder
+elf32Rel byteOrder Elf32_Rel{..}
+ = elf32Addr byteOrder r_offset
+ <> elf32Word byteOrder r_info
+
+elf32Rela :: ByteOrder -> Elf32_Rela -> ByteString.Builder.Builder
+elf32Rela byteOrder Elf32_Rela{..}
+ = elf32Addr byteOrder r_offset
+ <> elf32Word byteOrder r_info
+ <> elf32Sword byteOrder r_addend
+
+stInfo :: ElfSymbolBinding -> ElfSymbolType -> Word8
+stInfo binding type' = fromIntegralEnum binding `shiftL` 4
+ .|. (fromIntegralEnum type' .&. 0xf)
+
+rInfo :: Elf32_Word -> Word8 -> Elf32_Word
+rInfo symbol type' = symbol `shiftL` 8
+ .|. fromIntegralEnum type'
diff --git a/rakelib/cross.rake b/rakelib/cross.rake
new file mode 100644
index 0000000..3de7d79
--- /dev/null
+++ b/rakelib/cross.rake
@@ -0,0 +1,307 @@
+require 'pathname'
+require 'uri'
+require 'net/http'
+require 'rake/clean'
+require 'open3'
+require 'etc'
+
+GCC_VERSION = "14.2.0"
+BINUTILS_VERSION = '2.43.1'
+GLIBC_VERSION = '2.40'
+KERNEL_VERSION = '5.15.166'
+
+TMP = Pathname.new('./build')
+
+CLEAN.include TMP
+
+class BuildTarget
+ attr_accessor(:build, :gcc, :target, :tmp)
+
+ def gxx
+ @gcc.gsub 'c', '+'
+ end
+
+ def sysroot
+ tmp + 'sysroot'
+ end
+
+ def rootfs
+ tmp + 'rootfs'
+ end
+
+ def tools
+ tmp + 'tools'
+ end
+end
+
+def gcc_verbose(gcc_binary)
+ read, write = IO.pipe
+ sh({'LANG' => 'C'}, gcc_binary, '--verbose', err: write)
+ write.close
+ output = read.read
+ read.close
+ output
+end
+
+def find_build_target(gcc_version, task)
+ gcc_binary = 'gcc'
+ output = gcc_verbose gcc_binary
+
+ if output.start_with? 'Apple clang'
+ gcc_binary = "gcc-#{gcc_version.split('.').first}"
+ output = gcc_verbose gcc_binary
+ end
+ result = output
+ .lines
+ .each_with_object(BuildTarget.new) do |line, accumulator|
+ if line.start_with? 'Target: '
+ accumulator.build = line.split(' ').last.strip
+ elsif line.start_with? 'COLLECT_GCC'
+ accumulator.gcc = line.split('=').last.strip
+ end
+ end
+ result.tmp = TMP
+ task.with_defaults target: 'riscv32-unknown-linux-gnu'
+ result.target = task[:target]
+ result
+end
+
+def download_and_unarchive(url, target)
+ case File.extname url.path
+ when '.bz2'
+ archive_type = '-j'
+ root_directory = File.basename url.path, '.tar.bz2'
+ when '.xz'
+ archive_type = '-J'
+ root_directory = File.basename url.path, '.tar.xz'
+ else
+ raise "Unsupported archive type #{url.path}."
+ end
+
+ Net::HTTP.start(url.host, url.port, use_ssl: url.scheme == 'https') do |http|
+ request = Net::HTTP::Get.new url.request_uri
+
+ http.request request do |response|
+ case response
+ when Net::HTTPRedirection
+ download_and_unarchive URI.parse(response['location'])
+ when Net::HTTPSuccess
+ Open3.popen2 'tar', '-C', target.to_path, archive_type, '-xv' do |stdin, stdout, wait_thread|
+ stdout.close
+
+ response.read_body do |chunk|
+ stdin.write chunk
+ end
+ end
+ else
+ response.error!
+ end
+ end
+ end
+ target + root_directory
+end
+
+namespace :cross do
+ desc 'Build cross binutils'
+ task :binutils, [:target] do |_, args|
+ options = find_build_target GCC_VERSION, args
+ options.tools.mkpath
+ source_directory = download_and_unarchive(
+ URI.parse("https://ftp.gnu.org/gnu/binutils/binutils-#{BINUTILS_VERSION}.tar.xz"),
+ options.tools)
+
+ cwd = source_directory.dirname + 'build-binutils'
+ cwd.mkpath
+ options.rootfs.mkpath
+
+ env = {
+ 'CC' => options.gcc,
+ 'CXX' => options.gxx
+ }
+ configure_options = [
+ "--prefix=#{options.rootfs.realpath}",
+ "--target=#{options.target}",
+ '--disable-nls',
+ '--enable-gprofng=no',
+ '--disable-werror',
+ '--enable-default-hash-style=gnu',
+ '--disable-libquadmath'
+ ]
+ configure = source_directory.relative_path_from(cwd) + 'configure'
+ sh env, configure.to_path, *configure_options, chdir: cwd.to_path
+ sh env, 'make', '-j', Etc.nprocessors.to_s, chdir: cwd.to_path
+ sh env, 'make', 'install', chdir: cwd.to_path
+ end
+
+ desc 'Build stage 1 GCC'
+ task :gcc1, [:target] do |_, args|
+ options = find_build_target GCC_VERSION, args
+ options.tools.mkpath
+ source_directory = download_and_unarchive(
+ URI.parse("https://gcc.gnu.org/pub/gcc/releases/gcc-#{GCC_VERSION}/gcc-#{GCC_VERSION}.tar.xz"),
+ options.tools)
+
+ cwd = source_directory.dirname + 'build-gcc'
+ cwd.mkpath
+ options.rootfs.mkpath
+ options.sysroot.mkpath
+
+ sh 'contrib/download_prerequisites', chdir: source_directory.to_path
+ configure_options = [
+ "--prefix=#{options.rootfs.realpath}",
+ "--with-sysroot=#{options.sysroot.realpath}",
+ '--enable-languages=c,c++',
+ '--disable-shared',
+ '--with-arch=rv32imafdc',
+ '--with-abi=ilp32d',
+ '--with-tune=rocket',
+ '--with-isa-spec=20191213',
+ '--disable-bootstrap',
+ '--disable-multilib',
+ '--disable-libmudflap',
+ '--disable-libssp',
+ '--disable-libquadmath',
+ '--disable-libsanitizer',
+ '--disable-threads',
+ '--disable-libatomic',
+ '--disable-libgomp',
+ '--disable-libvtv',
+ '--disable-libstdcxx',
+ '--disable-nls',
+ '--with-newlib',
+ '--without-headers',
+ "--target=#{options.target}",
+ "--build=#{options.build}",
+ "--host=#{options.build}"
+ ]
+ flags = '-O2 -fPIC'
+ env = {
+ 'CC' => options.gcc,
+ 'CXX' => options.gxx,
+ 'CFLAGS' => flags,
+ 'CXXFLAGS' => flags,
+ 'PATH' => "#{options.rootfs.realpath + 'bin'}:#{ENV['PATH']}"
+ }
+ configure = source_directory.relative_path_from(cwd) + 'configure'
+ sh env, configure.to_path, *configure_options, chdir: cwd.to_path
+ sh env, 'make', '-j', Etc.nprocessors.to_s, chdir: cwd.to_path
+ sh env, 'make', 'install', chdir: cwd.to_path
+ end
+
+ desc 'Copy glibc headers'
+ task :headers, [:target] do |_, args|
+ options = find_build_target GCC_VERSION, args
+ options.tools.mkpath
+
+ source_directory = download_and_unarchive(
+ URI.parse("https://ftp.gnu.org/gnu/glibc/glibc-#{GLIBC_VERSION}.tar.xz"),
+ options.tools)
+ include_directory = options.tools + 'include'
+
+ include_directory.mkpath
+ cp (source_directory + 'elf/elf.h'), (include_directory + 'elf.h')
+ end
+
+ desc 'Build linux kernel'
+ task :kernel, [:target] do |_, args|
+ options = find_build_target GCC_VERSION, args
+ options.tools.mkpath
+
+ cwd = download_and_unarchive(
+ URI.parse("https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-#{KERNEL_VERSION}.tar.xz"),
+ options.tools)
+
+ env = {
+ 'CROSS_COMPILE' => "#{options.target}-",
+ 'ARCH' => 'riscv',
+ 'PATH' => "#{options.rootfs.realpath + 'bin'}:#{ENV['PATH']}",
+ 'HOSTCFLAGS' => "-D_UUID_T -D__GETHOSTUUID_H -I#{options.tools.realpath + 'include'}"
+ }
+ sh env, 'make', 'rv32_defconfig', chdir: cwd.to_path
+ sh env, 'make', '-j', Etc.nprocessors.to_s, chdir: cwd.to_path
+ sh env, 'make', 'headers', chdir: cwd.to_path
+
+ user_directory = options.sysroot + 'usr'
+
+ user_directory.mkpath
+ cp_r (cwd + 'usr/include'), (user_directory + 'include')
+ end
+
+ desc 'Build glibc'
+ task :glibc, [:target] do |_, args|
+ options = find_build_target GCC_VERSION, args
+ source_directory = options.tools + "glibc-#{GLIBC_VERSION}"
+ configure_options = [
+ '--prefix=/usr',
+ "--host=#{options.target}",
+ "--target=#{options.target}",
+ "--build=#{options.build}",
+ "--enable-kernel=#{KERNEL_VERSION}",
+ "--with-headers=#{options.sysroot.realpath + 'usr/include'}",
+ '--disable-nscd',
+ '--disable-libquadmath',
+ '--disable-libitm',
+ '--disable-werror',
+ 'libc_cv_forced_unwind=yes'
+ ]
+ bin = options.rootfs.realpath + 'bin'
+ env = {
+ 'PATH' => "#{bin}:#{ENV['PATH']}",
+ 'MAKE' => 'make' # Otherwise it uses gnumake which can be different and too old.
+ }
+ cwd = source_directory.dirname + 'build-glibc'
+ cwd.mkpath
+
+ configure = source_directory.relative_path_from(cwd) +'./configure'
+ sh env, configure.to_path, *configure_options, chdir: cwd.to_path
+ sh env, 'make', '-j', Etc.nprocessors.to_s, chdir: cwd.to_path
+ sh env, 'make', "install_root=#{options.sysroot.realpath}", 'install', chdir: cwd.to_path
+ end
+
+ desc 'Build stage 2 GCC'
+ task :gcc2, [:target] do |_, args|
+ options = find_build_target GCC_VERSION, args
+ source_directory = options.tools + "gcc-#{GCC_VERSION}"
+ cwd = options.tools + 'build-gcc'
+
+ rm_rf cwd
+ cwd.mkpath
+
+ configure_options = [
+ "--prefix=#{options.rootfs.realpath}",
+ "--with-sysroot=#{options.sysroot.realpath}",
+ '--enable-languages=c,c++,lto',
+ '--enable-lto',
+ '--enable-shared',
+ '--with-arch=rv32imafdc',
+ '--with-abi=ilp32d',
+ '--with-tune=rocket',
+ '--with-isa-spec=20191213',
+ '--disable-bootstrap',
+ '--disable-multilib',
+ '--enable-checking=release',
+ '--disable-libssp',
+ '--disable-libquadmath',
+ '--enable-threads=posix',
+ '--with-default-libstdcxx-abi=new',
+ '--disable-nls',
+ "--target=#{options.target}",
+ "--build=#{options.build}",
+ "--host=#{options.build}"
+
+ ]
+ flags = '-O2 -fPIC'
+ env = {
+ 'CFLAGS' => flags,
+ 'CXXFLAGS' => flags,
+ 'PATH' => "#{options.rootfs.realpath + 'bin'}:#{ENV['PATH']}"
+ }
+ configure = source_directory.relative_path_from(cwd) + 'configure'
+ sh env, configure.to_path, *configure_options, chdir: cwd.to_path
+ sh env, 'make', '-j', Etc.nprocessors.to_s, chdir: cwd.to_path
+ sh env, 'make', 'install', chdir: cwd.to_path
+ end
+end
+
+task cross: ['cross:binutils', 'cross:gcc1', 'cross:headers', 'cross:kernel', 'cross:glibc', 'cross:gcc2'] do
+end
diff --git a/src/Main.hs b/src/Main.hs
index bb8be4c..4004d5c 100644
--- a/src/Main.hs
+++ b/src/Main.hs
@@ -1,13 +1,95 @@
-module Main where
+module Main
+ ( main
+ ) where
-import Language.Elna.Parser (programP)
-import Text.Megaparsec (runParser, errorBundlePretty)
-import qualified Data.Text.IO as Text
+import Language.Elna.Object.Elf
+ ( ByteOrder(..)
+ , Elf32_Ehdr(..)
+ , Elf32_Off
+ , ElfMachine(..)
+ , ElfType(..)
+ , ElfVersion(..)
+ , ElfIdentification(..)
+ , ElfClass(..)
+ , ElfData(..)
+ , Elf32_Shdr(..)
+ , ElfSectionType(..)
+ , elf32Ehdr
+ , elf32Shdr
+ )
+import qualified Data.ByteString as ByteString
+import qualified Data.ByteString.Builder as ByteString.Builder
+import Data.Vector (Vector)
+import qualified Data.Vector as Vector
+import Control.Exception (throwIO)
+import System.IO (Handle, IOMode(..), SeekMode(..), hSeek, withFile)
+import Data.Foldable (traverse_)
-main :: IO ()
-main = Text.getContents
- >>= withParseResult . runParser programP ""
+riscv32Elf :: FilePath -> IO ()
+riscv32Elf outFile = withFile outFile WriteMode withObjectFile
where
- withParseResult (Right _) = pure ()
- withParseResult (Left errorBundle) =
- putStr $ errorBundlePretty errorBundle
+ headerSize :: Elf32_Off
+ headerSize = 52
+ withObjectFile objectHandle
+ = hSeek objectHandle AbsoluteSeek (fromIntegral headerSize)
+ >> putContents objectHandle
+ >>= afterContents objectHandle
+ afterContents objectHandle headers =
+ let headerEncodingResult = elf32Ehdr
+ $ header
+ $ Vector.foldr ((+) . sh_size) 0 headers
+ in traverse_ (ByteString.Builder.hPutBuilder objectHandle . elf32Shdr LSB) headers
+ >> either throwIO (putHeaders objectHandle) headerEncodingResult
+ putHeaders objectHandle encodedHeader
+ = hSeek objectHandle AbsoluteSeek 0
+ >> ByteString.Builder.hPutBuilder objectHandle encodedHeader
+ putContents :: Handle -> IO (Vector Elf32_Shdr)
+ putContents objectHandle =
+ let stringTable = "\0shstrtab\0"
+ written = Vector.fromList
+ [ Elf32_Shdr
+ { sh_type = SHT_NULL
+ , sh_size = 0
+ , sh_offset = 0
+ , sh_name = 0
+ , sh_link = 0
+ , sh_info = 0
+ , sh_flags = 0
+ , sh_entsize = 0
+ , sh_addralign = 0
+ , sh_addr = 0
+ }
+ , Elf32_Shdr
+ { sh_type = SHT_STRTAB
+ , sh_size = fromIntegral $ ByteString.length stringTable
+ , sh_offset = fromIntegral headerSize
+ , sh_name = 1
+ , sh_link = 0
+ , sh_info = 0
+ , sh_flags = 0
+ , sh_entsize = 0
+ , sh_addralign = 0
+ , sh_addr = 0
+ }
+ ]
+ in ByteString.hPut objectHandle stringTable
+ >> pure written
+ header written = Elf32_Ehdr
+ { e_version = EV_CURRENT
+ , e_type = ET_REL
+ , e_shstrndx = 1 -- String table. SHN_UNDEF
+ , e_shoff = written + headerSize
+ , e_shnum = 2
+ , e_shentsize = 40
+ , e_phoff = 0
+ , e_phnum = 0
+ , e_phentsize = 32
+ , e_machine = EM_RISCV
+ , e_ident = ElfIdentification ELFCLASS32 ELFDATA2LSB
+ , e_flags = 0x4 -- EF_RISCV_FLOAT_ABI_DOUBLE
+ , e_entry = 0
+ , e_ehsize = fromIntegral headerSize
+ }
+
+main :: IO ()
+main = riscv32Elf "here.o"