PELF (Pack an ELF) is a toolset designed to simplify the process of turning your binaries into single-file executables, similar to AppImages. The format used by PELF is called .AppBundle
or .blob
. The files are portable across systems of the same architecture and ABI. Architecture-independent bundles can be achieved using Wrappers, and Libc-independent ones can be achieved by using --add-ld-and-libc
(it is recommended that you generate the bundles on Musl-based systems, so that they are lighter and will work on other systems)
If you only intend on using .AppBundles, not necesarily work with them, you don't need any of this. You can get started by simply executing the bundle. The helper daemon is optional.
NOTE: Your tar command must support GZIP archives. (which covers most tar implementations, including the BSDs and Busybox's)
- POSIX = Runs on any Unix clone that has some degree of POSIX compatibility/compliance
- MUSL = Runs on Musl-based Linux distros
- GLIBC = Runs on Glibc-based Linux distros
- Linux = Runs on any Linux distro
These are .small.AppBundle, so these are (VERY) compressed and do require gunzip to be available, most systems have it.
- FreeDOOM. LINUX - GLIBC - MUSL
- MPV + ROFI + ANI-CLI + YT-DLP. LINUX - GLIBC - MUSL
- Chocolate-Doom (server + client) GLIBC - MUSL
- Zig v0.14.0 (language and C compiler). LINUX
- Go v1.22.4 (entire Go toolchain). LINUX
- Obfuscated RickRoll. POSIX
pelf
is the main tool used to create an .AppBundle
of your binaries. It takes your executable files and packages them into a single file for easy distribution and execution.
Usage:
Usage: ./pelf [--main-bin [BINARY]|--add-appdir [AppDir] [EXE_NAME]] [--output-to OUTPUT.AppBundle] <--add-library [LIB_PATH]|--add-binary [BIN_PATH]|--add-metadata [icon128x128.xpm|icon128x128.png|icon.svg|app.desktop]|--add-arbitrary [DIR|FILE]>
pelf_linker
is a utility that facilitates access to binaries inside an .AppBundle
for other programs. It ensures that binaries and dependencies within the (open, overlayed) bundles are accesible to external programs. This relies on the concept of Bundle Overlays.
Usage:
pelf_linker [--export] [binary]
pelf_extract
allows you to extract the contents of a PELF bundle to a specified folder. This can be useful for inspecting the contents of a bundle or for modifying its contents.
Usage:
./pelf_extract [input_file] [output_directory]
pelfd
is a daemon written in Go that automates the "installation" of .AppBundle
files. It automatically puts the metadata of your .AppBundles in the appropriate directories, such as .local/share/applications
and .local/share/icons
. So that the .AppBundles you put in ~/Programs (for example) will appear in your menus.
Usage:
pelfd &
One of the key features of PELF is its ability to overlay bundles on top of each other. This means that programs inside one bundle can access binaries and libraries from other bundles. For example, if you bundle wezterm
as a single file and add wezterm-mux-server
to the same bundle using --add-binary
, programs run by wezterm
will be able to see all of the binaries and libraries inside the wezterm
bundle.
This feature is particularly powerful because you can stack an infinite number of PELF bundles. For instance:
spectrwm.AppBundle
containsdmenu
,xclock
, and various X utilities likescrot
androfi
.wezterm.AppBundle
contains some Lua programs and utilities.mpv.AppBundle
containsani-cli
,ani-skip
,yt-dlp
,fzf
, andcurl
.
Using the pelf_linker
, the mpv.AppBundle
can access binaries inside spectrwm.AppBundle
as well as its own binaries. By doing mpv.AppBundle --pbundle_link ani-cli
, you can launch an instance of the ani-cli
included in the bundle, as well as ensure that it can access other utilities in the linked/stacked bundles.
As you can see, I have my window manager and all of my X utilities, including my terminal (Wezterm) as a single file, named SpectrWM.AppBundle. You can also see the concept of overlays in action, the
ani-cli
binary inside of the mpv.AppBundle, will have access to the ROFI binary packed in my SpectrWM.AppBundle, because it will be running as a child of that process. There is PATH and LD_LIBRARY_PATH inheritance.
To install the PELF toolkit, follow these steps:
- Clone the repository:
git clone https://github.com/xplshn/pelf.git ~/Programs cd ~/Programs rm LICENSE README.md cd cmd && go build && mv ./pelfd && cd - # Build the helper daemon rm -rf ./cmd ./examples
- Add ~/Programs to your $PATH in your .profile or .shrc (.kshrc|.ashrc|.bashrc)
To create an .AppBundle
from your binaries, use the pelf
tool:
./pelf --main-bin /usr/bin/wezterm --output-to ./wezterm.AppBundle --add-binary /usr/bin/wezterm-mux-server --add-metadata /usr/share/applications/wezterm.desktop --add-metadata ./wezterm128x128.png --add-metadata ./wezterm128x128.svg --add-metadata ./wezterm128x128.xpm
- You can use AppDirs that have an AppRun, but in order to integrate them (add metadata), you will have to use the
--add-metadata
option, also, there are various libraries which your AppRun can rely on from within an AppBundle, make sure you use them.
./pelf --add-appdir ./wezterm_AppDir wezterm --output-to ./wezterm.AppBundle --add-metadata ./wezterm_AppDir/usr/share/applications/wezterm.desktop --add-metadata ./wezterm_AppDir/.DirIcon
To leverage access to the binaries inside of your (open & overlayed) PELFs to other programs, you can use the pelf_linker
tool:
pelf_linker ytfzf
Overlayed access: Here we made ytfzf be able to access all the PATHs set in $PELF_BINDIRS
mpv.AppBundle --pbundle_link ytfzf
Scoped access: In this other example, we made ytfzf gain access to the programs inside of the example mpv.AppBundle.
To extract the contents of an .AppBundle
to a folder, use the pelf_extract
tool:
./pelf_extract openArena.AppBundle ./openArena_bundleDir
To start the pelfd
daemon and have it automatically manage your .AppBundle
files:
pelfd &
On the first-run, it will create a config file which you can modify: ~/.config/pelfd.json, this is how your config would look after the first run:
{
"options": {
"directories_to_walk": [
"~/Programs"
],
"probe_interval": 90,
"icon_dir": "/home/anto/.local/share/icons",
"app_dir": "/home/anto/.local/share/applications",
"probe_extensions": [
".AppBundle",
".blob"
]
},
"tracker": {}
}
"directories_to_walk"
: This is an array of directories that thepelfd
daemon will monitor for.AppBundle
or.blob
files. By default, it is set to["~/Programs"]
, meaning the daemon will only check for AppBundles in the~/Programs
directory. You can add more directories to this array if you want the daemon to watch multiple locations."probe_interval"
: This specifies the interval in seconds at which thepelfd
daemon will check the specified directories for new or modified AppBundles. By default, it is set to90
seconds."icon_dir"
: This is the directory where icons extracted from .AppBundles will be copied topelfd
. By default, it is set to"~/.local/share/icons"
, which is a common location for application icons on modern Unix clones."app_dir"
: This is the directory where the desktop files extracted from .AppBundles will be copied to bypelfd
. By default, it is set to"~/.local/share/applications"
..desktop
files provide information about the application, such as its name, icon, and command to execute. By default, it is set to"~/.local/share/applications"
, which is the standard location for application shortcuts on modern Unix clones."probe_extensions"
: This is an array of file extensions thatpelfd
will look for when probing the specified directories. By default, it is set to[".AppBundle", ".blob"]
, meaning the daemon will only consider files with these extensions as AppBundles. (CASE-SENSITIVE)- The "tracker" object in the config file is used to store information about the tracked AppBundles, this way, when an AppBundle is removed, its files can be safely "uninstalled", etc.
"correct_desktop_files"
: This option makes PELFD automatically correct the .desktop files provided by the bundles. So that they can be found by your menu application.
.AppBundles may come archived with different formats or encodings. For example, the included pelf_small
edition, will create bundles without using base64 and using GZIP directly with -9, for the best available compression, it may even end up being faster, due to avoiding base64. Remember to ALWAYS signal which edition a bundle was made with by adding it to the bundle's name! For example, .raw.AppBundle if your PELF tool was patched/modified to remove base64 encoding, .small.AppBundle if you used the pelf_small example included here.
- Optionally provide the option to mount the TAR archive instead of instead of copying the files. Or replace TAR altogether for a format that is also widely available.
- Employ the same tricks that the APE loader uses to be recognized as an ELF, preferably, implement a tool that can be used to turn any SH script into a (fake) "ELF".
- Simplify everything by splitting the loader into a very barebones loader and some helper binaries written in shell, for example, the embedded thumbnail generator could be one such helper. The idea being that even if the user isn't able to run the .AppBundle, he can always extract it and repackage it again without having to start from scratch.
- Code has to be readable to stay hackable, given that this is SH, it may end up being an unbearable, disgusting mess, so I have to be specially careful.
- My time to work on PELF is limited
- https://justine.lol/ape.html
- https://github.com/jart/cosmopolitan/blob/master/ape/loader.c
- https://github.com/jart/cosmopolitan/blob/master/ape/ape.S
Contributions to PELF are welcome! If you find a bug or have a feature request, please open an Issue. For direct contributions, fork the repository and submit a pull request with your changes.
PELF and its associated tools are licensed under the 3BSD License. See the LICENSE file for more details.