I'm learning symbol resolution in linking:
"Rule 2: given a strong symbol and multi weak symbols, choose the strong one."
(from the book CSAPP)
I tried the following example, but got multiple definition error. It seems not following the Rule2 above.
strong.c
int x = 20;
weak.c
#include <stdio.h>
int x;
int main() {
printf("%c\n", x);
return 0;
}
$ gcc --version
gcc (Ubuntu 12.2.0-3ubuntu1) 12.2.0
Copyright (C) 2022 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$ gcc -c strong.c
$ gcc -c weak.c
$ gcc weak.o strong.o
/usr/bin/ld: strong.o:(.data+0x0): multiple definition of `x'; weak.o:(.bss+0x0): first defined here
collect2: error: ld returned 1 exit status
$ nm strong.o
0000000000000000 D x
$ nm weak.o
0000000000000000 T main
U printf
0000000000000000 B x
I used nm to check the symbols: x in weak.o is B(uninitialized global, weak symbol); x in strong.o is D(initialized global, strong symbol).
Related
write file, one with the weak file and another with the normal symbol. Below are the contents of the files: where weak_ex() is the definition declared with weak symbol.
bgl-ads-2997:~/weak_symbol > cat m.c
#include <stdio.h>
#include "weak.h"
void main (){
printf ("weak = %d\n", weak_ex());
}
bgl-ads-2997:~/weak_symbol > cat weak.c
int __attribute__((weak)) weak_ex()
{
return 1;
}
bgl-ads-2997:~/weak_symbol > cat strong.c
int weak_ex()
{
return 10;
}
bgl-ads-2997:~/weak_symbol > cat weak.h
int weak_ex();
So with the above definitions weak symbol will return 1, and strong will return 10.
Then I compiled them as below:
gcc -g -c -o m.o m.c
gcc -g -c -o weak.o weak.c
gcc -g -c -o strong.o strong.c
gcc -shared -fpic -o libstrong.so strong.o
gcc -shared -fpic -o libweak.so weak.o
then linked them and executed as below:
using only the object file:
bgl-ads-2997:~/weak_symbol > gcc m.o weak.o strong.o -L`pwd` -Wl,-R`pwd` -o shared
bgl-ads-2997:~/weak_symbol > ./shared
weak = 10
In this case weak symbol is masked, and strong signal is taken as expected.
link with weak symbol in object file and strong symbol in shared object.
bgl-ads-2997:~/weak_symbol > gcc -lstrong m.o weak.o -L`pwd` -Wl,-R`pwd` -o shared
bgl-ads-2997:~/weak_symbol > ./shared
weak = 1
In this case weak symbol is not getting replaced.
Seems symbol are resolved in compilation time and not as part of execution, not not considering the shared Expecting this also to print 10 but not so, So is this the expected behaviour? is there a way to get the weak symbol replaced from the shared library?
I know that on ELF platforms, __attribute__((constructor)) uses the .ctors ELF section. Now I realized that the function attribute works with GCC on MinGW as well and I'm wondering how it is implemented.
For MinGW targets (and other COFF targets, like Cygwin) compiler just emits each constructor function address in .ctors COFF section:
$ cat c1.c
void c1() {
}
$ x86_64-w64-mingw32-gcc -c c1.c
$ objdump -x c1.o | grep ctors
# nothing
$ cat c1.c
__attribute__((constructor)) void c1() {
}
$ x86_64-w64-mingw32-gcc -c c1.c
$ objdump -x c1.o | grep ctors
5 .ctors 00000008 0000000000000000 0000000000000000 00000150 2**3
GNU ld linker (for MinGW targets) is then configured (via its default linker script) to combine these sections into regular .text section with __CTOR_LIST__ symbol pointing to the first item, and having the last item terminated with zero. (Probably .rdata section would be clearer since these are just addresses of functions, not CPU instructions, but for some reason .text is used. In fact LLVM LLD linker targeting MinGW places them in .rdata.)
LD linker:
$ x86_64-w64-mingw32-ld --verbose
...
.text ... {
...
__CTOR_LIST__ = .;
LONG (-1); LONG (-1);
KEEP (*(.ctors));
KEEP (*(.ctor));
KEEP (*(SORT_BY_NAME(.ctors.*)));
LONG (0); LONG (0);
...
...
}
Then it is up to C runtime library to run these constructors during initialization, by using this __CTOR_LIST__ symbol.
From mingw-w64 C runtime:
extern func_ptr __CTOR_LIST__[];
void __do_global_ctors (void)
{
// finds the last (zero terminated) item
...
// then runs from last to first:
for (i = nptrs; i >= 1; i--)
{
__CTOR_LIST__[i] ();
}
...
}
(also, it is very similar in Cygwin runtime)
This can be also seen in the debugger:
$ echo $MSYSTEM
MINGW64
$ cat c11.c
#include <stdio.h>
__attribute__((constructor))
void i1() {
puts("i 1");
}
int main() {
puts("main");
return 0;
}
$ gcc c11.c -o c11
$ gdb ./c11.exe
(gdb) b i1
(gdb) r
(gdb) bt
#0 0x00007ff603591548 in i1 ()
#1 0x00007ff6035915f2 in __do_global_ctors () at C:/_/M/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/gccmain.c:44
#2 0x00007ff60359164f in __main () at C:/_/M/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/gccmain.c:58
#3 0x00007ff60359139b in __tmainCRTStartup () at C:/_/M/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/crtexe.c:313
#4 0x00007ff6035914f6 in mainCRTStartup () at C:/_/M/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/crtexe.c:202
(gdb)
Note that in some environments (not MinGW and not Linux) it is instead the responsibility of GCC (its compiler runtime libgcc, more specifically its static part called crtbegin.o and crtend.o) and not C runtime to run these constructors.
Also, for comparison, on ELF targets (like Linux) GCC compiler used similar mechanism like the one described above for MinGW (it used ELF .ctors sections, although the rest was a bit different), but since GCC 4.7 (released in 2012) it uses slightly different mechanism (ELF .init_array section).
I am trying to create a static library and link it on MacOS X (several versions):
File foo.c:
char foo[111];
File bar.c:
#include <string.h>
extern char foo[];
int bar(char *src) {
strcpy(foo, src);
return strlen(foo);
}
Create a library:
$ cc -c foo.c bar.c
$ ar r libfoobar.a foo.o bar.o
ar: creating archive libfoobar.a
$ ranlib libfoobar.a
$ nm libfoobar.a
libfoobar.a(foo.o):
000000000000006f C _foo
libfoobar.a(bar.o):
U ___strcpy_chk
0000000000000000 T _bar
U _foo
U _strlen
Create a small test program:
File main.c:
#include <stdio.h>
int bar(char *);
int main(void) {
printf("foobarbar = %i\n", bar("123"));
return 0;
}
Compile and link:
$ cc -c main.c
$ cc -o m main.o -L. -lfoobar
Undefined symbols for architecture x86_64:
"_foo", referenced from:
_bar in libfoobar.a(bar.o)
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Why is the symbol not found? It is defined in foo.c? Shouldn't at least ranlib create an index in the library that allows a random order of the files there?
The same code works well under Linux (gcc), and also when the symbol in foo.c is not a char array, but an int.
There is a similar question: Object files not properly added to archive on mac which has this answer:
Option 1:
ar -rs my_archive.a foo.o bar.o other_object_files.o
ranlib -c my_archive.a
Option 2:
libtool -c -static -o my_archive.a foo.o bar.o other_object_files.o
It is -c flag that makes a difference for both options on ranlib and libtool respectively:
-c
Include common symbols as definitions with respect to the table
of contents. This is seldom the intended behavior for linking
from a library, as it forces the linking of a library member
just because it uses an uninitialized global that is undefined
at that point in the linking. This option is included only
because this was the original behavior of ranlib. This option
is not the default.
In the context of a tool comparison, I do not want to be unfair to ASan if it can detect the problem in the program below:
$ cat t.c
#include <stdio.h>
int *G;
int f(void) {
int l = 1;
int res = *G;
G = &l;
return res + *G;
}
int main(void) {
int x = 2;
G = &x;
f();
printf("%d\n", f());
}
$ clang -v
clang version 3.8.0-2ubuntu4 (tags/RELEASE_380/final)
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/4.9
...
$ clang -O2 -fsanitize=address t.c
$ ./a.out
1
$ clang -fsanitize=address t.c
$ ./a.out
2
The first occurence of G the second time f is called invokes undefined behavior, because G is indeterminate at that point. In addition, G is immediately dereferenced, making this the sort of memory error that one may expect ASan to detect. It is part of ASan's specifications that it sometimes fails to detect problems of the kind it is supposed to find, but I want to know if I could have used it to find this particular problem here.
I found the option -fsanitize-address-use-after-scope here, but this option does not work in the version of Clang I am using:
$ clang -fsanitize=address t.c -fsanitize-address-use-after-scope
clang: error: unknown argument: '-fsanitize-address-use-after-scope'
Is there an ASan version that flags an error at the execution of the above program, with or without special commandline options?
You are talking about use-after-return errors here. These should be supported by ASan but disabled by default due to significantly higher memory overhead (see e.g. here for details). To enable, run with ASAN_OPTIONS=detect_stack_use_after_return=1.
Unfortunately I can't check whether it works on your particular case but if it doesn't, you should probly file a bug at ASan's tracker.
yugr has pointed me to the correct way to activate detection of the error in my test program. This functionality already exists in Clang 3.8.
For completeness, the results with Clang 3.8 are below. It is interesting that the issue is detected at the default optimization level but is not detected at -O2.
$ clang -fsanitize=address t.c -Wall
$ ASAN_OPTIONS=detect_stack_use_after_return=1 ./a.out
=================================================================
==21949==ERROR: AddressSanitizer: stack-use-after-return on address 0x7f5eeb100060 ...
READ of size 4 at 0x7f5eeb100060 thread T0
...
$ clang -O2 -fsanitize=address t.c -Wall
$ ASAN_OPTIONS=detect_stack_use_after_return=1 ./a.out
1
Your version: clang version 3.8.0-2ubuntu4 (tags/RELEASE_380/final)
Header of page: Clang 5 documentation
You have to update your clang
I am compiling the below code with "-nostdlib". My understanding was that arm-none-eabi-gcc will not use the _start in "crt0.o" but it will use the user defined _start. For this I was expecting to create a start.S file and put the _start symbol.
But if I compile the below shown code without the _start symbol defined from my side, I am not getting any warning. I was expecting "warning: cannot find entry symbol _start;"
Questions:
1) Why am I not getting the warning ? From where did GCC get the _start symbol ?
2) If gcc got the _start symbol from a file from somewhere, could you let me know how to ask GCC to use the _start from my start.S file ?
$ cat test.c
int main()
{
volatile int i=0;
i = i+1;
return 0;
}
$ cat linker.ld
MEMORY
{
ram : ORIGIN = 0x8000, LENGTH = 20K
}
SECTIONS
{
.text : { *(.text*) } > ram
.bss : { *(.bss*) } > ram
}
$ arm-none-eabi-gcc -Wall -Werror -O2 -mfpu=neon-vfpv4 -mfloat-abi=hard -march=armv7-a -mtune=cortex-a7 -nostdlib -T linker.ld test.c -o test.o
$ arm-none-eabi-gcc --version
arm-none-eabi-gcc (GNU Tools for ARM Embedded Processors) 4.9.3 20150529 >(release) [ARM/embedded-4_9-branch revision 224288]
Compile and link with arm-none-eabi-gcc -v -Wall -Werror -O2.... to understand what the compiler is doing (and which crt0 it is using; that crt0 probably has a _start calling your main, also _start might be the default entry point for your linker)
Notice that -nostdlib is related to the (lack of) C standard library; perhaps you want to compile in a freestanding environment (see this), then use -ffreestanding (and in that case main has no particular meaning, you need to define your starting function[s], and no standard C functions like malloc or printf are available except perhaps setjmp).
Read the C99 standard n1256 draft. It explains what freestanding means in ยง5.1.2.1