How does an uint32_t pointer work in this code? - c++

I'm really confused by how uint32_t pointers work in C++
I was just fiddling around trying to learn TEA, and I didn't understand when they passed a uint32_t parameter to the encrypt function, and then in the function declared a uint32_t variable and assigning the parameter to it as if the parameter is an array.
Like this:
void encrypt (uint32_t* v, uint32_t* k) {
uint32_t v0=v[0], v1=v[1], sum=0, i;
So I decided to play around with uint32_t pointers, and wrote this short code:
int main ()
{
uint32_t *plain_text;
uint32_t key;
unsigned int temp = 123232;
plain_text = &temp;
key = 7744;
cout << plain_text[1] << endl;
return 0;
}
And it blew my mind when the output was the value of "key". I have no idea how it works... and then when I tried with plain_text[0], it came back with the value of "temp".
So I'm stuck as hell trying to understand what's happening.
Looking back at the TEA code, is the uint32_t* v pointing to an array rather than a single unsigned int? And was what I did just a fluke?

uint32_t is a type. It means unsigned 32-bit integer. On your system it is probably a typedef name for unsigned int.
There's nothing special about a pointer to this particular type; you can have pointers to any type.
The [] in C and C++ are actually pointer indexing notation. p[0] means to retrieve the value at the location the pointer points to. p[1] gets the value at the next memory location after that. Then p[2] is the next location after that, and so on.
You can use this notation with arrays too because the name of an array is converted to a pointer to its first element when used like this.
So, your code plain_text[1] tries to read the next element after temp. Since temp is not actually an array, this causes undefined behaviour. In your particular case, the manifestation of this undefined behaviour is that it managed to read the memory address after temp without crashing, and that address was the same address where key is stored.

Formally your program has undefined behavior.
The expression plain_text[1] is equivalent to *(plain_text + 1) ([expr.sub] / 1). Although you can point to one past the end of an array (objects that aren't arrays are still considered single-element arrays for the purposes of pointer arithmetic ([expr.unary.op] / 3)), you cannot dereference this address ([expr.unary.op] / 1).
At this point the compiler can do whatever it wants to, in this case it has simply decided to treat the expression as if it were pointing to an array and that plain_text + 1, i.e. &temp + 1 points to the next uint32_t object in the stack, which in this case by coincidence is key.
You can see what's going on if you look at the assembly
mov DWORD PTR -16[rbp], 123232 ; unsigned int temp=123232;
lea rax, -16[rbp]
mov QWORD PTR -8[rbp], rax ; plain_text=&temp;
mov DWORD PTR -12[rbp], 7744 ; key=7744;
mov rax, QWORD PTR -8[rbp]
add rax, 4 ; plain_text[1], i.e. -16[rbp] + 4 == -12[rbp] == key
mov eax, DWORD PTR [rax]
mov edx, eax
mov rcx, QWORD PTR .refptr._ZSt4cout[rip]
call _ZNSolsEj ; std::ostream::operator<<(unsigned int)
mov rdx, QWORD PTR .refptr._ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_[rip]
mov rcx, rax
call _ZNSolsEPFRSoS_E ; std::ostream::operator<<(std::ostream& (*)(std::ostream&))
mov eax, 0
add rsp, 48
pop rbp
ret

In C and C++ arrays decay to pointers, resulting in array/pointer equivalence.
a[1]
when a is a simple type is equivalent to
*(a + 1)
If a is an array of simple types, a will decay at the earliest opportunity to the address of element 0.
int arr[5] = { 0, 1, 2, 3, 4 };
int i = 10;
int* ptr;
ptr = arr;
std::cout << *ptr << "\n"; // outputs 0
ptr = &arr[0]; // same address
std::cout << *ptr << "\n"; // outputs 0
std::cout << ptr[4] << "\n"; // outputs 4
std::cout << *(ptr + 4) << "\n"; // outputs 4
ptr = &i;
std::cout << *ptr << "\n"; // outputs 10
std::cout << ptr[0] << "\n";
std::cout << ptr[1] << "\n"; // UNDEFINED BEHAVIOR.
std::cout << *(ptr + 1) << "\n"; // UNDEFINED BEHAVIOR.
To understand ptr[0] and ptr[1] you simply have to understand pointer arithmetic.

uint32_t *plain_text; // In memory, four bytes are reserved for ***plain_text***
uint32_t key; // In memory, the next four bytes after ***plain_text*** are reserved for ***key***
Thus: &plain_text[0] is plain_text and &plain_text[1] refers to the the next four bytes which are at &key.
This scenario may explain that behaviour.

Related

reverse_iterator weird behavior with 2D arrays

I have a 2D array. It's perfectly okay to iterate the rows in forward order, but when I do it in reverse, it doesn't work. I cannot figure out why.
I'm using MSVC v143 and the C++20 standard.
int arr[3][4];
for (int counter = 0, i = 0; i != 3; ++i) {
for (int j = 0; j != 4; ++j) {
arr[i][j] = counter++;
}
}
std::for_each(std::begin(arr), std::end(arr), [](auto const& row) {
for (auto const& i: row) {
fmt::print("{} ", i);
}
fmt::print("\n");
});
std::for_each(std::rbegin(arr), std::rend(arr), [](auto const& row) {
for (auto const& i: row) {
fmt::print("{} ", i);
}
fmt::print("\n");
});
The output for the first for_each is fine:
0 1 2 3
4 5 6 7
8 9 10 11
Yet the second one is garbage:
-424412040 251 -858993460 -858993460
-424412056 251 -858993460 -858993460
-424412072 251 -858993460 -858993460
When I print their addresses up I couldn't understand it:
<Row addr=0xfbe6b3fc58/>
0 1 2 3
<Row addr=0xfbe6b3fc68/>
4 5 6 7
<Row addr=0xfbe6b3fc78/>
8 9 10 11
<Row addr=0xfbe6b3fb98/>
-424412040 251 -858993460 -858993460
<Row addr=0xfbe6b3fb98/>
-424412056 251 -858993460 -858993460
<Row addr=0xfbe6b3fb98/>
-424412072 251 -858993460 -858993460
What is happening here?
This is very likely a code generation bug of MSVC related to pointers to multidimensional arrays: The std::reverse_iterator::operator*() hidden in the range-based loop is essentially doing a *--p, where p is a pointer type to an int[4] pointing to the end of the array. Decrementing and dereferencing in a single statement causes MSVC to load the address of the local variable p instead of the address of the previous element pointed to by the decremented p, essentially resulting in the address of the local variable p being returned.
You can observe the problem better in the following standalone example (https://godbolt.org/z/x9q5M74Md):
#include <iostream>
using Int4 = int[4]; // To avoid the awkward pointer-to-array syntax
int arr[3][4] = {};
Int4 & test1()
{
Int4 * p = arr;
Int4 * pP1 = p + 1;
// Works correctly
--pP1;
Int4 & deref = *pP1;
return deref;
}
Int4 & test2()
{
Int4 * p = arr;
Int4 * pP1 = p + 1;
// msvc incorrectly stores the address of the local variable pP1 (i.e. &pP1) in deref
Int4 & deref = *--pP1;
return deref;
}
int main()
{
std::cout << "arr = 0x" << &arr[0][0] << std::endl;
std::cout << "test1 = 0x" << &test1() << std::endl; // Works
std::cout << "test2 = 0x" << &test2() << std::endl; // Bad
}
In this example, &test1() correctly prints the address of the first element of arr. But &test2() actually prints the address of the local variable test2::pP1, i.e. it prints &test2::pP1. MSVC even warns that test2() returns the address of the local variable pP1 (C4172).
clang and gcc work fine. Versions before MSVC v19.23 also compile the code correctly.
Looking at the assembly output, clang and gcc emit the same code for test1() and test2(). But MSVC is doing:
; test1()
mov rax, QWORD PTR pP1$[rsp]
mov QWORD PTR deref$[rsp], rax
; test2()
lea rax, QWORD PTR pP1$[rsp]
mov QWORD PTR deref$[rsp], rax
Notice the lea instead of the mov statement, meaning that test2() loads the address of pP1.
MSVC seems to get confused somehow by pointers to multidimensional array.

Why does shifting a value further than its size not result in 0?

Actual refined question:
Why does this not print 0?
#include "stdafx.h"
#include <iostream>
#include <string>
int _tmain(int argc, _TCHAR* argv[])
{
unsigned char barray[] = {1,2,3,4,5,6,7,8,9};
unsigned long weirdValue = barray[3] << 32;
std::cout << weirdValue; // prints 4
std::string bla;
std::getline(std::cin, bla);
return 0;
}
The disassembly of the shift operation:
10: unsigned long weirdValue = barray[3] << 32;
00411424 movzx eax,byte ptr [ebp-1Dh]
00411428 shl eax,20h
0041142B mov dword ptr [ebp-2Ch],eax
Original question:
I found the following snippet in some old code we maintain. It converts a byte array to multiple float values and adds the floats to a list. Why does it work for byte arrays greater than 4?
unsigned long ulValue = 0;
for (USHORT usIndex = 0; usIndex < m_oData.usNumberOfBytes; usIndex++)
{
if (usIndex > 0 && (usIndex % 4) == 0)
{
float* pfValue = (float*)&ulValue;
oValues.push_back(*pfValue);
ulValue = 0;
}
ulValue += (m_oData.pabyDataBytes[usIndex] << (8*usIndex)); // Why does this work for usIndex > 3??
}
I would understand that this works if << was a rotate operator, not a shift operator. Or if it was
ulValue += (m_oData.pabyDataBytes[usIndex] << (8*(usIndex%4)))
But the code like i found it just confuses me.
The code is compiled using VS 2005.
If i try the original snippet in the immediate window, it doesn't work though.
I know how to do this properly, i just want to know why the code and especially the shift operation works as it is.
Edit: The disassembly for the shift operation is:
13D61D0A shl ecx,3 // multiply uIndex by 8
13D61D0D shl eax,cl // shift to left, does nothing for multiples of 32
13D61D0F add eax,dword ptr [ulValue]
13D61D15 mov dword ptr [ulValue],eax
So the disassembly is fine.
The shift count is masked to 5 bits, which limits the range to 0-31.
A shift of 32 therefore is same as a shift of zero.
http://x86.renejeschke.de/html/file_module_x86_id_285.html

Why can I mov into eax, into ax, but not into al, or even maybe into ah?

If I mov, eax 12345 and later mov var, eax (assuming var is a 32bit int etc..etc..) and output var later it'll output correctly.
Same with ax. mov ax, 65535 -> mov var16, ax will output correctly.
But if I mov into al or even ah it won't output anything.
Perhaps moving into ah not working makes sense. But why is that so for both?
typedef int int32;
typedef unsigned char int8;
typedef short int16;
int _tmain(int argc, _TCHAR* argv[])
{
int8 c1 = 0;
int8 c2 = 0;
__asm
{
mov al, 255
mov c1, al
}
cout << c1 << endl; //outputs nothing, same if I used ah
//whereas
int32 n = 0;
__asm
{
mov eax, 10000
mov n, eax
}
cout << n << endl; // works fine, well, you get the picture
return 0;
}
The problem is not (only) with your assembly code, it's the way you print your value:
cout << c1 << endl;
Even though c1 is unsigned char (aka uint8), C++ iostreams treat it the same way they treat an ordinary char. In other words, that code is roughly equivalent to:
printf("%c\n", c1);
Just cast it to unsigned int and it should work fine.
BTW, on some platforms (e.g. Linux PowerPC) the ordinary char is actually unsigned.
256 equals to 2^8
when you use int8 , it uses twos complement approach , so the number is between [-128 , 127]
I think if you change its type to unsigned integer , it must work

How to create a loop in ASM?

I am really struggling to get my head around this task which I've been given and I'm going out of my mind right now.
I have been given an example of the following loop:
#include <iostream> // for cout
using namespace std;
//declare an array of 'marks'.
const int array_size = 5;
int marks [] = {45,56,99,19,21};
int main()
{int index, average, total;
total = 0;
for ( index = 0; index < array_size; index++)
{
total = total + marks [index];
}
average = total/array_size;
cout << "\nAverage value = " << average << "\n\n";
return(0);
}
in assembly:
#include <iostream> // for cout
using namespace std;
const int array_size = 5;
int marks [] = {45,56,99,19,21}; //declare an array of 'marks'.
int main()
{int index, average, total;
__asm {
mov total,0 ;total = 0;
; ************************** This part is the FOR loop *************************
mov index,0 ;for ( index = 0; index < array_size; index++)
jmp checkend
forloop1:
mov eax,index ;add 1 to index
add eax,1
mov index,eax
checkend:
cmp index,5 ;check if 5 ('array_size') loops have been done
jge endfor1 ;jump if greater than or equal to (remember?)
mov ecx,index
mov edx,total ; total =
add edx,[ecx*4+marks] ; total + marks [index];
mov total,edx
jmp forloop1
; ******************************************************************************
endfor1:
mov eax,total ;average = total/array_size;
cdq ;convert EAX to quadword (uses EDX register)
mov ecx,5 ;get array_size as divisor
idiv ecx ;divides EDX:EAX pair by ECX, EAX=answer
mov average,eax ;save answer in variable 'average'
} //end of assembly section
cout << "\nAverage value = " << average << "\n\n";
return(0);
}
However, my problem is this: I need to convert the following loop into assembly, what is the best method of doing this based on the example I've been given? I'm really struggling with assigning the temp_char variable to an array variable.
By following loop I mean the encrypt_chars function loop
#define MAXCHARS 6
#define dollarchar '$' // string terminator
char OChars[MAXCHARS],
EChars[MAXCHARS],
DChars[MAXCHARS] = "Soon!"; // Global Original, Encrypted, Decrypted character strings
//----------------------------- C++ Functions ----------------------------------------------------------
void get_char(char& a_character)
{
cin >> a_character;
while (((a_character < '0') | (a_character > 'z')) && (a_character != dollarchar))
{
cout << "Alphanumeric characters only, please try again > ";
cin >> a_character;
}
}
//-------------------------------------------------------------------------------------------------------------
void get_original_chars(int& length)
{
char next_char;
length = 0;
get_char(next_char);
while ((length < MAXCHARS) && (next_char != dollarchar))
{
OChars[length++] = next_char;
get_char(next_char);
}
}
void encrypt_chars(int length, char EKey)
{
char temp_char; // char temporary store
for (int i = 0; i < length; i++) // encrypt characters one at a time
{
temp_char = OChars[i]; // temp_char now contains the address values of the individual character
__asm
{
push eax // Save values contained within register to stack
push ecx
movzx ecx, temp_char
push ecx // Push argument #2
lea eax, EKey
push eax // Push argument #1
call encrypt4
add esp, 8 // Clean parameters of stack
mov temp_char, al // Move the temp character into a register
pop ecx
pop eax
}
EChars[i] = temp_char; // Store encrypted char in the encrypted chars array
}
return;
// Inputs: register EAX = 32-bit address of Ekey,
// ECX = the character to be encrypted (in the low 8-bit field, CL).
// Output: register EAX = the encrypted value of the source character (in the low 8-bit field, AL).
__asm
{
encrypt4:
push ebp // Set stack
mov ebp, esp // Set up the base pointer
mov eax, [ebp + 8] // Move value of parameter 1 into EAX
mov ecx, [ebp + 12] // Move value of parameter 2 into ECX
push edi // Used for string and memory array copying
push ecx // Loop counter for pushing character onto stack
not byte ptr[eax] // Negation
add byte ptr[eax], 0x04 // Adds hex 4 to EKey
movzx edi, byte ptr[eax] // Moves value of EKey into EDI using zeroes
pop eax // Pop the character value from stack
xor eax, edi // XOR character to give encrypted value of source
pop edi // Pop original address of EDI from the stack
rol al, 1 // Rotates the encrypted value of source by 1 bit (left)
rol al, 1 // Rotates the encrypted value of source by 1 bit (left) again
add al, 0x04 // Adds hex 4 to encrypted value of source
mov esp, ebp // Deallocate values
pop ebp // Restore the base pointer
ret
}
//--- End of Assembly code
}
int main(void)
{
int char_count; // The number of actual characters entered (upto MAXCHARS limit).
char EKey; // Encryption key.
cout << "\nPlease enter your Encryption Key (EKey) letter: "; get_char(EKey);
cout << "\nNow enter upto " << MAXCHARS << " alphanumeric characters:\n";
get_original_chars(char_count);
cout << "\n\nOriginal source string = " << OChars << "\tHex = ";
for (int i = 0; i<char_count; i++) cout << hex << setw(2) << setfill('0') << ((int(OChars[i])) & 0xFF) << " ";
encrypt_chars(char_count, EKey);
cout << "\n\nEncrypted string = " << EChars << "\tHex = ";
for (int i = 0; i<char_count; i++) cout << ((int(EChars[i])) & 0xFF) << " ";
decrypt_chars(char_count, EKey);
cout << "\n\nDecrypted string = " << DChars << "\tHex = ";
for (int i = 0; i<char_count; i++) cout << ((int(DChars[i])) & 0xFF) << " ";
cout << "\n\nPress a key to end...";
while (!_kbhit()); //hold the screen until a key is pressed
return (0);
}
offering a cookie to the best explained (step-by-step) answer/ solution!!
EDIT: Disassembly code generated from the loop etc:
http://pastebin.com/u5MgJ2SW
notice line 32 where it says mov cl,byte ptr [eax+0CC0320h] Unless you
can tell me how to convert that into 'normal asm code'
mov cl,byte ptr [eax+0CC0320h]: eax at this point contains the value of i, 0CC0320h is the address of OChars, i.e. its begining. So, the "normal" asm code is as follows:
__asm {
mov eax, i
mov cl, byte ptr OChars[eax]
mov temp_char, cl
}

Passing arguments from c++ to assembly in cout

I'm writing simple mix of c++ and nasm assembly atm and dont understand why the results are different inside and outside of the "cout". Maybe this is some kind of exception however I would like to know the difference.Thanks for any help.
C++ PART
#include <iostream>
#include <cstring>
using namespace std;
extern "C" unsigned int quot (unsigned int, unsigned int);
extern "C" unsigned int remainder (unsigned int, unsigned int);
int main()
{
unsigned int i=0, j=0, k=0;
cout << "Numbers 'x y'" << endl;
cin >> i >> j;
k = quot(i,j);
cout<< "Result: " <<k;
k = remainder(i,j);
cout <<" r. "<< k <<endl;
cout << "Result: "<<quot(i,j)<<" r. "<<remainder(i,j)<<endl;
return 0;
}
NASM
quot and reminder functions are almost the same. the only difference is commented in the code
section .data
section .text
global quot
quot:
; intro
push ebp
mov ebp,esp
xor edx, edx
mov eax, [ebp+8]
mov ebx,[ebp+12]
div ebx
; DIFFERENCE: in remainder we have additionaly
; mov eax, edx
mov esp,ebp
pop ebp
ret
RESULTS
For 12 5 input we expect Result: 2 r. 2 but we obtain.
Result: 2 r. 2
Result: 2 r. 5
You have to preserve value of ebx in your asm functions (see http://en.wikipedia.org/wiki/X86_calling_conventions#cdecl). Violating the calling convention may result in various range of errors, from subtle to crashes.
Use ecx instead of ebx, or try div dword ptr [ebp+12].