4

I have a weird one. I'm working on an embedded system, using the vendors header files. I'm compiling the files using GCC 4.6.3. I want to use C++ for my code, I have error I can't figure out. I'm running a vendor example program, and all I've done is changed the name of the main.c file to main.cpp. As a result, I assume, the header files are being interpreted by the C++ compiler. One of them contains the following lines:

__attribute__((naked)) static return_type signature \
  { \
     __asm( \
         "svc %0\n" \
         "bx r14" : : "I" (number) : "r0" \
     ); \
}

The files compile properly if the name of the file is main.c, I assume this is because the file is being processed by the C compiler. The error I get if I use C++ is

error: impossible constraint in 'asm'

But again, I have no problem with the C compiler. I need to call functions that use this define in C++ files. I've considered writing wrapper functions that stay on the c side and linking to them, but it would be a real pain, and less efficient. Any suggestions?

10
  • 2
    If it's in a header file did you remember to extern "C" it? Commented Mar 29, 2013 at 23:16
  • 1
    I tried it as a last ditch, however I didn't expect it to work. extern "C" only effects linkage, not compilation, as far as I know. I think what I've got is a compilation error, not a linkage error. Thanks for the suggestion though. stackoverflow.com/questions/1041866/… Commented Mar 29, 2013 at 23:19
  • I see, I skipped over the part of it being a macro :P Commented Mar 30, 2013 at 0:03
  • It might affect the ABI too. Commented Mar 30, 2013 at 0:11
  • 1
    Are you sure the C++ compilation finds the cross compiler, and you aren't using the host compiler by accident? Commented Mar 30, 2013 at 1:15

2 Answers 2

1

svc also known as swi is the ARM/Thumb software interrupt instruction. It only takes constants, but they are different from other register constants. Ie, mov r0, #4096. You need to use the preprocessor and token pasting if you wish to specify an immediate. number can not be a variable or register.

#define syscall(number) __attribute__((naked)) static return_type signature \
  { \
     __asm( \
         "svc " #number "\n" \
         "bx r14" : : : "r0" \
     ); \
  }

will work. Note: The # is the 'C' preprocessors stringify. Also note that it is in-efficient to look at the SVC number as it is in the I-CACHE and inspecting requires D-CACHE. Generally it is always constant and the function number is passed in a register for faster syscall's.

The gcc manual says,

'I'- Integer that is valid as an immediate operand in a data processing instruction. That is, an integer in the range 0 to 255 rotated by a multiple of 2

This is typical of Data-processing operands - immediate, section A5.1.3 of the ARM ARM. The SVC operands are either fixed 8-bits in thumb mode or fixed 24-bits in ARM mode. It maybe possible with some other constraint that I am unaware of, but at least the preprocessor's stringification will work as long as a numeric constant is passed to the macro.

I guess it is lucky that this worked from gcc and unlucky that g++ did not. You can get further insight by using -S and looking at (and posting) the output using both tools.

Edit: Your code does seem to work with gcc-4.7.2, but number is a const int local in my case, the use of number maybe the issue. Perhaps it has a subtle semantic change from 'C' to 'C++'.

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks for the help. You put me on the right track with your edit. It was the datatype of number. The constants the vendor was using were defined as enums, which changes the type between C and C++. I've changed them to #define's and it's working a treat. Thanks.
Rather than change a whole bunch of enums to #defines, I found I could simply add a local variable with a cast to an integral type.
Thanks KendrickTaylor, that did the job for me! I guess we are both using nRF51? :) @ChristopherMason, could you paste a code snippet?
This cast works for me: "bx r14" : : "I" ((uint16_t)number) : "r0" I do this in a local file and use include order to preferentially include it, this way avoiding having to modify the Nordic SDK.
Ok, it is quite possible that the behavior will change depending on the gcc version and optimization level. Ie, it might work with the cast now, but you may have trouble if you upgrade and/or change optimization levels. The main point is the compiler must be able to deduce the value is a constant. For instance, compiling with -O0 may not work as the compiler may leave things in a register. With higher optimization levels, it is more likely the cast will work.
0

Check the GCC manual (inline assembler) for the exact meaning of the constraints for your machine. Older GCC versions have been notoriously sloppy in the checking of constraints, perhaps you are being bitten by this. It is strange that gcc and g++ (same version?) handle the code differently, it just might be a compiler bug, but I'd consider that only after all other explanations have been exhausted.

1 Comment

Thanks, I'll check it out. Both gcc and g++ are the same version.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.