I have this foo.cpp:
int add(int a, int b) {
return a + b;
}
int add_wrapper(int a, int b) {
return add(a,b);
}
Compiling like so
g++ -c -S -fPIC -O4 -finline-functions foo.cpp -o foo.s
Gives the (demangled) assembly:
.globl add(int, int)
.type add(int, int), #function
add(int, int):
.LFB0:
.cfi_startproc
endbr64
leal (%rdi,%rsi), %eax
ret
.cfi_endproc
.LFE0:
.size add(int, int), .-add(int, int)
.p2align 4
.globl add_wrapper(int, int)
.type add_wrapper(int, int), #function
add_wrapper(int, int):
.LFB1:
.cfi_startproc
endbr64
jmp add(int, int)#PLT
.cfi_endproc
.LFE1:
.size add_wrapper(int, int), .-add_wrapper(int, int)
.ident "GCC: (Ubuntu 11.2.0-19ubuntu1) 11.2.0"
Note that add() is not inlined.
However, without -fPIC, I get
.globl add(int, int)
.type add(int, int), #function
add(int, int):
.LFB0:
.cfi_startproc
endbr64
leal (%rdi,%rsi), %eax
ret
.cfi_endproc
.LFE0:
.size add(int, int), .-add(int, int)
.p2align 4
.globl add_wrapper(int, int)
.type add_wrapper(int, int), #function
add_wrapper(int, int):
.LFB3:
.cfi_startproc
endbr64
leal (%rdi,%rsi), %eax
ret
.cfi_endproc
.LFE3:
.size add_wrapper(int, int), .-add_wrapper(int, int)
With add() inlined.
If I add inline to the declaration of add() and compile with -fPIC, I get
.globl add_wrapper(int, int)
.type add_wrapper(int, int), #function
add_wrapper(int, int):
.LFB1:
.cfi_startproc
endbr64
leal (%rdi,%rsi), %eax
ret
.cfi_endproc
.LFE1:
.size add_wrapper(int, int), .-add_wrapper(int, int)
.ident "GCC: (Ubuntu 11.2.0-19ubuntu1) 11.2.0"
With add() omitted entirely.
So it seems that inlining is not disallowed by -fPIC because the compiler will inline if I label the function inline, but I don't understand why the compiler is unwilling to inline automatically with '-fPIC'.
Does anyone know why this is?
Is there a flag that will convince g++ to inline with -fPIC but without labeling functions as inline?
I'm using g++ 11.2, but Compiler Explorer shows that the behavior is consistent across versions: https://godbolt.org/z/Y14edz968.
Using -fPIC implies that you intend your functions to be used in a library. As neither function is labeled static, the compiler cannot assume anything and must make it available to link.
The function may still be used inline elsewhere in the compiled object code, but again, it cannot be removed from actual existence.
If you wish to remove a function from external availability, either label it with static
static int add( ... ) { ... }
or wrap it in an unnamed namespace.
namespace
{
int add(...) { ... }
}
At this point it can be inlined (either implicitly or explicitly) because you have specifically marked it as unavailable to external code, and consequently it might be optimized out of actual existence.
If you add the always_inline attribute to add, you can see the compiler error when it tries to inline:
warning: 'always_inline' function might not be inlinable [-Wattributes]
1 | [[gnu::always_inline]] int add(int a, int b) {
| ^~~
In function 'int add_wrapper(int, int)':
error: inlining failed in call to 'always_inline' 'int add(int, int)': function body can be overwritten at link time
note: called from here
6 | return add(a,b);
| ~~~^~~~~
The add(int, int) symbol might end up being something else (e.g., another library that was in LD_PRELOAD with an int add(int, int) is loaded first, your add_wrapper should call that one).
You can fix this by making add invisible ([[gnu::visibility("hidden")]]) or not giving it external linkage (with an anonymous namespace or static).
Or you can make g++ assume this will never happen with the switch -fno-semantic-interposition (which is the default on clang).
See New option in GCC 5.3: -fno-semantic-interposition for more information about semantic interposition.
Related
I am confused by the function pointer of the template function.
See
#include <cstdio>
class A
{
public:
A(){}
~A(){}
void command()
{
printf("Cmd");
}
};
template<class T>
void cmd_create()
{
T t;
t.command();
};
int main()
{
typedef void(*Funcptr)();
Funcptr f1 = &cmd_create<A>;
f1();
return 0;
}
The program output Cmd.
The assembly (-O3) shows that
.file "test.cpp"
.text
.section .rodata.str1.1,"aMS",#progbits,1
.LC0:
.string "Cmd"
.section .text.startup,"ax",#progbits
.p2align 4,,15
.globl main
.type main, #function
main:
.LFB38:
.cfi_startproc
leaq .LC0(%rip), %rsi
subq $8, %rsp
.cfi_def_cfa_offset 16
movl $1, %edi
xorl %eax, %eax
call __printf_chk#PLT
xorl %eax, %eax
addq $8, %rsp
.cfi_def_cfa_offset 8
ret
.cfi_endproc
.LFE38:
.size main, .-main
.ident "GCC: (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0"
.section .note.GNU-stack,"",#progbits
Obviously, &cmd_create<A> returns the A::command.
The cmd_create is defined as an empty return (void), however, the code still gets the A::command in such a weird way.
Thank you for your answer!
Obviously, &cmd_create returns the A::command.
I am not fluent with assembly, but obviously you misunderstand the code ;).
&cmd_create<A> is a pointer to the function cmd_create<A>. This function pointer is assigned to f1. The pointer is of type void(*)(), pointer to function returning void and no arguments. No function in the code returns anything (if we do not count printf or main).
f1(); calls the function pointed to by f1. That is: cmd_create<A>. The function creates an object of type A and calls its command member function which prints the output you see on the screen.
Enabling optimizations means that the compiler emits something that has the same observable behavior. It could emit the same as it would for int main() { printf("Cmd"); }.
This question already has answers here:
How to convert C++ Code to C [closed]
(6 answers)
Closed 6 years ago.
Suppose I where to take a c++ program and compile it into an assembly (.S) file.
Then I take that assembly file and "dissassemble" that into C, would that code be recompileable on a different platform?
The reason I ask this is that the platform I am trying to develop on does not have a c++ compiler by it does have a c compiler.
Yes, it's indeed possible in the way you describe. No, it won't be portable to any CPU architecture, OS, and compiler triplet other than yours.
Let's see why. Take some basic C++ code...
#include <iostream>
int main()
{
std::cout << "Hello, world!\n";
return 0;
}
Let's turn this into assembler using g++, on a x86-64 Linux box (I turned optimizations on, and discarded debug symbols, in purpose)...
$ g++ -o test.s -O3 -S test.cpp
And the result is...
.file "test.cpp"
.section .rodata.str1.1,"aMS",#progbits,1
.LC0:
.string "Hello, world!\n"
.section .text.unlikely,"ax",#progbits
.LCOLDB1:
.section .text.startup,"ax",#progbits
.LHOTB1:
.p2align 4,,15
.globl main
.type main, #function
main:
.LFB1027:
.cfi_startproc
subq $8, %rsp
.cfi_def_cfa_offset 16
movl $.LC0, %esi
movl $_ZSt4cout, %edi
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
xorl %eax, %eax
addq $8, %rsp
.cfi_def_cfa_offset 8
ret
.cfi_endproc
.LFE1027:
.size main, .-main
.section .text.unlikely
.LCOLDE1:
.section .text.startup
.LHOTE1:
.section .text.unlikely
.LCOLDB2:
.section .text.startup
.LHOTB2:
.p2align 4,,15
.type _GLOBAL__sub_I_main, #function
_GLOBAL__sub_I_main:
.LFB1032:
.cfi_startproc
subq $8, %rsp
.cfi_def_cfa_offset 16
movl $_ZStL8__ioinit, %edi
call _ZNSt8ios_base4InitC1Ev
movl $__dso_handle, %edx
movl $_ZStL8__ioinit, %esi
movl $_ZNSt8ios_base4InitD1Ev, %edi
addq $8, %rsp
.cfi_def_cfa_offset 8
jmp __cxa_atexit
.cfi_endproc
.LFE1032:
.size _GLOBAL__sub_I_main, .-_GLOBAL__sub_I_main
.section .text.unlikely
.LCOLDE2:
.section .text.startup
.LHOTE2:
.section .init_array,"aw"
.align 8
.quad _GLOBAL__sub_I_main
.local _ZStL8__ioinit
.comm _ZStL8__ioinit,1,1
.hidden __dso_handle
.ident "GCC: (GNU) 5.3.1 20151207 (Red Hat 5.3.1-2)"
.section .note.GNU-stack,"",#progbits
That mess is the price we pay for exception handling, templates, and namespaces. Let's disassemble this into C by hand, discarding the exception handling tables for a cleaner view...
/* std::ostream */
typedef struct
{
/* ... */
} _ZSo;
/* extern operator<<(std::basic_ostream&, const char*); */
extern _ZStlsIst11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc();
/* namespace std { extern ostream cout; } */
extern _ZSo _ZSt4cout;
/* Our string, of course! */
static const char* LC0 = "Hello, world!\n";
int main()
{
_ZStlsIst11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc(&_ZSt4cout, LC0);
return 0;
}
Seems unreadable, yet portable, right? Right now, it is. But, this code won't work! You need to define _ZSo (std::ostream) yet in C struct's terms, not to mention including all the exception handling stuff.
Things get near impossible to get portable once you start using try/throw/catch. I mean, there's absolutely no way that __cxa_allocate_exception or __cxa_throw will ever be portable! Your only way around this is to rewrite all your program (and that includes all libraries you use, even the standard library!) to use the slower setjmp/longjmp approach to exceptions instead of zero-cost exception handling.
Finally, but not least, a non-human disassembler will most likely fail at doing this properly, even at the simplest of inputs. Remember that, at the lowest levels of stuff (a.k.a machine code and assembly language), there's no concept of types. You can't never know if a register is a signed integer, and unsigned integer, or basically anything else, for instance. The compiler can also play with the stack pointer at its will, worsening the job.
Once compilation is over, the compiler, not expecting any future disassembling, wipes out all this precious information, because you normally don't need it at run-time. In most cases, disassembling is really not worth it, if even possible, and most likely you're seeking for a different solution. Translating from a higher-middle-level language to a lower-level language to a lower-middle-level language takes this to an extreme, and approaches the limits of what can be translated to what else.
This is my main function
#include<stdio.h>
#include<stdlib.h>
int name1(int g);
char* getStringFromC(void);
int main(){
int a=2;
char* g="this is called from assembly";
printf("%d\n",name1(a));
return 0;
}
.text
.globl name1
name1:
push %ebp
movl %esp, %ebp
movl 8(%ebp),%eax
movl %ebp,%esp
popl %ebp
ret//This function name1(int a) in assembly
Problem is how to access char* g from assembly ?? I though was 12(%ebp) or 16(%ebp) should contain that values which are like char* g and so on..
You're not supposed to. If it wasn't included as parameter and is not global, you shouldn't try to access it.
But for the sake of completeness, I'll answer anyway: Use the frame pointer. %ebp points to its previous value. Copy it to a scratch register and you can index the caller's local variables through it.
The example below does what you seem to want, and also shows how to
access a global variable. This works on my Ubuntu 13.10 x64 system.
As noted by Medinoc and hvd, looking at the stack in the way you're
trying to do isn't robust.
Makefile:
CFLAGS=-Wall -Wextra
all: main
main: main.o lib.o
main.c:
#include <stdio.h>
void* stack_peek(int g);
void* get_global_str(void);
char const *global_str="Global";
int main(){
int a=2;
char const * g="this is called from assembly";
printf("stack_peek(a): %p\n",stack_peek(a));
printf("g: %p\n",(void*)g);
printf("global_str: %p\n",(void*)global_str);
printf("get_global_str(): %p\n",(void*)get_global_str());
return 0;
}
lib.s:
.text
.globl global_str
.type global_str, #common
.globl stack_peek
.type stack_peek, #function
stack_peek:
pushq %rbp
movq %rsp, %rbp
movq 24(%rbp),%rax
movq %rbp,%rsp
popq %rbp
ret
.globl get_global_str
.type get_global_str, #function
get_global_str:
movq global_str,%rax
ret
Output:
$ make -B
cc -Wall -Wextra -c -o main.o main.c
as -o lib.o lib.s
cc main.o lib.o -o main
$ ./main
stack_peek(a): 0x40067b
g: 0x40067b
global_str: 0x400674
get_global_str(): 0x400674
If I have a logging class which, in release mode, is empty, and has an ostream operator which does nothing. It more-or-less looks like this:
struct null_logger
{
template<typename T> inline null_logger& operator<<(T) { return *this; }
};
I've created a simple test and pasted the generated assembly below:
const char* foo()
{
return "hello";
}
int main()
{
int i = 0;
null_logger() << i << foo() << " this is a test";
return 0;
}
To be honest, I don't fully understand the assembly. As per suggestion from #Als, I have looked for call statements, of which there are none. Is it therefore safe to assume that, in release mode, any calls to this ostream operator will be compiled out?
Here is the generated assembly, using g++ -O3 -S main.cpp
.file "main.cpp"
.section .rodata.str1.1,"aMS",#progbits,1
.LC0:
.string "hello"
.text
.p2align 4,,15
.globl _Z3foov
.type _Z3foov, #function
_Z3foov:
.LFB3:
movl $.LC0, %eax
ret
.LFE3:
.size _Z3foov, .-_Z3foov
.p2align 4,,15
.globl main
.type main, #function
main:
.LFB4:
xorl %eax, %eax
ret
.LFE4:
.size main, .-main
.section .eh_frame,"a",#progbits
.Lframe1:
.long .LECIE1-.LSCIE1
.LSCIE1:
.long 0x0
.byte 0x1
.globl __gxx_personality_v0
.string "zPR"
.uleb128 0x1
.sleb128 -8
.byte 0x10
.uleb128 0x6
.byte 0x3
.long __gxx_personality_v0
.byte 0x3
.byte 0xc
.uleb128 0x7
.uleb128 0x8
.byte 0x90
.uleb128 0x1
.align 8
.LECIE1:
.LSFDE1:
.long .LEFDE1-.LASFDE1
.LASFDE1:
.long .LASFDE1-.Lframe1
.long .LFB3
.long .LFE3-.LFB3
.uleb128 0x0
.align 8
.LEFDE1:
.LSFDE3:
.long .LEFDE3-.LASFDE3
.LASFDE3:
.long .LASFDE3-.Lframe1
.long .LFB4
.long .LFE4-.LFB4
.uleb128 0x0
.align 8
.LEFDE3:
.ident "GCC: (SUSE Linux) 4.3.4 [gcc-4_3-branch revision 152973]"
.section .comment.SUSE.OPTs,"MS",#progbits,1
.string "Ospwg"
.section .note.GNU-stack,"",#progbits
Not a direct answer to your question, but normally you disable logging in another way: You simply do not evaluate whatever is after LOG_DBG through short-circuiting, which is rather simple:
#ifdef NDEBUG
#define LOG_CHECK false &&
#elseif
#define LOG_CHECK /*empty*/
#define LOG_DBG LOG_CHECK /*your_debug_logging_code*/
This way, you guarantee that everything after LOG_DBG is dead code that can get optimized out in release mode.
Note that even if they were not compiled out, they wouldn't be executed at runtime thanks to the short-circuiting.
Now, for this to actually work, the /*your_debug_logging_code*/ needs to evaluate to a boolean value, for example via the safe-bool idiom. The IOstreams for example do that, to let you know if the state of the stream is still OK.
class Logger{
typedef void (Logger::*safe_bool)();
void safe_bool_check(){}
public:
// ...
operator safe_bool() const{
return check_state() ? &Logger::safe_bool_check : 0;
}
};
Of course, if you just want the boolean conversion to make the short-circuiting work, you can simply return 0; in the conversion operator.
Note that if you're using a really recent version of GCC or Clang, you might already have access to explicit conversion operators, and as such don't need the safe-bool idiom:
explicit operator bool() const{ return check_state(); }
Another way might be to use the ternary operator for the short circuiting. However, both "branches" need to be of the same type, so we'll use a little trick here, a simple class that can be constructed from anything:
namespace log_detail{
struct log_check_helper{
log_check_helper(){}
template<class T>
log_check_helper(T const&){}
};
}
#ifdef NDEBUG
#define LOG_CHECK true ? log_detail::log_check_helper() :
#else
#define LOG_CHECK /*empty*/
#endif
#define LOG_DBG LOG_CHECK /*your_debug_logging_code*/
With a working example that compiles and does not evaluate the IOstream code on Ideone.
I’m currently working on a reporting library as part of a large project. It contains a collection of logging and system message functions. I’m trying to utilize preprocessor macros to strip out a subset of the functions calls that are intended strictly for debugging, and the function definitions and implementations themselves, using conditional compilation and function like macros defined to nothing (similar to the way that assert() calls are removed if DEBUG is defined). I’m running into a problem. I prefer to fully qualify namespaces, I find it improves readability; and I have my reporting functions wrapped in a namespace. Because the colon character can’t be part of a macro token I am unable to include the namespace in the stripping of the function calls. If I defined the functions alone to nothing I end up with Namespace::. I've considered just using conditional compilation to block the function code for those functions, but I am worried that the compiler might not competently optimize out the empty functions.
namespace Reporting
{
const extern std::string logFileName;
void Report(std::string msg);
void Report(std::string msg, std::string msgLogAdd);
void Log(std::string msg);
void Message(std::string msg);
#ifdef DEBUG
void Debug_Log(std::string message);
void Debug_Message(std::string message);
void Debug_Report(std::string message);
void Debug_Assert(bool test, std::string message);
#else
#define Debug_Log(x);
#define Debug_Message(x);
#define Debug_Report(x);
#define Debug_Assert(x);
#endif
};
Any idea on how to deal with the namespace qualifiers with the preprocessor?
Thoughts on, problems with, just removing the function code?
Any other ways to accomplish my goal?
This is how I did it when I wrote a similar library several months back. And yes, your optimizer will remove empty, inline function calls. If you declare them out-of-line (not in the header file), your compiler will NOT inline them unless you use LTO.
namespace Reporting
{
const extern std::string logFileName;
void Report(std::string msg);
void Report(std::string msg, std::string msgLogAdd);
void Log(std::string msg);
void Message(std::string msg);
#ifdef DEBUG
inline void Debug_Log(std::string message) { return Log(message); }
inline void Debug_Message(std::string message) { return Message(message); }
inline void Debug_Report(std::string message) { return Report(message); }
inline void Debug_Assert(bool test, std::string message) { /* Not sure what to do here */ }
#else
inline void Debug_Log(std::string) {}
inline void Debug_Message(std::string) {}
inline void Debug_Report(std::string) {}
inline void Debug_Assert(std::string) {}
#endif
};
Just as a side note, don't pass strings by value unless you need to make a copy anyways. Use a const reference instead. It prevents an expensive allocation + strcpy on the string for EVERY function call.
EDIT: Actually, now that I think about it, just use a const char*. Looking at the assembly, it's a LOT faster, especially for empty function bodies.
GCC optimizes this out at -O1, I don't think there's much of an issue with this:
clark#clark-laptop /tmp $ cat t.cpp
#include <cstdio>
inline void do_nothing()
{
}
int main()
{
do_nothing();
return 0;
}
clark#clark-laptop /tmp $ g++ -O1 -S t.cpp
clark#clark-laptop /tmp $ cat t.s
.file "t.cpp"
.text
.globl main
.type main, #function
main:
.LFB32:
.cfi_startproc
movl $0, %eax
ret
.cfi_endproc
.LFE32:
.size main, .-main
.ident "GCC: (Gentoo 4.5.0 p1.2, pie-0.4.5) 4.5.0"
.section .note.GNU-stack,"",#progbits
After a bit of tweaking, it seems that this will only be a FULL removal if you use const char*, NOT std::string or const std::string&. Here's the assembly for the const char*:
clark#clark-laptop /tmp $ cat t.cpp
inline void do_nothing(const char*)
{
}
int main()
{
do_nothing("test");
return 0;
}
clark#clark-laptop /tmp $ g++ -O1 -S t.cpp
clark#clark-laptop /tmp $ cat t.s
.file "t.cpp"
.text
.globl main
.type main, #function
main:
.LFB1:
.cfi_startproc
movl $0, %eax
ret
.cfi_endproc
.LFE1:
.size main, .-main
.ident "GCC: (Gentoo 4.5.0 p1.2, pie-0.4.5) 4.5.0"
.section .note.GNU-stack,"",#progbits
And here's with const std::string&...
.file "t.cpp"
.section .rodata.str1.1,"aMS",#progbits,1
.LC0:
.string "test"
.text
.globl main
.type main, #function
main:
.LFB591:
.cfi_startproc
subq $24, %rsp
.cfi_def_cfa_offset 32
leaq 14(%rsp), %rdx
movq %rsp, %rdi
movl $.LC0, %esi
call _ZNSsC1EPKcRKSaIcE
movq (%rsp), %rdi
subq $24, %rdi
cmpq $_ZNSs4_Rep20_S_empty_rep_storageE, %rdi
je .L11
movl $_ZL22__gthrw_pthread_cancelm, %eax
testq %rax, %rax
je .L3
movl $-1, %eax
lock xaddl %eax, 16(%rdi)
jmp .L4
.L3:
movl 16(%rdi), %eax
leal -1(%rax), %edx
movl %edx, 16(%rdi)
.L4:
testl %eax, %eax
jg .L11
leaq 15(%rsp), %rsi
call _ZNSs4_Rep10_M_destroyERKSaIcE
.L11:
movl $0, %eax
addq $24, %rsp
.cfi_def_cfa_offset 8
ret
.cfi_endproc
.LFE591:
.size main, .-main
[Useless stuff removed...]
.ident "GCC: (Gentoo 4.5.0 p1.2, pie-0.4.5) 4.5.0"
.section .note.GNU-stack,"",#progbits
Huge difference, eh?
I am not sure if I fully understand your problem. Would the following help?
namespace X
{
namespace{int dummy;}
void debug_check(int);
}
#ifdef DEBUG
#define DEBUG_CHECK(ARG) debug_check(ARG)
#else
#define DEBUG_CHECK(ARG) dummy // just ignore args
#endif
int main()
{
X::DEBUG_CHECK(1);
}
This solution might not work, because it can generate a "statement without effect" warning. A potentially better solution would be to gobble the namespace prefix up in a function declaration:
// debug_check and "#ifdef DEBUG" part omitted
namespace X
{
typedef void dummy_type;
}
namespace Y
{
typedef void dummy_type;
}
typedef void dummy_type;
#define DEBUG(X) dummy_type dummy_fn();
int main()
{
X::DEBUG(1);
Y::DEBUG(2);
X::DEBUG(3);
Y::DEBUG(4);
DEBUG(5);
DEBUG(6);
};
As long as any definition of dummy_type yields the same type, this should legal, because typedefs are not distinct types.
you could just have your logging function replaced by a function that does nothing, no?
I know that this questions has been answered since ages, but I came across this problem when I put a log macro into a namespace. You were suggesting empty functions and optimization levels. Clark Gaebles made me think, because of the different results using const char*or const std::string&. The following code gives me no reasonable changes in assembly with no optimization levels enabled:
#include <iostream>
#undef _DEBUG // undefine to use __NOJOB
namespace Debug
{
typedef void __NOJOB;
class Logger
{
public:
static void Log( const char* msg, const char* file, int line )
{
std::cout << "Log: " << msg << " in " <<
file << ":" << line << std::endl;
}
};
}
#ifdef _DEBUG
#define Log( msg ) Logger::Log( msg, __FILE__, __LINE__ );
#else
#define Log( msg )__NOJOB(0);
#endif
int main()
{
Debug::Log( "please skip me" );
return 0;
}
created assembly by http://assembly.ynh.io/:
main:
.LFB972:
.cfi_startproc
0000 55 pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
0001 4889E5 movq %rsp, %rbp
.cfi_def_cfa_register 6 // <- stack main
// no code for void( 0 ) here
0004 B8000000 movl $0, %eax // return
00
0009 5D popq %rbp // -> end stack main
.cfi_def_cfa 7, 8
000a C3 ret
Maybe I made an mistake or understood something wrong? Would be nice to hearing from you.