Importing a bunch of pages from old websites.
[website_subgeniuskitty.com] / data / development / pdp-11 / software_development / modern_unix / gcc_bugs.md
CommitLineData
f6e94cb2
AT
1# Overview #
2
3Although GCC claims to support PDP-11 targets there are some bugs that must be
4worked around both in GCC and the GNU assembler.
5
6# GNU binutils Bugs #
7
8## Problem Description ##
9
10One of the addressing modes supported by the PDP-11 is 'index deferred',
11represented by `@X(Rn)`. This operand indicates that `Rn` contains a pointer
12which should be dereferenced and the result added to `X` to generate a new
13pointer to the final location. For example, consider the following four values,
14one stored in a register and the other three in memory. Then `@2(R1)` is the
15value `222`.
16
17 R1: 1000
18 1000: 2000
19 2000: 111
20 2002: 222
21
22Similarly, `@0(R1)` is the value `111`. In most PDP-11 assemblers, including
23DEC's MACRO-11 assembler, the string `@(Rn)` is an alias to `@0(Rn)`. But when
24the GNU assembler encounters `@(Rn)` it assembles it as though it were `(Rn)`,
25a single level of indirection instead of two levels!
26
27If we're only writing assembly then we can work around this bug by always using
28the form `@0(Rn)`. But what if we're writing C and using GCC to compile it?
29Consider the following C code example, taken directly from some stack-based
30debugger code written for the PDP-11.
31
32 uint16_t ** csp = (uint16_t **) 070000;
33 *csp = (uint16_t *) 060000;
34 **csp = 0;
35
36When GCC transpiles this to assembly it generates code of the form `@(Rn)` when
37assigning a value to `**csp` thus causing the value `0` to overwrite the value
38`060000` at `*csp` if GNU `as` is used to assemble the code.
39
40## Solution Description ##
41
42The following patch, tested on GNU binutils 2.28, fixes the bug. Since it
43overloads the `operand->code` variable to pass unrelated state information to
44`parse_reg()` I haven't submitted it for inclusion in GNU binutils. Once I'm
45done bug-hunting in my toolchain I will clean up all the fixes and submit them
46in one bundle.
47
48 --- tc-pdp11.c 2017-06-24 22:33:00.260210000 -0700
49 +++ tc-pdp11.c.fixed 2017-06-24 22:32:12.455205000 -0700
50 @@ -431,6 +431,9 @@
51 {
52 LITTLENUM_TYPE literal_float[2];
53
54 + /* Store the value (if any) passed by parse_op_noreg() before parse_reg() overwrites it. */
55 + int deferred = operand->code;
56 +
57 str = skip_whitespace (str);
58
59 switch (*str)
60 @@ -451,6 +454,15 @@
61 operand->code |= 020;
62 str++;
63 }
64 + /*
65 + * This catches the case where @(Rn) is interpreted as (Rn) rather than @0(Rn)
66 + */
67 + else if (deferred)
68 + {
69 + operand->additional = 1;
70 + operand->word = 0;
71 + operand->code |= 060;
72 + }
73 else
74 {
75 operand->code |= 010;
76 @@ -581,6 +593,12 @@
77
78 if (*str == '@' || *str == '*')
79 {
80 + /*
81 + * operand->code is overwritten by parse_reg() inside parse_op_no_deferred()
82 + * We use it to temporarily catch the alias @(Rn) -> @0(Rn) since
83 + * parse_op_no_deferred() starts at str+1 and thus misses the '@'.
84 + */
85 + operand->code |= 010;
86 str = parse_op_no_deferred (str + 1, operand);
87 if (operand->error)
88 return str;