jart / cosmopolitan

build-once run-anywhere c library
ISC License
18.08k stars 619 forks source link

Make GetProgramExecutableName reliable on X86_64 XNU (macOS) assimilated binaries #1169

Open mrdomino opened 5 months ago

mrdomino commented 5 months ago

Writing a somewhat long-ish description in the hopes that this might make a good starter bug for somebody who is not scared of a little assembly.

XNU doesn't do auxv the way other platforms do; instead, it has a sort of addendum to envp, i.e. more env-style key-value pairs after the sentinel NULL at the end of envp. The (generally?) first of these begins executable_path= and contains the path of the running binary.

If we are running assimilated on x86_64 XNU, that value is the only reliable way we have of locating the path of the running binary for use in GetProgramExecutableName (and hence the ZipOS /zip filesystem.)

In libc/crt/crt.S, the pseudo-auxv is currently zeroed out:

https://github.com/jart/cosmopolitan/blob/06d916b449357bfb714ca41346dc762e7801dd5c/libc/crt/crt.S#L104-L111

It would be good if the executable path were stored in __program_executable_name prior to this happening, if we are running assimilated.

mrdomino commented 5 months ago

From https://github.com/jart/cosmopolitan/issues/1168#issuecomment-2096817119:

I guess if we are continuing down the road of using AT_FLAGS to signal that argv[0] is preserved, and if we care about XNU, then another thing that might be done is to make that snippet in crt.S not run if we notice that we've been loaded.

Strawman logic might be:

if __program_executable_name is set
  do nothing, the loader has already set auxv up for us
else
  if it is good to do so (i.e. we're assimilated, not running under an older loader)
    save executable_path= to __program_executable_name
  zero out auxv
mrdomino commented 4 months ago

(The above pseudocode is still basically sound even though we're probably not continuing down the road of (ab)using AT_FLAGS that way.)