3

Help me please with gnu assembler for arm926ejs cpu.

I try to build a simple program(test.S):

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

and success build it:

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

but when I run the program in the arm target linux environment, I get an error:

./test 
Segmentation fault

What am I doing wrong? Can _start function be the thumb func? or It is always arm func?

g3plc
  • 61
  • 6
  • Have you tried getting a stack trace with gdb? – Ignacio Vazquez-Abrams Dec 04 '13 at 11:09
  • (gdb) target remote 192.168.3.16:1234 Remote debugging using 192.168.3.16:1234 0x00008054 in ?? () (gdb) backtrace #0 0x00008054 in ?? () #1 0x00000000 in ?? () (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 ?? () Could not insert single-step breakpoint at 0x0 (gdb) backtrace #0 0x00008056 in ?? () #1 0x00000000 in ?? () – g3plc Dec 04 '13 at 11:44

3 Answers3

2

Your problem is you end with

bx lr

and you expect Linux to take over after that. That exact line must be the cause of Segmentation fault.

You can try to create a minimal executable then try to bisect it to see the guts and understand how an executable is expected to behave.

See below for a working example:

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

compile with

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

and dump to see the guts

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

Now you should notice the odd address of _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
auselen
  • 27,577
  • 7
  • 73
  • 114
  • I know. How fixed it the asm operand. How will be it correct? – g3plc Dec 04 '13 at 11:58
  • link register ( lr ) is NULL. Do i can initialize lr after _start or it to must done linux dynamic loader? – g3plc Dec 04 '13 at 12:02
  • For ARM code this can easily be done by the code sequence: mov lr, pc bx rX (where rX is the name of the register containing the function pointer). This code does not work for the Thumb instruction set, since the MOV instruction will not set the bottom bit of the LR register, so that when the called function returns, it will return in ARM mode not Thumb mode. – g3plc Dec 04 '13 at 12:18
  • Instead the compiler generates this sequence: bl _call_via_rX (again where rX is the name if the register containing the function pointer). The special call_via_rX functions look like this: .thumb_func _call_via_r0: bx r0 nop The BL instruction ensures that the correct return address is stored in the LR register and then the BX instruction jumps to the address stored in the function pointer, switch modes if necessary. – g3plc Dec 04 '13 at 12:18
2

Can _start be a thumb function (in a Linux user program)?

Yes it can. The steps are not as simple as you may believe.

Please use the .code 16 as described by others. Also look at ARM Script predicate; my answer shows how to detect a thumb binary. The entry symbol must have the traditional _start+1 value or Linux will decide to call your _start in ARM mode.

Also your code is trying to emulate,

 int main(void) { return 2; }

The _start symbol must not do this (as per auselen). To do _start to main() in ARM mode you need,

 #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! */
         );
 }

It is good to clear the lr so that stack traces will terminate. You can avoid the argc and argv processing if you want. The start shows how to work with this. The estart is just a wrapper to convert the main() return code to an exit() call.

You need to convert the above assembler to Thumb equivalents. I would suggest using gcc inline assembler. You can convert to pure assembler source if you get inlines to work. However, doing this in 'C' source is probably more practical, unless you are trying to make a very minimal executable.

Helpful gcc arguements are,

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

Add -mthumb and you should have a harness for either mode.

Community
  • 1
  • 1
artless noise
  • 21,212
  • 6
  • 68
  • 105
  • Of course your Linux kernel must be configured to support *thumb* binaries. The code for this is extremely minimal, but it is a `.config` option. – artless noise Dec 04 '13 at 15:56
  • The command `readelf -h $1 | grep Entry.*[13579bdf]$` should return some text if the elf is a setup to run in *thumb* mode. – artless noise Dec 04 '13 at 16:02
  • (+1) Nice info, however I wonder if you managed to direct gcc (driver) or linker to actually create `_start` in thumb mode? – auselen Dec 04 '13 at 20:38
  • I have generated thumb binaries with *klibc* for an *initramfs*; these binaries link with klibc, but compile with `-nostartfiles` and have a thumb `_start`. The code above is for an *eglibc* system, but it doesn't use any libraries and is ARM code. I didn't put the two concepts together. – artless noise Dec 04 '13 at 21:37
  • @auselen Here is the [klibc startup](http://git.kernel.org/cgit/libs/klibc/klibc.git/tree/usr/klibc/arch/arm/crt0.S). Very similar to the above. The [libc_init.c](http://git.kernel.org/cgit/libs/klibc/klibc.git/tree/usr/klibc/libc_init.c) is equivalent to `estart()`; but more full featured. – artless noise Dec 04 '13 at 21:59
  • Ok then, this was nice to know, thanks. I think my sole mistake was to omit `.thumb_func` and try to force `as`, `ld` to create an odd address for `_start`. – auselen Dec 04 '13 at 22:14
  • Thanks for the info again and please see my updated answer. Funny thing I've noticed, if you strip the binary then it will create SIGILL. I don't know if that should be considered as a bug or loophole. – auselen Dec 05 '13 at 07:51
0

here answer.

Thanks for all.

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

  • Calls via function pointers should use the BX instruction if the call is made in ARM mode:

        .code 32
        mov lr, pc
        bx  rX
    

    This code sequence will not work in Thumb mode however, since the mov instruction will not set the bottom bit of the lr register. Instead a branch-and-link to the _call_via_rX functions should be used instead:

        .code 16
        bl  _call_via_rX
    

    where rX is replaced by the name of the register containing the function address.

Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
g3plc
  • 61
  • 6
  • This works if you want an infinite loop. I thought you wanted the program to run and exit; which is where my answer comes from. Actually, that paper doesn't make any sense. You have an ARM arm926ejs and it support good inter-networking. That paper is for CPUs before **ARM ARMv5** without `blx`. An infinite thumb loop is just `loop: blx loop` or just `loop: b loop`. – artless noise Dec 04 '13 at 21:45