Is there a command to run any ARM assembly file (.s) after having installed qemu

1

I have recently run the following commands to install qemu.

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

Now there are a no. of ARM files i would like to compile and execute on my linux subsystem. It would be great if someone could provide a walkthrough for this issue. I have included a no. of links below to some example files. I am looking for a general command that will successfully compile and run all these files. Just like in C where gcc -o test test.c would compile and run any C file. I do not have a raspberry pi

Here r a few example ARM programs:

.global _start
_start:
        MOV R1, #X
        MOV R2, #Y
        MOV R3, #Z
        MUL R5, R1, R1
        MUL R6, R2, R2
        ADD R5, R6
        MUL R6, R3, R3
        CMP R5, R6
        BEQ _same
        MOV R4, #0
        B _exit
_same:
        MOV R4, #1
_exit:
        MOV R0,  R4
        MOV R7,  #1
        SWI 0
.data
.global _start
_start:
        LDR R1, =X
        LDR R2, =Y
        LDR R3, =T
        LDR R4, [R1]
        STR R4, [R3]
        LDR R4, [R2]
        STR R4, [R1]
        LDR R4, [R3]
        STR R4, [R2]
_exit:
        MOV R0, #0
        MOV R7, #1
        SWI 0

.data
X: .word 0x16
Y: .word 0x21
T: .word 0x00
@ binary search for V in 25 element array.
        @ returns index for V if V is in the array.
        @ otherwise returns length of array (25).

        .global _start
_start:
        @ populate the array A
        LDR R1,=A
        MOV R2,#0x00
_populate_array:
        CMP R2,#MAX @ i == 25
        BEQ _search
        STR R2,[R1]
        ADD R1,#0x04
        ADD R2,#0x1
        B _populate_array
_search:
        MOV R6, #4      @ R6 == 4 
        MOV R1, #0      @ R1 == left
        MOV R2, #MAX    @ R2 == right
        SUB R2, #1      @ R2 = MAX-1
_loop:
        CMP R1, R2      @ is left>right ?
        BGT _fail

        @ compute new middle position
        ADD R3, R1, R2  @ R3 = left + right
        LSR R3, R3, #1  @ middle = (left + right)/2
        @ each integer element uses 4 bytes of memory
        @ R7 is the memory address for the middle integer value in array A
        MUL R7, R3, R6  @ R7 = middle * 4

        @ obtain middle value at middle position
        LDR R4, =A      @ R4 == &A    (load address of A's 1st byte)
        ADD R4, R7      @ R4 == &A[m] (add offset of m to that address)
        LDR R5, [R4]    @ R5 == A[m]  (get value in A at that address)

        @ compare middle value to searched for value V
        CMP R5, #V      @ V==A[m]?
        BEQ _exit       @ we've found the value V
        BGT _left       @ is V somewhere in left half?
        
        @ if not, V must be somewhere in right half if at all
        MOV R1, R3      @ left = middle
        ADD R1, #1      @ left = left + 1
        B _loop

_left:                  @ A[m]<V (V would be somewhere in left half)
        MOV R2, R3      @ right = middle;
        SUB R2, #1      @ right = right - 1
        B _loop
        
_fail: 
        MOV R3, #MAX
_exit:
        MOV R0, R5
        MOV R7, #1
        SWI 0
.data
.equ MAX, 25
A: .rept MAX
   .word 0
   .endr
.equ V, 17
.global main
main:
    @ Stack the return address (lr) in addition to a dummy register (ip) to
    @ keep the stack 8-byte aligned.
    push    {ip, lr}

    @ Load the argument and perform the call. This is like 'printf("...")' in C.
    ldr     r0, =message
    bl      printf

    @ Exit from 'main'. This is like 'return 0' in C.
    mov     r0, #0    @ Return 0.

    @ Pop the dummy ip to reverse our alignment fix, and pop the original lr
    @ value directly into pc — the Program Counter — to return.
    pop     {ip, pc}

    @ --------------------------------
    @ Data for the printf calls. The GNU assembler's ".asciz" directive
    @ automatically adds a NULL character termination.
message:
    .asciz "Hello, world.\n"
@ Here's how we will use the ARM registers R1-R5:
        @
        @ R1 - address of the next word
        @ R2 - the character counter
        @ R3 - contents of the next word
        @ R4 - next byte
        @ R5 - byte count

        .global _start

        @ program starts here
_start:
        LDR R1, =mystring

        @ initialise the character count (R2) to be 0
        MOV R2,#0

nextw:
        @ load the address of the next word into R3
        LDR R3,[R1]
        @ add 4 to the byte count
        MOV R5, #4

nextb:
        @ load next byte from word into R4
        MOV R4, R3
        @ use bitmask to get next byte value
        AND R4, #MASK
        @ if byte in R4 is 0, then we've found the "end of string" value
        CMP R4, #EOS
        BEQ endl
        @ otherwise, increment the character count by 1
    ADD R2, #1
        @ decrement bytes by 1
        SUB R5, #1
        @ while (bytes !=0) from the slide
        CMP R5, #0
        BEQ incr
        @ shift the contents of the word 8 bits right
        LSR R3, #8
        B nextb

incr:
        @ move to the next word address
        ADD R1,#4
        B nextw

endl:
        @ end of string '0' value found, we're done.
        @
        @ return character count as exit value
        MOV R0, R2
        MOV R7, #1
        SWI 0

.data
.equ MASK, 0x000000ff
.equ EOS, 0
mystring: .asciz "0123456789"

@ try other string examples
@ mystring: .asciz "thunderbirds"

Thx in advance!

assembly
gcc
arm
cross-compiling
qemu
asked on Stack Overflow Feb 12, 2021 by Craig Hemsforth • edited Feb 12, 2021 by Peter Cordes

1 Answer

0

The answer to your question is no, because it depends on the system your assembly code was targeting, and on what it is doing (I am just mentioning it here, but there is not really such a thing as an ARM assembly file, since such a file may use different instruction sets, such as arm/a32, or thumb2/t32).

For example, when compiling on a Linux system with gcc, the entry point is assumed to be main by default, not _start - this is why you first program had to be slightly modified - you could have used a program such as meld or winmerge for identifying the differences between the modified/unmodified versions. The program has to use the Linux system calls interface as well, which is the case of all your programs. See here for more information on SVC/SWI and the Linux system calls interface.

This article is a worth reading as well for understanding what does happen prior to the entry point to be called.

And if you wanted to execute programs directly running on an emulated ARM processor, using qemu-system-arm and arm-none-eabi-gcc would be the solution, but I would not recommend it until you will be more knowledgeable.

If you want to become familiar with ARM assembly on Windows, using WSL and qemu-arm is an excellent solution.

I am assuming here you replaced _start by main in the example programs you provided.

Your first program cannot even be assembled - make sure it does prior to use it in a question.

Your second program does compile/execute using:

/opt/arm/10/gcc-arm-10.2-2020.11-x86_64-arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf-gcc -static -o 2 2.s
 qemu-arm 2

Your third program does compile/execute using:

/opt/arm/10/gcc-arm-10.2-2020.11-x86_64-arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf-gcc -static -o 3 3.s
 qemu-arm 3

Your fourth program does compile/execute using:

 /opt/arm/10/gcc-arm-10.2-2020.11-x86_64-arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf-gcc -static -o 4 4.s
qemu-arm 4
Hello, world.

Your fifth program does compile/execute using:

/opt/arm/10/gcc-arm-10.2-2020.11-x86_64-arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf-gcc -static -o 5 5.s
qemu-arm 5

Bottom line, once modified, all you programs but the first one, that need to be fixed, were compiled with a 'unique' command, and executed with a 'unique' other one.


You can somewhat simplify the commands to be used for compiling/running your programs by:

Installing binfmt-support in WSL:

sudo apt-get install binfmt-support

Creating a file named etc/binfmt.d/qemu-arm.conf containing those two lines:

# /etc/binfmt.d/qemu-arm.conf                                                                                                                                                                                                                                         
:arm:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff://opt/qemu-4.2.0-static/bin/qemu-arm:OC

This will cause your system to transparently use qemu-arm for running your executable:

qemu-arm 4
Hello, world.

could then be replaced by:

./4
Hello, world.

Adding the location for the arm-none-linux-gnueabihf toolchain executables to your PATH. This can be for example be done by adding the following line to the .bashrc file present in your user home directory:

PATH=/opt/arm/10/gcc-arm-10.2-2020.11-x86_64-arm-none-linux-gnueabihf/bin:${PATH}

The command for compiling could then be:

 arm-none-linux-gnueabihf-gcc -static -o 4 4.s
answered on Stack Overflow Feb 12, 2021 by Frant • edited Feb 14, 2021 by Frant

User contributions licensed under CC BY-SA 3.0