From bec409dbd0359750f7bf8c224d5160922ec28713 Mon Sep 17 00:00:00 2001 From: Eduard Urbach Date: Thu, 15 Aug 2024 13:53:00 +0200 Subject: [PATCH] Improved alignment function --- lib/sys/sys_windows.q | 4 ++++ src/asm/Finalize.go | 7 +++---- src/exe/Align.go | 7 +++++++ src/exe/Padding.go | 6 ------ src/exe/elf/ELF.go | 10 ++++----- src/exe/macho/MachO.go | 14 ++++++------- src/exe/pe/EXE.go | 46 +++++++++++++++++++++++++----------------- src/exe/pe/pe.md | 5 +++++ 8 files changed, 56 insertions(+), 43 deletions(-) create mode 100644 src/exe/Align.go delete mode 100644 src/exe/Padding.go diff --git a/lib/sys/sys_windows.q b/lib/sys/sys_windows.q index dfab144..105b33d 100644 --- a/lib/sys/sys_windows.q +++ b/lib/sys/sys_windows.q @@ -1,3 +1,7 @@ write(_ Int, _ Pointer, _ Int) -> Int { return 0 +} + +exit(_ Int) { + return } \ No newline at end of file diff --git a/src/asm/Finalize.go b/src/asm/Finalize.go index 9f46845..4276e38 100644 --- a/src/asm/Finalize.go +++ b/src/asm/Finalize.go @@ -337,12 +337,11 @@ restart: } data, dataLabels = a.Data.Finalize() - - dataStart := Address(config.BaseAddress) + config.CodeOffset + Address(len(code)) - dataStart += exe.Padding(dataStart, config.Align) + dataStart := config.BaseAddress + config.CodeOffset + len(code) + dataStart, _ = exe.Align(dataStart, config.Align) for _, pointer := range dataPointers { - address := dataStart + pointer.Resolve() + address := Address(dataStart) + pointer.Resolve() slice := code[pointer.Position : pointer.Position+4] binary.LittleEndian.PutUint32(slice, uint32(address)) } diff --git a/src/exe/Align.go b/src/exe/Align.go new file mode 100644 index 0000000..024df62 --- /dev/null +++ b/src/exe/Align.go @@ -0,0 +1,7 @@ +package exe + +// Align calculates the next aligned address and the padding needed. +func Align[T int | uint | int64 | uint64 | int32 | uint32](n T, alignment T) (T, T) { + aligned := (n + (alignment - 1)) & ^(alignment - 1) + return aligned, aligned - n +} diff --git a/src/exe/Padding.go b/src/exe/Padding.go deleted file mode 100644 index bf8195c..0000000 --- a/src/exe/Padding.go +++ /dev/null @@ -1,6 +0,0 @@ -package exe - -// Padding calculates the padding needed to align `n` bytes with the specified alignment. -func Padding[T int | uint | int64 | uint64 | int32 | uint32](n T, align T) T { - return align - (n % align) -} diff --git a/src/exe/elf/ELF.go b/src/exe/elf/ELF.go index 413ece9..af9b469 100644 --- a/src/exe/elf/ELF.go +++ b/src/exe/elf/ELF.go @@ -21,10 +21,8 @@ func Write(writer io.Writer, code []byte, data []byte) { const HeaderEnd = HeaderSize + ProgramHeaderSize*2 var ( - codePadding = exe.Padding(HeaderEnd, config.Align) - codeEnd = config.CodeOffset + len(code) - dataPadding = exe.Padding(codeEnd, config.Align) - dataStart = codeEnd + dataPadding + codeStart, codePadding = exe.Align(HeaderEnd, config.Align) + dataStart, dataPadding = exe.Align(codeStart+len(code), config.Align) ) elf := &ELF{ @@ -74,11 +72,11 @@ func Write(writer io.Writer, code []byte, data []byte) { binary.Write(writer, binary.LittleEndian, &elf.Header) binary.Write(writer, binary.LittleEndian, &elf.CodeHeader) binary.Write(writer, binary.LittleEndian, &elf.DataHeader) - writer.Write(bytes.Repeat([]byte{0}, codePadding)) + writer.Write(bytes.Repeat([]byte{0x00}, codePadding)) writer.Write(code) if len(data) > 0 { - writer.Write(bytes.Repeat([]byte{0}, dataPadding)) + writer.Write(bytes.Repeat([]byte{0x00}, dataPadding)) writer.Write(data) } } diff --git a/src/exe/macho/MachO.go b/src/exe/macho/MachO.go index 5f2cbcb..ef44ff1 100644 --- a/src/exe/macho/MachO.go +++ b/src/exe/macho/MachO.go @@ -26,10 +26,8 @@ func Write(writer io.Writer, code []byte, data []byte) { ) var ( - codePadding = exe.Padding(HeaderEnd, config.Align) - codeEnd = config.CodeOffset + len(code) - dataPadding = exe.Padding(codeEnd, config.Align) - dataStart = codeEnd + dataPadding + codeStart, codePadding = exe.Align(HeaderEnd, config.Align) + dataStart, dataPadding = exe.Align(codeStart+len(code), config.Align) ) m := &MachO{ @@ -61,9 +59,9 @@ func Write(writer io.Writer, code []byte, data []byte) { Length: Segment64Size, Name: [16]byte{'_', '_', 'T', 'E', 'X', 'T'}, Address: config.BaseAddress, - SizeInMemory: uint64(codeEnd), + SizeInMemory: uint64(codeStart + len(code)), Offset: 0, - SizeInFile: uint64(codeEnd), + SizeInFile: uint64(codeStart + len(code)), NumSections: 0, Flag: 0, MaxProt: ProtReadable | ProtExecutable, @@ -119,8 +117,8 @@ func Write(writer io.Writer, code []byte, data []byte) { binary.Write(writer, binary.LittleEndian, &m.DataHeader) binary.Write(writer, binary.LittleEndian, &m.UnixThread) - writer.Write(bytes.Repeat([]byte{0}, int(codePadding))) + writer.Write(bytes.Repeat([]byte{0x00}, int(codePadding))) writer.Write(code) - writer.Write(bytes.Repeat([]byte{0}, int(dataPadding))) + writer.Write(bytes.Repeat([]byte{0x00}, int(dataPadding))) writer.Write(data) } diff --git a/src/exe/pe/EXE.go b/src/exe/pe/EXE.go index f9e55c4..09f96f3 100644 --- a/src/exe/pe/EXE.go +++ b/src/exe/pe/EXE.go @@ -9,31 +9,36 @@ import ( "git.akyoto.dev/cli/q/src/exe" ) -const NumSections = 2 - // EXE is the portable executable format used on Windows. type EXE struct { DOSHeader NTHeader OptionalHeader64 - Sections [NumSections]SectionHeader + Sections []SectionHeader } // Write writes the EXE file to the given writer. func Write(writer io.Writer, code []byte, data []byte) { - const ( - HeaderEnd = DOSHeaderSize + NTHeaderSize + OptionalHeader64Size + SectionHeaderSize*NumSections - ) + NumSections := 1 + + if len(data) != 0 { + NumSections += 1 + } + + HeaderEnd := DOSHeaderSize + NTHeaderSize + OptionalHeader64Size + SectionHeaderSize*NumSections var ( - codePadding = exe.Padding(HeaderEnd, config.Align) - codeEnd = config.CodeOffset + len(code) - dataPadding = exe.Padding(codeEnd, config.Align) - dataStart = codeEnd + dataPadding + codeStart, codePadding = exe.Align(HeaderEnd, config.Align) + dataStart, dataPadding = exe.Align(codeStart+len(code), config.Align) ) - imageSize := dataStart + len(data) - imageSize += exe.Padding(imageSize, config.Align) + imageSize := codeStart + len(code) + + if len(data) != 0 { + imageSize = dataStart + len(data) + } + + imageSize, _ = exe.Align(imageSize, config.Align) pe := &EXE{ DOSHeader: DOSHeader{ @@ -43,7 +48,7 @@ func Write(writer io.Writer, code []byte, data []byte) { NTHeader: NTHeader{ Signature: [4]byte{'P', 'E', 0, 0}, Machine: IMAGE_FILE_MACHINE_AMD64, - NumberOfSections: NumSections, + NumberOfSections: uint16(NumSections), SizeOfOptionalHeader: OptionalHeader64Size, Characteristics: IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_LARGE_ADDRESS_AWARE, }, @@ -61,7 +66,7 @@ func Write(writer io.Writer, code []byte, data []byte) { MajorSubsystemVersion: 0x06, SizeOfImage: uint32(imageSize), SizeOfHeaders: config.CodeOffset, // section bodies begin here - Subsystem: IMAGE_SUBSYSTEM_WINDOWS_GUI, + Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI, DllCharacteristics: IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA | IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE | IMAGE_DLLCHARACTERISTICS_NX_COMPAT | IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE, SizeOfStackReserve: 0x100000, SizeOfStackCommit: 0x1000, @@ -87,7 +92,7 @@ func Write(writer io.Writer, code []byte, data []byte) { {VirtualAddress: 0, Size: 0}, }, }, - Sections: [NumSections]SectionHeader{ + Sections: []SectionHeader{ { Name: [8]byte{'.', 't', 'e', 'x', 't'}, VirtualSize: uint32(len(code)), @@ -110,10 +115,13 @@ func Write(writer io.Writer, code []byte, data []byte) { binary.Write(writer, binary.LittleEndian, &pe.DOSHeader) binary.Write(writer, binary.LittleEndian, &pe.NTHeader) binary.Write(writer, binary.LittleEndian, &pe.OptionalHeader64) - binary.Write(writer, binary.LittleEndian, &pe.Sections) + binary.Write(writer, binary.LittleEndian, pe.Sections[:NumSections]) - writer.Write(bytes.Repeat([]byte{0}, int(codePadding))) + writer.Write(bytes.Repeat([]byte{0x00}, int(codePadding))) writer.Write(code) - writer.Write(bytes.Repeat([]byte{0}, int(dataPadding))) - writer.Write(data) + + if len(data) != 0 { + writer.Write(bytes.Repeat([]byte{0x00}, int(dataPadding))) + writer.Write(data) + } } diff --git a/src/exe/pe/pe.md b/src/exe/pe/pe.md index ae779df..7297fbb 100644 --- a/src/exe/pe/pe.md +++ b/src/exe/pe/pe.md @@ -1,5 +1,10 @@ ## Portable Executable +## Notes + +Unlike Linux, Windows does not ignore zero-length sections and will fail loading them because they don't exist within the file. +Adding a single byte to the section can fix this problem, but it's easier to just remove the section header entirely. + ## Links - https://learn.microsoft.com/en-us/previous-versions/ms809762(v=msdn.10)