Support for common symbols in OpenWatcom C compiler

90 views Asked by At

TL;DR How to use common (communal) symbols from C source code compiled with the OpenWatcom C compiler?

FYI Some introductions on common symbols (mostly GCC, so it doesn't answer my question about OpenWatcom):

I'm trying to use 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 -fcommon -s -O2 -o prog1 w1o.c
$ ./prog1o
FW1
$ gcc -fcommon -s -O2 -o prog12 w1o.c w2o.c
$ ./prog12o
FW2

Please note that this is non-standard behavior of GCC. The C standard says that both file-level mentions (one in w1o.c, one in w2o.c) are definitions of fwptr, and it's an error to have multiple definitions of the same symbol in a program. To make GCC follow the standard, use gcc -fno-common (this will make it fail to compile). However, I need the opposite: in my program I need common symbols, so I'm using gcc -fcommon.

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

Looking at the dmpobj output of w1o.obj, owcc has put fwptr to segment _BSS using a PUBDEF386 record.

To get a common symbol, we'd need a COMDEF record (see OMF intro). In fact, If I write w1o.c in assembly, and specify COMM _fwptr:DWORD:1, wasm will create a COMDEF record, and the program works as expected:

$ wasm -q -fo=.obj w1oc.asm
$ owcc -s -O2 -o prog1o w1oc.obj clib3r.lib
$ ./prog1o
FW1
$ owcc -s -O2 -o prog12o w1oc.obj w2o.c clib3r.lib
$ ./prog12o
FW2

FYI here is the file w1oc.asm I used (containing COMM _fwptr:DWORD:1):

.386p
.model flat
PUBLIC main_
COMM _fwptr:DWORD:1
EXTRN printf_:BYTE, __argc:BYTE, _cstart_:BYTE
DGROUP GROUP CONST
_TEXT SEGMENT PARA PUBLIC USE32 'CODE'
ASSUME CS:_TEXT, DS:DGROUP, SS:DGROUP
fw1_:
push offset FLAT:L$2
call near ptr FLAT:printf_
add esp,4
ret
mov eax,eax
main_:
mov eax,dword ptr FLAT:_fwptr
test eax,eax
je L$1
call eax
xor eax,eax
ret
L$1:
mov eax,offset FLAT:fw1_
call eax
xor eax,eax
ret
_TEXT ENDS
CONST SEGMENT DWORD PUBLIC USE32 'DATA'
L$2: DB 'FW1', 10, 0
CONST ENDS
END

It looks like wasm and wlink support common symbols, but the OpenWatcom C compiler (wcc386) doesn't. Or am I using it incorrectly?

2

There are 2 answers

0
pts On

As a workaround, I can use extern in my C source file w1o.c, and create custom .asm file with the comm _fwptr:DWORD:1. This works:

/* w1o.c */
#include <stdio.h>
extern void (*fwptr)(void);  /* See w1sc.asm for a COMM definition. */       
static void fw1(void) { printf("FW1\n"); }
int main(int argc, char **argv) {
  (void)argc; (void)argv;
  (fwptr ? fwptr : fw1)();
  return 0;
}
; w1sc.asm
COMM _fwptr:DWORD:1
/* w2o.c */
#include <stdio.h>
static void fw2(void) { printf("FW2\n"); }
void (*fwptr)(void) = fw2;

It works:

$ wasm -q -mf -fo=.obj w1sc.asm
$ owcc -s -O2 -o prog1o  w1o.c w1sc.obj
$ ./prog1o
FW1
$ owcc -s -O2 -o prog12o w1o.c w1sc.obj w2o.c
$./prog12o 
FW2

However, this is still a workaround, I'd like to do everything in C source code, if possible.

0
mevets On

Watcom is clearly wrong here. I am surprised, we used them as a compiler vendor for a while decades ago. They were certainly more interested in the micro-optimisation of the day; but weren't generally this bad.

Your expectations were correct. This implementation is not.

IIRC, the actual watcom crew ran away when sybase got control of them. It isn't so much open source as abandon-ware.