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 {
return 0
}
exit(_ Int) {
return
}

View File

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

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

View File

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

View File

@ -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)))
if len(data) != 0 {
writer.Write(bytes.Repeat([]byte{0x00}, int(dataPadding)))
writer.Write(data)
}
}

View File

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