vdso_linux_amd64.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. // Copyright © 2021 Jeffrey H. Johnson <trnsz@pobox.com>.
  2. // Copyright © 2021 Gridfinity, LLC.
  3. // Copyright © 2019 Neal.
  4. // Copyright © 2018 lrita@163.com.
  5. //
  6. // Use of this source code is governed by the MIT
  7. // license that can be found in the LICENSE file.
  8. package gonuma
  9. import (
  10. "io/ioutil"
  11. "unsafe"
  12. )
  13. const (
  14. // AtNull ...
  15. AtNull = 0 // End of vector
  16. // AtSysInfoEHdr ...
  17. AtSysInfoEHdr = 33
  18. // PtLoad ...
  19. PtLoad = 1 /* Loadable program segment */
  20. // PtDynamic ...
  21. PtDynamic = 2 /* Dynamic linking information */
  22. // DtNull ...
  23. DtNull = 0 /* Marks end of dynamic section */
  24. // DtHash ...
  25. DtHash = 4 /* Dynamic symbol hash table */
  26. // DtStrTab ...
  27. DtStrTab = 5 /* Address of string table */
  28. // DtSymTab ...
  29. DtSymTab = 6 /* Address of symbol table */
  30. // DtGNUHash ...
  31. DtGNUHash = 0x6ffffef5 /* GNU-style dynamic symbol hash table */
  32. // DtVerSym ...
  33. DtVerSym = 0x6ffffff0
  34. // DtVerDef ...
  35. DtVerDef = 0x6ffffffc
  36. // VerFlagBase ...
  37. VerFlagBase = 0x1 /* Version definition of file itself */
  38. // ShnUndef ...
  39. ShnUndef = 0 /* Undefined section */
  40. // ShtDynSym ...
  41. ShtDynSym = 11 /* Dynamic linker symbol table */
  42. // SttFunc ...
  43. SttFunc = 2 /* Symbol is a code object */
  44. // SttNoType ...
  45. SttNoType = 0 /* Symbol type is not specified */
  46. // StbGlobal ...
  47. StbGlobal = 1 /* Global symbol */
  48. // StbWeak ...
  49. StbWeak = 2 /* Weak symbol */
  50. // EINIdent ...
  51. EINIdent = 16
  52. // vdsoArrayMax = size of maximally sized arrays of this architecture.
  53. // See cmd/compile/internal/amd64/galign.go arch.MAXWIDTH for details.
  54. vdsoArrayMax = 1<<50 - 1
  55. // Maximum indices for the array types used when traversing the vDSO ELF
  56. // structures from architecture-specific max provided by vdso_linux_*.go.
  57. // VdsoSymTabSize ...
  58. VdsoSymTabSize = vdsoArrayMax / unsafe.Sizeof(elfSym{})
  59. vdsoDynSize = vdsoArrayMax / unsafe.Sizeof(elfDyn{})
  60. // VdsoSymStringsSize ...
  61. VdsoSymStringsSize = vdsoArrayMax // byte
  62. vdsoVerSymSize = vdsoArrayMax / 2 // uint16
  63. vdsoHashSize = vdsoArrayMax / 4 // uint32
  64. // vdsoBloomSizeScale is a scaling factor for gnuhash tables which are
  65. // uint32 indexed, but contain uintptrs
  66. vdsoBloomSizeScale = unsafe.Sizeof(uintptr(0)) / 4 // uint32
  67. )
  68. type vdsoVersionKey struct {
  69. version string
  70. verHash uint32
  71. }
  72. // ELF64 structure definitions for use by the vDSO loader
  73. type elfSym struct {
  74. stName uint32
  75. stInfo byte
  76. stOther byte
  77. stShndx uint16
  78. stValue uint64
  79. stSize uint64
  80. }
  81. type elfVerdef struct {
  82. vdVersion uint16 /* Version revision */
  83. vdFlags uint16 /* Version information */
  84. vdNdx uint16 /* Version Index */
  85. vdCnt uint16 /* Number of associated aux entries */
  86. vdHash uint32 /* Version name hash value */
  87. vdAux uint32 /* Offset in bytes to verdaux array */
  88. vdNext uint32 /* Offset in bytes to next verdef entry */
  89. }
  90. type elfEhdr struct {
  91. eIdent [EINIdent]byte /* Magic number and other info */
  92. eType uint16 /* Object file type */
  93. eMachine uint16 /* Architecture */
  94. eVersion uint32 /* Object file version */
  95. eEntry uint64 /* Entry point virtual address */
  96. ePhoff uint64 /* Program header table file offset */
  97. eShoff uint64 /* Section header table file offset */
  98. eFlags uint32 /* Processor-specific flags */
  99. eEhsize uint16 /* ELF header size in bytes */
  100. ePhentsize uint16 /* Program header table entry size */
  101. ePhnum uint16 /* Program header table entry count */
  102. eShentsize uint16 /* Section header table entry size */
  103. eShnum uint16 /* Section header table entry count */
  104. eShstrndx uint16 /* Section header string table index */
  105. }
  106. type elfPhdr struct {
  107. pType uint32 /* Segment type */
  108. pFlags uint32 /* Segment flags */
  109. pOffset uint64 /* Segment file offset */
  110. pVaddr uint64 /* Segment virtual address */
  111. pPaddr uint64 /* Segment physical address */
  112. pFilesz uint64 /* Segment size in file */
  113. pMemsz uint64 /* Segment size in memory */
  114. pAlign uint64 /* Segment alignment */
  115. }
  116. type elfShdr struct {
  117. shName uint32 /* Section name (string tbl index) */
  118. shType uint32 /* Section type */
  119. shFlags uint64 /* Section flags */
  120. shAddr uint64 /* Section virtual addr at execution */
  121. shOffset uint64 /* Section file offset */
  122. shSize uint64 /* Section size in bytes */
  123. shLink uint32 /* Link to another section */
  124. shInfo uint32 /* Additional section information */
  125. shAddralign uint64 /* Section alignment */
  126. shEntSize uint64 /* Entry size if section holds table */
  127. }
  128. type elfDyn struct {
  129. dTag int64 /* Dynamic entry type */
  130. dVal uint64 /* Integer value */
  131. }
  132. type elfVerdaux struct {
  133. vdaName uint32 /* Version or dependency names */
  134. vdaNext uint32 /* Offset in bytes to next verdaux entry */
  135. }
  136. type vdsoInfo struct {
  137. valid bool
  138. /* Load information */
  139. loadAddr unsafe.Pointer
  140. loadOffset unsafe.Pointer /* loadAddr - recorded vaddr */
  141. /* Symbol table */
  142. symtab *[VdsoSymTabSize]elfSym
  143. symstrings *[VdsoSymStringsSize]byte
  144. chain []uint32
  145. bucket []uint32
  146. symOff uint32
  147. isGNUHash bool
  148. /* Version table */
  149. versym *[vdsoVerSymSize]uint16
  150. verdef *elfVerdef
  151. }
  152. var (
  153. vdsoLinuxVersion = vdsoVersionKey{"LINUX_2.6", 0x3ae75f6}
  154. vdsoinfo vdsoInfo
  155. vdsoVersion int32
  156. vdsoGetCPU uintptr
  157. )
  158. /* How to extract and insert information held in the stInfo field. */
  159. // ELFstBind ...
  160. func ELFstBind(val byte) byte { return val >> 4 }
  161. // ELFstType ...
  162. func ELFstType(val byte) byte { return val & 0xf }
  163. //go:nosplit
  164. func add(p unsafe.Pointer, x uintptr) unsafe.Pointer {
  165. return unsafe.Pointer(uintptr(p) + x)
  166. }
  167. //go:linkname gostringnocopy runtime.gostringnocopy
  168. //go:nosplit
  169. func gostringnocopy(str *byte) string
  170. func vdsoInitFromSysinfoEhdr(info *vdsoInfo, hdr *elfEhdr) {
  171. info.valid = false
  172. info.loadAddr = unsafe.Pointer(hdr)
  173. pt := unsafe.Pointer(uintptr(info.loadAddr) + uintptr(hdr.ePhoff))
  174. // We need two things from the segment table: the load offset
  175. // and the dynamic table.
  176. var foundVaddr bool
  177. var dyn *[vdsoDynSize]elfDyn
  178. for i := uint16(0); i < hdr.ePhnum; i++ {
  179. pt := (*elfPhdr)(add(pt, uintptr(i)*unsafe.Sizeof(elfPhdr{})))
  180. switch pt.pType {
  181. case PtLoad:
  182. if !foundVaddr {
  183. foundVaddr = true
  184. info.loadOffset = unsafe.Pointer(
  185. uintptr(info.loadAddr) + uintptr(pt.pOffset-pt.pVaddr),
  186. )
  187. }
  188. case PtDynamic:
  189. dyn = (*[vdsoDynSize]elfDyn)(
  190. unsafe.Pointer(
  191. uintptr(info.loadAddr) + uintptr(pt.pOffset),
  192. ),
  193. )
  194. }
  195. }
  196. if !foundVaddr || dyn == nil {
  197. return // Failed
  198. }
  199. // Fish out the useful bits of the dynamic table.
  200. var hash, gnuhash *[vdsoHashSize]uint32
  201. info.symstrings = nil
  202. info.symtab = nil
  203. info.versym = nil
  204. info.verdef = nil
  205. for i := 0; dyn[i].dTag != DtNull; i++ {
  206. dt := &dyn[i]
  207. p := unsafe.Pointer(uintptr(info.loadOffset) + uintptr(dt.dVal))
  208. switch dt.dTag {
  209. case DtStrTab:
  210. info.symstrings = (*[VdsoSymStringsSize]byte)(unsafe.Pointer(p))
  211. case DtSymTab:
  212. info.symtab = (*[VdsoSymTabSize]elfSym)(unsafe.Pointer(p))
  213. case DtHash:
  214. hash = (*[vdsoHashSize]uint32)(unsafe.Pointer(p))
  215. case DtGNUHash:
  216. gnuhash = (*[vdsoHashSize]uint32)(unsafe.Pointer(p))
  217. case DtVerSym:
  218. info.versym = (*[vdsoVerSymSize]uint16)(unsafe.Pointer(p))
  219. case DtVerDef:
  220. info.verdef = (*elfVerdef)(unsafe.Pointer(p))
  221. }
  222. }
  223. if info.symstrings == nil || info.symtab == nil ||
  224. (hash == nil && gnuhash == nil) {
  225. return // Failed
  226. }
  227. if info.verdef == nil {
  228. info.versym = nil
  229. }
  230. if gnuhash != nil {
  231. // Parse the GNU hash table header.
  232. nbucket := gnuhash[0]
  233. info.symOff = gnuhash[1]
  234. bloomSize := gnuhash[2]
  235. info.bucket = gnuhash[4+bloomSize*uint32(vdsoBloomSizeScale):][:nbucket]
  236. info.chain = gnuhash[4+bloomSize*uint32(vdsoBloomSizeScale)+nbucket:]
  237. info.isGNUHash = true
  238. } else {
  239. // Parse the hash table header.
  240. nbucket := hash[0]
  241. nchain := hash[1]
  242. info.bucket = hash[2 : 2+nbucket]
  243. info.chain = hash[2+nbucket : 2+nbucket+nchain]
  244. }
  245. // That's all we need.
  246. info.valid = true
  247. }
  248. func vdsoFindVersion(info *vdsoInfo, ver *vdsoVersionKey) int32 {
  249. if !info.valid {
  250. return 0
  251. }
  252. def := info.verdef
  253. for {
  254. if def.vdFlags&VerFlagBase == 0 {
  255. aux := (*elfVerdaux)(
  256. add(unsafe.Pointer(def), uintptr(def.vdAux)),
  257. )
  258. if def.vdHash == ver.verHash &&
  259. ver.version == gostringnocopy(
  260. &info.symstrings[aux.vdaName],
  261. ) {
  262. return int32(def.vdNdx & 0x7fff)
  263. }
  264. }
  265. if def.vdNext == 0 {
  266. break
  267. }
  268. def = (*elfVerdef)(add(unsafe.Pointer(def), uintptr(def.vdNext)))
  269. }
  270. return -1 // cannot match any version
  271. }
  272. func vdsoParseSymbols(name string, info *vdsoInfo, version int32) uintptr {
  273. if !info.valid {
  274. return 0
  275. }
  276. load := func(symIndex uint32, name string) uintptr {
  277. sym := &info.symtab[symIndex]
  278. typ := ELFstType(sym.stInfo)
  279. bind := ELFstBind(sym.stInfo)
  280. // On ppc64x, VDSO functions are of type SttNoType.
  281. if typ != SttFunc && typ != SttNoType || bind != StbGlobal && bind != StbWeak ||
  282. sym.stShndx == ShnUndef {
  283. return 0
  284. }
  285. if name != gostringnocopy(&info.symstrings[sym.stName]) {
  286. return 0
  287. }
  288. // Check symbol version.
  289. if info.versym != nil && version != 0 &&
  290. int32(info.versym[symIndex]&0x7fff) != version {
  291. return 0
  292. }
  293. return uintptr(info.loadOffset) + uintptr(sym.stValue)
  294. }
  295. if !info.isGNUHash {
  296. // Old-style DT_HASH table.
  297. hash := ELFHash(name)
  298. for chain := info.bucket[hash%uint32(len(info.bucket))]; chain != 0; chain = info.chain[chain] {
  299. if p := load(chain, name); p != 0 {
  300. return p
  301. }
  302. }
  303. return 0
  304. }
  305. // New-style DT_GNU_HASH table.
  306. gnuhash := ELFGNUHash(name)
  307. symIndex := info.bucket[gnuhash%uint32(len(info.bucket))]
  308. if symIndex < info.symOff {
  309. return 0
  310. }
  311. for ; ; symIndex++ {
  312. hash := info.chain[symIndex-info.symOff]
  313. if hash|1 == gnuhash|1 {
  314. // Found a hash match.
  315. if p := load(symIndex, name); p != 0 {
  316. return p
  317. }
  318. }
  319. if hash&1 != 0 {
  320. // End of chain.
  321. break
  322. }
  323. }
  324. return 0
  325. }
  326. // ELFHash ...
  327. func ELFHash(name string) (h uint32) {
  328. for i := 0; i < len(name); i++ {
  329. h = h<<4 + uint32(name[i])
  330. g := h & 0xf0000000
  331. if g != 0 {
  332. h ^= g >> 24
  333. }
  334. h &= ^g
  335. }
  336. return
  337. }
  338. // ELFGNUHash ...
  339. func ELFGNUHash(name string) (h uint32) {
  340. h = 5381
  341. for i := 0; i < len(name); i++ {
  342. h = h*33 + uint32(name[i])
  343. }
  344. return
  345. }
  346. func init() {
  347. d, err := ioutil.ReadFile("/proc/self/auxv")
  348. if err != nil {
  349. panic(err)
  350. }
  351. var base unsafe.Pointer
  352. auxv := (*(*[128]uintptr)(unsafe.Pointer(&d[0])))[:len(d)/int(unsafe.Sizeof(uintptr(0)))]
  353. for i := 0; auxv[i] != AtNull; i += 2 {
  354. tag, val := auxv[i], auxv[i+1]
  355. if tag != AtSysInfoEHdr || val == 0 {
  356. continue
  357. }
  358. vdsoInitFromSysinfoEhdr(
  359. &vdsoinfo,
  360. (*elfEhdr)(unsafe.Pointer(uintptr(base)+val)),
  361. )
  362. }
  363. vdsoVersion = vdsoFindVersion(&vdsoinfo, &vdsoLinuxVersion)
  364. initVDSOAll()
  365. }
  366. // VdsoSym ...
  367. func VdsoSym(name string) uintptr {
  368. return vdsoParseSymbols(name, &vdsoinfo, vdsoVersion)
  369. }
  370. func initVDSODefault(name string, def uintptr) uintptr {
  371. if p := VdsoSym(name); p != 0 {
  372. def = p
  373. }
  374. return def
  375. }
  376. func initVDSOAll() {
  377. vdsoGetCPU = initVDSODefault("__vdso_getcpu", 0xffffffffff600800)
  378. }