Search code examples
c++clangmach-o

How can I change entry point for a mach-o executable for a c++ file?


I am trying to write a c++ program without main. Is it possible to change the entry point of a mach-o executable to a custom function (other than main())?

If not, then, is it possible to wrap main to call my version of main before the actual C main is called?

Edit:

I want to my custom function to call the C main. If I gave it a constructor attribute or added it to ctor list then main will be called twice. I do not want that to happen.

P.S I'm building executables in Mac OS X High Sierra with clang version 9.1.0


Solution

  • You can use the -e <symbol> option of ld, which you can invoke as -Wl,-e,_<symbol> from clang. Historically, the entry point of a program would be _start from crt0.o, however that has not been a thing on Darwin since Mac OS X 10.8 and iOS 6.0, where the LC_MAIN load command was introduced (replacing LC_UNIXTHREAD). The "old" way can still be used, but would have to be explicitly enabled with the -no_new_main linker flag (which has a counterpart -new_main, should you ever need it). The duty once carried by crt0.o has been shifted to the dynamic linker, /usr/lib/dyld, which can handle both LC_MAIN and LC_UNIXTHREAD as needed.

    So given a C program with a main:

    // t.c
    #include <stdio.h>
    
    int main(int argc, const char **argv)
    {
        printf("test %i\n", argc);
        return 0;
    }
    

    You can easily create a C++ file like this:

    // t.cpp
    
    extern int main(int, const char**);
    
    extern "C" int derp(int argc, const char **argv)
    {
        return main(0, (const char*[]){ (const char*)0 });
    }
    

    And compile them with clang++ -o t t.cpp -xc t.c -Wl,-e,_derp.
    Just be sure to either declare derp as extern "C", or specify the mangled symbol on the command line.

    You can also inspect the resulting executable with otool to make sure it uses LC_MAIN rather than LC_UNIXTHREAD:

    bash$ otool -l ./t | fgrep -B1 -A3 LC_MAIN
    Load command 11
           cmd LC_MAIN
       cmdsize 24
      entryoff 3808
     stacksize 0