github.com/notti/go-dynamic@v0.0.0-20190619201224-fc443047424c/relink/relink.go (about)

     1  // [not needed] Relink rewrites static (go) binaries to include interp and use symbols from dynamic libraries.
     2  //
     3  // Symbols must be named <symbol name>__dynload
     4  package main
     5  
     6  import (
     7  	"bytes"
     8  	"debug/elf"
     9  	"encoding/binary"
    10  	"errors"
    11  	"flag"
    12  	"io"
    13  	"log"
    14  	"os"
    15  	"sort"
    16  	"strings"
    17  )
    18  
    19  type def struct {
    20  	interp string
    21  	libs   []string
    22  }
    23  
    24  var defaults = map[string]def{
    25  	"amd64_linux": def{
    26  		interp: "/lib64/ld-linux-x86-64.so.2",
    27  		libs:   []string{"libc.so.6", "libpthread.so", "libdl.so"},
    28  	},
    29  	"386_linux": def{
    30  		interp: "/lib/ld-linux.so.2",
    31  		libs:   []string{"libc.so.6", "libpthread.so", "libdl.so"},
    32  	},
    33  }
    34  
    35  type elfFile struct {
    36  	f         *os.File
    37  	e         *elf.File
    38  	phoff     uint64
    39  	phentsize uint64
    40  	shoff     uint64
    41  	shentsize uint64
    42  	shstrndx  uint64
    43  	buffer    [1024]byte
    44  }
    45  
    46  func openElfFile(name string) (*elfFile, error) {
    47  	ret := &elfFile{}
    48  	var err error
    49  	ret.f, err = os.OpenFile(name, os.O_RDWR, 0777)
    50  	if err != nil {
    51  		return nil, err
    52  	}
    53  	ret.e, err = elf.NewFile(ret.f)
    54  	if err != nil {
    55  		return nil, err
    56  	}
    57  
    58  	_, err = ret.Seek(0, io.SeekCurrent)
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  
    63  	switch ret.e.Class {
    64  	case elf.ELFCLASS32:
    65  		var hdr elf.Header32
    66  		err = ret.ReadData(&hdr)
    67  		if err != nil {
    68  			return nil, err
    69  		}
    70  		ret.phoff = uint64(hdr.Phoff)
    71  		ret.phentsize = uint64(hdr.Phentsize)
    72  		ret.shoff = uint64(hdr.Shoff)
    73  		ret.shentsize = uint64(hdr.Shentsize)
    74  		ret.shstrndx = uint64(hdr.Shstrndx)
    75  	case elf.ELFCLASS64:
    76  		var hdr elf.Header64
    77  		err = ret.ReadData(&hdr)
    78  		if err != nil {
    79  			return nil, err
    80  		}
    81  		ret.phoff = hdr.Phoff
    82  		ret.phentsize = uint64(hdr.Phentsize)
    83  		ret.shoff = hdr.Shoff
    84  		ret.shentsize = uint64(hdr.Shentsize)
    85  		ret.shstrndx = uint64(hdr.Shstrndx)
    86  	}
    87  
    88  	return ret, nil
    89  }
    90  
    91  func (e *elfFile) Seek(where int64, whence int) (int64, error) {
    92  	return e.f.Seek(where, whence)
    93  }
    94  
    95  func (e *elfFile) ReadData(data interface{}) error {
    96  	return binary.Read(e.f, e.e.ByteOrder, data)
    97  }
    98  
    99  func (e *elfFile) WriteData(data interface{}) error {
   100  	return binary.Write(e.f, e.e.ByteOrder, data)
   101  }
   102  
   103  func (e *elfFile) Write(b []byte) error {
   104  	_, err := e.f.Write(b)
   105  	return err
   106  }
   107  
   108  func (e *elfFile) WriteAt(b []byte, off uint64) error {
   109  	_, err := e.f.WriteAt(b, int64(off))
   110  	return err
   111  }
   112  
   113  func (e *elfFile) Read8() (uint8, error) {
   114  	_, err := e.f.Read(e.buffer[:1])
   115  	return e.buffer[0], err
   116  }
   117  
   118  func (e *elfFile) Read16() (uint16, error) {
   119  	_, err := e.f.Read(e.buffer[:2])
   120  	return e.e.ByteOrder.Uint16(e.buffer[:]), err
   121  }
   122  
   123  func (e *elfFile) Read32() (uint32, error) {
   124  	_, err := e.f.Read(e.buffer[:4])
   125  	return e.e.ByteOrder.Uint32(e.buffer[:]), err
   126  }
   127  
   128  func (e *elfFile) Read64() (uint64, error) {
   129  	_, err := e.f.Read(e.buffer[:8])
   130  	return e.e.ByteOrder.Uint64(e.buffer[:]), err
   131  }
   132  
   133  func (e *elfFile) Read8At(where int64) (uint8, error) {
   134  	_, err := e.f.ReadAt(e.buffer[:1], where)
   135  	return e.buffer[0], err
   136  }
   137  
   138  func (e *elfFile) Read16At(where int64) (uint16, error) {
   139  	_, err := e.f.ReadAt(e.buffer[:2], where)
   140  	return e.e.ByteOrder.Uint16(e.buffer[:]), err
   141  }
   142  
   143  func (e *elfFile) Read32At(where int64) (uint32, error) {
   144  	_, err := e.f.ReadAt(e.buffer[:4], where)
   145  	return e.e.ByteOrder.Uint32(e.buffer[:]), err
   146  }
   147  
   148  func (e *elfFile) Read64At(where int64) (uint64, error) {
   149  	_, err := e.f.ReadAt(e.buffer[:8], where)
   150  	return e.e.ByteOrder.Uint64(e.buffer[:]), err
   151  }
   152  
   153  func (e *elfFile) Write8(data uint8) error {
   154  	_, err := e.f.Write([]byte{data})
   155  	return err
   156  }
   157  
   158  func (e *elfFile) Write16(data uint16) error {
   159  	e.e.ByteOrder.PutUint16(e.buffer[:], data)
   160  	_, err := e.f.Write(e.buffer[:2])
   161  	return err
   162  }
   163  
   164  func (e *elfFile) Write32(data uint32) error {
   165  	e.e.ByteOrder.PutUint32(e.buffer[:], data)
   166  	_, err := e.f.Write(e.buffer[:4])
   167  	return err
   168  }
   169  
   170  func (e *elfFile) Write64(data uint64) error {
   171  	e.e.ByteOrder.PutUint64(e.buffer[:], data)
   172  	_, err := e.f.Write(e.buffer[:8])
   173  	return err
   174  }
   175  
   176  func (e *elfFile) Write8At(data uint8, where uint64) error {
   177  	return e.WriteAt([]byte{data}, where)
   178  }
   179  
   180  func (e *elfFile) Write16At(data uint16, where uint64) error {
   181  	e.e.ByteOrder.PutUint16(e.buffer[:], data)
   182  	return e.WriteAt(e.buffer[:2], where)
   183  }
   184  
   185  func (e *elfFile) Write32At(data uint32, where uint64) error {
   186  	e.e.ByteOrder.PutUint32(e.buffer[:], data)
   187  	return e.WriteAt(e.buffer[:4], where)
   188  }
   189  
   190  func (e *elfFile) Write64At(data uint64, where uint64) error {
   191  	e.e.ByteOrder.PutUint64(e.buffer[:], data)
   192  	return e.WriteAt(e.buffer[:8], where)
   193  }
   194  
   195  func (e *elfFile) WriteSections() error {
   196  	if _, err := e.f.Seek(int64(e.shoff), io.SeekStart); err != nil {
   197  		return err
   198  	}
   199  	shstrtab := 0
   200  	shstrtabpos := e.shoff
   201  	names := make([]uint64, len(e.e.Sections))
   202  	for i, sec := range e.e.Sections {
   203  		names[i] = e.shoff - shstrtabpos
   204  		if err := e.Write(append([]byte(sec.Name), 0)); err != nil {
   205  			return err
   206  		}
   207  		e.shoff += uint64(len(sec.Name)) + 1
   208  		if sec.Name == ".shstrtab" {
   209  			shstrtab = i
   210  		}
   211  	}
   212  	e.e.Sections[shstrtab].Offset = shstrtabpos
   213  	e.e.Sections[shstrtab].FileSize = e.shoff - shstrtabpos
   214  
   215  	for i, sec := range e.e.Sections {
   216  		if err := e.WriteSection(sec.SectionHeader, names[i]); err != nil {
   217  			return err
   218  		}
   219  	}
   220  	switch e.e.Class {
   221  	case elf.ELFCLASS32:
   222  		if err := e.Write32At(uint32(e.shoff), 0x20); err != nil {
   223  			return err
   224  		}
   225  		if err := e.Write16At(uint16(len(e.e.Sections)), 0x30); err != nil {
   226  			return err
   227  		}
   228  	case elf.ELFCLASS64:
   229  		if err := e.Write64At(e.shoff, 0x28); err != nil {
   230  			return err
   231  		}
   232  		if err := e.Write16At(uint16(len(e.e.Sections)), 0x3C); err != nil {
   233  			return err
   234  		}
   235  	}
   236  	return nil
   237  }
   238  
   239  func (e *elfFile) WriteSection(sh elf.SectionHeader, name uint64) error {
   240  	switch e.e.Class {
   241  	case elf.ELFCLASS32:
   242  		hdr := elf.Section32{
   243  			Name:      uint32(name),
   244  			Type:      uint32(sh.Type),
   245  			Flags:     uint32(sh.Flags),
   246  			Addr:      uint32(sh.Addr),
   247  			Off:       uint32(sh.Offset),
   248  			Size:      uint32(sh.FileSize),
   249  			Link:      sh.Link,
   250  			Info:      sh.Info,
   251  			Addralign: uint32(sh.Addralign),
   252  			Entsize:   uint32(sh.Entsize),
   253  		}
   254  		return e.WriteData(hdr)
   255  	case elf.ELFCLASS64:
   256  		hdr := elf.Section64{
   257  			Name:      uint32(name),
   258  			Type:      uint32(sh.Type),
   259  			Flags:     uint64(sh.Flags),
   260  			Addr:      sh.Addr,
   261  			Off:       sh.Offset,
   262  			Size:      sh.FileSize,
   263  			Link:      sh.Link,
   264  			Info:      sh.Info,
   265  			Addralign: sh.Addralign,
   266  			Entsize:   sh.Entsize,
   267  		}
   268  		return e.WriteData(hdr)
   269  	}
   270  	// compression header not handeled
   271  	return errors.New("Unknown elf bit size")
   272  }
   273  
   274  func (e *elfFile) WritePrograms() error {
   275  	if _, err := e.Seek(int64(e.phoff), io.SeekStart); err != nil {
   276  		return err
   277  	}
   278  	for _, prog := range e.e.Progs {
   279  		if err := e.WriteProgram(prog.ProgHeader); err != nil {
   280  			return err
   281  		}
   282  	}
   283  	switch e.e.Class {
   284  	case elf.ELFCLASS32:
   285  		if err := e.Write32At(uint32(e.phoff), 0x1C); err != nil {
   286  			return err
   287  		}
   288  		if err := e.Write16At(uint16(len(e.e.Progs)), 0x2C); err != nil {
   289  			return err
   290  		}
   291  	case elf.ELFCLASS64:
   292  		if err := e.Write64At(e.phoff, 0x20); err != nil {
   293  			return err
   294  		}
   295  		if err := e.Write16At(uint16(len(e.e.Progs)), 0x38); err != nil {
   296  			return err
   297  		}
   298  	}
   299  	return nil
   300  }
   301  
   302  func (e *elfFile) WriteProgram(ph elf.ProgHeader) error {
   303  	switch e.e.Class {
   304  	case elf.ELFCLASS32:
   305  		hdr := elf.Prog32{
   306  			Type:   uint32(ph.Type),
   307  			Flags:  uint32(ph.Flags),
   308  			Off:    uint32(ph.Off),
   309  			Vaddr:  uint32(ph.Vaddr),
   310  			Paddr:  uint32(ph.Paddr),
   311  			Filesz: uint32(ph.Filesz),
   312  			Memsz:  uint32(ph.Memsz),
   313  			Align:  uint32(ph.Align),
   314  		}
   315  		return e.WriteData(hdr)
   316  	case elf.ELFCLASS64:
   317  		hdr := elf.Prog64{
   318  			Type:   uint32(ph.Type),
   319  			Flags:  uint32(ph.Flags),
   320  			Off:    ph.Off,
   321  			Vaddr:  ph.Vaddr,
   322  			Paddr:  ph.Paddr,
   323  			Filesz: ph.Filesz,
   324  			Memsz:  ph.Memsz,
   325  			Align:  ph.Align,
   326  		}
   327  		return e.WriteData(hdr)
   328  	}
   329  	return errors.New("Unknown elf bit size")
   330  }
   331  
   332  func (e *elfFile) Read(b []byte) error {
   333  	_, err := e.f.Read(b)
   334  	return err
   335  }
   336  
   337  func (e *elfFile) Copy(src, dst int64, length int) error {
   338  	buffer := make([]byte, length)
   339  	_, err := e.Seek(src, io.SeekStart)
   340  	if err != nil {
   341  		return err
   342  	}
   343  	err = e.Read(buffer)
   344  	if err != nil {
   345  		return err
   346  	}
   347  	_, err = e.Seek(dst, io.SeekStart)
   348  	if err != nil {
   349  		return err
   350  	}
   351  	err = e.Write(buffer)
   352  	if err != nil {
   353  		return err
   354  	}
   355  	return nil
   356  }
   357  
   358  func (e *elfFile) Close() error {
   359  	return e.f.Close()
   360  }
   361  
   362  // Dyn contains a single entry of the dynamic table
   363  type Dyn struct {
   364  	Tag elf.DynTag
   365  	Val uint64
   366  }
   367  
   368  func padding(addr, align uint64) uint64 {
   369  	align1 := align - 1
   370  	return (align - (addr & align1)) & align1
   371  }
   372  
   373  // DynSymbol represents a dynamic symbol
   374  type DynSymbol struct {
   375  	Name    string
   376  	Value   uint64
   377  	Size    uint64
   378  	Bind    elf.SymBind
   379  	Type    elf.SymType
   380  	Vis     elf.SymVis
   381  	Section int
   382  }
   383  
   384  func (e *elfFile) makeDynsym(elements []DynSymbol) (dynsym, dynstr []byte) {
   385  	sym := &bytes.Buffer{}
   386  	str := &bytes.Buffer{}
   387  	for _, elem := range elements {
   388  		namei := str.Len()
   389  		str.Write(append([]byte(elem.Name), 0))
   390  		switch e.e.Class {
   391  		case elf.ELFCLASS32:
   392  			binary.Write(sym, e.e.ByteOrder, elf.Sym32{
   393  				Name:  uint32(namei),
   394  				Value: uint32(elem.Value),
   395  				Size:  uint32(elem.Size),
   396  				Info:  byte(elem.Bind)<<4 | byte(elem.Type)&0x0f,
   397  				Other: byte(elem.Vis) & 0x03,
   398  				Shndx: uint16(elem.Section),
   399  			})
   400  		case elf.ELFCLASS64:
   401  			binary.Write(sym, e.e.ByteOrder, elf.Sym64{
   402  				Name:  uint32(namei),
   403  				Value: uint64(elem.Value),
   404  				Size:  uint64(elem.Size),
   405  				Info:  byte(elem.Bind)<<4 | byte(elem.Type)&0x0f,
   406  				Other: byte(elem.Vis) & 0x03,
   407  				Shndx: uint16(elem.Section),
   408  			})
   409  		}
   410  	}
   411  	if str.Len() == 0 {
   412  		str.WriteByte(0)
   413  	}
   414  	return sym.Bytes(), str.Bytes()
   415  }
   416  
   417  func (e *elfFile) makeDynsec(elements []Dyn) []byte {
   418  	ret := &bytes.Buffer{}
   419  	switch e.e.Class {
   420  	case elf.ELFCLASS32:
   421  		var secs []elf.Dyn32
   422  		for _, sec := range elements {
   423  			secs = append(secs, elf.Dyn32{
   424  				Tag: int32(sec.Tag),
   425  				Val: uint32(sec.Val),
   426  			})
   427  		}
   428  		binary.Write(ret, e.e.ByteOrder, secs)
   429  	case elf.ELFCLASS64:
   430  		var secs []elf.Dyn64
   431  		for _, sec := range elements {
   432  			secs = append(secs, elf.Dyn64{
   433  				Tag: int64(sec.Tag),
   434  				Val: uint64(sec.Val),
   435  			})
   436  		}
   437  		binary.Write(ret, e.e.ByteOrder, secs)
   438  	}
   439  	return ret.Bytes()
   440  }
   441  
   442  // RelSymbol represents a symbol in need of relocation
   443  type RelSymbol struct {
   444  	Off   uint64
   445  	SymNo uint64
   446  }
   447  
   448  func (e *elfFile) makeDynRel(symbols []RelSymbol) ([]byte, bool, uint64) {
   449  	ret := &bytes.Buffer{}
   450  	var rela bool
   451  	var relt uint64
   452  	switch e.e.Machine {
   453  	case elf.EM_386:
   454  		rela = false
   455  		relt = uint64(elf.R_386_JMP_SLOT)
   456  	case elf.EM_X86_64:
   457  		rela = true
   458  		relt = uint64(elf.R_X86_64_JMP_SLOT)
   459  	default:
   460  		log.Fatal("Unknown machine type ", e.e.Machine)
   461  	}
   462  
   463  	var relsz uint64
   464  
   465  	switch e.e.Class {
   466  	case elf.ELFCLASS32:
   467  		if rela {
   468  			for _, symbol := range symbols {
   469  				binary.Write(ret, e.e.ByteOrder, elf.Rela32{
   470  					Off:  uint32(symbol.Off),
   471  					Info: uint32(symbol.SymNo<<8 | relt),
   472  				})
   473  			}
   474  			relsz = 12
   475  		} else {
   476  			for _, symbol := range symbols {
   477  
   478  				binary.Write(ret, e.e.ByteOrder, elf.Rel32{
   479  					Off:  uint32(symbol.Off),
   480  					Info: uint32(symbol.SymNo<<8 | relt),
   481  				})
   482  			}
   483  			relsz = 8
   484  		}
   485  	case elf.ELFCLASS64:
   486  		if rela {
   487  			for _, symbol := range symbols {
   488  				binary.Write(ret, e.e.ByteOrder, elf.Rela64{
   489  					Off:  symbol.Off,
   490  					Info: symbol.SymNo<<32 | relt,
   491  				})
   492  			}
   493  			relsz = 24
   494  		} else {
   495  			for _, symbol := range symbols {
   496  				binary.Write(ret, e.e.ByteOrder, elf.Rel64{
   497  					Off:  symbol.Off,
   498  					Info: symbol.SymNo<<32 | relt,
   499  				})
   500  			}
   501  			relsz = 16
   502  		}
   503  	}
   504  	return ret.Bytes(), rela, relsz
   505  }
   506  
   507  type cgoSymbol struct {
   508  	v uint64
   509  	f uint64
   510  	s uint64
   511  }
   512  
   513  func main() {
   514  	libsArg := flag.String("libs", "", "Load comma separated list of given libs instead of defaults")
   515  	interp := flag.String("interp", "", "Use given interp instead of default")
   516  
   517  	flag.Parse()
   518  
   519  	if len(flag.Args()) != 1 {
   520  		log.Fatal("Need a static golang binary as argument")
   521  	}
   522  
   523  	libs := strings.Split(*libsArg, ",")
   524  
   525  	f, err := openElfFile(flag.Args()[0])
   526  	if err != nil {
   527  		log.Fatal(err)
   528  	}
   529  
   530  	/*
   531  		        KEEP EXEC (we are not dyn after all)
   532  		        try to put new program headers, dyn, interp into first 4k
   533  		        0  -+-----------------------------------+--
   534  		            | ELF                               |
   535  		            +-----------------------------------+
   536  		            | program headers                   |
   537  		            +-----------------------------------+
   538  		            | interp                            |
   539  		            +-----------------------------------+
   540  		should be   | dyn stuff                         |
   541  		 below 4k ->+-----------------------------------+
   542  		            | other stuff that needs relocation |
   543  		            +-----------------------------------+<- ensure mapping until here
   544  		            +-----------------------------------+
   545  		   entry -> | Everything else (e.g., text)      |
   546  		            +-----------------------------------+
   547  		            | .shstrtab                         |
   548  		            +-----------------------------------+
   549  		            | Section headers                   |
   550  		            +-----------------------------------+
   551  	*/
   552  
   553  	// First some sanity checks - and checks if we can do our meddling, after all we don't support everything in this POC
   554  
   555  	symbolList, err := f.e.Symbols()
   556  	if err != nil {
   557  		log.Fatal(err)
   558  	}
   559  
   560  	for _, sym := range symbolList {
   561  		if strings.HasSuffix(sym.Name, "nocgo.prelinked") {
   562  			log.Println("relink not needed")
   563  			return
   564  		}
   565  	}
   566  
   567  	if f.e.Type != elf.ET_EXEC {
   568  		log.Fatal("only static binaries not using an interp supported")
   569  	}
   570  
   571  	var base uint64
   572  	var baseProg int
   573  
   574  	for i, prog := range f.e.Progs {
   575  		if prog.Type == elf.PT_INTERP || prog.Type == elf.PT_DYNAMIC {
   576  			log.Fatal("only static binaries not using an interp supported")
   577  		}
   578  		if prog.Type == elf.PT_LOAD {
   579  			if base == 0 {
   580  				base = prog.Vaddr
   581  				baseProg = i
   582  			} else if prog.Vaddr < base {
   583  				base = prog.Vaddr
   584  				baseProg = i
   585  			}
   586  		}
   587  	}
   588  
   589  	if uint64(f.phoff+f.phentsize*uint64(len(f.e.Progs))) > f.e.Entry {
   590  		log.Fatal("Not enough space before entry point")
   591  	}
   592  
   593  	for _, sym := range symbolList {
   594  		if strings.HasPrefix(sym.Name, "_rt0_") {
   595  			if d, ok := defaults[sym.Name[5:]]; ok {
   596  				libs = d.libs
   597  				*interp = d.interp
   598  				break
   599  			}
   600  		}
   601  	}
   602  
   603  	interpProg := len(f.e.Progs)
   604  
   605  	f.e.Progs = append(f.e.Progs, &elf.Prog{
   606  		ProgHeader: elf.ProgHeader{
   607  			Type:   elf.PT_INTERP,
   608  			Flags:  elf.PF_R,
   609  			Off:    0, // fill later
   610  			Vaddr:  0, // fill later
   611  			Paddr:  0, // fill later
   612  			Filesz: 0, // fill later
   613  			Memsz:  0, // fill later
   614  			Align:  1,
   615  		}})
   616  
   617  	dynsecProg := len(f.e.Progs)
   618  
   619  	f.e.Progs = append(f.e.Progs, &elf.Prog{
   620  		ProgHeader: elf.ProgHeader{
   621  			Type:   elf.PT_DYNAMIC,
   622  			Flags:  elf.PF_R | elf.PF_W,
   623  			Off:    0, // fill later
   624  			Vaddr:  0, // fill later
   625  			Paddr:  0, // fill later
   626  			Filesz: 0, // fill later
   627  			Memsz:  0, // fill later
   628  			Align:  8,
   629  		}})
   630  
   631  	interpPos := f.phoff + f.phentsize*uint64(len(f.e.Progs))
   632  	interpB := append([]byte(*interp), 0)
   633  	interpLen := uint64(len(interpB))
   634  
   635  	f.e.Progs[interpProg].Off = interpPos
   636  	f.e.Progs[interpProg].Vaddr = interpPos + base
   637  	f.e.Progs[interpProg].Paddr = interpPos + base
   638  	f.e.Progs[interpProg].Filesz = interpLen
   639  	f.e.Progs[interpProg].Memsz = interpLen
   640  
   641  	hashPos := interpPos + interpLen
   642  	hashPos += padding(hashPos, 8)
   643  	hash := make([]byte, 8*4) // Empty 64bit DT_HASH
   644  	hashLen := uint64(len(hash))
   645  
   646  	var relList []RelSymbol
   647  
   648  	var symsection int
   649  
   650  	var symdefs []DynSymbol
   651  
   652  	symdefs = append(symdefs, DynSymbol{
   653  		Name:    "",
   654  		Value:   0,
   655  		Size:    0,
   656  		Bind:    elf.STB_LOCAL,
   657  		Type:    elf.STT_NOTYPE,
   658  		Vis:     elf.STV_DEFAULT,
   659  		Section: int(elf.SHN_UNDEF),
   660  	})
   661  
   662  	for _, sym := range symbolList {
   663  		if strings.HasSuffix(sym.Name, "__dynload") {
   664  			parts := strings.Split(sym.Name, ".")
   665  			name := parts[len(parts)-1]
   666  			dynsym := name[:len(name)-9]
   667  
   668  			symsection = int(sym.Section)
   669  			relList = append(relList, RelSymbol{
   670  				Off:   sym.Value,
   671  				SymNo: uint64(len(symdefs)),
   672  			})
   673  			symdefs = append(symdefs, DynSymbol{
   674  				Name:    dynsym,
   675  				Value:   0,
   676  				Size:    0,
   677  				Bind:    elf.STB_GLOBAL,
   678  				Type:    elf.STT_FUNC,
   679  				Vis:     elf.STV_DEFAULT,
   680  				Section: int(elf.SHN_UNDEF),
   681  			})
   682  		}
   683  	}
   684  
   685  	dynsym, dynstr := f.makeDynsym(symdefs)
   686  
   687  	var libOffsets []uint64
   688  
   689  	for _, l := range libs {
   690  		libOffsets = append(libOffsets, uint64(len(dynstr)))
   691  		dynstr = append(dynstr, []byte(l)...)
   692  		dynstr = append(dynstr, 0)
   693  	}
   694  
   695  	dynsymLocal := 0
   696  	dynstrPos := hashPos + hashLen
   697  	dynstrLen := uint64(len(dynstr))
   698  
   699  	dynsymPos := dynstrPos + dynstrLen
   700  	dynsymPos += padding(dynsymPos, 8)
   701  	dynsymLen := uint64(len(dynsym))
   702  
   703  	// TODO: DT_BIND_NOW?
   704  
   705  	dynrel, rela, relsz := f.makeDynRel(relList)
   706  	dynrelPos := dynsymPos + dynsymLen
   707  	dynrelPos += padding(dynrelPos, 8)
   708  	dynrelLen := uint64(len(dynrel))
   709  
   710  	var dynsecs []Dyn
   711  	for _, offset := range libOffsets {
   712  		dynsecs = append(dynsecs, Dyn{Tag: elf.DT_NEEDED, Val: uint64(offset)})
   713  	}
   714  
   715  	if rela {
   716  		dynsecs = append(dynsecs, Dyn{Tag: elf.DT_RELA, Val: uint64(base + dynrelPos)})
   717  		dynsecs = append(dynsecs, Dyn{Tag: elf.DT_RELASZ, Val: uint64(dynrelLen)})
   718  		dynsecs = append(dynsecs, Dyn{Tag: elf.DT_RELAENT, Val: uint64(relsz)})
   719  	} else {
   720  		dynsecs = append(dynsecs, Dyn{Tag: elf.DT_REL, Val: uint64(base + dynrelPos)})
   721  		dynsecs = append(dynsecs, Dyn{Tag: elf.DT_RELSZ, Val: uint64(dynrelLen)})
   722  		dynsecs = append(dynsecs, Dyn{Tag: elf.DT_RELENT, Val: uint64(relsz)})
   723  	}
   724  
   725  	dynsecs = append(dynsecs, []Dyn{
   726  		{Tag: elf.DT_STRTAB, Val: base + dynstrPos},
   727  		{Tag: elf.DT_STRSZ, Val: dynstrLen},
   728  		{Tag: elf.DT_SYMTAB, Val: base + dynsymPos},
   729  		{Tag: elf.DT_SYMENT, Val: dynsymLen},
   730  		{Tag: elf.DT_HASH, Val: hashPos + base},
   731  		{Tag: elf.DT_BIND_NOW, Val: 0},
   732  		{Tag: elf.DT_NULL, Val: 0},
   733  	}...)
   734  
   735  	dynsec := f.makeDynsec(dynsecs)
   736  	dynsecPos := dynrelPos + dynrelLen
   737  	dynsecPos += padding(dynsecPos, 8)
   738  	dynsecLen := uint64(len(dynsec))
   739  
   740  	f.e.Progs[dynsecProg].Off = dynsecPos
   741  	f.e.Progs[dynsecProg].Vaddr = dynsecPos + base
   742  	f.e.Progs[dynsecProg].Paddr = dynsecPos + base
   743  	f.e.Progs[dynsecProg].Filesz = dynsecLen
   744  	f.e.Progs[dynsecProg].Memsz = dynsecLen
   745  
   746  	afterDynsec := dynsecPos + dynsecLen
   747  
   748  	relPos := afterDynsec
   749  	var torelocate []*elf.Section
   750  	relocated := make(map[int]bool)
   751  
   752  	for {
   753  		var newRelocate []*elf.Section
   754  		for i, sec := range f.e.Sections {
   755  			if sec.Type == elf.SHT_NULL {
   756  				continue
   757  			}
   758  			if sec.Offset < relPos && !relocated[i] {
   759  				newRelocate = append(newRelocate, sec)
   760  				relocated[i] = true
   761  			}
   762  		}
   763  		if len(newRelocate) == 0 {
   764  			break
   765  		}
   766  		torelocate = append(torelocate, newRelocate...)
   767  
   768  		sort.Slice(torelocate, func(i, j int) bool { return torelocate[i].Offset < torelocate[j].Offset })
   769  		relPos = afterDynsec
   770  		for _, sec := range torelocate {
   771  			relPos += sec.Size
   772  			if sec.Addralign > 1 {
   773  				relPos += padding(relPos, sec.Addralign)
   774  			}
   775  		}
   776  	}
   777  
   778  	for _, sec := range torelocate {
   779  		data := make([]byte, sec.Size)
   780  		if _, err := f.f.ReadAt(data, int64(sec.Offset)); err != nil {
   781  			log.Fatal(err)
   782  		}
   783  		if sec.Addralign > 1 {
   784  			afterDynsec += padding(afterDynsec, sec.Addralign)
   785  		}
   786  		if err := f.WriteAt(data, afterDynsec); err != nil {
   787  			log.Fatal(err)
   788  		}
   789  		for _, prog := range f.e.Progs {
   790  			if prog.Off == sec.Offset {
   791  				prog.Off = afterDynsec
   792  			}
   793  			if prog.Vaddr == sec.Offset+base {
   794  				prog.Vaddr = afterDynsec + base
   795  				prog.Paddr = afterDynsec + base
   796  			}
   797  		}
   798  
   799  		sec.Addr += afterDynsec - sec.Offset // or base + offset
   800  		sec.Offset, afterDynsec = afterDynsec, afterDynsec+sec.Offset
   801  	}
   802  
   803  	if afterDynsec > f.e.Entry {
   804  		log.Fatal("not enough space before entry point")
   805  	}
   806  
   807  	if f.e.Progs[baseProg].Filesz < afterDynsec {
   808  		f.e.Progs[baseProg].Filesz = afterDynsec
   809  		f.e.Progs[baseProg].Memsz = afterDynsec
   810  	}
   811  
   812  	if err := f.WritePrograms(); err != nil {
   813  		log.Fatal(err)
   814  	}
   815  
   816  	if err := f.WriteAt(interpB, interpPos); err != nil {
   817  		log.Fatal(err)
   818  	}
   819  
   820  	if err := f.WriteAt(hash, hashPos); err != nil {
   821  		log.Fatal(err)
   822  	}
   823  
   824  	if err := f.WriteAt(dynstr, dynstrPos); err != nil {
   825  		log.Fatal(err)
   826  	}
   827  
   828  	if err := f.WriteAt(dynsym, dynsymPos); err != nil {
   829  		log.Fatal(err)
   830  	}
   831  
   832  	if err := f.WriteAt(dynrel, dynrelPos); err != nil {
   833  		log.Fatal(err)
   834  	}
   835  
   836  	if err := f.WriteAt(dynsec, dynsecPos); err != nil {
   837  		log.Fatal(err)
   838  	}
   839  
   840  	f.e.Sections = append(f.e.Sections, &elf.Section{
   841  		SectionHeader: elf.SectionHeader{
   842  			Name:      ".interp",
   843  			Type:      elf.SHT_PROGBITS,
   844  			Flags:     elf.SHF_ALLOC,
   845  			Addr:      base + interpPos,
   846  			Offset:    interpPos,
   847  			FileSize:  interpLen,
   848  			Addralign: 1,
   849  		}})
   850  
   851  	dynstrI := len(f.e.Sections)
   852  
   853  	f.e.Sections = append(f.e.Sections, &elf.Section{
   854  		SectionHeader: elf.SectionHeader{
   855  			Name:      ".dynstr",
   856  			Type:      elf.SHT_STRTAB,
   857  			Flags:     elf.SHF_ALLOC,
   858  			Addr:      base + dynstrPos,
   859  			Offset:    dynstrPos,
   860  			FileSize:  dynstrLen,
   861  			Addralign: 1,
   862  		}})
   863  
   864  	entSize := uint64(24)
   865  	if f.e.Class == elf.ELFCLASS32 {
   866  		entSize = 16
   867  	}
   868  
   869  	dynsymSec := len(f.e.Sections)
   870  
   871  	f.e.Sections = append(f.e.Sections, &elf.Section{
   872  		SectionHeader: elf.SectionHeader{
   873  			Name:      ".dynsym",
   874  			Type:      elf.SHT_DYNSYM,
   875  			Flags:     elf.SHF_ALLOC,
   876  			Addr:      base + dynsymPos,
   877  			Offset:    dynsymPos,
   878  			FileSize:  dynsymLen,
   879  			Addralign: 8,
   880  			Link:      uint32(dynstrI),
   881  			Entsize:   entSize,
   882  			Info:      uint32(dynsymLocal + 1),
   883  		}})
   884  
   885  	entSize = uint64(16)
   886  	if f.e.Class == elf.ELFCLASS32 {
   887  		entSize = 8
   888  	}
   889  
   890  	f.e.Sections = append(f.e.Sections, &elf.Section{
   891  		SectionHeader: elf.SectionHeader{
   892  			Name:      ".dynamic",
   893  			Type:      elf.SHT_DYNAMIC,
   894  			Flags:     elf.SHF_ALLOC | elf.SHF_WRITE,
   895  			Addr:      base + dynsecPos,
   896  			Offset:    dynsecPos,
   897  			FileSize:  dynsecLen,
   898  			Addralign: 8,
   899  			Link:      uint32(dynstrI),
   900  			Entsize:   entSize,
   901  		}})
   902  
   903  	dynname := ".rel"
   904  	if rela {
   905  		dynname = ".rela"
   906  	}
   907  	dynname += f.e.Sections[symsection].Name
   908  
   909  	shtype := elf.SHT_REL
   910  	if rela {
   911  		shtype = elf.SHT_RELA
   912  	}
   913  
   914  	f.e.Sections = append(f.e.Sections, &elf.Section{
   915  		SectionHeader: elf.SectionHeader{
   916  			Name:      dynname,
   917  			Type:      shtype,
   918  			Flags:     elf.SHF_ALLOC,
   919  			Addr:      base + dynrelPos,
   920  			Offset:    dynrelPos,
   921  			FileSize:  dynrelLen,
   922  			Addralign: 8,
   923  			Link:      uint32(dynsymSec),
   924  			Info:      uint32(symsection),
   925  			Entsize:   relsz,
   926  		}})
   927  
   928  	f.e.Sections = append(f.e.Sections, &elf.Section{
   929  		SectionHeader: elf.SectionHeader{
   930  			Name:      ".hash",
   931  			Type:      elf.SHT_HASH,
   932  			Flags:     elf.SHF_ALLOC,
   933  			Addr:      base + hashPos,
   934  			Offset:    hashPos,
   935  			FileSize:  hashLen,
   936  			Addralign: 8,
   937  			Link:      uint32(dynsymSec),
   938  		}})
   939  
   940  	shoff, err := f.f.Seek(0, io.SeekEnd)
   941  	if err != nil {
   942  		log.Fatal(err)
   943  	}
   944  	f.shoff = uint64(shoff)
   945  
   946  	if err := f.WriteSections(); err != nil {
   947  		log.Fatal(err)
   948  	}
   949  
   950  	f.Close()
   951  }