summaryrefslogtreecommitdiffstats
path: root/src/cmd/link/internal/loadxcoff/ldxcoff.go
blob: a5744216d66ae108d6ec1f8a652fb9f2b6a1ce4b (plain)
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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package loadxcoff implements a XCOFF file reader.
package loadxcoff

import (
	"cmd/internal/bio"
	"cmd/internal/objabi"
	"cmd/internal/sys"
	"cmd/link/internal/loader"
	"cmd/link/internal/sym"
	"errors"
	"fmt"
	"internal/xcoff"
)

// ldSection is an XCOFF section with its symbols.
type ldSection struct {
	xcoff.Section
	sym loader.Sym
}

// TODO(brainman): maybe just add ReadAt method to bio.Reader instead of creating xcoffBiobuf

// xcoffBiobuf makes bio.Reader look like io.ReaderAt.
type xcoffBiobuf bio.Reader

func (f *xcoffBiobuf) ReadAt(p []byte, off int64) (int, error) {
	ret := ((*bio.Reader)(f)).MustSeek(off, 0)
	if ret < 0 {
		return 0, errors.New("fail to seek")
	}
	n, err := f.Read(p)
	if err != nil {
		return 0, err
	}
	return n, nil
}

// loads the Xcoff file pn from f.
// Symbols are written into loader, and a slice of the text symbols is returned.
func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Reader, pkg string, length int64, pn string) (textp []loader.Sym, err error) {
	errorf := func(str string, args ...interface{}) ([]loader.Sym, error) {
		return nil, fmt.Errorf("loadxcoff: %v: %v", pn, fmt.Sprintf(str, args...))
	}

	var ldSections []*ldSection

	f, err := xcoff.NewFile((*xcoffBiobuf)(input))
	if err != nil {
		return nil, err
	}
	defer f.Close()

	for _, sect := range f.Sections {
		//only text, data and bss section
		if sect.Type < xcoff.STYP_TEXT || sect.Type > xcoff.STYP_BSS {
			continue
		}
		lds := new(ldSection)
		lds.Section = *sect
		name := fmt.Sprintf("%s(%s)", pkg, lds.Name)
		symbol := l.LookupOrCreateSym(name, localSymVersion)
		s := l.MakeSymbolUpdater(symbol)

		switch lds.Type {
		default:
			return errorf("unrecognized section type 0x%x", lds.Type)
		case xcoff.STYP_TEXT:
			s.SetType(sym.STEXT)
		case xcoff.STYP_DATA:
			s.SetType(sym.SNOPTRDATA)
		case xcoff.STYP_BSS:
			s.SetType(sym.SNOPTRBSS)
		}

		s.SetSize(int64(lds.Size))
		if s.Type() != sym.SNOPTRBSS {
			data, err := lds.Section.Data()
			if err != nil {
				return nil, err
			}
			s.SetData(data)
		}

		lds.sym = symbol
		ldSections = append(ldSections, lds)
	}

	// sx = symbol from file
	// s = symbol for loader
	for _, sx := range f.Symbols {
		// get symbol type
		stype, errmsg := getSymbolType(f, sx)
		if errmsg != "" {
			return errorf("error reading symbol %s: %s", sx.Name, errmsg)
		}
		if stype == sym.Sxxx {
			continue
		}

		s := l.LookupOrCreateSym(sx.Name, 0)

		// Text symbol
		if l.SymType(s) == sym.STEXT {
			if l.AttrOnList(s) {
				return errorf("symbol %s listed multiple times", l.SymName(s))
			}
			l.SetAttrOnList(s, true)
			textp = append(textp, s)
		}
	}

	// Read relocations
	for _, sect := range ldSections {
		// TODO(aix): Dwarf section relocation if needed
		if sect.Type != xcoff.STYP_TEXT && sect.Type != xcoff.STYP_DATA {
			continue
		}
		sb := l.MakeSymbolUpdater(sect.sym)
		for _, rx := range sect.Relocs {
			rSym := l.LookupOrCreateSym(rx.Symbol.Name, 0)
			if uint64(int32(rx.VirtualAddress)) != rx.VirtualAddress {
				return errorf("virtual address of a relocation is too big: 0x%x", rx.VirtualAddress)
			}
			rOff := int32(rx.VirtualAddress)
			var rSize uint8
			var rType objabi.RelocType
			var rAdd int64
			switch rx.Type {
			default:
				return errorf("section %s: unknown relocation of type 0x%x", sect.Name, rx.Type)
			case xcoff.R_POS:
				// Reloc the address of r.Sym
				// Length should be 64
				if rx.Length != 64 {
					return errorf("section %s: relocation R_POS has length different from 64: %d", sect.Name, rx.Length)
				}
				rSize = 8
				rType = objabi.R_CONST
				rAdd = int64(rx.Symbol.Value)

			case xcoff.R_RBR:
				rSize = 4
				rType = objabi.R_CALLPOWER
				rAdd = 0
			}
			r, _ := sb.AddRel(rType)
			r.SetOff(rOff)
			r.SetSiz(rSize)
			r.SetSym(rSym)
			r.SetAdd(rAdd)
		}
	}
	return textp, nil

}

// Convert symbol xcoff type to sym.SymKind
// Returns nil if this shouldn't be added into loader (like .file or .dw symbols )
func getSymbolType(f *xcoff.File, s *xcoff.Symbol) (stype sym.SymKind, err string) {
	// .file symbol
	if s.SectionNumber == -2 {
		if s.StorageClass == xcoff.C_FILE {
			return sym.Sxxx, ""
		}
		return sym.Sxxx, "unrecognised StorageClass for sectionNumber = -2"
	}

	// extern symbols
	// TODO(aix)
	if s.SectionNumber == 0 {
		return sym.Sxxx, ""
	}

	sectType := f.Sections[s.SectionNumber-1].SectionHeader.Type
	switch sectType {
	default:
		return sym.Sxxx, fmt.Sprintf("getSymbolType for Section type 0x%x not implemented", sectType)
	case xcoff.STYP_DWARF, xcoff.STYP_DEBUG:
		return sym.Sxxx, ""
	case xcoff.STYP_DATA, xcoff.STYP_BSS, xcoff.STYP_TEXT:
	}

	switch s.StorageClass {
	default:
		return sym.Sxxx, fmt.Sprintf("getSymbolType for Storage class 0x%x not implemented", s.StorageClass)
	case xcoff.C_HIDEXT, xcoff.C_EXT, xcoff.C_WEAKEXT:
		switch s.AuxCSect.StorageMappingClass {
		default:
			return sym.Sxxx, fmt.Sprintf("getSymbolType for Storage class 0x%x and Storage Map 0x%x not implemented", s.StorageClass, s.AuxCSect.StorageMappingClass)

		// Program Code
		case xcoff.XMC_PR:
			if sectType == xcoff.STYP_TEXT {
				return sym.STEXT, ""
			}
			return sym.Sxxx, fmt.Sprintf("unrecognised Section Type 0x%x for Storage Class 0x%x with Storage Map XMC_PR", sectType, s.StorageClass)

		// Read/Write Data
		case xcoff.XMC_RW:
			if sectType == xcoff.STYP_DATA {
				return sym.SDATA, ""
			}
			if sectType == xcoff.STYP_BSS {
				return sym.SBSS, ""
			}
			return sym.Sxxx, fmt.Sprintf("unrecognised Section Type 0x%x for Storage Class 0x%x with Storage Map XMC_RW", sectType, s.StorageClass)

		// Function descriptor
		case xcoff.XMC_DS:
			if sectType == xcoff.STYP_DATA {
				return sym.SDATA, ""
			}
			return sym.Sxxx, fmt.Sprintf("unrecognised Section Type 0x%x for Storage Class 0x%x with Storage Map XMC_DS", sectType, s.StorageClass)

		// TOC anchor and TOC entry
		case xcoff.XMC_TC0, xcoff.XMC_TE:
			if sectType == xcoff.STYP_DATA {
				return sym.SXCOFFTOC, ""
			}
			return sym.Sxxx, fmt.Sprintf("unrecognised Section Type 0x%x for Storage Class 0x%x with Storage Map XMC_DS", sectType, s.StorageClass)

		}
	}
}