This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

std::mt19937 with ARM Compiler 6 uses sys_open and breaks retarget.c

I'm using Keil V5.30.0.0 and ArmClang.exe V6.14.

When I tried using std::mt19937, like so:

#include <random>

std::mt19937 mt;
std::uniform_int_distribution< uint32_t > dist(0);

auto a = dist( mt );

I got linker errors:

.\obj\rcu.axf: Error: L6200E: Symbol __stdin multiply defined (by stdio_streams.o and retarget.o).
.\obj\rcu.axf: Error: L6200E: Symbol __stdout multiply defined (by stdio_streams.o and retarget.o).
.\obj\rcu.axf: Error: L6200E: Symbol __stderr multiply defined (by stdio_streams.o and retarget.o).

I'm using more or less classical retarget.c, tuned for armclang.

I tried to remove those definitions from retarget.c and then got error:

.\obj\rcu.axf: Error: L6915E: Library reports error: __use_no_semihosting_swi was requested, but _sys_open was referenced

Then I removed asm(".global __use_no_semihosting_swi\n"); and my firmware hit BKPT before main, in call to _sys_open.

Why is this happening? mt19937 and uniform_int_distribution by themselves should not use any IO, should they? 

  • I suggest simplifying to a really simple test case, and working back up. I tried to build a simple test for Cortex-M4 based on your code fragment: no build errors. (Also MDK 5.30, ARMCLANG 6.14, configured to use STDIN with 'User' code) But my case was only built to test compilation, not to run anywhere.

    #include <random>
    #include <iostream>
    
    extern "C"{
    // Set RTE option Compiler::I/O::STDIN to 'User'
    int stdin_getchar (void);
    }
    
    std::mt19937 mt;
    std::uniform_int_distribution< uint32_t > dist(0);
    auto a = dist( mt );
    
    int main(void)
    {
       std::mt19937 mt;
       std::uniform_int_distribution< uint32_t > dist(0);
    
       auto b = dist( mt );
       //return a + b;
       std::cout << "Hello, world!\n" << a << std::endl;
       return 0;
    }
    

    The random number classes do define stream operator templates, so shouldn't be an issue unless you use stream operators with the distributions. I had no issues building with iostream or not. 

    I'm curious about your file retarget.c -- the RTE manager will add retarget_io.c to your project when you configure IO using the RTE manager. Why and how did your retarget.c need 'tuning' for ARMCLANG? 

    There is helpful documentation for retargeting I/O on the Keil website, here. Per that documentation, uVision doesn't support semihosting. This support article might also help.

  • I  tried using empty project (of my own) and copy-pasted your code for main. I also tried enabling retargetting of STDIN and STDOUT via RTE manager as you suggested.

    Several things happened:

    - size of the binary went to 84 kilobytes with -Oz, even when main is empty. Have no idea how retarget_io.c manages to waste so much space ._.
    - project compiled but stalled on BKPT before main with a callchain like _initio -> freopen -> _sys_open. This happens even if main is empty!


    This reminded me of an issue that I stumbled in three years ago - community.arm.com/.../using-itm-retarget-with-keil-software-packs

    Bloated code size is the reason why I still use a hand-written retarget.cpp (not .c, that was a misprint earlier, sorry) instead of RTE Managing it.

    Here it is, more or less:

    #if ( (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) )
    
        asm(".global __use_no_semihosting_swi\n");
        
    #elif __CC_ARM
    
        #pragma import(__use_no_semihosting_swi)
        
        namespace std { struct __FILE { int handle;} ; }
    
    #endif
    
    #include <stdio.h>
    #include <rt_sys.h>
    #include <rt_misc.h>
    
    
    std::FILE std::__stdout;
    std::FILE std::__stdin;
    std::FILE std::__stderr;
    
    extern "C"
    { 
        int fputc(int c, FILE *f)
        {
            return ITM_SendChar(c);
        }
    
        int fgetc(FILE *f)
        {
            char ch = 0;
    
            return((int)ch);
        }
    
        int ferror(FILE *f)
        {
            /* Your implementation of ferror */
            return EOF;
        }
    
        void _ttywrch(int ch)
        {
            ITM_SendChar(ch);            
        }
        
        char *_sys_command_string(char *cmd, int len)
        {
            return NULL;
        }
        
        void _sys_exit(int return_code) 
        {
            while(1);
        }        
    }
    

    So 'tuning for armclang' was this line about semihosting - asm(".global __use_no_semihosting_swi\n");

    On its own this works very nicely, at least in Simulator (which is all I need at the moment).

  • This explains the original linker error: _sys_open is needed by the iostream functions pulled in from <random>, your retarget.cpp doesn't provide a definition and you disabled semihosting, so the linker complained that it couldn't find a suitable _sys_open. Allowing semihosting pulls in version of _sys_open that uses semihosting, which is why you end up on a breakpoint before main. (If you want to decode the semihosting call, see Chapter 7 in the ARM Compiler Software Development Guide.)

    The bulk of your 84KB binary is most likely due to locale and other I/O support. <random> includes <istream> and <ostream>, which is enough to pull in the IO libraries, even if your <random> objects don't use streams.  Large binary sizes aren't really the fault of retarget_io.c -- it's just a very small part of the overall puzzle. 

  • Hmm, looks like you are correct; removing #include <random> leads to small binary.

    Why this include pulls so much unused code that isn't removed even with -LTO and -ffunction-sections - is a whole other story, albeit very interesting :)

    Thanks for explanation!

    However.. when I used retarget_io.c, provided by RTE Manager, I didn't disable semihosting and still ended up on a breakpoint before main..