Solved

(MS VC++) Could you explain this to me ?

Posted on 2004-07-31
37
432 Views
Last Modified: 2010-04-17
------------| MS VC++ Code |--------------------

typedef union
{
      int            i;
      char            c;
      short            t;
} MY_UNION;

namespace SOME_SPACE { static MY_UNION uni; };

#define MY_TYPE(s,m)   SetType(((s *)&SOME_SPACE::uni)->m)

int SetType(int nValue)
{
  return 5;
}

typedef struct
{
                int                        count1;
      char                        Buffer1[257];
      char                        Buffer2[257];
      char                        Buffer3[257];
      char                        Buffer4[257];
      char                        Buffer5[257];
      char                        Buffer6[257];
      char                        Buffer7[257];
      char                        Buffer8[257];
      char                        Buffer9[257];
      char                        Buffer10[257];
      char                        Buffer11[257];
      char                        Buffer12[257];
      char                        Buffer13[257];
      char                        Buffer14[257];
      char                        Buffer15[257];
      char                        Buffer16[257];
      char                        Buffer17[257];
      char                        Buffer18[257];
      char                        Buffer19[257];
      char                        Buffer20[257];
      char                        Buffer21[257];
      char                        Buffer22[257];
      char                        Buffer23[257];
      char                        Buffer24[257];
      char                        Buffer25[257];
      char                        Buffer26[257];
      char                        Buffer27[257];
      char                        Buffer28[257];
      char                        Buffer29[257];
      char                        Buffer30[257];
      char                        Buffer31[257];
      char                        Buffer32[257];
      char                        Buffer33[257];
      char                        Buffer34[257];
      char                        Buffer35[257];
      char                        Buffer36[257];
      char                        Buffer37[257];
      char                        Buffer38[257];
      char                        Buffer39[257];
      char                        Buffer40[257];
      char                        Buffer41[257];
      char                        Buffer42[257];
      char                        Buffer43[257];
      char                        Buffer44[257];
      char                        Buffer45[257];
      char                        Buffer46[257];
      char                        Buffer47[257];
      char                        Buffer48[257];
      char                        Buffer49[257];
      char                        Buffer50[257];
      char                        Buffer51[257];
      char                        Buffer52[257];
      char                        Buffer53[257];
      char                        Buffer54[257];
      char                        Buffer55[257];
      char                        Buffer56[257];
      char                        Buffer57[257];
      char                        Buffer58[257];
      char                        Buffer59[257];
      char                        Buffer60[257];
      char                        Buffer61[257];
      char                        Buffer62[257];
      char                        Buffer63[257];
      char                        Buffer64[257];
      char                        Buffer65[257];
      char                        Buffer66[257];
      char                        Buffer67[257];
      char                        Buffer68[257];
      char                        Buffer69[257];
      char                        Buffer70[257];
      char                        Buffer71[257];
                int                        mode;

} BIG_STRUCT;


void main()
{

      int res = MY_TYPE(BIG_STRUCT, mode);

}


---------------------End of Code---------------------------


Could you explain the following to me:

If BIG_STRUCT is as big as it is shown above, the program causes an exception (Access Violation).

If we have the BIG_STRUCT structure a few character strings shorter (Buffer strings 67-71 are commented out), the program works fine.  Whats going on?


-andrey
0
Comment
Question by:andrey_2007
  • 14
  • 10
  • 5
  • +2
37 Comments
 
LVL 1

Expert Comment

by:rpz
ID: 11687294
You're typecasting a 4 byte variable into a 18 kb struct and returning the last member? That would put you about 18247 bytes beyond what you allocated memory for. I guess you hit a valid pointer by accident when commenting out parts of the struct. What is the purpose of this code exactly?
0
 
LVL 3

Author Comment

by:andrey_2007
ID: 11688461
I am affraid that it is incorrect. The code above has worked in production for ~ 10 years for a smaller structure.
The purpose of the macro MY_TYPE is to translate the type of a member of the structure into the argument of the function SetType(...). In real life there are a bunch of overloading functions SetType(...) with different types of argument, and the union  MY_UNION represents all possible types. I believe that the difference for smaller and bigger structure is more understanble when you look at the Assembly code but I still cannot completely explain it.

Thanks.
0
 
LVL 22

Expert Comment

by:grg99
ID: 11689051
It looks like you've inherited some "working", but horribly-designed code.

Any time one does wild-casting into a structure, there's lots of possibilities for errors.  Especially in a language like C, with no intrinsic array-bounds checking.


*Usually* when a program "works" but breaks when you change the size of something that should be completely unrelated, it tends to be that the program originally "worked" by pure dumb luck-- i.e. the struct member selections or array indexes just happened to point to valid memory by pure arithmetic chance.   Changing the size of something else changed the arrangement of things in memory just enough to cause the bad code to wipe out something.

0
 
LVL 3

Author Comment

by:andrey_2007
ID: 11689274
Sorry. My understamding is that you did not not look at the assembly code. Sorry again but I don't need a "general" explanation.
0
 
LVL 3

Author Comment

by:andrey_2007
ID: 11689285
grg99,

By the way, what kind of luck we are talking about , if you can copy and paste this code,shorten the number of strings in the structure BIG_STRUCT to 65  (instead of 71), this code will work perfectly  for you every time.
0
 
LVL 22

Expert Comment

by:grg99
ID: 11689340
>grg99,By the way, what kind of luck we are talking about , if you can copy and paste this code,shorten the number of strings in the structure BIG_STRUCT to 65  (instead of 71), this code will work perfectly  for you every time.


"Works perfectly" in most environments usually means "hasnt crashed or made any obvious errors".

It's more than likely that some of those addressing macros/functions/casts are going awry, and not indexing to the addresses you expect.  For example, if you accidentally use an index on a pointer, instead of its base type, it may be indexing by 4 bytes, instead of your expected 1, 2, or some other size.  This will end up addressing some unexpected spot in your BIG_STRUCT, which is soo big, that the wildly incorrect  address may be still inside the structure and with a little more luck unused at that time.  Soo the program may "work", but it's using unexpected memory locations.  Any minor change in the size of the structure, or the indexing, or program data may end this honeymoon.    Been there, done that, have the tee shirt, the CD, and trophy.  

I've seen this many many a time.  Anytime a program is sensitive to minor changes in structure sizes, or adding debug lines, it's very very very very very very very very very very very very very very very very very very very very very very very very very very very very very often a case of wild pointers.



0
 
LVL 3

Author Comment

by:andrey_2007
ID: 11689388
OK, you did not try this code. Thank you for your time. "Works perfectly" means that the code always produces the right result for you and for me and for all people on the Earth. In this particular case the result should be 5. This code (certainly not this sample, but the idea was the same) was written ~ 10 years ago by a recognized C++ guru. I personally don't like this code (I 've been 12+ years in C++ and 20 years in C). But the question is straight. Could you explain why this particular code ALWAYS works for the structue shorter than 66 string's fields and ALWAYSs causes Access Violation work if we have biger structure?
Please, don't respond if you don't know this particular answer.

Thanks.
0
 
LVL 1

Accepted Solution

by:
rpz earned 125 total points
ID: 11689610
After reading this conversation, I decided to try to run your code. In my case (VC++ 7.1), the AV occured when having 59 or more strings. Why then? I checked with the debugger, and here is what I found after having exactly 59 strings.

The variable displayed below is SOME_SPACE::uni dereferenced as a BIG_STRUCT. As you can see, it happens to have valid pointers to all but the last member, but the memory pointed to is absolute rubbish.

-      (BIG_STRUCT*)ptr      0x00428500 uni      BIG_STRUCT *
      count1      0      int
+      Buffer1      0x00428504 ""      char [257]
... .and it goes on like this...
+      Buffer43      0x0042af2e ""      char [257]
+      Buffer44      0x0042b02f ""      char [257]
+      Buffer45      0x0042b130 ""      char [257]
+      Buffer46      0x0042b231 "çwü§çw1 çw~ÞçwTÉæwÈàçwaçw›¢çwߧçwéhçwæwžæwý·çwbsçw.ðçw èwYSçw"      char [257]
+      Buffer47      0x0042b332 "ress"      char [257]
+      Buffer48      0x0042b433 "tringsW"      char [257]
+      Buffer49      0x0042b534 "oseHandle"      char [257]
+      Buffer50      0x0042b635 "StringTypeA"      char [257]
+      Buffer51      0x0042b736 ".dll"      char [257]
+      Buffer52      0x0042b837 ""      char [257]
+      Buffer53      0x0042b938 ""      char [257]
+      Buffer54      0x0042ba39 ""      char [257]
+      Buffer55      0x0042bb3a ""      char [257]
+      Buffer56      0x0042bc3b ""      char [257]
+      Buffer57      0x0042bd3c ""      char [257]
+      Buffer58      0x0042be3d ""      char [257]
+      Buffer59      0x0042bf3e ""      char [257]
      mode      CXX0030: Error: expression cannot be evaluated      int


So, what happens at 58 strings? It ends like this
+      Buffer55      0x0042bb3a ""      char [257]
+      Buffer56      0x0042bc3b ""      char [257]
+      Buffer57      0x0042bd3c ""      char [257]
+      Buffer58      0x0042be3d ""      char [257]
      mode      0      int

Voilà, the "mode" member is now valid (although the memory is certainly not what you alllocated for). If you want more evidence of this, let's have a look at the hex dump from a part within the structure:
...
0x0042B56E  48 65 61 70 56 61 6c 69 64 61 74 65 00 00 fc 00 47 65 74 43 50 49 6e 66 6f 00 f5 00 47 65 74 41 43 50 00 00 8b 01 47 65 74  HeapValidate..ü.GetCPInfo.õ.GetACP..‹.Get
0x0042B597  4f 45 4d 43 50 00 00 73 03 56 69 72 74 75 61 6c 41 6c 6c 6f 63 00 00 10 02 48 65 61 70 52 65 41 6c 6c 6f 63 00 7b 03 56 69  OEMCP..s.VirtualAlloc....HeapReAlloc.{.Vi
0x0042B5C0  72 74 75 61 6c 51 75 65 72 79 00 00 1f 02 49 6e 74 65 72 6c 6f 63 6b 65 64 45 78 63 68 61 6e 67 65 00 e1 02 53 65 74 43 6f  rtualQuery....InterlockedExchange.á.SetCo
0x0042B5E9  6e 73 6f 6c 65 43 74 72 6c 48 61 6e 64 6c 65 72 00 6b 02 4d 75 6c 74 69 42 79 74 65 54 6f 57 69 64 65 43 68 61 72 00 3a 02  nsoleCtrlHandler.k.MultiByteToWideChar.:.
0x0042B612  4c 43 4d 61 70 53 74 72 69 6e 67 41 00 00 3b 02 4c 43 4d 61 70 53 74 72 69 6e 67 57 00 00 b2 01 47 65 74 53 74 72 69 6e 67  LCMapStringA..;.LCMapStringW..².GetString
0x0042B63B  54 79 70 65 41 00 00 b5 01 47 65 74 53 74 72 69 6e 67 54 79 70 65 57 00 00 97 02 51 75 65 72 79 50 65 72 66 6f 72 6d 61 6e  TypeA..µ.GetStringTypeW..—.QueryPerforman
0x0042B664  63 65 43 6f 75 6e 74 65 72 00 d5 01 47 65 74 54 69 63 6b 43 6f 75 6e 74 00 00 3e 01 47 65 74 43 75 72 72 65 6e 74 54 68 72  ceCounter.Õ.GetTickCount..>.GetCurrentThr
0x0042B68D  65 61 64 49 64 00 00 3b 01 47 65 74 43 75 72 72 65 6e 74 50 72 6f 63 65 73 73 49 64 00 c0 01 47 65 74 53 79 73 74 65 6d 54  eadId..;.GetCurrentProcessId.À.GetSystemT
0x0042B6B6  69 6d 65 41 73 46 69 6c 65 54 69 6d 65 00 79 03 56 69 72 74 75 61 6c 50 72 6f 74 65 63 74 00 00 bb 01 47 65 74 53 79 73 74  imeAsFileTime.y.VirtualProtect..».GetSyst
....

Looks like a DLL import table or something.

I still believe you were just lucky those ten years.
0
 
LVL 1

Expert Comment

by:rpz
ID: 11689628
Sorry, I forgot to show the disassembly for the call in question. This is the version that fails:

     int res = MY_TYPE(BIG_STRUCT, mode);
00411A85  mov         eax,dword ptr ds:[0042C040h]
00411A8A  push        eax  
00411A8B  call        SetType (4111B3h)

And this version does not:
     int res = MY_TYPE(BIG_STRUCT, mode);
00411A85  mov         eax,dword ptr ds:[0042BF40h]
00411A8A  push        eax  
00411A8B  call        SetType (4111B3h)

And the memory looks as this in the area of interest:

0x0042BFAE  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  .........................................
0x0042BFD7  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  .........................................
0x0042C000  ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ??  .........................................
0x0042C029  ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ??  .........................................

As you can see, this string (in my case #59) just passes the border to the unallocated memory.
0
 
LVL 3

Author Comment

by:andrey_2007
ID: 11689740

It is exactly what I have for VC++ 6.0. The difference is the number of strings: I am OK with 66 strings. Moreover, when I look at dissassembly code I can see that compiler's table __cfltcvt_tab is used for small structure (I tested 3 member structure ) and __except_list table for bigger structure when the code still works (66 string fields for my VC++ 6.0). At this point of time I still believe that the problem is still related to some internal compiler's ot LINKEDIT limitations. What is lucky about this? The original C++ code is not very good, but it is still legitimate. What is wrong with it? Why does it work on all machines for structures with the size less than 16000 bytes or so? I don't think that the luckiness is a proper explanation. I need to decide what to do with the code that perfectly works for about 100 production systems and does not work for one new developer that happened to increase the length of the structure tremendously.

Thanks.
0
 
LVL 1

Expert Comment

by:rpz
ID: 11689857
> What is lucky about this? The original C++ code is not very good, but it is still legitimate. What is wrong with it?
If you look at the disassembly in my previous comment, you see that the code tries to access an invalid memory location, in this case 0x0042BF40. The reason the code works with the fewer number of strings on your production machines is simply that the memory pointed to by the last member is then unused by whatever allocated it. "Luck" doesn't necessarily mean the errors fluctuate. As long as your compiled code remains the same, a faulty program could very well work "flawlessly", but even a very slight change to it could cause it to break down completely, as has already been pointed out in a previous comment. It could be dangerous to say that this code is "legitimate". It is, at best, a hack that uses the internal workings of the compiler to hide problems with invalid pointers.

I am still quite unclear with what the code should really do, but one way to get your sample program not to crash is allocating more memory to the MY_UNION union, like this:
typedef union
{
     int          i;
     char          c;
     short          t;
     char bigdata[8+257*71];
} MY_UNION;
0
 
LVL 3

Author Comment

by:andrey_2007
ID: 11690073
Unfortunetely, you did not understand what the code does. I really don't have time to explain this to you in detail. But the point is in the union, only those types should be presented that we are going to point to, in the structure. For my example you can point to int, char or short in the structure. You cannot point to the string. I hope now it will be clearer for you. So your suggestion about union is obvious but completely useless (Sorry.)  
0
 
LVL 8

Expert Comment

by:adg080898
ID: 11690116
 static MY_UNION uni;

This allocates four bytes of space. It is a static structure, so it will be located in the uninitialized data area.

And the macro

  #define MY_TYPE(s,m)   SetType(((s *)&SOME_SPACE::uni)->m)

Takes the address of the four bytes mentioned above, and *pretends* that the four bytes are instead the huge structure of arrays. it then calculates offset of the "mode" member, which is WAY past the end of "uni".

The code is completely and utterly wrong. If it worked, it was pure luck, as the other experts have tried to tell you.
0
 
LVL 8

Expert Comment

by:adg080898
ID: 11690143
If you want an explanation for why you get the access violation, it is simple:

uni is 4 bytes. You are then calculating this pointer WAY past the end of uni. If you comment out a few of those arrays, your pointer ends up pointing to some weird area of the executable, like the import section or debug info. If you put those arrays back in, your pointer is so far past the uni structure, it is *past the end of the executable*. You are trying to access the mode member (which is not there), and your pointer is way out in space accessing memory that was never allocated or reserved, out past the end of the program.
0
 
LVL 3

Author Comment

by:andrey_2007
ID: 11690172
adg,

It is not so obvious. When the program works (also for huge structures, like 16,000 bytes ), the value of int mode  goes as the argument of SetType(...). You can check it through a debugger. So basically not the whole structure's chunk of memory , but just integer "mode" goes to the 4 bytes that was reserved by static MY_UNION uni;
0
 
LVL 8

Expert Comment

by:adg080898
ID: 11690179
The executable is constructed from several sections:

.text: assembly code
.data: initialized data
.bss: uninitialized data (filled with zeros before program start)
.idata: import data
.rsrc: resources such as dialogs and icons
etc....

each of these sections is rounded up to the next higher multiple of 4KB. Now, your (4 byte) uni structure gets placed in the .bss section. If that is the only data in the program, .bss will probably end up being 4KB.

Now, if I access, say, 18248 bytes past the END of uni, my pointer will end up pointing to some garbage, probably so far ahead that it is past the end of the memory image. rpz demonstrated that it points well into the "idata" section, way past the end of "bss".

Reassuring us how good the original programmer was will NOT somehow magically make this code valid. It is completely wrong. It might be right in context (in the actual app), but the code listed here is completely invalid. Perhaps he obfuscated the code to buy some job security? Forgive me for being blunt, but myself and all the other people who posted here are trying to tell you that the code is not valid! :)

Here is an example of what you are doing:

----

static char aSmallArray[500];

int main()
{
    printf("This is going to crash!\n", aSmallArray[80000]);

    return 0;
}

----

See how it tries to read data WAY past the end of aSmallArray?
0
 
LVL 8

Expert Comment

by:adg080898
ID: 11690198
We don't need to convince you, your CPU attempts to lookup the address of the data (way off the end of the executable). The CPU finds that the paging tables say the memory (way off in space) is not allocated to any process. The CPU raises a paging exception, windows handles it, your program dies, end-of-story.
0
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 
LVL 3

Author Comment

by:andrey_2007
ID: 11690241
adg,

You did not respond my previous comment. Please talk from yourself, not from the whole community. Please try not to be rude. Don't take it personally.
0
 
LVL 8

Expert Comment

by:adg080898
ID: 11690245
If you can show me where it is ALLOCATING 16000+ bytes, then your code might be valid. You code allocates a 4-byte union, then accesses memory that is over 18000 bytes PAST the end of the union.
0
 
LVL 8

Expert Comment

by:adg080898
ID: 11690247
You were not entirely polite to rpz. (saying his suggestion was "useless")
0
 
LVL 3

Author Comment

by:andrey_2007
ID: 11690254
adg,

Do you agree that only integer "mode" goes into static uni or not?
0
 
LVL 8

Assisted Solution

by:adg080898
adg080898 earned 125 total points
ID: 11691012
When you use a struct, each member has a name, size, and offset.

When I do this:

typedef struct {
    char aBuf1[8];
    int nSomeNum;
    char aBuf2[8];
} Something;

Then the compiler interprets that to mean:

At offset 0 there is an array of 8 chars named aBuf1.
At offset 8 there is an int named nSomeNum.
At offset 12 there is an array of 8 chars names aBuf2.

Each member of a struct has a certain "offset".

In BIG_STRUCT, the offset of "mode" is 18252.

I can do this:
  int x = ((Something*)p)->nSomeNum;

In this case, the compiler will take the value of p (and PRETEND that it is an address of a Something structure) and it will add the offset of nSomeNum, 8.

So if p was 0x00402000, it will take that and add the offset of nSomeNum, which is 8. The actual addres that is read is 0x00402008.

Now back to your code...

It takes the address of "uni".

(BIG_STRUCT*)&uni

This tells the compiler to take the address of uni, and PRETEND that it is the address of a BIG_STRUCT.

The second macro parameter says to access the "mode" member of the structure. The MY_TYPE macro expands to this:

((BIG_STRUCT*)&uni)->mode

So it says, "Take the address of uni, pretend that it is a BIG_STRUCT, add 18252 to that, and read the int stored there."

But at uni, only 4 bytes are allocated. If I access 18252 bytes from the beginning of uni, I am accessing something out in space, extremely far past the end of uni.
0
 
LVL 8

Expert Comment

by:adg080898
ID: 11691034
The real error is this line:

namespace SOME_SPACE { static MY_UNION uni; };

If you were to replace it with:

namespace SOME_SPACE { static BIG_STRUCT uni; };

and move it below the "typedef struct" it will work. It will allocate 18256 bytes for uni, and then the computer can add 18252 to the base address of uni and actually read uni!
0
 
LVL 8

Expert Comment

by:adg080898
ID: 11691039
....below the entire typeef struct, after the ;
0
 
LVL 8

Expert Comment

by:adg080898
ID: 11691040
I mean, after "} BIG_STRUCT;"
0
 
LVL 8

Expert Comment

by:adg080898
ID: 11691095
Why not use an array of arrays:

typedef struct
{
    int                    count1;
    char                 Buffers[71][257];
    int                    mode;
} BIG_STRUCT;
0
 
LVL 22

Expert Comment

by:grg99
ID: 11692584
To the other experts:

A long time ago a VERY SMART fellow, an excellent programmer,  asked me for advice: his car stereo was blowing fuses at peculiar times.   I thought over the problem and gave him three plausible explanations of what could be wrong; each explanation was a bit subtle, but they were each logically consistent, fit all the known facts, and matched my experience with other similar situations.

All he could say is "BUT HOW CAN THAT BE?".   He was very smart, but had some mental block, he thought if every connection LOOKED okay, and he thought every wire went to the right place, and no speaker had a little thought balloon saying "I MAY LOOK OKAY FROM THE OUTSIDE, BUT I HAVE A SHORTED CROSSOVER CAPACITOR", he thought everything HAD to be okay.

There's not much you can do for such folk, they're stuck  in a mental logjam.



0
 
LVL 14

Assisted Solution

by:Daniel Junges
Daniel Junges earned 125 total points
ID: 11704324
hi andrey,

This code serves to read data from memory, begining at the address of SOME_SPACE::uni. I testes it on my enviroment(XP SP1, VC7.1), it works without access violation, my be because i have 512mb of memory. Probably it cause an exception when (BIGSTRUCT)SOME_SPACE::uni contains some readonly memory blocks. If it runs since 10 years on a system, then probably this OS os very old and have more tolerance that newers OS's.

regards junges
0
 
LVL 8

Expert Comment

by:adg080898
ID: 11714253
andrey_2007:
>> Do you agree that only integer "mode" goes into static uni or not?

uni is a union, and the largest member of that union is 4 bytes. Therefore uni is four bytes.

If you want proof, step into the program with the MSVC debugger, press Shift+F9, and type sizeof(uni) and press enter. It says 4, meaning uni is 4 bytes.

To get the address of uni in the debugger, you press Shift+F9 and type

&uni

I get 0x00427C50. That is the address where uni is stored.

Now, if you step into the assembly, you see that it reads address 0x0042C39C.

So if the beginning of uni is 0x00427C50 and uni is four bytes, then uni ends at 0x00427c54.

Your program is reading address 0x0042C39C, which is 18248 bytes past the end of uni, exactly what I calculated in previous postings.

The MY_TYPE macro accesses memory that is 18248 bytes past the end of uni. It is accessing something, but it is not accessing uni. On your compiler and my compiler, that just HAPPENS to be past the end of the executable. Apparently junges libraries are larger, so it doesn't crash there, but it is still accessing some crazy memory location, not uni.

So, to answer that question: No, there is no space in uni that is 18248 bytes from the beginning of uni, because uni is 4 bytes.

uni doesn't even have a "mode" member.

There is nowhere in that program listing where it stores any information. It *reads* a memory location, but that crashes it before it even touches "res". The memory address that the MY_TYPE macro calculates is so high that it is outside the entire program image, in unallocated space, causing a crash.

So integer 'mode' never goes into static uni.
0
 
LVL 22

Assisted Solution

by:grg99
grg99 earned 125 total points
ID: 11714529
Another possibility:

It's quite possible the program has been running "perfectly" for 10 years due to address-wraparound.

If the program was compiled in any of the old 16-bit real modes for the PC, then it's quite possible for address calculations to:

(1)  End up past the end of the "legally allocated" segment, but still within physical, perhaps unallocated for any other purpose,  memory.

(2) Or "wrap around" over the top of the physical segment end, and end up somewhere very different, but still in the 64K data segment.  With a little luck, some unused address.

-------------

This often shows up as a crash when you move any data around, or when you port the program to some addressing mode that has more segment limit checks.

--------------

Don't feel too bad if this is the case:  It's happened to others, including the famous case that's cost $many many Millions of $:

One bit of very old program loader code from Microsoft only worked if segments wrapped within the 16-bit addressing space.   The problem wasnt detected until the 286 chip was in development.  By then there were millions of copies of programs that used this bad loader, so something drastic had to be done to all future "PC compatibles":  The A20 kludge was added.  Every PC since then has had extra hardware to enable/disable address wrapping.  This took extra explicit hardware on the early PC's, costing maybe $2 each, now it's subsumed into the CPU support chips.  

So don't feel TOO bad if you have old code that works only due to the vaguaries of 16-bit addressing, you're far from alone in this.

0
 
LVL 8

Expert Comment

by:adg080898
ID: 11722540
The A20 kludge is a wraparound at the 1MEG line, not a segment wraparound. Even if it were running under a segmented architecture, it would still be accessing some random memory address, it would just be some random address within a given 64K area.
0
 
LVL 8

Expert Comment

by:adg080898
ID: 11722622
Since the program accesses memory that is way past the structure it is trying to read, if you add data after "uni", add at least 18252 bytes, then your accesses of "uni" will actually be reading some area of the variables after uni, and it won't crash. If you *write* to that pointer calculated by MY_TYPE, you will be corrupting the content of the variables declared after uni. If by chance your corruption of memory doesn't happen to disturb anything important, it might actually run. This is a type of bug that happens sometimes when working with pointers, where you calculate a completely wrong address and read/write the wrong address, causing extremely weird bugs to occur elsewhere in the program because you are corrupting unrelated variables. But you would never deliberately do that. You should allocate memory properly and calculate pointers to data areas you own.
0
 
LVL 22

Expert Comment

by:grg99
ID: 11729710
>The A20 kludge is a wraparound at the 1MEG line, not a segment wraparound.

Thats right, it was expected that using a segment of > $F000 with high offsets would wrap around to low memory.
Same basic idea, only with the segment part, not the offset part.  What a weird world.

Way back in ancient days, whenever we ran BASIC on a PDP-8, afterwards one of the disks would be unreadable until a reboot.   Turned out BASIC was trying to write a flag of #0001 to the right offset, but the wrong memory bank (sort of like a segment).  That bashed address just happened to be smack dab in an important system disk info table.   Nobody  noticed the problem until they got enough disk devices to reach table entry #7, which was the one that got bashed.  The flag it was trying to set was a rather unimportant one, having to do with system swapping, so nobody noticed the flag wasnt getting set.   This stuff goes waaaaaay back, folks.





0
 
LVL 3

Author Comment

by:andrey_2007
ID: 11917550
I have not abondoned this question. Do no delete or close.
0
 
LVL 1

Expert Comment

by:rpz
ID: 11925351
I think the original question has been answered many times over (Q: Why does the program crash? A: You write outside of allocated memory). If you have additional questions, you should post it as a new question.
0

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

Suggested Solutions

Displaying an arrayList in a listView using the default adapter is rarely the best solution. To get full control of your display data, and to be able to refresh it after editing, requires the use of a custom adapter.
If you’re thinking to yourself “That description sounds a lot like two people doing the work that one could accomplish,” you’re not alone.
An introduction to basic programming syntax in Java by creating a simple program. Viewers can follow the tutorial as they create their first class in Java. Definitions and explanations about each element are given to help prepare viewers for future …
In this fourth video of the Xpdf series, we discuss and demonstrate the PDFinfo utility, which retrieves the contents of a PDF's Info Dictionary, as well as some other information, including the page count. We show how to isolate the page count in a…

708 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

17 Experts available now in Live!

Get 1:1 Help Now