Principle of Ximen Chui Xue
This article explains how to write a GNU ARM assembly program and run it on an Android phone.
(1) Why Learn ARM Assembly?
If learning is only done when needed, then nothing needs to be learned in advance. The situation of “learning when needed” often exists and makes sense; your time and energy should not be spent on knowledge and skills that are not currently useful (will they be useful in the future? Let’s deal with problems that have already appeared or are about to appear; if there are no problems, then just enjoy yourself). Similarly, learning ARM assembly is best done because you need this knowledge and skill for what you are “doing”, such as translating someone else’s assembly code, making some reverse modifications to assembly code, or understanding the assembly code when a program crashes, etc. As for showing off, that is also a reason (showing off may also give you a certain perspective).
(2) How to Write a Hello World?
Just write it with your familiar editor and save it as a .s file. As for the syntax of GNU ARM ASM, refer to the manual and examples.
For example, to say hello:
.data
msg: .asciz "hello, gnu asm\n"
len = . - msg .text
.global main
main:
push {r0,r1,r2,lr}
ldr r1, =msg @ address
mov r0, #1 @ stdout
ldr r2, =len @ length
mov %r7, $4 /* write is syscall #4 */
swi $0 /* invoke syscall */
pop {r0,r1,r2,pc}
And for summation:
.data
msg: .asciz "sum=%d\n"
.text
sum: push {lr} mov r2, r0
mov r3, r1
add r0, r2, r3
pop {pc}
.global main
main:
push {r0, r1, r2, lr} mov r0, #100
mov r1, #120
bl sum
mov r1, r0
ldr r0, =msg
bl printf pop {r0, r1, r2, pc}
(3) How to Compile an Assembly Program?
Since the final goal is to run on an Android device, the executable format must be ELF, so it needs to be compiled into ELF format.
On Mac, you need to use a cross-compilation tool (a tool that compiles programs for another platform), which can be: arm-linux-androideabi-gcc (for example, in the directory: toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/), but when compiling (for example, directly gcc -o test test.c), you may encounter issues with header files (like stdio.h) not being recognized, due to not specifying the directory of the header files.
Solution: Use the compilation toolchain.
A compilation toolchain is an environment suitable for a certain scenario (such as generating executable files for a certain platform), including the header files needed during compilation.
To create this compilation environment, you need to use the make-standalone-toolchain.sh script in the NDK.
The final compilation script is as follows:
#!/bin/bash
# toolchain
TOOLCHAIN=~/asmtoolchain
SYSROOT=$TOOLCHAIN/sysroot/
ANDROID_NDK_ROOT=/Users/sky/Documents/android-ndk-r10d
NDK_PATH=$ANDROID_NDK_ROOT
MIN_PLATFORM=android-8
TOOLCHAIN_VER=arm-linux-androideabi-4.8
OUT_NAME=helloasm
OBJS=helloasm.s
if [ ! -d $SYSROOT ]; then
$NDK_PATH/build/tools/make-standalone-toolchain.sh –platform=$MIN_PLATFORM –install-dir=$TOOLCHAIN –toolchain=$TOOLCHAIN_VER
fi
PATH=$TOOLCHAIN/bin:$PATH
CC=arm-linux-androideabi-gcc
#LD=arm-linux-androideabi-ld
#AR=arm-linux-androideabi-ar
$CC –sysroot=$SYSROOT -o $OUT_NAME $OBJS
The executable file compiled above is in ELF format (you can check with file helloasm).
(4) Running the Executable File
Copy the executable file (for example, helloasm) to the /data directory of the Android phone to run it (first adb push to /sdcard/, then adb shell to the phone and copy to /data, then chmod 777 helloasm, after which it can be run).
(5) Disassembling the Final Executable File
Disassemble the compiled executable file and see how different it is from the source file.
For example, after disassembling the above summation executable program using Hopper, you can see code like this (part):
sum:
@ args = 0, pretend = 0, frame = 8
@ frame_needed = 1, uses_anonymous_args = 0
@ link register save eliminated.
str fp, [sp, #-4]!
add fp, sp, #0
sub sp, sp, #12
mov r3, #10
str r3, [fp, #-8]
mov r3, #3
str r3, [fp, #-12]
ldr r2, [fp, #-8]
ldr r3, [fp, #-12]
add r3, r2, r3
mov r0, r3
sub sp, fp, #0
@ sp needed
ldr fp, [sp], #4
bx lr
.size sum, .-sum
.section .rodata
.align 2
.LC0:
.ascii "%d\012\000"
.text
.align 2
.global main .type main, %functionmain:
@ args = 0, pretend = 0, frame = 16
@ frame_needed = 1, uses_anonymous_args = 0
stmfd sp!, {fp, lr} add fp, sp, #4
sub sp, sp, #16
str r0, [fp, #-16]
str r1, [fp, #-20]
bl sum(PLT)
str r0, [fp, #-8]
ldr r3, .L5.LPIC0:
add r3, pc, r3
mov r0, r3
ldr r1, [fp, #-8]
bl printf(PLT) mov r3, #0
mov r0, r3
sub sp, fp, #4
@ sp needed
ldmfd sp!, {fp, pc}
Isn’t it similar?
Newbie: But what is the use of this?
Ximen Chui Xue: Writing a few small programs is just to familiarize oneself with some basic concepts of ASM, but for practical application, it still relies on information gathering and analysis.
Reply “1” in the public account to join the programmer group chat
Must-read 100 programming books for front-end developers
Download 300 classic programming books
Download 83 A-round and angel round financing plans
Download 1000G free programming teaching videos, all classics
Download 100 sets of WeChat mini-program source code
Essential for programmers!! Complete set of WeChat mini-program development tutorials
Follow the public account, ask the editor to send red envelopes