Binfmtc – binfmt_misc C scripting interface
103 points
6 months ago
| 11 comments
| netfort.gr.jp
| HN
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...

reply
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 $@ ?

reply
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.

reply
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().
reply
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.

reply
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.
reply
suprjami
6 months ago
[-]
Is there a way to do this and have the shell remove the temporary file after exec?
reply
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.
reply
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; 
   }
reply
pwdisswordfishz
6 months ago
[-]
Or even

    /*bin/true ; exec tcc -run "$0" "$@" # */
reply
nrclark
6 months ago
[-]
I'd have expected this to need a hashbang (#!/bin/sh) at the beginning. Why doesn't it?
reply
rwmj
6 months ago
[-]
Because your shell will execute anything that looks like a text file & has +x bit set as a shell script.
reply
zx2c4
6 months ago
[-]
Similar project of mine from a long while ago: https://git.zx2c4.com/cscript/about/
reply
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?

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

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

reply
enriquto
6 months ago
[-]
This is a neat hack, but the whole file is not a valid C program.
reply
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?

reply
enriquto
6 months ago
[-]
of course it is legitimized. I just meant that the selling point of binfmtc is that the same .c file can either be compiled by any C compiler, or executed directly.
reply
kazinator
6 months ago
[-]
We could perhaps split it into a two-file system where a "foo.sh" containing certain boilerplate will execute a "foo.c" that doesn't require any special conventions at all.

"foo.sh" could be identical for any .c file, and so we could symbolically link them all to a common file.

Of course, neither that file nor symlinks to it require a .sh suffix.

The contents might look like this:

  #!/bin/sh
  c_file=${0%.sh}.c
  x_file=${0%.sh}.bin

  # If cached executable file is missing, or out of date w.r.t.
  # the source file, compile it:

  if [ "$c_file" -nt "$x_file" ]; then
    cc $CFLAGS "$c_file" -o "$x_file"
  fi

  # Run cached executable file
  $x_file
reply
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

reply
elitepleb
6 months ago
[-]
reply
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
reply
rurban
6 months ago
[-]
(2006)
reply
JSR_FDED
6 months ago
[-]
C is still my first love. You can hold the whole language in your head, and it’s fast. Yes there are footguns but it’s a libertarian programming language - you’re responsible for what you build. No hand holding.
reply
ykonstant
6 months ago
[-]
I like that too, but the problem is that C doesn't keep its end of the deal. No hand holding, but make what you are doing transparent. It used to be the case back in the 80s, but not anymore. Not with our optimizing compilers and oodles of UB and spec subtleties and implicit actions.
reply
Surac
6 months ago
[-]
So lovely
reply