# SPDX-License-Identifier: Apache-2.0
set_property(TARGET linker PROPERTY devices_start_symbol "_device_list_start")

find_package(GnuLd REQUIRED)
set(CMAKE_LINKER ${GNULD_LINKER})

set_ifndef(LINKERFLAGPREFIX -Wl)

if(NOT "${ZEPHYR_TOOLCHAIN_VARIANT}" STREQUAL "host")
  if(CONFIG_CPP_EXCEPTIONS AND LIBGCC_DIR)
    # When building with C++ Exceptions, it is important that crtbegin and crtend
    # are linked at specific locations.
    # The location is so important that we cannot let this be controlled by normal
    # link libraries, instead we must control the link command specifically as
    # part of toolchain.
    set(CMAKE_CXX_LINK_EXECUTABLE
        "<CMAKE_CXX_COMPILER> <FLAGS> <CMAKE_CXX_LINK_FLAGS> <LINK_FLAGS> ${LIBGCC_DIR}/crtbegin.o <OBJECTS> -o <TARGET> <LINK_LIBRARIES> ${LIBGCC_DIR}/crtend.o")
  endif()
endif()

# Run $LINKER_SCRIPT file through the C preprocessor, producing ${linker_script_gen}
# NOTE: ${linker_script_gen} will be produced at build-time; not at configure-time
macro(configure_linker_script linker_script_gen linker_pass_define)
  set(extra_dependencies ${ARGN})

  if(CONFIG_CMAKE_LINKER_GENERATOR)
    add_custom_command(
      OUTPUT ${linker_script_gen}
      COMMAND ${CMAKE_COMMAND}
        -DPASS="${linker_pass_define}"
        -DFORMAT="$<TARGET_PROPERTY:linker,FORMAT>"
        -DENTRY="$<TARGET_PROPERTY:linker,ENTRY>"
        -DMEMORY_REGIONS="$<TARGET_PROPERTY:linker,MEMORY_REGIONS>"
        -DGROUPS="$<TARGET_PROPERTY:linker,GROUPS>"
        -DSECTIONS="$<TARGET_PROPERTY:linker,SECTIONS>"
        -DSECTION_SETTINGS="$<TARGET_PROPERTY:linker,SECTION_SETTINGS>"
        -DSYMBOLS="$<TARGET_PROPERTY:linker,SYMBOLS>"
        -DOUT_FILE=${CMAKE_CURRENT_BINARY_DIR}/${linker_script_gen}
        -P ${ZEPHYR_BASE}/cmake/linker/ld/ld_script.cmake
      )
  else()
    set(template_script_defines ${linker_pass_define})
    list(TRANSFORM template_script_defines PREPEND "-D")

    # Only Ninja and Makefile generators support DEPFILE.
    if((CMAKE_GENERATOR STREQUAL "Ninja")
       OR (CMAKE_GENERATOR MATCHES "Makefiles")
    )
      set(linker_script_dep DEPFILE ${PROJECT_BINARY_DIR}/${linker_script_gen}.dep)
    else()
      # TODO: How would the linker script dependencies work for non-linker
      # script generators.
      message(STATUS "Warning; this generator is not well supported. The
    Linker script may not be regenerated when it should.")
      set(linker_script_dep "")
    endif()

    zephyr_get_include_directories_for_lang(C current_includes)
    get_property(current_defines GLOBAL PROPERTY PROPERTY_LINKER_SCRIPT_DEFINES)
    if(DEFINED SOC_LINKER_SCRIPT)
      cmake_path(GET SOC_LINKER_SCRIPT PARENT_PATH soc_linker_script_includes)
      set(soc_linker_script_includes -I${soc_linker_script_includes})
    endif()

    add_custom_command(
      OUTPUT ${linker_script_gen}
      DEPENDS
      ${LINKER_SCRIPT}
      ${AUTOCONF_H}
      ${extra_dependencies}
      # NB: 'linker_script_dep' will use a keyword that ends 'DEPENDS'
      ${linker_script_dep}
      COMMAND ${CMAKE_C_COMPILER}
      -x assembler-with-cpp
      ${NOSYSDEF_CFLAG}
      -MD -MF ${linker_script_gen}.dep -MT ${linker_script_gen}
      -D_LINKER
      -D_ASMLANGUAGE
      -imacros ${AUTOCONF_H}
      ${current_includes}
      ${soc_linker_script_includes}
      ${current_defines}
      ${template_script_defines}
      -E ${LINKER_SCRIPT}
      -P # Prevent generation of debug `#line' directives.
      -o ${linker_script_gen}
      VERBATIM
      WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
      COMMAND_EXPAND_LISTS
    )
  endif()
endmacro()

# Force symbols to be entered in the output file as undefined symbols
function(toolchain_ld_force_undefined_symbols)
  foreach(symbol ${ARGN})
    zephyr_link_libraries(${LINKERFLAGPREFIX},-u,${symbol})
  endforeach()
endfunction()

# Link a target to given libraries with toolchain-specific argument order
#
# Usage:
#   toolchain_ld_link_elf(
#     TARGET_ELF             <target_elf>
#     OUTPUT_MAP             <output_map_file_of_target>
#     LIBRARIES_PRE_SCRIPT   [libraries_pre_script]
#     LINKER_SCRIPT          <linker_script>
#     LIBRARIES_POST_SCRIPT  [libraries_post_script]
#     DEPENDENCIES           [dependencies]
#   )
function(toolchain_ld_link_elf)
  cmake_parse_arguments(
    TOOLCHAIN_LD_LINK_ELF                                     # prefix of output variables
    ""                                                        # list of names of the boolean arguments
    "TARGET_ELF;OUTPUT_MAP;LINKER_SCRIPT"                     # list of names of scalar arguments
    "LIBRARIES_PRE_SCRIPT;LIBRARIES_POST_SCRIPT;DEPENDENCIES" # list of names of list arguments
    ${ARGN}                                                   # input args to parse
  )

  if((${CMAKE_LINKER} STREQUAL "${CROSS_COMPILE}ld.bfd") OR
     ${GNULD_LINKER_IS_BFD})
    # ld.bfd was found so let's explicitly use that for linking, see #32237
    set(use_linker "-fuse-ld=bfd")
  endif()

  target_link_libraries(
    ${TOOLCHAIN_LD_LINK_ELF_TARGET_ELF}
    ${TOOLCHAIN_LD_LINK_ELF_LIBRARIES_PRE_SCRIPT}
    ${use_linker}
    ${TOPT}
    ${TOOLCHAIN_LD_LINK_ELF_LINKER_SCRIPT}
    ${TOOLCHAIN_LD_LINK_ELF_LIBRARIES_POST_SCRIPT}

    ${LINKERFLAGPREFIX},-Map=${TOOLCHAIN_LD_LINK_ELF_OUTPUT_MAP}
    ${LINKERFLAGPREFIX},--whole-archive
    ${WHOLE_ARCHIVE_LIBS}
    ${LINKERFLAGPREFIX},--no-whole-archive
    ${NO_WHOLE_ARCHIVE_LIBS}
    $<TARGET_OBJECTS:${OFFSETS_LIB}>
    ${LIB_INCLUDE_DIR}
    -L${PROJECT_BINARY_DIR}
    ${TOOLCHAIN_LIBS}

    ${TOOLCHAIN_LD_LINK_ELF_DEPENDENCIES}
  )
endfunction(toolchain_ld_link_elf)

# Load toolchain_ld-family macros
include(${ZEPHYR_BASE}/cmake/linker/${LINKER}/target_base.cmake)
include(${ZEPHYR_BASE}/cmake/linker/${LINKER}/target_baremetal.cmake)
include(${ZEPHYR_BASE}/cmake/linker/${LINKER}/target_cpp.cmake)
include(${ZEPHYR_BASE}/cmake/linker/${LINKER}/target_relocation.cmake)
include(${ZEPHYR_BASE}/cmake/linker/${LINKER}/target_configure.cmake)
