Collect Linux ELF and macOS Mach-O runtime dependencies without executing the input binary.
With Go installed, download, build, and install the utility directly from GitHub:
go install github.com/meatproxy/elfgather@latestNo repository checkout is required. Run the same command again to update to the latest version selected by Go.
The executable is installed in GOBIN if configured, otherwise in GOPATH/bin
(normally $HOME/go/bin). Make sure that directory is in your PATH. For the
default location:
export PATH="$PATH:$HOME/go/bin"
elfgather --helpAdd the export line to your shell configuration to keep it for future sessions.
If you prefer to build from a local checkout:
go build -o elfgather .
./elfgather --help# Inspect an executable and its transitive dependencies.
elfgather /absolute/path/app --json
# Copy dependencies, retaining their absolute directory layout under ./deps.
elfgather /absolute/path/app --dest ./deps
# Inspect only the arm64 slice of a macOS universal executable.
elfgather /absolute/path/app --arch arm64 --dry-run --json
# Include a known dlopen plugin and copy the executable too.
elfgather /absolute/path/app --dest ./deps \
--extra-lib /absolute/path/plugin.dylib --include /absolute/path/appPass exactly one executable as a positional argument, before or after the options.
Options use Go's standard flag package and accept one or two leading hyphens
(-dest or --dest). Put all options together, before or after the executable;
interleaving options on both sides is not supported. Use -- after the options
before a filename beginning with -. Boolean values use --json=false, not
--json false.
Without --dest, the command lists dependencies without writing, just like
--dry-run. With --dest, files are copied unless --dry-run is also set.
Successful copying is silent unless --json explicitly requests a report.
The destination may already contain files: matching files and symlinks are
replaced, while unrelated files are preserved. The destination itself must be
a real directory. Conflicting directories or symlinks in parent paths are
rejected rather than followed or deleted. Files are staged and destination
conflicts are checked before replacement; updates to an existing directory are
atomic per file, not for the entire directory.
The executable itself is not copied unless
added with --include. --extra-lib and --include can be repeated.
The ELF resolver follows DT_NEEDED, RPATH/RUNPATH, loader tokens, the
system library cache, and default search directories. The interpreter from
PT_INTERP is included. --library-path defaults to LD_LIBRARY_PATH for ELF;
pass --library-path '' to ignore it. $LIB and $PLATFORM, when used, require
--lib-token and --platform-token respectively.
Format detection uses the input signature, not the host operating system. Mach-O inspection also works on Linux if the referenced third-party files are available at the paths being resolved; there is no cross-system sysroot remapping.
Supported features:
- Thin Mach-O and standard universal (
FAT_MAGIC) files for arm64 and x86_64, including their arm64e/x86_64h subtypes. Non-macOS platforms are rejected when identified by their platform load commands. --arch all(default),arm64,amd64, or the aliasx86_64. Dependencies are resolved separately for each selected executable slice; matching subtypes are preferred, with generic-library fallback. Files are copied once and are not thinned, so selecting one architecture does not prepare the other slices for execution.- Recursive
LC_LOAD_DYLIB,LC_LOAD_WEAK_DYLIB,LC_REEXPORT_DYLIB, andLC_LOAD_UPWARD_DYLIBdependencies.LC_ID_DYLIBis not treated as an import. - Absolute install names,
@executable_path,@loader_path, and@rpath, with inheritedLC_RPATHentries expanded relative to the object declaring them. Image paths are canonicalized for loader-token expansion. - Optional explicit
--library-path /dir1:/dir2overrides: dylibs are looked up by basename, frameworks by theirName.framework/...suffix, before ordinary non-system paths. Bare dylib names require these explicit search directories. - Full third-party framework trees, including resources and internal version symlinks. A framework symlink whose resolved target escapes the framework is rejected. Absolute symlinks are rebased inside the copied tree.
- Additional dylibs and Mach-O bundles via
--extra-lib; their dependencies use the main executable's runpaths as the initial context.
The JSON report includes format, architectures, and per-dependency arch,
status, and (when applicable) weak. Mach-O statuses are:
| Status | Meaning |
|---|---|
resolved |
A compatible third-party dylib was found and scheduled for copying. |
system |
A path under /usr/lib/ or /System/Library/ is left to macOS. |
missing-weak |
An optional import has no file or compatible architecture. |
System paths are an exclusion policy, not an existence or compatibility
check. macOS can provide these libraries through its dyld shared cache even when
there is no file at the recorded path. They are neither copied nor traversed;
dyld itself is not copied. /usr/local/lib and Homebrew libraries are not
excluded by this policy.
A missing required import, malformed library, or unsupported input fails the collection. Missing weak imports are reported rather than silently discarded.
This is a dependency collector, not a relocatable app bundler. It does not rewrite
install names or runpaths, sign code, extract the dyld shared cache, preserve
extended attributes, or verify macOS deployment-target/ABI compatibility. The
result retains source paths under the destination; moving it elsewhere may
require install_name_tool and subsequent signing.
The Mach-O resolver uses a deterministic static search model; it does not
reproduce all dyld process policies. DYLD_* environment overrides, insertion,
fallback directories, SIP/hardened-runtime restrictions, legacy framework path
fallbacks, and working-directory-relative paths are not emulated. LD_LIBRARY_PATH
is ignored for Mach-O. Use explicit --library-path when needed. Nested @rpath
in LC_RPATH, lazy-load commands, 32-bit CPUs, and nonstandard/64-bit universal
headers are currently unsupported. All slices parsed from a universal file must
use supported CPU/platform formats, even when --arch selects a subset.
Neither format can discover arbitrary runtime dlopen() paths. Supply known
plugins with --extra-lib and other runtime data with --include. Extra Mach-O
plugins must contain compatible slices for every selected executable architecture.
go test ./...
go vet ./...Cross-platform tests generate Mach-O fixtures for transitive resolution, inherited runpaths, weak imports, universal files, cycles, malformed commands, and framework copying. Linux tests build and execute ELF fixtures with GCC. On macOS, an additional integration test uses Clang to build a real executable plus two dylibs, collects them, removes the original libraries, and launches the copied executable. Native tests require their respective compiler.
Mach-O format and lookup references: Go debug/macho, Apple run-path libraries, dyld loader source.