原文地址:"Hello World" in Assembly
原作者:jacobbramley
即便是对于有经验的软件工程师而言,汇编语言也是一项极具挑战的事物。大多数时候,一系列奇怪的指令和符号很难让人读懂;这就是为什么我们使用 C 语言这样的语言;采用此类语言时,编译器会处理这些指令和符号,而无须您进行读取。但是,了解处理器的指令集可以更为轻松地发现和实施 C 语言级优化,并且将有助于您了解程序实际上正在执行的操作。此外,它还使您能针对 C 语言环境下很难实施的特定任务创建一些微调代码。别的不说,至少它十分有趣。
即便是对于有经验的软件工程师而言,汇编语言也是一项极具挑战的事物。大多数时候,一系列奇怪的指令和符号很难让人读懂;这就是为什么我们使用
C
语言这样的语言;采用此类语言时,编译器会处理这些指令和符号,而无须您进行读取。但是,了解处理器的指令集可以更为轻松地发现和实施
语言级优化,并且将有助于您了解程序实际上正在执行的操作。此外,它还使您能针对
语言环境下很难实施的特定任务创建一些微调代码。别的不说,至少它十分有趣。
本文旨在提供有关 ARM 汇编语言的简单指令。代码将以您可以读懂的方式显示出来,而无须了解每个指令的细微之处和具体之处。今后发布的博文将更为详细地说明该机制。
本文旨在提供有关
ARM
汇编语言的简单指令。代码将以您可以读懂的方式显示出来,而无须了解每个指令的细微之处和具体之处。今后发布的博文将更为详细地说明该机制。
为了执行任何兴趣的功能,您需要一台 ARM 设备和一个适用的工具链。如果您拥有一台功能相对强大、且配备桌面操作系统(如 Ubuntu)的设备,则可以直接在系统板上执行;这就是本地开发。在 Ubuntu 上,您可以使用内置的 apt-get 实用程序获取工具链;只需输入 apt-get install build-essential(作为根),就可获取相对较新的 GCC 版本。此种情况下,此类开发与在您 PC 执行上的开发非常相似,唯一不同之处在于您要写入 ARM 汇编代码,而非 x86 汇编代码。
为了执行任何兴趣的功能,您需要一台
设备和一个适用的工具链。如果您拥有一台功能相对强大、且配备桌面操作系统(如
Ubuntu
)的设备,则可以直接在系统板上执行;这就是本地开发。在
上,您可以使用内置的
apt-get
实用程序获取工具链;只需输入
apt-get install build-essential
(作为根),就可获取相对较新的
GCC
版本。此种情况下,此类开发与在您
PC
执行上的开发非常相似,唯一不同之处在于您要写入
汇编代码,而非
x86
汇编代码。
如果您没有功能非常强大的 ARM 设备或者您的平台不支持轻松执行本地构建,则可能要使用一个交叉编译器。您可以从 Code Sourcery 公司网站免费下载 ARM 交叉处理器;这其实是一个预先构建的 ARM 交叉处理器(和汇编程序),因此不必担心需要自己亲自构建。此种情况下,必须在 PC 上编译您的代码,然后将二进制迁移至您的平台以便在此处执行。
如果您没有功能非常强大的
设备或者您的平台不支持轻松执行本地构建,则可能要使用一个交叉编译器。您可以从
Code Sourcery 公司网站
免费下载
交叉处理器;这其实是一个预先构建的
交叉处理器(和汇编程序),因此不必担心需要自己亲自构建。此种情况下,必须在
上编译您的代码,然后将二进制迁移至您的平台以便在此处执行。
汇编实际上是一种可人工读取计算机代码的形式。每个汇编指令均会将或多或少地映射至一个计算机指令,以便您可以较好地控制处理器正在执行的操作。它的语法比 C
汇编实际上是一种可人工读取计算机代码的形式。每个汇编指令均会将或多或少地映射至一个计算机指令,以便您可以较好地控制处理器正在执行的操作。它的语法比
语言更为简单;您不能组成复杂的复合语句,除非明确列出了语句计算所需的指令。例如,C
语言更为简单;您不能组成复杂的复合语句,除非明确列出了语句计算所需的指令。例如,
语言表达式 a=(b+c)*d
语言表达式
a=(b+c)*d
可能类似于
ARM 汇编的表达式:
汇编的表达式:
add r0, r1, r2 mul r0, r3, r0
表达式必须拆分为a=(b+c) 和 a=a*d 两部分。
表达式必须拆分为
a=(b+c)
和
a=a*d
两部分。
请记住,即便大多数汇编程序正在执行相同的作业,它们也会使用不同的语法,这一点十分重要。例如,汇编程序在 ARM 的 RVCT 中使用的语法不同于 GNU 汇编程序在 GCC 中使用的语法。此处,我们默认使用 GCC 语法,这是因为 GCC 工具目前免费提供并适用于多个平台。另外,GNU 汇编程序针对每个平台使用不同的行注释分隔符。就 ARM 而言,它为@。GNU 汇编程序还允许使用 C 语言类型的多行注释(如 "/* ...*/")。
请记住,即便大多数汇编程序正在执行相同的作业,它们也会使用不同的语法,这一点十分重要。例如,汇编程序在
的
RVCT
中使用的语法不同于
GNU
汇编程序在
中使用的语法。此处,我们默认使用
语法,这是因为
工具目前免费提供并适用于多个平台。另外,
汇编程序针对每个平台使用不同的行注释分隔符。就
而言,它为
@
。
汇编程序还允许使用
语言类型的多行注释(如
"/* ...*/"
)。
对于许多而言,传统的入门程序为 "Hello World"
对于许多而言,传统的入门程序为
"Hello World"
程序。在 C 语言中,这看起来类似:
程序。在
语言中,这看起来类似:
#include <stdio.h> int main(void) { printf("Hello, world.\n"); return0; }
这非常好,但是它实际上对于处理器而言意味着什么呢?它的执行方式如何?相同程序的汇编版本十分相似。我不会在此处详述每种指令,但是我将展示代码,并会在今后的博文中讨论各种不同的机制。
方便起见,完整的示例程序已随附在此页面上,同时也在下文列出:
.syntax unified @ -------------------------------- .global main main: @ Stack the return address (lr) in addition to a dummy register (ip) to @ keep the stack 8-byte aligned. push {ip, lr} @ Load the argument and perform the call. This is like 'printf("...")' in C. ldr r0, =message bl printf @ Exit from 'main'. This is like 'return 0' in C. mov r0, #0 @ Return 0. @ Pop the dummy ip to reverse our alignment fix, and pop the original lr @ value directly into pc — the Program Counter — to return. pop {ip, pc} @ -------------------------------- @ Data for the printf calls. The GNU assembler's ".asciz" directive @ automatically adds a NULL character termination. message: .asciz "Hello, world.\n"
我们可以使用下列命令汇编并运行此程序(针对 ARM Linux 等类似平台):
gcc -o hello_world hello_world.s $ ./hello_world
您将在控制台上看到 "Hello, world." 文本。
如果您正在使用交叉编译器(如 RVCT 或 Code Sourcery 版本的 GCC),则需要在 PC 上运行第一个步骤 - 可能是使用类似于 arm‑none‑linux‑gnueabi‑gcc 的内容替换gcc — 然后在运行程序之前将输出二进制复制到 ARM 目标。
arm‑none‑linux‑gnueabi‑gcc
gcc
如果您对这如何与 C 语言编译器将执行的操作相关感到好奇,请尝试使用 gcc -S hello_world.c -O2 而非常用的编译命令编译 C 语言版本。您还可以使用 objdump 检查现有对象或二进制,以便分解输出。如果您向编译器提供不同的 -O 标记,则会存在少许细微之处,并且您将看到不同的结果。编译器输出也会因编译器版本而有所不同。
gcc -S hello_world.c -O2
objdump
-O
各种机制和指令的详细信息将在其他博文中予以讨论,但是从该示例中可以看到,C 语言和汇编实施之间的近似映射应该很明显。