-
Notifications
You must be signed in to change notification settings - Fork 0
/
link.go
164 lines (143 loc) · 4.77 KB
/
link.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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
package linker
import (
"encoding/binary"
"fmt"
"github.com/dnsge/orange/arch"
"io"
)
type linkContext struct {
Symbols map[string]*InputObjectFile
Instructions []arch.Instruction
}
func Link(inputFiles []io.Reader, outputFile io.Writer) error {
objectFiles := make([]*InputObjectFile, len(inputFiles))
for i, f := range inputFiles {
obj, err := readObjectFile(f)
if err != nil {
return err
}
objectFiles[i] = obj
}
collectedSymbols, err := collectSymbolTableEntries(objectFiles)
if err != nil {
return err
}
collectedSections, sectionOrder := collectAssembledSections(objectFiles)
instructions := layoutAllSections(collectedSections, sectionOrder)
linkCtx := &linkContext{
Symbols: collectedSymbols,
Instructions: instructions,
}
err = linkCtx.relocateAll(objectFiles)
if err != nil {
return err
}
err = linkCtx.writeInstructions(outputFile)
return err
}
// collectSymbolTableEntries groups all the symbols from the input object
// files' symbol tables. Unresolved symbols (e.g. requested by a file but
// never defined) or duplicate symbols (e.g. two matching labels in different
// files) will return an error.
func collectSymbolTableEntries(objectFiles []*InputObjectFile) (map[string]*InputObjectFile, error) {
res := make(map[string]*InputObjectFile)
for _, objFile := range objectFiles {
for _, symbol := range objFile.SymbolTable {
// If the symbol is already in res and was resolved in a different file
if existing, ok := res[symbol.LabelName]; ok && existing != nil {
if symbol.Resolved {
// duplicate symbol entry
return nil, fmt.Errorf("duplicate symbol %q", symbol.LabelName)
} else {
// Symbol is not resolved in this file, but it was already resolved
// elsewhere. Just continue.
continue
}
}
if symbol.Resolved {
// Mark as resolved
res[symbol.LabelName] = objFile
} else {
// Mark as still unresolved
res[symbol.LabelName] = nil
}
}
}
// Check for any nil entries which signifies an undefined symbol
for name, objFile := range res {
if objFile == nil {
return nil, fmt.Errorf("undefined symbol %q", name)
}
}
return res, nil
}
// collectAssembledSections groups all the AssembledSections specified by the
// input object files, returning a map of every section name to the array of
// section instances and the order that the section names appeared.
func collectAssembledSections(objectFiles []*InputObjectFile) (map[string][]*AssembledSection, []string) {
// use map and order slice to imitate ordered map behavior
sections := make(map[string][]*AssembledSection)
var order []string
for _, objFile := range objectFiles {
for _, section := range objFile.Sections {
sectionGroup := sections[section.Name]
if sectionGroup == nil {
// first of this section name, add to order
order = append(order, section.Name)
}
sections[section.Name] = append(sectionGroup, section)
}
}
return sections, order
}
func layoutAllSections(sections map[string][]*AssembledSection, sectionOrder []string) (res []arch.Instruction) {
address := 0
for _, sectionName := range sectionOrder {
assembledSections := sections[sectionName]
// iterate over each defined section that shares the same name
for _, section := range assembledSections {
res = append(res, section.RawData...)
section.absoluteOffset = address
address += section.Size
}
}
return res
}
// relocateAll performs the relocation for every object file given. Each symbol
// specified in the InputObjectFile's relocation table is found and the
// corresponding instruction is then updated.
func (l *linkContext) relocateAll(objectFiles []*InputObjectFile) error {
for _, objFile := range objectFiles {
for _, relocation := range objFile.RelocationTable {
symbolFile, ok := l.Symbols[relocation.LabelName]
if !ok {
return fmt.Errorf("relocation table contains undefined symbol %q", relocation.LabelName)
}
// Find the address of the symbol to relocate for
symbolAddress, err := symbolFile.GetSymbolAbsoluteAddress(relocation.LabelName)
if err != nil {
return err
}
// Compute the absolute address of the instruction being relocated
relocationAddress, err := objFile.GetSectionOffsetAbsoluteAddress(relocation.SectionName, relocation.SectionOffset)
if err != nil {
return err
}
// Actually modify the instruction from linkContext.Instructions
err = performRelocation(l.Instructions, symbolAddress, relocationAddress, relocation)
if err != nil {
return err
}
}
}
return nil
}
// writeInstructions writes each instruction to the output writer
func (l *linkContext) writeInstructions(writer io.Writer) error {
for i := range l.Instructions {
if err := binary.Write(writer, arch.ByteOrder, l.Instructions[i]); err != nil {
return err
}
}
return nil
}