Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 14 of 14

Thread: Teensy 4.0 linker issues with STL libraries

  1. #1
    Junior Member
    Join Date
    Aug 2019
    Posts
    2

    Teensy 4.0 linker issues with STL libraries

    Hello,
    I received my teensy 4.0 and I am excited to test it. I am quite new to the Teensy platform and Teensyduino, but I had experience with ESP32 and ESP-IDF.

    I have code that makes extensive use of std::string, pair, vector and others and it worked fine on ESP32. However with TeensyDuino even using a vector reserve generates this error during compilation:


    Code:
    Linking everything together...
    /home/exeless/arduino-1.8.9/hardware/teensy/../tools/arm/bin/arm-none-eabi-gcc -O1 -Wl,--gc-sections,--relax -T/home/exeless/arduino-1.8.9/hardware/teensy/avr/cores/teensy4/imxrt1062.ld -mthumb -mcpu=cortex-m7 -mfloat-abi=hard -mfpu=fpv5-d16 -o /tmp/arduino_build_438136/Arduino_src.ino.elf /tmp/arduino_build_438136/sketch/Arduino_src.ino.cpp.o /tmp/arduino_build_438136/libraries/EchoBay/Reservoir.cpp.o /tmp/arduino_build_438136/../arduino_cache_326848/core/core_teensy_avr_teensy40_usb_serial,opt_o1std,keys_it-it_c98261a91e586b5428d648c0cf6e354f.a -L/tmp/arduino_build_438136 -larm_cortexM7lfsp_math -lm -lc -lstdc++ -lsupc++
    /home/exeless/arduino-1.8.9/hardware/tools/arm/bin/../lib/gcc/arm-none-eabi/5.4.1/armv7e-m/fpu/fpv5-d16/libgcc.a(unwind-arm.o): In function `get_eit_entry':
    unwind-arm.c:(.text+0x134): undefined reference to `__exidx_end'
    unwind-arm.c:(.text+0x138): undefined reference to `__exidx_start'
    collect2: error: ld returned 1 exit status
    Searching around in the forum it seems to be related to some hindrances that the Arduino platform have against the STL library. Do you have any guidance for me? Using makefiles instead of Arduino IDE won't be an issue for me.

    Thank you

  2. #2
    Junior Member
    Join Date
    Aug 2019
    Posts
    4
    Did you ever find a solution to this? I'm also running into the same problem.

  3. #3
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    533
    Just tried std::string and std::vector. The following compiles and runs without problems. Tested on a T4.0

    Code:
    #include "Arduino.h"
    #include <vector>
    #include <string>
    
    void setup()
    {
       while(!Serial);
       
       std::string s = "Hello STL";
       Serial.println(s.c_str());
    
       std::vector<int> v;
    
       v.reserve(100);
       v.push_back(1);
       v.push_back(2);
    
       for (auto it = v.begin(); it != v.end(); ++it)
       {
          Serial.println(*it);
       }
    }
    
    void loop()
    {   
    }
    Care to show a minimal example of what doesn't work?
    Last edited by luni; 09-11-2019 at 05:51 AM.

  4. #4
    Junior Member
    Join Date
    Sep 2019
    Posts
    6
    I haven't used the STL, but have been fiddling with linker related questions recently.

    In that light, I suspect you could add a the symbols "__exidx_end" and "__exidx_start" to Teensy 4's linker script in file:
    "core/core/imxrt1062.ld"

    Try looking into "unwind-arm.c" and figuring out what values the symbols should have. Judging by their names, they are memory locations.

    You might thoroughly need to learn the GNU Linker script file syntax and purpose as well, if not already familiar with it... And possibly about different IMXRT1062 memory types/locations...

  5. #5
    Junior Member
    Join Date
    Aug 2019
    Posts
    4
    I've been putting together a minimal example and it is compiling just fine. In the test example I've included all of the calls that I'm using in my main project:

    Code:
    #include <vector>
    #include <Servo.h>
    #include <Wire.h>
    #include "VL53L1X.h"
    #include "CrossPlatformI2C_Core.h"
    #include "TeensyThreads.h"
    #include <EEPROM.h>
    #include <PID_v1.h>
    #include <algorithm>
    #include "Adafruit_VL53L0X.h"
    #include "EM7180_Master.h"
    #include "MegunoLink.h"
    
    
    std::vector<int> buffer;
    
    void setup()
    {
        Serial.begin(115200);
    
        buffer.insert(buffer.end(), 1);
        buffer.insert(buffer.begin(), 2);
        int value = buffer[0];
    
        for (std::vector<int>::iterator it = buffer.begin(); it != buffer.end(); ++it)
        {
            Serial.print(*it);
        }
    }
    
    void loop()
    {
        // put your main code here, to run repeatedly:
    
    }
    All of that compiles and links without any errors. I've compiled both projects side-by-side and the linker commands are nearly identical, the only differences being the other classes that make up my project. The linker command is the long line that starts with:
    Code:
    "C:\\Program Files (x86)\\Arduino\\hardware\\teensy/../tools/arm/bin/arm-none-eabi-gcc"
    -O2
    -Wl,--gc-sections,--relax
    "-TC:\\Program Files (x86)\\Arduino\\hardware\\teensy\\avr\\cores\\teensy4/imxrt1062.ld"
    -mthumb
    -mcpu=cortex-m7
    -mfloat-abi=hard
    -mfpu=fpv5-d16
    -o
    ...
    I've also tried clearing out the cached files from previous builds, maybe there are other files that I'm missing.

    Anyhow I'll keep at it, I'm obviously still missing something!

    --
    Evan

  6. #6
    Junior Member
    Join Date
    Aug 2019
    Posts
    4
    Ok I figured it out, the linker fails if the copy constructor or the assignment operator is used, here is the example:

    Code:
    #include <vector>
    #include <algorithm>
    
    std::vector<int> buffer;
    
    void setup()
    {
        Serial.begin(115200);
    
        buffer.insert(buffer.end(), 1);
        buffer.insert(buffer.begin(), 2);
        int value = buffer[0];
    
        buffer.size();
    
        // Works
        std::vector<int> sorted_buffer;
        for (int i=0; i<buffer.size(); i++) {
            sorted_buffer.push_back(buffer[i]);
        }
    
        // Assignment operator causes linker failure
        // std::vector<int> sorted_buffer;
        // sorted_buffer = buffer;
    
        // Copy constructor causes linker failure
        // std::vector<int> sorted_buffer(buffer);
    
        std::sort(sorted_buffer.begin(), sorted_buffer.end());
    
        for (std::vector<int>::iterator it = buffer.begin(); it != buffer.end(); ++it)
        {
            Serial.print(*it);
        }
    
    }
    
    void loop()
    {
        // put your main code here, to run repeatedly:
    
    }
    At least I have a work around, not sure if this is the right place to post a bug though.

    --
    Evan

  7. #7
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    533
    Interesting. Seems to be some known problem. E.g. https://answers.launchpad.net/gcc-ar...uestion/203480. Simply adding definitions for the two symbols makes the linker happy and everything works.

    But honestly this is over my head.

    Maybe @MichaelMeissner can give an explanation for the problem and if the fix does not generate any unwanted side effects.

    Working code:
    Code:
    #include <algorithm>
    #include <vector>
    
    unsigned __exidx_start;
    unsigned __exidx_end;
    
    std::vector<int> buffer;
    
    void setup()
    {
       while(!Serial);
    
       buffer.insert(buffer.end(), 1);
       buffer.insert(buffer.begin(), 2);
       //int value = buffer[0];
    
       buffer.size();
    
       //Works
       std::vector<int> sorted_buffer;
       for (unsigned i = 0; i < buffer.size(); i++)
       {
          sorted_buffer.push_back(buffer[i]);
       }
    
       // Assignment operator OK
       std::vector<int> sorted_buffer2;
       sorted_buffer2 = buffer;
    
       //Copy constructor OK
       std::vector<int> sorted_buffer3(buffer);
    
       std::sort(sorted_buffer.begin(), sorted_buffer.end());
    
       Serial.print("buffer: ");
       for (std::vector<int>::iterator it = buffer.begin(); it != buffer.end(); ++it)
       {      
          Serial.printf("%d ",*it);
       }
    
       Serial.print("\nsorted_buffer: ");
       for (std::vector<int>::iterator it = sorted_buffer.begin(); it != sorted_buffer.end(); ++it)
       {
          Serial.printf("%d ", *it);
       }
    
       Serial.print("\nsorted_buffer2: ");
       for (std::vector<int>::iterator it = sorted_buffer2.begin(); it != sorted_buffer2.end(); ++it)
       {
          Serial.printf("%d ", *it);
       }
    }
    
    void loop()
    {
       // put your main code here, to run repeatedly:
    }

    Output:
    Code:
    buffer: 2 1 
    sorted_buffer: 1 2 
    sorted_buffer2: 2 1

  8. #8
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    5,423
    @luni - I am no expert in this, but google exidx-start finds: https://stackoverflow.com/questions/...hat-do-they-do
    Sounds like they are used for exception handling

    As you mentioned hopefully @MichaelMeissner can fill in more of the details.

    Not sure how the GCC here sets up to handle things like try/catch ...

  9. #9
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    533
    Yes, but exception handling should be deactivated by -fno-exceptions (which teensyduino sets). Maybe one of the libs which are linked in binary were originally compiled without -fno-exceptions? But as I mentioned, this is something I don't really understand.

    I did a few tests with the the code above, seems to work nicely. However, I would thoroughly test it with some larger project using the STL before I'd use the workaround for production code.

  10. #10
    Senior Member+ mjs513's Avatar
    Join Date
    Jul 2014
    Location
    New York
    Posts
    4,102
    Interesting. This is very similar to the problem that I was having a couple of years ago with having to define missing symbols for abort, kill, write and getpid. This was resolved by doing what was recommended in this post: https://forum.pjrc.com/threads/28181...ll=1#post86394.

    Maybe doing something similar would be better:

    Code:
    extern "C"{
      int __exidx_start(){ return -1;}
      int __exidx_end(){ return -1; }
    }
    I put it after the includes in the example and it works as well:
    Code:
    buffer: 2 1 
    sorted_buffer: 1 2 
    sorted_buffer2: 2 1

  11. #11
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    3,070
    I would also declare them as weak in case the real ones get added later on (or duplicates)

  12. #12
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    533
    I just remembered that I had another issue with the STL some time ago when it complained about a missing std::__throw_bad_alloc();
    IIRC, this function is required if you have -fno-exceptions. It is called instead of throwing an exception in case of an error at memory allocation. Gave it a try and voila everything compiles and links flawlessly without the strange __exidx stuff.

    Code:
    #include <algorithm>
    #include <vector>
    
    void std::__throw_bad_alloc()
    {
      while(1);  // do whatever you want to do instead of a c++ exception
    }
    
    
    std::vector<int> buffer;
    
    void setup()
    {
       while(!Serial);
    
       buffer.insert(buffer.end(), 1);
       buffer.insert(buffer.begin(), 2);
       //int value = buffer[0];
    
       buffer.size();
    
       //Works
       std::vector<int> sorted_buffer;
       for (unsigned i = 0; i < buffer.size(); i++)
       {
          sorted_buffer.push_back(buffer[i]);
       }
    
       // Assignment operator OK
       std::vector<int> sorted_buffer2;
       sorted_buffer2 = buffer;
    
       //Copy constructor OK
       std::vector<int> sorted_buffer3(buffer);
    
       std::sort(sorted_buffer.begin(), sorted_buffer.end());
    
       Serial.print("buffer: ");
       for (std::vector<int>::iterator it = buffer.begin(); it != buffer.end(); ++it)
       {      
          Serial.printf("%d ",*it);
       }
    
       Serial.print("\nsorted_buffer: ");
       for (std::vector<int>::iterator it = sorted_buffer.begin(); it != sorted_buffer.end(); ++it)
       {
          Serial.printf("%d ", *it);
       }
    
       Serial.print("\nsorted_buffer2: ");
       for (std::vector<int>::iterator it = sorted_buffer2.begin(); it != sorted_buffer2.end(); ++it)
       {
          Serial.printf("%d ", *it);
       }
    }
    
    void loop()
    {
       // put your main code here, to run repeatedly:
    }
    EDIT:
    BTW: The problem seems to come from linking in libstdc++ . If you remove it from the list of linked libs the core compiles like it did before. But now the linker complains about the missing __throw_bad_alloc() which makes much more sense since we don't have exceptions enabled. Can it be that there is something wrong with libstdc++?.
    I wonder if we really need it? At least the complete core and the test sketch from above compile and link perfectly without.
    Last edited by luni; 09-12-2019 at 07:25 PM.

  13. #13
    Senior Member+ mjs513's Avatar
    Join Date
    Jul 2014
    Location
    New York
    Posts
    4,102
    @luni

    You are making me remember as well. Think I had the same problem when I was working on UAVCAN. I actually had to add this as well:
    Code:
    namespace std {
      void __throw_bad_alloc()
      {
        Serial.println("Unable to allocate memory");
      }
    
      void __throw_length_error( char const*e )
      {
        Serial.print("Length Error :");
        Serial.println(e);
      }
    
      void __throw_bad_function_call()
      {
        Serial.println("Bad function call!");
      }
    }
    for the same reason with -fno_exceptions.

  14. #14
    __exidx is defined in the linker script and is probably missing. It's used for stack unwinding. It can be turned off with following option -fno-unwind-tables.
    A good alternative to STL on embedded systems are https://www.etlcpp.com/


    .ARM :
    {
    __exidx_start = .;
    *(.ARM.exidx*)
    __exidx_end = .;
    } > m_text

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •