FIX: Setting Bitfields with /Oe Overwrites Stored BP

Last reviewed: September 18, 1997
Article ID: Q114074
6.00 6.00a 7.00 | 6.00 6.00a | 1.00
MS-DOS          | OS/2       | WINDOWS
kbtool kbfixlist kbbuglist

The information in this article applies to:

  • The Microsoft C/C++ Compiler (CL.EXE), included with:

        - Microsoft C for MS-DOS, versions 6.0, 6.0a, and 6.0ax
        - Microsoft C for OS/2, versions 6.0 and 6.0a
        - Microsoft C/C++ for MS-DOS, version 7.0
        - Microsoft Visual C++ for Windows, version 1.0
    

SYMPTOMS

Returning from a function which modifies bitfields of a global structure causes the system to hang, crash with a memory violation, or execute an invalid instruction.

CAUSE

Incorrect code can be generated when bitfields of a global structure are modified and /Oe (global register allocation) optimization is used. For the problem to be reproduced, /Gt<n> (data size threshold) must be specified with n less than the size of the global struct.

This incorrect code overwrites the value of BP pushed onto the stack on entry into the function.

The sample code at the end of this article demonstrates the problem.

RESOLUTION

There are a few possible ways to work around this problem.

  • Compile without the /Oe (global register allocation) optimization. Similarly, use #pragma optimize ( "e", off) to turn /Oe optimization off just for the one function.
  • Compile with /Gt<n> (data size threshold) where n is greater than or equal to the size of the global structure.
  • Rebuild with Visual C++ 1.5 or later, which corrects this problem.

STATUS

Microsoft has confirmed this to be a problem in the Microsoft products listed above. This is not a problem in Visual C++, 32-bit Edition. This problem was corrected in Visual C++ version 1.5.

Sample Code

/* Compile options needed:  /AL /Oe /Fc /Gt14
   Description: This code, when compiled with Microsoft C/C++
                compilers previous to version 8.0c and with /Oe,
                overwrite a stored BP on the line indicated.
                Without /Oe, the problem does not occur.

                The printf's do not demonstrate the problem,
                it is the failure to return from the function call,
                or examination of the .COD listing, which
                demonstrates this.
*/

#include <stdio.h>

struct test_struct {
    char *text;
    int iTest;
    unsigned unsigned_1:1;
    unsigned unsigned_2:1;
    unsigned unsigned_3:1;
    unsigned unsigned_4:1;
    unsigned unsigned_5:1;
    unsigned unsigned_6:1;
    unsigned unsigned_7:1;
    unsigned unsigned_8:1;
    unsigned unsigned_9:1;
    };

static struct test_struct tst_strct[2];

void oe_error( int iX, char *text, int iTest,
               int test_1, int test_2, int test_3, int test_4,
               int test_5, int test_6, int test_7, int test_8,
               int test_9);

void test(void);

void main(int argc, char *argv)
{
   int iTest_1;

   iTest_1 = 10;

   // The following is needed for creation of a stackframe in main():
   if(iTest_1+argc < 10) {
      // will never be reached.
      test();
   }

   test();

   printf("It should be: iTest_1 = 10\n" );
   printf("       It is: iTest_1 = %d\n", iTest_1);
}

// Take comment off following line to work around problem
// #pragma optimize ( "e", off)

void test(void)
{
   int iX = 1;

   oe_error( iX, "TEXT", 5, 1, 1, 1, 1, 0, 0, 0, 0, 0);
}

void oe_error( int iX, char *text, int iTest,
               int test_1, int test_2, int test_3, int test_4,
               int test_5, int test_6, int test_7, int test_8,
               int test_9)
{
   tst_strct[iX].text = text;
   tst_strct[iX].iTest = iTest;
   tst_strct[iX].unsigned_1 = test_1;
   tst_strct[iX].unsigned_2 = test_2;
   tst_strct[iX].unsigned_3 = test_3;
   tst_strct[iX].unsigned_4 = test_4;
   tst_strct[iX].unsigned_5 = test_5;
   tst_strct[iX].unsigned_6 = test_6;
   tst_strct[iX].unsigned_7 = test_7;
   tst_strct[iX].unsigned_8 = test_8;
   tst_strct[iX].unsigned_9 = test_9;
   /* The stored BP is overwritten here */
}

// Uncomment for work around
// #pragma optimize ("", on)

/* The code which causes BP to be overwritten comes immediately after
   the line tst_strct[iX].unsigned_9 = test_9; in the .COD listing.
   The problem is that this code does

   *** 000099    81 c3 06 00    add    bx,OFFSET $S175_tst_strct+6
   *** 00009d    89 5e fe       mov    WORD PTR [bp-2],bx

>> *** 0000a0    8c 5e 00       mov   WORD PTR [bp],ds
>> This will overwrite the stored BP, so that BP is not correctly >> retrieved at the end of this function.

   See the .COD listing produced when compiling for more information.
*/


Additional reference words: 8.00 1.00
KBCategory: kbtool kbfixlist kbbuglist
KBSubcategory: CLIss
Keywords : CLIss kb16bitonly kbbuglist kbfixlist kbtool
Version : 6.00 6.00a 7.00 | 6.00 6.00a | 1
Platform : MS-DOS OS/2 WINDOWS
Solution Type : kbfix


THE INFORMATION PROVIDED IN THE MICROSOFT KNOWLEDGE BASE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. MICROSOFT DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL MICROSOFT CORPORATION OR ITS SUPPLIERS BE LIABLE FOR ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL, CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF MICROSOFT CORPORATION OR ITS SUPPLIERS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES SO THE FOREGOING LIMITATION MAY NOT APPLY.

Last reviewed: September 18, 1997
© 1998 Microsoft Corporation. All rights reserved. Terms of Use.