【问题标题】:Usage of as + gcc vs gcc only for ARM assemblyas + gcc vs gcc 仅用于 ARM 汇编
【发布时间】:2017-08-06 01:09:41
【问题描述】:

我正在使用asgcc来汇编和创建ARM汇编程序的可执行文件,按照this教程的建议,如下:

给定一个汇编源文件program.s,我运行:

as -o program.o program.s

然后:

gcc -o program program.o

但是,像这样直接在程序集源上运行gcc

gcc -o program program.s

产生相同的结果。

gcc 是否在幕后呼叫as?考虑到 gcc 单独能够从源代码生成可执行文件,是否有任何理由同时使用 asgcc

我在 Raspberry Pi 3、Raspbian Jessie(Debian 衍生产品)、gcc 4.9.2 和 2.25 上运行它。

【问题讨论】:

  • gcc 本身代表“GNU compiler collection”(注意它没有以任何方式提及 C,这是正确的,C 编译器隐藏在 cc 下,而 C++ 是在g++ 下,还有更多的编译器在gcc)。所以它实际上是一种“包装器”,如何调用所有其他工具来生成最终的二进制可执行文件,有点聪明,知道哪些选项用于所有工具(如目标平台),哪些只属于编译器或链接器。

标签: gcc assembly arm


【解决方案1】:

GCC 在幕后调用各种东西;不仅仅是as,还有ld。如果您想证明这一点,这很容易检测(将正确的 asld 和其他二进制文件替换为打印出命令行的二进制文件,然后运行 ​​GCC 并查看该二进制文件被调用)。

当你使用 GCC 作为汇编器时,它会经过一个 C 预处理器,所以你可以做一些相当恶心的事情,比如:

开始.s

//this is a comment
@this is a comment
#define FOO BAR

.globl _start
_start:
    mov sp,#0x80000
    bl hello
    b .

.globl world
world:
    bx lr

要查看更多内容,请查看其他文件:

所以.h

unsigned int world ( unsigned int, unsigned int );

#define FIVE 5
#define SIX 6

so.c

#include "so.h"

unsigned int hello ( void )
{
    unsigned int a,b,c;
    a=FIVE;
    b=SIX;
    c=world(a,b);
    return(c+1);
}

构建

arm-none-eabi-gcc -save-temps -nostdlib -nostartfiles -ffreestanding -O2 start.s so.c -o so.elf
arm-none-eabi-objdump -D so.elf

生产

00008000 <_start>:
    8000:   e3a0d702    mov sp, #524288 ; 0x80000
    8004:   eb000001    bl  8010 <hello>
    8008:   eafffffe    b   8008 <_start+0x8>

0000800c <world>:
    800c:   e12fff1e    bx  lr

00008010 <hello>:
    8010:   e92d4010    push    {r4, lr}
    8014:   e3a01006    mov r1, #6
    8018:   e3a00005    mov r0, #5
    801c:   ebfffffa    bl  800c <world>
    8020:   e8bd4010    pop {r4, lr}
    8024:   e2800001    add r0, r0, #1
    8028:   e12fff1e    bx  lr

是一个非常简单的项目。这是预处理器之后的 so.i,它会获取包含文件并替换定义:

# 1 "so.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "so.c"


# 1 "so.h" 1

unsigned int world ( unsigned int, unsigned int );
# 4 "so.c" 2

unsigned int hello ( void )
{
    unsigned int a,b,c;
    a=5;
    b=6;
    c=world(a,b);
    return(c+1);
}

然后 GCC 调用实际的编译器(其程序名称不是 GCC)。

产生so.s:

    .cpu arm7tdmi
    .eabi_attribute 20, 1
    .eabi_attribute 21, 1
    .eabi_attribute 23, 3
    .eabi_attribute 24, 1
    .eabi_attribute 25, 1
    .eabi_attribute 26, 1
    .eabi_attribute 30, 2
    .eabi_attribute 34, 0
    .eabi_attribute 18, 4
    .file   "so.c"
    .text
    .align  2
    .global hello
    .syntax unified
    .arm
    .fpu softvfp
    .type   hello, %function
hello:
    @ Function supports interworking.
    @ args = 0, pretend = 0, frame = 0
    @ frame_needed = 0, uses_anonymous_args = 0
    push    {r4, lr}
    mov r1, #6
    mov r0, #5
    bl  world
    pop {r4, lr}
    add r0, r0, #1
    bx  lr
    .size   hello, .-hello
    .ident  "GCC: (GNU) 6.3.0"

然后将其馈送到汇编器以制作 so.o。然后调用链接器将这些变成so.elf。

现在,您可以直接进行大部分通话。这并不意味着这些程序有它们调用的其他程序。 GCC 仍然调用一个或多个程序来实际进行编译。

arm-none-eabi-as start.s -o start.o
arm-none-eabi-gcc -O2 -S so.c 
arm-none-eabi-as so.s -o so.o
arm-none-eabi-ld start.o so.o -o so.elf
arm-none-eabi-objdump -D so.elf

给出相同的结果:

00008000 <_start>:
    8000:   e3a0d702    mov sp, #524288 ; 0x80000
    8004:   eb000001    bl  8010 <hello>
    8008:   eafffffe    b   8008 <_start+0x8>

0000800c <world>:
    800c:   e12fff1e    bx  lr

00008010 <hello>:
    8010:   e92d4010    push    {r4, lr}
    8014:   e3a01006    mov r1, #6
    8018:   e3a00005    mov r0, #5
    801c:   ebfffffa    bl  800c <world>
    8020:   e8bd4010    pop {r4, lr}
    8024:   e2800001    add r0, r0, #1
    8028:   e12fff1e    bx  lr

在 GCC 中使用 -S 确实感觉有点不对劲。像这样使用它反而感觉更自然:

arm-none-eabi-gcc -O2 -c so.c -o so.o

现在有一个链接器脚本,我们没有提供工具链默认的链接器脚本。我们可以控制它,并且根据它的目标,也许我们应该控制它。

我不高兴看到as 的新/当前版本可以容忍 C cmets 等...以前不是这样的,必须是最新版本的新事物。

因此,术语“工具链”是一系列链接在一​​起的工具,一个按顺序链接到下一个。

并非所有编译器都采用汇编语言步骤。有些编译成中间代码,然后还有另一个工具可以将编译器特定的中间代码转换为汇编语言。然后调用一些汇编程序(GCC 的中间代码在编译步骤中的表中,您可以要求它编译为该代码,然后从那里转到其中一个目标的汇编语言)。

一些编译器直接使用机器代码而不是汇编语言。这可能是那些“仅仅因为它在那里就爬山”与“四处走动”的事情之一。就像纯粹用汇编语言编写操作系统一样。

对于任何大小合适的项目和可以支持它的工具,您将拥有一个链接器和一个汇编器,这是您为支持新目标而制作的第一个工具。处理器(芯片或 ip 或两者)供应商将拥有一个汇编程序,然后还有其他可用的工具。

尝试使用汇编语言手动编译上述简单的 C 程序。然后再次尝试使用汇编语言,手动,只使用机器代码。您会发现,使用汇编语言作为中间步骤对于编译器开发人员来说要明智得多,而且它一直都是这样做的,这也是继续这样做的一个很好的理由。

如果你在你正在使用的 gnu 工具链目录中闲逛,你可能会发现类似 cc1 的程序:

./libexec/gcc/arm-none-eabi/6.3.0/cc1 --help
The following options are specific to just the language Ada:
 None found.  Use --help=Ada to show *all* the options supported by the Ada front-end.

The following options are specific to just the language AdaSCIL:
 None found.  Use --help=AdaSCIL to show *all* the options supported by the AdaSCIL front-end.

The following options are specific to just the language AdaWhy:
 None found.  Use --help=AdaWhy to show *all* the options supported by the AdaWhy front-end.

The following options are specific to just the language C:
 None found.  Use --help=C to show *all* the options supported by the C front-end.

The following options are specific to just the language C++:
  -Wplacement-new                   
  -Wplacement-new=                  0xffffffff

The following options are specific to just the language Fortran:

现在,如果您针对使用 -save-temps 保存的 so.i 文件运行 cc1 程序,您将获得 so.s 汇编语言文件。

您可能会继续挖掘目录或 gnu 工具源以找到更多好东西。

请注意,这个问题之前已经在 Stack Overflow 上以各种方式问过很多次了。

还请注意,main() 并没有什么特别之处,正如我所展示的那样。在某些编译器中可能是这样,但我可以制作不需要该函数名的程序。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-07
    • 1970-01-01
    相关资源
    最近更新 更多