Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(84)

Delta Between Two Patch Sets: src/cmd/link/pclntab_test.go

Issue 53820043: code review 53820043: cmd/link: pclntab generation (Closed)
Left Patch Set: diff -r 5069dab9a825 https://code.google.com/p/go/ Created 11 years, 2 months ago
Right Patch Set: diff -r 30b337011ce5 https://code.google.com/p/go/ Created 11 years, 1 month ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
Right: Side by side diff | Download
« no previous file with change/comment | « src/cmd/link/pclntab.go ('k') | src/cmd/link/prog.go » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
(no file at all)
1 // Copyright 2014 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package main
6
7 import (
8 "bytes"
9 "debug/goobj"
10 "fmt"
11 "math/rand"
12 "sort"
13 "strings"
14 "testing"
15 )
16
17 // Test of pcln table encoding.
18 // testdata/genpcln.go generates an assembly file with
19 // pseudorandom values for the data that pclntab stores.
20 // This test recomputes the same pseudorandom stream
21 // and checks that the final linked binary uses those values
22 // as well.
23 func TestPclntab(t *testing.T) {
24 p := &Prog{
25 GOOS: "darwin",
26 GOARCH: "amd64",
27 Error: func(s string) { t.Error(s) },
28 StartSym: "start",
29 omitRuntime: true,
30 }
31 var buf bytes.Buffer
32 p.link(&buf, "testdata/pclntab.6")
33 if p.NumError > 0 {
34 return
35 }
36
37 // The algorithm for computing values here must match
38 // the one in testdata/genpcln.go.
39 for f := 0; f < 3; f++ {
40 file := "input"
41 line := 1
42 rnd := rand.New(rand.NewSource(int64(f)))
43 args := rnd.Intn(100) * 8
44 frame := 32 + rnd.Intn(32)/8*8
45 size := 200 + rnd.Intn(100)*8
46
47 name := fmt.Sprintf("func%d", f)
48 r, off, fargs, fframe, ok := findFunc(t, p, name)
49 if !ok {
50 continue // error already printed
51 }
52 if fargs != args {
53 t.Errorf("%s: args=%d, want %d", name, fargs, args)
54 }
55 if fframe != frame+8 {
56 t.Errorf("%s: frame=%d, want %d", name, fframe, frame+8)
57 }
58
59 // Check FUNCDATA 1.
60 fdata, ok := loadFuncdata(t, r, name, off, 1)
61 if ok {
62 fsym := p.Syms[goobj.SymID{Name: fmt.Sprintf("funcdata%d ", f)}]
63 if fsym == nil {
64 t.Errorf("funcdata%d is missing in binary", f)
65 } else if fdata != fsym.Addr {
66 t.Errorf("%s: funcdata 1 = %#x, want %#x", name, fdata, fsym.Addr)
67 }
68 }
69
70 // Walk code checking pcdata values.
71 spadj := 0
72 pcdata1 := -1
73 pcdata2 := -1
74
75 checkPCSP(t, r, name, off, 0, 0)
76 checkPCData(t, r, name, off, 0, 0, -1)
77 checkPCData(t, r, name, off, 0, 1, -1)
78 checkPCData(t, r, name, off, 0, 2, -1)
79
80 firstpc := 4
81 for i := 0; i < size; i++ {
82 pc := firstpc + i // skip SP adjustment to allocate fram e
83 if i >= 0x100 && t.Failed() {
84 break
85 }
86 // Possible SP adjustment.
87 checkPCSP(t, r, name, off, pc, frame+spadj)
88 if rnd.Intn(100) == 0 {
89 checkPCFileLine(t, r, name, off, pc, file, line)
90 checkPCData(t, r, name, off, pc, 1, pcdata1)
91 checkPCData(t, r, name, off, pc, 2, pcdata2)
92 i += 1
93 pc = firstpc + i
94 checkPCFileLine(t, r, name, off, pc-1, file, lin e)
95 checkPCData(t, r, name, off, pc-1, 1, pcdata1)
96 checkPCData(t, r, name, off, pc-1, 2, pcdata2)
97 checkPCSP(t, r, name, off, pc-1, frame+spadj)
98
99 if spadj <= -32 || spadj < 32 && rnd.Intn(2) == 0 {
100 spadj += 8
101 } else {
102 spadj -= 8
103 }
104 checkPCSP(t, r, name, off, pc, frame+spadj)
105 }
106
107 // Possible PCFile change.
108 if rnd.Intn(100) == 0 {
109 file = fmt.Sprintf("file%d.s", rnd.Intn(10))
110 line = rnd.Intn(100) + 1
111 }
112
113 // Possible PCLine change.
114 if rnd.Intn(10) == 0 {
115 line = rnd.Intn(1000) + 1
116 }
117
118 // Possible PCData $1 change.
119 if rnd.Intn(100) == 0 {
120 pcdata1 = rnd.Intn(1000)
121 }
122
123 // Possible PCData $2 change.
124 if rnd.Intn(100) == 0 {
125 pcdata2 = rnd.Intn(1000)
126 }
127
128 if i == 0 {
129 checkPCFileLine(t, r, name, off, 0, file, line)
130 checkPCFileLine(t, r, name, off, pc-1, file, lin e)
131 }
132 checkPCFileLine(t, r, name, off, pc, file, line)
133 checkPCData(t, r, name, off, pc, 1, pcdata1)
134 checkPCData(t, r, name, off, pc, 2, pcdata2)
135 }
136 }
137 }
138
139 // findFunc finds the function information in the pclntab of p
140 // for the function with the given name.
141 // It returns a symbol reader for pclntab, the offset of the function informatio n
142 // within that symbol, and the args and frame values read out of the information .
143 func findFunc(t *testing.T, p *Prog, name string) (r *SymReader, off, args, fram e int, ok bool) {
144 tabsym := p.Syms[goobj.SymID{Name: "pclntab"}]
145 if tabsym == nil {
146 t.Errorf("pclntab is missing in binary")
147 return
148 }
149
150 r = new(SymReader)
151 r.Init(p, tabsym)
152
153 // pclntab must with 8-byte header
154 if r.Uint32(0) != 0xfffffffb || r.Uint8(4) != 0 || r.Uint8(5) != 0 || r. Uint8(6) != uint8(p.pcquantum) || r.Uint8(7) != uint8(p.ptrsize) {
155 t.Errorf("pclntab has incorrect header %.8x", r.data[:8])
156 return
157 }
158
159 sym := p.Syms[goobj.SymID{Name: name}]
160 if sym == nil {
161 t.Errorf("%s is missing in the binary", name)
162 return
163 }
164
165 // index is nfunc addr0 off0 addr1 off1 ... addr_nfunc (sentinel)
166 nfunc := int(r.Addr(8))
167 i := sort.Search(nfunc, func(i int) bool {
168 return r.Addr(8+p.ptrsize*(1+2*i)) >= sym.Addr
169 })
170 if entry := r.Addr(8 + p.ptrsize*(1+2*i)); entry != sym.Addr {
171 indexTab := make([]Addr, 2*nfunc+1)
172 for j := range indexTab {
173 indexTab[j] = r.Addr(8 + p.ptrsize*(1+j))
174 }
175 t.Errorf("pclntab is missing entry for %s (%#x): %#x", name, sym .Addr, indexTab)
176 return
177 }
178
179 off = int(r.Addr(8 + p.ptrsize*(1+2*i+1)))
180
181 // func description at off is
182 // entry addr
183 // nameoff uint32
184 // args uint32
185 // frame uint32
186 // pcspoff uint32
187 // pcfileoff uint32
188 // pclineoff uint32
189 // npcdata uint32
190 // nfuncdata uint32
191 // pcdata npcdata*uint32
192 // funcdata nfuncdata*addr
193 //
194 if entry := r.Addr(off); entry != sym.Addr {
195 t.Errorf("pclntab inconsistent: entry for %s addr=%#x has entry= %#x", name, sym.Addr, entry)
196 return
197 }
198 nameoff := int(r.Uint32(off + p.ptrsize))
199 args = int(r.Uint32(off + p.ptrsize + 1*4))
200 frame = int(r.Uint32(off + p.ptrsize + 2*4))
201
202 fname := r.String(nameoff)
203 if fname != name {
204 t.Errorf("pclntab inconsistent: entry for %s addr=%#x has name % q", name, sym.Addr, fname)
205 }
206
207 ok = true // off, args, frame are usable
208 return
209 }
210
211 // loadFuncdata returns the funcdata #fnum value
212 // loaded from the function information for name.
213 func loadFuncdata(t *testing.T, r *SymReader, name string, off int, fnum int) (A ddr, bool) {
214 npcdata := int(r.Uint32(off + r.p.ptrsize + 6*4))
215 nfuncdata := int(r.Uint32(off + r.p.ptrsize + 7*4))
216 if fnum >= nfuncdata {
217 t.Errorf("pclntab(%s): no funcdata %d (only < %d)", name, fnum, nfuncdata)
218 return 0, false
219 }
220 fdataoff := off + r.p.ptrsize + (8+npcdata)*4 + fnum*r.p.ptrsize
221 fdataoff += fdataoff & 4
222 return r.Addr(fdataoff), true
223 }
224
225 // checkPCSP checks that the PCSP table in the function information at off
226 // lists spadj as the sp delta for pc.
227 func checkPCSP(t *testing.T, r *SymReader, name string, off, pc, spadj int) {
228 pcoff := r.Uint32(off + r.p.ptrsize + 3*4)
229 pcval, ok := readPCData(t, r, name, "PCSP", pcoff, pc)
230 if !ok {
231 return
232 }
233 if pcval != spadj {
234 t.Errorf("pclntab(%s): at pc=+%#x, pcsp=%d, want %d", name, pc, pcval, spadj)
235 }
236 }
237
238 // checkPCSP checks that the PCFile and PCLine tables in the function informatio n at off
239 // list file, line as the file name and line number for pc.
240 func checkPCFileLine(t *testing.T, r *SymReader, name string, off, pc int, file string, line int) {
241 pcfileoff := r.Uint32(off + r.p.ptrsize + 4*4)
242 pclineoff := r.Uint32(off + r.p.ptrsize + 5*4)
243 pcfilenum, ok1 := readPCData(t, r, name, "PCFile", pcfileoff, pc)
244 pcline, ok2 := readPCData(t, r, name, "PCLine", pclineoff, pc)
245 if !ok1 || !ok2 {
246 return
247 }
248 nfunc := int(r.Addr(8))
249 filetaboff := r.Uint32(8 + r.p.ptrsize*2*(nfunc+1))
250 nfile := int(r.Uint32(int(filetaboff)))
251 if pcfilenum <= 0 || pcfilenum >= nfile {
252 t.Errorf("pclntab(%s): at pc=+%#x, filenum=%d (invalid; nfile=%d )", name, pc, pcfilenum, nfile)
253 }
254 pcfile := r.String(int(r.Uint32(int(filetaboff) + pcfilenum*4)))
255 if !strings.HasSuffix(pcfile, file) {
256 t.Errorf("pclntab(%s): at pc=+%#x, file=%q, want %q", name, pc, pcfile, file)
257 }
258 if pcline != line {
259 t.Errorf("pclntab(%s): at pc=+%#x, line=%d, want %d", name, pc, pcline, line)
260 }
261 }
262
263 // checkPCData checks that the PCData#pnum table in the function information at off
264 // list val as the value for pc.
265 func checkPCData(t *testing.T, r *SymReader, name string, off, pc, pnum, val int ) {
266 pcoff := r.Uint32(off + r.p.ptrsize + (8+pnum)*4)
267 pcval, ok := readPCData(t, r, name, fmt.Sprintf("PCData#%d", pnum), pcof f, pc)
268 if !ok {
269 return
270 }
271 if pcval != val {
272 t.Errorf("pclntab(%s): at pc=+%#x, pcdata#%d=%d, want %d", name, pc, pnum, pcval, val)
273 }
274 }
275
276 // readPCData reads the PCData table offset off
277 // to obtain and return the value associated with pc.
278 func readPCData(t *testing.T, r *SymReader, name, pcdataname string, pcoff uint3 2, pc int) (int, bool) {
279 var it PCIter
280 for it.Init(r.p, r.data[pcoff:]); !it.Done; it.Next() {
281 if it.PC <= uint32(pc) && uint32(pc) < it.NextPC {
282 return int(it.Value), true
283 }
284 }
285 if it.Corrupt {
286 t.Errorf("pclntab(%s): %s: corrupt pcdata table", name, pcdatana me)
287 }
288 return 0, false
289 }
290
291 // A SymReader provides typed access to the data for a symbol.
292 type SymReader struct {
293 p *Prog
294 data []byte
295 }
296
297 func (r *SymReader) Init(p *Prog, sym *Sym) {
298 seg := sym.Section.Segment
299 off := sym.Addr - seg.VirtAddr
300 data := seg.Data[off : off+Addr(sym.Size)]
301 r.p = p
302 r.data = data
303 }
304
305 func (r *SymReader) Uint8(off int) uint8 {
306 return r.data[off]
307 }
308
309 func (r *SymReader) Uint16(off int) uint16 {
310 return r.p.byteorder.Uint16(r.data[off:])
311 }
312
313 func (r *SymReader) Uint32(off int) uint32 {
314 return r.p.byteorder.Uint32(r.data[off:])
315 }
316
317 func (r *SymReader) Uint64(off int) uint64 {
318 return r.p.byteorder.Uint64(r.data[off:])
319 }
320
321 func (r *SymReader) Addr(off int) Addr {
322 if r.p.ptrsize == 4 {
323 return Addr(r.Uint32(off))
324 }
325 return Addr(r.Uint64(off))
326 }
327
328 func (r *SymReader) String(off int) string {
329 end := off
330 for r.data[end] != '\x00' {
331 end++
332 }
333 return string(r.data[off:end])
334 }
LEFTRIGHT

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b