weezy20/replace-exe
A smol Zig library that lets a running executable replace (or delete) itself.
A smol Zig library that lets a running executable replace (or delete) itself.
This can be used, for instance, in applications implementing a self update feature keeping the current installation path intact.
Only windows, linux, and unix like systems (macOS, *BSD) are supported.
zig fetch --save git+https://github.com/weezy20/replace-exe.git
const exe = b.addExecutable(.{
...
});
// Add the replace_exe dependency
const libreplace_exe = b.dependency("replace_exe", .{});
exe.root_module.addImport("replace_exe", libreplace_exe.module("replace_exe"));
selfReplace or selfDelete from your code:// step 1: import
const re = @import("replace_exe");
// step 2: register hook as soon as possible in main(). This is a no-op on non-windows OS:
pub fn main() !void {
const allocator = std.heap.page_allocator;
// CRITICAL (windows): Call init() BEFORE any application logic.
// On Windows, this detects if the process is a cleanup helper and exits immediately.
// Any code before this line will run in helper processes too if spawned using selfDelete or selfReplace in windows!
re.init(allocator); // or re.init(null) if you want to go with the default ArenaAllocator(std.heap.page_allocator) for `selfDeleteInit()` operations
// your logic here..
}
// Replace current executable with a new one
try re.selfReplace(allocator, "path/to/new/executable");
// Warning: Deletes current executable
try re.selfDelete();
Note: On windows, if you're deleting the parent folder itself of the current exe itself then you might not want to use selfDelete directly but instead provide the current parent dir (or really any dir that you want to prevent from being locked) using selfDeleteExcludingPath(path: []const u8) where the function ensures that no temporary exes are put into that path, thereby preventing its deletion for the lifetime of the current running executable.
The current strategy is to place temporary exes in %TMP% or %TEMP% & if that fails due to cross filesystem paths (exe & temp dir being on different filesystems) we fallback to storing the temp exe helpers in current parent of running exe unless selfDeleteExcludingPath(p) is provided in which case, the parent of p would be selected & as a fallback, we would go back to using the same parent dir of current-exe.
Recommended guidelines:
- Call these functions at most once each per program execution
- (UNSAFE windows) If using both, prefer do selfReplace before selfDelete - This will still not cleanup the helpers properly as the original file would've been moved & will spawn the new/exe/ which my not have the call to
init()thereby preventing self-cleanup. It's best to avoid this.- Ideally, make them mutually exclusive in your application logic
Some example code is provided in the demo folder:
demo.zig is an application that calls selfReplace to replace itself with the updated version demo2.zig
Build the demo applications with:
# Build zig demo exes:
zig build -Ddemo
# Build the demo-c executable alongside the above:
zig build -Ddemo -Dcapi
Then run the first demo exe:
./zig-out/bin/demo delete # self-delete
./zig-out/bin/demo replace ?</path/to/new/exe> # self-replace; default path is ./zig-out/bin/demo2
Try out the demo-c exe which calls libreplace-exe from C:
./zig-out/bin/demo-c delete
# verify demo-c is deleted
If you're using it via FFI, the function signatures are defined in replace_exe.h and can be used in your code as the following:
selfReplace becomes self_replace(const char* path)selfDelete becomes self_delete()selfDeleteExcludingPath becomes self_delete_excluding_path(const char* path)init(?std.mem.Allocator) becomes init()See c_api.zig for definitions. Building the library as a shared object or static library for use with C/C++:
Building:
# This builds a .so shared library like libreplace-exe.so that you can link against
zig build -Dcapi -Dso -Doptimization=ReleaseFast
# Or if you prefer a static library:
zig build -Dcapi -Doptimization=ReleaseFast
Example usage in C (using dynamic library): See demo.c
gcc demo/demo.c -Izig-out/include -Lzig-out/lib -lreplace-exe -o test
LD_LIBRARY_PATH=zig-out/lib ./test /path/to/new/executable
LD_LIBRARY_PATH=zig-out/lib ./test
Or if you prefer go (using cgo): See demo/main.go for an example.
libreplace-exe.a or libreplace-exe.so:go build -o demo-go demo/main.go
./demo-go replace /path/to/new/executable