Import class-dump info into GDB
Asked Answered
M

3

28

Is there a way to import the output from class-dump into GDB?

Example code:

$ cat > test.m
#include <stdio.h>
#import <Foundation/Foundation.h>

@interface TestClass : NSObject

+ (int)randomNum;

@end

@implementation TestClass

+ (int)randomNum {
    return 4; // chosen by fair dice roll.
              // guaranteed to be random.
}

@end

int main(void) {
    printf("num: %d\n", [TestClass randomNum]);
    return 0;
}
^D

$ gcc test.m -lobjc -o test
$ ./test
num: 4
$ gdb test
...
(gdb) b +[TestClass randomNum]
Breakpoint 1 at 0x100000e5c
(gdb) ^D
$ strip test
$ gdb test
...
(gdb) b +[TestClass randomNum]
Function "+[TestClass randomNum]" not defined.
(gdb) ^D

$ class-dump -A test
...
@interface TestClass : NSObject
{
}

+ (int)randomNum;   // IMP=0x0000000100000e50

@end

I know I can now use b *0x0000000100000e50 in gdb, but is there a way of modifying GDB's symbol table to make it accept b +[TestClass randomNum]?

Edit: It would be preferably if it would work with GDB v6 and not only GDB v7, as GDB v6 is the latest version with Apple's patches.

Microtome answered 9/7, 2013 at 17:2 Comment(1)
Probably one of the best questions I've ever read on Stack Overflow. (+1)Wrestle
M
37

It’s possible to load a symbol file in gdb with the add-symbol-file command. The hardest part is to produce this symbol file.

With the help of libMachObjC (which is part of class-dump), it’s very easy to dump all addresses and their corresponding Objective-C methods. I have written a small tool, objc-symbols which does exactly this.

Let’s use Calendar.app as an example. If you try to list the symbols with the nm tool, you will notice that the Calendar app has been stripped:

$ nm -U /Applications/Calendar.app/Contents/MacOS/Calendar 
0000000100000000 T __mh_execute_header
0000000005614542 - 00 0000   OPT radr://5614542

But with objc-symbols you can easily retrieve the addresses of all the missing Objective-C methods:

$ objc-symbols /Applications/Calendar.app
00000001000c774c +[CALCanvasAttributedText textWithPosition:size:text:]
00000001000c8936 -[CALCanvasAttributedText createTextureIfNeeded]
00000001000c8886 -[CALCanvasAttributedText bounds]
00000001000c883b -[CALCanvasAttributedText updateBezierRepresentation]
...
00000001000309eb -[CALApplication applicationDidFinishLaunching:]
...

Then, with SymTabCreator you can create a symbol file, which is just actually an empty dylib with all the symbols.

Using objc-symbols and SymTabCreator together is straightforward:

$ objc-symbols /Applications/Calendar.app | SymTabCreator -o Calendar.stabs

You can check that Calendar.stabs contains all the symbols:

$ nm Calendar.stabs 
000000010014a58b T +[APLCALSource printingCachedTextSize]
000000010013e7c5 T +[APLColorSource alternateGenerator]
000000010013e780 T +[APLColorSource defaultColorSource]
000000010013e7bd T +[APLColorSource defaultGenerator]
000000010011eb12 T +[APLConstraint constraintOfClass:withProperties:]
...
00000001000309eb T -[CALApplication applicationDidFinishLaunching:]
...

Now let’s see what happens in gdb:

$ gdb --silent /Applications/Calendar.app
Reading symbols for shared libraries ................................. done

Without the symbol file:

(gdb) b -[CALApplication applicationDidFinishLaunching:]
Function "-[CALApplication applicationDidFinishLaunching:]" not defined.
Make breakpoint pending on future shared library load? (y or [n]) n

And after loading the symbol file:

(gdb) add-symbol-file Calendar.stabs 
add symbol table from file "Calendar.stabs"? (y or n) y
Reading symbols from /Users/0xced/Calendar.stabs...done.
(gdb) b -[CALApplication applicationDidFinishLaunching:]
Breakpoint 1 at 0x1000309f2

You will notice that the breakpoint address does not exactly match the symbol address (0x1000309f2 vs 0x1000309eb, 7 bytes of difference), this is because gdb automatically recognizes the function prologue and sets the breakpoint just after.


GDB script

You can use this GDB script to automate this, given that the stripped executable is the current target.

Add the script from below to your .gdbinit, target the stripped executable and run the command objc_symbols in gdb:

$ gdb test
...
(gdb) b +[TestClass randomNum]
Function "+[TestClass randomNum]" not defined.
(gdb) objc_symbols
(gdb) b +[TestClass randomNum]
Breakpoint 1 at 0x100000ee1
(gdb) ^D

define objc_symbols
    shell rm -f /tmp/gdb-objc_symbols

    set logging redirect on
    set logging file /tmp/gdb-objc_symbols
    set logging on

    info target

    set logging off

    shell target="$(head -1 /tmp/gdb-objc_symbols | head -1 | awk -F '"' '{ print $2 }')"; objc-symbols "$target" | SymTabCreator -o /tmp/gdb-symtab

    set logging on
    add-symbol-file /tmp/gdb-symtab
    set logging off
end
Mace answered 25/7, 2013 at 12:55 Comment(7)
I can't compile SymTabCreator, I get the error /Users/Tyilo/Downloads/SymTabCreator-master/SymTabCreator.m:80:54: Operand of type 'NSArray' where arithmetic or pointer type is required.Microtome
@Tyilo: You need to upgrade your developer tools. Xcode has supported object subscripting since 4.4. developer.apple.com/library/mac/releasenotes/ObjectiveC/…Erased
@PeterHosey Well I'm using Xcode 4.6, so there must be some build setting that prevents me from using objc subscripting.Microtome
@PeterHosey It works fine when building the Debug version, but it only happens with the Release version.Microtome
Hey! Really nice code :) Like it a lot. I've created a script that simplifies all this into a single line. See gist.github.com/nickskull/6083359Soccer
@Tyilo: I haven't looked, but it might be building both i386 and x86_64 architectures. The 64-bit build will succeed, on either configuration, but the 32-bit build will fail because subscripting (among other features) is 64-bit-only. Try unsetting the Architectures build setting.Erased
@DominikHadl check the script in the answer. It's way faster and easier.Microtome
I
4

There is no direct way to do this (that I know of), but it seems like a great idea.

And now there is a way to do it... nice answer, 0xced!

The DWARF file format is well documented, IIRC, and, as the lldb source is available, you have a working example of a parser.

Since the source to class-dump is also available, it shouldn't be too hard to modify it to spew DWARF output that could then be loaded into the debugger.

Obviously, you wouldn't be able to dump symbols with full fidelity, but this would probably be quite useful.

Infatuated answered 9/7, 2013 at 17:16 Comment(2)
just to be clear … the original questioner was asking about gdb, but whether the original questioner ought to be more interested in lldb since that's what's current or else the original questioner really wants gdb, most of what is said here still stands … source to gdb can be retrieved from gnu.org/software/gdb/current if that's truly what's desired.Salvia
@Salvia Yes -- good point. Also, it appears that gdb can load DWARF, as well.Infatuated
W
1

You can use DSYMCreator.

  1. With DSYMCreator, you can create a symbol file from an iOS executable binary.

    It's a toolchain, so you can use it like this.

    $ ./main.py --only-objc /path/to/binary/xxx

    Then a file /path/to/binary/xxx.symbol will be created, which is a DWARF format symbol. you can import it to lldb by yourself.

  2. Apart from that, DSYMCreator also supports to export symbols from IDA Pro, you can use it like this.

    $ ./main.py /path/to/binary/xxx

    YES, just ignore --only-objc flag. Then the IDA Pro will run automatically, and then a file /path/to/binary/xxx.symbol will be created, which is the symbol file.

Thanks 0xced for creating objc-symbols, which is a part of DSYMCreator toolchain.

BTW, https://github.com/tobefuturer/restore-symbol is another choice.

Walkyrie answered 13/9, 2016 at 10:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.