Open RolphWoggom opened 3 years ago
[ '0x5f5d60':vftable{ ea:'0x5f5d60',
entries:entries{ '0':vftentry{ demangled_name:'',
ea:'0x548460',
import:false,
name:"virt_meth_0x548460",
offset:0,
type:meth
}
},
length:1,
vftptr:'0x4'
},
'0x5f5d60':vftable{ ea:'0x5f5d60',
entries:entries{ '0':vftentry{ demangled_name:'',
ea:'0x548460',
import:false,
name:"virt_meth_0x548460",
offset:0,
type:meth
}
},
length:1,
vftptr:'0x0'
}
]
The vftable in question is installed by 0x548110 and 0x548200, at different offsets. I think the oustanding question at this point is are these on the same class?
From comparing this to the PS2 version with symbols it seems that:
So does it seem like 0x5f5d60 is legitimately installed at two different offsets in radSoundHalListener?
Can you get an object layout for radSoundHalListener from the PS2 version?
Here is the class hierarchy courtesy of RTTI:
data:006433EC ; public struct radSoundHalListener /* mdisp:0 */ :
.data:006433EC ; public struct IRadSoundHalListener /* mdisp:0 */ :
.data:006433EC ; public struct IRefCount /* mdisp:0 */,
.data:006433EC ; public struct radSoundObject /* mdisp:4 */ :
.data:006433EC ; public class radRefCount /* mdisp:4 */ :
.data:006433EC ; public class radObject /* mdisp:4 */ :
.data:006433EC ; public class radBaseObject /* mdisp:4 */
.data:006433EC ; struct radSoundHalListener `RTTI Type Descriptor'
@sei-ccohen are a bit confused: Because of the negative offset accessed in 0x548110, we believed there was a virtual base involved. But according to the above, there is no virtual base on radSoundHalListener.
The key to understanding what is going on is understanding why 0x548110 thinks it is ok to reference the object at a negative offset. Most likely whatever is causing the offset difference is also causing us to get confused about the vftable being installed in two different offsets.
We are still experimenting, but we were able to generate a negative offset in a virtual function. https://www.godbolt.org/z/feoKPMajx
Basically, this happens when a virtual function is only accessed from the second (or later?) base class, and the function accesses a member in the first base class. This needs a bit more thought, but it means that the object pointer for a virtual function in a derived class may not always be pointing at the start of the derived class!
This needs a bit more thought, but it means that the object pointer for a virtual function in a derived class may not always be pointing at the start of the derived class!
Seems like this is happening here:
Sounds like the problem encountered here was identified?
Yes, I think that is what is happening there. We are thinking about the best way to fix it.
By the way, in the meantime, you should be able to manually remove the problematic vftables from the .results file to get the export to work.
Great news! And thanks for the tip. I was able to export by removing finalInheritance(0x5f5d60, 0x5f5c04, 0x4, 0x5f5d60, false).
and finalInheritance(0x5f7a80, 0x5f5bf4, 0x4, 0x5f7a80, false).
. The second one doesn't use a negative offset directly but is preceded by it (near 0x567614).
Here is the class hierarchy courtesy of RTTI:
data:006433EC ; public struct radSoundHalListener /* mdisp:0 */ : .data:006433EC ; public struct IRadSoundHalListener /* mdisp:0 */ : .data:006433EC ; public struct IRefCount /* mdisp:0 */, .data:006433EC ; public struct radSoundObject /* mdisp:4 */ : .data:006433EC ; public class radRefCount /* mdisp:4 */ : .data:006433EC ; public class radObject /* mdisp:4 */ : .data:006433EC ; public class radBaseObject /* mdisp:4 */ .data:006433EC ; struct radSoundHalListener `RTTI Type Descriptor'
What tool was used to create this? Is that an IDA thing?
Glad to hear you were able to get the JSON export to work.
Yes, the class hierarchy is an IDA feature I recently discovered by accident. It adds the hierarchy as a comment above the RTTI Type Descriptors.
I haven't used it, but I think https://github.com/astrelsky/Ghidra-Cpp-Class-Analyzer is a similar capability for Ghidra.
I recently found that Jan Gray talked about this:
Consider next S::rvf(), which overrides R::rvf(). Most implementations note that S::rvf() must have a hidden this parameter of type S*. Since R’s rvf vftable slot may be used when this call occurs:
((R)ps)->rvf(); // (((R)ps)->R::vfptr[1])((R)ps)
Most implementations add another thunk to convert the R passed to rvf into an S. Some also add an additional vftable entry to the end of S’s vftable to provide a way to call ps- rvf() without first converting to an R*. MSC++ avoids this by intentionally compiling S::rvf() so as to expect a this pointer which addresses not the S object but rather the R embedded instance within the S. (We call this “giving overrides the same expected address point as in the class that first introduced this virtual function”.) This is all done transparently, by applying a “logical this adjustment” to all member fetches, conversions from this, and so on, that occur within the member function. (Just as with multiple inheritance member access, this adjustment is constant-folded into other member displacement address arithmetic.)
One thing I noticed recently is that there are actually stubs for logical this adjustments that we should be able to detect.
Here's an example for experimentation: https://godbolt.org/z/qs55rzaj6
When analyzing the Simpsons: Hit & Run demo JSON generation fails.
Executable, ApiDB, facts and results uploaded here, please let me know if you need any of the other files.
The error is
ERROR: Duplicate key: '0x5f5d60'
: