2

I have the following C program:

#include <stdio.h>

int main() {
    int i = 0;
    int N = 10;
    while(i < N) {
        printf("counting to %d: %d", N, i);
        //i = i + 1;
    }
    return 0;
}

I would like to compile this first to assembly, then to binary for instructional purposes. So, I issue the following commands:

$ gcc -S count.c -o count.s
$ as -o count.o count.s
$ ld -o count -e main -dynamic-linker /lib64/ld-linux-x86-64.so.2 /usr/lib/x86_64-linux-gnu/libc.so count.o -lc

These compile the C to assembly, assemble the assembly to binary, and then link the library containing the printf function, respectively.

This works. Output:

counting to 10: 0counting to 10: 0counting to 10: 0counting to 10: 0counting to 10: 0counting to 10: 0counting to 10: 0counting to 10: 0counting to 10: 0counting to 10: 0counting to 10: 0counting to 10: 0counting to 10: 0

etc until I ctrl-c the program.

However, when I uncomment the i = i + 1 line:

Segmentation fault (core dumped)

What is going wrong here?

UPDATE: Here is count.s (with the i = i + 1 line included)

    .file   "count.c"
    .text
    .section    .rodata
.LC0:
    .string "counting to %d: %d"
    .text
    .globl  main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    subq    $16, %rsp
    movl    $0, -8(%rbp)
    movl    $10, -4(%rbp)
    jmp .L2
.L3:
    movl    -8(%rbp), %edx
    movl    -4(%rbp), %eax
    movl    %eax, %esi
    leaq    .LC0(%rip), %rdi
    movl    $0, %eax
    call    printf@PLT
    addl    $1, -8(%rbp)
.L2:
    movl    -8(%rbp), %eax
    cmpl    -4(%rbp), %eax
    jl  .L3
    movl    $0, %eax
    leave
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .ident  "GCC: (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0"
    .section    .note.GNU-stack,"",@progbits
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Him
  • 5,257
  • 3
  • 26
  • 83
  • 2
    Please share `count.s`. – CherryDT Nov 15 '21 at 17:27
  • 6
    `main` is not a suitable entrypoint and your code crashes when trying to return from `main` as there is nothing to return to. You need to remove the `-e main` part and link in the C runtime initialisation code. – fuz Nov 15 '21 at 17:31
  • @fuz "C runtime initialization code"? Do you know what library that might be? – Him Nov 15 '21 at 17:56
  • The C initialization is automatically linked with gcc when you omit -e main normally. – user123 Nov 15 '21 at 17:58
  • Like @fuz said, the segfault happens when you return from main because the crt wasn't linked so, instead of doing like the answer below, the code simply returns to an address somewhere on the stack which causes a segfault. – user123 Nov 15 '21 at 18:00
  • @user123 When I don't `-e main` I get `ld: warning: cannot find entry symbol _start; defaulting to 00000000004002d0`. – Him Nov 15 '21 at 18:01
  • You need to link it manually here because you do the commands in several steps here where gcc cannot pass the right arguments to ld. – user123 Nov 15 '21 at 18:05
  • @user123 do you have an article that you suggest? – Him Nov 15 '21 at 18:14
  • Simply search c runtime Linux, crt0.s, etc on google. It provides the _start symbol this is why you have the error above. ld expects the _start symbol by default so it looks for that but doesn't find it. – user123 Nov 15 '21 at 18:18
  • When you use -e main, you override the default and tell ld to use main as the symbol. – user123 Nov 15 '21 at 18:23
  • I deleted my comment because c runtime isn't a library you don't link it with -l -L. Instead just link the object files in order. Like: ```ld count.o -o count /usr/lib/crt1.o /usr/lib/crti.o /usr/lib/crtn.o \ -dynamic-linker /lib/ld-linux.so.2 -lc``` (see https://stackoverflow.com/questions/6656317/linking-a-c-program-directly-with-ld-fails-with-undefined-reference-to-libc-c). I haven't tested any of this though. Edit the paths to the object files for your own system. crt1.o isn't in /usr/lib for me. – user123 Nov 15 '21 at 19:22

2 Answers2

1

The below works perfectly fine for me on Ubuntu 20 (taken from Ciro Santilli's answer at Linking a C program directly with ld fails with undefined reference to `__libc_csu_fini`).

gcc -S count.c -o count.s
as -o count.o count.s
ld -o count -dynamic-linker /lib64/ld-linux-x86-64.so.2 /usr/lib/x86_64-linux-gnu/crt1.o /usr/lib/x86_64-linux-gnu/crti.o -L/usr/lib/gcc/x86_64-linux-gnu/4.8/ -lc count.o /usr/lib/x86_64-linux-gnu/crtn.o
user123
  • 2,510
  • 2
  • 6
  • 20
-3

If you on Linux 64 add at the end of the main function:

     mov eax, 60   
     xor edi, edi  
     syscall      

On linux 32

    mov  eax  1
    xor ebx,ebx  
    int 0x80 
0___________
  • 60,014
  • 4
  • 34
  • 74
  • 1
    OP is programming in C. This is not helpful. Also, it does not deinitialise the C runtime correctly. – fuz Nov 15 '21 at 18:55
  • @fuz he is not using C runtime at all. So your comment (and DV) is rather not very correct. This program entry point is the `main` function. – 0___________ Nov 15 '21 at 18:59
  • 1
    He is calling `printf` which needs correct deinitialisation to flush its buffers on exit. – fuz Nov 15 '21 at 19:47
  • depending on the implementation. I for example use in my uC development my on one which does not require any. – 0___________ Nov 15 '21 at 20:13
  • OP is programming for Linux where deinitialisation is required. – fuz Nov 15 '21 at 23:14