#
# Copyright (C) 2012-2026 Intel Corporation.
# SPDX-License-Identifier: MIT
#

###### Common definitions ######

# In this section we place all the generic flags. Later sections provide
# additional flags depending on architecutre, compiler etc.

## General flags

# If we are testing Pin, add some internal checks. Don't do this by default
# since it may incur a performance penatly.
PIN_TESTFLAGS :=
ifeq ($(PIN_TESTING),1)
    PIN_TESTFLAGS += -slow_asserts
endif

#If running on Docker set TARGET_DOCKER
ifneq ($(wildcard /.dockerenv),)
    TARGET_DOCKER := 1
endif

#If running on LXC set TARGET_LXC
ifeq ($(shell which systemd-detect-virt 2>&1 >/dev/null ; echo $$?),0)
ifeq ($(shell systemd-detect-virt | grep lxc >/dev/null; echo $$?),0)
    TARGET_LXC := 1
endif
endif

# Flags to pass to "make" when invoking a .test recipe.
MAKE_TESTFLAGS :=

## Output control

# When cross compiling, the first flag in these variables should be -m32.
# So we wait until the end of the file to define them.
COMP_OBJ :=
COMP_EXE :=
LINK_EXE :=

## Special compilation/linkage directives

STATIC := -static
NO_RANDOM :=
PIC := -fPIC
# There's no concept of compiling a file to be specifically PIE in windows.
NO_PIC := -fno-PIC -fno-pic
PIE := -pie
SSE2 := -msse2
AVX := -mavx
AVX2 := -mavx2
AVX512 := -mavx512f -mavx512bw -mavx512vl
FP387 := -mfpmath=387 -mno-sse
ASMCPP := -x assembler-with-cpp
NO_STDLIBS := -nostartfiles -nodefaultlibs -nostdlib 
EXPORT_ALL := -Wl,--export-dynamic
CRT_INC_DIR := -isystem
# The following is essentially a no-op for compatibility. It has no effect on the resulting linked file.
EXPORT := -Wl,--trace-symbol=
# Flags to suppress errors when the linker has trouble solving relocations but they are still valid.
SUPPRESS_RELOCS :=

## Include paths

# COMPONENT_INCLUDES will be defined later in the directory structure section.
# APP_INCLUDES, TOOL_INCLUDES and RPC_PLUGIN_INCLUDES are internal utilities for this file.
APP_INCLUDES :=
TOOL_INCLUDES :=
RPC_PLUGIN_INCLUDES :=

## Library paths

APP_LPATHS :=
TOOL_LPATHS :=
SATOOL_LPATHS :=
CXX_LPATHS :=

## Libraries to link


APP_LIBS := -Wl,--as-needed -lm
APP_DL_LIB :=
APP_LIB_XED := -lxed

ifeq ($(USE_DEBUG_LIBS),1)
    DEBUG_SUFFIX := _d
else
    DEBUG_SUFFIX :=
endif


TOOL_LIBS := -lpin${DEBUG_SUFFIX} -lpinrt-adaptor-static${DEBUG_SUFFIX} -lxed
SATOOL_LIBS := -lsapin${DEBUG_SUFFIX} -lpinrt-adaptor-static${DEBUG_SUFFIX} -lxed
CXX_LIBS :=

## Compiler flags

# Throughout this file, we only fill in the NOOPT versions of these variables.
# At the bottom of the file, the full versions are built from the NOOPT and
# the relevant optimization and debug flags (see below).
# e.g. APP_CXXFLAGS := $(APP_CXXFLAGS_NOOPT) $(APP_OPT_CXX) $(DBG_INFO_CXX)

# On Unix, ASM_FLAGS is identical to APP_CXXFLAGS, therefore it is built at
# the end of this file, same as APP_CXXFLAGS.

# Used by both Application C and CXX compilers
APP_FLAGS_NOOPT :=
# Exlcusive to application C compiler
APP_CFLAGS_NOOPT := -std=gnu99
# Exlcusive to application CXX compiler
APP_CXXFLAGS_NOOPT := -std=c++11
RPC_PLUGIN_CXXFLAGS_NOOPT := -std=c++17
TOOL_CXXFLAGS_NOOPT := -std=c++17 -Wall -Werror -Wno-unknown-pragmas
DLL_CXXFLAGS :=
ENABLE_DEPRECATED := -DPIN_DEPRECATED_WARNINGS=0
CPP11FLAGS := -std=c++11
CPP11LIBS := -Wl,--no-as-needed -lpthread
RTM_FLAGS := -mrtm

## Linker flags

# Throughout this file, we only fill in the NOOPT versions of these variables.
# At the bottom of the file, the full versions are built from the NOOPT and
# the relevant optimization and debug flags (see below).
# e.g. TOOL_LDFLAGS := $(TOOL_LDFLAGS_NOOPT) $(TOOL_OPT_LD) $(DBG_INFO_LD)

APP_LDFLAGS_NOOPT :=
TOOL_LDFLAGS_NOOPT := -shared
SATOOL_LDFLAGS_NOOPT :=
DLL_LDFLAGS := -shared

# Flags to link an executable file from a single assembly file with main() function as entry point.
APP_LDFLAGS_LINK_ASM :=

###### Debugging and optimizations ######

## Utility variables "internal" to this file

# The following variables depend on the user-defined DEBUG variable.
# If DEBUG=1 was specified, no optimizations will be applied and debug
# information will be generated. Otherwise, full optimization will be
# performed and debug information will not be generated.

# On Unix platforms we don't apply any link-time optimizations. The
# variables are defined here for readability.

# On Unix platforms the debug info flags for the compiler and linker
# are the same, we support two different variables for compatibility
# with Windows. This will incur two instances of the "-g" flag when
# compiling most applications with debug information. It's ugly but
# it doesn't do any harm.

ifeq ($(DEBUG),1)
    APP_OPT_CXX := -O0
    APP_OPT_LD :=
    TOOL_OPT_CXX := -O0
    TOOL_OPT_LD :=
    DBG_INFO_CXX := -g
    DBG_INFO_LD := -g
else
    APP_OPT_CXX := -O3
    APP_OPT_LD :=
    TOOL_OPT_CXX := -O3 -fomit-frame-pointer -fno-strict-aliasing
    TOOL_OPT_LD :=
    DBG_INFO_CXX :=
    DBG_INFO_LD :=
endif

## Debugging flags to be used in any makefile.rules file

# The following variables do not depend on the user-defined DEBUG
# variable. When they are used, debug information will always be
# generated.

DBG_INFO_CXX_ALWAYS := -g
DBG_INFO_LD_ALWAYS := -g

###### Additional flags depending on the compiler ######

# These variables are used to determine if the target system has a compiler or not. Some systems, do
# not have a native compiler and we don't want annoying error messages about 'missing cc' etc.
# This check needs to run on the target machine (which may be a remote machine), so we can't use the
# wildcard function, we must use the shell.
CCPATH ?= $(shell which $(CC) 2> /dev/null)
CXXPATH ?= $(shell which $(CXX) 2> /dev/null)
APP_CCPATH ?= $(shell which $(APP_CC) 2> /dev/null)
APP_CXXPATH ?= $(shell which $(APP_CXX) 2> /dev/null)
export CCPATH
export CXXPATH
export APP_CCPATH
export APP_CXXPATH

# This allows us to acquire information about the compiler
ifneq ($(CCPATH),)
    HELPOUT := $(shell $(CC) -v --help 2>&1)
endif

# GLIBC version 2.4 implements the function __stack_chk_fail used by new GCC
# versions when stack-protector is on. Therefore, disable this option (if supported)
ifneq ($(findstring stack-protector,$(HELPOUT)),)
    TOOL_CXXFLAGS_NOOPT += -fno-stack-protector
endif

ifneq ($(findstring -fexceptions,$(HELPOUT))$(findstring -f[no-]exceptions,$(HELPOUT)),)
    TOOL_CXXFLAGS_NOOPT += -fno-exceptions
endif

ifneq ($(findstring -funwind-tables,$(HELPOUT)),)
    TOOL_CXXFLAGS_NOOPT += -funwind-tables
endif

ifneq ($(findstring clang LLVM compiler,$(HELPOUT)),)
    TOOL_CXXFLAGS_NOOPT += -funwind-tables
endif

ifneq ($(findstring -fasynchronous-unwind-tables,$(HELPOUT)),)
    TOOL_CXXFLAGS_NOOPT += -fasynchronous-unwind-tables
endif

ifneq ($(findstring -fno-rtti,$(HELPOUT))$(findstring -frtti,$(HELPOUT)),)
    TOOL_CXXFLAGS_NOOPT += -fno-rtti
endif

ifneq ($(findstring clang LLVM compiler,$(HELPOUT)),)
    TOOL_CXXFLAGS_NOOPT += -Wtypedef-redefinition
endif

# Define the applications compiler, since we change the CXX and LINKER we have to set APP_CXX and APP_LINKER earlier here.

# The hierarchy/precedence of setting environment variable in makefile (e.g., APP_CXX) is as followed:
# 1. set by shell - user defined variables or shell variables defined outside of makefile
#                   or in the make command line. 
#
# 2. set by makefile - will not override shell settings, unless  "override" keyword is used.
#                      without "override" it will be set only if not already set by shell.
#
# 3. ?= in makefile means initialization, if not set at all so far (neither 1 or 2).
#    Note that variables defiened with '?=' are deferred, meaning it's value is not
#    immediately assigned but instead evaluated later, when it is actually used

APP_CC := $(CC)
APP_CXX := $(CXX)
APP_ASMBLR := $(APP_CC)
APP_LINKER := $(APP_CXX)
APP_ARCHIVER := $(ARCHIVER)
TOOL_ARCHIVER := $(ARCHIVER)

ifeq ($(KIT),1)
    # In the kit tree (distribution), the default Pin root is the kit root.
    # However, don't overwrite a user-defined PIN_ROOT definition (if it exists)
    PIN_ROOT ?= ../../..
    PIN_ROOT_ABS := $(shell cd $(PIN_ROOT) && pwd -P)
    XED_ROOT := $(PIN_ROOT)/extras/xed-$(TARGET)
    TOOLS_ROOT := $(PIN_ROOT)/source/tools



    # Pass original CXX (g++/icpx compiler path) to pin wrapper through PIN_WRAPPER_GCC variable.
    ifeq ($(COMPILER),icx)
        # Since we use icx also for c++ files, replace icpx with icx (in case of full path, we assume icx resides in the same folder).
        export PIN_WRAPPER_GCC := $(patsubst %icpx,%icx,$(CXX))
        TOOL_CC := $(PIN_ROOT)/$(TARGET)/pinrt/bin/pin-icx
        TOOL_CXX := $(PIN_ROOT)/$(TARGET)/pinrt/bin/pin-icx++
        TOOL_LINKER := $(PIN_ROOT)/$(TARGET)/pinrt/bin/pin-icx-ld
    else
        # Since we use gcc also for c++ files, replace g++ with gcc (in case of full path, we assume gcc resides in the same folder).
        export PIN_WRAPPER_GCC := $(patsubst %g++,%gcc,$(CXX))
        TOOL_CC := $(PIN_ROOT)/$(TARGET)/pinrt/bin/pin-gcc
        TOOL_CXX := $(PIN_ROOT)/$(TARGET)/pinrt/bin/pin-g++
        TOOL_LINKER := $(TOOL_CXX)
    endif


    COMPONENT_INCLUDES := -I$(PIN_ROOT)/extras/components/include
    TOOL_INCLUDES += -I$(PIN_ROOT)/source/include/pin \
                        -I$(PIN_ROOT)/source/include/pin/gen

    TOOL_INCLUDES += $(CRT_INC_DIR) $(PIN_ROOT_ABS)/$(TARGET)/pinrt/include/adaptor

    APP_LPATHS += -L$(PIN_ROOT)/extras/components/lib/$(TARGET)
else
    # settings for building tool without a kit distribution. This is currently not suported.
    $(error "Building outside without a kit distribution is not supported. Please make sure you have a kit distribution.")
endif



TOOL_LPATHS += -L$(PIN_ROOT)/$(TARGET)/lib
SATOOL_LPATHS += -L$(PIN_ROOT)/$(TARGET)/lib
SATOOL_LDFLAGS_NOOPT += -Wl,-pie

PIN := $(PIN_ROOT)/pin
PIN_EXEC_NAME := $(notdir $(PIN))
PINBIN := $(PIN_ROOT)/$(TARGET)/bin/pinbin

VSCRIPT_DIR := $(PIN_ROOT)/source/include/pin

# Command to set the search path for libraries required by a stand-alone tool.
# Note that some OSs don't look for SOs in the current directory automatically so we need to add "."
# to the search path.
LOADER_LIBRARY_PATH := LD_LIBRARY_PATH
SET_DLL_PATH := export $(LOADER_LIBRARY_PATH)=$(OBJDIR):$(PIN_ROOT)/$(TARGET)/lib:$(PIN_ROOT)/extras/xed-$(TARGET)/lib:$(PIN_ROOT)/$(TARGET)/pinrt/lib:.:$$$(LOADER_LIBRARY_PATH);

TOOL_INCLUDES += $(COMPONENT_INCLUDES) \
                 -I$(XED_ROOT)/include/xed \
                 -I$(TOOLS_ROOT)/Utils
APP_INCLUDES += -I$(TOOLS_ROOT)/Utils
RPC_PLUGIN_INCLUDES += -I$(PIN_ROOT)/$(TARGET)/pinrt/include/adaptor

APP_LPATHS += -L$(XED_ROOT)/lib
TOOL_LPATHS += -L$(XED_ROOT)/lib
SATOOL_LPATHS += -L$(XED_ROOT)/lib

DWARF_LIBS := -lpindwarf -ldwarf

###### Additional flags depending on architecture ######

ifeq ($(TARGET),ia32)
	APP_FLAGS_NOOPT += -DTARGET_IA32 -DHOST_IA32 -DFUND_TC_TARGETCPU=FUND_CPU_IA32 -DFUND_TC_HOSTCPU=FUND_CPU_IA32
    #TOOL_CXXFLAGS_NOOPT += -DTARGET_IA32 -DHOST_IA32
    
    # cross compilation
    ifeq ($(HOST_ARCH),intel64)
        COMP_OBJ += -m32
        COMP_EXE += -m32
        LINK_EXE += -m32
    endif
endif

TOOL_CXXFLAGS_NOOPT += -fPIC
DLL_CXXFLAGS += -fPIC

ifeq ($(TARGET),intel64)
	APP_FLAGS_NOOPT += -DTARGET_IA32E -DHOST_IA32E -DFUND_TC_TARGETCPU=FUND_CPU_INTEL64 -DFUND_TC_HOSTCPU=FUND_CPU_INTEL64
    #TOOL_CXXFLAGS_NOOPT += -DTARGET_IA32E -DHOST_IA32E
endif

###### Additional flags depending on OS ######

ifeq ($(COMPILER),icx)
    TOOL_LDFLAGS_NOOPT += -c++
    SATOOL_LDFLAGS_NOOPT += -c++
else
    TOOL_CXXFLAGS_NOOPT += -Wno-dangling-pointer
endif

NO_PIE := -no-pie
APP_FLAGS_NOOPT += -DTARGET_LINUX -DFUND_TC_TARGETOS=FUND_OS_LINUX -DFUND_TC_HOSTOS=FUND_OS_LINUX
GCC_VER_LOWER_THAN_6 := $(shell $(TOOLS_ROOT)/Utils/testToolVersion $(APP_CCPATH) lt 6.0)
ifeq ($(GCC_VER_LOWER_THAN_6),0)
    LDFLAGS_DEFAULT_NO_PIE := $(NO_PIE)
endif
APP_LDFLAGS_NOOPT += $(LDFLAGS_DEFAULT_NO_PIE)
APP_DL_LIB += -ldl
APP_LIBS += -ldl -lpthread
TOOL_LIBS += $(DWARF_LIBS)
SATOOL_LIBS += $(DWARF_LIBS)
TOOL_LDFLAGS_NOOPT += -Wl,-Bsymbolic -Wl,--version-script=$(VSCRIPT_DIR)/pintool.ver
SATOOL_LDFLAGS_NOOPT += -Wl,-Bsymbolic
DWARF4 += -gdwarf-4

# Add -faligned-new flag for gcc >= 7.0. This flag enables C++17 support for dynamic
# allocation (using new operator) of an object that has alignment requirement that is
# larger then the default for the system
GCC_VER_LOWER_THAN_7 := $(shell $(TOOLS_ROOT)/Utils/testToolVersion $(CXXPATH) lt 7.0)
ifeq ($(GCC_VER_LOWER_THAN_7),0)
    # GCC version >= 7.0
    TOOL_CXXFLAGS_NOOPT += -faligned-new
endif

DIST_NAME_FEDORA := $(shell $(TOOLS_ROOT)/Utils/testLinuxDistName eq fedora)
DIST_NAME_RHEL = $(shell $(TOOLS_ROOT)/Utils/testLinuxDistName eq rhel)
ifeq ($(DIST_NAME_RHEL),0)
    DIST_NAME_RHEL = $(shell $(TOOLS_ROOT)/Utils/testLinuxDistName has redhat)
endif
DIST_NAME_SUSE := $(shell $(TOOLS_ROOT)/Utils/testLinuxDistName has opensuse)
DIST_NAME_SLES := $(shell $(TOOLS_ROOT)/Utils/testLinuxDistName has sles)
DIST_NAME_UBUNTU := $(shell $(TOOLS_ROOT)/Utils/testLinuxDistName eq ubuntu)
DIST_NAME_CENTOS := $(shell $(TOOLS_ROOT)/Utils/testLinuxDistName eq centos)
DIST_NAME_OPENSUSE := $(shell $(TOOLS_ROOT)/Utils/testLinuxDistName eq opensuse-leap)

CET_ENABLED := $(shell $(TOOLS_ROOT)/Utils/testLinuxIsCetEnabled)

# On Fedora version 28 and up, on RedHat 8 and up,
# on CentOS 8 and up and on Ubuntu 20.10 and up,
# on SLES 15.3 and up and on OpenSUSE Leap 15.3 and up -
# the RPC Interfaces have been removed from glibc.
# An additional package is installed to provide the RPC interface.
# The include path and library from the additional package are specified by the 
# RPC_INCLUDES and RPC_LIBS variables
RPC_INCLUDES :=
RPC_LIBS := 
ifeq ($(DIST_NAME_FEDORA),1)
    DIST_VER_GE_28 := $(shell $(TOOLS_ROOT)/Utils/testLinuxDistVersion ge 28)
    ifeq ($(DIST_VER_GE_28),1)
        RPC_INCLUDES += -I/usr/include/tirpc
        RPC_LIBS += -ltirpc
    endif
endif
ifeq ($(DIST_NAME_RHEL),1)
    DIST_VER_GE_8 := $(shell $(TOOLS_ROOT)/Utils/testLinuxDistVersion ge 8)
    ifeq ($(DIST_VER_GE_8),1)
        RPC_INCLUDES += -I/usr/include/tirpc
        RPC_LIBS += -ltirpc
    endif
endif
ifeq ($(DIST_NAME_CENTOS),1)
    DIST_VER_GE_8 := $(shell $(TOOLS_ROOT)/Utils/testLinuxDistVersion ge 8)
    ifeq ($(DIST_VER_GE_8),1)
        RPC_INCLUDES += -I/usr/include/tirpc
        RPC_LIBS += -ltirpc
    endif
endif
ifeq ($(DIST_NAME_UBUNTU),1)
    DIST_VER_GE_20_10 := $(shell $(TOOLS_ROOT)/Utils/testLinuxDistVersion ge 20.10)
    ifeq ($(DIST_VER_GE_20_10),1)
        RPC_INCLUDES += -I/usr/include/tirpc
        RPC_LIBS += -ltirpc
    endif
endif
ifeq ($(DIST_NAME_SLES),1)
    DIST_VER_GE_15_3 := $(shell $(TOOLS_ROOT)/Utils/testLinuxDistVersion ge 15.3)
    ifeq ($(DIST_VER_GE_15_3),1)
        RPC_INCLUDES += -I/usr/include/tirpc
        RPC_LIBS += -ltirpc
    endif
endif
ifeq ($(DIST_NAME_OPENSUSE),1)
    DIST_VER_GE_15_3 := $(shell $(TOOLS_ROOT)/Utils/testLinuxDistVersion ge 15.3)
    ifeq ($(DIST_VER_GE_15_3),1)
        RPC_INCLUDES += -I/usr/include/tirpc
        RPC_LIBS += -ltirpc
    endif
endif


TOOL_LIBS += -lunwind-dynamic
SATOOL_LIBS += -lunwind-dynamic

# Fedora 44 and later ia32 rejects text relocations emitted by legacy hand-written assembly in selected tests.
# Keep this compatibility flag scoped through explicit link rules.
ifeq ($(TARGET),ia32)
    ifeq ($(DIST_NAME_FEDORA),1)
        DIST_VER_GE_44 := $(shell $(TOOLS_ROOT)/Utils/testLinuxDistVersion ge 44)
        ifeq ($(DIST_VER_GE_44),1)
            SUPPRESS_RELOCS := -Wl,-z,notext
        endif
    endif
endif

###### Finalize flags ######

## Output control

COMP_OBJ += -c -o 
COMP_EXE += -o 
LINK_EXE += -o 

## Compiler and linker flags

# First define the assembler flags - they do not require any additional include paths.
ASM_FLAGS := $(APP_FLAGS_NOOPT) $(ASMCPP) $(TOOL_INCLUDES)

# Now add the include paths to the compilation flags.
APP_FLAGS_NOOPT += $(APP_INCLUDES)
# RPC plugin flags is the same as app flags with addition of specific plugin include paths.
RPC_PLUGIN_CXXFLAGS_NOOPT += $(RPC_PLUGIN_INCLUDES)
TOOL_CXXFLAGS_NOOPT += $(TOOL_INCLUDES)

# Define the versions containing the full options
APP_CFLAGS_NOOPT += $(APP_FLAGS_NOOPT)
APP_CXXFLAGS_NOOPT += $(APP_FLAGS_NOOPT)
APP_CXXFLAGS := $(APP_CXXFLAGS_NOOPT) $(APP_OPT_CXX) $(DBG_INFO_CXX)
APP_CFLAGS := $(APP_CFLAGS_NOOPT) $(APP_OPT_CXX) $(DBG_INFO_CXX)
APP_LDFLAGS := $(APP_LDFLAGS_NOOPT) $(APP_OPT_LD) $(DBG_INFO_LD)
RPC_PLUGIN_CXXFLAGS := $(RPC_PLUGIN_CXXFLAGS_NOOPT) $(APP_OPT_CXX) $(DBG_INFO_CXX)
TOOL_CXXFLAGS := $(TOOL_CXXFLAGS_NOOPT) $(TOOL_OPT_CXX) $(DBG_INFO_CXX)
TOOL_CFLAGS := $(filter-out -std=c++17 -fno-rtti -fno-exceptions -fexceptions -faligned-new,$(TOOL_CXXFLAGS))
TOOL_LDFLAGS := $(TOOL_LDFLAGS_NOOPT) $(TOOL_OPT_LD) $(DBG_INFO_LD)
SATOOL_LDFLAGS := $(SATOOL_LDFLAGS_NOOPT) $(TOOL_OPT_LD) $(DBG_INFO_LD)

VERSION_SCRIPT_PATTERN := -Wl,--version-script=%
TOOL_LIBRARY_LDFLAGS_NOOPT := $(filter-out $(VERSION_SCRIPT_PATTERN),$(TOOL_LDFLAGS_NOOPT))


# Add debugging flags to the NOOPT versions and disable optimizations
APP_CFLAGS_NOOPT += -O0 $(DBG_INFO_CXX)
APP_CXXFLAGS_NOOPT += -O0 $(DBG_INFO_CXX)
RPC_PLUGIN_CXXFLAGS_NOOPT += -O0 $(DBG_INFO_CXX)
APP_LDFLAGS_NOOPT += $(DBG_INFO_LD)
TOOL_CXXFLAGS_NOOPT += -O0 $(DBG_INFO_CXX)
TOOL_LDFLAGS_NOOPT += $(DBG_INFO_LD)

# Tool's compiler options for static analysis tools
SATOOL_CXXFLAGS_NOOPT := $(TOOL_CXXFLAGS_NOOPT)
SATOOL_CXXFLAGS := $(TOOL_CXXFLAGS)

## Pin program finalization:

# Add any additional flags that the user specified to the Pin command line.
PIN += $(PIN_TESTFLAGS) $(PINFLAGS)
