Refactor python shared snippets
This commit is contained in:
parent
066f1f06c1
commit
789e79613c
4 changed files with 126 additions and 96 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -32,3 +32,4 @@
|
|||
*.app
|
||||
|
||||
dwarf-assembly
|
||||
__pycache__
|
||||
|
|
|
@ -9,7 +9,7 @@ import os
|
|||
import subprocess
|
||||
from collections import namedtuple
|
||||
|
||||
from generate_eh_elf import elf_so_deps
|
||||
from shared_python import elf_so_deps
|
||||
|
||||
|
||||
''' An ELF object, including the path to the ELF itself, and the path to its
|
||||
|
@ -41,11 +41,11 @@ def section_size(elf_loc, section_name):
|
|||
except subprocess.CalledProcessError as exn:
|
||||
raise Exception(("Cannot obtain section {} size for {}: objdump "
|
||||
"terminated with exit code {}.").format(
|
||||
section_name, elf_loc))
|
||||
section_name, elf_loc, exn.returncode))
|
||||
|
||||
for line in objdump_out.split('\n'):
|
||||
line = line.strip()
|
||||
if not line or not ('0' <= line[0] <= '9'): # not a section line
|
||||
if not line or not '0' <= line[0] <= '9': # not a section line
|
||||
continue
|
||||
|
||||
spl = line.split()
|
||||
|
|
|
@ -9,10 +9,10 @@ all the shared objects it depends upon.
|
|||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
import re
|
||||
import tempfile
|
||||
import argparse
|
||||
from collections import namedtuple
|
||||
|
||||
from shared_python import elf_so_deps, do_remote, is_newer
|
||||
|
||||
|
||||
DWARF_ASSEMBLY_BIN = os.path.join(
|
||||
|
@ -23,33 +23,6 @@ C_BIN = (
|
|||
else os.environ['C'])
|
||||
|
||||
|
||||
def elf_so_deps(path):
|
||||
''' Get the list of shared objects dependencies of the given ELF object.
|
||||
This is obtained by running `ldd`. '''
|
||||
|
||||
deps_list = []
|
||||
|
||||
try:
|
||||
ldd_output = subprocess.check_output(['/usr/bin/ldd', path]) \
|
||||
.decode('utf-8')
|
||||
ldd_re = re.compile(r'^.* => (.*) \(0x[0-9a-fA-F]*\)$')
|
||||
|
||||
ldd_lines = ldd_output.strip().split('\n')
|
||||
for line in ldd_lines:
|
||||
line = line.strip()
|
||||
match = ldd_re.match(line)
|
||||
if match is None:
|
||||
continue # Just ignore that line — it might be eg. linux-vdso
|
||||
deps_list.append(match.group(1))
|
||||
|
||||
return deps_list
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
raise Exception(
|
||||
("Cannot get dependencies for {}: ldd terminated with exit code "
|
||||
"{}.").format(path, e.returncode))
|
||||
|
||||
|
||||
def gen_dw_asm_c(obj_path, out_path, dwarf_assembly_args):
|
||||
''' Generate the C code produced by dwarf-assembly from `obj_path`, saving
|
||||
it as `out_path` '''
|
||||
|
@ -61,66 +34,13 @@ def gen_dw_asm_c(obj_path, out_path, dwarf_assembly_args):
|
|||
[DWARF_ASSEMBLY_BIN, obj_path] + dwarf_assembly_args) \
|
||||
.decode('utf-8')
|
||||
out_handle.write(dw_asm_output)
|
||||
except subprocess.CalledProcessError as e:
|
||||
except subprocess.CalledProcessError as exn:
|
||||
raise Exception(
|
||||
("Cannot generate C code from object file {} using {}: process "
|
||||
"terminated with exit code {}.").format(
|
||||
obj_path,
|
||||
DWARF_ASSEMBLY_BIN,
|
||||
e.returncode))
|
||||
|
||||
|
||||
def do_remote(remote, command, send_files=None, retr_files=None):
|
||||
def ssh_do(cmd_args, working_directory=None):
|
||||
try:
|
||||
cmd = ['ssh', remote]
|
||||
if working_directory:
|
||||
cmd += ['cd', working_directory, '&&']
|
||||
cmd += cmd_args
|
||||
|
||||
return subprocess.check_output(cmd).decode('utf-8').strip()
|
||||
except subprocess.CalledProcessError as e:
|
||||
return None
|
||||
|
||||
def ssh_copy(what, where, is_upload):
|
||||
if is_upload:
|
||||
where = '{}:{}'.format(remote, where)
|
||||
else:
|
||||
what = '{}:{}'.format(remote, what)
|
||||
|
||||
subprocess.check_output(['scp', what, where])
|
||||
|
||||
TransferredFile = namedtuple('TransferredFile', 'local remote')
|
||||
|
||||
def interpret_transferred_file(descr):
|
||||
if type(descr) == type(''):
|
||||
return TransferredFile(descr, descr)
|
||||
if os.path.isdir(descr[1]):
|
||||
to = os.path.join(descr[1], descr[0])
|
||||
else:
|
||||
to = descr[1]
|
||||
return TransferredFile(descr[0], to)
|
||||
|
||||
# Create temp dir
|
||||
tmp_dir = ssh_do(['mktemp', '-d'])
|
||||
|
||||
# Upload `send_files`
|
||||
for f in send_files:
|
||||
dest = tmp_dir+'/'
|
||||
ssh_copy(f, dest, is_upload=True)
|
||||
|
||||
# Do whatever must be done
|
||||
output = ssh_do(command, working_directory=tmp_dir)
|
||||
|
||||
# Download `retr_files`
|
||||
for f in map(interpret_transferred_file, retr_files):
|
||||
src = os.path.join(tmp_dir, f.local)
|
||||
ssh_copy(src, f.remote, is_upload=False)
|
||||
|
||||
# Remove temp dir
|
||||
ssh_do(['rm', '-rf', tmp_dir])
|
||||
|
||||
return output
|
||||
exn.returncode))
|
||||
|
||||
|
||||
def gen_eh_elf(obj_path, args, dwarf_assembly_args=None):
|
||||
|
@ -141,15 +61,8 @@ def gen_eh_elf(obj_path, args, dwarf_assembly_args=None):
|
|||
out_base_name = os.path.basename(obj_path) + '.eh_elf'
|
||||
out_so_path = os.path.join(out_dir, (out_base_name + '.so'))
|
||||
|
||||
try:
|
||||
so_mtime = os.path.getmtime(out_so_path)
|
||||
obj_mtime = os.path.getmtime(obj_path)
|
||||
|
||||
if obj_mtime < so_mtime:
|
||||
return # The object is recent enough, no need to recreate it
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
if is_newer(out_so_path, obj_path):
|
||||
return # The object is recent enough, no need to recreate it
|
||||
|
||||
with tempfile.TemporaryDirectory() as compile_dir:
|
||||
# Generate the C source file
|
||||
|
|
116
shared_python.py
Normal file
116
shared_python.py
Normal file
|
@ -0,0 +1,116 @@
|
|||
""" Shared snippets between the various python scripts of this project """
|
||||
|
||||
import subprocess
|
||||
import re
|
||||
import os
|
||||
from collections import namedtuple
|
||||
|
||||
|
||||
def is_newer(file1, file2):
|
||||
''' Returns True iff file1 is newer than file2 '''
|
||||
try:
|
||||
f1_mtime = os.path.getmtime(file1)
|
||||
f2_mtime = os.path.getmtime(file2)
|
||||
|
||||
if f1_mtime > f2_mtime:
|
||||
return True
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def elf_so_deps(path):
|
||||
''' Get the list of shared objects dependencies of the given ELF object.
|
||||
This is obtained by running `ldd`. '''
|
||||
|
||||
deps_list = []
|
||||
|
||||
try:
|
||||
ldd_output = subprocess.check_output(['/usr/bin/ldd', path]) \
|
||||
.decode('utf-8')
|
||||
ldd_re = re.compile(r'^.* => (.*) \(0x[0-9a-fA-F]*\)$')
|
||||
|
||||
ldd_lines = ldd_output.strip().split('\n')
|
||||
for line in ldd_lines:
|
||||
line = line.strip()
|
||||
match = ldd_re.match(line)
|
||||
if match is None:
|
||||
continue # Just ignore that line — it might be eg. linux-vdso
|
||||
deps_list.append(match.group(1))
|
||||
|
||||
return deps_list
|
||||
|
||||
except subprocess.CalledProcessError as exn:
|
||||
raise Exception(
|
||||
("Cannot get dependencies for {}: ldd terminated with exit code "
|
||||
"{}.").format(path, exn.returncode))
|
||||
|
||||
|
||||
def do_remote(remote, command, send_files=None, retr_files=None):
|
||||
''' Execute remotely (via ssh) a given command
|
||||
|
||||
The command is executed on the machine described by `remote` (see ssh(1)).
|
||||
|
||||
send_files is a list of file paths that must be first copied at the root of
|
||||
a temporary directory on `remote` before running the command. Consider
|
||||
yourself jailed in that directory.
|
||||
|
||||
retr_files is a list of files that will be copied to the local machine
|
||||
after the command is executed. Each list item can either be a string, which
|
||||
is both the path on the remote and the local machine; or a pair
|
||||
`(file_name, local_path)`. In the latter case, `file_name` is copied as
|
||||
`local_path/file_name` if `local_path` is a directory, or as `local_path`
|
||||
otherwise, on the local machine.
|
||||
'''
|
||||
|
||||
def ssh_do(cmd_args, working_directory=None):
|
||||
try:
|
||||
cmd = ['ssh', remote]
|
||||
if working_directory:
|
||||
cmd += ['cd', working_directory, '&&']
|
||||
cmd += cmd_args
|
||||
|
||||
return subprocess.check_output(cmd).decode('utf-8').strip()
|
||||
except subprocess.CalledProcessError as e:
|
||||
return None
|
||||
|
||||
def ssh_copy(what, where, is_upload):
|
||||
if is_upload:
|
||||
where = '{}:{}'.format(remote, where)
|
||||
else:
|
||||
what = '{}:{}'.format(remote, what)
|
||||
|
||||
subprocess.check_output(['scp', what, where])
|
||||
|
||||
TransferredFile = namedtuple('TransferredFile', 'local remote')
|
||||
|
||||
def interpret_transferred_file(descr):
|
||||
if isinstance(descr, type('')):
|
||||
return TransferredFile(descr, descr)
|
||||
if os.path.isdir(descr[1]):
|
||||
to = os.path.join(descr[1], descr[0])
|
||||
else:
|
||||
to = descr[1]
|
||||
return TransferredFile(descr[0], to)
|
||||
|
||||
# Create temp dir
|
||||
tmp_dir = ssh_do(['mktemp', '-d'])
|
||||
|
||||
# Upload `send_files`
|
||||
for f in send_files:
|
||||
dest = tmp_dir+'/'
|
||||
ssh_copy(f, dest, is_upload=True)
|
||||
|
||||
# Do whatever must be done
|
||||
output = ssh_do(command, working_directory=tmp_dir)
|
||||
|
||||
# Download `retr_files`
|
||||
for f in map(interpret_transferred_file, retr_files):
|
||||
src = os.path.join(tmp_dir, f.local)
|
||||
ssh_copy(src, f.remote, is_upload=False)
|
||||
|
||||
# Remove temp dir
|
||||
ssh_do(['rm', '-rf', tmp_dir])
|
||||
|
||||
return output
|
Loading…
Add table
Reference in a new issue