Reputation: 512
I have these two files, 9-main.c and 9-mult.s.
9-main.c :
#include <stdio.h>
int mult(int);
int main(){
int result = mult(5);
printf("Result: %d\n",result);
return 0;
}
9-mult.s :
.global mult
mult:
mov r1,r0
lsl r0,r0,#4
add r0,r1,r0
bx lr
I am trying to link them:
$ arm-linux-gnueabihf-gcc 9-main.c 9-mult.s
But if I execute a.out:
$ ./a.out
Illegal instruction
What could be the reason?
Upvotes: 0
Views: 852
Reputation: 71536
You really need to get the arm documentation and learn the instruction set.
You should disassemble your program to confirm. The first question is are you cross compiling this on an x86 and then trying to run ARM on x86? I think you would get a different error. Next is it an a.out file format issue, that may be primitive so trying to run aarch32 in aarch64 mode and would be bad instructions or...
For armv7-a compatibility mode on a 64 bit arm (armv8...) if told that or built for that the gcc compiler will default to thumb/thumb2 mode.
int mult(int);
int fun ( void )
{
return(mult(5)+1);
}
00000000 <fun>:
0: e96d 3e02 strd r3, lr, [sp, #-8]!
4: f04f 0005 mov.w r0, #5
8: f7ff fffe bl 0 <mult>
c: f100 0001 add.w r0, r0, #1
10: bd08 pop {r3, pc}
12: bf00 nop
then as written
.global mult
mult:
mov r1,r0
lsl r0,r0,#4
add r0,r1,r0
bx lr
will default to arm unless specified otherwise on the command line (which it wasn't)
00000000 <mult>:
0: e1a01000 mov r1, r0
4: e1a00200 lsl r0, r0, #4
8: e0810000 add r0, r1, r0
c: e12fff1e bx lr
because mult is not marked as a function label in the eyes of gnu linker then when you link it
00002000 <fun>:
2000: e96d 3e02 strd r3, lr, [sp, #-8]!
2004: f04f 0005 mov.w r0, #5
2008: f000 f804 bl 2014 <mult>
200c: f100 0001 add.w r0, r0, #1
2010: bd08 pop {r3, pc}
2012: bf00 nop
00002014 <mult>:
2014: e1a01000 mov r1, r0
2018: e1a00200 lsl r0, r0, #4
201c: e0810000 add r0, r1, r0
2020: e12fff1e bx lr
boom, there is your fault. bl to mult without a trampoline means it is in thumb mode and now it is trying to execute those bytes as thumb instructions not arm. It might survive for a while but will eventually fault.
(Frant has confirmed this for you, assuming this is the path you took to this particular fault)
So you have a few choices, can read Frant's answer:
.global mult
.type mult, %function
mult:
mov r1,r0
lsl r0,r0,#4
add r0,r1,r0
bx lr
Tell the tools that mult is a function label, re-assemble and re-link:
00002000 <fun>:
2000: e96d 3e02 strd r3, lr, [sp, #-8]!
2004: f04f 0005 mov.w r0, #5
2008: f000 e804 blx 2014 <mult>
200c: f100 0001 add.w r0, r0, #1
2010: bd08 pop {r3, pc}
2012: bf00 nop
00002014 <mult>:
2014: e1a01000 mov r1, r0
2018: e1a00200 lsl r0, r0, #4
201c: e0810000 add r0, r1, r0
2020: e12fff1e bx lr
Because it is an armv7 we got the blx thumb2 extension, if I specify armv4t across the project then we get the trampoline I was expecting. Both are fine depends on the instruction set, and armv4t will work on the aarch32 compatibility mode side of the 64 bit processor.
00002000 <fun>:
2000: b510 push {r4, lr}
2002: 2005 movs r0, #5
2004: f000 f80c bl 2020 <__mult_from_thumb>
2008: 3001 adds r0, #1
200a: bc10 pop {r4}
200c: bc02 pop {r1}
200e: 4708 bx r1
00002010 <mult>:
2010: e1a01000 mov r1, r0
2014: e1a00200 lsl r0, r0, #4
2018: e0810000 add r0, r1, r0
201c: e12fff1e bx lr
00002020 <__mult_from_thumb>:
2020: 4778 bx pc
2022: e7fd b.n 2020 <__mult_from_thumb>
2024: eafffff9 b 2010 <mult>
The linker adds the mult_from_thumb trampoline to convert between thumb mode and arm mode. bx lr can handle mode switches so you don't need a trampoline on the way out only on the way in.
Your other option would be to build mult as thumb
.thumb
.global mult
mult:
mov r1,r0
lsl r0,r0,#4
add r0,r1,r0
bx lr
00002000 <fun>:
2000: b510 push {r4, lr}
2002: 2005 movs r0, #5
2004: f000 f804 bl 2010 <mult>
2008: 3001 adds r0, #1
200a: bc10 pop {r4}
200c: bc02 pop {r1}
200e: 4708 bx r1
00002010 <mult>:
2010: 1c01 adds r1, r0, #0
2012: 0100 lsls r0, r0, #4
2014: 1808 adds r0, r1, r0
2016: 4770 bx lr
But this would fault if called from arm mode code.
With gnu assembler you can use .type...function or .thumb_func, to make this more correct.
.thumb
.global mult
.thumb_func
mult:
mov r1,r0
lsl r0,r0,#4
add r0,r1,r0
bx lr
00002000 <fun>:
2000: b510 push {r4, lr}
2002: 2005 movs r0, #5
2004: f000 f804 bl 2010 <mult>
2008: 3001 adds r0, #1
200a: bc10 pop {r4}
200c: bc02 pop {r1}
200e: 4708 bx r1
00002010 <mult>:
2010: 1c01 adds r1, r0, #0
2012: 0100 lsls r0, r0, #4
2014: 1808 adds r0, r1, r0
2016: 4770 bx lr
Same machine code so the .thumb would have made it work dumb luck, but declaring the label as a function is as important as declaring it as global. With the .type you can put that wherever like you can with .globl but .thumb_func has to be before the label, not immediately but basically the next label it finds it will mark as a function.
So after
arm-linux-gnueabihf-gcc 9-main.c 9-mult.s -o myprog.elf
while you are learning asm, and even sometimes when not, certainly if you are mixing languages you should always follow that with:
arm-linux-gnueabihf-objdump -D myprog.elf
and examine the output.
Can use lowercase -d for just the code, uppercase -D will also include the data sections which are often as important to see that it built what you wanted it to build.
To complete asking your question I recommend you disassemble and post the main function and the mult function to see if it is an interwork problem. Or did you instead try to run an arm binary on an x86 or something like that?
Frant has shown a more verbose set of directives, if you examine the full output of the compiler
.cpu cortex-a72
.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, 2
.eabi_attribute 34, 1
.eabi_attribute 18, 4
.file "so.c"
.text
.align 1
.p2align 2,,3
.global fun
.arch armv8-a
.arch_extension crc
.syntax unified
.thumb
.thumb_func
.fpu softvfp
.type fun, %function
fun:
@ args = 0, pretend = 0, frame = 0
@ frame_needed = 0, uses_anonymous_args = 0
strd r3, lr, [sp, #-8]!
mov r0, #5
bl mult
add r0, r0, #1
pop {r3, pc}
.size fun, .-fun
.ident "GCC: (GNU) 10.2.0"
You will see among other things the .type function. They double dipped here and did both the .type and .thumb_func which makes sense, this is an algorithmically driven output.
Upvotes: 2
Reputation: 5895
You are compiling/executing in a non-trivial environment for a beginner: compiling/linking/executing a mix of aarch32
C/assembly code on a aarch64
system.
I would suggest to start specifying which architecture you are assembling for as well as the exact instruction set you are using - this is useful information for people who want to help - and to use some standard directives for defining functions in GNU AS assembly language, the way they would be generated by the gcc compiler:
.arch armv7-a // target architecture is armv7-a/Aarch32
.fpu vfpv3-d16 // specify available floating-point hardware
.syntax unified // use unified assembler syntax
.arm // generate 32 bit Arm code
.type mult, %function // 'mult' symbol is related to a function
.global mult // 'mult' is a global symbol
.text // code will be put into the .text section
mult:
mov r1,r0
lsl r0,r0,#4
add r0,r1,r0
bx lr
.end // end of program
./a.out
Result: 85
Please refer to the GNU AS documentation for more details.
I am still looking for the root cause of your problem, which was actually fixed by using .type mult, %function
. Note that the exact same error does occur on a Cortex-A7
Linux system, that is the fact that use are running a 64 bit Linux is not the issue.
Compiling two executables (with/without the .type directive) and comparing the outputs of the readelf -a
command shows only one difference:
267c267
< 104: 00000534 0 NOTYPE GLOBAL DEFAULT 13 mult
---
> 104: 00000534 0 FUNC GLOBAL DEFAULT 13 mult
UPDATE:
The reason for your problem was pointed-out by old_timer - see his comment hereafter.
Your compiler is generating thumb2/T32 code by default, but your assembly code was arm/A32 by default since you did not specify which instruction set you where using. Compiling you original code with gcc -marm
option (arm/A32) works fine:
arm-linux-gnueabihf-gcc-10 -g -marm -o mult mult.c mult.s
./mult
Result: 85
The other option would be to force GNU AS to generate thumb2/T32 code in mult.s:
.thumb
.global mult
mult:
mov r1,r0
lsl r0,r0,#4
add r0,r1,r0
bx lr
arm-linux-gnueabihf-gcc-10 -g -o mult mult.c mult.s
./mult
Result: 85
For more details on thumb2/T32, arm/A32 and interwork code, see here and here.
Upvotes: 0