// Copyright 2014 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Loading of code and data fragments from package files into final image.

package main

import "os"

// load allocates segment images, populates them with data
// read from package files, and applies relocations to the data.
func (p *Prog) load() {
	// TODO(rsc): mmap the output file and store the data directly.
	// That will make writing the output file more efficient.
	for _, seg := range p.Segments {
		seg.Data = make([]byte, seg.FileSize)
	}
	for _, pkg := range p.Packages {
		p.loadPackage(pkg)
	}
}

// loadPackage loads and relocates data for all the
// symbols needed in the given package.
func (p *Prog) loadPackage(pkg *Package) {
	if pkg.File == "" {
		// This "package" contains internally generated symbols only.
		// All such symbols have a sym.Bytes field holding the actual data
		// (if any), plus relocations.
		for _, sym := range pkg.Syms {
			if sym.Bytes == nil {
				continue
			}
			seg := sym.Section.Segment
			off := sym.Addr - seg.VirtAddr
			data := seg.Data[off : off+Addr(sym.Size)]
			copy(data, sym.Bytes)
			p.relocateSym(sym, data)
		}
		return
	}

	// Package stored in file.
	f, err := os.Open(pkg.File)
	if err != nil {
		p.errorf("%v", err)
		return
	}
	defer f.Close()

	// TODO(rsc): Mmap file into memory.

	for _, sym := range pkg.Syms {
		if sym.Data.Size == 0 {
			continue
		}
		// TODO(rsc): If not using mmap, at least coalesce nearby reads.
		if sym.Section == nil {
			p.errorf("internal error: missing section for %s", sym.Name)
		}
		seg := sym.Section.Segment
		off := sym.Addr - seg.VirtAddr
		if off >= Addr(len(seg.Data)) || off+Addr(sym.Data.Size) > Addr(len(seg.Data)) {
			p.errorf("internal error: allocated space for %s too small: %d bytes for %d+%d (%d)", sym, len(seg.Data), off, sym.Data.Size, sym.Size)
		}
		data := seg.Data[off : off+Addr(sym.Data.Size)]
		_, err := f.ReadAt(data, sym.Data.Offset)
		if err != nil {
			p.errorf("reading %v: %v", sym.SymID, err)
		}
		p.relocateSym(sym, data)
	}
}

// TODO(rsc): Define full enumeration for relocation types.
const (
	R_ADDR    = 1
	R_SIZE    = 2
	R_CALL    = 3
	R_CALLARM = 4
	R_CALLIND = 5
	R_CONST   = 6
	R_PCREL   = 7
)

// relocateSym applies relocations to sym's data.
func (p *Prog) relocateSym(sym *Sym, data []byte) {
	for i := range sym.Reloc {
		r := &sym.Reloc[i]
		targ := p.Syms[r.Sym]
		if targ == nil {
			p.errorf("%v: reference to undefined symbol %v", sym, r.Sym)
			continue
		}
		val := targ.Addr + Addr(r.Add)
		switch r.Type {
		default:
			p.errorf("%v: unknown relocation type %d", sym, r.Type)
		case R_ADDR, R_CALLIND:
			// ok
		case R_PCREL, R_CALL:
			val -= sym.Addr + Addr(r.Offset+r.Size)
		}
		frag := data[r.Offset : r.Offset+r.Size]
		switch r.Size {
		default:
			p.errorf("%v: unknown relocation size %d", sym, r.Size)
		case 4:
			// TODO(rsc): Check for overflow?
			p.byteorder.PutUint32(frag, uint32(val))
		case 8:
			p.byteorder.PutUint64(frag, uint64(val))
		}
	}
}
