Search code examples
macosdebug-information

Check if mac executable has debug info


I want to make sure my executable has debug info, trying the linux equivalent doesn't help:

$ file ./my_lovely_program
./my_lovely_program: Mach-O 64-bit executable arm64 # with debug info? without?

EDIT (from the answer of @haggbart)

It seems that my executable has no debug info (?)

$ dwarfdump --debug-info ./compi
./compi:    file format Mach-O arm64

.debug_info contents: # <--- empty, right?

And with the other option, I'm not sure:

$ otool -hv ./compi
./compi:
Mach header
      magic  cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
MH_MAGIC_64    ARM64        ALL  0x00     EXECUTE    19       1816   NOUNDEFS DYLDLINK TWOLEVEL WEAK_DEFINES BINDS_TO_WEAK PIE

This is very weird because I can perfectly debug it with lldb

(lldb) b main
Breakpoint 1: where = compi`main + 24 at main.cpp:50:9, address = 0x0000000100018650
(lldb) run
Process 6067 launched: '/Users/oren/Downloads/compi' (arm64)
Process 6067 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x0000000100018650 compi`main(argc=3, argv=0x000000016fdff7b8) at main.cpp:50:9
   47   /*****************/
   48   int main(int argc, char **argv)
   49   {
-> 50       if (argc == 3)
   51       {
   52           const char *input = argv[1];
   53           const char *output = argv[2];
Target 0: (compi) stopped.

Solution

  • Mach-O isn't like ELF: Its debug info is "sold separately" in a .dSYM file. When you compile with -g you'll see a file gets generated along side your output, such that:

    (~) gcc a.c -o /tmp/a -g2

    (~) %ls -lFd /tmp/a /tmp/a.dSYM -rwxr-xr-x 1 morpheus wheel 34078 Dec 6 12:56 /tmp/a*

    drwxr-xr-x 3 morpheus wheel 96 Dec 6 12:56 /tmp/a.dSYM/

    The .dSYM is a bundle (i.e. a directory structure) whose Contents/Resources/DWARF has the "companion file":

    (~) %file /tmp/a.dSYM/Contents/Resources/DWARF/a                      
    /tmp/a.dSYM/Contents/Resources/DWARF/a: Mach-O 64-bit dSYM companion file arm64
    (~) %jtool2 -l /tmp/a.dSYM/Contents/Resources/DWARF/a | grep UUID     
    LC 00: LC_UUID                  UUID: BDD5C13E-F7B8-3B4D-BAF9-14DF3CD03724
    (~) %jtool2 -l /tmp/a | grep UUID                                     
    LC 09: LC_UUID                  UUID: BDD5C13E-F7B8-3B4D-BAF9-14DF3CD03724
    

    tools like lldb can figure out the debug data by trying for the companion file directory (usually in same location as the binary, or specified in a path), and then check the LC_UUID matches. This enables you to ship the binary without its dSym, and use the dSym when symbolicating a crash report (this is what Apple does). The debug info includes all local variable names, as well as debug_aranges (addr2line), etc:

     (~) %jtool2 -l /tmp/a.dSYM/Contents/Resources/DWARF/a | grep DWARF   
    LC 07: LC_SEGMENT_64             Mem: 0x100009000-0x10000a000   __DWARF
            Mem: 0x100009000-0x10000921f        __DWARF.__debug_line    
            Mem: 0x10000921f-0x10000924f        __DWARF.__debug_aranges 
            Mem: 0x10000924f-0x1000093dc        __DWARF.__debug_info    
            Mem: 0x1000093dc-0x100009478        __DWARF.__debug_abbrev  
            Mem: 0x100009478-0x100009590        __DWARF.__debug_str 
            Mem: 0x100009590-0x1000095e8        __DWARF.__apple_names   
            Mem: 0x1000095e8-0x10000960c        __DWARF.__apple_namespac    
            Mem: 0x10000960c-0x100009773        __DWARF.__apple_types   
            Mem: 0x100009773-0x100009797        __DWARF.__apple_objc
    

    If you really want to get of any debug info - including, say, local function symbols (which are included by default in the binary), strip -d -x is your friend. This operates on the binary.

    Note that running "dsymutil" (As suggested in other answers) can be a bit misleading, since in order to display information it will track down the accompanying dSym - which will be present on your machine, but not if you move the binary elsewhere.