You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

473 lines
13 KiB

  1. // Copyright 2018 Google Inc. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. /*
  15. * Line tables
  16. */
  17. package gosym
  18. import (
  19. "encoding/binary"
  20. "sync"
  21. )
  22. // A LineTable is a data structure mapping program counters to line numbers.
  23. //
  24. // In Go 1.1 and earlier, each function (represented by a Func) had its own LineTable,
  25. // and the line number corresponded to a numbering of all source lines in the
  26. // program, across all files. That absolute line number would then have to be
  27. // converted separately to a file name and line number within the file.
  28. //
  29. // In Go 1.2, the format of the data changed so that there is a single LineTable
  30. // for the entire program, shared by all Funcs, and there are no absolute line
  31. // numbers, just line numbers within specific files.
  32. //
  33. // For the most part, LineTable's methods should be treated as an internal
  34. // detail of the package; callers should use the methods on Table instead.
  35. type LineTable struct {
  36. Data []byte
  37. PC uint64
  38. Line int
  39. // Go 1.2 state
  40. mu sync.Mutex
  41. go12 int // is this in Go 1.2 format? -1 no, 0 unknown, 1 yes
  42. binary binary.ByteOrder
  43. quantum uint32
  44. ptrsize uint32
  45. functab []byte
  46. nfunctab uint32
  47. filetab []byte
  48. nfiletab uint32
  49. fileMap map[string]uint32
  50. }
  51. // NOTE(rsc): This is wrong for GOARCH=arm, which uses a quantum of 4,
  52. // but we have no idea whether we're using arm or not. This only
  53. // matters in the old (pre-Go 1.2) symbol table format, so it's not worth
  54. // fixing.
  55. const oldQuantum = 1
  56. func (t *LineTable) parse(targetPC uint64, targetLine int) (b []byte, pc uint64, line int) {
  57. // The PC/line table can be thought of as a sequence of
  58. // <pc update>* <line update>
  59. // batches. Each update batch results in a (pc, line) pair,
  60. // where line applies to every PC from pc up to but not
  61. // including the pc of the next pair.
  62. //
  63. // Here we process each update individually, which simplifies
  64. // the code, but makes the corner cases more confusing.
  65. b, pc, line = t.Data, t.PC, t.Line
  66. for pc <= targetPC && line != targetLine && len(b) > 0 {
  67. code := b[0]
  68. b = b[1:]
  69. switch {
  70. case code == 0:
  71. if len(b) < 4 {
  72. b = b[0:0]
  73. break
  74. }
  75. val := binary.BigEndian.Uint32(b)
  76. b = b[4:]
  77. line += int(val)
  78. case code <= 64:
  79. line += int(code)
  80. case code <= 128:
  81. line -= int(code - 64)
  82. default:
  83. pc += oldQuantum * uint64(code-128)
  84. continue
  85. }
  86. pc += oldQuantum
  87. }
  88. return b, pc, line
  89. }
  90. func (t *LineTable) slice(pc uint64) *LineTable {
  91. data, pc, line := t.parse(pc, -1)
  92. return &LineTable{Data: data, PC: pc, Line: line}
  93. }
  94. // PCToLine returns the line number for the given program counter.
  95. // Callers should use Table's PCToLine method instead.
  96. func (t *LineTable) PCToLine(pc uint64) int {
  97. if t.isGo12() {
  98. return t.go12PCToLine(pc)
  99. }
  100. _, _, line := t.parse(pc, -1)
  101. return line
  102. }
  103. // LineToPC returns the program counter for the given line number,
  104. // considering only program counters before maxpc.
  105. // Callers should use Table's LineToPC method instead.
  106. func (t *LineTable) LineToPC(line int, maxpc uint64) uint64 {
  107. if t.isGo12() {
  108. return 0
  109. }
  110. _, pc, line1 := t.parse(maxpc, line)
  111. if line1 != line {
  112. return 0
  113. }
  114. // Subtract quantum from PC to account for post-line increment
  115. return pc - oldQuantum
  116. }
  117. // NewLineTable returns a new PC/line table
  118. // corresponding to the encoded data.
  119. // Text must be the start address of the
  120. // corresponding text segment.
  121. func NewLineTable(data []byte, text uint64) *LineTable {
  122. return &LineTable{Data: data, PC: text, Line: 0}
  123. }
  124. // Go 1.2 symbol table format.
  125. // See golang.org/s/go12symtab.
  126. //
  127. // A general note about the methods here: rather than try to avoid
  128. // index out of bounds errors, we trust Go to detect them, and then
  129. // we recover from the panics and treat them as indicative of a malformed
  130. // or incomplete table.
  131. //
  132. // The methods called by symtab.go, which begin with "go12" prefixes,
  133. // are expected to have that recovery logic.
  134. // isGo12 reports whether this is a Go 1.2 (or later) symbol table.
  135. func (t *LineTable) isGo12() bool {
  136. t.go12Init()
  137. return t.go12 == 1
  138. }
  139. const go12magic = 0xfffffffb
  140. // uintptr returns the pointer-sized value encoded at b.
  141. // The pointer size is dictated by the table being read.
  142. func (t *LineTable) uintptr(b []byte) uint64 {
  143. if t.ptrsize == 4 {
  144. return uint64(t.binary.Uint32(b))
  145. }
  146. return t.binary.Uint64(b)
  147. }
  148. // go12init initializes the Go 1.2 metadata if t is a Go 1.2 symbol table.
  149. func (t *LineTable) go12Init() {
  150. t.mu.Lock()
  151. defer t.mu.Unlock()
  152. if t.go12 != 0 {
  153. return
  154. }
  155. defer func() {
  156. // If we panic parsing, assume it's not a Go 1.2 symbol table.
  157. recover()
  158. }()
  159. // Check header: 4-byte magic, two zeros, pc quantum, pointer size.
  160. t.go12 = -1 // not Go 1.2 until proven otherwise
  161. if len(t.Data) < 16 || t.Data[4] != 0 || t.Data[5] != 0 ||
  162. (t.Data[6] != 1 && t.Data[6] != 4) || // pc quantum
  163. (t.Data[7] != 4 && t.Data[7] != 8) { // pointer size
  164. return
  165. }
  166. switch uint32(go12magic) {
  167. case binary.LittleEndian.Uint32(t.Data):
  168. t.binary = binary.LittleEndian
  169. case binary.BigEndian.Uint32(t.Data):
  170. t.binary = binary.BigEndian
  171. default:
  172. return
  173. }
  174. t.quantum = uint32(t.Data[6])
  175. t.ptrsize = uint32(t.Data[7])
  176. t.nfunctab = uint32(t.uintptr(t.Data[8:]))
  177. t.functab = t.Data[8+t.ptrsize:]
  178. functabsize := t.nfunctab*2*t.ptrsize + t.ptrsize
  179. fileoff := t.binary.Uint32(t.functab[functabsize:])
  180. t.functab = t.functab[:functabsize]
  181. t.filetab = t.Data[fileoff:]
  182. t.nfiletab = t.binary.Uint32(t.filetab)
  183. t.filetab = t.filetab[:t.nfiletab*4]
  184. t.go12 = 1 // so far so good
  185. }
  186. // go12Funcs returns a slice of Funcs derived from the Go 1.2 pcln table.
  187. func (t *LineTable) go12Funcs() []Func {
  188. // Assume it is malformed and return nil on error.
  189. defer func() {
  190. recover()
  191. }()
  192. n := len(t.functab) / int(t.ptrsize) / 2
  193. funcs := make([]Func, n)
  194. for i := range funcs {
  195. f := &funcs[i]
  196. f.Entry = uint64(t.uintptr(t.functab[2*i*int(t.ptrsize):]))
  197. f.End = uint64(t.uintptr(t.functab[(2*i+2)*int(t.ptrsize):]))
  198. info := t.Data[t.uintptr(t.functab[(2*i+1)*int(t.ptrsize):]):]
  199. f.LineTable = t
  200. f.FrameSize = int(t.binary.Uint32(info[t.ptrsize+2*4:]))
  201. f.Sym = &Sym{
  202. Value: f.Entry,
  203. Type: 'T',
  204. Name: t.string(t.binary.Uint32(info[t.ptrsize:])),
  205. GoType: 0,
  206. Func: f,
  207. }
  208. }
  209. return funcs
  210. }
  211. // findFunc returns the func corresponding to the given program counter.
  212. func (t *LineTable) findFunc(pc uint64) []byte {
  213. if pc < t.uintptr(t.functab) || pc >= t.uintptr(t.functab[len(t.functab)-int(t.ptrsize):]) {
  214. return nil
  215. }
  216. // The function table is a list of 2*nfunctab+1 uintptrs,
  217. // alternating program counters and offsets to func structures.
  218. f := t.functab
  219. nf := t.nfunctab
  220. for nf > 0 {
  221. m := nf / 2
  222. fm := f[2*t.ptrsize*m:]
  223. if t.uintptr(fm) <= pc && pc < t.uintptr(fm[2*t.ptrsize:]) {
  224. return t.Data[t.uintptr(fm[t.ptrsize:]):]
  225. } else if pc < t.uintptr(fm) {
  226. nf = m
  227. } else {
  228. f = f[(m+1)*2*t.ptrsize:]
  229. nf -= m + 1
  230. }
  231. }
  232. return nil
  233. }
  234. // readvarint reads, removes, and returns a varint from *pp.
  235. func (t *LineTable) readvarint(pp *[]byte) uint32 {
  236. var v, shift uint32
  237. p := *pp
  238. for shift = 0; ; shift += 7 {
  239. b := p[0]
  240. p = p[1:]
  241. v |= (uint32(b) & 0x7F) << shift
  242. if b&0x80 == 0 {
  243. break
  244. }
  245. }
  246. *pp = p
  247. return v
  248. }
  249. // string returns a Go string found at off.
  250. func (t *LineTable) string(off uint32) string {
  251. for i := off; ; i++ {
  252. if t.Data[i] == 0 {
  253. return string(t.Data[off:i])
  254. }
  255. }
  256. }
  257. // step advances to the next pc, value pair in the encoded table.
  258. func (t *LineTable) step(p *[]byte, pc *uint64, val *int32, first bool) bool {
  259. uvdelta := t.readvarint(p)
  260. if uvdelta == 0 && !first {
  261. return false
  262. }
  263. if uvdelta&1 != 0 {
  264. uvdelta = ^(uvdelta >> 1)
  265. } else {
  266. uvdelta >>= 1
  267. }
  268. vdelta := int32(uvdelta)
  269. pcdelta := t.readvarint(p) * t.quantum
  270. *pc += uint64(pcdelta)
  271. *val += vdelta
  272. return true
  273. }
  274. // pcvalue reports the value associated with the target pc.
  275. // off is the offset to the beginning of the pc-value table,
  276. // and entry is the start PC for the corresponding function.
  277. func (t *LineTable) pcvalue(off uint32, entry, targetpc uint64) int32 {
  278. if off == 0 {
  279. return -1
  280. }
  281. p := t.Data[off:]
  282. val := int32(-1)
  283. pc := entry
  284. for t.step(&p, &pc, &val, pc == entry) {
  285. if targetpc < pc {
  286. return val
  287. }
  288. }
  289. return -1
  290. }
  291. // findFileLine scans one function in the binary looking for a
  292. // program counter in the given file on the given line.
  293. // It does so by running the pc-value tables mapping program counter
  294. // to file number. Since most functions come from a single file, these
  295. // are usually short and quick to scan. If a file match is found, then the
  296. // code goes to the expense of looking for a simultaneous line number match.
  297. func (t *LineTable) findFileLine(entry uint64, filetab, linetab uint32, filenum, line int32) uint64 {
  298. if filetab == 0 || linetab == 0 {
  299. return 0
  300. }
  301. fp := t.Data[filetab:]
  302. fl := t.Data[linetab:]
  303. fileVal := int32(-1)
  304. filePC := entry
  305. lineVal := int32(-1)
  306. linePC := entry
  307. fileStartPC := filePC
  308. for t.step(&fp, &filePC, &fileVal, filePC == entry) {
  309. if fileVal == filenum && fileStartPC < filePC {
  310. // fileVal is in effect starting at fileStartPC up to
  311. // but not including filePC, and it's the file we want.
  312. // Run the PC table looking for a matching line number
  313. // or until we reach filePC.
  314. lineStartPC := linePC
  315. for linePC < filePC && t.step(&fl, &linePC, &lineVal, linePC == entry) {
  316. // lineVal is in effect until linePC, and lineStartPC < filePC.
  317. if lineVal == line {
  318. if fileStartPC <= lineStartPC {
  319. return lineStartPC
  320. }
  321. if fileStartPC < linePC {
  322. return fileStartPC
  323. }
  324. }
  325. lineStartPC = linePC
  326. }
  327. }
  328. fileStartPC = filePC
  329. }
  330. return 0
  331. }
  332. // go12PCToLine maps program counter to line number for the Go 1.2 pcln table.
  333. func (t *LineTable) go12PCToLine(pc uint64) (line int) {
  334. return t.go12PCToVal(pc, t.ptrsize+5*4)
  335. }
  336. // go12PCToSPAdj maps program counter to Stack Pointer adjustment for the Go 1.2 pcln table.
  337. func (t *LineTable) go12PCToSPAdj(pc uint64) (spadj int) {
  338. return t.go12PCToVal(pc, t.ptrsize+3*4)
  339. }
  340. func (t *LineTable) go12PCToVal(pc uint64, fOffset uint32) (val int) {
  341. defer func() {
  342. if recover() != nil {
  343. val = -1
  344. }
  345. }()
  346. f := t.findFunc(pc)
  347. if f == nil {
  348. return -1
  349. }
  350. entry := t.uintptr(f)
  351. linetab := t.binary.Uint32(f[fOffset:])
  352. return int(t.pcvalue(linetab, entry, pc))
  353. }
  354. // go12PCToFile maps program counter to file name for the Go 1.2 pcln table.
  355. func (t *LineTable) go12PCToFile(pc uint64) (file string) {
  356. defer func() {
  357. if recover() != nil {
  358. file = ""
  359. }
  360. }()
  361. f := t.findFunc(pc)
  362. if f == nil {
  363. return ""
  364. }
  365. entry := t.uintptr(f)
  366. filetab := t.binary.Uint32(f[t.ptrsize+4*4:])
  367. fno := t.pcvalue(filetab, entry, pc)
  368. if fno <= 0 {
  369. return ""
  370. }
  371. return t.string(t.binary.Uint32(t.filetab[4*fno:]))
  372. }
  373. // go12LineToPC maps a (file, line) pair to a program counter for the Go 1.2 pcln table.
  374. func (t *LineTable) go12LineToPC(file string, line int) (pc uint64) {
  375. defer func() {
  376. if recover() != nil {
  377. pc = 0
  378. }
  379. }()
  380. t.initFileMap()
  381. filenum := t.fileMap[file]
  382. if filenum == 0 {
  383. return 0
  384. }
  385. // Scan all functions.
  386. // If this turns out to be a bottleneck, we could build a map[int32][]int32
  387. // mapping file number to a list of functions with code from that file.
  388. for i := uint32(0); i < t.nfunctab; i++ {
  389. f := t.Data[t.uintptr(t.functab[2*t.ptrsize*i+t.ptrsize:]):]
  390. entry := t.uintptr(f)
  391. filetab := t.binary.Uint32(f[t.ptrsize+4*4:])
  392. linetab := t.binary.Uint32(f[t.ptrsize+5*4:])
  393. pc := t.findFileLine(entry, filetab, linetab, int32(filenum), int32(line))
  394. if pc != 0 {
  395. return pc
  396. }
  397. }
  398. return 0
  399. }
  400. // initFileMap initializes the map from file name to file number.
  401. func (t *LineTable) initFileMap() {
  402. t.mu.Lock()
  403. defer t.mu.Unlock()
  404. if t.fileMap != nil {
  405. return
  406. }
  407. m := make(map[string]uint32)
  408. for i := uint32(1); i < t.nfiletab; i++ {
  409. s := t.string(t.binary.Uint32(t.filetab[4*i:]))
  410. m[s] = i
  411. }
  412. t.fileMap = m
  413. }
  414. // go12MapFiles adds to m a key for every file in the Go 1.2 LineTable.
  415. // Every key maps to obj. That's not a very interesting map, but it provides
  416. // a way for callers to obtain the list of files in the program.
  417. func (t *LineTable) go12MapFiles(m map[string]*Obj, obj *Obj) {
  418. defer func() {
  419. recover()
  420. }()
  421. t.initFileMap()
  422. for file := range t.fileMap {
  423. m[file] = obj
  424. }
  425. }