[英]GNU assembler did not produce a program that I can execute
我尝试组装一些由gcc生成的中间代码。 我将命令as -o hello hello.s
,据我所知,这是正确的语法。 当我尝试运行该程序时,它说bash: ./hello: cannot execute binary file
。 汇编代码似乎没有问题,因为它是gcc生成的代码,而且我调用汇编程序的方式似乎也没有问题,因为根据本手册 。 谁能帮我这个?
编译器和汇编器均不会生成可执行文件。 两者都生成目标文件 ,然后可以将其与其他目标文件和/或库文件链接以生成可执行文件。
例如,命令gcc -c
仅调用编译器。 它可以将诸如hello.c
类的源文件作为输入,并生成诸如hello.o
类的目标文件作为输出。
同样, as
可以采用诸如hello.s
类的汇编语言源文件并生成诸如hello.o
类的目标文件。
链接器是一个单独的工具,可以从目标文件生成可执行文件。
碰巧一步编译和链接是如此方便,以至于默认情况下gcc
命令就是这样做的。 gcc hello.c -o hello
调用编译器和链接器以生成可执行文件。
请注意, gcc
命令不仅是编译器。 它是一个驱动程序,调用预处理器,适当的编译器,汇编器和/或链接器。 (可以将预处理器和汇编器视为编译器的组件,在某些情况下,它们甚至不是独立的程序,或者编译器可以生成机器对象代码而不是汇编代码。)
实际上,对于汇编语言,您也可以在一个命令中执行相同的多步骤过程:
gcc hello.s -o hello
将调用汇编器和链接器并生成一个可执行文件。
这是特定于gcc的(可能适用于Unix系统的大多数其他编译器)。 其他实现的组织方式可能有所不同。
假设您的程序集文件名为hello.s ,看起来像(假设32位Linux目标):
.data
msg: .asciz "Hello World\n"
msglen = .-msg
.text
.global _start
_start:
/* Use int $0x80/eax=4 to write to STDOUT */
/* Output Hello World */
mov $4, %eax /* write system call */
mov $0, %ebx /* File descriptor 0 = STDOUT */
mov $msg, %ecx /* The message to output */
mov $msglen, %edx /* length of message */
int $0x80 /* make the system call */
/* Exit the program with int $0x80/eax=1 */
mov $1, %eax /* 1 = exit system call */
mov $0, %ebx /* value to exit with */
int $0x80 /* make the system call */
这是一个采用AT&T语法的32位Linux汇编程序,该程序使用32位系统调用通过int $0x80
向标准输出显示Hello World
。 它不使用任何C函数,因此可以使用GNU汇编程序进行汇编as
并与GNU链接程序ld
链接以生成最终的可执行文件。
as --32 hello.s -o hello.o
ld -melf_i386 hello.o -o hello
第一行将hello.s组装成一个名为hello.o的32位ELF对象。 然后,使用第二个命令将hello.o链接到名为hello的32位ELF可执行文件。 默认情况下,GNU链接器假定您的程序在标签_start
处开始执行。
或者,您可以使用GCC使用以下命令来汇编和链接该程序:
gcc -nostdlib -m32 hello.s -o hello
这将生成一个名为hello的32位ELF可执行文件。 -nostdlib
告诉GCC不要在C运行时库中链接,并允许我们使用_start
作为程序的入口点。
如果要将汇编程序链接到C运行时和库,以便可以利用C的printf之类的函数,则情况会有些不同。 假设您有需要printf (或任何C库函数)的程序:
.data
msg: .asciz "Hello World\n"
.text
.global main
main:
push %ebp /* Setup the stack frame */
mov %esp, %ebp /* Stack frames make GDB debugging easier */
push $msg /* Message to print */
call printf
add $4,%esp /* cleanup the stack */
xor %eax, %eax /* Return 0 when exiting */
mov %ebp, %esp /* destroy our stack frame */
pop %ebp
ret /* Return to C runtime that called us
and allow it to do program termination */
现在,您的入口点必须是大多数* nix类型系统的main
入口。 原因是C 运行时将具有一个名为_start
的入口点,该入口点将对C运行时进行初始化,然后调用我们在汇编代码中提供的名为main
的函数。 要编译/汇编和链接它,我们可以使用:
gcc -m32 hello.s -o hello
注意:在Windows上, C运行时调用的入口点是_WinMain
而不是main
。
在评论中,您还询问了NASM,因此组装时我将提供一些信息。 假设您的程序集文件名为hello.asm ,看起来像(不需要C运行时库):
SECTION .data ; data section
msg db "Hello World", 13, 10
len equ $-msg
SECTION .text ; code section
global _start ; make label available to linker
_start: ; standard gcc entry point
mov edx,len ; length of string to print
mov ecx,msg ; pointer to string
mov ebx,1 ; write to STDOUT (file descriptor 0)
mov eax,4 ; write command
int 0x80 ; interrupt 80 hex, call kernel
mov ebx,0 ; exit code, 0=normal
mov eax,1 ; exit command to kernel
int 0x80 ; interrupt 80 hex, call kernel
然后将其构建为可执行文件,可以使用以下命令:
nasm -f elf32 hello.asm -o hello.o
gcc -nostdlib -m32 hello.o -o hello
第一个命令将hello.asm组装到ELF目标文件hello.o中 。 第二行进行链接。 -nostdlib
从链接中排除C运行时(如_printf
等功能将不可用)。 第二行将hello.o链接到可执行文件hello 。
另外,您可以跳过使用GCC并直接使用链接器,如下所示:
nasm -f elf32 hello.asm -o hello.o
ld -melf_i386 hello.o -o hello
如果您需要C运行时和库来调用诸如printf
东西,那就有些不同了。 假设您有这个需要printf
NASM代码:
extern printf
SECTION .data ; Data section, initialized variables
msg: db "Hello World", 13, 10, 0
SECTION .text ; Code section.
global main ; the standard gcc entry point
main: ; the program label for the entry point
push ebp ; Setup the stack frame
mov ebp, esp ; Stack frames make GDB debugging easier
push msg ; Message to print
call printf
add esp, 4 ; Cleanup the stack
mov eax, 0 ; Return value of 0
mov esp, ebp ; Destroy our stack frame
pop ebp
endit:
ret ; Return to C runtime that called us
; and allow it to do program termination
然后将其构建为可执行文件,可以使用以下命令:
nasm -f elf32 hello.asm -o hello.o
gcc -m32 hello.o -o hello
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.