I have just got into ARM programming. Learned a few basics but am having issues with running code. I use VSCode on Linux Subsystem as my IDE.
I have nothing installed on my computer and i would like to run ARM code. I have read online something about "qemu" and "kernel" and stuff, but am not sure what they mean. It would be great if someone provides a detailed walkthrough for such a problem. I do not have a raspberry pi.
For example, how do i run the following division.s file on VSCode?
.global _start
_start:
MOV R1, #X
MOV R2, #Y
MOV R3, #Z
CMP R1, R2 # is x>y ?
BGT _tryx
CMP R2, R3 # is y>z ?
BGT _isy
MOV R4, R3
B _exit
_isy:
MOV R4, R2
B _exit
_tryx:
CMP R1, R3 # is x>z ?
BGT _isx
MOV R4, R3
B _exit
_isx:
MOV R4, R1
_exit:
MOV R0, R4
MOV R7, #1
SWI 0
.data
.equ X, 3
.equ Y, 5
.equ Z, 4
Are there any extensions i need to install? Is there anything i need to download? I have used gcc to compile C code. Can it be used here too?
Thx in advance! :D
Your question is rather a broad one. This being said, a slightly modified version of your program can be executed in WSL using the following procedure:
sudo apt-get install qemu-user
sudo mkdir -p /opt/arm/10
wget 'https://developer.arm.com/-/media/Files/downloads/gnu-a/10.2-2020.11/binrel/gcc-arm-10.2-2020.11-x86_64-arm-none-linux-gnueabihf.tar.xz?revision=d0b90559-3960-4e4b-9297-7ddbc3e52783&la=en&hash=985078B758BC782BC338DB947347107FBCF8EF6B' -O gcc-arm-10.2-2020.11-x86_64-arm-none-linux-gnueabihf.tar.xz
sudo tar Jxf gcc-arm-10.2-2020.11-x86_64-arm-none-linux-gnueabihf.tar.xz -C /opt/arm/10
/tmp/division.s:
# count how often we can take Y from X
.global main
main:
MOV R1, #X
MOV R2, #Y
MOV R3, #0 # Q
_loop:
CMP R1, R2
BLT _exit
SUB R1, R2
ADD R3, #1
B _loop
_exit:
MOV R0, R3
MOV R7, #1
SWI 0
.data
.equ X, 23
.equ Y, 4
Compiling:
/opt/arm/10/gcc-arm-10.2-2020.11-x86_64-arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf-gcc -static -o /tmp/division /tmp/division.s
Executing - WSL:
qemu-arm /tmp/division
echo $?
5
Which is the expected result, since 23 div 4 is 5.
Executing - Windows 10:
C:\>c:\Windows\System32\bash -c "qemu-arm /tmp/division; echo $?"
5
C:\>
Or:
C:\>c:\Windows\System32\bash -c "qemu-arm /tmp/division"
C:\>echo %ERRORLEVEL%
5
Note that division.s may have been compiled in Windows 10 as well by downloading/installing gcc-arm-10.2-2020.11-mingw-w64-i686-arm-none-linux-gnueabihf.tar.xz instead of gcc-arm-10.2-2020.11-x86_64-arm-none-linux-gnueabihf.tar.xz - your choice.
I let it to you than to go into the details of using the information above for running your program from vscode, you question being a bit too broad IMHO.
Update: division.s was compiled statically on purpose for avoiding having to specify the locations for any non-needed dynamic libraries.
Compiling it without using the -static option, and executing it would result in the following error message to be displayed:
qemu-arm division
/lib/ld-linux-armhf.so.3: No such file or directory
It can be avoided by telling qemu-arm where to look for required dynamic libraries, /lib/ld-linux-armhf.so.3 in this case:
qemu-arm -L /opt/arm/10/gcc-arm-10.2-2020.11-x86_64-arm-none-linux-gnueabihf/arm-none-linux-gnueabihf/libc division
echo $?
5
That looks like a Linux program. And I guess you have an 64-bit x86 computer.
You want QEMU, specifically the executable qemu-arm which emulates an ARM processor running Linux on another Linux compatible system. In Debian derived Linux distributions it is in the package qemu-arm.
And on ebian derived Linux distributions for a compiler you can install the gcc-arm-linux-gnueabihf package for compiling for ARMv7 and ARMv8 with hardware floating point (Debian port name armhf), or gcc-arm-linux-gnueabi for some older ARMs with no hardware floating point Debian port name armel).
If you wish to install libraries, you need add the architecture to dpkg's list, for example:
dpkg --add-architecture armhf
Related
If you look at my recent post history I went from hello world assembly to building it on linux and running on mac.
I didn't see an obvious way to implement realloc efficiently. So I'm looking into if I can link it. The issue is I'm not sure if I'm allowed to distribute mac files so I don't think I can distribute a lib file (unless I can build one myself?)
Below is my c code, that's all I'm interested in linking for the moment.
#include <stdlib.h>
int main() {
free(realloc(malloc(1024*10), 1024*100));
}
I see a stubs section in objdump
0000000100003f94 <__TEXT.__stubs>:
100003f94: b0000010 adrp x16, 100004000 <_main+0x98>
100003f98: f9400210 ldr x16, [x16]
100003f9c: d61f0200 br x16
100003fa0: b0000010 adrp x16, 100004000 <_main+0x98>
100003fa4: f9400610 ldr x16, [x16, #8]
100003fa8: d61f0200 br x16
100003fac: b0000010 adrp x16, 100004000 <_main+0x98>
100003fb0: f9400a10 ldr x16, [x16, #16]
100003fb4: d61f0200 br x16
Section:
3 __DATA_CONST.__got 00000018 0000000100004000 0000000100004000 00004000 2**3
CONTENTS, ALLOC, LOAD, DATA
I'm not sure how to put all of this together in order to link without requiring my team to copy mac specific libs to their system (and I'm sure I'm not allowed to distribute it?). Is there a way I can build a lib that I can distribute and use to cross compile?
I'm using Eclipse based CubeIDE and QEMU debugging plugin.
I'm working in assembler and can debug simple project (adding two numbers in registers) on STM32 Cortex M7 board (STM32H750DK). Now I'd like to do the same using QEMU and have problems, because I can't find suitable generic Cortex M7 machine. I've tried mps2-an500, but it doesn't work.
I've done similar already under qemu-system-gnuarmeclipse.exe on STM32F407 board and it's supported even visually (board picture), and I can see LED diodes blinking.
I'd be enough just plain emulation (not visual) for cortex M7 board.
Has anyone done something similar or any other advice how to do this properly ?
Thanks.
I think you are supposed to show some code for the attempts you made when asking a question on Stackoverflow.
In your case, this could have been a minimal, reproducible example of your attempts to use the mps2-an500 QEMU virtual machine.
This being said, a procedure for building and debugging a program using QEMU and targeting the mps2-an500 virtual machine could be:
Download QEMU 7.10 for Windows, and install it into the \opt\qemu-7.1.0 directory - you will have to create it,
Download the arm-gnu-toolchain-11.3.rel1-mingw-w64-i686-arm-none-eabi toolchain,and install into the \opt\arm\11 directory - you will have to create it,
In a directory of your choice, create the following files:
build.cmd:
#set CROSS_COMPILE=\opt\arm\11\arm-gnu-toolchain-11.3.rel1-mingw-w64-i686-arm-none-eabi\bin\arm-none-eabi-
#set CC=%CROSS_COMPILE%gcc
#set OBJDUMP=%CROSS_COMPILE%objdump
#set GDB=%CROSS_COMPILE%gdb
#set QEMU_SYSTEM_ARM=\opt\qemu-7.1.0\qemu-system-arm
#%CC% -g -mthumb -mtune=cortex-m7 -nostdlib -nostartfiles -ffreestanding -Wl,-Ttext,0x00000000 -o mps2-an500.elf startup.s
#%OBJDUMP% -d mps2-an500.elf > mps2-an500.objdump
#echo QEMU/GDB commands:
#echo %QEMU_SYSTEM_ARM% -m 16M -nographic -machine mps2-an500 -S -cpu cortex-m7 -gdb tcp::2345,ipv4 -kernel mps2-an500.elf
#echo %GDB%
startup.s:
.file "startup.s"
.arch armv7-a
.fpu vfpv3-d16
.thumb
.syntax unified
.equ __StackTop, 0x21000000
.global _start
.align 2
.long __StackTop
.long _start
_start:
mov r0,#3
mov r1,#5
add r2, r0, r1
wait: b wait
.size _start, .-_start
.end
Execute the build.cmd batch procedure, it will create mps2-an500.elf and mps2-an500.lst, and display the QEMU and GDB commands you will use for debugging the (very) basic example:
build.cmd
QEMU/GDB commands:
\opt\qemu-7.1.0\qemu-system-arm -m 16M -nographic -machine mps2-an500 -S -cpu cortex-m7 -gdb tcp::2345,ipv4 -kernel mps2-an500.elf
\opt\arm\11\arm-gnu-toolchain-11.3.rel1-mingw-w64-i686-arm-none-eabi\bin\arm-none-eabi-gdb
In one console mode session, execute:
\opt\qemu-7.1.0\qemu-system-arm -m 16M -nographic -machine mps2-an500 -S -cpu cortex-m7 -gdb tcp::2345,ipv4 -kernel mps2-an500.elf
in another, execute the GDB command:
\opt\arm\11\arm-gnu-toolchain-11.3.rel1-mingw-w64-i686-arm-none-eabi\bin\arm-none-eabi-gdb --command=mps2-an500.gdb
In the GDB session, execute the following commands in sequence:
target remote localhost:2345
file mps2-an500.elf
break _start
break wait
set $sp = 0x21000000
set $pc = _start
stepi
stepi
stepi
info registers
The transcript for the GDB session should look like:
\opt\arm\11\arm-gnu-toolchain-11.3.rel1-mingw-w64-i686-arm-none-eabi\bin\arm-none-eabi-gdb
GNU gdb (Arm GNU Toolchain 11.3.Rel1) 12.1.90.20220802-git
Copyright (C) 2022 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "--host=i686-w64-mingw32 --target=arm-none-eabi".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://bugs.linaro.org/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word".
(gdb) target remote localhost:2345
Remote debugging using localhost:2345
warning: No executable has been specified and target does not support
determining executable automatically. Try using the "file" command.
0x00000014 in ?? ()
(gdb) file mps2-an500.elf
A program is being debugged already.
Are you sure you want to change the file? (y or n) y
Reading symbols from mps2-an500.elf...
(gdb) break _start
Breakpoint 1 at 0x8: file startup.s, line 12.
(gdb) break wait
Breakpoint 2 at 0x14: file startup.s, line 15.
(gdb) set $sp = 0x21000000
(gdb) set $pc = _start
(gdb) stepi
13 mov r1,#5
(gdb) stepi
14 add r2, r0, r1
(gdb) stepi
Breakpoint 2, _start () at startup.s:15
15 wait: b wait
(gdb) info registers
r0 0x3 3
r1 0x5 5
r2 0x8 8
r3 0x0 0
r4 0x0 0
r5 0x0 0
r6 0x0 0
r7 0x0 0
r8 0x0 0
r9 0x0 0
r10 0x0 0
r11 0x0 0
r12 0x0 0
sp 0x21000000 0x21000000
lr 0xfffffff9 -7
pc 0x14 0x14 <_start+12>
xpsr 0x41000003 1090519043
fpscr 0x0 0
(gdb)
I have compiled and ran a simple hello world program on ARM Foundation Platform. The code is given below.
#include <stdio.h>
int main(int argc, char *argv[])
{
printf("Hello world!\n");
return 0;
}
This program is compiled and linked using ARM GNU tool chain as given below.
$aarch64-none-elf-gcc -c -march=armv8-a -g hello.c hello.o
aarch64-none-elf-gcc: warning: hello.o: linker input file unused because linking not done
$aarch64-none-elf-gcc -specs=aem-ve.specs -Wl,-Map=linkmap.txt hello.o -o hello.axf
I could successfully execute this program on ARM Foundation Platform (I think, foundation platform is similar to ARM Fixed Virtual Platform) and it prints "Hello world!"
The contents of 'aem-ve.specs' file is given below:
cat ./aarch64-none-elf/lib/aem-ve.specs
# aem-ve.specs
#
# Spec file for AArch64 baremetal newlib, libgloss on VE platform with version 2
# of AngelAPI semi-hosting.
#
# This Spec file is also appropriate for the foundation model.
%rename link old_link
*link:
-Ttext-segment 0x80000000 %(old_link)
%rename lib libc
*libgloss:
-lrdimon
*lib:
cpu-init/rdimon-aem-el3.o%s --start-group %(libc) %(libgloss) --end-group
*startfile:
crti%O%s crtbegin%O%s %{!pg:rdimon-crt0%O%s} %{pg:rdimon-crt0%O%s}
Is it possible to execute the same binary on QEMU? If so could you please share the sample command. If not, could you please share the right and best way to execute it on QEMU?
I have traced instruction execution of Foundation platform using the "trace" option and analysed using 'tarmac-profile' tool and 'Tarmac-calltree' tool. The outputs are given below:
$ ./tarmac-profile hello.trace --image hello.axf
Address Count Time Function name
0x80000000 1 120001
0x80001030 1 30001 register_fini
0x80001050 1 60001 deregister_tm_clones
0x800010c0 1 210001 __do_global_dtors_aux
0x80001148 1 23500001 frame_dummy
0x800012c0 1 10480001 main
0x80002000 1 40001 main
0x80003784 1 360001 main
0x80003818 1 460001 _cpu_init_hook
0x80003870 1 590001 _write_r
0x800038d0 1 1090001 __call_exitprocs
...
...
./tarmac-calltree hello.trace --image hello.axf
o t:10000 l:2288 pc:0x80001148 - t:23510000 l:7819 pc:0x80008060 : frame_dummy
- t:240000 l:2338 pc:0x800011a8 - t:720000 l:2443 pc:0x800011ac
o t:250000 l:2340 pc:0x80003818 - t:710000 l:2442 pc:0x80003828 : _cpu_init_hook
- t:260000 l:2343 pc:0x8000381c - t:320000 l:2354 pc:0x80003820
o t:270000 l:2345 pc:0x80002000 - t:310000 l:2353 pc:0x80002010 : main
- t:320000 l:2354 pc:0x80003820 - t:700000 l:2436 pc:0x80003824
o t:330000 l:2356 pc:0x80003784 - t:690000 l:2435 pc:0x80003814 : main
- t:760000 l:2453 pc:0x800011bc - t:2010000 l:2970 pc:0x800011c0
o t:770000 l:2455 pc:0x80004200 - t:2000000 l:2969 pc:0x800042c0 : memset
- t:2010000 l:2970 pc:0x800011c0 - t:4870000 l:3587 pc:0x800011c4
o t:2020000 l:2972 pc:0x80007970 - t:4860000 l:3586 pc:0x80007b04 : initialise_monitor_handles
- t:2960000 l:3165 pc:0x80007b24 - t:4340000 l:3465 pc:0x80007b28
I have tried the following method to execute on QEMU, without any success.
I have started the QEMU with gdb based debugging through TCP Port
$ qemu-system-aarch64 -semihosting -m 128M -nographic -monitor none -serial stdio -machine virt,gic-version=2,secure=on,virtualization=on -cpu cortex-a53 -kernel hello.axf -S -gdb tcp::9000
The result of debug session is given below:
gdb) target remote localhost:9000
Remote debugging using localhost:9000
_start () at /data/jenkins/workspace/GNU-toolchain/arm-11/src/newlib-cygwin/libgloss/aarch64/crt0.S:90
90 /data/jenkins/workspace/GNU-toolchain/arm-11/src/newlib-cygwin/libgloss/aarch64/crt0.S: No such file or directory.
(gdb) si
<The system hangs here>
I have tried to disassemble the code using gdb and its output is given below. It looks like the code is not loaded correctly
(gdb) disas frame_dummy
Dump of assembler code for function frame_dummy:
0x0000000080001110 <+0>: udf #0
0x0000000080001114 <+4>: udf #0
0x0000000080001118 <+8>: udf #0
0x000000008000111c <+12>: udf #0
0x0000000080001120 <+16>: udf #0
0x0000000080001124 <+20>: udf #0
0x0000000080001128 <+24>: udf #0
0x000000008000112c <+28>: udf #0
0x0000000080001130 <+32>: udf #0
0x0000000080001134 <+36>: udf #0
0x0000000080001138 <+40>: udf #0
0x000000008000113c <+44>: udf #0
0x0000000080001140 <+48>: udf #0
0x0000000080001144 <+52>: udf #0
End of assembler dump.
Could you please shed some light on this. Any hint given is appreciated very much.
The Foundation Model and the QEMU 'virt' board are not the same machine type. They have different devices at different physical addresses, and in particular they do not have RAM at the same address. To run bare metal code on the 'virt' machine type you will need to adjust your code. This is normal for bare metal -- the whole point is that you are running directly on some piece of (emulated) hardware and need to match the details of it.
Specifically, the minimum change you need to make here is that the RAM on the 'virt' board starts at 0x4000_0000, not the 0x8000_0000 that the Foundation Model uses. There may be others as well, but that is the immediate cause of what you're seeing.
I'm trying to develop a program on a bare metal architecture (stm32f4 board with ARM cortex-m4f processor). I'm experiencing a strange problem with the ABI call __aebi_idivmod.
The call is generated by the compiler because in the code there is multiple use of the % operand.The strange thing is that if I look at the dump of the .elf generated by the compilation using objdump, what I get is:
080080e0 <__aeabi_idivmod>:
80080e0: e3510000 cmp r1, #0
80080e4: 0afffff9 beq 80080d0 <.divsi3_skip_div0_test+0x110>
80080e8: e92d4003 push {r0, r1, lr}
80080ec: ebffffb3 bl 8007fc0 <.divsi3_skip_div0_test>
80080f0: e8bd4006 pop {r1, r2, lr}
80080f4: e0030092 mul r3, r2, r0
80080f8: e0411003 sub r1, r1, r3
80080fc: e12fff1e bx lr
On the other hand when I run the program on the board, if I look at the actual ABI call code using gdb I get:
(gdb) disas __aeabi_idivmod
Dump of assembler code for function __aeabi_idivmod:
0x080080e0 <+0>: movs r0, r0
0x080080e2 <+2>: b.n 0x8008788
0x080080e4 <+4>: ; <UNDEFINED> instruction: 0xfff90aff
0x080080e8 <+8>: ands r3, r0
0x080080ea <+10>: stmdb sp!, {r0, r1, r4, r5, r7, r8, r9, r10, r11, r12, sp, lr, pc}
0x080080ee <+14>: ; <UNDEFINED> instruction: 0xebff4006
0x080080f2 <+18>: ldmia.w sp!, {r1, r4, r7}
0x080080f6 <+22>: b.n 0x8008100 <__aeabi_ldiv0>
0x080080f8 <+24>: asrs r3, r0, #32
0x080080fa <+26>: b.n 0x8008180 <K+120>
0x080080fc <+28>: vrhadd.u16 d14, d14, d31
End of assembler dump.
The location of the code corresponds, but it is completely wrong: there are two instructions that are not even recognized by gdb and the second instruction:
0x080080e2 <+2>: b.n 0x8008788
is jumping outside of the .text section of the process that ends at 0x866c:
Loading section .text, size 0x866c lma 0x8000000
Any clue about I'm doing wrong? Any help would be appreciated.
EDIT: I'm using this compiler: https://launchpad.net/gcc-arm-embedded/+download
arm-none-eabi-gcc --version
arm-none-eabi-gcc (GNU Tools for ARM Embedded Processors) 4.8.3 20131129 (release) [ARM/embedded-4_8-branch revision 205641]
EDIT2 (following the response of Notlikethat): The command that I use to link my program is this:
$(ARMGNU)-ld -o program.gcc.thumb.elf -T memmap vectors.o program.gcc.thumb.o $(OBJS2) $(LIBGCC)
memmap is the linker script, vectors.o contains startup code, $(OBJS2) contains all the object files that must be linked together and $(LIBGCC) is the variable referencing the library containing the ABI call that we're discussing. From what I've understood from the comment of Notlikethat I should add some option here, to force the compilation of the libgcc to thumb code instead of normale ARM code. I've tried to add to the linker these options, but I'm getting the same error in both cases:
arm-none-eabi-ld: unrecognised emulation mode: cpu=cortex-m3
Supported emulations: armelf
EDIT3: I have realized only in this moment that the processor of my board is cortex-m4f not cortex-m3. I don't think this should not make much different.
The problem is that your toolchain, by default, has its standard library compiled to ARM instructions - since the Cortex-M processors only support Thumb-2*, they will fault when attempting to use an interworking branch to ARM code.
Now, the -mthumb option tells the compiler to compile your code to Thumb - the standard library is pre-compiled and doesn't even come into the picture until link time. Fortunately, the readme says that toolchain is multilib enabled, so you can pick a suitable library without having to find a v7-M-specific toolchain.
From what I understand of the inner workings of multilib, you have to provide the exact magic combination of options it's looking for, which in this case would be either:
-mthumb -mcpu=cortex-m4
or:
-mthumb -march=armv7e-m
which will cause GCC to give the linker a suitably compatible library instead of the default (there are further options listed in the docs if you need floating-point support too).
If you're using a seperate link step, I believe you need to use gcc as the link command in place of ld for multilib to work correctly - that's certainly the case for the Linaro toolchain I have here (which only has two-way hard-float/ARMv4+soft-float multilib).
* strictly, not-quite-all-of-Thumb-2
I'm trying to write the following program to dump the interrupt vector table using FreeDOS in a virtual machine. I know that DEBUG will allow me to write an assembly program, but how do I create the following IVTDUMP.COM file, save it and run it?
Note: I'd like to try and do it all straight from FreeDOS if possible. I tried using the EDIT command but it errors out and I'm pretty sure I'm missing something.
for
(
address=IDT_255_ADDR;
address>=IDT_001_ADDR;
address=address-IDT_VECTOR_SZ,vector--
)
{
printf("%03d %08p ",vector,address);
__asm
{
PUSH ES
MOV AX,0
MOV ES,AX
MOV BX,address
MOV AX,ES:[BX]
MOV ipAddr,AX
INC BX
INC BX
MOV AX,ES:[BX]
MOV csAddr,AX
POP ES
};
printf("[CS:IP]=[%04X,%04X]\n",csAddr,ipAddr);
}
Things like for, address and printf are not part of assembly. You will have to rewrite that to actual assembly code or copy the macros and assembler you want to use to your freedos environment.
If you want to use debug as included in freedos you can use the a command to start writing assembly instructions, the n command to give a name and w to write the code to the file.
C:\debug
-a
06BC:0100 int 20
06BC:0102
-n ivtdump.com
-rcx 2
-w
Writing 0002 bytes.
-q
C:\
This sample program only exits the program through int 20. (The 2 after rcx indicates the length of the program to write to disk)