I am in the process of trying to learn C's more advanced aspects and wrote this when experimenting with the __inline__ keyword:
#include <stdio.h>
void f(int);
void g(int);
__inline__ void f(int egg)
{
printf("f %d\n", egg);
g(egg);
}
__inline__ void g(int egg)
{
printf("g %d\n", egg);
f(egg);
}
int main()
{
f(123);
return 0;
}
I went to the GNU manuals and found that the -Winline compiler option warns when a function marked __inline__ can't be substituted.
I was indeed expecting a warning, but I compiled this program with gcc -ansi -pedantic -Wall -Wextra -Winline -o test test.c and there were no warnings.
When I ran the program, it printed out the number a whole bunch of times before a segmentation fault, presumably due to the recursion limit being exceeded.
My question is, how does gcc behave in cases like that? If it does inline the functions, how does it know it hit a recursive call between two functions?
Thank you in advance
https://gcc.gnu.org/onlinedocs/gcc-7.4.0/gcc/Inline.html#Inline
GCC does not inline any functions when not optimizing unless you specify the ‘always_inline’ attribute for the function
Since you are compiling without optimization, gcc does not even try to inline your functions, hence you do not get a warning that it wasn't done.
When I compile your code with -O -Winline, I get a warning as expected:
inline.c: In function ‘main’:
inline.c:8:17: warning: inlining failed in call to ‘f’: call is unlikely and code size would grow [-Winline]
__inline__ void f(int egg)
^
inline.c:24:5: note: called from here
f(123);
^~~~~~
According to what I see in goldbolt this case the compiler is smart enough to understand that that code is equivalent to a endless loop (see .L2 in the code below).
This optimization is possible when recursive functions are tail recursive
This has nothing to do with __inline__. In fact if you remove the __inline__ keyword you get the same optimization and also the assembly code for g and f, which are never called.
.LC0:
.string "f %d\n"
.LC1:
.string "g %d\n"
main:
sub rsp, 8
.L2:
mov esi, 123
mov edi, OFFSET FLAT:.LC0
xor eax, eax
call printf
mov esi, 123
mov edi, OFFSET FLAT:.LC1
xor eax, eax
call printf
jmp .L2
The following compare the assembly generated by gcc with (on the right) and withtout (on the left) __inline__ keyword for g and f.
As you can see the main contains exactly the same code. The only difference is that you get additional code for g and f.
Related
Recently I read the code of one public C library and found below function definition:
void* block_alloc(void** block, size_t* len, size_t type_size)
{
return malloc(type_size);
(void)block;
(void)len;
}
I wonder whether it will arrive at the statements after return. If not, what's the purpose of these 2 statements that convert some data to void ?
As Basil notes, the (void) statements are likely intended to silence compiler warnings about the unused parameters. But - you can move the (void) statements before the return to make them less confusing, and with the same effect.
In fact, there's yet another way to achieve the same effect, without resorting to any extra statements. It's supported by many compilers already today, although it's not officially in the C standard before C2X:
void* block_alloc(void**, size_t*, size_t type_size)
{
return malloc(type_size);
}
if you don't name the parameters, typical compilers don't expect you to be using them.
First, these statements appearing in the block after a return will never be executed.
Check by reading some C standard like n1570.
Second, on some compilers (perhaps GCC 10 invoked as gcc -Wall -Wextra) the useless statements might avoid some warnings.
In my opinion, coding these statements before the return won't change the machine code emitted by an optimizing compiler (use gcc -Wall -Wextra -O2 -fverbose-asm -S to check and emit the assembler code) and makes the C source code more understandable.
GCC provides, as an extension, the variable __attribute__ named unused.
Perhaps in your software your block_alloc is assigned to some important function pointer (whose signature is requested)
It is used to silence the warnings. Some programming standards required all the parameters to be used in the function body, and their static analyzers will not pass the code without it.
It is added after the return to prevent a generation of the code in some circumstances:
int foo(volatile unsigned x)
{
(void)x;
return 0;
}
int foo1(volatile unsigned x)
{
return 0;
(void)x;
}
foo:
mov DWORD PTR [rsp-4], edi
mov eax, DWORD PTR [rsp-4]
xor eax, eax
ret
foo1:
mov DWORD PTR [rsp-4], edi
xor eax, eax
ret
I tried to write a char[] in stdout using inline NASM (note .intel_syntax and .att_syntax are added so it can be compiled with gcc)
but it doesn't write anything in stdout.
I am using Linux 16 (x86) on Virtual machine
Is it cause of char c[] ? ( I read by this way of compiling we cannot use memory variables but what to do instead?)
#include<stdio.h>
char c[] = "abcd";
int main(){
asm (".intel_syntax noprefix");
// write c into stdout
asm("mov EAX,4"); // 4: write
asm("mov EBX,1"); // 1: stdout
asm("mov ECX,c");
asm("mov EDX,5");
asm("int 0x80");
// exit program
asm("mov EAX,1")
asm("int 0x80")
asm (".att_syntax noprefix");
}
the output is nothing
The GNU assembler (which is what gcc uses) does not use NASM syntax. It rather uses a variant of Microsoft's MASM syntax where no brackets are needed to dereference a variable. Since you don't want to load the value of the c variable but rather its address, you need an offset keyword:
mov ecx, offset c
I strongly recommend you to avoid inline assembly as much as possible for learning assembly. Using inline assembly in gcc requires good knowledge of how exactly this whole thing works and writing random instructions usually leads to wrong code. Even your simple code is already fundamentally broken and would not work if it was any more complicated than that (so the compiler had a chance to try to use the registers you overwrote without telling).
Instead, put your assembly in a separate file and link it in. This sidesteps all issues you have with inline assembly and allows you to use NASM as you wanted. For example, try something like this:
main.c
char c[] = "abcd";
/* the function you define in print_c.asm */
extern void print_c();
int main() {
print_c(); /* call your assembly function */
}
print_c.asm
; pull in c defined in main.c
extern c
section .text
global print_c
print_c:
; write c to stdout
mov eax, 4
mov ebx, 1
mov ecx, c
mov edx, 5
int 0x80
; exit program
mov eax, 1
int 0x80
Then assemble, compile, and link with:
nasm -felf print_c.asm
cc -m32 -o print_c print_c.o main.c
I'm cleaning up some code in a driver (Netgear A6210) written in C, and I've run into a helper function, VIRTUAL_IF_DOWN(), which is forcibly inlined (e.g. __inline instead of inline) and contains a seemingly arbitrary return statement at the end.
__inline void VIRTUAL_IF_DOWN(void *pAd)
{
/* Some code here */
return ;
}
However, this helper function is called in the body of two other functions, before control is given back to the rest of the program, so my question is, does this return statement get inlined with the rest of the function, thus breaking out of the larger function or just do nothing? My general rule of thumb for inlined functions is that I should always treat them as separate functions and not assume that they'll be inlined as-is, anyway, I've give one of the encapsulating functions as an example:
static void rtusb_disconnect(struct usb_interface *intf)
{
/* Some code here and then an ugly looking preprocessor branch */
#ifdef IFUP_IN_PROBE
VIRTUAL_IF_DOWN(pAd); // Function is used here
#endif
/* Other code here */
}
I apologize for the messy boilerplate code, but even if the return statement just gets inlined it seems to be obfuscating the code. It seems like bad practice to hide a statement that can affect the flow behind an inlined function. What would be a better solution?
Another part of my question would be, is inlining determined at the preprocessor stage of compilation or later, such as in the assembler or linker stage?
Inlining is more then just copy pasting code in contrast to macro pre-processing.
When a compiler encounters an inlining directive of any sort, it evaluates what the inlined functions returns or does.
You can use sites such as godbolt.org to see what assembly is generated for C code. For example, the following functions evaluate to the same assembly code:
#include <stdio.h>
inline void test1(int number){
printf("%d", number);
return;
}
inline int test2(){
return 1+1;
}
void doSomething() {
test1(test2());
}
void doSomethingElse() {
printf("%d", 2);
}
and the assembly:
.LC0:
.string "%d"
_Z11doSomethingv:
sub rsp, 8
mov esi, 2
mov edi, OFFSET FLAT:.LC0
mov eax, 0
call printf
add rsp, 8
ret
_Z15doSomethingElsev:
sub rsp, 8
mov esi, 2
mov edi, OFFSET FLAT:.LC0
mov eax, 0
call printf
add rsp, 8
ret
You can see this example at https://godbolt.org/z/rBULdo
You should also note that inlining is a compiler optimization. Different compilation flags could result in different results from inlining.
It also depends on how much information is available at compile time versus run time. If the compiler knows more information at compile time it could optimize better then what it could if everything was only known at run time.
See link for GCC's behaviour when encountering the inline attribute
return; at the end of a void function is redundant.
Inlining preserves semantics: If return returns from the non-inlined call, then return in an inlined function returns from the inlined "call", i.e. it jumps to the end of the inlined body.
Inlining happens during compilation, i.e. after preprocessing and parsing, but before code generation and assembling.
I stumbled over an interesting question in a forum a long time ago and I want to know the answer.
Consider the following C function:
f1.c
#include <stdbool.h>
bool f1()
{
int var1 = 1000;
int var2 = 2000;
int var3 = var1 + var2;
return (var3 == 0) ? true : false;
}
This should always return false since var3 == 3000. The main function looks like this:
main.c
#include <stdio.h>
#include <stdbool.h>
int main()
{
printf( f1() == true ? "true\n" : "false\n");
if( f1() )
{
printf("executed\n");
}
return 0;
}
Since f1() should always return false, one would expect the program to print only one false to the screen. But after compiling and running it, executed is also displayed:
$ gcc main.c f1.c -o test
$ ./test
false
executed
Why is that? Does this code have some sort of undefined behavior?
Note: I compiled it with gcc (Ubuntu 4.9.2-10ubuntu13) 4.9.2.
As noted in other answers, the problem is that you use gcc with no compiler options set. If you do this, it defaults to what is called "gnu90", which is a non-standard implementation of the old, withdrawn C90 standard from 1990.
In the old C90 standard there was a major flaw in the C language: if you didn't declare a prototype before using a function, it would default to int func () (where ( ) means "accept any parameter"). This changes the calling convention of the function func, but it doesn't change the actual function definition. Since the size of bool and int are different, your code invokes undefined behavior when the function is called.
This dangerous nonsense behavior was fixed in the year 1999, with the release of the C99 standard. Implicit function declarations were banned.
Unfortunately, GCC up to version 5.x.x still uses the old C standard by default. There is probably no reason why you should want to compile your code as anything but standard C. So you have to explicitly tell GCC that it should compile your code as modern C code, instead of some 25+ years old, non-standard GNU crap.
Fix the problem by always compiling your program as:
gcc -std=c11 -pedantic-errors -Wall -Wextra
-std=c11 tells it to make a half-hearted attempt to compile according the (current) C standard (informally known as C11).
-pedantic-errors tells it to whole-heartedly do the above, and give compiler errors when you write incorrect code which violates the C standard.
-Wall means give me some extra warnings that might be good to have.
-Wextra means give me some other extra warnings that might be good to have.
You don't have a prototype declared for f1() in main.c, so it is implicitly defined as int f1(), meaning it is a function that takes an unknown number of arguments and returns an int.
If int and bool are of different sizes, this will result in undefined behavior. For example, on my machine, int is 4 bytes and bool is one byte. Since the function is defined to return bool, it puts one byte on the stack when it returns. However, since it's implicitly declared to return int from main.c, the calling function will try to read 4 bytes from the stack.
The default compilers options in gcc won't tell you that it's doing this. But if you compile with -Wall -Wextra, you'll get this:
main.c: In function ‘main’:
main.c:6: warning: implicit declaration of function ‘f1’
To fix this, add a declaration for f1 in main.c, before main:
bool f1(void);
Note that the argument list is explicitly set to void, which tells the compiler the function takes no arguments, as opposed to an empty parameter list which means an unknown number of arguments. The definition f1 in f1.c should also be changed to reflect this.
I think it's interesting to see where the size-mismatch mentioned in Lundin's excellent answer actually happens.
If you compile with --save-temps, you will get assembly files that you can look at. Here's the part where f1() does the == 0 comparison and returns its value:
cmpl $0, -4(%rbp)
sete %al
The returning part is sete %al. In C's x86 calling conventions, return values 4 bytes or smaller (which includes int and bool) are returned via register %eax. %al is the lowest byte of %eax. So, the upper 3 bytes of %eax are left in an uncontrolled state.
Now in main():
call f1
testl %eax, %eax
je .L2
This checks whether the whole of %eax is zero, because it thinks it's testing an int.
Adding an explicit function declaration changes main() to:
call f1
testb %al, %al
je .L2
which is what we want.
Please compile with a command such as this one:
gcc -Wall -Wextra -Werror -std=gnu99 -o main.exe main.c
Output:
main.c: In function 'main':
main.c:14:5: error: implicit declaration of function 'f1' [-Werror=impl
icit-function-declaration]
printf( f1() == true ? "true\n" : "false\n");
^
cc1.exe: all warnings being treated as errors
With such a message, you should know what to do to correct it.
Edit: After reading a (now deleted) comment, I tried to compile your code without the flags. Well, This led me to linker errors with no compiler warnings instead of compiler errors. And those linker errors are more difficult to understand, so even if -std-gnu99 is not necessary, please try to allways use at least -Wall -Werror it will save you a lot of pain in the ass.
I have the following code (two files):
main.c
#include <stdio.h>
void print();
void asm_print();
int main() {
asm_print();
printf("done\n");
return 0;
}
void print() {
printf("printing with number: %d\n", 1);
// printf("printing without number\n");
}
lib.s
.intel_syntax noprefix
.text
.globl asm_print
asm_print:
push rbp
mov rbp, rsp
call print
mov rsp, rbp
pop rbp
ret
expected output
printing with number: 1
done
If I compile on linux using gcc4.9.3 and the command line:
gcc -O2 -m64 -Wall -Wextra -Werror -std=gnu99 main.c lib.s
everything works fine. This also works if I use –O1 or –O3. If I compile on cygwin64 using gcc4.9.3 and the command line:
gcc –O1 -m64 -Wall -Wextra -Werror -std=gnu99 main.c lib.s
everything works fine.
If, however, I change the above to use –O2 or –O3, only the first line of output is produced. If in the function print() I comment out the first line and uncomment the second line, I get the expected output:
printing without number
done
regardless of the amount of optimization I use. Can anyone suggest what is wrong with the code such that I get the expected output regardless of the amount of optimization used on cygwin64?
The problem is that the windows 64 bit ABI is different than the 32-bit ABI, and requires the caller to allocate scratch parameter (home) space of 32 bytes on the stack for use by the callee.
http://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64/
https://msdn.microsoft.com/en-us/library/tawsa7cb.aspx
So what you need to do is to decrement the stack by at least 32 before the call. In addition, x64 requires maintaining the stack pointer on a multiple of 16 bytes. The 64 bit return address is 8 bytes, so you actually need to move rsp by 40, 56, etc.
asm_print:
push rbp
mov rbp, rsp
sub rsp, 40
call print
add rsp, 40
pop rbp
ret
Presumably, when you call print / printf with just a string constant, it doesn't actually use any of the scratch space, so nothing bad happens. On the other hand, when you use the %d format, it needs the parameter space, and clobbers your saved registers on the stack.
The reason it works with optimization disabled, is that the print function doesn't use the home space, and allocates parameter space when it calls printf. If you use -O2, the compiler does tail-call elimination and replaces the "call printf" instruction with a "jmp printf". This essentially results in re-using the parameter space that was supposed to be allocated by asm_print.