Subject: C Compiler bug with right shift of unsigned long Index: /usr/src/lib/ccom/c11.c Description: In the (lightly edited) words of the fellow (ragge@tethuvudet.se) who provided the fix: Quick overview: Using ">>" or ">>=" on unsigned long variables may use registers that become trashed. The result will be wrong and the binary will likely crash. Cause: The implementation of unsigned long right shifts are handled by calling the assembly routine 'ulsh' (which expects the input in r0 and r1). The compiler expects the call to be like a regular instruction and use compiler assigned registers. So, if the compiler thinks it is a good idea to have the input in registers r2/r3 the operation will fail since the subroutine has different expectations. Fix: The syntax for the call(s) are special, since input is 3 words, and do not match any other embedded call in the compiler. So, instead of trying to add that functionality to the compiler I did a simple fix: If a right shift of an unsigned long is found when reading in an expression tree from the intermediate file a flag is set. When the expression tree is evaluated the number of available registers is set to only r0/r1, therefore forcing the compiler to use them when evaluating the expression tree. An undefined behaviour bug was also fixed. See in c11.c the line: *sp++ = tnode(op, geti(), *--sp, tp); It is not defined in which order sp is incremented and decremented. Repeat-By: Compile the following test program: ----ulsh.c---- #include unsigned long d = 0x12345678; int main(int argc, char *argv[]) { unsigned char bytes[4]; int i = 0; bytes[i++] = d>>24; bytes[i++] = d>>16; bytes[i++] = d>>8; bytes[i++] = d; printf("%lx -> %x %x %x %x\n", d, bytes[0], bytes[1], bytes[2], bytes[3]); } --------- vernon.5-> cc ulsh.c vernon.6-> ./a.out 12345678 -> 0 0 78 78 Bus error (core dumped) AFTER applying the patch: cc ulsh.c vernon.53-> ./a.out 12345678 -> 12 34 56 78 Fix: In addition to fixing the bug in c1 the Makefile was revised. The use of DESTDIR in the system is inconsistent which makes the feature unusable. As programs are modified DESTDIR will be removed. Cut where indicated and save to a file (/tmp/490.patch). Then: cd / patch -p0 < /tmp/490.patch cd /usr/src/lib/ccom make c1 install -s -m 755 -o root -g wheel c1 /lib/c1 make clean This and previous updates to 2.11BSD are available at the following locations: ftp://ftp.dfupdate.se/pub/pdp11/2.11BSD https://www.tuhs.org/Archive/Distributions/UCB/2.11BSD/Patches/ ftp://ftp.2bsd.com/2.11BSD http://www.2bsd.com/2.11BSD ---------------------------cut here-------------------- *** ./usr/src/lib/ccom/c11.c.old Thu Jun 3 11:05:07 1993 --- ./usr/src/lib/ccom/c11.c Wed Apr 16 13:50:32 2025 *************** *** 1,7 **** /* ! * C compiler */ #include "c1.h" degree(t) --- 1,11 ---- /* ! * C compiler, part 2 */ + #if !defined(lint) && defined(DOSCCS) + static char sccsid[] = "@(#)c11.c 2.2 (2.11BSD) 2025/4/16"; + #endif + #include "c1.h" degree(t) *************** *** 830,838 **** char s[80]; /* big for asm() stuff & long variable names */ struct swtab *swp; long outloc; ! int lbl, cond, lbl2, lbl3; double atof(); curbase = funcbase; sp = expstack; for (;;) { --- 834,843 ---- char s[80]; /* big for asm() stuff & long variable names */ struct swtab *swp; long outloc; ! int lbl, cond, lbl2, lbl3, gotrsh; double atof(); + gotrsh = 0; curbase = funcbase; sp = expstack; for (;;) { *************** *** 980,988 **** outloc = ftell(stdout); if (op==CBRANCH) cbranch(tp, lbl, cond, 0); ! else if (op==EXPR) rcexpr(tp, efftab, 0); ! else { if (tp->t.type==LONG || tp->t.type==UNLONG) { rcexpr(tnode(RFORCE, tp->t.type, tp, TNULL), efftab, 0); printf("ashc $0,r0\n"); --- 985,998 ---- outloc = ftell(stdout); if (op==CBRANCH) cbranch(tp, lbl, cond, 0); ! else if (op==EXPR) { ! int onreg = nreg; ! if (gotrsh) ! nreg = 2; rcexpr(tp, efftab, 0); ! nreg = onreg; ! gotrsh = 0; ! } else { if (tp->t.type==LONG || tp->t.type==UNLONG) { rcexpr(tnode(RFORCE, tp->t.type, tp, TNULL), efftab, 0); printf("ashc $0,r0\n"); *************** *** 1100,1106 **** exit(1); } tp = *--sp; ! *sp++ = tnode(op, geti(), *--sp, tp); } else sp[-1] = tnode(op, geti(), sp[-1], TNULL); break; --- 1110,1121 ---- exit(1); } tp = *--sp; ! --sp; ! *sp = tnode(op, geti(), *sp, tp); ! if ((*sp)->t.type == UNLONG && ! (op == ASRSH || op == RSHIFT)) ! gotrsh = 1; ! sp++; } else sp[-1] = tnode(op, geti(), sp[-1], TNULL); break; *** ./usr/src/lib/ccom/Makefile.old Mon Oct 3 17:00:56 2022 --- ./usr/src/lib/ccom/Makefile Wed Apr 16 13:32:28 2025 *************** *** 2,10 **** LDC0FLAGS= -i LDC1FLAGS= -n - DESTDIR = - LIB=/lib - all: c0 c1 cvopt c0: c00.o c01.o c02.o c03.o c04.o c05.o ansi.o ${CC} ${LDC0FLAGS} ${CFLAGS} -o c0 c00.o c01.o c02.o c03.o c04.o \ --- 2,7 ---- *************** *** 28,41 **** ${CC} ${CFLAGS} -o cvopt cvopt.c install: c0 c1 ! -mv ${DESTDIR}${LIB}/c0 ${DESTDIR}${LIB}/oc0 ! -mv ${DESTDIR}${LIB}/c1 ${DESTDIR}${LIB}/oc1 ! install -s c0 ${DESTDIR}${LIB}/c0 ! install -s c1 ${DESTDIR}${LIB}/c1 restore: ! mv ${DESTDIR}${LIB}/oc0 ${DESTDIR}${LIB}/c0 ! mv ${DESTDIR}${LIB}/oc1 ${DESTDIR}${LIB}/c1 lint: lint -haxc -I. c0?.c > lint.c0 --- 25,38 ---- ${CC} ${CFLAGS} -o cvopt cvopt.c install: c0 c1 ! -mv /lib/c0 /lib/oc0 ! -mv /lib/c1 /lib/oc1 ! install -s -m 755 -o root -g wheel c0 /lib/c0 ! install -s -m 755 -o root -g wheel c1 /lib/c1 restore: ! mv /lib/oc0 /lib/c0 ! mv /lib/oc1 /lib/c1 lint: lint -haxc -I. c0?.c > lint.c0 *** ./VERSION.old Wed Mar 26 20:09:17 2025 --- ./VERSION Wed Apr 16 14:14:17 2025 *************** *** 1,5 **** ! Current Patch Level: 489 ! Date: March 31, 2025 2.11 BSD ============ --- 1,5 ---- ! Current Patch Level: 490 ! Date: April 16, 2025 2.11 BSD ============