【问题标题】:Can _start be the thumb function?_start 可以是拇指功能吗?
【发布时间】:2013-12-20 14:09:12
【问题描述】:

请帮助我使用 arm926ejs cpu 的 gnu 汇编器。

我尝试构建一个简单的程序(test.S):

.global _start 
_start:
    mov r0, #2
    bx lr

并成功构建它:

arm-none-linux-gnueabi-as -mthumb -o test.o test.S
arm-none-linux-gnueabi-ld -o test test.o

但是当我在arm目标linux环境下运行程序时,出现错误:

./test 
Segmentation fault

我做错了什么? _start 函数可以是拇指函数吗? 要么 总是arm func?

【问题讨论】:

  • 您是否尝试过使用 gdb 获取堆栈跟踪?
  • (gdb) target remote 192.168.3.16:1234 在 ?? 中使用 192.168.3.16:1234 进行远程调试 0x00008054 () (gdb) 回溯 #0 0x00008054 in ?? () #1 0x00000000 在?? () (gdb) set arm fallback-mode thumb (gdb) x/i $pc => 0x8054: movs r0, #5 (gdb) si 0x00008056 in ?? () (gdb) x/i $pc => 0x8056: bx lr (gdb) si 0x00008056 in ?? () 无法在 0x0 (gdb) 回溯 #0 0x00008056 处插入单步断点?? () #1 0x00000000 在?? ()

标签: linux gcc arm thumb


【解决方案1】:

_start 可以是拇指函数吗(在 Linux 用户程序中)?

是的,它可以。这些步骤并不像您想象的那么简单。

请按照其他人的说明使用.code 16。另请看ARM Script predicate;我的回答显示了如何检测 thumb 二进制文件。入口符号必须具有传统的 _start+1 值,否则 Linux 将决定在 ARM 模式下调用您的 _start

您的代码也在尝试模拟,

 int main(void) { return 2; }

_start 符号不得执行此操作(根据 auselen)。要在 ARM 模式下执行 _startmain() 您需要,

 #include <linux/unistd.h>
 static inline void exit(int status)
 {
         asm volatile ("mov      r0, %0\n\t"
                 "mov    r7, %1\n\t"
                 "swi    #7\n\t"
                 : : "r" (status),
                   "Ir" (__NR_exit)
                 : "r0", "r7");
 }
 /* Wrapper for main return code. */
 void __attribute__ ((unused)) estart (int argc, char*argv[])
 {
     int rval = main(argc,argv);
     exit(rval);
 }

 /* Setup arguments for estart [like main()]. */
 void __attribute__ ((naked)) _start (void)
 {
     asm(" sub     lr, lr, lr\n"   /* Clear the link register. */
         " ldr     r0, [sp]\n"     /* Get argc... */
         " add     r1, sp, #4\n"   /* ... and argv ... */
         " b       estart\n"       /* Let's go! */
         );
 }

最好清除lr,这样堆栈跟踪就会终止。如果需要,您可以避免 argcargv 处理。 start 展示了如何使用它。 estart 只是将main() 返回码转换为exit() 调用的包装器。

您需要将上述汇编程序转换为 Thumb 等效项。我建议使用 gcc 内联汇编器。如果内联可以工作,则可以转换为纯汇编源代码。但是,在“C”源代码中执行此操作可能更实用,除非您尝试制作非常小的可执行文件。

有用的 gcc 论证是,

 -nostartfiles -static -nostdlib -isystem <path to linux user headers>

添加-mthumb,您应该拥有适用于任何一种模式的线束。

【讨论】:

  • 当然,您的 Linux 内核必须配置为支持 thumb 二进制文件。用于此的代码非常少,但它是一个.config 选项。
  • 如果精灵是在 thumb 模式下运行的设置,命令 readelf -h $1 | grep Entry.*[13579bdf]$ 应该返回一些文本。
  • (+1) 不错的信息,但是我想知道您是否设法指示 gcc(驱动程序)或链接器在 thumb 模式下实际创建 _start
  • 我已经为 initramfs 生成了带有 klibc 的拇指二进制文件;这些二进制文件与 klibc 链接,但使用 -nostartfiles 进行编译并有一个拇指 _start。上面的代码是针对 eglibc 系统的,但它不使用任何库并且是 ARM 代码。我没有把这两个概念放在一起。
  • @auselen 这是klibc startup。与上面的非常相似。 libc_init.c 等价于estart();但功能更全。
【解决方案2】:

你的问题是你结束了

bx lr

你希望 Linux 在那之后接管。那条确切的行一定是Segmentation fault的原因。

您可以尝试创建一个最小的可执行文件,然后尝试将其一分为二以查看内容并了解预期可执行文件的行为方式。

请参阅下面的工作示例:

.global _start
.thumb_func
_start:
    mov r0, #42
    mov r7, #1
    svc #0

编译

arm-linux-gnueabihf-as start.s -o start.o && arm-linux-gnueabihf-ld start.o -o start_test

然后转储看看胆量

$ arm-linux-gnueabihf-readelf -a -W start_test

现在你应该注意到_start的奇数地址

ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           ARM
  Version:                           0x1
  Entry point address:               0x8055
  Start of program headers:          52 (bytes into file)
  Start of section headers:          160 (bytes into file)
  Flags:                             0x5000000, Version5 EABI
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         1
  Size of section headers:           40 (bytes)
  Number of section headers:         6
  Section header string table index: 3

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .text             PROGBITS        00008054 000054 000006 00  AX  0   0  4
  [ 2] .ARM.attributes   ARM_ATTRIBUTES  00000000 00005a 000014 00      0   0  1
  [ 3] .shstrtab         STRTAB          00000000 00006e 000031 00      0   0  1
  [ 4] .symtab           SYMTAB          00000000 000190 0000e0 10      5   6  4
  [ 5] .strtab           STRTAB          00000000 000270 000058 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

There are no section groups in this file.

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x000000 0x00008000 0x00008000 0x0005a 0x0005a R E 0x8000

 Section to Segment mapping:
  Segment Sections...
   00     .text 

There is no dynamic section in this file.

There are no relocations in this file.

There are no unwind sections in this file.

Symbol table '.symtab' contains 14 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 00008054     0 SECTION LOCAL  DEFAULT    1 
     2: 00000000     0 SECTION LOCAL  DEFAULT    2 
     3: 00000000     0 FILE    LOCAL  DEFAULT  ABS start.o
     4: 00008054     0 NOTYPE  LOCAL  DEFAULT    1 $t
     5: 00000000     0 FILE    LOCAL  DEFAULT  ABS 
     6: 0001005a     0 NOTYPE  GLOBAL DEFAULT    1 _bss_end__
     7: 0001005a     0 NOTYPE  GLOBAL DEFAULT    1 __bss_start__
     8: 0001005a     0 NOTYPE  GLOBAL DEFAULT    1 __bss_end__
     9: 00008055     0 FUNC    GLOBAL DEFAULT    1 _start
    10: 0001005a     0 NOTYPE  GLOBAL DEFAULT    1 __bss_start
    11: 0001005c     0 NOTYPE  GLOBAL DEFAULT    1 __end__
    12: 0001005a     0 NOTYPE  GLOBAL DEFAULT    1 _edata
    13: 0001005c     0 NOTYPE  GLOBAL DEFAULT    1 _end

No version information found in this file.
Attribute Section: aeabi
File Attributes
  Tag_CPU_arch: v4T
  Tag_THUMB_ISA_use: Thumb-1

【讨论】:

  • 我知道。如何修复它的 asm 操作数。如何正确?
  • 链接寄存器 (lr) 为 NULL。我可以在 _start 之后初始化 lr 还是必须完成 linux 动态加载器?
  • 对于 ARM 代码,这可以通过以下代码序列轻松完成:mov lr, pc bx rX(其中 rX 是包含函数指针的寄存器的名称)。此代码不适用于 Thumb 指令集,因为 MOV 指令不会设置 LR 寄存器的低位,所以当被调用函数返回时,它会以 ARM 模式而不是 Thumb 模式返回。
  • 编译器生成这个序列:bl _call_via_rX(同样,如果寄存器包含函数指针,rX 是名称)。特殊的 call_via_rX 函数如下所示: .thumb_func _call_via_r0: bx r0 nop BL 指令确保正确的返回地址存储在 LR 寄存器中,然后 BX 指令跳转到存储在函数指针中的地址,必要时切换模式。
【解决方案3】:

这里回答。

谢谢大家。

http://stuff.mit.edu/afs/sipb/project/egcs/src/egcs/gcc/config/arm/README-interworking

  • 如果在 ARM 模式下进行调用,则通过函数指针调用应使用 BX 指令:

        .code 32
        mov lr, pc
        bx  rX
    

    但是,此代码序列在 Thumb 模式下不起作用,因为 mov 指令不会设置lr 寄存器的底部位。相反,应该使用指向_call_via_rX 函数的分支和链接:

        .code 16
        bl  _call_via_rX
    

    其中rX 被替换为包含函数地址的寄存器的名称。

【讨论】:

  • 如果你想要一个无限循环,这很有效。我以为您希望程序运行并退出;这就是我的答案的来源。实际上,那张纸没有任何意义。你有一个 ARM arm926ejs,它支持良好的互联网络。该论文适用于没有blxARM ARMv5 之前的 CPU。无限拇指循环只是loop: blx loop 或只是loop: b loop
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-05-27
  • 1970-01-01
  • 2010-12-25
  • 1970-01-01
  • 2012-02-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多