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

package ld

import (
	"cmd/internal/objabi"
	"cmd/internal/sys"
	"encoding/binary"
)

// Target holds the configuration we're building for.
type Target struct {
	Arch *sys.Arch

	HeadType objabi.HeadType

	LinkMode  LinkMode
	BuildMode BuildMode

	linkShared    bool
	canUsePlugins bool
	IsELF         bool
}

//
// Target type functions
//

func (t *Target) IsExe() bool {
	return t.BuildMode == BuildModeExe
}

func (t *Target) IsShared() bool {
	return t.BuildMode == BuildModeShared
}

func (t *Target) IsPlugin() bool {
	return t.BuildMode == BuildModePlugin
}

func (t *Target) IsInternal() bool {
	return t.LinkMode == LinkInternal
}

func (t *Target) IsExternal() bool {
	return t.LinkMode == LinkExternal
}

func (t *Target) IsPIE() bool {
	return t.BuildMode == BuildModePIE
}

func (t *Target) IsSharedGoLink() bool {
	return t.linkShared
}

func (t *Target) CanUsePlugins() bool {
	return t.canUsePlugins
}

func (t *Target) IsElf() bool {
	t.mustSetHeadType()
	return t.IsELF
}

func (t *Target) IsDynlinkingGo() bool {
	return t.IsShared() || t.IsSharedGoLink() || t.IsPlugin() || t.CanUsePlugins()
}

// UseRelro reports whether to make use of "read only relocations" aka
// relro.
func (t *Target) UseRelro() bool {
	switch t.BuildMode {
	case BuildModeCArchive, BuildModeCShared, BuildModeShared, BuildModePIE, BuildModePlugin:
		return t.IsELF || t.HeadType == objabi.Haix || t.HeadType == objabi.Hdarwin
	default:
		if t.HeadType == objabi.Hdarwin && t.IsARM64() {
			// On darwin/ARM64, everything is PIE.
			return true
		}
		return t.linkShared || (t.HeadType == objabi.Haix && t.LinkMode == LinkExternal)
	}
}

//
// Processor functions
//

func (t *Target) Is386() bool {
	return t.Arch.Family == sys.I386
}

func (t *Target) IsARM() bool {
	return t.Arch.Family == sys.ARM
}

func (t *Target) IsARM64() bool {
	return t.Arch.Family == sys.ARM64
}

func (t *Target) IsAMD64() bool {
	return t.Arch.Family == sys.AMD64
}

func (t *Target) IsMIPS() bool {
	return t.Arch.Family == sys.MIPS
}

func (t *Target) IsMIPS64() bool {
	return t.Arch.Family == sys.MIPS64
}

func (t *Target) IsLOONG64() bool {
	return t.Arch.Family == sys.Loong64
}

func (t *Target) IsPPC64() bool {
	return t.Arch.Family == sys.PPC64
}

func (t *Target) IsRISCV64() bool {
	return t.Arch.Family == sys.RISCV64
}

func (t *Target) IsS390X() bool {
	return t.Arch.Family == sys.S390X
}

func (t *Target) IsWasm() bool {
	return t.Arch.Family == sys.Wasm
}

//
// OS Functions
//

func (t *Target) IsLinux() bool {
	t.mustSetHeadType()
	return t.HeadType == objabi.Hlinux
}

func (t *Target) IsDarwin() bool {
	t.mustSetHeadType()
	return t.HeadType == objabi.Hdarwin
}

func (t *Target) IsWindows() bool {
	t.mustSetHeadType()
	return t.HeadType == objabi.Hwindows
}

func (t *Target) IsPlan9() bool {
	t.mustSetHeadType()
	return t.HeadType == objabi.Hplan9
}

func (t *Target) IsAIX() bool {
	t.mustSetHeadType()
	return t.HeadType == objabi.Haix
}

func (t *Target) IsSolaris() bool {
	t.mustSetHeadType()
	return t.HeadType == objabi.Hsolaris
}

func (t *Target) IsNetbsd() bool {
	t.mustSetHeadType()
	return t.HeadType == objabi.Hnetbsd
}

func (t *Target) IsOpenbsd() bool {
	t.mustSetHeadType()
	return t.HeadType == objabi.Hopenbsd
}

func (t *Target) IsFreebsd() bool {
	t.mustSetHeadType()
	return t.HeadType == objabi.Hfreebsd
}

func (t *Target) mustSetHeadType() {
	if t.HeadType == objabi.Hunknown {
		panic("HeadType is not set")
	}
}

//
// MISC
//

func (t *Target) IsBigEndian() bool {
	return t.Arch.ByteOrder == binary.BigEndian
}

func (t *Target) UsesLibc() bool {
	t.mustSetHeadType()
	switch t.HeadType {
	case objabi.Haix, objabi.Hdarwin, objabi.Hopenbsd, objabi.Hsolaris, objabi.Hwindows:
		// platforms where we use libc for syscalls.
		return true
	}
	return false
}