buildinfo.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. // Copyright 2021 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. // Package buildinfo provides access to information embedded in a Go binary
  5. // about how it was built. This includes the Go toolchain version, and the
  6. // set of modules used (for binaries built in module mode).
  7. //
  8. // Build information is available for the currently running binary in
  9. // runtime/debug.ReadBuildInfo.
  10. package buildinfo
  11. import (
  12. "bytes"
  13. "debug/elf"
  14. "debug/macho"
  15. "debug/pe"
  16. "encoding/binary"
  17. "errors"
  18. "fmt"
  19. "internal/xcoff"
  20. "io"
  21. "io/fs"
  22. "os"
  23. "runtime/debug"
  24. )
  25. // Type alias for build info. We cannot move the types here, since
  26. // runtime/debug would need to import this package, which would make it
  27. // a much larger dependency.
  28. type BuildInfo = debug.BuildInfo
  29. var (
  30. // errUnrecognizedFormat is returned when a given executable file doesn't
  31. // appear to be in a known format, or it breaks the rules of that format,
  32. // or when there are I/O errors reading the file.
  33. errUnrecognizedFormat = errors.New("unrecognized file format")
  34. // errNotGoExe is returned when a given executable file is valid but does
  35. // not contain Go build information.
  36. errNotGoExe = errors.New("not a Go executable")
  37. // The build info blob left by the linker is identified by
  38. // a 16-byte header, consisting of buildInfoMagic (14 bytes),
  39. // the binary's pointer size (1 byte),
  40. // and whether the binary is big endian (1 byte).
  41. buildInfoMagic = []byte("\xff Go buildinf:")
  42. )
  43. // ReadFile returns build information embedded in a Go binary
  44. // file at the given path. Most information is only available for binaries built
  45. // with module support.
  46. func ReadFile(name string) (info *BuildInfo, err error) {
  47. defer func() {
  48. if pathErr := (*fs.PathError)(nil); errors.As(err, &pathErr) {
  49. err = fmt.Errorf("could not read Go build info: %w", err)
  50. } else if err != nil {
  51. err = fmt.Errorf("could not read Go build info from %s: %w", name, err)
  52. }
  53. }()
  54. f, err := os.Open(name)
  55. if err != nil {
  56. return nil, err
  57. }
  58. defer f.Close()
  59. return Read(f)
  60. }
  61. // Read returns build information embedded in a Go binary file
  62. // accessed through the given ReaderAt. Most information is only available for
  63. // binaries built with module support.
  64. func Read(r io.ReaderAt) (*BuildInfo, error) {
  65. vers, mod, err := readRawBuildInfo(r)
  66. if err != nil {
  67. return nil, err
  68. }
  69. bi, err := debug.ParseBuildInfo(mod)
  70. if err != nil {
  71. return nil, err
  72. }
  73. bi.GoVersion = vers
  74. return bi, nil
  75. }
  76. type exe interface {
  77. // ReadData reads and returns up to size bytes starting at virtual address addr.
  78. ReadData(addr, size uint64) ([]byte, error)
  79. // DataStart returns the virtual address of the segment or section that
  80. // should contain build information. This is either a specially named section
  81. // or the first writable non-zero data segment.
  82. DataStart() uint64
  83. }
  84. // readRawBuildInfo extracts the Go toolchain version and module information
  85. // strings from a Go binary. On success, vers should be non-empty. mod
  86. // is empty if the binary was not built with modules enabled.
  87. func readRawBuildInfo(r io.ReaderAt) (vers, mod string, err error) {
  88. // Read the first bytes of the file to identify the format, then delegate to
  89. // a format-specific function to load segment and section headers.
  90. ident := make([]byte, 16)
  91. if n, err := r.ReadAt(ident, 0); n < len(ident) || err != nil {
  92. return "", "", errUnrecognizedFormat
  93. }
  94. var x exe
  95. switch {
  96. case bytes.HasPrefix(ident, []byte("\x7FELF")):
  97. f, err := elf.NewFile(r)
  98. if err != nil {
  99. return "", "", errUnrecognizedFormat
  100. }
  101. x = &elfExe{f}
  102. case bytes.HasPrefix(ident, []byte("MZ")):
  103. f, err := pe.NewFile(r)
  104. if err != nil {
  105. return "", "", errUnrecognizedFormat
  106. }
  107. x = &peExe{f}
  108. case bytes.HasPrefix(ident, []byte("\xFE\xED\xFA")) || bytes.HasPrefix(ident[1:], []byte("\xFA\xED\xFE")):
  109. f, err := macho.NewFile(r)
  110. if err != nil {
  111. return "", "", errUnrecognizedFormat
  112. }
  113. x = &machoExe{f}
  114. case bytes.HasPrefix(ident, []byte{0x01, 0xDF}) || bytes.HasPrefix(ident, []byte{0x01, 0xF7}):
  115. f, err := xcoff.NewFile(r)
  116. if err != nil {
  117. return "", "", errUnrecognizedFormat
  118. }
  119. x = &xcoffExe{f}
  120. default:
  121. return "", "", errUnrecognizedFormat
  122. }
  123. // Read the first 64kB of dataAddr to find the build info blob.
  124. // On some platforms, the blob will be in its own section, and DataStart
  125. // returns the address of that section. On others, it's somewhere in the
  126. // data segment; the linker puts it near the beginning.
  127. // See cmd/link/internal/ld.Link.buildinfo.
  128. dataAddr := x.DataStart()
  129. data, err := x.ReadData(dataAddr, 64*1024)
  130. if err != nil {
  131. return "", "", err
  132. }
  133. const (
  134. buildInfoAlign = 16
  135. buildInfoSize = 32
  136. )
  137. for {
  138. i := bytes.Index(data, buildInfoMagic)
  139. if i < 0 || len(data)-i < buildInfoSize {
  140. return "", "", errNotGoExe
  141. }
  142. if i%buildInfoAlign == 0 && len(data)-i >= buildInfoSize {
  143. data = data[i:]
  144. break
  145. }
  146. data = data[(i+buildInfoAlign-1)&^buildInfoAlign:]
  147. }
  148. // Decode the blob.
  149. // The first 14 bytes are buildInfoMagic.
  150. // The next two bytes indicate pointer size in bytes (4 or 8) and endianness
  151. // (0 for little, 1 for big).
  152. // Two virtual addresses to Go strings follow that: runtime.buildVersion,
  153. // and runtime.modinfo.
  154. // On 32-bit platforms, the last 8 bytes are unused.
  155. // If the endianness has the 2 bit set, then the pointers are zero
  156. // and the 32-byte header is followed by varint-prefixed string data
  157. // for the two string values we care about.
  158. ptrSize := int(data[14])
  159. if data[15]&2 != 0 {
  160. vers, data = decodeString(data[32:])
  161. mod, data = decodeString(data)
  162. } else {
  163. bigEndian := data[15] != 0
  164. var bo binary.ByteOrder
  165. if bigEndian {
  166. bo = binary.BigEndian
  167. } else {
  168. bo = binary.LittleEndian
  169. }
  170. var readPtr func([]byte) uint64
  171. if ptrSize == 4 {
  172. readPtr = func(b []byte) uint64 { return uint64(bo.Uint32(b)) }
  173. } else {
  174. readPtr = bo.Uint64
  175. }
  176. vers = readString(x, ptrSize, readPtr, readPtr(data[16:]))
  177. mod = readString(x, ptrSize, readPtr, readPtr(data[16+ptrSize:]))
  178. }
  179. if vers == "" {
  180. return "", "", errNotGoExe
  181. }
  182. if len(mod) >= 33 && mod[len(mod)-17] == '\n' {
  183. // Strip module framing: sentinel strings delimiting the module info.
  184. // These are cmd/go/internal/modload.infoStart and infoEnd.
  185. mod = mod[16 : len(mod)-16]
  186. } else {
  187. mod = ""
  188. }
  189. return vers, mod, nil
  190. }
  191. func decodeString(data []byte) (s string, rest []byte) {
  192. u, n := binary.Uvarint(data)
  193. if n <= 0 || u >= uint64(len(data)-n) {
  194. return "", nil
  195. }
  196. return string(data[n : uint64(n)+u]), data[uint64(n)+u:]
  197. }
  198. // readString returns the string at address addr in the executable x.
  199. func readString(x exe, ptrSize int, readPtr func([]byte) uint64, addr uint64) string {
  200. hdr, err := x.ReadData(addr, uint64(2*ptrSize))
  201. if err != nil || len(hdr) < 2*ptrSize {
  202. return ""
  203. }
  204. dataAddr := readPtr(hdr)
  205. dataLen := readPtr(hdr[ptrSize:])
  206. data, err := x.ReadData(dataAddr, dataLen)
  207. if err != nil || uint64(len(data)) < dataLen {
  208. return ""
  209. }
  210. return string(data)
  211. }
  212. // elfExe is the ELF implementation of the exe interface.
  213. type elfExe struct {
  214. f *elf.File
  215. }
  216. func (x *elfExe) ReadData(addr, size uint64) ([]byte, error) {
  217. for _, prog := range x.f.Progs {
  218. if prog.Vaddr <= addr && addr <= prog.Vaddr+prog.Filesz-1 {
  219. n := prog.Vaddr + prog.Filesz - addr
  220. if n > size {
  221. n = size
  222. }
  223. data := make([]byte, n)
  224. _, err := prog.ReadAt(data, int64(addr-prog.Vaddr))
  225. if err != nil {
  226. return nil, err
  227. }
  228. return data, nil
  229. }
  230. }
  231. return nil, errUnrecognizedFormat
  232. }
  233. func (x *elfExe) DataStart() uint64 {
  234. for _, s := range x.f.Sections {
  235. if s.Name == ".go.buildinfo" {
  236. return s.Addr
  237. }
  238. }
  239. for _, p := range x.f.Progs {
  240. if p.Type == elf.PT_LOAD && p.Flags&(elf.PF_X|elf.PF_W) == elf.PF_W {
  241. return p.Vaddr
  242. }
  243. }
  244. return 0
  245. }
  246. // peExe is the PE (Windows Portable Executable) implementation of the exe interface.
  247. type peExe struct {
  248. f *pe.File
  249. }
  250. func (x *peExe) imageBase() uint64 {
  251. switch oh := x.f.OptionalHeader.(type) {
  252. case *pe.OptionalHeader32:
  253. return uint64(oh.ImageBase)
  254. case *pe.OptionalHeader64:
  255. return oh.ImageBase
  256. }
  257. return 0
  258. }
  259. func (x *peExe) ReadData(addr, size uint64) ([]byte, error) {
  260. addr -= x.imageBase()
  261. for _, sect := range x.f.Sections {
  262. if uint64(sect.VirtualAddress) <= addr && addr <= uint64(sect.VirtualAddress+sect.Size-1) {
  263. n := uint64(sect.VirtualAddress+sect.Size) - addr
  264. if n > size {
  265. n = size
  266. }
  267. data := make([]byte, n)
  268. _, err := sect.ReadAt(data, int64(addr-uint64(sect.VirtualAddress)))
  269. if err != nil {
  270. return nil, errUnrecognizedFormat
  271. }
  272. return data, nil
  273. }
  274. }
  275. return nil, errUnrecognizedFormat
  276. }
  277. func (x *peExe) DataStart() uint64 {
  278. // Assume data is first writable section.
  279. const (
  280. IMAGE_SCN_CNT_CODE = 0x00000020
  281. IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040
  282. IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080
  283. IMAGE_SCN_MEM_EXECUTE = 0x20000000
  284. IMAGE_SCN_MEM_READ = 0x40000000
  285. IMAGE_SCN_MEM_WRITE = 0x80000000
  286. IMAGE_SCN_MEM_DISCARDABLE = 0x2000000
  287. IMAGE_SCN_LNK_NRELOC_OVFL = 0x1000000
  288. IMAGE_SCN_ALIGN_32BYTES = 0x600000
  289. )
  290. for _, sect := range x.f.Sections {
  291. if sect.VirtualAddress != 0 && sect.Size != 0 &&
  292. sect.Characteristics&^IMAGE_SCN_ALIGN_32BYTES == IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE {
  293. return uint64(sect.VirtualAddress) + x.imageBase()
  294. }
  295. }
  296. return 0
  297. }
  298. // machoExe is the Mach-O (Apple macOS/iOS) implementation of the exe interface.
  299. type machoExe struct {
  300. f *macho.File
  301. }
  302. func (x *machoExe) ReadData(addr, size uint64) ([]byte, error) {
  303. for _, load := range x.f.Loads {
  304. seg, ok := load.(*macho.Segment)
  305. if !ok {
  306. continue
  307. }
  308. if seg.Addr <= addr && addr <= seg.Addr+seg.Filesz-1 {
  309. if seg.Name == "__PAGEZERO" {
  310. continue
  311. }
  312. n := seg.Addr + seg.Filesz - addr
  313. if n > size {
  314. n = size
  315. }
  316. data := make([]byte, n)
  317. _, err := seg.ReadAt(data, int64(addr-seg.Addr))
  318. if err != nil {
  319. return nil, err
  320. }
  321. return data, nil
  322. }
  323. }
  324. return nil, errUnrecognizedFormat
  325. }
  326. func (x *machoExe) DataStart() uint64 {
  327. // Look for section named "__go_buildinfo".
  328. for _, sec := range x.f.Sections {
  329. if sec.Name == "__go_buildinfo" {
  330. return sec.Addr
  331. }
  332. }
  333. // Try the first non-empty writable segment.
  334. const RW = 3
  335. for _, load := range x.f.Loads {
  336. seg, ok := load.(*macho.Segment)
  337. if ok && seg.Addr != 0 && seg.Filesz != 0 && seg.Prot == RW && seg.Maxprot == RW {
  338. return seg.Addr
  339. }
  340. }
  341. return 0
  342. }
  343. // xcoffExe is the XCOFF (AIX eXtended COFF) implementation of the exe interface.
  344. type xcoffExe struct {
  345. f *xcoff.File
  346. }
  347. func (x *xcoffExe) ReadData(addr, size uint64) ([]byte, error) {
  348. for _, sect := range x.f.Sections {
  349. if uint64(sect.VirtualAddress) <= addr && addr <= uint64(sect.VirtualAddress+sect.Size-1) {
  350. n := uint64(sect.VirtualAddress+sect.Size) - addr
  351. if n > size {
  352. n = size
  353. }
  354. data := make([]byte, n)
  355. _, err := sect.ReadAt(data, int64(addr-uint64(sect.VirtualAddress)))
  356. if err != nil {
  357. return nil, err
  358. }
  359. return data, nil
  360. }
  361. }
  362. return nil, fmt.Errorf("address not mapped")
  363. }
  364. func (x *xcoffExe) DataStart() uint64 {
  365. return x.f.SectionByType(xcoff.STYP_DATA).VirtualAddress
  366. }