Package: pylddwrap
pylddwrapPylddwrap wraps ldd *nix utility to determine shared libraries required by a program.We need to dynamically package subset of our system at deployment time. Consequently, we have to determine the
dependencies on shared libraries of our binaries programmatically.The output of ldd Linux command, while informative, is not structured enough to be easily integrated into a program.
At the time of this writing, we only found two alternative ldd wrappers on Internetpython-lddandldd.py, but their
output was either too basic for our use case or the project was still incipient.Pylddwrap, in contrast, returns a well-structured list of the dependencies. The command-line tool outputs the
dependencies either as a table (for visual inspection) or as a JSON-formatted string (for use with other tools).
The included Python module lddwrap returns a Python object with type annotations so that it can be used readily by the
deployment scripts and other modules.For more information on the ldd tool, please seeldd manual.UsageCommand-Line Tool pylddwrapAssume we need the dependencies of the /bin/ls. The following command gives them as a table:pylddwrap/bin/lsThe output of the command looks like this:soname          | path                                  | found | mem_address        | unused
----------------+---------------------------------------+-------+--------------------+-------
linux-vdso.so.1 | None                                  | True  | 0x00007ffd8750f000 | False
libselinux.so.1 | /lib/x86_64-linux-gnu/libselinux.so.1 | True  | 0x00007f4e73dc3000 | True
libc.so.6       | /lib/x86_64-linux-gnu/libc.so.6       | True  | 0x00007f4e739f9000 | False
libpcre.so.3    | /lib/x86_64-linux-gnu/libpcre.so.3    | True  | 0x00007f4e73789000 | False
libdl.so.2      | /lib/x86_64-linux-gnu/libdl.so.2      | True  | 0x00007f4e73585000 | False
None            | /lib64/ld-linux-x86-64.so.2           | True  | 0x00007f4e73fe5000 | False
libpthread.so.0 | /lib/x86_64-linux-gnu/libpthread.so.0 | True  | 0x00007f4e73368000 | FalseTo obtain the dependencies as JSON, invoke:pylddwrap--formatjson/bin/lsThe JSON output is structured like this:[
  {
    "soname": "linux-vdso.so.1",
    "path": "None",
    "found": true,
    "mem_address": "0x00007ffed857f000",
    "unused": false
  },
  ...
]You can also sort the table with--sortedwhich will sort bysoname:pylddwrap/bin/pwd--sortedPylddwrap gives the table sorted bysoname:soname          | path                            | found | mem_address        | unused
----------------+---------------------------------+-------+--------------------+-------
None            | /lib64/ld-linux-x86-64.so.2     | True  | 0x00007fd54894d000 | False
libc.so.6       | /lib/x86_64-linux-gnu/libc.so.6 | True  | 0x00007fd548353000 | False
linux-vdso.so.1 | None                            | True  | 0x00007ffe0953f000 | FalseAlternatively, you can sort by any other column. For example, to sort
bypath:pylddwrap/bin/pwd--sortedpathThe output will be:soname          | path                            | found | mem_address        | unused
----------------+---------------------------------+-------+--------------------+-------
linux-vdso.so.1 | None                            | True  | 0x00007ffe0953f000 | False
libc.so.6       | /lib/x86_64-linux-gnu/libc.so.6 | True  | 0x00007fd548353000 | False
None            | /lib64/ld-linux-x86-64.so.2     | True  | 0x00007fd54894d000 | Falseldwrap Python ModuleWe provide lddwrap Python module which you can integrate into your deployment scripts and other modules.The following example shows how to list the dependencies of /bin/ls:importpathlibimportlddwrappath=pathlib.Path("/bin/ls")deps=lddwrap.list_dependencies(path=path)fordepindeps:print(dep)"""
soname: linux-vdso.so.1, path: None, found: True, mem_address: (0x00007ffe8e2fb000), unused: None
soname: libselinux.so.1, path: /lib/x86_64-linux-gnu/libselinux.so.1, found: True, mem_address: (0x00007f7759ccc000), unused: None
soname: libc.so.6, path: /lib/x86_64-linux-gnu/libc.so.6, found: True, mem_address: (0x00007f7759902000), unused: None
...
"""List all dependencies of the /bin/ls utility and check if the direct dependencies are used.
If unused for list_dependencies is set to False then the unused variable of the dependencies will not be determined
and are therefore unknown and set to None. Otherwise information about direct usage will be retrieved and added to the
dependencies.importpathlibimportlddwrappath=pathlib.Path("/bin/ls")deps=lddwrap.list_dependencies(path=path,unused=True)print(deps[1])# soname: libselinux.so.1,# path: /lib/x86_64-linux-gnu/libselinux.so.1,# found: True,# mem_address: (0x00007f5a6064a000),# unused: TrueLddwrap operates normally with the environment variables of the caller. In cases where your dependencies are
determined differently than the current environment, you pass a separate environment (in form of a dictionary) as an argument:importosimportpathlibimportlddwrapenv=os.environ.copy()env['LD_LIBRARY_PATH']="some/important/path"path=pathlib.Path("/bin/ls")deps=lddwrap.list_dependencies(path=path,env=env)InstallationInstall pylddwrap with pip:pip3installpylddwrapDevelopmentCheck out the repository.In the repository root, create the virtual environment:python3-mvenvvenv3Activate the virtual environment:sourcevenv3/bin/activateInstall the development dependencies:pip3install-e.[dev]Tests can be run directly usingunittest:python3-munittestdiscovertests/Pre-commit ChecksWe provide a set of pre-commit checks that lint and check code for formatting.Namely, we use:yapfto check the formatting.The style of the docstrings is checked withpydocstyle.Static type analysis is performed withmypy.Various linter checks are done withpylint.Apply the automatic formatting by running theformatenvironment:tox-eformatRun the pre-commit checks and tests usingtox:toxVersioningWe followSemantic Versioning. The version X.Y.Z indicates:X is the major version (backward-incompatible),Y is the minor version (backward-compatible), andZ is the patch version (backward-compatible bug fix).