【问题标题】:gcc assembler - create only the minimal instructions necessarygcc 汇编器 - 只创建必要的最少指令
【发布时间】:2019-10-29 20:03:33
【问题描述】:

我在汇编中创建了一个非常小的应用程序。它将一些寄存器设置为 0 并进行乘法运算。没什么特别的。

但是,gcc 在机器码中添加了很多我不想要的东西。

我在 objdump 中找到的一小部分内容:

  • deregister_tm_clones
  • register_tm_clones
  • __do_global_dtors_aux
  • frame_dummy
  • __libc_fini_array
  • memset
  • 还有更多

我知道我不需要它们,但我不知道如何告诉编译器停止包含它们。我尝试使用优化选项,但这并没有改变任何东西。

我编译它基本上是这样的:GCC -o ./main.elf ./main.S

非常感谢您的帮助!

【问题讨论】:

  • 如果您使用 gcc 驱动程序的唯一原因是预处理汇编代码,您可以自己运行预处理器 (cpp),然后运行 ​​gas 和 ld 进行汇编和链接。

标签: gcc assembly embedded machine-code


【解决方案1】:

GCC 自动链接 C/C++ 运行时启动 crt0.o 和标准库。您可以提供自己的启动代码来覆盖默认值,并提供命令行选项来强制它不链接任何标准库。

控制启动和默认库的选项包括:

-nostartfiles
-nostdlib
-nodefaultlubs
-nolibc

每个都以不同的方式影响链接,但在这种情况下,-nostdlib 将排除 crt0.o 和标准库。当然,如果您的代码没有引用标准库,那么在任何情况下都不会链接任何内容,但是如果确实引用了它,明确排除它会有助于生成链接错误。

见:https://gcc.gnu.org/onlinedocs/gcc/Link-Options.html

请注意,如果您的代码未建立有效的 C 运行时环境,例如提供静态初始化和堆栈(最低限度),则某些 C 代码可能无法按预期方式运行。如果您不使用与 crt0 相同的默认入口点(我认为是_start),您可能还需要通过--entry=entry 指定入口点。

或者,您可以使用 -c 选项调用 gcc 并单独调用链接器 ld 而无需指定任何库。

【讨论】:

  • 是的,如果你想跳过 CRT 启动,你写一个 _start 而不是 main_start 不是一个函数,你不能从它ret,只能进行_exitexit_group 系统调用。
  • @PeterCordes 这不是我想要的。我假设它定义了直接入口点而不是 C 入口点。我站得更正了。我想如果 crt0 替换不引用它就没有效果?这只是一个小提示,也许不是我想的问题。
  • 哦,内核在运行用户空间之前还是先建了一个栈。请参阅 ABI 文档以获取初始进程状态,而不是您在 main 顶部获得的函数调用状态。但是如果使用--entry=main 尝试从main 返回,它将使您的代码出现段错误。此外,在某些系统(例如 Linux)上,即使 CRT 启动没有运行其 init 函数,动态链接也可以让 libc 自行初始化,但是将 libc 静态链接到静态可执行文件会导致许多函数(如 stdio 和 exit)损坏,除了不需要像_exit这样的任何数据的系统调用包装器。
  • 无论如何,你通常不想使用--entry,如果你正在编写ELF入口点,只需调用你的入口点_startld 默认将其作为入口点。 CRT0 提供了一个 _start(最终调用 main),但是如果你想省略 CRT,你必须自己编写。
  • @PeterCordes 实际上我收回了这一点,考虑到嵌入式标签,这更有可能是一个独立系统。当您提到内核建立的环境时,我们忘记了。在 ARM Cortex M 中,实际上可以用 C 编写启动代码(如果您不依赖静态 init)直接从复位向量运行,因为初始堆栈指针值包含在向量表中。但我离题了。
【解决方案2】:

所以.S:

nop
nop

然后构建。

as so.S -o so.o
ld -Ttext=0x1000 so.o -o so.elf
objdump -D so.elf

Disassembly of section .text:

0000000000001000 <__bss_start-0x200002>:
    1000:   90                      nop
    1001:   90                      nop

objcopy -O binary so.elf so.bin
hexdump -C so.bin
00000000  90 90                                             |..|
00000002

使用 gcc

gcc -nostartfiles -nostdlib -nodefaultlibs -ffreestanding so.S -Xlinker "-Ttext=0x1000" -o so.elf

这会在文件中留下额外的垃圾,但是

gcc so.S -c -o so.o
ld -Ttext=0x2000 so.o -o so.elf
ld: warning: cannot find entry symbol _start; defaulting to 0000000000002000
objdump -D so.elf


Disassembly of section .text:

0000000000002000 <__bss_start-0x200002>:
    2000:   90                      nop
    2001:   90                      nop

但是如果编写汇编语言,你最好使用汇编器而不是编译器。

_start 不是必需的,除非您需要在文件中定义一个入口点,然后您需要这样做:

.globl _start
_start:

可能加上链接器中的某些内容,以将其作为文件格式(如 elf、exe 等)的入口点。

也适用于交叉编译

arm-none-eabi-as so.s -o so.o
arm-none-eabi-ld -Ttext=0x3000 so.o -o so.elf
arm-none-eabi-ld: warning: cannot find entry symbol _start; defaulting to 0000000000003000
arm-none-eabi-objdump -D so.elf

so.elf:     file format elf32-littlearm


Disassembly of section .text:

00003000 <__bss_end__-0x10008>:
    3000:   e1a00000    nop         ; (mov r0, r0)
    3004:   e1a00000    nop         ; (mov r0, r0)


pdp11-aout-as so.s -o so.elf
pdp11-aout-as so.s -o so.o
pdp11-aout-ld -Ttext=0x400 so.o -o so.elf
pdp11-aout-objdump -D so.elf

so.elf:     file format a.out-pdp11


Disassembly of section .text:

00000400 <so.o>:
 400:   00a0            nop
 402:   00a0            nop

等等。

【讨论】:

  • 是的,我从不做 gcc 的事情,所以我从 so.s 开始,然后复制到 so.S,所以一些命令行使用了一个文件,而另一些则使用了另一个。
猜你喜欢
  • 2010-12-31
  • 1970-01-01
  • 2020-04-13
  • 1970-01-01
  • 1970-01-01
  • 2020-10-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多