In one of the past tips we mentioned the __unused
attribute which can be applied to function arguments. When can it be useful?
Let’s consider this code from Apple’s dyld:
v19
is passed as fist argument to dyld4::ProcessConfig::PathOverrides::setString()
. Since its name looks like a class method, the decompiler assigned the class type to the first argument (normally corresponding to the implicit this
argument). However, strncmp
returns a simple integer with the comparison result and has no relation to the PathOverrides
class. What’s going on?
To clarify things, it can be useful to look inside the function being called. It is pretty short so we can show the whole output:
const char *__fastcall dyld4::ProcessConfig::PathOverrides::setString( dyld4::ProcessConfig::PathOverrides *this, lsl::Allocator *a2, const char **a3, const char *__s) { size_t v7; // x22 size_t v8; // x8 char *v9; // x22 char *v10; // x0 const char *result; // x0 __int64 v12; // [xsp+0h] [xbp-40h] BYREF if ( *a3 ) { v7 = _platform_strlen(*a3); v8 = (v7 + _platform_strlen(__s) + 17) & 0xFFFFFFFFFFFFFFF0LL; __chkstk_darwin(); v9 = (char *)&v12 - v8; v10 = strcpy((char *)&v12 - v8, *a3); *(_WORD *)&v9[_platform_strlen(v10)] = 58; strcat(v9, __s); result = (const char *)lsl::Allocator::strdup(a2, v9); } else { result = (const char *)lsl::Allocator::strdup(a2, __s); } *a3 = result; return result; }
You may notice a curious thing: the this
argument is not used in the body of the function. This can be confirmed by checking the cross-references (shortcut X
):
An additional confirmation is the assembly code preceding the call to the function:
We can see X1
, X2
and X3
being initialized with values for the three arguments, but X0
(this
) is not explicitly initialized, so the decompiler falls back to the last initialized value (result of the call to __platform_strncmp()
), which is obviously unrelated. Can we make the decompilation nicer?
The solution is to mark the this
argument as unused by editing either the full function prototype or just the argument’s type:
After returning to the caller and refreshing, the output is much nicer-looking:
The decompiler inlined the strncmp
call into the if condition because it no longer needs the separate v19
variable. The bogus this argument got replaced by the dummy placeholder UNUSED_ARG()
.