编译gcc时的问题.s链接时的代码

时间:2016-03-08 20:48:05

标签: c linux gcc assembly linker

第一次来到这里,我正在运行Kali linux 64bits,我是一个Linux新手和一个新的ASM以及....所以我在C中拉了一个代码,这完全没问题.....这里是代码:

#include<stdio.h>
#include<string.h>    //strlen
#include<sys/socket.h>
#include<arpa/inet.h> //inet_addr

int main(int argc , char *argv[])
{
int socket_desc;
struct sockaddr_in server;
char *message , server_reply[2000];

//Create socket
socket_desc = socket(AF_INET , SOCK_STREAM , 0);
if (socket_desc == -1)
{
    printf("Could not create socket");
}

server.sin_addr.s_addr = inet_addr("127.0.0.1");
server.sin_family = AF_INET;
server.sin_port = htons( 2000 );

//Connect to remote server
if (connect(socket_desc , (struct sockaddr *)&server , sizeof(server)) <0)
{
    puts("connect error");
    return 1;
}

puts("Connected\n");

//Send some data
message = "Hola!!!!\n\r\n";
if( send(socket_desc , message , strlen(message) , 0) < 0)
{
    puts("Send failed");
    return 1;
}
puts("Data Send\n");

//Receive a reply from the server
if( recv(socket_desc, server_reply , 2000 , 0) < 0)
{
    puts("recv failed");
}
puts("Reply received\n");
puts(server_reply);

return 0;
}

所以...我使用gcc -S -o example.s example.c来获取ASM代码......这是:

.file   "test.c"
.section    .rodata
.LC0:
.string "Could not create socket"
.LC1:
.string "127.0.0.1"
.LC2:
.string "connect error"
.LC3:
.string "Connected\n"
.align 8
.LC4:
.string "Hola!!  , \n\r\n"
.LC5:
.string "Send failed"
.LC6:
.string "Data Send\n"
.LC7:
.string "recv failed"
.LC8:
.string "Reply received\n"
.text
.globl  main
.type   main, @function
main:
.LFB2:
.cfi_startproc
pushq   %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq    %rsp, %rbp
.cfi_def_cfa_register 6
subq    $2048, %rsp
movl    %edi, -2036(%rbp)
movq    %rsi, -2048(%rbp)
movl    $0, %edx
movl    $1, %esi
movl    $2, %edi
call    socket
movl    %eax, -4(%rbp)
cmpl    $-1, -4(%rbp)
jne .L2
movl    $.LC0, %edi
movl    $0, %eax
call    printf
.L2:
movl    $.LC1, %edi
call    inet_addr
movl    %eax, -28(%rbp)
movw    $2, -32(%rbp)
movl    $2000, %edi
call    htons
movw    %ax, -30(%rbp)
leaq    -32(%rbp), %rcx
movl    -4(%rbp), %eax
movl    $16, %edx
movq    %rcx, %rsi
movl    %eax, %edi
call    connect
testl   %eax, %eax
jns .L3
movl    $.LC2, %edi
call    puts
movl    $1, %eax
jmp .L7
.L3:
movl    $.LC3, %edi
call    puts
movq    $.LC4, -16(%rbp)
movq    -16(%rbp), %rax
movq    %rax, %rdi
call    strlen
movq    %rax, %rdx
movq    -16(%rbp), %rsi
movl    -4(%rbp), %eax
movl    $0, %ecx
movl    %eax, %edi
call    send
testq   %rax, %rax
jns .L5
movl    $.LC5, %edi
call    puts
movl    $1, %eax
jmp .L7
.L5:
movl    $.LC6, %edi
call    puts
leaq    -2032(%rbp), %rsi
movl    -4(%rbp), %eax
movl    $0, %ecx
movl    $2000, %edx
movl    %eax, %edi
call    recv
testq   %rax, %rax
jns .L6
movl    $.LC7, %edi
call    puts
.L6:
movl    $.LC8, %edi
call    puts
leaq    -2032(%rbp), %rax
movq    %rax, %rdi
call    puts
movl    $0, %eax
.L7:
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE2:
.size   main, .-main
.ident  "GCC: (Debian 4.9.2-10) 4.9.2"

因此在使用example.s -o example.o后,我使用了ld example.o -o示例,这就是我得到以下错误的地方:

  

ld:警告:找不到条目符号_start;默认为00000000004000b0   test.o:在函数main': test.c:(.text+0x28): undefined reference to socket'中   test.c :(。text + 0x40):对printf' test.c:(.text+0x4a): undefined reference to inet_addr'的未定义引用   test.c :(。text + 0x5d):对htons' test.c:(.text+0x77): undefined reference to connect'的未定义引用   test.c :(。text + 0x85):对puts' test.c:(.text+0x99): undefined reference to puts'的未定义引用   test.c :(。text + 0xad):对strlen' test.c:(.text+0xc3): undefined reference to send'的未定义引用   test.c :(。text + 0xd2):对puts' test.c:(.text+0xe3): undefined reference to puts'的未定义引用   test.c :(。text + 0xfe):对recv' test.c:(.text+0x10d): undefined reference to puts'的未定义引用   test.c :(。text + 0x117):未定义引用puts' test.c:(.text+0x126): undefined reference to puts'

在我看来,gcc没有正确使用.start,global main等等,但说实话我不知道如何解决它。如果这是正确的那么为什么呢?

任何帮助都会很感激。

谢谢。

4 个答案:

答案 0 :(得分:2)

问题是ld example.o -o example尝试仅链接example.o而不是其他任何内容。要获得缺少的符号,您需要链接更多(例如,启动代码,标准库,C运行时等)。尝试gcc -v example.c以查看应如何调用链接器。

答案 1 :(得分:2)

Harry's answer中给出的命令是好的:

gcc -Wall -O -fverbose-asm -S example.c
gcc -c example.s -o example.o
gcc  example.o -o example

基本上,您应该知道GCC会将您的代码链接到:

如何将所有这些链接在一起的是gcc命令,它将启动一些ld。在编译命令中将gcc替换为gcc -v,以了解究竟发生了什么。如果您想发布自己的ld命令,则应添加提供上面列出的选项。你得到的错误主要是因为缺乏crt0&amp; libc

Linux上的BTW大多数C标准库(例如GNU libcmusl-libc)都是free software(GCC也是如此),因此您可以研究它们的源代码。

另请尝试gcc -dumpspecs,其中介绍what gcc了解发出各种命令(注意gcc只是一个驱动程序;真正的C编译器是{{1} }})。另请阅读GCC上的wikipage。 documentation of GCC MELT上的一些幻灯片和参考文献提供了更多信息。另见this及其中的图片。

我强烈建议您使用cc1汇总(您的某些汇编代码)并链接内容(因为您不想要处理上面提到的所有血腥细节,加上我没有提到的其他一些细节。

答案 2 :(得分:1)

这是一个重要的部分:

/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../x86_64-linux-gnu/crt1.o
/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../x86_64-linux-gnu/crti.o
/usr/lib/gcc/x86_64-linux-gnu/4.9/crtbegin.o
-lgcc 
--as-needed -lgcc_s
--no-as-needed -lc -lgcc
--as-needed -lgcc_s
--no-as-needed /usr/lib/gcc/x86_64-linux-gnu/4.9/crtend.o
/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../x86_64-linux-gnu/crtn.o

crt1crticrtbegin提供实际定义_start入口点的启动代码(稍后控件将传递给您的main ),stdio已初始化等。同样strandcrtnmain返回后处理清理。 lc提供标准库(如puts和其他缺少的符号)。 lgcclgcc_s具有gcc特定的运行时支持。

最重要的是,您需要将所有内容链接到其中。

答案 3 :(得分:0)

试试这个

gcc -Wall -O -fverbose-asm -S example.c
gcc -c example.s -o example.o
gcc  example.o -o example