繁体   English   中英

GNU汇编器未生成我可以执行的程序

[英]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系统的大多数其他编译器)。 其他实现的组织方式可能有所不同。

使用GNU汇编器

假设您的程序集文件名为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运行时和库,以便可以利用Cprintf之类的函数,则情况会有些不同。 假设您有需要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合作

在评论中,您还询问了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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM