/* GNU Guix --- Functional package management for GNU Copyright (C) 2022 Ricardo Wurmus <rekado@elephly.net> This file is part of GNU Guix. GNU Guix is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. GNU Guix is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Guix. If not, see <http://www.gnu.org/licenses/>. */ /* This is stupid, but so is almost anything in the periphery of computers. This code compiles to an executable that can be prepended to any binary; when executed it sets environment variables that have been set at compile time and then re-executes itself without the prepended binary. Example to build a wrapped "yes" binary named "yes.wrapped": thing=$(readlink -f $(type -p yes)); \ gcc -std=gnu99 -fPIC -Os -DENVVARS='"hello","1","world","bleh"' \ -DORIGINAL_SIZE=$(stat --format=%s $thing) -Wall \ /path/to/elf-wrap.c -o elf-wrap && \ cat elf-wrap $thing > $(basename $thing).wrapped && \ chmod --reference=$thing $(basename $thing).wrapped */ #define _GNU_SOURCE #include <fcntl.h> #include <stdlib.h> #include <stdio.h> #include <sys/mman.h> #include <sys/sendfile.h> #include <sys/stat.h> #include <unistd.h> extern char **environ; #define ARR(...) {__VA_ARGS__, NULL} int main (int argc, char *argv[]) { int in; int out; if ((in = open (argv[0], O_RDONLY, 0)) < 0) { fprintf (stderr, "cannot read self as file"); exit(EXIT_FAILURE); } /* write binary without wrapper to in-memory file */ out = memfd_create ("guix-wrapper", 0); if (out == -1) { fprintf (stderr, "cannot create in-memory fd"); exit(EXIT_FAILURE); } struct stat stat_buf; fstat (in, &stat_buf); off_t offset = stat_buf.st_size - ORIGINAL_SIZE; sendfile (out, in, &offset, ORIGINAL_SIZE); close (in); fcntl (out, FD_CLOEXEC); /* set env vars and run the wrapped binary */ char *env[] = ARR(ENVVARS); unsigned int i = 0; for (i = 0; env[i] != NULL; i+=2) { setenv (env[i], env[i+1], 1); } fexecve(out, argv, environ); return EXIT_FAILURE; }