diff --git a/src/asm/Assembler.go b/src/asm/Assembler.go index 4140208..56f3595 100644 --- a/src/asm/Assembler.go +++ b/src/asm/Assembler.go @@ -59,8 +59,7 @@ func (a *Assembler) Compile(b *build.Build) (code []byte, data []byte, libs dll. } } - x := exe.New(elf.HeaderEnd, b.FileAlign(), b.MemoryAlign()) - x.InitSections(c.code, c.data, nil) + x := exe.New(elf.HeaderEnd, b.FileAlign(), b.MemoryAlign(), b.Congruent(), c.code, c.data, nil) dataSectionOffset := x.Sections[1].MemoryOffset - x.Sections[0].MemoryOffset for dataLabel, address := range dataLabels { diff --git a/src/build/Congruent.go b/src/build/Congruent.go new file mode 100644 index 0000000..240b5e7 --- /dev/null +++ b/src/build/Congruent.go @@ -0,0 +1,6 @@ +package build + +// Congruent returns true if the platform needs to force `virtual address % alignment == file offset % alignment`. +func (build *Build) Congruent() bool { + return build.OS == Linux +} \ No newline at end of file diff --git a/src/build/FileAlign.go b/src/build/FileAlign.go index ca9954b..5bec044 100644 --- a/src/build/FileAlign.go +++ b/src/build/FileAlign.go @@ -1,6 +1,13 @@ package build +// cacheLineSize is the smallest unit of data that can be transferred between the RAM and the CPU cache. +const cacheLineSize = 64 + // FileAlign returns the file alignment. func (build *Build) FileAlign() int { + if build.OS == Linux { + return cacheLineSize + } + return build.MemoryAlign() } \ No newline at end of file diff --git a/src/build/MemoryAlign.go b/src/build/MemoryAlign.go index ae5b90a..c7477ed 100644 --- a/src/build/MemoryAlign.go +++ b/src/build/MemoryAlign.go @@ -4,7 +4,7 @@ package build func (build *Build) MemoryAlign() int { switch build.Arch { case ARM: - return 0x4000 + return 0x10000 default: return 0x1000 } diff --git a/src/elf/ELF.go b/src/elf/ELF.go index 87b485f..5889e9e 100644 --- a/src/elf/ELF.go +++ b/src/elf/ELF.go @@ -18,8 +18,7 @@ type ELF struct { // Write writes the ELF64 format to the given writer. func Write(writer io.WriteSeeker, b *build.Build, codeBytes []byte, dataBytes []byte) { - x := exe.New(HeaderEnd, b.FileAlign(), b.MemoryAlign()) - x.InitSections(codeBytes, dataBytes) + x := exe.New(HeaderEnd, b.FileAlign(), b.MemoryAlign(), b.Congruent(), codeBytes, dataBytes) code := x.Sections[0] data := x.Sections[1] diff --git a/src/exe/Executable.go b/src/exe/Executable.go index 20655f3..b0c6fa0 100644 --- a/src/exe/Executable.go +++ b/src/exe/Executable.go @@ -6,26 +6,25 @@ type Executable struct { headerEnd int fileAlign int memoryAlign int + congruent bool } // New creates a new executable. -func New(headerEnd int, fileAlign int, memoryAlign int) *Executable { - return &Executable{ +func New(headerEnd int, fileAlign int, memoryAlign int, congruent bool, raw ...[]byte) *Executable { + exe := &Executable{ + Sections: make([]*Section, len(raw)), headerEnd: headerEnd, fileAlign: fileAlign, memoryAlign: memoryAlign, + congruent: congruent, } -} - -// InitSections generates sections from raw byte slices. -func (exe *Executable) InitSections(raw ...[]byte) { - exe.Sections = make([]*Section, len(raw)) for i, data := range raw { exe.Sections[i] = &Section{Bytes: data} } exe.Update() + return exe } // Update recalculates all section offsets. @@ -34,9 +33,17 @@ func (exe *Executable) Update() { first.FileOffset, first.Padding = AlignPad(exe.headerEnd, exe.fileAlign) first.MemoryOffset = Align(exe.headerEnd, exe.memoryAlign) + if exe.congruent && exe.fileAlign != exe.memoryAlign { + first.MemoryOffset += first.FileOffset % exe.memoryAlign + } + for i, section := range exe.Sections[1:] { previous := exe.Sections[i] section.FileOffset, section.Padding = AlignPad(previous.FileOffset+len(previous.Bytes), exe.fileAlign) section.MemoryOffset = Align(previous.MemoryOffset+len(previous.Bytes), exe.memoryAlign) + + if exe.congruent && exe.fileAlign != exe.memoryAlign { + section.MemoryOffset += section.FileOffset % exe.memoryAlign + } } } \ No newline at end of file diff --git a/src/exe/Executable_test.go b/src/exe/Executable_test.go index a00c6f6..7b0af1a 100644 --- a/src/exe/Executable_test.go +++ b/src/exe/Executable_test.go @@ -7,13 +7,25 @@ import ( "git.urbach.dev/go/assert" ) -func TestExecutable(t *testing.T) { - align := 0x1000 - x := exe.New(1, align, align) - x.InitSections([]byte{1}, []byte{1}) +func TestSimple(t *testing.T) { + align := 32 + x := exe.New(1, align, align, false, []byte{1}, []byte{1}) assert.Equal(t, len(x.Sections), 2) assert.Equal(t, x.Sections[0].Padding, align-1) assert.Equal(t, x.Sections[0].FileOffset, align) assert.Equal(t, x.Sections[1].Padding, align-1) assert.Equal(t, x.Sections[1].FileOffset, align*2) +} + +func TestCongruent(t *testing.T) { + fileAlign := 16 + memoryAlign := 32 + x := exe.New(1, fileAlign, memoryAlign, true, []byte{1}, []byte{1}, []byte{1}) + assert.Equal(t, len(x.Sections), 3) + assert.Equal(t, x.Sections[0].FileOffset, fileAlign) + assert.Equal(t, x.Sections[1].FileOffset, fileAlign*2) + assert.Equal(t, x.Sections[2].FileOffset, fileAlign*3) + assert.Equal(t, x.Sections[0].MemoryOffset, memoryAlign+(fileAlign%memoryAlign)) + assert.Equal(t, x.Sections[1].MemoryOffset, memoryAlign*2+(fileAlign*2%memoryAlign)) + assert.Equal(t, x.Sections[2].MemoryOffset, memoryAlign*3+(fileAlign*3%memoryAlign)) } \ No newline at end of file diff --git a/src/macho/MachO.go b/src/macho/MachO.go index 31b2524..1d3f6e4 100644 --- a/src/macho/MachO.go +++ b/src/macho/MachO.go @@ -19,8 +19,7 @@ type MachO struct { // Write writes the Mach-O format to the given writer. func Write(writer io.WriteSeeker, b *build.Build, codeBytes []byte, dataBytes []byte) { - x := exe.New(HeaderEnd, b.FileAlign(), b.MemoryAlign()) - x.InitSections(codeBytes, dataBytes) + x := exe.New(HeaderEnd, b.FileAlign(), b.MemoryAlign(), b.Congruent(), codeBytes, dataBytes) code := x.Sections[0] data := x.Sections[1] arch, microArch := Arch(b.Arch) diff --git a/src/pe/EXE.go b/src/pe/EXE.go index 9baf3f1..3283965 100644 --- a/src/pe/EXE.go +++ b/src/pe/EXE.go @@ -20,8 +20,7 @@ type EXE struct { // Write writes the EXE file to the given writer. func Write(writer io.WriteSeeker, b *build.Build, codeBytes []byte, dataBytes []byte, libs dll.List) { - x := exe.New(HeaderEnd, b.FileAlign(), b.MemoryAlign()) - x.InitSections(codeBytes, dataBytes, nil) + x := exe.New(HeaderEnd, b.FileAlign(), b.MemoryAlign(), b.Congruent(), codeBytes, dataBytes, nil) code := x.Sections[0] data := x.Sections[1] imports := x.Sections[2]