Printing Objective-C method implementation at runtime - objective-c

Is it possible to print/log the implementation of a certain class method at runtime to the console screen? I am assuming the log will be in assembly which is fine by me.

You could add a breakpoint at the start of the line, step through line by line and call "disassemble" in the debugger:
One line of my code (with private information replaced) for example produced this:
-(void) method
{
__weak typeof(self) selfReference = self; // <-- This call was disassembled.
...
Project`-[Class method] + 32 at Class.m:176:
-> 0x9c5cc: ldr r1, [sp, #304]
0x9c5ce: add r0, sp, #296
0x9c5d0: blx 0x33abec ; symbol stub for: objc_initWeak
0x9c5d4: ldr r1, [sp, #304]
Edit
I can't verify it's working perfectly since I'm not too handy with assembly, but you can use the debugger (Clang I'm using) to just call
disassemble -n methodName
This claims to
Disassemble entire contents of the given function name.
NB: I did this with a breakpoint at the start of the method I was using to test
Try creating a symbolic breakpoint to stop at the method in question:

Related

Do empty functions get optimized away in Rust?

I want to put some performance impacting function calls behind a feature gate in my code. If the feature isn't enabled, I was thinking of just having an empty implementation of that function implemented instead. That way, hopefully, the Rust complier can completely remove that from the function.
Something like this:
// Included if feature is enabled
fn foo() {
// ...
}
// Included if the feature is disabled
fn foo() {}
// Performance critical code
for i in 1..1000000000 {
// ...
foo();
}
Would the call to foo() get optimized away if it is empty?
Just try it in the amazing Compiler Explorer :)
The result assembly for your example is:
example::main:
push rbp
mov rbp, rsp
mov eax, 1
.LBB0_1:
xor ecx, ecx
cmp eax, 1000000000
setl cl
add ecx, eax
cmp eax, 1000000000
mov eax, ecx
jl .LBB0_1
pop rbp
ret
As you can see there is no call instruction and foo() isn't called at all. However, you might wonder why the loop isn't removed, as it doesn't have an effect on the outside world. I can just assume that sometimes those loops are in fact used to waste time in some sense. If you decrease the counter to 100, the loop is completely removed.
Anyway: Yes, the optimizer will remove empty functions!
According to my check with release mode on current stable Rust, the following code:
fn foo() {}
fn main() {
for _ in 1..1000000000 {
foo();
}
println!(); // a side effect so that the whole program is not optimized away
}
Compiles to the same assembly as if the loop was empty:
for _ in 1..1000000000 {}

Conditional breakpoint using strcmp() in GDB on Mac OS X conflicts with Objective-C runtime

I'm trying to debug a program that I do not have the source for on Mac OS X. I would like to know what arguments it is calling gettattrlist() with, and inspect the return value, for two different volumes (in order to compare and see why it will let you use one volume and not the other).
I first tried dtruss; but that's useless for getattrlist(); it shows only the pointers that are being passed into getattrlist() (and doesn't even know how many arguments getattrlist() takes).
635/0x1dc5: getattrlist("/Volumes/MyVolume\0", 0x113FA6380, 0x113FA5FD0) = 0 0
635/0x1dc5: getattrlist("/Volumes/MyVolume\0", 0x113FA4F00, 0x113FA4B30) = 0 0
635/0x1dc5: getattrlist("/Volumes/MyVolume\0", 0x113FA5870, 0x113FA54C0) = 0 0
635/0x19c6: getattrlist("/Volumes/MyVolume\0", 0x7FFF5FBF9140, 0x7FFF5FBF8D70) = 0 0
635/0x19c6: getattrlist("/Volumes/MyVolume\0", 0x7FFF5FBFA8A0, 0x7FFF5FBFA4F0) = 0 0
So I tried GDB. I can set an unconditional breakpoint on getattrlist(), and take a look at its first argument, but it's called way too often for that to be useful.
(gdb) break getattrlist
Breakpoint 1 at 0x7fff8e90b6ac
(gdb) cont
Continuing.
Breakpoint 1, 0x00007fff8e90b6ac in getattrlist ()
(gdb) p (char *)$rdi
$1 = 0x7fff5fbfd67e "/some/random/path"
So, I probably need a conditional breakpoint, that will break only when the first argument matches the path I'm interested in. That shouldn't be too hard, right?
(gdb) delete
Delete all breakpoints? (y or n) y
(gdb) break getattrlist if ((int)strcmp((char *)$rdi, "/Volumes/My Volume")) == 0
Breakpoint 2 at 0x7fff8e90b6ac
(gdb) cont
Continuing.
Canceling call as the malloc lock is held so it isn't safe to call the runtime.
Issue the command:
set objc-non-blocking-mode off
to override this check if you are sure your call doesn't use the malloc libraries or the ObjC runtime.
Error in testing breakpoint condition:
Canceling call as the malloc lock is held so it isn't safe to call the runtime.
Issue the command:
set objc-non-blocking-mode off
to override this check if you are sure your call doesn't use the malloc libraries or the ObjC runtime.
Breakpoint 2, 0x00007fff8e90b6ac in getattrlist ()
(gdb) p (char *)$rdi
$12 = 0x7fff5fbfd67e "/some/other/random/path"
What's this? GDB has ignored my condition because it suspects that it might call malloc() or the ObjC runtime? OK, well, strcmp() shouldn't call malloc(); it should just compare the strings byte by byte until it gets to a null character. So lets set that option the message recommends to override the check:
(gdb) set objc-non-blocking-mode off
(gdb) cont
Continuing.
Segmentation fault: 11
No dice. GDB and the application both die.
Any suggestions on how to set a conditional watchpoint on a string from GDB without running into this issue? Or other ways of capturing the arguments and return values (which are stored via an output argument) of getattrlist(), that works better than dtruss()?
Edit
Tried matt's solution, but no luck:
(gdb) set $vol = (char *) malloc((int)strlen("/Volumes/My Volume") + 1)
(gdb) call (int)strcpy($vol, "/Volumes/My Volume")
$1 = 236411760
(gdb) break getattrlist if ((int)strcmp((char *)$rdi, $vol)) == 0
Breakpoint 1 at 0x7fff8e90b6ac
(gdb) cont
Continuing.
Unsafe to run code: malloc zone lock is held for some zone..
Error in testing breakpoint condition:
Canceling call as the malloc lock is held so it isn't safe to call the runtime.
Issue the command:
set objc-non-blocking-mode off
to override this check if you are sure your call doesn't use the malloc libraries or the ObjC runtime.
Breakpoint 1, 0x00007fff8e90b6ac in getattrlist ()
(gdb) p (char *)$rdi
$4 = 0x11a715838 "/some/other/random/path"
I decided to try memcmp() instead of strcmp(); no luck there either:
(gdb) break getattrlist if ((int)memcmp((char *)$rdi, $vol, 18)) == 0
Breakpoint 1 at 0x7fff8e90b6ac
(gdb) cont
Continuing.
Unsafe to run code: malloc zone lock is held for some zone..
Error in testing breakpoint condition:
Canceling call as the malloc lock is held so it isn't safe to call the runtime.
Issue the command:
set objc-non-blocking-mode off
to override this check if you are sure your call doesn't use the malloc libraries or the ObjC runtime.
Breakpoint 1, 0x00007fff8e90b6ac in getattrlist ()
(gdb)
At this point, I figured "OK, now there really shouldn't be anything using malloc()", so I decided to try set objc-non-blocking-mode off again. Still no luck:
(gdb) set objc-non-blocking-mode off
(gdb) cont
Continuing.
Reading symbols for shared libraries ... done
Reading symbols for shared libraries . done
Reading symbols for shared libraries ....... done
[Switching to process 5456 thread 0x2971b]
[Switching to process 5456 thread 0x29e2f]
warning: Unable to restore previously selected frame.
Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_INVALID_ADDRESS at address: 0x0000000000000000
[Switching to process 5456 thread 0x29e2f]
0x0000000000000000 in ?? ()
Error in testing breakpoint condition:
The program being debugged was signaled while in a function called from GDB.
GDB remains in the frame where the signal was received.
To change this behavior use "set unwindonsignal on"
Evaluation of the expression containing the function (memcmp) will be abandoned.
Breakpoint 1, 0x0000000000000000 in ?? ()
Error in testing breakpoint condition:
The program being debugged was signaled while in a function called from GDB.
GDB remains in the frame where the signal was received.
To change this behavior use "set unwindonsignal on"
Evaluation of the expression containing the function (memcmp) will be abandoned.
Breakpoint 1, 0x0000000000000000 in ?? ()
Hmm. What state am I in?
(gdb) bt
#0 0x0000000000000000 in ?? ()
#1 0x000000011e6ec070 in ?? ()
Ick. That doesn't look good. What if I continue here?
(gdb) cont
Continuing.
[Switching to process 5456 thread 0x2971b]
(gdb) bt
#0 0x00007fff8e90b6ac in getattrlist ()
#1 0x00007fff897c9c4b in GetPathVolFSAttributes ()
#2 0x00007fff897c9459 in PathGetObjectInfo ()
#3 0x00007fff897c9279 in FSPathMakeRefInternal ()
#4 0x00007fff8767b3ee in FSNodePrepareFSRef ()
... snip ...
(gdb) p (char *)$rdi
$2 = 0x10db1c2b0 "/System/Library/CoreServices/CoreTypes.bundle"
Nope. Still not actually breaking on the correct call to getattrlist(); and everything has died in the meantime due to the null pointer dereference.
I believe that something like the following should work.
(gdb) start
...
(gdb) set $x = malloc(strlen("foobar") + 1)
(gdb) call strcpy($x, "foobar")
(gdb) break a_leg if strcmp(foo, $x) == 0
In recent versions of GDB:
(gdb) start
(gdb) break a_leg if $_streq(foo, "foobar")
I'm not sure in what version this was introduced, but it's at least present in 7.7.1.

lifetime of variables defined inside block statement in C

in this C-code fragment:
void func(void)
{
int x=10;
if (x>2)
{
int y=2;
//block statement
{
int m=12;
}
}
else
{
int z=5;
}
}
when does x,y,z and m get allocated and deallocated from func stack frame ?
The actual allocation depends on your compiler, but many compilers allocate space on the stack at the beginning of the function and free it just before the function returns. Note that this is separate from when the variables are actually accessible though, which is just till the end of the block they are defined in.
In your example, with optimization turned on, the compiler is likely not to allocate any space on the stack for your variables and simply return, since it can determine at compile time that the function doesn't actually have any effect.
According to C++ rules, you should think that every local variable is destroyed in the end of its block. This si the time when destructor is called. However, compiler may decide to allocate/deallocate all local variables together in the beginning/end of the function, this is what VC++ compiler does:
void func(void)
{
001413B0 push ebp
001413B1 mov ebp,esp
001413B3 sub esp,0F0h
001413B9 push ebx
001413BA push esi
001413BB push edi
001413BC lea edi,[ebp-0F0h]
001413C2 mov ecx,3Ch
001413C7 mov eax,0CCCCCCCCh
001413CC rep stos dword ptr es:[edi]
int x=10;
001413CE mov dword ptr [x],0Ah
if (x>2)
001413D5 cmp dword ptr [x],2
001413D9 jle func+3Bh (1413EBh)
{
int y=2;
001413DB mov dword ptr [y],2
//block statement
{
int m=12;
001413E2 mov dword ptr [m],0Ch
}
}
else
001413E9 jmp func+42h (1413F2h)
{
int z=5;
001413EB mov dword ptr [z],5
}
}
But these are implementation details, compiler is free to adjust stack pointer by another way.
So, actual stack pointer adjustment is not defined, but constructor/destructor calls are done exactly according to the function internal blocks. And of course, you cannot use a variable outside its block - this is not compiled. Though stack space may be allocated at this point.
In case of automatic variables (variables declared within a block of code are automatic by default) memory is allocated automatically upon entry to a block and freed automatically upon exit from the block (if you're using c with gcc). You can check this source or this source for more information.

ICC inline assembler doesn`t like push/pop

I try to excute assembler inline with icc in msasm:
int main (void)
{
__asm{
mov eax, 5h; //works
push eax; // after shell command /opt/intel/bin/icc -use_msasm asm.c:
// asm.c(7): (col. 5) error: Unsupported instruction form in asm
// instruction push.
//pop ebp; // the same
};
printf("success!\n");
return 1;
}
Does anybody know why icc doesn`t accept push and pop?
Thanks in advance!
You should use x64 version of registers instead.
So the correct version should like this:
__asm{
mov rax, 5h;
push rax;
};
Also, pay attention to architecture differences when dealing with pointers, 0x8*******, etc. You should never use batch Find and Replace without reading your inline first.

Segmentation fault attempting to print an Objective-C field in gdb

In GDB I get a segmentation fault if I attempt to do the following:
print bg->top
The code looks a bit like this:
#interface Sprite : Object
{
#public
int top;
/* Other fields */
}
#end
bg = [Sprite load: "test.png"];
/* GDB is at a breakpoint after the above line */
The message I get is:
(gdb) print bg->top
Program received signal SIGSEGV, Segmentation fault.
0x6a7e3048 in libobjc-2!__objc_class_links_resolved () from C:\MinGW\bin\libobjc-2.dll
The program being debugged was signaled while in a function called from GDB.
GDB remains in the frame where the signal was received.
To change this behavior use "set unwindonsignal on".
Evaluation of the expression containing the function
(objc_lookup_class) will be abandoned.
When the function is done executing, GDB will silently stop.
Why is this?
I'm using the GNU Objective-C runtime and I'm not using GNUStep.
how is 'bg' declared?
id bg or Sprite *bg
using 'id bg'
you must cast bg to the correct class,
(gdb) p bg->top
There is no member named top.
(gdb) p ((struct Sprite *)bg)->top
$1 = 0
using Sprite *bg
(gdb) p bg->top
$1 = 0
using GNU gdb (GDB) 7.3.50.20110421-cvs
under linux, which version of gdb are you using?
is the Sprite class implemented separately in a DLL/library or
in the main application?
its possible it could be some manifestation of
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=39465