forked from firedancer-io/radiance
-
Notifications
You must be signed in to change notification settings - Fork 0
/
copy.go
138 lines (117 loc) · 3.46 KB
/
copy.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
package loader
import (
"debug/elf"
"fmt"
"io"
"go.firedancer.io/radiance/pkg/sbf"
)
// The following ELF loading rules seem mostly arbitrary.
// For the sake of cleanliness, this loader doesn't process
// some badly malformed ELFs that would pass on Solana mainnet.
// The Solana protocol is being improved in this area.
// copy allocates program buffers and copies ELF contents.
func (l *Loader) copy() error {
l.progRange = newAddrRange()
l.rodatas = make([]addrRange, 0, 4)
if err := l.getText(); err != nil {
return err
}
if err := l.mapSections(); err != nil {
return err
}
if err := l.copySections(); err != nil {
return err
}
return nil
}
// getText remembers the range of .text in the program buffer
func (l *Loader) getText() error {
if err := l.checkSectionAddrs(l.shText); err != nil {
return fmt.Errorf("invalid .text: %w", err)
}
l.textRange = addrRange{min: l.shText.Off, max: l.shText.Off + l.shText.Size}
return nil
}
// mapRodataLike reserves ranges for sections in the program buffer
func (l *Loader) mapSections() error {
// Walk all non-standard rodata sections
iter := l.newShTableIter()
for iter.Next() && iter.Err() == nil {
i, sh := iter.Index(), iter.Item()
// Skip standard sections
sectionName, err := l.getString(&l.shShstrtab, sh.Name, maxSectionNameLen)
if err != nil {
return fmt.Errorf("getString: %w", err)
}
switch sectionName {
case ".text", ".rodata", ".data.rel.ro", ".eh_frame":
// ok
default:
continue
}
if err := l.checkSectionAddrs(&sh); err != nil {
return fmt.Errorf("invalid rodata-like section %d: %w", i, err)
}
// Section overlap check & bounds tracking
section := addrRange{min: sh.Off, max: sh.Off + sh.Size}
if section.len() == 0 {
continue
}
if l.progRange.containsRange(section) {
// TODO rbpf probably doesn't have this restriction
return fmt.Errorf("rodata section %d overlaps with other section", i)
}
l.progRange.insert(section)
if section.min != l.textRange.min {
l.rodatas = append(l.rodatas, section)
}
}
return iter.Err()
}
func (l *Loader) checkSectionAddrs(sh *elf.Section64) error {
// TODO Support true vaddr ELFs
if sh.Size > l.fileSize {
return io.ErrUnexpectedEOF
}
if sh.Addr != sh.Off {
return fmt.Errorf("section physical address out-of-place")
}
// Ensure section within VM program range
vaddr := clampAddUint64(sbf.VaddrProgram, sh.Addr)
vaddrEnd := vaddr + sh.Size
if vaddrEnd < vaddr || vaddrEnd > sbf.VaddrStack {
return fmt.Errorf("section virtual address out-of-bounds")
}
return nil
}
// copySections copies text and rodata-like sections from the ELF into VM memory.
func (l *Loader) copySections() error {
if l.progRange.len() == 0 {
// TODO what is the correct behavior here?
return fmt.Errorf("program is empty (???)")
}
l.progRange.extendToFit(0)
// Allocate!
l.program = make([]byte, l.progRange.len())
// Read data from ELF file
for _, section := range l.rodatas {
if err := l.copySection(section); err != nil {
return err
}
}
if err := l.copySection(l.textRange); err != nil {
return err
}
// Special sub-slice for text
l.text = l.getRange(l.textRange)
return nil
}
func (l *Loader) copySection(section addrRange) (err error) {
off, size := int64(section.min), int64(section.len())
rd := io.NewSectionReader(l.rd, off, size)
_, err = io.ReadFull(rd, l.program[section.min:section.max])
return
}
func (l *Loader) getRange(section addrRange) []byte {
return l.program[section.min:section.max]
}