GNU/Linux >> Belajar Linux >  >> Linux

Apa arti dari setiap baris output perakitan dari dunia halo C?

Begini caranya:

        .file   "test.c"

Nama file sumber asli (digunakan oleh debugger).

        .section        .rodata
.LC0:
        .string "Hello world!"

String yang diakhiri dengan nol disertakan dalam bagian ".rodata" ("ro" berarti "hanya baca":aplikasi akan dapat membaca data, tetapi upaya apa pun untuk menulis ke dalamnya akan memicu pengecualian).

        .text

Sekarang kita menulis sesuatu ke bagian ".text", yang merupakan tempat kode.

.globl main
        .type   main, @function
main:

Kami mendefinisikan fungsi yang disebut "utama" dan terlihat secara global (file objek lain akan dapat menjalankannya).

        leal    4(%esp), %ecx

Kita simpan di register %ecx nilai 4+%esp (%esp adalah penunjuk tumpukan).

        andl    $-16, %esp

%esp sedikit dimodifikasi sehingga menjadi kelipatan 16. Untuk beberapa tipe data (format floating-point sesuai dengan double C dan long double ), kinerja lebih baik ketika akses memori berada di alamat yang kelipatan 16. Ini tidak terlalu diperlukan di sini, tetapi bila digunakan tanpa bendera pengoptimalan (-O2 ...), kompiler cenderung menghasilkan cukup banyak kode umum yang tidak berguna (yaitu kode yang mungkin berguna dalam beberapa kasus tetapi tidak di sini).

        pushl   -4(%ecx)

Yang ini agak aneh:pada saat itu, kata di alamat -4(%ecx) adalah kata yang berada di atas tumpukan sebelum andl . Kode mengambil kata itu (yang seharusnya menjadi alamat pengirim) dan mendorongnya lagi. Jenis ini mengemulasi apa yang akan diperoleh dengan panggilan dari fungsi yang memiliki tumpukan selaras 16 byte. Dugaan saya adalah push ini adalah sisa dari urutan penyalinan argumen. Karena fungsi telah menyesuaikan penunjuk tumpukan, fungsi harus menyalin argumen fungsi, yang dapat diakses melalui nilai lama penunjuk tumpukan. Di sini, tidak ada argumen, kecuali alamat pengembalian fungsi. Perhatikan bahwa kata ini tidak akan digunakan (sekali lagi, ini adalah kode tanpa pengoptimalan).

        pushl   %ebp
        movl    %esp, %ebp

Ini adalah prolog fungsi standar:kita menyimpan %ebp (karena kita akan memodifikasinya), lalu atur %ebp untuk menunjuk ke bingkai tumpukan. Setelah itu, %ebp akan digunakan untuk mengakses argumen fungsi, membuat %esp bebas lagi. (Ya, tidak ada argumen, jadi ini tidak berguna untuk fungsi tersebut.)

        pushl   %ecx

Kami menyimpan %ecx (kita akan membutuhkannya saat keluar dari fungsi, untuk mengembalikan %esp pada nilai sebelum andl ).

        subl    $20, %esp

Kami mencadangkan 32 byte pada tumpukan (ingat bahwa tumpukan bertambah "turun"). Ruang itu akan digunakan untuk menyimpan argumen ke printf() (itu berlebihan, karena ada satu argumen, yang akan menggunakan 4 byte [itu adalah pointer]).

        movl    $.LC0, (%esp)
        call    printf

Kami "mendorong" argumen ke printf() (yakni kami memastikan bahwa %esp menunjuk ke sebuah kata yang mengandung argumen, di sini $.LC0 , yang merupakan alamat string konstanta di bagian rodata). Kemudian kita panggil printf() .

        addl    $20, %esp

Ketika printf() kembali, kami menghapus ruang yang dialokasikan untuk argumen. addl ini membatalkan apa yang subl di atas.

        popl    %ecx

Kami memulihkan %ecx (didorong ke atas); printf() mungkin telah memodifikasinya (konvensi panggilan menjelaskan register mana yang dapat dimodifikasi oleh suatu fungsi tanpa memulihkannya saat keluar; %ecx adalah salah satu register tersebut).

        popl    %ebp

Epilog fungsi:ini memulihkan %ebp (sesuai dengan pushl %ebp di atas).

        leal    -4(%ecx), %esp

Kami memulihkan %esp ke nilai awalnya. Efek dari opcode ini adalah untuk menyimpan di %esp nilai %ecx-4 . %ecx diatur dalam opcode fungsi pertama. Ini membatalkan semua perubahan pada %esp , termasuk andl .

        ret

Fungsi keluar.

        .size   main, .-main

Ini mengatur ukuran main() fungsi:kapan saja selama perakitan, ". " adalah alias untuk "alamat tempat kami menambahkan sesuatu saat ini". Jika instruksi lain ditambahkan di sini, itu akan menuju ke alamat yang ditentukan oleh ". ". Jadi, ".-main ", di sini, adalah ukuran yang tepat dari kode fungsi main() . .size direktif menginstruksikan assembler untuk menulis informasi itu di file objek.

        .ident  "GCC: (GNU) 4.3.0 20080428 (Red Hat 4.3.0-8)"

GCC sangat suka meninggalkan jejak aksinya. String ini berakhir sebagai semacam komentar di file objek. Penaut akan menghapusnya.

        .section        .note.GNU-stack,"",@progbits

Bagian khusus tempat GCC menulis bahwa kode dapat mengakomodasi tumpukan yang tidak dapat dieksekusi. Ini adalah kasus normal. Tumpukan yang dapat dieksekusi diperlukan untuk beberapa penggunaan khusus (bukan standar C). Pada prosesor modern, kernel dapat membuat tumpukan yang tidak dapat dieksekusi (tumpukan yang memicu pengecualian jika seseorang mencoba mengeksekusi sebagai kode beberapa data yang ada di tumpukan); ini dipandang oleh sebagian orang sebagai "fitur keamanan" karena meletakkan kode pada stack adalah cara yang umum untuk mengeksploitasi buffer overflows. Dengan bagian ini, file yang dapat dieksekusi akan ditandai sebagai "kompatibel dengan tumpukan yang tidak dapat dieksekusi" yang akan dengan senang hati disediakan oleh kernel.


    leal    4(%esp), %ecx
    andl    $-16, %esp
    pushl   -4(%ecx)
    pushl   %ebp
    movl    %esp, %ebp
    pushl   %ecx
    subl    $20, %esp

instruksi ini tidak dapat dibandingkan dengan program c Anda, instruksi ini selalu dijalankan di awal setiap fungsi (tetapi tergantung pada kompiler/platform)

    movl    $.LC0, (%esp)
    call    printf

blok ini sesuai dengan panggilan printf() Anda. instruksi pertama menempatkan argumennya pada tumpukan (penunjuk ke "hello world") lalu memanggil fungsi tersebut.

    addl    $20, %esp
    popl    %ecx
    popl    %ebp
    leal    -4(%ecx), %esp
    ret

instruksi ini berlawanan dengan blok pertama, mereka semacam barang manipulasi tumpukan. selalu dieksekusi juga


Berikut beberapa tambahan untuk @Thomas Pornin jawabannya.

  • .LC0 konstanta lokal, misalnya string literal.
  • .LFB0 awal fungsi lokal,
  • .LFE0 akhiran fungsi lokal,

Akhiran dari label ini adalah angka, dan dimulai dari 0.

Ini adalah konvensi assembler gcc.


Linux
  1. Apa refid dalam output ntpq -p?

  2. Apa arti dari *nix?

  3. Apa arti dari fork() dan grep di Linux?

  1. Apa arti 'daftar hitam' di GStreamer?

  2. Apa arti pipa linux | melakukan?

  3. Apa kolom buffer di output dari gratis?

  1. Cara Menyisipkan Teks di awal setiap baris di Vim

  2. Apa arti curl -k -i -X ​​di Linux?

  3. Arti baris buffer/cache di keluaran free