Symbian
Symbian OS Library

FAQ-0616 What causes "undefined reference to LC0"?

[Index][spacer] [Previous] [Next]



 

Classification: C++ Category: Development
Created: 06/16/98 Modified: 12/29/2001
Number: FAQ-0616
Platform: ER5

Question:
My code compiles for WINS, but when I build it for MARM I get link errors which say "undefined reference to LCnnn" for
some number, even though I don't have anything called LCnnn in the source code anywhere. What causes this and how do I
fix it?


Answer:
This is a known bug with the version of GCC currently used for MARM builds.

If an class scoped enumeration constant is passed to a function which takes the enum parameter by const reference, GCC generates incorrect assembler code, resulting in errors at link time of the form:
      ...(.text+...)... undefined reference to 'LC0'

      [[ 17th June 1998: Example updated to include the CArrayFixFlat<> version of this problem ]]

      Here is an example of some source code which compiles, but generates code with unresolved references to LC0 and LC1:

          #include

          class foo
          {

          public:
          enum bar {EBarValue1, EBarGum};

          void test2();
          private:
          CArrayFixFlat* iArray; // array of foo::bar values
          };


          void problem(const foo::bar& aParam); // const reference to a foo::bar value

          void test()
          {
          problem(foo::EBarGum);
          // call supplying a specific foo::bar constant
          }


          void foo::test2()
          {
          iArray->AppendL(foo::EBarGum); // call supplying a specific foo::bar constant
          }
          And the assembler that is generated by GCC:
              1 @ Generated by gcc cygnus-2.7.2-960323 (Psion GCC tools v113 05/03/1997) for ARM/pe
              2 .file "enum.cpp"
              3 .section .rdata
              4 .text
              5 .align 0
              6 .global test__Fv
              7 test__Fv:
              8 @ args = 0, pretend = 0, frame = 0
              9 @ frame_needed = 0, current_function_anonymous_args = 0
              10 @ I don't think this function clobbers lr
              11 0000 00009FE5 ldr r0, .L1216
              12 0004 FEFFFFEA b problem__FRCQ23foo3bar
              13 .L1217:
              14 .align 0
              15 .L1216:
              16 0008 00000000
              .word .LC0
              17 .section .rdata
              18 .text
              19 .align 0
              20 .global test2__3foo
              21 test2__3foo:
              22 @ args = 0, pretend = 0, frame = 0
              23 @ frame_needed = 0, current_function_anonymous_args = 0
              24 @ I don't think this function clobbers lr

              25 000c 000090E5 ldr r0, [r0, #0]
              26 0010 04209FE5 ldr r2, .L1225
              27 0014 041090E5 ldr r1, [r0, #4]
              28 0018 FEFFFFEA b InsertL__13CArrayFixBaseiPCv
              29 .L1226:
              30 .align 0
              31 .L1225:
              32 001c 00000000
              .word .LC1
              The compiler has correctly decided that it needs to pass a pointer to somewhere that contains the numeric value of the constant, but it has forgotten to generate the constant itself.

              HOW DO I FIX IT

              It's not usually necessary to pass these values by reference (and the Psion Software coding standards tell you not to do it).
              The problem() function should be fixed by changing the definition to:
                void problem(foo::bar aParam); // passed by value, not by reference - const not needed
                As a bonus this is also more efficient.

                The CArrayFixFlat<> example is more awkward - this is an entirely reasonable thing to want to do, and the implementation of CArrayFixFlat<> cannot be changed, so the calling code must be altered. It turns out to be sufficient to do the following:
                  inline void foo::ArrayAppendL(foo::bar aValue) { iArray->AppendL(aValue); }

                  void foo::test2()
                  {
                  ArrayAppendL(foo::EBarGum); // use the inline function instead
                  }
                  Somehow the parameter passing aspect of the inline function causes GCC to take a different path through its code generation, and it then produces valid code.