I have two C source files:
/* w1.c */
#include <stdio.h>
__attribute__((weak)) void fw(void) { printf("FW1\n"); }
int main(int argc, char **argv) {
(void)argc; (void)argv;
fw();
return 0;
}
/* w2.c */
#include <stdio.h>
void fw(void) { printf("FW2\n"); }
If I compile and run them with gcc, FW1 or FW2 is printed, depending on whether w2.c is used:
$ gcc -s -O2 -o prog1 w1.c
$ ./prog1
FW1
$ gcc -s -O2 -o prog12 w1.c w2.c
$ ./prog12
FW2
This works like this because in w1.c the symbol fw is weak, so it gets ignored iff another file (i.e. w2.c) defines a non-weak symbol with the same name.
Thuse the mere presence of w2.c can modify the behavior of w1.c, through a weak symbol defined in w1.c.
Is there something likes this with the OpenWatcom C compiler? What is the syntax insteda of __attribute__((weak))? Simply omitting __attribute__((weak)) won't work, because linking will fail because of the duplicate symbol. I want the compiler commands and programs ./prog1 and ./prog12) above work with owcc instead of gcc, and possibly adding some flags.
I need this because I want to implement a cleanup mechanism (calling function fw) which should be replaced automatically with something more sophisticated if additional source (or object) files are also used. Thus I'd need two implementations of the cleanup mechanism (simple fw in w1.c, sophisticated fw in w2.c).
I also considered using common symbols (i.e. those which can be defined in multiple files as long as at most one file has a nonzero definition). Here are my source files, with the common symbol being fwptr:
/* w1o.c */
#include <stdio.h>
void (*fwptr)(void);
static void fw1(void) { printf("FW1\n"); }
int main(int argc, char **argv) {
(void)argc; (void)argv;
(fwptr ? fwptr : fw1)();
return 0;
}
/* w2o.c */
#include <stdio.h>
static void fw2(void) { printf("FW2\n"); }
void (*fwptr)(void) = fw2;
As expected, if I compile and run them with gcc, FW1 or FW2 is printed, depending on whether w2o.c is used:
$ gcc -s -O2 -o prog1 w1o.c
$ ./prog1o
FW1
$ gcc -s -O2 -o prog12 w1o.c w2o.c
$ ./prog12o
FW2
However, OpenWatcom doesn't allow multiple definitions of a symbol:
$ owcc -s -O2 -o prog1o w1o.c
$ ./prog1o
FW1
$ owcc -s -O2 -o prog12o w1o.c w2o.c
Warning! W1027: file w2o.o(/tmp/w2o.c): redefinition of _fwptr ignored
$ ./prog12o
FW1
Here is what's going on with GCC:
/* symbol.c */
int sym0 = 0;
int sym1 = 1;
int sym2;
extern int sym3;
int func() { return sym0 + sym1 + sym2 + sym3; }
$ gcc -fno-pic -m32 -Os -c -o symbol.o symbol.c
$ readelf -a symbol.o
...
Num: Value Size Type Bind Vis Ndx Name
... 8: 00000000 28 FUNC GLOBAL DEFAULT 1 func
9: 00000000 4 OBJECT GLOBAL DEFAULT 3 sym1
10: 00000000 4 OBJECT GLOBAL DEFAULT 4 sym0
11: 00000004 4 OBJECT GLOBAL DEFAULT COM sym2
12: 00000000 0 NOTYPE GLOBAL DEFAULT UND sym3
As seen above, sym3 (the common one) is different from the others, and at link time we use this to provide an alternative definition.
When compiling the same code with OpenWatcom owcc, and dumping the .obj file with OpenWatcom dmpobj, I get:
- sym0: EXTDEF (type 0), PUBDEF386 (type 0), LEDATA386: 4 bytes in segment _DATA
- sym1: EXTDEF (type 0), PUBDEF386 (type 0), LEDATA386: 4 bytes in segment _DATA
- sym2: EXTDEF (type 0), PUBDEF386 (type 0), 4 bytes in segment _BSS
- sym3: EXTDEF (type 0), no PUBDEF386, no definition in _DATA or _BSS
I have asked a new question about common symbols with OpenWatcom: Support for common symbols in OpenWatcom C compiler
A few ways to get a weak symbol without the knowledge of
owcc....sfile.ofileJust so we're on the same page. We need a way to identify which symbols we want to be weak.
We can scan the source files, looking for a "directive" that will be ignored by
owcc.Special comments are often good for this:
Or, we could use a "phony" macro:
Or, we could have a
weak.txtfile that lists the weak symbols.Either way, now the converter knows what symbols to change ...
Change the generated assembler to add a
.weakdirectiveI pulled the watcom source from the github repo. AFAICT, the watcom assembler is relatively simple.
What assembler does
owccuse? Can it use the GNUasthat comes withgccet. al.?I'm guessing that it can because weak symbols are [generally] an ELF feature. I'm also assuming that the native assembler for
owccdoesn't support them.If we can use GNU
as, there is a way. Take a look at the.sfrom the build of yourw1.cundergcc.Normally, we get:
But, notice that we just get:
We can tell
owccto generate the assembler source. Then, edit it and change the directives for any symbols that we want to be weak. Then, build from the.sThese steps can be hidden under a script for convenience.
Use utility to change the symbol binding in the ELF relocatable
Again, I assume we have an ELF relocatable.
Here is
readelf -sfrom your source without the__attribute__((weak)):Notice that
fwhas a binding ofGLOBALHere is Here is
readelf -sfrom your source with the__attribute__((weak)):Notice that the binding for
fwisWEAKThere are various ELF utilities that allow manipulation of
.ofiles. There ought to be one that can change symbol attributes to change the binding toWEAK.Or, look at
man 5 elfandlibelf. We can write our own utility. There should be many examples on the web that can be [easily] modified.Here's a modified version of the source (e.g.
com.c):Here are some commands I ran:
I then did a diff of hex dumps of
st.oandwk.o:So, the difference between
GLOBALandWEAKis a single bit/byte. That is, global is 0x12 and weak is 0x22.We can make sense of this by looking at a portion of
/usr/include/elf.h: