github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/osutil/buildid_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2017 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package osutil_test
    21  
    22  import (
    23  	"encoding/hex"
    24  	"io/ioutil"
    25  	"os"
    26  	"os/exec"
    27  	"path/filepath"
    28  	"regexp"
    29  
    30  	. "gopkg.in/check.v1"
    31  
    32  	"github.com/snapcore/snapd/osutil"
    33  )
    34  
    35  type buildIDSuite struct{}
    36  
    37  var _ = Suite(&buildIDSuite{})
    38  
    39  var truePath = osutil.LookPathDefault("true", "/bin/true")
    40  var falsePath = osutil.LookPathDefault("false", "/bin/false")
    41  var gccPath = osutil.LookPathDefault("gcc", "/usr/bin/gcc")
    42  
    43  func buildID(c *C, fname string) string {
    44  	// XXX host's 'file' command may be too old to know about Go BuildID or
    45  	// hexstring GNU BuildID, use with caution
    46  	output, err := exec.Command("file", fname).CombinedOutput()
    47  	c.Assert(err, IsNil)
    48  
    49  	c.Logf("file output: %q", string(output))
    50  
    51  	// BuildID can look like:
    52  	//  BuildID[sha1]=443877f9ec13c82365478130fc95cb5ff5181912
    53  	//  BuildID[md5/uuid]=ae38cdf243d2111064dfee99dfc30013
    54  	//  Go BuildID=YDAw4RLIEKpyxl90JbFQ/s9mld--03zAIIQ1tGb_5/aL-yPp ...
    55  	re := regexp.MustCompile(`BuildID(\[.*\]|)=([a-zA-Z0-9/_-]+)`)
    56  	matches := re.FindStringSubmatch(string(output))
    57  
    58  	c.Assert(matches, HasLen, 3)
    59  
    60  	return matches[2]
    61  }
    62  
    63  func (s *buildIDSuite) TestReadBuildID(c *C) {
    64  	for _, fname := range []string{truePath, falsePath} {
    65  
    66  		id, err := osutil.ReadBuildID(fname)
    67  		c.Assert(err, IsNil)
    68  		c.Assert(id, Equals, buildID(c, fname), Commentf("executable: %s", fname))
    69  	}
    70  }
    71  
    72  func (s *buildIDSuite) TestReadBuildIDNoID(c *C) {
    73  	stripedTruth := filepath.Join(c.MkDir(), "true")
    74  	osutil.CopyFile(truePath, stripedTruth, 0)
    75  	output, err := exec.Command("strip", "-R", ".note.gnu.build-id", stripedTruth).CombinedOutput()
    76  	c.Assert(string(output), Equals, "")
    77  	c.Assert(err, IsNil)
    78  
    79  	id, err := osutil.ReadBuildID(stripedTruth)
    80  	c.Assert(err, Equals, osutil.ErrNoBuildID)
    81  	c.Assert(id, Equals, "")
    82  }
    83  
    84  func (s *buildIDSuite) TestReadBuildIDmd5(c *C) {
    85  	if !osutil.FileExists(gccPath) {
    86  		c.Skip("No gcc found")
    87  	}
    88  
    89  	md5Truth := filepath.Join(c.MkDir(), "true")
    90  	err := ioutil.WriteFile(md5Truth+".c", []byte(`int main(){return 0;}`), 0644)
    91  	c.Assert(err, IsNil)
    92  	output, err := exec.Command(gccPath, "-Wl,--build-id=md5", "-xc", md5Truth+".c", "-o", md5Truth).CombinedOutput()
    93  	c.Assert(string(output), Equals, "")
    94  	c.Assert(err, IsNil)
    95  
    96  	id, err := osutil.ReadBuildID(md5Truth)
    97  	c.Assert(err, IsNil)
    98  	c.Assert(id, Equals, buildID(c, md5Truth))
    99  }
   100  
   101  func (s *buildIDSuite) TestReadBuildIDFixedELF(c *C) {
   102  	if !osutil.FileExists(gccPath) {
   103  		c.Skip("No gcc found")
   104  	}
   105  
   106  	md5Truth := filepath.Join(c.MkDir(), "true")
   107  	err := ioutil.WriteFile(md5Truth+".c", []byte(`int main(){return 0;}`), 0644)
   108  	c.Assert(err, IsNil)
   109  	output, err := exec.Command(gccPath, "-Wl,--build-id=0xdeadcafe", "-xc", md5Truth+".c", "-o", md5Truth).CombinedOutput()
   110  	c.Assert(string(output), Equals, "")
   111  	c.Assert(err, IsNil)
   112  
   113  	id, err := osutil.ReadBuildID(md5Truth)
   114  	c.Assert(err, IsNil)
   115  	// XXX cannot call buildID() as the host's 'file' command may be too old
   116  	// to know about hexstring format of GNU BuildID
   117  	c.Assert(id, Equals, "deadcafe")
   118  }
   119  
   120  func (s *buildIDSuite) TestMyBuildID(c *C) {
   121  	restore := osutil.MockOsReadlink(func(string) (string, error) {
   122  		return truePath, nil
   123  	})
   124  	defer restore()
   125  
   126  	id, err := osutil.MyBuildID()
   127  	c.Assert(err, IsNil)
   128  	c.Check(id, Equals, buildID(c, truePath))
   129  }
   130  
   131  func (s *buildIDSuite) TestReadBuildGo(c *C) {
   132  	if os.Getenv("DH_GOPKG") != "" {
   133  		// Failure reason is unknown but only reproducible
   134  		// inside the any 21.04+ sbuild/pbuilder build
   135  		// environment during the build (with dh-golang).
   136  		//
   137  		// Not reproducible outside of dpkg-buildpackage.
   138  		c.Skip("This `go build` fails inside the dpkg-buildpackage environment with `loadinternal: cannot find runtime/cgo`")
   139  	}
   140  
   141  	tmpdir := c.MkDir()
   142  	goTruth := filepath.Join(tmpdir, "true")
   143  	err := ioutil.WriteFile(goTruth+".go", []byte(`package main; func main(){}`), 0644)
   144  	c.Assert(err, IsNil)
   145  	// force specific Go BuildID
   146  	cmd := exec.Command("go", "build", "-o", goTruth, "-ldflags=-buildid=foobar", goTruth+".go")
   147  	// set custom homedir to ensure tests work in an sbuild environment
   148  	// that force a non-existing homedir
   149  	cmd.Env = append(os.Environ(), "HOME="+tmpdir)
   150  	output, err := cmd.CombinedOutput()
   151  	c.Assert(string(output), Equals, "")
   152  	c.Assert(err, IsNil)
   153  
   154  	id, err := osutil.ReadBuildID(goTruth)
   155  	c.Assert(err, IsNil)
   156  
   157  	// ReadBuildID returns a hex encoded string, however buildID()
   158  	// returns the "raw" string so we need to decode first
   159  	decoded, err := hex.DecodeString(id)
   160  	c.Assert(err, IsNil)
   161  	// XXX cannot call buildID() as the host's 'file' command may be too old
   162  	// to know about Go BuildID
   163  	c.Assert(string(decoded), Equals, "foobar")
   164  }