Memory allocation woes
-
This was right under my nose for about two weeks before I noticed. Vigorous face-palming ensued when I noticed the difference. It was in some really (pre-heap) simple memory allocation. For some reason, I was overwriting memory starting at odd locations. This broke my IDT and GDT, so I couldn't even debug properly My memory allocation was quite simple. It took the kernel's ending address in memory, page-aligned it and added 0x1000. When a small amount of memory was requested, it simply added the memory size to the ending address and returned the previous ending address. It turns out that I was allocating memory at 0x0; for those who don't know why that's bad, it can overwrite the console memory, ACPI tables, and the stack. The strange thing was, my ending address was fine. The problem came from this line:
uint MemoryAccess::placementAddress = (uint)&end & 0xFFFFF000 + 0x1000;
Because of operator precedence, the AND was getting performed in the wrong order. Two characters fixed the problem: ( and )
uint MemoryAccess::placementAddress = ((uint)&end & 0xFFFFF000) + 0x1000;
Computafreak wrote:
the AND was getting performed in the wrong order
No. The AND was being performed in the RIGHT order. It just wasn't the order you wanted!
Phil
The opinions expressed in this post are not necessarily those of the author, especially if you find them impolite, inaccurate or inflammatory.
-
Computafreak wrote:
the AND was getting performed in the wrong order
No. The AND was being performed in the RIGHT order. It just wasn't the order you wanted!
Phil
The opinions expressed in this post are not necessarily those of the author, especially if you find them impolite, inaccurate or inflammatory.
precedent order... easy to forget!
-
precedent order... easy to forget!
-
Even harder when I never learned it (apart from the basic mathematical one) in the first place
That's why I always fully parenthesize expressions. I've used too many languages and too many compilers to remember the arcane rules when just inserting the parenteses fixes the problem once and for all.
Software Zen:
delete this;
Fold With Us![^] -
That's why I always fully parenthesize expressions. I've used too many languages and too many compilers to remember the arcane rules when just inserting the parenteses fixes the problem once and for all.
Software Zen:
delete this;
Fold With Us![^]yeah, same here. i've been bitten by these kinds of precedence tricks too many times.
-
This was right under my nose for about two weeks before I noticed. Vigorous face-palming ensued when I noticed the difference. It was in some really (pre-heap) simple memory allocation. For some reason, I was overwriting memory starting at odd locations. This broke my IDT and GDT, so I couldn't even debug properly My memory allocation was quite simple. It took the kernel's ending address in memory, page-aligned it and added 0x1000. When a small amount of memory was requested, it simply added the memory size to the ending address and returned the previous ending address. It turns out that I was allocating memory at 0x0; for those who don't know why that's bad, it can overwrite the console memory, ACPI tables, and the stack. The strange thing was, my ending address was fine. The problem came from this line:
uint MemoryAccess::placementAddress = (uint)&end & 0xFFFFF000 + 0x1000;
Because of operator precedence, the AND was getting performed in the wrong order. Two characters fixed the problem: ( and )
uint MemoryAccess::placementAddress = ((uint)&end & 0xFFFFF000) + 0x1000;
Yep. Whenever & and | are involved, I use parenthesizes.
-
Yep. Whenever & and | are involved, I use parenthesizes.
A lesson I learned bitterly. I wasted far too much time on that. The most irritating thing was that I had a little problem with paging, so decided to rewrite that particular class. Now I don't need that function to begin with - I use a ported liballoc instead. It's sad - if I had tackled the rewrite first, that problem would have resolved itself :sigh:
-
That's why I always fully parenthesize expressions. I've used too many languages and too many compilers to remember the arcane rules when just inserting the parenteses fixes the problem once and for all.
Software Zen:
delete this;
Fold With Us![^]Borland's compilers in the 1980's-1990's had an option to produce a warning if certain variations in precedence rules would cause expressions to parse differently. I don't remember the exact circumstances that would yield a warning, but I think it would squawk (if the option was turned on) if reversing the precedence of the shift and bitwise operators would change the code. Sometimes appeasing the compiler required adding ugly parenthesis in cases where the 'natural' reading would have been the correct one, but generally the warning was very helpful. Nowadays it seems Microsoft's compilers like to squawk at silly things while ignoring more serious ones. Coercing from double to single in vb.net without a cast? Error. Trying to 'and' a 64-bit flag with 'not &h80000000' or 'not someUnsignedIntVar'? No squawk, but clears the upper 32 bits of the flag.
-
Borland's compilers in the 1980's-1990's had an option to produce a warning if certain variations in precedence rules would cause expressions to parse differently. I don't remember the exact circumstances that would yield a warning, but I think it would squawk (if the option was turned on) if reversing the precedence of the shift and bitwise operators would change the code. Sometimes appeasing the compiler required adding ugly parenthesis in cases where the 'natural' reading would have been the correct one, but generally the warning was very helpful. Nowadays it seems Microsoft's compilers like to squawk at silly things while ignoring more serious ones. Coercing from double to single in vb.net without a cast? Error. Trying to 'and' a 64-bit flag with 'not &h80000000' or 'not someUnsignedIntVar'? No squawk, but clears the upper 32 bits of the flag.
Bit-twiddling in VB just seems like you're asking for trouble.
Software Zen:
delete this;
Fold With Us![^] -
Bit-twiddling in VB just seems like you're asking for trouble.
Software Zen:
delete this;
Fold With Us![^]Gary R. Wheeler wrote:
Bit-twiddling in VB just seems like you're asking for trouble.
The same problem exists in C. If I were writing a language spec, I would specify that the 'not' operator shall always behave as though it returned the longest existing integer type, and that signed/unsigned comparisons shall always behave in numerically-correct fashion (i.e. if the signed number is negative, it's less than the signed one; otherwise the values compare numerically). Actually, I'd specify that any integer expression all of whose whose intermediate subexpressions fit in the largest integer type must be evaluated as though all calculations were done in that largest type. In many cases, a halfway-intelligent compiler should be able to figure out what size operands are actually necessary, and I can't think of any case where such behavior would break decently-written code. Do you know of any languages that work that way? BTW, a couple more things: (1) a decent compiler should be able to recognize the cases where casting to a long doesn't really mean casting to a long, such as "longvar = int1 * (long)int2;" or "longvar &= ~2;". In the former case, the hardware should use one int*int->long multiply rather than sign extending the two integers, performing four uint*uint->ulong multiplies, and adding up the partial products; a decent compiler should also recognize "longvar &= ~(long)smallpositivevalue;" and not bother doing anything with the upper bits of longvar; (2) I wonder why more languages and CPUs don't have an "and not" operator/instruction. I think Vax Basic included such an operator, and the ARM instruction set does, but I've not seen them often. One more thing: I think it's cool that the formula for computing sum(i=0..inf)(2^i) yields -1. So computers and "real math" agree.