Jumping into ARM assembly

Discussion in 'General Discussions' started by Linux-SWAT, Oct 2, 2015.

  1. Linux-SWAT

    Linux-SWAT Hardcore Member

    Joined:
    Feb 13, 2010
    Messages:
    8,203
    The compare char in a loop, better understood. I hope.

    Code:
    	.text
    	.align 2
    
    	.global _start
    
    _start:
           	ldr r8, =quit_char @ will load the address of quit_char into r8
    	ldrb r8, [r8]      @ load 'q' ascii code (pointed to r8) to r8
            mov r2, #1         @ buffer length
            mov r7, #3         @ system call number, 3 is 'read'
            mov r0, #0         @ file descriptor 0 - stdin
            ldr r1, =input     @ r1 will point every input in the loop
    
    _loop:
            svc 0         @ call the Linux kernel and wait for the input
            ldrb r9, [r1] @ load the ascii code of the input (pointed to r1) to r9
    
    	cmp r9, r8    @ compare if the input ascii == q	ascii
    	bne _loop     @ if not, come back to _loop
    
    	mov r7, #1    @ exit
    	svc 0
    
    	.data
    input:       .byte 0
    quit_char:   .byte 'q'
    
    I still don't understand why when the program stops, it's like i've pressed "enter" :

    ~/devel$ as hello.s -o hello.o ; ld -o hello hello.o ; ./hello
    q
    ~/devel$
    ~/devel$
     
  2. ibisum

    ibisum Well-Known Member

    Joined:
    May 6, 2009
    Messages:
    1,135
    This is probably one of the best threads I've ever seen on OPB.  You guys rock!
     
  3. Exophase

    Exophase Nothing good will ever come of Exophase.

    Joined:
    Sep 21, 2006
    Messages:
    10,308
    Location:
    Cleveland OH
    I think it's because the shell requires the enter to push the line to stdin, but your program never actually reads it off of stdin because it reads one character at a time and quits after the 'q'. So the enter is left for the shell to read.

    You should be able to load the quit char directly, btw:

    Code:
    ldr r8, [=quit_char] @ will load the value at quit_char into r8
    
     
    Last edited by a moderator: Oct 5, 2015
  4. Linux-SWAT

    Linux-SWAT Hardcore Member

    Joined:
    Feb 13, 2010
    Messages:
    8,203
    Still, this enter annoys me :/ .
    At least I don't have one enter for every "bad" char type.

    hello.s:7: Error: ARM register expected -- `ldr r8,[=quit_char]'

    Maybe it's another instruction ?
     
  5. Linux-SWAT

    Linux-SWAT Hardcore Member

    Joined:
    Feb 13, 2010
    Messages:
    8,203
    A little gdb entracte.
    The init file is ~/.gdbinit and is simple to set for gaining some time.
    Read the descriptions for ww and wx command effect inside the gdb shell.

    Thanks to Jo !

    Code:
    set pagination off
    
    define ww
      b _start
      run
      echo \n
    end
    
    document ww
      Breakpoint at _start, then runs the program.
    end
    
    define wx
      echo \n
      x/16i _start
      echo \n
      si
      info registers
      echo \n
    end
    
    document wx
      Step-by-step execution, displaying 16 lines of code, then registers.
    end
    
    
    
     
    Last edited by a moderator: Oct 6, 2015
  6. Exophase

    Exophase Nothing good will ever come of Exophase.

    Joined:
    Sep 21, 2006
    Messages:
    10,308
    Location:
    Cleveland OH
    Yeah sorry, GCC's PC-relative load syntax is annoying and I forgot it... I think it's just this:

    Code:
    ldr r8, quit_char
     
  7. Linux-SWAT

    Linux-SWAT Hardcore Member

    Joined:
    Feb 13, 2010
    Messages:
    8,203
    hello.s:7: Error: internal_relocation (type: OFFSET_IMM) not fixed up

    You broke my program :( .
    Hopefully, I have backups.
     
  8. Neelix

    Neelix Insecticidal Maniac

    Joined:
    Jan 8, 2011
    Messages:
    3,224
    Location:
    Melbourne, Australia
    try this?
     

    ldr r8, [quit_char]

    -Neelix
     
     
    Last edited by a moderator: Oct 5, 2015
  9. Linux-SWAT

    Linux-SWAT Hardcore Member

    Joined:
    Feb 13, 2010
    Messages:
    8,203
    hello.s:7: Error: ARM register expected -- `ldr r8,[quit_char]'
     
  10. levi

    levi Still fresh, damnit!

    Joined:
    Oct 6, 2008
    Messages:
    11,225
    Location:
    Somewhere off the coast of the EU
    Gah, LDR looks like a pain to use.  It's only a meta instruction that's translated into a series of actual ARM ops - mainly a MOV command.  It's certainly possible to write a PC-relative MOV instruction to do what you want - 'MOV R8, [PC, #offset]' but to do that you need to know the pipeline length and the addresses of both the instruction and the data to work out the offset.

    Regarding the extra newline, to test it, can you type in 'qecho foo' into it when you run it, and see if it runs 'echo foo' after it finishes.  That would prove it's not consuming any subsequent characters, and they get consumed instead by the shell after exit.
     
  11. Exophase

    Exophase Nothing good will ever come of Exophase.

    Joined:
    Sep 21, 2006
    Messages:
    10,308
    Location:
    Cleveland OH
    It does still translate ldr instruction, and you don't need to know the pipeline length because PC is architecturally specified to have the same offset it had since the first ARMs. That is, if you really wanted to try to do it manually.

    The problem here is I didn't notice that quit_char is all the way in another section (.data), which makes it too far away to access with a PC-relative load. But since it's a constant there's no reason why it can't be in .text instead, like right before or after the function, which would make it in range.

    For that matter, there's no reason why it can't just be an immediate, ie:

    .equ quit_char 'q'
    mov r8, #quit_char

    Although I don't know if .equ will accept const chars. I normally use GCC's preprocessor and .S files (note the capital) personally.

    EDIT: This forum upgrade sucks. Doing code tags manually with stuff following after the tag doesn't seem to be viable anymore. And I can't see a non-wysiwyg editor option. Lame.
     
    Last edited by a moderator: Oct 6, 2015
  12. Linux-SWAT

    Linux-SWAT Hardcore Member

    Joined:
    Feb 13, 2010
    Messages:
    8,203
    You're right Levi !

    $ as hello.s -o hello.o ; ld -o hello hello.o ; ./hello
    qecho 'huhu'
    $ echo 'huhu'
    huhu
    $

    What would be a clean solution ?
     
    Last edited by a moderator: Oct 6, 2015
  13. dmarschal

    dmarschal Member

    Joined:
    Feb 13, 2009
    Messages:
    116
    Location:
    Hungary
    I always put constants into .text section. print macro is a good example.

    Code:
    .macro print str
     mov r0,#console
     adr r1,1f
     mov r2,#3f-1f
     b 2f
    1: .ascii "\str"
    3: .align
    2: invoke sys_write
    .endm 
    See the result with "objdump -D". You can benefit that PC is pointing two instructions ahead so you can access data using the PC register.
     
    Code:
    ldr r1,[pc]
    b 1f
    .word 0x1234   @ data to be loaded
    1:
    BTW, LDR does the very same. It puts .word constants into the .text section to be loaded using the PC register. You can 'control' the literal pool's placement using the .lpool directive. Sometimes GCC overwrite it but most of the times it works fine.

    Back to the first example.
    Also, "console" constant is defined at the beginning of the assembly file. If the constant fits in 8 bits (not just the lower 8 bits) then I always use constants and simple MOV instructions. There is an ARMv7 instruction "movw" and "movt" that loads 16 bit data into a register's high and low half.


    Advice, never use the same register in the next instruction.
    Code:
    ldr r8,=somedata
    ldrb r8,[r8]
    
    These two lines halt one pipeline (or both if the following instruction in not pairable) for 1-3 clock ticks. If you insert an instruction between the two, like a "mov r2,#console" then this instruction will be executed parallel on the other pipeline. After an LDR you have to insert two or three instructions to be balanced.

    editor sucks
    over



     
     
    Last edited by a moderator: Oct 6, 2015
  14. Linux-SWAT

    Linux-SWAT Hardcore Member

    Joined:
    Feb 13, 2010
    Messages:
    8,203
    Exophase, tried with .equ, but I get :
    hello.s:13: Error: expected comma after "quit_char"
    hello.s:14: Error: undefined symbol quit_char used as an immediate value

    Here's the compare char in a loop following dmarschal's advice :

    Code:
    	.text
    	.align 2
    
    	.global _start
    
    _start:
           	ldr r8, =quit_char @ will load the address of quit_char into r8
    	ldrb r9, [r8]      @ load 'q' ascii code (pointed by r8) to r9
    	mov r2, #1         @ buffer length
    	mov r7, #3         @ system call number, 3 is 'read'
    	mov r0, #0         @ file descriptor 0 - stdin
    	ldr r1, =input     @ r1 will point every input in the loop
    
    _loop:
          	svc 0          @ call the Linux kernel and wait for the input
    	ldrb r10, [r1] @ load the ascii code of the input (pointed to r1) to r10
    
    	cmp r10, r9    @ compare if the input ascii == q ascii
    	bne _loop      @ if not, come back to _loop
    
    	mov r7, #1     @ exit
    	svc 0
    
    	.data
    input:         .byte 0
    quit_char:     .byte 'q'
    
     
    Last edited by a moderator: Oct 6, 2015
  15. dmarschal

    dmarschal Member

    Joined:
    Feb 13, 2009
    Messages:
    116
    Location:
    Hungary
    use

    Code:
    quit_char = 'q'
    before declaring the .text section. and use it like this:
    Code:
    mov r9,#quit_char
    advice, put all constants and structure offsets in a separate include file and ".include" it before the section declares.

    declare syscalls like
     
    Code:
    sys_read=3
    and use 
     
    Code:
    mov r7,#sys_read
    in your example






     
     
    Last edited by a moderator: Oct 6, 2015
  16. pmprog

    pmprog Advanced Member

    Joined:
    Apr 25, 2011
    Messages:
    3,860
    heh, reminds me of my old x86 assember days :)
     
  17. dmarschal

    dmarschal Member

    Joined:
    Feb 13, 2009
    Messages:
    116
    Location:
    Hungary
    Linux-SWAT:
    Using syscall 3, sys_read: It returns the character count in R0 so the next loop will read from 1,stdout, not from 0,stdin.

    Always at least four-align your data. Use an align after a .char declaration OR pack four characters into one .word to save space.
     
     
  18. levi

    levi Still fresh, damnit!

    Joined:
    Oct 6, 2008
    Messages:
    11,225
    Location:
    Somewhere off the coast of the EU
    Probably if you continue to call sys_read until you get a newline code back, after you've received a q.  Something like:

    LDR R1, =input @Address
    MOV R2, #1 @Length
    MOV R7, #3 @Sys_Read
    _getremainder:
    MOV R0, #0 @Stdin
    SVC 0
    LDRB R10, [R1]
    CMP R10, #10 @Newline ASCII code
    BNE _getremainder
    MOV R7, #1 @Exit
    SVC 0

    Some of that code can probably be eliminated depending on the prior setup of your registers.
     
    Last edited by a moderator: Oct 6, 2015
  19. Linux-SWAT

    Linux-SWAT Hardcore Member

    Joined:
    Feb 13, 2010
    Messages:
    8,203
    Code:
    What does quit_char = 'q' before declaring the .text section do ?
    Why is it different from previous propositions ?
    
     
  20. Linux-SWAT

    Linux-SWAT Hardcore Member

    Joined:
    Feb 13, 2010
    Messages:
    8,203
    Intermediate version handling the newline :

    Code:
            .text
            .align 2
    
            .global _start
    
    _start:
            ldr r8, =quit_char @ will load the address of quit_char into r8
            ldrb r9, [r8]      @ load 'q' ascii code (pointed to r8) into r9
            mov r2, #1         @ buffer length
            mov r7, #3         @ system call number, 3 is 'read'
            mov r0, #0         @ file descriptor 0 - stdin
            ldr r1, =input     @ r1 will point every input in the loop
    
    _loop:
            svc 0          @ call the Linux kernel and wait for the input
            ldrb r10, [r1] @ load the ascii code of the input (pointed to r1) into r10
    
            cmp r10, r9    @ compare if the input ascii == q ascii
            bne _loop      @ if not, come back to _loop
    
    _getremainder:
            svc 0
            ldrb r10, [r1]
            cmp r10, #10  @Newline ascii code
            bne _getremainder
    
            mov r7, #1    @ exit
            svc 0
    
            .data
    input:       .byte 0
    quit_char:   .byte 'q'
    
     
    Last edited by a moderator: Oct 6, 2015

Share This Page

Loading...