Source file src/cmd/link/internal/ld/fips.go

     1  // Copyright 2024 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  /*
     6  FIPS-140 Verification Support
     7  
     8  See ../../../internal/obj/fips.go for a basic overview.
     9  This file is concerned with computing the hash of the FIPS code+data.
    10  Package obj has taken care of marking the FIPS symbols with the
    11  special types STEXTFIPS, SRODATAFIPS, SNOPTRDATAFIPS, and SDATAFIPS.
    12  
    13  # FIPS Symbol Layout
    14  
    15  The first order of business is collecting the FIPS symbols into
    16  contiguous sections of the final binary and identifying the start and
    17  end of those sections. The linker already tracks the start and end of
    18  the text section as runtime.text and runtime.etext, and similarly for
    19  other sections, but the implementation of those symbols is tricky and
    20  platform-specific. The problem is that they are zero-length
    21  pseudo-symbols that share addresses with other symbols, which makes
    22  everything harder. For the FIPS sections, we avoid that subtlety by
    23  defining actual non-zero-length symbols bracketing each section and
    24  use those symbols as the boundaries.
    25  
    26  Specifically, we define a 1-byte symbol go:textfipsstart of type
    27  STEXTFIPSSTART and a 1-byte symbol go:textfipsend of type STEXTFIPSEND,
    28  and we place those two symbols immediately before and after the
    29  STEXTFIPS symbols. We do the same for SRODATAFIPS, SNOPTRDATAFIPS,
    30  and SDATAFIPS. Because the symbols are real (but otherwise unused) data,
    31  they can be treated as normal symbols for symbol table purposes and
    32  don't need the same kind of special handling that runtime.text and
    33  friends do.
    34  
    35  Note that treating the FIPS text as starting at &go:textfipsstart and
    36  ending at &go:textfipsend means that go:textfipsstart is included in
    37  the verified data while go:textfipsend is not. That's fine: they are
    38  only framing and neither strictly needs to be in the hash.
    39  
    40  The new special symbols are created by [loadfips].
    41  
    42  # FIPS Info Layout
    43  
    44  Having collated the FIPS symbols, we need to compute the hash
    45  and then leave both the expected hash and the FIPS address ranges
    46  for the run-time check in crypto/internal/fips/check.
    47  We do that by creating a special symbol named go:fipsinfo of the form
    48  
    49  	struct {
    50  		sum   [32]byte
    51  		self  uintptr // points to start of struct
    52  		sects [4]struct{
    53  			start uintptr
    54  			end   uintptr
    55  		}
    56  	}
    57  
    58  The crypto/internal/fips/check uses linkname to access this symbol,
    59  which is of course not included in the hash.
    60  
    61  # FIPS Info Calculation
    62  
    63  When using internal linking, [asmbfips] runs after writing the output
    64  binary but before code-signing it. It reads the relevant sections
    65  back from the output file, hashes them, and then writes the go:fipsinfo
    66  content into the output file.
    67  
    68  When using external linking, especially with -buildmode=pie, we cannot
    69  predict the specific PLT index references that the linker will insert
    70  into the FIPS code sections, so we must read the final linked executable
    71  after external linking, compute the hash, and then write it back to the
    72  executable in the go:fipsinfo sum field. [hostlinkfips] does this.
    73  It finds go:fipsinfo easily because that symbol is given its own section
    74  (.go.fipsinfo on ELF, __go_fipsinfo on Mach-O), and then it can use the
    75  sections field to find the relevant parts of the executable, hash them,
    76  and fill in sum.
    77  
    78  Both [asmbfips] and [hostlinkfips] need the same hash calculation code.
    79  The [fipsObj] type provides that calculation.
    80  
    81  # Debugging
    82  
    83  It is of course impossible to debug a mismatched hash directly:
    84  two random 32-byte strings differ. For debugging, the linker flag
    85  -fipso can be set to the name of a file (such as /tmp/fips.o)
    86  where the linker will write the “FIPS object” that is being hashed.
    87  
    88  There is also commented-out code in crypto/internal/fips/check that
    89  will write /tmp/fipscheck.o during the run-time verification.
    90  
    91  When the hashes differ, the first step is to uncomment the
    92  /tmp/fipscheck.o-writing code and then rebuild with
    93  -ldflags=-fipso=/tmp/fips.o. Then when the hash check fails,
    94  compare /tmp/fips.o and /tmp/fipscheck.o to find the differences.
    95  */
    96  
    97  package ld
    98  
    99  import (
   100  	"bufio"
   101  	"bytes"
   102  	"cmd/internal/obj"
   103  	"cmd/internal/objabi"
   104  	"cmd/link/internal/loader"
   105  	"cmd/link/internal/sym"
   106  	"crypto/hmac"
   107  	"crypto/sha256"
   108  	"debug/elf"
   109  	"debug/macho"
   110  	"debug/pe"
   111  	"encoding/binary"
   112  	"fmt"
   113  	"hash"
   114  	"io"
   115  	"os"
   116  )
   117  
   118  const enableFIPS = true
   119  
   120  // fipsSyms are the special FIPS section bracketing symbols.
   121  var fipsSyms = []struct {
   122  	name string
   123  	kind sym.SymKind
   124  	sym  loader.Sym
   125  	seg  *sym.Segment
   126  }{
   127  	{name: "go:textfipsstart", kind: sym.STEXTFIPSSTART, seg: &Segtext},
   128  	{name: "go:textfipsend", kind: sym.STEXTFIPSEND},
   129  	{name: "go:rodatafipsstart", kind: sym.SRODATAFIPSSTART, seg: &Segrodata},
   130  	{name: "go:rodatafipsend", kind: sym.SRODATAFIPSEND},
   131  	{name: "go:noptrdatafipsstart", kind: sym.SNOPTRDATAFIPSSTART, seg: &Segdata},
   132  	{name: "go:noptrdatafipsend", kind: sym.SNOPTRDATAFIPSEND},
   133  	{name: "go:datafipsstart", kind: sym.SDATAFIPSSTART, seg: &Segdata},
   134  	{name: "go:datafipsend", kind: sym.SDATAFIPSEND},
   135  }
   136  
   137  // fipsinfo is the loader symbol for go:fipsinfo.
   138  var fipsinfo loader.Sym
   139  
   140  const (
   141  	fipsMagic    = "\xff Go fipsinfo \xff\x00"
   142  	fipsMagicLen = 16
   143  	fipsSumLen   = 32
   144  )
   145  
   146  // loadfips creates the special bracketing symbols and go:fipsinfo.
   147  func loadfips(ctxt *Link) {
   148  	if !obj.EnableFIPS() {
   149  		return
   150  	}
   151  	if ctxt.BuildMode == BuildModePlugin { // not sure why this doesn't work
   152  		return
   153  	}
   154  	// Write the fipsinfo symbol, which crypto/internal/fips/check uses.
   155  	ldr := ctxt.loader
   156  	// TODO lock down linkname
   157  	info := ldr.CreateSymForUpdate("go:fipsinfo", 0)
   158  	info.SetType(sym.SFIPSINFO)
   159  
   160  	data := make([]byte, fipsMagicLen+fipsSumLen)
   161  	copy(data, fipsMagic)
   162  	info.SetData(data)
   163  	info.SetSize(int64(len(data)))      // magic + checksum, to be filled in
   164  	info.AddAddr(ctxt.Arch, info.Sym()) // self-reference
   165  
   166  	for i := range fipsSyms {
   167  		s := &fipsSyms[i]
   168  		sb := ldr.CreateSymForUpdate(s.name, 0)
   169  		sb.SetType(s.kind)
   170  		sb.SetLocal(true)
   171  		sb.SetSize(1)
   172  		s.sym = sb.Sym()
   173  		info.AddAddr(ctxt.Arch, s.sym)
   174  		if s.kind == sym.STEXTFIPSSTART || s.kind == sym.STEXTFIPSEND {
   175  			ctxt.Textp = append(ctxt.Textp, s.sym)
   176  		}
   177  	}
   178  
   179  	fipsinfo = info.Sym()
   180  }
   181  
   182  // fipsObj calculates the fips object hash and optionally writes
   183  // the hashed content to a file for debugging.
   184  type fipsObj struct {
   185  	r   io.ReaderAt
   186  	w   io.Writer
   187  	wf  *os.File
   188  	h   hash.Hash
   189  	tmp [8]byte
   190  }
   191  
   192  // newFipsObj creates a fipsObj reading from r and writing to fipso
   193  // (unless fipso is the empty string, in which case it writes nowhere
   194  // and only computes the hash).
   195  func newFipsObj(r io.ReaderAt, fipso string) (*fipsObj, error) {
   196  	f := &fipsObj{r: r}
   197  	f.h = hmac.New(sha256.New, make([]byte, 32))
   198  	f.w = f.h
   199  	if fipso != "" {
   200  		wf, err := os.Create(fipso)
   201  		if err != nil {
   202  			return nil, err
   203  		}
   204  		f.wf = wf
   205  		f.w = io.MultiWriter(f.h, wf)
   206  	}
   207  
   208  	if _, err := f.w.Write([]byte("go fips object v1\n")); err != nil {
   209  		f.Close()
   210  		return nil, err
   211  	}
   212  	return f, nil
   213  }
   214  
   215  // addSection adds the section of r (passed to newFipsObj)
   216  // starting at byte offset start and ending before byte offset end
   217  // to the fips object file.
   218  func (f *fipsObj) addSection(start, end int64) error {
   219  	n := end - start
   220  	binary.BigEndian.PutUint64(f.tmp[:], uint64(n))
   221  	f.w.Write(f.tmp[:])
   222  	_, err := io.Copy(f.w, io.NewSectionReader(f.r, start, n))
   223  	return err
   224  }
   225  
   226  // sum returns the hash of the fips object file.
   227  func (f *fipsObj) sum() []byte {
   228  	return f.h.Sum(nil)
   229  }
   230  
   231  // Close closes the fipsObj. In particular it closes the output
   232  // object file specified by fipso in the call to [newFipsObj].
   233  func (f *fipsObj) Close() error {
   234  	if f.wf != nil {
   235  		return f.wf.Close()
   236  	}
   237  	return nil
   238  }
   239  
   240  // asmbfips is called from [asmb] to update go:fipsinfo
   241  // when using internal linking.
   242  // See [hostlinkfips] for external linking.
   243  func asmbfips(ctxt *Link, fipso string) {
   244  	if !obj.EnableFIPS() {
   245  		return
   246  	}
   247  	if ctxt.LinkMode == LinkExternal {
   248  		return
   249  	}
   250  	if ctxt.BuildMode == BuildModePlugin { // not sure why this doesn't work
   251  		return
   252  	}
   253  
   254  	// Create a new FIPS object with data read from our output file.
   255  	f, err := newFipsObj(bytes.NewReader(ctxt.Out.Data()), fipso)
   256  	if err != nil {
   257  		Errorf("asmbfips: %v", err)
   258  		return
   259  	}
   260  	defer f.Close()
   261  
   262  	// Add the FIPS sections to the FIPS object.
   263  	ldr := ctxt.loader
   264  	for i := 0; i < len(fipsSyms); i += 2 {
   265  		start := &fipsSyms[i]
   266  		end := &fipsSyms[i+1]
   267  		startAddr := ldr.SymValue(start.sym)
   268  		endAddr := ldr.SymValue(end.sym)
   269  		seg := start.seg
   270  		if seg.Vaddr == 0 && seg == &Segrodata { // some systems use text instead of separate rodata
   271  			seg = &Segtext
   272  		}
   273  		base := int64(seg.Fileoff - seg.Vaddr)
   274  		if !(seg.Vaddr <= uint64(startAddr) && startAddr <= endAddr && uint64(endAddr) <= seg.Vaddr+seg.Filelen) {
   275  			Errorf("asmbfips: %s not in expected segment (%#x..%#x not in %#x..%#x)", start.name, startAddr, endAddr, seg.Vaddr, seg.Vaddr+seg.Filelen)
   276  			return
   277  		}
   278  
   279  		if err := f.addSection(startAddr+base, endAddr+base); err != nil {
   280  			Errorf("asmbfips: %v", err)
   281  			return
   282  		}
   283  	}
   284  
   285  	// Overwrite the go:fipsinfo sum field with the calculated sum.
   286  	addr := uint64(ldr.SymValue(fipsinfo))
   287  	seg := &Segdata
   288  	if !(seg.Vaddr <= addr && addr+32 < seg.Vaddr+seg.Filelen) {
   289  		Errorf("asmbfips: fipsinfo not in expected segment (%#x..%#x not in %#x..%#x)", addr, addr+32, seg.Vaddr, seg.Vaddr+seg.Filelen)
   290  		return
   291  	}
   292  	ctxt.Out.SeekSet(int64(seg.Fileoff + addr - seg.Vaddr + fipsMagicLen))
   293  	ctxt.Out.Write(f.sum())
   294  
   295  	if err := f.Close(); err != nil {
   296  		Errorf("asmbfips: %v", err)
   297  		return
   298  	}
   299  }
   300  
   301  // hostlinkfips is called from [hostlink] to update go:fipsinfo
   302  // when using external linking.
   303  // See [asmbfips] for internal linking.
   304  func hostlinkfips(ctxt *Link, exe, fipso string) error {
   305  	if !obj.EnableFIPS() {
   306  		return nil
   307  	}
   308  	if ctxt.BuildMode == BuildModePlugin { // not sure why this doesn't work
   309  		return nil
   310  	}
   311  	switch {
   312  	case ctxt.IsElf():
   313  		return elffips(ctxt, exe, fipso)
   314  	case ctxt.HeadType == objabi.Hdarwin:
   315  		return machofips(ctxt, exe, fipso)
   316  	case ctxt.HeadType == objabi.Hwindows:
   317  		return pefips(ctxt, exe, fipso)
   318  	}
   319  
   320  	// If we can't do FIPS, leave the output binary alone.
   321  	// If people enable FIPS the init-time check will fail,
   322  	// but the binaries will work otherwise.
   323  	return fmt.Errorf("fips unsupported on %s", ctxt.HeadType)
   324  }
   325  
   326  // machofips updates go:fipsinfo after external linking
   327  // on systems using Mach-O (GOOS=darwin, GOOS=ios).
   328  func machofips(ctxt *Link, exe, fipso string) error {
   329  	// Open executable both for reading Mach-O and for the fipsObj.
   330  	mf, err := macho.Open(exe)
   331  	if err != nil {
   332  		return err
   333  	}
   334  	defer mf.Close()
   335  
   336  	wf, err := os.OpenFile(exe, os.O_RDWR, 0)
   337  	if err != nil {
   338  		return err
   339  	}
   340  	defer wf.Close()
   341  
   342  	f, err := newFipsObj(wf, fipso)
   343  	if err != nil {
   344  		return err
   345  	}
   346  	defer f.Close()
   347  
   348  	// Find the go:fipsinfo symbol.
   349  	sect := mf.Section("__go_fipsinfo")
   350  	if sect == nil {
   351  		return fmt.Errorf("cannot find __go_fipsinfo")
   352  	}
   353  	data, err := sect.Data()
   354  	if err != nil {
   355  		return err
   356  	}
   357  
   358  	uptr := ctxt.Arch.ByteOrder.Uint64
   359  	if ctxt.Arch.PtrSize == 4 {
   360  		uptr = func(x []byte) uint64 {
   361  			return uint64(ctxt.Arch.ByteOrder.Uint32(x))
   362  		}
   363  	}
   364  
   365  	// Add the sections listed in go:fipsinfo to the FIPS object.
   366  	// On Mac, the debug/macho package is not reporting any relocations,
   367  	// but the addends are all in the data already, all relative to
   368  	// the same base.
   369  	// Determine the base used for the self pointer, and then apply
   370  	// that base to the other uintptrs.
   371  	// The very high bits of the uint64s seem to be relocation metadata,
   372  	// so clear them.
   373  	// For non-pie builds, there are no relocations at all:
   374  	// the data holds the actual pointers.
   375  	// This code handles both pie and non-pie binaries.
   376  	const addendMask = 1<<48 - 1
   377  	data = data[fipsMagicLen+fipsSumLen:]
   378  	self := int64(uptr(data)) & addendMask
   379  	base := int64(sect.Offset) - self
   380  	data = data[ctxt.Arch.PtrSize:]
   381  
   382  	for i := 0; i < 4; i++ {
   383  		start := int64(uptr(data[0:]))&addendMask + base
   384  		end := int64(uptr(data[ctxt.Arch.PtrSize:]))&addendMask + base
   385  		data = data[2*ctxt.Arch.PtrSize:]
   386  		if err := f.addSection(start, end); err != nil {
   387  			return err
   388  		}
   389  	}
   390  
   391  	// Overwrite the go:fipsinfo sum field with the calculated sum.
   392  	if _, err := wf.WriteAt(f.sum(), int64(sect.Offset)+fipsMagicLen); err != nil {
   393  		return err
   394  	}
   395  	if err := wf.Close(); err != nil {
   396  		return err
   397  	}
   398  	return f.Close()
   399  }
   400  
   401  // machofips updates go:fipsinfo after external linking
   402  // on systems using ELF (most Unix systems).
   403  func elffips(ctxt *Link, exe, fipso string) error {
   404  	// Open executable both for reading ELF and for the fipsObj.
   405  	ef, err := elf.Open(exe)
   406  	if err != nil {
   407  		return err
   408  	}
   409  	defer ef.Close()
   410  
   411  	wf, err := os.OpenFile(exe, os.O_RDWR, 0)
   412  	if err != nil {
   413  		return err
   414  	}
   415  	defer wf.Close()
   416  
   417  	f, err := newFipsObj(wf, fipso)
   418  	if err != nil {
   419  		return err
   420  	}
   421  	defer f.Close()
   422  
   423  	// Find the go:fipsinfo symbol.
   424  	sect := ef.Section(".go.fipsinfo")
   425  	if sect == nil {
   426  		return fmt.Errorf("cannot find .go.fipsinfo")
   427  	}
   428  
   429  	data, err := sect.Data()
   430  	if err != nil {
   431  		return err
   432  	}
   433  
   434  	uptr := ctxt.Arch.ByteOrder.Uint64
   435  	if ctxt.Arch.PtrSize == 4 {
   436  		uptr = func(x []byte) uint64 {
   437  			return uint64(ctxt.Arch.ByteOrder.Uint32(x))
   438  		}
   439  	}
   440  
   441  	// Add the sections listed in go:fipsinfo to the FIPS object.
   442  	// We expect R_zzz_RELATIVE relocations where the zero-based
   443  	// values are already stored in the data. That is, the addend
   444  	// is in the data itself in addition to being in the relocation tables.
   445  	// So no need to parse the relocation tables unless we find a
   446  	// toolchain that doesn't initialize the data this way.
   447  	// For non-pie builds, there are no relocations at all:
   448  	// the data holds the actual pointers.
   449  	// This code handles both pie and non-pie binaries.
   450  	data = data[fipsMagicLen+fipsSumLen:]
   451  	data = data[ctxt.Arch.PtrSize:]
   452  
   453  Addrs:
   454  	for i := 0; i < 4; i++ {
   455  		start := uptr(data[0:])
   456  		end := uptr(data[ctxt.Arch.PtrSize:])
   457  		data = data[2*ctxt.Arch.PtrSize:]
   458  		for _, prog := range ef.Progs {
   459  			if prog.Type == elf.PT_LOAD && prog.Vaddr <= start && start <= end && end <= prog.Vaddr+prog.Filesz {
   460  				if err := f.addSection(int64(start+prog.Off-prog.Vaddr), int64(end+prog.Off-prog.Vaddr)); err != nil {
   461  					return err
   462  				}
   463  				continue Addrs
   464  			}
   465  		}
   466  		return fmt.Errorf("invalid pointers found in .go.fipsinfo")
   467  	}
   468  
   469  	// Overwrite the go:fipsinfo sum field with the calculated sum.
   470  	if _, err := wf.WriteAt(f.sum(), int64(sect.Offset)+fipsMagicLen); err != nil {
   471  		return err
   472  	}
   473  	if err := wf.Close(); err != nil {
   474  		return err
   475  	}
   476  	return f.Close()
   477  }
   478  
   479  // pefips updates go:fipsinfo after external linking
   480  // on systems using PE (GOOS=windows).
   481  func pefips(ctxt *Link, exe, fipso string) error {
   482  	// Open executable both for reading Mach-O and for the fipsObj.
   483  	pf, err := pe.Open(exe)
   484  	if err != nil {
   485  		return err
   486  	}
   487  	defer pf.Close()
   488  
   489  	wf, err := os.OpenFile(exe, os.O_RDWR, 0)
   490  	if err != nil {
   491  		return err
   492  	}
   493  	defer wf.Close()
   494  
   495  	f, err := newFipsObj(wf, fipso)
   496  	if err != nil {
   497  		return err
   498  	}
   499  	defer f.Close()
   500  
   501  	// Find the go:fipsinfo symbol.
   502  	// PE does not put it in its own section, so we have to scan for it.
   503  	// It is near the start of the data segment, right after go:buildinfo,
   504  	// so we should not have to scan too far.
   505  	const maxScan = 16 << 20
   506  	sect := pf.Section(".data")
   507  	if sect == nil {
   508  		return fmt.Errorf("cannot find .data")
   509  	}
   510  	b := bufio.NewReader(sect.Open())
   511  	off := int64(0)
   512  	data := make([]byte, fipsMagicLen+fipsSumLen+9*ctxt.Arch.PtrSize)
   513  	for ; ; off += 16 {
   514  		if off >= maxScan {
   515  			break
   516  		}
   517  		if _, err := io.ReadFull(b, data[:fipsMagicLen]); err != nil {
   518  			return fmt.Errorf("scanning PE for FIPS magic: %v", err)
   519  		}
   520  		if string(data[:fipsMagicLen]) == fipsMagic {
   521  			if _, err := io.ReadFull(b, data[fipsMagicLen:]); err != nil {
   522  				return fmt.Errorf("scanning PE for FIPS magic: %v", err)
   523  			}
   524  			break
   525  		}
   526  	}
   527  
   528  	uptr := ctxt.Arch.ByteOrder.Uint64
   529  	if ctxt.Arch.PtrSize == 4 {
   530  		uptr = func(x []byte) uint64 {
   531  			return uint64(ctxt.Arch.ByteOrder.Uint32(x))
   532  		}
   533  	}
   534  
   535  	// Add the sections listed in go:fipsinfo to the FIPS object.
   536  	// Determine the base used for the self pointer, and then apply
   537  	// that base to the other uintptrs.
   538  	// For pie builds, the addends are in the data.
   539  	// For non-pie builds, there are no relocations at all:
   540  	// the data holds the actual pointers.
   541  	// This code handles both pie and non-pie binaries.
   542  	data = data[fipsMagicLen+fipsSumLen:]
   543  	self := int64(uptr(data))
   544  	data = data[ctxt.Arch.PtrSize:]
   545  
   546  	// On 64-bit binaries the pointers have extra bits set
   547  	// that don't appear in the actual section headers.
   548  	// For example, one generated test binary looks like:
   549  	//
   550  	//	.data VirtualAddress = 0x2af000
   551  	//	.data (file) Offset = 0x2ac400
   552  	//	.data (file) Size = 0x1fc00
   553  	//	go:fipsinfo found at offset 0x2ac5e0 (off=0x1e0)
   554  	//	go:fipsinfo self pointer = 0x01402af1e0
   555  	//
   556  	// From the section headers, the address of the go:fipsinfo symbol
   557  	// should be 0x2af000 + (0x2ac5e0 - 0x2ac400) = 0x2af1e0,
   558  	// yet in this case its pointer is 0x1402af1e0, meaning the
   559  	// data section's VirtualAddress is really 0x1402af000.
   560  	// This is not (only) a 32-bit truncation problem, since the uint32
   561  	// truncation of that address would be 0x402af000, not 0x2af000.
   562  	// Perhaps there is some 64-bit extension that debug/pe is not
   563  	// reading or is misreading. In any event, we can derive the delta
   564  	// between computed VirtualAddress and listed VirtualAddress
   565  	// and apply it to the rest of the pointers.
   566  	// As a sanity check, the low 12 bits (virtual page offset)
   567  	// must match between our computed address and the actual one.
   568  	peself := int64(sect.VirtualAddress) + off
   569  	if self&0xfff != off&0xfff {
   570  		return fmt.Errorf("corrupt pointer found in go:fipsinfo")
   571  	}
   572  	delta := peself - self
   573  
   574  Addrs:
   575  	for i := 0; i < 4; i++ {
   576  		start := int64(uptr(data[0:])) + delta
   577  		end := int64(uptr(data[ctxt.Arch.PtrSize:])) + delta
   578  		data = data[2*ctxt.Arch.PtrSize:]
   579  		for _, sect := range pf.Sections {
   580  			if int64(sect.VirtualAddress) <= start && start <= end && end <= int64(sect.VirtualAddress)+int64(sect.Size) {
   581  				off := int64(sect.Offset) - int64(sect.VirtualAddress)
   582  				if err := f.addSection(start+off, end+off); err != nil {
   583  					return err
   584  				}
   585  				continue Addrs
   586  			}
   587  		}
   588  		return fmt.Errorf("invalid pointers found in go:fipsinfo")
   589  	}
   590  
   591  	// Overwrite the go:fipsinfo sum field with the calculated sum.
   592  	if _, err := wf.WriteAt(f.sum(), int64(sect.Offset)+off+fipsMagicLen); err != nil {
   593  		return err
   594  	}
   595  	if err := wf.Close(); err != nil {
   596  		return err
   597  	}
   598  	return f.Close()
   599  }
   600  

View as plain text