7e6f552e89a5fe68736425fed642b30f14f10bc4
[platform/framework/web/crosswalk.git] / src / native_client / pnacl / driver / pnacl-nativeld.py
1 #!/usr/bin/python
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.
5 #
6 #
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.
10 #
11 # All inputs must be native objects or linker scripts.
12 #
13 # --pnacl-sb will cause the sandboxed LD to be used.
14 # The bulk of this file is logic to invoke the sandboxed translator.
15
16 from driver_tools import *
17 from driver_env import env
18 from driver_log import Log
19
20 import subprocess
21
22 EXTRA_ENV = {
23   'INPUTS'   : '',
24   'OUTPUT'   : '',
25
26   # the INPUTS file coming from the llc translation step
27   'LLC_TRANSLATED_FILE' : '',
28   'SPLIT_MODULE' : '0',
29   'USE_STDLIB': '1',
30
31   # Determine if we should build nexes compatible with the IRT.
32   'USE_IRT' : '1',
33
34   # Upstream gold has the segment gap built in, but the gap can be modified
35   # when not using the IRT. The gap does need to be at least one bundle so the
36   # halt sled can be added for the TCB in case the segment ends up being a
37   # multiple of 64k.
38   # --eh-frame-hdr asks the linker to generate an .eh_frame_hdr section,
39   # which is a presorted list of registered frames. This section is
40   # used by libgcc_eh/libgcc_s to avoid doing the sort during runtime.
41   # http://www.airs.com/blog/archives/462
42   #
43   # BE CAREFUL: anything added to LD_FLAGS should be synchronized with
44   # flags used by the in-browser translator.
45   # See: binutils/gold/nacl_file.cc
46   'LD_FLAGS'    : '-nostdlib ' +
47                   # Only relevant for ARM where it suppresses a warning.
48                   # Ignored for other archs.
49                   '--no-fix-cortex-a8 ' +
50                   '-m ${LD_EMUL} ' +
51                   '--eh-frame-hdr ' +
52                   '${ARCH == X8632_NONSFI ? -pie : -static} ' +
53                   # "_begin" allows a PIE to find its load address in
54                   # order to apply dynamic relocations.
55                   '${ARCH == X8632_NONSFI ? -defsym=_begin=0} ' +
56                   # Give an error if any TEXTRELs occur.
57                   '-z text ' +
58                   '--build-id ' +
59                   '${!USE_IRT ? --rosegment-gap=32}',
60
61   'LD_EMUL'        : '${LD_EMUL_%ARCH%}',
62   'LD_EMUL_ARM'    : 'armelf_nacl',
63   'LD_EMUL_X8632'  : 'elf_nacl',
64   'LD_EMUL_X8632_NONSFI': 'elf_nacl',
65   'LD_EMUL_X8664'  : 'elf64_nacl',
66   'LD_EMUL_MIPS32' : 'elf32ltsmip_nacl',
67
68   'SEARCH_DIRS'        : '${SEARCH_DIRS_USER} ${SEARCH_DIRS_BUILTIN}',
69   'SEARCH_DIRS_USER'   : '',
70   'SEARCH_DIRS_BUILTIN': '${USE_STDLIB ? ${LIBS_ARCH}/}',
71
72   'LIBS_ARCH'        : '${LIBS_%ARCH%}',
73   'LIBS_ARM'         : '${BASE_LIB_NATIVE}arm',
74   'LIBS_X8632'       : '${BASE_LIB_NATIVE}x86-32',
75   'LIBS_X8632_NONSFI': '${BASE_LIB_NATIVE}x86-32-nonsfi',
76   'LIBS_X8664'       : '${BASE_LIB_NATIVE}x86-64',
77   'LIBS_MIPS32'      : '${BASE_LIB_NATIVE}mips32',
78
79   # Note: this is only used in the unsandboxed case
80   'RUN_LD' : '${LD} ${LD_FLAGS} ${inputs} -o ${output}'
81 }
82
83 def PassThrough(*args):
84   env.append('LD_FLAGS', *args)
85
86 LDPatterns = [
87   ( '-o(.+)',          "env.set('OUTPUT', pathtools.normalize($0))"),
88   ( ('-o', '(.+)'),    "env.set('OUTPUT', pathtools.normalize($0))"),
89
90   ( '--noirt',                      "env.set('USE_IRT', '0')"),
91
92   ( '-static',         "env.set('STATIC', '1')"),
93   ( '-nostdlib',       "env.set('USE_STDLIB', '0')"),
94
95   ( '-L(.+)',
96     "env.append('SEARCH_DIRS_USER', pathtools.normalize($0))"),
97   ( ('-L', '(.*)'),
98     "env.append('SEARCH_DIRS_USER', pathtools.normalize($0))"),
99
100   # Note: we do not yet support all the combinations of flags which affect
101   # layout of the various sections and segments because the corner cases in gold
102   # may not all be worked out yet. They can be added (and tested!) as needed.
103   ( ('(-Ttext=.*)'),              PassThrough),
104   ( ('(-Trodata=.*)'),            PassThrough),
105   ( ('(-Ttext-segment=.*)'),      PassThrough),
106   ( ('(-Trodata-segment=.*)'),    PassThrough),
107   ( ('(--section-start)', '(.+)'),PassThrough),
108   ( ('(--section-start=.*)'),     PassThrough),
109   ( ('(-e)','(.*)'),              PassThrough),
110   ( '(--entry=.*)',               PassThrough),
111   ( '(-M)',                       PassThrough),
112   ( '(-t)',                       PassThrough),
113   ( ('-y','(.*)'),                PassThrough),
114   ( ('(-defsym)','(.*)'),         PassThrough),
115   ( '-export-dynamic',            PassThrough),
116
117   ( '(--print-gc-sections)',      PassThrough),
118   ( '(--gc-sections)',            PassThrough),
119   ( '(--unresolved-symbols=.*)',  PassThrough),
120   ( '(--dynamic-linker=.*)',      PassThrough),
121   ( '(-g)',                       PassThrough),
122   ( '(--build-id)',               PassThrough),
123
124   ( '-melf_nacl',            "env.set('ARCH', 'X8632')"),
125   ( ('-m','elf_nacl'),       "env.set('ARCH', 'X8632')"),
126   ( '-melf64_nacl',          "env.set('ARCH', 'X8664')"),
127   ( ('-m','elf64_nacl'),     "env.set('ARCH', 'X8664')"),
128   ( '-marmelf_nacl',         "env.set('ARCH', 'ARM')"),
129   ( ('-m','armelf_nacl'),    "env.set('ARCH', 'ARM')"),
130   ( '-mmipselelf_nacl',      "env.set('ARCH', 'MIPS32')"),
131   ( ('-m','mipselelf_nacl'), "env.set('ARCH', 'MIPS32')"),
132
133   # Inputs and options that need to be kept in order
134   ( '(--no-as-needed)',    "env.append('INPUTS', $0)"),
135   ( '(--as-needed)',       "env.append('INPUTS', $0)"),
136   ( '(--start-group)',     "env.append('INPUTS', $0)"),
137   ( '(--end-group)',       "env.append('INPUTS', $0)"),
138   ( '(-Bstatic)',          "env.append('INPUTS', $0)"),
139   ( '(-Bdynamic)',         "env.append('INPUTS', $0)"),
140   # This is the file passed from llc during translation (used to be via shmem)
141   ( ('--llc-translated-file=(.*)'), "env.append('INPUTS', $0)\n"
142                                     "env.set('LLC_TRANSLATED_FILE', $0)"),
143   ( '-split-module=([0-9]+)', "env.set('SPLIT_MODULE', $0)"),
144   ( '(--(no-)?whole-archive)', "env.append('INPUTS', $0)"),
145
146   ( '(-l.*)',              "env.append('INPUTS', $0)"),
147   ( '(--undefined=.*)',    "env.append('INPUTS', $0)"),
148
149   ( '(-.*)',               UnrecognizedOption),
150   ( '(.*)',                "env.append('INPUTS', pathtools.normalize($0))"),
151 ]
152
153
154 def main(argv):
155   env.update(EXTRA_ENV)
156
157   ParseArgs(argv, LDPatterns)
158
159   GetArch(required=True)
160   inputs = env.get('INPUTS')
161   output = env.getone('OUTPUT')
162
163   if output == '':
164     output = pathtools.normalize('a.out')
165
166   # Expand all parameters
167   # This resolves -lfoo into actual filenames,
168   # and expands linker scripts into command-line arguments.
169   inputs = ldtools.ExpandInputs(inputs,
170                                 env.get('SEARCH_DIRS'),
171                                 env.getbool('STATIC'),
172                                 ldtools.LibraryTypes.NATIVE)
173
174   env.push()
175   env.set('inputs', *inputs)
176   env.set('output', output)
177
178   if env.getbool('SANDBOXED'):
179     RunLDSandboxed()
180   else:
181     Run('${RUN_LD}')
182   env.pop()
183   # only reached in case of no errors
184   return 0
185
186 def IsFlag(arg):
187   return arg.startswith('-')
188
189 def RunLDSandboxed():
190   if not env.getbool('USE_STDLIB'):
191     Log.Fatal('-nostdlib is not supported by the sandboxed translator')
192   CheckTranslatorPrerequisites()
193   # The "main" input file is the application's combined object file.
194   all_inputs = env.get('inputs')
195
196   main_input = env.getone('LLC_TRANSLATED_FILE')
197   if not main_input:
198     Log.Fatal("Sandboxed LD requires one shm input file")
199
200   outfile = env.getone('output')
201
202   modules = int(env.getone('SPLIT_MODULE'))
203   if modules > 1:
204     first_mainfile = all_inputs.index(main_input)
205     first_extra = all_inputs.index(main_input) + modules
206     # Just the split module files
207     llc_outputs = all_inputs[first_mainfile:first_extra]
208     # everything else
209     all_inputs = all_inputs[:first_mainfile] + all_inputs[first_extra:]
210   else:
211     llc_outputs = [main_input]
212
213   files = LinkerFiles(all_inputs)
214   ld_flags = env.get('LD_FLAGS')
215
216   script = MakeSelUniversalScriptForLD(ld_flags,
217                                        llc_outputs,
218                                        files,
219                                        outfile)
220
221
222   Run('${SEL_UNIVERSAL_PREFIX} ${SEL_UNIVERSAL} ' +
223       '${SEL_UNIVERSAL_FLAGS} -- ${LD_SB}',
224       stdin_contents=script,
225       # stdout/stderr will be automatically dumped
226       # upon failure
227       redirect_stderr=subprocess.PIPE,
228       redirect_stdout=subprocess.PIPE)
229
230
231 def MakeSelUniversalScriptForLD(ld_flags,
232                                 llc_outputs,
233                                 files,
234                                 outfile):
235   """ Return sel_universal script text for invoking LD.nexe with the
236   given ld_flags, llc_outputs (which are treated specially), and
237   other input files (for native libraries). The output will be written
238   to outfile.  """
239   script = []
240
241   # Open the output file.
242   script.append('readwrite_file nexefile %s' % outfile)
243
244   files_to_map = list(files)
245   # Create a reverse-service mapping for each input file and add it to
246   # the sel universal script.
247   for f in files_to_map:
248     basename = pathtools.basename(f)
249     # If we are using the dummy shim, map it with the filename of the real
250     # shim, so the baked-in commandline will work.
251     if basename == 'libpnacl_irt_shim_dummy.a':
252       basename = 'libpnacl_irt_shim.a'
253     script.append('reverse_service_add_manifest_mapping files/%s %s' %
254                   (basename, f))
255
256   modules = len(llc_outputs)
257   script.extend(['readonly_file objfile%d %s' % (i, f)
258                  for i, f in zip(range(modules), llc_outputs)])
259   script.append('rpc RunWithSplit i(%d) ' % modules +
260                 ' '.join(['h(objfile%s)' % m for m in range(modules)] +
261                          ['h(invalid)' for x in range(modules, 16)]) +
262                 ' h(nexefile) *')
263   script.append('echo "ld complete"')
264   script.append('')
265   return '\n'.join(script)
266
267 # Given linker arguments (including -L, -l, and filenames),
268 # returns the list of files which are pulled by the linker,
269 # with real path names set set up the real -> flat name mapping.
270 def LinkerFiles(args):
271   ret = []
272   for f in args:
273     if IsFlag(f):
274       continue
275     else:
276       if not pathtools.exists(f):
277         Log.Fatal("Unable to open '%s'", pathtools.touser(f))
278       ret.append(f)
279   return ret