Implemented ELF header

This commit is contained in:
Eduard Urbach 2023-10-17 20:29:36 +02:00
parent cae6696c3e
commit 4554fb82cb
Signed by: akyoto
GPG Key ID: C874F672B1AF20C0
7 changed files with 217 additions and 2 deletions

View File

@ -19,7 +19,7 @@ go build
## Usage
```shell
q build
./q build examples/hello
```
## License

View File

@ -1,6 +1,12 @@
package build
import "path/filepath"
import (
"bufio"
"os"
"path/filepath"
"git.akyoto.dev/cli/q/build/elf"
)
// Build describes a compiler build.
type Build struct {
@ -30,5 +36,37 @@ func New(directory string) (*Build, error) {
// Run parses the input files and generates an executable file.
func (build *Build) Run() error {
if build.WriteExecutable {
sampleCode := []byte{
0xb8, 0x3c, 0x00, 0x00, 0x00, // mov rax, 60
0xbf, 0x00, 0x00, 0x00, 0x00, // mov rdi, 0
0x0f, 0x05, // syscall
}
return writeToDisk(build.ExecutablePath, sampleCode, nil)
}
return nil
}
// writeToDisk writes the executable file to disk.
func writeToDisk(filePath string, code []byte, data []byte) error {
file, err := os.Create(filePath)
if err != nil {
return err
}
buffer := bufio.NewWriter(file)
executable := elf.New()
executable.Write(buffer)
buffer.Flush()
err = file.Close()
if err != nil {
return err
}
return os.Chmod(filePath, 0755)
}

32
build/elf/Header.go Normal file
View File

@ -0,0 +1,32 @@
package elf
const (
LittleEndian = 1
TypeExecutable = 2
ArchitectureAMD64 = 0x3E
HeaderSize = 64
)
// Header contains general information.
type Header struct {
Magic [4]byte
Class byte
Endianness byte
Version byte
OSABI byte
ABIVersion byte
_ [7]byte
Type int16
Architecture int16
FileVersion int32
EntryPointInMemory int64
ProgramHeaderOffset int64
SectionHeaderOffset int64
Flags int32
Size int16
ProgramHeaderEntrySize int16
ProgramHeaderEntryCount int16
SectionHeaderEntrySize int16
SectionHeaderEntryCount int16
SectionNameStringTableIndex int16
}

View File

@ -0,0 +1,37 @@
package elf
// ProgramHeaderSize is equal to the size of a program header in bytes.
const ProgramHeaderSize = 56
// ProgramHeader points to the executable part of our program.
type ProgramHeader struct {
Type ProgramType
Flags ProgramFlags
Offset int64
VirtualAddress int64
PhysicalAddress int64
SizeInFile int64
SizeInMemory int64
Align int64
}
type ProgramType int32
const (
ProgramTypeNULL ProgramType = 0
ProgramTypeLOAD ProgramType = 1
ProgramTypeDYNAMIC ProgramType = 2
ProgramTypeINTERP ProgramType = 3
ProgramTypeNOTE ProgramType = 4
ProgramTypeSHLIB ProgramType = 5
ProgramTypePHDR ProgramType = 6
ProgramTypeTLS ProgramType = 7
)
type ProgramFlags int32
const (
ProgramFlagsExecutable ProgramFlags = 0x1
ProgramFlagsWritable ProgramFlags = 0x2
ProgramFlagsReadable ProgramFlags = 0x4
)

View File

@ -0,0 +1,45 @@
package elf
// SectionHeaderSize is equal to the size of a section header in bytes.
const SectionHeaderSize = 64
// SectionHeader points to the data sections of our program.
type SectionHeader struct {
NameIndex int32
Type SectionType
Flags SectionFlags
VirtualAddress int64
Offset int64
SizeInFile int64
Link int32
Info int32
Align int64
EntrySize int64
}
type SectionType int32
const (
SectionTypeNULL SectionType = 0
SectionTypePROGBITS SectionType = 1
SectionTypeSYMTAB SectionType = 2
SectionTypeSTRTAB SectionType = 3
SectionTypeRELA SectionType = 4
SectionTypeHASH SectionType = 5
SectionTypeDYNAMIC SectionType = 6
SectionTypeNOTE SectionType = 7
SectionTypeNOBITS SectionType = 8
SectionTypeREL SectionType = 9
SectionTypeSHLIB SectionType = 10
SectionTypeDYNSYM SectionType = 11
)
type SectionFlags int64
const (
SectionFlagsWritable SectionFlags = 1 << 0
SectionFlagsAllocate SectionFlags = 1 << 1
SectionFlagsExecutable SectionFlags = 1 << 2
SectionFlagsStrings SectionFlags = 1 << 5
SectionFlagsTLS SectionFlags = 1 << 10
)

51
build/elf/elf.go Normal file
View File

@ -0,0 +1,51 @@
package elf
import (
"encoding/binary"
"io"
)
const (
baseAddress = 0x400000
programAlign = 16
sectionAlign = 16
)
// ELF64 represents an ELF 64-bit file.
type ELF64 struct {
Header
}
// New creates a new 64-bit ELF binary.
func New() *ELF64 {
elf := &ELF64{
Header: Header{
Magic: [4]byte{0x7F, 'E', 'L', 'F'},
Class: 2,
Endianness: LittleEndian,
Version: 1,
OSABI: 0,
ABIVersion: 0,
Type: TypeExecutable,
Architecture: ArchitectureAMD64,
FileVersion: 1,
EntryPointInMemory: 0,
ProgramHeaderOffset: HeaderSize,
SectionHeaderOffset: 0, // TODO
Flags: 0,
Size: HeaderSize,
ProgramHeaderEntrySize: ProgramHeaderSize,
ProgramHeaderEntryCount: 0,
SectionHeaderEntrySize: SectionHeaderSize,
SectionHeaderEntryCount: 0,
SectionNameStringTableIndex: 0,
},
}
return elf
}
// Write writes the ELF64 format to the given writer.
func (elf *ELF64) Write(writer io.Writer) {
binary.Write(writer, binary.LittleEndian, &elf.Header)
}

12
build/elf/elf.md Normal file
View File

@ -0,0 +1,12 @@
# ELF
## File contents
- ELF header
- Program headers
- Sections
- Section headers
## Entry point
The entry point is defined in the first 64 bytes (ELF header).