#!/usr/bin/python
# Copyright (c) 2012 The Native Client Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
#
#
# This is a thin wrapper for native LD. This is not meant to be
# used by the user, but is called from pnacl-translate.
# This implements the native linking part of translation.
#
# All inputs must be native objects or linker scripts.
#
# --pnacl-sb will cause the sandboxed LD to be used.
# The bulk of this file is logic to invoke the sandboxed translator.

import subprocess

from driver_tools import CheckTranslatorPrerequisites, GetArch, ParseArgs, \
    Run, UnrecognizedOption
from driver_env import env
from driver_log import Log
import ldtools
import pathtools


EXTRA_ENV = {
  'INPUTS'   : '',
  'OUTPUT'   : '',

  # the INPUTS file coming from the llc translation step
  'LLC_TRANSLATED_FILE' : '',
  'SPLIT_MODULE' : '0',
  'USE_STDLIB': '1',

  # Determine if we should build nexes compatible with the IRT.
  'USE_IRT' : '1',

  # Upstream gold has the segment gap built in, but the gap can be modified
  # when not using the IRT. The gap does need to be at least one bundle so the
  # halt sled can be added for the TCB in case the segment ends up being a
  # multiple of 64k.
  # --eh-frame-hdr asks the linker to generate an .eh_frame_hdr section,
  # which is a presorted list of registered frames. This section is
  # used by libgcc_eh/libgcc_s to avoid doing the sort during runtime.
  # http://www.airs.com/blog/archives/462
  #
  # BE CAREFUL: anything added to LD_FLAGS should be synchronized with
  # flags used by the in-browser translator.
  # See: binutils/gold/nacl_file.cc
  'LD_FLAGS'    : '-nostdlib ' +
                  # Only relevant for ARM where it suppresses a warning.
                  # Ignored for other archs.
                  '--no-fix-cortex-a8 ' +
                  '-m ${LD_EMUL} ' +
                  '--eh-frame-hdr ' +
                  '${NONSFI_NACL ? -pie : -static} ' +
                  # "_begin" allows a PIE to find its load address in
                  # order to apply dynamic relocations.
                  '${NONSFI_NACL ? -defsym=_begin=0} ' +
                  # Give an error if any TEXTRELs occur.
                  '-z text ' +
                  '--build-id ' +
                  # Give non-IRT builds 12MB of text before starting rodata
                  # instead of the larger default gap. The gap cannot be
                  # too small (e.g., 0) because sel_ldr requires space for
                  # adding a halt sled.
                  '${!USE_IRT ? --rosegment-gap=0xc00000}',

  'LD_EMUL'        : '${LD_EMUL_%BASE_ARCH%}',
  'LD_EMUL_ARM'    : 'armelf_nacl',
  'LD_EMUL_X8632'  : 'elf_nacl',
  'LD_EMUL_X8664'  : 'elf64_nacl',
  'LD_EMUL_MIPS32' : 'elf32ltsmip_nacl',

  'SEARCH_DIRS'        : '${SEARCH_DIRS_USER} ${SEARCH_DIRS_BUILTIN}',
  'SEARCH_DIRS_USER'   : '',
  'SEARCH_DIRS_BUILTIN': '${USE_STDLIB ? ${LIBS_ARCH}/}',

  'LIBS_ARCH'        : '${LIBS_%ARCH%}',
  'LIBS_ARM'         : '${BASE_LIB_NATIVE}arm',
  'LIBS_ARM_NONSFI'  : '${BASE_LIB_NATIVE}arm-nonsfi',
  'LIBS_X8632'       : '${BASE_LIB_NATIVE}x86-32',
  'LIBS_X8632_NONSFI': '${BASE_LIB_NATIVE}x86-32-nonsfi',
  'LIBS_X8664'       : '${BASE_LIB_NATIVE}x86-64',
  'LIBS_MIPS32'      : '${BASE_LIB_NATIVE}mips32',

  # Note: this is only used in the unsandboxed case
  'RUN_LD' : '${LD} ${LD_FLAGS} ${inputs} -o ${output}'
}

def PassThrough(*args):
  env.append('LD_FLAGS', *args)

LDPatterns = [
  ( '-o(.+)',          "env.set('OUTPUT', pathtools.normalize($0))"),
  ( ('-o', '(.+)'),    "env.set('OUTPUT', pathtools.normalize($0))"),

  ( '--noirt',                      "env.set('USE_IRT', '0')"),

  ( '-static',         "env.set('STATIC', '1')"),
  ( '-nostdlib',       "env.set('USE_STDLIB', '0')"),

  ( '-L(.+)',
    "env.append('SEARCH_DIRS_USER', pathtools.normalize($0))"),
  ( ('-L', '(.*)'),
    "env.append('SEARCH_DIRS_USER', pathtools.normalize($0))"),

  # Note: we do not yet support all the combinations of flags which affect
  # layout of the various sections and segments because the corner cases in gold
  # may not all be worked out yet. They can be added (and tested!) as needed.
  ( ('(-Ttext=.*)'),              PassThrough),
  ( ('(-Trodata=.*)'),            PassThrough),
  ( ('(-Ttext-segment=.*)'),      PassThrough),
  ( ('(-Trodata-segment=.*)'),    PassThrough),
  ( ('(--section-start)', '(.+)'),PassThrough),
  ( ('(--section-start=.*)'),     PassThrough),
  ( ('(-e)','(.*)'),              PassThrough),
  ( '(--entry=.*)',               PassThrough),
  ( '(-M)',                       PassThrough),
  ( '(-t)',                       PassThrough),
  ( ('-y','(.*)'),                PassThrough),
  ( ('(-defsym)','(.*)'),         PassThrough),
  ( '-export-dynamic',            PassThrough),

  ( '(--print-gc-sections)',      PassThrough),
  ( '(--gc-sections)',            PassThrough),
  ( '(--unresolved-symbols=.*)',  PassThrough),
  ( '(--dynamic-linker=.*)',      PassThrough),
  ( '(-g)',                       PassThrough),
  ( '(--build-id)',               PassThrough),

  ( '-melf_nacl',            "env.set('ARCH', 'X8632')"),
  ( ('-m','elf_nacl'),       "env.set('ARCH', 'X8632')"),
  ( '-melf64_nacl',          "env.set('ARCH', 'X8664')"),
  ( ('-m','elf64_nacl'),     "env.set('ARCH', 'X8664')"),
  ( '-marmelf_nacl',         "env.set('ARCH', 'ARM')"),
  ( ('-m','armelf_nacl'),    "env.set('ARCH', 'ARM')"),
  ( '-mmipselelf_nacl',      "env.set('ARCH', 'MIPS32')"),
  ( ('-m','mipselelf_nacl'), "env.set('ARCH', 'MIPS32')"),

  # Inputs and options that need to be kept in order
  ( '(--no-as-needed)',    "env.append('INPUTS', $0)"),
  ( '(--as-needed)',       "env.append('INPUTS', $0)"),
  ( '(--start-group)',     "env.append('INPUTS', $0)"),
  ( '(--end-group)',       "env.append('INPUTS', $0)"),
  ( '(-Bstatic)',          "env.append('INPUTS', $0)"),
  ( '(-Bdynamic)',         "env.append('INPUTS', $0)"),
  # This is the file passed from llc during translation (used to be via shmem)
  ( ('--llc-translated-file=(.*)'), "env.append('INPUTS', $0)\n"
                                    "env.set('LLC_TRANSLATED_FILE', $0)"),
  ( '-split-module=([0-9]+)', "env.set('SPLIT_MODULE', $0)"),