diff options
author | sbc@chromium.org <sbc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-11-29 22:52:20 +0000 |
---|---|---|
committer | sbc@chromium.org <sbc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-11-29 22:52:20 +0000 |
commit | 087332bd38a5ab9c6e580d242be2404bb1347b5b (patch) | |
tree | 83c907a7a300f70ad226d3b2763b11c9c74ec879 /native_client_sdk | |
parent | 7660284091909d6cad79ac4b8895d7c049ace453 (diff) | |
download | chromium_src-087332bd38a5ab9c6e580d242be2404bb1347b5b.zip chromium_src-087332bd38a5ab9c6e580d242be2404bb1347b5b.tar.gz chromium_src-087332bd38a5ab9c6e580d242be2404bb1347b5b.tar.bz2 |
[NaCl SDK] remove --toolchain option from create_nmf.
This simplifies the create_nmf invocations in our example
Makefiles.
The --toolchain option was only needed to determin if an
executable is dynamic or not (e.g. if it was built with
glibc toolchain or not). With this change create_nmf now
knows enough about the elf headers to determine this
on its own.
The -t/--toolchain is now deprecated (but not removed
in case anyone was using it).
R=binji
BUG=
Review URL: https://chromiumcodereview.appspot.com/11348287
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@170267 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'native_client_sdk')
-rwxr-xr-x | native_client_sdk/src/build_tools/make_rules.py | 10 | ||||
-rwxr-xr-x | native_client_sdk/src/tools/create_nmf.py | 181 |
2 files changed, 110 insertions, 81 deletions
diff --git a/native_client_sdk/src/build_tools/make_rules.py b/native_client_sdk/src/build_tools/make_rules.py index d7f9c0e..ad5c819 100755 --- a/native_client_sdk/src/build_tools/make_rules.py +++ b/native_client_sdk/src/build_tools/make_rules.py @@ -16,7 +16,6 @@ NEWLIB_CC?=$(TC_PATH)/$(OSNAME)_x86_newlib/bin/i686-nacl-gcc -c NEWLIB_CXX?=$(TC_PATH)/$(OSNAME)_x86_newlib/bin/i686-nacl-g++ -c NEWLIB_LINK?=$(TC_PATH)/$(OSNAME)_x86_newlib/bin/i686-nacl-g++ -Wl,-as-needed NEWLIB_LIB?=$(TC_PATH)/$(OSNAME)_x86_newlib/bin/i686-nacl-ar r -NEWLIB_DUMP?=$(TC_PATH)/$(OSNAME)_x86_newlib/x86_64-nacl/bin/objdump NEWLIB_CCFLAGS?=-MMD -pthread $(NACL_WARNINGS) -idirafter $(NACL_SDK_ROOT)/include NEWLIB_LDFLAGS?=-pthread """ @@ -38,7 +37,6 @@ PNACL_CC?=$(TC_PATH)/$(OSNAME)_x86_pnacl/newlib/bin/pnacl-clang -c PNACL_CXX?=$(TC_PATH)/$(OSNAME)_x86_pnacl/newlib/bin/pnacl-clang++ -c PNACL_LINK?=$(TC_PATH)/$(OSNAME)_x86_pnacl/newlib/bin/pnacl-clang++ PNACL_LIB?=$(TC_PATH)/$(OSNAME)_x86_pnacl/newlib/bin/pnacl-ar r -PNACL_DUMP?=$(TC_PATH)/$(OSNAME)_x86_pnacl/newlib/bin/objdump PNACL_CCFLAGS?=-MMD -pthread $(NACL_WARNINGS) -idirafter $(NACL_SDK_ROOT)/include PNACL_LDFLAGS?=-pthread TRANSLATE:=$(TC_PATH)/$(OSNAME)_x86_pnacl/newlib/bin/pnacl-translate @@ -141,7 +139,7 @@ WIN_LIB_RULES = { # NMF_RULE = """ <tc>/<config>/<proj>.nmf : <NMF_TARGETS> -<TAB>$(NMF) -D $(<DUMP>) -o $@ $^ -t <tc> -s <tc>/<config> +<TAB>$(NMF) -o $@ $^ -s <tc>/<config> """ NMF_EMPTY = """ @@ -151,7 +149,7 @@ NMF_EMPTY = """ GLIBC_NMF_RULE = """ <tc>/<config>/<proj>.nmf : <NMF_TARGETS> -<TAB>$(NMF) -D $(<DUMP>) -o $@ $(GLIBC_PATHS) $^ -t <tc> -s <tc>/<config> $(GLIBC_REMAP) +<TAB>$(NMF) -D $(<OBJDUMP>) -o $@ $(GLIBC_PATHS) $^ -s <tc>/<config> $(GLIBC_REMAP) """ EXT_MAP = { @@ -428,7 +426,6 @@ class MakeRules(object): tcname = tc self.vars['<CC>'] = '%s_CC' % TC self.vars['<CXX>'] = '%s_CXX' % TC - self.vars['<DUMP>'] = '%s_DUMP' % TC self.vars['<LIB>'] = '%s_LIB' % TC self.vars['<LINK>'] = '%s_LINK' % TC self.vars['<tc>'] = tc @@ -489,10 +486,11 @@ def GenerateNMFRules(tc, main, dlls, cfg, arches): for arch in arches: replace = { '<config>' : cfg, - '<DUMP>' : '%s_DUMP' % tc.upper(), '<TAB>' : '\t', '<tc>' : tc } + if tc == 'glibc': + replace['<OBJDUMP>'] = 'GLIBC_DUMP' if '<ARCH>' in arch: replace['<ARCH>'] = arch['<ARCH>'] for dll in dlls: diff --git a/native_client_sdk/src/tools/create_nmf.py b/native_client_sdk/src/tools/create_nmf.py index 2f8654f..4ece60a 100755 --- a/native_client_sdk/src/tools/create_nmf.py +++ b/native_client_sdk/src/tools/create_nmf.py @@ -85,6 +85,91 @@ class Error(Exception): pass +def ParseElfHeader(path): + """Determine properties of a nexe by parsing elf header. + Return tuple of architecture and boolean signalling whether + the executable is dynamic (has INTERP header) or static. + """ + # From elf.h: + # typedef struct + # { + # unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ + # Elf64_Half e_type; /* Object file type */ + # Elf64_Half e_machine; /* Architecture */ + # ... + # } Elf32_Ehdr; + elf_header_format = '16s2H' + elf_header_size = struct.calcsize(elf_header_format) + + with open(path, 'rb') as f: + header = f.read(elf_header_size) + + header = struct.unpack(elf_header_format, header) + e_ident, _, e_machine = header[:3] + + elf_magic = '\x7fELF' + if e_ident[:4] != elf_magic: + raise Error("Not a valid NaCL executable: %s" % path) + + e_machine_mapping = { + 3 : 'x86-32', + 40 : 'arm', + 62 : 'x86-64' + } + if e_machine not in e_machine_mapping: + raise Error("Unknown machine type: %s" % e_machine) + + # Set arch based on the machine type in the elf header + arch = e_machine_mapping[e_machine] + + # Now read the full header in either 64bit or 32bit mode + if arch == 'x86-64': + elf_header_format = '16s2HI3lI3H' + else: + elf_header_format = '16s2HI3II3H' + + dynamic = IsDynamicElf(path, elf_header_format) + return arch, dynamic + + +def IsDynamicElf(path, elf_header_format): + """Examine an elf file to determine if it is dynamically + linked or not. + This is determined by searching the program headers for + a header of type PT_INTERP. + """ + elf_header_size = struct.calcsize(elf_header_format) + + with open(path, 'rb') as f: + header = f.read(elf_header_size) + header = struct.unpack(elf_header_format, header) + p_header_offset = header[5] + p_header_entry_size = header[9] + num_p_header = header[10] + f.seek(p_header_offset) + p_headers = f.read(p_header_entry_size*num_p_header) + + # Read the first word of each Phdr to find out its type. + # + # typedef struct + # { + # Elf32_Word p_type; /* Segment type */ + # ... + # } Elf32_Phdr; + elf_phdr_format = 'I' + PT_INTERP = 3 + + while p_headers: + p_header = p_headers[:p_header_entry_size] + p_headers = p_headers[p_header_entry_size:] + phdr_type = struct.unpack(elf_phdr_format, p_header[:4])[0] + if phdr_type == PT_INTERP: + return True + + return False + + + class ArchFile(object): '''Simple structure containing information about @@ -101,41 +186,7 @@ class ArchFile(object): self.url = url self.arch = arch if arch is None: - self._ReadElfHeader() - - def _ReadElfHeader(self): - """Determine architecture of nexe by reading elf header.""" - # From elf.h: - # typedef struct - # { - # unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ - # Elf64_Half e_type; /* Object file type */ - # Elf64_Half e_machine; /* Architecture */ - # ... - # } Elf32_Ehdr; - elf_header_format = '16s2H' - elf_header_size = struct.calcsize(elf_header_format) - - with open(self.path, 'rb') as f: - header = f.read(elf_header_size) - - header = struct.unpack(elf_header_format, header) - e_ident, _, e_machine = header - - elf_magic = '\x7fELF' - if e_ident[:4] != elf_magic: - raise Error("Not a valid NaCL executable: %s" % self.path) - - e_machine_mapping = { - 3 : 'x86-32', - 40 : 'arm', - 62 : 'x86-64' - } - if e_machine not in e_machine_mapping: - raise Error("Unknown machine type: %s" % e_machine) - - # set arch based on the machine type in the elf header - self.arch = e_machine_mapping[e_machine] + self.arch = ParseElfHeader(path)[0] def __repr__(self): return "<ArchFile %s>" % self.path @@ -155,7 +206,7 @@ class NmfUtils(object): def __init__(self, main_files=None, objdump=None, lib_path=None, extra_files=None, lib_prefix=None, - toolchain=None, remap=None): + remap=None): '''Constructor Args: @@ -167,8 +218,6 @@ class NmfUtils(object): lib_prefix: A list of path components to prepend to the library paths, both for staging the libraries and for inclusion into the nmf file. Examples: ['..'], ['lib_dir'] - toolchain: Specify which toolchain newlib|glibc|pnacl which can require - different forms of the NMF. remap: Remaps the library name in the manifest. ''' self.objdump = objdump @@ -178,10 +227,8 @@ class NmfUtils(object): self.manifest = None self.needed = {} self.lib_prefix = lib_prefix or [] - self.toolchain = toolchain self.remap = remap or {} - def GleanFromObjdump(self, files): '''Get architecture and dependency information for given files @@ -209,11 +256,15 @@ class NmfUtils(object): input_info = {} needed = set() output, err_output = proc.communicate() + if proc.returncode: + raise Error('%s\nStdError=%s\nobjdump failed with error code: %d' % + (output, err_output, proc.returncode)) + for line in output.splitlines(True): # Objdump should display the architecture first and then the dependencies # second for each file in the list. matched = FormatMatcher.match(line) - if matched is not None: + if matched: filename = matched.group(1) arch = OBJDUMP_ARCH_MAP[matched.group(2)] if files[filename] is None or arch in files[filename]: @@ -224,13 +275,9 @@ class NmfUtils(object): path=filename, url='/'.join(self.lib_prefix + [ARCH_LOCATION[arch], name])) matched = NeededMatcher.match(line) - if matched is not None: + if matched: if files[filename] is None or arch in files[filename]: needed.add('/'.join([arch, matched.group(1)])) - status = proc.poll() - if status != 0: - raise Error('%s\nStdError=%s\nobjdump failed with error code: %d' % - (output, err_output, status)) return input_info, needed def FindLibsInPath(self, name): @@ -261,10 +308,11 @@ class NmfUtils(object): if self.needed: return self.needed - runnable = (self.toolchain != 'newlib' and self.toolchain != 'pnacl') DebugPrint('GetNeeded(%s)' % self.main_files) - if runnable: + dynamic = any(ParseElfHeader(f)[1] for f in self.main_files) + + if dynamic: examined = set() all_files, unexamined = self.GleanFromObjdump( dict([(f, None) for f in self.main_files])) @@ -335,9 +383,11 @@ class NmfUtils(object): FILES key mapped as 'main.exe' instead of it's original name so that the loader can find it.''' manifest = { FILES_KEY: {}, PROGRAM_KEY: {} } - runnable = (self.toolchain != 'newlib' and self.toolchain != 'pnacl') needed = self.GetNeeded() + + runnable = any(n.endswith(RUNNABLE_LD) for n in needed) + for need, archinfo in needed.items(): urlinfo = { URL_KEY: archinfo.url } name = archinfo.name @@ -383,19 +433,6 @@ class NmfUtils(object): return '\n'.join([line.rstrip() for line in pretty_lines]) + '\n' -def DetermineToolchain(objdump): - objdump = objdump.replace('\\', '/') - paths = objdump.split('/') - count = len(paths) - for index in range(count - 2, 0, -1): - if paths[index] == 'toolchain': - if paths[index + 1].endswith('newlib'): - return 'newlib' - if paths[index + 1].endswith('glibc'): - return 'glibc' - raise Error('Could not deternime which toolchain to use.') - - def Trace(msg): if Trace.verbose: sys.stderr.write(str(msg) + '\n') @@ -403,7 +440,7 @@ def Trace(msg): Trace.verbose = False -def Main(argv): +def main(argv): parser = optparse.OptionParser( usage='Usage: %prog [options] nexe [extra_libs...]') parser.add_option('-o', '--output', dest='output', @@ -425,9 +462,7 @@ def Main(argv): parser.add_option('-r', '--remove', dest='remove', help='Remove the prefix from the files.', metavar='PATH') - parser.add_option('-t', '--toolchain', dest='toolchain', - help='Add DIRECTORY to library search path', - default=None, metavar='TOOLCHAIN') + parser.add_option('-t', '--toolchain', help='Legacy option, do not use') parser.add_option('-n', '--name', dest='name', help='Rename FOO as BAR', action='append', default=[], metavar='FOO,BAR') @@ -435,21 +470,18 @@ def Main(argv): help='Verbose output', action='store_true') parser.add_option('-d', '--debug-mode', help='Debug mode', action='store_true') - (options, args) = parser.parse_args(argv) + options, args = parser.parse_args(argv) if options.verbose: Trace.verbose = True if options.debug_mode: DebugPrint.debug_mode = True + if options.toolchain is not None: + print "warning: option -t/--toolchain is deprecated." + if len(args) < 1: raise Error("No nexe files specified. See --help for more info") - if not options.toolchain: - options.toolchain = DetermineToolchain(os.path.abspath(options.objdump)) - - if options.toolchain not in ['newlib', 'glibc', 'pnacl']: - raise Error('Unknown toolchain: ' + str(options.toolchain)) - remap = {} for ren in options.name: parts = ren.split(',') @@ -466,7 +498,6 @@ def Main(argv): main_files=args, lib_path=options.lib_path, lib_prefix=path_prefix, - toolchain=options.toolchain, remap=remap) nmf.GetManifest() @@ -486,7 +517,7 @@ def Main(argv): # Invoke this file directly for simple testing. if __name__ == '__main__': try: - rtn = Main(sys.argv[1:]) + rtn = main(sys.argv[1:]) except Error, e: sys.stderr.write("%s: %s\n" % (os.path.basename(__file__), e)) rtn = 1 |