Improved mac support
This commit is contained in:
parent
af259b364c
commit
f70a2e848d
25 changed files with 119 additions and 34 deletions
89
src/os/linux/elf/ELF.go
Normal file
89
src/os/linux/elf/ELF.go
Normal file
|
@ -0,0 +1,89 @@
|
|||
package elf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
|
||||
"git.akyoto.dev/cli/q/src/config"
|
||||
)
|
||||
|
||||
// ELF represents an ELF file.
|
||||
type ELF struct {
|
||||
Header
|
||||
CodeHeader ProgramHeader
|
||||
DataHeader ProgramHeader
|
||||
CodePadding []byte
|
||||
Code []byte
|
||||
DataPadding []byte
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// New creates a new ELF binary.
|
||||
func New(code []byte, data []byte) *ELF {
|
||||
dataOffset := config.CodeOffset + int64(len(code))
|
||||
dataPadding := Padding(dataOffset, config.Align)
|
||||
dataOffset += dataPadding
|
||||
|
||||
return &ELF{
|
||||
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: config.BaseAddress + config.CodeOffset,
|
||||
ProgramHeaderOffset: HeaderSize,
|
||||
SectionHeaderOffset: 0,
|
||||
Flags: 0,
|
||||
Size: HeaderSize,
|
||||
ProgramHeaderEntrySize: ProgramHeaderSize,
|
||||
ProgramHeaderEntryCount: 2,
|
||||
SectionHeaderEntrySize: SectionHeaderSize,
|
||||
SectionHeaderEntryCount: 0,
|
||||
SectionNameStringTableIndex: 0,
|
||||
},
|
||||
CodeHeader: ProgramHeader{
|
||||
Type: ProgramTypeLOAD,
|
||||
Flags: ProgramFlagsExecutable | ProgramFlagsReadable,
|
||||
Offset: config.CodeOffset,
|
||||
VirtualAddress: config.BaseAddress + config.CodeOffset,
|
||||
PhysicalAddress: config.BaseAddress + config.CodeOffset,
|
||||
SizeInFile: int64(len(code)),
|
||||
SizeInMemory: int64(len(code)),
|
||||
Align: config.Align,
|
||||
},
|
||||
DataHeader: ProgramHeader{
|
||||
Type: ProgramTypeLOAD,
|
||||
Flags: ProgramFlagsReadable,
|
||||
Offset: dataOffset,
|
||||
VirtualAddress: config.BaseAddress + dataOffset,
|
||||
PhysicalAddress: config.BaseAddress + dataOffset,
|
||||
SizeInFile: int64(len(data)),
|
||||
SizeInMemory: int64(len(data)),
|
||||
Align: config.Align,
|
||||
},
|
||||
CodePadding: nil,
|
||||
Code: code,
|
||||
DataPadding: bytes.Repeat([]byte{0}, int(dataPadding)),
|
||||
Data: data,
|
||||
}
|
||||
}
|
||||
|
||||
// Write writes the ELF64 format to the given writer.
|
||||
func (elf *ELF) Write(writer io.Writer) {
|
||||
binary.Write(writer, binary.LittleEndian, &elf.Header)
|
||||
binary.Write(writer, binary.LittleEndian, &elf.CodeHeader)
|
||||
binary.Write(writer, binary.LittleEndian, &elf.DataHeader)
|
||||
writer.Write(elf.CodePadding)
|
||||
writer.Write(elf.Code)
|
||||
|
||||
if len(elf.Data) > 0 {
|
||||
writer.Write(elf.DataPadding)
|
||||
writer.Write(elf.Data)
|
||||
}
|
||||
}
|
13
src/os/linux/elf/ELF_test.go
Normal file
13
src/os/linux/elf/ELF_test.go
Normal file
|
@ -0,0 +1,13 @@
|
|||
package elf_test
|
||||
|
||||
import (
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"git.akyoto.dev/cli/q/src/os/linux/elf"
|
||||
)
|
||||
|
||||
func TestELF(t *testing.T) {
|
||||
exe := elf.New(nil, nil)
|
||||
exe.Write(io.Discard)
|
||||
}
|
32
src/os/linux/elf/Header.go
Normal file
32
src/os/linux/elf/Header.go
Normal 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
|
||||
}
|
6
src/os/linux/elf/Padding.go
Normal file
6
src/os/linux/elf/Padding.go
Normal file
|
@ -0,0 +1,6 @@
|
|||
package elf
|
||||
|
||||
// Padding calculates the padding needed to align `n` bytes with the specified alignment.
|
||||
func Padding[T int | int32 | int64 | uint | uint32 | uint64](n T, align T) T {
|
||||
return align - (n % align)
|
||||
}
|
37
src/os/linux/elf/ProgramHeader.go
Normal file
37
src/os/linux/elf/ProgramHeader.go
Normal 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
|
||||
)
|
45
src/os/linux/elf/SectionHeader.go
Normal file
45
src/os/linux/elf/SectionHeader.go
Normal 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
|
||||
)
|
30
src/os/linux/elf/elf.md
Normal file
30
src/os/linux/elf/elf.md
Normal file
|
@ -0,0 +1,30 @@
|
|||
# ELF
|
||||
|
||||
## Basic structure
|
||||
|
||||
1. ELF header (0x00 - 0x40)
|
||||
2. Program header (0x40 - 0x78)
|
||||
3. Padding (0x78 - 0x80)
|
||||
4. Machine code (0x80)
|
||||
|
||||
## Entry point
|
||||
|
||||
The entry point is defined in the first 64 bytes (ELF header).
|
||||
|
||||
## Base address
|
||||
|
||||
The minimum base address is controlled by the `mmap` settings:
|
||||
|
||||
```shell
|
||||
sysctl vm.mmap_min_addr
|
||||
```
|
||||
|
||||
Usually, this value is 65536 (0x1000).
|
||||
|
||||
## Initialization in Linux
|
||||
|
||||
ELF loader:
|
||||
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/fs/binfmt_elf.c
|
||||
|
||||
ELF register definitions:
|
||||
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/x86/include/asm/elf.h
|
10
src/os/mac/Syscall.go
Normal file
10
src/os/mac/Syscall.go
Normal file
|
@ -0,0 +1,10 @@
|
|||
package mac
|
||||
|
||||
// Syscall numbers are divided into classes, here we need the BSD inherited syscalls.
|
||||
const SyscallClassUnix = 0x2000000
|
||||
|
||||
// https://github.com/apple-oss-distributions/xnu/blob/main/bsd/kern/syscalls.master
|
||||
const (
|
||||
Exit = 1 | SyscallClassUnix
|
||||
Write = 4 | SyscallClassUnix
|
||||
)
|
10
src/os/mac/macho/CPU.go
Normal file
10
src/os/mac/macho/CPU.go
Normal file
|
@ -0,0 +1,10 @@
|
|||
package macho
|
||||
|
||||
type CPU uint32
|
||||
|
||||
const (
|
||||
CPU_X86 CPU = 7
|
||||
CPU_X86_64 CPU = CPU_X86 | 0x01000000
|
||||
CPU_ARM CPU = 12
|
||||
CPU_ARM_64 CPU = CPU_ARM | 0x01000000
|
||||
)
|
13
src/os/mac/macho/Header.go
Normal file
13
src/os/mac/macho/Header.go
Normal file
|
@ -0,0 +1,13 @@
|
|||
package macho
|
||||
|
||||
// Header contains general information.
|
||||
type Header struct {
|
||||
Magic uint32
|
||||
Architecture CPU
|
||||
MicroArchitecture uint32
|
||||
Type HeaderType
|
||||
NumCommands uint32
|
||||
SizeCommands uint32
|
||||
Flags HeaderFlags
|
||||
Reserved uint32
|
||||
}
|
32
src/os/mac/macho/HeaderFlags.go
Normal file
32
src/os/mac/macho/HeaderFlags.go
Normal file
|
@ -0,0 +1,32 @@
|
|||
package macho
|
||||
|
||||
type HeaderFlags uint32
|
||||
|
||||
const (
|
||||
FlagNoUndefs HeaderFlags = 0x1
|
||||
FlagIncrLink HeaderFlags = 0x2
|
||||
FlagDyldLink HeaderFlags = 0x4
|
||||
FlagBindAtLoad HeaderFlags = 0x8
|
||||
FlagPrebound HeaderFlags = 0x10
|
||||
FlagSplitSegs HeaderFlags = 0x20
|
||||
FlagLazyInit HeaderFlags = 0x40
|
||||
FlagTwoLevel HeaderFlags = 0x80
|
||||
FlagForceFlat HeaderFlags = 0x100
|
||||
FlagNoMultiDefs HeaderFlags = 0x200
|
||||
FlagNoFixPrebinding HeaderFlags = 0x400
|
||||
FlagPrebindable HeaderFlags = 0x800
|
||||
FlagAllModsBound HeaderFlags = 0x1000
|
||||
FlagSubsectionsViaSymbols HeaderFlags = 0x2000
|
||||
FlagCanonical HeaderFlags = 0x4000
|
||||
FlagWeakDefines HeaderFlags = 0x8000
|
||||
FlagBindsToWeak HeaderFlags = 0x10000
|
||||
FlagAllowStackExecution HeaderFlags = 0x20000
|
||||
FlagRootSafe HeaderFlags = 0x40000
|
||||
FlagSetuidSafe HeaderFlags = 0x80000
|
||||
FlagNoReexportedDylibs HeaderFlags = 0x100000
|
||||
FlagPIE HeaderFlags = 0x200000
|
||||
FlagDeadStrippableDylib HeaderFlags = 0x400000
|
||||
FlagHasTLVDescriptors HeaderFlags = 0x800000
|
||||
FlagNoHeapExecution HeaderFlags = 0x1000000
|
||||
FlagAppExtensionSafe HeaderFlags = 0x2000000
|
||||
)
|
12
src/os/mac/macho/HeaderType.go
Normal file
12
src/os/mac/macho/HeaderType.go
Normal file
|
@ -0,0 +1,12 @@
|
|||
package macho
|
||||
|
||||
type HeaderType uint32
|
||||
|
||||
const (
|
||||
TypeObject HeaderType = 0x1
|
||||
TypeExecute HeaderType = 0x2
|
||||
TypeCore HeaderType = 0x4
|
||||
TypeDylib HeaderType = 0x6
|
||||
TypeBundle HeaderType = 0x8
|
||||
TypeDsym HeaderType = 0xA
|
||||
)
|
34
src/os/mac/macho/LoadCommand.go
Normal file
34
src/os/mac/macho/LoadCommand.go
Normal file
|
@ -0,0 +1,34 @@
|
|||
package macho
|
||||
|
||||
type LoadCommand uint32
|
||||
|
||||
const (
|
||||
LcSegment LoadCommand = 0x1
|
||||
LcSymtab LoadCommand = 0x2
|
||||
LcThread LoadCommand = 0x4
|
||||
LcUnixthread LoadCommand = 0x5
|
||||
LcDysymtab LoadCommand = 0xB
|
||||
LcDylib LoadCommand = 0xC
|
||||
LcIdDylib LoadCommand = 0xD
|
||||
LcLoadDylinker LoadCommand = 0xE
|
||||
LcIdDylinker LoadCommand = 0xF
|
||||
LcSegment64 LoadCommand = 0x19
|
||||
LcUuid LoadCommand = 0x1B
|
||||
LcCodeSignature LoadCommand = 0x1D
|
||||
LcSegmentSplitInfo LoadCommand = 0x1E
|
||||
LcRpath LoadCommand = 0x8000001C
|
||||
LcEncryptionInfo LoadCommand = 0x21
|
||||
LcDyldInfo LoadCommand = 0x22
|
||||
LcDyldInfoOnly LoadCommand = 0x80000022
|
||||
LcVersionMinMacosx LoadCommand = 0x24
|
||||
LcVersionMinIphoneos LoadCommand = 0x25
|
||||
LcFunctionStarts LoadCommand = 0x26
|
||||
LcDyldEnvironment LoadCommand = 0x27
|
||||
LcMain LoadCommand = 0x80000028
|
||||
LcDataInCode LoadCommand = 0x29
|
||||
LcSourceVersion LoadCommand = 0x2A
|
||||
LcDylibCodeSignDrs LoadCommand = 0x2B
|
||||
LcEncryptionInfo64 LoadCommand = 0x2C
|
||||
LcVersionMinTvos LoadCommand = 0x2F
|
||||
LcVersionMinWatchos LoadCommand = 0x30
|
||||
)
|
122
src/os/mac/macho/MachO.go
Normal file
122
src/os/mac/macho/MachO.go
Normal file
|
@ -0,0 +1,122 @@
|
|||
package macho
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
|
||||
"git.akyoto.dev/cli/q/src/config"
|
||||
"git.akyoto.dev/cli/q/src/os/linux/elf"
|
||||
)
|
||||
|
||||
// MachO is the executable format used on MacOS.
|
||||
type MachO struct {
|
||||
Header
|
||||
Code []byte
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// New creates a new Mach-O binary.
|
||||
func New(code []byte, data []byte) *MachO {
|
||||
return &MachO{
|
||||
Header: Header{
|
||||
Magic: 0xFEEDFACF,
|
||||
Architecture: CPU_X86_64,
|
||||
MicroArchitecture: 3 | 0x80000000,
|
||||
Type: TypeExecute,
|
||||
NumCommands: 4,
|
||||
SizeCommands: 0x48*3 + 184,
|
||||
Flags: FlagNoUndefs,
|
||||
Reserved: 0,
|
||||
},
|
||||
Code: code,
|
||||
Data: data,
|
||||
}
|
||||
}
|
||||
|
||||
// Write writes the Mach-O format to the given writer.
|
||||
func (m *MachO) Write(writer io.Writer) {
|
||||
binary.Write(writer, binary.LittleEndian, &m.Header)
|
||||
|
||||
binary.Write(writer, binary.LittleEndian, &Segment64{
|
||||
LoadCommand: LcSegment64,
|
||||
Length: 0x48,
|
||||
Name: [16]byte{'_', '_', 'P', 'A', 'G', 'E', 'Z', 'E', 'R', 'O'},
|
||||
Address: 0,
|
||||
SizeInMemory: config.BaseAddress,
|
||||
Offset: 0,
|
||||
SizeInFile: 0,
|
||||
NumSections: 0,
|
||||
Flag: 0,
|
||||
MaxProt: 0,
|
||||
InitProt: 0,
|
||||
})
|
||||
|
||||
codeStart := 32 + m.Header.SizeCommands
|
||||
codeEnd := uint64(codeStart) + uint64(len(m.Code))
|
||||
dataPadding := elf.Padding(codeEnd, 4096)
|
||||
dataStart := codeEnd + dataPadding
|
||||
|
||||
binary.Write(writer, binary.LittleEndian, &Segment64{
|
||||
LoadCommand: LcSegment64,
|
||||
Length: 0x48,
|
||||
Name: [16]byte{'_', '_', 'T', 'E', 'X', 'T'},
|
||||
Address: config.BaseAddress,
|
||||
SizeInMemory: codeEnd,
|
||||
Offset: 0,
|
||||
SizeInFile: codeEnd,
|
||||
NumSections: 0,
|
||||
Flag: 0,
|
||||
MaxProt: ProtReadable | ProtWritable | ProtExecutable,
|
||||
InitProt: ProtReadable | ProtExecutable,
|
||||
})
|
||||
|
||||
binary.Write(writer, binary.LittleEndian, &Segment64{
|
||||
LoadCommand: LcSegment64,
|
||||
Length: 0x48,
|
||||
Name: [16]byte{'_', '_', 'D', 'A', 'T', 'A'},
|
||||
Address: config.BaseAddress + dataStart,
|
||||
SizeInMemory: uint64(len(m.Data)),
|
||||
Offset: dataStart,
|
||||
SizeInFile: uint64(len(m.Data)),
|
||||
NumSections: 0,
|
||||
Flag: 0,
|
||||
MaxProt: ProtReadable,
|
||||
InitProt: ProtReadable,
|
||||
})
|
||||
|
||||
binary.Write(writer, binary.LittleEndian, &Thread{
|
||||
LoadCommand: LcUnixthread,
|
||||
Len: 184,
|
||||
Type: 0x4,
|
||||
})
|
||||
|
||||
binary.Write(writer, binary.LittleEndian, []uint32{
|
||||
42,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
config.BaseAddress + uint32(codeStart), 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
})
|
||||
|
||||
writer.Write(m.Code)
|
||||
writer.Write(bytes.Repeat([]byte{0}, int(dataPadding)))
|
||||
writer.Write(m.Data)
|
||||
}
|
9
src/os/mac/macho/Prot.go
Normal file
9
src/os/mac/macho/Prot.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
package macho
|
||||
|
||||
type Prot uint32
|
||||
|
||||
const (
|
||||
ProtReadable Prot = 0x1
|
||||
ProtWritable Prot = 0x2
|
||||
ProtExecutable Prot = 0x4
|
||||
)
|
16
src/os/mac/macho/Segment64.go
Normal file
16
src/os/mac/macho/Segment64.go
Normal file
|
@ -0,0 +1,16 @@
|
|||
package macho
|
||||
|
||||
// Segment64 is a segment load command.
|
||||
type Segment64 struct {
|
||||
LoadCommand
|
||||
Length uint32
|
||||
Name [16]byte
|
||||
Address uint64
|
||||
SizeInMemory uint64
|
||||
Offset uint64
|
||||
SizeInFile uint64
|
||||
MaxProt Prot
|
||||
InitProt Prot
|
||||
NumSections uint32
|
||||
Flag uint32
|
||||
}
|
8
src/os/mac/macho/Thread.go
Normal file
8
src/os/mac/macho/Thread.go
Normal file
|
@ -0,0 +1,8 @@
|
|||
package macho
|
||||
|
||||
// Thread is a thread state load command.
|
||||
type Thread struct {
|
||||
LoadCommand
|
||||
Len uint32
|
||||
Type uint32
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue