Re: wchar_t as a string parameter

Tech-Archive recommends: Repair Windows Errors & Optimize Windows Performance



See below...
On Thu, 4 Dec 2008 13:25:01 -0800, JR <JR@xxxxxxxxxxxxxxxxxxxxxxxxx> wrote:

OK, you set a breakpoint. You display &szMyString, and it says 0012fe30.
You
single-step into MyFunction. You examine the parameter. It says 0012fe34. Is
my understanding correct?

Yes, you are correct. I did debug this in Release mode, btw...this is where
this behavior is occurring.

I have pasted the disassembly (for both DEBUG and RELEASE builds) for your
viewing 'pleasure'. I can't make much from it, but I am hope someone can.
One thing I do see between the 2 builds is the naming convention used in the
calls to the function(s): the debug build looks like it is using the stdcall
naming convention and the release build is not. I specifically am using the
__stdcall convention in BOTH builds. Maybe this is just a by-product of the
difference in the 2 builds...you tell me.

Anyway,

RELEASE BUILD:
TCHAR szRelease[31] = {0};
100072E4 push 3Ch
100072E6 lea edi,[esi+0Ch]
100072E9 push eax
100072EA mov dword ptr [esi+4],eax
100072ED mov dword ptr [esi+8],eax
100072F0 mov dword ptr [edi],eax
100072F2 mov word ptr [esp+10h],ax
100072F7 lea eax,[esp+12h]
100072FB push eax
100072FC mov dword ptr [esi],offset CGF1Struct::`vftable'
(10015FD4h)
10007302 call memset (100126BAh)
10007307 add esp,0Ch
if( gf1_RetrieveReleaseLevel(szRelease) )
1000730A lea ecx,[esp+8]
1000730E push ecx
****
Because these are using esp+8 to compute the address, this means your optimizations have
used Frame Pointer Optimization, where no EBP is maintained. This makes your code very
sensitive to misbehavior of esp. You would want to set a breakpoint here and look at the
value in ecx, which should be the same as szRelease as far as what the debugger tells you.
Record the value that is in esp at this point. That is, the point AT the push instruction
(the stack has not yet been pushed)

Note, by the way, that your programming is FAR below any acceptable threshold of safety;
you did NOT specify _countof(szRelease) as a parameter, to tell the called function how
large the buffer is. In most modern programming environments, a mistake like this is held
as a firable offense (I'm not kidding). It is NEVER considered safe to pass a pointer to
a buffer that is being filled in by any function without explicitly passing the length of
the buffer, and the called function guarantees that it will NEVER exceed this buffer size.
Code like this is the kind you read about in the front pages of weekly professional news
rags, usually under the headline of "Flaw in <your program here> allows security attacks".
Seriously, this kind of coding is considered Not Acceptable.
****
1000730F call gf1_RetrieveReleaseLevel (1000F990h)
10007314 test eax,eax
****
Set a breakpoint at this instruction. Look at the value of esp. It had better be the
same as the value you recorded above. It is clear that this code is assuming __stdcall
linkage. This seems odd because it should have been calling _gf1_RetrieveReleaseLevel@4.
Key here is this is no
add esp, 4
following the call, which would be essential if it was using __cdecl linkage. Note that
in the debug build, it DOES call _gf1_RetrieveReleaseLevel@4. So there's something
definitely wonky happening here.

Also, show the command line that is used for both the debug and release builds.
****
10007316 je CGF1Struct::CGF1Struct+59h (10007329h)
m_lReleaseLevelID = gf1_RetrieveReleaseLevelID(szRelease, &m_bUnicode);
10007318 push edi
10007319 lea edx,[esp+0Ch]
1000731D push edx
1000731E call gf1_RetrieveReleaseLevelID (1000FAB0h)
****
Notice this is _gf1_RetrieveReleaseLevelID@8 in the debug build, which is what I would
expect it to be.

You did not show the function prolog and epilog of gf1_RetrieveReleaseLevel, which is
critical to understanding the interactions here.

Either you have found a new and wondrous bug in the compiler, or you are compiling with
inconsistent settings somehow. Again, the value of ESP, if saved at instruction 10007318
(before that instruction is executed) must match the value of ESP right after the call
instruction above. If they are out of sync, that's why you are getting the error.

By the way, it appears that what might be happening is not that the second time you call
the same function in the same place that you have an error, but that when you pass the
string in to two different functions from two different places in the same subroutine, you
see two different addresses, which is not actually what you said initially.
****
10007323 movsx eax,ax
10007326 mov dword ptr [esi+8],eax


DEBUG BUILD:
TCHAR szRelease[31] = {0};
1002E85C mov word ptr [ebp-54h],0
1002E862 push 3Ch
1002E864 push 0
1002E866 lea eax,[ebp-52h]
1002E869 push eax
1002E86A call @ILT+1390(_memset) (1001C573h)
1002E86F add esp,0Ch
if( gf1_RetrieveReleaseLevel(szRelease) )
1002E872 lea eax,[ebp-54h]
1002E875 push eax
1002E876 call @ILT+3180(_gf1_RetrieveReleaseLevel@4) (1001CC71h)
1002E87B test eax,eax
1002E87D je CGF1Struct::CGF1Struct+98h (1002E898h)
m_lReleaseLevelID = gf1_RetrieveReleaseLevelID(szRelease, &m_bUnicode);
1002E87F mov eax,dword ptr [ebp-0Ch]
1002E882 add eax,0Ch
1002E885 push eax
1002E886 lea ecx,[ebp-54h]
1002E889 push ecx
1002E88A call @ILT+4195(_gf1_RetrieveReleaseLevelID@8) (1001D068h)
1002E88F movsx edx,ax
1002E892 mov eax,dword ptr [ebp-0Ch]
1002E895 mov dword ptr [eax+8],edx

FUNCTION DECLARATIONS:

GF1_API BOOL __stdcall gf1_RetrieveReleaseLevel(LPTSTR szReleaseLevel);
GF1_API short __stdcall gf1_RetrieveReleaseLevelID(LPCTSTR
szReleaseLevel, BOOL * m_bUnicodeRelease);
****
You have not said what a GF1_API is, so I have no idea if this is a contributor to the
problem or not; note also that if these have inconsistent definitions between Debug and
Release, I need to know that.

C-CODE:

TCHAR szRelease[31] = {0};
if( gf1_RetrieveReleaseLevel(szRelease) )
m_lReleaseLevelID = gf1_RetrieveReleaseLevelID(szRelease, &m_bUnicode);


If the disassembly is truly showing that one build is using the cdecl
calling convention, how can this be? Not only am I declaring the functions
as stdcall, I also have the build settings for both releases set to stdcall.

As far as stepping through the assembly code and examining the stack...I
have no idea how to do that or what to look for. Is there any 'quick'
explanation for that?
****
I've captured it pretty much as above. You can't be an effective C/C++ programmer without
knowing how to at least *read* assembly code. I keep my Intel instruction decoder manual
next to my computer (within arm's reach) and I have their free downloaded pdf file on my
laptop for when I travel. I've learned to read assembly language on a huge variety of
machines, including a few for which I did not even have the manual (it involves a bit of
outguessing what the hardware designer probably meant). Since ultimately it is the
machine code that is the truth, the whole truth, and nothing but the truth, you can't
avoid it.

There was a time when I was fluent in *writing* x86 assembly code, but that was when I was
writing device drivers for MS-DOS apps. I stopped writing assembly code in 1989, but I've
never found a week to go by when I don't need to *read* it.
****

Thanks for all the help so far...
Jeremy
Joseph M. Newcomer [MVP]
email: newcomer@xxxxxxxxxxxx
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
.



Relevant Pages

  • Re: Disassembly window for Windows Mobile 5
    ... when trying to debug my assembly code on the WM5 ... I try to step over or run past an instruction that modifies ... "No symbols are loaded for any call stack frame. ...
    (microsoft.public.vstudio.general)
  • RE: Step by Step skips large chunks of code
    ... The interesting thing is that it still skips code during debug but not ... has been correctly complied into assembly code. ... Microsoft Online Community Support ... where an initial response from the community or a Microsoft Support ...
    (microsoft.public.vsnet.debugging)
  • Re: PROMPT option
    ... Just few lines after the following instruction ... If launch the routine, as ususal from the tacl prompt, when the ... If I debug step by step the routine, as soon as I reach the ... from the executor software command line) was issued. ...
    (comp.sys.tandem)
  • Re: Why do Intels processors provide four debug registers rather than only one?
    ... there are four debug registers inside ... to executes the instruction at address 5, ... What makes you think that the program will execute the instruction at 5 ...
    (comp.lang.asm.x86)
  • Re: 8086 Assembly Programming Help
    ... > I am using DEBUG through MS-DOS to make and run a certain program. ... > I know how to do the basic things like MOV, ADD, SUB but when it comes ... subcommand for code and a subcommand + db subsubcommand for data, ... and points to the first instruction ...
    (comp.lang.asm.x86)