diff --git a/go.mod b/go.mod index 82dd598..29d0c49 100644 --- a/go.mod +++ b/go.mod @@ -7,4 +7,4 @@ require ( git.akyoto.dev/go/color v0.1.1 ) -require golang.org/x/sys v0.24.0 // indirect +require golang.org/x/sys v0.29.0 // indirect diff --git a/go.sum b/go.sum index 929bcce..5b3d719 100644 --- a/go.sum +++ b/go.sum @@ -2,5 +2,5 @@ git.akyoto.dev/go/assert v0.1.3 h1:QwCUbmG4aZYsNk/OuRBz1zWVKmGlDUHhOnnDBfn8Qw8= git.akyoto.dev/go/assert v0.1.3/go.mod h1:0GzMaM0eURuDwtGkJJkCsI7r2aUKr+5GmWNTFPgDocM= git.akyoto.dev/go/color v0.1.1 h1:mMAoMIwLBPNy7ocRSxdsCFs7onPC3GfDEiJErCneqRE= git.akyoto.dev/go/color v0.1.1/go.mod h1:ywOjoD0O0sk6bIn92uAJf7mErlEFCuQInL84y4Lqi3Q= -golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= -golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= diff --git a/src/asm/Finalize.go b/src/asm/Finalize.go index b8a2622..22bebac 100644 --- a/src/asm/Finalize.go +++ b/src/asm/Finalize.go @@ -8,7 +8,10 @@ import ( "git.akyoto.dev/cli/q/src/config" "git.akyoto.dev/cli/q/src/dll" + "git.akyoto.dev/cli/q/src/elf" "git.akyoto.dev/cli/q/src/fs" + "git.akyoto.dev/cli/q/src/macho" + "git.akyoto.dev/cli/q/src/pe" "git.akyoto.dev/cli/q/src/sizeof" "git.akyoto.dev/cli/q/src/x64" ) @@ -370,21 +373,40 @@ restart: } } + headerEnd := Address(0) + + switch config.TargetOS { + case "linux": + headerEnd = elf.HeaderEnd + case "macos": + headerEnd = macho.HeaderEnd + case "windows": + headerEnd = pe.HeaderEnd + } + + codeStart, _ := fs.Align(headerEnd, config.Align) + dataStart, _ := fs.Align(codeStart+Address(len(code)), config.Align) data, dataLabels = a.Data.Finalize() - dataStart := config.BaseAddress + config.CodeOffset + len(code) - dataStart, _ = fs.Align(dataStart, config.Align) for _, pointer := range dataPointers { - address := Address(dataStart) + pointer.Resolve() + address := config.BaseAddress + Address(dataStart) + pointer.Resolve() slice := code[pointer.Position : pointer.Position+4] binary.LittleEndian.PutUint32(slice, uint32(address)) } - for _, pointer := range dllPointers { - destination := Address(0x3000) + pointer.Resolve() - address := destination - Address(config.CodeOffset+pointer.Position+Address(pointer.Size)) - slice := code[pointer.Position : pointer.Position+4] - binary.LittleEndian.PutUint32(slice, uint32(address)) + if config.TargetOS == "windows" { + if len(data) == 0 { + data = []byte{0} + } + + importsStart, _ := fs.Align(dataStart+Address(len(data)), config.Align) + + for _, pointer := range dllPointers { + destination := Address(importsStart) + pointer.Resolve() + delta := destination - Address(codeStart+pointer.Position+Address(pointer.Size)) + slice := code[pointer.Position : pointer.Position+4] + binary.LittleEndian.PutUint32(slice, uint32(delta)) + } } return code, data diff --git a/src/config/config.go b/src/config/config.go index 32153a1..6689c3e 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -9,11 +9,8 @@ const ( // The base address is the virtual address for our ELF file. BaseAddress = 0x40 * MinAddress - // Align decides the alignment of the sections and it must be a multiple of the page size. + // Align is the alignment of the sections and it must be a multiple of the page size. Align = 0x1000 - - // The code offset is the offset of the executable machine code within the file. - CodeOffset = Align ) var ( diff --git a/src/elf/ELF.go b/src/elf/ELF.go index 5ac1cb2..0248c7e 100644 --- a/src/elf/ELF.go +++ b/src/elf/ELF.go @@ -9,6 +9,8 @@ import ( "git.akyoto.dev/cli/q/src/fs" ) +const HeaderEnd = HeaderSize + ProgramHeaderSize*2 + // ELF represents an ELF file. type ELF struct { Header @@ -18,8 +20,6 @@ type ELF struct { // Write writes the ELF64 format to the given writer. func Write(writer io.Writer, code []byte, data []byte) { - const HeaderEnd = HeaderSize + ProgramHeaderSize*2 - var ( codeStart, codePadding = fs.Align(HeaderEnd, config.Align) dataStart, dataPadding = fs.Align(codeStart+len(code), config.Align) @@ -36,7 +36,7 @@ func Write(writer io.Writer, code []byte, data []byte) { Type: TypeExecutable, Architecture: ArchitectureAMD64, FileVersion: 1, - EntryPointInMemory: config.BaseAddress + config.CodeOffset, + EntryPointInMemory: int64(config.BaseAddress + codeStart), ProgramHeaderOffset: HeaderSize, SectionHeaderOffset: 0, Flags: 0, @@ -50,9 +50,9 @@ func Write(writer io.Writer, code []byte, data []byte) { CodeHeader: ProgramHeader{ Type: ProgramTypeLOAD, Flags: ProgramFlagsExecutable | ProgramFlagsReadable, - Offset: config.CodeOffset, - VirtualAddress: config.BaseAddress + config.CodeOffset, - PhysicalAddress: config.BaseAddress + config.CodeOffset, + Offset: int64(codeStart), + VirtualAddress: int64(config.BaseAddress + codeStart), + PhysicalAddress: int64(config.BaseAddress + codeStart), SizeInFile: int64(len(code)), SizeInMemory: int64(len(code)), Align: config.Align, diff --git a/src/macho/MachO.go b/src/macho/MachO.go index 5353a6d..21b5b19 100644 --- a/src/macho/MachO.go +++ b/src/macho/MachO.go @@ -9,6 +9,11 @@ import ( "git.akyoto.dev/cli/q/src/fs" ) +const ( + SizeCommands = Segment64Size*3 + ThreadSize + HeaderEnd = HeaderSize + SizeCommands +) + // MachO is the executable format used on MacOS. type MachO struct { Header @@ -20,11 +25,6 @@ type MachO struct { // Write writes the Mach-O format to the given writer. func Write(writer io.Writer, code []byte, data []byte) { - const ( - SizeCommands = Segment64Size*3 + ThreadSize - HeaderEnd = HeaderSize + SizeCommands - ) - var ( codeStart, codePadding = fs.Align(HeaderEnd, config.Align) dataStart, dataPadding = fs.Align(codeStart+len(code), config.Align) @@ -102,7 +102,7 @@ func Write(writer io.Writer, code []byte, data []byte) { 0, 0, 0, 0, 0, 0, - config.BaseAddress + config.CodeOffset, 0, + uint32(config.BaseAddress + codeStart), 0, 0, 0, 0, 0, 0, 0, diff --git a/src/pe/EXE.go b/src/pe/EXE.go index 5ab108f..ea856b5 100644 --- a/src/pe/EXE.go +++ b/src/pe/EXE.go @@ -10,6 +10,11 @@ import ( "git.akyoto.dev/cli/q/src/fs" ) +const ( + NumSections = 3 + HeaderEnd = DOSHeaderSize + NTHeaderSize + OptionalHeader64Size + SectionHeaderSize*NumSections +) + // EXE is the portable executable format used on Windows. type EXE struct { DOSHeader @@ -20,12 +25,6 @@ type EXE struct { // Write writes the EXE file to the given writer. func Write(writer io.Writer, code []byte, data []byte, dlls dll.List) { - if len(data) == 0 { - data = []byte{0} - } - - NumSections := 3 - HeaderEnd := DOSHeaderSize + NTHeaderSize + OptionalHeader64Size + SectionHeaderSize*NumSections codeStart, codePadding := fs.Align(HeaderEnd, config.Align) dataStart, dataPadding := fs.Align(codeStart+len(code), config.Align) importsStart, importsPadding := fs.Align(dataStart+len(data), config.Align) @@ -115,8 +114,8 @@ func Write(writer io.Writer, code []byte, data []byte, dlls dll.List) { SizeOfCode: uint32(len(code)), SizeOfInitializedData: 0, SizeOfUninitializedData: 0, - AddressOfEntryPoint: config.CodeOffset, - BaseOfCode: config.CodeOffset, + AddressOfEntryPoint: uint32(codeStart), + BaseOfCode: uint32(codeStart), ImageBase: config.BaseAddress, SectionAlignment: config.Align, // power of 2, must be greater than or equal to FileAlignment FileAlignment: config.Align, // power of 2 @@ -128,7 +127,7 @@ func Write(writer io.Writer, code []byte, data []byte, dlls dll.List) { MinorSubsystemVersion: 0, Win32VersionValue: 0, SizeOfImage: uint32(imageSize), - SizeOfHeaders: config.CodeOffset, // section bodies begin here + SizeOfHeaders: uint32(codeStart), // section bodies begin here CheckSum: 0, Subsystem: uint16(subSystem), DllCharacteristics: IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA | IMAGE_DLLCHARACTERISTICS_NX_COMPAT | IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE, // IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE @@ -161,9 +160,9 @@ func Write(writer io.Writer, code []byte, data []byte, dlls dll.List) { { Name: [8]byte{'.', 't', 'e', 'x', 't'}, VirtualSize: uint32(len(code)), - VirtualAddress: config.CodeOffset, + VirtualAddress: uint32(codeStart), RawSize: uint32(len(code)), // must be a multiple of FileAlignment - RawAddress: config.CodeOffset, // must be a multiple of FileAlignment + RawAddress: uint32(codeStart), // must be a multiple of FileAlignment Characteristics: IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ, }, {