I wanted to see what was happening behind the scenes when an unsigned long long was assigned the value of an unsigned int. I made a simple C++ program to try it out and moved all the io out of main():
#include <iostream>
#include <stdlib.h>
void usage() {
std::cout << "Usage: ./u_to_ull <unsigned int>\n";
exit(0);
}
void atoiWarning(int foo) {
std::cout << "WARNING: atoi() returned " << foo << " and (unsigned int)foo is " <<
((unsigned int)foo) << "\n";
}
void result(unsigned long long baz) {
std::cout << "Result as unsigned long long is " << baz << "\n";
}
int main(int argc, char** argv) {
if (argc != 2) usage();
int foo = atoi(argv[1]);
if (foo < 0) atoiWarning(foo);
// Signed to unsigned
unsigned int bar = foo;
// Conversion
unsigned long long baz = -1;
baz = bar;
result(baz);
return 0;
}
The resulting assembly produced this for main:
0000000000400950 <main>:
400950: 55 push %rbp
400951: 48 89 e5 mov %rsp,%rbp
400954: 48 83 ec 20 sub $0x20,%rsp
400958: 89 7d ec mov %edi,-0x14(%rbp)
40095b: 48 89 75 e0 mov %rsi,-0x20(%rbp)
40095f: 83 7d ec 02 cmpl $0x2,-0x14(%rbp)
400963: 74 05 je 40096a <main+0x1a>
400965: e8 3a ff ff ff callq 4008a4 <_Z5usagev>
40096a: 48 8b 45 e0 mov -0x20(%rbp),%rax
40096e: 48 83 c0 08 add $0x8,%rax
400972: 48 8b 00 mov (%rax),%rax
400975: 48 89 c7 mov %rax,%rdi
400978: e8 0b fe ff ff callq 400788 <atoi#plt>
40097d: 89 45 f0 mov %eax,-0x10(%rbp)
400980: 83 7d f0 00 cmpl $0x0,-0x10(%rbp)
400984: 79 0a jns 400990 <main+0x40>
400986: 8b 45 f0 mov -0x10(%rbp),%eax
400989: 89 c7 mov %eax,%edi
40098b: e8 31 ff ff ff callq 4008c1 <_Z11atoiWarningi>
400990: 8b 45 f0 mov -0x10(%rbp),%eax
400993: 89 45 f4 mov %eax,-0xc(%rbp)
400996: 48 c7 45 f8 ff ff ff movq $0xffffffffffffffff,-0x8(%rbp)
40099d: ff
40099e: 8b 45 f4 mov -0xc(%rbp),%eax
4009a1: 48 89 45 f8 mov %rax,-0x8(%rbp)
4009a5: 48 8b 45 f8 mov -0x8(%rbp),%rax
4009a9: 48 89 c7 mov %rax,%rdi
4009ac: e8 66 ff ff ff callq 400917 <_Z6resulty>
4009b1: b8 00 00 00 00 mov $0x0,%eax
4009b6: c9 leaveq
4009b7: c3 retq
The -1 from the C++ makes it clear that -0x8(%rbp) corresponds to baz (due to $0xffffffffffffffff). -0x8(%rbp) is written to by %rax, but the top four bytes of %rax appear to not have been assigned, %eaxwas assigned
Does this suggest that the top 4 bytes of -0x8(%rbp) are undefined?
In the IntelĀ® 64 and IA-32 Architectures Software Developer Manuals, volume 1, chapter 3.4.1.1 (General-Purpose Registers in 64-Bit Mode), it says
32-bit operands generate a 32-bit result, zero-extended to a 64-bit result in the destination general-purpose register.
So after mov -0xc(%rbp),%eax, the upper half of rax is defined, and it's zero.
This also applies to the 87 C0 encoding of xchg eax, eax, but not to its 90 encoding (which is defined as nop, overruling the rule quoted above).
From C++98 (and C++11 seems to be unchanged) 4.7/2 (integral conversions - no promotions are relevant) we learn:
If the destination type is unsigned, the resulting value is the least
unsigned integer congruent to the source integer (modulo 2n where n is
the number of bits used to represent the unsigned type).
This clearly shows that as long as the source and destination are unsigned and the destination is at least as large as the source, the value will be unchanged. If the compiler generated code that failed to make the larger value equal, the compiler is buggy.
Related
This question already has answers here:
What are callee and caller saved registers?
(6 answers)
Calling convention on x64 [duplicate]
(1 answer)
Closed 1 year ago.
When calling a C++ function, the RAX AND RCX registers are changed.
How can I force the compiler to store the value of the registers?
I could call push pop, but I plan to call more complex functions and it is important for me that the registers are not corrupted (even for floating point numbers).
Architecture: amd64
IDE: Visual Studio 19
.data
extern TestCall: proto
.code
HookFunc proc
mov rax, [rbp + 8h]
call TestCall
ret
HookFunc endp
end
extern "C" void TestCall()
{
cout << "TestCall" << endl;
}
Disassembler
extern "C" __declspec(dllexport) void TestCall()
{
00007FFAA9C76FB0 40 55 push rbp
00007FFAA9C76FB2 57 push rdi
00007FFAA9C76FB3 48 81 EC E8 00 00 00 sub rsp,0E8h
00007FFAA9C76FBA 48 8D 6C 24 20 lea rbp,[rsp+20h]
00007FFAA9C76FBF 48 8D 0D 70 B0 01 00 lea rcx,[__ED185583_dllmain#cpp (07FFAA9C92036h)]
00007FFAA9C76FC6 E8 DD A7 FF FF call __CheckForDebuggerJustMyCode (07FFAA9C717A8h)
cout << "TestCall" << endl;
00007FFAA9C76FCB 48 8D 15 EE EF 00 00 lea rdx,[string "TestCall" (07FFAA9C85FC0h)]
00007FFAA9C76FD2 48 8B 0D 7F 82 01 00 mov rcx,qword ptr [__imp_std::cout (07FFAA9C8F258h)]
00007FFAA9C76FD9 E8 FE A0 FF FF call std::operator<<<std::char_traits<char> > (07FFAA9C710DCh)
00007FFAA9C76FDE 48 8D 15 66 A0 FF FF lea rdx,[std::endl<char,std::char_traits<char> > (07FFAA9C7104Bh)]
00007FFAA9C76FE5 48 8B C8 mov rcx,rax
00007FFAA9C76FE8 FF 15 92 82 01 00 call qword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (07FFAA9C8F280h)]
}
00007FFAA9C76FEE 48 8D A5 C8 00 00 00 lea rsp,[rbp+0C8h]
00007FFAA9C76FF5 5F pop rdi
00007FFAA9C76FF6 5D pop rbp
00007FFAA9C76FF7 C3 ret
Attempting to compile with finstrument-functions and exclude a template function with multiple template parameters, using the \ method to escape commas (as described for exclude-file-list here) fails to properly disable instrumenting the function passed.
GCC command used:
gcc -finstrument-functions -finstrument-functions-exclude-function-list='test<float\, int>' main.cpp -o a.out -O0
Above creates a binary file with the "test" function instrumented. Assembly snippet and main.cpp file included below
gcc -dumpversion returns "6.2.0", above command run on red hat enterprise linux, version 7.4
Contents of main.cpp:
template<class T, class U>
T test(int a, T b){
int res = 0;
for(int i = 0; i < 1000; i++){
res += i;
}
return(res);
}
int main(int argc, char** argv){
float a = test<float, int>(argc, 1.0);
return(0);
}
objdumped output for "test" function:
000000000040059f <float test<float, int>(int, float)>:
40059f: 55 push %rbp
4005a0: 48 89 e5 mov %rsp,%rbp
4005a3: 48 83 ec 20 sub $0x20,%rsp
4005a7: 89 7d ec mov %edi,-0x14(%rbp)
4005aa: f3 0f 11 45 e8 movss %xmm0,-0x18(%rbp)
4005af: 48 8b 45 08 mov 0x8(%rbp),%rax
4005b3: 48 89 c6 mov %rax,%rsi
4005b6: bf 9f 05 40 00 mov $0x40059f,%edi
4005bb: e8 70 fe ff ff callq 400430 <__cyg_profile_func_enter#plt>
4005c0: c7 45 f8 00 00 00 00 movl $0x0,-0x8(%rbp)
4005c7: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%rbp)
4005ce: 81 7d fc e7 03 00 00 cmpl $0x3e7,-0x4(%rbp)
4005d5: 7f 11 jg 4005e8 <float test<float, int>(int, float)+0x49>
4005d7: 8b 55 f8 mov -0x8(%rbp),%edx
4005da: 8b 45 fc mov -0x4(%rbp),%eax
4005dd: 01 d0 add %edx,%eax
4005df: 89 45 f8 mov %eax,-0x8(%rbp)
4005e2: 83 45 fc 01 addl $0x1,-0x4(%rbp)
4005e6: eb e6 jmp 4005ce <float test<float, int>(int, float)+0x2f>
4005e8: 8b 45 f8 mov -0x8(%rbp),%eax
4005eb: 66 0f ef c9 pxor %xmm1,%xmm1
4005ef: f3 0f 2a c8 cvtsi2ss %eax,%xmm1
4005f3: f3 0f 11 4d e4 movss %xmm1,-0x1c(%rbp)
4005f8: 48 8b 45 08 mov 0x8(%rbp),%rax
4005fc: 48 89 c6 mov %rax,%rsi
4005ff: bf 9f 05 40 00 mov $0x40059f,%edi
400604: e8 17 fe ff ff callq 400420 <__cyg_profile_func_exit#plt>
400609: f3 0f 10 45 e4 movss -0x1c(%rbp),%xmm0
40060e: c9 leaveq
40060f: c3 retq
I expected the test function to not be instrumented, but it is. Does anyone know why this is?
Compiler explorer example
Just in case anyone comes by this way, both this and finstrument-functions-exclude-function-list not respecting namespace parts of a function are bugs, and I have filed against both. Hopefully a fix will be implemented soon (working on one currently).
Namespace / class mishandling
Comma mishandling
I have this following code which i can not understand:
#include <cstdio>
#include <iostream>
using namespace std;
class A
{
public:
int t = 0;
A(){
cout << "constructed" << t<< endl;
}
A (A&& a) {
cout << "in move ctor, moving"<< a.t << endl;
}
~A() {
cout << "deleting"<< t << endl;
}
};
A f1 (A a)
{
a.t = 1;
std::cout << "f1: " << endl;
return a;
}
int main() {
A a = f1(A()) ;
printf("what is happening\n");
}
and the output is
constructed0
in move ctor, moving0
f1:
in move ctor, moving1
in move ctor, moving0
deleting0
deleting1
deleting0
what is happening
deleting0
The thing that I can not understand is the phase where the temporary object created for f1 (the one with a.t=1) is being destroyed.
From the output I assume it is being destroyed at the and of the line A a = f1(A()) ; While I thought it was created inside f1 and for f1, and therefore will be destroyed when exiting the function, before deleting0 is being called.
What am I missing?
So after a bit research I have the Answer.
Here is the disassembly of the code (changed the move constructor to a copy constructor for readability):
int A::counter = 0;
A f1 (A a)
{
400a18: 55 push %rbp
400a19: 48 89 e5 mov %rsp,%rbp
400a1c: 48 83 ec 10 sub $0x10,%rsp
400a20: 48 89 7d f8 mov %rdi,-0x8(%rbp)
400a24: 48 89 75 f0 mov %rsi,-0x10(%rbp)
cout << __LINE__ << endl;
400a28: be 1d 00 00 00 mov $0x1d,%esi
400a2d: bf 80 13 60 00 mov $0x601380,%edi
400a32: e8 c1 fd ff ff callq 4007f8 <_ZNSolsEi#plt>
400a37: be 78 08 40 00 mov $0x400878,%esi
400a3c: 48 89 c7 mov %rax,%rdi
400a3f: e8 24 fe ff ff callq 400868 <_ZNSolsEPFRSoS_E#plt>
a.t = 1;
400a44: 48 8b 45 f0 mov -0x10(%rbp),%rax
400a48: c7 00 01 00 00 00 movl $0x1,(%rax)
std::cout << "f1: " << endl;
400a4e: be ce 0e 40 00 mov $0x400ece,%esi
400a53: bf 80 13 60 00 mov $0x601380,%edi
400a58: e8 fb fd ff ff callq 400858 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc#plt>
400a5d: be 78 08 40 00 mov $0x400878,%esi
400a62: 48 89 c7 mov %rax,%rdi
400a65: e8 fe fd ff ff callq 400868 <_ZNSolsEPFRSoS_E#plt>
cout << __LINE__ << endl;
400a6a: be 20 00 00 00 mov $0x20,%esi
400a6f: bf 80 13 60 00 mov $0x601380,%edi
400a74: e8 7f fd ff ff callq 4007f8 <_ZNSolsEi#plt>
400a79: be 78 08 40 00 mov $0x400878,%esi
400a7e: 48 89 c7 mov %rax,%rdi
400a81: e8 e2 fd ff ff callq 400868 <_ZNSolsEPFRSoS_E#plt>
return a;
400a86: 48 8b 55 f0 mov -0x10(%rbp),%rdx
400a8a: 48 8b 45 f8 mov -0x8(%rbp),%rax
400a8e: 48 89 d6 mov %rdx,%rsi
400a91: 48 89 c7 mov %rax,%rdi
400a94: e8 dd 01 00 00 callq 400c76 <_ZN1AC1ERKS_>
}
400a99: 48 8b 45 f8 mov -0x8(%rbp),%rax
400a9d: c9 leaveq
400a9e: c3 retq
0000000000400a9f <main>:
int main() {
400a9f: 55 push %rbp
400aa0: 48 89 e5 mov %rsp,%rbp
400aa3: 53 push %rbx
400aa4: 48 83 ec 48 sub $0x48,%rsp
A a = f1(A()) ;
400aa8: 48 8d 45 e0 lea -0x20(%rbp),%rax
400aac: 48 89 c7 mov %rax,%rdi
400aaf: e8 2a 01 00 00 callq 400bde <_ZN1AC1Ev>
400ab4: 48 8d 55 e0 lea -0x20(%rbp),%rdx
400ab8: 48 8d 45 d0 lea -0x30(%rbp),%rax
400abc: 48 89 d6 mov %rdx,%rsi
400abf: 48 89 c7 mov %rax,%rdi
400ac2: e8 af 01 00 00 callq 400c76 <_ZN1AC1ERKS_>
400ac7: 48 8d 45 c0 lea -0x40(%rbp),%rax
400acb: 48 8d 55 d0 lea -0x30(%rbp),%rdx
400acf: 48 89 d6 mov %rdx,%rsi
400ad2: 48 89 c7 mov %rax,%rdi
400ad5: e8 3e ff ff ff callq 400a18 <_Z2f11A>
400ada: 48 8d 55 c0 lea -0x40(%rbp),%rdx
400ade: 48 8d 45 b0 lea -0x50(%rbp),%rax
400ae2: 48 89 d6 mov %rdx,%rsi
400ae5: 48 89 c7 mov %rax,%rdi
400ae8: e8 89 01 00 00 callq 400c76 <_ZN1AC1ERKS_>
400aed: 48 8d 45 c0 lea -0x40(%rbp),%rax
400af1: 48 89 c7 mov %rax,%rdi
400af4: e8 31 02 00 00 callq 400d2a <_ZN1AD1Ev>
400af9: 48 8d 45 d0 lea -0x30(%rbp),%rax
400afd: 48 89 c7 mov %rax,%rdi
400b00: e8 25 02 00 00 callq 400d2a <_ZN1AD1Ev>
400b05: 48 8d 45 e0 lea -0x20(%rbp),%rax
400b09: 48 89 c7 mov %rax,%rdi
400b0c: e8 19 02 00 00 callq 400d2a <_ZN1AD1Ev>
printf("what is happening\n");
400b11: bf d3 0e 40 00 mov $0x400ed3,%edi
400b16: e8 ed fc ff ff callq 400808 <puts#plt>
cout << __LINE__ << endl;
return a;
}
The Copy constructor is called "ZN1AC1ERKS" after mangling process.
As we can see, the temporary object that is being created for f1, is being created before the function call, in main, and not as i expected, in f1's scope.
The meaning is as follows:
Temporary objects being created for functions that are called by value are not created in the functions scope, but rather on the line called the function, thus they will be destroyed before the next line execution, in the ordinary first created last destroyed way.
In a large framework which used to use many smart pointers and now uses raw pointers, I come across situations like this quite often:
class A {
public:
int* m;
A() : m() {}
};
The reason is because int* m used to be a smart pointer and so the initializer list called a default constructor. Now that int* m is a raw pointer I am not certain if this is equivalent to:
class A {
public:
int* m;
A() : m(nullptr) {}
};
Without the explicit nullptr is A::m still initialized to zero? A look at no optimization objdump -d makes it appear to be yes but I am not certain. The reason I feel that the answer is yes is due to this line in the objdump -d (I posted more of the objdump -d below):
400644: 48 c7 00 00 00 00 00 movq $0x0,(%rax)
Little program that tries to find undefined behavior:
class A {
public:
int* m;
A() : m(nullptr) {}
};
int main() {
A buf[1000000];
unsigned int count = 0;
for (unsigned int i = 0; i < 1000000; ++i) {
count += buf[i].m ? 1 : 0;
}
return count;
}
Compilation, execution, and return value:
g++ -std=c++14 -O0 foo.cpp
./a.out; echo $?
0
Relevant assembly sections from objdump -d:
00000000004005b8 <main>:
4005b8: 55 push %rbp
4005b9: 48 89 e5 mov %rsp,%rbp
4005bc: 41 54 push %r12
4005be: 53 push %rbx
4005bf: 48 81 ec 10 12 7a 00 sub $0x7a1210,%rsp
4005c6: 48 8d 85 e0 ed 85 ff lea -0x7a1220(%rbp),%rax
4005cd: bb 3f 42 0f 00 mov $0xf423f,%ebx
4005d2: 49 89 c4 mov %rax,%r12
4005d5: eb 10 jmp 4005e7 <main+0x2f>
4005d7: 4c 89 e7 mov %r12,%rdi
4005da: e8 59 00 00 00 callq 400638 <_ZN1AC1Ev>
4005df: 49 83 c4 08 add $0x8,%r12
4005e3: 48 83 eb 01 sub $0x1,%rbx
4005e7: 48 83 fb ff cmp $0xffffffffffffffff,%rbx
4005eb: 75 ea jne 4005d7 <main+0x1f>
4005ed: c7 45 ec 00 00 00 00 movl $0x0,-0x14(%rbp)
4005f4: c7 45 e8 00 00 00 00 movl $0x0,-0x18(%rbp)
4005fb: eb 23 jmp 400620 <main+0x68>
4005fd: 8b 45 e8 mov -0x18(%rbp),%eax
400600: 48 8b 84 c5 e0 ed 85 mov -0x7a1220(%rbp,%rax,8),%rax
400607: ff
400608: 48 85 c0 test %rax,%rax
40060b: 74 07 je 400614 <main+0x5c>
40060d: b8 01 00 00 00 mov $0x1,%eax
400612: eb 05 jmp 400619 <main+0x61>
400614: b8 00 00 00 00 mov $0x0,%eax
400619: 01 45 ec add %eax,-0x14(%rbp)
40061c: 83 45 e8 01 addl $0x1,-0x18(%rbp)
400620: 81 7d e8 3f 42 0f 00 cmpl $0xf423f,-0x18(%rbp)
400627: 76 d4 jbe 4005fd <main+0x45>
400629: 8b 45 ec mov -0x14(%rbp),%eax
40062c: 48 81 c4 10 12 7a 00 add $0x7a1210,%rsp
400633: 5b pop %rbx
400634: 41 5c pop %r12
400636: 5d pop %rbp
400637: c3 retq
0000000000400638 <_ZN1AC1Ev>:
400638: 55 push %rbp
400639: 48 89 e5 mov %rsp,%rbp
40063c: 48 89 7d f8 mov %rdi,-0x8(%rbp)
400640: 48 8b 45 f8 mov -0x8(%rbp),%rax
400644: 48 c7 00 00 00 00 00 movq $0x0,(%rax)
40064b: 5d pop %rbp
40064c: c3 retq
40064d: 0f 1f 00 nopl (%rax)
Empty () initializer stands for default-initialization in C++98 and for value-initialization in C++03 and later. For scalar types (including pointers) value-initialization/default-initialization leads to zero-initialization.
Which means that in your case m() and m(nullptr) will have exactly the same effect: in both cases m is initialized as a null pointer. In C++ it was like that since the beginning of standardized times.
I have the following code:
void function(char *str)
{
int i;
char buffer[strlen(str) + 1];
strcpy(buffer, str);
buffer[strlen(str)] = '\0';
printf("Buffer: %s\n", buffer);
}
I would expect this code to throw a compile time error, as the 'buffer' being allocated on the stack has a runtime dependent length (based on strlen()). However in GCC the compilation passes. How does this work? Is the buffer dynamically allocated, or if it is still stack local, what is the size allocated?
C99 allows variable length arrays. Not compiling your code in C99 will not give any error because GCC also allow variable length array as an extension.
6.19 Arrays of Variable Length:
Variable-length automatic arrays are allowed in ISO C99, and as an extension GCC accepts them in C90 mode and in C++.
By disassembling your function you could easily verify this:
$ objdump -S <yourprogram>
...
void function(char *str)
{
4011a0: 55 push %ebp
4011a1: 89 e5 mov %esp,%ebp
4011a3: 53 push %ebx
4011a4: 83 ec 24 sub $0x24,%esp
4011a7: 89 e0 mov %esp,%eax
4011a9: 89 c3 mov %eax,%ebx
int i;
char buffer[strlen(str) + 1];
4011ab: 8b 45 08 mov 0x8(%ebp),%eax
4011ae: 89 04 24 mov %eax,(%esp)
4011b1: e8 42 01 00 00 call 4012f8 <_strlen>
4011b6: 83 c0 01 add $0x1,%eax
4011b9: 89 c2 mov %eax,%edx
4011bb: 83 ea 01 sub $0x1,%edx
4011be: 89 55 f4 mov %edx,-0xc(%ebp)
4011c1: ba 10 00 00 00 mov $0x10,%edx
4011c6: 83 ea 01 sub $0x1,%edx
4011c9: 01 d0 add %edx,%eax
4011cb: b9 10 00 00 00 mov $0x10,%ecx
4011d0: ba 00 00 00 00 mov $0x0,%edx
4011d5: f7 f1 div %ecx
4011d7: 6b c0 10 imul $0x10,%eax,%eax
4011da: e8 6d 00 00 00 call 40124c <___chkstk_ms>
4011df: 29 c4 sub %eax,%esp
4011e1: 8d 44 24 08 lea 0x8(%esp),%eax
4011e5: 83 c0 00 add $0x0,%eax
4011e8: 89 45 f0 mov %eax,-0x10(%ebp)
....
The relevant piece of assembly here is sub %eax,%esp anyway. This shows that the stack was expanded based on whatever strlen returned earlier to get space for your buffer.