Come collegare un programma di montaggio di gas che utilizza la libreria standard C con ld senza usare gcc?

StackOverflow https://stackoverflow.com/questions/3577922

  •  01-10-2019
  •  | 
  •  

Domanda

Come esercizio per imparare più precisamente come programmi in C funzionano e quale livello minimo di contenuto deve esistere per un programma di essere in grado di utilizzare libc, ho preso su di me per tentare di programma principalmente in x86 assembly utilizzando gas e ld.

Come un po 'di divertimento sfida, ho assemblato con successo e legato diversi programmi legati a diverse librerie dinamiche self-made, ma io non sono riusciti a essere in grado di codice di un programma da zero per utilizzare chiamate di funzione libc senza direttamente utilizzando gcc.

Capisco le convenzioni di chiamata delle funzioni di libreria C individuale, e hanno programmi compilati fuori gcc attraverso l'uso di objdump e readelf accuratamente ispezionato, ma non ho ottenuto da nessuna parte per quanto riguarda le informazioni da includere in un file di assieme di gas e che cosa parametri di invocare in ld con successo link alla libc. Qualcuno ha qualche intuizione di questo?

Sto lavorando con Linux, su una macchina x86.

È stato utile?

Soluzione

Ci sono almeno tre cose che devi fare per utilizzare con successo libc con dinamica di collegamento:

  1. Collegamento /usr/lib/crt1.o, che contiene _start, che sarà il punto di ingresso per i binari ELF;
  2. Collegamento /usr/lib/crti.o (prima libc) e /usr/lib/crtn.o (dopo), che forniscono un codice di inizializzazione e finalizzazione;
  3. Segnala il linker che il binario utilizzerà il linker dinamico, /lib/ld-linux.so.

Ad esempio:

$ cat hello.s
 .text
 .globl main
main:
 push %ebp
 mov %esp, %ebp
 pushl $hw_str
 call puts
 add $4, %esp
 xor %eax, %eax
 leave
 ret

 .data
hw_str:
 .asciz "Hello world!"

$ as -o hello.o hello.s
$ ld -o hello -dynamic-linker /lib/ld-linux.so.2 /usr/lib/crt1.o /usr/lib/crti.o -lc hello.o /usr/lib/crtn.o
$ ./hello
Hello world!
$

Altri suggerimenti

Se si definisce main in assemblea

La risposta di Matteo fa un grande lavoro di raccontare i requisiti minimi.

mi permetta di mostrare come come trovare quei sentieri nel vostro sistema. Esegui:

gcc -v hello_world.c |& grep 'collect2' | tr ' ' '\n'

e poi raccogliere i file Matteo menzionato.

gcc -v ti dà gli usi comando GCC linker precisa.

collect2 è gli usi interni GCC eseguibili come linker front-end, che ha un'interfaccia simile a ld.

In Ubuntu 14.04 a 64 bit (GCC 4.8), ho finito con:

ld -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 \
  -lc hello_world.o \
  /usr/lib/x86_64-linux-gnu/crtn.o

Potrebbe anche essere necessario -lgcc e -lgcc_s. Vedi anche:? ne ho veramente bisogno libgcc

Se si definisce _start in assemblea

Se ho definito il _start, il mondo ciao da glibc lavorato con solo:

ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 -lc hello_world.o

Non sono sicuro se questo è robusto, vale a dire se le inizializzazioni crt possibile saltare in modo sicuro per richiamare le funzioni glibc. Vedi anche: Perché un programma di montaggio funziona solo se collegato con crt1.o crti.o e crtn.o?

Se si utilizza invece di _start main (come detto in alcune delle osservazioni di cui sopra), avrete anche bisogno di cambiare il modo in cui il programma si chiude, o si otterrà un guasto seg:

            .text
            .globl    _start
_start:     
            mov       $hw_str, %rdi
            call      puts
            movl      $0,%ebx   # first argument: exit code.
            movl      $1,%eax   # system call number: sys_exit.
            int       $0x80     # call kernel.

            .data
hw_str:     .asciz "Hello world!"

In Kubuntu 18.04.2 (GCC (Ubuntu 7.3.0-27ubuntu1 ~ 18.04) 7.3.0):

$ as -o hello.o hello.s
$ ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o hello hello.o -lc

Inoltre, un modo semplice per scoprire ciò che il linker dinamico è sul vostro sistema è quello di compilare un piccolo programma C e quindi eseguire ldd sul binario:

test.c:

int main() { return 0; }

compilare ed eseguire ldd contro eseguibile:

$ gcc -o test test.c
$ ldd test
    linux-vdso.so.1 (0x00007ffd0a182000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ff24d8e6000)
    /lib64/ld-linux-x86-64.so.2 (0x00007ff24ded9000)

Credo che qualcosa di simile dovrebbe funzionare:

  1. fare un semplice programma C
  2. gcc -S file.c
  3. Modifica file.s
  4. file.s gas
  5. ld file.o -lc crt1.o -o mio_prog
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top