Readit News logoReadit News
rwmj · 6 months ago
You can already do this without using binfmt ...

  #if 0
  gcc "$0" -o "$@".out && exec ./"$@".out
  #endif
  #include <stdio.h>
  int main () { printf ("hello, world\n"); return 0; }
Usage:

  $ chmod +x print.c
  $ ./print.c
  hello, world
(Could be better to use a temporary file though.)

There's a similar cute trick for compiled OCaml scripts that we use with nbdkit: https://libguestfs.org/nbdkit-cc-plugin.3.html#Using-this-pl...

teo_zero · 6 months ago
The use of $@ doesn't look right to me.

In the trivial case exposed here where there are no additional arguments to pass to the .c program, the shell executes

  gcc "print.c" -o .out && exec ./.out
and it works "by chance".

In a more complex scenario where print.c expects some parameters, it won't work. For example,

  ./print.c a b c
will result in the shell trying to invoke

  gcc "print.c" -o "a" "b" "c".out && exec ./"a" "b" "c".out
which makes no sense.

Are you sure you didn't intend $0 instead of $@ ?

rwmj · 6 months ago
It's true, that's a mistake!

OTOH we're trying to write self-compiling executable C scripts, so the safety, correctness and good sense ships sailed a while back.

mananaysiempre · 6 months ago
Compiler errors won’t cause as many funny consequences with

  gcc "$0" -o "$@".out && exec ./"$@".out || exit $?   # I'd use ${0%.c} not $@
Love this trick too, but the difference, as far as I understand, is that it only works with a Bourne(-compatible) shell, whereas shebangs or binfmt_misc also work with exec().

ckastner · 6 months ago
Oh this is neat. Took me a bit.

The shell treats the first line as a comment. It executes the second line, which eventually exec's the binary so the rest of the file do not matter to the shell.

And the compiler treats the first line as a preprocessor directive, so it ignores the second line.

I initially misread/mistook the first line for a shebang.

AlotOfReading · 6 months ago
You can also #embed the compiler binary, and execve it to much the same effect as binfmtc. I explored that trick for an IOCC entry that was never submitted because it ended up far too readable.
suprjami · 6 months ago
Is there a way to do this and have the shell remove the temporary file after exec?
teo_zero · 6 months ago
Yes, but it's not worth it. It's better to forget gcc and use tcc instead, which has the -run flag to compile and run without creating any intermediate file. It's also much quicker than gcc.
1vuio0pswjnm7 · 6 months ago
There are many, no doubt

   #if 0
   cc -static -pipe -xc "$0"||exit 100
   exec ./a.out
   #endif
   int puts(const char *);
   int unlink(const char *);
   int main(int argc,char *argv[]){ 
   puts("it works"); 
   unlink(argv[0]);
   return 0; 
   }

pwdisswordfishz · 6 months ago
Or even

    /*bin/true ; exec tcc -run "$0" "$@" # */

nrclark · 6 months ago
I'd have expected this to need a hashbang (#!/bin/sh) at the beginning. Why doesn't it?
rwmj · 6 months ago
Because your shell will execute anything that looks like a text file & has +x bit set as a shell script.
zx2c4 · 6 months ago
Similar project of mine from a long while ago: https://git.zx2c4.com/cscript/about/
radiospiel · 6 months ago
Amazing; when I tried something similar I used a "#!" line pointing to a C compiler + runner of sorts (https://github.com/radiospiel/jit). https://git.zx2c4.com/cscript/about/ is also following that approach.

What is the benefit of registering an extension via binfmt_misc?

lmz · 6 months ago
It's still valid C source code? Is the #! sequence valid C?
monocasa · 6 months ago
There's also tcc, which has a shebang compatible extension to allow it to be used by scripts.
kazinator · 6 months ago
This is doable entirely without a Linux-specific binfmt-misc hack.

https://rosettacode.org/wiki/Multiline_shebang#C

enriquto · 6 months ago
This is a neat hack, but the whole file is not a valid C program.
kazinator · 6 months ago
You mean it consists of a C program, plus non-C cruft to get it running?

Isn't that already legitimized by configure scripts, compiler command lines and Make files?

codr7 · 6 months ago
I did a simple hack for doing the same thing from inside a C program for my book:

https://github.com/codr7/hacktical-c/tree/main/dynamic

enriquto · 6 months ago
was surprised that "sudo apt install binfmtc" works out of the box on my box (linux mint) and i can do the magic just as described here