Improved alignment function

This commit is contained in:
Eduard Urbach 2024-08-15 13:53:00 +02:00
parent 7092cb6626
commit bec409dbd0
Signed by: akyoto
GPG Key ID: C874F672B1AF20C0
8 changed files with 56 additions and 43 deletions

View File

@ -1,3 +1,7 @@
write(_ Int, _ Pointer, _ Int) -> Int { write(_ Int, _ Pointer, _ Int) -> Int {
return 0 return 0
} }
exit(_ Int) {
return
}

View File

@ -337,12 +337,11 @@ restart:
} }
data, dataLabels = a.Data.Finalize() data, dataLabels = a.Data.Finalize()
dataStart := config.BaseAddress + config.CodeOffset + len(code)
dataStart := Address(config.BaseAddress) + config.CodeOffset + Address(len(code)) dataStart, _ = exe.Align(dataStart, config.Align)
dataStart += exe.Padding(dataStart, config.Align)
for _, pointer := range dataPointers { for _, pointer := range dataPointers {
address := dataStart + pointer.Resolve() address := Address(dataStart) + pointer.Resolve()
slice := code[pointer.Position : pointer.Position+4] slice := code[pointer.Position : pointer.Position+4]
binary.LittleEndian.PutUint32(slice, uint32(address)) binary.LittleEndian.PutUint32(slice, uint32(address))
} }

7
src/exe/Align.go Normal file
View File

@ -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
}

View File

@ -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)
}

View File

@ -21,10 +21,8 @@ func Write(writer io.Writer, code []byte, data []byte) {
const HeaderEnd = HeaderSize + ProgramHeaderSize*2 const HeaderEnd = HeaderSize + ProgramHeaderSize*2
var ( var (
codePadding = exe.Padding(HeaderEnd, config.Align) codeStart, codePadding = exe.Align(HeaderEnd, config.Align)
codeEnd = config.CodeOffset + len(code) dataStart, dataPadding = exe.Align(codeStart+len(code), config.Align)
dataPadding = exe.Padding(codeEnd, config.Align)
dataStart = codeEnd + dataPadding
) )
elf := &ELF{ 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.Header)
binary.Write(writer, binary.LittleEndian, &elf.CodeHeader) binary.Write(writer, binary.LittleEndian, &elf.CodeHeader)
binary.Write(writer, binary.LittleEndian, &elf.DataHeader) binary.Write(writer, binary.LittleEndian, &elf.DataHeader)
writer.Write(bytes.Repeat([]byte{0}, codePadding)) writer.Write(bytes.Repeat([]byte{0x00}, codePadding))
writer.Write(code) writer.Write(code)
if len(data) > 0 { if len(data) > 0 {
writer.Write(bytes.Repeat([]byte{0}, dataPadding)) writer.Write(bytes.Repeat([]byte{0x00}, dataPadding))
writer.Write(data) writer.Write(data)
} }
} }

View File

@ -26,10 +26,8 @@ func Write(writer io.Writer, code []byte, data []byte) {
) )
var ( var (
codePadding = exe.Padding(HeaderEnd, config.Align) codeStart, codePadding = exe.Align(HeaderEnd, config.Align)
codeEnd = config.CodeOffset + len(code) dataStart, dataPadding = exe.Align(codeStart+len(code), config.Align)
dataPadding = exe.Padding(codeEnd, config.Align)
dataStart = codeEnd + dataPadding
) )
m := &MachO{ m := &MachO{
@ -61,9 +59,9 @@ func Write(writer io.Writer, code []byte, data []byte) {
Length: Segment64Size, Length: Segment64Size,
Name: [16]byte{'_', '_', 'T', 'E', 'X', 'T'}, Name: [16]byte{'_', '_', 'T', 'E', 'X', 'T'},
Address: config.BaseAddress, Address: config.BaseAddress,
SizeInMemory: uint64(codeEnd), SizeInMemory: uint64(codeStart + len(code)),
Offset: 0, Offset: 0,
SizeInFile: uint64(codeEnd), SizeInFile: uint64(codeStart + len(code)),
NumSections: 0, NumSections: 0,
Flag: 0, Flag: 0,
MaxProt: ProtReadable | ProtExecutable, 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.DataHeader)
binary.Write(writer, binary.LittleEndian, &m.UnixThread) 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(code)
writer.Write(bytes.Repeat([]byte{0}, int(dataPadding))) writer.Write(bytes.Repeat([]byte{0x00}, int(dataPadding)))
writer.Write(data) writer.Write(data)
} }

View File

@ -9,31 +9,36 @@ import (
"git.akyoto.dev/cli/q/src/exe" "git.akyoto.dev/cli/q/src/exe"
) )
const NumSections = 2
// EXE is the portable executable format used on Windows. // EXE is the portable executable format used on Windows.
type EXE struct { type EXE struct {
DOSHeader DOSHeader
NTHeader NTHeader
OptionalHeader64 OptionalHeader64
Sections [NumSections]SectionHeader Sections []SectionHeader
} }
// Write writes the EXE file to the given writer. // Write writes the EXE file to the given writer.
func Write(writer io.Writer, code []byte, data []byte) { func Write(writer io.Writer, code []byte, data []byte) {
const ( NumSections := 1
HeaderEnd = DOSHeaderSize + NTHeaderSize + OptionalHeader64Size + SectionHeaderSize*NumSections
) if len(data) != 0 {
NumSections += 1
}
HeaderEnd := DOSHeaderSize + NTHeaderSize + OptionalHeader64Size + SectionHeaderSize*NumSections
var ( var (
codePadding = exe.Padding(HeaderEnd, config.Align) codeStart, codePadding = exe.Align(HeaderEnd, config.Align)
codeEnd = config.CodeOffset + len(code) dataStart, dataPadding = exe.Align(codeStart+len(code), config.Align)
dataPadding = exe.Padding(codeEnd, config.Align)
dataStart = codeEnd + dataPadding
) )
imageSize := dataStart + len(data) imageSize := codeStart + len(code)
imageSize += exe.Padding(imageSize, config.Align)
if len(data) != 0 {
imageSize = dataStart + len(data)
}
imageSize, _ = exe.Align(imageSize, config.Align)
pe := &EXE{ pe := &EXE{
DOSHeader: DOSHeader{ DOSHeader: DOSHeader{
@ -43,7 +48,7 @@ func Write(writer io.Writer, code []byte, data []byte) {
NTHeader: NTHeader{ NTHeader: NTHeader{
Signature: [4]byte{'P', 'E', 0, 0}, Signature: [4]byte{'P', 'E', 0, 0},
Machine: IMAGE_FILE_MACHINE_AMD64, Machine: IMAGE_FILE_MACHINE_AMD64,
NumberOfSections: NumSections, NumberOfSections: uint16(NumSections),
SizeOfOptionalHeader: OptionalHeader64Size, SizeOfOptionalHeader: OptionalHeader64Size,
Characteristics: IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_LARGE_ADDRESS_AWARE, 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, MajorSubsystemVersion: 0x06,
SizeOfImage: uint32(imageSize), SizeOfImage: uint32(imageSize),
SizeOfHeaders: config.CodeOffset, // section bodies begin here 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, DllCharacteristics: IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA | IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE | IMAGE_DLLCHARACTERISTICS_NX_COMPAT | IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE,
SizeOfStackReserve: 0x100000, SizeOfStackReserve: 0x100000,
SizeOfStackCommit: 0x1000, SizeOfStackCommit: 0x1000,
@ -87,7 +92,7 @@ func Write(writer io.Writer, code []byte, data []byte) {
{VirtualAddress: 0, Size: 0}, {VirtualAddress: 0, Size: 0},
}, },
}, },
Sections: [NumSections]SectionHeader{ Sections: []SectionHeader{
{ {
Name: [8]byte{'.', 't', 'e', 'x', 't'}, Name: [8]byte{'.', 't', 'e', 'x', 't'},
VirtualSize: uint32(len(code)), 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.DOSHeader)
binary.Write(writer, binary.LittleEndian, &pe.NTHeader) binary.Write(writer, binary.LittleEndian, &pe.NTHeader)
binary.Write(writer, binary.LittleEndian, &pe.OptionalHeader64) 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(code)
writer.Write(bytes.Repeat([]byte{0}, int(dataPadding)))
if len(data) != 0 {
writer.Write(bytes.Repeat([]byte{0x00}, int(dataPadding)))
writer.Write(data) writer.Write(data)
} }
}

View File

@ -1,5 +1,10 @@
## Portable Executable ## 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 ## Links
- https://learn.microsoft.com/en-us/previous-versions/ms809762(v=msdn.10) - https://learn.microsoft.com/en-us/previous-versions/ms809762(v=msdn.10)