Bug 23126 - SVC instruction is rejected for ARM Cortex-M0
Summary: SVC instruction is rejected for ARM Cortex-M0
Status: RESOLVED MOVED
Alias: None
Product: binutils
Classification: Unclassified
Component: gas (show other bugs)
Version: 2.30
: P2 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2018-04-29 07:28 UTC by Freddie Chopin
Modified: 2018-05-02 10:28 UTC (History)
1 user (show)

See Also:
Host:
Target: arm-none-eabi
Build:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Freddie Chopin 2018-04-29 07:28:57 UTC
New binutils 2.30 rejects `svc 0` (as part of `asm volatile(...)` in C++ source file):

-- >8 -- >8 -- >8 -- >8 -- >8 -- >8 -- >8 -- >8 -- >8 -- >8 --

arm-none-eabi-g++ -Wall -Wextra -Wshadow -std=gnu++11 -mcpu=cortex-m0 -mthumb -g -ggdb3 -O2  -fno-use-cxa-atexit -ffunction-sections -fdata-sections -fno-rtti -fno-exceptions   -Ioutput/include -Iinclude -Isource/architecture/ARM/ARMv6-M-ARMv7-M/include -Isource/architecture/ARM/ARMv6-M-ARMv7-M/external/CMSIS -Isource/chip/STM32/include -Isource/chip/STM32/STM32F0/include -Isource/chip/STM32/peripherals/GPIOv2/include -Isource/chip/STM32/peripherals/SPIv2/include -Isource/chip/STM32/peripherals/USARTv2/include -Isource/chip/STM32/STM32F0/external/CMSIS-STM32F0  -MD -MP -c source/architecture/ARM/ARMv6-M-ARMv7-M/ARMv6-M-ARMv7-M-supervisorCall.cpp -o output/source/architecture/ARM/ARMv6-M-ARMv7-M/ARMv6-M-ARMv7-M-supervisorCall.o
/tmp/cchwMvG1.s: Assembler messages:
/tmp/cchwMvG1.s:41: Error: SVC is not permitted on this architecture
make: *** [Makefile:261: output/source/architecture/ARM/ARMv6-M-ARMv7-M/ARMv6-M-ARMv7-M-supervisorCall.o] Error 1

-- >8 -- >8 -- >8 -- >8 -- >8 -- >8 -- >8 -- >8 -- >8 -- >8 --

This code was accepted in previous binutils (for years). SVC is listed with no notes about any special conditions in:
- Thumb ® 16-bit Instruction Set Quick Reference Card
- ARMv6-M Architecture Reference Manual

Additionally "ARMv6-M Architecture Reference Manual" says ("A4.9 Exception-generating instructions"):

-- >8 -- >8 -- >8 -- >8 -- >8 -- >8 -- >8 -- >8 -- >8 -- >8 --
In an ARMv6-M implementation that does not include the Unprivileged/Privileged Extension,
execution is always privileged. However in such an implementation, application code might use
supervisor calls to maintain a software hierarchy with a system kernel.
-- >8 -- >8 -- >8 -- >8 -- >8 -- >8 -- >8 -- >8 -- >8 -- >8 --

Adding `-march=armv6s-m` to the compilation flags makes the error go away, but I don't think that requiring it is a good solution.

Related problems:
https://sourceware.org/bugzilla/show_bug.cgi?id=12296
https://bugs.launchpad.net/gcc-arm-embedded/+bug/1449610
Comment 1 Freddie Chopin 2018-04-30 21:37:46 UTC
I think the cause of the problem is this change - https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=commit;h=173205ca3356cab0590c2debaac97107fb9a7fcd
Comment 2 Nick Clifton 2018-05-01 16:32:05 UTC
Hi Freddie,

  I am unable to reproduce this problem. :-(

  Are you able to capture an assembler input file and command line that
  I could try out locally ?

Cheers
  Nick
Comment 3 Freddie Chopin 2018-05-01 18:33:53 UTC
Hmmm... It seems that the problem is not entirely in GAS, but in GCC 8 (RC) as well...

With GCC 7.3.0 and most recent binutils everything works fine. With GCC 8.0.1 20180427:

-- >8 -- >8 -- >8 -- >8 -- >8 -- >8 -- >8 -- >8 -- >8 --

$ cat svc.cpp 
__attribute__ ((naked))
int supervisorCall(int (& function)(int, int, int, int), const int argument1, const int argument2, const int argument3,
		const int argument4)
{
	asm volatile
	(
			"	mov		r12, r0		\n"
			"	ldr		r0, [sp]	\n"
			"	svc		0			\n"
			"						\n"
			"	bx		lr			\n"
	);

	__builtin_unreachable();

	// suppress warnings
	(void)function;
	(void)argument1;
	(void)argument2;
	(void)argument3;
	(void)argument4;
}
$ arm-none-eabi-g++ -c svc.cpp -mcpu=cortex-m0 -save-temps
svc.s: Assembler messages:
svc.s:31: Error: SVC is not permitted on this architecture
$ diff -u svc-7.s svc.s 
--- svc-7.s	2018-05-01 20:14:09.031910734 +0200
+++ svc.s	2018-05-01 20:16:36.751143427 +0200
@@ -12,6 +12,7 @@
 	.text
 	.align	1
 	.global	_Z14supervisorCallRFiiiiiEiiii
+	.arch armv6-m
 	.syntax unified
 	.code	16
 	.thumb_func
@@ -37,4 +38,4 @@
 	.cantunwind
 	.fnend
 	.size	_Z14supervisorCallRFiiiiiEiiii, .-_Z14supervisorCallRFiiiiiEiiii
-	.ident	"GCC: (bleeding-edge-toolchain) 7.3.0"
+	.ident	"GCC: (bleeding-edge-toolchain) 8.0.1 20180427 (prerelease)"
$ cat svc.s 
	.cpu cortex-m0
	.eabi_attribute 20, 1
	.eabi_attribute 21, 1
	.eabi_attribute 23, 3
	.eabi_attribute 24, 1
	.eabi_attribute 25, 1
	.eabi_attribute 26, 1
	.eabi_attribute 30, 6
	.eabi_attribute 34, 0
	.eabi_attribute 18, 4
	.file	"svc.cpp"
	.text
	.align	1
	.global	_Z14supervisorCallRFiiiiiEiiii
	.arch armv6-m
	.syntax unified
	.code	16
	.thumb_func
	.fpu softvfp
	.type	_Z14supervisorCallRFiiiiiEiiii, %function
_Z14supervisorCallRFiiiiiEiiii:
	.fnstart
.LFB0:
	@ Naked Function: prologue and epilogue provided by programmer.
	@ args = 4, pretend = 0, frame = 0
	@ frame_needed = 1, uses_anonymous_args = 0
	.syntax divided
@ 12 "svc.cpp" 1
		mov		r12, r0		
	ldr		r0, [sp]	
	svc		0			
						
	bx		lr			

@ 0 "" 2
	.thumb
	.syntax unified
	.cantunwind
	.fnend
	.size	_Z14supervisorCallRFiiiiiEiiii, .-_Z14supervisorCallRFiiiiiEiiii
	.ident	"GCC: (bleeding-edge-toolchain) 8.0.1 20180427 (prerelease)"

-- >8 -- >8 -- >8 -- >8 -- >8 -- >8 -- >8 -- >8 -- >8 --

(svc-7.s is a temp file generated with GCC 7.3.0)

So new GCC adds explicit arch info - `.arch armv6-m`. The most simple test case:

-- >8 -- >8 -- >8 -- >8 -- >8 -- >8 -- >8 -- >8 -- >8 --

$ echo 'svc 0' > /tmp/just-svc.s
$ arm-none-eabi-as -mcpu=cortex-m0 just-svc.s 
$ arm-none-eabi-as -mcpu=cortex-m0 -march=armv6-m just-svc.s 
$ arm-none-eabi-as -march=armv6-m just-svc.s 
just-svc.s: Assembler messages:
just-svc.s:1: Error: SVC is not permitted on this architecture

-- >8 -- >8 -- >8 -- >8 -- >8 -- >8 -- >8 -- >8 -- >8 --

So it seems that if you have both `-mcpu=cortex-m0 -march=armv6-m` then GAS has no problem with that. However if both CPU and ARCH are specified with directives in the file, then GAS doesn't like this:

-- >8 -- >8 -- >8 -- >8 -- >8 -- >8 -- >8 -- >8 -- >8 --

$ cat svc-directives.s 
.cpu cortex-m0
.arch armv6-m
.syntax unified
.thumb
svc 0
$ arm-none-eabi-as svc-directives.s 
svc-directives.s: Assembler messages:
svc-directives.s:5: Error: SVC is not permitted on this architecture

-- >8 -- >8 -- >8 -- >8 -- >8 -- >8 -- >8 -- >8 -- >8 --

I still think all these GAS errors are wrong, as "ARMv6-M Architecture Reference Manual" says SVC is permitted even if not implemented in the core.

Do you think I should file a bug report for GCC too?
Comment 4 Nick Clifton 2018-05-02 09:14:10 UTC
Hi Freddie,

> Hmmm... It seems that the problem is not entirely in GAS, but in GCC 8 (RC)
> as well...

Actually, I think that you have found a separate gas bug^H^H feature...

> So it seems that if you have both `-mcpu=cortex-m0 -march=armv6-m` then GAS
> has no problem with that.

> However if both CPU and ARCH are specified with
> directives in the file, then GAS doesn't like this:

Right - it turns out that command line options are additive, so if you specify
-march=armv6-m -mcpu=cortx-m0 you get the features of both the architecture and cpu enabled, even if they are not the same.  (This also applies if the options are specified in the opposite order).

But - directives inside the assembler source file override any previous directives or command line options.  So if you have:

  .cpu cortex-m0
  .arch armv6-m

then you *only* get the features of the V6M architecture and not any other features that might be present in the cortex-m0.

The other important point to note here is that the assembler's "armv6-m" architecture does *not* support the SVC instruction.  In order to support that you must use the "armv6s-m" architecture.  The "s" in that string refers to the OS extension to the base V6M architecture, which in this case means the SVC instruction.

Note - the assembler does know that the cortex-m0 supports the SVC instruction,
but that is because internally it has a table that says that the cortex-m0 uses the armv6s-m architecture.  This explains why your svc-directive.s test fails to assemble.  It has the .arch directive after the .cpu directive and it uses the wrong architecture name.

With regard to the "ARMv6-M Architecture Reference Manual" entry:

  In an ARMv6-M implementation that does not include the Unprivileged/
  Privileged Extension, execution is always privileged. However in 
  such an implementation, application code might use supervisor calls to 
  maintain a software hierarchy with a system kernel.

This does not, to me, mean that the SVC instruction *must* be supported.  Rather
it says that if the SVC instruction is present in machine code that is running
on a v6-M implementation that does not separate privileged from unprivileged execution then this should be permitted.

However, I did find this document online, the "Cortex-M0 Devices Generic User Guide", which clearly shows that the SVC instruction is supported:

  http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0432c/CHDCICDF.html

So all in all, I think that the assembler is not, technically, wrong.  But it is rather confusing in its behaviour.


> Do you think I should file a bug report for GCC too?

Yes - for a -mcpu=cortex-m0 gcc command line option gcc should be generating a ".arch armv6s-m" directive and not a ".arch armv6-m" directive.  (This presumably applies to other cortex-m?? variants as well, although I have not checked).  Alternatively it should just generate a .cpu directive and no ".arch" directive, and leave it up to the assembler to divine the architecture involved.

Cheers
  Nick
Comment 5 Freddie Chopin 2018-05-02 10:20:24 UTC
> Right - it turns out that command line options are additive, so if you specify
> -march=armv6-m -mcpu=cortx-m0 you get the features of both the architecture and cpu enabled, even if they are not the same.  (This also applies if the options are specified in the opposite order).
> 
> But - directives inside the assembler source file override any previous directives or command line options.

So I guess this could be treated as a GAS bug, right? Should I file a separate report or maybe you'd just edit the description of this one?

> Yes - for a -mcpu=cortex-m0 gcc command line option gcc should be generating a ".arch armv6s-m" directive and not a ".arch armv6-m" directive.  (This presumably applies to other cortex-m?? variants as well, although I have not checked).  Alternatively it should just generate a .cpu directive and no ".arch" directive, and leave it up to the assembler to divine the architecture involved.

Here's the relevant GCC bug - https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85606
Comment 6 Nick Clifton 2018-05-02 10:28:55 UTC
Hi Freddie,

> > Right - it turns out that command line options are additive, so if you specify
> > -march=armv6-m -mcpu=cortx-m0 you get the features of both the architecture and cpu enabled, even if they are not the same.  (This also applies if the options are specified in the opposite order).
> > 
> > But - directives inside the assembler source file override any previous directives or command line options.
> 
> So I guess this could be treated as a GAS bug, right? Should I file a
> separate report or maybe you'd just edit the description of this one?

Please file a separate one.  (Although I suspect that all that will happen
is that the behaviour will be documented so that it is then considered 
"correct").

Cheers
  Nick