// Copyright 2013 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.

// Parsing of Mach-O executables (OS X).

package main

import (
	"debug/macho"
	"os"
	"sort"
)

func machoSymbols(f *os.File) (syms []Sym, goarch string) {
	p, err := macho.NewFile(f)
	if err != nil {
		errorf("parsing %s: %v", f.Name(), err)
		return
	}

	if p.Symtab == nil {
		errorf("%s: no symbol table", f.Name())
		return
	}

	switch p.Cpu {
	case macho.Cpu386:
		goarch = "386"
	case macho.CpuAmd64:
		goarch = "amd64"
	case macho.CpuArm:
		goarch = "arm"
	}

	// Build sorted list of addresses of all symbols.
	// We infer the size of a symbol by looking at where the next symbol begins.
	var addrs []uint64
	for _, s := range p.Symtab.Syms {
		addrs = append(addrs, s.Value)
	}
	sort.Sort(uint64s(addrs))

	for _, s := range p.Symtab.Syms {
		sym := Sym{Name: s.Name, Addr: s.Value, Code: '?'}
		i := sort.Search(len(addrs), func(x int) bool { return addrs[x] > s.Value })
		if i < len(addrs) {
			sym.Size = int64(addrs[i] - s.Value)
		}
		if s.Sect == 0 {
			sym.Code = 'U'
		} else if int(s.Sect) <= len(p.Sections) {
			sect := p.Sections[s.Sect-1]
			switch sect.Seg {
			case "__TEXT":
				sym.Code = 'R'
			case "__DATA":
				sym.Code = 'D'
			}
			switch sect.Seg + " " + sect.Name {
			case "__TEXT __text":
				sym.Code = 'T'
			case "__DATA __bss", "__DATA __noptrbss":
				sym.Code = 'B'
			}
		}
		syms = append(syms, sym)
	}

	return
}

type uint64s []uint64

func (x uint64s) Len() int           { return len(x) }
func (x uint64s) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }
func (x uint64s) Less(i, j int) bool { return x[i] < x[j] }
