在Windows上习惯了, 感觉一切都是那么自然, 一切都应该就是这样, 会不会在Linux下习惯了, 也会觉得Windows上的一切都不习惯? 我记得我刚开始Windows编程的时候, 是从汇编开始学的, 从汇编开始学, 虽然慢, 但是每一行代码做什么的都比较清楚. 这样慢慢的深入学习感觉一切都那么自然, 现在搞Android就不是这样了, 上来就有很多东西学, 但是总是感觉吃不透.有细节没理清楚. 我觉得Android学习也应该从汇编开始.

搞Android这帮, 好像遗忘汇编了. 只有一些搞嵌入式的才讲汇编, 为什么Android不可以从汇编开始. 后面在网上看到这篇文章, 才坚定了我觉得Android也是可以从汇编搞起走的信心.

http://peterdn.com/post/e28098Hello-World!e28099-in-ARM-assembly.aspx

这个汇编的语法是GAS. 编译用的就是ndk自带的工具, 看来没有太好的办法, 只能慢慢熟悉GAS语法了. 用GAS语法写一个arm上的HelloWorld居然是这样子的.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    .arch armv5te
    .fpu softvfp
@--------------------------------------------------------------------
    .data
msg:
    .ascii      "Hello, ARM!\n"
len = . - msg
@--------------------------------------------------------------------
    .text

.globl _start
_start:

    /* syscall write(int fd, const void *buf, size_t count) */
    mov     r0, $1     /* fd -> stdout */
    ldr     r1, =msg   /* buf -> msg */
    ldr     %r2, =len   /* count -> len(msg) */
    mov     %r7, $4     /* write is syscall #4 */
    swi     $0          /* invoke syscall */

    /* syscall exit(int status) */
    mov     %r0, $0     /* status -> 0 */
    mov     %r7, $1     /* exit is syscall #1 */
    swi     $0          /* invoke syscall */

仔细看和Intel语法差别还是不小. 但是习惯了就好了. 用MASM写习惯的, 感觉语法都有点古怪. 不过还好, 寄存器前面可以加%也可以不加, 不加更舒服吧, 数字一定要$开头, 真恶心. 编译连接的工具在ndk的这个目录下面

$(NDK_ROOT)\ toolchains\arm-linux-androideabi-4.8\prebuilt\windows-x86_64\bin

编译和连接

1
2
C:\ArmHello>arm-linux-androideabi-as.exe -o hello.o hello.S
C:\ArmHello>arm-linux-androideabi-ld.exe -s -o hello hello.o

如果没有任何错误出现, 就表示编译通过了. 连个编译成功的提示也没有. 这个和MASM 的ml和link倒是差别不是很大. 回头有时间再好好看看arm-linux-androideabi-as.exe 和 arm-linux-androideabi-ld.exe 的命令行. 这个汇编代码的意思, 相信不用说应该也明白. 就是往console写了一句Hello, ARM然后就退出了. 使用adb上传代码, 运行代码, 你应该看到这个.

在IDA里面看比较符合Intel的语法.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
; Segment type: Pure code
        AREA .text, CODE
        ; ORG 0x8074
        CODE32
        MOV     R0, #1
        LDR     R1, =s->HelloArm ; "Hello, ARM!\n"
        MOV     R2, #0xC
        MOV     R7, #4
        SVC     0               ; syscall write(int fd, const void *buf, size_t count)
        MOV     R0, #0
        MOV     R7, #1
        SVC     0               ; syscall exit(int status)

; ---------------------------------------------------------------------------
off_8094        DCD s->HelloArm         ; DATA XREF: .text:00008078r
; _text         ends                    ; "Hello, ARM!\n"

; ===========================================================================

; Segment type: Pure data
                AREA .data, DATA, ALIGN=0
                ; ORG 0x9098
s->HelloArm     DCB "Hello, ARM!",0xA   ; DATA XREF: .text:00008078o
                                        ; .text:off_8094o
; _data         ends

                END

不过还好, 只是有些细微差别, 看来要好好看看GAS语法了. 不过有点疑问的是, 我记得GAS的语法是左边赋值到右边啊, 这个汇编也是和Intel的一样, 右边到左边, 还算能够接受, 用GAS写过一点代码, 总是左右不分. 这也难怪, 叫人放弃坚持已久的左右. 怎么能够习惯.

代码写完, 当然就是调试了. 调试之前, 我们先让adb给我们做下端口转发:

adb forward tcp:12345 tcp:12345

调试我们使用ndk推荐的方式, 上传ndk目录下的. gdbserver 到Android 然后设置使用gdbserver运行hello.

20140711170446

运行这两条命令启动gdbserver调试. 我第一次使用的时候是个三星手机, 真恶心, 死活连不上. 所以注意下手机型号, 还有目录要全名称. 以免出现神奇的错误. 进入Listening状态就表示差不多了. 这时候使用ndk目录下的arm-linux-androideabi-gdb连接上去开始调试. gdb的常用命令

Ni 是单步步过,si单步步入,

set disassemble-next on 下一句指令显示反汇编,使用set arm force-mode arm或者set arm force-mode thumb让gdb切换thumb和arm代码显示。

Display /i $pc 显示当前的代码

Continue 就是windbg的f5,od的f9

Info breakpoints 显示断点,而 delete删除断点 disable禁用断点

Disas 0xAAAA,+20(20字节的数据) 显示反汇编

x/s 0x9098  以字符串的形式显示内存

info reg 显示寄存器信息掌握这几条指令就可以开始调试了.

20140711173431