0

我有这两个文件,9-main.c 和 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 倍数

.global mult

mult:
        mov     r1,r0
        lsl     r0,r0,#4
        add     r0,r1,r0
        bx      lr

我正在尝试链接它们:

$ arm-linux-gnueabihf-gcc 9-main.c 9-mult.s

但是如果我执行 a.out:

$ ./a.out 
Illegal instruction

可能是什么原因?

4

2 回答 2

2

您确实需要获取 arm 文档并学习指令集。

你应该反汇编你的程序来确认。第一个问题是您是否在 x86 上交叉编译它,然后尝试在 x86 上运行 ARM?我想你会得到一个不同的错误。接下来是 a.out 文件格式问题,这可能是原始的,因此尝试在 aarch64 模式下运行 aarch32 并且会是错误的指令或......

对于 64 位 arm (armv8...) 上的 armv7-a 兼容模式,如果被告知或为此构建,gcc 编译器将默认为 thumb/thumb2 模式。

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

然后写成

.global mult
mult:
        mov     r1,r0
        lsl     r0,r0,#4
        add     r0,r1,r0
        bx      lr

除非在命令行上另有指定(不是),否则将默认为 arm

00000000 <mult>:
   0:   e1a01000    mov r1, r0
   4:   e1a00200    lsl r0, r0, #4
   8:   e0810000    add r0, r1, r0
   c:   e12fff1e    bx  lr

因为 mult 在 gnu 链接器眼中没有被标记为函数标签,所以当你链接它时

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

繁荣,有你的错。bl to mult without a trampoline 意味着它处于拇指模式,现在它正试图将这些字节作为拇指指令而不是 arm 来执行。它可能会存活一段时间,但最终会出现故障。

(弗兰特已经为您确认了这一点,假设这是您解决此特定故障的路径)

所以你有几个选择,可以阅读弗兰特的回答:

.global mult
.type   mult, %function
mult:
        mov     r1,r0
        lsl     r0,r0,#4
        add     r0,r1,r0
        bx      lr

告诉工具mult是一个函数标签,重新组装重新链接:

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

因为它是一个 armv7,所以我们得到了 blx thumb2 扩展,如果我在整个项目中指定 armv4t,那么我们就会得到我所期待的蹦床。两者都很好,取决于指令集,armv4t 将在 64 位处理器的 aarch32 兼容模式端工作。

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>

链接器添加 mult_from_thumb trampoline 以在拇指模式和手臂模式之间进行转换。bx lr 可以处理模式切换,因此您在出去的时候不需要蹦床,只在进来的时候。

您的另一个选择是构建 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

但是,如果从 arm 模式代码调用,这会出错。

使用 gnu 汇编器,您可以使用 .type...function 或 .thumb_func 来使其更正确。

.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

相同的机器代码,因此 .thumb 会使其工作很糟糕,但是将标签声明为函数与将其声明为全局一样重要。使用 .type 您可以像使用 .globl 一样将其放在任何位置,但 .thumb_func 必须在标签之前,而不是立即,但基本上它找到的下一个标签将标记为函数。

所以之后

arm-linux-gnueabihf-gcc 9-main.c 9-mult.s -o myprog.elf

当你在学习 asm 时,甚至有时不是时,当然,如果你正在混合语言,你应该始终遵循:

arm-linux-gnueabihf-objdump -D myprog.elf

并检查输出。

可以仅对代码使用小写 -d,大写 -D 还将包括数据部分,这些部分通常对于查看它构建了您想要构建的内容同样重要。

为了完成你的问题,我建议你反汇编并发布 main 函数和 mult 函数,看看它是否是一个互通问题。或者您是否尝试在 x86 或类似的东西上运行 arm 二进制文件?

如果您检查编译器的完整输出,Frant 已经展示了一组更详细的指令

    .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"

除其他外,您将看到 .type 函数。他们在这里双浸并做了 .type 和 .thumb_func 这很有意义,这是一个算法驱动的输出。

于 2020-09-10T14:39:44.800 回答
0

您正在为初学者在不平凡的环境中编译/执行:在系统上编译/链接/执行混合的aarch32C/汇编代码aarch64

我建议开始指定您正在组装的架构以及您正在使用的确切指令集 - 这对于想要帮助的人来说是有用的信息 - 并使用一些标准指令来定义 GNU AS 汇编语言中的函数,它们将由 gcc 编译器生成的方式:

        .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

有关详细信息,请参阅GNU AS 文档。

我仍在寻找您的问题的根本原因,这实际上是通过使用.type mult, %function. 请注意,在 Linux 系统上确实会发生完全相同的错误Cortex-A7,即使用运行 64 位 Linux 的事实不是问题。

编译两个可执行文件(带/不带 .type 指令)并比较readelf -a命令的输出仅显示一个区别:

267c267
<    104: 00000534     0 NOTYPE  GLOBAL DEFAULT   13 mult
---
>    104: 00000534     0 FUNC    GLOBAL DEFAULT   13 mult

更新:

old_timer指出了您的问题的原因- 请参阅他的评论。

您的编译器默认生成 thumb2/T32 代码,但您的汇编代码默认为 arm/A32,因为您没有指定使用哪个指令集。-marm使用 gcc选项(arm/A32)编译原始代码可以正常工作:

arm-linux-gnueabihf-gcc-10 -g -marm -o mult mult.c mult.s
./mult
Result: 85

另一种选择是强制 GNU AS 在 mult.s 中生成 thumb2/T32 代码:

.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

有关 thumb2/T32、arm/A32 和互通代码的更多详细信息,请参见此处此处

于 2020-09-10T11:38:11.643 回答