2 # Copyright (c) 2012 The Native Client Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
7 # This is a thin wrapper for native LD. This is not meant to be
8 # used by the user, but is called from pnacl-translate.
9 # This implements the native linking part of translation.
11 # All inputs must be native objects or linker scripts.
13 # --pnacl-sb will cause the sandboxed LD to be used.
14 # The bulk of this file is logic to invoke the sandboxed translator.
18 from driver_tools import CheckTranslatorPrerequisites, GetArch, ParseArgs, \
19 Run, UnrecognizedOption
20 from driver_env import env
21 from driver_log import Log
30 # the INPUTS file coming from the llc translation step
31 'LLC_TRANSLATED_FILE' : '',
35 # Determine if we should build nexes compatible with the IRT.
38 # Upstream gold has the segment gap built in, but the gap can be modified
39 # when not using the IRT. The gap does need to be at least one bundle so the
40 # halt sled can be added for the TCB in case the segment ends up being a
42 # --eh-frame-hdr asks the linker to generate an .eh_frame_hdr section,
43 # which is a presorted list of registered frames. This section is
44 # used by libgcc_eh/libgcc_s to avoid doing the sort during runtime.
45 # http://www.airs.com/blog/archives/462
47 # BE CAREFUL: anything added to LD_FLAGS should be synchronized with
48 # flags used by the in-browser translator.
49 # See: binutils/gold/nacl_file.cc
50 'LD_FLAGS' : '-nostdlib ' +
51 # Only relevant for ARM where it suppresses a warning.
52 # Ignored for other archs.
53 '--no-fix-cortex-a8 ' +
56 '${NONSFI_NACL ? -pie : -static} ' +
57 # "_begin" allows a PIE to find its load address in
58 # order to apply dynamic relocations.
59 '${NONSFI_NACL ? -defsym=_begin=0} ' +
60 # Give an error if any TEXTRELs occur.
63 # Give non-IRT builds 12MB of text before starting rodata
64 # instead of the larger default gap. The gap cannot be
65 # too small (e.g., 0) because sel_ldr requires space for
67 '${!USE_IRT ? --rosegment-gap=0xc00000}',
69 'LD_EMUL' : '${LD_EMUL_%BASE_ARCH%}',
70 'LD_EMUL_ARM' : 'armelf_nacl',
71 'LD_EMUL_X8632' : 'elf_nacl',
72 'LD_EMUL_X8664' : 'elf64_nacl',
73 'LD_EMUL_MIPS32' : 'elf32ltsmip_nacl',
75 'SEARCH_DIRS' : '${SEARCH_DIRS_USER} ${SEARCH_DIRS_BUILTIN}',
76 'SEARCH_DIRS_USER' : '',
77 'SEARCH_DIRS_BUILTIN': '${USE_STDLIB ? ${LIBS_ARCH}/}',
79 'LIBS_ARCH' : '${LIBS_%ARCH%}',
80 'LIBS_ARM' : '${BASE_LIB_NATIVE}arm',
81 'LIBS_ARM_NONSFI' : '${BASE_LIB_NATIVE}arm-nonsfi',
82 'LIBS_X8632' : '${BASE_LIB_NATIVE}x86-32',
83 'LIBS_X8632_NONSFI': '${BASE_LIB_NATIVE}x86-32-nonsfi',
84 'LIBS_X8664' : '${BASE_LIB_NATIVE}x86-64',
85 'LIBS_MIPS32' : '${BASE_LIB_NATIVE}mips32',
87 # Note: this is only used in the unsandboxed case
88 'RUN_LD' : '${LD} ${LD_FLAGS} ${inputs} -o ${output}'
91 def PassThrough(*args):
92 env.append('LD_FLAGS', *args)
95 ( '-o(.+)', "env.set('OUTPUT', pathtools.normalize($0))"),
96 ( ('-o', '(.+)'), "env.set('OUTPUT', pathtools.normalize($0))"),
98 ( '--noirt', "env.set('USE_IRT', '0')"),
100 ( '-static', "env.set('STATIC', '1')"),
101 ( '-nostdlib', "env.set('USE_STDLIB', '0')"),
104 "env.append('SEARCH_DIRS_USER', pathtools.normalize($0))"),
106 "env.append('SEARCH_DIRS_USER', pathtools.normalize($0))"),
108 # Note: we do not yet support all the combinations of flags which affect
109 # layout of the various sections and segments because the corner cases in gold
110 # may not all be worked out yet. They can be added (and tested!) as needed.
111 ( ('(-Ttext=.*)'), PassThrough),
112 ( ('(-Trodata=.*)'), PassThrough),
113 ( ('(-Ttext-segment=.*)'), PassThrough),
114 ( ('(-Trodata-segment=.*)'), PassThrough),
115 ( ('(--section-start)', '(.+)'),PassThrough),
116 ( ('(--section-start=.*)'), PassThrough),
117 ( ('(-e)','(.*)'), PassThrough),
118 ( '(--entry=.*)', PassThrough),
119 ( '(-M)', PassThrough),
120 ( '(-t)', PassThrough),
121 ( ('-y','(.*)'), PassThrough),
122 ( ('(-defsym)','(.*)'), PassThrough),
123 ( '-export-dynamic', PassThrough),
125 ( '(--print-gc-sections)', PassThrough),
126 ( '(--gc-sections)', PassThrough),
127 ( '(--unresolved-symbols=.*)', PassThrough),
128 ( '(--dynamic-linker=.*)', PassThrough),
129 ( '(-g)', PassThrough),
130 ( '(--build-id)', PassThrough),
132 ( '-melf_nacl', "env.set('ARCH', 'X8632')"),
133 ( ('-m','elf_nacl'), "env.set('ARCH', 'X8632')"),
134 ( '-melf64_nacl', "env.set('ARCH', 'X8664')"),
135 ( ('-m','elf64_nacl'), "env.set('ARCH', 'X8664')"),
136 ( '-marmelf_nacl', "env.set('ARCH', 'ARM')"),
137 ( ('-m','armelf_nacl'), "env.set('ARCH', 'ARM')"),
138 ( '-mmipselelf_nacl', "env.set('ARCH', 'MIPS32')"),
139 ( ('-m','mipselelf_nacl'), "env.set('ARCH', 'MIPS32')"),
141 # Inputs and options that need to be kept in order
142 ( '(--no-as-needed)', "env.append('INPUTS', $0)"),
143 ( '(--as-needed)', "env.append('INPUTS', $0)"),
144 ( '(--start-group)', "env.append('INPUTS', $0)"),
145 ( '(--end-group)', "env.append('INPUTS', $0)"),
146 ( '(-Bstatic)', "env.append('INPUTS', $0)"),
147 ( '(-Bdynamic)', "env.append('INPUTS', $0)"),
148 # This is the file passed from llc during translation (used to be via shmem)
149 ( ('--llc-translated-file=(.*)'), "env.append('INPUTS', $0)\n"
150 "env.set('LLC_TRANSLATED_FILE', $0)"),
151 ( '-split-module=([0-9]+)', "env.set('SPLIT_MODULE', $0)"),
152 ( '(--(no-)?whole-archive)', "env.append('INPUTS', $0)"),
154 ( '(-l.*)', "env.append('INPUTS', $0)"),
155 ( '(--undefined=.*)', "env.append('INPUTS', $0)"),
157 ( '(-.*)', UnrecognizedOption),
158 ( '(.*)', "env.append('INPUTS', pathtools.normalize($0))"),
163 env.update(EXTRA_ENV)
165 ParseArgs(argv, LDPatterns)
167 GetArch(required=True)
168 inputs = env.get('INPUTS')
169 output = env.getone('OUTPUT')
172 output = pathtools.normalize('a.out')
174 # Expand all parameters
175 # This resolves -lfoo into actual filenames,
176 # and expands linker scripts into command-line arguments.
177 inputs = ldtools.ExpandInputs(inputs,
178 env.get('SEARCH_DIRS'),
179 env.getbool('STATIC'),
180 ldtools.LibraryTypes.NATIVE)
183 env.set('inputs', *inputs)
184 env.set('output', output)
186 if env.getbool('SANDBOXED'):
191 # only reached in case of no errors
195 return arg.startswith('-')
197 def RunLDSandboxed():
198 if not env.getbool('USE_STDLIB'):
199 Log.Fatal('-nostdlib is not supported by the sandboxed translator')
200 CheckTranslatorPrerequisites()
201 # The "main" input file is the application's combined object file.
202 all_inputs = env.get('inputs')
204 main_input = env.getone('LLC_TRANSLATED_FILE')
206 Log.Fatal("Sandboxed LD requires one shm input file")
208 outfile = env.getone('output')
210 modules = int(env.getone('SPLIT_MODULE'))
212 first_mainfile = all_inputs.index(main_input)
213 first_extra = all_inputs.index(main_input) + modules
214 # Just the split module files
215 llc_outputs = all_inputs[first_mainfile:first_extra]
217 all_inputs = all_inputs[:first_mainfile] + all_inputs[first_extra:]
219 llc_outputs = [main_input]
221 files = LinkerFiles(all_inputs)
222 ld_flags = env.get('LD_FLAGS')
224 script = MakeSelUniversalScriptForLD(ld_flags,
230 Run('${SEL_UNIVERSAL_PREFIX} ${SEL_UNIVERSAL} ' +
231 '${SEL_UNIVERSAL_FLAGS} -- ${LD_SB}',
232 stdin_contents=script,
233 # stdout/stderr will be automatically dumped
235 redirect_stderr=subprocess.PIPE,
236 redirect_stdout=subprocess.PIPE)
239 def MakeSelUniversalScriptForLD(ld_flags,
243 """ Return sel_universal script text for invoking LD.nexe with the
244 given ld_flags, llc_outputs (which are treated specially), and
245 other input files (for native libraries). The output will be written
249 # Open the output file.
250 script.append('readwrite_file nexefile %s' % outfile)
252 files_to_map = list(files)
253 # Create a reverse-service mapping for each input file and add it to
254 # the sel universal script.
255 for f in files_to_map:
256 basename = pathtools.basename(f)
257 # If we are using the dummy shim, map it with the filename of the real
258 # shim, so the baked-in commandline will work.
259 if basename == 'libpnacl_irt_shim_dummy.a':
260 basename = 'libpnacl_irt_shim.a'
261 script.append('reverse_service_add_manifest_mapping files/%s %s' %
264 modules = len(llc_outputs)
265 script.extend(['readonly_file objfile%d %s' % (i, f)
266 for i, f in zip(range(modules), llc_outputs)])
267 script.append('rpc RunWithSplit i(%d) ' % modules +
268 ' '.join(['h(objfile%s)' % m for m in range(modules)] +
269 ['h(invalid)' for x in range(modules, 16)]) +
271 script.append('echo "ld complete"')
273 return '\n'.join(script)
275 # Given linker arguments (including -L, -l, and filenames),
276 # returns the list of files which are pulled by the linker,
277 # with real path names set set up the real -> flat name mapping.
278 def LinkerFiles(args):
284 if not pathtools.exists(f):
285 Log.Fatal("Unable to open '%s'", pathtools.touser(f))