investigating visual studio assembly output - c++

While using /FA option for compiling code that uses this dummy class
class A {
public:
A() {}
int Initialize() {
return 0;
}
};
I looked over the generated asm file where this was defined and also used and saw this in the asm file
PUBLIC ?Initialize#A##QEAAHXZ ; A::Initialize
PUBLIC ??0A##QEAA#H#Z ; A::A
??0A##QEAA#H#Z PROC ; A::A, COMDAT
; File d:\dev\temp\consoleapplication1\consoleapplication1\consoleapp2.cpp
; Line 7
mov rax, rcx
ret 0
??0A##QEAA#H#Z ENDP ; A::A
_TEXT ENDS
; Function compile flags: /Ogtpy
; COMDAT ?Initialize#A##QEAAHXZ
_TEXT SEGMENT
this$dead$ = 8
?Initialize#A##QEAAHXZ PROC ; A::Initialize, COMDAT
; File d:\dev\temp\consoleapplication1\consoleapplication1\consoleapp2.cpp
; Line 9
xor eax, eax
; Line 10
ret 0
?Initialize#A##QEAAHXZ ENDP ; A::Initialize
As you can see there is generated "trivial" implementation functions for both constructor and Initialize function.
At first I thought that this non inline implementation was going to be used where class A is used but debugging showed that this was not the case (code seemed to be inlined). Class A is not used anywhere else except this asm file so why are those functions generated if not used ?
Whole program optimization was in place.

"so why are those functions generated if not used ?"
You are inspecting assembly code generated for a single translation unit, not the final assembly after linking.
The linker will strip out any unused functions finally.

Related

C++ inline assembly PROC ENDP error

I am trying to create procedure in assembly x86 inside a C++ program. My code is:
#include <stdio.h>
#include <stdlib.h>
int main(void){
_asm{
input1 PROC
push inputnumber
lea eax, inputmsg
push eax
call printf
add esp, 8
push ebx
lea eax, format
push eax
call scanf
add esp, 8
jmp check1
ret
input1 ENDP
}
}
However, when I try to compile the program with Visual studio I get the following error:
C2400 inline assembler syntax error in 'opcode'; found 'PROC'
C2400 inline assembler syntax error in 'opcode'; found 'ENDP'
I've read online but I cannot resolve it. Any suggestions how to fix it ?
Surprised that those are the only errors you get. PROC and ENDP are not recognized by the C inline assembler. Anyway, defining a function inside a function in C isn't a good idea. Try
int main(){
_asm{
push inputnumber
lea eax, inputmsg
:
call scanf
add esp, 8
ret
}
}
You will then end up with a whole bunch of undeclared variables and possibly warnings about scanf if you're using one of the MS compilers.

MSVC 2012 generates different vtable pointer offsets for different files

Let's say I've got in X64 release configuration
It's a an obfuscated code snippet...
// Hdr1.h
// Dozen of includes
class Cls1
{
public:
Cls1();
virtual void bar();
// ...
protected:
// about 7 fields where some of them are of complex template type.
bool isFlag1 : 1;
bool isFlag2 : 1;
};
// Hdr2
// Dozens of includes
class Cls2
{
public:
// ...
void foo();
};
I've got separate translation units to implement these classes. Say from foo I try to access virtual method of Cls1::bar and I get a crash(access violation).
void Cls2::foo()
{
//...
Cls1 * pCls1 = // somehow I get this goddamn pointer
pCls1->bar(); // Here I crash
}
From disassembly I see that Cls1::Cls1 puts vtable ptr at offset 8 to the very beginning of this. From disassembly of Cls2::foo I see that it takes pointer to vtable from offset zero. Debugger is also unable to see this vtable correctly. If I manually get vtable at offset 8 - addresses appear to be correct in this table.
The question is - why could this happen, what pragma could lead to this or anything else? Compilation flags are the same for both translation units.
Below I add a bit of disassembly:
This is a normal case that I face across the code:
Module1!CSomeOkClass::CreateObjInstance:
sub rsp,28h
mov edx,4 ; own inlined operator new
lea ecx,[rdx+34h] ; own inlined operator new
call OwnMemoryRoutines!OwnMalloc (someAddr) ; own inlined operator new
xor edx,edx
test rax,rax
je Module1!CSomeOkClass::CreateObjInstance+0x40 (someAddr)
**lea rcx,[Module1!CSomeOkClass::`vftable' (someAddr)] ; Inlined CSomeOkClass::CSomeOkClass < vtable ptr**
mov qword ptr [rax+8],rdx ; Inlined CSomeOkClass::CSomeOkClass
mov qword ptr [rax+10h],rdx ; Inlined CSomeOkClass::CSomeOkClass
mov qword ptr [rax+18h],rdx ; Inlined CSomeOkClass::CSomeOkClass
mov byte ptr [rax+20h],dl ; Inlined CSomeOkClass::CSomeOkClass
mov qword ptr [rax+28h],rdx ; Inlined CSomeOkClass::CSomeOkClass
**mov qword ptr [rax],rcx ; Inlined CSomeOkClass::CSomeOkClass < offset zero**
Now let's see what I've got for Cls1::Cls1:
Module1!Cls1::Cls1:
mov qword ptr [rsp+8],rbx
push rdi
sub rsp,20h
**lea rax,[Module1!Cls1::`vftable' (someAddress)] ; vtable address**
mov rbx,rdx
mov rdi,rcx
**mov qword ptr [rcx+8],rax ; Places at offset 8**
I assure you that Cls2 expects pointer to vtable to be at offset zero.
Compilation options are:
/nologo /WX /W3 /MD /c /Zc:wchar_t /Zc:forScope /Zm192 /bigobj /d2Zi+ /Zi /Oi /GS- /GF /Oy- /fp:fast /Gm- /Ox /Gy /Ob2 /GR- /Os
I noticed that Cls1::Cls1 heavily uses SSE instructions inlined from intrinsics.
Compiler version:
Microsoft (R) C/C++ Optimizing Compiler Version 17.00.50727.1 for x64
Please pay attention that this code works ok on different platforms/compilers.
I managed to figure out that the problem was in fact with this bitfield I have in the very end of Cl1 definition. The ctor generated places pointer to vtable at offset zero if I make isFlag1 + isFlag2 ordinary bools. These flags are initialized in the ctor's initializer list. By commenting out class's code one by line I narrowed down the problem to this bitfield. In order to investigate this I used WinDbg, /P compiler option, compiled cpp unit manually with the original flags provided + /FAs /Fa. It appears that it is a compiler's bug.
I managed to figure out that the problem was in fact with this bitfield I have in the very end of Cl1 definition. The ctor generated places pointer to vtable at offset zero if I make isFlag1 + isFlag2 ordinary bools. These flags are initialized in the ctor's initializer list. By commenting out class's code one by line I narrowed down the problem to this bitfield. In order to investigate this I used WinDbg, /P compiler option, compiled cpp unit manually with the original flags provided + /FAs /Fa. It appears that it is a compiler's bug.

Inserting a comment in __asm results in C2400 error (VS2012)

I was trying to check the compiled assembler of some code in VS 2012. I added two lines (before and after my code) as such:
__asm ; it begins here!
// My code
__asm ; it ends here!
However, VS didn't like that. I got
error C2400: inline assembler syntax error in 'opcode'; found 'bad token'
So I added a NOP, which I didn't want to:
__asm NOP ; Comment!
That worked fine. My question is twofold.
Why didn't VS allow me to add an assembly comment?
Is there a different way to add an assembly comment without adding an instruction, including NOP?
The reason it doesn't work is that __asm is a keyword, just like int is a keyword, it cannot appear by itself and must follow the proper syntax. Take the following bit of code as an example:
int main()
{
int // here's a comment, but it's ignored by the compiler
return 0;
}
The following code will fail with a compilation error, more specifically in VS2012 you get error C2143: syntax error : missing ';' before 'return'. This is an obvious error since we do not have the ending semi-colon to denote end of instruction; add the semi-colon and it compiles fine because we did not dis-obey the syntax of the C (or C++ in this case) language:
int main()
{
int // here's a comment, but it's ignored by the compiler
; // white space and comments are ignored by the compiler
return 0;
}
The same is true of the following code:
int main()
{
__asm ; here's a comment but it's ignored
return 0;
}
Except here we get the error error C2400: inline assembler syntax error in 'opcode'; found 'constant', becuase it's treating everything after the __asm keyword as an assembler instruction and the comment is being rightfully ignored .. so the following code WOULD work:
int main()
{
__asm ; here's a comment but it's ignored
NOP ; white space and comments are ignored by the compiler
__asm {; here's an __asm 'block'
} // outside of __asm block so only C style comments work
return 0;
}
So that answers your first question: Why didn't VS allow me to add an assembly comment?.. because it is a syntax error.
Now for your second question: Is there a different way to add an assembly comment without adding an instruction, including NOP?
Directly, no, there is not, but indirectly, yes there is. It's worth noting that the __asm keyword gets compiled into inline assembly in your program, so comments will be removed from the compiled assembly just as if it were a standard C/C++ comment, so trying to 'force' a comment in your assembly via that method is not necessary, instead, you can use the /FAs compiler flag and it will generate the assembly (machine code) mixed with the source, example:
Given the following (very simple) code:
int main()
{
// here's a normal comment
__asm { ; here's an asm comment and empty block
} // here's another normal comment
return 0;
}
When compiled with the /FAs compiler flag, the file.asm that was produced had the following output in it:
; Listing generated by Microsoft (R) Optimizing Compiler Version 18.00.31101.0
TITLE C:\test\file.cpp
.686P
.XMM
include listing.inc
.model flat
INCLUDELIB LIBCMT
INCLUDELIB OLDNAMES
PUBLIC _main
; Function compile flags: /Odtp
; File c:\test\file.cpp
_TEXT SEGMENT
_main PROC
; 2 : {
push ebp
mov ebp, esp
; 3 : // here's a normal comment
; 4 : __asm { ; here's an asm comment and empty block
; 5 : } // here's another normal comment
; 6 : return 0;
xor eax, eax
; 7 : }
pop ebp
ret 0
_main ENDP
_TEXT ENDS
END
Notice how it includes the source and comments. If this code did more, you would see more assembly and the source associated with that as well.
If you're wanting to put comments in the inline assembly itself, then you can use normal C/C++ style comments as well as assembly comments within the __asm block itself:
int main()
{
// here's a C comment
__asm { ; here's an asm comment
// some other comments
NOP ; asm type comment
NOP // C style comment
} // here's another comment
return 0;
}
Hope that can help.
EDIT:
It should be noted the following bit of code also compiles without error and I'm not 100% sure why:
int main()
{
__asm
__asm ; comment
// also just doing it on a single line works too: __asm __asm
return 0;
}
Compiling this code with the single __asm ; comment gives the compilation error, but with both it compiles fine; adding instructions to the above code and inspecting the .asm output shows that the second __asm is ignored for any other assembly commands preceding it. So I'm not 100% sure if this is a parsing bug or part of the __asm keyword syntax as there's no documentation on this behavior.
On Linux, g++ accepts this:
__asm(";myComment");
and outputs, when you run g++ -S -O3 filename.cpp:
# 5 "filename.cpp" 1
;myComment
However, clang++ does not like it, and complains with this, when you run clang++ -S -O3 filename.cpp:
filename.cpp:5:9: error: invalid instruction mnemonic 'myComment'
__asm(";myComment");
^
<inline asm>:1:3: note: instantiated into assembly here
;myComment
^~~~~~~~~
I was, however, able to get both g++ and clang++ to accept:
__asm("//myComment");
which outputs the same comment as in the assembly output above, for both compilers.
What clued me into this, as I was unable to find it anywhere else on the internet, was reading from here:
Microsoft Specific
Instructions in an __asm block can use assembly-language comments:
C++
__asm mov ax, offset buff ; Load address of buff
Because C macros expand into a single logical line, avoid using
assembly-language comments in macros. (See Defining __asm Blocks as C
Macros.) An __asm block can also contain C-style comments; for more
information, see Using C or C++ in __asm Blocks.
END Microsoft Specific
This page then links to here and here. These provide more information on the matter.

gdb in Windows: different behaviour when debugging compiled C and C++ code

I've noticed a strange behaviour of GDB 7.5 on Windows. Consider the following C program:
int foo(void){
int i = 5;
return i;
}
int main(int argc, char** argv){
foo();
return 0;
}
When compiled as either Classic C or C++, the GDB disass foo command gives the same assembly code, as follows:
Dump of assembler code for function foo:
0x00401954 <+0>: push %ebp
0x00401955 <+1>: mov %esp,%ebp
0x00401957 <+3>: sub $0x10,%esp
0x0040195a <+6>: movl $0x5,-0x4(%ebp)
0x00401961 <+13>: mov -0x4(%ebp),%eax
0x00401964 <+16>: leave
0x00401965 <+17>: ret
End of assembler dump.
However, after inserting a breakpoint at the "leave" command, like so: br *0x00401964, running the code up to that line, and attempting to print out the variable i, the executables produced by compiling it as C and C++ behaves differently. The C executable works as expected and prints out $i = 5, while with the C++ executable GDB chokes up and says "no symbol i in current context".
So just out of curiosity I'd like to know if this is a GDB bug or feature? Or is the compiler (GCC) doing something subtly different so that there's something happening between the lines? Thanks.
EDIT:
Well, I don't think it's true the compiler removed the function completely, because breaking at the line before "leave" and printing the value of i does work.
This is neither bug/feature nor a side effect of compiler optimization.
The disassembly clearly is the output of a non-optmized build (i is written
to the stack in foo+6 and reread from stack one step later in foo+13).
While the assembly output of C and C++ is the same in this case, the debug symbol output however is slightly different. The scope of i is more limited in C++. I can only speculate for the reasons. I would guess that this is related to the fact that scoping is more complex in C++ (think of constructors, destructors, exception) and so the C++ part of gcc is stricter on scopes than the C part of gcc.
Details
(I checked everything on a 32-bit build but on a 64-bit Linux with gcc 4.8 and gdb 7.6. While some details will differ on Windows I expect the general mechanics to be the same)
Note that addresses differ in my case.
(gdb) disas foo
Dump of assembler code for function foo:
0x080483ed <+0>: push %ebp
0x080483ee <+1>: mov %esp,%ebp
0x080483f0 <+3>: sub $0x10,%esp
0x080483f3 <+6>: movl $0x5,-0x4(%ebp)
0x080483fa <+13>: mov -0x4(%ebp),%eax
0x080483fd <+16>: leave
0x080483fe <+17>: ret
End of assembler dump.
Technically, foo+0 and foo+1 are the function prologue, foo+3 to foo+13 is the function body, and foo+16 and foo+17 is the function epilogue. So only foo+3 to foo+13 represent the code between { and }. I would say that the C++ version is more correct in saying that i is out of scope before and after the function body.
To see that this is really a matter of debug symbols you can dump out gdb's internals of the debug structures with maintenance print symbols output_file_on_disk. For C it looks like:
block #000, object at 0x1847710, 1 syms/buckets in 0x80483ed..0x804840e
int foo(); block object 0x18470d0, 0x80483ed..0x80483ff
int main(int, char **); block object 0x18475d0, 0x80483ff..0x804840e section .text
block #001, object at 0x18476a0 under 0x1847710, 1 syms/buckets in 0x80483ed..0x804840e
typedef int int;
typedef char char;
block #002, object at 0x18470d0 under 0x18476a0, 1 syms/buckets in 0x80483ed..0x80483ff, function foo
int i; computed at runtime
block #003, object at 0x18475d0 under 0x18476a0, 2 syms/buckets in 0x80483ff..0x804840e, function main
int argc; computed at runtime
char **argv; computed at runtime
While this is C++
block #000, object at 0x1a3c790, 1 syms/buckets in 0x80483ed..0x804840e
int foo(); block object 0x1a3c0c0, 0x80483ed..0x80483ff
int main(int, char**); block object 0x1a3c640, 0x80483ff..0x804840e section .text
block #001, object at 0x1a3c720 under 0x1a3c790, 1 syms/buckets in 0x80483ed..0x804840e
typedef int int;
typedef char char;
block #002, object at 0x1a3c0c0 under 0x1a3c720, 0 syms/buckets in 0x80483ed..0x80483ff, function foo()
block #003, object at 0x1a3c050 under 0x1a3c0c0, 1 syms/buckets in 0x80483f3..0x80483fd
int i; computed at runtime
block #004, object at 0x1a3c640 under 0x1a3c720, 2 syms/buckets in 0x80483ff..0x804840e, function main(int, char**)
int argc; computed at runtime
char **argv; computed at runtime
So the debug symbols for the C++ code distinguish between the whole function (block #002) and the scope of the function body (block #003). This results in your observations.
(And to see that this is really not gdb just handling something wrong you can even analyze the binary with objdump on Linux or dumpbin on Windows. I did it on Linux and indeed it's the DWARF debug symbols that are different :-) )
It's not really a bug or a feature. The compiler is permitted to substitute functionally-equivalent code and generally does so if it can find a better way to do things. The example code is equivalent to doing nothing at all, so the compiler is free to remove it. This leaves the debugger with nothing to debug, which is good since debugging code that does nothing would be a waste of time anyway.

Inline assembly troubles

I tried to compile with GCC inline assembly code which compiled fine with MSVC, but got the following errors for basic operations:
// var is a template variable in a C++ function
__asm__
{
mov edx, var //error: Register name not specified for %edx
push ebx //error: Register name not specified for %ebx
sub esp, 8 //error: Register name not specified for %esp
}
After looking through documentation covering the topic, I found out that I should probably convert (even if I am only interested in x86) Intel style assembly code to AT&T style. However, after trying to use AT&T style I got even more weird errors:
mov var, %edx //error: Expected primary-expression before % token
mov $var, edx //error: label 'LASM$$s' used but not defined
I should also note that I tried to use LLVM-GCC, but it failed miserably with internal errors after encountering inline assembly.
What should I do?
For Apple's gcc you want -fasm-blocks which allows you to omit gcc's quoting requirement for inline asm and also lets you use Intel syntax.
// test_asm.c
int main(void)
{
int var;
__asm__
{
mov edx,var
push ebx
sub esp,8
}
return 0;
}
Compile this with:
$ gcc -Wall -m32 -fasm-blocks test_asm.c -o test_asm
Tested with gcc 4.2.1 on OS X 10.6.
g++ inline assembler is much more flexible than MSVC, and much more complicated. It treats an asm directive as a pseudo-instruction, which has to be described in the language of the code generator. Here is a working sample from my own code (for MinGW, not Mac):
// int BNASM_Add (DWORD* result, DWORD* a, int len)
//
// result += a
int BNASM_Add (DWORD* result, DWORD* a, int len)
{
int carry ;
asm volatile (
".intel_syntax\n"
" clc\n"
" cld\n"
"loop03:\n"
" lodsd\n"
" adc [edx],eax\n"
" lea edx,[edx+4]\n" // add edx,4 without disturbing the carry flag
" loop loop03\n"
" adc ecx,0\n" // Return the carry flag (ecx known to be zero)
".att_syntax\n"
: "=c"(carry) // Output: carry in ecx
: "d"(result), "S"(a), "c"(len) // Input: result in edx, a in esi, len in ecx
) ;
return carry ;
}
You can find documentation at http://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#Extended-Asm.