GNU汇编程序没有生成我可以执行的程序

时间:2015-11-14 20:34:01

标签: gcc assembly gnu

我尝试组装一些由gcc生成的中间代码。我使用了命令as -o hello hello.s,据我所知,这是正确的语法。当我试图运行该程序时,它说bash: ./hello: cannot execute binary file。组装代码似乎没有问题,因为它是由gcc生成的代码,并且看起来我调用汇编程序的方式似乎没有任何问题,因为这似乎是正确的语法根据this manual。任何人都可以帮我这个吗?

2 个答案:

答案 0 :(得分:3)

编译器和汇编器都不会生成可执行文件。两者都生成目标文件,然后可以与其他对象和/或库文件链接以生成可执行文件。

例如,命令gcc -c只调用编译器;它可以将hello.c之类的源文件作为输入,并生成像hello.o这样的目标文件作为输出。

同样,as可以使用汇总语言源文件hello.s,并生成像hello.o这样的对象文件。

链接器是一个单独的工具,可以从目标文件生成可执行文件。

恰好在一步中编译和链接非常方便,这就是gcc命令默认执行的操作; gcc hello.c -o hello调用编译器链接器生成可执行文件。

请注意,gcc命令不仅仅是一个编译器。它是一个驱动程序,它调用预处理程序,编译器本身,汇编程序和/或链接程序。 (预处理器和汇编器,可以被认为是编译器的组件,在某些情况下它们甚至不是单独的程序,或者编译器可以生成机器对象代码而不是汇编代码。)< / p>

实际上,您也可以在汇编语言的一个命令中执行相同的多步骤过程:

gcc hello.s -o hello

将调用汇编程序和链接器并生成可执行文件。

这是特定于gcc(可能对于类Unix系统的大多数其他编译器)。其他实现可能以不同的方式组织。

答案 1 :(得分:2)

使用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&amp; T语法的32位Linux汇编程序,通过Hello World使用32位系统调用向标准输出显示int $0x80。它不使用任何 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 运行时初始化,然后调用名为{{1}的函数我们在汇编代码中提供的。要编译/汇编和链接,我们可以使用:

main

注意:在Windows上, C 运行时调用的入口点为gcc -m32 hello.s -o hello ,而不是_WinMain

使用NASM

在评论中,您还询问了 NASM ,因此我将在汇总时提供一些信息。假设您的程序集文件名为 hello.asm ,看起来像(它不需要 C 运行时库):

main

然后将其构建为可执行文件,您可以使用以下命令:

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

第一个命令将 hello.asm 汇编到 ELF 目标文件 hello.o 。第二行是链接。 nasm -f elf32 hello.asm -o hello.o gcc -nostdlib -m32 hello.o -o hello 排除了 C 运行时的链接(像-nostdlib等函数不可用)。第二行将 hello.o 链接到可执行文件 hello

或者,您可以跳过使用 GCC 并直接使用链接器:

_printf

如果您需要 C 运行时和库来调用nasm -f elf32 hello.asm -o hello.o ld -melf_i386 hello.o -o hello 之类的内容,那么它会有所不同。假设您拥有需要printf NASM 代码:

printf

然后将其构建为可执行文件,您可以使用以下命令:

    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