- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在使用 as
和 gcc
来汇编和创建 ARM 汇编程序的可执行文件,正如 this 所推荐的那样教程,如下:
给定一个汇编源文件,program.s
,我运行:
as -o program.o program.s
然后:
gcc -o program program.o
但是,像这样直接在程序集源上运行 gcc
:
gcc -o program program.s
产生相同的结果。
gcc
会在后台调用 as
吗?鉴于 gcc
单独能够从源代码生成可执行文件,是否有任何理由同时使用 as
和 gcc
?
我在 Raspberry Pi 3、Raspbian Jessie(Debian 衍生产品)、gcc 4.9.2 和 2.25 上运行它。
最佳答案
GCC 在幕后调用各种东西;不仅是 as
,还有 ld
。如果您想证明这一点,这很容易检测(将正确的 as
和 ld
以及其他二进制文件替换为打印出命令行的二进制文件,然后运行 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 注释等...以前不是这样的,必须是新的东西最新版本。
因此,术语“工具链”是一系列链接在一起的工具,一个按顺序链接到下一个。
并非所有编译器都采用汇编语言步骤。有些编译成中间代码,然后还有另一个工具可以将编译器特定的中间代码转换为汇编语言。然后调用一些汇编程序(GCC 的中间代码在编译步骤中的表内,您可以在 clang/llvm 中要求它编译为此代码,然后从那里转到其中一个目标的汇编语言)。
一些编译器直接使用机器代码而不是汇编语言。这可能是那些“仅仅因为它在那里就爬山”与“四处走动”的事情之一。就像纯粹用汇编语言编写操作系统一样。
对于任何大小合适的项目和可以支持它的工具,您将拥有一个链接器和一个汇编器,这是您为支持新目标而制作的第一个工具。处理器(芯片或 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()
并没有什么特别之处,正如我所展示的那样。在某些编译器中可能是这样,但我可以制作不需要该函数名的程序。
关于gcc - as + gcc vs gcc 仅用于 ARM 汇编,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45527732/
我试图在图形模式下打印一个字符。通常当我打印我正在做的一个字符时: mov ah,14 ; ah=14 mov al,'x' int 10h ; print the character 这
我试图通过更改其中的一个字节来修改存储在内存中的字符串。我为此使用了 movb,但由于某种原因,给定内存位置的字节没有改变。 在 gdb 调试器上: 14 movb %al, (%r10) # nex
我一直在阅读一些汇编代码,并且开始发现调用指令实际上是与程序计数器相关的。 但是,每当我使用 Visual Studio 或 Windbg 进行调试时,它总是显示 call 0xFFFFFF ...这
我最近一直在使用 Visual C++ 中的内联汇编,我想知道是否可以直接向堆栈上的局部变量添加值,例如: push 5 add [esp], 7 这样做可以吗?我问这个问题是因为我在执行此操作时随机
我有下一个代码: mov al, -5 add al, 132 add al, 1 据我检查,溢出标志和进位标志将在第一个操作中设置,而在第二个操作中,仅设置溢出。 但我不明白为什么: 在无符号数中,
在 64 位 x86 汇编 nasm 中,如何将单个字节从寄存器移动到 .data 节中定义的内存位置? 我知道这有效 global _main section .data quotient db 0
我的汇编代码有问题。我想打印存储在寄存器 cx 中的数字,但是当我尝试打印它时,它打印的是 ascii 字符而不是 ascii 数字,所以我决定编写一个程序将 ascii char 转换为 ascii
为什么第 1B 行的跳转指令(例如)变成了 EBBD? 我知道“jmp”= EB但是BD是怎么计算的呢? 最佳答案 短跳转使用一个带符号的偏移量添加到 JMP 之后的指令地址。 例如,第一个 JMP
以下两者有什么区别: mov eax, [eax+4] 和 add eax, 4 mov eax, [eax] 如果不是,那么汇编器是否会选择哪个来进行某种优化? 最佳答案 这
看《The Shellcoder's Handbook》中的一些汇编和反汇编代码,发现一条指令的序列操作数是不一样的。 例如,在 assembly 上: mov ebx,0 并且,在反汇编时: mov
我有这个非常简单的汇编代码: start: add ax, 100 ; if ax overflow add to bx 1 jmp start 但我不知道如何检测 ax 寄存器溢出,有人可以帮
在 64 位 x86 汇编 nasm 中,如何将单个字节从寄存器移动到 .data 节中定义的内存位置? 我知道这有效 global _main section .data quotient db 0
我的汇编代码有问题。我想打印存储在寄存器 cx 中的数字,但是当我尝试打印它时,它打印的是 ascii 字符而不是 ascii 数字,所以我决定编写一个程序将 ascii char 转换为 ascii
我正在学习一些关于操作系统开发的教程,我发现了一篇关于多重引导 header 。这些是您必须定义的一些“神奇”值才能使用GRUB2。这些是命令: # Declare constants used f
为什么第 1B 行的跳转指令(例如)变成了 EBBD? 我知道“jmp”= EB但是BD是怎么计算的呢? 最佳答案 短跳转使用一个带符号的偏移量添加到 JMP 之后的指令地址。 例如,第一个 JMP
我正在尝试从内存中复制一些单词并使用汇编将其保存到另一个内存地址。我正在尝试为其编写代码,但我不确定其中的某些部分。我将简要描述我想要做什么。 源地址、目标地址和要复制的字数是函数的输入参数。 最佳答
当我们想要像这样创建一个初始化变量时: name db 'zara ali' 我们创建了一个字节大小变量,但我们在其中存储了一个字符串 这怎么可能?? 当我们使用这条指令时: MOV ecx, nam
我还是汇编的新手,我还不知道汇编中的许多命令代码。我想在 16 位寄存器中进行除法。我想打印它的内容。我知道我需要将寄存器的内容转换为 ASCII 进行打印,但同样,我的问题是除法。请帮我。 比如cx
使用有什么区别: c.eq.s $1, $2 bc1t L2 并使用: beq $1, $2, L2 如果他们做同样的事情,为什么有两种分支方式?如果它们不同,那么它们各自的好处是什么
源代码: int main() { int i; for(i=0, i : push rbp 2. 0x000055555555463b :
我是一名优秀的程序员,十分优秀!