4 ms·
> It's legal to declare an external function symbol without specifying the parameters, so long as you actually match the types of real parameters Yes. And the
by dllthomas 10y ago
> It's legal to declare an external function symbol without specifying the parameters, so long as you actually match the types of real parameters
Yes. And the call here does not match the types of the real parameters, even before we consider the return type.
- int_19h 10y agoRight, but (again, ignoring the return type) the call doesn't actually get executed, and U.B. only happens if it does. Though I guess they could have wrapped it in if(0){...} to make this completely unambiguous.
- dllthomas 10y agoIs your claim "there is no undefined behavior because the program is not run"? While that's true (there's no behavior, much less undefined behavior), in that case I don't see why the return type deserves calling out special. This program exhibits undefined behavior iff executed, and that would be true even if the return type were void * . > I guess they could have wrapped it in if(0){...} to make this completely unambiguous. Depending on optimizations, that may mean the executable does not depend on the symbol we're trying to test the presence of. Indeed, on my local gcc that's the case, even with -O0. $ cat test.c void *memcpy(); int main() { if(TEST) { (void)memcpy(); } } $ gcc -DTEST=0 test.c $ objdump -T a.out a.out: file format elf64-x86-64 DYNAMIC SYMBOL TABLE: 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 __libc_start_main 0000000000000000 w D *UND* 0000000000000000 __gmon_start__ $ gcc -DTEST=1 test.c $ objdump -T a.out a.out: file format elf64-x86-64 DYNAMIC SYMBOL TABLE: 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 __libc_start_main 0000000000000000 w D *UND* 0000000000000000 __gmon_start__ 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.14 memcpy $ gcc -O0 -DTEST=0 test.c $ objdump -T a.out a.out: file format elf64-x86-64 DYNAMIC SYMBOL TABLE: 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 __libc_start_main 0000000000000000 w D *UND* 0000000000000000 __gmon_start__ c.f.: $ cat test.c void *blah(); int main() { if(TEST) { (void)blah(); } } $ gcc -DTEST=1 test.c /tmp/cc2Vk5mE.o: In function `main': test.c:(.text+0xa): undefined reference to `blah' collect2: error: ld returned 1 exit status $ gcc -DTEST=0 test.c $ objdump -T a.out a.out: file format elf64-x86-64 DYNAMIC SYMBOL TABLE: 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 __libc_start_main 0000000000000000 w D *UND* 0000000000000000 __gmon_start__ $ gcc -O0 -DTEST=0 test.c $ objdump -T a.out a.out: file format elf64-x86-64 DYNAMIC SYMBOL TABLE: 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 __libc_start_main 0000000000000000 w D *UND* 0000000000000000 __gmon_start__
- int_19h 10y agoI'm just trying to draw the line between U.B. that is guaranteed to happen at runtime (at which point the compiler is, technically speaking, not even obligated to provide you with a binary to run), and U.B. that will only happen if that particular code is executed, but doesn't trigger if that branch is omitted. It's probably overly pedantic, but given what modern optimizing compilers do, I'm in the "better safe than sorry" camp. You definitely have a point regarding compiler just ripping that code out. I guess the proper approach would be to do something like if(argv[0][0]), or test a volatile variable.