Added a linker
All checks were successful
/ test (push) Successful in 15s

This commit is contained in:
Eduard Urbach 2025-06-23 16:19:16 +02:00
parent 3ae47f93eb
commit cc2e98ca49
Signed by: akyoto
GPG key ID: 49226B848C78F6C8
26 changed files with 541 additions and 11 deletions

12
src/exe/Align.go Normal file
View file

@ -0,0 +1,12 @@
package exe
// Align calculates the next aligned address.
func Align[T int | uint | int64 | uint64 | int32 | uint32](n T, alignment T) T {
return (n + (alignment - 1)) & ^(alignment - 1)
}
// AlignPad calculates the next aligned address and the padding needed.
func AlignPad[T int | uint | int64 | uint64 | int32 | uint32](n T, alignment T) (T, T) {
aligned := Align(n, alignment)
return aligned, aligned - n
}

7
src/exe/Discard.go Normal file
View file

@ -0,0 +1,7 @@
package exe
// Discard implements a no-op WriteSeeker.
type Discard struct{}
func (w *Discard) Write(_ []byte) (int, error) { return 0, nil }
func (w *Discard) Seek(_ int64, _ int) (int64, error) { return 0, nil }

14
src/exe/Discard_test.go Normal file
View file

@ -0,0 +1,14 @@
package exe_test
import (
"io"
"testing"
"git.urbach.dev/cli/q/src/exe"
)
func TestDiscard(t *testing.T) {
discard := exe.Discard{}
discard.Write(nil)
discard.Seek(0, io.SeekCurrent)
}

42
src/exe/Executable.go Normal file
View file

@ -0,0 +1,42 @@
package exe
// Executable is a generic definition of the binary that later gets translated to OS-specific formats.
type Executable struct {
Sections []*Section
headerEnd int
fileAlign int
memoryAlign int
}
// New creates a new executable.
func New(headerEnd int, fileAlign int, memoryAlign int) *Executable {
return &Executable{
headerEnd: headerEnd,
fileAlign: fileAlign,
memoryAlign: memoryAlign,
}
}
// 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()
}
// Update recalculates all section offsets.
func (exe *Executable) Update() {
first := exe.Sections[0]
first.FileOffset, first.Padding = AlignPad(exe.headerEnd, exe.fileAlign)
first.MemoryOffset = Align(exe.headerEnd, 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)
}
}

View file

@ -0,0 +1,19 @@
package exe_test
import (
"testing"
"git.urbach.dev/cli/q/src/exe"
"git.urbach.dev/go/assert"
)
func TestExecutable(t *testing.T) {
align := 0x1000
x := exe.New(1, align, align)
x.InitSections([]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)
}

9
src/exe/Section.go Normal file
View file

@ -0,0 +1,9 @@
package exe
// Section represents some data within the executable that will also be loaded into memory.
type Section struct {
Bytes []byte
FileOffset int
Padding int
MemoryOffset int
}