Gcc complains about vstmia - why? - gcc

I'm trying to compile a program for Raspberry Pi 2B (ARMv7 / Neon), but I get an error from an inline assembly code:
Error: VFP single precision register expected -- `vstmia.64
r9,{d16-d31}'
The code is:
asm volatile (
"vstmia.64 %[reg]!, {d0 - d15} # read all regs\n\t"
"vstmia.64 %[reg], {d16 - d31} # read all regs\n\t"
::[reg] "r" (&vregs):
);
Funny thing is that it doesn't complain about the first vstmia.
I tried with single {d0 - d32} first and I thought maybe there were too many 64-bit registers, but that's obviously not the problem.
vregs is a 8-byte aligned storage.
I'm using arm-linux-gnueabihf-gcc 4.8.3, with this command line:
arm-linux-gnueabihf-gcc -mcpu=cortex-a7 -marm -O2 -g -std=gnu11 -MMD -MP -MF"ARM_decode_table.d" -MT"ARM_decode_table.o" -c -o "ARM_decode_table.o" "../ARM_decode_table.c"

By not specifying an appropriate -mfpu option, you get whatever FPU support the compiler's default configuration provides. From your configuration in this case, that is --with-fpu=vfp, which means crusty old VFPv2 with only 16 D registers overlaying the 32 S registers. Thus the first instruction targeting d0-d15 is fine, but the assembler refuses to assemble the second instruction which it knows won't work on the chosen target.
For Cortex-A7 with NEON, -mfpu=neon-vfpv4 will let the toolchain know that it can let rip and use everything you have available.

Related

ARM GCC Cortex-M7 bare-metal compiler flags for proper floating-point stack alignment?

Using the arm-none-eabi-gcc compiler from ARM I find that printf("%lf") prints garbage values when the stack is not 8-byte-aligned prior to calling. If I manually align the stack, then it always works:
// This always works, but fails without the get/set_MSP manipulation when
// the MSP is not 8-byte-aligned
unsigned msp = __get_MSP();
__set_MSP(msp & ~7);
// Prints wrong floating-point value when stack is not 8-byte-aligned.
// No difference using %f vs. %lf
printf("%lf\n", 1.234);
__set_MSP(msp);
arm-none-eabi-gcc -v returns:
gcc version 10.2.1 20201103 (release) (GNU Arm Embedded Toolchain 10-2020-q4-major)
and relevant compiler flags are:
-O3 -mcpu=cortex-m7 -mfpu=fpv5-d16 -mfloat-abi=hard -mthumb -Wall -fdata-sections -ffunction-sections -fno-strict-aliasing -std=gnu++17 -fno-rtti -fno-use-cxa-atexit
Can anyone suggest any compiler flags I may be missing (or shouldn't be specifying?) -- or other solutions?
Thanks.
Gcc is just generating code that produces the 8 byte alignment that is mandated by the ARM ABI specification. This is specifically pointed out in ABI for the ArmĀ® Architecture Advisory Note
From Section 3.1 "The need to align SP to a multiple of 8 at conforming call sites"
In return for preserving the natural alignment of data, conforming code
is permitted to rely on that alignment. To support aligning data allocated on the stack, the stack pointer (SP) is required to be 8-byte aligned on entry to a conforming function...

Compiling GAS code doesn't detect -fPIC option

I am trying to compile some GAS code for a project using the GCC gnu compiler. Here is how I am compiling it:
gcc -c boot.s -o boot.o -fPIC
After I compile my kernel.c file with the -fPIC argument, I try to link it with this command:
gcc -N -T linker.ld -o Slack\ Berry.bin -ffreestanding -nostdlib kernel.o boot.o -lgcc
It comes up with:
/usr/bin/ld: boot.o: relocation R_X86_64_32 against '.multiboot' can not be used when making a PIE object; recompile with -fPIC
This leads me to think that it is not compiling my GAS code with -fPIC. How can I fix this?
First of all you probly need -fPIE rather than -fPIC. -fPIE allows compiler to generate more efficient code but can only be used for code that's part of main executable (not shared library).
Now both -fPIC and -fPIE are compiler-only flags and are not passed to assembler. You'll need to explicitly use PIC-specific mnemonics in your assembly code instead of position-dependent calls and branches e.g instead of
movq $bar, %rdx
use
movq bar#GOTPCREL(%rip), %rdx
(normally to get the syntax I need I just run gcc -fPIE -S -o- on matching C snippet).
Recompile with -fPIC only applies if the asm was generated by a compiler, not written by hand. It has no effect on how asm is assembled into machine code.
The problem is that your PIE executable can't be linked with 32-bit absolute addresses. (Did you mean to make a PIE instead of a static position-dependent executable)?
You don't need the full shared-library stuff for referencing symbols in another library or the main executable (like #yugr's answer shows how to do). Your freestanding kernel may not even have a GOT or PLT, and definitely shouldn't use them for internal symbols.
The only change needed is lea bar(%rip), %rdx, a RIP-relative LEA instead of a mov $imm32, %r/m64. (movabs would work to, but be larger and usually slower.)
Or, if you actually meant to build with -static and create an executable that will be loaded at a fixed address in the low 32 bits of address space, you should use mov $bar, %edx to get a 5-byte mov $imm32, %r32 encoding instead of 7-byte mov $sign_extended_imm32, %r/m64 or a 7-byte LEA. See also Difference between movq and movabsq in x86-64

ld fails to find the entry symbol main when linking

I am writing a simple hello world bootloader in C with inline assembly using this article. Nothing fancy, no kernel loading and other advanced topics. Just a plain old "hello world" message.
Here are my files:
boot.c
/* generate 16-bit code */
__asm__(".code16\n");
/* jump boot code entry */
__asm__("jmpl $0x0000, $main\n");
/* user defined function to print series of characters terminated by null
character */
void printString(const char* pStr) {
while (*pStr) {
__asm__ __volatile__ (
"int $0x10" : : "a"(0x0e00 | *pStr), "b"(0x0007)
);
++pStr;
}
}
void main() {
/* calling the printString function passing string as an argument */
printString("Hello, world!");
}
boot.ld
ENTRY(main);
SECTIONS
{
. = 0x7C00;
.text : AT(0x7C00)
{
*(.text);
}
.sig : AT(0x7DFE)
{
SHORT(0xaa55);
}
}
I then ran the following commands: (different from the first article; adapted from another StackOverflow article as the commands in the first article won't work for me)
gcc -std=c99 -c -g -Os -march=i686 -m32 -ffreestanding -Wall -Werror boot.c -o boot.o
ld -static -T boot.ld -m elf_i386 -nostdlib --nmagic -o boot.elf boot.o
The first line compiles successfully, but I get errors upon executing the second line:
ld: warning: cannot find entry symbol main; defaulting to 0000000000007c00
boot.o:boot.c:(.text+0x2): undefined reference to 'main'
boot.o: In function 'main':
C:(...)/boot.c:16: undefined reference to '__main'
C:(...)/boot.c:16:(.text.startup+0xe): relocation truncated to fit: DISP16 against undefined symbol '__main'
What's wrong? I use Windows 10 x64 with the gcc compiler that comes with Dev-C++.
I'd suggest an i686-elf cross compiler rather than using a native windows compiler and tool chain. I think part of your problem is peculiarities related to the Windows i386pe format.
The .sig section is likely not being written at all since that unknown section probably isn't marked allocatable data. The result of that is the signature isn't written to the final binary file. It is also possible the virtual memory address (VMA) is not being set in boot.ld so it may not advance the boot signature into the last 2 bytes of the 512 byte sector. As well with the Windows format read only data will be placed in sections starting with .rdata. You'll want to make sure those are included after the data section and before the boot signature. Failure to do this will default the linker script into placing unprocessed input sections at the end beyond the boot signature.
Assuming you have made the changes as you mentioned in the comments about the extra underscores your files may work this way:
boot.ld:
ENTRY(__main);
SECTIONS
{
. = 0x7C00;
.text : AT(0x7C00)
{
*(.text);
}
.data :
{
*(.data);
*(.rdata*);
}
.sig 0x7DFE : AT(0x7DFE) SUBALIGN(0)
{
SHORT(0xaa55);
}
}
The commands to compile/link and adjust the .sig section to be a regular readonly allocated data section would look like:
gcc.exe -std=c99 -c -g -Os -march=i686 -m32 -ffreestanding -Wall -Werror boot.c -o boot.o
ld.exe -mi386pe -static -T boot.ld -nostdlib --nmagic -o boot.elf boot.o
# This adjusts the .sig section attributes and updates boot.elf
objcopy --set-section-flags .sig=alloc,contents,load,data,readonly boot.elf boot.elf
# Convert to binary
objcopy -O binary boot.elf boot.bin
Other Observations
Your use of __asm__(".code16\n"); will not generate usable code for a bootloader. You'll want to use the experimental pseudo 16-bit code generation that forces the assembler to modify instructions to be compatible with 32-bit code but encoded to be usable in 16-bit real mode. You can do this by using __asm__(".code16gcc\n"); at the top of each C/C++ files.
This tutorial has some bad advice. The global level basic assembly statement that does the JMP to main may be relocated to somewhere other than the beginning of the bootloader (some optimization levels may cause this). The startup code doesn't set ES, DS, CS to 0x0000, nor does it set the SS:SP stack segment and pointer. This can cause problems.
If trying to run from a USB drive on real hardware you may find you'll need a Boot Parameter Block. This Stackoverflow Answer I wrote discusses this issue and a possible work around under Real Hardware / USB / Laptop Issues
Note: The only useful code that GCC currently generates is 32-bit code that can run in 16-bit real mode. This means that you can't expect this code to run on a processor earlier than a 386 like the 80186/80286/8086 etc.
My general recommendation is to not create bootloaders with GCC unless you know what you are really doing and understand all the nuances involved. Writing it in assembly is probably a much better idea.
If you want a C/C++ compiler that generates true 16-bit code you may wish to look at OpenWatcom

What is the signification of LDFLAGS

I'm trying to compile AODV for ARM linux. I use a SabreLite as a board with kernel version 3.0.35_4.1.0. It's worth mention that i'm using openembedded to create my Linux Distribution for my board.
The AODV source code (http://sourceforge.net/projects/aodvuu/) has a README file which give some indications on how to install it on ARM as stated a bit here.
(http://w3.antd.nist.gov/wctg/aodv_kernel/kaodv_arm.html).
I was able to upgrade the makefile in order to be used with post 2.6 kernel version ( as stated above, i have the 3.0.35_4.1.0 kernel version).
So, basically, what i am trying to do is that i have to create a module (let's say file.ko) and then load it into the ARM (with insmod file.ko command).
To do that, i am using a cross compiler which some values are stated below:
echo $CC :
arm-oe-linux-gnueabi-gcc -march=armv7-a -mthumb-interwork -mfloat-abi=hard -mfpu=neon -mtune=cortex-a9 --sysroot=/usr/local/oecore-x86_64/sysroots/cortexa9hf-vfp-neon-oe-linux-gnueabi
echo $ARCH=arm
echo $CFLAGS: O2 -pipe -g -feliminate-unused-debug-types
echo $LD :
arm-oe-linux-gnueabi-ld --sysroot=/usr/local/oecore-x86_64/sysroots/cortexa9hf-vfp-neon-oe-linux-gnueabi
echo $LDFLAGS :
-Wl,-O1 -Wl,--hash-style=gnu -Wl,--as-needed -Wl,--as-needed
when i launch "make command", i get the following errors:
LD [M] /home/scof/script_emulation/AODV/aodv-uu/lnx/kaodv.o
arm-oe-linux-gnueabi-ld: unrecognized option '-Wl,-O1'
arm-oe-linux-gnueabi-ld: use the --help option for usage information
It states that there is something wrong with the linker. This linker comes from the cross compilation tools and i normally shouldn't touch it.
Anyway, to get this above errors fixed, i try to withdraw the LDFLAGS like this:
export LDFLAGS='',
and after this, the make command works and i get the module kaodv.ko. But when i insert it into my ARM to check, it does not work. It actually freeze my terminal
So my question is, do i have to specify the LDFLAGS when compiling ? Does withdrawing LDFLAGS can have impact on the generated kernel module.
Actually, i try to understand where might be the problem and the only thing that come to me is that may be i should not change manually the LDFLAGS. But if i don't change de LDFLAGS, i get the unrecognized option error.
My second question related to that is, what are the possibly value of LDFLAGS
in ARM compilation
Thanks !!
echo $LDFLAGS : -Wl,-O1 -Wl,--hash-style=gnu -Wl,--as-needed -Wl,--as-needed
There are two common methods of invoking the linker in a GCC-based toolchain. One is to do it directly, but another is to use GCC as a front end to invoke the linker, rather than invoke it directly. When doing this, options intended for the linker are prefixed with -Wl, so that GCC knows to pass them through rather than interpret them itself.
In your case the error message from LD itself
arm-oe-linux-gnueabi-ld: unrecognized option '-Wl,-O1'
Indicates that your build system is passing LDFLAGS directly to the linker, and not by way of GCC.
Therefore, you should remove the -Wl, prefix and your LDFLAGS would instead be
-O1 --hash-style=gnu --as-needed --as-needed
(the duplication of the last argument is probably pointless but benign)
-O1 is an option that tells the linker to optimize. I believe it something new, and your linker may be slightly out of date. Try removing -Wl,-O1, it should still work.

How to (cross-)compile to both ARM hard- and soft-float (softfp) with a single GCC (cross-)compiler?

I'd like to use a single (cross-)compiler to compile code for different ARM calling conventions: since I always want to use floating point and NEON instructions, I just want to select the hard-float calling convention or the soft-float (softfp) calling convention.
My compiler defaults to hard-float, but it supports both architectures that I need:
$ arm-linux-gnueabihf-gcc -print-multi-lib
.;
arm-linux-gnueabi;#marm#march=armv4t#mfloat-abi=soft
$
When I compile with the default parameters:
$ arm-linux-gnueabihf-g++ -Wall -o hello_world_armhf hello_world.cpp
It succeeds without any errors.
If I compile with the parameters returned by -print-multi-lib:
$ arm-linux-gnueabihf-g++ -marm -march=armv4t -mfloat-abi=soft -Wall -o hello_world hello_world.cpp
It again compiles without error (By the way, how can I test that the resultant code is hard- or soft-float?)
Unfortunately, if I try this:
$ arm-linux-gnueabihf-g++ -march=armv7-a -mthumb-interwork -mfloat-abi=softfp -mfpu=neon -Wall -o hello_world hello_world.cpp
[...]/gcc/bin/../lib/gcc/arm-linux-gnueabihf/4.7.3/../../../../arm-linux-gnueabihf/bin/ld: error: hello_world uses VFP register arguments, /tmp/ccwvfDJo.o does not
[...]/gcc/bin/../lib/gcc/arm-linux-gnueabihf/4.7.3/../../../../arm-linux-gnueabihf/bin/ld: failed to merge target specific data of file /tmp/ccwvfDJo.o
collect2: error: ld returned 1 exit status
$
I've tested some other permutations of the parameters, but it seems that anything other than the combination shown by -print-multi-lib results in an error.
I've read ARM compilation error, VFP registered used by executable, not object file but the problem there was that some parts of the binary were soft- and some were hard-float. I have a single C++ file to compile...
What parameter(s) I miss to be able to compile with -march=armv7-a -mthumb-interwork -mfloat-abi=softfp -mfpu=neon?
How is it possible that the error is about VFP register arguments while I explicitly have -mfloat-abi=softfp in the command line which prohibits VFP register arguments?
Thanks!
For the records, hello_world.cpp contains the following:
#include <iostream>
int main()
{
std::cout << "Hello, world!" << std::endl;
return 0;
}
You need another compiler with corresponding multilib support.
You can check multilib support with next command.
arm-none-eabi-gcc -print-multi-lib
.;
thumb;#mthumb
fpu;#mfloat-abi=hard
armv6-m;#mthumb#march=armv6s-m
armv7-m;#mthumb#march=armv7-m
armv7e-m;#mthumb#march=armv7e-m
armv7-ar/thumb;#mthumb#march=armv7
cortex-m7;#mthumb#mcpu=cortex-m7
armv7e-m/softfp;#mthumb#march=armv7e-m#mfloat-abi=softfp#mfpu=fpv4-sp-d16
armv7e-m/fpu;#mthumb#march=armv7e-m#mfloat-abi=hard#mfpu=fpv4-sp-d16
armv7-ar/thumb/softfp;#mthumb#march=armv7#mfloat-abi=softfp#mfpu=vfpv3-d16
armv7-ar/thumb/fpu;#mthumb#march=armv7#mfloat-abi=hard#mfpu=vfpv3-d16
cortex-m7/softfp/fpv5-sp-d16;#mthumb#mcpu=cortex-m7#mfloat-abi=softfp#mfpu=fpv5-sp-d16
cortex-m7/softfp/fpv5-d16;#mthumb#mcpu=cortex-m7#mfloat-abi=softfp#mfpu=fpv5-d16
cortex-m7/fpu/fpv5-sp-d16;#mthumb#mcpu=cortex-m7#mfloat-abi=hard#mfpu=fpv5-sp-d16
cortex-m7/fpu/fpv5-d16;#mthumb#mcpu=cortex-m7#mfloat-abi=hard#mfpu=fpv5-d16
https://stackoverflow.com/questions/37418986/how-to-interpret-the-output-of-gcc-print-multi-lib
How to interpret the output of gcc -print-multi-lib
With this configuration gcc -mfloat-abi=hard not only will build your files using FPU instructions but also link them with corresponding libs, avoiding "X uses VFP register arguments, Y does not" error.
The above-mentioned -print-multi-lib output produced by gcc with this patch and --with-multilib-list=armv6-m,armv7,armv7-m,armv7e-m,armv7-r,armv7-a,cortex-m7 configuration option.
If you are interested in building your own gcc with Cortex-A series multilib support, just use --with-multilib-list=aprofile configuration option for any arm*-*-* target without any patches (at list with gcc-6.2.0).
As per Linaro FAQ if your compiler prints arm-linux-gnueabi;#marm#march=armv4t#mfloat-abi=soft then you can only use -march=armv4t. If you want to use -march=armv7-a you need to build compiler yourself.
Following link could be helpful in building yourself GCC ARM Builds

Resources