github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/pkg/boot/bzimage/bzimage.go (about)

     1  // Copyright 2015-2018 the u-root 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  // Package bzImage implements decoding for bzImage files.
     6  //
     7  // The bzImage struct contains all the information about the file and can
     8  // be used to create a new bzImage.
     9  package bzimage
    10  
    11  // xz --check=crc32 $BCJ --lzma2=$LZMA2OPTS,dict=32MiB
    12  
    13  import (
    14  	"bytes"
    15  	"debug/elf"
    16  	"encoding/binary"
    17  	"fmt"
    18  	"io"
    19  	"io/ioutil"
    20  	"os/exec"
    21  	"reflect"
    22  	"strings"
    23  
    24  	"github.com/u-root/u-root/pkg/cpio"
    25  )
    26  
    27  var (
    28  	xzmagic = [...]byte{0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00}
    29  	// String of unknown meaning.
    30  	// The build script has this value:
    31  	//initRAMFStag = [4]byte{0250, 0362, 0156, 0x01}
    32  	// The resultant bzd has this value:
    33  	initRAMFStag = [4]byte{0xf8, 0x85, 0x21, 0x01}
    34  	Debug        = func(string, ...interface{}) {}
    35  )
    36  
    37  // UnmarshalBinary implements the encoding.BinaryUnmarshaler interface.
    38  // For now, it hardwires the KernelBase to 0x100000.
    39  // bzImages were created by a process of evilution, and they are wondrous to behold.
    40  // bzImages are almost impossible to modify. They form a sandwich with
    41  // the compressed kernel code in the middle. It's actually a BLT:
    42  // MBR and bootparams first 512 bytes
    43  //    the MBR includes 0xc0 bytes of boot code which is vestigial.
    44  // Then there is "preamble" code which is the kernel decompressor; then the
    45  // xz compressed kernel; then a library of sorts after the kernel which is called
    46  // by the early uncompressed kernel code. This is all linked together and forms
    47  // an essentially indivisible whole -- which we wish to divisible.
    48  // Hence the groveling around for the xz header that you see here, and hence the checks
    49  // to ensure that the kernel layout ends up largely the same before and after.
    50  // That said, if you keep layout unchanged, you can modify the uncompressed kernel.
    51  // For example, when you first build a kernel, you can:
    52  // dd if=/dev/urandom of=x bs=1048576 count=8
    53  // echo x | cpio -o > x.cpio
    54  // and use that as an initrd, it's more or less an 8 MiB block you can replace
    55  // as needed. Just make sure nothing grows. And make sure the initramfs is in
    56  // the same place. Ah, joy.
    57  func (b *BzImage) UnmarshalBinary(d []byte) error {
    58  	Debug("Processing %d byte image", len(d))
    59  	r := bytes.NewBuffer(d)
    60  	if err := binary.Read(r, binary.LittleEndian, &b.Header); err != nil {
    61  		return err
    62  	}
    63  	Debug("Header was %d bytes", len(d)-r.Len())
    64  	Debug("magic %x switch %v", b.Header.HeaderMagic, b.Header.RealModeSwitch)
    65  	if b.Header.HeaderMagic != HeaderMagic {
    66  		return fmt.Errorf("not a bzImage: magic should be %02x, and is %02x", HeaderMagic, b.Header.HeaderMagic)
    67  	}
    68  	Debug("RamDisk image %x size %x", b.Header.RamDiskImage, b.Header.RamDiskSize)
    69  	Debug("StartSys %x", b.Header.StartSys)
    70  	Debug("Boot type: %s(%x)", LoaderType[boottype(b.Header.TypeOfLoader)], b.Header.TypeOfLoader)
    71  	Debug("SetupSects %d", b.Header.SetupSects)
    72  
    73  	off := len(d) - r.Len()
    74  	b.KernelOffset = (uintptr(b.Header.SetupSects) + 1) * 512
    75  	bclen := int(b.KernelOffset) - off
    76  	Debug("Kernel offset is %d bytes, low1mcode is %d bytes", b.KernelOffset, bclen)
    77  	b.BootCode = make([]byte, bclen)
    78  	if _, err := r.Read(b.BootCode); err != nil {
    79  		return err
    80  	}
    81  	Debug("%d bytes of BootCode", len(b.BootCode))
    82  
    83  	Debug("Remaining length is %d bytes, PayloadSize %d", r.Len(), b.Header.PayloadSize)
    84  	x := bytes.Index(r.Bytes(), xzmagic[:])
    85  	if x == -1 {
    86  		return fmt.Errorf("can't find xz header")
    87  	}
    88  	Debug("xz is at %d", x)
    89  	b.HeadCode = make([]byte, x)
    90  	if _, err := r.Read(b.HeadCode); err != nil {
    91  		return fmt.Errorf("can't read HeadCode: %v", err)
    92  	}
    93  	// Now size up the kernel code. Is it just PayloadSize?
    94  	b.compressed = make([]byte, b.Header.PayloadSize)
    95  	if _, err := r.Read(b.compressed); err != nil {
    96  		return fmt.Errorf("can't read HeadCode: %v", err)
    97  	}
    98  	var err error
    99  	Debug("Uncompress %d bytes", len(b.compressed))
   100  	if b.KernelCode, err = unpack(b.compressed); err != nil {
   101  		return err
   102  	}
   103  	b.TailCode = make([]byte, r.Len())
   104  	if _, err := r.Read(b.TailCode); err != nil {
   105  		return fmt.Errorf("can't read TailCode: %v", err)
   106  	}
   107  	Debug("Kernel at %d, %d bytes", b.KernelOffset, len(b.KernelCode))
   108  	b.KernelBase = uintptr(0x100000)
   109  	if b.Header.RamDiskImage == 0 {
   110  		return nil
   111  	}
   112  	if r.Len() != 0 {
   113  		return fmt.Errorf("%d bytes left over", r.Len())
   114  	}
   115  	return nil
   116  }
   117  
   118  // MarshalBinary implements the encoding.BinaryMarshaler interface.
   119  func (b *BzImage) MarshalBinary() ([]byte, error) {
   120  	// First step, make sure we can compress the kernel.
   121  	dat, err := compress(b.KernelCode, "--lzma2=,dict=32MiB")
   122  	if err != nil {
   123  		return nil, err
   124  	}
   125  	dat = append(dat, initRAMFStag[:]...)
   126  	if len(dat) > len(b.compressed) {
   127  		return nil, fmt.Errorf("marshal: compressed KernelCode too big: was %d, now %d", len(b.compressed), len(dat))
   128  	}
   129  	Debug("b.compressed len %#x dat len %#x pad it out", len(b.compressed), len(dat))
   130  	if len(dat) < len(b.compressed) {
   131  		l := len(dat)
   132  		n := make([]byte, len(b.compressed)-4)
   133  		copy(n, dat[:l-4])
   134  		n = append(n, initRAMFStag[:]...)
   135  		dat = n
   136  	}
   137  
   138  	var w bytes.Buffer
   139  	if err := binary.Write(&w, binary.LittleEndian, &b.Header); err != nil {
   140  		return nil, err
   141  	}
   142  	Debug("Wrote %d bytes of header", w.Len())
   143  	if _, err := w.Write(b.BootCode); err != nil {
   144  		return nil, err
   145  	}
   146  	Debug("Wrote %d bytes of BootCode", w.Len())
   147  	if _, err := w.Write(b.HeadCode); err != nil {
   148  		return nil, err
   149  	}
   150  	Debug("Wrote %d bytes of HeadCode", w.Len())
   151  	if _, err := w.Write(dat); err != nil {
   152  		return nil, err
   153  	}
   154  	Debug("Last bytes %#02x", dat[len(dat)-4:])
   155  	Debug("Last bytes %#o", dat[len(dat)-4:])
   156  	Debug("Last bytes %#d", dat[len(dat)-4:])
   157  	b.compressed = dat
   158  	Debug("Wrote %d bytes of Compressed kernel", w.Len())
   159  	if _, err := w.Write(b.TailCode); err != nil {
   160  		return nil, err
   161  	}
   162  	Debug("Wrote %d bytes of header", w.Len())
   163  	Debug("Finished writing, len is now %d bytes", w.Len())
   164  
   165  	return w.Bytes(), nil
   166  }
   167  
   168  // unpack extracts the header code and data from the kernel part
   169  // of the bzImage. It also uncompresses the kernel.
   170  // It searches the Kernel []byte for an xz header. Where it begins
   171  // is never certain. We only do relatively newer images, i.e. we only
   172  // look for the xz magic.
   173  func unpack(d []byte) ([]byte, error) {
   174  	Debug("Kernel is %d bytes", len(d))
   175  	Debug("Some kernel data: %#02x %#02x", d[:32], d[len(d)-8:])
   176  	c := exec.Command("xzcat")
   177  	stdout, err := c.StdoutPipe()
   178  	if err != nil {
   179  		return nil, err
   180  	}
   181  	stderr, err := c.StderrPipe()
   182  	if err != nil {
   183  		return nil, err
   184  	}
   185  	c.Stdin = bytes.NewBuffer(d)
   186  	if err := c.Start(); err != nil {
   187  		return nil, err
   188  	}
   189  
   190  	dat, err := ioutil.ReadAll(stdout)
   191  	if err != nil {
   192  		return nil, err
   193  	}
   194  	// fyi, the xz standard and code are shit. A shame.
   195  	// You can enable this if you have a nasty bug from xz.
   196  	// Just be aware that xz ALWAYS errors out even when nothing is wrong.
   197  	if false {
   198  		if e, err := ioutil.ReadAll(stderr); err != nil || len(e) > 0 {
   199  			Debug("xz stderr: '%s', %v", string(e), err)
   200  		}
   201  	}
   202  	Debug("Uncompressed kernel is %d bytes", len(dat))
   203  	return dat, nil
   204  }
   205  
   206  // compress compresses a []byte via xz using the dictOps, collecting it from stdout
   207  func compress(b []byte, dictOps string) ([]byte, error) {
   208  	Debug("b is %d bytes", len(b))
   209  	c := exec.Command("xz", "--check=crc32", "--x86", dictOps, "--stdout")
   210  	stdout, err := c.StdoutPipe()
   211  	if err != nil {
   212  		return nil, err
   213  	}
   214  	c.Stdin = bytes.NewBuffer(b)
   215  	if err := c.Start(); err != nil {
   216  		return nil, err
   217  	}
   218  
   219  	dat, err := ioutil.ReadAll(stdout)
   220  	if err != nil {
   221  		return nil, err
   222  	}
   223  	if err := c.Wait(); err != nil {
   224  		return nil, err
   225  	}
   226  	Debug("Compressed data is %d bytes, starts with %#02x", len(dat), dat[:32])
   227  	Debug("Last 16 bytes: %#02x", dat[len(dat)-16:])
   228  	return dat, nil
   229  }
   230  
   231  // Extract extracts the KernelCode as an ELF.
   232  func (b *BzImage) ELF() (*elf.File, error) {
   233  	e, err := elf.NewFile(bytes.NewReader(b.KernelCode))
   234  	if err != nil {
   235  		return nil, err
   236  	}
   237  	return e, nil
   238  }
   239  
   240  func Equal(a, b []byte) error {
   241  	if len(a) != len(b) {
   242  		return fmt.Errorf("images differ in len: %d bytes and %d bytes", len(a), len(b))
   243  	}
   244  	var ba BzImage
   245  	if err := ba.UnmarshalBinary(a); err != nil {
   246  		return err
   247  	}
   248  	var bb BzImage
   249  	if err := bb.UnmarshalBinary(b); err != nil {
   250  		return err
   251  	}
   252  	if !reflect.DeepEqual(ba.Header, bb.Header) {
   253  		return fmt.Errorf("headers do not match: %s", ba.Header.Diff(&bb.Header))
   254  	}
   255  	// this is overkill, I can't see any way it can happen.
   256  	if len(ba.KernelCode) != len(bb.KernelCode) {
   257  		return fmt.Errorf("kernel lengths differ: %d vs %d bytes", len(ba.KernelCode), len(bb.KernelCode))
   258  	}
   259  	if len(ba.BootCode) != len(bb.BootCode) {
   260  		return fmt.Errorf("boot code lengths differ: %d vs %d bytes", len(ba.KernelCode), len(bb.KernelCode))
   261  	}
   262  
   263  	if !reflect.DeepEqual(ba.BootCode, bb.BootCode) {
   264  		return fmt.Errorf("boot code does not match")
   265  	}
   266  	if !reflect.DeepEqual(ba.KernelCode, bb.KernelCode) {
   267  		return fmt.Errorf("kernels do not match")
   268  	}
   269  	return nil
   270  }
   271  
   272  func (b *BzImage) AddInitRAMFS(name string) error {
   273  	u, err := ioutil.ReadFile(name)
   274  	if err != nil {
   275  		return err
   276  	}
   277  	// Should we ever want to compress the initramfs this is one
   278  	// way to do it.
   279  	d := u
   280  	if false {
   281  		d, err = compress(u, "--lzma2=,dict=1MiB")
   282  		if err != nil {
   283  			return err
   284  		}
   285  	}
   286  	s, e, err := b.InitRAMFS()
   287  	if err != nil {
   288  		return err
   289  	}
   290  	l := e - s
   291  
   292  	if len(d) > l {
   293  		return fmt.Errorf("new initramfs is %d bytes, won't fit in %d byte old one", len(d), l)
   294  	}
   295  	// Do this in a stupid way that is easy to read.
   296  	// What's interesting: the kernel decompressor, if I read it right,
   297  	// finds it easier to skip a bunch of leading nulls. So do that.
   298  	n := make([]byte, l)
   299  	Debug("Offset into n is %d\n", len(n)-len(d))
   300  	copy(n[len(n)-len(d):], d)
   301  	Debug("Install %d byte initramfs in %d bytes of kernel code, @ %d:%d", len(d), len(n), s, e)
   302  	copy(b.KernelCode[s:e], n)
   303  	return nil
   304  }
   305  
   306  // MakeLinuxHeader marshals a LinuxHeader into a []byte.
   307  func MakeLinuxHeader(h *LinuxHeader) ([]byte, error) {
   308  	buf := new(bytes.Buffer)
   309  	err := binary.Write(buf, binary.LittleEndian, h)
   310  	return buf.Bytes(), err
   311  }
   312  
   313  // Show stringifies a LinuxHeader into a []string
   314  func (h *LinuxHeader) Show() []string {
   315  	var s []string
   316  
   317  	val := reflect.ValueOf(*h)
   318  	for i := 0; i < val.NumField(); i++ {
   319  		v := val.Field(i)
   320  		k := reflect.ValueOf(v).Kind()
   321  		n := val.Type().Field(i).Name
   322  		switch k {
   323  		case reflect.Slice:
   324  			s = append(s, fmt.Sprintf("%s:%#02x", n, v))
   325  		case reflect.Bool:
   326  			s = append(s, fmt.Sprintf("%s:%v", n, v))
   327  		default:
   328  			s = append(s, fmt.Sprintf("%s:%#02x", n, v))
   329  		}
   330  	}
   331  	return s
   332  }
   333  
   334  // Diff is a convenience function that returns a string showing
   335  // differences between a header and another header.
   336  func (h *LinuxHeader) Diff(i *LinuxHeader) string {
   337  	var s string
   338  	hs := h.Show()
   339  	is := i.Show()
   340  	for i := range hs {
   341  		if hs[i] != is[i] {
   342  			s += fmt.Sprintf("%s != %s", hs[i], is[i])
   343  		}
   344  	}
   345  	return s
   346  }
   347  
   348  // Diff is a convenience function that returns a string showing
   349  // differences between a bzImage and another bzImage
   350  func (b *BzImage) Diff(b2 *BzImage) string {
   351  	s := b.Header.Diff(&b2.Header)
   352  	if len(b.BootCode) != len(b2.BootCode) {
   353  		s = s + fmt.Sprintf("b Bootcode is %d; b2 BootCode is %d", len(b.BootCode), len(b2.BootCode))
   354  	}
   355  	if len(b.HeadCode) != len(b2.HeadCode) {
   356  		s = s + fmt.Sprintf("b Headcode is %d; b2 HeadCode is %d", len(b.HeadCode), len(b2.HeadCode))
   357  	}
   358  	if len(b.KernelCode) != len(b2.KernelCode) {
   359  		s = s + fmt.Sprintf("b Kernelcode is %d; b2 KernelCode is %d", len(b.KernelCode), len(b2.KernelCode))
   360  	}
   361  	if len(b.TailCode) != len(b2.TailCode) {
   362  		s = s + fmt.Sprintf("b Tailcode is %d; b2 TailCode is %d", len(b.TailCode), len(b2.TailCode))
   363  	}
   364  	if b.KernelBase != b2.KernelBase {
   365  		s = s + fmt.Sprintf("b KernelBase is %#x; b2 KernelBase is %#x", b.KernelBase, b2.KernelBase)
   366  	}
   367  	if b.KernelOffset != b2.KernelOffset {
   368  		s = s + fmt.Sprintf("b KernelOffset is %#x; b2 KernelOffset is %#x", b.KernelOffset, b2.KernelOffset)
   369  	}
   370  	return s
   371  }
   372  
   373  // String stringifies a LinuxHeader into comma-separated parts
   374  func (h *LinuxHeader) String() string {
   375  	return strings.Join(h.Show(), ",")
   376  }
   377  
   378  // InitRAMFS returns a []byte from KernelCode which can be used to save or replace
   379  // an existing InitRAMFS. The fun part is that there are no symbols; what we do instead
   380  // is find the programs what are RW and look for the cpio magic in them. If we find it,
   381  // we see if it can be read as a cpio and, if so, if there is a /dev or /init inside.
   382  // We repeat until we succeed or there's nothing left.
   383  func (b *BzImage) InitRAMFS() (int, int, error) {
   384  	f, err := b.ELF()
   385  	if err != nil {
   386  		return -1, -1, err
   387  	}
   388  	// Find the program header with RWE.
   389  	var dat []byte
   390  	var prog *elf.Prog
   391  	for _, p := range f.Progs {
   392  		if p.Flags&(elf.PF_X|elf.PF_W|elf.PF_R) == elf.PF_X|elf.PF_W|elf.PF_R {
   393  			dat, err = ioutil.ReadAll(p.Open())
   394  			if err != nil {
   395  				return -1, -1, err
   396  			}
   397  			prog = p
   398  			break
   399  		}
   400  	}
   401  	if dat == nil {
   402  		return -1, -1, fmt.Errorf("can't find an RWE prog in kernel")
   403  	}
   404  
   405  	archiver, err := cpio.Format("newc")
   406  	if err != nil {
   407  		return -1, -1, fmt.Errorf("format newc not supported: %v", err)
   408  	}
   409  	var cur int
   410  	for cur < len(dat) {
   411  		x := bytes.Index(dat, []byte("070701"))
   412  		if x == -1 {
   413  			return -1, -1, fmt.Errorf("no newc cpio magic found")
   414  		}
   415  		if err != nil {
   416  			return -1, -1, err
   417  		}
   418  		cur = x
   419  		var r = bytes.NewReader(dat[cur:])
   420  		rr := archiver.Reader(r)
   421  		Debug("r.Len is %v", r.Len())
   422  		var found bool
   423  		var size int
   424  		for {
   425  			rec, err := rr.ReadRecord()
   426  			Debug("Check %v", rec)
   427  			if err == io.EOF {
   428  				break
   429  			}
   430  			if err != nil {
   431  				Debug("error reading records: %v", err)
   432  				break
   433  			}
   434  			switch rec.Name {
   435  			case "init", "dev", "bin", "usr":
   436  				Debug("Found initramfs at %d, %d bytes", cur, len(dat)-r.Len())
   437  				found = true
   438  			}
   439  			size = int(rec.FilePos) + int(rec.FileSize)
   440  		}
   441  		Debug("Size is %d", size)
   442  		// Add the trailer size.
   443  		y := x + size
   444  		if found {
   445  			// The slice consists of the bytes for cur to the length of initramfs.
   446  			// We can derive the initramfs length by knowing how much is left of the reader.
   447  			Debug("Return %d %#x slice %d:%d from %d byte dat", len(dat[x:y]), len(dat[x:y]), cur, y, len(dat))
   448  			x += int(prog.Off)
   449  			y += int(prog.Off)
   450  			// We need to round y up to the end of the record. We have to do this after we
   451  			// add the prog.Off value to it.
   452  			y = (y + 3) &^ 3
   453  			// and add the size of the trailer record.
   454  			y += 120
   455  			y += 4 // and add at least one word of null
   456  			y = (y + 3) &^ 3
   457  			Debug("InitRAMFS: return %d, %d", x, y)
   458  			return x, y, nil
   459  		}
   460  		cur += 6
   461  	}
   462  	return -1, -1, fmt.Errorf("no cpio found")
   463  }