Will address of `foo' here be always non-zero? - c

I have the following code:
#include <stdio.h>
#include <stdlib.h>
extern void foo(double);
int main(void) {
// printf("address is: %p\n", *foo);
if (foo)
puts("indeed");
else
puts("not");
exit(0);
}
It always compiles and prints indeed if line number 7 is commented but gcc warns me that:
the address of 'foo' will always evaluate as 'true' [-Waddress]
However, it doesn't compile if line 7 is uncommented:
/tmp/ccWvhcze.o: In function `main':
post.c:(.text.startup+0x5): undefined reference to `foo'
collect2: error: ld returned 1 exit status
Well, if address is always non-zero according to gcc why does linking fail? Of course, I would expect it because foo is just not defined here but why does gcc claim that address will always be non-zero? Is it mandated by C standard for all identifiers in the translation unit to always evaluate to true?

Testing if (pointer_expression) checks whether the pointer is a null pointer or not. Every valid object and function is guaranteed to not be a null pointer. (Note that a null pointer is not necessarily "zero".)

The compiler removes the test in if () because it's guaranteed to be true. Because of that the linker has nothing to resolve. Adding a printf of foo adds back the need to resolve foo.

You must remember that compilation and linking are two completely separate and independent phases for C programs. The compiler is saying "if (and only if) you link and execute this code, foo will always be non-zero". However, you then go on to attempt to link that object file into an executable without supplying a definition for foo (only a declaration). That's illegal, so linking fails. foo isn't NULL or not-NULL, it doesn't have a value.

Not tested because I'm writing this from my smartphone, I guess foo may become zero.
For example, linking with this NASM code will make foo zero if my thought is correct.
bits 32
absolute 0
global foo
global _foo
foo:
_foo:
UPDATE:
I tested this code with GCC 4.8.1 and NASM 2.11.08 and got output
address is: 00000000
indeed
Also, although this code is differ from the original, this code for xv6 on Ubuntu, GCC 4.6.3 ran on Vagrant VM
#include "types.h"
#include "user.h"
void foo(double a) {
(void)a;
}
int main(void) {
printf(1, "address is : %p\n", (void*)*foo);
if (foo)
printf(1, "indeed\n");
else
printf(1, "not\n");
exit();
}
emitted output
address is : 0
indeed
(remove -Werror from the CFLAGS in xv6 Makefile, or you will get a compile error)
Also note that *foo becomes address of function foo because foo as operand of operator * is converted to a pointer to function foo, then dereferenced with * and becomes function foo, then converted to a pointer again for function parameter.

As others have pointed out the GCC assumes 'foo' will never be zero. If you have function 'foo' then it will have some address, if function foo is not defined then your program simply will not link and there will be no executable to run. If you are linking to 'dll' or 'so' file that had function 'foo' at the time of linking, but later at the time of execution it was removed then your executable will not load. So GCC safely assumes that 'foo' will never be zero.
However, it is correct in 99.(99)% of the cases. As MikeCat pointed out the function foo could be located at address zero. This will never happen for user space executable or even the kernel code does not resort to such tricks. The only practical programs that do play with putting objects at absolute addresses are boot loaders and other things that run directly on the hardware.
They rely on linker scripts to place objects at correct addresses. Here is an example of code that does it technically yes, but this is very sick thing to do. The only C program I know that needs to resort to such tricks is boot loader code https://github.com/trini/u-boot/blob/master/arch/arm/mach-at91/armv7/u-boot-spl.lds
But, even if boot loaders can do such thing, GCC still might be correct. Most of the CPUs or OSes reserve address '0' for some special purposes. Most of the time the interrupt vector table is located there, in other cases OS or Architecture itself reserves address 0 for null pointer so any attempt to access it will result in exception. I believe x86 protected mode, which is the mode in which all popular OSes (Linux, Windows) run user programs, is one of such architectures.
So GCC in your case GCC is probably correct at optimizing away the if statement, because you are using x86 version of gcc. If you ever find the architecture where address 0 is not reserved, it will most likely have a version of GCC that does not perform this type of optimization.

Related

Is there a safe way to specify the value of an object may be uninitialized because it is never used?

Disclaimer: The following is a purely academic question; I keep this code at least 100 m away from any production system. The problem posed here is something that cannot be measured in any “real life” case.
Consider the following code (godbolt link):
#include <stdlib.h>
typedef int (*func_t)(int *ptr); // functions must conform to this interface
extern int uses_the_ptr(int *ptr);
extern int doesnt_use_the_ptr(int *ptr);
int foo() {
// actual selection is complex, there are multiple functions,
// but I know `func` will point to a function that doesn't use the argument
func_t func = doesnt_use_the_ptr;
int *unused_ptr_arg = NULL; // I pay a zeroing (e.g. `xor reg reg`) in every compiler
int *unused_ptr_arg; // UB, gcc zeroes (thanks for saving me from myself, gcc), clang doesn't
int *unused_ptr_arg __attribute__((__unused__)); // Neither zeroing, nor UB, this is what I want
return (*func)(unused_ptr_arg);
}
The compiler has no reasonable way to know that unused_ptr_arg is unneeded (and so the zeroing is wasted time), but I do, so I want to inform the compiler that unused_ptr_arg may have any value, such as whatever happens to be in the register that would be used for passing it to func.
Is there a way to do this? I know I’m way outside the standard, so I’ll be fine with compiler-specific extensions (especially for gcc & clang).
Using GCC/Clang `asm` Construct
In GCC and Clang, and other compilers that support GCC’s extended assembly syntax, you can do this:
int *unused_ptr_arg;
__asm__("" : "=x" (unused_ptr_arg));
return (*func)(unused_ptr_arg);
That __asm__ construct says “Here is some assembly code to insert into the program at this point. It writes a result to unused_ptr_arg in whatever location you choose for it.” (The x constraint means the compiler may choose memory, a processor register, or anything else the machine supports.) But the actual assembly code is empty (""). So no assembly code is generated, but the compiler believes that unused_ptr_arg has been initialized. In Clang 6.0.0 and GCC 7.3 (latest versions currently at Compiler Explorer) for x86-64, this generates a jmp with no xor.
Using Standard C
Consider this:
int *unused_ptr_arg;
(void) &unused_ptr_arg;
return (*func)(unused_ptr_arg);
The purpose of (void) &unused_ptr_arg; is to take the address of unused_ptr_arg, even though the address is not used. This disables the rule in C 2011 [N1570] 6.3.2.1 2 that says behavior is undefined if a program uses the value of an uninitialized object of automatic storage duration that could have been declared with register. Because its address is taken, it could not have been declared with register, and therefore using the value is no longer undefined behavior according to this rule.
In consequence, the object has an indeterminate value. Then there is an issue of whether pointers may have a trap representation. If pointers do not have trap representations in the C implementation being used, then no trap will occur due to merely referring to the value, as when passing it as an argument.
The result with Clang 6.0.0 at Compiler Explorer is a jmp instruction with no setting of the parameter register, even if -Wall -Werror is added to the compiler options. In contrast, if the (void) line is removed, a compiler error results.
int *unused_ptr_arg = NULL;
This is what you should be doing. You don't pay for anything. Zeroing an int is a no-op. Ok technically it's not, but practically it is. You will never ever ever see the time of this operation in your program. And I don't mean that it's so small that you won't notice it. I mean that it's so small that so many other factors and operations that are order of magnitude longer will "swallow" it.
This is not actually possible across all architectures for a very good reason.
A call to a function may need to spill its arguments to the stack, and in IA64, spilling uninitialized registers to the stack can crash because the previous contents of the register was a speculative load that loaded an address that wasn't mapped.
To prevent the possibility of zero-ing with each run of int foo(), simply make unused_ptr_arg static.
int foo() {
func_t func = doesnt_use_the_ptr;
static int *unused_ptr_arg;
return (*func)(unused_ptr_arg);
}

Warning about implicitly defined function, works in clang, fails in GCC 5.4

I have one participant in a basic C course who created a mystery. The task is to write a function double square_to(double *) which just squares the number in-place and also returns the result.
Stripped down, the code looks like this:
main.c:
#include "mymath.h"
#include <stdio.h>
int main() {
double t = 5;
double *p;
double m;
p = &t;
m = square_to(p);
printf("%f\n%f\n", t, m);
return 0;
}
mymath.h:
#ifndef MYMATH_H
#define MYMATH_H
double square_to(double *p);
#endif
mymath.c:
#include "mymath.h"
#include <stdio.h>
double square_to(double *p) {
*p = (*p) * (*p);
return *p;
}
Compile flags are:
-Wall -pedantic -o day5 main.c mymath.c
I do not see anything wrong with it. It also works just fine with Clang and GCC on his Ubuntu and my Fedora. The program outputs 25.000 twice in each case.
The part where it gets strange is when his original files are used. There are a couple of other functions from other parts of the course in the mymath.c. Those function compile apparently. In the main.c, his GCC 5.4 warns about the implicit declaration of square_to. When running the program, it outputs 25.000 and some random number which appears to be an integer. Running it again outputs a different number. Therefore this is no casting problem, it is a real undefined behavior.
Compiling the same code with Clang (just exchanging gcc with clang in the command line, -Wall and -Wpedantic active), it works just fine, giving 25.000 in every case. This is a red flag as standard compliant code should compile just fine with both compilers. On my Fedora installation it works with GCC 6.2.1.
The full code is in this gist.
I have no idea why the full code changes the behavior of the square_to function. What is wrong with the full program?
Update
Before I left the tutorial I asked the student to download the code in the gist and compile it. He did so with gcc -Wall -pedantic main.c mymath.c and it compiled without errors. Running it gave 25.000 25.000 as it should be.
So the problem is nowhere in any code that I have. There has to be something very strange in the directory that he works from. I have double checked the path in the editor and the terminal, I also did a less main.c and it was the correct one.
There is no way to answer this question now as I don't have anything to reproduce the problem. The participant also had various other similar problems. See those here:
There it can be seen that a function somehow is called with double * although there are only int in the functions. Then gcc points to a declaration which supposedly conflicts and it is the very same prototype.
I'm for closing this question because nothing more can be learned without having access to the particular environment that the participant had. He just said that he would start in a new directory tomorrow. If it still persists, I will collect the whole directory.
From: Implicit function declarations sometimes work in C?
If the expression that precedes the parenthesized argument list in a
function call consists solely of an identifier, and if no declaration
is visible for this identifier, the identifier is implicitly declared
exactly as if, in the innermost block containing the function call,
the declaration
extern int identifier();
appeared.
Since main.c thinks the function should return an int, that is what you get (later cast to a double in the assignment).
On Intel and AMD processors running Linux, integers are returned in rax and doubles are returned in the floating point registers. So my guess is that in this case, the integer you are seeing is whatever happened to be in the rax register when the function returned.
But this is just a guess, undefined behaviour is undefined.
Since there seems to be some confusion: If you get a warning about implicit declarations, that means that GCC didn't see any valid declarations before the usage. Either that, or your compiler is broken.
Properly declaring int-returning functions in C<99 is optional but highly recommended.

Why does declaring main as an array compile?

I saw a snippet of code on CodeGolf that's intended as a compiler bomb, where main is declared as a huge array. I tried the following (non-bomb) version:
int main[1] = { 0 };
It seems to compile fine under Clang and with only a warning under GCC:
warning: 'main' is usually a function [-Wmain]
The resulting binary is, of course, garbage.
But why does it compile at all? Is it even allowed by the C specification? The section that I think is relevant says:
5.1.2.2.1 Program startup
The function called at program startup is named main. The implementation declares no prototype for this function. It shall be defined with a return type of int and with no parameters [...] or with two parameters [...] or in some other implementation-defined manner.
Does "some other implementation-defined manner" include a global array? (It seems to me that the spec still refers to a function.)
If not, is it a compiler extension? Or a feature of the toolchains, that serves some other purpose and they decided to make it available through the frontend?
It's because C allows for "non-hosted" or freestanding environment which doesn't require the main function. This means that the name main is freed for other uses. This is why the language as such allows for such declarations. Most compilers are designed to support both (the difference is mostly how linking is done) and therefore they don't disallow constructs that would be illegal in hosted environment.
The section you refers to in the standard refers to hosted environment, the corresponding for freestanding is:
in a freestanding environment (in which C program execution may take place without any
benefit of an operating system), the name and type of the function called at program
startup are implementation-defined. Any library facilities available to a freestanding
program, other than the minimal set required by clause 4, are implementation-defined.
If you then link it as usual it will go bad since the linker normally has little knowledge about the nature of the symbols (what type it has or even if it's a function or variable). In this case the linker will happily resolve calls to main to the variable named main. If the symbol is not found it will result in link error.
If you're linking it as usual you're basically trying to use the compiler in hosted operation and then not defining main as you're supposed to means undefined behavior as per appendix J.2:
the behavior is undefined in the following circumstances:
...
program in a hosted environment does not define a function named
main
using one
of the specified forms (5.1.2.2.1)
The purpose of the freestanding possibility is to be able to use C in environments where (for example) standard libraries or CRT initialization is not given. This means that the code that is run before main is called (that's the CRT initialization that initializes the C runtime) might not provided and you would be expected to provide that yourself (and you may decide to have a main or may decide not to).
If you are interested how to create program in main array: https://jroweboy.github.io/c/asm/2015/01/26/when-is-main-not-a-function.html. The example source there just contains a char (and later int) array called main which is filled with machine instructions.
The main steps and problems were:
Obtain the machine instructions of a main function from a gdb memory dump and copy it into the array
Tag the data in main[] executable by declaring it const (data is apparently either writable or executable)
Last detail: Change an address for actual string data.
The resulting C code is just
const int main[] = {
-443987883, 440, 113408, -1922629632,
4149, 899584, 84869120, 15544,
266023168, 1818576901, 1461743468, 1684828783,
-1017312735
};
but results in an executable program on a 64 bit PC:
$ gcc -Wall final_array.c -o sixth
final_array.c:1:11: warning: ‘main’ is usually a function [-Wmain]
const int main[] = {
^
$ ./sixth
Hello World!
The problem is that main is not a reserved identifier. The C standard only says that in hosted systems there is usually a function called main. But nothing in the standard prevents you from abusing the same identifier for other sinister purposes.
GCC gives you a smug warning "main is usually a function", hinting that the use of the identifier main for other unrelated purposes isn't a brilliant idea.
Silly example:
#include <stdio.h>
int main (void)
{
int main = 5;
main:
printf("%d\n", main);
main--;
if(main)
{
goto main;
}
else
{
int main (void);
main();
}
}
This program will repeatedly print the numbers 5,4,3,2,1 until it gets a stack overflow and crashes (don't try this at home). Unfortunately, the above program is a strictly conforming C program and the compiler can't stop you from writing it.
main is - after compiling - just another symbol in an object file like many others (global functions, global variables, etc).
The linker will link the symbol main regardless of its type. Indeed, the linker cannot see the type of the symbol at all (he can see, that it isn't in the .text-section however, but he doesn't care ;))
Using gcc, the standard entry point is _start, which in turn calls main() after preparing the runtime environment. So it will jump to the address of the integer array, which usually will result in a bad instruction, segfault or some other bad behaviour.
This all of course has nothing to do with the C-standard.
It only compiles because you don't use the proper options (and works because linkers sometimes only care for the names of symbols, not their type).
$ gcc -std=c89 -pedantic -Wall x.c
x.c:1:5: warning: ISO C forbids zero-size array ‘main’ [-Wpedantic]
int main[0];
^
x.c:1:5: warning: ‘main’ is usually a function [-Wmain]
const int main[1] = { 0xc3c3c3c3 };
This compiles and executes on x86_64... does nothing just return :D

Is it safe to use const external value as non-const extern value?

Please see next codes and result:
foo.c:
const int extern_const = 1;
main.c
#include <stdio.h>
extern int extern_const;
int main(void)
{
printf("before: %d\n", extern_const);
extern_const = 2;
printf("after : %d\n", extern_const);
return 0;
}
compile and result:
$ gcc -shared -fpic foo.c -o libfoo.so
$ gcc main.c -L. -lfoo -o test
$ LD_LIBRARY_PATH=`pwd` ./test
before: 1
after : 2
I declared a const int variable extern_const and it resides in a shared library libfoo.so.
In main.c, I declared the extern_const as just extern int not extern const int and changed the value from 1 to 2. Is this safe and effective?
The execution result shows that the substitution works anyway. I've heard that overwriting const value caucses undefined behavior and in fact, when I compiled foo.c and main.c at once (without creating shared library), the program actually ended with segmentation fault before the second printf.
What I want to know is next things:
Is it safe to change the value of any const variable in external library, generally?
If not, is it safe for GCC/GNU toolkits?
If both of 1. and 2. are wrong, did I get a lucky case of undefined behaviors?
If 1. or 2. is right, what does make such difference for the cases with/without library?
Modifying constant objects is Undefined Behavior. Anything may happen.
In your case, you might have become unlucky because GCC does not yet pool all constant variables and literals, it did not put it into a read-only section (Define more of them and it might happen), and your main() is the first and last code accessing that external constant object (though under false flag as non-const).
6.7.3 Type qualifiers §6
If an attempt is made to modify an object defined with a const-qualified type through use
of an lvalue with non-const-qualified type, the behavior is undefined. If an attempt is
made to refer to an object defined with a volatile-qualified type through use of an lvalue
with non-volatile-qualified type, the behavior is undefined.133)
No.
No.
Yes.
Basically, it's up to the compiler/toolchain/operating system to provide protection for const variables. Some combinations of those go out of their way to make sure that what's supposed to be read-only will be read-only even if it means allocating a whole page (several kB) just to store one variable. Others make different trade-offs and will not waste a lot of space just to protect one variable and trust the programmer to not do crazy things like this.
The other answers are absolutely correct about this being Undefined Behavior and thus something you should not do. The reason your broken code is "working" for you is a side effect of the way dynamic linking works, namely copy relocations. Basically, what happens is that, since the main executable is not position-independent, it has to have addresses for all data objects it accesses directly hard-coded as immediates in the instructions which perform the accesses. Thus, the linker allocates writable space (since the value, which comes from a shared library at runtime, can't be known) in the main program's writable data segment and includes an instruction to the dynamic linker for it to copy the value from the shared library to the main program's data when performing relocations at startup. Any references from the shared library are then patched up to point to the new copy in the main program's data.
If you want to see your code fail, try to compile your main program as a position-independent executable:
$ gcc -fPIE -pie main.c -L. -lfoo -o test
and see what happens. Note that PIE is default on many hardened systems. Likewise, the ABIs for some cpu architectures (MIPS is one, if I'm not mistaken) never need copy relocations, and thus your program should crash even without PIE on such archs.
In foo.c, add:
int y;
void f(void) {
y = extern_const;
}
Compile foo.c with optimization to assembly. Read the assembly for f(). If your compiler is like mine, you should see that f() has been optimized to the equivalent of y = 1;
This means that according to my compiler (and probably yours), what you did is not okay, and you deserve any strange demon that may fly out of your nose as a result of declaring extern_const differently in different files.
Your sample is violating the one definition rule. This is undefined behavior, so anything can happen during compile phase and your program is not guaranteed to do anything useful, see also here.
From C standard
J.2 Undefined behavior
Two declarations of the same object or function specify types that are not compatible
(6.2.7).
also
6.2.7 Compatible type and composite type
2 All declarations that refer to the same object or function shall have compatible type;
otherwise, the behavior is undefined.
also
6.7.3 Type qualifiers
10 For two qualified types to be compatible, both shall have the identically qualified version
of a compatible type; the order of type qualifiers within a list of specifiers or qualifiers
does not affect the specified type.
Besides, as others stated, it is also undefined behavior to modify a const object:
6.7.3 Type qualifiers
6 If an attempt is made to modify an object defined with a const-qualified type through use
of an lvalue with non-const-qualified type, the behavior is undefined. If an attempt is
made to refer to an object defined with a volatile-qualified type through use of an lvalue
with non-volatile-qualified type, the behavior is undefined.133)

multiple function declarations in different compilation units

zero.c:
int sq();
one.c:
int sq(int i) { return i*i; }
two.c:
int sq(int i, int j);
main.c:
int main() {
printf("%d\n",sq());
printf("%d\n",sq(2));
printf("%d\n",sq(2,3));
}
then I compile each file individually and gcc zero.o one.o two.o main.o -o main
./main gives
1
4
4
I'm a little confused as how this would work successfully. what really happens when I call sq() or sq(2) or sq(2,3)
If you want to know what really happens, have gcc output the assembly for main.o and take a look. I think you will find that when you call sq() the arguments are loaded into the base registers on your machine, and then sq(int i) will do a multiply instruction on the first register. If you pass additional arguments they won't have any affect, and if you don't pass any arguments it will just work on whatever value was previously loaded into that register.
zero.c & two.c do not have any function definition. It's only prototype declaration. Thus, it will not create any assembly code having function definition. (hint: compile with gcc -s flag to verify.)
Only two.c has function definition. Thus two.s will have a function sq, which takes the first argument (generally passed on the stack or the first register of the processor, like eax on intel or r0 in arm) & returns its square.
Since you have not given any prototype in main.c, the compiler (.c -> .s) will not complain. It may probably treat it as int sq(...), but I am not sure about it.
Thus, for 3 different inputs:
sq(), sq(2), sq(2,3) will all call to the same function, which is declared in two.c.
Now, the outputs for sq(2) & sq(2,3) are obvious - return square of the first argument passed. The output for sq() will depend on what is on the stack/eax/r0 as seen in sq's stack. Seems that it was 1. hint: run under gdb to verify.
According to the C spec, your code invokes undefined behavior in multiple ways (and possibly more):
Two declarations of the same function in the same scope use different return or argument types/counts
For a call to a function without a function prototype in scope, the number of arguments does not equal the number of parameters
Since this is undefined behavior, you have no way to predict what will happen. The result doesn't even necessarily have to be consistent. If you aren't seeing compile-time warnings/errors for this code, then you need to turn up your compiler's warning level.
Adding a prototype in main.c will probably resolve the compiler's warnings with this code. The linker may still have issues, though, because you have multiple functions with the same name in the same scope and it's not exactly clear which one you want the code to use.
So, I wrote an answer earlier based on what I read in the post. Which was wrong. Here's the correct answer.
zero.c doesn't generate any code. two.c doesn't generate any code.
main.c and one.c are the only files that actually generate code.
Calling a function with one argument, sq(int i) in one.c, with no arguments is undefined behaviour (so "anything can happen", including something resembling what you expect in some cases). Calling with two arguments is also undefined behaviour - again, it will not necessarily "go wrong" when you do this, but you it is not guaranteed to work (or do what you expect) - it could for example just as well return 9 because it puts arguments into registers from the last to first.

Resources